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

Tree @master (Download .tar.gz)

desugar.lua @masterraw · history · blame

--
-- decoy.desugar
--

-- 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"
string = require "string"
io = require "io"

require "decoy.model"
local ast = require "decoy.ast"


local Desugarer = {}

function Desugarer:desugar_exprs(sexp)
    if sexp == Nil then
        return Nil
    elseif Cons.is_class_of(sexp) then
        return Cons.new(
            self:desugar_expr(sexp:head()),
            self:desugar_exprs(sexp:tail())
        )
    else
        error("Not a proper list")
    end
end

function Desugarer:desugar_expr(expr)
    debug("desugar", depict(expr))
    if Cons.is_class_of(expr) then
        local head = expr:head()
        if Symbol.is_class_of(head) then
            if head:text() == "quote" then
                return expr
            elseif head:text() == "let*" then
                local bindings = expr:tail():head()
                local body = expr:tail():tail():head()
                local result = self:desugar_let_star(bindings, body)
                debug("desugar", "let* desugar result:" .. depict(result))
                return result
            elseif head:text() == "cond" then
                local result = self:desugar_cond(expr:tail())
                debug("desugar", "cond desugar result:" .. depict(result))
                return result
            elseif head:text() == "if" then
                local test_ = self:desugar_expr(expr:tail():head())
                local then_ = self:desugar_expr(expr:tail():tail():head())
                local else_ = self:desugar_expr(expr:tail():tail():tail():head())
                return (
                    Cons.new(Symbol.new("if"),
                        Cons.new(test_,
                            Cons.new(then_,
                                Cons.new(else_, Nil))))
                )
            elseif head:text() == "lambda" then
                local formals = expr:tail():head()
                local body = expr:tail():tail():head()
                return (
                    Cons.new(Symbol.new("lambda"),
                        Cons.new(formals,
                            Cons.new(self:desugar_expr(body), Nil)))
                )
            else
                return Cons.new(head, self:desugar_exprs(expr:tail()))
            end
        else
            debug("desugar", "desugaring application head:" .. depict(expr:head()))
            local head = self:desugar_expr(expr:head())
            debug("desugar", "head:" .. depict(head))
            debug("desugar", "desugaring application tail:" .. depict(expr:tail()))
            local tail = self:desugar_exprs(expr:tail())
            debug("desugar", "tail:" .. depict(tail))
            return Cons.new(head, tail)
        end
    elseif ast.Define.is_class_of(expr) then
        return ast.Define.new(expr.name, self:desugar_expr(expr.defn))
    elseif ast.ImportFrom.is_class_of(expr) then
        return expr
    else
        --error("Unsupported form for desugaring: " ..  depict(expr))
        return expr
    end
end

function Desugarer:desugar_let_star(bindings, body)
    if bindings == Nil then
        return self:desugar_expr(body)
    else
        local binding = bindings:head()
        local name = binding:head()
        local expr = binding:tail():head()
        return (
            Cons.new(Symbol.new("bind"),
                Cons.new(name,
                    Cons.new(self:desugar_expr(expr),
                        Cons.new(self:desugar_let_star(bindings:tail(), body), Nil))))
        )
    end
end

function Desugarer:desugar_cond(expr)
    debug("desugar", "desugar_cond:" .. depict(expr))
    if expr == Nil then
        return Nil
    end
    local branch = expr:head()
    debug("desugar", "desugar_cond branch:" .. depict(branch))
    local test = branch:head()
    if Symbol.is_class_of(test) and test:text() == "else" then
        debug("desugar", "desugar_cond else:" .. depict(branch:tail():head()))
        result = self:desugar_expr(branch:tail():head())
        debug("desugar", "desugar_cond else result:" .. depict(result))
            return result
    end
    return (
        Cons.new(Symbol.new("if"),
            Cons.new(self:desugar_expr(test),
                Cons.new(self:desugar_expr(branch:tail():head()),
                    Cons.new(self:desugar_cond(expr:tail()), Nil))))
    )
end

Desugarer.new = function()
    local self = {
    }
    setmetatable(self, {__index = Desugarer})
    return self
end

return Desugarer