from parsimonious.grammar import Grammar
from parsimonious.nodes import NodeVisitor,rule
import math
from parsimonious.grammar import Grammar
grammar = Grammar("""
Expression
= ("clever hans "/"") question
question
= question_what / question_love
question_what = ("what is"/"what's"/"can you tell me"/"") " "? terms
question_love = "i love you" space "hans"?
terms
= op_add_subtract
op_add_subtract
= op_add_subtract_combo
/ op_multiply_divide
op_add_subtract_combo = op_multiply_divide (space (add/subtract) space op_multiply_divide)+
op_multiply_divide
= op_multiply_divide_combo
/ op_power
op_multiply_divide_combo = op_power (space (multiply/divide) space op_power)+
op_power
= op_power_combo
/ atom
op_power_combo = (atom space power space)+ atom
atom
= atom_unary
/ atom_square_root
/ atom_cube_root
/ atom_root
/ atom_function
/ number
atom_unary = number space unaryop
atom_square_root = (("the "? "square root" " of"?)/"root"/"route"/"√") space number
atom_cube_root = (("the "? "cube root" " of"?)) space number
atom_root = "the "? ordinal space "root of" space number
atom_function = (gcd/lcm) space terms space "and" space terms
unaryop
= "squared"
/ "cubed"
/ "factorial"
binaryop
= add
/ multiply
/ subtract
/ divide
/ power
add = ("+"/"add"/"plus")
multiply = ("×"/"x"/"times"/"multiplied by")
subtract = ("-"/"minus"/"take away"/"takeaway"/"take-away"/"subtract")
divide = ("÷"/"divided by"/"over")
power = ("^"/"to the power of"/"to the")
gcd = "the "? ("greatest common factor of"/"greatest common divisor of"/"gcd of"/"gcf of"/("biggest number that divides" " both"?))
lcm = "the "? ("least common multiple of"/"lcm of"/("smallest multiple of" " both"?))
space = (" "*)
number
= digit / number_word
number_word
= "one"
/ ("two"/"to"/"too")
/ "three"
/ ("four"/"for")
/ "five"
/ "six"
/ "seven"
/ ("eight"/"ate")
/ "nine"
/ "zero"
ordinal
= ordinal_digit / ordinal_word
ordinal_digit
= digit ("st"/"nd"/"rd"/"th")
ordinal_word
= "first"
/ "second"
/ "third"
/ "fourth"
/ "fifth"
/ "sixth"
/ "seventh"
/ "eighth"
/ "ninth"
/ "tenth"
digit = ~r"[0-9]+"
""")
ops = {
'+': lambda a,b:a+b,
'-': lambda a,b:a-b,
'*': lambda a,b:a*b,
'/': lambda a,b:a/b,
'^': lambda a,b:a**b,
'squared': lambda a:a*a,
}
unary_ops = {
'squared': lambda a:a*a,
'cubed': lambda a:a*a*a,
'factorial': math.factorial,
}
def gcd(a,b):
a,b = int(a),int(b)
if a<1 or b<1:
raise Exception("gcd of non-positive numbers")
if a<b:
return gcd(b,a)
while b!=0:
a,b = b,a%b
return a
def lcm(a,b):
return a*b//gcd(a,b)
function_ops = {
'gcd': gcd,
'lcm': lcm
}
class HansVisitor(NodeVisitor):
grammar = grammar
def visit_space(self,*args):
pass
def visit_digit(self,node,visited_children):
return int(node.text)
def visit_number_word(self,node,visited_children):
d = {
'one': 1,
'two': 2, 'to': 2, 'too': 2,
'three': 3,
'four': 4, 'for': 4,
'five': 5,
'six': 6,
'seven': 7,
'eight': 8, 'ate': 8,
'nine': 9,
'zero': 0
}
return d[node.text]
def visit_number(self,node,visited_children):
return visited_children[0]
visit_ordinal = NodeVisitor.lift_child
def visit_ordinal_digit(self,node,visited_children):
return visited_children[0]
def visit_ordinal_word(self,node,visited_children):
d = {
'first': 1,
'second': 2,
'third': 3,
'fourth': 4,
'fifth': 5,
'sixth': 6,
'seventh': 7,
'eighth': 8,
'ninth': 9,
'tenth': 10,
}
return d[node.text]
def visit_add(self,*args):
return '+'
def visit_multiply(self,*args):
return '*'
def visit_subtract(self,*args):
return '-'
def visit_divide(self,*args):
return '/'
def visit_power(self,*args):
return '^'
def generic_visit(self,node,visited_children):
if not node.expr_name:
return visited_children
return 'g '+node.expr_name
visit_op_add_subtract = NodeVisitor.lift_child
visit_op_multiply_divide = NodeVisitor.lift_child
visit_op_power = NodeVisitor.lift_child
visit_atom = NodeVisitor.lift_child
def visit_op_add_subtract_combo(self,node,visited_children):
a = visited_children[0]
for bit in visited_children[1]:
_,op,_,b = bit
op = op[0]
a = ops[op](a,b)
return a
visit_op_multiply_divide_combo = visit_op_add_subtract_combo
def visit_op_power_combo(self,node,visited_children):
a = visited_children[1]
for bit in visited_children[0][::-1]:
b,_,op,_ = bit
op = op[0]
a = ops[op](b,a)
return a
def visit_op_all_squared(self,node,visited_children):
return ('squared',None)
def visit_unaryop(self,node,visited_children):
return node.text
def visit_atom_unary(self,node,visited_children):
n,_,op = visited_children
return unary_ops[op](n)
def visit_atom_square_root(self,node,visited_children):
n = visited_children[-1]
return math.sqrt(n)
def visit_atom_cube_root(self,node,visited_children):
n = visited_children[-1]
return math.pow(n,1/3)
def visit_atom_root(self,node,visited_children):
_,r,_,_,_,n = visited_children
return math.pow(n,1/r)
def visit_gcd(self,node,visited_children):
return 'gcd'
def visit_lcm(self,node,visited_children):
return 'lcm'
def visit_atom_function(self,node,visited_children):
op,_,a,_,_,_,b = visited_children
op = op[0]
return function_ops[op](a,b)
def visit_question_what(self,node,visited_children):
return ('what',visited_children[-1])
def visit_question_love(self,*args):
return ('love',True)
def visit_question(self,node,visited_children):
return visited_children[0]
def visit_Expression(self,node,visited_children):
return visited_children[-1]
if __name__ == '__main__':
import sys
text = sys.argv[1]
visitor = HansVisitor()
result = visitor.parse(text)
print(': ',result)