~vcs-imports/freeode/trunk

323 by eike
Workaround for new matplotlib bug
1
# -*- coding: utf-8 -*-
235 by eike
put practically all files into a package 'freeode'
2
#***************************************************************************
490 by eike
Updated copyright
3
#    Copyright (C) 2006 - 2009 by Eike Welk                                *
235 by eike
put practically all files into a package 'freeode'
4
#    eike.welk@post.rwth-aachen.de                                         *
5
#                                                                          *
482 by eike
The old Float implementation is replaced by the new Float implementation
6
#    Credits:                                                              *
403 by eike
only cosmetich changes. The introduction of the new expression parser will come soon.
7
#    Inspiration and little bits of text and code came from:               *
8
#     'fourFn.py', 'simpleArith.py' example programs, by Paul McGuire,     *
9
#     the 'Spark' library by John Aycock,                                  *
10
#     and the Python Reference Manual by Guido van Rossum.                 *
352 by eike
minimal changes
11
#    Many thanks for their excellent contributions to publicly available   *
348 by eike
Much better error reporting
12
#    knowledge.                                                            *
352 by eike
minimal changes
13
#                                                                          *
482 by eike
The old Float implementation is replaced by the new Float implementation
14
#                                                                          *
352 by eike
minimal changes
15
#    License: GPL                                                          *
235 by eike
put practically all files into a package 'freeode'
16
#                                                                          *
17
#    This program is free software; you can redistribute it and/or modify  *
18
#    it under the terms of the GNU General Public License as published by  *
19
#    the Free Software Foundation; either version 2 of the License, or     *
20
#    (at your option) any later version.                                   *
21
#                                                                          *
22
#    This program is distributed in the hope that it will be useful,       *
23
#    but WITHOUT ANY WARRANTY; without even the implied warranty of        *
24
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
25
#    GNU General Public License for more details.                          *
26
#                                                                          *
27
#    You should have received a copy of the GNU General Public License     *
28
#    along with this program; if not, write to the                         *
29
#    Free Software Foundation, Inc.,                                       *
30
#    59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
31
#***************************************************************************
353 by eike
change some comments
32
405 by eike
working expression parser is now in SIML
33
"""
235 by eike
put practically all files into a package 'freeode'
34
Parser for the SIML simulation language.
405 by eike
working expression parser is now in SIML
35
"""
235 by eike
put practically all files into a package 'freeode'
36
337 by eike
Minor (comment) changes
37
#TODO: write unit tests that exercise every error message of simlparser.py
38
323 by eike
Workaround for new matplotlib bug
39
from __future__ import division
40
399 by eike
stating to implement new syntax
41
__version__ = "$Revision: $"
42
# $Source$
322 by eike
small progress
43
346 by eike
Added ErrStop class for better syntax error generation.
44
#import debugger
279 by eike
upped version to 0.3.1
45
#import pdb
235 by eike
put practically all files into a package 'freeode'
46
import os
47
#import parser library
519 by eike
Added comparison functions to IFloat.
48
from freeode.third_party.pyparsing import (
428 by eike
Fixing Pyparsing library seems successful.
49
    Literal, CaselessLiteral, Keyword, Word,
351 by eike
corrected some typos
50
    ZeroOrMore, OneOrMore, Forward, nums, alphas, alphanums, restOfLine,
574 by eike
test commit
51
    oneOf, LineEnd, indentedBlock,
410 by eike
Syntax is now converted to '-' and ChMsg; ErrStop is removed.
52
    delimitedList, Suppress, operatorPrecedence, opAssoc,
53
    StringEnd, quotedString, Combine, Group, Optional,
54
    ParseException, ParseFatalException, ParserElement, )
235 by eike
put practically all files into a package 'freeode'
55
#import our own syntax tree classes
56
from freeode.ast import *
57
399 by eike
stating to implement new syntax
58
235 by eike
put practically all files into a package 'freeode'
59
479 by eike
A new workable Float object exists. Really tere are two objects: class and instance. There are Siml methods for all mathematical operators.
60
#Enable a fast parsing mode with caching.
428 by eike
Fixing Pyparsing library seems successful.
61
ParserElement.enablePackrat()
346 by eike
Added ErrStop class for better syntax error generation.
62
410 by eike
Syntax is now converted to '-' and ChMsg; ErrStop is removed.
63
406 by eike
Indented (Python like) grammar seems to work.
64
class ChMsg(object):
405 by eike
working expression parser is now in SIML
65
    '''
66
    Change a parser's error message.
574 by eike
test commit
67
405 by eike
working expression parser is now in SIML
68
    Attach to parser with setFailAction
69
    '''
70
    def __init__(self, prepend=None, append=None):
408 by eike
Indented grammar broken due to bug in pyparsing!
71
        object.__init__(self)
405 by eike
working expression parser is now in SIML
72
        self.prepend_str = prepend
73
        self.append_str = append
574 by eike
test commit
74
405 by eike
working expression parser is now in SIML
75
    def __call__(self, s,loc,expr,err):
76
        '''Change error message. Called by parser when it fails.
574 by eike
test commit
77
405 by eike
working expression parser is now in SIML
78
        Arguments:
79
            - s = string being parsed
80
            - loc = location where expression match was attempted and failed
81
            - expr = the parse expression that failed
82
            - err = the exception thrown
574 by eike
test commit
83
405 by eike
working expression parser is now in SIML
84
        Return:
85
           The function returns no value.  It may throw ParseFatalException
86
           if it is desired to stop parsing immediately.
87
        '''
406 by eike
Indented (Python like) grammar seems to work.
88
        #TODO: Does this class need to react to regular ParseExceptions too? (ParseSyntaxException)
89
        #Work only with fatal errors from Pyparsing
90
        if isinstance(err, ParseFatalException):
91
            if self.prepend_str is not None:
92
                err.msg = self.prepend_str + err.msg
93
            if self.append_str is not None:
94
                err.msg = err.msg + self.append_str
95
            raise err
574 by eike
test commit
96
97
406 by eike
Indented (Python like) grammar seems to work.
98
#TODO: remove?
99
#
100
#class ParseActionException(Exception):
101
#    '''Exception raised by the parse actions of the parser.'''
102
#    pass
235 by eike
put practically all files into a package 'freeode'
103
104
105
413 by eike
renamed ParseStage --> Parser
106
class Parser(object):
235 by eike
put practically all files into a package 'freeode'
107
    '''
367 by eike
Small changes, mostly docs.
108
    Parse the Siml program. Generate a parse tree.
399 by eike
stating to implement new syntax
109
235 by eike
put practically all files into a package 'freeode'
110
    The syntax definition (BNF) resides here.
351 by eike
corrected some typos
111
112
    The parsing is done by the pyparsing library which combines
113
    lexer and parser. The Pyparsing library generates a tree of
399 by eike
stating to implement new syntax
114
    ParseResult objects. These objects are replaced by objects inheriting
367 by eike
Small changes, mostly docs.
115
    from ast.Node in the parse actions (_action* functions) of this class.
235 by eike
put practically all files into a package 'freeode'
116
351 by eike
corrected some typos
117
    Normally a file name is given to the class, and a tree of ast.Node objects is
235 by eike
put practically all files into a package 'freeode'
118
    returned. The program can also be entered as a string.
119
    Additionally the class can parse parts of a program: expressions.
279 by eike
upped version to 0.3.1
120
367 by eike
Small changes, mostly docs.
121
    The parse* methods return a tree of ast.Node objects; the parse tree.
235 by eike
put practically all files into a package 'freeode'
122
123
    Usage:
413 by eike
renamed ParseStage --> Parser
124
    parser = Parser()
310 by eike
Built in functions with multiple arguments complete.
125
    ast1 = parser.parseExpressionStr('0+1+2+3+4')
373 by eike
small progress in creating the program tree
126
    ast2 = parser.parseModuleFile('foo-bar.siml')
235 by eike
put practically all files into a package 'freeode'
127
    '''
128
129
    noTreeModification = 0
130
    '''
131
    Define how much the parse result is modified, for easier debuging.
351 by eike
corrected some typos
132
    0: normal operation. Compilation does not work otherwise.
403 by eike
only cosmetich changes. The introduction of the new expression parser will come soon.
133
    1: Do not modify parse result from the Pyparsing library.
279 by eike
upped version to 0.3.1
134
403 by eike
only cosmetich changes. The introduction of the new expression parser will come soon.
135
    ParseResult objects (which come from the Pyparsing library) are printed
136
    as nested lists: ['1', '+', ['2', '*', '3']]
235 by eike
put practically all files into a package 'freeode'
137
    '''
138
366 by eike
- Removed built in names
139
    keywords = set()
140
    '''
403 by eike
only cosmetich changes. The introduction of the new expression parser will come soon.
141
    Set of all keywords (filled by _defineLanguageSyntax() and defineKeyword(...)).
366 by eike
- Removed built in names
142
    '''
143
    #Special variables, that are built into the language (filled by _defineLanguageSyntax())
144
    builtInVars = set()
235 by eike
put practically all files into a package 'freeode'
145
146
147
    def __init__(self):
148
        object.__init__(self)
403 by eike
only cosmetich changes. The introduction of the new expression parser will come soon.
149
        #The parser object for the whole program (from pyParsing).
235 by eike
put practically all files into a package 'freeode'
150
        self._parser = None
403 by eike
only cosmetich changes. The introduction of the new expression parser will come soon.
151
        #The parser for expressions
235 by eike
put practically all files into a package 'freeode'
152
        self._expressionParser = None
403 by eike
only cosmetich changes. The introduction of the new expression parser will come soon.
153
        #Name of SIML program file, that will be parsed
235 by eike
put practically all files into a package 'freeode'
154
        self.progFileName = None
377 by eike
Aded:
155
        #name, that will be given to the root node of a module
156
        #usually part of the filename
157
        self.moduleName = None
403 by eike
only cosmetich changes. The introduction of the new expression parser will come soon.
158
        #String that will be parsed
235 by eike
put practically all files into a package 'freeode'
159
        self.inputString = None
406 by eike
Indented (Python like) grammar seems to work.
160
        #indent stack for indentedBlock helper from Pyparsing
161
        self.indentStack = [1]
403 by eike
only cosmetich changes. The introduction of the new expression parser will come soon.
162
235 by eike
put practically all files into a package 'freeode'
163
        #Create parser objects
279 by eike
upped version to 0.3.1
164
        self._defineLanguageSyntax()
235 by eike
put practically all files into a package 'freeode'
165
166
167
    def defineKeyword(self, inString):
168
        '''
413 by eike
renamed ParseStage --> Parser
169
        Store keyword (in Parser.keywords) and create parser for it.
235 by eike
put practically all files into a package 'freeode'
170
        Use this function (in _defineLanguageSyntax(...)) instead of using the
171
        Keyword class directly.
172
        '''
413 by eike
renamed ParseStage --> Parser
173
        Parser.keywords.add(inString)
235 by eike
put practically all files into a package 'freeode'
174
        return Keyword(inString)
175
176
    def createTextLocation(self, atChar):
403 by eike
only cosmetich changes. The introduction of the new expression parser will come soon.
177
        '''Create a text location object at the given char.'''
235 by eike
put practically all files into a package 'freeode'
178
        return TextLocation(atChar, self.inputString, self.progFileName)
279 by eike
upped version to 0.3.1
179
180
235 by eike
put practically all files into a package 'freeode'
181
#------------- Parse Actions -------------------------------------------------*
369 by eike
Removed multiple warnings from simlparser.py
182
    def _actionDebug(self, s, loc, toks):
351 by eike
corrected some typos
183
        '''Parse action for debugging.'''
235 by eike
put practically all files into a package 'freeode'
184
        print '------debug action'
369 by eike
Removed multiple warnings from simlparser.py
185
        print s
235 by eike
put practically all files into a package 'freeode'
186
        print loc
187
        print toks
188
        print '-------------------'
189
        return None
190
369 by eike
Removed multiple warnings from simlparser.py
191
    def _actionCheckIdentifier(self, s, loc, toks):
235 by eike
put practically all files into a package 'freeode'
192
        '''
309 by eike
built in functions can now have multiple arguments.
193
        Tests wether an identifier is legal.
194
        If the identifier is equal to any keyword the parse action raises
195
        an exception.
235 by eike
put practically all files into a package 'freeode'
196
        Does not change any parse results
351 by eike
corrected some typos
197
309 by eike
built in functions can now have multiple arguments.
198
        tokList is structured like this: ['a1']
235 by eike
put practically all files into a package 'freeode'
199
        '''
309 by eike
built in functions can now have multiple arguments.
200
        tokList = toks.asList() #asList() this time ads *no* extra pair of brackets
401 by eike
some nice and short parser for expressions exists
201
        identifier = tokList[0]
413 by eike
renamed ParseStage --> Parser
202
        if identifier in Parser.keywords:
235 by eike
put practically all files into a package 'freeode'
203
            #print 'found keyword', toks[0], 'at loc: ', loc
401 by eike
some nice and short parser for expressions exists
204
            errMsg = 'Keyword can not be used as an identifier: ' + identifier
369 by eike
Removed multiple warnings from simlparser.py
205
            raise ParseException(s, loc, errMsg)
404 by eike
Found pyparsing bug:
206
        return
361 by eike
More complex data statement is reflected in AST.
207
401 by eike
some nice and short parser for expressions exists
208
    def _actionCheckIdentifierFatal(self, s, loc, toks):
209
        '''
406 by eike
Indented (Python like) grammar seems to work.
210
        Tests whether an identifier is legal.
401 by eike
some nice and short parser for expressions exists
211
        If the identifier is equal to any keyword the parse action raises
403 by eike
only cosmetich changes. The introduction of the new expression parser will come soon.
212
        an user visible exception. This will usually stop the compiler.
401 by eike
some nice and short parser for expressions exists
213
        Does not change any parse results
214
215
        tokList is structured like this: ['a1']
216
        '''
217
        tokList = toks.asList() #asList() this time ads *no* extra pair of brackets
218
        identifier = tokList[0]
413 by eike
renamed ParseStage --> Parser
219
        if identifier in Parser.keywords:
401 by eike
some nice and short parser for expressions exists
220
            #print 'found keyword', toks[0], 'at loc: ', loc
221
            errMsg = 'Keyword can not be used as an identifier: ' + identifier
408 by eike
Indented grammar broken due to bug in pyparsing!
222
#            txtLoc = self.createTextLocation(loc)
223
            raise ParseFatalException(s, loc, errMsg)
413 by eike
renamed ParseStage --> Parser
224
        if identifier in Parser.builtInVars:
404 by eike
Found pyparsing bug:
225
            errMsg = 'Built in variables can not be redefined: ' + identifier
408 by eike
Indented grammar broken due to bug in pyparsing!
226
#            txtLoc = self.createTextLocation(loc)
227
            raise ParseFatalException(s, loc, errMsg)
404 by eike
Found pyparsing bug:
228
        return
401 by eike
some nice and short parser for expressions exists
229
366 by eike
- Removed built in names
230
#    def _actionBuiltInValue(self, str, loc, toks):
231
#        '''
232
#        Create AST node for a built in value: pi, time
233
#        tokList has the following structure:
234
#        [<identifier>]
235
#        '''
413 by eike
renamed ParseStage --> Parser
236
#        if Parser.noTreeModification:
366 by eike
- Removed built in names
237
#            return None #No parse result modifications for debugging
238
#        tokList = toks.asList()[0] #asList() ads an extra pair of brackets
239
#        #create AST node
240
#        nCurr = NodeBuiltInVal()
241
#        nCurr.loc = self.createTextLocation(loc) #Store position
242
#        nCurr.dat = tokList #Store the built in value's name
243
#        return nCurr
279 by eike
upped version to 0.3.1
244
245
417 by eike
some works towards expression evaluation done
246
    def _action_number(self, s, loc, toks): #IGNORE:W0613
235 by eike
put practically all files into a package 'freeode'
247
        '''
248
        Create node for a number: 5.23
249
        tokList has the following structure:
250
        [<number>]
251
        '''
413 by eike
renamed ParseStage --> Parser
252
        if Parser.noTreeModification:
351 by eike
corrected some typos
253
            return None #No parse result modifications for debugging
235 by eike
put practically all files into a package 'freeode'
254
        tokList = toks.asList()[0] #asList() ads an extra pair of brackets
415 by eike
New node class put into ast module and passes all unit tests.
255
        nCurr = NodeFloat()
279 by eike
upped version to 0.3.1
256
        nCurr.loc = self.createTextLocation(loc) #Store position
417 by eike
some works towards expression evaluation done
257
        nCurr.value = tokList[0] #Store the number
279 by eike
upped version to 0.3.1
258
        return nCurr
259
417 by eike
some works towards expression evaluation done
260
    def _action_string(self, s, loc, toks): #IGNORE:W0613
235 by eike
put practically all files into a package 'freeode'
261
        '''
262
        Create node for a string: 'qwert'
263
        tokList has the following structure:
264
        [<string>]
265
        '''
413 by eike
renamed ParseStage --> Parser
266
        if Parser.noTreeModification:
351 by eike
corrected some typos
267
            return None #No parse result modifications for debugging
235 by eike
put practically all files into a package 'freeode'
268
        tokList = toks.asList()[0] #asList() ads an extra pair of brackets
269
        nCurr = NodeString()
279 by eike
upped version to 0.3.1
270
        nCurr.loc = self.createTextLocation(loc) #Store position
417 by eike
some works towards expression evaluation done
271
        nCurr.value = tokList[1:-1] #Store the string; remove quotes
279 by eike
upped version to 0.3.1
272
        return nCurr
273
417 by eike
some works towards expression evaluation done
274
    def _action_parentheses_pair(self, s, loc, toks): #IGNORE:W0613
235 by eike
put practically all files into a package 'freeode'
275
        '''
276
        Create node for a pair of parentheses that enclose an expression: (...)
444 by eike
Now a simple program can be parsed and interpreted!
277
        tok_list has the following structure:
235 by eike
put practically all files into a package 'freeode'
278
        ['(', <expression>, ')']
403 by eike
only cosmetich changes. The introduction of the new expression parser will come soon.
279
280
        The information about parentheses is necessary to be able to output
281
        correct Python code, without the need for complicated algorithms.
235 by eike
put practically all files into a package 'freeode'
282
        '''
413 by eike
renamed ParseStage --> Parser
283
        if Parser.noTreeModification:
351 by eike
corrected some typos
284
            return None #No parse result modifications for debugging
444 by eike
Now a simple program can be parsed and interpreted!
285
        tok_list = toks.asList()
286
        node = NodeParentheses()
287
        node.loc = self.createTextLocation(loc) #Store position
509 by eike
small progress
288
        node.arguments = (tok_list[0],) #store child expression
444 by eike
Now a simple program can be parsed and interpreted!
289
        return node
235 by eike
put practically all files into a package 'freeode'
290
404 by eike
Found pyparsing bug:
291
    def _action_op_prefix(self, s, loc, toks): #IGNORE:W0613
235 by eike
put practically all files into a package 'freeode'
292
        '''
293
        Create node for math prefix operators: -
442 by eike
implemented '$' operator
294
        tok_list has the following structure:
406 by eike
Indented (Python like) grammar seems to work.
295
        [<operator>, <expression>]
235 by eike
put practically all files into a package 'freeode'
296
        '''
413 by eike
renamed ParseStage --> Parser
297
        if Parser.noTreeModification:
351 by eike
corrected some typos
298
            return None #No parse result modifications for debugging
442 by eike
implemented '$' operator
299
        tok_list = toks.asList()[0] #Group() ads an extra pair of brackets
574 by eike
test commit
300
        #collect the relevant data
543 by eike
implemented __diff__; special function to access the derivative of a variable
301
        operator = tok_list[0]        #operator
442 by eike
implemented '$' operator
302
        expr_rhs = tok_list[1]        #RHS expression
574 by eike
test commit
303
        node = NodeOpPrefix1(operator, (expr_rhs,),
543 by eike
implemented __diff__; special function to access the derivative of a variable
304
                             self.createTextLocation(loc))
442 by eike
implemented '$' operator
305
        return node
235 by eike
put practically all files into a package 'freeode'
306
404 by eike
Found pyparsing bug:
307
    def _action_op_infix_left(self, s, loc, toks): #IGNORE:W0613
308
        '''
309
        Build tree of infix operators from list of operators and operands.
310
311
        operatorPrecedence returns such a list for left assocative operaators.
312
        tokList has the following structure:
574 by eike
test commit
313
        [<expression_1>, <operator_1>, <expression_2>, <operator_2>, ...
417 by eike
some works towards expression evaluation done
314
         <expression_n+1>]
404 by eike
Found pyparsing bug:
315
        '''
413 by eike
renamed ParseStage --> Parser
316
        if Parser.noTreeModification:
404 by eike
Found pyparsing bug:
317
            return None #No parse result modifications for debugging
405 by eike
working expression parser is now in SIML
318
        tokList = toks.asList()[0]
319
        tree = self._action_op_infix(s,  loc, tokList[0:3])
320
        for i in range(3, len(tokList),  2):
321
            tree = self._action_op_infix(s,  loc,  [tree,  tokList[i],  tokList[i+1]])
404 by eike
Found pyparsing bug:
322
        return tree
323
324
    def _action_op_infix(self, s, loc, toks): #IGNORE:W0613
