19f22d7c2SAndrew Rist# ************************************************************* 29f22d7c2SAndrew Rist# 39f22d7c2SAndrew Rist# Licensed to the Apache Software Foundation (ASF) under one 49f22d7c2SAndrew Rist# or more contributor license agreements. See the NOTICE file 59f22d7c2SAndrew Rist# distributed with this work for additional information 69f22d7c2SAndrew Rist# regarding copyright ownership. The ASF licenses this file 79f22d7c2SAndrew Rist# to you under the Apache License, Version 2.0 (the 89f22d7c2SAndrew Rist# "License"); you may not use this file except in compliance 99f22d7c2SAndrew Rist# with the License. You may obtain a copy of the License at 109f22d7c2SAndrew Rist# 119f22d7c2SAndrew Rist# http://www.apache.org/licenses/LICENSE-2.0 129f22d7c2SAndrew Rist# 139f22d7c2SAndrew Rist# Unless required by applicable law or agreed to in writing, 149f22d7c2SAndrew Rist# software distributed under the License is distributed on an 159f22d7c2SAndrew Rist# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 169f22d7c2SAndrew Rist# KIND, either express or implied. See the License for the 179f22d7c2SAndrew Rist# specific language governing permissions and limitations 189f22d7c2SAndrew Rist# under the License. 199f22d7c2SAndrew Rist# 209f22d7c2SAndrew Rist# ************************************************************* 219f22d7c2SAndrew Rist 22cdf0e10cSrcweirimport sys 23cdf0e10cSrcweirfrom globals import * 24cdf0e10cSrcweirimport srclexer 25cdf0e10cSrcweir 26cdf0e10cSrcweir# simple name translation map 27cdf0e10cSrcweirpostTransMap = {"ok-button": "okbutton", 28cdf0e10cSrcweir "cancel-button": "cancelbutton", 29cdf0e10cSrcweir "help-button": "helpbutton"} 30cdf0e10cSrcweir 31cdf0e10cSrcweirdef transName (name): 32cdf0e10cSrcweir """Translate a mixed-casing name to dash-separated name. 33cdf0e10cSrcweir 34cdf0e10cSrcweirTranslate a mixed-casing name (e.g. MyLongName) to a dash-separated name 35cdf0e10cSrcweir(e.g. my-long-name). 36cdf0e10cSrcweir""" 37cdf0e10cSrcweir def isUpper (c): 38cdf0e10cSrcweir return c >= 'A' and c <= 'Z' 39cdf0e10cSrcweir 40cdf0e10cSrcweir newname = '' 41cdf0e10cSrcweir parts = [] 42cdf0e10cSrcweir buf = '' 43cdf0e10cSrcweir for c in name: 44cdf0e10cSrcweir if isUpper(c) and len(buf) > 1: 45cdf0e10cSrcweir parts.append(buf) 46cdf0e10cSrcweir buf = c 47cdf0e10cSrcweir else: 48cdf0e10cSrcweir buf += c 49cdf0e10cSrcweir 50cdf0e10cSrcweir if len(buf) > 0: 51cdf0e10cSrcweir parts.append(buf) 52cdf0e10cSrcweir 53cdf0e10cSrcweir first = True 54cdf0e10cSrcweir for part in parts: 55cdf0e10cSrcweir if first: 56cdf0e10cSrcweir first = False 57cdf0e10cSrcweir else: 58cdf0e10cSrcweir newname += '-' 59cdf0e10cSrcweir newname += part.lower() 60cdf0e10cSrcweir 61cdf0e10cSrcweir # special-case mapping ... 62cdf0e10cSrcweir if 0: #postTransMap.has_key(newname): 63cdf0e10cSrcweir newname = postTransMap[newname] 64cdf0e10cSrcweir 65cdf0e10cSrcweir return newname 66cdf0e10cSrcweir 67cdf0e10cSrcweir 68cdf0e10cSrcweirdef transValue (value): 69cdf0e10cSrcweir """Translate certain values. 70cdf0e10cSrcweir 71cdf0e10cSrcweirExamples of translated values include TRUE -> true, FALSE -> false. 72cdf0e10cSrcweir""" 73cdf0e10cSrcweir if value.lower() in ["true", "false"]: 74cdf0e10cSrcweir value = value.lower() 75cdf0e10cSrcweir return value 76cdf0e10cSrcweir 77cdf0e10cSrcweir 78cdf0e10cSrcweirdef renameAttribute (name, elemName): 79cdf0e10cSrcweir 80cdf0e10cSrcweir # TODO: all manner of evil special cases ... 81cdf0e10cSrcweir if elemName == 'metric-field' and name == 'spin-size': 82cdf0e10cSrcweir return 'step-size' 83cdf0e10cSrcweir 84cdf0e10cSrcweir return name 85cdf0e10cSrcweir 86cdf0e10cSrcweir 87cdf0e10cSrcweirclass Statement(object): 88cdf0e10cSrcweir """Container to hold information for a single statement. 89cdf0e10cSrcweir 90cdf0e10cSrcweirEach statement consists of the left-hand-side token(s), and right-hand-side 91cdf0e10cSrcweirtokens, separated by a '=' token. This class stores the information on the 92cdf0e10cSrcweirleft-hand-side tokens. 93cdf0e10cSrcweir""" 94cdf0e10cSrcweir def __init__ (self): 95cdf0e10cSrcweir self.leftTokens = [] 96cdf0e10cSrcweir self.leftScope = None 97cdf0e10cSrcweir 98cdf0e10cSrcweir 99cdf0e10cSrcweirclass MacroExpander(object): 100cdf0e10cSrcweir def __init__ (self, tokens, defines): 101cdf0e10cSrcweir self.tokens = tokens 102cdf0e10cSrcweir self.defines = defines 103cdf0e10cSrcweir 104cdf0e10cSrcweir def expand (self): 105cdf0e10cSrcweir self.pos = 0 106cdf0e10cSrcweir while self.pos < len(self.tokens): 107cdf0e10cSrcweir self.expandToken() 108cdf0e10cSrcweir 109cdf0e10cSrcweir def expandToken (self): 110cdf0e10cSrcweir token = self.tokens[self.pos] 111*7d9fa7c3SPedro Giffuni if token not in self.defines: 112cdf0e10cSrcweir self.pos += 1 113cdf0e10cSrcweir return 114cdf0e10cSrcweir 115cdf0e10cSrcweir macro = self.defines[token] 116*7d9fa7c3SPedro Giffuni nvars = len(list(macro.vars.keys())) 117cdf0e10cSrcweir if nvars == 0: 118cdf0e10cSrcweir # Simple expansion 119cdf0e10cSrcweir self.tokens[self.pos:self.pos+1] = macro.tokens 120cdf0e10cSrcweir return 121cdf0e10cSrcweir else: 122cdf0e10cSrcweir # Expansion with arguments. 123cdf0e10cSrcweir values, lastPos = self.parseValues() 124cdf0e10cSrcweir newtokens = [] 125cdf0e10cSrcweir for mtoken in macro.tokens: 126*7d9fa7c3SPedro Giffuni if mtoken in macro.vars: 127cdf0e10cSrcweir # variable 128cdf0e10cSrcweir pos = macro.vars[mtoken] 129cdf0e10cSrcweir valtokens = values[pos] 130cdf0e10cSrcweir for valtoken in valtokens: 131cdf0e10cSrcweir newtokens.append(valtoken) 132cdf0e10cSrcweir else: 133cdf0e10cSrcweir # not a variable 134cdf0e10cSrcweir newtokens.append(mtoken) 135cdf0e10cSrcweir 136cdf0e10cSrcweir self.tokens[self.pos:self.pos+lastPos+1] = newtokens 137cdf0e10cSrcweir 138cdf0e10cSrcweir 139cdf0e10cSrcweir def parseValues (self): 140cdf0e10cSrcweir """Parse tokens to get macro function variable values. 141cdf0e10cSrcweir 142cdf0e10cSrcweirBe aware that there is an implicit quotes around the text between the open 143cdf0e10cSrcweirparen, the comma(s), and the close paren. For instance, if a macro is defined 144cdf0e10cSrcweiras FOO(a, b) and is used as FOO(one two three, and four), then the 'a' must be 145cdf0e10cSrcweirreplaced with 'one two three', and the 'b' replaced with 'and four'. In other 146cdf0e10cSrcweirwords, whitespace does not end a token. 147cdf0e10cSrcweir 148cdf0e10cSrcweir""" 149cdf0e10cSrcweir values = [] 150cdf0e10cSrcweir i = 1 151cdf0e10cSrcweir scope = 0 152cdf0e10cSrcweir value = [] 153cdf0e10cSrcweir while True: 154cdf0e10cSrcweir try: 155cdf0e10cSrcweir tk = self.tokens[self.pos+i] 156cdf0e10cSrcweir except IndexError: 157cdf0e10cSrcweir progress ("error parsing values (%d)\n"%i) 158*7d9fa7c3SPedro Giffuni for j in range(0, i): 159*7d9fa7c3SPedro Giffuni print(self.tokens[self.pos+j], end=' ') 160*7d9fa7c3SPedro Giffuni print('') 161cdf0e10cSrcweir srclexer.dumpTokens(self.tokens) 162cdf0e10cSrcweir srclexer.dumpTokens(self.newtokens) 163*7d9fa7c3SPedro Giffuni print("tokens expanded so far:") 164cdf0e10cSrcweir for tk in self.expandedTokens: 165*7d9fa7c3SPedro Giffuni print("-"*20) 166*7d9fa7c3SPedro Giffuni print(tk) 167cdf0e10cSrcweir srclexer.dumpTokens(self.defines[tk].tokens) 168cdf0e10cSrcweir sys.exit(1) 169cdf0e10cSrcweir if tk == '(': 170cdf0e10cSrcweir value = [] 171cdf0e10cSrcweir scope += 1 172cdf0e10cSrcweir elif tk == ',': 173cdf0e10cSrcweir values.append(value) 174cdf0e10cSrcweir value = [] 175cdf0e10cSrcweir elif tk == ')': 176cdf0e10cSrcweir scope -= 1 177cdf0e10cSrcweir values.append(value) 178cdf0e10cSrcweir value = [] 179cdf0e10cSrcweir if scope == 0: 180cdf0e10cSrcweir break 181cdf0e10cSrcweir else: 182cdf0e10cSrcweir raise ParseError ('') 183cdf0e10cSrcweir else: 184cdf0e10cSrcweir value.append(tk) 185cdf0e10cSrcweir i += 1 186cdf0e10cSrcweir 187cdf0e10cSrcweir return values, i 188cdf0e10cSrcweir 189cdf0e10cSrcweir def getTokens (self): 190cdf0e10cSrcweir return self.tokens 191cdf0e10cSrcweir 192cdf0e10cSrcweir 193cdf0e10cSrcweirclass SrcParser(object): 194cdf0e10cSrcweir 195cdf0e10cSrcweir def __init__ (self, tokens, defines = None): 196cdf0e10cSrcweir self.tokens = tokens 197cdf0e10cSrcweir self.defines = defines 198cdf0e10cSrcweir self.debug = False 199cdf0e10cSrcweir self.onlyExpandMacros = False 200cdf0e10cSrcweir 201cdf0e10cSrcweir def init (self): 202cdf0e10cSrcweir self.elementStack = [RootNode()] 203cdf0e10cSrcweir self.stmtData = Statement() 204cdf0e10cSrcweir self.tokenBuf = [] 205cdf0e10cSrcweir self.leftTokens = [] 206cdf0e10cSrcweir 207cdf0e10cSrcweir # Expand defined macros. 208cdf0e10cSrcweir if self.debug: 209cdf0e10cSrcweir progress ("-"*68+"\n") 210*7d9fa7c3SPedro Giffuni for key in list(self.defines.keys()): 211cdf0e10cSrcweir progress ("define: %s\n"%key) 212cdf0e10cSrcweir 213cdf0e10cSrcweir self.expandMacro() 214cdf0e10cSrcweir self.tokenSize = len(self.tokens) 215cdf0e10cSrcweir 216cdf0e10cSrcweir def expandMacro (self): 217cdf0e10cSrcweir macroExp = MacroExpander(self.tokens, self.defines) 218cdf0e10cSrcweir macroExp.expand() 219cdf0e10cSrcweir self.tokens = macroExp.getTokens() 220cdf0e10cSrcweir if self.onlyExpandMacros: 221cdf0e10cSrcweir srclexer.dumpTokens(self.tokens) 222cdf0e10cSrcweir sys.exit(0) 223cdf0e10cSrcweir 224cdf0e10cSrcweir def parse (self): 225cdf0e10cSrcweir """Parse it! 226cdf0e10cSrcweir 227cdf0e10cSrcweirThis is the main loop for the parser. This is where it all begins and ends. 228cdf0e10cSrcweir""" 229cdf0e10cSrcweir self.init() 230cdf0e10cSrcweir 231cdf0e10cSrcweir i = 0 232cdf0e10cSrcweir while i < self.tokenSize: 233cdf0e10cSrcweir tk = self.tokens[i] 234cdf0e10cSrcweir if tk == '{': 235cdf0e10cSrcweir i = self.openBrace(i) 236cdf0e10cSrcweir elif tk == '}': 237cdf0e10cSrcweir i = self.closeBrace(i) 238cdf0e10cSrcweir elif tk == ';': 239cdf0e10cSrcweir i = self.semiColon(i) 240cdf0e10cSrcweir elif tk == '=': 241cdf0e10cSrcweir i = self.assignment(i) 242cdf0e10cSrcweir else: 243cdf0e10cSrcweir self.tokenBuf.append(tk) 244cdf0e10cSrcweir 245cdf0e10cSrcweir i += 1 246cdf0e10cSrcweir 247cdf0e10cSrcweir return self.elementStack[0] 248cdf0e10cSrcweir 249cdf0e10cSrcweir #------------------------------------------------------------------------- 250cdf0e10cSrcweir # Token Handlers 251cdf0e10cSrcweir 252cdf0e10cSrcweir """ 253cdf0e10cSrcweirEach token handler takes the current token position and returns the position 254cdf0e10cSrcweirof the last token processed. For the most part, the current token position 255cdf0e10cSrcweirand the last processed token are one and the same, in which case the handler 256cdf0e10cSrcweircan simply return the position value it receives without incrementing it. 257cdf0e10cSrcweir 258cdf0e10cSrcweirIf you need to read ahead to process more tokens than just the current token, 259cdf0e10cSrcweirmake sure that the new token position points to the last token that has been 260cdf0e10cSrcweirprocessed, not the next token that has not yet been processed. This is 261cdf0e10cSrcweirbecause the main loop increments the token position when it returns from the 262cdf0e10cSrcweirhandler. 263cdf0e10cSrcweir""" 264cdf0e10cSrcweir 265cdf0e10cSrcweir # assignment token '=' 266cdf0e10cSrcweir def assignment (self, i): 267cdf0e10cSrcweir self.leftTokens = self.tokenBuf[:] 268cdf0e10cSrcweir if self.stmtData.leftScope == None: 269cdf0e10cSrcweir # Keep track of lhs data in case of compound statement. 270cdf0e10cSrcweir self.stmtData.leftTokens = self.tokenBuf[:] 271cdf0e10cSrcweir self.stmtData.leftScope = len(self.elementStack) - 1 272cdf0e10cSrcweir 273cdf0e10cSrcweir self.tokenBuf = [] 274cdf0e10cSrcweir return i 275cdf0e10cSrcweir 276cdf0e10cSrcweir # open brace token '{' 277cdf0e10cSrcweir def openBrace (self, i): 278cdf0e10cSrcweir bufSize = len(self.tokenBuf) 279cdf0e10cSrcweir leftSize = len(self.leftTokens) 280cdf0e10cSrcweir obj = None 281cdf0e10cSrcweir if bufSize == 0 and leftSize > 0: 282cdf0e10cSrcweir # Name = { ... 283cdf0e10cSrcweir obj = Element(self.leftTokens[0]) 284cdf0e10cSrcweir 285cdf0e10cSrcweir elif bufSize > 0 and leftSize == 0: 286cdf0e10cSrcweir # Type Name { ... 287cdf0e10cSrcweir wgtType = self.tokenBuf[0] 288cdf0e10cSrcweir wgtRID = None 289cdf0e10cSrcweir if bufSize >= 2: 290cdf0e10cSrcweir wgtRID = self.tokenBuf[1] 291cdf0e10cSrcweir obj = Element(wgtType, wgtRID) 292cdf0e10cSrcweir 293cdf0e10cSrcweir else: 294cdf0e10cSrcweir # LeftName = Name { ... 295cdf0e10cSrcweir obj = Element(self.leftTokens[0]) 296cdf0e10cSrcweir obj.setAttr("type", self.tokenBuf[0]) 297cdf0e10cSrcweir 298cdf0e10cSrcweir obj.name = transName(obj.name) 299cdf0e10cSrcweir 300cdf0e10cSrcweir if obj.name == 'string-list': 301cdf0e10cSrcweir i = self.parseStringList(i) 302cdf0e10cSrcweir elif obj.name == 'filter-list': 303cdf0e10cSrcweir i = self.parseFilterList(i, obj) 304cdf0e10cSrcweir else: 305cdf0e10cSrcweir self.elementStack[-1].appendChild(obj) 306cdf0e10cSrcweir self.elementStack.append(obj) 307cdf0e10cSrcweir 308cdf0e10cSrcweir self.tokenBuf = [] 309cdf0e10cSrcweir self.leftTokens = [] 310cdf0e10cSrcweir 311cdf0e10cSrcweir return i 312cdf0e10cSrcweir 313cdf0e10cSrcweir # close brace token '}' 314cdf0e10cSrcweir def closeBrace (self, i): 315cdf0e10cSrcweir if len(self.tokenBuf) > 0: 316cdf0e10cSrcweir if self.debug: 317*7d9fa7c3SPedro Giffuni print(self.tokenBuf) 318cdf0e10cSrcweir raise ParseError ('') 319cdf0e10cSrcweir self.elementStack.pop() 320cdf0e10cSrcweir return i 321cdf0e10cSrcweir 322cdf0e10cSrcweir # semi colon token ';' 323cdf0e10cSrcweir def semiColon (self, i): 324cdf0e10cSrcweir stackSize = len(self.elementStack) 325cdf0e10cSrcweir scope = stackSize - 1 326cdf0e10cSrcweir if len(self.tokenBuf) == 0: 327cdf0e10cSrcweir pass 328cdf0e10cSrcweir elif scope == 0: 329cdf0e10cSrcweir # We are not supposed to have any statment in global scope. 330cdf0e10cSrcweir # Just ignore this statement. 331cdf0e10cSrcweir pass 332cdf0e10cSrcweir else: 333cdf0e10cSrcweir # Statement within a scope. Import it as an attribute for the 334cdf0e10cSrcweir # current element. 335cdf0e10cSrcweir elem = self.elementStack[-1] 336cdf0e10cSrcweir 337cdf0e10cSrcweir name = "none" 338cdf0e10cSrcweir if len(self.leftTokens) > 0: 339cdf0e10cSrcweir # Use the leftmost token as the name for now. If we need to 340cdf0e10cSrcweir # do more complex parsing of lhs, add more code here. 341cdf0e10cSrcweir name = self.leftTokens[0] 342cdf0e10cSrcweir name = transName(name) 343cdf0e10cSrcweir 344cdf0e10cSrcweir if name == 'pos': 345cdf0e10cSrcweir i = self.parsePosAttr(i) 346cdf0e10cSrcweir elif name == 'size': 347cdf0e10cSrcweir i = self.parseSizeAttr(i) 348cdf0e10cSrcweir elif len (self.tokenBuf) == 1: 349cdf0e10cSrcweir # Simple value 350cdf0e10cSrcweir value = transValue(self.tokenBuf[0]) 351cdf0e10cSrcweir name = renameAttribute(name, elem.name) 352cdf0e10cSrcweir elem.setAttr(name, value) 353cdf0e10cSrcweir 354cdf0e10cSrcweir if not self.stmtData.leftScope == None and self.stmtData.leftScope < scope: 355cdf0e10cSrcweir # This is a nested scope within a statement. Do nothing for now. 356cdf0e10cSrcweir pass 357cdf0e10cSrcweir 358cdf0e10cSrcweir if self.stmtData.leftScope == scope: 359cdf0e10cSrcweir # end of (nested) statement. 360cdf0e10cSrcweir self.stmtData.leftScope = None 361cdf0e10cSrcweir 362cdf0e10cSrcweir self.tokenBuf = [] 363cdf0e10cSrcweir self.leftTokens = [] 364cdf0e10cSrcweir 365cdf0e10cSrcweir return i 366cdf0e10cSrcweir 367cdf0e10cSrcweir def parseStringList (self, i): 368cdf0e10cSrcweir 369cdf0e10cSrcweir i += 1 370cdf0e10cSrcweir while i < self.tokenSize: 371cdf0e10cSrcweir tk = self.tokens[i] 372cdf0e10cSrcweir if tk == '}': 373cdf0e10cSrcweir break 374cdf0e10cSrcweir i += 1 375cdf0e10cSrcweir 376cdf0e10cSrcweir return i 377cdf0e10cSrcweir 378cdf0e10cSrcweir def parseFilterList (self, i, obj): 379cdf0e10cSrcweir self.elementStack[-1].appendChild(obj) 380cdf0e10cSrcweir self.elementStack.append(obj) 381cdf0e10cSrcweir 382cdf0e10cSrcweir return i 383cdf0e10cSrcweir 384cdf0e10cSrcweir def parsePosAttr (self, i): 385cdf0e10cSrcweir 386cdf0e10cSrcweir # MAP_APPFONT ( 6 , 5 ) 387cdf0e10cSrcweir elem = self.elementStack[-1] 388cdf0e10cSrcweir x, y = self.parseMapAppfont(self.tokenBuf) 389cdf0e10cSrcweir elem.setAttr("x", x) 390cdf0e10cSrcweir elem.setAttr("y", y) 391cdf0e10cSrcweir 392cdf0e10cSrcweir return i 393cdf0e10cSrcweir 394cdf0e10cSrcweir def parseSizeAttr (self, i): 395cdf0e10cSrcweir 396cdf0e10cSrcweir # MAP_APPFONT ( 6 , 5 ) 397cdf0e10cSrcweir elem = self.elementStack[-1] 398cdf0e10cSrcweir width, height = self.parseMapAppfont(self.tokenBuf) 399cdf0e10cSrcweir elem.setAttr("width", width) 400cdf0e10cSrcweir elem.setAttr("height", height) 401cdf0e10cSrcweir 402cdf0e10cSrcweir return i 403cdf0e10cSrcweir 404cdf0e10cSrcweir def parseMapAppfont (self, tokens): 405cdf0e10cSrcweir values = [] 406cdf0e10cSrcweir scope = 0 407cdf0e10cSrcweir val = '' 408cdf0e10cSrcweir for tk in tokens: 409cdf0e10cSrcweir if tk == '(': 410cdf0e10cSrcweir scope += 1 411cdf0e10cSrcweir if scope == 1: 412cdf0e10cSrcweir val = '' 413cdf0e10cSrcweir else: 414cdf0e10cSrcweir val += tk 415cdf0e10cSrcweir elif tk == ')': 416cdf0e10cSrcweir scope -= 1 417cdf0e10cSrcweir if scope == 0: 418cdf0e10cSrcweir if len(val) == 0: 419cdf0e10cSrcweir raise ParseError ('') 420cdf0e10cSrcweir values.append(val) 421cdf0e10cSrcweir break 422cdf0e10cSrcweir else: 423cdf0e10cSrcweir val += tk 424cdf0e10cSrcweir elif tk == ',': 425cdf0e10cSrcweir if len(val) == 0: 426cdf0e10cSrcweir raise ParseError ('') 427cdf0e10cSrcweir values.append(val) 428cdf0e10cSrcweir val = '' 429cdf0e10cSrcweir elif scope > 0: 430cdf0e10cSrcweir val += tk 431cdf0e10cSrcweir 432cdf0e10cSrcweir if len(values) != 2: 433cdf0e10cSrcweir raise ParseError ('') 434cdf0e10cSrcweir 435cdf0e10cSrcweir return eval(values[0]), eval(values[1]) 436