556 lines
11 KiB
Lua
556 lines
11 KiB
Lua
local AST = require"ast"
|
|
|
|
local ETypes = require"etype"
|
|
local Target = require"target"
|
|
|
|
local Logger = require"logger"
|
|
|
|
local NEXT_LABEL_ID = 1
|
|
|
|
local Scope = {}
|
|
Scope.__index = Scope
|
|
function Scope.new(parent)
|
|
return setmetatable({parent = parent, items = {}}, Scope)
|
|
end
|
|
function Scope:find(name)
|
|
if self.items[name] then
|
|
return self.items[name]
|
|
end
|
|
if self.parent then
|
|
return self.parent:find(name)
|
|
end
|
|
return nil
|
|
end
|
|
function Scope:add(name)
|
|
self.items[name] = AST.VReg(name)
|
|
return self.items[name]
|
|
end
|
|
|
|
local Parser = {}
|
|
Parser.__index = Parser
|
|
|
|
function Parser:parse_root()
|
|
local root = AST.root()
|
|
|
|
while true do
|
|
if self:peek(0).type == "eof" then
|
|
break
|
|
end
|
|
|
|
local declaration, err = self:parse_declaration()
|
|
|
|
if not declaration then
|
|
self:log("err", err or "can't parse")
|
|
break
|
|
end
|
|
|
|
table.insert(root.children, declaration)
|
|
end
|
|
|
|
return root
|
|
end
|
|
|
|
function Parser:parse_declaration()
|
|
local old_i = self.i
|
|
|
|
local export = self:maybe"export"
|
|
|
|
if not self:maybe"ident" then
|
|
self.i = old_i
|
|
return nil, "expected identifier"
|
|
end
|
|
|
|
local name = self:last().data
|
|
|
|
if not self:maybe":" then
|
|
return nil, "expected :"
|
|
end
|
|
|
|
local expr, err = self:parse_expr(0)
|
|
|
|
if not expr then
|
|
return nil, err
|
|
end
|
|
|
|
return AST.decl(name, expr, export)
|
|
end
|
|
|
|
function Parser:parse_expr(precedence)
|
|
local old_idx = self.i
|
|
|
|
if precedence == 0 then
|
|
local ret, err = self:parse_expr(precedence + 1)
|
|
|
|
if not ret then
|
|
self.i = old_idx
|
|
return nil, err
|
|
end
|
|
|
|
while self:maybe">" or self:maybe"<" or self:maybe"==" or self:maybe"!=" or self:maybe">=" or self:maybe"<=" do
|
|
local op = self:last().type
|
|
|
|
local a = ret
|
|
local b, err = self:parse_expr(precedence + 1)
|
|
|
|
if not b then
|
|
self.i = old_idx
|
|
return nil, err
|
|
end
|
|
|
|
assert(a.etype == b.etype)
|
|
|
|
ret = AST.binop(a.etype, a, op, b)
|
|
end
|
|
|
|
return ret
|
|
elseif precedence == 1 then
|
|
local ret, err = self:parse_expr(precedence + 1)
|
|
|
|
if not ret then
|
|
self.i = old_idx
|
|
return nil, err
|
|
end
|
|
|
|
while self:maybe"+" or self:maybe"-" do
|
|
local op = self:last().type
|
|
|
|
local a = ret
|
|
local b, err = self:parse_expr(precedence + 1)
|
|
|
|
if not b then
|
|
self.i = old_idx
|
|
return nil, err
|
|
end
|
|
|
|
assert(a.etype == b.etype)
|
|
|
|
ret = AST.binop(a.etype, a, op, b)
|
|
end
|
|
|
|
return ret
|
|
elseif precedence == 2 then
|
|
local ret, err = self:parse_expr(precedence + 1)
|
|
|
|
if not ret then
|
|
self.i = old_idx
|
|
return nil, err
|
|
end
|
|
|
|
while self:maybe"*" or self:maybe"/" do
|
|
local op = self:last().type
|
|
|
|
local a = ret
|
|
local b, err = self:parse_expr(precedence + 1)
|
|
|
|
if not b then
|
|
self.i = old_idx
|
|
return nil, err
|
|
end
|
|
|
|
assert(a.etype == b.etype)
|
|
|
|
ret = AST.binop(a.etype, a, op, b)
|
|
end
|
|
|
|
return ret
|
|
elseif precedence == 3 then
|
|
local ret, err
|
|
while self:maybe"&" or self:maybe"*" do
|
|
local op = self:last().type
|
|
|
|
local a, err = self:parse_expr(precedence)
|
|
if not a then
|
|
self.i = old_idx
|
|
return nil, err
|
|
end
|
|
|
|
ret = AST.unop(op == "&" and ETypes.ref(a.etype) or ETypes.deref(a.etype), op, a)
|
|
end
|
|
|
|
if not ret then
|
|
ret, err = self:parse_expr(precedence + 1)
|
|
end
|
|
|
|
if not ret then
|
|
self.i = old_idx
|
|
return nil, err
|
|
end
|
|
|
|
return ret
|
|
elseif precedence == 4 then
|
|
local a, err = self:parse_expr(precedence + 1)
|
|
|
|
if not a then
|
|
self.i = old_idx
|
|
return nil, err
|
|
end
|
|
|
|
while self:maybe"[" do
|
|
if a.etype.kind == "pointer" then
|
|
a = AST.unop(a.etype.to, "*", a)
|
|
end
|
|
|
|
assert(a.etype.kind == "array", "Indexing a non-array")
|
|
|
|
local b, err = self:parse_expr(0)
|
|
|
|
if not b then
|
|
self.i = old_idx
|
|
return nil, err
|
|
end
|
|
|
|
a = AST.unop(a.etype.element_etype, "*", AST.binop(nil, a, "+", b))
|
|
|
|
assert(self:maybe"]")
|
|
end
|
|
|
|
return a
|
|
elseif precedence == 5 then
|
|
local asdf = self.i
|
|
|
|
-- Okay for etype to be nil
|
|
local etype = self:parse_etype(0)
|
|
|
|
if self:maybe"?" then
|
|
return AST.unknown(etype)
|
|
elseif self:maybe'string' then
|
|
return AST.cast(AST.string(self:last().data), etype)
|
|
elseif self:maybe"num" then
|
|
local tok = self:last().data
|
|
local base = tonumber(tok:match"^([0-9]+)r") or 10
|
|
local val = tok:match"r([0-9a-zA-Z]+)$" or tok
|
|
return AST.int(etype, tonumber(val, base))
|
|
elseif self:maybe"ident" then
|
|
assert(not etype)
|
|
|
|
local name = self:last().data
|
|
local vreg = self.scope:find(name)
|
|
if not vreg then
|
|
self.i = old_idx
|
|
return nil, "Undeclared variable " .. name
|
|
end
|
|
|
|
return AST.var(vreg.etype, vreg)
|
|
elseif self:maybe"[" then
|
|
assert(etype)
|
|
|
|
local children = {}
|
|
|
|
if not self:maybe"]" then
|
|
while true do
|
|
local ex = self:parse_expr(0)
|
|
assert(ex)
|
|
|
|
if etype then
|
|
ex = AST.cast(ex, etype.element_etype)
|
|
end
|
|
|
|
table.insert(children, ex)
|
|
|
|
if self:maybe"]" then
|
|
break
|
|
end
|
|
|
|
if not self:maybe"," then
|
|
local last = self:last()
|
|
self.i = old_idx
|
|
return nil, "expected comma", last
|
|
end
|
|
end
|
|
end
|
|
|
|
local n = AST.array(etype, 0)
|
|
for _, child in ipairs(children) do
|
|
table.insert(n.children, child)
|
|
end
|
|
return n
|
|
elseif self:maybe"{" then
|
|
local n = AST.func(etype)
|
|
while not self:maybe"}" do
|
|
local status, err = self:parse_stmt(n)
|
|
|
|
if not status then
|
|
self.i = old_idx
|
|
return nil, err
|
|
end
|
|
end
|
|
return n
|
|
end
|
|
end
|
|
|
|
self.i = old_idx
|
|
return nil
|
|
end
|
|
|
|
function Parser:parse_stmt(chunk)
|
|
local old_idx = self.i
|
|
|
|
if self:maybe";" then
|
|
return true
|
|
elseif self:maybe"return" then
|
|
local ex = self:parse_expr(0)
|
|
|
|
-- ex can be null
|
|
|
|
table.insert(chunk.children, AST.ret(ex))
|
|
|
|
return true
|
|
elseif self:maybe"if" then
|
|
local condition = self:parse_expr(0)
|
|
|
|
local lbl_id = NEXT_LABEL_ID
|
|
NEXT_LABEL_ID = NEXT_LABEL_ID + 1
|
|
|
|
table.insert(chunk.children, AST.jump(condition, lbl_id))
|
|
|
|
if not self:maybe"{" then
|
|
self.i = old_idx
|
|
return false, "expected {"
|
|
end
|
|
|
|
while not self:maybe"}" do
|
|
local status, err = self:parse_stmt(chunk)
|
|
if not status then
|
|
self.i = old_idx
|
|
return nil, err
|
|
end
|
|
end
|
|
|
|
table.insert(chunk.children, AST.label(lbl_id))
|
|
|
|
return true
|
|
elseif self:peek(0).type == "ident" and self:peek(1).type == "=" then
|
|
local name = self:next().data
|
|
self:next()
|
|
|
|
local expr = self:parse_expr(0)
|
|
|
|
local vreg = self.scope:find(name)
|
|
if not vreg then
|
|
assert(expr.etype)
|
|
|
|
vreg = self.scope:add(name)
|
|
vreg.etype = expr.etype
|
|
else
|
|
assert(vreg.etype == expr.etype, "Type mismatch in assignment")
|
|
end
|
|
|
|
table.insert(chunk.children, AST.assign(AST.var(vreg.etype, vreg), expr))
|
|
|
|
return true
|
|
end
|
|
|
|
-- Try parsing assignment
|
|
|
|
local ex = self:parse_expr(0)
|
|
if not ex then
|
|
self.i = old_idx
|
|
return nil, "expected expression"
|
|
end
|
|
|
|
if not self:maybe"=" then
|
|
self.i = old_idx
|
|
return nil, "expected ="
|
|
end
|
|
|
|
local ex2 = self:parse_expr(0)
|
|
if not ex2 then
|
|
self.i = old_idx
|
|
return nil, "expected expression"
|
|
end
|
|
|
|
table.insert(chunk.children, AST.assign(ex, AST.cast(ex2, ex.etype)))
|
|
|
|
return true
|
|
end
|
|
|
|
function Parser:parse_etype(precedence)
|
|
local old_idx = self.i
|
|
|
|
if precedence == 0 then
|
|
local ret = self:parse_etype(precedence + 1)
|
|
|
|
if not ret then
|
|
return nil
|
|
end
|
|
|
|
while self:maybe"->" do
|
|
local a = ret
|
|
local b = self:parse_etype(precedence + 1)
|
|
|
|
ret = ETypes.func(a, b)
|
|
|
|
while self:maybe("mod") do
|
|
local mod_type = self:last().data
|
|
|
|
if mod_type == "@save" then
|
|
if not self:maybe"(" then
|
|
local last = self:last()
|
|
self.i = old_idx
|
|
return nil, "expected (", last
|
|
end
|
|
|
|
local ex = self:parse_expr(0)
|
|
if not ex or ex.kind ~= "expr-array" then
|
|
local last = self:last()
|
|
self.i = old_idx
|
|
return nil, "expected array", last
|
|
end
|
|
|
|
if not self:maybe")" then
|
|
local last = self:last()
|
|
self.i = old_idx
|
|
return nil, "expected )", last
|
|
end
|
|
|
|
local items = {}
|
|
for _, child in ipairs(ex.children) do
|
|
assert(child.kind == "expr-string")
|
|
|
|
if not require"target".REGS[child.value] then
|
|
self:log("warn", "skipping unknown register " .. child.value)
|
|
end
|
|
|
|
items[child.value] = true
|
|
end
|
|
|
|
ret.modifiers["save"] = items
|
|
else
|
|
self:log("warn", "skipping unknown modifier " .. mod_type)
|
|
|
|
if self:maybe"(" then
|
|
local depth = 1
|
|
while true do
|
|
local t = self:next()
|
|
if t.type == "(" then
|
|
depth = depth + 1
|
|
elseif t.type == ")" then
|
|
depth = depth - 1
|
|
if depth == 0 then
|
|
break
|
|
end
|
|
elseif t.type == "eof" then
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return ret
|
|
elseif precedence == 1 then
|
|
local a = self:parse_etype(precedence + 1)
|
|
|
|
if not a then
|
|
return nil
|
|
end
|
|
|
|
while self:maybe"*" or self:maybe"[" do
|
|
local old_idx = self.i
|
|
|
|
if self:last().type == "*" then
|
|
a = ETypes.ref(a)
|
|
else
|
|
local length_expr = self:parse_expr(0)
|
|
|
|
if not length_expr or not self:maybe"]" or (length_expr.kind ~= "expr-unknown" and length_expr.kind ~= "expr-int") then
|
|
-- This cannot be an array type, most likely a case like (string[?] [...])
|
|
self.i = old_idx - 1
|
|
break
|
|
end
|
|
|
|
local length
|
|
if length_expr.kind == "expr-int" then
|
|
length = length_expr.value
|
|
end
|
|
|
|
a = ETypes.array(a, length)
|
|
end
|
|
end
|
|
return a
|
|
elseif precedence == 2 then
|
|
if self:maybe"(" then
|
|
if self:maybe")" then
|
|
return ETypes.struct({})
|
|
else
|
|
self:go_back()
|
|
end
|
|
end
|
|
|
|
if self:maybe"stringkw" then
|
|
return ETypes.string()
|
|
end
|
|
|
|
if not self:maybe"ident" then
|
|
self.i = old_idx
|
|
return nil
|
|
end
|
|
|
|
local str = self:last().data
|
|
|
|
local ret
|
|
if str:match"[ui][0-9]+" then
|
|
ret = ETypes.scalar(str:sub(1, 1) == "u", tonumber(str:sub(2)))
|
|
elseif str:match"[ui]gpr" then
|
|
ret = ETypes.scalar(str:sub(1, 1) == "u", Target.GPR_SIZE)
|
|
else
|
|
self.i = old_idx
|
|
return nil
|
|
end
|
|
|
|
return ret
|
|
end
|
|
|
|
self.i = old_idx
|
|
return nil
|
|
end
|
|
|
|
function Parser:go_back()
|
|
if self.i <= 1 then
|
|
error("Already at the beginning")
|
|
end
|
|
self.i = self.i - 1
|
|
end
|
|
|
|
function Parser:last()
|
|
return self.tokens[self.i - 1]
|
|
end
|
|
|
|
function Parser:peek(idx)
|
|
if self.i + idx > #self.tokens then
|
|
return {type = "eof"}
|
|
end
|
|
return self.tokens[self.i + idx]
|
|
end
|
|
|
|
function Parser:maybe(token_type)
|
|
if self.i > #self.tokens then
|
|
return false
|
|
end
|
|
|
|
if self.tokens[self.i].type == token_type then
|
|
self.i = self.i + 1
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function Parser:next()
|
|
local ret = self:peek(0)
|
|
if ret.type ~= "eof" then
|
|
self.i = self.i + 1
|
|
end
|
|
return ret
|
|
end
|
|
|
|
function Parser:log(msg_type, fmt, ...)
|
|
local tok = self:last()
|
|
Logger.log(msg_type, tok.y .. ":" .. tok.x .. ", " .. fmt, ...)
|
|
end
|
|
|
|
return function(tokens)
|
|
return setmetatable({i = 1, tokens = tokens, scope = Scope.new(nil)}, Parser)
|
|
end
|