Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
-- This file:
-- http://angg.twu.net/dednat5/stacks.lua
-- http://angg.twu.net/dednat5/stacks.lua.html
--  (find-angg        "dednat5/stacks.lua")
--
-- The easiest way to work with user-given expressions in dednat6 is
-- to build them as syntactic trees using a stack-based, Forth-like
-- language. We want to generate two kinds of output from these
-- expressions: 1) 2D ascii diagrams like the one below, for debugging
-- (built using the "Rect" class),
--
--   > = synttorect {[0]="+", {[0]="*", "2", "3"}, {[0]="*", "4", "5"}}
--   +_____.
--   |     |
--   *__.  *__.
--   |  |  |  |
--   2  3  4  5
--
-- and 2) LaTeX output, mainly for "underbrace structures" like this
-- one:
--
--    P & Q -> P
--   \-/ \-/  \-/
--    0   1    0
--   \-----/
--      0
--   \----------/
--          1
--
-- This file defines the classes "Rect" and "Stack", and some
-- high-level words that use them.
--
-- «.Rect»		(to "Rect")
-- «.Rect-tests»	(to "Rect-tests")
-- «.syntotorect»	(to "syntotorect")
-- «.synttorect-tests»	(to "synttorect-tests")
-- «.dedtorect»		(to "dedtorect")
-- «.dedtorect-tests»	(to "dedtorect-tests")
-- «.Stack»		(to "Stack")
-- «.Stack-tests»	(to "Stack-tests")
-- «.synttotex»		(to "synttotex")
-- «.ubs»		(to "ubs")
-- «.ubs-tests»		(to "ubs-tests")





--  ____           _   
-- |  _ \ ___  ___| |_ 
-- | |_) / _ \/ __| __|
-- |  _ <  __/ (__| |_ 
-- |_| \_\___|\___|\__|
--                     
-- This is a total rewrite (different data structures, methods, etc) of:
-- (find-dn5 "gab.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,
  __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,
    --
    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,
    --
    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,
  },
}

-- «Rect-tests» (to ".Rect-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "stacks.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(".", "|")
= "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"))

--]]



--                  _   _                      _   
--  ___ _   _ _ __ | |_| |_ ___  _ __ ___  ___| |_ 
-- / __| | | | '_ \| __| __/ _ \| '__/ _ \/ __| __|
-- \__ \ |_| | | | | |_| || (_) | | |  __/ (__| |_ 
-- |___/\__, |_| |_|\__|\__\___/|_|  \___|\___|\__|
--      |___/                                      
--
-- «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 "stacks.lua"
tree = {[0]="+", {[0]="*", "2", "3"}, {[0]="*", "4", "5"}}
tree = {[0]="+", {[0]="*", "2", "3"}, {[0]="*", "4", "5"}, b="=", 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
      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

-- «dedtorect-tests» (to ".dedtorect-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "stacks.lua"
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)

--]]





--  ____  _             _    
-- / ___|| |_ __ _  ___| | __
-- \___ \| __/ _` |/ __| |/ /
--  ___) | || (_| | (__|   < 
-- |____/ \__\__,_|\___|_|\_\
--                           
-- «Stack» (to ".Stack")

Stack = Class {
  type    = "Stack",
  new     = function () return Stack {} end,
  __index = {
    push  = function (s, o) table.insert(s, o); return s end,
    dropn = function (s, n) for i=1,n do s[#s] = nil end end,
    pop   = function (s) return                            s[#s], s:dropn(1) end,
    pop2  = function (s) return                   s[#s-1], s[#s], s:dropn(2) end,
    pop3  = function (s) return          s[#s-2], s[#s-1], s[#s], s:dropn(3) end,
    pop4  = function (s) return s[#s-3], s[#s-2], s[#s-1], s[#s], s:dropn(4) end,
    clean = function (s) s:dropn(#s); return s end,
    PP    = function (s) PP(s); return s end,
    --
    doword = function (s, word)
        if     word == "()"  then s:push {"(", s:pop(), ")", tex="(<2>)"}
        elseif word == "u"   then s:push {[0]=s:pop(), s:pop(), tex="\\underbrace{\n<1>\n}_{<0>}"}
        elseif word == "bin" then s:push {[2]=s:pop(), [3]=s:pop(), [1]=s:pop(), tex="<1> <2>\n <3>"}
        elseif word == "pre" then s:push {[1]=s:pop(), [2]=s:pop(), tex="<1> <2>"}
        else   s:push(word)
        end
        return s
      end,
    dowords = function (s, bigstr)
        for _,word in ipairs(split(bigstr)) do s:doword(word) end
        return s
      end,
  },
}

--[==[
-- «Stack-tests» (to ".Stack-tests")
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "istanbulall.lua"
s = Stack.new()
s:push(22):push(33):PP()
= s:clean():push(22):push(33):PP():push(44):PP():dropn(2):PP():pop()

--]==]



-- «synttotex» (to ".synttotex")
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



--        _         
--  _   _| |__  ___ 
-- | | | | '_ \/ __|
-- | |_| | |_) \__ \
--  \__,_|_.__/|___/
--                  
-- «ubs» (to ".ubs")
ubs0 = function (bigstr) return Stack.new():dowords(bigstr):pop() end
ubstree = function (bigstr) return synttorect(ubs0(bigstr)) end
ubstex  = function (bigstr) return synttotex (ubs0(bigstr)) end

ubs = function (bigstr) return ubstex(bigstr) end
ubs = function (bigstr) print(ubstree(bigstr)); print(); return ubstex(bigstr) end


--[==[
-- «ubs-tests» (to ".ubs-tests")
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "stacks.lua"
--
-- (find-istfile "1.org" "Some non-tautologies: DeMorgan")
--
-- ¬(P  & Q)->(¬ P v  ¬ Q)
--   10  01      10    01
--     00      02    20
-- 32              22
--          22
--
= ubs "P Q \\& bin () 2 + bin"
= ubs [[
  P 10 u   Q 01 u   \& bin 00 u    () \neg pre 32 u
  P 10 u   \neg pre 02 u   Q 01 u \neg pre 20 u   \lor bin 22 u ()
  \to bin 22 u
]]

PP(ubs("a () ()"))
= synttorect(ubs("a () ()"))
= synttorect(ubs([[ P 10 u ]]))
= synttorect(ubs([[ P 10 u Q 01 u \& bin ]]))
= synttorect(ubs([[ P 10 u Q 01 u \& bin 00 u    () \neg pre 32 u ]]))
= ubs [[ P 10 u Q 01 u \& bin 00 u    () \neg pre 32 u ]]

= ubstree [[ P 0 u Q 1 u \& bin 0 u ]] 
= ubstree  [[ P 0 u   Q 1 u   \& bin 0 u   P 0 u   -> bin 1 u ]] 
= ubs      [[ P 0 u   Q 1 u   \& bin 0 u   P 0 u   -> bin 1 u ]] 
= ubstreetex [[ P 0 u   Q 1 u   \& bin 0 u   P 0 u   \to bin 1 u ]] 

--]==]




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

--]]


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