Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
-- This file:
--   http://anggtwu.net/LUA/Subst1.lua.html
--   http://anggtwu.net/LUA/Subst1.lua
--          (find-angg "LUA/Subst1.lua")
-- Author: Eduardo Ochs <eduardoochs@gmail.com>
--
-- (defun s1 () (interactive) (find-angg "LUA/Subst1.lua"))
-- (defun e1 () (interactive) (find-angg "LUA/ELpeg1.lua"))
-- (defun i1 () (interactive) (find-angg "LUA/Indent1.lua"))

-- «.Subst»			(to "Subst")
-- «.Subst-tests»		(to "Subst-tests")
-- «.totex»			(to "totex")
--   «.ToTeX-fakeclass»		(to "ToTeX-fakeclass")
-- «.totex-tests»		(to "totex-tests")

-- Superseded by:
--   (find-angg "LUA/ToTeX1.lua")


-- (find-angg "LUA/Indent1.lua" "string.indent")
indent        = indent        or function (s) return s end
string.indent = string.indent or function (s) return s end


--  ____        _         _   
-- / ___| _   _| |__  ___| |_ 
-- \___ \| | | | '_ \/ __| __|
--  ___) | |_| | |_) \__ \ |_ 
-- |____/ \__,_|_.__/|___/\__|
--                            
-- This is like the Dang class of tikz1.lua,
--   (find-angg "LUA/tikz1.lua" "Dang")
-- but with lots of configurable options.
-- Subst has four kinds of expansions: in
--     Subst({}):gsub "_<1>_<2+3>_<:return 4+5>_<aa>_<+6><-7><!>_"
--   the "1"           in <1>           is expanded with expn,
--   the "2+3"         in <2+3>         is expanded with expexpr,
--   the ":return 2+3" in <:return 2+3> is expanded with expeval, and
--   the "aa"          in <aa>          is expanded with expfield.
-- The substrings "<+6>", "<-7>", "<!>" at the end above all fall in
-- the "do not expand" category; they are left unchanged. This is for:
--   (find-angg "LUA/Indent1.lua")
-- Note that the original Dang class only supports exprs and evals.
-- Also, the "tostring" method below has several configurable subcases.
-- Usually when we define objects of the class Subst we override at least
-- table_tos and expn. The overrides are done with addoverrides.
--   See: (find-angg "LUA/lua50init.lua" "addoverrides")
--        (to "totex")
--
-- «Subst»  (to ".Subst")

Subst = Class {
  type = "Subst",
  __tostring = function (su) return su:tostring() end,
  __index = {
    --
    fields   = {aa="AAA", bb="BBB"},
    hasfield = function (su, s)    return su.fields[s] end,
    expfield = function (su, s)    return su.fields[s] end,
    expn     = function (su, n)    return format("(expn %d)", n) end,
    expexpr  = function (su, code) return format("(expexpr %s)", code) end,
    expeval  = function (su, code) return format("(expeval %s)", code) end,
    --
    nil_tos    = function (su)      return "(nil)" end,
    number_tos = function (su, n)   return myntos(n) end,
    table_tos  = function (su, tbl) return mytostring(tbl) end,
    tostring   = function (su)      return su:tostring1(su.o) end,
    tostring1  = function (su, o)
        if type(o) == "string" then return o                end
        if type(o) == "nil"    then return su:nil_tos()     end
        if type(o) == "number" then return su:number_tos(o) end
        if type(o) == "table"  then return su:table_tos(o)  end
        return tostring(o)
      end,
    --
    donotexpand = function (su, s)
        return s:match("^[-+!][0-9]*$")
      end,
    --
    pat = "<(.-)>",
    --
    gsub1 = function (su, s)
        if su:donotexpand(s)   then return nil                  end
        if s:match("^[0-9]+$") then return su:expn(s+0)         end
        if su:hasfield(s)      then return su:expfield(s)       end
        if s:match("^:")       then return su:expeval(s:sub(2)) end
        if true                then return su:expexpr(s)        end
      end,
    gsub = function (su, fmt)
        local f = function (s) return su:tostring1(su:gsub1(s)) end
        return (fmt:gsub(su.pat, f))
      end,
    --
    with         = function (su, o) su.o = o; return su end,
    addoverrides = function (su, O) return addoverrides(su, O) end,
  },
}

-- «Subst-tests»  (to ".Subst-tests")
--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Subst1.lua"
  su = Subst {o={20,30}}
= su
  su = Subst {}
fmt = "field: <aa>\n" ..
      "expr:  <2+3>\n" ..
      "eval:  <:return 4+5>\n" ..
      "n:     <1>"
