OSDN Git Service

Supported Japanese math font families by \DeclareSymbolFont etc.
[luatex-ja/luatexja.git] / src / luatexja / base.lua
1 --
2 -- luatexja/base.lua
3 --
4 luatexbase.provides_module({
5   name = 'luatexja.base',
6   date = '2011/04/01',
7   version = '0.1',
8   description = '',
9 })
10 module('luatexja.base', package.seeall)
11 local err, warn, info, log = luatexbase.errwarinf(_NAME)
12
13 local ltb = luatexbase
14 local tostring = tostring
15 local node, table, tex, token = node, table, tex, token
16
17 -------------------- TeX stream I/O
18 do
19
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 
25   function mprint(...)
26     local arg = {...}
27     local lines = {}
28     if type(arg[1]) == "number" then
29       table.insert(lines, arg[1])
30       table.remove(arg, 1)
31     end
32     for _, cnk in ipairs(arg) do
33       local ls = cnk:explode("\n")
34       if ls[#ls] == "" then
35         table.remove(ls, #ls)
36       end
37       for _, l in ipairs(ls) do
38         table.insert(lines, l)
39       end
40     end
41     return tex.print(unpack(lines))
42   end
43
44 end
45 -------------------- Handling of TeX values
46 do
47
48   local glue_spec_id = node.id("glue_spec")
49
50   local function copy_skip(s1, s2)
51     if not s1 then
52       s1 = node.new(glue_spec_id)
53     end
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
59     return s1
60   end
61
62 --! ixbase.to_dimen() と同じ
63   function to_dimen(val)
64     if val == nil then
65       return 0
66     elseif type(val) == "number" then
67       return val
68     else
69       return tex.sp(tostring(val))
70     end
71   end
72
73   local function parse_dimen(val)
74     val = tostring(val):lower()
75     local r, fil = val:match("([-.%d]+)fi(l*)")
76     if r then
77       val, fil = r.."pt", fil:len() + 1
78     else
79       fil = 0
80     end
81     return tex.sp(val), fil
82   end
83
84 --! ixbase.to_skip() と同じ
85   function to_skip(val)
86     if type(val) == "userdata" then
87       return val
88     end
89     local res = node.new(glue_spec_id)
90     if val == nil then
91       res.width = 0
92     elseif type(val) == "number" then
93       res.width = val
94     elseif type(val) == "table" then
95       copy_skip(res, val)
96     else
97       local t = tostring(val):lower():explode()
98       local w, p, m = t[1], t[3], t[5]
99       if t[2] == "minus" then
100         p, m = nil, t[3]
101       end
102       res.width = tex.sp(t[1])
103       if t[3] then
104         res.stretch, res.stretch_order = parse_dimen(t[3])
105       end
106       if t[5] then
107         res.shrink, res.shrink_order = parse_dimen(t[5])
108       end
109     end
110     return res
111   end
112
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))
117   end
118
119 end
120 -------------------- Virtual table for LaTeX counters
121 do
122
123 --! ixbase.counter と同じ
124   counter = {}
125   local mt_counter = {}
126   setmetatable(counter, mt_counter)
127
128   function mt_counter.__index(tbl, key)
129     return tex.count['c@'..key]
130   end
131   function mt_counter.__newindex(tbl, key, val)
132     tex.count['c@'..key] = val
133   end
134
135 --! ixbase.length は tex.skip と全く同じなので不要.
136
137 end
138 -------------------- Fully-expandable error messaging
139 do
140 --! LaTeX 形式のエラーメッセージ(\PackageError 等)を
141 --! Lua 関数の呼び出しで行う.
142
143   local LF = "\\n"
144   local err_break = ""
145   local err_main = ""
146   local err_help = ""
147
148   local function message_cont(str, c)
149     return str:gsub(err_break, LF .. c)
150   end
151   local function into_lines(str)
152     return str:gsub(err_break, LF):explode(LF)
153   end
154
155   function _error_set_break(str)
156     err_break = str
157   end
158
159   function _error_set_message(msgcont, main, help)
160     err_main = message_cont(main, msgcont)
161     err_help = into_lines(help)
162   end
163
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
169     tex.newlinechar = 10
170     tex.errorcontextlines = -1
171     tex.error(err_main, err_help)
172     tex.escapechar = escapechar
173     tex.newlinechar = newlinechar
174     tex.errorcontextlines = errorcontextlines
175   end
176
177   local message_a = "Type  H <return>  for immediate help"
178
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)
182     _error_show(true)
183   end
184
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
191     tex.newlinechar = -1
192     texio.write_nl(out, br..main..on_line.."."..br)
193     tex.newlinechar = newlinechar
194   end
195
196   function generic_warning(msgcont, main)
197     _generic_warn_info(msgcont, main, true, true)
198   end
199   function generic_warning_no_line(msgcont, main)
200     _generic_warn_info(msgcont, main, true, false)
201   end
202   function generic_info(msgcont, main)
203     _generic_warn_info(msgcont, main, false, true)
204   end
205   function generic_info_no_line(msgcont, main)
206     _generic_warn_info(msgcont, main, false, false)
207   end
208
209   function package_error(pkgname, main, help)
210     generic_error("("..pkgname.."                ",
211       "Package "..pkgname.." Error: "..main,
212       "See the "..pkgname.." package documentation for explanation.",
213       help)
214   end
215   function package_warning(pkgname, main)
216     generic_warning("("..pkgname.."                ",
217       "Package "..pkgname.." Warning: "..main)
218   end
219   function package_warning_no_line(pkgname, main)
220     generic_warning_no_line("("..pkgname.."                ",
221       "Package "..pkgname.." Warning: "..main)
222   end
223   function package_info(pkgname, main)
224     generic_info("("..pkgname.."             ",
225       "Package "..pkgname.." Info: "..main)
226   end
227   function package_info_no_line(pkgname, main)
228     generic_info_no_line("("..pkgname.."             ",
229       "Package "..pkgname.." Info: "..main)
230   end
231
232 end
233 -------------------- Number handling in TeX source
234 do
235
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")
240
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'.")
245   end
246
247   local function get_expd_next()
248     local next = token.get_next()
249     while token.is_expandable(next) do
250       token.expand(next)
251       next = token.get_next()
252     end
253     return next
254   end
255
256   local function grab_decimal(next, res)
257     table.insert(res, next)
258     while true do
259       next = get_expd_next()
260       if not (next[1] == 12 and 0x30 <= next[2] and next[2] <= 0x39) then
261         break
262       end
263       table.insert(res, next)
264     end
265     if next[1] == 10 then next = nil end
266     return true, next
267   end
268
269   local function grab_hexa(next, res)
270     local ok = false
271     table.insert(res, next)
272     while true do
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
277         break
278       end
279       ok = true
280       table.insert(res, next)
281     end
282     if next[1] == 10 then next = nil end
283     return ok, next
284   end
285
286   local function grab_octal(next, res)
287     local ok = false
288     table.insert(res, next)
289     while true do
290       next = get_expd_next()
291       if not (next[1] == 12 and (0x30 <= next[2] and next[2] <= 0x37)) then
292         break
293       end
294       ok = true
295       table.insert(res, next)
296     end
297     if next[1] == 10 then next = nil end
298     return ok, next
299   end
300
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
307     return true, next
308   end
309
310   local function scan_with(delay, scanner)
311     local function proc()
312       if delay ~= 0 then
313         if delay > 0 then delay = delay - 1 end
314         return token.get_next()
315       else
316         local cont, back = scanner()
317         if not cont then
318           ltb.remove_from_callback("token_filter", "ltj@grab@num")
319         end
320         return back
321       end
322     end
323     ltb.add_to_callback("token_filter", proc, "ltj@grab@num", 1)
324   end
325
326   function scan_brace()
327     scan_with(1, function()
328       local next = token.get_next()
329       if next[1] == 1 then
330         return false, { tok_escape, next }
331       elseif next[1] == 10 then
332         return true, { next }
333       else
334         return false, { next }
335       end
336     end)
337   end
338
339   function scan_number()
340     scan_with(1, function()
341       local next = get_expd_next()
342       local res, ok = { tok_num }, false
343       while true do
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
347           break
348         end
349         next = get_expd_next()
350       end
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)
361         ok, next = true, nil
362       end
363       if ok then
364          table.insert(res, tok_num)
365       else
366          error_scan()
367          res = { tok_escape }
368       end
369        if next then table.insert(res, next) end
370        return false, res
371     end)
372   end
373
374 end
375 -------------------- mock of debug logger
376
377 if not _M.debug or _M.debug == _G.debug then
378   local function no_op() end
379   debug = no_op
380   package_debug = no_op
381   show_term = no_op
382   show_log = no_op
383   function debug_logger()
384     return no_op
385   end
386 end
387
388 -------------------- all done
389 -- EOF