4 luatexbase.provides_module({
5 name = 'luatexja.base',
10 module('luatexja.base', package.seeall)
11 local err, warn, info, log = luatexbase.errwarinf(_NAME)
13 local ltb = luatexbase
14 local tostring = tostring
15 local node, table, tex, token = node, table, tex, token
17 -------------------- TeX stream I/O
20 --! ixbase.print() と同じ
21 --- Extension to tex.print(). Each argument string may contain
22 -- newline characters, in which case the string is output (to
23 -- TeX input stream) as multiple lines.
24 -- @param ... (string) string to output
28 if type(arg[1]) == "number" then
29 table.insert(lines, arg[1])
32 for _, cnk in ipairs(arg) do
33 local ls = cnk:explode("\n")
37 for _, l in ipairs(ls) do
38 table.insert(lines, l)
41 return tex.print(unpack(lines))
45 -------------------- Handling of TeX values
48 local glue_spec_id = node.id("glue_spec")
50 local function copy_skip(s1, s2)
52 s1 = node.new(glue_spec_id)
54 s1.width = s2.width or 0
55 s1.stretch = s2.stretch or 0
56 s1.stretch_order = s2.stretch_order or 0
57 s1.shrink = s2.shrink or 0
58 s1.shrink_order = s2.shrink_order or 0
62 --! ixbase.to_dimen() と同じ
63 function to_dimen(val)
66 elseif type(val) == "number" then
69 return tex.sp(tostring(val))
73 local function parse_dimen(val)
74 val = tostring(val):lower()
75 local r, fil = val:match("([-.%d]+)fi(l*)")
77 val, fil = r.."pt", fil:len() + 1
81 return tex.sp(val), fil
84 --! ixbase.to_skip() と同じ
86 if type(val) == "userdata" then
89 local res = node.new(glue_spec_id)
92 elseif type(val) == "number" then
94 elseif type(val) == "table" then
97 local t = tostring(val):lower():explode()
98 local w, p, m = t[1], t[3], t[5]
99 if t[2] == "minus" then
102 res.width = tex.sp(t[1])
104 res.stretch, res.stretch_order = parse_dimen(t[3])
107 res.shrink, res.shrink_order = parse_dimen(t[5])
113 function dump_skip(s)
114 print(("%s+%s<%s>-%s<%s>"):format(
115 s.width or 0, s.stretch or 0, s.stretch_order or 0,
116 s.shrink or 0, s.shrink_order or 0))
120 -------------------- Virtual table for LaTeX counters
123 --! ixbase.counter と同じ
125 local mt_counter = {}
126 setmetatable(counter, mt_counter)
128 function mt_counter.__index(tbl, key)
129 return tex.count['c@'..key]
131 function mt_counter.__newindex(tbl, key, val)
132 tex.count['c@'..key] = val
135 --! ixbase.length は tex.skip と全く同じなので不要.
138 -------------------- Fully-expandable error messaging
140 --! LaTeX 形式のエラーメッセージ(\PackageError 等)を
148 local function message_cont(str, c)
149 return str:gsub(err_break, LF .. c)
151 local function into_lines(str)
152 return str:gsub(err_break, LF):explode(LF)
155 function _error_set_break(str)
159 function _error_set_message(msgcont, main, help)
160 err_main = message_cont(main, msgcont)
161 err_help = into_lines(help)
164 function _error_show(escchar)
165 local escapechar = tex.escapechar
166 local newlinechar = tex.newlinechar
167 local errorcontextlines = tex.errorcontextlines
168 if not escchar then tex.escapechar = -1 end
170 tex.errorcontextlines = -1
171 tex.error(err_main, err_help)
172 tex.escapechar = escapechar
173 tex.newlinechar = newlinechar
174 tex.errorcontextlines = errorcontextlines
177 local message_a = "Type H <return> for immediate help"
179 function generic_error(msgcont, main, ref, help)
180 local mainref = main..".\n\n"..ref.."\n"..message_a
181 _error_set_message(msgcont, mainref, help)
185 function _generic_warn_info(msgcont, main, warn, line)
186 local mainc = message_cont(main, msgcont)
187 local br = warn and "\n" or ""
188 local out = warn and "term and log" or "log"
189 local on_line = line and (" on input line "..tex.inputlineno) or ""
190 local newlinechar = tex.newlinechar
192 texio.write_nl(out, br..main..on_line.."."..br)
193 tex.newlinechar = newlinechar
196 function generic_warning(msgcont, main)
197 _generic_warn_info(msgcont, main, true, true)
199 function generic_warning_no_line(msgcont, main)
200 _generic_warn_info(msgcont, main, true, false)
202 function generic_info(msgcont, main)
203 _generic_warn_info(msgcont, main, false, true)
205 function generic_info_no_line(msgcont, main)
206 _generic_warn_info(msgcont, main, false, false)
209 function package_error(pkgname, main, help)
210 generic_error("("..pkgname.." ",
211 "Package "..pkgname.." Error: "..main,
212 "See the "..pkgname.." package documentation for explanation.",
215 function package_warning(pkgname, main)
216 generic_warning("("..pkgname.." ",
217 "Package "..pkgname.." Warning: "..main)
219 function package_warning_no_line(pkgname, main)
220 generic_warning_no_line("("..pkgname.." ",
221 "Package "..pkgname.." Warning: "..main)
223 function package_info(pkgname, main)
224 generic_info("("..pkgname.." ",
225 "Package "..pkgname.." Info: "..main)
227 function package_info_no_line(pkgname, main)
228 generic_info_no_line("("..pkgname.." ",
229 "Package "..pkgname.." Info: "..main)
233 -------------------- Number handling in TeX source
236 local tok_escape = token.create("ltj@@q@escape")
237 local tok_num = token.create("ltj@@q@escapenum")
238 local c_id_assign_int = token.command_id("assign_int")
239 local c_id_char_given = token.command_id("char_given")
241 local function error_scan()
242 _M.package_error("luatexja",
243 "Missing number of a permitted form, treated as zero",
244 "A number should have been here; I inserted '0'.")
247 local function get_expd_next()
248 local next = token.get_next()
249 while token.is_expandable(next) do
251 next = token.get_next()
256 local function grab_decimal(next, res)
257 table.insert(res, next)
259 next = get_expd_next()
260 if not (next[1] == 12 and 0x30 <= next[2] and next[2] <= 0x39) then
263 table.insert(res, next)
265 if next[1] == 10 then next = nil end
269 local function grab_hexa(next, res)
271 table.insert(res, next)
273 next = get_expd_next()
274 if not ((next[1] == 12 and (0x30 <= next[2] and next[2] <= 0x39)) or
275 ((next[1] == 12 or next[1] == 11) and
276 (0x41 <= next[2] and next[2] <= 0x46))) then
280 table.insert(res, next)
282 if next[1] == 10 then next = nil end
286 local function grab_octal(next, res)
288 table.insert(res, next)
290 next = get_expd_next()
291 if not (next[1] == 12 and (0x30 <= next[2] and next[2] <= 0x37)) then
295 table.insert(res, next)
297 if next[1] == 10 then next = nil end
301 local function grab_charnum(next, res)
302 table.insert(res, next)
303 next = token.get_next()
304 table.insert(res, next)
305 next = get_expd_next()
306 if next[1] == 10 then next = nil end
310 local function scan_with(delay, scanner)
311 local function proc()
313 if delay > 0 then delay = delay - 1 end
314 return token.get_next()
316 local cont, back = scanner()
318 ltb.remove_from_callback("token_filter", "ltj@grab@num")
323 ltb.add_to_callback("token_filter", proc, "ltj@grab@num", 1)
326 function scan_brace()
327 scan_with(1, function()
328 local next = token.get_next()
330 return false, { tok_escape, next }
331 elseif next[1] == 10 then
332 return true, { next }
334 return false, { next }
339 function scan_number()
340 scan_with(1, function()
341 local next = get_expd_next()
342 local res, ok = { tok_num }, false
344 if next[1] == 12 and (next[2] == 0x2B or next[2] == 0x2D) then
345 table.insert(res, next)
346 elseif next[1] ~= 10 then
349 next = get_expd_next()
351 if next[1] == 12 and 0x30 <= next[2] and next[2] <= 0x39 then
352 ok, next = grab_decimal(next, res)
353 elseif next[1] == 12 and next[2] == 0x22 then
354 ok, next = grab_hexa(next, res)
355 elseif next[1] == 12 and next[2] == 0x27 then
356 ok, next = grab_octal(next, res)
357 elseif next[1] == 12 and next[2] == 0x60 then
358 ok, next = grab_charnum(next, res)
359 elseif next[1] == c_id_assign_int or next[1] == c_id_char_given then
360 table.insert(res, next)
364 table.insert(res, tok_num)
369 if next then table.insert(res, next) end
375 -------------------- mock of debug logger
377 if not _M.debug or _M.debug == _G.debug then
378 local function no_op() end
380 package_debug = no_op
383 function debug_logger()
388 -------------------- all done