Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
-- This file: -- http://anggtwu.net/LUA/Tos3.lua.html -- http://anggtwu.net/LUA/Tos3.lua -- (find-angg "LUA/Tos3.lua") -- Author: Eduardo Ochs <eduardoochs@gmail.com> -- -- This is a rewrite of this class, -- (find-angg "LUA/lua50init.lua" "Tos") -- (find-angg "LUA/Tos.lua") -- that can print nested tables with nested indentation, -- can serialize tables, and do lots of other nice things. -- The default is still to print tables linearly. -- -- (defun e () (interactive) (find-angg "LUA/Tos3.lua")) -- (defun o () (interactive) (find-angg "LUA/Tos2.lua")) -- «.MakePrint» (to "MakePrint") -- «.MakePrint-tests» (to "MakePrint-tests") -- «.TosConcat» (to "TosConcat") -- «.TosConcat-tests» (to "TosConcat-tests") -- «.Tos» (to "Tos") -- «.Tos-tests» (to "Tos-tests") -- «.variants» (to "variants") -- «.variants-tests» (to "variants-tests") -- «.mytostring» (to "mytostring") -- «.PP» (to "PP") -- «.PP-tests» (to "PP-tests") -- __ __ _ ____ _ _ -- | \/ | __ _| | _____| _ \ _ __(_)_ __ | |_ -- | |\/| |/ _` | |/ / _ \ |_) | '__| | '_ \| __| -- | | | | (_| | < __/ __/| | | | | | | |_ -- |_| |_|\__,_|_|\_\___|_| |_| |_|_| |_|\__| -- -- «MakePrint» (to ".MakePrint") -- See: (find-angg "LUA/lua50init.lua" "PP" "PP_ =") MakePrint = Class { type = "MakePrint", with = function (...) return MakePrint{}:with(...) end, __index = { with = function (mp,...) mp.o = pack(...); mp.orig = mp.o; return mp end, concat = function (mp,sep) mp.o = table.concat(mp.o, sep); return mp end, mapn = function (mp,f) mp.o = map(f, mp.o, mp.o.n); return mp end, map = function (mp,f) mp.o = map(f, mp.o); return mp end, pre = function (mp,p) return mp:map(function (s) return p..s end) end, print = function (mp) print(mp.o); return mp end, ret = function (mp) return myunpack(mp.orig) end, -- fold = function (mp,f) mp.o = foldl1(f, mp.o); return mp end, _ddot = function (a,b) return a.." "..b end, ddot = function (mp) return mp:fold(mp._ddot) end, }, } -- «MakePrint-tests» (to ".MakePrint-tests") --[[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "Tos3.lua" PP2 = function (...) return MakePrint.with(...):mapn(mytostring):pre(" "):concat():print():ret() end PP2(nil, {20,30}, nil) = PP2(nil, {20,30}, nil) require "Tree1" -- (find-angg "LUA/Tree1.lua") PPT = function (...) return MakePrint.with(...):mapn(treetor):ddot():print():ret() end PPT({1, 2, {3, 4}}, {22, 33}, true, "foo") = PPT({1, 2, {3, 4}}, {22, 33}, true, "foo") --]] -- _____ ____ _ -- |_ _|__ ___ / ___|___ _ __ ___ __ _| |_ -- | |/ _ \/ __| | / _ \| '_ \ / __/ _` | __| -- | | (_) \__ \ |__| (_) | | | | (_| (_| | |_ -- |_|\___/|___/\____\___/|_| |_|\___\__,_|\__| -- -- This class implements several ways of concatenating lists of -- sorted "key-value string"s. The most basic way works like this, -- {"a=b", "c=d"} -> "{a=b, c=d}" -- and the more advanced ways use newlines and indentation in -- different ways depending on the level. -- -- «TosConcat» (to ".TosConcat") TosConcat = Class { type = "TosConcat", __call = function (tc, strs) return tc:concat(strs) end, __tostring = function (tc) local q = function (s) return format("%q", s) end local f = function (s) return (q(s):gsub("\\\n", "\\n")) end return " abcz_: "..mapconcat(f, {tc:abcz_()}, " ") end, __index = { -- -- Basic: abcz = function (tc) return tc.a, tc.b, tc.c, tc.z end, abcz_ = function (tc) return tc.a, tc.b, tc.c, tc.z, tc._ end, concat = function (tc, strs) local a,b,c,z = tc:abcz() if #strs == 0 then return z end return a..table.concat(strs, b)..c end, indent = function (tc) return tc end, -- -- With expansion: e = function (tc, str) return (str:gsub("_", tc._)) end, abcz1 = function (tc) return tc:e(tc.a),tc:e(tc.b),tc:e(tc.c),tc:e(tc.z) end, indent1 = function (tc) tc = copy(tc); tc._ = tc._ .. " "; return tc end, }, } -- Basic TosConcat'ers: tosc0 = TosConcat {a="{", b=", ", c= "}", z="{}"} toscv = TosConcat {a="{", b=",\n ", c= "\n}", z="{}", indent = function (tc) return tosc0 end, } -- TosConcat'ers with (recursive) indentation: toscind1 = TosConcat {a="{\n_ ", b=",\n_ ", c="\n_}", z="{}", _="", abcz = TosConcat.__index.abcz1, indent = TosConcat.__index.indent1, } toscind2 = TosConcat {a="{\n_ ", b=",\n_ ", c= "}", z="{}", _="", abcz = TosConcat.__index.abcz1, indent = TosConcat.__index.indent1, } -- «TosConcat-tests» (to ".TosConcat-tests") --[==[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "Tos3.lua" = toscv = toscv:indent() = tosc0 = tosc0:indent() = toscind1 = toscind1:indent() = toscind1:indent():indent() = toscind2 = toscind2:indent() = toscind2:indent():indent() = toscind2 test = function (sometc) tc = sometc tci = tc:indent() tcii = tci:indent() print(tc {"a=b", "c=" .. tci {"d=e", "f=" .. tcii {"g=h", "i=j"}}}) end test(tosc0) test(toscv) test(toscind1) test(toscind2) --]==] -- _____ -- |_ _|__ ___ -- | |/ _ \/ __| -- | | (_) \__ \ -- |_|\___/|___/ -- -- Convert objets "Tos"trings. -- -- «Tos» (to ".Tos") Tos = Class { type = "Tos", __index = { -- -- Basic methods to convert things to strings: -- n: number to string -- s: string to string - adds quotes -- f0: function to string - low level, like <function: 0x...> -- f1: function to string - high level, uses PrintFunction -- f: function to string - uses PrintFunction by default -- raw: like this: true -- rawa: raw with angle brackets - like this: <true> -- other: defaults to rawa -- ob: object (basic, i.e., non-table) to string -- n = function (tos, n) return tostring(n) end, s = function (tos, s) return format("%q", s) end, f1 = function (tos, f) return PrintFunction.tostring(f) end, f0 = function (tos, f) return "<"..tostring(f)..">" end, f = function (tos, f) return tos:f1(f) end, -- raw = function (tos, o) return rawtostring(o) end, rawa = function (tos, o) return "<"..rawtostring(o)..">" end, other = function (tos, o) return "<"..tostring(o)..">" end, -- ob = function (tos, o) local ty = type(o) if ty=="number" then return tos:n(o) end if ty=="string" then return tos:s(o) end if ty=="function" then return tos:f(o) end if ty=="table" then return nil end -- see :o() return tos:other(o) end, -- -- Low-level recursive methods to convert things to strings: -- k: key - the default uses tos:o() for simplicity -- v: value - the default uses tos:o() for simplicity -- kvp: key-value pair - {key="a", val=42} -> "a"=42 -- lskvss: list of sorted "key-value string"s - calls :concat(), see below -- k = function (tos, k) return tos:o(k) end, v = function (tos, v) return tos:o(v) end, kvp = function (tos, kvp) return tos:k(kvp.key).."="..tos:v(kvp.val) end, lskvss = function (tos, lskvss) return tos:concat(lskvss) end, -- see below -- -- Methods that use a TosConcat object to concatenate lists of -- sorted "key-value string"s. In the default case our output -- is linear, like this, "{a=b, c={d=e, f=g}}", and :indent() -- is almost a no-op - except for some copying. -- tosconcat = tosc0, concat = function (tos, strs) return tos.tosconcat(strs) end, indent = function (tos) tos = copy(tos) tos.tosconcat = tos.tosconcat:indent() return tos end, -- -- The low-level side of converting a table to a list of sorted -- key-value pairs and to a list of sorted key-value strings. -- _comparekvs = function (kv1, kv2) -- a function, not a method! local key1,key2 = kv1.key, kv2.key return rawtostring_comp(key1, key2) end, tolskvps = function (tos, T) local lkvps = {} for k,v in pairs(T) do table.insert(lkvps, {key=k, val=v}) end local lskvps = sorted(lkvps, tos._comparekvs) return lskvps end, tolskvss = function (tos, T) local lskvps = tos:tolskvps(T) local f = function (kvp) return tos:kvp(kvp) end local lskvss = map(f, lskvps) return lskvss end, -- -- High-level recursive methods to convert things to strings: -- t0: table to string -- t: table to string, maybe with add-ons -- o: object (including tables) to string -- -- Note that in a case like this -- A = {a=b, c={d=e, f=g}} -- we use two diffent ":concat()s"... -- -- first: tos:indent():concat( {"d=e", "f=g") -- then: tos :concat({"a=b", "c={d=e, f=g}") -- t0 = function (tos, T) return tos:concat(tos:indent():tolskvss(T)) end, t = function (tos, T) return tos:t0(T) end, o = function (tos, o) return tos:ob(o) or tos:t(o) end, }, } -- «Tos-tests» (to ".Tos-tests") --[==[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "Tos3.lua" A = { 2, "foo", {3, 4}, VTable{5, 6} } A = { 2, "foo", a={3, 4}, b=VTable{5, 6} } tos30 = Tos {} = tos30:o(42) = tos30:o(true) = tos30:o(nil) = tos30:kvp({key=2, val=3}) = tos30:o({4,5}) = tos30:o(split) = tos30:o(A) = tos30:concat {"a=b", "c=d"} tos3v = Tos { tosconcat=toscv } = tos3v:o(A) AA = {a=1, b={c=2, d={3, 4}}} = tosv:o(AA) --]==] -- __ __ _ _ -- \ \ / /_ _ _ __(_) __ _ _ __ | |_ ___ -- \ \ / / _` | '__| |/ _` | '_ \| __/ __| -- \ V / (_| | | | | (_| | | | | |_\__ \ -- \_/ \__,_|_| |_|\__,_|_| |_|\__|___/ -- -- Sometimes we need to print tables in a format that be read back. -- For example, we have: -- -- PP ({10, "20", a="b"}) --> {1=10, 2="20", "a"="b"} (bad) -- PPPI({10, "20", a="b"}) --> {[1]=10, [2]="20", a="b"} (good) -- -- This is called "serialization", and it needs to print keys inside -- square brackets; see :kb() below. Also, :tp() is a variant of :t() -- that print the "class" of a table as a prefix, like this: -- -- PPPI(VTable {10, 20}) --> VTable {[1]=10, [2]=20} -- -- This only works for tables tables that are "objects" of "classes" -- created with eoo.lua. -- -- «variants» (to ".variants") -- table.addentries(Tos.__index, { -- -- :kb() is like k(), but with square brackets -- :tp() is like :t(), but with a class prefix -- :tcols() is an alternative to (the outermost) t -- isname = function (tos, o) return type(o) == "string" and o:match"^[%a_][%a%d_]*$" end, kb = function (tos, k) return tos:isname(k) and k or "["..tos:o(k).."]" end, -- classsep = ":", tp0 = function (tos, T) local mt = getmetatable(T) local typename = mt and mt.type local prefix = typename and (typename .. tos.classsep) or "" return prefix..tos:t0(T) end, tp = function (tos, T) return tos:tp0(T) end, -- tcolfmt = " %-14s %s", tcolk = function (tos, k) return tostring(k)..":" end, tcol = function (tos, kvp) local kstr,vstr = tos:tcolk(kvp.key),tos:o(kvp.val) return format(tos.tcolfmt, kstr, vstr) end, tcols = function (tos, T) local lskvps = tos:tolskvps(T) -- list of sorted key-value pairs local f = function (kvp) return tos:tcol(kvp) end return mapconcat(f, lskvps, "\n") end, }) -- «variants-tests» (to ".variants-tests") --[[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "Tos3.lua" A = {10, "20", a="b"} A = {10, "20", a="b", VTable {42, 99}} PP (A) PPPI(A) --]] -- «mytostring» (to ".mytostring") -- «PP» (to ".PP") -- (find-angg "LUA/lua50init.lua" "mytostring") -- (find-angg "LUA/lua50init.lua" "PP") -- (find-angg "LUA/lua50init.lua" "PP" "PP_") tos0 = Tos {} tosv = Tos { tosconcat=toscv } tospv = Tos { tosconcat=toscv, t=Tos.__index.tp } tosp = Tos { t=Tos.__index.tp } mytostring = function (o) return tos0:o(o) end mytostringv = function (o) return tosv:o(o) end mytostringp = function (o) return tosp:o(o) end mytostringvp = function (o) return tospv:o(o) end mytostringpv = function (o) return tospv:o(o) end PPPV = function (o) print(mytostringpv(o)); return o end PPP = function (o) print(mytostringp (o)); return o end PPV = function (o) print(mytostringv (o)); return o end PP = function (...) return PP_(mytostring, ...) end tosi = Tos { tosconcat=toscind1, k=Tos.__index.kb } tosip = Tos { tosconcat=toscind1, k=Tos.__index.kb, t=Tos.__index.tp, classsep=" " } mytostringi = function (o) return tosi:o(o) end mytostringip = function (o) return tosip:o(o) end PPI = function (o) print(mytostringi(o)); return o end PPIP = function (o) print(mytostringip(o)); return o end PPPI = function (o) print(mytostringip(o)); return o end PPC = function (T) print(tos0:tcols(T)) end -- «PP-tests» (to ".PP-tests") --[==[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "Tos3.lua" AA = {a=1, b={c=2, d={3, "4"}}, VTable {5,6}} = AA PP(AA) PPV(AA) PPI(AA) PPPI(AA) PPP(AA) -- PPC(Tos) PPC(Tos.__index) PPPI(AA) --]==] -- (find-angg "LUA/lua50init.lua" "HTable-and-VTable") HTable = Class { type = "HTable", __tostring = mytostring, __index = { }, } HTableP = Class { type = "HTableP", __tostring = mytostringp, __index = { }, } VTable = Class { type = "VTable", __tostring = mytostringv, __index = { }, } VTableP = Class { type = "VTableP", __tostring = mytostringvp, __index = { }, } -- Local Variables: -- coding: utf-8-unix -- End: