|
Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
#!/usr/bin/env lua51
-- dednat5.lua - a TeX preprocessor to typeset trees and diagrams
-- This is a rewrite of dednat4.
-- Status: this is a very preliminary version, don't expect it
-- to work...
-- See:
-- (find-es "dednat" "dednat5")
-- Reescrevendo o dednat4:
-- prefixos pra encurtar tudo:
-- A: abbreviations
-- D: diagrams
-- T: trees
-- F: Forth
-- See: (find-dn4ex "edrxmain.tex")
-- and: (find-dn4ex "edrxmain41.tex")
-- and: (find-dn4ex "edrxmain41a.tex")
-- Written by: Eduardo Ochs <eduardoochs@gmail.com>
-- Current revision: 2010apr21
-- For the full history etc see some text that I haven't written yet.
-- See: http://angg.twu.net/dednat4.html
-- See: http://angg.twu.net/dednat4/
-- License: GPL
--
-- «.prefixes-and-ptables» (to "prefixes-and-ptables")
-- «.heads» (to "heads")
-- «.dednat4dir» (to "dednat4dir")
-- «.edrxlib» (to "edrxlib")
-- «.compat.lua» (to "compat.lua")
-- «.string-methods» (to "string-methods")
--
-- «.heads» (to "heads")
-- «.processfile» (to "processfile")
-- «.abbrevs» (to "abbrevs")
-- Terminology and main ideas
-- ==========================
-- A dictionary is a table whose keys are all strings.
-- A prefix_table is a dictionary in which "partial matches" point to 0.
-- prefix_tables are used for two things:
-- line_heads, where the expansions are functions,
-- abbrevs, where the expansions are strings.
-- expand is the function that expands all abbrevs in a string.
--
-- Each tree_line is split into tree_words.
-- Each tree_word may be a tree_node, a tree_bar, or a tree_name, or garbage.
-- Each tree_bar is split into a tree_bar_char and a tree_bar_comment.
-- tree_dict is a dictionary that maps "tree word names", like "tw:20:2"
-- (the second tree word in line 20 of the .tex) to structures.
-- If a tree_word is a tree_bar we get the list of tree_nodes above it.
-- If a tree_word is a tree_name we look for the tree_word above its "^".
-- If a tree_word is a tree_node we look for a tree_bar above it.
-- tree_nodes_between is the low-level function used to find "tree_words above".
-- tree_output is called at each tree_name. It may be:
-- tree_output_pt for Paul Taylor's version,
-- tree_output_tat for Makoto Tatsuta's package,
-- tree_output_buss for Sam Buss's package (currently unimplemented).
-- tree_output_body is like tree_output, but without the "\def" header.
--
-- Each diag_line is a sequence of words, to be executed one by one.
-- Each diag_word is executed by running the correspondent function in _D,
-- or by pushing the diag_node with that name into the stack.
-- diag_this_word is the variable that holds the word that we will execute.
-- diag_this_col is the "column pointer"; it is used and modified by words
-- that take over the parser to read and process immediate data.
--
-- Each stack has methods push, pop, pick, pock.
--
-- For running the diag language we have several stacks:
-- diag_nodes, for nodes declared in 2D, plus maybe others;
-- diag_arrows, that are used by diag_output (in enddiagram);
-- diag_contexts, for the (( ))s.
-- diag_node_names is a dictionary that lets us refer to nodes by their 2D names.
--
-- diag_output produces a
--
-- TO DO: abbrevs with arguments.
eval = function (str) return assert(loadstring(str))() end
--%%%%%
--%
--% «prefixes-and-ptables» (to ".prefixes-and-ptables")
--%
--%%%%%
-- Tests:
-- (find-dn4 "dednat5-shadow.lua" "expansion")
-- Internal functions with ugly names:
prefix_longest = function (str, j, ptable)
local bestk, expansion
for k=j,#str do
local candidate = string.sub(str, j, k)
if ptable[candidate] == nil then return bestk, expansion end
if ptable[candidate] ~= 0 then
bestk, expansion = k, ptable[candidate]
end
end
return bestj, expansion
end
prefix_fixed_then_longest = function (str, i, ptable)
for j=i,#str do
local k, expansion = prefix_longest(str, j, ptable)
if k then return j, k, expansion end
end
end
expand_all = function (str, i, ptable, expansions)
local j, k, expansion = prefix_fixed_then_longest(str, i, ptable)
if j then
tinsert(expansions, string.sub(str, i, j-1))
tinsert(expansions, expansion)
return expand_all(str, k+1, ptable, expansions)
else
tinsert(expansions, string.sub(str, i))
return expansions
end
end
ptable_add = function (ptable, key, expansion)
ptable[key] = expansion
for len=#key-1,1,-1 do
local subkey = string.sub(key, 1, len)
if ptable[subkey] == nil then
ptable[subkey] = 0
else
break
end
end
end
-- Call as: ptable_adds(ptable, key1, exp1, key2, exp2, ...)
ptable_adds = function (ptable, ...)
local args = pack(...)
for i=1,#args,2 do
ptable_add(ptable, args[i], args[i+1])
end
end
-- Call as: ptable_adds(ptable, key1, key2, ...)
ptable_dels = function (ptable, ...)
for _,key in ipairs({...}) do
ptable[key] = 0
end
end
-- Interface functions with nice names:
expansion_table = {}
expansion_adds = function (...) ptable_adds(expansion_table, ...) end
expansion_add = expansion_adds
expansion_del = function (...) ptable_dels(expansion_table, ...) end
expand = function (str)
return table.concat(expand_all(str, 1, expansion_table, {}))
end
--%%%%%
--%
--% «heads» (to ".heads")
--%
--%%%%%
-- Tests:
-- (find-dn4 "dednat5-shadow.lua" "heads")
head_table = {}
head_add = function (...) ptable_adds(head_table, ...) end
head_add("", "NONE")
head_for = function (str)
local k, expansion = prefix_longest(str, 1, head_table)
if k then
return string.sub(str, 1, k), string.sub(str, k+1), expansion
end
return "", str, head_table[""]
end
-- process_blocks is less than dednat[45]'s "processfile".
-- (find-dn4 "dednat5.lua" "processfile")
process_block = function ()
linestr = flines[linen]
prefix, rest, head = head_for(linestr)
local beforefirst = head.beforefirst or function () end
local aftereach = head.aftereach or function () end
local afterlast = head.afterlast or function () end
if head.afterlast then
beforefirst()
aftereach(rest)
while flines[linen+1] and head_for(flines[linen+1]) == prefix do
linen = linen + 1
linestr = flines[linen]
prefix, rest, head = head_for(linestr)
aftereach(rest);
end
afterlast()
else
aftereach(linestr)
end
linen = linen + 1
end
process_blocks = function ()
while flines[linen] do process_block() end
end
--%%%%
--%
--% «abbrevs» (to ".abbrevs")
--%
--%%%%
abbrevs = {}
abbrev_add = function (abbrev, expansion)
for i=1,#abbrev-1 do
local s = abbrev:sub(1,i)
abbrevs[s] = abbrevs[s] or 0 -- make s a prefix (if it is not an abbrev)
end
abbrevs[abbrev] = expansion
end
abbrev_longest = function (str, i)
local bestj, abbrev, expansion
for j=i,#str do
local teststr = str:sub(i, j)
local a = abbrevs[teststr] -- is teststr a prefix, or an abbrev?
if a == nil then break end -- it is neither: break the loop
if a ~= 0 then -- it's an abbrev: save j and a and continue
bestj, abbrev, expansion = j, teststr, a
end
-- if a == 0 then PP(i, j, teststr, "is a prefix and not an abbrev") end
end
return bestj, abbrev, expansion
end
abbrev_expand1 = function (str, i)
for j=i,#str do
local k, abbrev, expansion = abbrev_longest(str, j)
if k then
local literal = str:sub(i, j-1) -- the literal part before the abbrev
return j, k, literal, abbrev, expansion
end
-- PP(i, j, str:sub(j), "does not start with an abbrev")
end
end
unabbrev = function (str)
local result = {}
local i = 1
while i <= #str do
local j, k, literal, abbrev, expansion = abbrev_expand1(str, i)
if j then
table.insert(result, literal)
table.insert(result, expansion)
i = k + 1
else
table.insert(result, str:sub(i))
break
end
end
-- PP(result)
return table.concat(result)
end
addabbrevs = function (...)
local arg = {...}
for i=1,#arg,2 do
abbrev_add(arg[i], arg[i+1])
end
end
--[[
-- Tests:
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
abbrevs = {}
abbrev_add("cde", "CCDDEE")
PP(abbrevs)
--> {"c"=0, "cd"=0, "cde"="CCDDEE"}
= abbrev_expand1("abcdefg", 1)
--> 3 5 ab cde CCDDEE
= abbrev_expand1("abcdefg", 2)
--> 3 5 b cde CCDDEE
= unabbrev("abcdefg")
--> abCCDDEEfg
= unabbrev("abcdefg_cd_cde__")
--> abCCDDEEfg_cd_CCDDEE__
abbrev_add("c", "**")
= unabbrev("abcdefg_cd_cde__")
--> abCCDDEEfg_**d_CCDDEE__
abbrevs = {}
addabbrevs("cde", "CCDDEE", "c", "**")
PP(abbrevs)
--> {"c"="**", "cd"=0, "cde"="CCDDEE"}
--]]
-- «standardabbrevs» (to ".standardabbrevs")
-- (find-dn4ex "edrx08.sty")
standardabbrevs = function ()
addabbrevs(
"->^", "\\ton ", "`->", "\\ito ", "-.>", "\\tnto ",
"=>", "\\funto ", "<->", "\\bij ", "->", "\\to ",
"|-", "\\vdash ", "|->", "\\mto ", "\"", " ")
end
-- Local Variables:
-- coding: raw-text-unix
-- End: