--
--- luatexja/ltj-base.lua
+-- ltj-base.lua
--
local ltb = luatexbase
local tostring = tostring
local cat_lp = luatexbase.catcodetables['latex-package']
--------------------
+local ltjb = {}
+luatexja.base = ltjb
local public_name = 'luatexja'
local public_version = 'alpha'
+ltjb.public_name = public_name
+ltjb.public_version = public_version
+
-------------------- Fully-expandable error messaging
local _error_set_break, _error_set_message, _error_show
--! LaTeX 形式のエラーメッセージ(\PackageError 等)を
--! Lua 関数の呼び出しで行う.
- local LF = "\n"
- local err_break = ""
+ local LF, BEL = "\n", "\a"
local err_main = ""
local err_help = ""
local function message_cont(str, c)
- return str:gsub(err_break, LF .. c)
+ return str:gsub(LF, LF .. c)
end
local function into_lines(str)
- return str:gsub(err_break, LF):explode(LF)
- end
-
- _error_set_break = function (str)
- err_break = str
+ return str:explode(LF)
end
_error_set_message = function (msgcont, main, help)
- err_main = message_cont(main, msgcont)
- err_help = into_lines(help)
+ err_main = message_cont(main, msgcont):gsub(BEL, LF)
+ err_help = (help and help~="") and into_lines(help)
+ or {"Sorry, I don't know how to help in this situation.",
+ "Maybe you should try asking a human?" }
end
_error_show = function (escchar)
local message_a = "Type H <return> for immediate help"
generic_error = function (msgcont, main, ref, help)
- local mainref = main..".\n\n"..ref.."\n"..message_a
+ local mainref = main..".\a\a"..ref..BEL..message_a
_error_set_message(msgcont, mainref, help)
_error_show(true)
end
local on_line = line and (" on input line "..tex.inputlineno) or ""
local newlinechar = tex.newlinechar
tex.newlinechar = -1
- texio.write_nl(out, br..main..on_line.."."..br)
+ texio.write_nl(out, br..mainc..on_line.."."..br)
tex.newlinechar = newlinechar
end
end
package_error = function (pkgname, main, help)
- generic_error("("..pkgname.." ",
+ generic_error("("..pkgname..") ",
"Package "..pkgname.." Error: "..main,
"See the "..pkgname.." package documentation for explanation.",
help)
end
package_warning = function (pkgname, main)
- generic_warning("("..pkgname.." ",
+ generic_warning("("..pkgname..") ",
"Package "..pkgname.." Warning: "..main)
end
package_warning_no_line = function (pkgname, main)
- generic_warning_no_line("("..pkgname.." ",
+ generic_warning_no_line("("..pkgname..") ",
"Package "..pkgname.." Warning: "..main)
end
package_info = function (pkgname, main)
- generic_info("("..pkgname.." ",
+ generic_info("("..pkgname..") ",
"Package "..pkgname.." Info: "..main)
end
package_info_no_line = function (pkgname, main)
- generic_info_no_line("("..pkgname.." ",
+ generic_info_no_line("("..pkgname..") ",
"Package "..pkgname.." Info: "..main)
end
end
return tex.print(unpack(lines))
end
+ltjb.mprint = mprint
-------------------- Handling of TeX values
-local to_dimen, to_skip, dump_skip
-
do
- local glue_spec_id = node.id("glue_spec")
-
- local function copy_skip(s1, s2)
- if not s1 then
- s1 = node.new(glue_spec_id)
- end
- s1.width = s2.width or 0
- s1.stretch = s2.stretch or 0
- s1.stretch_order = s2.stretch_order or 0
- s1.shrink = s2.shrink or 0
- s1.shrink_order = s2.shrink_order or 0
- return s1
- end
-
--! ixbase.to_dimen() と同じ
- to_dimen = function (val)
+ local function to_dimen(val)
if val == nil then
return 0
elseif type(val) == "number" then
return tex.sp(val), fil
end
---! ixbase.to_skip() と同じ
- to_skip = function (val)
- if type(val) == "userdata" then
- return val
- end
- local res = node.new(glue_spec_id)
- if val == nil then
- res.width = 0
- elseif type(val) == "number" then
- res.width = val
- elseif type(val) == "table" then
- copy_skip(res, val)
- else
- local t = tostring(val):lower():explode()
- local w, p, m = t[1], t[3], t[5]
- if t[2] == "minus" then
- p, m = nil, t[3]
- end
- res.width = tex.sp(t[1])
- if t[3] then
- res.stretch, res.stretch_order = parse_dimen(t[3])
- end
- if t[5] then
- res.shrink, res.shrink_order = parse_dimen(t[5])
- end
- end
- return res
- end
-
- dump_skip = function (s)
- print(("%s+%s<%s>-%s<%s>"):format(
- s.width or 0, s.stretch or 0, s.stretch_order or 0,
- s.shrink or 0, s.shrink_order or 0))
- end
-
+ ltjb.to_dimen = to_dimen
end
--------------------- Virtual table for LaTeX counters
-local counter
+-------------------- Virtual table for LaTeX counters
+-- not used in current LuaTeX-ja
do
--! ixbase.counter と同じ
- counter = {}
+ local counter = {}
local mt_counter = {}
setmetatable(counter, mt_counter)
function mt_counter.__newindex(tbl, key, val)
tex.count['c@'..key] = val
end
+ ltjb.counter = counter
--! ixbase.length は tex.skip と全く同じなので不要.
end
--------------------- Number handling in TeX source
-local scan_brace, scan_number
-
+-------------------- common error message
do
-
- local tok_escape = token.create("ltj@@q@escape")
- local tok_num = token.create("ltj@@q@escapenum")
- local c_id_assign_int = token.command_id("assign_int")
- local c_id_char_given = token.command_id("char_given")
-
- local function error_scan()
- package_error("luatexja",
- "Missing number of a permitted form, treated as zero",
- "A number should have been here; I inserted '0'.")
- end
-
- local function get_expd_next()
- local next = token.get_next()
- while token.is_expandable(next) do
- token.expand(next)
- next = token.get_next()
- end
- return next
- end
-
- local function grab_decimal(next, res)
- table.insert(res, next)
- while true do
- next = get_expd_next()
- if not (next[1] == 12 and 0x30 <= next[2] and next[2] <= 0x39) then
- break
- end
- table.insert(res, next)
- end
- if next[1] == 10 then next = nil end
- return true, next
- end
-
- local function grab_hexa(next, res)
- local ok = false
- table.insert(res, next)
- while true do
- next = get_expd_next()
- if not ((next[1] == 12 and (0x30 <= next[2] and next[2] <= 0x39)) or
- ((next[1] == 12 or next[1] == 11) and
- (0x41 <= next[2] and next[2] <= 0x46))) then
- break
- end
- ok = true
- table.insert(res, next)
- end
- if next[1] == 10 then next = nil end
- return ok, next
- end
-
- local function grab_octal(next, res)
- local ok = false
- table.insert(res, next)
- while true do
- next = get_expd_next()
- if not (next[1] == 12 and (0x30 <= next[2] and next[2] <= 0x37)) then
- break
- end
- ok = true
- table.insert(res, next)
- end
- if next[1] == 10 then next = nil end
- return ok, next
- end
-
- local function grab_charnum(next, res)
- table.insert(res, next)
- next = token.get_next()
- table.insert(res, next)
- next = get_expd_next()
- if next[1] == 10 then next = nil end
- return true, next
- end
-
- local function scan_with(delay, scanner)
- local function proc()
- if delay ~= 0 then
- if delay > 0 then delay = delay - 1 end
- return token.get_next()
- else
- local cont, back = scanner()
- if not cont then
- ltb.remove_from_callback("token_filter", "ltj@grab@num")
- end
- return back
+ local function in_unicode(c, admit_math)
+ local low = admit_math and -1 or 0
+ if type(c)~='number' or c<low or c>0x10FFFF then
+ local s = 'A character number must be between ' .. tostring(low)
+ .. ' and 0x10ffff.\n'
+ .. (admit_math and "(-1 is used for denoting `math boundary')\n" or '')
+ .. 'So I changed this one to zero.'
+ package_error('luatexja',
+ 'bad character code (' .. tostring(c) .. ')', s)
+ c=0
end
- end
- ltb.add_to_callback("token_filter", proc, "ltj@grab@num", 1)
- end
-
- scan_brace = function ()
- scan_with(1, function()
- local next = token.get_next()
- if next[1] == 1 then
- return false, { tok_escape, next }
- elseif next[1] == 10 then
- return true, { next }
- else
- return false, { next }
- end
- end)
- end
-
- scan_number = function ()
- scan_with(1, function()
- local next = get_expd_next()
- local res, ok = { tok_num }, false
- while true do
- if next[1] == 12 and (next[2] == 0x2B or next[2] == 0x2D) then
- table.insert(res, next)
- elseif next[1] ~= 10 then
- break
- end
- next = get_expd_next()
- end
- if next[1] == 12 and 0x30 <= next[2] and next[2] <= 0x39 then
- ok, next = grab_decimal(next, res)
- elseif next[1] == 12 and next[2] == 0x22 then
- ok, next = grab_hexa(next, res)
- elseif next[1] == 12 and next[2] == 0x27 then
- ok, next = grab_octal(next, res)
- elseif next[1] == 12 and next[2] == 0x60 then
- ok, next = grab_charnum(next, res)
- elseif next[1] == c_id_assign_int or next[1] == c_id_char_given then
- table.insert(res, next)
- ok, next = true, nil
- end
- if ok then
- table.insert(res, tok_num)
- else
- error_scan()
- res = { tok_escape }
- end
- if next then table.insert(res, next) end
- return false, res
- end)
- end
-
-end
--------------------- TeX register allocation
-local const_number, count_number, attribute_number, dimen_number, skip_number
-
-do
- local cmod_base_count = token.create('ltj@@count@zero')[2]
- local cmod_base_attr = token.create('ltj@@attr@zero')[2]
- local cmod_base_dimen = token.create('ltj@@dimen@zero')[2]
- local cmod_base_skip = token.create('ltj@@skip@zero')[2]
-
- const_number = function (name)
- if name:sub(1, 1) == '\\' then name = name:sub(2) end
- return token.create(name)[2]
- end
-
- count_number = function (name)
- if name:sub(1, 1) == '\\' then name = name:sub(2) end
- return token.create(name)[2] - cmod_base_count
- end
-
- attribute_number = function (name)
- if name:sub(1, 1) == '\\' then name = name:sub(2) end
- return token.create(name)[2] - cmod_base_attr
- end
-
- dimen_number = function (name)
- if name:sub(1, 1) == '\\' then name = name:sub(2) end
- return token.create(name)[2] - cmod_base_dimen
- end
-
- skip_number = function (name)
- if name:sub(1, 1) == '\\' then name = name:sub(2) end
- return token.create(name)[2] - cmod_base_skip
- end
-
-end
--------------------- mock of debug logger
-
-if not debug or debug == _G.debug then
- local function no_op() end
- debug = no_op
- package_debug = no_op
- show_term = no_op
- show_log = no_op
- function debug_logger()
- return no_op
- end
-end
-
--------------------- getting next token
-local cstemp = nil
-local function get_cs(s)
- cstemp = token.csname_name(token.get_next())
- tex.sprint(cat_lp,'\\' .. s)
+ return c
+ end
+ ltjb.in_unicode = in_unicode
end
-
-------------------- cache management
-- load_cache (filename, outdate)
-- * filename: without suffix '.lua'
require('lualibs-lpeg') -- string.split
require('lualibs-os') -- os.type
-
-local load_cache, save_cache_luc, save_cache
+require('lualibs-gzip') -- gzip.*
do
local kpse_var_value = kpse.var_value
local join, isreadable = file.join, file.isreadable
local tofile, serialize = table.tofile, table.serialize
local luc_suffix = jit and '.lub' or '.luc'
+ local dump = string.dump
-- determine save path
local savepath = ''
if not lfs.isdir(testpath) then dir.mkdirs(testpath) end
if lfs.isdir(testpath) then savepath = testpath; break end
end
+ local serial_spec = {functions=false, noquotes=true}
+
+ local function remove_file_if_exist(name)
+ if os.rename(name,name) then os.remove(name) end
+ end
+ local function remove_cache (filename)
+ local fullpath_wo_ext = savepath .. '/' .. filename .. '.lu'
+ remove_file_if_exist(fullpath_wo_ext .. 'a')
+ remove_file_if_exist(fullpath_wo_ext .. 'a.gz')
+ remove_file_if_exist(fullpath_wo_ext .. 'b')
+ remove_file_if_exist(fullpath_wo_ext .. 'c')
+ end
- save_cache_luc = function (filename, t, serialized)
+ local function save_cache_luc(filename, t, serialized)
local fullpath = savepath .. '/' .. filename .. luc_suffix
- local s = serialized or serialize(t, 'return', false)
+ local s = serialized or serialize(t, 'return', false, serial_spec)
if s then
local sa = load(s)
local f = io.open(fullpath, 'wb')
if f and sa then
- f:write(string.dump(sa, true))
- texio.write('(save cache: ' .. fullpath .. ')')
+ f:write(dump(sa, true))
+ texio.write('log', '(save cache: ' .. fullpath .. ')')
+ f:close()
end
- f:close()
end
end
- save_cache = function (filename, t)
- local fullpath = savepath .. '/' .. filename .. '.lua'
- local s = serialize(t, 'return', false)
+ local function save_cache(filename, t)
+ local fullpath = savepath .. '/' .. filename .. '.lua.gz'
+ local s = serialize(t, 'return', false, serial_spec)
if s then
- local f = io.open(fullpath, 'w')
- if f then
- f:write(s)
- texio.write('(save cache: ' .. fullpath .. ')')
- end
- f:close()
- save_cache_luc(filename, t, s)
+ gzip.save(fullpath, s, 1)
+ texio.write('log', '(save cache: ' .. fullpath .. ')')
+ save_cache_luc(filename, t, s)
end
end
- local function load_cache_a (filename, outdate)
+ local function load_cache_a(filename, outdate, compressed)
local result
for _,v in pairs(path) do
local fn = join(v, cache_dir, filename)
- if isreadable(fn) then
- texio.write('(load cache: ' .. fn .. ')')
- result = loadfile(fn)
- result = result and result(); break
+ if isreadable(fn) then
+ texio.write('log','(load cache: ' .. filename .. ')')
+ if compressed then
+ result = loadstring(gzip.load(fn))
+ else
+ result = loadfile(fn)
+ end
+ result = result and result()
+ break
end
end
if (not result) or outdate(result) then
return result
end
end
- load_cache = function (filename, outdate)
- local r = load_cache_a(filename .. luc_suffix, outdate)
+
+ local function load_cache(filename, outdate)
+ remove_file_if_exist(savepath .. '/' .. filename .. '.lua')
+ local r = load_cache_a(filename .. luc_suffix, outdate, false)
if r then
return r
else
- local r = load_cache_a(filename .. '.lua', outdate)
+ local r = load_cache_a(filename .. '.lua.gz', outdate, true)
if r then save_cache_luc(filename, r) end -- update the precompiled cache
return r
end
end
+ ltjb.remove_cache = remove_cache
+ ltjb.load_cache = load_cache
+ ltjb.save_cache_luc = save_cache_luc
+ ltjb.save_cache = save_cache
end
+----
+do
+ local tex_set_attr, tex_get_attr = tex.setattribute, tex.getattribute
+ function ltjb.ensure_tex_attr(a, v)
+ if tex_get_attr(a)~=v then
+ tex_set_attr(a, v)
+ end
+ end
+end
+----
+
+ltjb._error_set_message = _error_set_message
+ltjb._error_show = _error_show
+ltjb._generic_warn_info = _generic_warn_info
+
+ltjb.package_error = package_error
+ltjb.package_warning = package_warning
+ltjb.package_warning_no_line = package_warning_no_line
+ltjb.package_info = package_info
+ltjb.package_info_no_line = package_info_no_line
+
+ltjb.generic_error = generic_error
+ltjb.generic_warning = generic_warning
+ltjb.generic_warning_no_line = generic_warning_no_line
+ltjb.generic_info = generic_info
+ltjb.generic_info_no_line = generic_info_no_line
+
+ltjb.ltj_warning_no_line = ltj_warning_no_line
+ltjb.ltj_error = ltj_error
+
+---- deterministic version of luatexbase.add_to_callback
+function ltjb.add_to_callback(name,fun,description,priority)
+ local priority= priority
+ if priority==nil then
+ priority=#luatexbase.callback_descriptions(name)+1
+ end
+ if(luatexbase.callbacktypes[name] == 3 and
+ priority == 1 and
+ #luatexbase.callback_descriptions(name)==1) then
+ luatexbase.module_warning("luatexbase",
+ "resetting exclusive callback: " .. name)
+ luatexbase.reset_callback(name)
+ end
+ local saved_callback={}
+ for k,v in ipairs(luatexbase.callback_descriptions(name)) do
+ if k >= priority then
+ local ff,dd = luatexbase.remove_from_callback(name, v)
+ saved_callback[#saved_callback+1]={ff,dd}
+ end
+ end
+ luatexbase.base_add_to_callback(name,fun,description)
+ for _,v in ipairs(saved_callback) do
+ luatexbase.base_add_to_callback(name,v[1],v[2])
+ end
+ return
+end
+
+-------------------- mock of debug logger
+if not ltjb.out_debug then
+ local function no_op() end
+ ltjb.start_time_measure = no_op
+ ltjb.stop_time_measure = no_op
+ ltjb.out_debug = no_op
+ ltjb.package_debug = no_op
+ ltjb.debug_logger = function() return no_op end
+ ltjb.show_term = no_op
+ ltjb.show_log = no_op
+end
-luatexja.base = {
- public_name = public_name,
- public_version = public_version,
- --
- _error_set_break = _error_set_break,
- _error_set_message = _error_set_message,
- _error_show = _error_show,
- _generic_warn_info = _generic_warn_info,
- --
- package_error = package_error,
- package_warning = package_warning,
- package_warning_no_line = package_warning_no_line,
- package_info = package_info,
- package_info_no_line = package_info_no_line,
- --
- generic_error = generic_error,
- generic_warning = generic_warning,
- generic_warning_no_line = generic_warning_no_line,
- generic_info = generic_info,
- generic_info_no_line = generic_info_no_line,
- --
- ltj_warning_no_line = ltj_warning_no_line,
- ltj_error = ltj_error,
- --
- mprint = mprint,
- --
- to_dimen = to_dimen,
- dump_skip = dump_skip,
- to_skip = to_skip,
- --
- counter = counter,
- --
- scan_brace = scan_brace,
- scan_number = scan_number,
- --
- const_number = const_number,
- count_number = count_number,
- attribute_number = attribute_number,
- dimen_number = dimen_number,
- skip_number = skip_number,
- --
- get_cs = get_cs,
- --
- load_cache = load_cache,
- save_cache_luc = save_cache_luc,
- save_cache = save_cache,
- --
- debug = debug,
- package_debug = package_debug,
- debug_logger = debug_logger,
- show_term = show_term,
- show_log = show_log,
- --
-}
-------------------- all done
-- EOF