Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
-- rect.lua: concatenable ascii rectangles, for drawing trees in ascii.
-- This thing supports both "syntax trees" and "deduction trees".
-- This file:
-- http://angg.twu.net/dednat6/dednat6/rect.lua
-- http://angg.twu.net/dednat6/dednat6/rect.lua.html
--         (find-angg "dednat6/dednat6/rect.lua")
-- Author: Eduardo Ochs <eduardoochs@gmail.com>
-- Version: 2020aug24
-- License: GPL3
--
-- Some examples:
--
--   > = synttorect {[0]="+", {[0]="*", "2", "3"}, {[0]="*", "4", "5"}}
--   +_____.
--   |     |
--   *__.  *__.
--   |  |  |  |
--   2  3  4  5
--
--   > = dedtorect {[0]="+", {[0]="*", "2", "3"}, {[0]="*", "4", "5"}}
--   2  3  4  5
--   ----  ----
--   *     *
--   -------
--   +
--
--   > = dedtorect {[0]="a", "b", {[0]="c", label="foo", bar="=", "d", "e"}}
--      d  e
--      ====foo
--   b  c
--   ----
--   a
--
-- This is a hacking tool that is currently mainly used by:
--
--   (find-dn6 "treetex.lua")
--   (find-dn6 "treetex.lua" "TreeNode")
--   (find-dn6 "treetex.lua" "TreeNode" "__tostring")
--   (find-dn6 "treetex.lua" "TreeNode" "return dedtorect(tn)")
--   (find-dn6 "treetex.lua" "ProofSty-test")
--   (find-dn6 "treetex.lua" "BussProofs-test")
--
-- The function dedtorect, defined below, accepts both Lua tables in
-- the format above, that is easy to write by hand, and TreeNode
-- objects, that are harder to write by hand but are easier to use in
-- functions. The understand how the conversion to TreeNodes works,
-- see:
--
--   (find-dn6 "treetex.lua" "TreeNode")
--   (find-dn6 "treetex.lua" "TreeNode" "from =")
--
-- At this moment Dednat6 only builds TreeNode objects by parsing
-- %:-blocks in .tex files, and only uses these TreeNode objects to
-- generate "\defded"s, but at one point I thought that I could need
-- to generate trees from programs, and edit them in Lua... so I wrote
-- this library.

-- «.Rect»			(to "Rect")
-- «.Rect-tests»		(to "Rect-tests")
-- «.Rect-ded-tests»		(to "Rect-ded-tests")
-- «.syntotorect»		(to "syntotorect")
-- «.synttorect-tests»		(to "synttorect-tests")
-- «.dedtorect»			(to "dedtorect")
-- «.dedtorect-tests»		(to "dedtorect-tests")

