Warning: this is an htmlized version! The original is across this link, and the conversion rules are here.
```-- This file:
-- http://angg.twu.net/dednat5/rect.lua
-- http://angg.twu.net/dednat5/rect.lua.html
--  (find-angg        "dednat5/rect.lua")
--
-- Author:  Eduardo Ochs <eduardoochs@gmail.com>
-- Version: 2015aug05
-- Licence: GPL3
--
-- This is a total rewrite (different data structures, methods, etc) of:
-- (find-dn5 "gab.lua" "Rect")
-- Example:
--   > = synttorect {[0]="+", {[0]="*", "2", "3"}, {[0]="*", "4", "5"}}
--   +_____.
--   |     |
--   *__.  *__.
--   |  |  |  |
--   2  3  4  5

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,
__tostring = function (rect) return rect:tostring() 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,
--
syn1 = function (r1, opname) return r1:copy():push2(opname or ".", "|") end,
synconcat = function (r1, r2)
end,
--
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,
dedroot = function (rect, str)
table.insert(rect, ""); table.insert(rect, str); return rect
end,
dedbar = function (rect, c, label)
local abovebar, belowbar = rect[#rect-2], rect[#rect]
local w = max(#(abovebar or ""), #belowbar)
rect:pad0(#rect-1, w, c or "-", label)
return rect
end,
},
}

synttorect = function (o)
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

dedtorect = function (o)
if type(o) == "table" then
local r = dedtorect(o[1])
for i=2,#o do r = r:dedconcat(dedtorect(o[i])) end
return r:dedroot(o[0] or "?"):dedbar(o.b or "-", o.label)
end
error()
end

synttotex = function (o)
if type(o) == "string" then return o end
if type(o) == "table" then
return (o.tex:gsub("<([%d])>", function (n) return synttotex(o[n+0]) end))
end
error()
end

--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "rect.lua"

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

= torect("a"):dedconcat(torect("b"))
= torect("a"):dedconcat(torect("b")):dedroot("c"):dedbar("-", "foo")
= torect("a"):dedconcat(torect("bbbbbb")):dedroot("c"):dedbar("-", "foo")
= torect("a"):dedconcat(torect("b")):dedroot("cccccc"):dedbar("-", "foo")
= torect("2"):syn1("*"):synconcat(torect("3"))
tree = {[0]="+", {[0]="*", "2", "3"}, {[0]="*", "4", "5"}}
tree = {[0]="+", {[0]="*", "2", "3"}, {[0]="*", "4", "5"}, b="=", label="hi"}
= synttorect(tree)
= dedtorect (tree)

tree = { { { "[P&Q]^1",
[0]="P"
},
{ { "[P&Q]^1",
[0]="Q"
},
"Q->R",
[0]="R"
},
[0]="P&R"
},
label="1",
[0]="P&Q->P&R"
}
= dedtorect (tree)

--]]

-- Local Variables:
-- coding: raw-text-unix
-- End:

```