summaryrefslogtreecommitdiff
path: root/toolkit/src2xml/source/srcparser.py
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/src2xml/source/srcparser.py')
-rw-r--r--toolkit/src2xml/source/srcparser.py416
1 files changed, 416 insertions, 0 deletions
diff --git a/toolkit/src2xml/source/srcparser.py b/toolkit/src2xml/source/srcparser.py
new file mode 100644
index 000000000000..b25d39ecb66e
--- /dev/null
+++ b/toolkit/src2xml/source/srcparser.py
@@ -0,0 +1,416 @@
+import sys
+from globals import *
+import srclexer
+
+# simple name translation map
+postTransMap = {"ok-button": "okbutton",
+ "cancel-button": "cancelbutton",
+ "help-button": "helpbutton"}
+
+def transName (name):
+ """Translate a mixed-casing name to dash-separated name.
+
+Translate a mixed-casing name (e.g. MyLongName) to a dash-separated name
+(e.g. my-long-name).
+"""
+ def isUpper (c):
+ return c >= 'A' and c <= 'Z'
+
+ newname = ''
+ parts = []
+ buf = ''
+ for c in name:
+ if isUpper(c) and len(buf) > 1:
+ parts.append(buf)
+ buf = c
+ else:
+ buf += c
+
+ if len(buf) > 0:
+ parts.append(buf)
+
+ first = True
+ for part in parts:
+ if first:
+ first = False
+ else:
+ newname += '-'
+ newname += part.lower()
+
+ # special-case mapping ...
+ if 0: #postTransMap.has_key(newname):
+ newname = postTransMap[newname]
+
+ return newname
+
+
+def transValue (value):
+ """Translate certain values.
+
+Examples of translated values include TRUE -> true, FALSE -> false.
+"""
+ if value.lower() in ["true", "false"]:
+ value = value.lower()
+ return value
+
+
+def renameAttribute (name, elemName):
+
+ # TODO: all manner of evil special cases ...
+ if elemName == 'metric-field' and name == 'spin-size':
+ return 'step-size'
+
+ return name
+
+
+class Statement(object):
+ """Container to hold information for a single statement.
+
+Each statement consists of the left-hand-side token(s), and right-hand-side
+tokens, separated by a '=' token. This class stores the information on the
+left-hand-side tokens.
+"""
+ def __init__ (self):
+ self.leftTokens = []
+ self.leftScope = None
+
+
+class MacroExpander(object):
+ def __init__ (self, tokens, defines):
+ self.tokens = tokens
+ self.defines = defines
+
+ def expand (self):
+ self.pos = 0
+ while self.pos < len(self.tokens):
+ self.expandToken()
+
+ def expandToken (self):
+ token = self.tokens[self.pos]
+ if not self.defines.has_key(token):
+ self.pos += 1
+ return
+
+ macro = self.defines[token]
+ nvars = len(macro.vars.keys())
+ if nvars == 0:
+ # Simple expansion
+ self.tokens[self.pos:self.pos+1] = macro.tokens
+ return
+ else:
+ # Expansion with arguments.
+ values, lastPos = self.parseValues()
+ newtokens = []
+ for mtoken in macro.tokens:
+ if macro.vars.has_key(mtoken):
+ # variable
+ pos = macro.vars[mtoken]
+ valtokens = values[pos]
+ for valtoken in valtokens:
+ newtokens.append(valtoken)
+ else:
+ # not a variable
+ newtokens.append(mtoken)
+
+ self.tokens[self.pos:self.pos+lastPos+1] = newtokens
+
+
+ def parseValues (self):
+ """Parse tokens to get macro function variable values.
+
+Be aware that there is an implicit quotes around the text between the open
+paren, the comma(s), and the close paren. For instance, if a macro is defined
+as FOO(a, b) and is used as FOO(one two three, and four), then the 'a' must be
+replaced with 'one two three', and the 'b' replaced with 'and four'. In other
+words, whitespace does not end a token.
+
+"""
+ values = []
+ i = 1
+ scope = 0
+ value = []
+ while True:
+ try:
+ tk = self.tokens[self.pos+i]
+ except IndexError:
+ progress ("error parsing values (%d)\n"%i)
+ for j in xrange(0, i):
+ print self.tokens[self.pos+j],
+ print ''
+ srclexer.dumpTokens(self.tokens)
+ srclexer.dumpTokens(self.newtokens)
+ print "tokens expanded so far:"
+ for tk in self.expandedTokens:
+ print "-"*20
+ print tk
+ srclexer.dumpTokens(self.defines[tk].tokens)
+ sys.exit(1)
+ if tk == '(':
+ value = []
+ scope += 1
+ elif tk == ',':
+ values.append(value)
+ value = []
+ elif tk == ')':
+ scope -= 1
+ values.append(value)
+ value = []
+ if scope == 0:
+ break
+ else:
+ raise ParseError ('')
+ else:
+ value.append(tk)
+ i += 1
+
+ return values, i
+
+ def getTokens (self):
+ return self.tokens
+
+
+class SrcParser(object):
+
+ def __init__ (self, tokens, defines = None):
+ self.tokens = tokens
+ self.defines = defines
+ self.debug = False
+ self.onlyExpandMacros = False
+
+ def init (self):
+ self.elementStack = [RootNode()]
+ self.stmtData = Statement()
+ self.tokenBuf = []
+ self.leftTokens = []
+
+ # Expand defined macros.
+ if self.debug:
+ progress ("-"*68+"\n")
+ for key in self.defines.keys():
+ progress ("define: %s\n"%key)
+
+ self.expandMacro()
+ self.tokenSize = len(self.tokens)
+
+ def expandMacro (self):
+ macroExp = MacroExpander(self.tokens, self.defines)
+ macroExp.expand()
+ self.tokens = macroExp.getTokens()
+ if self.onlyExpandMacros:
+ srclexer.dumpTokens(self.tokens)
+ sys.exit(0)
+
+ def parse (self):
+ """Parse it!
+
+This is the main loop for the parser. This is where it all begins and ends.
+"""
+ self.init()
+
+ i = 0
+ while i < self.tokenSize:
+ tk = self.tokens[i]
+ if tk == '{':
+ i = self.openBrace(i)
+ elif tk == '}':
+ i = self.closeBrace(i)
+ elif tk == ';':
+ i = self.semiColon(i)
+ elif tk == '=':
+ i = self.assignment(i)
+ else:
+ self.tokenBuf.append(tk)
+
+ i += 1
+
+ return self.elementStack[0]
+
+ #-------------------------------------------------------------------------
+ # Token Handlers
+
+ """
+Each token handler takes the current token position and returns the position
+of the last token processed. For the most part, the current token position
+and the last processed token are one and the same, in which case the handler
+can simply return the position value it receives without incrementing it.
+
+If you need to read ahead to process more tokens than just the current token,
+make sure that the new token position points to the last token that has been
+processed, not the next token that has not yet been processed. This is
+because the main loop increments the token position when it returns from the
+handler.
+"""
+
+ # assignment token '='
+ def assignment (self, i):
+ self.leftTokens = self.tokenBuf[:]
+ if self.stmtData.leftScope == None:
+ # Keep track of lhs data in case of compound statement.
+ self.stmtData.leftTokens = self.tokenBuf[:]
+ self.stmtData.leftScope = len(self.elementStack) - 1
+
+ self.tokenBuf = []
+ return i
+
+ # open brace token '{'
+ def openBrace (self, i):
+ bufSize = len(self.tokenBuf)
+ leftSize = len(self.leftTokens)
+ obj = None
+ if bufSize == 0 and leftSize > 0:
+ # Name = { ...
+ obj = Element(self.leftTokens[0])
+
+ elif bufSize > 0 and leftSize == 0:
+ # Type Name { ...
+ wgtType = self.tokenBuf[0]
+ wgtRID = None
+ if bufSize >= 2:
+ wgtRID = self.tokenBuf[1]
+ obj = Element(wgtType, wgtRID)
+
+ else:
+ # LeftName = Name { ...
+ obj = Element(self.leftTokens[0])
+ obj.setAttr("type", self.tokenBuf[0])
+
+ obj.name = transName(obj.name)
+
+ if obj.name == 'string-list':
+ i = self.parseStringList(i)
+ elif obj.name == 'filter-list':
+ i = self.parseFilterList(i, obj)
+ else:
+ self.elementStack[-1].appendChild(obj)
+ self.elementStack.append(obj)
+
+ self.tokenBuf = []
+ self.leftTokens = []
+
+ return i
+
+ # close brace token '}'
+ def closeBrace (self, i):
+ if len(self.tokenBuf) > 0:
+ if self.debug:
+ print self.tokenBuf
+ raise ParseError ('')
+ self.elementStack.pop()
+ return i
+
+ # semi colon token ';'
+ def semiColon (self, i):
+ stackSize = len(self.elementStack)
+ scope = stackSize - 1
+ if len(self.tokenBuf) == 0:
+ pass
+ elif scope == 0:
+ # We are not supposed to have any statment in global scope.
+ # Just ignore this statement.
+ pass
+ else:
+ # Statement within a scope. Import it as an attribute for the
+ # current element.
+ elem = self.elementStack[-1]
+
+ name = "none"
+ if len(self.leftTokens) > 0:
+ # Use the leftmost token as the name for now. If we need to
+ # do more complex parsing of lhs, add more code here.
+ name = self.leftTokens[0]
+ name = transName(name)
+
+ if name == 'pos':
+ i = self.parsePosAttr(i)
+ elif name == 'size':
+ i = self.parseSizeAttr(i)
+ elif len (self.tokenBuf) == 1:
+ # Simple value
+ value = transValue(self.tokenBuf[0])
+ name = renameAttribute(name, elem.name)
+ elem.setAttr(name, value)
+
+ if not self.stmtData.leftScope == None and self.stmtData.leftScope < scope:
+ # This is a nested scope within a statement. Do nothing for now.
+ pass
+
+ if self.stmtData.leftScope == scope:
+ # end of (nested) statement.
+ self.stmtData.leftScope = None
+
+ self.tokenBuf = []
+ self.leftTokens = []
+
+ return i
+
+ def parseStringList (self, i):
+
+ i += 1
+ while i < self.tokenSize:
+ tk = self.tokens[i]
+ if tk == '}':
+ break
+ i += 1
+
+ return i
+
+ def parseFilterList (self, i, obj):
+ self.elementStack[-1].appendChild(obj)
+ self.elementStack.append(obj)
+
+ return i
+
+ def parsePosAttr (self, i):
+
+ # MAP_APPFONT ( 6 , 5 )
+ elem = self.elementStack[-1]
+ x, y = self.parseMapAppfont(self.tokenBuf)
+ elem.setAttr("x", x)
+ elem.setAttr("y", y)
+
+ return i
+
+ def parseSizeAttr (self, i):
+
+ # MAP_APPFONT ( 6 , 5 )
+ elem = self.elementStack[-1]
+ width, height = self.parseMapAppfont(self.tokenBuf)
+ elem.setAttr("width", width)
+ elem.setAttr("height", height)
+
+ return i
+
+ def parseMapAppfont (self, tokens):
+ values = []
+ scope = 0
+ val = ''
+ for tk in tokens:
+ if tk == '(':
+ scope += 1
+ if scope == 1:
+ val = ''
+ else:
+ val += tk
+ elif tk == ')':
+ scope -= 1
+ if scope == 0:
+ if len(val) == 0:
+ raise ParseError ('')
+ values.append(val)
+ break
+ else:
+ val += tk
+ elif tk == ',':
+ if len(val) == 0:
+ raise ParseError ('')
+ values.append(val)
+ val = ''
+ elif scope > 0:
+ val += tk
+
+ if len(values) != 2:
+ raise ParseError ('')
+
+ return eval(values[0]), eval(values[1])
+
+