print.lua

Faddle · faddle.git · Tickets

Branches > main > 0f9a9fe > files / org / faddle / lang / print.lua

local list = require("org.faddle.lang.list")
local is_list = list.is_list
local first = list.first
local rest = list.rest
local count = list.count
local symbol = require("org.faddle.lang.symbol")
local is_symbol = symbol.is_symbol
local name = symbol.name
local module = symbol.module

local function quote(s)
  return '"' .. s:gsub('\\','\\\\')
                 :gsub('"','\\"')
                 :gsub('\n','\\n')
                 :gsub('\r','\\r')
                 :gsub('\t','\\t')
                 :gsub('%z','\\0')
         .. '"'
end

local function print_symbol(sym)
  -- Symbols are unqualified as of now
  if module(sym) then
    return module(sym) .. ":" .. name(sym)
  else
    return name(sym)
  end
end

local print_value

local function print_list(x, seen)
  local l = x
  local xs = {}
  while count(l) > 0 do
    xs[#xs+1] = print_value(first(l), seen)
    l = rest(l)
  end
  return "(" .. table.concat(xs, " ") .. ")"
end

-- Could be a good idea if multiple tables with the same "value" are keys, like {[{}] = 1, [{}] = 2}, to print them differently using an id?
-- Otherwise that prints as {{} 1, {} 2} which is quite weird
-- Like [#0x4089324 {} #0x302498 {}] idk.
-- You could also print them with an id so they read back even if they're recursive :D
local function print_associative_table(x, seen)
  if seen[x] then return "..." end
  seen[x] = true

  local xs = {}
  for k, v in pairs(x) do
    xs[#xs+1] = print_value(k, seen) .. " " .. print_value(v, seen)
  end
  return "{" .. table.concat(xs, ", ") .. "}"
end

local function print_sequential_table(x, seen)
  if seen[x] then return "..." end
  seen[x] = true

  local xs = {}
  for i = 1, #x do
    xs[#xs+1] = print_value(x[i], seen)
  end
  return "[" .. table.concat(xs, " ") .. "]"
end

local function is_sequential(t)
  local i = 1
  if #t == 0 then return false end
  for k in pairs(t) do
    if k == i then
      i = i + 1
    else
      return false
    end
  end

  return true
end

local function print_table(x, seen)
  return is_sequential(x) and print_sequential_table(x, seen) or print_associative_table(x, seen)
end

local has_debug = type(debug) == "table" and type(debug.getinfo) == "function"

local debug = debug

local function print_function(f)
  if has_debug then
    local info = debug.getinfo(f)
    local defined = info.short_src and info.linedefined and (info.name and " " or "") .. info.short_src .. ":" .. info.linedefined or "..."
    return "#fn [" .. (info.name or "") .. defined .. "]"
  else
    return "#fn [...]"
  end
end

print_value = function(x, seen)
  local t = type(x)
  if t == "nil" then return "." end
  if t == "boolean" or t == "number" then return tostring(x) end
  if t == "string" then return quote(x) end
  if is_list(x) then return print_list(x, seen) end
  if is_symbol(x) then return print_symbol(x) end
  if t == "table" then return print_table(x, seen) end
  if t == "function" then return print_function(x) end
  error("idk how to print that man: " .. t)
end

local function lisp_print(...)
  local cnt = select("#", ...)
  local xs = {"."}
  for i = 1, cnt do
    xs[i] = print_value(select(i, ...), {})
  end
  return table.concat(xs, ", ")
end

return {print = lisp_print}