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

Tree @master (Download .tar.gz)

model.lua @masterraw · history · blame

--
-- decoy.model
--

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


--[[ ========== DEBUG ========= ]]--

debug_what = ""

do_debug = function(what, fn)
    if debug_what:find(what) then
        fn()
    end
end


debug = function(what, s)
    do_debug(what, function() print("--> (" .. s .. ")") end)
end


render = function(v, count)
    if not count then count = 0 end
    if count > 100 then return "!!OVERFLOW!!" end
    if type(v) == "table" then
        local s = "{"
        local key, value
        for key, value in pairs(v) do
            s = s .. render(key, count + 1) .. ": " .. render(value, count + 1) .. ","
        end
        return s .. "}"
    else
        return tostring(v)
    end
end


--[[ ========== Data Model ========= ]]--

--
-- The following mapping is used for types:
-- 
--     lambda function        function
--     empty list             Nil (singleton table)
--     cons cell              Cons (table)
--     string                 String (table) -- note, this means they're not interned
--     symbol                 Symbol (table) -- note, this means they're not interned
--     boolean                True (singleton table), False (singleton table)
--     number                 Number (table)
--

local list = require "decoy.model.list"
Cons = list.Cons
Nil = list.Nil
assert(Nil ~= nil)

local bool = require "decoy.model.boolean"
Boolean = bool.Boolean
True = bool.True
False = bool.False

Symbol = require "decoy.model.symbol"
String = require "decoy.model.string"
Number = require "decoy.model.number"


--[[ ========== Utils ========= ]]--


depict = function(sexp)
    local s = ""
    if type(sexp) == "function" then
        return "<<" .. tostring(sexp) .. ">>"
    elseif sexp == Nil then
        return "()"
    elseif Cons.is_class_of(sexp) then
        s = s .. "("
        s = s .. depict(sexp:head())
        sexp = sexp:tail()
        local done = false
        while not done do
            if Cons.is_class_of(sexp) then
                s = s .. " "
                s = s .. depict(sexp:head())
                sexp = sexp:tail()
            elseif sexp == Nil then
                done = true
            else
                s = s .. " . " .. depict(sexp)
                done = true
            end
        end
        s = s .. ")"
        return s
    elseif Symbol.is_class_of(sexp) then
        return sexp:text()
    elseif String.is_class_of(sexp) then
        return "\"" .. sexp:text() .. "\""
    elseif Number.is_class_of(sexp) then
        return tostring(sexp:value())
    elseif Boolean.is_class_of(sexp) then
        if sexp:value() then return "#t" else return "#f" end
    elseif sexp == nil then
        return "<<<LUA NIL>>>"
    elseif sexp.depict then
        return sexp:depict()
    else
        error("Invalid lua representation of s-expression: " .. render(sexp) .. ": " .. type(sexp))
    end
end