--  ____           _   
-- |  _ \ ___  ___| |_ 
-- | |_) / _ \/ __| __|
-- |  _ <  __/ (__| |_ 
-- |_| \_\___|\___|\__|
--                     
-- This is my n-th rewrite of a class of ascii rectangles...
-- See: (find-dn6 "edrxlib.lua" "Rect")
-- «Rect» (to ".Rect")

copy = function (A)
    local B = {}
    for k,v in pairs(A) do B[k] = v end
    setmetatable(B, getmetatable(A))
    return B
  end

torect = function (o)
    if otype(o) == "Rect" then return o end
    if type(o) == "string" then return Rect.new(o) end
    error()
  end

Rect = Class {
  type = "Rect",
  new  = function (str) return Rect(splitlines(str)) end,
  rep  = function (str, n) local r=Rect{}; for i=1,n do r[i]=str end; return r end,
  from = function (o) return type(o) == "string" and Rect.new(o) or o end,
  --
  -- A hack to let us build syntax trees very quickly:
  syntree = function (op, a1, ...)
      if not a1 then return Rect.from(op) end
      local r = Rect.from(a1):syn1(op)
      for _,an in ipairs({...}) do r = r:synconcat(Rect.from(an)) end
      return r
    end,
  --
  __tostring = function (rect) return rect:tostring() end,
  __concat = function (r1, r2) return torect(r1):concat(torect(r2)) end,
  __index = {
    tostring = function (rect) return table.concat(rect, "\n") end,
    copy = function (rect) return copy(rect) end,
    width = function (rect) return foldl(max, 0, map(string.len, rect)) end,
    push1 = function (rect, str) table.insert(rect, 1, str); return rect end,
    push2 = function (rect, str1, str2) return rect:push1(str2):push1(str1) end,
    pad0  = function (rect, y, w, c, rstr)
        rect[y] = ((rect[y] or "")..(c or " "):rep(w)):sub(1, w)..(rstr or "")
        return rect
      end,
    lower = function (rect, n, str)
        for i=1,n do rect:push1(str or "") end
        return rect
      end,
    concat = function (r1, r2, w, dy)
        r1 = r1:copy()
        w = w or r1:width()
        dy = dy or 0
        for y=#r1+1,#r2+dy do r1[y] = "" end
        for y=1,#r2 do r1:pad0(y+dy, w, nil, r2[y]) end
        return r1
      end,
    prepend = function (rect, str) return Rect.rep(str, #rect)..rect end,
    --
    -- Methods for building syntax trees
    syn1 = function (r1, opname) return r1:copy():push2(opname or ".", "|") end,
    synconcat = function (r1, r2)
        return r1:copy():pad0(1, r1:width()+2, "_")..r2:copy():push2(".", "|")
      end,
    --
    -- Methods for building deduction trees
    dedconcat = function (r1, r2)
        local w = r1:width() + 2
        if #r1 <  #r2 then return r1:copy():lower(#r2-#r1):concat(r2, w) end
        if #r1 == #r2 then return r1:copy():concat(r2, w) end
        if #r1  > #r2 then return r1:copy():concat(r2, w, #r1-#r2) end
      end,
    dedsetbar = function (r, barchar, barname)
        if #r == 1 then table.insert(r, 1, "") end
        local trim = function (str) return (str:match("^(.-) *$")) end
        local strover  = trim(r[#r-2] or "")
        local strunder = trim(r[#r])
        local len = max(#strover, #strunder)
        r[#r-1] = (barchar or "-"):rep(len)..(barname or "")
        return r
      end,
    dedaddroot = function (r, rootstr, barchar, barname)
        table.insert(r, "")
        table.insert(r, rootstr)
        return r:dedsetbar(barchar, barname)
      end,
  },
}

-- «Rect-tests» (to ".Rect-tests")
--[[
 (eepitch-lua51)
 (eepitch-kill)
 (eepitch-lua51)
dofile "rect.lua"

r = Rect.new "a\nbb\nccc"
= r
PP(r)
= r:width()
= r:copy():pad0(1, 4, nil, "d")
= r:copy():pad0(1, 4, "_", "d")
= r:copy():push2("op", "|")
= r:copy():push2("op", "|"):pad0(1, r:width()+1, "_")
= r:copy():push2("op", "|"):pad0(1, r:width()+1, "_")..r:copy():push2(".", "|")
= "This => "..r.." <="

abc = Rect.new "a\nbb\nccc"
= abc
PP(abc)
= abc:syn1("op")
= abc:syn1("op"):synconcat(abc)
= abc:syn1("op"):synconcat(Rect.from "d")
= abc:syn1("op"):synconcat(Rect.from(abc))

syntree = Rect.syntree
= syntree "a"
= syntree("a", "b")
= syntree(abc)
= syntree("a", abc)
= syntree("a", abc, "d")
= syntree("a", abc, "d", abc)

-- «Rect-ded-tests» (to ".Rect-ded-tests")
 (eepitch-lua51)
 (eepitch-kill)
 (eepitch-lua51)
dofile "rect.lua"
abc   = Rect.new("a  b\n----\nc")
defgh = Rect.new("   d\n   -\ne  f  g\n-------\nh")
= abc
= defgh
= abc:dedconcat(defgh)
= defgh:dedconcat(abc)
= defgh:dedconcat(abc):dedaddroot("i")
= defgh:dedconcat(abc):dedaddroot("i", "=")
= defgh:dedconcat(abc):dedaddroot("i", "=", "foo")
= defgh:dedconcat(abc):dedaddroot("iiiiiiiii", "=", "foo")
= defgh:dedconcat(abc):dedaddroot("iiiiiiiiii", "=", "foo")
= defgh:dedconcat(abc):dedaddroot("iiiiiiiiiii", "=", "foo")
= Rect.new("")
= Rect.new(""):dedaddroot("iiiii", "=", "foo")

--]]



--                  _   _                      _   
--  ___ _   _ _ __ | |_| |_ ___  _ __ ___  ___| |_ 
-- / __| | | | '_ \| __| __/ _ \| '__/ _ \/ __| __|
-- \__ \ |_| | | | | |_| || (_) | | |  __/ (__| |_ 
-- |___/\__, |_| |_|\__|\__\___/|_|  \___|\___|\__|
--      |___/                                      
--
-- «syntotorect» (to ".syntotorect")

synttorect = function (o)
    if type(o) == "string" then return torect(o) end
    if type(o) == "table" then
      local r = synttorect(o[1]):syn1(o[0])
      for i=2,#o do r = r:synconcat(synttorect(o[i])) end
      return r
    end
    error()
  end

-- «synttorect-tests» (to ".synttorect-tests")
--[[
 (eepitch-lua51)
 (eepitch-kill)
 (eepitch-lua51)
dofile "rect.lua"
tree = {[0]="+", {[0]="*", "2", "3"}, {[0]="*", "4", "5"}}
tree = {[0]="+", {[0]="*", "2", "3"}, {[0]="*", "4", "5"}, bar="=", label="hi"}
= synttorect(tree)
= dedtorect (tree)

--]]


--      _          _ _                      _   
--   __| | ___  __| | |_ ___  _ __ ___  ___| |_ 
--  / _` |/ _ \/ _` | __/ _ \| '__/ _ \/ __| __|
-- | (_| |  __/ (_| | || (_) | | |  __/ (__| |_ 
--  \__,_|\___|\__,_|\__\___/|_|  \___|\___|\__|
--                                              
-- «dedtorect» (to ".dedtorect")
dedtorect = function (o)
    if type(o) == "string" then return torect(o) end
    if type(o) == "table" then
      if #o == 0 then
        local r = dedtorect(o[0])
        if o.bar or o.label then r:dedsetbar(o.bar, o.label) end
        return r
      else
        r = dedtorect(o[1])
        for i=2,#o do r = r:dedconcat(dedtorect(o[i])) end
        return r:dedaddroot(o[0] or "?", o.bar, o.label)
      end
    end
    error()
  end

-- «dedtorect-tests» (to ".dedtorect-tests")
-- See: (find-dn6 "treesegs.lua" "allsegments-tests")
--[[
 (eepitch-lua51)
 (eepitch-kill)
 (eepitch-lua51)
dofile "rect.lua"
= dedtorect "a"
= dedtorect {[0]="a"}
= dedtorect {[0]="a", "b", "c"}
= dedtorect {[0]="a", "b", {"c"}}
= dedtorect {[0]="a", "b", {[0]="c"}}
= dedtorect {[0]="a", "b", {[0]="c", bar="-"}}
= dedtorect {[0]="a", "b", {[0]="c", label="foo"}}
= dedtorect {[0]="a", "b", {[0]="c", label="foo", bar="=", "d", "e"}}
= dedtorect {[0]="a", "b", {[0]="c", label="foo", "d", "e"}}
= dedtorect {[0]="a", "b", {[0]="c", "d", "e"}}

--]]




-- Local Variables:
-- coding: utf-8-unix
-- End: