Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
-- This file:
--   http://anggtwu.net/LUA/Comprehensions1.lua.html
--   http://anggtwu.net/LUA/Comprehensions1.lua
--          (find-angg "LUA/Comprehensions1.lua")
-- Author: Eduardo Ochs <eduardoochs@gmail.com>
-- Date: 2025sep13
--
-- These classes can be used to show how set comprehensions are
-- calculated step-by-step, using trees drawn as LaTeX matrices like
-- the ones in:
--
--   (mpgp 9 "comprehension-tables")
--   (mpga   "comprehension-tables")
--
-- «.tocurly»			(to "tocurly")
-- «.tocurly-tests»		(to "tocurly-tests")
-- «.MTree»			(to "MTree")
-- «.MTree-tests-defs»		(to "MTree-tests-defs")
-- «.MTree-tests»		(to "MTree-tests")
-- «.Filter»			(to "Filter")
-- «.Comprehension»		(to "Comprehension")
-- «.Comprehension-tests»	(to "Comprehension-tests")


-- «tocurly»  (to ".tocurly")
tocurly_prefix = ""
tocurly_prefix = "\\"
tocurly = function (L, p)
    p = p or tocurly_prefix
    return p.."{"..mapconcat(tostring, L, ",")..p.."}"
  end

-- «tocurly-tests»  (to ".tocurly-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Comprehensions1.lua"
= tocurly(seq(2,10,3))
= tocurly(seq(2,10,-1))
  tocurly_prefix = ""
= tocurly(seq(2,10,3))

--]]



--  __  __ _____              
-- |  \/  |_   _| __ ___  ___ 
-- | |\/| | | || '__/ _ \/ _ \
-- | |  | | | || | |  __/  __/
-- |_|  |_| |_||_|  \___|\___|
--                            
-- «MTree»  (to ".MTree")
-- Base on: (mpgp 9 "comprehension-tables")
--          (mpga   "comprehension-tables")
--          (mpga   "comprehension-tables" "Stop")
--
-- MTrees are used to show how set comprehensions
-- can be calculated using trees.
-- The "M" means "LaTeX <M>atrix",
-- and if m is an MTree then m:totex()
-- generates the body of a LaTeX matrix,
-- in a format like this:
--
--   x & 6-x & \{x,...,6-x\} & y & (x,y) \\\hline
--   1 &  5  & \{1,2,3,4,5\} & 1 & (1,1) & 
--     &     &               & 2 & (1,2) & 
--     &     &               & 3 & (1,3) & 
--     &     &               & 4 & (1,4) & 
--     &     &               & 5 & (1,5) & 
--   2 &  4  &   \{2,3,4\}   & 2 & (2,2) & 
--     &     &               & 3 & (2,3) & 
--     &     &               & 4 & (2,4) & 
--   3 &  3  &     \{3\}     & 3 & (3,3) & 
--   4 &  2  &     \{\}      & \Stop & 
--   5 &  1  &     \{\}      & \Stop & 
--
MTree = Class {
  type = "MTree",
  new  = function (o) return MTree {[0]=o} end,
  _    = function (o) return MTree {[0]=o} end,
  from = function (o)
      if otype(o) == "MTree"  then return o end
      if  type(o) == "number" then o = tostring(o) end
      if  type(o) == "string" then return MTree {[0]=o} end
      local T = MTree(map(MTree.from, o))
      T[0] = o[0]
      return T
    end,
  tosyntree  = function (o) return MTree.from(o):tosyntree() end,
  totexrect  = function (o) return MTree.from(o):totexrect() end,
  torect     = function (o) return MTree.from(o):torect()    end,
  totex      = function (o) return MTree.from(o):totex()     end,
  __tostring = function (o) return MTree.from(o):tostring()  end,
  __index = {
    --
    -- Generate the top line with the column names, as a Rect.
    top_rect_tex     = function (T) return T:top_rect(" & ", " \\\\\\hline") end,
    top_rect_syntree = function (T) return T:top_rect(" | ", "") end,
    top_rect         = function (T,sep,post)
        if not T.columnnames then return Rect {} end
        local line = table.concat(T.columnnames, sep)..post
        return Rect {line}
      end,
    --
    -- The default :tostring() method, that uses SynTrees.
    tostring  = function (T) return T:torect():tostring() end,
    torect    = function (T) return T:top_rect_syntree() / T:tosyntree():torect() end,
    tosyntree = function (T) return SynTree.from(T) end,
    --
    -- The :totex() method generates the body of a LaTeX matrix.
    -- The matrix is generated from left to right, recursively.
    totex       = function (T) return T:totexrect():tostring() end,
    totexrect   = function (T) return T:top_rect_tex() / T:totexrect_LR() end,
    totexrect_R = function (T)
        local R = Rect {}
        for _,node in ipairs(T) do
          for _,line in ipairs(MTree.totexrect(node)) do
            table.insert(R, line)
          end
        end
        return R
      end,
    totexrect_LR = function (T)
        local R = T:totexrect_R()
        if not T[0] then return R end
        if #T==0 then return Rect {T[0].." \\\\"} end
        local Ltop   = format("%s & ", T[0])
        local Lblank = Ltop:gsub("[^&]", " ")
        local L      = Ltop / Rect.rep(Lblank, #R-1)
        return L..R  
      end,
    --
    -- Methods that add elements and subtrees to an MTree.
    -- These methods are very tricky! See MTree_test{1,2,3} below.
    add_ = function (T,o)
        local subTree = MTree.new(o)
        table.insert(T,subTree)
        return subTree
      end,
    addstop  = function (T) T:add_("\\Stop") end,
    addfalse = function (T) T:add_("\\False"):addstop() end,
    addtrue  = function (T) return T:add_("\\True") end,
    addstopifempty = function (T) if #T == 0 then T:addstop() end end,
    setcolumnnames = function (T,cols) T.columnnames = cols; return T end,
  },
}

--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Comprehensions1.lua"
T = MTree.new()
T:add_(1)
PPV(T)

do
  local T = T:add_(2)
  T:add_(23)
  T:add_(24)
end
T:add_(5)
= T
= T:totex()
PPV(T)

T = MTree.new()
for x=1,2 do
  local T = T:add_(x)
  for y=3,4 do
    local T = T:add_(y)
  end
end
= T
= T:totex()
T.columnnames = {"x","y"}
= T
= T:totex()

PPV(T)

--]]



