fparse.py 3.4 KB

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