luametalatex/luametalatex-firstcode.lua
Marcel Fabian Krüger 94ba4a2557 Better errors and new whatsit handling
The whatsit handling is actually much more similar to the old whatsit
handling then the previous new one, so that part could be seen as a
partial revert of the new whatsit handling... Anyway, whatsits work
different than in the previous commit now.
2020-07-01 19:47:25 +02:00

208 lines
6.4 KiB
Lua

local functions = lua.getfunctionstable()
-- I am not sure why this is necessary, but otherwise LuaMetaTeX resets
-- the functions table every time the getter is called
function lua.get_functions_table() return functions end
local set_lua = token.set_lua
-- There are two approaches to manage luafunctions ids without triggering
-- issues with ltluatex assigned numbers: Originally we assigned numbers
-- starting with 1, then switched to luatexbase ASAP and synchronised both
-- numbering schemes. But there is a useful quirk in ltluatex's luafunction
-- allocator: It only uses numbers upto 65535, so we can just use bigger
-- numbers. (This might have negative repercussins on performance because it
-- probably doesn't store the function in the array part of the Lua table.
-- Let's reconsider if this ever becomes a problem.
-- local new_luafunction = luatexbase.new_luafunction
local predefined_luafunctions = status.ini_version and 65536 -- 1<<16 -- We start with 1<<16 + 1 (1<<16=65536 is reserved for luametalatex-local)
local function new_luafunction(name)
if predefined_luafunctions then
predefined_luafunctions = predefined_luafunctions + 1
return predefined_luafunctions
else
error[[Here only preexisting luafunctions should be set]]
end
end
local undefined_cmd = token.command_id'undefined_cs'
local lua_call_cmd = token.command_id'lua_call'
local lua_value_cmd = token.command_id'lua_value'
local lua_expandable_call_cmd = token.command_id'lua_expandable_call'
function token.luacmd(name, func, ...)
local idx
local tok = token.create(name)
local cmd = tok.command
if cmd == lua_value_cmd then
idx = tok.mode
elseif cmd == lua_call_cmd then
idx = tok.mode
elseif cmd == lua_expandable_call_cmd then
idx = tok.mode
elseif ... == 'force' then
idx = new_luafunction(name)
set_lua(name, idx, select(2, ...))
elseif cmd == undefined_cmd then
idx = new_luafunction(name)
set_lua(name, idx, ...)
else
error(tok.cmdname)
end
if functions[idx] then
error[[Already defined]]
end
functions[idx] = func
return idx
end
local properties = node.direct.get_properties_table()
node.direct.properties = properties
function node.direct.get_properties_table()
return properties
end
-- setmetatable(node.direct.get_properties_table(), {
-- __index = function(t, id)
-- local new = {}
-- t[id] = new
-- return new
-- end
-- })
local new_whatsit = require'luametalatex-whatsits'.new
local whatsit_id = node.id'whatsit'
local spacer_cmd, relax_cmd = token.command_id'spacer', token.command_id'relax'
local function scan_filename()
local name = {}
local quoted = false
local tok, cmd
repeat
tok = token.scan_token()
cmd = tok.command
until cmd ~= spacer_cmd and cmd ~= relax_cmd
while (tok.command <= 12 and tok.mode <= token.biggest_char()
or (token.put_next(tok) and false))
and (quoted or tok.mode ~= string.byte' ') do
if tok.mode == string.byte'"' then
quoted = not quoted
else
name[#name+1] = tok.mode
end
tok = token.scan_token()
end
return utf8.char(table.unpack(name))
end
local ofiles = {}
local function do_openout(p)
if ofiles[p.file] then
error[[Existing file]]
else
local msg
ofiles[p.file], msg = io.open(p.name, 'w')
if not ofiles[p.file] then
error(msg)
end
end
end
local open_whatsit = new_whatsit('open', do_openout)
token.luacmd("openout", function(_, immediate) -- \openout
local file = token.scan_int()
token.scan_keyword'='
local name = scan_filename()
local props = {file = file, name = name}
if immediate == "immediate" then
do_openout(props)
else
local whatsit = node.direct.new(whatsit_id, open_whatsit)
properties[whatsit] = props
node.direct.write(whatsit)
end
end, "protected")
local function do_closeout(p)
if ofiles[p.file] then
ofiles[p.file]:close()
ofiles[p.file] = nil
end
end
local close_whatsit = new_whatsit('close', do_closeout)
token.luacmd("closeout", function(_, immediate) -- \closeout
local file = token.scan_int()
local props = {file = file}
if immediate == "immediate" then
do_closeout(props)
else
local whatsit = node.direct.new(whatsit_id, close_whatsit)
properties[whatsit] = props
node.direct.write(whatsit)
end
end, "protected")
local function do_write(p)
local content = token.to_string(p.data) .. '\n'
local file = ofiles[p.file]
if file then
file:write(content)
else
texio.write_nl(p.file < 0 and "log" or "term and log", content)
end
end
local write_whatsit = new_whatsit('write', do_write)
token.luacmd("write", function(_, immediate) -- \write
local file = token.scan_int()
local content = token.scan_tokenlist()
local props = {file = file, data = content}
if immediate == "immediate" then
do_write(props)
else
local whatsit = node.direct.new(whatsit_id, write_whatsit)
properties[whatsit] = props
node.direct.write(whatsit)
end
end, "protected")
token.luacmd("immediate", function() -- \immediate
local next_tok = token.scan_token()
if next_tok.command ~= lua_call_cmd then
return token.put_next(next_tok)
end
local function_id = next_tok.mode
return functions[function_id](function_id, 'immediate')
end, "protected")
-- functions[43] = function() -- \pdfvariable
-- local name = token.scan_string()
-- print('Missing \\pdf' .. name)
-- end
if status.ini_version then
function fixupluafunctions()
return predefined_luafunctions
end
else
function fixupluafunctions(i)
predefined_luafunctions = i
end
local prepared_code = lua.bytecode[1]
prepared_code()
lua.bytecode[1] = nil
-- function fixupluafunctions()
-- new_luafunction = luatexbase.new_luafunction
-- fixupluafunctions = nil
-- end
end
require'luametalatex-baseregisters'
require'luametalatex-back-pdf'
require'luametalatex-node-luaotfload'
local integer_code do
local value_values = token.values'value'
for i=0,#value_values do
if value_values[i] == "integer" then
integer_code = i
break
end
end
end
token.luacmd("Umathcodenum", function(_, scanning)
if scanning then
local class, family, char = tex.getmathcodes (token.scan_int())
return integer_code, char | (class | family << 3) << 21
else
local char = token.scan_int()
local mathcode = token.scan_int()
tex.setmathcodes(char, (mathcode >> 21) & 7, mathcode >> 24, mathcode & 0x1FFFFF)
end
end, "force", "global", "value")