eval.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. # Copyright (c) 2016, 2020 by Rocky Bernstein
  2. """Simple expression evaluator in SPARK
  3. """
  4. from expr_parser import parse_expr
  5. from spark_parser import GenericASTTraversal # , DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
  6. import operator
  7. BINARY_OPERATORS = {
  8. "**": pow,
  9. "*": operator.mul,
  10. "/": operator.truediv,
  11. "//": operator.floordiv,
  12. "%": operator.mod,
  13. "+": operator.add,
  14. "-": operator.sub,
  15. "<<": operator.lshift,
  16. ">>": operator.rshift,
  17. "&": operator.and_,
  18. "^": operator.xor,
  19. "|": operator.or_,
  20. }
  21. BINOP_SET = frozenset(BINARY_OPERATORS.keys())
  22. class ExprFormatterError(Exception):
  23. def __init__(self, errmsg):
  24. self.errmsg = errmsg
  25. def __str__(self):
  26. return self.errmsg
  27. class ExprEvaluator(GenericASTTraversal, object):
  28. def __init__(self):
  29. GenericASTTraversal.__init__(self, ast=None)
  30. self.ERROR = None
  31. return
  32. def traverse(self, node):
  33. self.preorder(node)
  34. return node.value
  35. def n_NUMBER(self, node):
  36. if node.attr.find("j") >= 0:
  37. node.value = complex(node.attr)
  38. elif node.attr.find(".") > 0:
  39. node.value = float(node.attr)
  40. else:
  41. node.value = int(node.attr)
  42. self.prune()
  43. def n_BOOL(self, node):
  44. node.value = node.attr
  45. self.prune()
  46. def n_atom(self, node):
  47. """atom ::= NUMBER | '(' expr ')' """
  48. length = len(node)
  49. if length == 1:
  50. self.preorder(node[0])
  51. node.value = node[0].value
  52. self.prune()
  53. elif length == 3:
  54. self.preorder(node[1])
  55. node.value = node[1].value
  56. self.prune()
  57. else:
  58. assert False, "Expecting atom to have length 1 or 3"
  59. def n_expr(self, node):
  60. """
  61. expr ::= expr ADD_OP term | term
  62. term ::= term MULT_OP term | atom
  63. """
  64. if len(node) == 1:
  65. self.preorder(node[0])
  66. node.value = node[0].value
  67. self.prune()
  68. else:
  69. self.preorder(node[0])
  70. self.preorder(node[2])
  71. op = node[1].attr
  72. assert op in BINOP_SET, "Expecting operator to be in %s" % op
  73. node.value = BINARY_OPERATORS[node[1].attr](node[0].value, node[2].value)
  74. self.prune()
  75. assert False, "Expecting atom to have length 1 or 3"
  76. n_factor = n_term = n_expr
  77. def eval_expr(
  78. expr_str,
  79. show_tokens=False,
  80. show_parse_tree=False,
  81. showgrammar=False,
  82. compile_mode="exec",
  83. ):
  84. """
  85. evaluate simple expression
  86. """
  87. parser_debug = {
  88. "rules": False,
  89. "transition": False,
  90. "reduce": showgrammar,
  91. "errorstack": True,
  92. "context": True,
  93. }
  94. parsed = parse_expr(expr_str, show_tokens=show_tokens, parser_debug=parser_debug)
  95. if show_parse_tree:
  96. print(parsed)
  97. assert parsed == "expr", "Should have parsed grammar start"
  98. evaluator = ExprEvaluator()
  99. # What we've been waiting for: Generate source from AST!
  100. return evaluator.traverse(parsed)
  101. if __name__ == "__main__":
  102. def eval_test(eval_str):
  103. result = eval_expr(
  104. eval_str, show_tokens=False, show_parse_tree=True, showgrammar=False,
  105. )
  106. print("%s = %s" % (eval_str, result))
  107. return
  108. eval_test("1")
  109. eval_test("1.0")
  110. eval_test("1 + 2")
  111. eval_test("1 * 2 + 3")
  112. eval_test("1 + 2 * 3")
  113. eval_test("(1 + 2) * 3")
  114. eval_test("(10.5 + 2 * 5) // (2 << 1) / 5")