--  __  __ _____                _            _       
-- |  \/  |_   _| __ ___  ___  | |_ ___  ___| |_ ___ 
-- | |\/| | | || '__/ _ \/ _ \ | __/ _ \/ __| __/ __|
-- | |  | | | || | |  __/  __/ | ||  __/\__ \ |_\__ \
-- |_|  |_| |_||_|  \___|\___|  \__\___||___/\__|___/
--                                                   
-- «MTree-tests-defs»  (to ".MTree-tests-defs")
-- These tests were hand-written, but the class `Comprehension',
-- defined below, generates code similar to these examples.

MTree_test0 = function ()
    local T = MTree.new()
    for x=1,2 do
      for y=3,4 do
        for z=5,6 do
          T:add_(format("(%s,%s,%s)", x,y,z))
        end
      end
    end
    return T
  end

MTree_test1 = function ()
    local T_ = MTree.new()
    for x=1,2 do
      local Tx = T:add_(x)
      for y=3,4 do
        local Ty = T:add_(y)
        for z=5,6 do
          local Tx = T:add_(z)
        end
      end
    end
    return T_
  end

-- { x in {1,...,5}, y in {x,...,6-x} ; (x,y) }
--                               \-/
--                               lim
--                        \---------/
--                           range
--
MTree_test2 = function ()
    local T = MTree._()
    for x=1,5 do
      local lim = 6-x
      local range = tocurly(seq(x,lim))
      local T = T:add_(x)       -- also called T
      local T = T:add_(lim)     -- also called T
      local T = T:add_(range)   -- also called T, etc
      for y=x,6-x do
        local T = T:add_(y)
        local T = T:add_(format("(%s,%s)",x,y))
      end
      T:addstopifempty()
    end
    T.columnnames = {"x", "6-x", "\\{x,...,6-x\\}", "y", "(x,y)"}
    return T
  end

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

= MTree_test0()
= MTree_test0():totex()

T3 = {[0]=3, 5, 6}
T4 = {[0]=4, 5, 6}
T1 = {[0]=1, T3, T4}
T2 = {[0]=2, T3, T4}
T0 =       { T1, T2}
T  = MTree.from(T0)
T.columnnames = {"x","y","z"}

= T 
= MTree.from(T0)
= MTree.totex(T0)
= MTree.totex(T)

T = MTree_test1()
= T;
= T:totex();

tocurly_prefix = ""
T = MTree_test2()
= T;
= T:totex();

tocurly_prefix = "\\"
T = MTree_test2()
= T;
= T:totex();

--]]


-- «Filter»  (to ".Filter")

MTree_test3 = function ()
    local T = MTree.new()
    for x=1,5 do
      local lim = 6-x
      local range = tocurly(seq(x,lim))
      local T = T:add_(x)       -- also called T
      local T = T:add_(lim)     -- also called T
      local T = T:add_(range)   -- also called T, etc
      for y=x,6-x do
        local T = T:add_(y)
        local T = T:add_(format("(%s,%s)",x,y))
      end
      T:addstopifempty()
    end
    T.columnnames = {"x", "6-x", "\\{x,...,6-x\\}", "y", "(x,y)"}
    return T
  end

--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Comprehensions1.lua"
= MTree_test3()

--]]






--   ____                               _                    _             
--  / ___|___  _ __ ___  _ __  _ __ ___| |__   ___ _ __  ___(_) ___  _ __  
-- | |   / _ \| '_ ` _ \| '_ \| '__/ _ \ '_ \ / _ \ '_ \/ __| |/ _ \| '_ \ 
-- | |__| (_) | | | | | | |_) | | |  __/ | | |  __/ | | \__ \ | (_) | | | |
--  \____\___/|_| |_| |_| .__/|_|  \___|_| |_|\___|_| |_|___/_|\___/|_| |_|
--                      |_|                                                
--
-- «Comprehension»  (to ".Comprehension")

Comprehension = Class {
  type = "Comprehension",
  new  = function () return Comprehension {prog = Rect{}} end,
  fromcommands = function (commands)
      return Comprehension.new():runcommands(commands):Function()
    end,
  __tostring = function (co) return co.prog:tostring() end,
  __index = {
    pre = function (co,r1)
        co.prog = torect(r1)/co.prog
        return co
      end,
    post = function (co,r2)
        co.prog = co.prog/torect(r2)
        return co
      end,
    --
    Column = function (co,expr)
         return co:pre(format("local T = T:add_(%s)", expr))
      end,
    Local = function (co,vardef,var)
         if var then co:Column(var) end
         return co:pre(format("local %s", vardef))
      end,
    For = function (co,varin,var)
         if var then co:Column(var) end
         co.prog = torect(format("for %s do", varin))
                   / ("  " .. co.prog)
                   / torect("end")
                   / torect("T:addstopifempty()")
         return co
      end,
    --
    var  = function (co,str)   return varin:match("^%s*([0-9A-Za-z_]+)") end,
    Gen  = function (co,varin) return co:For(varin, co:var(varin)) end,
    Expr = function (co,expr)  return co:Column(expr) end,
    --
    Function = function (co)
        if co.columnnames then
          local body = mapconcat(mytostring, co.columnnames, ", ")
          co:pre(format("T.columnnames = {%s}", body))
        end
        co:pre ("local T = MTree.new()")
        co:post("return T")
        co.prog = "function ()"
                  / ("    " .. co.prog)
                  / "  end"
        return co
      end,
    Colnames = function (co, ...)
        co.columnnames = {...}
        return co
      end,
    --
    splitcommand = function (co,line)
        return VTable(map(bitrim, split(line, "([^|]+)")))
      end,
    runcommand = function (co,line)
        local parts = co:splitcommand(line)
        co[parts[1]](co, unpack(parts,2))
        return co
      end,
    runcommands = function (co,lines)
        if type(lines) == "string" then lines = splitlines(lines) end
        for i=#lines,1,-1 do
          local line = lines[i]
          line = line:gsub("%-%-.*", "")
          line = bitrim(line)
          if line ~= "" then co:runcommand(line) end
        end
        return co
      end,
    eval = function (co) return expr(tostring(co))() end,
  },
}

-- «Comprehension-tests»  (to ".Comprehension-tests")
--[==[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Comprehensions1.lua"
tocurly_prefix = ""
tocurly_prefix = "\\"
= MTree_test2()

co = Comprehension.new()
= co:Column  ('format("(%s,%s)",x,y)')
= co:For     ('y=x,6-x',   'y')
= co:Local   ('range = tocurly(seq(x,lim))', 'range')
= co:Local   ('lim = 6-x', 'lim')
= co:For     ('x=1,5',     'x')
= co:Function()

co = Comprehension.new()
= co:splitcommand "Local  | range = tocurly(seq(x,lim)) | range"
= co:runcommand   "Local  | range = tocurly(seq(x,lim)) | range"

cmds = [=[
  Colnames | x | 6-x | \{x,\ldots,6-x\} | y | (x,y) 
  For      | x = 1,5                     | x
  Local    | lim = 6-x                   | lim
  Local    | range = tocurly(seq(x,lim)) | range
  For      | y = x,6-x                   | y
  Column   | format("(%s,%s)",x,y)
]=]

co = Comprehension.fromcommands(cmds)
= co
= co:eval()
= co:eval():totex()

--]==]










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