one-file-projects/term.py

275 lines
7.2 KiB
Python

#!/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())