First math term calculator with SLR parser
This commit is contained in:
parent
e72ebf59e2
commit
5cfbbf8832
1 changed files with 275 additions and 0 deletions
275
term.py
Normal file
275
term.py
Normal file
|
@ -0,0 +1,275 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
class Token:
|
||||
def __init__(self,data=None):
|
||||
self.data = data
|
||||
def __repr__(self):
|
||||
return "%s<%s>" % (self.__class__.__name__, repr(self.data))
|
||||
|
||||
class AddOpToken(Token):
|
||||
pass
|
||||
|
||||
class SubtractOpToken(Token):
|
||||
pass
|
||||
|
||||
class MultiplyOpToken(Token):
|
||||
pass
|
||||
|
||||
class DivideOpToken(Token):
|
||||
pass
|
||||
|
||||
class NumToken(Token):
|
||||
pass
|
||||
|
||||
class PLeftToken(Token):
|
||||
pass
|
||||
|
||||
class PRightToken(Token):
|
||||
pass
|
||||
|
||||
class EOLToken(Token):
|
||||
pass
|
||||
|
||||
def lex(text):
|
||||
END = object()
|
||||
it = iter(text)
|
||||
c = next(it,END)
|
||||
while True:
|
||||
if c == END:
|
||||
break
|
||||
elif c in "0123456789":
|
||||
s = ""
|
||||
while c != END and c in "0123456789":
|
||||
s+=c
|
||||
c = next(it,END)
|
||||
yield NumToken(int(s))
|
||||
continue
|
||||
elif c == "+":
|
||||
yield AddOpToken()
|
||||
elif c == "-":
|
||||
yield SubtractOpToken()
|
||||
elif c == "*":
|
||||
yield MultiplyOpToken()
|
||||
elif c == "/":
|
||||
yield DivideOpToken()
|
||||
elif c == "(":
|
||||
yield PLeftToken()
|
||||
elif c == ")":
|
||||
yield PRightToken()
|
||||
elif c == " ":
|
||||
pass
|
||||
else:
|
||||
raise Exception("Unexpected character %s" % c)
|
||||
c = next(it,END)
|
||||
|
||||
class OperatorNode:
|
||||
def __init__(self, left, right):
|
||||
self.left = left
|
||||
self.right = right
|
||||
|
||||
def calculate(self):
|
||||
raise Exception("Not implemented")
|
||||
|
||||
def __repr__(self):
|
||||
return "%s < %s, %s >" % (self.__class__.__name__, repr(self.left), repr(self.right))
|
||||
|
||||
class AdditionNode(OperatorNode):
|
||||
def calculate(self):
|
||||
return self.left.calculate() + self.right.calculate()
|
||||
|
||||
class SubtractionNode(OperatorNode):
|
||||
def calculate(self):
|
||||
return self.left.calculate() - self.right.calculate()
|
||||
|
||||
class MultiplicationNode(OperatorNode):
|
||||
def calculate(self):
|
||||
return self.left.calculate() * self.right.calculate()
|
||||
|
||||
class DivisionNode(OperatorNode):
|
||||
def calculate(self):
|
||||
right = self.right.calculate()
|
||||
if right == 0:
|
||||
raise Exception("Division by Zero Error")
|
||||
return self.left.calculate() // right
|
||||
|
||||
class NumNode:
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def calculate(self):
|
||||
return self.value
|
||||
|
||||
def __repr__(self):
|
||||
return "NumNode < %d >" % self.value
|
||||
|
||||
# 1. S -> E
|
||||
# 2. E -> E+T
|
||||
# 3. E -> E-T
|
||||
# 4. E -> T
|
||||
# 5. T -> T*F
|
||||
# 6. T -> T/F
|
||||
# 7. T -> F
|
||||
# 8. F -> ( E )
|
||||
# 9. F -> num
|
||||
|
||||
def reduce_left_right(stack):
|
||||
stack.pop() #state
|
||||
right = stack.pop()
|
||||
stack.pop() #state
|
||||
stack.pop() #operator
|
||||
stack.pop() #state
|
||||
left = stack.pop()
|
||||
|
||||
return left, right
|
||||
|
||||
|
||||
def parse(text):
|
||||
it = lex(text)
|
||||
T_NUM = NumToken
|
||||
T_ADD = AddOpToken
|
||||
T_SUB = SubtractOpToken
|
||||
T_MUL = MultiplyOpToken
|
||||
T_DIV = DivideOpToken
|
||||
T_LPAR = PLeftToken
|
||||
T_RPAR = PRightToken
|
||||
END = EOLToken
|
||||
|
||||
NTS_E = object()
|
||||
NTS_T = object()
|
||||
NTS_F = object()
|
||||
|
||||
a = object()
|
||||
r = object()
|
||||
s = object()
|
||||
|
||||
table = [
|
||||
{ T_NUM: (s,4), T_LPAR: (s,3) },
|
||||
{ T_ADD: (s,8), T_SUB: (s,9), END: (a,0) },
|
||||
{ T_ADD: (r,4), T_SUB: (r,4), T_MUL: (s,12), T_DIV: (s,13), T_RPAR: (r,4), END: (r,4) },
|
||||
{ T_NUM: (s,4), T_LPAR: (s,3) },
|
||||
{ T_ADD: (r,9), T_SUB: (r,9), T_MUL: (r,9), T_DIV: (r,9), T_RPAR: (r,9), END: (r,9) },
|
||||
{ T_ADD: (r,7), T_SUB: (r,7), T_MUL: (r,7), T_DIV: (r,7), T_RPAR: (r,7), END: (r,7) },
|
||||
{ T_ADD: (s,8), T_SUB: (s,9), T_RPAR: (s,7) },
|
||||
{ T_ADD: (r,8), T_SUB: (r,8), T_MUL: (r,8), T_DIV: (r,8), T_RPAR: (r,8), END: (r,8) },
|
||||
{ T_NUM: (s,4), T_LPAR: (s,3) },
|
||||
{ T_NUM: (s,4), T_LPAR: (s,3) },
|
||||
{ T_ADD: (r,2), T_SUB: (r,2), T_MUL: (s,12), T_DIV: (s,13), T_RPAR: (r,2), END: (r,2) },
|
||||
{ T_ADD: (r,3), T_SUB: (r,3), T_MUL: (s,12), T_DIV: (s,13), T_RPAR: (r,3), END: (r,3) },
|
||||
{ T_NUM: (s,4), T_LPAR: (s,3) },
|
||||
{ T_NUM: (s,4), T_LPAR: (s,3) },
|
||||
{ T_ADD: (r,5), T_SUB: (r,5), T_MUL: (r,5), T_DIV: (r,5), T_RPAR: (r,5), END: (r,5) },
|
||||
{ T_ADD: (r,6), T_SUB: (r,6), T_MUL: (r,6), T_DIV: (r,6), T_RPAR: (r,6), END: (r,6) }
|
||||
]
|
||||
|
||||
goto = [
|
||||
{ NTS_E: 1, NTS_T:2, NTS_F: 5 },
|
||||
{},
|
||||
{},
|
||||
{ NTS_E: 6, NTS_T:2, NTS_F: 5 },
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
{ NTS_T:10, NTS_F: 5 },
|
||||
{ NTS_T:11, NTS_F: 5 },
|
||||
{},
|
||||
{},
|
||||
{ NTS_F: 14 },
|
||||
{ NTS_F: 15 },
|
||||
{},
|
||||
{}
|
||||
]
|
||||
|
||||
stack = []
|
||||
stack.append(0)
|
||||
lookahead = next(it,END())
|
||||
|
||||
while True:
|
||||
try:
|
||||
op, dat = table[stack[-1]][lookahead.__class__]
|
||||
except KeyError:
|
||||
raise Exception("Unexpected token: %s" % repr(lookahead))
|
||||
|
||||
if op == s:
|
||||
stack.append( lookahead )
|
||||
stack.append( dat )
|
||||
lookahead = next(it,END())
|
||||
elif op == r:
|
||||
# 1. S -> E
|
||||
# 2. E -> E+T
|
||||
# 3. E -> E-T
|
||||
# 4. E -> T
|
||||
# 5. T -> T*F
|
||||
# 6. T -> T/F
|
||||
# 7. T -> F
|
||||
# 8. F -> ( E )
|
||||
# 9. F -> num
|
||||
if dat == 2:
|
||||
left, right = reduce_left_right(stack)
|
||||
pstate = stack[-1]
|
||||
nts = NTS_E
|
||||
stack.append( AdditionNode( left, right ) )
|
||||
elif dat == 3:
|
||||
left, right = reduce_left_right(stack)
|
||||
pstate = stack[-1]
|
||||
nts = NTS_E
|
||||
stack.append( SubtractionNode( left, right ) )
|
||||
elif dat == 4:
|
||||
stack.pop() #state
|
||||
v = stack.pop()
|
||||
pstate = stack[-1]
|
||||
nts = NTS_E
|
||||
stack.append( v )
|
||||
elif dat == 5:
|
||||
left, right = reduce_left_right(stack)
|
||||
pstate = stack[-1]
|
||||
nts = NTS_T
|
||||
stack.append( MultiplicationNode( left, right ) )
|
||||
elif dat == 6:
|
||||
left, right = reduce_left_right(stack)
|
||||
pstate = stack[-1]
|
||||
nts = NTS_T
|
||||
stack.append( DivisionNode( left, right ) )
|
||||
elif dat == 7:
|
||||
stack.pop() #state
|
||||
v = stack.pop()
|
||||
pstate = stack[-1]
|
||||
nts = NTS_T
|
||||
stack.append( v )
|
||||
elif dat == 8:
|
||||
stack.pop() #state
|
||||
stack.pop() #rpar
|
||||
stack.pop() #state
|
||||
v = stack.pop() # inner
|
||||
stack.pop() #state
|
||||
stack.pop() #lpar
|
||||
pstate = stack[-1]
|
||||
nts = NTS_F
|
||||
stack.append( v )
|
||||
elif dat == 9:
|
||||
stack.pop() #state
|
||||
tok = stack.pop()
|
||||
pstate = stack[-1]
|
||||
nts = NTS_F
|
||||
stack.append( NumNode( tok.data ) )
|
||||
else:
|
||||
raise Exception("Rule %d not found" % dat)
|
||||
|
||||
try:
|
||||
stack.append( goto[pstate][nts] )
|
||||
except:
|
||||
raise Exception("WTF")
|
||||
elif op == a:
|
||||
break
|
||||
|
||||
stack.pop()
|
||||
return stack.pop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
l = input("> ")
|
||||
print('"%s"' % l)
|
||||
print(list(lex(l)))
|
||||
ast = parse(l)
|
||||
print(repr(ast))
|
||||
print(l," = ",ast.calculate())
|
||||
|
Loading…
Reference in a new issue