Advertisement
Guest User

Untitled

a guest
Jan 14th, 2020
103
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.59 KB | None | 0 0
  1. import random
  2. import itertools
  3. import re
  4. import math
  5.  
  6. END_OF_STREAM_TOKEN = "\0"
  7.  
  8. class Node:
  9. def __init__(self, name, *args):
  10. self.name = name
  11. self.args = args
  12. def __repr__(self):
  13. params = "()"
  14. if len(self.args) > 1:
  15. params = str(tuple(self.args))
  16. elif len(self.args) == 1:
  17. params = f"({self.args[0]})"
  18. return f"{self.name}Node{params}"
  19.  
  20. def parse_expr(tokens):
  21. obj = parse_add_or_sub(tokens)
  22. if tokens[-1] is END_OF_STREAM_TOKEN:
  23. tokens.pop()
  24. return obj
  25. else:
  26. raise Exception("Expected end of token stream, got {repr(tokens[-1])} instead")
  27.  
  28. def parse_add_or_sub(tokens):
  29. left = parse_mul_or_div(tokens)
  30. if tokens[-1] in "+-":
  31. op = tokens.pop()
  32. right = parse_add_or_sub(tokens)
  33. return Node("BinOp", op, left, right)
  34. else:
  35. return left
  36.  
  37. def parse_mul_or_div(tokens):
  38. left = parse_atom(tokens)
  39. if tokens[-1] in "*/":
  40. op = tokens.pop()
  41. right = parse_mul_or_div(tokens)
  42. return Node("BinOp", op, left, right)
  43. else:
  44. return left
  45.  
  46. def parse_atom(tokens):
  47. if tokens[-1] == "(":
  48. tokens.pop()
  49. node = parse_add_or_sub(tokens)
  50. if tokens.pop() != ")": raise Exception("unexpected token")
  51. return node
  52. elif tokens[-1].isdigit():
  53. return Node("Int", int(tokens.pop()))
  54. else:
  55. raise Exception("Don't know how to parse atom with token {repr(tokens[-1])}")
  56.  
  57. def evaluate(node):
  58. if node.name == "BinOp":
  59. op, *args = node.args
  60. if len(args) == 1:
  61. return evaluate(args[0])
  62. else:
  63. assert len(args) == 2
  64. l,r = evaluate(args[0]), evaluate(args[1])
  65. if op == "+": return l+r
  66. elif op == "-": return l-r
  67. elif op == "*": return l*r
  68. elif op == "/": return l/r
  69. else: raise Exception(f"Don't know how to evaluate operator {repr(op)}")
  70.  
  71. elif node.name == "Int":
  72. return node.args[0]
  73. else:
  74. raise Exception(f"Don't know how to convert node with name {node.name}")
  75.  
  76. def tokenize(s):
  77. patterns = [
  78. re.compile(r"\d+"),
  79. re.compile(r"'[^']*'"),
  80. re.compile(r"[+*/-]"),
  81. re.compile(re.escape("(")),
  82. re.compile(re.escape(")")),
  83. re.compile(r"\s")
  84. ]
  85.  
  86. ignored = re.compile(r"\s")
  87.  
  88. tokens = []
  89. i = 0
  90. while i < len(s):
  91. for pattern in patterns:
  92. m = pattern.match(s[i:])
  93. if m:
  94. token = m.group()
  95. i += len(token)
  96. if not ignored.match(token):
  97. tokens.append(token)
  98. break
  99. else:
  100. raise Exception(f"No token pattern found for string {repr(s[i:])}")
  101. tokens.append(END_OF_STREAM_TOKEN)
  102. tokens.reverse()
  103. return tokens
  104.  
  105. s = "(2 + 2) * (2 + 2) / 23 - 42"
  106. tokens = tokenize(s)
  107.  
  108. ast = parse_expr(tokens)
  109. assert len(tokens) == 0, f"tokens remaining after parsing: {tokens}"
  110. print(s)
  111. #(2 + 2) * (2 + 2) / 23 - 42
  112.  
  113. print(ast)
  114. # BinOpNode(
  115. # '-',
  116. # BinOpNode(
  117. # '*',
  118. # BinOpNode(
  119. # '+',
  120. # IntNode(2),
  121. # IntNode(2)
  122. # ),
  123. # BinOpNode(
  124. # '/',
  125. # BinOpNode(
  126. # '+',
  127. # IntNode(2),
  128. # IntNode(2)
  129. # ),
  130. # IntNode(23)
  131. # )
  132. # ),
  133. # IntNode(42)
  134. # )
  135.  
  136. print(evaluate(ast))
  137.  
  138. #-41.30434782608695
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement