wolfhece 2.1.20__py3-none-any.whl → 2.1.21__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wolfhece/PyVertexvectors.py +72 -30
- wolfhece/apps/version.py +1 -1
- wolfhece/math_parser/__init__.py +855 -0
- wolfhece/math_parser/calculator.py +179 -0
- wolfhece/wolf_array.py +45 -50
- {wolfhece-2.1.20.dist-info → wolfhece-2.1.21.dist-info}/METADATA +2 -1
- {wolfhece-2.1.20.dist-info → wolfhece-2.1.21.dist-info}/RECORD +10 -8
- {wolfhece-2.1.20.dist-info → wolfhece-2.1.21.dist-info}/WHEEL +0 -0
- {wolfhece-2.1.20.dist-info → wolfhece-2.1.21.dist-info}/entry_points.txt +0 -0
- {wolfhece-2.1.20.dist-info → wolfhece-2.1.21.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,855 @@
|
|
1
|
+
#! /usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# Author: AxiaCore S.A.S. http://axiacore.com
|
4
|
+
#
|
5
|
+
# Based on js-expression-eval, by Matthew Crumley (email@matthewcrumley.com, http://silentmatt.com/)
|
6
|
+
# https://github.com/silentmatt/js-expression-eval
|
7
|
+
#
|
8
|
+
# Ported to Python and modified by Vera Mazhuga (ctrl-alt-delete@live.com, http://vero4ka.info/)
|
9
|
+
#
|
10
|
+
# You are free to use and modify this code in anyway you find useful. Please leave this comment in the code
|
11
|
+
# to acknowledge its original source. If you feel like it, I enjoy hearing about projects that use my code,
|
12
|
+
# but don't feel like you have to let me know or ask permission.
|
13
|
+
|
14
|
+
import math
|
15
|
+
import random
|
16
|
+
import re
|
17
|
+
|
18
|
+
TNUMBER = 0
|
19
|
+
TOP1 = 1
|
20
|
+
TOP2 = 2
|
21
|
+
TVAR = 3
|
22
|
+
TFUNCALL = 4
|
23
|
+
|
24
|
+
|
25
|
+
class Token():
|
26
|
+
|
27
|
+
def __init__(self, type_, index_, prio_, number_):
|
28
|
+
self.type_ = type_
|
29
|
+
self.index_ = index_ or 0
|
30
|
+
self.prio_ = prio_ or 0
|
31
|
+
self.number_ = number_ if number_ != None else 0
|
32
|
+
|
33
|
+
def toString(self):
|
34
|
+
if self.type_ == TNUMBER:
|
35
|
+
return self.number_
|
36
|
+
if self.type_ == TOP1 or self.type_ == TOP2 or self.type_ == TVAR:
|
37
|
+
return self.index_
|
38
|
+
elif self.type_ == TFUNCALL:
|
39
|
+
return 'CALL'
|
40
|
+
else:
|
41
|
+
return 'Invalid Token'
|
42
|
+
|
43
|
+
|
44
|
+
class Expression():
|
45
|
+
|
46
|
+
def __init__(self, tokens, ops1, ops2, functions):
|
47
|
+
self.tokens = tokens
|
48
|
+
self.ops1 = ops1
|
49
|
+
self.ops2 = ops2
|
50
|
+
self.functions = functions
|
51
|
+
|
52
|
+
def simplify(self, values):
|
53
|
+
values = values or {}
|
54
|
+
nstack = []
|
55
|
+
newexpression = []
|
56
|
+
L = len(self.tokens)
|
57
|
+
for i in range(0, L):
|
58
|
+
item = self.tokens[i]
|
59
|
+
type_ = item.type_
|
60
|
+
if type_ == TNUMBER:
|
61
|
+
nstack.append(item)
|
62
|
+
elif type_ == TVAR and item.index_ in values:
|
63
|
+
item = Token(TNUMBER, 0, 0, values[item.index_])
|
64
|
+
nstack.append(item)
|
65
|
+
elif type_ == TOP2 and len(nstack) > 1:
|
66
|
+
n2 = nstack.pop()
|
67
|
+
n1 = nstack.pop()
|
68
|
+
f = self.ops2[item.index_]
|
69
|
+
item = Token(TNUMBER, 0, 0, f(n1.number_, n2.number_))
|
70
|
+
nstack.append(item)
|
71
|
+
elif type_ == TOP1 and nstack:
|
72
|
+
n1 = nstack.pop()
|
73
|
+
f = self.ops1[item.index_]
|
74
|
+
item = Token(TNUMBER, 0, 0, f(n1.number_))
|
75
|
+
nstack.append(item)
|
76
|
+
else:
|
77
|
+
while len(nstack) > 0:
|
78
|
+
newexpression.append(nstack.pop(0))
|
79
|
+
newexpression.append(item)
|
80
|
+
while nstack:
|
81
|
+
newexpression.append(nstack.pop(0))
|
82
|
+
|
83
|
+
return Expression(newexpression, self.ops1, self.ops2, self.functions)
|
84
|
+
|
85
|
+
def substitute(self, variable, expr):
|
86
|
+
if not isinstance(expr, Expression):
|
87
|
+
expr = Parser().parse(str(expr))
|
88
|
+
newexpression = []
|
89
|
+
L = len(self.tokens)
|
90
|
+
for i in range(0, L):
|
91
|
+
item = self.tokens[i]
|
92
|
+
type_ = item.type_
|
93
|
+
if type_ == TVAR and item.index_ == variable:
|
94
|
+
for j in range(0, len(expr.tokens)):
|
95
|
+
expritem = expr.tokens[j]
|
96
|
+
replitem = Token(
|
97
|
+
expritem.type_,
|
98
|
+
expritem.index_,
|
99
|
+
expritem.prio_,
|
100
|
+
expritem.number_,
|
101
|
+
)
|
102
|
+
newexpression.append(replitem)
|
103
|
+
else:
|
104
|
+
newexpression.append(item)
|
105
|
+
|
106
|
+
ret = Expression(newexpression, self.ops1, self.ops2, self.functions)
|
107
|
+
return ret
|
108
|
+
|
109
|
+
def evaluate(self, values):
|
110
|
+
values = values or {}
|
111
|
+
nstack = []
|
112
|
+
L = len(self.tokens)
|
113
|
+
for item in self.tokens:
|
114
|
+
type_ = item.type_
|
115
|
+
if type_ == TNUMBER:
|
116
|
+
nstack.append(item.number_)
|
117
|
+
elif type_ == TOP2:
|
118
|
+
n2 = nstack.pop()
|
119
|
+
n1 = nstack.pop()
|
120
|
+
f = self.ops2[item.index_]
|
121
|
+
nstack.append(f(n1, n2))
|
122
|
+
elif type_ == TVAR:
|
123
|
+
if item.index_ in values:
|
124
|
+
nstack.append(values[item.index_])
|
125
|
+
elif item.index_ in self.functions:
|
126
|
+
nstack.append(self.functions[item.index_])
|
127
|
+
else:
|
128
|
+
raise Exception('undefined variable: ' + item.index_)
|
129
|
+
elif type_ == TOP1:
|
130
|
+
n1 = nstack.pop()
|
131
|
+
f = self.ops1[item.index_]
|
132
|
+
nstack.append(f(n1))
|
133
|
+
elif type_ == TFUNCALL:
|
134
|
+
n1 = nstack.pop()
|
135
|
+
f = nstack.pop()
|
136
|
+
if callable(f):
|
137
|
+
if type(n1) is list:
|
138
|
+
nstack.append(f(*n1))
|
139
|
+
else:
|
140
|
+
nstack.append(f(n1))
|
141
|
+
else:
|
142
|
+
raise Exception(f + ' is not a function')
|
143
|
+
else:
|
144
|
+
raise Exception('invalid Expression')
|
145
|
+
if len(nstack) > 1:
|
146
|
+
raise Exception('invalid Expression (parity)')
|
147
|
+
return nstack[0]
|
148
|
+
|
149
|
+
def toString(self, toJS=False):
|
150
|
+
nstack = []
|
151
|
+
L = len(self.tokens)
|
152
|
+
for i in range(0, L):
|
153
|
+
item = self.tokens[i]
|
154
|
+
type_ = item.type_
|
155
|
+
if type_ == TNUMBER:
|
156
|
+
if type(item.number_) == str:
|
157
|
+
nstack.append("'"+item.number_+"'")
|
158
|
+
else:
|
159
|
+
nstack.append( item.number_)
|
160
|
+
elif type_ == TOP2:
|
161
|
+
n2 = nstack.pop()
|
162
|
+
n1 = nstack.pop()
|
163
|
+
f = item.index_
|
164
|
+
if toJS and f == '^':
|
165
|
+
nstack.append('math.pow(' + n1 + ',' + n2 + ')')
|
166
|
+
else:
|
167
|
+
frm='({n1}{f}{n2})'
|
168
|
+
if f == ',':
|
169
|
+
frm = '{n1}{f}{n2}'
|
170
|
+
|
171
|
+
nstack.append(frm.format(
|
172
|
+
n1=n1,
|
173
|
+
n2=n2,
|
174
|
+
f=f,
|
175
|
+
))
|
176
|
+
|
177
|
+
|
178
|
+
elif type_ == TVAR:
|
179
|
+
nstack.append(item.index_)
|
180
|
+
elif type_ == TOP1:
|
181
|
+
n1 = nstack.pop()
|
182
|
+
f = item.index_
|
183
|
+
if f == '-':
|
184
|
+
nstack.append('(' + f + str(n1) + ')')
|
185
|
+
else:
|
186
|
+
nstack.append(f + '(' + str(n1) + ')')
|
187
|
+
elif type_ == TFUNCALL:
|
188
|
+
n1 = nstack.pop()
|
189
|
+
f = nstack.pop()
|
190
|
+
nstack.append(f + '(' + n1 + ')')
|
191
|
+
else:
|
192
|
+
raise Exception('invalid Expression')
|
193
|
+
if len(nstack) > 1:
|
194
|
+
raise Exception('invalid Expression (parity)')
|
195
|
+
return nstack[0]
|
196
|
+
|
197
|
+
def __str__(self):
|
198
|
+
return self.toString()
|
199
|
+
|
200
|
+
def symbols(self):
|
201
|
+
vars = []
|
202
|
+
for i in range(0, len(self.tokens)):
|
203
|
+
item = self.tokens[i]
|
204
|
+
if item.type_ == TVAR and not item.index_ in vars:
|
205
|
+
vars.append(item.index_)
|
206
|
+
return vars
|
207
|
+
|
208
|
+
def variables(self):
|
209
|
+
return [
|
210
|
+
sym for sym in self.symbols()
|
211
|
+
if sym not in self.functions]
|
212
|
+
|
213
|
+
|
214
|
+
class Parser:
|
215
|
+
|
216
|
+
PRIMARY = 1
|
217
|
+
OPERATOR = 2
|
218
|
+
FUNCTION = 4
|
219
|
+
LPAREN = 8
|
220
|
+
RPAREN = 16
|
221
|
+
COMMA = 32
|
222
|
+
SIGN = 64
|
223
|
+
CALL = 128
|
224
|
+
NULLARY_CALL = 256
|
225
|
+
|
226
|
+
def add(self, a, b):
|
227
|
+
return a + b
|
228
|
+
|
229
|
+
def norm(self, a, b):
|
230
|
+
return math.sqrt(a * a + b * b)
|
231
|
+
|
232
|
+
def Froude(self, a, b, c):
|
233
|
+
|
234
|
+
return math.sqrt(a * a + b * b) / math.sqrt(9.81 * c)
|
235
|
+
|
236
|
+
def sto(self, a, b):
|
237
|
+
return dict(a = b)
|
238
|
+
|
239
|
+
def sub(self, a, b):
|
240
|
+
return a - b
|
241
|
+
|
242
|
+
def mul(self, a, b):
|
243
|
+
return a * b
|
244
|
+
|
245
|
+
def div(self, a, b):
|
246
|
+
return a / b
|
247
|
+
|
248
|
+
def pow(self, a, b):
|
249
|
+
return a ** b
|
250
|
+
|
251
|
+
def mod(self, a, b):
|
252
|
+
return a % b
|
253
|
+
|
254
|
+
def concat(self, a, b,*args):
|
255
|
+
result=u'{0}{1}'.format(a, b)
|
256
|
+
for arg in args:
|
257
|
+
result=u'{0}{1}'.format(result, arg)
|
258
|
+
return result
|
259
|
+
|
260
|
+
def equal (self, a, b ):
|
261
|
+
return a == b
|
262
|
+
|
263
|
+
def notEqual (self, a, b ):
|
264
|
+
return a != b
|
265
|
+
|
266
|
+
def greaterThan (self, a, b ):
|
267
|
+
return a > b
|
268
|
+
|
269
|
+
def lessThan (self, a, b ):
|
270
|
+
return a < b
|
271
|
+
|
272
|
+
def greaterThanEqual (self, a, b ):
|
273
|
+
return a >= b
|
274
|
+
|
275
|
+
def lessThanEqual (self, a, b ):
|
276
|
+
return a <= b
|
277
|
+
|
278
|
+
def andOperator (self, a, b ):
|
279
|
+
return ( a and b )
|
280
|
+
|
281
|
+
def orOperator (self, a, b ):
|
282
|
+
return ( a or b )
|
283
|
+
|
284
|
+
def xorOperator (self, a, b ):
|
285
|
+
return ( a ^ b )
|
286
|
+
|
287
|
+
def inOperator(self, a, b):
|
288
|
+
return a in b
|
289
|
+
|
290
|
+
def notOperator(self, a):
|
291
|
+
return not a
|
292
|
+
|
293
|
+
def neg(self, a):
|
294
|
+
return -a
|
295
|
+
|
296
|
+
def random(self, a):
|
297
|
+
return random.random() * (a or 1)
|
298
|
+
|
299
|
+
def fac(self, a): # a!
|
300
|
+
return math.factorial(a)
|
301
|
+
|
302
|
+
def pyt(self, a, b):
|
303
|
+
return math.sqrt(a * a + b * b)
|
304
|
+
|
305
|
+
def sind(self, a):
|
306
|
+
return math.sin(math.radians(a))
|
307
|
+
|
308
|
+
def cosd(self, a):
|
309
|
+
return math.cos(math.radians(a))
|
310
|
+
|
311
|
+
def tand(self, a):
|
312
|
+
return math.tan(math.radians(a))
|
313
|
+
|
314
|
+
def asind(self, a):
|
315
|
+
return math.degrees(math.asin(a))
|
316
|
+
|
317
|
+
def acosd(self, a):
|
318
|
+
return math.degrees(math.acos(a))
|
319
|
+
|
320
|
+
def atand(self, a):
|
321
|
+
return math.degrees(math.atan(a))
|
322
|
+
|
323
|
+
def roll(self, a, b):
|
324
|
+
rolls = []
|
325
|
+
roll = 0
|
326
|
+
final = 0
|
327
|
+
for c in range(1, a):
|
328
|
+
roll = random.randint(1, b)
|
329
|
+
rolls.append(roll)
|
330
|
+
return rolls
|
331
|
+
|
332
|
+
def ifFunction(self,a,b,c):
|
333
|
+
return b if a else c
|
334
|
+
|
335
|
+
def append(self, a, b):
|
336
|
+
if type(a) != list:
|
337
|
+
return [a, b]
|
338
|
+
a.append(b)
|
339
|
+
return a
|
340
|
+
|
341
|
+
def __init__(self, string_literal_quotes = ("'", "\"")):
|
342
|
+
self.string_literal_quotes = string_literal_quotes
|
343
|
+
|
344
|
+
self.success = False
|
345
|
+
self.errormsg = ''
|
346
|
+
self.expression = ''
|
347
|
+
|
348
|
+
self.pos = 0
|
349
|
+
|
350
|
+
self.tokennumber = 0
|
351
|
+
self.tokenprio = 0
|
352
|
+
self.tokenindex = 0
|
353
|
+
self.tmpprio = 0
|
354
|
+
|
355
|
+
self.ops1 = {
|
356
|
+
'sin': math.sin,
|
357
|
+
'cos': math.cos,
|
358
|
+
'tan': math.tan,
|
359
|
+
'asin': math.asin,
|
360
|
+
'acos': math.acos,
|
361
|
+
'atan': math.atan,
|
362
|
+
|
363
|
+
'sind': self.sind,
|
364
|
+
'cosd': self.cosd,
|
365
|
+
'tand': self.tand,
|
366
|
+
'asind': self.asind,
|
367
|
+
'acosd': self.acosd,
|
368
|
+
'atand': self.atand,
|
369
|
+
|
370
|
+
'sqrt': math.sqrt,
|
371
|
+
'abs': abs,
|
372
|
+
'ceil': math.ceil,
|
373
|
+
'floor': math.floor,
|
374
|
+
'round': round,
|
375
|
+
'-': self.neg,
|
376
|
+
'not': self.notOperator,
|
377
|
+
'exp': math.exp,
|
378
|
+
}
|
379
|
+
|
380
|
+
self.ops2 = {
|
381
|
+
'+': self.add,
|
382
|
+
'-': self.sub,
|
383
|
+
'*': self.mul,
|
384
|
+
'/': self.div,
|
385
|
+
'%': self.mod,
|
386
|
+
'^': self.pow,
|
387
|
+
'**': self.pow,
|
388
|
+
',': self.append,
|
389
|
+
'||': self.concat,
|
390
|
+
"==": self.equal,
|
391
|
+
"!=": self.notEqual,
|
392
|
+
">": self.greaterThan,
|
393
|
+
"<": self.lessThan,
|
394
|
+
">=": self.greaterThanEqual,
|
395
|
+
"<=": self.lessThanEqual,
|
396
|
+
"and": self.andOperator,
|
397
|
+
"or": self.orOperator,
|
398
|
+
"xor": self.xorOperator,
|
399
|
+
"in": self.inOperator,
|
400
|
+
"D": self.roll,
|
401
|
+
}
|
402
|
+
|
403
|
+
self.functions = {
|
404
|
+
'random': self.random,
|
405
|
+
'fac': self.fac,
|
406
|
+
'log': math.log,
|
407
|
+
'min': min,
|
408
|
+
'max': max,
|
409
|
+
'pyt': self.pyt,
|
410
|
+
'pow': math.pow,
|
411
|
+
'atan2': math.atan2,
|
412
|
+
'concat':self.concat,
|
413
|
+
'if': self.ifFunction,
|
414
|
+
'norm': self.norm,
|
415
|
+
'Froude': self.Froude,
|
416
|
+
'sto': self.sto,
|
417
|
+
}
|
418
|
+
|
419
|
+
self.consts = {
|
420
|
+
'E': math.e,
|
421
|
+
'PI': math.pi,
|
422
|
+
'pi': math.pi,
|
423
|
+
'g':9.81
|
424
|
+
}
|
425
|
+
|
426
|
+
self.values = {
|
427
|
+
'sin': math.sin,
|
428
|
+
'cos': math.cos,
|
429
|
+
'tan': math.tan,
|
430
|
+
'asin': math.asin,
|
431
|
+
'acos': math.acos,
|
432
|
+
'atan': math.atan,
|
433
|
+
'sqrt': math.sqrt,
|
434
|
+
'log': math.log,
|
435
|
+
'abs': abs,
|
436
|
+
'ceil': math.ceil,
|
437
|
+
'floor': math.floor,
|
438
|
+
'round': round,
|
439
|
+
'random': self.random,
|
440
|
+
'fac': self.fac,
|
441
|
+
'exp': math.exp,
|
442
|
+
'min': min,
|
443
|
+
'max': max,
|
444
|
+
'pyt': self.pyt,
|
445
|
+
'pow': math.pow,
|
446
|
+
'atan2': math.atan2,
|
447
|
+
'E': math.e,
|
448
|
+
'PI': math.pi,
|
449
|
+
'g': 9.81,
|
450
|
+
'pi':math.pi
|
451
|
+
}
|
452
|
+
|
453
|
+
def parse(self, expr) -> Expression:
|
454
|
+
self.errormsg = ''
|
455
|
+
self.success = True
|
456
|
+
operstack = []
|
457
|
+
tokenstack = []
|
458
|
+
self.tmpprio = 0
|
459
|
+
expected = self.PRIMARY | self.LPAREN | self.FUNCTION | self.SIGN
|
460
|
+
noperators = 0
|
461
|
+
self.expression = expr
|
462
|
+
self.pos = 0
|
463
|
+
|
464
|
+
while self.pos < len(self.expression):
|
465
|
+
if self.isOperator():
|
466
|
+
if self.isSign() and expected & self.SIGN:
|
467
|
+
if self.isNegativeSign():
|
468
|
+
self.tokenprio = 5
|
469
|
+
self.tokenindex = '-'
|
470
|
+
noperators += 1
|
471
|
+
self.addfunc(tokenstack, operstack, TOP1)
|
472
|
+
expected = \
|
473
|
+
self.PRIMARY | self.LPAREN | self.FUNCTION | self.SIGN
|
474
|
+
elif self.isLogicalNot() and expected & self.SIGN:
|
475
|
+
self.tokenprio = 2
|
476
|
+
self.tokenindex = 'not'
|
477
|
+
noperators += 1
|
478
|
+
self.addfunc(tokenstack, operstack, TOP1)
|
479
|
+
expected = \
|
480
|
+
self.PRIMARY | self.LPAREN | self.FUNCTION | self.SIGN
|
481
|
+
elif self.isComment():
|
482
|
+
pass
|
483
|
+
else:
|
484
|
+
if expected and self.OPERATOR == 0:
|
485
|
+
self.error_parsing(self.pos, 'unexpected operator')
|
486
|
+
noperators += 2
|
487
|
+
self.addfunc(tokenstack, operstack, TOP2)
|
488
|
+
expected = \
|
489
|
+
self.PRIMARY | self.LPAREN | self.FUNCTION | self.SIGN
|
490
|
+
elif self.isNumber():
|
491
|
+
if expected and self.PRIMARY == 0:
|
492
|
+
self.error_parsing(self.pos, 'unexpected number')
|
493
|
+
token = Token(TNUMBER, 0, 0, self.tokennumber)
|
494
|
+
tokenstack.append(token)
|
495
|
+
expected = self.OPERATOR | self.RPAREN | self.COMMA
|
496
|
+
elif self.isString():
|
497
|
+
if (expected & self.PRIMARY) == 0:
|
498
|
+
self.error_parsing(self.pos, 'unexpected string')
|
499
|
+
token = Token(TNUMBER, 0, 0, self.tokennumber)
|
500
|
+
tokenstack.append(token)
|
501
|
+
expected = self.OPERATOR | self.RPAREN | self.COMMA
|
502
|
+
elif self.isLeftParenth():
|
503
|
+
if (expected & self.LPAREN) == 0:
|
504
|
+
self.error_parsing(self.pos, 'unexpected \"(\"')
|
505
|
+
if expected & self.CALL:
|
506
|
+
noperators += 2
|
507
|
+
self.tokenprio = -2
|
508
|
+
self.tokenindex = -1
|
509
|
+
self.addfunc(tokenstack, operstack, TFUNCALL)
|
510
|
+
expected = \
|
511
|
+
self.PRIMARY | self.LPAREN | self.FUNCTION | \
|
512
|
+
self.SIGN | self.NULLARY_CALL
|
513
|
+
elif self.isRightParenth():
|
514
|
+
if expected & self.NULLARY_CALL:
|
515
|
+
token = Token(TNUMBER, 0, 0, [])
|
516
|
+
tokenstack.append(token)
|
517
|
+
elif (expected & self.RPAREN) == 0:
|
518
|
+
self.error_parsing(self.pos, 'unexpected \")\"')
|
519
|
+
expected = \
|
520
|
+
self.OPERATOR | self.RPAREN | self.COMMA | \
|
521
|
+
self.LPAREN | self.CALL
|
522
|
+
elif self.isComma():
|
523
|
+
if (expected & self.COMMA) == 0:
|
524
|
+
self.error_parsing(self.pos, 'unexpected \",\"')
|
525
|
+
self.addfunc(tokenstack, operstack, TOP2)
|
526
|
+
noperators += 2
|
527
|
+
expected = \
|
528
|
+
self.PRIMARY | self.LPAREN | self.FUNCTION | self.SIGN
|
529
|
+
elif self.isConst():
|
530
|
+
if (expected & self.PRIMARY) == 0:
|
531
|
+
self.error_parsing(self.pos, 'unexpected constant')
|
532
|
+
consttoken = Token(TNUMBER, 0, 0, self.tokennumber)
|
533
|
+
tokenstack.append(consttoken)
|
534
|
+
expected = self.OPERATOR | self.RPAREN | self.COMMA
|
535
|
+
elif self.isOp2():
|
536
|
+
if (expected & self.FUNCTION) == 0:
|
537
|
+
self.error_parsing(self.pos, 'unexpected function')
|
538
|
+
self.addfunc(tokenstack, operstack, TOP2)
|
539
|
+
noperators += 2
|
540
|
+
expected = self.LPAREN
|
541
|
+
elif self.isOp1():
|
542
|
+
if (expected & self.FUNCTION) == 0:
|
543
|
+
self.error_parsing(self.pos, 'unexpected function')
|
544
|
+
self.addfunc(tokenstack, operstack, TOP1)
|
545
|
+
noperators += 1
|
546
|
+
expected = self.LPAREN
|
547
|
+
elif self.isVar():
|
548
|
+
if (expected & self.PRIMARY) == 0:
|
549
|
+
self.error_parsing(self.pos, 'unexpected variable')
|
550
|
+
vartoken = Token(TVAR, self.tokenindex, 0, 0)
|
551
|
+
tokenstack.append(vartoken)
|
552
|
+
expected = \
|
553
|
+
self.OPERATOR | self.RPAREN | \
|
554
|
+
self.COMMA | self.LPAREN | self.CALL
|
555
|
+
elif self.isWhite():
|
556
|
+
pass
|
557
|
+
else:
|
558
|
+
if self.errormsg == '':
|
559
|
+
self.error_parsing(self.pos, 'unknown character')
|
560
|
+
else:
|
561
|
+
self.error_parsing(self.pos, self.errormsg)
|
562
|
+
if self.tmpprio < 0 or self.tmpprio >= 10:
|
563
|
+
self.error_parsing(self.pos, 'unmatched \"()\"')
|
564
|
+
while len(operstack) > 0:
|
565
|
+
tmp = operstack.pop()
|
566
|
+
tokenstack.append(tmp)
|
567
|
+
if (noperators + 1) != len(tokenstack):
|
568
|
+
self.error_parsing(self.pos, 'parity')
|
569
|
+
|
570
|
+
return Expression(tokenstack, self.ops1, self.ops2, self.functions)
|
571
|
+
|
572
|
+
def evaluate(self, expr, variables):
|
573
|
+
return self.parse(expr).evaluate(variables)
|
574
|
+
|
575
|
+
def error_parsing(self, column, msg):
|
576
|
+
self.success = False
|
577
|
+
self.errormsg = 'parse error [column ' + str(column) + ']: ' + msg + ', expression: ' + self.expression
|
578
|
+
raise Exception(self.errormsg)
|
579
|
+
|
580
|
+
def addfunc(self, tokenstack, operstack, type_):
|
581
|
+
operator = Token(
|
582
|
+
type_,
|
583
|
+
self.tokenindex,
|
584
|
+
self.tokenprio + self.tmpprio,
|
585
|
+
0,
|
586
|
+
)
|
587
|
+
while len(operstack) > 0:
|
588
|
+
if operator.prio_ <= operstack[len(operstack) - 1].prio_:
|
589
|
+
tokenstack.append(operstack.pop())
|
590
|
+
else:
|
591
|
+
break
|
592
|
+
operstack.append(operator)
|
593
|
+
|
594
|
+
def isNumber(self):
|
595
|
+
r = False
|
596
|
+
|
597
|
+
if self.expression[self.pos] == 'E':
|
598
|
+
return False
|
599
|
+
|
600
|
+
# number in scientific notation
|
601
|
+
pattern = r'([-+]?([0-9]*\.?[0-9]*)[eE][-+]?[0-9]+).*'
|
602
|
+
match = re.match(pattern, self.expression[self.pos: ])
|
603
|
+
if match:
|
604
|
+
self.pos += len(match.group(1))
|
605
|
+
self.tokennumber = float(match.group(1))
|
606
|
+
return True
|
607
|
+
|
608
|
+
hex_pattern = r'(0x[0-9a-fA-F]+)'
|
609
|
+
match = re.match(hex_pattern, self.expression[self.pos: ])
|
610
|
+
if match:
|
611
|
+
self.pos += len(match.group(1))
|
612
|
+
self.tokennumber = int(match.group(1), base=16)
|
613
|
+
return True
|
614
|
+
|
615
|
+
# number in decimal
|
616
|
+
str = ''
|
617
|
+
while self.pos < len(self.expression):
|
618
|
+
code = self.expression[self.pos]
|
619
|
+
if (code >= '0' and code <= '9') or code == '.':
|
620
|
+
if (len(str) == 0 and code == '.' ):
|
621
|
+
str = '0'
|
622
|
+
str += code
|
623
|
+
self.pos += 1
|
624
|
+
try:
|
625
|
+
self.tokennumber = int(str)
|
626
|
+
except ValueError:
|
627
|
+
self.tokennumber = float(str)
|
628
|
+
r = True
|
629
|
+
else:
|
630
|
+
break
|
631
|
+
return r
|
632
|
+
|
633
|
+
def unescape(self, v, pos):
|
634
|
+
buffer = []
|
635
|
+
escaping = False
|
636
|
+
|
637
|
+
for i in range(0, len(v)):
|
638
|
+
c = v[i]
|
639
|
+
|
640
|
+
if escaping:
|
641
|
+
if c == "'":
|
642
|
+
buffer.append("'")
|
643
|
+
break
|
644
|
+
elif c == '\\':
|
645
|
+
buffer.append('\\')
|
646
|
+
break
|
647
|
+
elif c == '/':
|
648
|
+
buffer.append('/')
|
649
|
+
break
|
650
|
+
elif c == 'b':
|
651
|
+
buffer.append('\b')
|
652
|
+
break
|
653
|
+
elif c == 'f':
|
654
|
+
buffer.append('\f')
|
655
|
+
break
|
656
|
+
elif c == 'n':
|
657
|
+
buffer.append('\n')
|
658
|
+
break
|
659
|
+
elif c == 'r':
|
660
|
+
buffer.append('\r')
|
661
|
+
break
|
662
|
+
elif c == 't':
|
663
|
+
buffer.append('\t')
|
664
|
+
break
|
665
|
+
elif c == 'u':
|
666
|
+
# interpret the following 4 characters
|
667
|
+
# as the hex of the unicode code point
|
668
|
+
codePoint = int(v[i + 1, i + 5], 16)
|
669
|
+
buffer.append(chr(codePoint))
|
670
|
+
i += 4
|
671
|
+
break
|
672
|
+
else:
|
673
|
+
raise self.error_parsing(
|
674
|
+
pos + i,
|
675
|
+
'Illegal escape sequence: \'\\' + c + '\'',
|
676
|
+
)
|
677
|
+
escaping = False
|
678
|
+
else:
|
679
|
+
if c == '\\':
|
680
|
+
escaping = True
|
681
|
+
else:
|
682
|
+
buffer.append(c)
|
683
|
+
|
684
|
+
return ''.join(buffer)
|
685
|
+
|
686
|
+
def isString(self):
|
687
|
+
r = False
|
688
|
+
str = ''
|
689
|
+
startpos = self.pos
|
690
|
+
if self.pos < len(self.expression) and self.expression[self.pos] in self.string_literal_quotes:
|
691
|
+
quote_type = self.expression[self.pos]
|
692
|
+
self.pos += 1
|
693
|
+
while self.pos < len(self.expression):
|
694
|
+
code = self.expression[self.pos]
|
695
|
+
if code != quote_type or (str != '' and str[-1] == '\\'):
|
696
|
+
str += self.expression[self.pos]
|
697
|
+
self.pos += 1
|
698
|
+
else:
|
699
|
+
self.pos += 1
|
700
|
+
self.tokennumber = self.unescape(str, startpos)
|
701
|
+
r = True
|
702
|
+
break
|
703
|
+
return r
|
704
|
+
|
705
|
+
def isConst(self):
|
706
|
+
for i in self.consts:
|
707
|
+
L = len(i)
|
708
|
+
str = self.expression[self.pos:self.pos+L]
|
709
|
+
if i == str:
|
710
|
+
if len(self.expression) <= self.pos + L:
|
711
|
+
self.tokennumber = self.consts[i]
|
712
|
+
self.pos += L
|
713
|
+
return True
|
714
|
+
if not self.expression[self.pos + L].isalnum() and self.expression[self.pos + L] != "_":
|
715
|
+
self.tokennumber = self.consts[i]
|
716
|
+
self.pos += L
|
717
|
+
return True
|
718
|
+
return False
|
719
|
+
|
720
|
+
def isOperator(self):
|
721
|
+
ops = (
|
722
|
+
('**', 8, '**'),
|
723
|
+
('^', 8, '^'),
|
724
|
+
('%', 6, '%'),
|
725
|
+
('/', 6, '/'),
|
726
|
+
(u'\u2219', 5, '*'), # bullet operator
|
727
|
+
(u'\u2022', 5, '*'), # black small circle
|
728
|
+
('*', 5, '*'),
|
729
|
+
('+', 4, '+'),
|
730
|
+
('-', 4, '-'),
|
731
|
+
('||', 3, '||'),
|
732
|
+
('==', 3, '=='),
|
733
|
+
('!=', 3, '!='),
|
734
|
+
('<=', 3, '<='),
|
735
|
+
('>=', 3, '>='),
|
736
|
+
('<', 3, '<'),
|
737
|
+
('>', 3, '>'),
|
738
|
+
('in ', 3, 'in'),
|
739
|
+
('not ', 2, 'not'),
|
740
|
+
('and ', 1, 'and'),
|
741
|
+
('xor ', 0, 'xor'),
|
742
|
+
('or ', 0, 'or'),
|
743
|
+
)
|
744
|
+
for token, priority, index in ops:
|
745
|
+
if self.expression.startswith(token, self.pos):
|
746
|
+
self.tokenprio = priority
|
747
|
+
self.tokenindex = index
|
748
|
+
self.pos += len(token)
|
749
|
+
return True
|
750
|
+
return False
|
751
|
+
|
752
|
+
def isSign(self):
|
753
|
+
code = self.expression[self.pos - 1]
|
754
|
+
return (code == '+') or (code == '-')
|
755
|
+
|
756
|
+
def isPositiveSign(self):
|
757
|
+
code = self.expression[self.pos - 1]
|
758
|
+
return code == '+'
|
759
|
+
|
760
|
+
def isNegativeSign(self):
|
761
|
+
code = self.expression[self.pos - 1]
|
762
|
+
return code == '-'
|
763
|
+
|
764
|
+
def isLogicalNot(self):
|
765
|
+
code = self.expression[self.pos - 4: self.pos]
|
766
|
+
return code == 'not '
|
767
|
+
|
768
|
+
def isLeftParenth(self):
|
769
|
+
code = self.expression[self.pos]
|
770
|
+
if code == '(':
|
771
|
+
self.pos += 1
|
772
|
+
self.tmpprio += 10
|
773
|
+
return True
|
774
|
+
return False
|
775
|
+
|
776
|
+
def isRightParenth(self):
|
777
|
+
code = self.expression[self.pos]
|
778
|
+
if code == ')':
|
779
|
+
self.pos += 1
|
780
|
+
self.tmpprio -= 10
|
781
|
+
return True
|
782
|
+
return False
|
783
|
+
|
784
|
+
def isComma(self):
|
785
|
+
code = self.expression[self.pos]
|
786
|
+
if code==',':
|
787
|
+
self.pos+=1
|
788
|
+
self.tokenprio=-1
|
789
|
+
self.tokenindex=","
|
790
|
+
return True
|
791
|
+
return False
|
792
|
+
|
793
|
+
def isWhite(self):
|
794
|
+
code = self.expression[self.pos]
|
795
|
+
if code.isspace():
|
796
|
+
self.pos += 1
|
797
|
+
return True
|
798
|
+
return False
|
799
|
+
|
800
|
+
def isOp1(self):
|
801
|
+
str = ''
|
802
|
+
for i in range(self.pos, len(self.expression)):
|
803
|
+
c = self.expression[i]
|
804
|
+
if c.upper() == c.lower():
|
805
|
+
if i == self.pos or (c != '_' and (c < '0' or c > '9')):
|
806
|
+
break
|
807
|
+
str += c
|
808
|
+
if len(str) > 0 and str in self.ops1:
|
809
|
+
self.tokenindex = str
|
810
|
+
self.tokenprio = 9
|
811
|
+
self.pos += len(str)
|
812
|
+
return True
|
813
|
+
return False
|
814
|
+
|
815
|
+
def isOp2(self):
|
816
|
+
str = ''
|
817
|
+
for i in range(self.pos, len(self.expression)):
|
818
|
+
c = self.expression[i]
|
819
|
+
if c.upper() == c.lower():
|
820
|
+
if i == self.pos or (c != '_' and (c < '0' or c > '9')):
|
821
|
+
break
|
822
|
+
str += c
|
823
|
+
if len(str) > 0 and (str in self.ops2):
|
824
|
+
self.tokenindex = str
|
825
|
+
self.tokenprio = 9
|
826
|
+
self.pos += len(str)
|
827
|
+
return True
|
828
|
+
return False
|
829
|
+
|
830
|
+
def isVar(self):
|
831
|
+
str = ''
|
832
|
+
inQuotes = False
|
833
|
+
for i in range(self.pos, len(self.expression)):
|
834
|
+
c = self.expression[i]
|
835
|
+
if c.lower() == c.upper():
|
836
|
+
if ((i == self.pos and c != '"') or (not (c in '_."') and (c < '0' or c > '9'))) and not inQuotes :
|
837
|
+
break
|
838
|
+
if c == '"':
|
839
|
+
inQuotes = not inQuotes
|
840
|
+
str += c
|
841
|
+
if str:
|
842
|
+
self.tokenindex = str
|
843
|
+
self.tokenprio = 6
|
844
|
+
self.pos += len(str)
|
845
|
+
return True
|
846
|
+
return False
|
847
|
+
|
848
|
+
def isComment(self):
|
849
|
+
code = self.expression[self.pos - 1]
|
850
|
+
if code == '/' and self.expression[self.pos] == '*':
|
851
|
+
self.pos = self.expression.index('*/', self.pos) + 2
|
852
|
+
if self.pos == 1:
|
853
|
+
self.pos = len(self.expression)
|
854
|
+
return True
|
855
|
+
return False
|