= su:gsub(fmt)

=       expr "2+3"
=       eval "return 2+3"
  su.expexpr = function (su, code) return expr(code) end
  su.expeval = function (su, code) return eval(code) end
= su:expexpr "2+3"
= su:expeval "return 2+3"
= su:gsub(fmt)

  su = Subst {o={20,30}}
= su:gsub(fmt)
O = {
  expn = function (su, n) return "["..su.o[n].."]" end,
}
su:addoverrides(O)
= su:gsub(fmt)

--]]



--  _        _            
-- | |_ ___ | |_ _____  __
-- | __/ _ \| __/ _ \ \/ /
-- | || (_) | ||  __/>  < 
--  \__\___/ \__\___/_/\_\
--                        
-- Based on: (find-angg "LUA/ELpeg1.lua" "ToTeX")
-- This is the basic way to convert ASTs to TeX code.
-- Every time that totex00 is run it creates a new Subst object with:
--   1. overrides for three methods: table_tos, expn, expexpr
--   2. a few other methods: tag, fmt, getn,
--   3. a copy of the argument o.
-- This new Subst object is recursive. In a case like this,
--   o = mkast("*", 2, 3)
--   fmts["*"] = "<1> \\cdot <2>"
-- something like this will happen:
--       totex(o)
--   --> totex0(o)
--   --> totex00(o):tostring()
--   --> totex00(o):tostring1(o)
--   --> totex00(o):table_tos(o)
--   --> totex00(o):gsub("<1> \\cdot <2>")
--   --> totex00(o):expn(1)
--   --> totex00(o):getn(1)
--   --> totex00(o[1])
--
-- Note that this recursion scheme uses several globals: totex,
-- totex0, totex00, fmts, and sometimes funs. I've tried to write
-- alternatives to this with no globals, but in all cases their code
-- ended up very intrincate... in this version with globals at least
-- the code is quite simple.
--
-- To use indent, override the definition of totex below with:
--   totex = function (o) return totex0 (o):indent() end
-- See: (find-angg "LUA/Indent1.lua" "Indent")
--
-- «totex»  (to ".totex")

--totex = function (o) return totex0(o) end
totex   = function (o) return tostring(totex0(o)):indent() end
totex0  = function (o) return totex00(o) end
totex00 = function (o) return ToTeX.fromo(o) end


-- «ToTeX-fakeclass»  (to ".ToTeX-fakeclass")
-- This is a fake class in the sense that its creators,
-- ToTeX.from(A) and ToTeX.fromo(o), return objects of the class
-- Subst, but with some methods overridden. Also, ToTeX.__index
-- is not a list of methods but a list of overrides.

ToTeX = Class {
  type  = "ToTeX",
  from  = function (A) return Subst(copy(A)):addoverrides(ToTeX.__index) end,
  fromo = function (o) return ToTeX.from({o=o}) end,
  __index = {
    --
    -- Override table_tos
    tag  = function (su) return su.o[0] end,
    fmt  = function (su) return fmts[su:tag()] end,
    table_tos = function (su)
        if not su:tag() then return "[No tag]" end
        if not su:fmt() then return "[No fmt: "..su:tag().."]" end
        return su:gsub(su:fmt())
      end,
    --
    -- Override expn
    getn = function (su, n) return totex00(su.o[n]) end,
    expn = function (su, n) return su:getn(n):tostring() end,
    --
    -- For debugging:
    expexpr = function (su, code)
        return L("su,o => "..code)(su, su.o)
      end,
  },
}

-- «totex-tests»  (to ".totex-tests")
--[==[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "Subst1.lua"
dofile "ELpeg1.lua"  -- for mkast

  s = Subst    {o={20,30}}
= s
  t = ToTeX.fromo({20,30})
= t
= otype(s)   --> Subst
= otype(t)   --> Subst
= s
= t

fmts        = VTable {}
fmts["+"]   = "<1> + <2>"
fmts["*"]   = "<1> \\cdot <2>"

funs        = VTable {}
funs["sin"] = VTable {}

o = mkast("+", "a", mkast("*", 2, 3.4567))
= o
= totex  (o)
= totex0 (o)
= totex00(o)
= totex00(o).o
= totex00(o):tag()
= totex00(o):fmt()
= totex00(o):getn(1)
= totex00(o):getn(1).o
= totex00(o):getn(2)
= totex00(o):getn(2).o
= totex00(o):getn(2):fmt()
= totex00(o):getn(2):getn(2)
= totex00(o):getn(2):getn(2).o
= totex00()
= totex00():gsub("a<2+3>b")

--]==]





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