1if not modules then modules = { } end modules ['syst-lua'] = {
2 version = 1.001,
3 comment = "companion to syst-lua.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9local load, type, tonumber = load, type, tonumber
10local find, gsub = string.find, string.gsub
11local concat = table.concat
12local utfchar = utf.char
13local S, C, P, lpegmatch, lpegtsplitat = lpeg.S, lpeg.C, lpeg.P, lpeg.match, lpeg.tsplitat
14
15local xmath = xmath or math
16local xcomplex = xcomplex or { }
17
18
19local scancmdchr = token.scancmdchrexpanded
20local scantoken = token.scantoken
21local getcsname = token.getcsname
22
23local cmd = tokens.commands
24local letter_code = cmd.letter
25local other_char_code = cmd.other_char
26local spacer_code = cmd.spacer
27local other_char_code = cmd.other_char
28local relax_code = cmd.relax
29local register_int_code = cmd.register_int
30local internal_int_code = cmd.internal_int
31local register_dimen_code = cmd.register_dimen
32local internal_dimen_code = cmd.internal_dimen
33local register_glue_code = cmd.register_glue
34local internal_glue_code = cmd.internal_glue
35local register_toks_code = cmd.register_toks
36local internal_toks_code = cmd.internal_toks
37local char_given_code = cmd.char_given
38local math_given_code = cmd.math_given
39local xmath_given_code = cmd.xmath_given
40local some_item_code = cmd.some_item
41
42local getdimen = tex.getdimen
43local getglue = tex.getglue
44local getcount = tex.getcount
45local gettoks = tex.gettoks
46local gettex = tex.get
47
48local context = context
49commands = commands or { }
50local commands = commands
51local context = context
52local implement = interfaces.implement
53local dimenfactors = number.dimenfactors
54
55local ctx_protected_cs = context.protected.cs
56local ctx_firstoftwoarguments = context.firstoftwoarguments
57local ctx_secondoftwoarguments = context.secondoftwoarguments
58local ctx_firstofoneargument = context.firstofoneargument
59local ctx_gobbleoneargument = context.gobbleoneargument
60
61local boolean_value <const> = tokens.values.boolean
62
63implement {
64 name = "writestatus",
65 arguments = "2 arguments",
66 actions = logs.status,
67}
68
69function commands.doifelse(b)
70 if b then
71 ctx_firstoftwoarguments()
72 else
73 ctx_secondoftwoarguments()
74 end
75end
76
77function commands.doifelsesomething(b)
78 if b and b ~= "" then
79 ctx_firstoftwoarguments()
80 else
81 ctx_secondoftwoarguments()
82 end
83end
84
85function commands.doif(b)
86 if b then
87 ctx_firstofoneargument()
88 else
89 ctx_gobbleoneargument()
90 end
91end
92
93function commands.doifsomething(b)
94 if b and b ~= "" then
95 ctx_firstofoneargument()
96 else
97 ctx_gobbleoneargument()
98 end
99end
100
101function commands.doifnot(b)
102 if b then
103 ctx_gobbleoneargument()
104 else
105 ctx_firstofoneargument()
106 end
107end
108
109function commands.doifnotthing(b)
110 if b and b ~= "" then
111 ctx_gobbleoneargument()
112 else
113 ctx_firstofoneargument()
114 end
115end
116
117commands.testcase = commands.doifelse
118
119function commands.boolcase(b)
120 context(b and 1 or 0)
121end
122
123function commands.doifelsespaces(str)
124 if find(str,"^ +$") then
125 ctx_firstoftwoarguments()
126 else
127 ctx_secondoftwoarguments()
128 end
129end
130
131local pattern = lpeg.patterns.validdimen
132
133function commands.doifelsedimenstring(str)
134 if lpegmatch(pattern,str) then
135 ctx_firstoftwoarguments()
136 else
137 ctx_secondoftwoarguments()
138 end
139end
140
141local p_first = C((1-P(",")-P(-1))^0)
142
143implement {
144 name = "firstinset",
145 arguments = "string",
146 actions = function(str) context(lpegmatch(p_first,str or "")) end,
147 public = true,
148}
149
150implement {
151 name = "ntimes",
152 arguments = { "string", "integer" },
153 actions = { string.rep, context }
154}
155
156implement {
157 name = "execute",
158 arguments = "string",
159 actions = os.execute
160}
161
162implement {
163 name = "doifelsesame",
164 arguments = "2 strings",
165 actions = function(a,b)
166 if a == b then
167 ctx_firstoftwoarguments()
168 else
169 ctx_secondoftwoarguments()
170 end
171 end
172}
173
174implement {
175 name = "doifsame",
176 arguments = "2 strings",
177 actions = function(a,b)
178 if a == b then
179 ctx_firstofoneargument()
180 else
181 ctx_gobbleoneargument()
182 end
183 end
184}
185
186implement {
187 name = "doifnotsame",
188 arguments = "2 strings",
189 actions = function(a,b)
190 if a == b then
191 ctx_gobbleoneargument()
192 else
193 ctx_firstofoneargument()
194 end
195 end
196}
197
198
199
200
201
202
203
204
205do
206
207 local result = { "return " }
208 local word = { }
209 local r = 1
210 local w = 0
211
212 local report = logs.reporter("system","expression")
213
214 local function unexpected(c)
215 report("unexpected token %a",c)
216 end
217
218 local function unexpected(c)
219 report("unexpected token %a",c)
220 end
221
222 local function expression()
223 local w = 0
224 local r = 1
225 while true do
226 local n, i = scancmdchr()
227 if n == letter_code then
228 w = w + 1 ; word[w] = utfchar(i)
229 else
230 if w > 0 then
231
232 local s = concat(word,"",1,w)
233 local d = dimenfactors[s]
234 if d then
235 r = r + 1 ; result[r] = "*"
236 r = r + 1 ; result[r] = 1/d
237 else
238 if xmath[s] then
239 r = r + 1 ; result[r] = "xmath."
240 elseif xcomplex[s] then
241 r = r + 1 ; result[r] = "xcomplex."
242 end
243 r = r + 1 ; result[r] = s
244 end
245 w = 0
246 end
247 if n == other_char_code then
248 r = r + 1 ; result[r] = utfchar(i)
249 elseif n == spacer_code then
250
251 elseif n == relax_code then
252 break
253 elseif n == register_int_code or n == internal_int_code then
254 r = r + 1 ; result[r] = getcount(i)
255 elseif n == register_dimen_code or n == internal_dimen_code then
256 r = r + 1 ; result[r] = getdimen(i)
257 elseif n == register_glue_code or n == n == register_dimen_code_glue_code then
258 r = r + 1 ; result[r] = getglue(i)
259 elseif n == register_toks_code or n == n == register_dimen_code_toks_code then
260 r = r + 1 ; result[r] = gettoks(i)
261 elseif n == char_given_code or n == math_given_code or n == xmath_given_code then
262 r = r + 1 ; result[r] = i
263 elseif n == some_item_code then
264 local n = getcsname(t)
265 if n then
266 local s = gettex(n)
267 if s then
268 r = r + 1 ; result[r] = s
269 else
270 unexpected(c)
271 end
272 else
273 unexpected(c)
274 end
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290 else
291 unexpected(c)
292 end
293 end
294 end
295 local code = concat(result,"",1,r)
296 local func = load(code)
297 if type(func) == "function" then
298 context(func())
299 else
300 report("invalid lua %a",code)
301 end
302 end
303
304 implement {
305 public = true,
306 name = "expression",
307 actions = expression,
308 }
309
310end
311
312do
313
314 interfaces.implement {
315 name = "iflua",
316 public = true,
317 usage = "condition",
318 arguments = "string",
319 actions = function(s)
320 local c = load("return(" .. s .. ")")
321 return boolean_code, (c and c()) and true or false
322 end,
323 }
324
325end
326
327do
328
329
330
331
332
333 local channels = { }
334 local lastdone = { }
335
336 local findbinfile = resolvers.findbinfile
337 local loadbinfile = resolvers.loadbinfile
338 local opentexfile = resolvers.opentexfile
339
340 local scaninteger = tokens.scanners.integer
341 local scankeyword = tokens.scanners.keyword
342 local scanstring = tokens.scanners.string
343 local scancsname = tokens.scanners.csname
344
345 local setmacro = tokens.setters.macro
346 local rlncatcodes = tex.rlncatcodes
347 local texgetcount = tex.getcount
348
349 local bytes = string.bytes
350 local getcatcode = tex.getcatcode
351
352 local char, concat = string.char, table.concat
353
354
355
356
357
358 local t = { }
359 local l = 0
360
361 implement {
362 name = "openin",
363 public = true,
364 usage = "value",
365 actions = function()
366 local n = scaninteger()
367 scankeyword("=")
368 local s = scanstring(true)
369 local c = channels[n]
370 if c then
371 c:close()
372 end
373 local f = findbinfile(s,"tex")
374 if f then
375 channels[n] = opentexfile(f)
376 lastdone[n] = false
377 else
378 channels[n] = false
379 lastdone[n] = true
380 end
381 end,
382 }
383
384 implement {
385 name = "closein",
386 public = true,
387 usage = "value",
388 actions = function()
389 local n = scaninteger()
390 local c = channels[n]
391 if c then
392 c:close()
393 end
394 channels[n] = false
395 t = { }
396 end,
397 }
398
399
400
401
402
403 local readerror = function(what,n)
404 tex.error(string.formatters["too many %s brace tokens in read from channel %i"](what,n))
405 end
406
407 interfaces.implement {
408 name = "read",
409 public = true,
410 usage = "value",
411 actions = function(prefix)
412 local n = scaninteger()
413 local c = channels[n]
414 if scankeyword("line") and c then
415 c:gotoline(scaninteger())
416 end
417 scankeyword("to")
418 local m = scancsname(true)
419 local g = 0
420 local s
421 l = 0
422 if c then
423 while true do
424 local s = c and c:reader()
425 if s then
426 l = l + 1
427 s = gsub(s," *$","")
428 t[l] = s
429 for c in bytes(s) do
430 local cc = getcatcode(c)
431 if cc == 1 then
432 g = g + 1
433 elseif cc == 2 then
434 g = g - 1
435 end
436 end
437 if g <= 0 then
438 break
439 end
440 else
441 break
442 end
443 end
444 if g > 0 then
445 readerror("left",n)
446 s = ""
447 elseif g < 0 then
448 readerror("right",n)
449 s = ""
450 elseif l == 0 then
451 if c:endoffile() then
452 lastdone[n] = true
453 s = "\\par"
454 else
455 s = ""
456 end
457 channels[n] = false
458 else
459 local e = texgetcount("endlinechar")
460 if e < 0 or e > 127 then
461 e = ""
462 else
463 e = " "
464 l = l + 1
465 t[l] = ""
466 end
467 s = concat(t, e, 1, l)
468 end
469 else
470 s = ""
471 end
472 setmacro(m, s, prefix)
473 end,
474 }
475
476
477
478
479
480 implement {
481 name = "readline",
482 public = true,
483 usage = "value",
484 actions = function(prefix)
485 local n = scaninteger()
486 local c = channels[n]
487 if scankeyword("line") and c then
488 c:gotoline(scaninteger())
489 end
490 scankeyword("to")
491 local m = scancsname(true)
492 local s = c and c:reader()
493 if s then
494 local e = texgetcount("endlinechar")
495 if e > 0 then
496 s = s .. char(e)
497 end
498 else
499 channels[n] = false
500 s = ""
501 end
502 setmacro(rlncatcodes, m, s, prefix)
503 end,
504 }
505
506 implement {
507 name = "readlinedirect",
508 public = true,
509 untraced = true,
510 actions = function(prefix)
511 local n = scaninteger()
512 local c = channels[n]
513 if scankeyword("line") and c then
514 c:gotoline(scaninteger())
515 end
516 local s = c and c:reader()
517 if s then
518 local e = texgetcount("endlinechar")
519 if e > 0 then
520 s = s .. char(e)
521 end
522 context(s)
523 else
524 channels[n] = false
525 end
526 end,
527 }
528
529
530
531
532 implement {
533 name = "ifeof",
534 public = true,
535 usage = "condition",
536 actions = function()
537 local n = scaninteger()
538 return boolean_value, not channels[n]
539 end,
540 }
541
542
543
544
545 local getnest = tex.getnest
546
547 implement {
548 name = "ifmvl",
549 public = true,
550 usage = "condition",
551 actions = function()
552 return boolean_value, getnest("ptr") == 0
553 end,
554 }
555
556end
557 |