235 by eike
put practically all files into a package 'freeode'
325
        '''
405 by eike
working expression parser is now in SIML
326
        Create node for math infix operators: + - * / **
235 by eike
put practically all files into a package 'freeode'
327
        tokList has the following structure:
328
        [<expression_l>, <operator>, <expression_r>]
329
        '''
413 by eike
renamed ParseStage --> Parser
330
        if Parser.noTreeModification:
351 by eike
corrected some typos
331
            return None #No parse result modifications for debugging
427 by eike
attribute access works.
332
        #toks might be a list or a ParseResults object
405 by eike
working expression parser is now in SIML
333
        if not isinstance(toks,  list):
334
            #Convert parse result to list, remove extra pair of brackets
574 by eike
test commit
335
            tokList = toks.asList()[0]
405 by eike
working expression parser is now in SIML
336
        else:
337
            #function was called by _action_op_infix_left(...) toks is already
338
            #a list.
339
            tokList = toks
574 by eike
test commit
340
        #collect the relevant data
417 by eike
some works towards expression evaluation done
341
        exprLhs = tokList[0]        #expression left hand side
427 by eike
attribute access works.
342
        operator = tokList[1]       #operator
417 by eike
some works towards expression evaluation done
343
        exprRhs = tokList[2]        #rhs
427 by eike
attribute access works.
344
        #create correct node type
345
        if operator == '.':
346
            nCurr = NodeAttrAccess()
347
        else:
348
            nCurr = NodeOpInfix2()
349
        nCurr.loc = self.createTextLocation(loc) #Store position
350
        #Store both expressions and operator
351
        nCurr.operator = operator
509 by eike
small progress
352
        nCurr.arguments = (exprLhs, exprRhs)
235 by eike
put practically all files into a package 'freeode'
353
        return nCurr
354
405 by eike
working expression parser is now in SIML
355
    def _action_identifier(self, s, loc, toks): #IGNORE:W0613
235 by eike
put practically all files into a package 'freeode'
356
        '''
574 by eike
test commit
357
        Create node for an identifier.
358
577 by eike
Remove all DotName objects from parser.
359
        BNF:
360
        identifierBase = Word(alphas+'_', alphanums+'_')            .setName('identifier')#.setDebug(True)
361
        identifier  =   identifierBase.copy()                       .setParseAction(self._actionCheckIdentifier)\
362
                                                                    .setParseAction(self._action_identifier)
235 by eike
put practically all files into a package 'freeode'
363
        '''
413 by eike
renamed ParseStage --> Parser
364
        if Parser.noTreeModification:
351 by eike
corrected some typos
365
            return None #No parse result modifications for debugging
577 by eike
Remove all DotName objects from parser.
366
        n_id = NodeIdentifier()
367
        n_id.loc = self.createTextLocation(loc) #Store position
368
        n_id.name = toks[0]
369
        return n_id
235 by eike
put practically all files into a package 'freeode'
370
431 by eike
A working basic interpreter exists.
371
    def _action_expression_stmt(self, s, loc, toks):
372
        '''
574 by eike
test commit
373
        Create node for a function call. Really any expression can be
431 by eike
A working basic interpreter exists.
374
        present, but only function calls make sense.
574 by eike
test commit
375
431 by eike
A working basic interpreter exists.
376
        BNF:
377
        expression_stmt = Group(expression)
378
        '''
379
        if Parser.noTreeModification:
380
            return None #No parse result modifications for debugging
381
#        tokList = toks.asList()[0] #Group() ads an extra pair of brackets
382
        nCurr = NodeExpressionStmt()
383
        nCurr.loc = self.createTextLocation(loc) #Store position
384
        nCurr.expression = toks[0][0]
385
        return nCurr
574 by eike
test commit
386
387
    def _action_if_clause(self, s, loc, toks): #IGNORE:W0613
519 by eike
Added comparison functions to IFloat.
388
        '''
389
        Create node for one clause of the if statement.
574 by eike
test commit
390
519 by eike
Added comparison functions to IFloat.
391
        Each clause is stored as a pair: <condition, statement list>. The 'else'
392
        clause has a condition that is always true.
574 by eike
test commit
393
519 by eike
Added comparison functions to IFloat.
394
        A clause is:
395
            if condition:
396
                statement
397
                ...
398
        or:
399
            elif condition:
400
                ...
401
        or:
402
            else:
403
                ...
574 by eike
test commit
404
519 by eike
Added comparison functions to IFloat.
405
        For BNF look at: _action_if_statement
406
        '''
407
        if Parser.noTreeModification:
408
            return None #No parse result modifications for debugging
409
        loc_ex = self.createTextLocation(loc) #Store position
410
        #'if', 'elif' have different structure than 'else' clause
411
        keyword = toks[0]
412
        if keyword in ['if', 'elif']:
413
            condition = toks[1]
414
            statements = toks[3].asList()
415
        elif keyword == 'else':
416
            condition = NodeFloat('1', loc_ex) #always true  - there is no bool yet
417
            statements = toks[2].asList()
418
        else:
419
            raise Exception('Unknown keyword in if ... elif ... else statement.')
420
        #repackage statements; they are stored in nested lists
421
        stmts_flat = []
422
        for sublist in statements:
574 by eike
test commit
423
            stmts_flat.append(sublist[0])
519 by eike
Added comparison functions to IFloat.
424
        #create node for this clause and return it
425
        node = NodeClause(condition, stmts_flat, loc_ex)
426
        return node
574 by eike
test commit
427
428
    def _action_if_statement(self, s, loc, toks): #IGNORE:W0613
235 by eike
put practically all files into a package 'freeode'
429
        '''
430
        Create node for if ... : ... else: ... statement.
574 by eike
test commit
431
235 by eike
put practically all files into a package 'freeode'
432
        BNF:
519 by eike
Added comparison functions to IFloat.
433
        if_stmt = Group \
434
            (             (kw('if')   - expression + ':' + suite)   .setParseAction(self._action_if_clause)
574 by eike
test commit
435
             + ZeroOrMore((kw('elif') - expression + ':' + suite)   .setParseAction(self._action_if_clause)
519 by eike
Added comparison functions to IFloat.
436
                                                                    .setFailAction(ChMsg(prepend='elif: ')))
437
             + Optional(  (kw('else') - ':' + suite)                .setParseAction(self._action_if_clause)
438
                                                                    .setFailAction(ChMsg(prepend='else: ')))
439
             )                                                      .setParseAction(self._action_if_statement) \
440
                                                                    .setFailAction(ChMsg(prepend='if statement: ')) \
235 by eike
put practically all files into a package 'freeode'
441
        '''
413 by eike
renamed ParseStage --> Parser
442
        if Parser.noTreeModification:
351 by eike
corrected some typos
443
            return None #No parse result modifications for debugging
519 by eike
Added comparison functions to IFloat.
444
        tokList = toks.asList()[0] #Group() ads an extra pair of brackets
445
        loc_ex = self.createTextLocation(loc) #Store position
446
        node = NodeIfStmt(tokList, loc_ex)
447
        return node
235 by eike
put practically all files into a package 'freeode'
448
441 by eike
First step at better type information, because every class is really a template.
449
    def _action_assign_stmt(self, s, loc, toks): #IGNORE:W0613
235 by eike
put practically all files into a package 'freeode'
450
        '''
451
        Create node for assignment: a = 2*b
452
        BNF:
441 by eike
First step at better type information, because every class is really a template.
453
        assign_stmt = Group(expression_ex + '=' - expression)
235 by eike
put practically all files into a package 'freeode'
454
        '''
413 by eike
renamed ParseStage --> Parser
455
        if Parser.noTreeModification:
351 by eike
corrected some typos
456
            return None #No parse result modifications for debugging
441 by eike
First step at better type information, because every class is really a template.
457
        tokList = toks.asList()[0] #Group() ads an extra pair of brackets
235 by eike
put practically all files into a package 'freeode'
458
        nCurr = NodeAssignment()
279 by eike
upped version to 0.3.1
459
        nCurr.loc = self.createTextLocation(loc) #Store position
574 by eike
test commit
460
        nCurr.target = tokList[0]
461
        nCurr.expression = tokList[2]
235 by eike
put practically all files into a package 'freeode'
462
        return nCurr
463
492 by eike
Little progress in code generation architecture.
464
#    def _action_print_stmt(self, s, loc, toks): #IGNORE:W0613
465
#        '''
466
#        Create node for print statement:
467
#            print 'hello', foo.x
468
#        BNF:
469
#        expression_list = delimitedList(expression, ',')            .setName('exprList')
470
#        print_stmt = Group(kw('print')
471
#                          - Optional(expression_list)               .setResultsName('arg_list')
574 by eike
test commit
472
#                          + Optional(',')                           .setResultsName('trail_comma')
492 by eike
Little progress in code generation architecture.
473
#                          )                                         .setParseAction(self._action_print_stmt)\
474
#        '''
475
#        if Parser.noTreeModification:
476
#            return None #No parse result modifications for debugging
574 by eike
test commit
477
#        #tokList = toks.asList()[0] #Group adds
492 by eike
Little progress in code generation architecture.
478
#        toks = toks[0]             #an extra pair of brackets
479
#        nCurr = NodePrintStmt()
480
#        nCurr.loc = self.createTextLocation(loc) #Store position
481
#        nCurr.arguments = toks.arg_list.asList()
482
#        if toks.trailComma:
483
#            nCurr.newline = False
484
#        else:
485
#            nCurr.newline = True
486
#        return nCurr
487
488
#    def _actionGraphStmt(self, s, loc, toks): #IGNORE:W0613
489
#        '''
490
#        Create node for graph statement:
491
#            graph foo.x, foo.p
492
#        BNF:
493
#        graphStmt = Group(kw('graph') + exprList  .setResultsName('argList')
494
#                          + ';')                  .setParseAction(self._actionDebug)\
495
#        '''
496
#        if Parser.noTreeModification:
497
#            return None #No parse result modifications for debugging
498
#        #tokList = toks.asList()[0] #there always seems to be
499
#        toks = toks[0]             #an extra pair of brackets
500
#        nCurr = NodeGraphStmt()
501
#        nCurr.loc = self.createTextLocation(loc) #Store position
502
#        nCurr.kids = toks.argList.asList()[0]
503
#        return nCurr
504
505
#    def _actionStoreStmt(self, s, loc, toks): #IGNORE:W0613
506
#        '''
507
#        Create node for graph statement:
508
#            graph foo.x, foo.p
509
#        BNF:
510
#        graphStmt = Group(kw('graph') + exprList  .setResultsName('argList')
511
#                          + ';')                  .setParseAction(self._actionDebug)\
512
#        '''
513
#        if Parser.noTreeModification:
514
#            return None #No parse result modifications for debugging
515
#        #tokList = toks.asList()[0] #there always seems to be
516
#        toks = toks[0]             #an extra pair of brackets
517
#        nCurr = NodeStoreStmt()
518
#        nCurr.loc = self.createTextLocation(loc) #Store position
519
#        nCurr.kids = toks.argList.asList()
520
#        return nCurr
279 by eike
upped version to 0.3.1
521
510 by eike
implemented pass statement.
522
    def _action_pass_stmt(self, s, loc, toks): #IGNORE:W0613
