Advertisement
Guest User

Eike Welk

a guest
Apr 20th, 2008
97
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 45.26 KB | None | 0 0
  1. # -*- coding: utf-8 -*-
  2. #***************************************************************************
  3. #    Copyright (C) 2008 by Eike Welk                                       *
  4. #    eike.welk@post.rwth-aachen.de                                         *
  5. #                                                                          *
  6. #    Inspiration came from:                                                *
  7. #    'fourFn.py', an example program, by Paul McGuire,                     *
  8. #    and the 'Spark' library by John Aycock.                               *
  9. #    Many thanks for their excellent contributions to publicly available   *
  10. #    knowledge.                                                            *
  11. #                                                                          *
  12. #    License: GPL                                                          *
  13. #                                                                          *
  14. #    This program is free software; you can redistribute it and/or modify  *
  15. #    it under the terms of the GNU General Public License as published by  *
  16. #    the Free Software Foundation; either version 2 of the License, or     *
  17. #    (at your option) any later version.                                   *
  18. #                                                                          *
  19. #    This program is distributed in the hope that it will be useful,       *
  20. #    but WITHOUT ANY WARRANTY; without even the implied warranty of        *
  21. #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
  22. #    GNU General Public License for more details.                          *
  23. #                                                                          *
  24. #    You should have received a copy of the GNU General Public License     *
  25. #    along with this program; if not, write to the                         *
  26. #    Free Software Foundation, Inc.,                                       *
  27. #    59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  28. #***************************************************************************
  29. '''
  30. Parser for the SIML simulation language.
  31. '''
  32.  
  33. from __future__ import division
  34.  
  35. __version__ = "$Revision: $"
  36.  
  37.  
  38. #import debugger
  39. #import pdb
  40. #import operation system stuff
  41. #import sys
  42. import os
  43. #import parser library
  44. import pyparsing
  45. from pyparsing import ( _ustr, Literal, CaselessLiteral, Keyword, Word,
  46.     ZeroOrMore, OneOrMore, Forward, nums, alphas, alphanums, restOfLine,
  47.     StringEnd, sglQuotedString, MatchFirst, Combine, Group, Optional,
  48.     ParseException, ParseFatalException, ParseElementEnhance )
  49. #import our own syntax tree classes
  50. from freeode.ast import *
  51.  
  52.  
  53.  
  54. #Enable a fast parsing mode with caching. May not always work.
  55. pyparsing.ParserElement.enablePackrat()
  56.  
  57.  
  58.  
  59. #Took code from pyparsing.Optional as a template
  60. class ErrStop(ParseElementEnhance):
  61.     """Parser that prevents backtracking.
  62.       The parser tries to match the given expression (which consists of other
  63.       parsers). If this expression does not match the parser raises a
  64.       ParseFatalException and parsing stops.
  65.       Otherwise, if the given expression matches, its parse results are returned
  66.       and the ErrStop has no effect on the parse results.
  67.    """
  68.     #TODO: implement setErrorAction( callableObject )
  69.     def __init__(self, expr):
  70.         super(ErrStop, self).__init__(expr, savelist=False)
  71.         self.mayReturnEmpty = True
  72.         #Additional string, that will be put in front of the error message.
  73.         self.errMsgStart = ''
  74.  
  75.     def parseImpl(self, instring, loc, doActions=True):
  76.         try:
  77.             loc, tokens = self.expr._parse(instring, loc, doActions, callPreParse=False)
  78.         except IndexError:
  79.             raise ParseFatalException(instring, loc, 'Index error: ', self.expr)
  80.         except ParseException, theError:
  81.             errMsg = self.errMsgStart + theError.msg
  82.             raise ParseFatalException(instring, theError.loc, errMsg, self.expr)
  83.         return loc, tokens
  84.  
  85.     def setErrMsgStart(self, msg):
  86.         """Set additional error message.
  87.           This string will be put in front of the error message of the given
  88.           parser.
  89.        """
  90.         self.errMsgStart = msg
  91.         return self
  92.  
  93.     def __str__(self):
  94.         if hasattr(self,"name"):
  95.             return self.name
  96.  
  97.         if self.strRepr is None:
  98.             self.strRepr = "[" + _ustr(self.expr) + "]"
  99.  
  100.         return self.strRepr
  101.  
  102.  
  103.  
  104. class ParseActionException(Exception):
  105.     '''Exception raised by the parse actions of the parser'''
  106.     pass
  107.  
  108.  
  109.  
  110. class ParseStage(object):
  111.     '''
  112.    The syntax definition (BNF) resides here.
  113.  
  114.    The parsing is done by the pyparsing library which combines
  115.    lexer and parser. The Pyparsing library generates a tree of
  116.    ParseResult objects. These objects
  117.    are replaced by objects inheriting from ast.Node
  118.    in the parse actions of this class.
  119.  
  120.    Normally a file name is given to the class, and a tree of ast.Node objects is
  121.    returned. The program can also be entered as a string.
  122.    Additionally the class can parse parts of a program: expressions.
  123.  
  124.    The parse* methods return a tree of Node objects; the abstract syntax
  125.    tree (AST)
  126.  
  127.    Usage:
  128.    parser = ParseStage()
  129.    ast1 = parser.parseExpressionStr('0+1+2+3+4')
  130.    ast2 = parser.parseProgramFile('foo-bar.siml')
  131.    '''
  132.  
  133.     noTreeModification = 0
  134.     '''
  135.    Define how much the parse result is modified, for easier debuging.
  136.    0: normal operation. Compilation does not work otherwise.
  137.    1: Do not modify parse result (from pyParsing library).
  138.  
  139.    ParseResult objects are printed as nested lists: ['1', '+', ['2', '*', '3']]
  140.    '''
  141.  
  142.     keywords = set([])
  143.     '''
  144.    List of all keywords (filled by _defineLanguageSyntax() and defineKeyword).
  145.    '''
  146.  
  147.  
  148.     def __init__(self):
  149.         object.__init__(self)
  150.         self._parser = None
  151.         '''The parser object for the whole program (from pyParsing).'''
  152.         self._expressionParser = None
  153.         '''The parser for expressions'''
  154.         #self._lastStmtLocator = StoreLoc(self)
  155.         self._locLastStmt = TextLocation()
  156.         '''Object to remember location of last parsed statement; for
  157.           error message creation.'''
  158.         self.progFileName = None
  159.         '''Name of SIML program file, that will be parsed'''
  160.         self.inputString = None
  161.         '''String that will be parsed'''
  162.         self._builtInFunc = {}
  163.         '''Names of built in functions and some info about them.
  164.           Format: {'function_name':number_of_function_arguments}
  165.           Example: {'sin':1, 'max':2}'''
  166.         #Create parser objects
  167.         self._defineLanguageSyntax()
  168.  
  169.  
  170.     def defineKeyword(self, inString):
  171.         '''
  172.        Store keyword (in ParseStage.keywords) and create parser for it.
  173.        Use this function (in _defineLanguageSyntax(...)) instead of using the
  174.        Keyword class directly.
  175.        '''
  176.         ParseStage.keywords.add(inString)
  177.         return Keyword(inString)
  178.  
  179.  
  180.     def createTextLocation(self, atChar):
  181.         '''Create a text location object at the given char'''
  182.         return TextLocation(atChar, self.inputString, self.progFileName)
  183.  
  184.  
  185. #------------- Parse Actions -------------------------------------------------*
  186.     def _actionDebug(self, str, loc, toks):
  187.         '''Parse action for debugging.'''
  188.         print '------debug action'
  189.         print str
  190.         print loc
  191.         print toks
  192.         print '-------------------'
  193.         return None
  194.  
  195.  
  196.     def _actionCheckIdentifier(self, str, loc, toks):
  197.         '''
  198.        Tests wether an identifier is legal.
  199.        If the identifier is equal to any keyword the parse action raises
  200.        an exception.
  201.        Does not change any parse results
  202.  
  203.        tokList is structured like this: ['a1']
  204.        '''
  205.         #
  206.         tokList = toks.asList() #asList() this time ads *no* extra pair of brackets
  207.         identier = tokList[0]
  208.         if identier in ParseStage.keywords:
  209.             #print 'found keyword', toks[0], 'at loc: ', loc
  210.             raise ParseException(str, loc,
  211.                                  'Keyword can not be used as an identifier: ' + identier)
  212.             #raise ParseFatalException(
  213.             #    str, loc, 'Identifier same as keyword: %s' % toks[0] )
  214. #            raise UserException(
  215. #                'Keyword can not be used as an identifier: ' + identier,
  216. #                 self.createTextLocation(loc))
  217.  
  218.  
  219.     def _actionStoreStmtLoc(self, str, loc, toks):
  220.         '''
  221.        Remember location of last parsed statement. Useful for error
  222.        error message creation, since the locations of pyparsing's
  223.        syntax errors are frequently quit off.
  224.        '''
  225.         self._locLastStmt = self.createTextLocation(loc)
  226.  
  227.  
  228.     def _actionBuiltInValue(self, str, loc, toks):
  229.         '''
  230.        Create AST node for a built in value: pi, time
  231.        tokList has the following structure:
  232.        [<identifier>]
  233.        '''
  234.         if ParseStage.noTreeModification:
  235.             return None #No parse result modifications for debugging
  236.         tokList = toks.asList()[0] #asList() ads an extra pair of brackets
  237.         #create AST node
  238.         nCurr = NodeBuiltInVal()
  239.         nCurr.loc = self.createTextLocation(loc) #Store position
  240.         nCurr.dat = tokList #Store the built in value's name
  241.         return nCurr
  242.  
  243.  
  244.     def _actionNumber(self, str, loc, toks):
  245.         '''
  246.        Create node for a number: 5.23
  247.        tokList has the following structure:
  248.        [<number>]
  249.        '''
  250.         if ParseStage.noTreeModification:
  251.             return None #No parse result modifications for debugging
  252.         tokList = toks.asList()[0] #asList() ads an extra pair of brackets
  253.         nCurr = NodeNum()
  254.         nCurr.loc = self.createTextLocation(loc) #Store position
  255.         nCurr.dat = tokList[0] #Store the number
  256.         return nCurr
  257.  
  258.  
  259.     def _actionString(self, str, loc, toks):
  260.         '''
  261.        Create node for a string: 'qwert'
  262.        tokList has the following structure:
  263.        [<string>]
  264.        '''
  265.         if ParseStage.noTreeModification:
  266.             return None #No parse result modifications for debugging
  267.         tokList = toks.asList()[0] #asList() ads an extra pair of brackets
  268.         nCurr = NodeString()
  269.         nCurr.loc = self.createTextLocation(loc) #Store position
  270.         #nCurr.dat = tokList #Store the string
  271.         nCurr.dat = tokList[1:-1] #Store the string; remove quotes
  272.         return nCurr
  273.  
  274.  
  275.     def _actionBuiltInFunction(self, str, loc, toks):
  276.         '''
  277.        Create node for function call: sin(2.1)
  278.  
  279.        Definition:
  280.        funcCall = Group(builtInFuncName         .setResultsName('funcName')
  281.                         + '(' + expressionList  .setResultsName('arguments')
  282.                         + ')' )                 .setParseAction(self._actionBuiltInFunction) \
  283.                                                 .setName('funcCall')#.setDebug(True)
  284.        '''
  285.         if ParseStage.noTreeModification:
  286.             return None #No parse result modifications for debugging
  287.         #tokList = toks.asList()[0] #there always seems to be
  288.         toks = toks[0]             #an extra pair of brackets
  289.         nCurr = NodeBuiltInFuncCall()
  290.         nCurr.loc = self.createTextLocation(loc) #Store position
  291.         nCurr.dat = toks.funcName #function dentifier
  292.         nCurr.kids = toks.arguments.asList() #child expression(s)
  293.         #check if number of function arguments is correct
  294.         if len(nCurr.kids) != self._builtInFunc[nCurr.dat]:
  295.             msg = ('Illegal number of function arguments. \n' +
  296.                    'Function: %s, required number of arguments: %d, ' +
  297.                    'given arguments: %d.') % \
  298.                   (nCurr.dat, self._builtInFunc[nCurr.dat], len(nCurr.kids))
  299.             raise UserException(msg, self.createTextLocation(loc))
  300.         return nCurr
  301.  
  302.  
  303.     def _actionParenthesesPair(self, str, loc, toks):
  304.         '''
  305.        Create node for a pair of parentheses that enclose an expression: (...)
  306.        tokList has the following structure:
  307.        ['(', <expression>, ')']
  308.        '''
  309.         if ParseStage.noTreeModification:
  310.             return None #No parse result modifications for debugging
  311.         tokList = toks.asList()[0] #asList() ads an extra pair of brackets
  312.         nCurr = NodeParentheses()
  313.         nCurr.loc = self.createTextLocation(loc) #Store position
  314.         nCurr.kids = [tokList[1]] #store child expression
  315.         return nCurr
  316.  
  317.  
  318.     def _actionPrefixOp(self, str, loc, toks):
  319.         '''
  320.        Create node for math prefix operators: -
  321.        tokList has the following structure:
  322.        [<operator>, <expression_l>]
  323.        '''
  324.         if ParseStage.noTreeModification:
  325.             return None #No parse result modifications for debugging
  326.         tokList = toks.asList()[0] #asList() ads an extra pair of brackets
  327.         nCurr = NodeOpPrefix1()
  328.         nCurr.loc = self.createTextLocation(loc) #Store position
  329.         nCurr.operator = tokList[0]  #Store operator
  330.         nCurr.kids=[tokList[1]] #Store child tree
  331.         return nCurr
  332.  
  333.  
  334.     def _actionInfixOp(self, str, loc, toks):
  335.         '''
  336.        Create node for math infix operators: + - * / ^
  337.        tokList has the following structure:
  338.        [<expression_l>, <operator>, <expression_r>]
  339.        '''
  340.         if ParseStage.noTreeModification:
  341.             return None #No parse result modifications for debugging
  342.         tokList = toks.asList()[0] #asList() ads an extra pair of brackets
  343.         nCurr = NodeOpInfix2()
  344.         nCurr.loc = self.createTextLocation(loc) #Store position
  345.         #create children and store operator
  346.         lhsTree = tokList[0]   #child lhs
  347.         nCurr.operator = tokList[1] #operator
  348.         rhsTree = tokList[2]   #child rhs
  349.         nCurr.kids=[lhsTree, rhsTree]
  350.         return nCurr
  351.  
  352.  
  353.     def _actionAttributeAccess(self, str, loc, toks):
  354.         '''
  355.        Create node for access to a variable or parameter: bb.ccc.dd
  356.        tokList has the following structure:
  357.        [<part1>, <part2>, <part3>, ...]
  358.        '''
  359.         if ParseStage.noTreeModification:
  360.             return None #No parse result modifications for debugging
  361.         tokList = toks.asList()[0] #asList() ads an extra pair of brackets
  362.         nCurr = NodeAttrAccess()
  363.         nCurr.loc = self.createTextLocation(loc) #Store position
  364.         #Look if there is a '$' that indicates time derivatives
  365.         if tokList[0] == '$':
  366.             nCurr.deriv = ('time',)
  367.             del tokList[0]
  368.         #The remaining tokens are the dot separated name
  369.         nCurr.attrName = DotName(tokList)
  370.         return nCurr
  371.  
  372.  
  373.     def _actionIfStatement(self, str, loc, toks):
  374.         '''
  375.        Create node for if ... : ... else: ... statement.
  376.        BNF:
  377.        ifStatement = Group(kw('if') + boolExpression + ':'
  378.                            + statementList
  379.                            + Optional(kw('else') +':' + statementList)
  380.                            + kw('end'))
  381.        '''
  382.         if ParseStage.noTreeModification:
  383.             return None #No parse result modifications for debugging
  384.         tokList = toks.asList()[0] #asList() ads an extra pair of brackets
  385.         nCurr = NodeIfStmt()
  386.         nCurr.loc = self.createTextLocation(loc) #Store position
  387.         #if ... then ... end
  388.         if len(tokList) == 5:
  389.             condition = tokList[1]
  390.             thenStmts = tokList[3]
  391.             nCurr.kids=[condition, thenStmts]
  392.         #if ... then ... else ... end
  393.         elif len(tokList) == 8:
  394.             condition = tokList[1]
  395.             thenStmts = tokList[3]
  396.             elseStmts = tokList[6]
  397.             nCurr.kids=[condition, thenStmts, elseStmts]
  398.         else:
  399.             raise ParseActionException('Broken >if< statement! loc: ' + str(nCurr.loc))
  400.         return nCurr
  401.  
  402.  
  403.     def _actionAssignment(self, str, loc, toks):
  404.         '''
  405.        Create node for assignment: a = 2*b
  406.        BNF:
  407.        assignment = Group(valAccess + '=' + expression + ';')
  408.        '''
  409.         if ParseStage.noTreeModification:
  410.             return None #No parse result modifications for debugging
  411.         tokList = toks.asList()[0] #asList() ads an extra pair of brackets
  412.         nCurr = NodeAssignment()
  413.         nCurr.loc = self.createTextLocation(loc) #Store position
  414.         #create children and store operator
  415.         lhsTree = tokList[0]   #child lhs
  416.         nCurr.operator = tokList[1] #operator
  417.         rhsTree = tokList[2]   #child rhs
  418.         nCurr.kids=[lhsTree, rhsTree]
  419.         return nCurr
  420.  
  421.  
  422.     def _actionFuncExecute(self, str, loc, toks):
  423.         '''
  424.        Create node for execution of a function (insertion of the code):
  425.            call foo.init()
  426.        BNF:
  427.        funcExecute = Group(kw('call')
  428.                             + dotIdentifier    .setResultsName('funcName')
  429.                             + '(' + ')'
  430.                             + ';')             .setParseAction(self._createBlockExecute)\
  431.        '''
  432.         if ParseStage.noTreeModification:
  433.             return None #No parse result modifications for debuging
  434.         #tokList = toks.asList()[0] #there always seems to be
  435.         toks = toks[0]             #an extra pair of brackets
  436.         nCurr = NodeFuncExecute()
  437.         nCurr.loc = self.createTextLocation(loc) #Store position
  438.         nCurr.funcName = DotName(toks.funcName)    #full (dotted) function name
  439.         return nCurr
  440.  
  441.  
  442.     def _actionPrintStmt(self, str, loc, toks):
  443.         '''
  444.        Create node for print statement:
  445.            print 'hello', foo.x
  446.        BNF:
  447.        printStmt = Group(kw('print') + exprList  .setResultsName('argList')
  448.                          + Optional(',')         .setResultsName('trailComma')
  449.                          + ';')                  .setParseAction(self._actionPrintStmt)\
  450.        '''
  451.         if ParseStage.noTreeModification:
  452.             return None #No parse result modifications for debugging
  453.         #tokList = toks.asList()[0] #there always seems to be
  454.         toks = toks[0]             #an extra pair of brackets
  455.         nCurr = NodePrintStmt()
  456.         nCurr.loc = self.createTextLocation(loc) #Store position
  457.         nCurr.kids = toks.argList.asList()
  458.         if toks.trailComma:
  459.             nCurr.newline = False
  460.         return nCurr
  461.  
  462.  
  463.     def _actionGraphStmt(self, str, loc, toks):
  464.         '''
  465.        Create node for graph statement:
  466.            graph foo.x, foo.p
  467.        BNF:
  468.        graphStmt = Group(kw('graph') + exprList  .setResultsName('argList')
  469.                          + ';')                  .setParseAction(self._actionDebug)\
  470.        '''
  471.         if ParseStage.noTreeModification:
  472.             return None #No parse result modifications for debugging
  473.         #tokList = toks.asList()[0] #there always seems to be
  474.         toks = toks[0]             #an extra pair of brackets
  475.         nCurr = NodeGraphStmt()
  476.         nCurr.loc = self.createTextLocation(loc) #Store position
  477.         nCurr.kids = toks.argList.asList()
  478.         return nCurr
  479.  
  480.  
  481.     def _actionStoreStmt(self, str, loc, toks):
  482.         '''
  483.        Create node for graph statement:
  484.            graph foo.x, foo.p
  485.        BNF:
  486.        graphStmt = Group(kw('graph') + exprList  .setResultsName('argList')
  487.                          + ';')                  .setParseAction(self._actionDebug)\
  488.        '''
  489.         if ParseStage.noTreeModification:
  490.             return None #No parse result modifications for debugging
  491.         #tokList = toks.asList()[0] #there always seems to be
  492.         toks = toks[0]             #an extra pair of brackets
  493.         nCurr = NodeStoreStmt()
  494.         nCurr.loc = self.createTextLocation(loc) #Store position
  495.         nCurr.kids = toks.argList.asList()
  496.         return nCurr
  497.  
  498.  
  499.     def _actionStatementList(self, str, loc, toks):
  500.         '''
  501.        Create node for list of statements: a=1; b=2; ...
  502.        BNF:
  503.        statementList << Group(OneOrMore(statement))
  504.        '''
  505.         if ParseStage.noTreeModification:
  506.             return None #No parse result modifications for debugging
  507.         tokList = toks.asList()[0] #asList() ads an extra pair of brackets
  508.         nCurr = NodeStmtList()
  509.         nCurr.loc = self.createTextLocation(loc) #Store position
  510.         #create children - each child is a statement
  511.         for tok in tokList:
  512.             nCurr.kids.append(tok)
  513.         return nCurr
  514.  
  515.  
  516.     def _actionAttrDefinition(self, str, loc, toks):
  517.         '''
  518.        Create node for defining parameter, variable or submodel:
  519.            'data foo, bar: baz.boo parameter;
  520.        One such statement can define multiple parmeters; and an individual
  521.        NodeAttrDef is created for each. They are returned together inside a
  522.        list node of type NodeStmtList.
  523.        BNF:
  524.        attrNameList = Group( identifier +
  525.                                ZeroOrMore(',' + identifier))
  526.        attrRole = kw('parameter') | kw('variable')
  527.        #parse 'data foo, bar: baz.boo parameter;
  528.        attributeDef = Group(kw('data')
  529.                             + attrNameList                          .setResultsName('attrNameList')
  530.                             + ':' + dotIdentifier                   .setResultsName('className')
  531.                             + Optional(attrRole)                    .setResultsName('attrRole')
  532.                             + ';')
  533.        '''
  534.         if ParseStage.noTreeModification:
  535.             return None #No parse result modifications for debugging
  536.         #tokList = toks.asList()[0] #there always seems to be
  537.         toks = toks[0]             #an extra pair of brackets
  538.         #multiple attributes can be defined in a single statement
  539.         #Create a node for each of them and put them into a statement list
  540.         attrDefList = NodeStmtList(loc=self.createTextLocation(loc))
  541.         nameList = toks.attrNameList.asList()
  542.         for name in nameList:
  543.             if name in self.keywords:
  544.                 errMsg = 'Keyword can not be used as an identifier: ' + name
  545.                 raise ParseFatalException(str, loc, errMsg)
  546.             attrDef = NodeAttrDef(loc=self.createTextLocation(loc))
  547.             attrDef.attrName = DotName(name) #store attribute name
  548.             attrDef.className = DotName(toks.className.asList())  #store class name
  549.             #store the role
  550.             if toks.attrRole == 'parameter':
  551.                 attrDef.role = RoleParameter
  552.             else:
  553.                 #we do not know if variable or parameter; submodels will be
  554.                 #labled variables even though these categories don't apply
  555.                 #to them.
  556.                 attrDef.role = RoleVariable
  557.             attrDefList.appendChild(attrDef)
  558.         #Special case: only one attribute defined
  559.         if len(attrDefList) == 1:
  560.             return attrDefList[0] #take it out of the list and return it
  561.         else:
  562.             return attrDefList #return list with multiple definitions
  563.  
  564.  
  565.     def _actionFuncDefinition(self, str, loc, toks):
  566.         '''
  567.        Create node for definition of a (member) function:
  568.            func init(): a=1; end
  569.        BNF:
  570.        memberFuncDef = Group(kw('func')
  571.                              + identifier                           .setResultsName('funcName')
  572.                              + '(' + ')' + ':'
  573.                              + ZeroOrMore(statement)                .setResultsName('funcBody', True)
  574.                              + kw('end'))        '''
  575.         if ParseStage.noTreeModification:
  576.             return None #No parse result modifications for debuging
  577.         #tokList = toks.asList()[0] #there always seems to be
  578.         toks = toks[0]             #an extra pair of brackets
  579.         nCurr = NodeFuncDef()
  580.         nCurr.loc = self.createTextLocation(loc) #Store position
  581.         #store name of block
  582.         nCurr.name = DotName(toks.funcName)
  583.         #create children - each child is a statement
  584.         statements = []
  585.         if len(toks.funcBody) > 0:
  586.             statements = toks.funcBody.asList()[0]
  587.         for stmt1 in statements:
  588.             nCurr.appendChild(stmt1)
  589.         return nCurr
  590.  
  591.  
  592.     def _actionClassDef(self, str, loc, toks):
  593.         '''
  594.        Create node for definition of a class:
  595.            class foo(Model): ... end
  596.        BNF:
  597.        classDef = Group(kw('class')
  598.                         + identifier                 .setResultsName('className')
  599.                         + '(' + dotIdentifier        .setResultsName('superName')
  600.                         + ')' + ':'
  601.                         + OneOrMore(attributeDef)    .setResultsName('attributeDef', True)
  602.                         + ZeroOrMore(memberFuncDef)  .setResultsName('memberFuncDef', True)
  603.                         + kw('end'))
  604.        '''
  605.         if ParseStage.noTreeModification:
  606.             return None #No parse result modifications for debugging
  607.         #tokList = toks.asList()[0] #there always seems to be
  608.         toks = toks[0]             #an extra pair of brackets
  609.         nCurr = NodeClassDef()
  610.         nCurr.loc = self.createTextLocation(loc) #Store position
  611.         #store class name and name of super class
  612.         nCurr.className = DotName(toks.className)
  613.         nCurr.superName = DotName(toks.superName)
  614.         #create children (may or may not be present):  data, functions
  615.         data, funcs = [], [] #special cases for empty sections necessary
  616.         if len(toks.attributeDef) > 0:
  617.             data = toks.attributeDef.asList()[0]
  618.         if len(toks.memberFuncDef) > 0:
  619.             funcs =  toks.memberFuncDef.asList()[0]
  620.         for stmt1 in data + funcs:
  621.             nCurr.appendChild(stmt1)
  622.         return nCurr
  623.  
  624.  
  625.     def _actionProgram(self, str, loc, toks):
  626.         '''
  627.        Create the root node of a program.
  628.        BNF:
  629.        program = Group(OneOrMore(classDef))
  630.        '''
  631.         if ParseStage.noTreeModification:
  632.             return None #No parse result modifications for debugging
  633.         tokList = toks.asList()[0] #asList() ads an extra pair of brackets
  634.         nCurr = NodeProgram()
  635.         nCurr.loc = self.createTextLocation(loc) #Store position
  636.         #create children - each child is a class
  637.         for tok in tokList:
  638.             nCurr.kids.append(tok)
  639.         return nCurr
  640.  
  641.  
  642. #------------------- BNF --------------------------------------------------------
  643.     def _defineLanguageSyntax(self):
  644.         '''
  645.        Here is Siml's BNF
  646.        Creates the objects of the pyParsing library,
  647.        that do all the work.
  648.        '''
  649.         #define short alias so they don't clutter the text
  650.         kw = self.defineKeyword # Usage: test = kw('variable')
  651.         L = Literal # Usage: L('+')
  652.  
  653.         #Values that are built into the language
  654.         builtInValue = (kw('pi') | kw('time'))                      .setParseAction(self._actionBuiltInValue)\
  655.                                                                     .setName('builtInValue')#.setDebug(True)
  656.  
  657.         #Functions that are built into the language
  658.         #Dict: {'function_name':number_of_function_arguments}
  659.         self._builtInFunc = {'sin':1, 'cos':1, 'tan':1,
  660.                              'sqrt':1, 'exp':1, 'log':1,
  661.                              'min':2, 'max':2}
  662.         builtInFuncName = MatchFirst(
  663.             [kw(funcName)
  664.              for funcName in self._builtInFunc.keys()])             .setName('builtInFuncName')#.setDebug(True)
  665.  
  666.         #Integer (unsigned).
  667.         uInteger = Word(nums)                                       .setName('uInteger')#.setDebug(True)
  668.         #Floating point number (unsigned).
  669.         eE = CaselessLiteral( 'E' )
  670.         uNumber = Group( Combine(
  671.                     uInteger +
  672.                     Optional('.' + Optional(uInteger)) +
  673.                     Optional(eE + Word('+-'+nums, nums))))          .setParseAction(self._actionNumber)\
  674.                                                                     .setName('uNumber')#.setDebug(True)
  675.         #string
  676. #        stringConst = QuotedString(quoteChar='\'', escChar='\\')    .setParseAction(self._actionString)\
  677. #                                                                    .setName('string')#.setDebug(True)
  678.         stringConst = sglQuotedString                               .setParseAction(self._actionString)\
  679.                                                                     .setName('string')#.setDebug(True)
  680.  
  681.         # .............. Mathematical expression .............................................................
  682.         #'Forward declarations' for recursive rules
  683.         expressionList = Forward() #For built in functions TODO: this should be a list of bool expressions
  684.         boolExpression = Forward()
  685.         expression = Forward()
  686.         term =  Forward()
  687.         factor = Forward()
  688.         signedAtom = Forward()
  689.         valAccess = Forward() #For PDE: may also contain expressions for slices: a.b.c(2.5:3.5)
  690.  
  691.         #Basic building blocks of mathematical expressions e.g.: (1, x, e,
  692.         #sin(2*a), (a+2), a.b.c(2.5:3.5))
  693.         #Function call, parenthesis and memory access can however contain
  694.         #expressions.
  695.         #TODO: funcCall should be unified with with call to member function
  696.         funcCall = Group(builtInFuncName                            .setResultsName('funcName')
  697.                          + '(' + expressionList                     .setResultsName('arguments')
  698.                          + ')' )                                    .setParseAction(self._actionBuiltInFunction) \
  699.                                                                     .setName('funcCall')#.setDebug(True)
  700.         parentheses = Group('(' + expression + ')')                 .setParseAction(self._actionParenthesesPair) \
  701.                                                                     .setName('parentheses')#.setDebug(True)
  702.         atom = ( uNumber | stringConst | builtInValue |
  703.                  funcCall | valAccess | parentheses     )           .setName('atom')#.setDebug(True)
  704.  
  705.         #The basic mathematical operations: -a+b*c^d.
  706.         #All operations have right-to-left associativity; although this is only
  707.         #required for exponentiation. Precedence decreases towards the bottom.
  708.         #Unary minus: -a, not a;
  709.         negop = '-' | kw('not')
  710.         unaryMinus = Group(negop + signedAtom)          .setParseAction(self._actionPrefixOp) \
  711.                                                         .setName('unaryMinus')#.setDebug(True)
  712.         signedAtom << (atom | unaryMinus)               .setName('signedAtom')#.setDebug(True)
  713.  
  714.         #Exponentiation: a^b;
  715.         factor1 = signedAtom                            .setName('factor1')#.setDebug(True)
  716.         factor2 = Group(signedAtom + '**' + factor)     .setParseAction(self._actionInfixOp) \
  717.                                                         .setName('factor2')#.setDebug(True)
  718.         factor << (factor2 | factor1)                   .setName('factor')#.setDebug(True)
  719.  
  720.         #multiplicative operations: a*b; a/b
  721.         multop = L('*') | L('/') | L('and')
  722.         term1 = factor                                  .setName('term1')#.setDebug(True)
  723.         term2 = Group(factor + multop + term)           .setParseAction(self._actionInfixOp) \
  724.                                                         .setName('term2')#.setDebug(True)
  725.         term << (term2 | term1)                         .setName('term')#.setDebug(True)
  726.  
  727.         #additive operations: a+b; a-b
  728.         addop  = L('+') | L('-') | L('or')
  729.         expression1 = term                              .setName('expression1')#.setDebug(True)
  730.         expression2 = Group(term + addop + expression)  .setParseAction(self._actionInfixOp) \
  731.                                                         .setName('expression2')#.setDebug(True)
  732.         expression << (expression2 | expression1)       .setName('expression')#.setDebug(True)
  733.  
  734.         #Relational operators : <, >, ==, ...
  735.         relop = L('<') | L('>') | L('<=') | L('>=') | L('==') | L('!=')
  736.         boolExpr1 = expression
  737.         boolExpr2 = Group(expression + relop + boolExpression)  .setParseAction(self._actionInfixOp) \
  738.                                                                 .setName('boolExpr2')#.setDebug(True)
  739.         boolExpression << (boolExpr2 | boolExpr1)               .setName('boolExpression')#.setDebug(True)
  740.  
  741.         #expression list - sparse: 2, foo.bar, 3*sin(baz)
  742.         commaSup = Literal(',').suppress()
  743.         expressionList << Group(boolExpression
  744.                                 + ZeroOrMore(commaSup + boolExpression)).setName('exprList')
  745.         #................ End mathematical expression ................................................---
  746.  
  747.         #................ Identifiers ...................................................................
  748.         identifier = Word(alphas+'_', alphanums+'_')            .setName('identifier')#.setDebug(True)
  749.         #Use this when defining new objects. The new identifier is checked if it is not a keyword
  750.         newIdentifier = identifier.copy()                       .setParseAction(self._actionCheckIdentifier)
  751.         #Compound identifiers for variables or parameters 'aaa.bbb'.
  752.         dotSup = Literal('.').suppress()
  753.         dotIdentifier = Group(identifier +
  754.                               ZeroOrMore(dotSup + identifier))  .setName('dotIdentifier')#.setDebug(True)
  755.         #Method to access a stored value: dotted name ('a.b.c'),
  756.         # with optional differentiation operator ('$a.b.c'),
  757.         # and optional partial access ('a.b.c[2:5]'). (partial access is currently not implemented)
  758.         valAccess << Group( Optional('$') +
  759.                             identifier +
  760.                             ZeroOrMore(dotSup + identifier) )   .setParseAction(self._actionAttributeAccess) \
  761.                                                                 .setName('valAccess')#.setDebug(True)
  762.  
  763. #------------------- Statements ---------------------------------------------------------------
  764.         statementList = Forward()
  765.         #Flow control - if then else
  766.         ifStatement = Group(kw('if')
  767.                             + ErrStop( boolExpression + ':'
  768.                                        + statementList
  769.                                        + Optional(kw('else')
  770.                                                   + ErrStop(':' + statementList))
  771.                                        + kw('end'))
  772.                             )                                        .setParseAction(self._actionIfStatement)\
  773.                                                                      .setName('ifStatement')#.setDebug(True)
  774.         #compute expression and assign to value
  775.         assignment = Group(valAccess + '='
  776.                            + ErrStop(boolExpression + ';')           .setErrMsgStart('Assignment statement: ')
  777.                            )                                         .setParseAction(self._actionAssignment)\
  778.                                                                      .setName('assignment')#.setDebug(True)
  779.         #execute a block - insert code of a child model
  780.         #function arguments are currently missing
  781.         #TODO: Unify with builtin functions.
  782.         funcExecute = Group(kw('call')
  783.                              + ErrStop(dotIdentifier                 .setResultsName('funcName')
  784.                                        + '(' + ')'
  785.                                        + ';')                        .setErrMsgStart('Call statement: ')
  786.                              )                                       .setParseAction(self._actionFuncExecute)\
  787.                                                                      .setName('blockExecute')#.setDebug(True)
  788.         #print something to stdout
  789.         printStmt = Group(kw('print')
  790.                           + ErrStop(expressionList                   .setResultsName('argList')
  791.                                     + Optional(',')                  .setResultsName('trailComma')
  792.                                     + ';')                           .setErrMsgStart('Print statement: ')
  793.                           )                                          .setParseAction(self._actionPrintStmt)\
  794.                                                                      .setName('printStmt')#.setDebug(True)
  795.         #show graphs
  796.         graphStmt = Group(kw('graph')
  797.                           + ErrStop(expressionList                   .setResultsName('argList')
  798.                                     + ';')                           .setErrMsgStart('Graph statement: ')
  799.                           )                                          .setParseAction(self._actionGraphStmt)\
  800.                                                                      .setName('graphStmt')#.setDebug(True)
  801.         #store to disk
  802.         storeStmt = Group(kw('save')
  803.                           + ErrStop(Group(Optional(stringConst))     .setResultsName('argList')
  804.                                     + ';')                           .setErrMsgStart('Save statement: ')
  805.                           )                                          .setParseAction(self._actionStoreStmt)\
  806.                                                                      .setName('storeStmt')#.setDebug(True)
  807.  
  808.         statement = (storeStmt | graphStmt | printStmt |
  809.                      funcExecute | ifStatement | assignment)         .setParseAction(self._actionStoreStmtLoc)\
  810.                                                                      .setName('statement')#.setDebug(True)
  811.         statementList << Group(OneOrMore(statement))                 .setParseAction(self._actionStatementList)\
  812.                                                                      .setName('statementList')#.setDebug(True)
  813.  
  814. #---------- Define new objects ---------------------------------------------------------------------*
  815.         #define parameters, variables and submodels
  816.         #commaSup = Literal(',').suppress()
  817.         #parse: 'foo, bar, baz
  818.         #Identifiers must not be keywords, check is done in _actionAttrDefinition
  819.         newAttrList = Group(identifier
  820.                             + ZeroOrMore(commaSup + identifier))     .setName('attrNameList')
  821.         attrRole = kw('parameter') | kw('variable')
  822.         #parse 'data foo, bar: baz.boo parameter;
  823.         attributeDef = Group(kw('data')
  824.                              + ErrStop(newAttrList                   .setResultsName('attrNameList')
  825.                                        + ':' + dotIdentifier         .setResultsName('className')
  826.                                        + Optional(attrRole)          .setResultsName('attrRole')
  827.                                        + ';')                        .setErrMsgStart('Wrong syntax in data definition. ')
  828.                              )                                       .setParseAction(self._actionAttrDefinition)\
  829.                                                                      .setName('attributeDef')#.setDebug(True)
  830.         #define member function (method)
  831.         #TODO: function arguments are currently missing
  832.         #TODO: unify with built in functions
  833.         funcDef = Group(kw('func')
  834.                         + ErrStop(newIdentifier                      .setResultsName('funcName')
  835.                                   + '(' + ')' + ':'
  836.                                   + ZeroOrMore(statement)            .setResultsName('funcBody', True)
  837.                                   + kw('end'))                       .setErrMsgStart('Wrong syntax in function definition. ')
  838.                         )                                            .setParseAction(self._actionFuncDefinition)\
  839.                                                                      .setName('memberFuncDef')#.setDebug(True)
  840.         #definition of a class (process, model, type?)
  841.         classDef = Group(kw('class')
  842.                          + ErrStop(newIdentifier                     .setResultsName('className')
  843.                                    + '(' + dotIdentifier             .setResultsName('superName')
  844.                                    + ')' + ':'
  845.                                    + ZeroOrMore(attributeDef)        .setResultsName('attributeDef', True)
  846.                                    + ZeroOrMore(funcDef)             .setResultsName('memberFuncDef', True)
  847.                                    + kw('end'))                      .setErrMsgStart('Wrong syntax in class definition. ')
  848.                          )                                           .setParseAction(self._actionClassDef)\
  849.                                                                      .setName('classDef')#.setDebug(True)
  850.  
  851.         program = (Group(OneOrMore(classDef)) + StringEnd())         .setParseAction(self._actionProgram)\
  852.                                                                      .setName('program')#.setDebug(True)
  853.  
  854.         #................ End of language definition ..................................................
  855.  
  856.         #determine start symbol
  857.         startSymbol = program
  858.         #set up comments
  859.         singleLineCommentCpp = '//' + restOfLine
  860.         singleLineCommentPy = '#' + restOfLine
  861.         startSymbol.ignore(singleLineCommentCpp)
  862.         startSymbol.ignore(singleLineCommentPy)
  863.         #no tab expansion
  864.         startSymbol.parseWithTabs()
  865.         #store parsers
  866.         self._parser = startSymbol
  867.         self._expressionParser = boolExpression
  868.  
  869.  
  870.     def parseExpressionStr(self, inString):
  871.         '''Parse a single expression. Example: 2*a+b'''
  872.         self.inputString = inString
  873.         return self._expressionParser.parseString(inString).asList()[0]
  874.  
  875.  
  876.     def parseProgramStr(self, inString):
  877.         '''Parse a whole program. The program is entered as a string.'''
  878.         self.inputString = inString
  879.         result = self._parser.parseString(inString).asList()[0]
  880.         return result
  881.  
  882.  
  883.     def parseProgramFile(self, fileName):
  884.         '''Parse a whole program. The program's file name is supplied.'''
  885.         self.progFileName = os.path.abspath(fileName)
  886.         #open and read the file
  887.         try:
  888.             inputFile = open(self.progFileName, 'r')
  889.             inputFileContents = inputFile.read()
  890.             inputFile.close()
  891.         except IOError, theError:
  892.             message = 'Could not read input file.\n' + str(theError)
  893.             raise UserException(message, None)
  894.         #parse the program
  895.         try:
  896.             astTree = self.parseProgramStr(inputFileContents)
  897.         except (ParseException, ParseFatalException), theError:
  898.             #add additional information to the pyparsing exceptions.
  899.             msgPyParsing = str(theError) + '\n'
  900.             #see if there is the loc of a successfully parsed statement
  901.             if self._locLastStmt.isValid():
  902.                 msgLastStmt = 'Last parsed statement was: \n' \
  903.                               + str(self._locLastStmt)
  904.             else:
  905.                 msgLastStmt = ''
  906.             #make UserException that will be visible to the user
  907.             loc =  TextLocation(theError.loc, theError.pstr, self.progFileName)
  908.             raise UserException(msgPyParsing + msgLastStmt, loc)
  909.         return astTree
  910.  
  911.  
  912.  
  913.  
  914.  
  915. def doTests():
  916.     '''Perform various tests.'''
  917.  
  918. #------------ testProg1 -----------------------
  919.     testProg1 = (
  920. '''
  921. class Test(Model):
  922.    data V, h: Real;
  923.    data A_bott, A_o, mu, q, g: Real parameter;
  924.  
  925.    func dynamic():
  926.        h = V/A_bott;
  927.        $V = q - mu*A_o*sqrt(2*g*h);
  928.        print 'h: ', h,;
  929.    end
  930.  
  931.    func init():
  932.        V = 0;
  933.        A_bott = 1; A_o = 0.02; mu = 0.55;
  934.        q = 0.05;
  935.    end
  936. end
  937.  
  938. class RunTest(Process):
  939.    data g: Real parameter;
  940.    data test: Test;
  941.  
  942.    func dynamic():
  943.        call test.dynamic();
  944.    end
  945.  
  946.    func init():
  947.        g = 9.81;
  948.        call test.init();
  949.        solutionParameters.simulationTime = 100;
  950.        solutionParameters.reportingInterval = 1;
  951.    end
  952.    func final():
  953.        #store;
  954.        graph test.V, test.h;
  955.        print 'Simulation finished successfully.';
  956.    end
  957. end
  958. ''' )
  959.  
  960.  
  961.     #test the parser ----------------------------------------------------------------------
  962.     flagTestParser = True
  963.     if flagTestParser:
  964.         parser = ParseStage()
  965.         #ParseStage.debugSyntax = 1
  966.         #ParseStage.debugSyntax = 2
  967.         #print parser.parseProgram(testProg2)
  968.  
  969.         print parser.parseProgramStr(testProg1)
  970.         #print parser.parseProgram('if a==0 then b=-1; else b=2+3+4; a=1; end')
  971.         #print parser.parseExpression('0*1*2*3*4').asList()[0]
  972.         #print parser.parseExpression('0^1^2^3^4')
  973.         #print parser.parseExpression('0+1*2+3+4').asList()[0]
  974.         #print parser.parseExpression('0*1^2*3*4')
  975.         #print parser.parseExpression('0+(1+2)+3+4')
  976.         #print parser.parseExpression('-0+1+--2*-3--4')
  977.         #print parser.parseExpression('-aa.a+bb.b+--cc.c*-dd.d--ee.e+f').asList()[0]
  978.         #print parser.parseExpression('time+0+sin(2+3*4)+5').asList()[0]
  979.         #print parser.parseExpression('0+a1.a2+bb.b1.b2+3+4 #comment')
  980.         #print parser.parseExpression('0.123+1.2e3')
  981.         #parser.parseExpression('0+1*2^3^4+5+6*7+8+9')
  982.  
  983.         print 'keywords:'
  984.         print parser.keywords
  985.  
  986.  
  987. ##    pdb.set_trace()
  988.  
  989.  
  990. if __name__ == '__main__':
  991.     # Self-testing code goes here.
  992.     #TODO: add unit tests
  993.     #TODO: add doctest tests. With doctest tests are embedded in the documentation
  994.  
  995.     doTests()
  996. else:
  997.     # This will be executed in case the
  998.     #    source has been imported as a
  999.     #    module.
  1000.     pass
  1001.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement