2 -- luatexja/ltj-base.lua
5 local tostring = tostring
6 local node, table, tex, token = node, table, tex, token
8 local cat_lp = luatexbase.catcodetables['latex-package']
14 local public_name = 'luatexja'
15 local public_version = 'alpha'
16 ltjb.public_name = public_name
17 ltjb.public_version = public_version
20 -------------------- Fully-expandable error messaging
21 local _error_set_break, _error_set_message, _error_show
22 local generic_error, _generic_warn_info
23 local generic_warning, generic_warning_no_line
24 local generic_info, generic_info_no_line
25 local package_error, package_warning, package_warning_no_line
26 local package_info, package_info_no_line
27 local ltj_error, ltj_warning_no_line
30 --! LaTeX 形式のエラーメッセージ(\PackageError 等)を
38 local function message_cont(str, c)
39 return str:gsub(err_break, LF .. c)
41 local function into_lines(str)
42 return str:gsub(err_break, LF):explode(LF)
45 _error_set_break = function (str)
49 _error_set_message = function (msgcont, main, help)
50 err_main = message_cont(main, msgcont)
51 err_help = into_lines(help)
54 _error_show = function (escchar)
55 local escapechar = tex.escapechar
56 local newlinechar = tex.newlinechar
57 local errorcontextlines = tex.errorcontextlines
58 if not escchar then tex.escapechar = -1 end
60 tex.errorcontextlines = -1
61 tex.error(err_main, err_help)
62 tex.escapechar = escapechar
63 tex.newlinechar = newlinechar
64 tex.errorcontextlines = errorcontextlines
67 local message_a = "Type H <return> for immediate help"
69 generic_error = function (msgcont, main, ref, help)
70 local mainref = main..".\n\n"..ref.."\n"..message_a
71 _error_set_message(msgcont, mainref, help)
75 _generic_warn_info = function (msgcont, main, warn, line)
76 local mainc = message_cont(main, msgcont)
77 local br = warn and "\n" or ""
78 local out = warn and "term and log" or "log"
79 local on_line = line and (" on input line "..tex.inputlineno) or ""
80 local newlinechar = tex.newlinechar
82 texio.write_nl(out, br..main..on_line.."."..br)
83 tex.newlinechar = newlinechar
86 generic_warning = function (msgcont, main)
87 _generic_warn_info(msgcont, main, true, true)
89 generic_warning_no_line = function (msgcont, main)
90 _generic_warn_info(msgcont, main, true, false)
92 generic_info = function (msgcont, main)
93 _generic_warn_info(msgcont, main, false, true)
95 generic_info_no_line = function (msgcont, main)
96 _generic_warn_info(msgcont, main, false, false)
99 package_error = function (pkgname, main, help)
100 generic_error("("..pkgname.." ",
101 "Package "..pkgname.." Error: "..main,
102 "See the "..pkgname.." package documentation for explanation.",
105 package_warning = function (pkgname, main)
106 generic_warning("("..pkgname.." ",
107 "Package "..pkgname.." Warning: "..main)
109 package_warning_no_line = function (pkgname, main)
110 generic_warning_no_line("("..pkgname.." ",
111 "Package "..pkgname.." Warning: "..main)
113 package_info = function (pkgname, main)
114 generic_info("("..pkgname.." ",
115 "Package "..pkgname.." Info: "..main)
117 package_info_no_line = function (pkgname, main)
118 generic_info_no_line("("..pkgname.." ",
119 "Package "..pkgname.." Info: "..main)
122 ltj_error = function (main, help)
123 package_error(public_name, main, help)
125 ltj_warning_no_line = function (main)
126 package_warning_no_line(public_name, main, help)
130 -------------------- TeX stream I/O
131 --! ixbase.print() と同じ
132 --- Extension to tex.print(). Each argument string may contain
133 -- newline characters, in which case the string is output (to
134 -- TeX input stream) as multiple lines.
135 -- @param ... (string) string to output
136 local function mprint(...)
139 if type(arg[1]) == "number" then
140 table.insert(lines, arg[1])
143 for _, cnk in ipairs(arg) do
144 local ls = cnk:explode("\n")
145 if ls[#ls] == "" then
146 table.remove(ls, #ls)
148 for _, l in ipairs(ls) do
149 table.insert(lines, l)
152 return tex.print(unpack(lines))
156 -------------------- Handling of TeX values
159 local glue_spec_id = node.id("glue_spec")
161 local function copy_skip(s1, s2)
163 s1 = node.new(glue_spec_id)
165 s1.width = s2.width or 0
166 s1.stretch = s2.stretch or 0
167 s1.stretch_order = s2.stretch_order or 0
168 s1.shrink = s2.shrink or 0
169 s1.shrink_order = s2.shrink_order or 0
173 --! ixbase.to_dimen() と同じ
174 local function to_dimen(val)
177 elseif type(val) == "number" then
180 return tex.sp(tostring(val))
184 local function parse_dimen(val)
185 val = tostring(val):lower()
186 local r, fil = val:match("([-.%d]+)fi(l*)")
188 val, fil = r.."pt", fil:len() + 1
192 return tex.sp(val), fil
195 --! ixbase.to_skip() と同じ
196 local function to_skip(val)
197 if type(val) == "userdata" then
200 local res = node.new(glue_spec_id)
203 elseif type(val) == "number" then
205 elseif type(val) == "table" then
208 local t = tostring(val):lower():explode()
209 local w, p, m = t[1], t[3], t[5]
210 if t[2] == "minus" then
213 res.width = tex.sp(t[1])
215 res.stretch, res.stretch_order = parse_dimen(p)
218 res.shrink, res.shrink_order = parse_dimen(m)
224 local function dump_skip(s)
225 print(("%s+%s<%s>-%s<%s>"):format(
226 s.width or 0, s.stretch or 0, s.stretch_order or 0,
227 s.shrink or 0, s.shrink_order or 0))
230 ltjb.to_dimen = to_dimen
231 ltjb.dump_skip = dump_skip
232 ltjb.to_skip = to_skip
235 -------------------- Virtual table for LaTeX counters
236 -- not used in current LuaTeX-ja
238 --! ixbase.counter と同じ
240 local mt_counter = {}
241 setmetatable(counter, mt_counter)
243 function mt_counter.__index(tbl, key)
244 return tex.count['c@'..key]
246 function mt_counter.__newindex(tbl, key, val)
247 tex.count['c@'..key] = val
249 ltjb.counter = counter
251 --! ixbase.length は tex.skip と全く同じなので不要.
254 -------------------- common error message
256 local function in_unicode(c, admit_math)
257 local low = admit_math and -1 or 0
258 if type(c)~='number' or c<low or c>0x10FFFF then
259 local s = 'A character number must be between ' .. tostring(low)
260 .. ' and 0x10ffff.\n'
261 .. (admit_math and "(-1 is used for denoting `math boundary')\n" or '')
262 .. 'So I changed this one to zero.'
263 package_error('luatexja',
264 'bad character code (' .. tostring(c) .. ')', s)
269 ltjb.in_unicode = in_unicode
272 -------------------- cache management
273 -- load_cache (filename, outdate)
274 -- * filename: without suffix '.lua'
275 -- * outdate(t): return true iff the cache is outdated
276 -- * return value: non-nil iff the cache is up-to-date
277 -- save_cache (filename, t): no return value
278 -- save_cache_luc (filename, t): no return value
279 -- save_cache always calls save_cache_luc.
280 -- But sometimes we want to create only the precompiled cache,
281 -- when its 'text' version is already present in LuaTeX-ja distribution.
283 require('lualibs-lpeg') -- string.split
284 require('lualibs-os') -- os.type
287 local kpse_var_value = kpse.var_value
288 local path, pathtmp = kpse_var_value("TEXMFVAR")
289 pathtmp = kpse_var_value("TEXMFSYSVAR")
290 if pathtmp then path = (path and path .. ';' or '') .. pathtmp end
291 pathtmp = kpse_var_value("TEXMFCACHE")
292 if pathtmp then path = (path and path .. ';' or '') .. pathtmp end
294 if os.type~='windows' then path = string.gsub(path, ':', ';') end
295 path = table.unique(string.split(path, ';'))
297 local cache_dir = '/luatexja'
298 local find_file = kpse.find_file
299 local join, isreadable = file.join, file.isreadable
300 local tofile, serialize = table.tofile, table.serialize
301 local luc_suffix = jit and '.lub' or '.luc'
303 -- determine save path
305 for _,v in pairs(path) do
306 local testpath = join(v, cache_dir)
307 if not lfs.isdir(testpath) then dir.mkdirs(testpath) end
308 if lfs.isdir(testpath) then savepath = testpath; break end
311 save_cache_luc = function (filename, t, serialized)
312 local fullpath = savepath .. '/' .. filename .. luc_suffix
313 local s = serialized or serialize(t, 'return', false)
316 local f = io.open(fullpath, 'wb')
318 f:write(string.dump(sa, true))
319 texio.write('(save cache: ' .. fullpath .. ')')
325 save_cache = function (filename, t)
326 local fullpath = savepath .. '/' .. filename .. '.lua'
327 local s = serialize(t, 'return', false)
329 local f = io.open(fullpath, 'w')
332 texio.write('(save cache: ' .. fullpath .. ')')
335 save_cache_luc(filename, t, s)
339 local function load_cache_a (filename, outdate)
341 for _,v in pairs(path) do
342 local fn = join(v, cache_dir, filename)
343 if isreadable(fn) then
344 texio.write('(load cache: ' .. fn .. ')')
345 result = loadfile(fn)
346 result = result and result(); break
349 if (not result) or outdate(result) then
356 load_cache = function (filename, outdate)
357 local r = load_cache_a(filename .. luc_suffix, outdate)
361 local r = load_cache_a(filename .. '.lua', outdate)
362 if r then save_cache_luc(filename, r) end -- update the precompiled cache
367 ltjb.load_cache = load_cache
368 ltjb.save_cache_luc = save_cache_luc
369 ltjb.save_cache = save_cache
374 local tex_set_attr, tex_get_attr = tex.setattribute, tex.getattribute
375 function ltjb.ensure_tex_attr(a, v)
376 if tex_get_attr(a)~=v then
383 ltjb._error_set_break = _error_set_break
384 ltjb._error_set_message = _error_set_message
385 ltjb._error_show = _error_show
386 ltjb._generic_warn_info = _generic_warn_info
388 ltjb.package_error = package_error
389 ltjb.package_warning = package_warning
390 ltjb.package_warning_no_line = package_warning_no_line
391 ltjb.package_info = package_info
392 ltjb.package_info_no_line = package_info_no_line
394 ltjb.generic_error = generic_error
395 ltjb.generic_warning = generic_warning
396 ltjb.generic_warning_no_line = generic_warning_no_line
397 ltjb.generic_info = generic_info
398 ltjb.generic_info_no_line = generic_info_no_line
400 ltjb.ltj_warning_no_line = ltj_warning_no_line
401 ltjb.ltj_error = ltj_error
403 -------------------- mock of debug logger
404 if not ltjb.out_debug then
405 local function no_op() end
406 ltjb.start_time_measure = no_op
407 ltjb.stop_time_measure = no_op
408 ltjb.out_debug = no_op
409 ltjb.package_debug = no_op
410 ltjb.debug_logger = function() return no_op end
411 ltjb.show_term = no_op
412 ltjb.show_log = no_op
415 -------------------- all done