1if not modules then modules = { } end modules ['l-macros'] = {
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
10
11
12
13
14local S, P, R, V, C, Cs, Cc, Ct, Carg = lpeg.S, lpeg.P, lpeg.R, lpeg.V, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.Ct, lpeg.Carg
15local lpegmatch = lpeg.match
16local concat = table.concat
17local format, sub, match = string.format, string.sub, string.match
18local next, load, type = next, load, type
19
20local newline = S("\n\r")^1
21local continue = P("\\") * newline
22local whitespace = S(" \t\n\r")
23local spaces = S(" \t") + continue
24local nametoken = R("az","AZ","__","09")
25local name = nametoken^1
26local body = ((continue/"" + 1) - newline)^1
27local lparent = P("(")
28local rparent = P(")")
29local noparent = 1 - (lparent + rparent)
30local nested = P { lparent * (noparent + V(1))^0 * rparent }
31local escaped = P("\\") * P(1)
32local squote = P("'")
33local dquote = P('"')
34local quoted = dquote * (escaped + (1-dquote))^0 * dquote
35 + squote * (escaped + (1-squote))^0 * squote
36
37local arguments = lparent * Ct((Cs((nested+(quoted + 1 - S("),")))^1) + S(", "))^0) * rparent
38
39local macros = lua.macros or { }
40lua.macros = macros
41
42local patterns = { }
43local definitions = { }
44local resolve
45local subparser
46
47local report_lua = function(...)
48 if logs and logs.reporter then
49 report_lua = logs.reporter("system","lua")
50 report_lua(...)
51 else
52 print(format(...))
53 end
54end
55
56
57
58local safeguard = P("local") * whitespace^1 * name * (whitespace + P("="))
59
60resolve = safeguard + C(C(name) * (arguments^-1)) / function(raw,s,a)
61 local d = definitions[s]
62 if d then
63 if a then
64 local n = #a
65 local p = patterns[s][n]
66 if p then
67 local d = d[n]
68 for i=1,n do
69 a[i] = lpegmatch(subparser,a[i]) or a[i]
70 end
71 return lpegmatch(p,d,1,a) or d
72 else
73 return raw
74 end
75 else
76 return d[0] or raw
77 end
78 elseif a then
79 for i=1,#a do
80 a[i] = lpegmatch(subparser,a[i]) or a[i]
81 end
82 return s .. "(" .. concat(a,",") .. ")"
83 else
84 return raw
85 end
86end
87
88subparser = Cs((resolve + P(1))^1)
89
90local enddefine = P("#enddefine") / ""
91
92local beginregister = (C(name) * (arguments + Cc(false)) * C((1-enddefine)^1) * enddefine) / function(k,a,v)
93 local n = 0
94 if a then
95 n = #a
96 local pattern = P(false)
97 for i=1,n do
98 pattern = pattern + (P(a[i]) * Carg(1)) / function(t) return t[i] end
99 end
100 pattern = Cs((pattern + P(1))^1)
101 local p = patterns[k]
102 if not p then
103 p = { [0] = false, false, false, false, false, false, false, false, false }
104 patterns[k] = p
105 end
106 p[n] = pattern
107 end
108 local d = definitions[k]
109 if not d then
110 d = { a = a, [0] = false, false, false, false, false, false, false, false, false }
111 definitions[k] = d
112 end
113 d[n] = lpegmatch(subparser,v) or v
114 return ""
115end
116
117local register = (Cs(name) * (arguments + Cc(false)) * spaces^0 * Cs(body)) / function(k,a,v)
118 local n = 0
119 if a then
120 n = #a
121 local pattern = P(false)
122 for i=1,n do
123 pattern = pattern + (P(a[i]) * Carg(1)) / function(t) return t[i] end
124 end
125 pattern = Cs((pattern + P(1))^1)
126 local p = patterns[k]
127 if not p then
128 p = { [0] = false, false, false, false, false, false, false, false, false }
129 patterns[k] = p
130 end
131 p[n] = pattern
132 end
133 local d = definitions[k]
134 if not d then
135 d = { a = a, [0] = false, false, false, false, false, false, false, false, false }
136 definitions[k] = d
137 end
138 d[n] = lpegmatch(subparser,v) or v
139 return ""
140end
141
142local unregister = (C(name) * spaces^0 * (arguments + Cc(false))) / function(k,a)
143 local n = 0
144 if a then
145 n = #a
146 local p = patterns[k]
147 if p then
148 p[n] = false
149 end
150 end
151 local d = definitions[k]
152 if d then
153 d[n] = false
154 end
155 return ""
156end
157
158local begindefine = (P("begindefine") * spaces^0 / "") * beginregister
159local define = (P("define" ) * spaces^0 / "") * register
160local undefine = (P("undefine" ) * spaces^0 / "") * unregister
161
162local parser = Cs( ( ( (P("#")/"") * (define + begindefine + undefine) * (newline^0/"") ) + resolve + P(1) )^0 )
163
164function macros.reset()
165 definitions = { }
166 patterns = { }
167end
168
169function macros.showdefinitions()
170
171 for name, list in table.sortedhash(definitions) do
172 local arguments = list.a
173 if arguments then
174 arguments = "(" .. concat(arguments,",") .. ")"
175 else
176 arguments = ""
177 end
178 print("macro: " .. name .. arguments)
179 for i=0,#list do
180 local l = list[i]
181 if l then
182 print(" " .. l)
183 end
184 end
185 end
186end
187
188function macros.resolvestring(str)
189 return lpegmatch(parser,str) or str
190end
191
192function macros.resolving()
193 return next(patterns)
194end
195
196local function reload(path,name,data)
197 local only = match(name,".-([^/]+)%.lua")
198 if only and only ~= "" then
199 local name = path .. "/" .. only
200 local f = io.open(name,"wb")
201 f:write(data)
202 f:close()
203 local f = loadfile(name)
204 os.remove(name)
205 return f
206 end
207end
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223local function reload(path,name,data)
224 if path and path ~= "" then
225 local only = string.match(name,".-([^/]+)%.lua")
226 if only and only ~= "" then
227 local name = path .. "/" .. only .. "-macro.lua"
228 local f = io.open(name,"wb")
229 if f then
230 f:write(data)
231 f:close()
232 local l = loadfile(name)
233 os.remove(name)
234 return l
235 end
236 end
237 end
238 return load(data,name)
239end
240
241local function loaded(name,trace,detail)
242
243 local f = io.open(name,"rb")
244 if not f then
245 return false, format("file '%s' not found",name)
246 end
247 local c = f:read("*a")
248 if not c then
249 return false, format("file '%s' is invalid",name)
250 end
251 f:close()
252 local n = lpegmatch(parser,c)
253 if trace then
254 if #n ~= #c then
255 report_lua("macros expanded in '%s' (%i => %i bytes)",name,#c,#n)
256 if detail then
257 report_lua()
258 report_lua(n)
259 report_lua()
260 end
261 elseif detail then
262 report_lua("no macros expanded in '%s'",name)
263 end
264 end
265
266
267
268
269 return reload(lfs and lfs.currentdir(),name,n)
270end
271
272macros.loaded = loaded
273
274function required(name,trace)
275 local filename = file.addsuffix(name,"lua")
276 local fullname = resolvers and resolvers.findfile(filename) or filename
277 if not fullname or fullname == "" then
278 return false
279 end
280 local codeblob = package.loaded[fullname]
281 if codeblob then
282 return codeblob
283 end
284 local code, message = loaded(fullname,macros,trace,trace)
285 if type(code) == "function" then
286 code = code()
287 else
288 report_lua("error when loading '%s'",fullname)
289 return false, message
290 end
291 if code == nil then
292 code = false
293 end
294 package.loaded[fullname] = code
295 return code
296end
297
298macros.required = required
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363 |