-
Notifications
You must be signed in to change notification settings - Fork 1
/
fparse.py
122 lines (103 loc) · 3.38 KB
/
fparse.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
"""
http://pyparsing.wikispaces.com/file/view/fourFn.py/30154950/fourFn.py
Copyright 2003-2006 by Paul McGuire
This is a modified version of fourFn.py by Paul McGuire
C.Perreau
"""
from pyparsing import Literal, CaselessLiteral, Word, Combine, Group, Optional, \
ZeroOrMore, Forward, nums, alphas
import math
import operator
import logging
exprStack = []
def pushFirst(strg, loc, toks):
exprStack.append(toks[0])
def pushUMinus(strg, loc, toks):
if toks and toks[0] == '-':
exprStack.append('unary -')
# ~ exprStack.append( '-1' )
#~ exprStack.append( '*' )
bnf = None
def BNF():
"""
expop :: '^'
multop :: '*' | '/'
addop :: '+' | '-'
integer :: ['+' | '-'] '0'..'9'+
atom :: PI | E | real | fn '(' expr ')' | '(' expr ')'
factor :: atom [ expop factor ]*
term :: factor [ multop factor ]*
expr :: term [ addop term ]*
"""
global bnf
if not bnf:
point = Literal(".")
e = Literal("E")
fnumber = Combine(Word("+-" + nums, nums) +
Optional(point + Optional(Word(nums))) +
Optional(e + Word("+-" + nums, nums)))
ident = Word(alphas, alphas + nums + "_$")
plus = Literal("+")
minus = Literal("-")
mult = Literal("*")
div = Literal("/")
lpar = Literal("(").suppress()
rpar = Literal(")").suppress()
addop = plus | minus
multop = mult | div
expop = Literal("^")
pi = CaselessLiteral("PI")
expr = Forward()
atom = (Optional("-") + ( pi | e | fnumber | ident + lpar + expr + rpar ).setParseAction(pushFirst) | (
lpar + expr.suppress() + rpar )).setParseAction(pushUMinus)
factor = Forward()
factor << atom + ZeroOrMore(( expop + factor ).setParseAction(pushFirst))
term = factor + ZeroOrMore(( multop + factor ).setParseAction(pushFirst))
expr << term + ZeroOrMore(( addop + term ).setParseAction(pushFirst))
bnf = expr
return bnf
# map operator symbols to corresponding arithmetic operations
epsilon = 1e-12
opn = {"+": operator.add,
"-": operator.sub,
"*": operator.mul,
"/": operator.truediv,
"^": operator.pow}
math_method = [method for method in dir(math) if callable(getattr(math, method))]
fn = {"trunc": lambda a: int(a),
"round": round,
"e": math.exp,
"sgn": lambda a: abs(a) > epsilon and cmp(a, 0) or 0}
for method in math_method:
fn[method] = getattr(math, method)
def evaluateStack(s):
op = s.pop()
if op == 'unary -':
return -evaluateStack(s)
if op in "+-*/^":
op2 = evaluateStack(s)
op1 = evaluateStack(s)
return opn[op](op1, op2)
elif op == "PI":
return math.pi # 3.1415926535
elif op in fn:
return fn[op](evaluateStack(s))
elif op[0].isalpha():
logging.warn("Warning : Couldn't evaluate : " + op)
return 0
else:
return float(op)
def fparse(expression):
def generated(**kwargs):
global exprStack
exprStack = []
fstring = expression
for var in kwargs.keys():
fstring = fstring.replace(var, str(kwargs[var]))
BNF().parseString(fstring)
val = evaluateStack(exprStack[:])
return val
return generated
if __name__ == "__main__":
result = fparse("e(x)")(x=1)
print result