merge the formfield patch from ooo-build
[ooovba.git] / toolkit / src2xml / source / srcparser.py
blobb25d39ecb66e330a3da9620a64a00dcb8550a6af
1 import sys
2 from globals import *
3 import srclexer
5 # simple name translation map
6 postTransMap = {"ok-button": "okbutton",
7 "cancel-button": "cancelbutton",
8 "help-button": "helpbutton"}
10 def transName (name):
11 """Translate a mixed-casing name to dash-separated name.
13 Translate 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'
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
29 if len(buf) > 0:
30 parts.append(buf)
32 first = True
33 for part in parts:
34 if first:
35 first = False
36 else:
37 newname += '-'
38 newname += part.lower()
40 # special-case mapping ...
41 if 0: #postTransMap.has_key(newname):
42 newname = postTransMap[newname]
44 return newname
47 def transValue (value):
48 """Translate certain values.
50 Examples of translated values include TRUE -> true, FALSE -> false.
51 """
52 if value.lower() in ["true", "false"]:
53 value = value.lower()
54 return value
57 def renameAttribute (name, elemName):
59 # TODO: all manner of evil special cases ...
60 if elemName == 'metric-field' and name == 'spin-size':
61 return 'step-size'
63 return name
66 class Statement(object):
67 """Container to hold information for a single statement.
69 Each statement consists of the left-hand-side token(s), and right-hand-side
70 tokens, separated by a '=' token. This class stores the information on the
71 left-hand-side tokens.
72 """
73 def __init__ (self):
74 self.leftTokens = []
75 self.leftScope = None
78 class MacroExpander(object):
79 def __init__ (self, tokens, defines):
80 self.tokens = tokens
81 self.defines = defines
83 def expand (self):
84 self.pos = 0
85 while self.pos < len(self.tokens):
86 self.expandToken()
88 def expandToken (self):
89 token = self.tokens[self.pos]
90 if not self.defines.has_key(token):
91 self.pos += 1
92 return
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)
115 self.tokens[self.pos:self.pos+lastPos+1] = newtokens
118 def parseValues (self):
119 """Parse tokens to get macro function variable values.
121 Be aware that there is an implicit quotes around the text between the open
122 paren, the comma(s), and the close paren. For instance, if a macro is defined
123 as FOO(a, b) and is used as FOO(one two three, and four), then the 'a' must be
124 replaced with 'one two three', and the 'b' replaced with 'and four'. In other
125 words, whitespace does not end a token.
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
166 return values, i
168 def getTokens (self):
169 return self.tokens
172 class SrcParser(object):
174 def __init__ (self, tokens, defines = None):
175 self.tokens = tokens
176 self.defines = defines
177 self.debug = False
178 self.onlyExpandMacros = False
180 def init (self):
181 self.elementStack = [RootNode()]
182 self.stmtData = Statement()
183 self.tokenBuf = []
184 self.leftTokens = []
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)
192 self.expandMacro()
193 self.tokenSize = len(self.tokens)
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)
203 def parse (self):
204 """Parse it!
206 This is the main loop for the parser. This is where it all begins and ends.
208 self.init()
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)
224 i += 1
226 return self.elementStack[0]
228 #-------------------------------------------------------------------------
229 # Token Handlers
232 Each token handler takes the current token position and returns the position
233 of the last token processed. For the most part, the current token position
234 and the last processed token are one and the same, in which case the handler
235 can simply return the position value it receives without incrementing it.
237 If you need to read ahead to process more tokens than just the current token,
238 make sure that the new token position points to the last token that has been
239 processed, not the next token that has not yet been processed. This is
240 because the main loop increments the token position when it returns from the
241 handler.
242 """
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
252 self.tokenBuf = []
253 return i
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])
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)
272 else:
273 # LeftName = Name { ...
274 obj = Element(self.leftTokens[0])
275 obj.setAttr("type", self.tokenBuf[0])
277 obj.name = transName(obj.name)
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)
287 self.tokenBuf = []
288 self.leftTokens = []
290 return i
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
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]
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)
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)
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
337 if self.stmtData.leftScope == scope:
338 # end of (nested) statement.
339 self.stmtData.leftScope = None
341 self.tokenBuf = []
342 self.leftTokens = []
344 return i
346 def parseStringList (self, i):
348 i += 1
349 while i < self.tokenSize:
350 tk = self.tokens[i]
351 if tk == '}':
352 break
353 i += 1
355 return i
357 def parseFilterList (self, i, obj):
358 self.elementStack[-1].appendChild(obj)
359 self.elementStack.append(obj)
361 return i
363 def parsePosAttr (self, i):
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)
371 return i
373 def parseSizeAttr (self, i):
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)
381 return i
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
411 if len(values) != 2:
412 raise ParseError ('')
414 return eval(values[0]), eval(values[1])