477 lines
9.9 KiB
Lua
477 lines
9.9 KiB
Lua
function tprint (tbl, indent)
|
|
if not indent then indent = 0 end
|
|
for k, v in pairs(tbl) do
|
|
formatting = string.rep(" ", indent) .. k .. ": "
|
|
if type(v) == "table" then
|
|
print(formatting)
|
|
tprint(v, indent+1)
|
|
else
|
|
print(formatting .. tostring(v))
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
Token = {}
|
|
Token.__index = Token
|
|
local INTEGER, PLUS, EOF, MINUS, MUL, DIV, LPAREN, RPAREN, BEGIN, END, DOT, ID, ASSIGN, SEMI = "INT","+","EOF", "-", "*", "/", "(", ")", "BEGIN", "END", "DOT", "ID", "ASSIGN", "SEMI"
|
|
function Token.__tostring(self)
|
|
return string.format("Token(%s, %s)",self.type,self.value)
|
|
end
|
|
function Token:new(tp,val)
|
|
local c = {}
|
|
setmetatable(c,self)
|
|
c.Type = 'Token'
|
|
c.type = tp
|
|
c.value = val
|
|
--print("Token:"..tp)
|
|
return c
|
|
end
|
|
|
|
Lexer = {}
|
|
Lexer.__index = Lexer
|
|
function Lexer:new(text)
|
|
local c = {}
|
|
setmetatable(c,self)
|
|
c.Type = 'Lexer'
|
|
c.text = text
|
|
c.pos = 1
|
|
c.current_char = c.text:sub(1,1)
|
|
c.stop = false
|
|
return c
|
|
end
|
|
function Lexer:advance()
|
|
self.pos = self.pos + 1
|
|
if self.pos > #self.text then
|
|
self.current_char = false
|
|
return
|
|
else
|
|
self.current_char = self.text:sub(self.pos,self.pos)
|
|
end
|
|
end
|
|
local RESERVED_KEYWORDS = {
|
|
BEGIN = Token:new('BEGIN',BEGIN),
|
|
END = Token:new('END',END)
|
|
}
|
|
function Lexer:_id()
|
|
local result = ''
|
|
while self.current_char and self.current_char:match("%w") do
|
|
result = result .. self.current_char
|
|
self:advance()
|
|
end
|
|
local token = RESERVED_KEYWORDS[result] or Token:new(ID,result)
|
|
return token
|
|
end
|
|
function Lexer:peek()
|
|
local peek_pos = self.pos + 1
|
|
if peek_pos > #self.text then
|
|
return
|
|
else
|
|
return self.text:sub(peek_pos,peek_pos)
|
|
end
|
|
end
|
|
function Lexer:error(err)
|
|
error(err)
|
|
end
|
|
function Lexer:skip_whitespace()
|
|
while self.current_char and self.current_char:match("%s") do
|
|
self:advance()
|
|
end
|
|
end
|
|
function Lexer:integer()
|
|
local result = {}
|
|
while self.current_char and self.current_char:match("%d") do
|
|
table.insert(result,self.current_char)
|
|
self:advance()
|
|
end
|
|
return tonumber(table.concat(result))
|
|
end
|
|
function Lexer:get_next_token()
|
|
while self.current_char do
|
|
::continue::
|
|
if self.current_char and self.current_char:match("%s") then
|
|
self:skip_whitespace()
|
|
goto continue
|
|
end
|
|
if self.current_char and self.current_char:match("%d") then
|
|
return Token:new(INTEGER,self:integer())
|
|
end
|
|
if self.current_char and self.current_char == "+" then
|
|
self:advance()
|
|
return Token:new(PLUS,"+")
|
|
end
|
|
if self.current_char and self.current_char == "-" then--55+77
|
|
self:advance()
|
|
return Token:new(MINUS,"-")
|
|
end
|
|
if self.current_char and self.current_char == "*" then
|
|
self:advance()
|
|
return Token:new(MUL,"*")
|
|
end
|
|
if self.current_char and self.current_char == "/" then
|
|
self:advance()
|
|
return Token:new(DIV,"/")
|
|
end
|
|
if self.current_char and self.current_char == "(" then
|
|
self:advance()
|
|
return Token:new(LPAREN,"(")
|
|
end
|
|
if self.current_char and self.current_char == ")" then
|
|
self:advance()
|
|
return Token:new(RPAREN,")")
|
|
end
|
|
if self.current_char and self.current_char:match("%w") then
|
|
return self:_id()
|
|
end
|
|
if self.current_char and self.current_char == ":" and self:peek() == "=" then
|
|
self:advance()
|
|
self:advance()
|
|
return Token:new(ASSIGN, ":=")
|
|
end
|
|
if self.current_char and self.current_char == ";" then
|
|
self:advance()
|
|
return Token:new(SEMI, ';')
|
|
end
|
|
if self.current_char and self.current_char == "." then
|
|
self:advance()
|
|
return Token:new(DOT,".")
|
|
end
|
|
if self.current_char == false then
|
|
return Token:new(EOF, "EOF")
|
|
end
|
|
self:error("Invalid Symbol! "..tostring(self.current_char))
|
|
end
|
|
return Token:new(EOF, "EOF")
|
|
end
|
|
|
|
AST = {}
|
|
AST.__index = AST
|
|
function AST:new()
|
|
local c = {}
|
|
setmetatable(c,self)
|
|
c.Type = 'AST'
|
|
return c
|
|
end
|
|
|
|
Compound = {}
|
|
Compound.__index = AST
|
|
function Compound:new()
|
|
local c = {}
|
|
setmetatable(c,self)
|
|
c.Type = 'Compound'
|
|
c.children = {}
|
|
return c
|
|
end
|
|
|
|
Assign = {}
|
|
Assign.__index = AST
|
|
function Assign:new(left,op,right)
|
|
local c = {}
|
|
setmetatable(c,self)
|
|
c.Type = 'Assign'
|
|
c.left = left
|
|
c.op = op
|
|
c.token = op
|
|
c.right = right
|
|
return c
|
|
end
|
|
|
|
Var = {}
|
|
Var.__index = AST
|
|
function Var:new(token)
|
|
local c = {}
|
|
setmetatable(c,self)
|
|
c.Type = "Var"
|
|
c.token = token
|
|
c.value = token.value
|
|
return c
|
|
end
|
|
|
|
NoOp = {}
|
|
NoOp.__index = AST
|
|
function NoOp:new()
|
|
local c = {}
|
|
setmetatable(c,self)
|
|
c.Type = "NoOp"
|
|
return c
|
|
end
|
|
|
|
UnaryOp = {}
|
|
UnaryOp.__index = AST
|
|
function UnaryOp:new(op,expr)
|
|
local c = {}
|
|
setmetatable(c,self)
|
|
c.Type = 'UnaryOp'
|
|
c.token = op
|
|
c.op = op
|
|
c.expr = expr
|
|
return c
|
|
end
|
|
|
|
BinOp = {}
|
|
BinOp.__index = AST
|
|
function BinOp:new(left, op, right)
|
|
local c = {}
|
|
setmetatable(c,self)
|
|
c.Type = 'BinOp'
|
|
c.left = left
|
|
c.token = op
|
|
c.op = op
|
|
c.right = right
|
|
return c
|
|
end
|
|
|
|
Num = {}
|
|
Num.__index = AST
|
|
function Num:new(token)
|
|
local c = {}
|
|
setmetatable(c,self)
|
|
c.Type = 'Num'
|
|
c.token = token
|
|
c.value = token.value
|
|
return c
|
|
end
|
|
|
|
Parser = {}
|
|
Parser.__index = Parser
|
|
function Parser:new(lexer)
|
|
local c = {}
|
|
setmetatable(c,self)
|
|
c.Type = 'Parser'
|
|
c.lexer = lexer
|
|
c.current_token = lexer:get_next_token()
|
|
return c
|
|
end
|
|
function Parser:error()
|
|
error('Error Invalid Syntax')
|
|
end
|
|
function Parser:eat(token_type)
|
|
if self.current_token.type == token_type then
|
|
self.current_token = self.lexer:get_next_token()
|
|
else
|
|
self:error()
|
|
end
|
|
end
|
|
function Parser:program()
|
|
local node = self:compound_statement()
|
|
self:eat(DOT)
|
|
return node
|
|
end
|
|
function Parser:compound_statement()
|
|
self:eat(BEGIN)
|
|
local nodes = self:statement_list()
|
|
self:eat(END)
|
|
|
|
local root = Compound:new()
|
|
for _,node in pairs(nodes) do
|
|
table.insert(root.children,node)
|
|
end
|
|
|
|
return root
|
|
end
|
|
function Parser:statement_list()
|
|
local node = self:statement()
|
|
|
|
local results = {node}
|
|
|
|
while self.current_token.type == SEMI do
|
|
self:eat(SEMI)
|
|
table.insert(results,self:statement())
|
|
end
|
|
|
|
if self.current_token.type == ID then
|
|
self:error()
|
|
end
|
|
|
|
return results
|
|
end
|
|
function Parser:statement()
|
|
local node
|
|
if self.current_token.type == BEGIN then
|
|
node = self:compound_statement()
|
|
elseif self.current_token.type == ID then
|
|
node = self:assignment_statement()
|
|
else
|
|
node = self:empty()
|
|
end
|
|
return node
|
|
end
|
|
function Parser:assignment_statement()
|
|
local left = self:variable()
|
|
local token = self.current_token
|
|
self:eat(ASSIGN)
|
|
local right = self:expr()
|
|
local node = Assign:new(left, token, right)
|
|
return node
|
|
end
|
|
function Parser:variable()
|
|
local node = Var:new(self.current_token)
|
|
self:eat(ID)
|
|
return node
|
|
end
|
|
function Parser:empty()
|
|
return NoOp:new()
|
|
end
|
|
function Parser:factor()
|
|
local token = self.current_token
|
|
local node
|
|
if token.type == PLUS then
|
|
self:eat(PLUS)
|
|
node = UnaryOp:new(token,self:factor())
|
|
return node
|
|
elseif token.type == MINUS then
|
|
self:eat(MINUS)
|
|
node = UnaryOp:new(token,self:factor())
|
|
return node
|
|
elseif token.type == INTEGER then
|
|
self:eat(INTEGER)
|
|
return Num:new(token)
|
|
elseif token.type == LPAREN then
|
|
self:eat(LPAREN)
|
|
local node = self:expr()
|
|
self:eat(RPAREN)
|
|
return node
|
|
else
|
|
node = self:variable()
|
|
return node
|
|
end
|
|
end
|
|
function Parser:term()
|
|
local node = self:factor()
|
|
local token
|
|
while self.current_token.type == MUL or self.current_token.type == DIV do
|
|
token = self.current_token
|
|
if token.type == MUL then
|
|
self:eat(MUL)
|
|
elseif token.type == DIV then
|
|
self:eat(DIV)
|
|
end
|
|
node = BinOp:new(node,token,self:factor())
|
|
end
|
|
|
|
return node
|
|
end
|
|
function Parser:expr()
|
|
local node = self:term()
|
|
local token
|
|
while self.current_token.type == PLUS or self.current_token.type == MINUS do
|
|
token = self.current_token
|
|
if token.type == PLUS then
|
|
self:eat(PLUS)
|
|
elseif token.type == MINUS then
|
|
self:eat(MINUS)
|
|
end
|
|
node = BinOp:new(node,token,self:term())
|
|
end
|
|
|
|
return node
|
|
end
|
|
function Parser:parse()
|
|
local node = self:program()
|
|
if self.current_token.type ~= EOF then
|
|
self:error()
|
|
end
|
|
return node
|
|
end
|
|
|
|
NodeVisitor = {}
|
|
NodeVisitor.__index = NodeVisitor
|
|
function NodeVisitor:new(node)
|
|
local c = {}
|
|
setmetatable(c,self)
|
|
c.Type = 'NodeVisitor'
|
|
return c
|
|
end
|
|
function NodeVisitor:visit(node)
|
|
local visitor = self["visit_".. node.Type] or self.generic_visit
|
|
return visitor(self,node)
|
|
end
|
|
function NodeVisitor:generic_visit(node)
|
|
error("No visit_"..node.Type.." method")
|
|
end
|
|
|
|
Interpreter = {}
|
|
Interpreter.__index = NodeVisitor
|
|
function Interpreter:new(parser)
|
|
local c = {}
|
|
setmetatable(c,self)
|
|
c.Type = 'Interpreter'
|
|
c.parser = parser
|
|
c.GLOBAL_SCOPE = {}
|
|
function c:visit_Compound(node)
|
|
for _,child in pairs(node.children) do
|
|
self:visit(child)
|
|
end
|
|
end
|
|
function c:visit_Assign(node)
|
|
local var_name = node.left.value
|
|
self.GLOBAL_SCOPE[var_name] = self:visit(node.right)
|
|
end
|
|
function c:visit_Var(node)
|
|
local var_name = node.value
|
|
local val = self.GLOBAL_SCOPE[var_name]
|
|
if val then
|
|
return val
|
|
else
|
|
error("NameError:"..var_name)
|
|
end
|
|
end
|
|
function c:visit_NoOp(node)
|
|
return
|
|
end
|
|
function c:visit_BinOp(node)
|
|
if node.op.type == PLUS then
|
|
local a,b = self:visit(node.left),self:visit(node.right)
|
|
print("ADD ",a,b)
|
|
return a + b
|
|
elseif node.op.type == MINUS then
|
|
return self:visit(node.left) - self:visit(node.right)
|
|
elseif node.op.type == MUL then
|
|
return self:visit(node.left) * self:visit(node.right)
|
|
elseif node.op.type == DIV then
|
|
return self:visit(node.left) / self:visit(node.right)
|
|
end
|
|
end
|
|
function c:visit_UnaryOp(node)
|
|
local op = node.op.type
|
|
if op == PLUS then
|
|
return self:visit(node.expr)
|
|
elseif op == MINUS then
|
|
return -self:visit(node.expr)
|
|
end
|
|
end
|
|
function c:visit_Num(node)
|
|
return node.value
|
|
end
|
|
function c:interpret()
|
|
local tree = self.parser:parse()
|
|
return self:visit(tree)
|
|
end
|
|
return c
|
|
end
|
|
|
|
text = [[
|
|
BEGIN
|
|
ret := 11+a;
|
|
END.
|
|
]]
|
|
lexer = Lexer:new(text)
|
|
parser = Parser:new(lexer)
|
|
interpreter = Interpreter:new(parser)
|
|
interpreter.GLOBAL_SCOPE = {a=100}
|
|
interpreter:interpret()
|
|
for i,v in pairs(interpreter.GLOBAL_SCOPE) do
|
|
print(i,v)
|
|
end
|
|
while true do
|
|
io.write("calc> ")
|
|
local text = io.read()
|
|
if text then
|
|
lexer = Lexer:new(text)
|
|
parser = Parser:new(lexer)
|
|
interpreter = Interpreter:new(parser)
|
|
result = interpreter:interpret()
|
|
print(result)
|
|
end
|
|
end
|