From 3d931502aa40193d50c7aeb22e290acce12c6c42 Mon Sep 17 00:00:00 2001 From: opussf Date: Sat, 23 May 2020 15:58:46 -0700 Subject: [PATCH 01/15] Initial work --- FEATURE.md | 6 +++++ src/saxParser.lua | 58 +++++++++++++++++++++++++++++++++++++++++++++++ test/test.lua | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 FEATURE.md create mode 100644 src/saxParser.lua diff --git a/FEATURE.md b/FEATURE.md new file mode 100644 index 0000000..9458473 --- /dev/null +++ b/FEATURE.md @@ -0,0 +1,6 @@ +## FEATURES + +# xml2src + +Write a SAX parser to handle the wow.xml files for addons. + diff --git a/src/saxParser.lua b/src/saxParser.lua new file mode 100644 index 0000000..06b1d12 --- /dev/null +++ b/src/saxParser.lua @@ -0,0 +1,58 @@ +-- saxParser.lua +----------------------------------------- +-- Author : Opussf +-- Date : $Date:$ +-- Revision: @VERSION@ +----------------------------------------- + +-- A SAX parser takes a content handler, which provides these methods: +-- startDocument() -- called at the start of the Document +-- endDocument() -- called at the end of the Document +-- startElement( tagName, attrs ) -- with each tag start. attrs is a table of attributes and values +-- endElement( tagName ) -- when a tag ends +-- characters( char ) -- for each character not being in a tag +-- The parser calls each of these methods as these events happen. + +-- A SAX parser defines a parser (created with makeParser) +-- a Parser has a ContentHandler assigned +-- a Parser also defines these methods: +-- setContentHandler( contentHandler ) +-- setFeature() -- prob not going to implement +-- parse( text ) +-- parse( file ) + +contentHandler = {} +function contentHandler.startDocument() +end +function contentHandler.endDocument() +end +function contentHandler.startElement( tagName, attrs ) +end +function contentHandler.endElement( tagName ) +end +function contentHandler.characters( char ) +end + +saxParser = {} +-- SAX Parser +-- interface +function saxParser.makeParser() + -- make a parser. This is probably intended to be a + return saxParser +end +function saxParser.setContentHandler( contentHandlerIn ) + -- takes a table + saxParser.contentHandler = contentHandlerIn +end +function saxParser.setFeature() + -- research this +end +function saxParser.parse( fileIn ) + +end + + + + + + diff --git a/test/test.lua b/test/test.lua index 04a7c49..891c43f 100644 --- a/test/test.lua +++ b/test/test.lua @@ -1032,6 +1032,63 @@ end ----- TOC tests +----- Sax tests +require "saxParser" +function test.before_testContentHandler() +end +function test.after_testContentHandler() +end +function test.testContentHandler_hasStartDocument() + test.before_testContentHandler() + assertTrue( contentHandler.startDocument ) + test.after_testContentHandler() +end +function test.testContentHandler_hasEndDocument() + test.before_testContentHandler() + assertTrue( contentHandler.endDocument ) + test.after_testContentHandler() +end +function test.testContentHandler_hasStartElement() + test.before_testContentHandler() + assertTrue( contentHandler.startElement ) + test.after_testContentHandler() +end +function test.testContentHandler_hasEndElement() + test.before_testContentHandler() + assertTrue( contentHandler.endElement ) + test.after_testContentHandler() +end +function test.testContentHandler_hasCharacters() + test.before_testContentHandler() + assertTrue( contentHandler.characters ) + test.after_testContentHandler() +end + + +function test.before_testSax() +end +function test.after_testSax() +end +function test.testSAX_MakeParser() + test.before_testSax() + assertTrue( saxParser.makeParser() ) + test.after_testSax() +end +function test.testSAX_setContentHandler() + test.before_testSax() + ch = contentHandler + parser = saxParser.makeParser() + parser.setContentHandler( ch ) + assertTrue( parser.contentHandler ) + ch = nil + parser = nil + test.after_testSax() +end + + + + + ---------------------------------- -- Run the tests ---------------------------------- From 24b763949c91f964ad49a7b16f511e439c59ceae Mon Sep 17 00:00:00 2001 From: opussf Date: Sat, 23 May 2020 16:57:50 -0700 Subject: [PATCH 02/15] ContentHandler uses 'this' struture. Call both startDocument and endDocument when parsing. --- src/saxParser.lua | 34 +++++++++++++++++++++++---- test/test.lua | 59 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 83 insertions(+), 10 deletions(-) diff --git a/src/saxParser.lua b/src/saxParser.lua index 06b1d12..3610771 100644 --- a/src/saxParser.lua +++ b/src/saxParser.lua @@ -21,16 +21,18 @@ -- parse( text ) -- parse( file ) + contentHandler = {} -function contentHandler.startDocument() +-- normally the contentHandler is an object, where data structures are created in the new object. +function contentHandler.startDocument( this ) end -function contentHandler.endDocument() +function contentHandler.endDocument( this ) end -function contentHandler.startElement( tagName, attrs ) +function contentHandler.startElement( this, tagName, attrs ) end -function contentHandler.endElement( tagName ) +function contentHandler.endElement( this, tagName ) end -function contentHandler.characters( char ) +function contentHandler.characters( this, char ) end saxParser = {} @@ -48,11 +50,33 @@ function saxParser.setFeature() -- research this end function saxParser.parse( fileIn ) + f = io.open( fileIn, "r" ) + if f then fileIn = f:read( "*all" ) end -- read the contents of the file + + -- call the startDocument method for the given contentHandler + if saxParser.contentHandler and saxParser.contentHandler.startDocument then + saxParser.contentHandler:startDocument() + end + -- for line in io.lines( f ) do + -- + -- for each char in lua: + -- call the endDocument method for the given contentHandler + if saxParser.contentHandler and saxParser.contentHandler.endDocument then + saxParser.contentHandler:endDocument() + end end +--[[ +loop over a string in lua: +for i = , #str do + local c = str:sub( i, i ) + +for c in str:match( "." ) do +str:gsub( ".", function ) +]] diff --git a/test/test.lua b/test/test.lua index 891c43f..b63873e 100644 --- a/test/test.lua +++ b/test/test.lua @@ -1035,8 +1035,16 @@ end ----- Sax tests require "saxParser" function test.before_testContentHandler() + originalContentHandler = {} + for k,v in pairs( contentHandler ) do + originalContentHandler[k] = v + end end function test.after_testContentHandler() + contentHandler = {} + for k,v in pairs( originalContentHandler ) do + contentHandler[k] = v + end end function test.testContentHandler_hasStartDocument() test.before_testContentHandler() @@ -1066,8 +1074,16 @@ end function test.before_testSax() + originalContentHandler = {} + for k,v in pairs( contentHandler ) do + originalContentHandler[k] = v + end end function test.after_testSax() + contentHandler = {} + for k,v in pairs( originalContentHandler ) do + contentHandler[k] = v + end end function test.testSAX_MakeParser() test.before_testSax() @@ -1080,13 +1096,46 @@ function test.testSAX_setContentHandler() parser = saxParser.makeParser() parser.setContentHandler( ch ) assertTrue( parser.contentHandler ) - ch = nil - parser = nil + --ch = nil + --parser = nil test.after_testSax() end - - - +function test.testSAX_Parse_StartDocument_TextIn() + test.before_testSax() + ch = contentHandler + ch.startDocument = function( this ) this.started = true; end + parser = saxParser.makeParser() + parser.setContentHandler( ch ) + parser.parse( "" ) + assertTrue( ch.started ) +end +function test.testSAX_Parse_StartDocument_FileIn() + test.before_testSax() + ch = contentHandler + ch.startDocument = function( this ) this.started = true; end + parser = saxParser.makeParser() + parser.setContentHandler( ch ) + parser.parse( "../build.xml" ) + assertTrue( ch.started ) +end +function test.testSAX_Parse_StartDocument_NotGiven_TextIn() + test.before_testSax() + ch = contentHandler + ch.startDocument = nil + parser = saxParser.makeParser() + parser.setContentHandler( ch ) + parser.parse( "" ) + assertIsNil( ch.started ) +end +function test.testSAX_Parse_EndDocument_TextIn() + test.before_testSax() + ch = contentHandler + ch.endDocument = function( this ) this.ended = true; end + parser = saxParser.makeParser() + parser.setContentHandler( ch ) + parser.parse( "" ) + assertTrue( ch.ended ) +end ---------------------------------- From c03d10254d8a2ac8591237c16e7f60e273cfa2a0 Mon Sep 17 00:00:00 2001 From: opussf Date: Sat, 23 May 2020 20:05:13 -0700 Subject: [PATCH 03/15] More works If you don't stand for something, you'll fall for anything. --- src/saxParser.lua | 99 +++++++++++++++++++++++++++++++++++++++++++++-- test/test.lua | 40 ++++++++++++++++--- 2 files changed, 130 insertions(+), 9 deletions(-) diff --git a/src/saxParser.lua b/src/saxParser.lua index 3610771..b874038 100644 --- a/src/saxParser.lua +++ b/src/saxParser.lua @@ -57,9 +57,102 @@ function saxParser.parse( fileIn ) if saxParser.contentHandler and saxParser.contentHandler.startDocument then saxParser.contentHandler:startDocument() end - -- for line in io.lines( f ) do - -- - -- for each char in lua: + + -- loop through each char + stateValue = 0 -- current state of the parser + -- 0 is outside of a tag + -- 1 is in a tag name + + tagName = "" + attributes = {} -- start with an empty attributes table + attribKey = "" -- + attribValue = "" -- + tagDepth = 0 -- how many tags deep + + -- start by looking for a start tag + while( #fileIn > 0 ) do + print( fileIn ) + if stateValue == 0 then -- outside of a tag + local tagStart, tagEnd, tagName = string.find( fileIn, "^<(%S+)" ) -- tags start with a < + if tagStart then + print( "Found a tag start: <"..tagName ) + tagDepth = tagDepth + 1 + stateValue = 1 + attributes = {} + fileIn = string.sub( fileIn, tagEnd+1 ) + end + tagEndStart, tagEndEnd, tagName = string.find( fileIn, "^" ) + if tagEndStart then + print( "Found a closing tag: " ) + tagDepth = tagDepth - 1 + stateValue = 0 + fileIn = string.sub( fileIn, tagEndEnd+1 ) + end + elseif stateValue == 1 then + local attribStart, attribEnd, key, value = string.find( fileIn, "^%s*(%S+)%s*=%s*[\"\'](%S*)[\"\']" ) + if attribStart then + print( "Found an attribute: "..key.."=\""..value.."\"" ) + attributes[key] = value + --print( "set "..key.." to "..value ) + fileIn = string.sub( fileIn, attribEnd+1 ) + else + tagEndStart, tagEndEnd, tagEnd = string.find( fileIn, "^%s*([/]*>)" ) + print( "tagEnd:"..tagName ) + if tagEndStart then + saxParser.contentHandler:startElement( tagName, attributes ) + if tagEnd == "/>" then -- this is also the end of the tag + saxParser.contentHandler:endElement( tagName ) + tagDepth = tagDepth - 1 + end + stateValue = 0 + fileIn = string.sub( fileIn, tagEndEnd+1 ) + end + end + end + end + + + + +--[[ + if msg then + local i,c = strmatch(msg, "^(|c.*|r)%s*(%d*)$") + if i then -- i is an item, c is a count or nil + return i, c + else -- Not a valid item link + msg = string.lower(msg) + local a,b,c = strfind(msg, "(%S+)") --contiguous string of non-space characters + if a then + -- c is the matched string, strsub is everything after that, skipping the space + return c, strsub(msg, b+2) + else + return "" + end + end + end + + + + -- loop over the characters + for i = 1, #fileIn do + local c = fileIn:sub( i, i ) + if stateValue == 0 then -- outside of a tag + if c == "<" then -- start of a tag + tagName = "" -- reset the tag name + stateValue = 1 -- change stateValue + end + elseif stateValue == 1 then -- is in a tagName + if( c ~= "/" and c ~= " " and c ~= ">" ) then + tagName = tagName .. c + end + if( c == " " ) then + stateValue = 2 + end + elseif statueValue == 2 then -- is in an attribute name + if( c ~= "=" ) + + end + ]] -- call the endDocument method for the given contentHandler if saxParser.contentHandler and saxParser.contentHandler.endDocument then diff --git a/test/test.lua b/test/test.lua index b63873e..5d135bf 100644 --- a/test/test.lua +++ b/test/test.lua @@ -1100,7 +1100,7 @@ function test.testSAX_setContentHandler() --parser = nil test.after_testSax() end -function test.testSAX_Parse_StartDocument_TextIn() +function test.notestSAX_Parse_StartDocument_TextIn() test.before_testSax() ch = contentHandler ch.startDocument = function( this ) this.started = true; end @@ -1109,7 +1109,7 @@ function test.testSAX_Parse_StartDocument_TextIn() parser.parse( "" ) assertTrue( ch.started ) end -function test.testSAX_Parse_StartDocument_FileIn() +function test.notestSAX_Parse_StartDocument_FileIn() test.before_testSax() ch = contentHandler ch.startDocument = function( this ) this.started = true; end @@ -1118,7 +1118,7 @@ function test.testSAX_Parse_StartDocument_FileIn() parser.parse( "../build.xml" ) assertTrue( ch.started ) end -function test.testSAX_Parse_StartDocument_NotGiven_TextIn() +function test.notestSAX_Parse_StartDocument_NotGiven_TextIn() test.before_testSax() ch = contentHandler ch.startDocument = nil @@ -1127,7 +1127,7 @@ function test.testSAX_Parse_StartDocument_NotGiven_TextIn() parser.parse( "" ) assertIsNil( ch.started ) end -function test.testSAX_Parse_EndDocument_TextIn() +function test.notestSAX_Parse_EndDocument_TextIn() test.before_testSax() ch = contentHandler ch.endDocument = function( this ) this.ended = true; end @@ -1136,8 +1136,36 @@ function test.testSAX_Parse_EndDocument_TextIn() parser.parse( "" ) assertTrue( ch.ended ) end - - +function test.testSAX_Pase_StartElement_TextIn() + -- affirm that the startElement method is called + test.before_testSax() + ch = contentHandler + ch.startElement = function( this, tagIn, attribs ) this.tagIn = tagIn; end + parser = saxParser.makeParser() + parser.setContentHandler( ch ) + parser.parse( "" ) + assertEquals( "xml", ch.tagIn ) +end +function test.testSAX_Pase_StartElementAttribs_TextIn() + -- affirm that the startElement method is called + test.before_testSax() + ch = contentHandler + ch.startElement = function( this, tagIn, attribs ) this.version = attribs["version"]; end + parser = saxParser.makeParser() + parser.setContentHandler( ch ) + parser.parse( "" ) + assertEquals( "1", ch.version ) +end +function test.testSAX_Pase_StartElementAttribs_TextIn2() + -- affirm that the startElement method is called + test.before_testSax() + ch = contentHandler + ch.startElement = function( this, tagIn, attribs ) this.version = attribs["version"]; end + parser = saxParser.makeParser() + parser.setContentHandler( ch ) + parser.parse( "" ) + assertEquals( "2", ch.version ) +end ---------------------------------- -- Run the tests ---------------------------------- From 5c08fedf78182ade380e2b6af72d0dcb96e2bc6e Mon Sep 17 00:00:00 2001 From: opussf Date: Wed, 19 Aug 2020 13:38:54 -0700 Subject: [PATCH 04/15] xml2src: EOD commit --- src/saxParser.lua | 21 +++++++++++++-------- src/wowStubs.lua | 5 +++++ test/test.lua | 2 +- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/saxParser.lua b/src/saxParser.lua index b874038..1cfcfaf 100644 --- a/src/saxParser.lua +++ b/src/saxParser.lua @@ -65,29 +65,34 @@ function saxParser.parse( fileIn ) tagName = "" attributes = {} -- start with an empty attributes table - attribKey = "" -- - attribValue = "" -- tagDepth = 0 -- how many tags deep -- start by looking for a start tag while( #fileIn > 0 ) do - print( fileIn ) + print( "fileIn: "..string.sub( fileIn, 1, 50 ) ) if stateValue == 0 then -- outside of a tag - local tagStart, tagEnd, tagName = string.find( fileIn, "^<(%S+)" ) -- tags start with a < + local tagStart, tagEnd, tagName = string.find( fileIn, "^<(%S+)[^/>]" ) -- tags start with a < + local tagEndStart, tagEndEnd, tagEndName = string.find( fileIn, "^" ) if tagStart then print( "Found a tag start: <"..tagName ) tagDepth = tagDepth + 1 stateValue = 1 attributes = {} fileIn = string.sub( fileIn, tagEnd+1 ) - end - tagEndStart, tagEndEnd, tagName = string.find( fileIn, "^" ) - if tagEndStart then - print( "Found a closing tag: " ) + elseif tagEndStart then + print( "Found a closing tag: " ) tagDepth = tagDepth - 1 stateValue = 0 fileIn = string.sub( fileIn, tagEndEnd+1 ) + else + c = string.sub( fileIn, 1, 1 ) + if c ~= "<" then + print( "Char :"..c..":" ) + saxParser.contentHandler:characters( c ) + fileIn = string.sub( fileIn, 2 ) + end end + elseif stateValue == 1 then local attribStart, attribEnd, key, value = string.find( fileIn, "^%s*(%S+)%s*=%s*[\"\'](%S*)[\"\']" ) if attribStart then diff --git a/src/wowStubs.lua b/src/wowStubs.lua index 3cf57e0..7513e17 100644 --- a/src/wowStubs.lua +++ b/src/wowStubs.lua @@ -381,7 +381,9 @@ Frame = { ["UnregisterEvent"] = function(self, event) self.Events[event] = nil; end, ["GetName"] = function(self) return self.framename end, ["SetFrameStrata"] = function() end, + ["width"] = 100, ["SetWidth"] = function(self, value) self.width = value; end, + ["GetWidth"] = function(self) return( self.width ); end, ["SetHeight"] = function(self, value) self.height = value; end, ["CreateFontString"] = function(self, ...) return(CreateFontString(...)) end, @@ -1374,6 +1376,9 @@ end function ClearAchievementComparisonUnit() -- mostly does nothing... end +function SetRaidTarget( target, iconID ) + -- sets the raid icon ID on target +end function BNSendWhisper( id, msg ) table.insert( chatLog, { ["msg"] = msg, ["chatType"] = "BNWhisper", ["language"] = "", ["channel"] = "BNWhisper" } diff --git a/test/test.lua b/test/test.lua index 5d135bf..397bfc4 100644 --- a/test/test.lua +++ b/test/test.lua @@ -1109,7 +1109,7 @@ function test.notestSAX_Parse_StartDocument_TextIn() parser.parse( "" ) assertTrue( ch.started ) end -function test.notestSAX_Parse_StartDocument_FileIn() +function test.testSAX_Parse_StartDocument_FileIn() test.before_testSax() ch = contentHandler ch.startDocument = function( this ) this.started = true; end From 59af5f11027a58a0873e839c654b3c482a161241 Mon Sep 17 00:00:00 2001 From: opussf Date: Tue, 6 Oct 2020 15:18:11 -0700 Subject: [PATCH 05/15] Only run if the toc file can be opened. The universe is under no obligation to make sense to you. --- src/wowStubs.lua | 68 +++++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/src/wowStubs.lua b/src/wowStubs.lua index cdfd31d..6a1d7ee 100644 --- a/src/wowStubs.lua +++ b/src/wowStubs.lua @@ -1578,44 +1578,46 @@ function ParseTOC( tocFile, useRequire ) -- set useRequire to use the old require method local tocFileTable = {} local f = io.open( tocFile, "r" ) - local tocContents = f:read( "*all" ) - while true do - local linestart, lineend, line = string.find( tocContents, "(.-)\n" ) - if linestart then - local lua, luaEnd, luaFile = string.find( line, "([%a]*)%.lua" ) - local xml, xmlEnd, xmlFile = string.find( line, "([%a]*)%.xml" ) - local hash, hashEnd, hashKey, hashValue = string.find( line, "## ([%a]*): (.*)" ) - if( hash ) then - addonData[ hashKey ] = hashValue - elseif( lua ) then - table.insert( tocFileTable, luaFile ) + if f then + local tocContents = f:read( "*all" ) + while true do + local linestart, lineend, line = string.find( tocContents, "(.-)\n" ) + if linestart then + local lua, luaEnd, luaFile = string.find( line, "([%a]*)%.lua" ) + local xml, xmlEnd, xmlFile = string.find( line, "([%a]*)%.xml" ) + local hash, hashEnd, hashKey, hashValue = string.find( line, "## ([%a]*): (.*)" ) + if( hash ) then + addonData[ hashKey ] = hashValue + elseif( lua ) then + table.insert( tocFileTable, luaFile ) + end + tocContents = string.sub( tocContents, lineend+1 ) + else + break end - tocContents = string.sub( tocContents, lineend+1 ) - else - break end - end - pathSeparator = string.sub(package.config, 1, 1) - -- first character of this string (http://www.lua.org/manual/5.2/manual.html#pdf-package.config) - includePath = tocFile - while( string.sub( includePath, -1, -1 ) ~= pathSeparator ) do - includePath = string.sub( includePath, 1, -2 ) - end - addonName = string.sub( tocFile, string.len( includePath ) + 1, -5 ) + pathSeparator = string.sub(package.config, 1, 1) + -- first character of this string (http://www.lua.org/manual/5.2/manual.html#pdf-package.config) + includePath = tocFile + while( string.sub( includePath, -1, -1 ) ~= pathSeparator ) do + includePath = string.sub( includePath, 1, -2 ) + end + addonName = string.sub( tocFile, string.len( includePath ) + 1, -5 ) - if( useRequire ) then - --add to the include package.path - package.path = includePath.."?.lua;" .. package.path - end + if( useRequire ) then + --add to the include package.path + package.path = includePath.."?.lua;" .. package.path + end - sharedTable = {} + sharedTable = {} - for _,f in pairs( tocFileTable ) do - if( useRequire ) then - require( f ) - else - local loadedfile = assert( loadfile( includePath..f..".lua" ) ) - loadedfile( addonName, sharedTable ) + for _,f in pairs( tocFileTable ) do + if( useRequire ) then + require( f ) + else + local loadedfile = assert( loadfile( includePath..f..".lua" ) ) + loadedfile( addonName, sharedTable ) + end end end end From f30bb03d74bcf0ddce1894e2d291c3035850425f Mon Sep 17 00:00:00 2001 From: opussf Date: Tue, 6 Oct 2020 15:20:25 -0700 Subject: [PATCH 06/15] SAX Parser info A bargain is something you don't need at a price you can't resist. --- FEATURE.md | 2 ++ src/scripts/runmany.sh | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/FEATURE.md b/FEATURE.md index 9458473..5ea49da 100644 --- a/FEATURE.md +++ b/FEATURE.md @@ -4,3 +4,5 @@ Write a SAX parser to handle the wow.xml files for addons. +http://www.jelks.nu/XML/xmlebnf.html + diff --git a/src/scripts/runmany.sh b/src/scripts/runmany.sh index 2d80c30..a20d6a3 100755 --- a/src/scripts/runmany.sh +++ b/src/scripts/runmany.sh @@ -8,11 +8,12 @@ for n in $(seq -f "%05g" 9999 1) ; do if [ ! "$?" == "0" ]; then cp target/reports/antout.txt target/reports/antOut$n.txt #mv $reportFile target/reports/testOut$n.xml - ls -alt target/reports/testOut$n.xml + ls -alt $reportFile until $(~/Scripts/checkFileChanged.sh ./test/test.lua); do sleep 1 done else - ls -alt $reportFile + #ls -alt $reportFile + sleep 1 fi done From 23418ccad35ac22938db3a13df01e83d3fc1bc1e Mon Sep 17 00:00:00 2001 From: opussf Date: Sun, 28 Jan 2024 15:33:40 -1000 Subject: [PATCH 07/15] Minor rewrite --- src/saxParser.lua | 133 ++++++++++++++++++++++++++++++++++------------ test/test.lua | 44 +++++++++++++-- 2 files changed, 139 insertions(+), 38 deletions(-) diff --git a/src/saxParser.lua b/src/saxParser.lua index 1cfcfaf..822240c 100644 --- a/src/saxParser.lua +++ b/src/saxParser.lua @@ -59,61 +59,126 @@ function saxParser.parse( fileIn ) end -- loop through each char - stateValue = 0 -- current state of the parser - -- 0 is outside of a tag - -- 1 is in a tag name + State = { + Outside = { 0 }, -- outside of a tag + Tagname = { 1 }, + InTag = { 2 }, -- in a tag name + InElement = { 3 }, + } + stateValue = State.Outside + + tagHints = { + ["?"] = function( str ) + print("Prolog - prune") + local endProlog = string.find( str, "?>" ) + if endProlog then + return string.sub( str, endProlog+2 ) + end + return str + end, + ["!"] = function( str ) + local endComment = string.find( str, "-->" ) + if endComment then + return string.sub( str, endComment+3 ) + end + return str + end, + ["/"] = function( str ) + -- print("Closing tag") + local endTag = string.find( str, ">" ) + if endTag then + tagName = table.remove( tagDepth ) + print( "Fire endElement( "..tagName.." )" ) + saxParser.contentHandler:endElement( tagName ) + return string.sub( str, endTag+1 ) + end + return str + end, + } tagName = "" attributes = {} -- start with an empty attributes table - tagDepth = 0 -- how many tags deep + tagDepth = {} -- how many tags deep + ccc = 0 -- start by looking for a start tag - while( #fileIn > 0 ) do - print( "fileIn: "..string.sub( fileIn, 1, 50 ) ) - if stateValue == 0 then -- outside of a tag - local tagStart, tagEnd, tagName = string.find( fileIn, "^<(%S+)[^/>]" ) -- tags start with a < - local tagEndStart, tagEndEnd, tagEndName = string.find( fileIn, "^" ) + while( #fileIn > 0 and ccc<2000 ) do + print( #fileIn, "fileIn: "..string.sub( fileIn, 1, 60 ).."\t-->"..stateValue[1] ) + if stateValue == State.Outside then -- outside of a tag + local tagStart = string.find( fileIn, "<" ) + --print( "Start:"..tagStart ) + if tagStart then + tagTypeHint = string.sub( fileIn, tagStart+1, tagStart+1 ) + --print( "Hint: "..tagTypeHint ) + if tagHints[tagTypeHint] then + fileIn = tagHints[tagTypeHint]( fileIn ) + else -- this is not a known special case (this is a tag) + stateValue = State.Tagname + fileIn = string.sub( fileIn, tagStart+1 ) + end + end + elseif stateValue == State.Tagname then + tagStart, tagEnd, tagName = string.find( fileIn, "^([%a_][%a%d-_.]*)" ) + --print( "TagName: "..tagStart, tagEnd, ">"..tagName.."<" ) if tagStart then - print( "Found a tag start: <"..tagName ) - tagDepth = tagDepth + 1 - stateValue = 1 + --print( "Found a tag start: <"..tagName..">" ) + table.insert( tagDepth, tagName ) + stateValue = State.InTag attributes = {} fileIn = string.sub( fileIn, tagEnd+1 ) - elseif tagEndStart then - print( "Found a closing tag: " ) - tagDepth = tagDepth - 1 - stateValue = 0 - fileIn = string.sub( fileIn, tagEndEnd+1 ) - else - c = string.sub( fileIn, 1, 1 ) - if c ~= "<" then - print( "Char :"..c..":" ) - saxParser.contentHandler:characters( c ) - fileIn = string.sub( fileIn, 2 ) - end end - - elseif stateValue == 1 then - local attribStart, attribEnd, key, value = string.find( fileIn, "^%s*(%S+)%s*=%s*[\"\'](%S*)[\"\']" ) + elseif stateValue == State.InTag then + local attribStart, attribEnd, key, value = string.find( fileIn, "^%s*(%S+)%s*=%s*[\"\'](.-)[\"\']" ) if attribStart then - print( "Found an attribute: "..key.."=\""..value.."\"" ) + print( "Found an attribute: ["..key.."] =\""..value.."\"--" ) attributes[key] = value - --print( "set "..key.." to "..value ) fileIn = string.sub( fileIn, attribEnd+1 ) else - tagEndStart, tagEndEnd, tagEnd = string.find( fileIn, "^%s*([/]*>)" ) - print( "tagEnd:"..tagName ) + local tagEndStart, tagEndEnd, tagEnd = string.find( fileIn, "^%s*([/]*>)" ) + --print( "tagEnd: "..tagEndStart, tagEndEnd, tagName.." >"..tagEnd.."<" ) if tagEndStart then + print( "Fire startElement( "..tagName.." )" ) + print( "\twith attributes: ") + for k,v in pairs( attributes ) do + print( "\t\t"..k..":="..v ) + end + table.insert( tagDepth, tagName ) saxParser.contentHandler:startElement( tagName, attributes ) if tagEnd == "/>" then -- this is also the end of the tag - saxParser.contentHandler:endElement( tagName ) - tagDepth = tagDepth - 1 + tagHints["/"]( fileIn ) end - stateValue = 0 + stateValue = State.Outside fileIn = string.sub( fileIn, tagEndEnd+1 ) end end + elseif stateValue == State.Outside then + local c = string.sub( fileIn, 1, 1 ) + if c then + print( "Char: "..c..":" ) + saxParser.contentHandler.characters( c ) + fileIn = string.sub( fileIn, 2 ) + end end + -- local tagStart, tagEnd, tagName = string.find( fileIn, "^<(%S+)[^/>]" ) -- tags start with a < %S is non-space chars + -- local tagEndStart, tagEndEnd, tagEndName = string.find( fileIn, "^" ) + -- print( "Start: "..tagStart, tagEnd, ">"..tagName.."<" ) + -- print( "End : "..tagEndStart, tagEndEnd, tagEndName ) + -- if tagStart then + -- elseif tagEndStart then + -- print( "Found a closing tag: " ) + -- tagDepth = tagDepth - 1 + -- stateValue = 0 + -- fileIn = string.sub( fileIn, tagEndEnd+1 ) + -- else + -- c = string.sub( fileIn, 1, 1 ) + -- if c ~= "<" then + -- print( "Char :"..c..":" ) + -- saxParser.contentHandler:characters( c ) + -- fileIn = string.sub( fileIn, 2 ) + -- end + -- end + -- end + ccc = ccc + 1 end diff --git a/test/test.lua b/test/test.lua index e6708c8..4c468c8 100644 --- a/test/test.lua +++ b/test/test.lua @@ -1146,7 +1146,7 @@ function test.notestSAX_Parse_StartDocument_TextIn() parser.parse( "" ) assertTrue( ch.started ) end -function test.testSAX_Parse_StartDocument_FileIn() +function test.notestSAX_Parse_StartDocument_FileIn() test.before_testSax() ch = contentHandler ch.startDocument = function( this ) this.started = true; end @@ -1173,7 +1173,7 @@ function test.notestSAX_Parse_EndDocument_TextIn() parser.parse( "" ) assertTrue( ch.ended ) end -function test.testSAX_Pase_StartElement_TextIn() +function test.testSAX_Parse_StartElement_TextIn() -- affirm that the startElement method is called test.before_testSax() ch = contentHandler @@ -1183,7 +1183,7 @@ function test.testSAX_Pase_StartElement_TextIn() parser.parse( "" ) assertEquals( "xml", ch.tagIn ) end -function test.testSAX_Pase_StartElementAttribs_TextIn() +function test.testSAX_Parse_StartElementAttribs_TextIn() -- affirm that the startElement method is called test.before_testSax() ch = contentHandler @@ -1193,7 +1193,7 @@ function test.testSAX_Pase_StartElementAttribs_TextIn() parser.parse( "" ) assertEquals( "1", ch.version ) end -function test.testSAX_Pase_StartElementAttribs_TextIn2() +function test.testSAX_Parse_StartElementAttribs_TextIn2() -- affirm that the startElement method is called test.before_testSax() ch = contentHandler @@ -1203,6 +1203,42 @@ function test.testSAX_Pase_StartElementAttribs_TextIn2() parser.parse( "" ) assertEquals( "2", ch.version ) end +function test.testSax_Parse_StartElement_Prolog() + test.before_testSax() + ch = contentHandler + ch.startElement = function( this, tagIn, attribs ) this.version = attribs["version"]; end + parser = saxParser.makeParser() + parser.setContentHandler( ch ) + parser.parse( "" ) + assertEquals( "3", ch.version ) +end +function test.testSax_Parse_StartElement_Comment() + test.before_testSax() + ch = contentHandler + ch.startElement = function( this, tagIn, attribs ) this.version = attribs["version"]; end + parser = saxParser.makeParser() + parser.setContentHandler( ch ) + parser.parse( "" ) + assertEquals( "4", ch.version ) +end +function test.textSax_Parse_NestedElements() + test.before_testSax() + ch = contentHandler + ch.startElement = function( this, tagIn, attribs ) this.version = attribs["version"]; end + parser = saxParser.makeParser() + parser.setContentHandler( ch ) + parser.parse( "" ) + assertEquals( "5", ch.broken ) +end +function test.testSax_Parse_wow_xml() + test.before_testSax() + ch = contentHandler + ch.startElement = function( this, tagIn, attribs ) if tagIn == "target" then this.target = (this.target and this.target + 1 or 1) end; end + parser = saxParser.makeParser() + parser.setContentHandler( ch ) + parser.parse( "../../Steps/src/steps.xml" ) + assertEquals( 10, ch.target ) +end ---------------------------------- -- Run the tests ---------------------------------- From 06bc75a7bb73b15fe0c665d511f9939a743ac6a3 Mon Sep 17 00:00:00 2001 From: opussf Date: Mon, 29 Jan 2024 02:44:37 -1000 Subject: [PATCH 08/15] Works better --- src/saxParser.lua | 214 +++++++++++++++------------------------------- test/test.lua | 5 +- 2 files changed, 74 insertions(+), 145 deletions(-) diff --git a/src/saxParser.lua b/src/saxParser.lua index 822240c..809a237 100644 --- a/src/saxParser.lua +++ b/src/saxParser.lua @@ -21,6 +21,9 @@ -- parse( text ) -- parse( file ) +-- https://www.w3schools.com/xml/xml_elements.asp +-- https://www.w3schools.com/xml/xml_syntax.asp + contentHandler = {} -- normally the contentHandler is an object, where data structures are created in the new object. @@ -60,169 +63,94 @@ function saxParser.parse( fileIn ) -- loop through each char State = { - Outside = { 0 }, -- outside of a tag - Tagname = { 1 }, - InTag = { 2 }, -- in a tag name - InElement = { 3 }, + Outside = { 0 }, -- outside of a tag + ElementName = { 1 }, + InElement = { 2 }, -- + -- InAttrName = { 2 }, + -- InTag = { 2 }, -- in a tag name + -- InElement = { 3 }, } - stateValue = State.Outside - - tagHints = { - ["?"] = function( str ) - print("Prolog - prune") - local endProlog = string.find( str, "?>" ) + currentState = State.Outside + elementDepth = {} -- table of current element depth + elementName = "" + chars = "" + + while( #fileIn > 0 ) do + print( currentState[1].."\t"..#fileIn, "fileIn: "..string.sub( fileIn, 1, 60 ) ) + c = string.sub( fileIn, 1, 1 ) + n = string.sub( fileIn, 2, 2 ) + handled = false + if currentState == State.Outside then + if c == "<" then + if n == "?" then + local endProlog = string.find( fileIn, "?>" ) if endProlog then - return string.sub( str, endProlog+2 ) + fileIn = string.sub( fileIn, endProlog+2 ) end - return str - end, - ["!"] = function( str ) - local endComment = string.find( str, "-->" ) + elseif n == "!" then + local endComment = string.find( fileIn, "-->" ) if endComment then - return string.sub( str, endComment+3 ) + fileIn = string.sub( fileIn, endComment+3 ) end - return str - end, - ["/"] = function( str ) - -- print("Closing tag") - local endTag = string.find( str, ">" ) - if endTag then - tagName = table.remove( tagDepth ) - print( "Fire endElement( "..tagName.." )" ) - saxParser.contentHandler:endElement( tagName ) - return string.sub( str, endTag+1 ) - end - return str - end, - } - - tagName = "" - attributes = {} -- start with an empty attributes table - tagDepth = {} -- how many tags deep - - ccc = 0 - -- start by looking for a start tag - while( #fileIn > 0 and ccc<2000 ) do - print( #fileIn, "fileIn: "..string.sub( fileIn, 1, 60 ).."\t-->"..stateValue[1] ) - if stateValue == State.Outside then -- outside of a tag - local tagStart = string.find( fileIn, "<" ) - --print( "Start:"..tagStart ) - if tagStart then - tagTypeHint = string.sub( fileIn, tagStart+1, tagStart+1 ) - --print( "Hint: "..tagTypeHint ) - if tagHints[tagTypeHint] then - fileIn = tagHints[tagTypeHint]( fileIn ) - else -- this is not a known special case (this is a tag) - stateValue = State.Tagname - fileIn = string.sub( fileIn, tagStart+1 ) + else + currentState = State.ElementName + elementName = "" + fileIn = string.sub( fileIn, 2 ) end + else + saxParser.contentHandler:characters( c ) + fileIn = string.sub( fileIn, 2 ) end - elseif stateValue == State.Tagname then + elseif currentState == State.ElementName then tagStart, tagEnd, tagName = string.find( fileIn, "^([%a_][%a%d-_.]*)" ) - --print( "TagName: "..tagStart, tagEnd, ">"..tagName.."<" ) if tagStart then - --print( "Found a tag start: <"..tagName..">" ) - table.insert( tagDepth, tagName ) - stateValue = State.InTag + elementName = tagName attributes = {} - fileIn = string.sub( fileIn, tagEnd+1 ) + currentState = State.InElement + fileIn = string.sub( fileIn, tagEnd + 1 ) end - elseif stateValue == State.InTag then - local attribStart, attribEnd, key, value = string.find( fileIn, "^%s*(%S+)%s*=%s*[\"\'](.-)[\"\']" ) + tagStart, tagEnd, tagName = string.find( fileIn, "^/([%a_][%a%d-_.]*)" ) + if tagStart then + elementName = tagName + -- print( "Fire endElement( "..tagName.." )" ) + table.remove( elementDepth ) + saxParser.contentHandler:endElement( tagName ) + currentState = State.Outside + fileIn = string.sub( fileIn, tagEnd + 2 ) + end + elseif currentState == State.InElement then + attribStart, attribEnd, key, value = string.find( fileIn, "^%s*(%S+)%s*=%s*[\"\'](.-)[\"\']" ) if attribStart then - print( "Found an attribute: ["..key.."] =\""..value.."\"--" ) attributes[key] = value fileIn = string.sub( fileIn, attribEnd+1 ) - else - local tagEndStart, tagEndEnd, tagEnd = string.find( fileIn, "^%s*([/]*>)" ) - --print( "tagEnd: "..tagEndStart, tagEndEnd, tagName.." >"..tagEnd.."<" ) - if tagEndStart then - print( "Fire startElement( "..tagName.." )" ) - print( "\twith attributes: ") - for k,v in pairs( attributes ) do - print( "\t\t"..k..":="..v ) - end - table.insert( tagDepth, tagName ) - saxParser.contentHandler:startElement( tagName, attributes ) - if tagEnd == "/>" then -- this is also the end of the tag - tagHints["/"]( fileIn ) + elseif c == " " then + fileIn = string.sub( fileIn, 2 ) + elseif c == ">" or n == ">" then + -- print( "Fire startElement( "..elementName.." )" ) + -- print( "\twith attributes: ") + -- for k,v in pairs( attributes ) do + -- print( "\t\t"..k..":="..v ) + -- end + table.insert( elementDepth, elementName ) + saxParser.contentHandler:startElement( elementName, attributes ) + currentState = State.Outside + if c == "/" and n == ">" then + -- print( "Fire endElement( "..elementName.." )" ) + depthElement = table.remove( elementDepth ) + saxParser.contentHandler:endElement( elementName ) + if depthElement ~= elementName then + print( "WARNING: "..elementName.." is not the expected element: "..depthElement ) end - stateValue = State.Outside - fileIn = string.sub( fileIn, tagEndEnd+1 ) + currentState = State.Outside end - end - elseif stateValue == State.Outside then - local c = string.sub( fileIn, 1, 1 ) - if c then - print( "Char: "..c..":" ) - saxParser.contentHandler.characters( c ) - fileIn = string.sub( fileIn, 2 ) + fileIn = string.sub( fileIn, (n==">" and 3 or 2) ) end end - -- local tagStart, tagEnd, tagName = string.find( fileIn, "^<(%S+)[^/>]" ) -- tags start with a < %S is non-space chars - -- local tagEndStart, tagEndEnd, tagEndName = string.find( fileIn, "^" ) - -- print( "Start: "..tagStart, tagEnd, ">"..tagName.."<" ) - -- print( "End : "..tagEndStart, tagEndEnd, tagEndName ) - -- if tagStart then - -- elseif tagEndStart then - -- print( "Found a closing tag: " ) - -- tagDepth = tagDepth - 1 - -- stateValue = 0 - -- fileIn = string.sub( fileIn, tagEndEnd+1 ) - -- else - -- c = string.sub( fileIn, 1, 1 ) - -- if c ~= "<" then - -- print( "Char :"..c..":" ) - -- saxParser.contentHandler:characters( c ) - -- fileIn = string.sub( fileIn, 2 ) - -- end - -- end + -- print( "elementDepth: " ) + -- for _, v in pairs( elementDepth ) do + -- print( "\t"..v ) -- end - ccc = ccc + 1 - end - - - - ---[[ - if msg then - local i,c = strmatch(msg, "^(|c.*|r)%s*(%d*)$") - if i then -- i is an item, c is a count or nil - return i, c - else -- Not a valid item link - msg = string.lower(msg) - local a,b,c = strfind(msg, "(%S+)") --contiguous string of non-space characters - if a then - -- c is the matched string, strsub is everything after that, skipping the space - return c, strsub(msg, b+2) - else - return "" - end - end - end - - - - -- loop over the characters - for i = 1, #fileIn do - local c = fileIn:sub( i, i ) - if stateValue == 0 then -- outside of a tag - if c == "<" then -- start of a tag - tagName = "" -- reset the tag name - stateValue = 1 -- change stateValue - end - elseif stateValue == 1 then -- is in a tagName - if( c ~= "/" and c ~= " " and c ~= ">" ) then - tagName = tagName .. c - end - if( c == " " ) then - stateValue = 2 - end - elseif statueValue == 2 then -- is in an attribute name - if( c ~= "=" ) - end - ]] -- call the endDocument method for the given contentHandler if saxParser.contentHandler and saxParser.contentHandler.endDocument then diff --git a/test/test.lua b/test/test.lua index 4c468c8..a6aaad6 100644 --- a/test/test.lua +++ b/test/test.lua @@ -1164,7 +1164,7 @@ function test.notestSAX_Parse_StartDocument_NotGiven_TextIn() parser.parse( "" ) assertIsNil( ch.started ) end -function test.notestSAX_Parse_EndDocument_TextIn() +function test.testSAX_Parse_EndDocument_TextIn() test.before_testSax() ch = contentHandler ch.endDocument = function( this ) this.ended = true; end @@ -1224,11 +1224,12 @@ end function test.textSax_Parse_NestedElements() test.before_testSax() ch = contentHandler - ch.startElement = function( this, tagIn, attribs ) this.version = attribs["version"]; end + ch.startElement = function( this, tagIn, attribs ) this.broken = attribs["broken"]; end parser = saxParser.makeParser() parser.setContentHandler( ch ) parser.parse( "" ) assertEquals( "5", ch.broken ) + fail("Hello") end function test.testSax_Parse_wow_xml() test.before_testSax() From 520193dacd5035e3231c4747dc48430cbe9f36af Mon Sep 17 00:00:00 2001 From: opussf Date: Mon, 29 Jan 2024 03:01:09 -1000 Subject: [PATCH 09/15] Not perfect, it will work for now. --- src/saxParser.lua | 2 +- test/test.lua | 12 +----------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/saxParser.lua b/src/saxParser.lua index 809a237..3bdad41 100644 --- a/src/saxParser.lua +++ b/src/saxParser.lua @@ -76,7 +76,7 @@ function saxParser.parse( fileIn ) chars = "" while( #fileIn > 0 ) do - print( currentState[1].."\t"..#fileIn, "fileIn: "..string.sub( fileIn, 1, 60 ) ) + -- print( currentState[1].."\t"..#fileIn, "fileIn: "..string.sub( fileIn, 1, 60 ) ) c = string.sub( fileIn, 1, 1 ) n = string.sub( fileIn, 2, 2 ) handled = false diff --git a/test/test.lua b/test/test.lua index a6aaad6..3d45fae 100644 --- a/test/test.lua +++ b/test/test.lua @@ -1221,7 +1221,7 @@ function test.testSax_Parse_StartElement_Comment() parser.parse( "" ) assertEquals( "4", ch.version ) end -function test.textSax_Parse_NestedElements() +function test.testSax_Parse_NestedElements() test.before_testSax() ch = contentHandler ch.startElement = function( this, tagIn, attribs ) this.broken = attribs["broken"]; end @@ -1229,16 +1229,6 @@ function test.textSax_Parse_NestedElements() parser.setContentHandler( ch ) parser.parse( "" ) assertEquals( "5", ch.broken ) - fail("Hello") -end -function test.testSax_Parse_wow_xml() - test.before_testSax() - ch = contentHandler - ch.startElement = function( this, tagIn, attribs ) if tagIn == "target" then this.target = (this.target and this.target + 1 or 1) end; end - parser = saxParser.makeParser() - parser.setContentHandler( ch ) - parser.parse( "../../Steps/src/steps.xml" ) - assertEquals( 10, ch.target ) end ---------------------------------- -- Run the tests From f73faec3ffe4677a8b1fa3dfae7105b94e9499ce Mon Sep 17 00:00:00 2001 From: opussf Date: Mon, 29 Jan 2024 03:04:11 -1000 Subject: [PATCH 10/15] Cleanup --- src/saxParser.lua | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/saxParser.lua b/src/saxParser.lua index 3bdad41..05ba4b6 100644 --- a/src/saxParser.lua +++ b/src/saxParser.lua @@ -146,10 +146,7 @@ function saxParser.parse( fileIn ) fileIn = string.sub( fileIn, (n==">" and 3 or 2) ) end end - -- print( "elementDepth: " ) - -- for _, v in pairs( elementDepth ) do - -- print( "\t"..v ) - -- end + -- print( "elementDepth: "..table.concat( elementDepth, "\t" ) ) end -- call the endDocument method for the given contentHandler From b5f7460074551556b54947a1068032bd58e3ae7b Mon Sep 17 00:00:00 2001 From: opussf Date: Mon, 29 Jan 2024 12:41:42 -1000 Subject: [PATCH 11/15] Make it happen --- src/saxParser.lua | 2 +- src/wowStubs.lua | 17 +++++++++++++++++ test/test.lua | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/saxParser.lua b/src/saxParser.lua index 05ba4b6..fdaa5a4 100644 --- a/src/saxParser.lua +++ b/src/saxParser.lua @@ -42,7 +42,7 @@ saxParser = {} -- SAX Parser -- interface function saxParser.makeParser() - -- make a parser. This is probably intended to be a + -- make a parser. This is probably intended to be a factory function return saxParser end function saxParser.setContentHandler( contentHandlerIn ) diff --git a/src/wowStubs.lua b/src/wowStubs.lua index bb99f94..1d5c6ca 100644 --- a/src/wowStubs.lua +++ b/src/wowStubs.lua @@ -1732,8 +1732,25 @@ end ----------------------------------------- -- XML functions +require "saxParser" function ParseXML( xmlFile ) + ch = contentHandler + ch.startElement = function( self, tagIn, attribs ) + print( tagIn ) + if _G["Create"..tagIn] then + if attribs.name then + _G[attribs.name] = _G["Create"..tagIn]( attribs.name ) + _G[attribs.name].framename = attribs.name + else + fail("A "..tagIn.." needs a name") + end + end + end + parser = saxParser.makeParser() + parser.setContentHandler( ch ) + parser.parse( xmlFile ) print("parse: "..xmlFile ) + print( topFrame:GetName() ) end ----------------------------------------- diff --git a/test/test.lua b/test/test.lua index 3d45fae..f16721a 100644 --- a/test/test.lua +++ b/test/test.lua @@ -1062,7 +1062,7 @@ function CreateFile( filename, contents ) return tocFile end function test.testTOC_() - CreateFile( "test.xml", "\n\n" ) + CreateFile( "test.xml", "\n\n" ) CreateFile( "test.lua", "print(\"hi\")\n") generatedFile = CreateFile( "test.toc", "test.lua\ntest.xml\n" ) ParseTOC( generatedFile ) From 9c03f5da5748832387ab88b82dfba4c39f3ea072 Mon Sep 17 00:00:00 2001 From: opussf Date: Mon, 29 Jan 2024 12:43:03 -1000 Subject: [PATCH 12/15] Cleaner --- src/wowStubs.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wowStubs.lua b/src/wowStubs.lua index 1d5c6ca..a8b440c 100644 --- a/src/wowStubs.lua +++ b/src/wowStubs.lua @@ -1735,7 +1735,7 @@ end require "saxParser" function ParseXML( xmlFile ) ch = contentHandler - ch.startElement = function( self, tagIn, attribs ) + ch.startElement = function( self, tagIn, attribs ) print( tagIn ) if _G["Create"..tagIn] then if attribs.name then From 1e8c7510689f7aa6e717b0a640b1e2bd034b74ea Mon Sep 17 00:00:00 2001 From: opussf Date: Mon, 29 Jan 2024 15:49:34 -1000 Subject: [PATCH 13/15] Combine --- src/saxParser.lua | 170 ---------------------------------------------- src/wowStubs.lua | 158 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 152 insertions(+), 176 deletions(-) diff --git a/src/saxParser.lua b/src/saxParser.lua index fdaa5a4..e69de29 100644 --- a/src/saxParser.lua +++ b/src/saxParser.lua @@ -1,170 +0,0 @@ --- saxParser.lua ------------------------------------------ --- Author : Opussf --- Date : $Date:$ --- Revision: @VERSION@ ------------------------------------------ - --- A SAX parser takes a content handler, which provides these methods: --- startDocument() -- called at the start of the Document --- endDocument() -- called at the end of the Document --- startElement( tagName, attrs ) -- with each tag start. attrs is a table of attributes and values --- endElement( tagName ) -- when a tag ends --- characters( char ) -- for each character not being in a tag --- The parser calls each of these methods as these events happen. - --- A SAX parser defines a parser (created with makeParser) --- a Parser has a ContentHandler assigned --- a Parser also defines these methods: --- setContentHandler( contentHandler ) --- setFeature() -- prob not going to implement --- parse( text ) --- parse( file ) - --- https://www.w3schools.com/xml/xml_elements.asp --- https://www.w3schools.com/xml/xml_syntax.asp - - -contentHandler = {} --- normally the contentHandler is an object, where data structures are created in the new object. -function contentHandler.startDocument( this ) -end -function contentHandler.endDocument( this ) -end -function contentHandler.startElement( this, tagName, attrs ) -end -function contentHandler.endElement( this, tagName ) -end -function contentHandler.characters( this, char ) -end - -saxParser = {} --- SAX Parser --- interface -function saxParser.makeParser() - -- make a parser. This is probably intended to be a factory function - return saxParser -end -function saxParser.setContentHandler( contentHandlerIn ) - -- takes a table - saxParser.contentHandler = contentHandlerIn -end -function saxParser.setFeature() - -- research this -end -function saxParser.parse( fileIn ) - f = io.open( fileIn, "r" ) - if f then fileIn = f:read( "*all" ) end -- read the contents of the file - - -- call the startDocument method for the given contentHandler - if saxParser.contentHandler and saxParser.contentHandler.startDocument then - saxParser.contentHandler:startDocument() - end - - -- loop through each char - State = { - Outside = { 0 }, -- outside of a tag - ElementName = { 1 }, - InElement = { 2 }, -- - -- InAttrName = { 2 }, - -- InTag = { 2 }, -- in a tag name - -- InElement = { 3 }, - } - currentState = State.Outside - elementDepth = {} -- table of current element depth - elementName = "" - chars = "" - - while( #fileIn > 0 ) do - -- print( currentState[1].."\t"..#fileIn, "fileIn: "..string.sub( fileIn, 1, 60 ) ) - c = string.sub( fileIn, 1, 1 ) - n = string.sub( fileIn, 2, 2 ) - handled = false - if currentState == State.Outside then - if c == "<" then - if n == "?" then - local endProlog = string.find( fileIn, "?>" ) - if endProlog then - fileIn = string.sub( fileIn, endProlog+2 ) - end - elseif n == "!" then - local endComment = string.find( fileIn, "-->" ) - if endComment then - fileIn = string.sub( fileIn, endComment+3 ) - end - else - currentState = State.ElementName - elementName = "" - fileIn = string.sub( fileIn, 2 ) - end - else - saxParser.contentHandler:characters( c ) - fileIn = string.sub( fileIn, 2 ) - end - elseif currentState == State.ElementName then - tagStart, tagEnd, tagName = string.find( fileIn, "^([%a_][%a%d-_.]*)" ) - if tagStart then - elementName = tagName - attributes = {} - currentState = State.InElement - fileIn = string.sub( fileIn, tagEnd + 1 ) - end - tagStart, tagEnd, tagName = string.find( fileIn, "^/([%a_][%a%d-_.]*)" ) - if tagStart then - elementName = tagName - -- print( "Fire endElement( "..tagName.." )" ) - table.remove( elementDepth ) - saxParser.contentHandler:endElement( tagName ) - currentState = State.Outside - fileIn = string.sub( fileIn, tagEnd + 2 ) - end - elseif currentState == State.InElement then - attribStart, attribEnd, key, value = string.find( fileIn, "^%s*(%S+)%s*=%s*[\"\'](.-)[\"\']" ) - if attribStart then - attributes[key] = value - fileIn = string.sub( fileIn, attribEnd+1 ) - elseif c == " " then - fileIn = string.sub( fileIn, 2 ) - elseif c == ">" or n == ">" then - -- print( "Fire startElement( "..elementName.." )" ) - -- print( "\twith attributes: ") - -- for k,v in pairs( attributes ) do - -- print( "\t\t"..k..":="..v ) - -- end - table.insert( elementDepth, elementName ) - saxParser.contentHandler:startElement( elementName, attributes ) - currentState = State.Outside - if c == "/" and n == ">" then - -- print( "Fire endElement( "..elementName.." )" ) - depthElement = table.remove( elementDepth ) - saxParser.contentHandler:endElement( elementName ) - if depthElement ~= elementName then - print( "WARNING: "..elementName.." is not the expected element: "..depthElement ) - end - currentState = State.Outside - end - fileIn = string.sub( fileIn, (n==">" and 3 or 2) ) - end - end - -- print( "elementDepth: "..table.concat( elementDepth, "\t" ) ) - end - - -- call the endDocument method for the given contentHandler - if saxParser.contentHandler and saxParser.contentHandler.endDocument then - saxParser.contentHandler:endDocument() - end -end - - ---[[ -loop over a string in lua: -for i = , #str do - local c = str:sub( i, i ) - - -for c in str:match( "." ) do - -str:gsub( ".", function ) - - -]] diff --git a/src/wowStubs.lua b/src/wowStubs.lua index a8b440c..14ff84c 100644 --- a/src/wowStubs.lua +++ b/src/wowStubs.lua @@ -1730,13 +1730,161 @@ function C_ChatInfo.SendAddonMessage() return true end ------------------------------------------ --- XML functions -require "saxParser" +-- A SAX parser takes a content handler, which provides these methods: +-- startDocument() -- called at the start of the Document +-- endDocument() -- called at the end of the Document +-- startElement( tagName, attrs ) -- with each tag start. attrs is a table of attributes and values +-- endElement( tagName ) -- when a tag ends +-- characters( char ) -- for each character not being in a tag +-- The parser calls each of these methods as these events happen. + +-- A SAX parser defines a parser (created with makeParser) +-- a Parser has a ContentHandler assigned +-- a Parser also defines these methods: +-- setContentHandler( contentHandler ) +-- setFeature() -- prob not going to implement +-- parse( text ) +-- parse( file ) + +-- https://www.w3schools.com/xml/xml_elements.asp +-- https://www.w3schools.com/xml/xml_syntax.asp + + +contentHandler = {} +-- normally the contentHandler is an object, where data structures are created in the new object. +function contentHandler.startDocument( this ) +end +function contentHandler.endDocument( this ) +end +function contentHandler.startElement( this, tagName, attrs ) +end +function contentHandler.endElement( this, tagName ) +end +function contentHandler.characters( this, char ) +end + +saxParser = {} +-- SAX Parser +-- interface +function saxParser.makeParser() + -- make a parser. This is probably intended to be a factory function + return saxParser +end +function saxParser.setContentHandler( contentHandlerIn ) + -- takes a table + saxParser.contentHandler = contentHandlerIn +end +function saxParser.setFeature() + -- research this +end +function saxParser.parse( fileIn ) + f = io.open( fileIn, "r" ) + if f then fileIn = f:read( "*all" ) end -- read the contents of the file + + -- call the startDocument method for the given contentHandler + if saxParser.contentHandler and saxParser.contentHandler.startDocument then + saxParser.contentHandler:startDocument() + end + + -- loop through each char + State = { + Outside = { 0 }, -- outside of a tag + ElementName = { 1 }, + InElement = { 2 }, -- + -- InAttrName = { 2 }, + -- InTag = { 2 }, -- in a tag name + -- InElement = { 3 }, + } + currentState = State.Outside + elementDepth = {} -- table of current element depth + elementName = "" + chars = "" + + while( #fileIn > 0 ) do + -- print( currentState[1].."\t"..#fileIn, "fileIn: "..string.sub( fileIn, 1, 60 ) ) + c = string.sub( fileIn, 1, 1 ) + n = string.sub( fileIn, 2, 2 ) + handled = false + if currentState == State.Outside then + if c == "<" then + if n == "?" then + local endProlog = string.find( fileIn, "?>" ) + if endProlog then + fileIn = string.sub( fileIn, endProlog+2 ) + end + elseif n == "!" then + local endComment = string.find( fileIn, "-->" ) + if endComment then + fileIn = string.sub( fileIn, endComment+3 ) + end + else + currentState = State.ElementName + elementName = "" + fileIn = string.sub( fileIn, 2 ) + end + else + saxParser.contentHandler:characters( c ) + fileIn = string.sub( fileIn, 2 ) + end + elseif currentState == State.ElementName then + tagStart, tagEnd, tagName = string.find( fileIn, "^([%a_][%a%d-_.]*)" ) + if tagStart then + elementName = tagName + attributes = {} + currentState = State.InElement + fileIn = string.sub( fileIn, tagEnd + 1 ) + end + tagStart, tagEnd, tagName = string.find( fileIn, "^/([%a_][%a%d-_.]*)" ) + if tagStart then + elementName = tagName + -- print( "Fire endElement( "..tagName.." )" ) + depthElement = table.remove( elementDepth ) + saxParser.contentHandler:endElement( tagName ) + if depthElement ~= elementName then + fail( "ERROR: Closing "..elementName.." is not the expected element to close; "..depthElement.." is expected." ) + end + currentState = State.Outside + fileIn = string.sub( fileIn, tagEnd + 2 ) + end + elseif currentState == State.InElement then + attribStart, attribEnd, key, value = string.find( fileIn, "^%s*(%S+)%s*=%s*[\"\'](.-)[\"\']" ) + if attribStart then + attributes[key] = value + fileIn = string.sub( fileIn, attribEnd+1 ) + elseif c == " " then + fileIn = string.sub( fileIn, 2 ) + elseif c == ">" or n == ">" then + -- print( "Fire startElement( "..elementName.." )" ) + -- print( "\twith attributes: ") + -- for k,v in pairs( attributes ) do + -- print( "\t\t"..k..":="..v ) + -- end + table.insert( elementDepth, elementName ) + saxParser.contentHandler:startElement( elementName, attributes ) + currentState = State.Outside + if c == "/" and n == ">" then + -- print( "Fire endElement( "..elementName.." )" ) + depthElement = table.remove( elementDepth ) + saxParser.contentHandler:endElement( elementName ) + if depthElement ~= elementName then + fail( "ERROR: Closing "..elementName.." is not the expected element to close; "..depthElement.." is expected." ) + end + currentState = State.Outside + end + fileIn = string.sub( fileIn, (n==">" and 3 or 2) ) + end + end + -- print( "elementDepth: "..table.concat( elementDepth, "\t" ) ) + end + + -- call the endDocument method for the given contentHandler + if saxParser.contentHandler and saxParser.contentHandler.endDocument then + saxParser.contentHandler:endDocument() + end +end function ParseXML( xmlFile ) ch = contentHandler ch.startElement = function( self, tagIn, attribs ) - print( tagIn ) if _G["Create"..tagIn] then if attribs.name then _G[attribs.name] = _G["Create"..tagIn]( attribs.name ) @@ -1749,8 +1897,6 @@ function ParseXML( xmlFile ) parser = saxParser.makeParser() parser.setContentHandler( ch ) parser.parse( xmlFile ) - print("parse: "..xmlFile ) - print( topFrame:GetName() ) end ----------------------------------------- From 7f2832cbec35dc52ae4713e605f97834577025ef Mon Sep 17 00:00:00 2001 From: opussf Date: Mon, 29 Jan 2024 22:52:06 -1000 Subject: [PATCH 14/15] Cleaner --- src/saxParser.lua | 0 src/wowStubs.lua | 7 ++----- 2 files changed, 2 insertions(+), 5 deletions(-) delete mode 100644 src/saxParser.lua diff --git a/src/saxParser.lua b/src/saxParser.lua deleted file mode 100644 index e69de29..0000000 diff --git a/src/wowStubs.lua b/src/wowStubs.lua index 14ff84c..f80bb2e 100644 --- a/src/wowStubs.lua +++ b/src/wowStubs.lua @@ -1789,11 +1789,8 @@ function saxParser.parse( fileIn ) -- loop through each char State = { Outside = { 0 }, -- outside of a tag - ElementName = { 1 }, - InElement = { 2 }, -- - -- InAttrName = { 2 }, - -- InTag = { 2 }, -- in a tag name - -- InElement = { 3 }, + ElementName = { 1 }, -- When to parse for a name + InElement = { 2 }, -- In the element } currentState = State.Outside elementDepth = {} -- table of current element depth From eb52d543652a91abefd2c096c8ad5d8fb878b6ec Mon Sep 17 00:00:00 2001 From: opussf Date: Mon, 29 Jan 2024 23:21:16 -1000 Subject: [PATCH 15/15] Clean up test.xml, and remove saxParser require --- test/test.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/test.lua b/test/test.lua index f16721a..6a68de9 100644 --- a/test/test.lua +++ b/test/test.lua @@ -1062,7 +1062,7 @@ function CreateFile( filename, contents ) return tocFile end function test.testTOC_() - CreateFile( "test.xml", "\n\n" ) + CreateFile( "test.xml", "\n\n" ) CreateFile( "test.lua", "print(\"hi\")\n") generatedFile = CreateFile( "test.toc", "test.lua\ntest.xml\n" ) ParseTOC( generatedFile ) @@ -1070,7 +1070,6 @@ end ----- Sax tests -require "saxParser" function test.before_testContentHandler() originalContentHandler = {} for k,v in pairs( contentHandler ) do