OSDN Git Service

Use everysel package for patching \selectfont.
[luatex-ja/luatexja.git] / src / ltj-base.lua
1 --
2 -- luatexja/ltj-base.lua
3 --
4 local ltb = luatexbase
5 local tostring = tostring
6 local node, table, tex, token = node, table, tex, token
7
8 local cat_lp = luatexbase.catcodetables['latex-package']
9
10 -------------------- 
11 luatexja.base = {}
12
13 local public_name = 'luatexja'
14 local public_version = 'alpha'
15 luatexja.base.public_name = public_name
16 luatexja.base.public_version = public_version
17
18
19 -------------------- Fully-expandable error messaging
20 local _error_set_break, _error_set_message, _error_show
21 local generic_error, _generic_warn_info
22 local generic_warning, generic_warning_no_line
23 local generic_info, generic_info_no_line
24 local package_error, package_warning, package_warning_no_line
25 local package_info, package_info_no_line
26 local ltj_error, ltj_warning_no_line
27
28 do
29 --! LaTeX 形式のエラーメッセージ(\PackageError 等)を
30 --! Lua 関数の呼び出しで行う.
31
32   local LF = "\n"
33   local err_break = ""
34   local err_main = ""
35   local err_help = ""
36
37   local function message_cont(str, c)
38     return str:gsub(err_break, LF .. c)
39   end
40   local function into_lines(str)
41     return str:gsub(err_break, LF):explode(LF)
42   end
43
44   _error_set_break = function (str)
45     err_break = str
46   end
47
48   _error_set_message = function (msgcont, main, help)
49     err_main = message_cont(main, msgcont)
50     err_help = into_lines(help)
51   end
52
53   _error_show = function (escchar)
54     local escapechar = tex.escapechar
55     local newlinechar = tex.newlinechar
56     local errorcontextlines = tex.errorcontextlines
57     if not escchar then tex.escapechar = -1 end
58     tex.newlinechar = 10
59     tex.errorcontextlines = -1
60     tex.error(err_main, err_help)
61     tex.escapechar = escapechar
62     tex.newlinechar = newlinechar
63     tex.errorcontextlines = errorcontextlines
64   end
65
66   local message_a = "Type  H <return>  for immediate help"
67
68   generic_error = function (msgcont, main, ref, help)
69     local mainref = main..".\n\n"..ref.."\n"..message_a
70     _error_set_message(msgcont, mainref, help)
71     _error_show(true)
72   end
73
74   _generic_warn_info = function (msgcont, main, warn, line)
75     local mainc = message_cont(main, msgcont)
76     local br = warn and "\n" or ""
77     local out = warn and "term and log" or "log"
78     local on_line = line and (" on input line "..tex.inputlineno) or ""
79     local newlinechar = tex.newlinechar
80     tex.newlinechar = -1
81     texio.write_nl(out, br..main..on_line.."."..br)
82     tex.newlinechar = newlinechar
83   end
84
85   generic_warning = function (msgcont, main)
86     _generic_warn_info(msgcont, main, true, true)
87   end
88   generic_warning_no_line = function (msgcont, main)
89     _generic_warn_info(msgcont, main, true, false)
90   end
91   generic_info = function (msgcont, main)
92     _generic_warn_info(msgcont, main, false, true)
93   end
94   generic_info_no_line = function (msgcont, main)
95     _generic_warn_info(msgcont, main, false, false)
96   end
97
98   package_error = function (pkgname, main, help)
99     generic_error("("..pkgname.."                ",
100       "Package "..pkgname.." Error: "..main,
101       "See the "..pkgname.." package documentation for explanation.",
102       help)
103   end
104   package_warning = function (pkgname, main)
105     generic_warning("("..pkgname.."                ",
106       "Package "..pkgname.." Warning: "..main)
107   end
108   package_warning_no_line = function (pkgname, main)
109     generic_warning_no_line("("..pkgname.."                ",
110       "Package "..pkgname.." Warning: "..main)
111   end
112   package_info = function (pkgname, main)
113     generic_info("("..pkgname.."             ",
114       "Package "..pkgname.." Info: "..main)
115   end
116   package_info_no_line = function (pkgname, main)
117     generic_info_no_line("("..pkgname.."             ",
118       "Package "..pkgname.." Info: "..main)
119   end
120
121   ltj_error = function (main, help)
122     package_error(public_name, main, help)
123   end
124   ltj_warning_no_line = function (main)
125     package_warning_no_line(public_name, main, help)
126   end
127
128 end
129 -------------------- TeX stream I/O
130 --! ixbase.print() と同じ
131 --- Extension to tex.print(). Each argument string may contain
132 -- newline characters, in which case the string is output (to
133 -- TeX input stream) as multiple lines.
134 -- @param ... (string) string to output 
135 local function mprint(...)
136    local arg = {...}
137    local lines = {}
138    if type(arg[1]) == "number" then
139       table.insert(lines, arg[1])
140       table.remove(arg, 1)
141    end
142    for _, cnk in ipairs(arg) do
143       local ls = cnk:explode("\n")
144       if ls[#ls] == "" then
145          table.remove(ls, #ls)
146       end
147       for _, l in ipairs(ls) do
148          table.insert(lines, l)
149       end
150    end
151    return tex.print(unpack(lines))
152 end
153 luatexja.base.mprint = mprint
154
155 -------------------- Handling of TeX values
156 do
157
158   local glue_spec_id = node.id("glue_spec")
159
160   local function copy_skip(s1, s2)
161     if not s1 then
162       s1 = node.new(glue_spec_id)
163     end
164     s1.width = s2.width or 0
165     s1.stretch = s2.stretch or 0
166     s1.stretch_order = s2.stretch_order or 0
167     s1.shrink = s2.shrink or 0
168     s1.shrink_order = s2.shrink_order or 0
169     return s1
170   end
171
172 --! ixbase.to_dimen() と同じ
173   local function to_dimen(val)
174     if val == nil then
175       return 0
176     elseif type(val) == "number" then
177       return val
178     else
179       return tex.sp(tostring(val))
180     end
181   end
182
183   local function parse_dimen(val)
184     val = tostring(val):lower()
185     local r, fil = val:match("([-.%d]+)fi(l*)")
186     if r then
187       val, fil = r.."pt", fil:len() + 1
188     else
189       fil = 0
190     end
191     return tex.sp(val), fil
192   end
193
194 --! ixbase.to_skip() と同じ
195   local function to_skip(val)
196     if type(val) == "userdata" then
197       return val
198     end
199     local res = node.new(glue_spec_id)
200     if val == nil then
201       res.width = 0
202     elseif type(val) == "number" then
203       res.width = val
204     elseif type(val) == "table" then
205       copy_skip(res, val)
206     else
207       local t = tostring(val):lower():explode()
208       local w, p, m = t[1], t[3], t[5]
209       if t[2] == "minus" then
210         p, m = nil, t[3]
211       end
212       res.width = tex.sp(t[1])
213       if p then
214         res.stretch, res.stretch_order = parse_dimen(p)
215       end
216       if m then
217         res.shrink, res.shrink_order = parse_dimen(m)
218       end
219     end
220     return res
221   end
222
223   local function dump_skip(s)
224     print(("%s+%s<%s>-%s<%s>"):format(
225       s.width or 0, s.stretch or 0, s.stretch_order or 0,
226       s.shrink or 0, s.shrink_order or 0))
227   end
228
229   luatexja.base.to_dimen = to_dimen
230   luatexja.base.dump_skip = dump_skip
231   luatexja.base.to_skip = to_skip
232 end
233
234 -------------------- Virtual table for LaTeX counters
235 -- not used in current LuaTeX-ja
236 do
237 --! ixbase.counter と同じ
238   counter = {}
239   local mt_counter = {}
240   setmetatable(counter, mt_counter)
241
242   function mt_counter.__index(tbl, key)
243     return tex.count['c@'..key]
244   end
245   function mt_counter.__newindex(tbl, key, val)
246     tex.count['c@'..key] = val
247   end
248   luatexja.base.counter = counter
249
250 --! ixbase.length は tex.skip と全く同じなので不要.
251 end
252
253 -------------------- Number handling in TeX source
254 do
255
256   local tok_escape = token.create("ltj@@q@escape")
257   local tok_num = token.create("ltj@@q@escapenum")
258   local c_id_assign_int = token.command_id("assign_int")
259   local c_id_char_given = token.command_id("char_given")
260
261   local function error_scan()
262     package_error("luatexja",
263       "Missing number of a permitted form, treated as zero",
264       "A number should have been here; I inserted '0'.")
265   end
266
267   local function get_expd_next()
268     local next = token.get_next()
269     while token.is_expandable(next) do
270       token.expand(next)
271       next = token.get_next()
272     end
273     return next
274   end
275
276   local function grab_decimal(next, res)
277     table.insert(res, next)
278     while true do
279       next = get_expd_next()
280       if not (next[1] == 12 and 0x30 <= next[2] and next[2] <= 0x39) then
281         break
282       end
283       table.insert(res, next)
284     end
285     if next[1] == 10 then next = nil end
286     return true, next
287   end
288
289   local function grab_hexa(next, res)
290     local ok = false
291     table.insert(res, next)
292     while true do
293       next = get_expd_next()
294       if not ((next[1] == 12 and (0x30 <= next[2] and next[2] <= 0x39)) or
295               ((next[1] == 12 or next[1] == 11) and
296                (0x41 <= next[2] and next[2] <= 0x46))) then
297         break
298       end
299       ok = true
300       table.insert(res, next)
301     end
302     if next[1] == 10 then next = nil end
303     return ok, next
304   end
305
306   local function grab_octal(next, res)
307     local ok = false
308     table.insert(res, next)
309     while true do
310       next = get_expd_next()
311       if not (next[1] == 12 and (0x30 <= next[2] and next[2] <= 0x37)) then
312         break
313       end
314       ok = true
315       table.insert(res, next)
316     end
317     if next[1] == 10 then next = nil end
318     return ok, next
319   end
320
321   local function grab_charnum(next, res)
322     table.insert(res, next)
323     next = token.get_next()
324     table.insert(res, next)
325     next = get_expd_next()
326     if next[1] == 10 then next = nil end
327     return true, next
328   end
329
330   local function scan_with(delay, scanner)
331     local function proc()
332       if delay ~= 0 then
333         if delay > 0 then delay = delay - 1 end
334         return token.get_next()
335       else
336         local cont, back = scanner()
337         if not cont then
338           ltb.remove_from_callback("token_filter", "ltj@grab@num")
339         end
340         return back
341       end
342     end
343     ltb.add_to_callback("token_filter", proc, "ltj@grab@num", 1)
344   end
345
346   local function scan_brace()
347     scan_with(1, function()
348       local next = token.get_next()
349       if next[1] == 1 then
350         return false, { tok_escape, next }
351       elseif next[1] == 10 then
352         return true, { next }
353       else
354         return false, { next }
355       end
356     end)
357   end
358
359   local function scan_number()
360     scan_with(1, function()
361       local next = get_expd_next()
362       local res, ok = { tok_num }, false
363       while true do
364         if next[1] == 12 and (next[2] == 0x2B or next[2] == 0x2D) then
365           table.insert(res, next)
366         elseif next[1] ~= 10 then
367           break
368         end
369         next = get_expd_next()
370       end
371       if next[1] == 12 and 0x30 <= next[2] and next[2] <= 0x39 then
372         ok, next = grab_decimal(next, res)
373       elseif next[1] == 12 and next[2] == 0x22 then
374         ok, next = grab_hexa(next, res)
375       elseif next[1] == 12 and next[2] == 0x27 then
376         ok, next = grab_octal(next, res)
377       elseif next[1] == 12 and next[2] == 0x60 then
378         ok, next = grab_charnum(next, res)
379       elseif next[1] == c_id_assign_int or next[1] == c_id_char_given then
380         table.insert(res, next)
381         ok, next = true, nil
382       end
383       if ok then
384          table.insert(res, tok_num)
385       else
386          error_scan()
387          res = { tok_escape }
388       end
389        if next then table.insert(res, next) end
390        return false, res
391     end)
392   end
393
394   luatexja.base.scan_brace = scan_brace
395   luatexja.base.scan_number = scan_number
396 end
397
398 -------------------- TeX register allocation
399 -- not used in current LuaTeX-ja
400
401 do
402   local cmod_base_count = token.create('ltj@@count@zero')[2]
403   local cmod_base_attr = token.create('ltj@@attr@zero')[2]
404   local cmod_base_dimen = token.create('ltj@@dimen@zero')[2]
405   local cmod_base_skip = token.create('ltj@@skip@zero')[2]
406
407   local function const_number(name)
408     if name:sub(1, 1) == '\\' then name = name:sub(2) end
409     return token.create(name)[2]
410   end
411
412   local function count_number(name)
413     if name:sub(1, 1) == '\\' then name = name:sub(2) end
414     return token.create(name)[2] - cmod_base_count
415   end
416
417   local function attribute_number(name)
418     if name:sub(1, 1) == '\\' then name = name:sub(2) end
419     return token.create(name)[2] - cmod_base_attr
420   end
421
422   local function dimen_number(name)
423     if name:sub(1, 1) == '\\' then name = name:sub(2) end
424     return token.create(name)[2] - cmod_base_dimen
425   end
426
427   local function skip_number(name)
428     if name:sub(1, 1) == '\\' then name = name:sub(2) end
429     return token.create(name)[2] - cmod_base_skip
430   end
431
432   luatexja.base.const_number = const_number
433   luatexja.base.count_number = count_number
434   luatexja.base.attribute_number = attribute_number
435   luatexja.base.dimen_number = dimen_number
436   luatexja.base.skip_number = skip_number
437 end
438
439 -------------------- mock of debug logger
440 if not debug or debug == _G.debug then
441   local function no_op() end
442   debug = no_op
443   package_debug = no_op
444   show_term = no_op
445   show_log = no_op
446   function debug_logger()
447     return no_op
448   end
449 end
450
451 -------------------- getting next token
452 local cstemp = nil
453 local function get_cs(s)
454    cstemp = token.csname_name(token.get_next())
455    tex.sprint(cat_lp,'\\' .. s)
456 end
457 luatexja.base.get_cs = get_cs
458
459 -------------------- common error message
460 do
461    local function in_unicode(c, admit_math)
462       local low = admit_math and -1 or 0
463       if type(c)~='number' or c<low or c>0x10FFFF then
464          local s = 'A character number must be between ' .. tostring(low) 
465             .. ' and 0x10ffff.\n'
466             .. (admit_math and "(-1 is used for denoting `math boundary')\n" or '')
467             .. 'So I changed this one to zero.'
468          package_error('luatexja',
469                             'bad character code (' .. tostring(c) .. ')', s)
470          c=0
471       end
472       return c
473    end
474    luatexja.base.in_unicode = in_unicode
475 end
476
477 -------------------- cache management
478 -- load_cache (filename, outdate)
479 --   * filename: without suffix '.lua'
480 --   * outdate(t): return true iff the cache is outdated
481 --   * return value: non-nil iff the cache is up-to-date
482 -- save_cache (filename, t): no return value
483 -- save_cache_luc (filename, t): no return value
484 --   save_cache always calls save_cache_luc. 
485 --   But sometimes we want to create only the precompiled cache,
486 --   when its 'text' version is already present in LuaTeX-ja distribution.
487
488 require('lualibs-lpeg') -- string.split
489 require('lualibs-os')   -- os.type
490
491 do
492    local kpse_var_value = kpse.var_value
493    local path, pathtmp = kpse_var_value("TEXMFVAR")
494    pathtmp = kpse_var_value("TEXMFSYSVAR")
495    if pathtmp then path = (path and path .. ';' or '') .. pathtmp end
496    pathtmp = kpse_var_value("TEXMFCACHE")
497    if pathtmp then path = (path and path .. ';' or '') .. pathtmp end
498
499    if os.type~='windows' then path = string.gsub(path, ':', ';') end
500    path = table.unique(string.split(path, ';'))
501
502    local cache_dir = '/luatexja'
503    local find_file = kpse.find_file
504    local join, isreadable = file.join, file.isreadable
505    local tofile, serialize = table.tofile, table.serialize
506    local luc_suffix = jit and '.lub' or '.luc'
507
508    -- determine save path
509    local savepath = ''
510    for _,v in pairs(path) do
511       local testpath =  join(v, cache_dir)
512       if not lfs.isdir(testpath) then dir.mkdirs(testpath) end
513       if lfs.isdir(testpath) then savepath = testpath; break end
514    end
515
516    save_cache_luc = function (filename, t, serialized)
517       local fullpath = savepath .. '/' ..  filename .. luc_suffix
518       local s = serialized or serialize(t, 'return', false)
519       if s then
520          local sa = load(s)
521          local f = io.open(fullpath, 'wb')
522          if f and sa then 
523             f:write(string.dump(sa, true)) 
524             texio.write('(save cache: ' .. fullpath .. ')')
525          end
526          f:close()
527       end
528    end
529
530    save_cache = function (filename, t)
531       local fullpath = savepath .. '/' ..  filename .. '.lua'
532       local s = serialize(t, 'return', false)
533       if s then
534          local f = io.open(fullpath, 'w')
535          if f then 
536             f:write(s) 
537             texio.write('(save cache: ' .. fullpath .. ')')
538          end
539          f:close()
540          save_cache_luc(filename, t, s)
541       end
542    end
543
544    local function load_cache_a (filename, outdate)
545       local result
546       for _,v in pairs(path) do
547          local fn = join(v, cache_dir, filename)
548          if isreadable(fn) then 
549             texio.write('(load cache: ' .. fn .. ')')
550             result = loadfile(fn)
551             result = result and result(); break
552          end
553       end
554       if (not result) or outdate(result) then 
555          return nil 
556       else 
557          return result 
558       end
559    end
560    
561    load_cache = function (filename, outdate)
562       local r = load_cache_a(filename ..  luc_suffix, outdate)
563       if r then 
564          return r
565       else
566          local r = load_cache_a(filename .. '.lua', outdate)
567          if r then save_cache_luc(filename, r) end -- update the precompiled cache
568          return r
569       end
570    end
571
572    luatexja.base.load_cache = load_cache
573    luatexja.base.save_cache_luc = save_cache_luc
574    luatexja.base.save_cache = save_cache
575 end
576
577 luatexja.base._error_set_break = _error_set_break
578 luatexja.base._error_set_message = _error_set_message
579 luatexja.base._error_show = _error_show
580 luatexja.base._generic_warn_info = _generic_warn_info
581
582 luatexja.base.package_error = package_error
583 luatexja.base.package_warning = package_warning
584 luatexja.base.package_warning_no_line = package_warning_no_line
585 luatexja.base.package_info = package_info
586 luatexja.base.package_info_no_line = package_info_no_line
587
588 luatexja.base.generic_error = generic_error
589 luatexja.base.generic_warning = generic_warning
590 luatexja.base.generic_warning_no_line = generic_warning_no_line
591 luatexja.base.generic_info = generic_info
592 luatexja.base.generic_info_no_line = generic_info_no_line
593
594 luatexja.base.ltj_warning_no_line = ltj_warning_no_line
595 luatexja.base.ltj_error = ltj_error
596
597 luatexja.base.debug = debug
598 luatexja.base.package_debug = package_debug
599 luatexja.base.debug_logger = debug_logger
600 luatexja.base.show_term = show_term
601 luatexja.base.show_log = show_log
602
603 -------------------- all done
604 -- EOF