luat-mac.lua /size: 16 Kb    last modification: 2020-07-01 14:35
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
luat-mac
'
]
=
{
2
version
=
1
.
001
,
3
comment
=
"
companion to luat-lib.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 9
-- Sometimes we run into situations like:
10
--
11
-- \def\foo#1{\expandafter\def\csname#1\endcsname}
12
--
13
-- As this confuses the parser, the following should be used instead:
14
--
15
-- \def\foo#1{\expandafter\normaldef\csname#1\endcsname}
16 17
local
P
,
V
,
S
,
R
,
C
,
Cs
,
Cmt
,
Carg
=
lpeg
.
P
,
lpeg
.
V
,
lpeg
.
S
,
lpeg
.
R
,
lpeg
.
C
,
lpeg
.
Cs
,
lpeg
.
Cmt
,
lpeg
.
Carg
18
local
lpegmatch
,
patterns
=
lpeg
.
match
,
lpeg
.
patterns
19 20
local
insert
,
remove
=
table
.
insert
,
table
.
remove
21
local
rep
,
sub
=
string
.
rep
,
string
.
sub
22
local
setmetatable
=
setmetatable
23
local
filesuffix
=
file
.
suffix
24
local
convertlmxstring
=
lmx
and
lmx
.
convertstring
25
local
savedata
=
io
.
savedata
26 27
local
pushtarget
,
poptarget
=
logs
.
pushtarget
,
logs
.
poptarget
28 29
local
report_macros
=
logs
.
reporter
(
"
interface
"
,
"
macros
"
)
30 31
local
stack
,
top
,
n
,
hashes
=
{
}
,
nil
,
0
,
{
}
32 33
-- local function set(s)
34
-- if top then
35
-- n = n + 1
36
-- if n > 9 then
37
-- report_macros("number of arguments > 9, ignoring %s",s)
38
-- else
39
-- local ns = #stack
40
-- local h = hashes[ns]
41
-- if not h then
42
-- h = rep("#",2^(ns-1))
43
-- hashes[ns] = h
44
-- end
45
-- if s == "ignore" then
46
-- top[s] = ""
47
-- return h .. "0"
48
-- else
49
-- local m = h .. n
50
-- top[s] = m
51
-- return m
52
-- end
53
-- end
54
-- end
55
-- end
56 57
-- local function get(s)
58
-- if s == "ignore" then
59
-- return ""
60
-- else
61
-- if not top then
62
-- report_macros("keeping #%s, no stack",s)
63
-- return "#" .. s -- can be lua
64
-- end
65
-- local m = top[s]
66
-- if m then
67
-- return m
68
-- else
69
-- report_macros("keeping #%s, not on stack",s)
70
-- return "#" .. s -- quite likely an error
71
-- end
72
-- end
73
-- end
74 75
local
function
set
(
s
)
76
if
top
then
77
local
ns
=
#
stack
78
local
h
=
hashes
[
ns
]
79
if
not
h
then
80
h
=
rep
(
"
#
"
,
2
^
(
ns
-1
)
)
81
hashes
[
ns
]
=
h
82
end
83
if
s
=
=
"
ignore
"
then
84
return
h
.
.
"
-
"
85
else
86
n
=
n
+
1
87
if
n
>
9
then
88
report_macros
(
"
number of arguments > 9, ignoring %s
"
,
s
)
89
elseif
s
=
=
"
discard
"
then
90
top
[
s
]
=
"
"
91
return
h
.
.
"
0
"
92
else
93
local
m
=
h
.
.
n
94
top
[
s
]
=
m
95
return
m
96
end
97
end
98
end
99
end
100 101
local
function
get
(
s
)
102
if
s
=
=
"
ignore
"
or
s
=
=
"
discard
"
then
103
return
"
"
104
else
105
if
not
top
then
106
report_macros
(
"
keeping #%s, no stack
"
,
s
)
107
return
"
#
"
.
.
s
-- can be lua
108
end
109
local
m
=
top
[
s
]
110
if
m
then
111
return
m
112
else
113
report_macros
(
"
keeping #%s, not on stack
"
,
s
)
114
return
"
#
"
.
.
s
-- quite likely an error
115
end
116
end
117
end
118 119
local
function
push
(
)
120
top
=
{
}
121
n
=
0
122
local
s
=
stack
[
#
stack
]
123
if
s
then
124
setmetatable
(
top
,
{
__index
=
s
}
)
125
end
126
insert
(
stack
,
top
)
127
end
128 129
local
function
pop
(
)
130
top
=
remove
(
stack
)
131
end
132 133
local
leftbrace
=
P
(
"
{
"
)
-- will be in patterns
134
local
rightbrace
=
P
(
"
}
"
)
135
local
escape
=
P
(
"
\\
"
)
136 137
local
space
=
patterns
.
space
138
local
spaces
=
space
^
1
139
local
newline
=
patterns
.
newline
140
local
nobrace
=
1
-
leftbrace
-
rightbrace
141 142
local
longleft
=
leftbrace
-- P("(")
143
local
longright
=
rightbrace
-- P(")")
144
local
nolong
=
1
-
longleft
-
longright
145 146
local
utf8character
=
P
(
1
)
*
R
(
"
\128\191
"
)
^
1
-- unchecked but fast
147 148
local
name
=
(
R
(
"
AZ
"
,
"
az
"
)
+
utf8character
)
^
1
149
local
csname
=
(
R
(
"
AZ
"
,
"
az
"
)
+
S
(
"
@?!_:-*
"
)
+
utf8character
)
^
1
150
local
longname
=
(
longleft
/
"
"
)
*
(
nolong
^
1
)
*
(
longright
/
"
"
)
151
local
variable
=
P
(
"
#
"
)
*
Cs
(
name
+
longname
)
152
local
escapedname
=
escape
*
csname
153
local
definer
=
escape
*
(
P
(
"
def
"
)
+
S
(
"
egx
"
)
*
P
(
"
def
"
)
)
-- tex
154
local
setter
=
escape
*
P
(
"
set
"
)
*
(
P
(
"
u
"
)
^
-1
*
S
(
"
egx
"
)
^
-1
)
*
P
(
"
value
"
)
-- context specific
155
--- + escape * P("install") * (1-P("handler"))^1 * P("handler") -- context specific
156
local
startcode
=
P
(
"
\\starttexdefinition
"
)
-- context specific
157
local
stopcode
=
P
(
"
\\stoptexdefinition
"
)
-- context specific
158
local
anything
=
patterns
.
anything
159
local
always
=
patterns
.
alwaysmatched
160 161
local
definer
=
escape
*
(
P
(
"
u
"
)
^
-1
*
S
(
"
egx
"
)
^
-1
*
P
(
"
def
"
)
)
-- tex
162 163
-- The comment nilling can become an option but it nicely compensates the Lua
164
-- parsing here with less parsing at the TeX end. We keep lines so the errors
165
-- get reported all right, but comments are never seen there anyway. We keep
166
-- comment that starts inline as it can be something special with a % (at some
167
-- point we can do that as well, esp if we never use \% or `% somewhere
168
-- unpredictable). We need to skip comments anyway. Hm, too tricky, this
169
-- stripping as we can have Lua code etc.
170 171
local
commenttoken
=
P
(
"
%
"
)
172
local
crorlf
=
S
(
"
\n\r
"
)
173
----- commentline = commenttoken * ((Carg(1) * C((1-crorlf)^0))/function(strip,s) return strip and "" or s end)
174
local
commentline
=
commenttoken
*
(
(
1
-
crorlf
)
^
0
)
175
local
leadingcomment
=
(
commentline
*
crorlf
^
1
)
^
1
176
local
furthercomment
=
(
crorlf
^
1
*
commentline
)
^
1
177 178
local
pushlocal
=
always
/
push
179
local
poplocal
=
always
/
pop
180
local
declaration
=
variable
/
set
181
local
identifier
=
variable
/
get
182 183
local
argument
=
P
{
leftbrace
*
(
(
identifier
+
V
(
1
)
+
(
1
-
leftbrace
-
rightbrace
)
)
^
0
)
*
rightbrace
}
184 185
local
function
matcherror
(
str
,
pos
)
186
report_macros
(
"
runaway definition at: %s
"
,
sub
(
str
,
pos
-30
,
pos
)
)
187
end
188 189
local
csname_endcsname
=
P
(
"
\\csname
"
)
*
(
identifier
+
(
1
-
P
(
"
\\endcsname
"
)
)
)
^
1
190 191
local
grammar
=
{
"
converter
"
,
192
texcode
=
pushlocal
193
*
startcode
194
*
spaces
195
*
(
csname
*
spaces
)
^
1
-- new: multiple, new:csname instead of name
196
-- * (declaration + furthercomment + (1 - newline - space))^0
197
*
(
(
declaration
*
(
space
^
0
/
"
"
)
)
^
1
+
furthercomment
+
(
1
-
newline
-
space
)
)
^
0
-- accepts #a #b #c
198
*
V
(
"
texbody
"
)
199
*
stopcode
200
*
poplocal
,
201
texbody
=
(
202
leadingcomment
-- new per 2015-03-03 (ugly)
203
+
V
(
"
definition
"
)
204
+
identifier
205
+
V
(
"
braced
"
)
206
+
(
1
-
stopcode
)
207
)
^
0
,
208
definition
=
pushlocal
209
*
definer
210
*
spaces
^
0
211
*
escapedname
212
-- * (declaration + furthercomment + commentline + (1-leftbrace))^0
213
*
(
declaration
+
furthercomment
+
commentline
+
csname_endcsname
+
(
1
-
leftbrace
)
)
^
0
214
*
V
(
"
braced
"
)
215
*
poplocal
,
216
setcode
=
pushlocal
217
*
setter
218
*
argument
219
*
(
declaration
+
furthercomment
+
commentline
+
(
1
-
leftbrace
)
)
^
0
220
*
V
(
"
braced
"
)
221
*
poplocal
,
222
braced
=
leftbrace
223
*
(
V
(
"
definition
"
)
224
+
identifier
225
+
V
(
"
setcode
"
)
226
+
V
(
"
texcode
"
)
227
+
V
(
"
braced
"
)
228
+
furthercomment
229
+
leadingcomment
-- new per 2012-05-15 (message on mailing list)
230
+
nobrace
231
)
^
0
232
-- * rightbrace^-1, -- the -1 catches errors
233
*
(
rightbrace
+
Cmt
(
always
,
matcherror
)
)
,
234 235
pattern
=
leadingcomment
236
+
V
(
"
definition
"
)
237
+
V
(
"
setcode
"
)
238
+
V
(
"
texcode
"
)
239
+
furthercomment
240
+
anything
,
241 242
converter
=
V
(
"
pattern
"
)
^
1
,
243
}
244 245
local
parser
=
Cs
(
grammar
)
246 247
local
checker
=
P
(
"
%
"
)
*
(
1
-
newline
-
P
(
"
macros
"
)
)
^
0
248
*
P
(
"
macros
"
)
*
space
^
0
*
P
(
"
=
"
)
*
space
^
0
*
C
(
patterns
.
letter
^
1
)
249 250
-- maybe namespace
251 252
local
resolvers
=
resolvers
253 254
local
macros
=
{
}
255
resolvers
.
macros
=
macros
256 257
local
loadtexfile
=
resolvers
.
loadtexfile
258 259
function
macros
.
preprocessed
(
str
,
strip
)
260
return
lpegmatch
(
parser
,
str
,
1
,
strip
)
261
end
262 263
function
macros
.
convertfile
(
oldname
,
newname
)
-- beware, no testing on oldname == newname
264
local
data
=
loadtexfile
(
oldname
)
265
data
=
interfaces
.
preprocessed
(
data
)
or
"
"
-- interfaces not yet defined
266
savedata
(
newname
,
data
)
267
end
268 269
function
macros
.
version
(
data
)
270
return
lpegmatch
(
checker
,
data
)
271
end
272 273
-- the document variables hack is temporary
274 275
local
processors
=
{
}
276 277
function
processors
.
mkvi
(
str
,
filename
)
278
local
oldsize
=
#
str
279
str
=
lpegmatch
(
parser
,
str
,
1
,
true
)
or
str
280
pushtarget
(
"
logfile
"
)
281
report_macros
(
"
processed mkvi file %a, delta %s
"
,
filename
,
oldsize
-
#
str
)
282
poptarget
(
)
283
return
str
284
end
285 286
function
processors
.
mkix
(
str
,
filename
)
-- we could intercept earlier so that caching works better
287
if
not
document
then
-- because now we hash the string as well as the
288
document
=
{
}
289
end
290
if
not
document
.
variables
then
291
document
.
variables
=
{
}
292
end
293
local
oldsize
=
#
str
294
str
=
convertlmxstring
(
str
,
document
.
variables
,
false
)
or
str
295
pushtarget
(
"
logfile
"
)
296
report_macros
(
"
processed mkix file %a, delta %s
"
,
filename
,
oldsize
-
#
str
)
297
poptarget
(
)
298
return
str
299
end
300 301
function
processors
.
mkxi
(
str
,
filename
)
302
if
not
document
then
303
document
=
{
}
304
end
305
if
not
document
.
variables
then
306
document
.
variables
=
{
}
307
end
308
local
oldsize
=
#
str
309
str
=
convertlmxstring
(
str
,
document
.
variables
,
false
)
or
str
310
str
=
lpegmatch
(
parser
,
str
,
1
,
true
)
or
str
311
pushtarget
(
"
logfile
"
)
312
report_macros
(
"
processed mkxi file %a, delta %s
"
,
filename
,
oldsize
-
#
str
)
313
poptarget
(
)
314
return
str
315
end
316 317
processors
.
mklx
=
processors
.
mkvi
318
processors
.
mkxl
=
processors
.
mkiv
319 320
function
macros
.
processmk
(
str
,
filename
)
321
if
filename
then
322
local
suffix
=
filesuffix
(
filename
)
323
local
processor
=
processors
[
suffix
]
or
processors
[
lpegmatch
(
checker
,
str
)
]
324
if
processor
then
325
str
=
processor
(
str
,
filename
)
326
end
327
end
328
return
str
329
end
330 331
local
function
validvi
(
filename
,
str
)
332
local
suffix
=
filesuffix
(
filename
)
333
if
suffix
=
=
"
mkvi
"
or
suffix
=
=
"
mklx
"
then
334
return
true
335
else
336
local
check
=
lpegmatch
(
checker
,
str
)
337
return
check
=
=
"
mkvi
"
or
check
=
=
"
mklx
"
338
end
339
end
340 341
function
macros
.
processmkvi
(
str
,
filename
)
342
if
filename
and
filename
~
=
"
"
and
validvi
(
filename
,
str
)
then
343
local
oldsize
=
#
str
344
str
=
lpegmatch
(
parser
,
str
,
1
,
true
)
or
str
345
pushtarget
(
"
logfile
"
)
346
report_macros
(
"
processed mkvi file %a, delta %s
"
,
filename
,
oldsize
-
#
str
)
347
poptarget
(
)
348
end
349
return
str
350
end
351 352
macros
.
processmklx
=
macros
.
processmkvi
353 354
-- bonus
355 356
local
schemes
=
resolvers
.
schemes
357 358
if
schemes
then
359 360
local
function
handler
(
protocol
,
name
,
cachename
)
361
local
hashed
=
url
.
hashed
(
name
)
362
local
path
=
hashed
.
path
363
if
path
and
path
~
=
"
"
then
364
local
str
=
loadtexfile
(
path
)
365
if
validvi
(
path
,
str
)
then
366
-- already done automatically
367
savedata
(
cachename
,
str
)
368
else
369
local
result
=
lpegmatch
(
parser
,
str
,
1
,
true
)
or
str
370
pushtarget
(
"
logfile
"
)
371
report_macros
(
"
processed scheme %a, delta %s
"
,
filename
,
#
str
-
#
result
)
372
poptarget
(
)
373
savedata
(
cachename
,
result
)
374
end
375
end
376
return
cachename
377
end
378 379
schemes
.
install
(
'
mkvi
'
,
handler
,
1
)
380
schemes
.
install
(
'
mklx
'
,
handler
,
1
)
381 382
end
383 384
-- print(macros.preprocessed(
385
-- [[
386
-- \starttexdefinition unexpanded test #aa #bb #cc
387
-- test
388
-- \stoptexdefinition
389
-- ]]))
390 391
-- print(macros.preprocessed([[\checked \def \bla #bla{bla#{bla}}]]))
392
-- print(macros.preprocessed([[\checked \def \bla #bla#discard#foo{bla#{bla}+#ignore+bla#foo}]]))
393
-- print(macros.preprocessed([[\checked \def \bla #bla#ignore#foo{bla#{bla}+#ignore+bla#foo}]]))
394
-- print(macros.preprocessed([[\def\bla#bla{#{bla}bla}]]))
395
-- print(macros.preprocessed([[\def\blä#{blá}{blà:#{blá}}]]))
396
-- print(macros.preprocessed([[\def\blä#bla{blà:#bla}]]))
397
-- print(macros.preprocessed([[\setvalue{xx}#bla{blà:#bla}]]))
398
-- print(macros.preprocessed([[\def\foo#bar{\setvalue{xx#bar}{#bar}}]]))
399
-- print(macros.preprocessed([[\def\bla#bla{bla:#{bla}}]]))
400
-- print(macros.preprocessed([[\def\bla_bla#bla{bla:#bla}]]))
401
-- print(macros.preprocessed([[\def\test#oeps{test:#oeps}]]))
402
-- print(macros.preprocessed([[\def\test_oeps#oeps{test:#oeps}]]))
403
-- print(macros.preprocessed([[\def\test#oeps{test:#{oeps}}]]))
404
-- print(macros.preprocessed([[\def\test#{oeps:1}{test:#{oeps:1}}]]))
405
-- print(macros.preprocessed([[\def\test#{oeps}{test:#oeps}]]))
406
-- print(macros.preprocessed([[\def\x[#a][#b][#c]{\setvalue{\y{#a}\z{#b}}{#c}}]]))
407
-- print(macros.preprocessed([[\def\test#{oeps}{test:#oeps \halign{##\cr #oeps\cr}]]))
408
-- print(macros.preprocessed([[\def\test#{oeps}{test:#oeps \halign{##\cr #oeps\cr}}]]))
409
-- print(macros.preprocessed([[% test
410
-- \def\test#oeps{#oeps} % {test}
411
-- % test
412
--
413
-- % test
414
-- two
415
-- %test]]))
416
-- print(macros.preprocessed([[
417
-- \def\scrn_button_make_normal#namespace#current#currentparameter#text%
418
-- {\ctxlua{structures.references.injectcurrentset(nil,nil)}%
419
-- % \hbox attr \referenceattribute \lastreferenceattribute {\localframed[#namespace:#current]{#text}}}
420
-- \hbox attr \referenceattribute \lastreferenceattribute {\directlocalframed[#namespace:#current]{#text}}}
421
-- ]]))
422
--
423
-- print(macros.preprocessed([[
424
-- \def\definefoo[#name]%
425
-- {\setvalue{start#name}{\dostartfoo{#name}}}
426
-- \def\dostartfoo#name%
427
-- {\def\noexpand\next#content\expandafter\noexpand\csname stop#name\endcsname{#name : #content}%
428
-- \next}
429
-- \def\dostartfoo#name%
430
-- {\normalexpanded{\def\noexpand\next#content\expandafter\noexpand\csname stop#name\endcsname}{#name : #content}%
431
-- \next}
432
-- ]]))
433
--
434
-- print(macros.preprocessed([[
435
-- \def\dosomething#content{%%% {{
436
-- % { }{{ %%
437
-- \bgroup\italic#content\egroup
438
-- }
439
-- ]]))
440
--
441
-- print(macros.preprocessed([[
442
-- \unexpanded\def\start#tag#stoptag%
443
-- {\initialize{#tag}%
444
-- \normalexpanded
445
-- {\def\yes[#one]#two\csname\e!stop#stoptag\endcsname{\command_yes[#one]{#two}}%
446
-- \def\nop #one\csname\e!stop#stoptag\endcsname{\command_nop {#one}}}%
447
-- \doifelsenextoptional\yes\nop}
448
-- ]]))
449
--
450
-- print(macros.preprocessed([[
451
-- \normalexpanded{\long\def\expandafter\noexpand\csname\e!start\v!interactionmenu\endcsname[#tag]#content\expandafter\noexpand\csname\e!stop\v!interactionmenu\endcsname}%
452
-- {\def\currentinteractionmenu{#tag}%
453
-- \expandafter\settrue\csname\??menustate\interactionmenuparameter\c!category\endcsname
454
-- \setinteractionmenuparameter\c!menu{#content}}
455
-- ]]))
456
--
457
-- Just an experiment:
458
--
459
-- \catcode\numexpr"10FF25=\commentcatcode %% > 110000 is invalid
460
--
461
-- We could have a push/pop mechanism but binding to txtcatcodes
462
-- is okay too.
463 464
local
txtcatcodes
=
false
-- also signal and yet unknown
465 466
local
commentsignal
=
utf
.
char
(
0x10FF25
)
467 468
local
encodecomment
=
P
(
"
%%
"
)
/
commentsignal
--
469
----- encodepattern = Cs(((1-encodecomment)^0 * encodecomment)) -- strips but not nice for verbatim
470
local
encodepattern
=
Cs
(
(
encodecomment
+
1
)
^
0
)
471
local
decodecomment
=
P
(
commentsignal
)
/
"
%%%%
"
-- why doubles here?
472
local
decodepattern
=
Cs
(
(
decodecomment
+
1
)
^
0
)
473 474
function
macros
.
encodecomment
(
str
)
475
if
txtcatcodes
and
tex
.
catcodetable
=
=
txtcatcodes
then
476
return
lpegmatch
(
encodepattern
,
str
)
or
str
477
else
478
return
str
479
end
480
end
481 482
function
macros
.
decodecomment
(
str
)
-- normally not needed
483
return
txtcatcodes
and
lpegmatch
(
decodepattern
,
str
)
or
str
484
end
485 486
-- resolvers.macros.commentsignal = commentsignal
487
-- resolvers.macros.encodecommentpattern = encodepattern
488
-- resolvers.macros.decodecommentpattern = decodepattern
489 490
local
sequencers
=
utilities
.
sequencers
491
local
appendaction
=
sequencers
and
sequencers
.
appendaction
492 493
if
appendaction
then
494 495
local
textlineactions
=
resolvers
.
openers
.
helpers
.
textlineactions
496
local
textfileactions
=
resolvers
.
openers
.
helpers
.
textfileactions
497 498
appendaction
(
textfileactions
,
"
system
"
,
"
resolvers.macros.processmk
"
)
499
appendaction
(
textfileactions
,
"
system
"
,
"
resolvers.macros.processmkvi
"
)
500 501
function
macros
.
enablecomment
(
thecatcodes
)
502
if
not
txtcatcodes
then
503
txtcatcodes
=
thecatcodes
or
catcodes
.
numbers
.
txtcatcodes
504
appendaction
(
textlineactions
,
"
system
"
,
"
resolvers.macros.encodecomment
"
)
505
end
506
end
507 508
end
509