T wo T r:main.lua
From LuaTeXWiki
--- Copyright (c) 2021 by Toadstone Enterprises.
--- ISC-type license, see License.txt for details.
-----------------------------------------------------------------
--- Why is this needed?
local main = {}
package.loaded[...] = main
local util = require("utils")
local link_nodes = util.link_nodes
local make_glyph = util.make_glyph
local make_glue = util.make_glue
local make_penalty = util.make_penalty
local is_whitespace = util.is_whitespace
local is_linefeed = util.is_linefeed
local is_nobreak = util.is_nobreak
local copy_table = util.copy_table
local read_line = util.read_line
local reader = require("reader")
local read_value = reader.read_value
local format = require("format")
local update_state = format.update_state
local update_locals = format.update_locals
local push_tbl = format.push_tbl
local pop_tbl = format.pop_tbl
local top_tbl = format.top_tbl
local pop_do_command_stop = format.pop_do_command_stop
local commands = commands or require("commands")
local get_command = commands.get_command
local initialize_command = commands.initialize_command
local pages = pages or require("pages")
local build_par = pages.build_par
local process_par = pages.process_par
-----------------------------------------------------------------
local do_char = function(value, head, tail)
-- We are building the hlist that will be passed on to
-- tex.linebreak for creating a paragraph.
local tbl = top_tbl()
local n = nil
local p = nil -- for penalties
if is_nobreak(value) then
-- we treat all unbreakable whitespace equally
p = make_penalty(10000)
n = make_glue("spaceskip",
tbl.space,
tbl.space_stretch,
tbl.space_shrink)
elseif is_whitespace(value) then
-- we treat all (breakable) whitespace equally
n = make_glue("spaceskip",
tbl.space,
tbl.space_stretch,
tbl.space_shrink)
else
-- presumably a glyph
n = make_glyph(value,
tbl.font,
tbl.lang,
tbl.lefthyphenmin,
tbl.righthyphenmin)
end
if (head == nil) then
-- starting a new paragraph, but we add the initial glue in
-- build_par.
head = n
elseif p then
-- We have a penalty
link_nodes(tail, p, n)
else
-- only a single node
link_nodes(tail, n)
end
-- n is our new tail
return head, n
end
local reading_par = function(head, tail)
-- before we start reading a paragraph, head and tail will be nil.
return (node.is_node(head) and node.is_node(tail))
end
main.main_loop = function()
local head, tail, eat_the_white, eol_seen = update_locals()
local value = read_value()
while value do
if value == unicode.utf8.byte("%") then
-- Start of comment. Comment goes to the end of the line.
read_line()
elseif is_linefeed(value) then
if (eol_seen and reading_par(head, tail)) then
-- eol_seen ==> this is a blank line,
-- reading_par(head, tail) ==> we are in the middle of a paragraph.
-- Thus we avoid building 'empty' paragraphs.
local par = build_par(head, tail)
process_par(par)
-- After we process the par, we reset head and tail to nil
-- in preparation for the next paragraph to be read in.
head = nil
tail = nil
elseif ((not eat_the_white) and reading_par(head, tail)) then
-- (not eat_the_white) ==> this is the first bit of white space
-- seen,
-- reading_par(head, tail) ==> we are in the middle of a paragraph.
-- Thus, we avoid starting a paragraph with white space.
head, tail = do_char(value, head, tail)
eat_the_white = true
eol_seen = true
end
elseif is_whitespace(value) then
if ((not eat_the_white) and reading_par(head, tail)) then
-- (not eat_the_white) ==> this is the first bit of white space
-- seen,
-- reading_par(head, tail) ==> we are in the middle of a paragraph.
-- Thus, we avoid starting a paragraph with white space.
head, tail = do_char(value, head, tail)
-- But we do not set eol_seen to false. Thus an "empty" line can
-- contain white space.
eat_the_white = true
end
elseif value == unicode.utf8.byte("\\") then
-- get the command, and initialize it
command = get_command()
update_state(head, tail, false, false)
initialize_command(command)
-- initialize_command may hace done some stuff, resetting
-- head, tail, eat_the_white, and eol_seen so we reset them here.
head, tail, eat_the_white, eol_seen = update_locals()
elseif value == unicode.utf8.byte("{") then
-- enter a group or start a command
update_state(head, tail, false , false)
local do_command_start = State.do_command_start
if do_command_start then
-- We have a command, but before executing it, we reset
-- State.do_command_start so that we do not get into a loop.
State.do_command_start = nil
do_command_start()
head, tail, eat_the_white, eol_seen = update_locals()
else
-- enter a group.
push_tbl(copy_table(top_tbl()))
end
elseif value == unicode.utf8.byte("}") then
-- leave a group or stop a command
local do_command_stop = pop_do_command_stop()
if do_command_stop then
update_state(head, tail, false, false)
do_command_stop()
head, tail, eat_the_white, eol_seen = update_locals()
else
pop_tbl()
end
else
-- presumably a glyph
eat_the_white = false
eol_seen = false
head, tail = do_char(value, head, tail)
end
value = read_value()
end -- while (value) do
if reading_par(head, tail) then
-- eof, and we may have a par to build.
if reading_par(head, tail) then
local par = build_par(head, tail)
process_par(par)
end
end
update_state(nil, nil, false, false)
-- Check for unpopped tbls or do_command_stops?
end
return main