--
-- decoy.parser
--
-- SPDX-FileCopyrightText: Copyright (c) 2023-2024 Chris Pressey, Cat's Eye Technologies.
-- This work is distributed under a 2-clause BSD license. For more information, see:
-- SPDX-License-Identifier: LicenseRef-BSD-2-Clause-X-Decoy
table = require "table"
require "decoy.model"
local Lexer = require "decoy.lexer"
local ast = require "decoy.ast"
local Parser = {}
function Parser:parse_sexps()
local es = {}
local e
while (not self.lexer:is_eof()) and self.lexer:get_token_text() ~= ")" do
e = self:parse_sexp()
table.insert(es, e)
end
return es
end
function Parser:parse_sexp()
local e
local lexer = self.lexer
if lexer:consume("(") then
local es = self:parse_sexps()
lexer:expect(")")
e = Cons.table_to_list(es)
elseif lexer:get_token_type() == "symbol" then
e = Symbol.new(lexer:get_token_text())
lexer:scan()
elseif lexer:get_token_type() == "strlit" then
e = String.new(lexer:get_token_text())
lexer:scan()
elseif lexer:get_token_type() == "numlit" then
local value = tonumber(lexer:get_token_text())
e = Number.new(value)
lexer:scan()
else
error("what is this I don't even: " .. lexer:get_token_text())
end
return e
end
function Parser:parse_toplevels()
local es = {}
local e
while not self.lexer:is_eof() do
e = self:parse_toplevel()
table.insert(es, e)
end
return es
end
function Parser:parse_toplevel()
local e
local lexer = self.lexer
if lexer:consume("(") then
if lexer:consume("define") then
assert(lexer:get_token_type() == "symbol")
local name = lexer:get_token_text()
lexer:consume_type("symbol")
local defn = self:parse_sexp()
lexer:expect(")")
e = ast.Define.new(name, defn)
elseif lexer:consume("import-from") then
assert(lexer:get_token_type() == "strlit")
local name = lexer:get_token_text()
lexer:consume_type("strlit")
local import_list = self:parse_sexp()
local imports = Cons.list_to_table(import_list)
lexer:expect(")")
e = ast.ImportFrom.new(name, imports)
else
local es = self:parse_sexps()
lexer:expect(")")
e = Cons.table_to_list(es)
end
else
e = self:parse_sexp()
end
return e
end
Parser.new = function(source)
local self = {
line = 1,
pos = 0,
token,
lexer = Lexer.new(source),
parse_special_forms = false,
}
setmetatable(self, {__index = Parser})
-- init
self.lexer:scan()
return self
end
return Parser