Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
#!/usr/bin/env lua50 # -*- coding: raw-text-unix -*- -- dednat, rewritten for lua5.0 -- intended to be much faster than the previous versions. -- unfinished. -- 2003jul13 -- «.segsabove» (to "segsabove") -- «.abbrevs» (to "abbrevs") -- «.untabify» (to "untabify") -- «.auxtreefunctions» (to "auxtreefunctions") -- «.tatsuta» (to "tatsuta") -- «.paul.taylor» (to "paul.taylor") -- «.heads» (to "heads") -- «.processfile» (to "processfile") -- «.luahead» (to "luahead") -- «.abbrevhead» (to "abbrevhead") -- «.treehead» (to "treehead") -- «.processfile-tree» (to "processfile-tree") -- «.main» (to "main") -- The data structures: -- -- lines an array of "line"s; only have entries for the "%:" lines -- -- line.linen an integer -- line.text a string -- line.segs an array of "seg"s -- -- seg.linen an integer -- seg.segn an integer - for any seg s we have lines[s.linen][s.segn]==s -- seg.lcol an integer -- seg.rcol an integer -- seg.text a string -- -- isprefix a table - isprefix[str] is either true or nil -- abbrevs a table - abbrevs[str] is the expansion of str, or nil if none --%%%% --% --% «segsabove» (to ".segsabove") --% --%%%% relativeplacement = function (upperseg, lowerseg) if upperseg.rcol <= lowerseg.lcol then return "L" end if upperseg.lcol >= lowerseg.rcol then return "R" end return "I" end nextseg = function (seg) return lines[seg.linen].segs[seg.segn+1] end firstsegabove = function (lowerseg, nlines) local upperseg upperseg = lines[lowerseg.linen - (nlines or 1)] and lines[lowerseg.linen - (nlines or 1)].segs[1] while upperseg and relativeplacement(upperseg, lowerseg)=="L" do upperseg = nextseg(upperseg) end if upperseg and relativeplacement(upperseg, lowerseg)=="I" then return upperseg end end nextsegabove = function (upperseg, lowerseg) local nextupperseg = nextseg(upperseg) return nextupperseg and relativeplacement(nextupperseg, lowerseg)=="I" and nextupperseg end stuffabovenode = function (lowerseg) local barseg, firstupperseg, upperseg, n barseg = firstsegabove(lowerseg) if not barseg then return -1 end firstupperseg = firstsegabove(barseg) if not firstupperseg then return 0, barseg end n = 1 upperseg = firstupperseg while 1 do upperseg = nextsegabove(upperseg, barseg) if upperseg then n = n+1 else return n, barseg, firstupperseg end end end --%%%% --% --% «abbrevs» (to ".abbrevs") --% --%%%% isprefix = {} abbrevs = {} addabbrev = function (abbrev, expansion) for i = 1,string.len(abbrev)-1 do isprefix[string.sub(abbrev, 1, i)] = true end abbrevs[abbrev] = expansion end unabbrev = function (str) local len, newstr, i = string.len(str), "", 1 local j, teststr, longest while i<=len do longest = nil for j=i,len do teststr = string.sub(str, i, j) if abbrevs[teststr] then longest = teststr else if not isprefix[teststr] then break end end end if longest then newstr = newstr .. abbrevs[longest] i = i + string.len(longest) else newstr = newstr .. string.sub(str, i, i) i = i + 1 end end return newstr end -- these are mainly for tests and demos: addabbrevs = function (...) for i=1,table.getn(arg),2 do addabbrev(arg[i], arg[i+1]) end end standardabbrevs = function () addabbrevs( "->^", "\\ton ", "`->", "\\ito ", "-.>", "\\tnto ", "=>", "\\funto ", "<->", "\\bij ", "->", "\\to ", "|-", "\\vdash ", "�>", "\\mto ", "|->", "\\mto ", "\"", " ") end --%%%% --% --% «untabify» (to ".untabify") --% --%%%% do local reps = {" ", " ", " ", " ", " ", " ", " ", " "} --reps={"--------", "-------", "------", "-----", "----", "---", "--", "-"} untabify = function (str, col) local pos, newstr, _, nontab, tab = 0, "" col = col or 0 -- 0-based: ((col mod 8)==2) -> (tab -> 6 spaces) while 1 do _, pos, nontab, tab = string.find(str, "^([^\t]*)(\t?)", pos+1) if tab=="" then return newstr..nontab end col = math.mod(col + string.len(nontab), 8) newstr = newstr..nontab..reps[col+1] col = 0 end end end --[[ P(untabify("34\t\t01234567\t0123\t", 3)) P("3456701234567012345670123456701234567") --]] --%%%% --% --% «auxtreefunctions» (to ".auxtreefunctions") --% --%%%% dolinenumbers = "eev" optionalhyperlink = function (pre, linen, post) if dolinenumbers == "eev" then return format("%s(find-fline \"%s\" %d)%s", pre, sourcefname, linen, post) elseif dolinenumbers then return format("%sfile %s line %d%s", pre, sourcefname, linen, post) else return "" end end barcharandtext = function (barseg) local _, __, text _, __, text = string.find(barseg.text, "^-+(.*)") if text then return "-",text end _, __, text = string.find(barseg.text, "^=+(.*)") if text then return "=",text end errprintf("Bad bar at line %d, col %d: %s\n", barseg.linen, barseg.lcol, barseg.text) end --%%%% --% --% «tatsuta» (to ".tatsuta") --% --%%%% -- (find-angg "dednat/dednat2.lua" "tatsuta_do_node") -- (find-lua50ref "Precedence") mathstrut = mathstrut or "\\mathstrut " 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) local rulemodifier = (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 function tex_tree_tatsuta(treetagseg, treelabel, treerootseg) return format("\\defded{%s}{%s\n%s }\n", treelabel, optionalhyperlink(" % ", treetagseg.linen, ""), tex_node_tatsuta(" ", treerootseg)) end tex_tree_function = tex_tree_tatsuta --%%%% --% --% «paul.taylor» (to ".paul.taylor") --% --%%%% -- (find-es "tex" "ptproof") tex_node_paultaylor = function (indent, lowerseg) local n, barseg, upperseg = stuffabovenode(lowerseg) if not barseg then return unabbrev(lowerseg.text) end local barchar, bartext = barcharandtext(barseg) local justifies = (barchar=="=" and "\\Justifies" or "\\justifies") local using = (bartext=="" and "" or "\\using "..unabbrev(bartext).." ") local newindent = indent.." " local uppertex, segtex, istree, previstree if n==0 then return "\\[ "..using..justifies.."\n".. indent..unabbrev(lowerseg.text).." \\]", "tree" else uppertex, istree = tex_node_paultaylor(newindent, upperseg) for i=2,n do upperseg = nextseg(upperseg) previstree = istree segtex, istree = tex_node_paultaylor(newindent, upperseg) if previstree or istree then quad = "" else quad = " \\quad" end uppertex = uppertex..quad.."\n".. newindent..segtex end end return "\\[ "..uppertex.." "..using..justifies.."\n".. indent..unabbrev(lowerseg.text).." \\]", "tree" end function tex_tree_paultaylor(treetagseg, treelabel, treerootseg) return "\\defded{"..treelabel.."}{".. optionalhyperlink(" % ", treetagseg.linen, "").. "\n \\begin{prooftree}\n ".. tex_node_paultaylor(" ", treerootseg).. "\n \\end{prooftree}}" end -- tex_tree_function = tex_tree_paultaylor --%%%% --% --% «heads» (to ".heads") --% --%%%% -- heads a table of "head"s, indexed by strings - ex: heads["%:"]=... -- head.code a function taking no parameters, executed after each line -- head.after a function taking no parameters, executed after the block -- head.headstr the prefix string - ex: "%:" -- stickyhead nil or a "sticky head", a head with a non-nil head.after -- -- other globals used: linestr, linen, sourcefname heads = {} registerhead = function (headstr, head) head.headstr = headstr heads[headstr] = head end registerhead("", {}) doflushstickyblocks = function () local after = stickyhead and stickyhead.after stickyhead = nil if after then after() end end dolineinnewblock = function () if head.code then head.code() end stickyhead = head.after and head end doline = function () head = heads[string.sub(linestr, 1, 3)] or heads[string.sub(linestr, 1, 2)] or heads[string.sub(linestr, 1, 1)] or heads[""] if stickyhead then if head.headstr == stickyhead.headstr then if head.code then head.code() end else doflushstickyblocks() dolineinnewblock() end else dolineinnewblock() end end -- «processfile» (to ".processfile") -- minimal version without any head-specific code. -- See: (to "processfile-tree") processfile = function (fname) local _sourcefname, _linen, _linestr, _head, _lines = sourcefname, linen, linestr, head, lines sourcefname = fname linen = 0 stickyhead = nil for _linestr in io.lines(fname) do linen = linen + 1 linestr = _linestr doline() end doflushstickyblocks() sourcefname, linen, linestr, head = _sourcefname, _linen, _linestr, _head end --%%%% --% --% «luahead» (to ".luahead") --% --%%%% luaheadcode = function () if luacode then luacode = luacode..string.sub(linestr, 3).."\n" else luacode = string.sub(linestr, 3).."\n" luacodestartlinen = linen end end luaheadafter = function () local chunkname = "\"%L\" chunk starting at line "..luacodestartlinen local code = luacode -- Pvars("luastartlinen", "luacode") luacode = nil luacodestartlinen = nil -- print(" assert(loadstring(code, chunkname))()") assert(loadstring(code, chunkname))() end registerhead("%L", {code=luaheadcode, after=luaheadafter}) -- One of the uses of "%L"s is for loading extensions into dednat3. -- For example, a line like this: -- %L require "diagxy.lua" -- will load the "diagxy" extension, that interprets lines starting -- with "%D" in a certain special way. -- To make these "require" commands simpler we add to LUA_PATH the -- directory where dednat3.lua was found (unless it was the current -- directory). do local _, __, arg0path = string.find(arg[0], "^(.*)/[^/]*$") if arg0path then LUA_PATH = (LUA_PATH or getenv("LUA_PATH") or "?;?.lua").. ";"..arg0path.."/?" end end --%%%% --% --% «abbrevhead» (to ".abbrevhead") --% --%%%% abbrevheadcode = function () local _, __, abbrev, expansion = string.find(linestr, "^%%:*(.-)*(.-)*") addabbrev(abbrev, expansion) end registerhead("%:*", {code=abbrevheadcode}) --%%%% --% --% «treehead» (to ".treehead") --% --%%%% lines = {} treetagsegs = {} splitintosegs = function (line) local col, nsegs, _, __, spaces, text = 1, 0 line.segs = {} while 1 do _, __, spaces, text = string.find(line.text, "^( *)([^ ]*)", col) if text and text ~= "" then nsegs = nsegs + 1 local nspaces, nchars = string.len(spaces), string.len(text) line.segs[nsegs] = {linen=line.linen, segn=nsegs, lcol=col+nspaces, rcol=col+nspaces+nchars, text=text} col = col + nspaces + nchars else break end end end processtreetags = function (line) seg = line.segs[1] while seg do if string.sub(seg.text, 1, 1)=="^" then local treelabel = string.sub(seg.text, 2) if treetagsegs[treelabel] then errprintf("Tree redefined: tree %s, lines %d and %d\n", treelabel, line.linen, treetagsegs[treelabel].linen) end local treerootseg = firstsegabove(seg, 2) if not treerootseg then errprintf("No root seg: line %d, tree %s\n", line.linen, treelabel) end print(tex_tree_function(seg, treelabel, treerootseg)) end seg = nextseg(seg) end end treeheadcode = function () lines[linen] = {linen=linen, text=untabify(string.sub(linestr, 3), 2)} splitintosegs(lines[linen]) processtreetags(lines[linen]) end registerhead("%:", {code=treeheadcode}) -- «processfile-tree» (to ".processfile-tree") -- This is a wrapper, see (to "processfile") do local _processfile = processfile processfile = function (fname) local _lines = lines lines = {} -- "%:"-specific _processfile(fname) lines = _lines end end --%%%% --% --% «main» (to ".main") --% --%%%% -- (find-lua50ref "Input and Output Facilities") -- (find-lua50ref "file:write") -- (find-lua50ref "string.find") treetagsegs = {} if arg and arg[1] and arg[1]~="" then processfile(arg[1]) end --[[ #* cat > /tmp/dn3test.tex <<'%%%' bla %:*->*\to * . %:*<->*\bij * . %:*=>*\funto * . %:*-.>*\tnto * . %: %: - %: c d %: ====? %: a a->b %: ------- %: b %: %: ^tree1 %: %L require "diagxy.lua" %D diagram miniadj %D 2Dx 100 140 %D 2D 140 a^L <= a %D 2D - - %D 2D | <-> | %D 2D v v %D 2D 100 b => b^R %D a (( a^L => drop b |-> drop b^R => )) b^R |-> %D enddiagram %%% cd /tmp/ ~/dednat/dednat3.lua dn3test.tex | tee ~/o # ~/dednat/dednat3.lua ~/LATEX/2002h.tex > 2002h.dnt #* ]]