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