523
        '''
524
        Create node for pass statement (which does nothing):
574 by eike
test commit
525
510 by eike
implemented pass statement.
526
        BNF:
527
        pass_stmt = kw('pass')                                      .setParseAction(self._action_pass_stmt)
528
        '''
529
        if Parser.noTreeModification:
530
            return None #No parse result modifications for debugging
516 by eike
Alpha quality compiler works
531
        n_curr = NodePassStmt()
510 by eike
implemented pass statement.
532
        n_curr.loc = self.createTextLocation(loc) #Store position
533
        return n_curr
574 by eike
test commit
534
535
536
422 by eike
small work on function call
537
    def _action_return_stmt(self, s, loc, toks): #IGNORE:W0613
365 by eike
Added retun, pragma and foreign_code statements.
538
        '''
539
        Create node for return statement:
540
            return 2*a;
541
        BNF:
422 by eike
small work on function call
542
        return_stmt = (kw('return') - Optional(expression           .setResultsName('ret_val'))
543
                      )                                             .setParseAction(self._action_return_stmt) \
544
                                                                    .setFailAction(ChMsg(prepend='return statement: '))
365 by eike
Added retun, pragma and foreign_code statements.
545
        '''
413 by eike
renamed ParseStage --> Parser
546
        if Parser.noTreeModification:
365 by eike
Added retun, pragma and foreign_code statements.
547
            return None #No parse result modifications for debugging
548
        nCurr = NodeReturnStmt()
549
        nCurr.loc = self.createTextLocation(loc) #Store position
422 by eike
small work on function call
550
        if isinstance(toks.ret_val, Node):
551
            nCurr.arguments.append(toks.ret_val)
365 by eike
Added retun, pragma and foreign_code statements.
552
        return nCurr
553
369 by eike
Removed multiple warnings from simlparser.py
554
    def _actionPragmaStmt(self, s, loc, toks): #IGNORE:W0613
365 by eike
Added retun, pragma and foreign_code statements.
555
        '''
556
        Create node for pragma statement:
557
            pragma no flatten;
558
        BNF:
399 by eike
stating to implement new syntax
559
        pragmaStmt = (kw('pragma')
365 by eike
Added retun, pragma and foreign_code statements.
560
                      + ES(OneOrMore(Word(alphanums+'_')) + stmtEnd) .setErrMsgStart('Pragma statement: ')
561
                      )                                              #.setParseAction(self._actionPragmaStmt)
562
         '''
413 by eike
renamed ParseStage --> Parser
563
        if Parser.noTreeModification:
365 by eike
Added retun, pragma and foreign_code statements.
564
            return None #No parse result modifications for debugging
565
        nCurr = NodePragmaStmt()
566
        nCurr.loc = self.createTextLocation(loc) #Store position
567
        for i in range(1, len(toks)):
568
            nCurr.options.append(toks[i])
569
        return nCurr
570
406 by eike
Indented (Python like) grammar seems to work.
571
#    def _actionForeignCodeStmt(self, s, loc, toks): #IGNORE:W0613
572
#        '''
573
#        Create node for foreign_code statement:
574
#            foreign_code python replace_call ::{{ sin(x) }}:: ;
575
#        BNF:
576
#        foreignCodeStmt = (kw('foreign_code')
577
#                           + ES(Word(alphanums+'_')                  .setResultsName('language')
578
#                                                                     .setName('language specification')
579
#                                + Word(alphanums+'_')                .setResultsName('method')
580
#                                                                     .setName('code insertion method')
581
#                                + QuotedString(quoteChar='::{{',
582
#                                               endQuoteChar='}}::')  .setResultsName('code')
583
#                                                                     .setName('code to insert')
584
#                                + stmtEnd)                           .setErrMsgStart('Foreign code statement: ')
585
#                           )                                         .setParseAction(self._actionForeignCodeStmt)
586
#         '''
413 by eike
renamed ParseStage --> Parser
587
#        if Parser.noTreeModification:
406 by eike
Indented (Python like) grammar seems to work.
588
#            return None #No parse result modifications for debugging
589
#        nCurr = NodeForeignCodeStmt()
590
#        nCurr.loc = self.createTextLocation(loc) #Store position
591
#        nCurr.language = toks.language
592
#        nCurr.method = toks.method
593
#        nCurr.code = toks.code
594
#        return nCurr
365 by eike
Added retun, pragma and foreign_code statements.
595
431 by eike
A working basic interpreter exists.
596
    def _action_compile_stmt(self, s, loc, toks): #IGNORE:W0613
388 by eike
very small progress!
597
        '''
431 by eike
A working basic interpreter exists.
598
        Create node for compile statement.
574 by eike
test commit
599
388 by eike
very small progress!
600
        BNF:
574 by eike
test commit
601
        compile_stmt = (kw('compile')
431 by eike
A working basic interpreter exists.
602
                        - Optional(newIdentifier                    .setResultsName('name')
574 by eike
test commit
603
                                   + ':')
431 by eike
A working basic interpreter exists.
604
                        + expression                                .setResultsName('class_name')
605
                        )                                           .setParseAction(self._action_compile_stmt)\
388 by eike
very small progress!
606
         '''
413 by eike
renamed ParseStage --> Parser
607
        if Parser.noTreeModification:
388 by eike
very small progress!
608
            return None #No parse result modifications for debugging
577 by eike
Remove all DotName objects from parser.
609
        n_curr = NodeCompileStmt()
610
        n_curr.loc = self.createTextLocation(loc) #Store position
611
        n_curr.class_spec = toks.class_name
391 by eike
bug hunting and beauty session.
612
        if toks.name:
577 by eike
Remove all DotName objects from parser.
613
            n_curr.name = toks.name
614
        return n_curr
574 by eike
test commit
615
399 by eike
stating to implement new syntax
616
444 by eike
Now a simple program can be parsed and interpreted!
617
    def _action_stmt_list(self, s, loc, toks): #IGNORE:W0613
618
        '''
619
        Create node for list of statements: a=1; b=2; ...
620
        BNF:
574 by eike
test commit
621
        stmt_list = Group(delimitedList(Group(simple_stmt), ';')
622
                             + Optional(Suppress(";")) )
444 by eike
Now a simple program can be parsed and interpreted!
623
        '''
624
        if Parser.noTreeModification:
625
            return None #No parse result modifications for debugging
626
        tok_list = toks.asList()[0] #Group() ads an extra pair of brackets
627
        node = NodeStmtList()
628
        node.loc = self.createTextLocation(loc) #Store position
629
        #store function body; take each statement out of its sublist
630
        for sublist in tok_list:
631
            node.statements.append(sublist[0])
632
        return node
235 by eike
put practically all files into a package 'freeode'
633
634
419 by eike
some repairing of parse actions
635
    def _action_data_def(self, s, loc, toks): #IGNORE:W0613
235 by eike
put practically all files into a package 'freeode'
636
        '''
279 by eike
upped version to 0.3.1
637
        Create node for defining parameter, variable or submodel:
235 by eike
put practically all files into a package 'freeode'
638
            'data foo, bar: baz.boo parameter;
639
        One such statement can define multiple parmeters; and an individual
378 by eike
Started compile time interpreter
640
        NodeDataDef is created for each. They are returned together inside a
339 by eike
More changes from tuple to DotName object.
641
        list node of type NodeStmtList.
235 by eike
put practically all files into a package 'freeode'
642
        BNF:
574 by eike
test commit
643
        newAttrList = delimitedList(newIdentifier)
419 by eike
some repairing of parse actions
644
        data_stmt = Group(kw('data')
645
                          - newAttrList                             .setResultsName('attr_name_list')
646
                          + ':' + expression                        .setResultsName('class_name')
647
                          + Optional(attrRole)                      .setResultsName('attr_role')
648
                          + Optional('=' - expression               .setResultsName('default_value')
649
                                     )                              .setFailAction(ChMsg(prepend='default value: '))
650
                          )                                         .setParseAction(self._action_data_def)\
651
         '''
413 by eike
renamed ParseStage --> Parser
652
        if Parser.noTreeModification:
351 by eike
corrected some typos
653
            return None #No parse result modifications for debugging
279 by eike
upped version to 0.3.1
654
        #tokList = toks.asList()[0] #there always seems to be
235 by eike
put practically all files into a package 'freeode'
655
        toks = toks[0]             #an extra pair of brackets
656
        #multiple attributes can be defined in a single statement
419 by eike
some repairing of parse actions
657
        #Create a node for each of them and put them into a special statement list
577 by eike
Remove all DotName objects from parser.
658
        data_def_list = NodeStmtList()
659
        data_def_list.loc = self.createTextLocation(loc)
660
        name_list = toks.attr_name_list.asList()
661
        for name in name_list:
662
            data_def = NodeDataDef()
663
            data_def.loc = self.createTextLocation(loc)
664
            data_def.name = name #store attribute name
665
            data_def.class_spec = toks.class_name #toks.class_name is NodeIdenifier
361 by eike
More complex data statement is reflected in AST.
666
            #map role string to role object, and store the role
399 by eike
stating to implement new syntax
667
            #If role is not specified RoleVariable is assumed.
394 by eike
very small progress
668
            #Submodels will be labeled variables even though these categories don't apply to them.
577 by eike
Remove all DotName objects from parser.
669
            role_dict = {'const':RoleConstant, 'param':RoleParameter, 'variable':RoleAlgebraicVariable,
399 by eike
stating to implement new syntax
670
                        'algebraic_variable':RoleAlgebraicVariable,
499 by eike
Default roles of data statements are now part of the environment. Current default roles are:
671
                        'state_variable':RoleStateVariable,
544 by eike
small code beautification attempts.
672
                        'time_differential':RoleTimeDifferential,
499 by eike
Default roles of data statements are now part of the environment. Current default roles are:
673
                        'role_unknown':RoleUnkown}
577 by eike
Remove all DotName objects from parser.
674
            data_def.role = role_dict.get(toks.attr_role, None)
361 by eike
More complex data statement is reflected in AST.
675
            #store the default value
419 by eike
some repairing of parse actions
676
            if isinstance(toks.default_value, Node):
577 by eike
Remove all DotName objects from parser.
677
                data_def.default_value = toks.default_value
426 by eike
defining and instantiating a class basically works.
678
                raise UserException('Default values are currently unsupported!',
506 by eike
- Enabled keyword arguments.
679
                                    self.createTextLocation(loc), errno=2138010)
419 by eike
some repairing of parse actions
680
            #store the attribute definition in the list
577 by eike
Remove all DotName objects from parser.
681
            data_def_list.statements.append(data_def)
279 by eike
upped version to 0.3.1
682
        #Special case: only one attribute defined
577 by eike
Remove all DotName objects from parser.
683
        if len(data_def_list.statements) == 1:
684
            return data_def_list.statements[0] #take it out of the list and return it
235 by eike
put practically all files into a package 'freeode'
685
        else:
577 by eike
Remove all DotName objects from parser.
686
            return data_def_list #return list with multiple definitions
279 by eike
upped version to 0.3.1
687
235 by eike
put practically all files into a package 'freeode'
688
404 by eike
Found pyparsing bug:
689
    def _action_slicing(self, s, loc, toks): #IGNORE:W0613
690
        '''
691
        Create node for slicing operation.
692
        '''
413 by eike
renamed ParseStage --> Parser
693
        if Parser.noTreeModification:
404 by eike
Found pyparsing bug:
694
            return None #No parse result modifications for debuging
695
        raise UserException('Slicing is currently unsupported!',
506 by eike
- Enabled keyword arguments.
696
                            self.createTextLocation(loc), errno=2139010)
404 by eike
Found pyparsing bug:
697
698
405 by eike
working expression parser is now in SIML
699
#    def _action_func_call_arg(self, s, loc, toks): #IGNORE:W0613
700
#        '''
701
#        Create node for one argument of a function call.
702
#            x=2.5  ,  x*2+sin(a)
703
#        Node types are: either a mathematical expression, or an assignment.
704
#        BNF:
705
#        call_argument = ( Group(identifier                         .setResultsName('keyword')
706
#                                + '=' + expression                 .setResultsName('value')
707
#                                )                                  .setResultsName('keyword_argument')
708
#                        | Group(expression)                        .setResultsName('positional_argument')
709
#                        )                                          .setParseAction(self._action_func_call_arg)
710
#        '''
413 by eike
renamed ParseStage --> Parser
711
#        if Parser.noTreeModification:
405 by eike
working expression parser is now in SIML
712
#            return None #No parse result modifications for debuging
713
#        #named argument: x=2.5
714
#        if toks.keyword_argument:
715
#            #TODO:Should a new node NodeNamedArg be introduced? A named argument is
716
#            # not really an assignment. Making it an assignment may break the
717
#            # attribute renaming algorithm.
718
#            raise UserException('Keyword arguments are currently unsupported!',
719
#                                self.createTextLocation(loc))
720
#        #positional argument: 2.5
721
#        elif toks.positional_argument:
722
#            nCurr = toks.positionalArg[0]
723
#        else:
724
#            raise ParseActionException('Broken function argument. ' +
725
#                                        str(self.createTextLocation(loc)) + ' ' +
726
#                                        str(toks))
727
#        return nCurr
399 by eike
stating to implement new syntax
728
404 by eike
Found pyparsing bug:
729
    def _action_func_call(self, s, loc, toks): #IGNORE:W0613
363 by eike
Function generalization is complete.
730
        '''
731
        Create node for calling a function or method.
732
            bar.doFoo(10, x, a=2.5)
733
        BNF:
734
        '''
413 by eike
renamed ParseStage --> Parser
735
        if Parser.noTreeModification:
363 by eike
Function generalization is complete.
736
            return None #No parse result modifications for debuging
405 by eike
working expression parser is now in SIML
737
        toks = toks[0] #remove extra bracket of group
574 by eike
test commit
738
        n_curr = NodeFuncCall()
509 by eike
small progress
739
        n_curr.loc = self.createTextLocation(loc) #Store position
363 by eike
Function generalization is complete.
740
        #store function name
549 by eike
some refactoring:
741
        n_curr.function = toks[0]
399 by eike
stating to implement new syntax
742
        #store function arguments:
509 by eike
small progress
743
        there_was_keyword_argument = False #For check positional arguments must come before keyword arguments
744
        pos_arg_list = [] #collect positional arguments
405 by eike
working expression parser is now in SIML
745
        for arg in toks[1].argument_list:
746
            if arg.positional_argument:
747
                if there_was_keyword_argument:
509 by eike
small progress
748
                    raise UserException('Positional arguments must come '
749
                                        'before keyword arguments.',
750
                                        n_curr.loc, errno=2140010)
751
                pos_arg_list.append(arg.positional_argument[0][0])
405 by eike
working expression parser is now in SIML
752
            elif arg.keyword_argument:
753
                there_was_keyword_argument = True
506 by eike
- Enabled keyword arguments.
754
                arg_name = str(arg.keyword_argument[0][0].name)
755
                arg_val = arg.keyword_argument[0][2]
509 by eike
small progress
756
                n_curr.keyword_arguments[arg_name] = arg_val
757
        n_curr.arguments = tuple(pos_arg_list)
758
        return n_curr
399 by eike
stating to implement new syntax
759
760
421 by eike
repaired function definition statement.
761
    def _action_func_def_arg(self, s, loc, toks): #IGNORE:W0613
363 by eike
Function generalization is complete.
762
        '''
399 by eike
stating to implement new syntax
763
        Create node for one function argument of a function definition.
764
        A NodeDataDef is created; therefore this method is quite similar
419 by eike
some repairing of parse actions
765
        to _action_data_def.
361 by eike
More complex data statement is reflected in AST.
766
        BNF:
421 by eike
repaired function definition statement.
767
        func_def_arg = Group(identifier                             .setResultsName('name')
768
                           + Optional(':' - expression              .setResultsName('type')
769
                                      )                             .setFailAction(ChMsg(prepend='type specifier: '))
770
                           + Optional('=' - expression              .setResultsName('default_value')
771
                                      )                             .setFailAction(ChMsg(prepend='default value: '))
772
                           )                                        .setParseAction(self._action_func_def_arg)
361 by eike
More complex data statement is reflected in AST.
773
        '''
413 by eike
renamed ParseStage --> Parser
774
        if Parser.noTreeModification:
361 by eike
More complex data statement is reflected in AST.
775
            return None #No parse result modifications for debuging
474 by eike
The new Siml (user defined) functions are used by the interpreter. The old user defind functions are gone.
776
        toks = toks[0]             #Group adds an extra pair of brackets
777
        ncurr = NodeFuncArg()
778
        ncurr.loc = self.createTextLocation(loc) #Store position
362 by eike
Function definitions now store the additional information:
779
        #store argument name
577 by eike
Remove all DotName objects from parser.
780
        ncurr.name = toks.name.name
362 by eike
Function definitions now store the additional information:
781
        #store optional type of argument
421 by eike
repaired function definition statement.
782
        if toks.type:
474 by eike
The new Siml (user defined) functions are used by the interpreter. The old user defind functions are gone.
783
            ncurr.type = toks.type
362 by eike
Function definitions now store the additional information:
784
        #store optional default value
421 by eike
repaired function definition statement.
785
        if toks.default_value:
474 by eike
The new Siml (user defined) functions are used by the interpreter. The old user defind functions are gone.
786
            ncurr.default_value = toks.default_value
574 by eike
test commit
787
474 by eike
The new Siml (user defined) functions are used by the interpreter. The old user defind functions are gone.
788
        return ncurr
399 by eike
stating to implement new syntax
789
790
421 by eike
repaired function definition statement.
791
    def _action_func_def(self, s, loc, toks): #IGNORE:W0613
235 by eike
put practically all files into a package 'freeode'
792
        '''
361 by eike
More complex data statement is reflected in AST.
793
        Create node for definition of a function or method.
362 by eike
Function definitions now store the additional information:
794
            func doFoo(a:Real=2.5, b) -> Real: {... }
235 by eike
put practically all files into a package 'freeode'
795
        BNF:
421 by eike
repaired function definition statement.
796
        func_def_arg_list = (delimitedList(func_def_arg, ',')
574 by eike
test commit
797
                             + Optional(','))
421 by eike
repaired function definition statement.
798
        #the function: func doFoo(a:Real=2.5, b) -> Real {...}
799
        func_def_stmt = Group(kw('func') - newIdentifier            .setResultsName('func_name')
800
                        + ('(' - Optional(func_def_arg_list         .setResultsName('arg_list'))
801
                           + ')' )                                  .setFailAction(ChMsg(prepend='argument list: '))
802
                        + Optional('->' - expression                .setResultsName('return_type')
803
                                   )                                .setFailAction(ChMsg(prepend='return type: : '))
399 by eike
stating to implement new syntax
804
                        + ':'
421 by eike
repaired function definition statement.
805
                        + suite                                     .setResultsName('func_body')
806
                        )                                           .setParseAction(self._action_func_def) \
807
                                                                    .setFailAction(ChMsg(prepend='function definition: ')) \
361 by eike
More complex data statement is reflected in AST.
808
        '''
413 by eike
renamed ParseStage --> Parser
809
        if Parser.noTreeModification:
235 by eike
put practically all files into a package 'freeode'
810
            return None #No parse result modifications for debuging
474 by eike
The new Siml (user defined) functions are used by the interpreter. The old user defind functions are gone.
811
        toks = toks[0]             #Group adds an extra pair of brackets
812
        ncurr = NodeFuncDef()
813
        ncurr.loc = self.createTextLocation(loc) #Store position
361 by eike
More complex data statement is reflected in AST.
814
        #store function name
577 by eike
Remove all DotName objects from parser.
815
        ncurr.name = toks.func_name
474 by eike
The new Siml (user defined) functions are used by the interpreter. The old user defind functions are gone.
816
        #store function arguments: SimpleArgumentList performs some checks
427 by eike
attribute access works.
817
        if toks.arg_list:
474 by eike
The new Siml (user defined) functions are used by the interpreter. The old user defind functions are gone.
818
            ncurr.arguments = SimpleArgumentList(toks.arg_list.asList(), ncurr.loc)
523 by eike
Polished 'if' statement:
819
        else:
820
            #empty argument lists need a loc too.
821
            ncurr.arguments = SimpleArgumentList([], ncurr.loc)
362 by eike
Function definitions now store the additional information:
822
        #store return type
421 by eike
repaired function definition statement.
823
        if toks.return_type:
474 by eike
The new Siml (user defined) functions are used by the interpreter. The old user defind functions are gone.
824
            ncurr.return_type = toks.return_type
421 by eike
repaired function definition statement.
825
        #store function body; take each statement out of its sublist
426 by eike
defining and instantiating a class basically works.
826
        for sublist in toks.func_body:
474 by eike
The new Siml (user defined) functions are used by the interpreter. The old user defind functions are gone.
827
            ncurr.statements.append(sublist[0])
828
        return ncurr
