|
Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
-- block.lua: implement the class Block, that is used for texfile
-- blocks, publocks and head blocks. This file supersedes and replaces
-- texfile.lua...
--
-- This file:
-- http://angg.twu.net/dednat6/dednat6/block.lua
-- http://angg.twu.net/dednat6/dednat6/block.lua.html
-- (find-angg "dednat6/dednat6/block.lua")
--
-- It replaces this:
-- http://angg.twu.net/dednat6/dednat6/texfile.lua
-- http://angg.twu.net/dednat6/dednat6/texfile.lua.html
-- (find-angg "dednat6/dednat6/texfile.lua")
--
-- In may 2018 I realized that three data structures in dednat6 could
-- be unified into a single one, the "Block"; they are the "texfile
-- blocks", the "pu blocks", and the "head blocks".
--
-- The algorithm is described in sections 3 and 3.1 of:
-- https://tug.org/TUGboat/tb39-3/tb123ochs-dednat.pdf
-- http://angg.twu.net/dednat6/tugboat-rev2.pdf
-- (turp 4 "heads-and-blocks" "Heads and blocks")
-- (tur "heads-and-blocks" "Heads and blocks")
--
-- Suppose that we have a .tex file with 1000 lines, with a "\pu" at
-- line 200, another "\pu" at line 300, and with lines with the "%D"
-- head from lines 210 to 215. Then we would have these objects at
-- different points of the execution of the program:
--
-- texbl = Block {i=1, j=1000, nline=1}
-- publ = Block {i=1, j=199}
-- publ = Block {i=201, j=299}
-- headbl = Block {i=210, j=215, head="%D"}
--
-- «.TexLines» (to "TexLines")
-- «.Block» (to "Block")
-- «.texfile0» (to "texfile0")
-- «.tf_push_and_tf_pop» (to "tf_push_and_tf_pop")
-- (find-LATEXfile "dednat6load.lua" "texfile0(status.filename)")
-- (find-dn6 "texfile.lua" "texfile0")
-- (find-dn6 "heads6.lua" "lua-head")
-- (find-dn6 "heads6.lua" "lua-head" "= tf:getblock()")
-- (find-dn6 "texfile.lua" "TexFile")
-- (find-dn6 "texfile.lua" "TexFile" "getblock =")
-- (find-dn6 "texfile.lua" "TexFile" "head =")
-- (find-dn6 "texfile.lua" "TexFile" "process1 =")
-- _____ _ _ _
-- |_ _|____ _| | (_)_ __ ___ ___ ___| | __ _ ___ ___
-- | |/ _ \ \/ / | | | '_ \ / _ \/ __| / __| |/ _` / __/ __|
-- | | __/> <| |___| | | | | __/\__ \ | (__| | (_| \__ \__ \
-- |_|\___/_/\_\_____|_|_| |_|\___||___/ \___|_|\__,_|___/___/
--
-- «TexLines» (to ".TexLines")
-- The TexLines class.
-- The contents of the current .tex file are stored in a TexLines object,
-- in the global variable "texlines".
--
-- Internally, a TexLines object is a just a table returned by
-- splitlines(ee_readfile("foo.tex")) with a "name=" field and a
-- nice metatable.
TexLines = Class {
type = "TexLines",
new = function (name, lines)
return TexLines({name=name}):setlines(lines)
end,
read = function (fname)
return TexLines.new(fnamenondirectory(fname), unixnewlines(ee_readfile(fname)))
end,
test = function (str)
local tr = {["L"]="%L", ["D"]="%D", [":"]="%:", ["p"]="\\pu"}
local tl = TexLines {name="(test)"}
for c in str:gmatch"." do table.insert(tl, tr[c] or c) end
return tl
end,
__tostring = function (tl) return tl:tostring() end,
__index = {
setlines = function (tl, lines)
if type(lines) == "string" then lines = splitlines(lines) end
for i=1,#lines do tl[i] = lines[i] end
return tl
end,
nlines = function (tl) return #tl end,
line = function (tl, i) return tl[i] end,
--
head = function (tl, i)
local li = tl:line(i)
local p = function (len)
local s = li:sub(1, len)
return heads[s] and s
end
return li and (p(3) or p(2) or p(1) or p(0))
end,
nohead = function (tl, i)
return tl:line(i):sub(#tl:head(i) + 2)
end,
--
tostring1 = function (tl, i) return format("%3d: %s", i, tl:line(i)) end,
tostring = function (tl, i, j)
local T = {}
for k=(i or 1),(j or tl:nlines()) do
table.insert(T, tl:tostring1(k))
end
return table.concat(T, "\n")
end,
--
toblock = function (tl)
return Block {i=1, j=#tl, nline=1, name=tl.name}
end,
},
}
--[[
• (eepitch-lua51)
• (eepitch-kill)
• (eepitch-lua51)
dofile "block.lua"
heads = {["%L"]={}, ["%D"]={}, ["%:"]={}}
tl = TexLines.test "p DD::: DD DD LLL:::p::p "
= tl
= tl:head(4)
= tl:head(8)
--]]
-- ____ _ _ _
-- | __ )| | ___ ___| | __ ___| | __ _ ___ ___
-- | _ \| |/ _ \ / __| |/ / / __| |/ _` / __/ __|
-- | |_) | | (_) | (__| < | (__| | (_| \__ \__ \
-- |____/|_|\___/ \___|_|\_\ \___|_|\__,_|___/___/
--
-- «Block» (to ".Block")
-- The Block class, that is used to represent texfile blocks, head
-- blocks, pu blocks and arbitrary (non-bad) blocks, as described in
-- the section "Heads and blocks" of the article about Dednat6 on
-- TUGBoat:
-- https://tug.org/TUGboat/tb39-3/tb123ochs-dednat.pdf
-- http://angg.twu.net/dednat6/tugboat-rev2.pdf
-- (tubp 4 "heads-and-blocks")
-- (tub "heads-and-blocks")
--
-- Internally, a Block object always has fields "i" and "j"; a texfile
-- block also has a field "nline" and a field "name", and a head block
-- has a field "head". So:
-- Block {i=1, j=100, nline=42, name="foo.tex"} --> a texfile block
-- Block {i=5, j=7, head="%:"} --> a head block
-- Block {i=2, j=20} --> an arbitrary/pu block
--
-- The texfile block for the current file is stored in the global
-- variable "tf", and there is legacy code in other modules of dednat6
-- that does this:
-- i,j,lualines = tf:getblock()
-- chunkname = tf.name..":%L:"..i
--
-- Implementation: "tf:getblock()" uses the global block variable
-- "lastheadblock" (that is set by bl:processheadblock()), reads the
-- lines from i to j from texlines using texlines:line(k), and
-- concatenates them in a string with newlines. There is a method
-- ":getblock()" in TexLines and another in Block - ugly but works!
headblocks = {} -- a "log" of all the head blocks processed so far
Block = Class {
type = "Block",
__tostring = mytostring,
__index = {
firstheadblockin = function (bl, i0, j0)
local i,j
for i1=i0,j0 do
if texlines:head(i1) then i=i1; break end
end
if not i then return end
local head = texlines:head(i)
for j1=i+1,j0 do
if texlines:head(j1) ~= head then
return Block {i=i, j=j1-1, head=head}
end
end
return Block {i=i, j=bl.j, head=head}
end,
--
processheadblock = function (bl)
lastheadblock = bl -- for ":getblock()"s
table.insert(headblocks, bl)
local action = heads[bl.head].action -- uses "tf:getblock()"
if action then action() else print("No action for "..bl.head) end
end,
processarbitraryblock = function (bl)
-- print("process arbitrary:", mytostring(bl))
local i0 = bl.i
while true do
local headbl = bl:firstheadblockin(i0, bl.j)
if not headbl then return end
headbl:processheadblock()
i0 = headbl.j + 1
end
end,
process = function (bl)
-- print("process:", mytostring(bl))
if bl.head
then bl:processheadblock(bl)
else bl:processarbitraryblock(bl)
end
end,
--
processuntil = function (bl, puline)
local publock = Block {i=bl.nline, j=puline-1}
publock:process()
bl.nline = puline+1
return bl
end,
--
getblock = function (bl, headwidth)
local i,j,head = lastheadblock.i, lastheadblock.j, lastheadblock.head
local A = {}
for k=i,j do
table.insert(A, texlines:line(k):sub((headwidth or #head)+1))
end
return i,j,A
end,
getblockstr = function (bl, headwidth)
local i,j,A = tf:getblock(headwidth)
return i,j,table.concat(A, "\n")
end,
-- hyperlink = function (bl)
-- return "Line "..lastheadblock.i
-- end,
hyperlink = function (bl)
return format("In the \"%s\"-block in lines %d--%d",
lastheadblock.head, lastheadblock.i, lastheadblock.j)
end,
},
}
--[[
• (eepitch-lua51)
• (eepitch-kill)
• (eepitch-lua51)
dofile "block.lua"
heads = {["%L"]="%L", ["%D"]="%D", ["%:"]="%:"}
Block.__index.processheadblock = function (bl) print(mytostring(bl)) end
texlines = TexLines.test "p DD::: DD DD LLL:::p:p"
tf = texlines:toblock()
= texlines
= tf
= tf:processuntil(1)
= tf:processuntil(21)
= tf:processuntil(23)
texlines = TexLines.test "p DD::: DD DD LLL:::p:p"
tf = texlines:toblock()
tf:process() -- process the whole file
--]]
-- _ __ _ _ ___
-- | |_ _____ __/ _(_) | ___ / _ \
-- | __/ _ \ \/ / |_| | |/ _ \ | | |
-- | || __/> <| _| | | __/ |_| |
-- \__\___/_/\_\_| |_|_|\___|\___/
--
-- «texfile0» (to ".texfile0")
-- texfile0 and texfile: high-level words to read a .tex file.
-- Replaces this:
-- (find-dn6 "texfile.lua" "texfile0")
texfile0 = function (fname)
texlines = TexLines.read(fname)
tf = texlines:toblock()
end
texfile = function (fname)
texfile0(fname..".tex")
end
pu = function (puline) tf:processuntil(puline or tex.inputlineno) end
--[[
• (eepitch-lua51)
• (eepitch-kill)
• (eepitch-lua51)
dofile "block.lua"
texfile0("../2018tugboat.tex")
= tf
PP(getmetatable(tf))
= texlines
heads = {
["%L"]={action = function (...) PP(tf:getblock()) end},
["%D"]={action = function (...) PP(tf:getblock()) end},
["%:"]={action = function (...) PP(tf:getblock()) end},
}
tf:process()
tf:processuntil(110)
--]]
-- _ __ _
-- | |_ / _| _ __ _ _ ___| |__
-- | __| |_ | '_ \| | | / __| '_ \
-- | |_| _| | |_) | |_| \__ \ | | |
-- \__|_|____| .__/ \__,_|___/_| |_|
-- |_____|_|
--
-- «tf_push_and_tf_pop» (to ".tf_push_and_tf_pop")
-- Use this if want to run dednat6 on a subfile.
-- If your main .tex file is foo.tex and it runs "\input bar.tex"
-- then you should put this before the first "\pu" in bar.tex,
--
-- \directlua{tf_push("foo.tex")}
--
-- and this after the last "\pu":
--
-- \directlua{tf_pop()}
--
-- This is an experimental feature, written in june 2019. I hope
-- the source code is obvious.
--
tf_stack = {}
tf_push = function (fname)
local obj = {texlines=texlines, tf=tf}
table.insert(tf_stack, obj)
texfile0(fname)
end
tf_pop = function ()
local obj = table.remove(tf_stack)
texlines = obj.texlines
tf = obj.tf
end
-- Local Variables:
-- coding: utf-8-unix
-- End: