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
|