Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
-- This file: -- http://angg.twu.net/dednat4/newtrees.lua -- http://angg.twu.net/dednat4/newtrees.lua.html -- (find-dn4file "newtrees.lua") -- This is going to be one module of dednat5 - a rewrite of dednat4. -- Rationale: in dednat4 there is no way to generate the TeX code for -- trees without creating first a 2D ascii representation for them. I -- am feeling more and more the need to generate trees -- "automatically" - see the slides below for details - -- -- (find-angg "LATEX/2010unilog-current.tex") -- http://angg.twu.net/LATEX/2010unilog-current.dvi -- http://angg.twu.net/LATEX/2010unilog-current.pdf -- -- (More later. 2010jul04) -- Very preliminary announcement: -- (find-TH "dednat4" "quick-start") -- «.metatables» (to "metatables") -- «.trees» (to "trees") -- «.wordsin» (to "wordsin") -- «.segs» (to "segs") -- «.trees-to-tex» (to "trees-to-tex") -- «.tests» (to "tests") -- «metatables» (to ".metatables") -- --- __ __ _ _ _ _ --- | \/ | ___| |_ __ _| |_ __ _| |__ | | ___ ___ --- | |\/| |/ _ \ __/ _` | __/ _` | '_ \| |/ _ \/ __| --- | | | | __/ || (_| | || (_| | |_) | | __/\__ \ --- |_| |_|\___|\__\__,_|\__\__,_|_.__/|_|\___||___/ --- -- It is hard to dump and inspect structures like doubly-linked lists, -- so use a further level of indirection: instead of representing -- -- +-----+ +-----+ (this is the "abstract" representation. -- | foo |r l| bar | <-- We use labels between the boxes to -- +-----+ +-----+ indicate which field of each box points -- to the other one) -- as: -- nodefoo = { tex="foo" } -- this is the "direct" -- nodebar = { tex="bar", l=nodefoo } -- representation, which -- nodefoo.r = nodebar -- we will _not_ use. -- -- we will use a table called "objs" and a string id in each table -- that stands for a box in the abstract representation, as this: -- -- objs._node1 = { id="_node1", tex="foo", r="_node2" } -- objs._node2 = { id="_node2", tex="bar", r="_node1" } -- -- Let me introduce a term. A "tort" will be a "tag or table" - a -- string id or a table. In the example above, "_node1" (a string) and -- objs._node1 (a table) are two different torts that represent the -- same "box". -- The metatable tricks will let us convert between the two torts for -- each box very easily, by just appending ".id"s and ".T"s. A ".id" -- converts a table to its string id, but leaves strings unchanged; a -- ".T" converts a string id to its corresponding table, but leaves -- table objects unchanged. Note that superfluous ".id"s and ".T"s do -- nothing: -- -- objs._node1 .T.T.id.T.id.id == "_node1" -- objs._node1 .T.T.id.T.id.id.T == objs._node1 -- "_node1".T.T.id.T.id.id.T == objs._node1 -- -- The code: objs = {} string_mt = getmetatable("foo") string_mt.__index = function (str, key) if key == "id" then return str end -- str.id = str if key == "T" then return objs[str] end -- str.T = objs[str] return string[key] -- str.f = string.f end obj_mt = obj_mt or {} obj_mt.__index = function (T, key) if key == "T" then return T end end setobjid = function (T, id) setmetatable(T, obj_mt) -- add a metatable to T T.id = id -- set T.id = id objs[id] = T -- set objs[id] = T return T end obj_newid = function (kind) local nkinds = "n"..kind.."s" objs[nkinds] = objs[nkinds] + 1 -- objs.nfoos = objs.nfoos + 1 local n = objs[nkinds] -- n = objs.nfoos local id = "_"..kind..n -- return "_foo"..n return id end obj_setid = function (kind, T) setmetatable(T, obj_mt) -- add a metatable to T local id = obj_newid(kind) -- id = "_foo42" T.id = id -- T.id = "_foo42" objs[id] = T -- objs._foo42 = T return T end obj_link = function (obj1, dir1, dir2, obj2) obj1.T[dir1] = obj2.id -- obj1.T.r = "_obj2" obj2.T[dir2] = obj1.id -- obj2.T.l = "_obj1" end obj_adjustchildren = function (dir, this) this = this.T for i=1,#this do local child = this[i].T this[i] = child.id -- this[1] = "_child1" child[dir] = this.id -- child1.L = "_this" child[dir.."n"] = i -- child1.Ln = 1 end end obj_appendto = function (parent, dir, this) tinsert(parent.T, this.id) this.T[dir] = parent.id this.T[dir.."n"] = #(parent.T) end obj_next = function (this, dir) local parent = this.T[dir] local n = this.T[dir.."n"] return parent.T[n+1] end -- «trees» (to ".trees") -- --- _____ --- |_ _| __ ___ ___ ___ --- | || '__/ _ \/ _ \/ __| --- | || | | __/ __/\__ \ --- |_||_| \___|\___||___/ --- -- -- Our internal representation for trees will use two kinds of -- "objects" (a.k.a. "boxes"): "nodes" and "bars". Here is an example: -- -- +------+ +-------+ -- | b | | b|->c | -- +------+ +-------+ -- b bn=1 b bn=2 -- 1 2 -- +-------------------+ -- | - | -- +-------------------+ -- b -- a -- +-----+ -- | c | -- +-----+ -- -- objs._node1 = newnode { tex="b", b="_bar1", bn=1 } -- objs._node2 = newnode { tex="b|->c", b="_bar1", bn=2 } -- objs._bar1 = newbar { rulechar="-", "_node1", "_node2", b="_node3" } -- objs._node3 = newnode { tex="c", a="_bar1" } -- objs.nnodes = 0 -- newnode_ = function (T) -- objs.nnodes = objs.nnodes + 1 -- return setobjid(T, "_node"..objs.nnodes) -- end -- newnode = function (tex, a) -- return newnode_{tex=tex, a=(a and a.id)} -- end -- -- objs.nbars = 0 -- newbar_ = function (T) -- objs.nbars = objs.nbars + 1 -- return setobjid(T, "_bar"..objs.nbars) -- end -- newbar = function (rulechar, rtex, ...) -- local bar = newbar_{rulechar=rulechar, rtex=rtex, ...} -- for i=1,#bar.T do -- bar.T[i] = bar.T[i].id -- bar.T[i].T.b = bar.id -- bar.T[i].T.bn = i -- end -- return bar -- end newnode = function (tex, a) return obj_setid("node", {tex=tex, a=(a and a.id)}) end newbar = function (rulechar, rtex, ...) return obj_adjustchildren("b", obj_setid("bar", {rulechar=rulechar, rtex=rtex, ...})) end -- «wordsin» (to ".wordsin") -- wordsin(str, ncharstoskip): -- An iterator to generate words from a string. -- Example: -- s = "%:foo bar"; for l,w,r in wordsin(s, 2) do PP(l,w,r, s:sub(l,r)) end -- prints: -- 3 "foo" 5 "foo" -- 7 "bar" 9 "bar" -- Note that l and r follow the string:sub convention. -- See: (find-angg "LUA/lua50init.lua" "mysortedpairs") -- (find-luamanualw3m "#pdf-string.gmatch") -- (find-luamanualw3m "#pdf-string.match") -- wordsin = function (str, skip) str = untabify(str) skip = skip or 0 return function () local spaces, l, word, r = str:match("(%s*)()(%S+)()", skip + 1) if l then skip = r - 1 return l, word, r - 1 end end end -- «segs» (to ".segs") -- --- ____ --- / ___| ___ __ _ ___ --- \___ \ / _ \/ _` / __| --- ___) | __/ (_| \__ \ --- |____/ \___|\__, |___/ --- |___/ -- -- Each line that starts with "%:" in a .tex file is split into -- "segs", and these segs can be traversed bidimensionally to form -- trees... The main data structure is this: in a line like -- -- %: foo baaar -- 11111 -- 12345678901234 -- -- the span of the segment "foo" is from columns 5 to 7, and the span -- of "baaar" is from 10 to 14; in box notation, we will store these -- segs as: -- +--+ +---------+ -- | |1 L| foo 5 7 | -- | | Ln=1+---------+ +----- -- | |2 -- | | -- | | -- | | -- | | -- | | -- | | -- -- -- -- -- «trees-to-tex» (to ".trees-to-tex") -- --- _____ __ _____ __ __ --- |_ _| __ ___ ___ ___ \ \ |_ _|__\ \/ / --- | || '__/ _ \/ _ \/ __| _____\ \ | |/ _ \\ / --- | || | | __/ __/\__ \ |_____/ / | | __// \ --- |_||_| \___|\___||___/ /_/ |_|\___/_/\_\ --- -- Functions to convert the box structure for trees into TeX. -- Note that these are NOT the functions that process the "%:" -- lines and generate the box structures... -- -- At some point these functions will replace the ones in dednat4: -- (find-dn4 "dednat4.lua" "tree-out") -- (find-dn4 "dednat4.lua" "tree-out" "tex_node_tatsuta") -- (find-dn4 "dednat4.lua" "tree-out" "tex_node_paultaylor") -- but right now they are still in an experimental stage. -- (find-TH "dednat4" "trees_and_abbrevs") mathstrut = mathstrut or "{|} " unabbrev = unabbrev or function (str) return str end new_tex_node_tatsuta = function (indent, node) node = node.T if not node.a then return indent..mathstrut..unabbrev(node.tex) end local bar = node.a.T local barchar, bartext = bar.rulechar, bar.rtex local rulemodifier = (barchar=="=" and "=" or (barchar==":" and "*" or "")) .. (bartext=="" and "" or "[{"..unabbrev(bartext).."}]") local newindent = indent.." " local uppertex local n = #bar if n==0 then return format("%s\\infer%s{ %s%s }{ }", indent, rulemodifier, mathstrut, unabbrev(node.tex)) else uppertex = new_tex_node_tatsuta(newindent, bar[1]) for i=2,n do uppertex = uppertex .. " &\n" .. new_tex_node_tatsuta(newindent, bar[i]) end end return format("%s\\infer%s{ %s%s }{\n%s }", indent, rulemodifier, mathstrut, unabbrev(node.tex), uppertex) end test_dot_tex = [[ %L standardabbrevs() %: %: - %: c d %: ====? %: a a->b %: ------- %: b %: %: ^tree1 %: ]] -- «tests» (to ".tests") --[[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) ee_dofile "~/dednat4/newtrees.lua" wordsin = function (str, skip) str = untabify(str) return string.gmatch(str, "%s*()(%S+)()", skip) end s = "%:foo bar" -- 1234567890 for a,b,c in wordsin(s, 2) do PP(a,b,c, s:sub(a,c)) end PP(("%:foo"):match("%s*()(%S+)()", 3)) -- skip is the # of chars to skip at the beginning -- l and r follow the string:sub convention s = "%:foo bar" -- 1234567890 for a,b,c in wordsin(s, 2) do PP(a,b,c, s:sub(a,c)) end mathstrut = "{|} " unabbrev = function (str) return str end -- PP(newnode"foo") -- PP(objs) root = newnode("c", newbar("-", "app", newnode"b", newnode"b|->c")) PP(root) PP(objs) dednat4dir = ee_expand "~/dednat4/" ee_dofile "~/dednat4/dednat4.lua" writefile("/tmp/test.tex", test_dot_tex) processfile("/tmp/test.tex") PP(tlines) PP(headfor "Foo bar") PP(headfor "%: bbb") nop = function () end closeblock = function () ((prevhead and prevhead.afterlast) or nop)() prevhead = nil end openblock = function () ((thishead and thishead.beforefirst) or nop)(linestr) end processline = function (linestr_) prevhead = thishead -- may be nil linen = linen + 1 linestr = linestr_ thishead = headfor(linestr) -- never nil PP(thishead) if prevhead ~= thishead then closeblock() openblock() end ((thishead and thishead.aftereach) or nop)(linestr) end linen = 0 processline "%L print 'foo'" processline "" PP(heads) closeblock = function () if prevhead and prevhead ~= thishead then prevhead.afterlast() prevhead = nil end -- (find-dn4 "dednat4.lua" "main") -- (find-dn4 "dednat4.lua" "main" "processtex") -- (find-dn4 "dednat4.lua" "process") -- (find-dn4 "dednat4.lua" "process" "processtex") -- (find-dn4 "dednat4.lua" "process" "processtex" "processfile") -- (find-dn4 "dednat4.lua" "processfile") -- (find-dn4 "dednat4.lua" "tree-head") -- (find-dn4 "dednat4.lua" "tree-out") = tex_node_tatsuta("", root) tex_node_tatsuta = function (indent, lowerseg) local n, barseg, upperseg = stuffabovenode(lowerseg) if not barseg then return indent..mathstrut..unabbrev(lowerseg.text) end local barchar, bartext = barcharandtext(barseg) -- PP(barseg, barchar, bartext) local rulemodifier = (barchar=="=" and "=" or (barchar==":" and "*" or "")) .. (bartext=="" and "" or "[{"..unabbrev(bartext).."}]") local newindent = indent.." " local uppertex if n==0 then return format("%s\\infer%s{ %s%s }{ }", indent, rulemodifier, mathstrut, unabbrev(lowerseg.text)) else uppertex = tex_node_tatsuta(newindent, upperseg) for i=2,n do upperseg = nextseg(upperseg) uppertex = uppertex .. " &\n" .. tex_node_tatsuta(newindent, upperseg) end end return format("%s\\infer%s{ %s%s }{\n%s }", indent, rulemodifier, mathstrut, unabbrev(lowerseg.text), uppertex) end -- (find-dn4 "dednat4.lua" "diag-forth") --]] -- Local Variables: -- coding: raw-text-unix -- End: