|
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: