parser.lua (8587B)
local parser = { pos = 1 }
local ast = require("ast")
local utils = require("utils")
function parser:peek()
return self.tokens[self.pos] or 0
end
function parser:nextpeek()
return self.tokens[self.pos + 1] or 0
end
function parser:advance()
self.pos = self.pos + 1
end
function parser:expect(kind, extramsg)
local current = self.tokens[self.pos]
if current.kind ~= kind then
unhandled(current, "EXPECTED kind: " .. kind, extramsg)
end
parser:advance()
return current
end
function parser:match_ident()
local current = self.tokens[self.pos]
if current.kind ~= "ident" then
unhandled(current, "EXPECTED an IDENT")
end
return current
end
-----------------------
function parser:parse_postfix()
local node = parse_primary()
while true do
if parser:peek().kind == TK.PLUSPLUS then
return ast.postfix(OPER.POSTINCR, node)
elseif parser:peek().kind == TK.MINUSMINUS then
return ast.postfix(OPER.POSTDECR, node)
elseif parser:peek().kind == TK.L_BRACKET then -- parse inside brackets[]
local index = parser:parse_expression()
parser:expect(TK.R_BRACKET)
return ast.subscript(node, index)
elseif parser:peek().kind == TK.L_PAREN then
-- arguments
local args = parser:parse_arguments()
return ast.call(node, args)
elseif parser:peek().kind == TK.L_BRACE then -- parse name{} we don't hold grammar for this...
else
break
-- todo .member and potentially arrow operators here
end
end
end
function parser:parse_unary()
local inner = 0
if parser:peek().kind == TK.MINUS then
parser:advance()
return ast.unary(OPER.MINUS, parser:parse_unary())
elseif parser:peek().kind == TK.MINUSMINUS then
parser:advance()
return ast.unary(OPER.PREDECR, parser:parse_unary())
elseif parser:peek().kind == TK.BANG then
parser:advance()
return ast.unary(OPER.BANG, parser:parse_unary())
-- TODO add others '~a' '$a' '*a' '^a' '@a' '&a' ?
elseif parser:peek().kind == TK.PLUSPLUS then
parser:advance()
return ast.unary(OPER.PREINCR, parser:parse_unary())
elseif parser:peek().kind == TK.PLUS then
parser:advance()
return parser:parse_unary()
else
return parser:parse_postfix()
end
end
function parser:parse_term()
return parser:parse_unary()
end
function parser:parse_multiplicative()
local node = parse_term()
while true do
if parser:peek().kind == TK.STAR then
local rhs = parse_unary()
node = ast.binary(OP_MUL, node, rhs)
elseif parser:peek().kind == TK.SLASH then
local rhs = parse_unary()
node = ast.binary(OP_DIV, node, rhs)
elseif parser:peek().kind == TK.PERCENT then
local rhs = parse_unary()
node = ast.binary(OP_MOD, node, rhs)
else
break
end
end
return node
end
function parser:parse_additive()
local node = parse_multiplicative()
while true do
if parser:peek().kind == TK.PLUS then
local rhs = parse_multiplicative()
node = ast.binary(OP.PLUS, node, rhs)
elseif parser:peek().kind == TK.MINUS then
local rhs = parse_multiplicative()
node = ast.binary(OP.MINUS, node, rhs)
else
break
end
end
return node
end
function parser:parse_relational()
local node = parse_additive()
while true do
if parse:peek().kind == TK.LT then
local rhs = parser:parse_additive()
node = ast.binary(OP.LT, node, rhs)
elseif parse:peek().kind == TK.LTEQ then
local rhs = parser:parse_additive()
node = ast.binary(OP.LT_EQ, node, rhs)
-- todo more relationals
else
break
end
end
return node
end
function parser:parse_equality()
local node = parser:parse_relational()
while true do
if parser:peek().kind == TK.EQEQ then
local rhs = parser:parse_relational()
node = ast.binary(OP.EQUALITY, node, rhs)
-- elseif next == TK.BANGEQ then
-- todo inequality
else
break
end
end
return node
end
function parser:parse_assignment_expr()
local left = parser:parse_equality()
if parser:peek().kind == TK.EQ then
local right = parser:parse_assignment_expr()
-- todo check fail assignment target
-- todo make binary node
end
return left
end
function parser:parse_expr()
return parser:parse_assignment_expr()
end
function parser:parse_var()
print("todo parse_var")
end
function parser:parse_while()
print("todo parse_while")
end
function parser:parse_for()
print("todo parse_for")
end
function parser:parse_return()
print("todo parse_return")
end
function parser:parse_as_cast()
print("todo parse_as_cast")
end
function parser:parse_arguments()
parser:expect(TK.L_PAREN) -- swallow (
local args = {}
while parser:peek().kind ~= TK.R_PAREN do -- call parameters
local expr = parser:parse_expr()
args[#args + 1] = expr
if parser:peek().kind == TK.COMMA then
parser:advance()
else
break
end
parser:advance()
end
print(parser:peek().kind)
parser:expect(TK.R_PAREN)
return args
end
function parser:parse_call()
-- parse ident, parse parameters
local callee = parser:expect(TK.IDENT)
print("parse_call")
local args = parser:parse_arguments()
return ast.call(callee, args)
end
function parser:parse_statement()
local first = parser:peek().kind
local second = parser:nextpeek().kind
if first == TK.IDENT then
if second == TK.L_PAREN then
print("parsing a call statement")
return parser:parse_call()
elseif second == TK.IDENT then
return parser:parse_var()
else
print("parse_statement unhandled ident start with: " .. second)
end
elseif first == TK.KEYWORD then
local kw = parser:peek()
if kw.lexeme == "while" then
return parser:parse_while()
elseif kw.lexeme == "for" then
return parser:parse_for()
elseif kw.lexeme == "return" then
return parser:parse_return()
elseif kw.lexeme == "as" then
return parser:parse_as_cast()
else
print("parse_statement unhandled keyword yet: " .. kw.lexeme)
end
end
return { name = "idk" }
end
function parser:parse_function(is_exported)
local name = "unamed function"
local parameters = {}
local returntypes = {}
local statements = {}
local token = parser:peek()
parser:advance() -- consume fx/fn
name = parser:expect(TK.IDENT)
parser:expect(TK.L_PAREN)
while parser:peek().kind ~= TK.R_PAREN do -- parameters
local kind = parser:expect(TK.IDENT, "missing kind in param")
local name = parser:expect(TK.IDENT, "missing name in param")
parameters[#parameters + 1] = { kind = kind, name = name }
if parser:peek().kind == TK.COMMA then
parser:advance()
else
break
end
end
parser:expect(TK.R_PAREN)
while parser:peek().kind ~= TK.L_BRACE do -- return type(s)
local kind = parser:expect(TK.IDENT, "rettype no ident")
returntypes[#returntypes + 1] = { kind = kind }
if parser:peek().kind == TK.COMMA then
parser:advance()
else
break
end
end
parser:expect(TK.COLONCOLON, "no rbrace start of function")
while parser:peek().lexeme ~= "end" do
::continue::
local tok = parser:peek()
if tok.kind == TK.EOS then
parser:advance()
goto continue
end
-- function body statements'
print("parsing a body statement " .. parser:peek().lexeme)
local stmt = parser:parse_statement()
statements[#statements + 1] = stmt
parser:advance()
end
local node = ast.func(name, parameters, ret, statements)
return node
end
function parser:parse_use()
print("parse use")
self:advance()
local lib = parser:match_ident()
self:advance()
self:expect(TK.EOS, "use statement found without a end statement \\n|;")
local node = ast.use(lib)
return node
end
function unhandled(token, extramsg)
print(
"\27[33mat "
.. token.file
.. " L"
.. token.line
.. ":"
.. token.col
.. "\n\t UNHANDLED TOKEN: "
.. token.lexeme
.. "\t "
.. (extramsg or "")
)
print(" |> " .. debug.traceback() .. "\27[0m")
end
function parser:parse_program()
local nodes = {}
while self:peek() ~= 0 do
local token = self:peek()
local kind = token.kind
local word = token.lexeme
print("peek:", word)
if kind == TK.EOS then
-- nothing, advance
elseif kind == TK.KEYWORD and word == "use" then
nodes[#nodes + 1] = self:parse_use()
elseif kind == TK.KEYWORD and word == "fx" then
nodes[#nodes + 1] = self:parse_function(true)
elseif kind == TK.KEYWORD and word == "fn" then
nodes[#nodes + 1] = self:parse_function(false)
else
unhandled(token)
break
end
self:advance()
end
return nodes
end
function parser:parse(file, src, tokens)
self.file = file
self.src = src
self.tokens = tokens
self.pos = 1
local i = 1
local nodes = self:parse_program()
utils.print_table(nodes)
-- while i <= #tokens do
-- local t = tokens[i]
-- t2 = tokens[i + 1] or 0
-- i = i + 1
-- end
end
return parser