297 lines
No EOL
7.1 KiB
Lua
297 lines
No EOL
7.1 KiB
Lua
Token=function(type,value)
|
|
return {"type": type, "value": value}
|
|
end function
|
|
|
|
Lexer=function(text)
|
|
out={}
|
|
out.text=text
|
|
out.pos=0
|
|
out.currChar=out.text[out.pos]
|
|
|
|
out.error=function()
|
|
exit("Invalid character!")
|
|
end function
|
|
|
|
out.advance=function(self)
|
|
self.pos=self.pos+1
|
|
if self.pos > self.text.len-1 then
|
|
self.currChar=null
|
|
else
|
|
self.currChar=self.text[self.pos]
|
|
end if
|
|
end function
|
|
|
|
out.skip_ws=function(self)
|
|
while self.currChar and self.currChar == " "
|
|
self.advance
|
|
end while
|
|
end function
|
|
|
|
out.integer=function(self)
|
|
result=""
|
|
while self.currChar != null and (typeof(self.currChar.to_int) == "number" or self.currChar == ".")
|
|
result=result+self.currChar
|
|
self.advance
|
|
end while
|
|
return result.val
|
|
end function
|
|
|
|
out.gnt=function(self)
|
|
while self.currChar
|
|
|
|
if self.currChar == " " then
|
|
self.skip_ws
|
|
continue
|
|
end if
|
|
|
|
if typeof(self.currChar.to_int) == "number" then
|
|
return Token("INT",self.integer)
|
|
end if
|
|
|
|
if self.currChar == "+" then
|
|
self.advance
|
|
return Token("ADD","+")
|
|
end if
|
|
|
|
if self.currChar == "-" then
|
|
self.advance
|
|
return Token("SUB","-")
|
|
end if
|
|
|
|
if self.currChar == "*" then
|
|
self.advance
|
|
return Token("MUL","*")
|
|
end if
|
|
|
|
if self.currChar == "/" then
|
|
self.advance
|
|
return Token("DIV","/")
|
|
end if
|
|
|
|
if self.currChar == "^" then
|
|
self.advance
|
|
return Token("EXP","^")
|
|
end if
|
|
|
|
if self.currChar == "%" then
|
|
self.advance
|
|
return Token("MOD","%")
|
|
end if
|
|
|
|
if self.currChar == "(" then
|
|
self.advance
|
|
return Token("LPAREN","(")
|
|
end if
|
|
|
|
if self.currChar == ")" then
|
|
self.advance
|
|
return Token("RPAREN",")")
|
|
end if
|
|
|
|
self.error
|
|
end while
|
|
|
|
return Token("EOF",null)
|
|
end function
|
|
|
|
return out
|
|
end function
|
|
|
|
AST=function()
|
|
return {}
|
|
end function
|
|
|
|
BinOp=function(left,op,right)
|
|
out=AST
|
|
out.left=left
|
|
out.op=op
|
|
out.token=op
|
|
out.right=right
|
|
out.classID="BinOp"
|
|
return out
|
|
end function
|
|
|
|
Num=function(token)
|
|
out=AST
|
|
out.token=token
|
|
out.value=token.value
|
|
out.classID="Num"
|
|
return out
|
|
end function
|
|
|
|
UnaryOp=function(op,expr)
|
|
out=AST
|
|
out.op=op
|
|
out.token=op
|
|
out.expr=expr
|
|
out.classID="UnaryOp"
|
|
return out
|
|
end function
|
|
|
|
Parser=function(lexer)
|
|
out={}
|
|
out.lexer=lexer
|
|
out.ct=out.lexer.gnt
|
|
|
|
out.error=function()
|
|
exit("Invalid syntax!")
|
|
end function
|
|
|
|
out.eat=function(self,ttype)
|
|
if self.ct.type == ttype then self.ct=self.lexer.gnt else self.error
|
|
end function
|
|
|
|
out.factor=function(self)
|
|
token=self.ct
|
|
if token.type == "ADD" then
|
|
self.eat("ADD")
|
|
node=UnaryOp(token,self.factor)
|
|
return node
|
|
else if token.type == "SUB" then
|
|
self.eat("SUB")
|
|
node=UnaryOp(token,self.factor)
|
|
return node
|
|
else if token.type == "INT" then
|
|
self.eat("INT")
|
|
return Num(token)
|
|
else if token.type == "LPAREN" then
|
|
self.eat("LPAREN")
|
|
node=self.expr
|
|
self.eat("RPAREN")
|
|
return node
|
|
end if
|
|
end function
|
|
|
|
out.exp=function(self)
|
|
node=self.factor
|
|
|
|
while self.ct.type == "EXP"
|
|
token=self.ct
|
|
self.eat("EXP")
|
|
node=BinOp(node,token,self.factor)
|
|
end while
|
|
return node
|
|
end function
|
|
|
|
out.term=function(self)
|
|
node=self.exp
|
|
|
|
while ["MUL","DIV","MOD"].indexOf(self.ct.type) != null
|
|
token=self.ct
|
|
if token.type == "MUL" then
|
|
self.eat("MUL")
|
|
else if token.type == "DIV" then
|
|
self.eat("DIV")
|
|
else if token.type == "MOD" then
|
|
self.eat("MOD")
|
|
end if
|
|
|
|
node=BinOp(node,token,self.exp)
|
|
end while
|
|
return node
|
|
end function
|
|
|
|
out.expr=function(self)
|
|
node=self.term
|
|
|
|
while ["ADD","SUB"].indexOf(self.ct.type) != null
|
|
token=self.ct
|
|
if token.type == "ADD" then
|
|
self.eat("ADD")
|
|
else if token.type == "SUB" then
|
|
self.eat("SUB")
|
|
end if
|
|
|
|
node=BinOp(node,token,self.term)
|
|
end while
|
|
return node
|
|
end function
|
|
|
|
out.parse=function(self)
|
|
return self.expr
|
|
end function
|
|
return out
|
|
end function
|
|
|
|
NodeVisitor=function()
|
|
out={}
|
|
out.visit=function(self,node)
|
|
method="visit_"+typeof(node)
|
|
if not self.hasIndex(method) then self.generic_visit
|
|
result=null
|
|
if method == "visit_BinOp" then
|
|
result=self.visit_BinOp(node)
|
|
else if method == "visit_Num" then
|
|
result=self.visit_Num(node)
|
|
else if method == "visit_UnaryOp" then
|
|
result=self.visit_UnaryOp(node)
|
|
end if
|
|
return result
|
|
end function
|
|
|
|
out.generic_visit=function()
|
|
exit("No visit method")
|
|
end function
|
|
return out
|
|
end function
|
|
|
|
Interpreter=function(parser)
|
|
out=NodeVisitor
|
|
out.parser=parser
|
|
|
|
out.visit_BinOp=function(self,node)
|
|
if node.op.type == "ADD" then
|
|
return self.visit(node.left) + self.visit(node.right)
|
|
else if node.op.type == "SUB" then
|
|
return self.visit(node.left) - self.visit(node.right)
|
|
else if node.op.type == "MUL" then
|
|
return self.visit(node.left) * self.visit(node.right)
|
|
else if node.op.type == "DIV" then
|
|
return self.visit(node.left) / self.visit(node.right)
|
|
else if node.op.type == "MOD" then
|
|
return self.visit(node.left) % self.visit(node.right)
|
|
else if node.op.type == "EXP" then
|
|
return self.visit(node.left) ^ self.visit(node.right)
|
|
end if
|
|
end function
|
|
|
|
out.visit_Num=function(self,node)
|
|
return node.value
|
|
end function
|
|
|
|
out.visit_UnaryOp=function(self,node)
|
|
op=node.op.type
|
|
if op == "ADD" then
|
|
return self.visit(node.expr)
|
|
else if op == "SUB" then
|
|
return -self.visit(node.expr)
|
|
end if
|
|
end function
|
|
|
|
out.interp=function(self)
|
|
tree=self.parser.parse
|
|
return self.visit(tree)
|
|
end function
|
|
|
|
return out
|
|
end function
|
|
|
|
main=function()
|
|
print("Four Leaf Calculator")
|
|
print
|
|
while 1
|
|
text=user_input("calc> ")
|
|
if not text then continue
|
|
run(text)
|
|
end while
|
|
end function
|
|
|
|
run=function(script)
|
|
lexer=Lexer(script)
|
|
parser=Parser(lexer)
|
|
interp=Interpreter(parser)
|
|
result=interp.interp
|
|
print(result)
|
|
end function
|
|
|
|
main |