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
27ae54856bSPedro GiffunipostTransMap = {"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]
1117d9fa7c3SPedro Giffuni        if token not in self.defines:
112cdf0e10cSrcweir            self.pos += 1
113cdf0e10cSrcweir            return
114cdf0e10cSrcweir
115cdf0e10cSrcweir        macro = self.defines[token]
1167d9fa7c3SPedro 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:
1267d9fa7c3SPedro 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
142ae54856bSPedro GiffuniBe aware that there is an implicit quotes around the text between the open
143ae54856bSPedro Giffuniparen, the comma(s), and the close paren.  For instance, if a macro is defined
144ae54856bSPedro Giffunias FOO(a, b) and is used as FOO(one two three, and four), then the 'a' must be
145ae54856bSPedro Giffunireplaced with 'one two three', and the 'b' replaced with 'and four'.  In other
146ae54856bSPedro Giffuniwords, 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)
1587d9fa7c3SPedro Giffuni                for j in range(0, i):
1597d9fa7c3SPedro Giffuni                    print(self.tokens[self.pos+j], end=' ')
1607d9fa7c3SPedro Giffuni                print('')
161cdf0e10cSrcweir                srclexer.dumpTokens(self.tokens)
162cdf0e10cSrcweir                srclexer.dumpTokens(self.newtokens)
1637d9fa7c3SPedro Giffuni                print("tokens expanded so far:")
164cdf0e10cSrcweir                for tk in self.expandedTokens:
1657d9fa7c3SPedro Giffuni                    print("-"*20)
1667d9fa7c3SPedro 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")
2107d9fa7c3SPedro 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    """
253ae54856bSPedro GiffuniEach token handler takes the current token position and returns the position
254ae54856bSPedro Giffuniof the last token processed.  For the most part, the current token position
255ae54856bSPedro Giffuniand the last processed token are one and the same, in which case the handler
256ae54856bSPedro Giffunican simply return the position value it receives without incrementing it.
257ae54856bSPedro Giffuni
258ae54856bSPedro GiffuniIf you need to read ahead to process more tokens than just the current token,
259ae54856bSPedro Giffunimake sure that the new token position points to the last token that has been
260ae54856bSPedro Giffuniprocessed, not the next token that has not yet been processed.  This is
261ae54856bSPedro Giffunibecause the main loop increments the token position when it returns from the
262cdf0e10cSrcweirhandler.
263ae54856bSPedro Giffuni"""
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
275ae54856bSPedro Giffuni
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:
3177d9fa7c3SPedro 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:
329*86e1cf34SPedro Giffuni            # We are not supposed to have any statement in global scope.
330cdf0e10cSrcweir            # Just ignore this statement.
331cdf0e10cSrcweir            pass
332cdf0e10cSrcweir        else:
333ae54856bSPedro Giffuni            # 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