Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
-- This file:
--   http://angg.twu.net/LUA/Rect.lua.html
--   http://angg.twu.net/LUA/Rect.lua
--           (find-angg "LUA/Rect.lua")
-- Author: Eduardo Ochs <eduardoochs@gmail.com>
-- Version: 2021sep14
--
-- This is another copy of the Rect class. See:
--   (find-angg "LUA/lua50init.lua" "Rect")
--   (find-dn6 "rect.lua" "Rect")
--
-- This copy is the best place for tests, clean-ups, and mess-ups.


-- «.Rect»			(to "Rect")
-- «.Rect-tests»		(to "Rect-tests")
-- «.SynTree»			(to "SynTree")
-- «.SynTree-tests»		(to "SynTree-tests")
-- «.DedTree»			(to "DedTree")
-- «.UndTree»			(to "UndTree")
-- «.UndTree-tests»		(to "UndTree-tests")
-- «.undtolatex»		(to "undtolatex")
-- «.undtolatex-tests»		(to "undtolatex-tests")
--
-- «.Rect-ded-tests»		(to "Rect-ded-tests")




--  ____           _   
-- |  _ \ ___  ___| |_ 
-- | |_) / _ \/ __| __|
-- |  _ <  __/ (__| |_ 
-- |_| \_\___|\___|\__|
--                     
-- «Rect»  (to ".Rect")
-- Also here: (find-angg "LUA/lua50init.lua" "Rect")

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)
      if type(o) == "string" then return Rect.new(o) end
      if type(o) == "number" then return Rect.new(tostring(o)) end
      return o -- missing: test otypeness
    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 rect.w or foldl(max, 0, map(string.len8, 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,
    above = function (r1, r2)
        local r = copy(r1)
	for i=1,#r2 do table.insert(r, r2[i]) end
	return r
    end,
    --
    -- Low-level methods for building syntax trees:
    --   syn1 draws a "|" above a rect and draws the opname above it,
    --   synconcat draws two syntax trees side by side and joins them with "_"s.
    syn1 = function (r1, opname, v)
        return r1:copy():push2(opname or ".", v or "|")
      end,
    synconcat = function (r1, r2, v)
        return r1:copy():pad0(1, r1:width()+2, "_")
            .. r2:copy():push2(".", v or "|")
      end,
    --
    -- Low-level methods for building deduction trees:
    --   dedconcat draws two deduction trees side by side,
    --   dedsetbar draws or redraws the bar above the conclusion,
    --   dedaddroot adds a bar and a conclusion below some trees drawn
    --   side by side.
    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 "")  -- the hypotheses above the bar
        local strunder = trim(r[#r])          -- the conclusion below the bar
        local len = max(#strover, #strunder)
	local bar = (barchar or "-"):rep(len)
        r[#r-1] = bar..(barname or "")
        return r
      end,
    dedaddroot = function (r, rootstr, barchar, barname)
        table.insert(r, "")                  -- The bar will be here.
        table.insert(r, rootstr)             -- Draw the conclusion,
        return r:dedsetbar(barchar, barname) -- and then set the bar.
      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.." <="

--]]



--  ____            _____              
-- / ___| _   _ _ _|_   _| __ ___  ___ 
-- \___ \| | | | '_ \| || '__/ _ \/ _ \
--  ___) | |_| | | | | || | |  __/  __/
-- |____/ \__, |_| |_|_||_|  \___|\___|
--        |___/                        
--
-- «SynTree»  (to ".SynTree")
-- Also here: (find-angg "LUA/lua50init.lua" "SynTree")
--
SynTree = Class {
  type = "SynTree",
  from = function (A) return deepcopymt(A, SynTree) end,
  tabletorect = function (o)
      local op = o[0]
      if type(op) == "number" then op = tostring(op) end
      if #o == 0 then return Rect.new(op or ".") end
      local r = SynTree.torect(o[1]):syn1(op)
      for i=2,#o do r = r:synconcat(SynTree.torect(o[i])) end
      return r
    end,
  torect = function (o)
      if type(o) == "number" then return Rect.new(o) end
      if type(o) == "string" then return Rect.new(o) end
      if type(o) == "table" then return SynTree.tabletorect(o) end
    end,
  __tostring = function (o) return o:torect():tostring() end,
  __index = {
    torect = function (o)
        return SynTree.torect(o)
      end,
  },
}

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

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

* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Rect.lua"
s0 = {[0]="+", {[0]="*", "2", "3"}, {[0]="*", "4", "5"}}
s0 = {[0]="+", {[0]="*", "2", "3"}, {[0]="*", "4", "5"}, {6, 7, 8}}
s1 = SynTree.from(s0)
= s1
PPPV(s1)
PPPV(s1[1])
= mytostringpv(s1)
= s1[0]
= s1[1]

--]]


--  ____           _ _____              
-- |  _ \  ___  __| |_   _| __ ___  ___ 
-- | | | |/ _ \/ _` | | || '__/ _ \/ _ \
-- | |_| |  __/ (_| | | || | |  __/  __/
-- |____/ \___|\__,_| |_||_|  \___|\___|
--                                      
-- «DedTree»  (to ".DedTree")
-- Also here: (find-angg "LUA/lua50init.lua" "DedTree")
--
DedTree = Class {
  type = "DedTree",
  from = function (A) return deepcopymt(A, DedTree) end,
  torect = function (o)
      if type(o) == "number" then return Rect.new(o) end
      if type(o) == "string" then return Rect.new(o) end
      if type(o) == "table" then
        if #o == 0 then
          local r = DedTree.torect(o[0] or "?")
          r:dedsetbar(o.bar, o.label)
          return r
        else
          r = DedTree.torect(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,
  __tostring = function (o) return o:torect():tostring() end,
  __index = {
    torect = function (o)
        return DedTree.torect(o)
      end,
  },
}


--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Rect.lua"
d = DedTree.from
d = DedTree.torect

= d "a"
= d {[0]="a"}
= d {[0]="a", "b", "c"}
= d {[0]="a", "b",          {"c"}}
= d {[0]="a", "b", {[0]="?", "c"}}
= d {[0]="a", "b", {[0]="?"}}
= d {[0]="a", "b", {[0]="c"}}
= d {[0]="a", "b", {[0]="c", bar="-"}}
= d {[0]="a", "b", {[0]="c", label="foo"}}
= d {[0]="a", "b", {[0]="c", label="foo", bar="=", "d", "e"}}
= d {[0]="a", "b", {[0]="c", label="foo", "d", "e"}}
= d {[0]="a", "b", {[0]="c", "d", "e"}}

A = {[0]="a", "b", {[0]="c", label="foo", bar="=", "d", "e"}}
= DedTree.from(A)
= DedTree.from(A)[0]
= DedTree.from(A)[1]
= DedTree.from(A)[2]
= DedTree.from(A)[2].bar
= DedTree.from(A)[2].label

--]]




--  _   _           _ _____              
-- | | | |_ __   __| |_   _| __ ___  ___ 
-- | | | | '_ \ / _` | | || '__/ _ \/ _ \
-- | |_| | | | | (_| | | || | |  __/  __/
--  \___/|_| |_|\__,_| |_||_|  \___|\___|
--                                       
-- «UndTree»  (to ".UndTree")
--
UndTree = Class {
  type = "UndTree",
  from = function (A) return deepcopymt(A, UndTree) end,
  torect = function (o)
      if type(o) == "number" then return Rect.new(o) end
      if type(o) == "string" then return Rect.new(o) end
      if type(o) == "table" then return UndTree.from(o):utorect() end 
      error()
    end,
  -- __tostring = function (o) return o:torect():tostring() end,
  --
  __tostring = function (o) return UndTree.u8ify(o:torect():tostring()) end,
  u8ify1 = function (str) return "╰"..("─"):rep(#str - 2).."╯" end,
  u8ify  = function (str) return (str:gsub("`%-*'", UndTree.u8ify1)) end,
  --
  __index = {
    torect = function (o)
        return UndTree.torect(o)
      end,
    --
    sep = "  ",
    utorect0 = function (o)
        local r = UndTree.torect(o[1])
        for i=2,#o do r = r..o.sep..UndTree.torect(o[i]) end
        return r
      end,
    utorect1 = function (o, width)
        local a,b,c,d = "`", "-", "'", "-"
	if width >= 2 then return Rect.from(a..b:rep(width-2)..c) end
	return Rect.from(d:rep(width))
      end,
    utorect = function (o)
        local r0 = (#o == 0) and Rect.from(" ") or o:utorect0(1)
        local r2 = (o[0]) and Rect.from(o[0]) or Rect.from("")
	local w  = max(r0:width(), r2:width())
	local r1 = o:utorect1(w)
	return r0:above(r1):above(r2)
      end,
  },
}

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

= UndTree.u8ify("`--'")

= UndTree.from({[0]=4,  11, 22}):utorect()
= UndTree.from({[0]=4,  11, 22}):utorect0()
= UndTree.from({[0]=4,  11, 22}):utorect()


= UndTree.from({[0]=4         }):utorect()
= UndTree.from({[0]=nil, 11, 22}):utorect()
= UndTree.from({               }):utorect()

A = {[0]=nil, 11, 22}
B = {[0]=3, 44, 55}
B = {[0]=3, 44, A}
C = {[0]=nil, A, B}
= UndTree.from(A)
= UndTree.from(C)
PPP(UndTree.from(C))

-- (find-es "lua5" "install-5.1.5")

= UndTree.__index:utorect1(4)
= UndTree.__index:utorect1(2)
= UndTree.__index:utorect1(1)
= UndTree.__index:utorect1(0)


r0, r1, r2 = UndTree.from({[0]=4, 11, 22}):utorect()
= r0
= r1
= r2

A = Rect.from("a\nabc")
B = Rect.from("|\na\nabcd")
= A
= B
= A:above(B)


u = UndTree.from {11, 222}
= u:utorect0()

r = Rect.from("")
PPPV(r)
= r:width()

--]]



-- «undtolatex»  (to ".undtolatex")
undtolatex = function (o, pre)
    if type(o) == "string" then return o end
    if type(o) == "number" then return tostring(o) end
    local s = "     "
    local f = function (o) return undtolatex(o, pre..s).."\n"..pre..s end
    pre = pre or ""
    local upper = mapconcat(f, o, "")
    local lower = "}{"..o[0].."}"
    return "\\und{"..upper..lower
  end

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

A = {[0]=22, 33, 44, 55}
B = {[0]=66}
C = {[0]=77, 88, A, B, 99}
= undtolatex(A)
= ".."..undtolatex(A, "..")
= undtolatex(B)
= undtolatex(C)
= ".."..undtolatex(C, "..")

--]]





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

--]]



-- See: (find-dn6 "treesegs.lua" "allsegments-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Rect.lua"
dedtorect = DedTree.torect
dedtorect = DedTree.from
= 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"}}

A = {[0]="a", "b", {[0]="c", label="foo", bar="=", "d", "e"}}
= DedTree.from(A)

A = {[0]="a", "b", {[0]="c", label="foo", bar="=", "d", "e"}}
= DedTree.torect(A)

= mytostringpv(DedTree.from(A))

A = {[0]="a", "b", {[0]="c", label="foo", bar="=", "d", "e"}}
= mytostringpv(DedTree.torect(A))


= dedtorect(A)
= DedTree.from(A)
= DedTree.torect(A)


--]]









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