235 by eike
put practically all files into a package 'freeode'
829
830
406 by eike
Indented (Python like) grammar seems to work.
831
    def _action_class_def(self, s, loc, toks): #IGNORE:W0613
235 by eike
put practically all files into a package 'freeode'
832
        '''
578 by eike
corrected docstring
833
        Create node for definition of a class:
406 by eike
Indented (Python like) grammar seems to work.
834
            class foo(a):
835
                inherit Model
836
                data myA: Real = a
574 by eike
test commit
837
235 by eike
put practically all files into a package 'freeode'
838
        BNF:
426 by eike
defining and instantiating a class basically works.
839
        class_stmt = Group(kw('class')
840
                         - newIdentifier                            .setResultsName('classname')
841
                         + Optional('(' - argument_list + ')' )     .setFailAction(ChMsg(prepend='constructor arguments: '))
842
                         + ':' + suite                              .setResultsName('class_body_stmts')
843
                         )                                          .setParseAction(self._action_class_def)\
844
                                                                    .setFailAction(ChMsg(prepend='class definition: '))
235 by eike
put practically all files into a package 'freeode'
845
        '''
413 by eike
renamed ParseStage --> Parser
846
        if Parser.noTreeModification:
351 by eike
corrected some typos
847
            return None #No parse result modifications for debugging
406 by eike
Indented (Python like) grammar seems to work.
848
        #tokList = toks.asList()[0] #the Group creates
235 by eike
put practically all files into a package 'freeode'
849
        toks = toks[0]             #an extra pair of brackets
577 by eike
Remove all DotName objects from parser.
850
        class_def = NodeClassDef()
851
        class_def.loc = self.createTextLocation(loc) #Store position
235 by eike
put practically all files into a package 'freeode'
852
        #store class name and name of super class
577 by eike
Remove all DotName objects from parser.
853
        class_def.name = toks.classname
854
        #store arguments of class statement - base classes - semantics currently undefined
426 by eike
defining and instantiating a class basically works.
855
        if toks.arg_list:
577 by eike
Remove all DotName objects from parser.
856
            class_def.arguments = toks.arg_list.asList()
574 by eike
test commit
857
        #store class body; take each statement out of its sublist
426 by eike
defining and instantiating a class basically works.
858
        for sublist in toks.class_body_stmts:
577 by eike
Remove all DotName objects from parser.
859
            class_def.statements.append(sublist[0])
860
        return class_def
235 by eike
put practically all files into a package 'freeode'
861
862
419 by eike
some repairing of parse actions
863
    def _action_module(self, s, loc, toks): #IGNORE:W0613
235 by eike
put practically all files into a package 'freeode'
864
        '''
374 by eike
More simple parse tree modifications + some code beautifications.
865
        Create the root node of a module.
235 by eike
put practically all files into a package 'freeode'
866
        BNF:
574 by eike
test commit
867
        module = (indentedBlock(statement, self.indentStack, indent=False)
419 by eike
some repairing of parse actions
868
                  + StringEnd())                                      .setParseAction(self._action_module)
235 by eike
put practically all files into a package 'freeode'
869
        '''
413 by eike
renamed ParseStage --> Parser
870
        if Parser.noTreeModification:
351 by eike
corrected some typos
871
            return None #No parse result modifications for debugging
574 by eike
test commit
872
        tokList = toks.asList()[0]
577 by eike
Remove all DotName objects from parser.
873
        module = NodeModule()
874
        module.loc = self.createTextLocation(loc) #Store position
875
        module.name = self.moduleName
419 by eike
some repairing of parse actions
876
        #take the sublists out of the nested lists that indentedBlock produces
877
        statements = []
878
        for sublist in tokList:
879
            statements.append(sublist[0])
577 by eike
Remove all DotName objects from parser.
880
        module.statements = statements
881
        return module
235 by eike
put practically all files into a package 'freeode'
882
883
405 by eike
working expression parser is now in SIML
884
#------------------- BNF ------------------------------------------------------------------------*
235 by eike
put practically all files into a package 'freeode'
885
    def _defineLanguageSyntax(self):
886
        '''
887
        Here is Siml's BNF
888
        Creates the objects of the pyParsing library,
889
        that do all the work.
890
        '''
891
        #define short alias so they don't clutter the text
892
        kw = self.defineKeyword # Usage: test = kw('variable')
893
        L = Literal # Usage: L('+')
404 by eike
Found pyparsing bug:
894
        S = Suppress
235 by eike
put practically all files into a package 'freeode'
895
406 by eike
Indented (Python like) grammar seems to work.
896
        #end of line terminates statements, so it is not regular whitespace
897
        ParserElement.setDefaultWhitespaceChars('\t ')
574 by eike
test commit
898
        #the matching end of line token
406 by eike
Indented (Python like) grammar seems to work.
899
        newline = LineEnd().suppress()
574 by eike
test commit
900
404 by eike
Found pyparsing bug:
901
#------------------ Literals .................................................................
902
235 by eike
put practically all files into a package 'freeode'
903
        #Integer (unsigned).
904
        uInteger = Word(nums)                                       .setName('uInteger')#.setDebug(True)
583 by eike
changed some comments
905
#        # TODO: Change for speedup
906
#        #Snippet taken from: donn <donn.ingle@gmail.com> who quotes Paul McGuire:
907
#        # "I'm finding that complex items like real numbers just work better
908
#        # using a Regex than Combine'ing Words, Optionals, etc."
909
#        floater = PP.Regex(r"-?\d+(\.\d*)?([Ee][+-]?\d+)?")
235 by eike
put practically all files into a package 'freeode'
910
        #Floating point number (unsigned).
911
        eE = CaselessLiteral( 'E' )
404 by eike
Found pyparsing bug:
912
        uFloat = Group( Combine(
235 by eike
put practically all files into a package 'freeode'
913
                    uInteger +
914
                    Optional('.' + Optional(uInteger)) +
417 by eike
some works towards expression evaluation done
915
                    Optional(eE + Word('+-'+nums, nums))))          .setParseAction(self._action_number)\
404 by eike
Found pyparsing bug:
916
                                                                    .setName('uFloat')#.setDebug(True)
235 by eike
put practically all files into a package 'freeode'
917
        #string
410 by eike
Syntax is now converted to '-' and ChMsg; ErrStop is removed.
918
        #TODO: good error message for missing 2nd quote in single line string
417 by eike
some works towards expression evaluation done
919
        stringLiteral = quotedString                                .setParseAction(self._action_string)\
235 by eike
put practically all files into a package 'freeode'
920
                                                                    .setName('string')#.setDebug(True)
404 by eike
Found pyparsing bug:
921
        literal = uFloat | stringLiteral
235 by eike
put practically all files into a package 'freeode'
922
363 by eike
Function generalization is complete.
923
#------------------ Identifiers .................................................................
404 by eike
Found pyparsing bug:
924
925
        #Built in variables, handled specially at attribute access.
544 by eike
small code beautification attempts.
926
        Parser.builtInVars = set(['time', 'this'])
399 by eike
stating to implement new syntax
927
        #identifiers
405 by eike
working expression parser is now in SIML
928
        identifierBase = Word(alphas+'_', alphanums+'_')            .setName('identifier')#.setDebug(True)
401 by eike
some nice and short parser for expressions exists
929
        # identifier:    Should be used in expressions. If a keyword is used an ordinary parse error is
930
        #                raised. This is needed to parse expressions containing the operators 'and', 'or', 'not'.
577 by eike
Remove all DotName objects from parser.
931
        identifier  =   identifierBase.copy()                       .setParseAction(self._actionCheckIdentifier)\
932
                                                                    .setParseAction(self._action_identifier)
401 by eike
some nice and short parser for expressions exists
933
        # newIdentifier: Should be used in definition of new objects (data, class, function).
934
        #                If a keyword is used as a identifier a fatal, user visible error is raised.
405 by eike
working expression parser is now in SIML
935
        newIdentifier = identifierBase.copy()                       .setParseAction(self._actionCheckIdentifierFatal)
404 by eike
Found pyparsing bug:
936
937
#------------------ Mathematical expression .............................................................
410 by eike
Syntax is now converted to '-' and ChMsg; ErrStop is removed.
938
        #Expression: mathematical, logtical, and comparison operators;
939
        # together with attribute access, function call, and slicing.
574 by eike
test commit
940
        #Forward declaration for recursive top level rule
404 by eike
Found pyparsing bug:
941
        expression = Forward()
942
943
        #Atoms are the most basic elements of expressions.
405 by eike
working expression parser is now in SIML
944
        #Brackets or braces are also categorized syntactically as atoms.
945
        #TODO: future extension: enclosures can also create tuples
526 by eike
converted and expanded example program
946
        #TODO: Inside brackets any number of newlines should be allowed!
947
        #      Look at setWhitespaceChars().
948
        enclosure = (S('(') - expression + S(')'))                  .setParseAction(self._action_parentheses_pair)
405 by eike
working expression parser is now in SIML
949
        atom = identifier | literal | enclosure
574 by eike
test commit
950
526 by eike
converted and expanded example program
951
        #TODO: Inside brackets any number of newlines should be allowed!
952
        #      Look at setWhitespaceChars().
404 by eike
Found pyparsing bug:
953
        #Function/method call: everything within the round brackets is parsed here;
405 by eike
working expression parser is now in SIML
954
        # the function name is parsed in 'expression'. This parser is quite general;
955
        # more syntax checks are done in parse action to generate better error messages.
956
        #
957
        #one argument at the call site: x=2.5  ,  x  ,  2.5
408 by eike
Indented grammar broken due to bug in pyparsing!
958
        keyword_argument = Group(identifier                         #.setResultsName('keyword')
959
                                 + '=' - expression                 #.setResultsName('value')
960
                                 )                                  .setResultsName('keyword_argument', listAllMatches=True)
961
        positional_argument = Group(expression)                     .setResultsName('positional_argument', listAllMatches=True)
405 by eike
working expression parser is now in SIML
962
        call_argument = Group(keyword_argument | positional_argument) #extra group to make setResultsName work
408 by eike
Indented grammar broken due to bug in pyparsing!
963
        argument_list = ( delimitedList(call_argument)              .setResultsName('argument_list')
405 by eike
working expression parser is now in SIML
964
                          + Optional(',') )
526 by eike
converted and expanded example program
965
        #TODO: Error message 'Function arguments: '
966
        call = Group('(' - Optional(argument_list) + ')')
404 by eike
Found pyparsing bug:
967
968
        #Slicing/subscription: everything within the rectangular brackets is parsed here;
969
        # the variable name is parsed in 'expression'
970
        #Look at Python documentation for possibly better parser.
971
        proper_slice = Group(Optional(expression) + L(':') + Optional(expression)
972
                             + Optional(L(':') + Optional(expression)))
973
        ellipsis = L('...')
974
        slice_item = ellipsis | proper_slice | expression
975
        slice_list = delimitedList(slice_item) + Optional(S(','))
976
        slicing = Group(S('[') - slice_list + S(']'))
977
410 by eike
Syntax is now converted to '-' and ChMsg; ErrStop is removed.
978
        #attribute access, function call, and slicing. (operators with the strongest binding come first.)
979
        expression_ex = operatorPrecedence(atom,
405 by eike
working expression parser is now in SIML
980
            [(L('.'),       2, opAssoc.LEFT,             self._action_op_infix_left), #access to an object's attributes
981
             (L('$'),       1, opAssoc.RIGHT,            self._action_op_prefix), #time differential
982
             (call,         1, opAssoc.LEFT,             self._action_func_call), #function/method call: f(23)
983
             (slicing,      1, opAssoc.LEFT,             self._action_slicing), #slicing/subscription: a[23]
444 by eike
Now a simple program can be parsed and interpreted!
984
             ], handleBrackets=False)                               .setName('expression_ex')
410 by eike
Syntax is now converted to '-' and ChMsg; ErrStop is removed.
985
986
        #Power and unary (sign) operators are intertwined, to get correct operator precedence:
987
        #   -a**-b == -(a ** (-b))
988
        # This can currently (of Pyparsing 1.5.0) not be expressed by operatorPrecedence
989
        power,  u_expr = Forward(), Forward()
990
        #Exponentiation: a**b;
991
        #Strongest binding on left side, weaker than unary operations (-a) on right side.
992
        power1 = Group(expression_ex + '**' + u_expr)               .setParseAction(self._action_op_infix)
993
        power << (power1 | expression_ex)
994
        #Unary arithmetic operations: -a; +a
995
        u_expr1 = Group(oneOf('- +') + u_expr)                      .setParseAction(self._action_op_prefix)
574 by eike
test commit
996
        u_expr << (u_expr1 | power)
997
410 by eike
Syntax is now converted to '-' and ChMsg; ErrStop is removed.
998
        #arithmetic, logtical, and comparison operators; the top level parser
574 by eike
test commit
999
        expression << operatorPrecedence(u_expr,
480 by eike
Added modulo (%) operator.
1000
            [(oneOf('* / %'), 2, opAssoc.LEFT,           self._action_op_infix_left),
405 by eike
working expression parser is now in SIML
1001
             (oneOf('+ -'), 2, opAssoc.LEFT,             self._action_op_infix_left),
1002
             (oneOf('< > <= >= == !='), 2, opAssoc.LEFT, self._action_op_infix_left),
1003
             (kw('not'),    1, opAssoc.RIGHT,            self._action_op_prefix),
1004
             (kw('and'),    2, opAssoc.LEFT,             self._action_op_infix_left),
1005
             (kw('or'),     2, opAssoc.LEFT,             self._action_op_infix_left),
444 by eike
Now a simple program can be parsed and interpreted!
1006
             ], handleBrackets=False)                               .setName('expression')
404 by eike
Found pyparsing bug:
1007
406 by eike
Indented (Python like) grammar seems to work.
1008
#------------------- STATEMEMTS -------------------------------------------------------------------------*
1009
#------------------- Simple statements ..................................................................
1010
574 by eike
test commit
1011
        #Return values from a function
408 by eike
Indented grammar broken due to bug in pyparsing!
1012
        return_stmt = (kw('return') - Optional(expression           .setResultsName('ret_val'))
422 by eike
small work on function call
1013
                      )                                             .setParseAction(self._action_return_stmt) \
408 by eike
Indented grammar broken due to bug in pyparsing!
1014
                                                                    .setFailAction(ChMsg(prepend='return statement: '))
399 by eike
stating to implement new syntax
1015
365 by eike
Added retun, pragma and foreign_code statements.
1016
        #pragma statement: tell any kind of options to the compiler
574 by eike
test commit
1017
        pragma_stmt = (kw('pragma')
408 by eike
Indented grammar broken due to bug in pyparsing!
1018
                       - OneOrMore(Word(alphanums+'_')))            .setParseAction(self._actionPragmaStmt) \
1019
                                                                    .setFailAction(ChMsg(prepend='pragma statement: '))
399 by eike
stating to implement new syntax
1020
406 by eike
Indented (Python like) grammar seems to work.
1021
#        #foreign code statement: specify code in the target language that is
1022
#        #inserted into the compiled module
1023
#        #    foreign_code python replace_call ::{{ sin(x) }}:: ;
1024
#        foreignCodeStmt = (kw('foreign_code')
1025
#                           + ES(Word(alphanums+'_')                  .setResultsName('language')
1026
#                                                                     .setName('language specification')
1027
#                                + Word(alphanums+'_')                .setResultsName('method')
1028
#                                                                     .setName('code insertion method')
1029
#                                + QuotedString(quoteChar='::{{',
1030
#                                               endQuoteChar='}}::')  .setResultsName('code')
1031
#                                                                     .setName('code to insert')
1032
#                                + stmtEnd)                           .setErrMsgStart('Foreign code statement: ')
1033
#                           )                                         .setParseAction(self._actionForeignCodeStmt)
399 by eike
stating to implement new syntax
1034
471 by eike
Changed from print statement to print function.
1035
#        #expression list - parse: 2, foo.bar, 3*sin(baz)
431 by eike
A working basic interpreter exists.
1036
        #TODO: create built in store function instead
235 by eike
put practically all files into a package 'freeode'
1037
        #store to disk
431 by eike
A working basic interpreter exists.
1038
#        store_stmt = Group(kw('save') - expression_list             .setResultsName('arg_list')
1039
#                          - Optional(','))                          .setParseAction(self._actionStoreStmt)\
1040
#                                                                    .setFailAction(ChMsg(prepend='save statement: '))
408 by eike
Indented grammar broken due to bug in pyparsing!
1041
510 by eike
implemented pass statement.
1042
        #pass statement, do nothing - necessary for empty compund statements
1043
        pass_stmt = kw('pass')                                      .setParseAction(self._action_pass_stmt)
1044
388 by eike
very small progress!
1045
        #compile a class
574 by eike
test commit
1046
        compile_stmt = (kw('compile')
431 by eike
A working basic interpreter exists.
1047
                        - Optional(newIdentifier                    .setResultsName('name')
574 by eike
test commit
1048
                                   + ':')
408 by eike
Indented grammar broken due to bug in pyparsing!
1049
                        + expression                                .setResultsName('class_name')
431 by eike
A working basic interpreter exists.
1050
                        )                                           .setParseAction(self._action_compile_stmt)\
408 by eike
Indented grammar broken due to bug in pyparsing!
1051
                                                                    .setFailAction(ChMsg(prepend='compile statement: '))
399 by eike
stating to implement new syntax
1052
406 by eike
Indented (Python like) grammar seems to work.
1053
        #compute expression and assign to value
441 by eike
First step at better type information, because every class is really a template.
1054
        assign_stmt = Group(expression_ex + '=' - expression)       .setParseAction(self._action_assign_stmt)\
408 by eike
Indented grammar broken due to bug in pyparsing!
1055
                                                                    .setFailAction(ChMsg(prepend='assignment statement: '))
1056
431 by eike
A working basic interpreter exists.
1057
        #Evaluate an expression (usually call a fuction); the result is discarded.
1058
        expression_stmt = Group(expression)                         .setParseAction(self._action_expression_stmt)
574 by eike
test commit
1059
406 by eike
Indented (Python like) grammar seems to work.
1060
        #------------ data statemnt -------------------------------------------------------------------------
1061
        #define parameters, variables, constants and submodels
574 by eike
test commit
1062
        #TODO: add 'save' - 'no_save' keywords
406 by eike
Indented (Python like) grammar seems to work.
1063
        #The roles of data (maybe call it storage class?):
1064
        #variable:    changes during the simulation
1065
        #parameter:   constant during a (dynamic?) simulation, can change beween simulations,
1066
        #             can be computed in the init function.
1067
        #constant:    must be known at compile time, may be optimized away,
1068
        #             the compiler may generate special code depending on the value.
420 by eike
some small improvements to interpreter.
1069
        #TODO: maybe add role for automatically created variables
574 by eike
test commit
1070
        attrRole = (  kw('state_variable') | kw('time_differential')  | kw('algebraic_variable')
1071
                    | kw('role_unknown')
408 by eike
Indented grammar broken due to bug in pyparsing!
1072
                    | kw('variable') | kw('param') | kw('const')      )
1073
        #parse: 'foo, bar, baz
574 by eike
test commit
1074
        newAttrList = delimitedList(newIdentifier)
406 by eike
Indented (Python like) grammar seems to work.
1075
        #parse 'data foo, bar: baz.boo parameter;
1076
        data_stmt = Group(kw('data')
408 by eike
Indented grammar broken due to bug in pyparsing!
1077
                          - newAttrList                             .setResultsName('attr_name_list')
1078
                          + ':' + expression                        .setResultsName('class_name')
1079
                          + Optional(attrRole)                      .setResultsName('attr_role')
1080
                          + Optional('=' - expression               .setResultsName('default_value')
1081
                                     )                              .setFailAction(ChMsg(prepend='default value: '))
419 by eike
some repairing of parse actions
1082
                          )                                         .setParseAction(self._action_data_def)\
408 by eike
Indented grammar broken due to bug in pyparsing!
1083
                                                                    .setFailAction(ChMsg(prepend='data definition: '))
574 by eike
test commit
1084
1085
        simple_stmt = (data_stmt| pass_stmt |#print_stmt |
1086
                       return_stmt | pragma_stmt | #store_stmt | graph_stmt |
431 by eike
A working basic interpreter exists.
1087
                       compile_stmt | assign_stmt |expression_stmt ).setName('simple statement')
406 by eike
Indented (Python like) grammar seems to work.
1088
1089
#------------- Compound statements ............................................................................
1090
        #body of compound statements
1091
        suite = Forward()
574 by eike
test commit
1092
410 by eike
Syntax is now converted to '-' and ChMsg; ErrStop is removed.
1093
        #------------- if ...........................................................................................
406 by eike
Indented (Python like) grammar seems to work.
1094
        #Flow control - if then else
519 by eike
Added comparison functions to IFloat.
1095
        if_stmt = Group \
1096
            (             (kw('if')   - expression + ':' + suite)   .setParseAction(self._action_if_clause)
574 by eike
test commit
1097
             + ZeroOrMore((kw('elif') - expression + ':' + suite)   .setParseAction(self._action_if_clause)
519 by eike
Added comparison functions to IFloat.
1098
                                                                    .setFailAction(ChMsg(prepend='elif: ')))
1099
             + Optional(  (kw('else') - ':' + suite)                .setParseAction(self._action_if_clause)
1100
                                                                    .setFailAction(ChMsg(prepend='else: ')))
1101
             )                                                      .setParseAction(self._action_if_statement) \
410 by eike
Syntax is now converted to '-' and ChMsg; ErrStop is removed.
1102
                                                                    .setFailAction(ChMsg(prepend='if statement: ')) \
1103
                                                                    .setName('if statement')
574 by eike
test commit
1104
406 by eike
Indented (Python like) grammar seems to work.
1105
        #------------- Function / Method ............................................................................
363 by eike
Function generalization is complete.
1106
        #Function definition (class method or global function)
1107
        #one argument of the definition: inX:Real=2.5
410 by eike
Syntax is now converted to '-' and ChMsg; ErrStop is removed.
1108
        func_def_arg = Group(identifier                             .setResultsName('name')
421 by eike
repaired function definition statement.
1109
                           + Optional(':' - expression              .setResultsName('type')
1110
                                      )                             .setFailAction(ChMsg(prepend='type specifier: '))
1111
                           + Optional('=' - expression              .setResultsName('default_value')
1112
                                      )                             .setFailAction(ChMsg(prepend='default value: '))
1113
                           )                                        .setParseAction(self._action_func_def_arg)
1114
        func_def_arg_list = (delimitedList(func_def_arg, ',')
1115
                             + Optional(','))
363 by eike
Function generalization is complete.
1116
        #the function: func doFoo(a:Real=2.5, b) -> Real {...}
410 by eike
Syntax is now converted to '-' and ChMsg; ErrStop is removed.
1117
        func_def_stmt = Group(kw('func') - newIdentifier            .setResultsName('func_name')
1118
                        + ('(' - Optional(func_def_arg_list         .setResultsName('arg_list'))
1119
                           + ')' )                                  .setFailAction(ChMsg(prepend='argument list: '))
1120
                        + Optional('->' - expression                .setResultsName('return_type')
1121
                                   )                                .setFailAction(ChMsg(prepend='return type: : '))
399 by eike
stating to implement new syntax
1122
                        + ':'
421 by eike
repaired function definition statement.
1123
                        + suite                                     .setResultsName('func_body')
1124
                        )                                           .setParseAction(self._action_func_def) \
410 by eike
Syntax is now converted to '-' and ChMsg; ErrStop is removed.
1125
                                                                    .setFailAction(ChMsg(prepend='function definition: ')) \
1126
                                                                    .setName('function definition')#.setDebug(True)
363 by eike
Function generalization is complete.
1127
406 by eike
Indented (Python like) grammar seems to work.
1128
        #---------- class  ......................................................................
574 by eike
test commit
1129
        #definition of a class
406 by eike
Indented (Python like) grammar seems to work.
1130
        #TODO: "inherit" statement
410 by eike
Syntax is now converted to '-' and ChMsg; ErrStop is removed.
1131
        class_stmt = Group(kw('class')
426 by eike
defining and instantiating a class basically works.
1132
                           - newIdentifier                          .setResultsName('classname')
1133
                           + Optional('(' - func_def_arg_list       .setResultsName('arg_list')
1134
                                      + ')' )                       .setFailAction(ChMsg(prepend='constructor arguments: '))
1135
                           + ':' + suite                            .setResultsName('class_body_stmts')
1136
                           )                                        .setParseAction(self._action_class_def)\
408 by eike
Indented grammar broken due to bug in pyparsing!
1137
                                                                    .setFailAction(ChMsg(prepend='class definition: '))
574 by eike
test commit
1138
1139
410 by eike
Syntax is now converted to '-' and ChMsg; ErrStop is removed.
1140
        compound_stmt = (class_stmt | func_def_stmt | if_stmt)
574 by eike
test commit
1141
1142
406 by eike
Indented (Python like) grammar seems to work.
1143
        #------ Statement, Suite -------------------------------------------------------------------------
1144
        # See: http://docs.python.org/ref/compound.html
1145
        #list of simple statements, separated by semicolon: a=1; b=2; print a, b
574 by eike
test commit
1146
        stmt_list = Group(delimitedList(Group(simple_stmt), ';')
1147
                     + Optional(Suppress(";")) )
1148
        #necessary for statement list (stmt_list) inside of block (stmt_block)
444 by eike
Now a simple program can be parsed and interpreted!
1149
        stmt_list_1 = stmt_list.copy()                              .setParseAction(self._action_stmt_list)
406 by eike
Indented (Python like) grammar seems to work.
1150
        #Statement: one line of code, or a compound (if, class, func) statement
574 by eike
test commit
1151
        #TODO: set a fail action that says 'Expected statement here.' ('Expected end of file' is misleading.)
526 by eike
converted and expanded example program
1152
        #TODO: good error messages for the parser are important.
574 by eike
test commit
1153
        statement = (  simple_stmt + newline
1154
                     | stmt_list_1 + newline
406 by eike
Indented (Python like) grammar seems to work.
1155
                     | compound_stmt         )
1156
        #And indented block of statements
444 by eike
Now a simple program can be parsed and interpreted!
1157
        stmt_block = indentedBlock(statement, self.indentStack)     #.setParseAction(self._action_stmt_list)
406 by eike
Indented (Python like) grammar seems to work.
1158
        #Body of class or function; the dependent code of 'if'
1159
        # Statement list and indented block of statements lead to the same AST
574 by eike
test commit
1160
        suite << ( stmt_list + newline | newline + stmt_block )     #IGNORE:W0104
1161
1162
#        #simple definition for debuging:
408 by eike
Indented grammar broken due to bug in pyparsing!
1163
#        #simple statements are terminated by newline
1164
#        #while the indented block eats all newlines after the compound statement
574 by eike
test commit
1165
#        statement = simple_stmt + newline | compound_stmt
408 by eike
Indented grammar broken due to bug in pyparsing!
1166
#        #a suite is an indented block of statements
1167
#        suite << indentedBlock(statement, self.indentStack)
574 by eike
test commit
1168
406 by eike
Indented (Python like) grammar seems to work.
1169
#---------- module ------------------------------------------------------------------------------------#
574 by eike
test commit
1170
        module = (indentedBlock(statement, self.indentStack, indent=False)
419 by eike
some repairing of parse actions
1171
                  + StringEnd())                                      .setParseAction(self._action_module)
574 by eike
test commit
1172
1173
        #workaround for pyparsing bug ???
1174
#        module = (ZeroOrMore(newline)
1175
#                  + ZeroOrMore(Group(statement) + ZeroOrMore(newline))
408 by eike
Indented grammar broken due to bug in pyparsing!
1176
#                  + StringEnd())
235 by eike
put practically all files into a package 'freeode'
1177
        #................ End of language definition ..................................................
1178
1179
        #determine start symbol
374 by eike
More simple parse tree modifications + some code beautifications.
1180
        startSymbol = module
235 by eike
put practically all files into a package 'freeode'
1181
        #set up comments
1182
        singleLineCommentCpp = '//' + restOfLine
1183
        singleLineCommentPy = '#' + restOfLine
1184
        startSymbol.ignore(singleLineCommentCpp)
1185
        startSymbol.ignore(singleLineCommentPy)
1186
        #no tab expansion
1187
        startSymbol.parseWithTabs()
1188
        #store parsers
1189
        self._parser = startSymbol
359 by eike
Change language to {} for block definitions.
1190
        self._expressionParser = expression
279 by eike
upped version to 0.3.1
1191
1192
235 by eike
put practically all files into a package 'freeode'
1193
    def parseExpressionStr(self, inString):
1194
        '''Parse a single expression. Example: 2*a+b'''
1195
        self.inputString = inString
1196
        return self._expressionParser.parseString(inString).asList()[0]
1197
1198
377 by eike
Aded:
1199
    def parseModuleStr(self, inProgram, fileName=None, moduleName=None):
368 by eike
Gave standard library an own module.
1200
        '''
1201
        Parse a whole program. The program is entered as a string.
399 by eike
stating to implement new syntax
1202
368 by eike
Gave standard library an own module.
1203
        Parameters
1204
        ----------
1205
        inProgram : str
1206
            A program in the Siml language. This might also be a module.
1207
        fileName : str
1208
            File name, so that good error messages can be generated.
399 by eike
stating to implement new syntax
1209
368 by eike
Gave standard library an own module.
1210
        Returns
1211
        -------
473 by eike
Some preparations for big function definition change.
1212
        AST: ast.Node
574 by eike
test commit
1213
           Abstract Syntax Tree (AST): A tree of ast.Node objects that
473 by eike
Some preparations for big function definition change.
1214
           represents the program text.
368 by eike
Gave standard library an own module.
1215
        '''
1216
        self.inputString = inProgram
1217
        if fileName is not None:
1218
            self.progFileName = fileName
377 by eike
Aded:
1219
        if moduleName is not None:
1220
            self.moduleName = moduleName
406 by eike
Indented (Python like) grammar seems to work.
1221
        #initialize the indentation stack (just to be sure)
1222
        self.indentStack = [1]
368 by eike
Gave standard library an own module.
1223
        #parse the program
1224
        try:
1225
            astTree = self._parser.parseString(inProgram).asList()[0]
1226
        except (ParseException, ParseFatalException), theError:
1227
            #make UserException that will be visible to the user
1228
            msgPyParsing = str(theError)
1229
            loc =  TextLocation(theError.loc, theError.pstr, self.progFileName)
1230
            raise UserException(msgPyParsing, loc)
1231
        return astTree
235 by eike
put practically all files into a package 'freeode'
1232
1233
377 by eike
Aded:
1234
    def parseModuleFile(self, fileName, moduleName=None):
235 by eike
put practically all files into a package 'freeode'
1235
        '''Parse a whole program. The program's file name is supplied.'''
340 by eike
convert input file name reliably to an absolute file name.
1236
        self.progFileName = os.path.abspath(fileName)
377 by eike
Aded:
1237
        #TODO: deduce from file name?
1238
        self.moduleName = moduleName
235 by eike
put practically all files into a package 'freeode'
1239
        #open and read the file
1240
        try:
340 by eike
convert input file name reliably to an absolute file name.
1241
            inputFile = open(self.progFileName, 'r')
235 by eike
put practically all files into a package 'freeode'
1242
            inputFileContents = inputFile.read()
1243
            inputFile.close()
1244
        except IOError, theError:
315 by eike
all error messages go to stderr now.
1245
            message = 'Could not read input file.\n' + str(theError)
1246
            raise UserException(message, None)
235 by eike
put practically all files into a package 'freeode'
1247
        #parse the program
373 by eike
small progress in creating the program tree
1248
        return self.parseModuleStr(inputFileContents)
235 by eike
put practically all files into a package 'freeode'
1249
1250
1251
1252
if __name__ == '__main__':
574 by eike
test commit
1253
    #TODO: add doctest tests.
474 by eike
The new Siml (user defined) functions are used by the interpreter. The old user defind functions are gone.
1254
    pass