git @ Cat's Eye Technologies Decoy / master src / decoy / parser.lua
master

Tree @master (Download .tar.gz)

parser.lua @masterraw · history · blame

--
-- 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