lxml-lpt.lua /size: 53 Kb    last modification: 2020-07-01 14:35
1
if
not
modules
then
modules
=
{
}
end
modules
[
'
lxml-lpt
'
]
=
{
2
version
=
1
.
001
,
3
comment
=
"
this module is the basis for the lxml-* ones
"
,
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
-- e.ni is only valid after a filter run
10
-- todo: B/C/[get first match]
11 12
local
concat
,
remove
,
insert
=
table
.
concat
,
table
.
remove
,
table
.
insert
13
local
type
,
next
,
tonumber
,
tostring
,
setmetatable
,
load
,
select
=
type
,
next
,
tonumber
,
tostring
,
setmetatable
,
load
,
select
14
local
format
,
upper
,
lower
,
gmatch
,
gsub
,
find
,
rep
=
string
.
format
,
string
.
upper
,
string
.
lower
,
string
.
gmatch
,
string
.
gsub
,
string
.
find
,
string
.
rep
15
local
lpegmatch
,
lpegpatterns
=
lpeg
.
match
,
lpeg
.
patterns
16 17
local
setmetatableindex
=
table
.
setmetatableindex
18
local
formatters
=
string
.
formatters
-- no need (yet) as paths are cached anyway
19 20
-- beware, this is not xpath ... e.g. position is different (currently) and
21
-- we have reverse-sibling as reversed preceding sibling
22 23
--[[ldx-- 24<p>This module can be used stand alone but also inside <l n='mkiv'/> in 25which case it hooks into the tracker code. Therefore we provide a few 26functions that set the tracers. Here we overload a previously defined 27function.</p> 28<p>If I can get in the mood I will make a variant that is XSLT compliant 29but I wonder if it makes sense.</P> 30--ldx]]
--
31 32
--[[ldx-- 33<p>Expecially the lpath code is experimental, we will support some of xpath, but 34only things that make sense for us; as compensation it is possible to hook in your 35own functions. Apart from preprocessing content for <l n='context'/> we also need 36this module for process management, like handling <l n='ctx'/> and <l n='rlx'/> 37files.</p> 38 39<typing> 40a/b/c /*/c 41a/b/c/first() a/b/c/last() a/b/c/index(n) a/b/c/index(-n) 42a/b/c/text() a/b/c/text(1) a/b/c/text(-1) a/b/c/text(n) 43</typing> 44--ldx]]
--
45 46
local
trace_lpath
=
false
47
local
trace_lparse
=
false
48
local
trace_lprofile
=
false
49
local
report_lpath
=
logs
.
reporter
(
"
xml
"
,
"
lpath
"
)
50 51
if
trackers
then
52
trackers
.
register
(
"
xml.path
"
,
function
(
v
)
53
trace_lpath
=
v
54
end
)
55
trackers
.
register
(
"
xml.parse
"
,
function
(
v
)
56
trace_lparse
=
v
57
end
)
58
trackers
.
register
(
"
xml.profile
"
,
function
(
v
)
59
trace_lpath
=
v
60
trace_lparse
=
v
61
trace_lprofile
=
v
62
end
)
63
end
64 65
--[[ldx-- 66<p>We've now arrived at an interesting part: accessing the tree using a subset 67of <l n='xpath'/> and since we're not compatible we call it <l n='lpath'/>. We 68will explain more about its usage in other documents.</p> 69--ldx]]
--
70 71
local
xml
=
xml
72 73
local
lpathcalls
=
0
function
xml
.
lpathcalls
(
)
return
lpathcalls
end
74
local
lpathcached
=
0
function
xml
.
lpathcached
(
)
return
lpathcached
end
75 76
xml
.
functions
=
xml
.
functions
or
{
}
-- internal
77
local
functions
=
xml
.
functions
78 79
xml
.
expressions
=
xml
.
expressions
or
{
}
-- in expressions
80
local
expressions
=
xml
.
expressions
81 82
xml
.
finalizers
=
xml
.
finalizers
or
{
}
-- fast do-with ... (with return value other than collection)
83
local
finalizers
=
xml
.
finalizers
84 85
xml
.
specialhandler
=
xml
.
specialhandler
or
{
}
86
local
specialhandler
=
xml
.
specialhandler
87 88
lpegpatterns
.
xml
=
lpegpatterns
.
xml
or
{
}
89
local
xmlpatterns
=
lpegpatterns
.
xml
90 91
finalizers
.
xml
=
finalizers
.
xml
or
{
}
92
finalizers
.
tex
=
finalizers
.
tex
or
{
}
93 94
local
function
fallback
(
t
,
name
)
95
local
fn
=
finalizers
[
name
]
96
if
fn
then
97
t
[
name
]
=
fn
98
else
99
report_lpath
(
"
unknown sub finalizer %a
"
,
name
)
100
fn
=
function
(
)
end
101
end
102
return
fn
103
end
104 105
setmetatableindex
(
finalizers
.
xml
,
fallback
)
106
setmetatableindex
(
finalizers
.
tex
,
fallback
)
107 108
xml
.
defaultprotocol
=
"
xml
"
109 110
-- as xsl does not follow xpath completely here we will also
111
-- be more liberal especially with regards to the use of | and
112
-- the rootpath:
113
--
114
-- test : all 'test' under current
115
-- /test : 'test' relative to current
116
-- a|b|c : set of names
117
-- (a|b|c) : idem
118
-- ! : not
119
--
120
-- after all, we're not doing transformations but filtering. in
121
-- addition we provide filter functions (last bit)
122
--
123
-- todo: optimizer
124
--
125
-- .. : parent
126
-- * : all kids
127
-- / : anchor here
128
-- // : /**/
129
-- ** : all in between
130
--
131
-- so far we had (more practical as we don't transform)
132
--
133
-- {/test} : kids 'test' under current node
134
-- {test} : any kid with tag 'test'
135
-- {//test} : same as above
136 137
-- evaluator (needs to be redone, for the moment copied)
138 139
-- todo: apply_axis(list,notable) and collection vs single
140 141
local
apply_axis
=
{
}
142 143
apply_axis
[
'
root
'
]
=
function
(
list
)
144
local
collected
=
{
}
145
for
l
=
1
,
#
list
do
146
local
ll
=
list
[
l
]
147
local
rt
=
ll
148
while
ll
do
149
ll
=
ll
.
__p__
150
if
ll
then
151
rt
=
ll
152
end
153
end
154
collected
[
l
]
=
rt
155
end
156
return
collected
157
end
158 159
apply_axis
[
'
self
'
]
=
function
(
list
)
160
-- local collected = { }
161
-- for l=1,#list do
162
-- collected[l] = list[l]
163
-- end
164
-- return collected
165
return
list
166
end
167 168
apply_axis
[
'
child
'
]
=
function
(
list
)
169
local
collected
=
{
}
170
local
c
=
0
171
for
l
=
1
,
#
list
do
172
local
ll
=
list
[
l
]
173
local
dt
=
ll
.
dt
174
if
dt
then
-- weird that this is needed
175
local
n
=
#
dt
176
if
n
=
=
0
then
177
ll
.
en
=
0
178
elseif
n
=
=
1
then
179
local
dk
=
dt
[
1
]
180
if
dk
.
tg
then
181
c
=
c
+
1
182
collected
[
c
]
=
dk
183
dk
.
ni
=
1
-- refresh
184
dk
.
ei
=
1
185
ll
.
en
=
1
186
end
187
else
188
local
en
=
0
189
for
k
=
1
,
#
dt
do
190
local
dk
=
dt
[
k
]
191
if
dk
.
tg
then
192
c
=
c
+
1
193
en
=
en
+
1
194
collected
[
c
]
=
dk
195
dk
.
ni
=
k
-- refresh
196
dk
.
ei
=
en
197
end
198
end
199
ll
.
en
=
en
200
end
201
end
202
end
203
return
collected
204
end
205 206
local
function
collect
(
list
,
collected
,
c
)
207
local
dt
=
list
.
dt
208
if
dt
then
209
local
n
=
#
dt
210
if
n
=
=
0
then
211
list
.
en
=
0
212
elseif
n
=
=
1
then
213
local
dk
=
dt
[
1
]
214
if
dk
.
tg
then
215
c
=
c
+
1
216
collected
[
c
]
=
dk
217
dk
.
ni
=
1
-- refresh
218
dk
.
ei
=
1
219
c
=
collect
(
dk
,
collected
,
c
)
220
list
.
en
=
1
221
else
222
list
.
en
=
0
223
end
224
else
225
local
en
=
0
226
for
k
=
1
,
n
do
227
local
dk
=
dt
[
k
]
228
if
dk
.
tg
then
229
c
=
c
+
1
230
en
=
en
+
1
231
collected
[
c
]
=
dk
232
dk
.
ni
=
k
-- refresh
233
dk
.
ei
=
en
234
c
=
collect
(
dk
,
collected
,
c
)
235
end
236
end
237
list
.
en
=
en
238
end
239
end
240
return
c
241
end
242 243
apply_axis
[
'
descendant
'
]
=
function
(
list
)
244
local
collected
=
{
}
245
local
c
=
0
246
for
l
=
1
,
#
list
do
247
c
=
collect
(
list
[
l
]
,
collected
,
c
)
248
end
249
return
collected
250
end
251 252
local
function
collect
(
list
,
collected
,
c
)
253
local
dt
=
list
.
dt
254
if
dt
then
255
local
n
=
#
dt
256
if
n
=
=
0
then
257
list
.
en
=
0
258
elseif
n
=
=
1
then
259
local
dk
=
dt
[
1
]
260
if
dk
.
tg
then
261
c
=
c
+
1
262
collected
[
c
]
=
dk
263
dk
.
ni
=
1
-- refresh
264
dk
.
ei
=
1
265
c
=
collect
(
dk
,
collected
,
c
)
266
list
.
en
=
1
267
end
268
else
269
local
en
=
0
270
for
k
=
1
,
#
dt
do
271
local
dk
=
dt
[
k
]
272
if
dk
.
tg
then
273
c
=
c
+
1
274
en
=
en
+
1
275
collected
[
c
]
=
dk
276
dk
.
ni
=
k
-- refresh
277
dk
.
ei
=
en
278
c
=
collect
(
dk
,
collected
,
c
)
279
end
280
end
281
list
.
en
=
en
282
end
283
end
284
return
c
285
end
286 287
apply_axis
[
'
descendant-or-self
'
]
=
function
(
list
)
288
local
collected
=
{
}
289
local
c
=
0
290
for
l
=
1
,
#
list
do
291
local
ll
=
list
[
l
]
292
if
ll
.
special
~
=
true
then
-- catch double root
293
c
=
c
+
1
294
collected
[
c
]
=
ll
295
end
296
c
=
collect
(
ll
,
collected
,
c
)
297
end
298
return
collected
299
end
300 301
apply_axis
[
'
ancestor
'
]
=
function
(
list
)
302
local
collected
=
{
}
303
local
c
=
0
304
for
l
=
1
,
#
list
do
305
local
ll
=
list
[
l
]
306
while
ll
do
307
ll
=
ll
.
__p__
308
if
ll
then
309
c
=
c
+
1
310
collected
[
c
]
=
ll
311
end
312
end
313
end
314
return
collected
315
end
316 317
apply_axis
[
'
ancestor-or-self
'
]
=
function
(
list
)
318
local
collected
=
{
}
319
local
c
=
0
320
for
l
=
1
,
#
list
do
321
local
ll
=
list
[
l
]
322
c
=
c
+
1
323
collected
[
c
]
=
ll
324
while
ll
do
325
ll
=
ll
.
__p__
326
if
ll
then
327
c
=
c
+
1
328
collected
[
c
]
=
ll
329
end
330
end
331
end
332
return
collected
333
end
334 335
apply_axis
[
'
parent
'
]
=
function
(
list
)
336
local
collected
=
{
}
337
local
c
=
0
338
for
l
=
1
,
#
list
do
339
local
pl
=
list
[
l
]
.
__p__
340
if
pl
then
341
c
=
c
+
1
342
collected
[
c
]
=
pl
343
end
344
end
345
return
collected
346
end
347 348
apply_axis
[
'
attribute
'
]
=
function
(
list
)
349
return
{
}
350
end
351 352
apply_axis
[
'
namespace
'
]
=
function
(
list
)
353
return
{
}
354
end
355 356
apply_axis
[
'
following
'
]
=
function
(
list
)
-- incomplete
357
-- local collected, c = { }, 0
358
-- for l=1,#list do
359
-- local ll = list[l]
360
-- local p = ll.__p__
361
-- local d = p.dt
362
-- for i=ll.ni+1,#d do
363
-- local di = d[i]
364
-- if type(di) == "table" then
365
-- c = c + 1
366
-- collected[c] = di
367
-- break
368
-- end
369
-- end
370
-- end
371
-- return collected
372
return
{
}
373
end
374 375
apply_axis
[
'
preceding
'
]
=
function
(
list
)
-- incomplete
376
-- local collected = { }
377
-- local c = 0
378
-- for l=1,#list do
379
-- local ll = list[l]
380
-- local p = ll.__p__
381
-- local d = p.dt
382
-- for i=ll.ni-1,1,-1 do
383
-- local di = d[i]
384
-- if type(di) == "table" then
385
-- c = c + 1
386
-- collected[c] = di
387
-- break
388
-- end
389
-- end
390
-- end
391
-- return collected
392
return
{
}
393
end
394 395
apply_axis
[
'
following-sibling
'
]
=
function
(
list
)
396
local
collected
=
{
}
397
local
c
=
0
398
for
l
=
1
,
#
list
do
399
local
ll
=
list
[
l
]
400
local
p
=
ll
.
__p__
401
local
d
=
p
.
dt
402
for
i
=
ll
.
ni
+
1
,
#
d
do
403
local
di
=
d
[
i
]
404
if
type
(
di
)
=
=
"
table
"
then
405
c
=
c
+
1
406
collected
[
c
]
=
di
407
end
408
end
409
end
410
return
collected
411
end
412 413
apply_axis
[
'
preceding-sibling
'
]
=
function
(
list
)
414
local
collected
=
{
}
415
local
c
=
0
416
for
l
=
1
,
#
list
do
417
local
ll
=
list
[
l
]
418
local
p
=
ll
.
__p__
419
local
d
=
p
.
dt
420
for
i
=
1
,
ll
.
ni
-1
do
421
local
di
=
d
[
i
]
422
if
type
(
di
)
=
=
"
table
"
then
423
c
=
c
+
1
424
collected
[
c
]
=
di
425
end
426
end
427
end
428
return
collected
429
end
430 431
apply_axis
[
'
reverse-sibling
'
]
=
function
(
list
)
-- reverse preceding
432
local
collected
=
{
}
433
local
c
=
0
434
for
l
=
1
,
#
list
do
435
local
ll
=
list
[
l
]
436
local
p
=
ll
.
__p__
437
local
d
=
p
.
dt
438
for
i
=
ll
.
ni
-1
,
1
,
-1
do
439
local
di
=
d
[
i
]
440
if
type
(
di
)
=
=
"
table
"
then
441
c
=
c
+
1
442
collected
[
c
]
=
di
443
end
444
end
445
end
446
return
collected
447
end
448 449
apply_axis
[
'
auto-descendant-or-self
'
]
=
apply_axis
[
'
descendant-or-self
'
]
450
apply_axis
[
'
auto-descendant
'
]
=
apply_axis
[
'
descendant
'
]
451
apply_axis
[
'
auto-child
'
]
=
apply_axis
[
'
child
'
]
452
apply_axis
[
'
auto-self
'
]
=
apply_axis
[
'
self
'
]
453
apply_axis
[
'
initial-child
'
]
=
apply_axis
[
'
child
'
]
454 455
local
function
apply_nodes
(
list
,
directive
,
nodes
)
456
-- todo: nodes[1] etc ... negated node name in set ... when needed
457
-- ... currently ignored
458
local
maxn
=
#
nodes
459
if
maxn
=
=
3
then
--optimized loop
460
local
nns
=
nodes
[
2
]
461
local
ntg
=
nodes
[
3
]
462
if
not
nns
and
not
ntg
then
-- wildcard
463
if
directive
then
464
return
list
465
else
466
return
{
}
467
end
468
else
469
local
collected
=
{
}
470
local
c
=
0
471
local
m
=
0
472
local
p
=
nil
473
if
not
nns
then
-- only check tag
474
for
l
=
1
,
#
list
do
475
local
ll
=
list
[
l
]
476
local
ltg
=
ll
.
tg
477
if
ltg
then
478
if
directive
then
479
if
ntg
=
=
ltg
then
480
local
llp
=
ll
.
__p__
;
if
llp
~
=
p
then
p
=
llp
;
m
=
1
else
m
=
m
+
1
end
481
c
=
c
+
1
482
collected
[
c
]
=
ll
483
ll
.
mi
=
m
484
end
485
elseif
ntg
~
=
ltg
then
486
local
llp
=
ll
.
__p__
;
if
llp
~
=
p
then
p
=
llp
;
m
=
1
else
m
=
m
+
1
end
487
c
=
c
+
1
488
collected
[
c
]
=
ll
489
ll
.
mi
=
m
490
end
491
end
492
end
493
elseif
not
ntg
then
-- only check namespace
494
for
l
=
1
,
#
list
do
495
local
ll
=
list
[
l
]
496
local
lns
=
ll
.
rn
or
ll
.
ns
497
if
lns
then
498
if
directive
then
499
if
lns
=
=
nns
then
500
local
llp
=
ll
.
__p__
;
if
llp
~
=
p
then
p
=
llp
;
m
=
1
else
m
=
m
+
1
end
501
c
=
c
+
1
502
collected
[
c
]
=
ll
503
ll
.
mi
=
m
504
end
505
elseif
lns
~
=
nns
then
506
local
llp
=
ll
.
__p__
;
if
llp
~
=
p
then
p
=
llp
;
m
=
1
else
m
=
m
+
1
end
507
c
=
c
+
1
508
collected
[
c
]
=
ll
509
ll
.
mi
=
m
510
end
511
end
512
end
513
else
-- check both
514
for
l
=
1
,
#
list
do
515
local
ll
=
list
[
l
]
516
local
ltg
=
ll
.
tg
517
if
ltg
then
518
local
lns
=
ll
.
rn
or
ll
.
ns
519
local
ok
=
ltg
=
=
ntg
and
lns
=
=
nns
520
if
directive
then
521
if
ok
then
522
local
llp
=
ll
.
__p__
;
if
llp
~
=
p
then
p
=
llp
;
m
=
1
else
m
=
m
+
1
end
523
c
=
c
+
1
524
collected
[
c
]
=
ll
525
ll
.
mi
=
m
526
end
527
elseif
not
ok
then
528
local
llp
=
ll
.
__p__
;
if
llp
~
=
p
then
p
=
llp
;
m
=
1
else
m
=
m
+
1
end
529
c
=
c
+
1
530
collected
[
c
]
=
ll
531
ll
.
mi
=
m
532
end
533
end
534
end
535
end
536
return
collected
537
end
538
else
539
local
collected
=
{
}
540
local
c
=
0
541
local
m
=
0
542
local
p
=
nil
543
for
l
=
1
,
#
list
do
544
local
ll
=
list
[
l
]
545
local
ltg
=
ll
.
tg
546
if
ltg
then
547
local
lns
=
ll
.
rn
or
ll
.
ns
548
local
ok
=
false
549
for
n
=
1
,
maxn
,
3
do
550
local
nns
=
nodes
[
n
+
1
]
551
local
ntg
=
nodes
[
n
+
2
]
552
ok
=
(
not
ntg
or
ltg
=
=
ntg
)
and
(
not
nns
or
lns
=
=
nns
)
553
if
ok
then
554
break
555
end
556
end
557
if
directive
then
558
if
ok
then
559
local
llp
=
ll
.
__p__
;
if
llp
~
=
p
then
p
=
llp
;
m
=
1
else
m
=
m
+
1
end
560
c
=
c
+
1
561
collected
[
c
]
=
ll
562
ll
.
mi
=
m
563
end
564
elseif
not
ok
then
565
local
llp
=
ll
.
__p__
;
if
llp
~
=
p
then
p
=
llp
;
m
=
1
else
m
=
m
+
1
end
566
c
=
c
+
1
567
collected
[
c
]
=
ll
568
ll
.
mi
=
m
569
end
570
end
571
end
572
return
collected
573
end
574
end
575 576
local
quit_expression
=
false
577 578
local
function
apply_expression
(
list
,
expression
,
order
)
579
local
collected
=
{
}
580
local
c
=
0
581
quit_expression
=
false
582
for
l
=
1
,
#
list
do
583
local
ll
=
list
[
l
]
584
if
expression
(
list
,
ll
,
l
,
order
)
then
-- nasty, order alleen valid als n=1
585
c
=
c
+
1
586
collected
[
c
]
=
ll
587
end
588
if
quit_expression
then
589
break
590
end
591
end
592
return
collected
593
end
594 595
local
function
apply_selector
(
list
,
specification
)
596
if
xml
.
applyselector
then
597
apply_selector
=
xml
.
applyselector
598
return
apply_selector
(
list
,
specification
)
599
else
600
return
list
601
end
602
end
603 604
-- this one can be made faster but there are not that many conversions so it doesn't
605
-- really pay of
606 607
local
P
,
V
,
C
,
Cs
,
Cc
,
Ct
,
R
,
S
,
Cg
,
Cb
=
lpeg
.
P
,
lpeg
.
V
,
lpeg
.
C
,
lpeg
.
Cs
,
lpeg
.
Cc
,
lpeg
.
Ct
,
lpeg
.
R
,
lpeg
.
S
,
lpeg
.
Cg
,
lpeg
.
Cb
608 609
local
spaces
=
S
(
"
\n\r\t\f
"
)
^
0
610
local
lp_space
=
S
(
"
\n\r\t\f
"
)
611
local
lp_any
=
P
(
1
)
612
local
lp_noequal
=
P
(
"
!=
"
)
/
"
~=
"
+
P
(
"
<=
"
)
+
P
(
"
>=
"
)
+
P
(
"
==
"
)
613
local
lp_doequal
=
P
(
"
=
"
)
/
"
==
"
614
local
lp_or
=
P
(
"
|
"
)
/
"
or
"
615
local
lp_and
=
P
(
"
&
"
)
/
"
and
"
616 617
local
builtin
=
{
618
text
=
"
(ll.dt[1] or '')
"
,
-- fragile
619
content
=
"
ll.dt
"
,
620
name
=
"
((ll.ns~='' and ll.ns..':'..ll.tg) or ll.tg)
"
,
621
tag
=
"
ll.tg
"
,
622
position
=
"
l
"
,
-- is element in finalizer
623
firstindex
=
"
1
"
,
624
firstelement
=
"
1
"
,
625
first
=
"
1
"
,
626
lastindex
=
"
(#ll.__p__.dt or 1)
"
,
627
lastelement
=
"
(ll.__p__.en or 1)
"
,
628
last
=
"
#list
"
,
629
list
=
"
list
"
,
630
self
=
"
ll
"
,
631
rootposition
=
"
order
"
,
632
order
=
"
order
"
,
633
element
=
"
(ll.ei or 1)
"
,
634
index
=
"
(ll.ni or 1)
"
,
635
match
=
"
(ll.mi or 1)
"
,
636
namespace
=
"
ll.ns
"
,
637
ns
=
"
ll.ns
"
,
638 639
}
640 641
local
lp_builtin
=
lpeg
.
utfchartabletopattern
(
builtin
)
/
builtin
*
(
(
spaces
*
P
(
"
(
"
)
*
spaces
*
P
(
"
)
"
)
)
/
"
"
)
642 643
-- for the moment we keep namespaces with attributes
644 645
local
lp_attribute
=
(
P
(
"
@
"
)
+
P
(
"
attribute::
"
)
)
/
"
"
*
Cc
(
"
(ll.at and ll.at['
"
)
*
(
(
R
(
"
az
"
,
"
AZ
"
)
+
S
(
"
-_:
"
)
)
^
1
)
*
Cc
(
"
'])
"
)
646 647
----- lp_fastpos_p = (P("+")^0 * R("09")^1 * P(-1)) / function(s) return "l==" .. s end
648
----- lp_fastpos_n = (P("-") * R("09")^1 * P(-1)) / function(s) return "(" .. s .. "<0 and (#list+".. s .. "==l))" end
649 650
local
lp_fastpos_p
=
P
(
"
+
"
)
^
0
*
R
(
"
09
"
)
^
1
*
P
(
-1
)
/
"
l==%0
"
651
local
lp_fastpos_n
=
P
(
"
-
"
)
*
R
(
"
09
"
)
^
1
*
P
(
-1
)
/
"
(%0<0 and (#list+%0==l))
"
652
local
lp_fastpos
=
lp_fastpos_n
+
lp_fastpos_p
653 654
local
lp_reserved
=
C
(
"
and
"
)
+
C
(
"
or
"
)
+
C
(
"
not
"
)
+
C
(
"
div
"
)
+
C
(
"
mod
"
)
+
C
(
"
true
"
)
+
C
(
"
false
"
)
655 656
-- local lp_lua_function = C(R("az","AZ","__")^1 * (P(".") * R("az","AZ","__")^1)^1) * ("(") / function(t) -- todo: better . handling
657
-- return t .. "("
658
-- end
659 660
-- local lp_lua_function = (R("az","AZ","__")^1 * (P(".") * R("az","AZ","__")^1)^1) * ("(") / "%0("
661
local
lp_lua_function
=
Cs
(
(
R
(
"
az
"
,
"
AZ
"
,
"
__
"
)
^
1
*
(
P
(
"
.
"
)
*
R
(
"
az
"
,
"
AZ
"
,
"
__
"
)
^
1
)
^
1
)
*
(
"
(
"
)
)
/
"
%0
"
662 663
local
lp_function
=
C
(
R
(
"
az
"
,
"
AZ
"
,
"
__
"
)
^
1
)
*
P
(
"
(
"
)
/
function
(
t
)
-- todo: better . handling
664
if
expressions
[
t
]
then
665
return
"
expr.
"
.
.
t
.
.
"
(
"
666
else
667
return
"
expr.error(
"
668
end
669
end
670 671
local
lparent
=
P
(
"
(
"
)
672
local
rparent
=
P
(
"
)
"
)
673
local
noparent
=
1
-
(
lparent
+
rparent
)
674
local
nested
=
P
{
lparent
*
(
noparent
+
V
(
1
)
)
^
0
*
rparent
}
675
local
value
=
P
(
lparent
*
C
(
(
noparent
+
nested
)
^
0
)
*
rparent
)
-- P{"("*C(((1-S("()"))+V(1))^0)*")"}
676 677
local
lp_child
=
Cc
(
"
expr.child(ll,'
"
)
*
R
(
"
az
"
,
"
AZ
"
)
*
R
(
"
az
"
,
"
AZ
"
,
"
--
"
,
"
__
"
)
^
0
*
Cc
(
"
')
"
)
678
local
lp_number
=
S
(
"
+-
"
)
*
R
(
"
09
"
)
^
1
679
local
lp_string
=
Cc
(
"
'
"
)
*
R
(
"
az
"
,
"
AZ
"
,
"
--
"
,
"
__
"
)
^
1
*
Cc
(
"
'
"
)
680
local
lp_content
=
(
P
(
"
'
"
)
*
(
1
-
P
(
"
'
"
)
)
^
0
*
P
(
"
'
"
)
+
P
(
'
"
'
)
*
(
1
-
P
(
'
"
'
)
)
^
0
*
P
(
'
"
'
)
)
681 682
local
cleaner
683 684
local
lp_special
=
(
C
(
P
(
"
name
"
)
+
P
(
"
text
"
)
+
P
(
"
tag
"
)
+
P
(
"
count
"
)
+
P
(
"
child
"
)
)
)
*
value
/
function
(
t
,
s
)
685
if
expressions
[
t
]
then
686
s
=
s
and
s
~
=
"
"
and
lpegmatch
(
cleaner
,
s
)
687
if
s
and
s
~
=
"
"
then
688
return
"
expr.
"
.
.
t
.
.
"
(ll,
"
.
.
s
.
.
"
)
"
689
else
690
return
"
expr.
"
.
.
t
.
.
"
(ll)
"
691
end
692
else
693
return
"
expr.error(
"
.
.
t
.
.
"
)
"
694
end
695
end
696 697
local
content
=
698
lp_builtin
+
699
lp_attribute
+
700
lp_special
+
701
lp_noequal
+
lp_doequal
+
702
lp_or
+
lp_and
+
703
lp_reserved
+
704
lp_lua_function
+
lp_function
+
705
lp_content
+
-- too fragile
706
lp_child
+
707
lp_any
708 709
local
converter
=
Cs
(
710
lp_fastpos
+
(
P
{
lparent
*
(
V
(
1
)
)
^
0
*
rparent
+
content
}
)
^
0
711
)
712 713
cleaner
=
Cs
(
(
714
-- lp_fastpos +
715
lp_reserved
+
716
lp_number
+
717
lp_string
+
718
1
)
^
1
)
719 720
local
template_e
=
[[
721 local expr = xml.expressions 722 return function(list,ll,l,order) 723 return %s 724 end 725
]]
726 727
local
template_f_y
=
[[
728 local finalizer = xml.finalizers['%s']['%s'] 729 return function(collection) 730 return finalizer(collection,%s) 731 end 732
]]
733 734
local
template_f_n
=
[[
735 return xml.finalizers['%s']['%s'] 736
]]
737 738
--
739 740
local
register_last_match
=
{
kind
=
"
axis
"
,
axis
=
"
last-match
"
}
-- , apply = apply_axis["self"] }
741
local
register_self
=
{
kind
=
"
axis
"
,
axis
=
"
self
"
}
-- , apply = apply_axis["self"] }
742
local
register_parent
=
{
kind
=
"
axis
"
,
axis
=
"
parent
"
}
-- , apply = apply_axis["parent"] }
743
local
register_descendant
=
{
kind
=
"
axis
"
,
axis
=
"
descendant
"
}
-- , apply = apply_axis["descendant"] }
744
local
register_child
=
{
kind
=
"
axis
"
,
axis
=
"
child
"
}
-- , apply = apply_axis["child"] }
745
local
register_descendant_or_self
=
{
kind
=
"
axis
"
,
axis
=
"
descendant-or-self
"
}
-- , apply = apply_axis["descendant-or-self"] }
746
local
register_root
=
{
kind
=
"
axis
"
,
axis
=
"
root
"
}
-- , apply = apply_axis["root"] }
747
local
register_ancestor
=
{
kind
=
"
axis
"
,
axis
=
"
ancestor
"
}
-- , apply = apply_axis["ancestor"] }
748
local
register_ancestor_or_self
=
{
kind
=
"
axis
"
,
axis
=
"
ancestor-or-self
"
}
-- , apply = apply_axis["ancestor-or-self"] }
749
local
register_attribute
=
{
kind
=
"
axis
"
,
axis
=
"
attribute
"
}
-- , apply = apply_axis["attribute"] }
750
local
register_namespace
=
{
kind
=
"
axis
"
,
axis
=
"
namespace
"
}
-- , apply = apply_axis["namespace"] }
751
local
register_following
=
{
kind
=
"
axis
"
,
axis
=
"
following
"
}
-- , apply = apply_axis["following"] }
752
local
register_following_sibling
=
{
kind
=
"
axis
"
,
axis
=
"
following-sibling
"
}
-- , apply = apply_axis["following-sibling"] }
753
local
register_preceding
=
{
kind
=
"
axis
"
,
axis
=
"
preceding
"
}
-- , apply = apply_axis["preceding"] }
754
local
register_preceding_sibling
=
{
kind
=
"
axis
"
,
axis
=
"
preceding-sibling
"
}
-- , apply = apply_axis["preceding-sibling"] }
755
local
register_reverse_sibling
=
{
kind
=
"
axis
"
,
axis
=
"
reverse-sibling
"
}
-- , apply = apply_axis["reverse-sibling"] }
756 757
local
register_auto_descendant_or_self
=
{
kind
=
"
axis
"
,
axis
=
"
auto-descendant-or-self
"
}
-- , apply = apply_axis["auto-descendant-or-self"] }
758
local
register_auto_descendant
=
{
kind
=
"
axis
"
,
axis
=
"
auto-descendant
"
}
-- , apply = apply_axis["auto-descendant"] }
759
local
register_auto_self
=
{
kind
=
"
axis
"
,
axis
=
"
auto-self
"
}
-- , apply = apply_axis["auto-self"] }
760
local
register_auto_child
=
{
kind
=
"
axis
"
,
axis
=
"
auto-child
"
}
-- , apply = apply_axis["auto-child"] }
761 762
local
register_initial_child
=
{
kind
=
"
axis
"
,
axis
=
"
initial-child
"
}
-- , apply = apply_axis["initial-child"] }
763 764
local
register_all_nodes
=
{
kind
=
"
nodes
"
,
nodetest
=
true
,
nodes
=
{
true
,
false
,
false
}
}
765 766
local
skip
=
{
}
767 768
local
function
errorrunner_e
(
str
,
cnv
)
769
if
not
skip
[
str
]
then
770
report_lpath
(
"
error in expression: %s => %s
"
,
str
,
cnv
)
771
skip
[
str
]
=
cnv
or
str
772
end
773
return
false
774
end
775 776
local
function
errorrunner_f
(
str
,
arg
)
777
report_lpath
(
"
error in finalizer: %s(%s)
"
,
str
,
arg
or
"
"
)
778
return
false
779
end
780 781
local
function
register_nodes
(
nodetest
,
nodes
)
782
return
{
kind
=
"
nodes
"
,
nodetest
=
nodetest
,
nodes
=
nodes
}
783
end
784 785
local
function
register_selector
(
specification
)
786
return
{
kind
=
"
selector
"
,
specification
=
specification
}
787
end
788 789
local
function
register_expression
(
expression
)
790
local
converted
=
lpegmatch
(
converter
,
expression
)
791
local
wrapped
=
format
(
template_e
,
converted
)
792
local
runner
=
load
(
wrapped
)
793
-- print(wrapped)
794
runner
=
(
runner
and
runner
(
)
)
or
function
(
)
errorrunner_e
(
expression
,
converted
)
end
795
return
{
kind
=
"
expression
"
,
expression
=
expression
,
converted
=
converted
,
evaluator
=
runner
}
796
end
797 798
local
function
register_finalizer
(
protocol
,
name
,
arguments
)
799
local
runner
800
if
arguments
and
arguments
~
=
"
"
then
801
runner
=
load
(
format
(
template_f_y
,
protocol
or
xml
.
defaultprotocol
,
name
,
arguments
)
)
802
else
803
runner
=
load
(
format
(
template_f_n
,
protocol
or
xml
.
defaultprotocol
,
name
)
)
804
end
805
runner
=
(
runner
and
runner
(
)
)
or
function
(
)
errorrunner_f
(
name
,
arguments
)
end
806
return
{
kind
=
"
finalizer
"
,
name
=
name
,
arguments
=
arguments
,
finalizer
=
runner
}
807
end
808 809
local
expression
=
P
{
"
ex
"
,
810
ex
=
"
[
"
*
C
(
(
V
(
"
sq
"
)
+
V
(
"
dq
"
)
+
(
1
-
S
(
"
[]
"
)
)
+
V
(
"
ex
"
)
)
^
0
)
*
"
]
"
,
811
sq
=
"
'
"
*
(
1
-
S
(
"
'
"
)
)
^
0
*
"
'
"
,
812
dq
=
'
"
'
*
(
1
-
S
(
'
"
'
)
)
^
0
*
'
"
'
,
813
}
814 815
local
arguments
=
P
{
"
ar
"
,
816
ar
=
"
(
"
*
Cs
(
(
V
(
"
sq
"
)
+
V
(
"
dq
"
)
+
V
(
"
nq
"
)
+
P
(
1
-
P
(
"
)
"
)
)
)
^
0
)
*
"
)
"
,
817
nq
=
(
(
1
-
S
(
"
),'\"
"
)
)
^
1
)
/
function
(
s
)
return
format
(
"
%q
"
,
s
)
end
,
818
sq
=
P
(
"
'
"
)
*
(
1
-
P
(
"
'
"
)
)
^
0
*
P
(
"
'
"
)
,
819
dq
=
P
(
'
"
'
)
*
(
1
-
P
(
'
"
'
)
)
^
0
*
P
(
'
"
'
)
,
820
}
821 822
-- todo: better arg parser
823 824
local
function
register_error
(
str
)
825
return
{
kind
=
"
error
"
,
error
=
format
(
"
unparsed: %s
"
,
str
)
}
826
end
827 828
-- there is a difference in * and /*/ and so we need to catch a few special cases
829 830
local
special_1
=
P
(
"
*
"
)
*
Cc
(
register_auto_descendant
)
*
Cc
(
register_all_nodes
)
-- last one not needed
831
local
special_2
=
P
(
"
/
"
)
*
Cc
(
register_auto_self
)
832
local
special_3
=
P
(
"
"
)
*
Cc
(
register_auto_self
)
833 834
local
no_nextcolon
=
P
(
-1
)
+
#
(
1
-
P
(
"
:
"
)
)
-- newer lpeg needs the P(-1)
835
local
no_nextlparent
=
P
(
-1
)
+
#
(
1
-
P
(
"
(
"
)
)
-- newer lpeg needs the P(-1)
836 837
local
pathparser
=
Ct
{
"
patterns
"
,
-- can be made a bit faster by moving some patterns outside
838 839
patterns
=
spaces
*
V
(
"
protocol
"
)
*
spaces
*
(
840
(
V
(
"
special
"
)
*
spaces
*
P
(
-1
)
)
+
841
(
V
(
"
initial
"
)
*
spaces
*
V
(
"
step
"
)
*
spaces
*
(
P
(
"
/
"
)
*
spaces
*
V
(
"
step
"
)
*
spaces
)
^
0
)
842
)
,
843 844
protocol
=
Cg
(
V
(
"
letters
"
)
,
"
protocol
"
)
*
P
(
"
://
"
)
+
Cg
(
Cc
(
nil
)
,
"
protocol
"
)
,
845 846
-- the / is needed for // as descendant or self is somewhat special
847
--
848
-- step = (V("shortcuts") + V("axis") * spaces * V("nodes")^0 + V("error")) * spaces * V("expressions")^0 * spaces * V("finalizer")^0,
849
step
=
(
(
V
(
"
shortcuts
"
)
+
V
(
"
selector
"
)
+
P
(
"
/
"
)
+
V
(
"
axis
"
)
)
*
spaces
*
V
(
"
nodes
"
)
^
0
+
V
(
"
error
"
)
)
*
spaces
*
V
(
"
expressions
"
)
^
0
*
spaces
*
V
(
"
finalizer
"
)
^
0
,
850 851
axis
=
V
(
"
last_match
"
)
852
+
V
(
"
descendant
"
)
853
+
V
(
"
child
"
)
854
+
V
(
"
parent
"
)
855
+
V
(
"
self
"
)
856
+
V
(
"
root
"
)
857
+
V
(
"
ancestor
"
)
858
+
V
(
"
descendant_or_self
"
)
859
+
V
(
"
following_sibling
"
)
860
+
V
(
"
following
"
)
861
+
V
(
"
reverse_sibling
"
)
862
+
V
(
"
preceding_sibling
"
)
863
+
V
(
"
preceding
"
)
864
+
V
(
"
ancestor_or_self
"
)
865
+
#
(
1
-
P
(
-1
)
)
*
Cc
(
register_auto_child
)
,
866 867
special
=
special_1
868
+
special_2
869
+
special_3
,
870 871
initial
=
(
P
(
"
/
"
)
*
spaces
*
Cc
(
register_initial_child
)
)
^
-1
,
872 873
error
=
(
P
(
1
)
^
1
)
/
register_error
,
874 875
shortcuts_a
=
V
(
"
s_descendant_or_self
"
)
876
+
V
(
"
s_descendant
"
)
877
+
V
(
"
s_child
"
)
878
+
V
(
"
s_parent
"
)
879
+
V
(
"
s_self
"
)
880
+
V
(
"
s_root
"
)
881
+
V
(
"
s_ancestor
"
)
882
+
V
(
"
s_lastmatch
"
)
,
883 884
shortcuts
=
V
(
"
shortcuts_a
"
)
*
(
spaces
*
"
/
"
*
spaces
*
V
(
"
shortcuts_a
"
)
)
^
0
,
885 886
s_descendant_or_self
=
(
P
(
"
***/
"
)
+
P
(
"
/
"
)
)
*
Cc
(
register_descendant_or_self
)
,
--- *** is a bonus
887
s_descendant
=
P
(
"
**
"
)
*
Cc
(
register_descendant
)
,
888
s_child
=
P
(
"
*
"
)
*
no_nextcolon
*
Cc
(
register_child
)
,
889
s_parent
=
P
(
"
..
"
)
*
Cc
(
register_parent
)
,
890
s_self
=
P
(
"
.
"
)
*
Cc
(
register_self
)
,
891
s_root
=
P
(
"
^^
"
)
*
Cc
(
register_root
)
,
892
s_ancestor
=
P
(
"
^
"
)
*
Cc
(
register_ancestor
)
,
893
s_lastmatch
=
P
(
"
=
"
)
*
Cc
(
register_last_match
)
,
894 895
-- we can speed this up when needed but we cache anyway so ...
896 897
descendant
=
P
(
"
descendant::
"
)
*
Cc
(
register_descendant
)
,
898
child
=
P
(
"
child::
"
)
*
Cc
(
register_child
)
,
899
parent
=
P
(
"
parent::
"
)
*
Cc
(
register_parent
)
,
900
self
=
P
(
"
self::
"
)
*
Cc
(
register_self
)
,
901
root
=
P
(
'
root::
'
)
*
Cc
(
register_root
)
,
902
ancestor
=
P
(
'
ancestor::
'
)
*
Cc
(
register_ancestor
)
,
903
descendant_or_self
=
P
(
'
descendant-or-self::
'
)
*
Cc
(
register_descendant_or_self
)
,
904
ancestor_or_self
=
P
(
'
ancestor-or-self::
'
)
*
Cc
(
register_ancestor_or_self
)
,
905
-- attribute = P('attribute::') * Cc(register_attribute),
906
-- namespace = P('namespace::') * Cc(register_namespace),
907
following
=
P
(
'
following::
'
)
*
Cc
(
register_following
)
,
908
following_sibling
=
P
(
'
following-sibling::
'
)
*
Cc
(
register_following_sibling
)
,
909
preceding
=
P
(
'
preceding::
'
)
*
Cc
(
register_preceding
)
,
910
preceding_sibling
=
P
(
'
preceding-sibling::
'
)
*
Cc
(
register_preceding_sibling
)
,
911
reverse_sibling
=
P
(
'
reverse-sibling::
'
)
*
Cc
(
register_reverse_sibling
)
,
912
last_match
=
P
(
'
last-match::
'
)
*
Cc
(
register_last_match
)
,
913 914
selector
=
P
(
"
{
"
)
*
C
(
(
1
-
P
(
"
}
"
)
)
^
1
)
*
P
(
"
}
"
)
/
register_selector
,
915 916
nodes
=
(
V
(
"
nodefunction
"
)
*
spaces
*
P
(
"
(
"
)
*
V
(
"
nodeset
"
)
*
P
(
"
)
"
)
+
V
(
"
nodetest
"
)
*
V
(
"
nodeset
"
)
)
/
register_nodes
,
917 918
expressions
=
expression
/
register_expression
,
919 920
letters
=
R
(
"
az
"
)
^
1
,
921
name
=
(
1
-
S
(
"
/[]()|:*!
"
)
)
^
1
,
-- make inline
922
negate
=
P
(
"
!
"
)
*
Cc
(
false
)
,
923 924
nodefunction
=
V
(
"
negate
"
)
+
P
(
"
not
"
)
*
Cc
(
false
)
+
Cc
(
true
)
,
925
nodetest
=
V
(
"
negate
"
)
+
Cc
(
true
)
,
926
nodename
=
(
V
(
"
negate
"
)
+
Cc
(
true
)
)
*
spaces
*
(
(
V
(
"
wildnodename
"
)
*
P
(
"
:
"
)
*
V
(
"
wildnodename
"
)
)
+
(
Cc
(
false
)
*
V
(
"
wildnodename
"
)
)
)
,
927
wildnodename
=
(
C
(
V
(
"
name
"
)
)
+
P
(
"
*
"
)
*
Cc
(
false
)
)
*
no_nextlparent
,
928
nodeset
=
spaces
*
Ct
(
V
(
"
nodename
"
)
*
(
spaces
*
P
(
"
|
"
)
*
spaces
*
V
(
"
nodename
"
)
)
^
0
)
*
spaces
,
929 930
finalizer
=
(
Cb
(
"
protocol
"
)
*
P
(
"
/
"
)
^
-1
*
C
(
V
(
"
name
"
)
)
*
arguments
*
P
(
-1
)
)
/
register_finalizer
,
931 932
}
933 934
xmlpatterns
.
pathparser
=
pathparser
935 936
local
cache
=
{
}
937 938
local
function
nodesettostring
(
set
,
nodetest
)
939
local
t
=
{
}
940
for
i
=
1
,
#
set
,
3
do
941
local
directive
,
ns
,
tg
=
set
[
i
]
,
set
[
i
+
1
]
,
set
[
i
+
2
]
942
if
not
ns
or
ns
=
=
"
"
then
ns
=
"
*
"
end
943
if
not
tg
or
tg
=
=
"
"
then
tg
=
"
*
"
end
944
tg
=
(
tg
=
=
"
@rt@
"
and
"
[root]
"
)
or
format
(
"
%s:%s
"
,
ns
,
tg
)
945
t
[
#
t
+
1
]
=
(
directive
and
tg
)
or
format
(
"
not(%s)
"
,
tg
)
946
end
947
if
nodetest
=
=
false
then
948
return
format
(
"
not(%s)
"
,
concat
(
t
,
"
|
"
)
)
949
else
950
return
concat
(
t
,
"
|
"
)
951
end
952
end
953 954
local
function
tagstostring
(
list
)
955
if
#
list
=
=
0
then
956
return
"
no elements
"
957
else
958
local
t
=
{
}
959
for
i
=
1
,
#
list
do
960
local
li
=
list
[
i
]
961
local
ns
=
li
.
ns
962
local
tg
=
li
.
tg
963
if
not
ns
or
ns
=
=
"
"
then
ns
=
"
*
"
end
964
if
not
tg
or
tg
=
=
"
"
then
tg
=
"
*
"
end
965
t
[
i
]
=
(
tg
=
=
"
@rt@
"
and
"
[root]
"
)
or
format
(
"
%s:%s
"
,
ns
,
tg
)
966
end
967
return
concat
(
t
,
"
"
)
968
end
969
end
970 971
xml
.
nodesettostring
=
nodesettostring
972 973
local
lpath
-- we have a harmless kind of circular reference
974 975
local
function
lshow
(
parsed
)
976
if
type
(
parsed
)
=
=
"
string
"
then
977
parsed
=
lpath
(
parsed
)
978
end
979
report_lpath
(
"
%s://%s => %s
"
,
parsed
.
protocol
or
xml
.
defaultprotocol
,
parsed
.
pattern
,
980
table
.
serialize
(
parsed
,
false
)
)
981
end
982 983
xml
.
lshow
=
lshow
984 985
local
function
add_comment
(
p
,
str
)
986
local
pc
=
p
.
comment
987
if
not
pc
then
988
p
.
comment
=
{
str
}
989
else
990
pc
[
#
pc
+
1
]
=
str
991
end
992
end
993 994
lpath
=
function
(
pattern
)
-- the gain of caching is rather minimal
995
lpathcalls
=
lpathcalls
+
1
996
if
type
(
pattern
)
=
=
"
table
"
then
997
return
pattern
998
else
999
local
parsed
=
cache
[
pattern
]
1000
if
parsed
then
1001
lpathcached
=
lpathcached
+
1
1002
else
1003
parsed
=
lpegmatch
(
pathparser
,
pattern
)
1004
if
parsed
then
1005
parsed
.
pattern
=
pattern
1006
local
np
=
#
parsed
1007
if
np
=
=
0
then
1008
parsed
=
{
pattern
=
pattern
,
register_self
,
state
=
"
parsing error
"
}
1009
report_lpath
(
"
parsing error in pattern: %s
"
,
pattern
)
1010
lshow
(
parsed
)
1011
else
1012
-- we could have done this with a more complex parser but this
1013
-- is cleaner
1014
local
pi
=
parsed
[
1
]
1015
if
pi
.
axis
=
=
"
auto-child
"
then
1016
if
false
then
1017
add_comment
(
parsed
,
"
auto-child replaced by auto-descendant-or-self
"
)
1018
parsed
[
1
]
=
register_auto_descendant_or_self
1019
else
1020
add_comment
(
parsed
,
"
auto-child replaced by auto-descendant
"
)
1021
parsed
[
1
]
=
register_auto_descendant
1022
end
1023
elseif
pi
.
axis
=
=
"
initial-child
"
and
np
>
1
and
parsed
[
2
]
.
axis
then
1024
add_comment
(
parsed
,
"
initial-child removed
"
)
-- we could also make it a auto-self
1025
remove
(
parsed
,
1
)
1026
end
1027
local
np
=
#
parsed
-- can have changed
1028
if
np
>
1
then
1029
local
pnp
=
parsed
[
np
]
1030
if
pnp
.
kind
=
=
"
nodes
"
and
pnp
.
nodetest
=
=
true
then
1031
local
nodes
=
pnp
.
nodes
1032
if
nodes
[
1
]
=
=
true
and
nodes
[
2
]
=
=
false
and
nodes
[
3
]
=
=
false
then
1033
add_comment
(
parsed
,
"
redundant final wildcard filter removed
"
)
1034
remove
(
parsed
,
np
)
1035
end
1036
end
1037
end
1038
end
1039
else
1040
parsed
=
{
pattern
=
pattern
}
1041
end
1042
cache
[
pattern
]
=
parsed
1043
if
trace_lparse
and
not
trace_lprofile
then
1044
lshow
(
parsed
)
1045
end
1046
end
1047
return
parsed
1048
end
1049
end
1050 1051
xml
.
lpath
=
lpath
1052 1053
-- we can move all calls inline and then merge the trace back
1054
-- technically we can combine axis and the next nodes which is
1055
-- what we did before but this a bit cleaner (but slower too)
1056
-- but interesting is that it's not that much faster when we
1057
-- go inline
1058
--
1059
-- beware: we need to return a collection even when we filter
1060
-- else the (simple) cache gets messed up
1061 1062
-- caching found lookups saves not that much (max .1 sec on a 8 sec run)
1063
-- and it also messes up finalizers
1064 1065
-- watch out: when there is a finalizer, it's always called as there
1066
-- can be cases that a finalizer returns (or does) something in case
1067
-- there is no match; an example of this is count()
1068 1069
do
1070 1071
local
profiled
=
{
}
1072
xml
.
profiled
=
profiled
1073
local
lastmatch
=
nil
-- we remember the last one .. drawback: no collection till new collect
1074
local
keepmatch
=
nil
-- we remember the last one .. drawback: no collection till new collect
1075 1076
if
directives
then
1077
directives
.
register
(
"
xml.path.keeplastmatch
"
,
function
(
v
)
1078
keepmatch
=
v
1079
lastmatch
=
nil
1080
end
)
1081
end
1082 1083
apply_axis
[
"
last-match
"
]
=
function
(
)
1084
return
lastmatch
or
{
}
1085
end
1086 1087
local
function
profiled_apply
(
list
,
parsed
,
nofparsed
,
order
)
1088
local
p
=
profiled
[
parsed
.
pattern
]
1089
if
p
then
1090
p
.
tested
=
p
.
tested
+
1
1091
else
1092
p
=
{
tested
=
1
,
matched
=
0
,
finalized
=
0
}
1093
profiled
[
parsed
.
pattern
]
=
p
1094
end
1095
local
collected
=
list
1096
for
i
=
1
,
nofparsed
do
1097
local
pi
=
parsed
[
i
]
1098
local
kind
=
pi
.
kind
1099
if
kind
=
=
"
axis
"
then
1100
collected
=
apply_axis
[
pi
.
axis
]
(
collected
)
1101
elseif
kind
=
=
"
nodes
"
then
1102
collected
=
apply_nodes
(
collected
,
pi
.
nodetest
,
pi
.
nodes
)
1103
elseif
kind
=
=
"
expression
"
then
1104
collected
=
apply_expression
(
collected
,
pi
.
evaluator
,
order
)
1105
elseif
kind
=
=
"
selector
"
then
1106
collected
=
apply_selector
(
collected
,
pi
.
specification
)
1107
elseif
kind
=
=
"
finalizer
"
then
1108
collected
=
pi
.
finalizer
(
collected
)
-- no check on # here
1109
p
.
matched
=
p
.
matched
+
1
1110
p
.
finalized
=
p
.
finalized
+
1
1111
return
collected
1112
end
1113
if
not
collected
or
#
collected
=
=
0
then
1114
local
pn
=
i
<
nofparsed
and
parsed
[
nofparsed
]
1115
if
pn
and
pn
.
kind
=
=
"
finalizer
"
then
1116
collected
=
pn
.
finalizer
(
collected
)
-- collected can be nil
1117
p
.
finalized
=
p
.
finalized
+
1
1118
return
collected
1119
end
1120
return
nil
1121
end
1122
end
1123
if
collected
then
1124
p
.
matched
=
p
.
matched
+
1
1125
end
1126
return
collected
1127
end
1128 1129
local
function
traced_apply
(
list
,
parsed
,
nofparsed
,
order
)
1130
if
trace_lparse
then
1131
lshow
(
parsed
)
1132
end
1133
report_lpath
(
"
collecting: %s
"
,
parsed
.
pattern
)
1134
report_lpath
(
"
root tags : %s
"
,
tagstostring
(
list
)
)
1135
report_lpath
(
"
order : %s
"
,
order
or
"
unset
"
)
1136
local
collected
=
list
1137
for
i
=
1
,
nofparsed
do
1138
local
pi
=
parsed
[
i
]
1139
local
kind
=
pi
.
kind
1140
if
kind
=
=
"
axis
"
then
1141
collected
=
apply_axis
[
pi
.
axis
]
(
collected
)
1142
report_lpath
(
"
% 10i : ax : %s
"
,
(
collected
and
#
collected
)
or
0
,
pi
.
axis
)
1143
elseif
kind
=
=
"
nodes
"
then
1144
collected
=
apply_nodes
(
collected
,
pi
.
nodetest
,
pi
.
nodes
)
1145
report_lpath
(
"
% 10i : ns : %s
"
,
(
collected
and
#
collected
)
or
0
,
nodesettostring
(
pi
.
nodes
,
pi
.
nodetest
)
)
1146
elseif
kind
=
=
"
expression
"
then
1147
collected
=
apply_expression
(
collected
,
pi
.
evaluator
,
order
)
1148
report_lpath
(
"
% 10i : ex : %s -> %s
"
,
(
collected
and
#
collected
)
or
0
,
pi
.
expression
,
pi
.
converted
)
1149
elseif
kind
=
=
"
selector
"
then
1150
collected
=
apply_selector
(
collected
,
pi
.
specification
)
1151
report_lpath
(
"
% 10i : se : %s
"
,
(
collected
and
#
collected
)
or
0
,
pi
.
specification
)
1152
elseif
kind
=
=
"
finalizer
"
then
1153
collected
=
pi
.
finalizer
(
collected
)
1154
report_lpath
(
"
% 10i : fi : %s : %s(%s)
"
,
(
type
(
collected
)
=
=
"
table
"
and
#
collected
)
or
0
,
parsed
.
protocol
or
xml
.
defaultprotocol
,
pi
.
name
,
pi
.
arguments
or
"
"
)
1155
return
collected
1156
end
1157
if
not
collected
or
#
collected
=
=
0
then
1158
local
pn
=
i
<
nofparsed
and
parsed
[
nofparsed
]
1159
if
pn
and
pn
.
kind
=
=
"
finalizer
"
then
1160
collected
=
pn
.
finalizer
(
collected
)
1161
report_lpath
(
"
% 10i : fi : %s : %s(%s)
"
,
(
type
(
collected
)
=
=
"
table
"
and
#
collected
)
or
0
,
parsed
.
protocol
or
xml
.
defaultprotocol
,
pn
.
name
,
pn
.
arguments
or
"
"
)
1162
return
collected
1163
end
1164
return
nil
1165
end
1166
end
1167
return
collected
1168
end
1169 1170
local
function
normal_apply
(
list
,
parsed
,
nofparsed
,
order
)
1171
local
collected
=
list
1172
for
i
=
1
,
nofparsed
do
1173
local
pi
=
parsed
[
i
]
1174
local
kind
=
pi
.
kind
1175
if
kind
=
=
"
axis
"
then
1176
local
axis
=
pi
.
axis
1177
if
axis
~
=
"
self
"
then
1178
collected
=
apply_axis
[
axis
]
(
collected
)
1179
end
1180
elseif
kind
=
=
"
nodes
"
then
1181
collected
=
apply_nodes
(
collected
,
pi
.
nodetest
,
pi
.
nodes
)
1182
elseif
kind
=
=
"
expression
"
then
1183
collected
=
apply_expression
(
collected
,
pi
.
evaluator
,
order
)
1184
elseif
kind
=
=
"
selector
"
then
1185
collected
=
apply_selector
(
collected
,
pi
.
specification
)
1186
elseif
kind
=
=
"
finalizer
"
then
1187
return
pi
.
finalizer
(
collected
)
1188
end
1189
if
not
collected
or
#
collected
=
=
0
then
1190
local
pf
=
i
<
nofparsed
and
parsed
[
nofparsed
]
.
finalizer
1191
if
pf
then
1192
return
pf
(
collected
)
-- can be anything
1193
end
1194
return
nil
1195
end
1196
end
1197
return
collected
1198
end
1199 1200
local
apply
=
normal_apply
1201 1202
if
trackers
then
1203
-- local function check()
1204
-- if trace_lprofile or then
1205
-- apply = profiled_apply
1206
-- elseif trace_lpath then
1207
-- apply = traced_apply
1208
-- else
1209
-- apply = normal_apply
1210
-- end
1211
-- end
1212
-- trackers.register("xml.path", check) -- can be "xml.path,xml.parse,xml.profile
1213
-- trackers.register("xml.parse", check)
1214
-- trackers.register("xml.profile",check)
1215 1216
trackers
.
register
(
"
xml.path,xml.parse,xml.profile
"
,
function
(
)
1217
if
trace_lprofile
then
1218
apply
=
profiled_apply
1219
elseif
trace_lpath
then
1220
apply
=
traced_apply
1221
else
1222
apply
=
normal_apply
1223
end
1224
end
)
1225
end
1226 1227 1228
function
xml
.
applylpath
(
list
,
pattern
)
1229
if
not
list
then
1230
lastmatch
=
nil
1231
return
1232
end
1233
local
parsed
=
cache
[
pattern
]
1234
if
parsed
then
1235
lpathcalls
=
lpathcalls
+
1
1236
lpathcached
=
lpathcached
+
1
1237
elseif
type
(
pattern
)
=
=
"
table
"
then
1238
lpathcalls
=
lpathcalls
+
1
1239
parsed
=
pattern
1240
else
1241
parsed
=
lpath
(
pattern
)
or
pattern
1242
end
1243
if
not
parsed
then
1244
lastmatch
=
nil
1245
return
1246
end
1247
local
nofparsed
=
#
parsed
1248
if
nofparsed
=
=
0
then
1249
lastmatch
=
nil
1250
return
-- something is wrong
1251
end
1252
local
collected
=
apply
(
{
list
}
,
parsed
,
nofparsed
,
list
.
mi
)
1253
lastmatch
=
keepmatch
and
collected
or
nil
1254
return
collected
1255
end
1256 1257
function
xml
.
lastmatch
(
)
1258
return
lastmatch
1259
end
1260 1261
local
stack
=
{
}
1262 1263
function
xml
.
pushmatch
(
)
1264
insert
(
stack
,
lastmatch
)
1265
end
1266 1267
function
xml
.
popmatch
(
)
1268
lastmatch
=
remove
(
stack
)
1269
end
1270 1271
end
1272 1273
local
applylpath
=
xml
.
applylpath
1274
--[[ldx-- 1275<p>This is the main filter function. It returns whatever is asked for.</p> 1276--ldx]]
--
1277 1278
function
xml
.
filter
(
root
,
pattern
)
-- no longer funny attribute handling here
1279
return
applylpath
(
root
,
pattern
)
1280
end
1281 1282
-- internal (parsed)
1283 1284
expressions
.
child
=
function
(
e
,
pattern
)
1285
return
applylpath
(
e
,
pattern
)
-- todo: cache
1286
end
1287 1288
expressions
.
count
=
function
(
e
,
pattern
)
-- what if pattern == empty or nil
1289
local
collected
=
applylpath
(
e
,
pattern
)
-- todo: cache
1290
return
pattern
and
(
collected
and
#
collected
)
or
0
1291
end
1292 1293
expressions
.
attribute
=
function
(
e
,
name
,
value
)
1294
if
type
(
e
)
=
=
"
table
"
and
name
then
1295
local
a
=
e
.
at
1296
if
a
then
1297
local
v
=
a
[
name
]
1298
if
value
then
1299
return
v
=
=
value
1300
else
1301
return
v
1302
end
1303
end
1304
end
1305
return
nil
1306
end
1307 1308
-- external
1309 1310
-- expressions.oneof = function(s,...)
1311
-- local t = {...}
1312
-- for i=1,#t do
1313
-- if s == t[i] then
1314
-- return true
1315
-- end
1316
-- end
1317
-- return false
1318
-- end
1319 1320
-- could be a hashed hash
1321 1322
expressions
.
oneof
=
function
(
s
,
...
)
1323
for
i
=
1
,
select
(
"
#
"
,
...
)
do
1324
if
s
=
=
select
(
i
,
...
)
then
1325
return
true
1326
end
1327
end
1328
return
false
1329
end
1330 1331
expressions
.
error
=
function
(
str
)
1332
xml
.
errorhandler
(
format
(
"
unknown function in lpath expression: %s
"
,
tostring
(
str
or
"
?
"
)
)
)
1333
return
false
1334
end
1335 1336
expressions
.
undefined
=
function
(
s
)
1337
return
s
=
=
nil
1338
end
1339 1340
expressions
.
quit
=
function
(
s
)
1341
if
s
or
s
=
=
nil
then
1342
quit_expression
=
true
1343
end
1344
return
true
1345
end
1346 1347
expressions
.
print
=
function
(
...
)
1348
print
(
...
)
1349
return
true
1350
end
1351 1352
expressions
.
find
=
find
1353
expressions
.
upper
=
upper
1354
expressions
.
lower
=
lower
1355
expressions
.
number
=
tonumber
1356
expressions
.
boolean
=
toboolean
1357 1358
function
expressions
.
contains
(
str
,
pattern
)
1359
local
t
=
type
(
str
)
1360
if
t
=
=
"
string
"
then
1361
if
find
(
str
,
pattern
)
then
1362
return
true
1363
end
1364
elseif
t
=
=
"
table
"
then
1365
for
i
=
1
,
#
str
do
1366
local
d
=
str
[
i
]
1367
if
type
(
d
)
=
=
"
string
"
and
find
(
d
,
pattern
)
then
1368
return
true
1369
end
1370
end
1371
end
1372
return
false
1373
end
1374 1375
function
expressions
.
idstring
(
str
)
1376
return
type
(
str
)
=
=
"
string
"
and
gsub
(
str
,
"
^#
"
,
"
"
)
or
"
"
1377
end
1378 1379
-- user interface
1380 1381
local
function
traverse
(
root
,
pattern
,
handle
)
1382
-- report_lpath("use 'xml.selection' instead for pattern: %s",pattern)
1383
local
collected
=
applylpath
(
root
,
pattern
)
1384
if
collected
then
1385
for
c
=
1
,
#
collected
do
1386
local
e
=
collected
[
c
]
1387
local
r
=
e
.
__p__
1388
handle
(
r
,
r
.
dt
,
e
.
ni
)
1389
end
1390
end
1391
end
1392 1393
local
function
selection
(
root
,
pattern
,
handle
)
1394
local
collected
=
applylpath
(
root
,
pattern
)
1395
if
collected
then
1396
if
handle
then
1397
for
c
=
1
,
#
collected
do
1398
handle
(
collected
[
c
]
)
1399
end
1400
else
1401
return
collected
1402
end
1403
end
1404
end
1405 1406
xml
.
traverse
=
traverse
-- old method, r, d, k
1407
xml
.
selection
=
selection
-- new method, simple handle
1408 1409
--~ function xml.cachedpatterns()
1410
--~ return cache
1411
--~ end
1412 1413
-- generic function finalizer (independant namespace)
1414 1415
local
function
dofunction
(
collected
,
fnc
,
...
)
1416
if
collected
then
1417
local
f
=
functions
[
fnc
]
1418
if
f
then
1419
for
c
=
1
,
#
collected
do
1420
f
(
collected
[
c
]
,
...
)
1421
end
1422
else
1423
report_lpath
(
"
unknown function %a
"
,
fnc
)
1424
end
1425
end
1426
end
1427 1428
finalizers
.
xml
[
"
function
"
]
=
dofunction
1429
finalizers
.
tex
[
"
function
"
]
=
dofunction
1430 1431
-- functions
1432 1433
expressions
.
text
=
function
(
e
,
n
)
1434
local
rdt
=
e
.
__p__
.
dt
1435
return
rdt
and
rdt
[
n
]
or
"
"
1436
end
1437 1438
expressions
.
name
=
function
(
e
,
n
)
-- ns + tg
1439
local
found
=
false
1440
n
=
tonumber
(
n
)
or
0
1441
if
n
=
=
0
then
1442
found
=
type
(
e
)
=
=
"
table
"
and
e
1443
elseif
n
<
0
then
1444
local
d
=
e
.
__p__
.
dt
1445
local
k
=
e
.
ni
1446
for
i
=
k
-1
,
1
,
-1
do
1447
local
di
=
d
[
i
]
1448
if
type
(
di
)
=
=
"
table
"
then
1449
if
n
=
=
-1
then
1450
found
=
di
1451
break
1452
else
1453
n
=
n
+
1
1454
end
1455
end
1456
end
1457
else
1458
local
d
=
e
.
__p__
.
dt
1459
local
k
=
e
.
ni
1460
for
i
=
k
+
1
,
#
d
,
1
do
1461
local
di
=
d
[
i
]
1462
if
type
(
di
)
=
=
"
table
"
then
1463
if
n
=
=
1
then
1464
found
=
di
1465
break
1466
else
1467
n
=
n
-
1
1468
end
1469
end
1470
end
1471
end
1472
if
found
then
1473
local
ns
=
found
.
rn
or
found
.
ns
or
"
"
1474
local
tg
=
found
.
tg
1475
if
ns
~
=
"
"
then
1476
return
ns
.
.
"
:
"
.
.
tg
1477
else
1478
return
tg
1479
end
1480
else
1481
return
"
"
1482
end
1483
end
1484 1485
expressions
.
tag
=
function
(
e
,
n
)
-- only tg
1486
if
not
e
then
1487
return
"
"
1488
else
1489
local
found
=
false
1490
n
=
tonumber
(
n
)
or
0
1491
if
n
=
=
0
then
1492
found
=
(
type
(
e
)
=
=
"
table
"
)
and
e
-- seems to fail
1493
elseif
n
<
0
then
1494
local
d
=
e
.
__p__
.
dt
1495
local
k
=
e
.
ni
1496
for
i
=
k
-1
,
1
,
-1
do
1497
local
di
=
d
[
i
]
1498
if
type
(
di
)
=
=
"
table
"
then
1499
if
n
=
=
-1
then
1500
found
=
di
1501
break
1502
else
1503
n
=
n
+
1
1504
end
1505
end
1506
end
1507
else
1508
local
d
=
e
.
__p__
.
dt
1509
local
k
=
e
.
ni
1510
for
i
=
k
+
1
,
#
d
,
1
do
1511
local
di
=
d
[
i
]
1512
if
type
(
di
)
=
=
"
table
"
then
1513
if
n
=
=
1
then
1514
found
=
di
1515
break
1516
else
1517
n
=
n
-
1
1518
end
1519
end
1520
end
1521
end
1522
return
(
found
and
found
.
tg
)
or
"
"
1523
end
1524
end
1525 1526
--[[ldx-- 1527<p>Often using an iterators looks nicer in the code than passing handler 1528functions. The <l n='lua'/> book describes how to use coroutines for that 1529purpose (<url href='http://www.lua.org/pil/9.3.html'/>). This permits 1530code like:</p> 1531 1532<typing> 1533for r, d, k in xml.elements(xml.load('text.xml'),"title") do 1534 print(d[k]) -- old method 1535end 1536for e in xml.collected(xml.load('text.xml'),"title") do 1537 print(e) -- new one 1538end 1539</typing> 1540--ldx]]
--
1541 1542
-- local wrap, yield = coroutine.wrap, coroutine.yield
1543
-- local dummy = function() end
1544
--
1545
-- function xml.elements(root,pattern,reverse) -- r, d, k
1546
-- local collected = applylpath(root,pattern)
1547
-- if collected then
1548
-- if reverse then
1549
-- return wrap(function() for c=#collected,1,-1 do
1550
-- local e = collected[c] local r = e.__p__ yield(r,r.dt,e.ni)
1551
-- end end)
1552
-- else
1553
-- return wrap(function() for c=1,#collected do
1554
-- local e = collected[c] local r = e.__p__ yield(r,r.dt,e.ni)
1555
-- end end)
1556
-- end
1557
-- end
1558
-- return wrap(dummy)
1559
-- end
1560
--
1561
-- function xml.collected(root,pattern,reverse) -- e
1562
-- local collected = applylpath(root,pattern)
1563
-- if collected then
1564
-- if reverse then
1565
-- return wrap(function() for c=#collected,1,-1 do yield(collected[c]) end end)
1566
-- else
1567
-- return wrap(function() for c=1,#collected do yield(collected[c]) end end)
1568
-- end
1569
-- end
1570
-- return wrap(dummy)
1571
-- end
1572 1573
-- faster:
1574 1575
local
dummy
=
function
(
)
end
1576 1577
function
xml
.
elements
(
root
,
pattern
,
reverse
)
-- r, d, k
1578
local
collected
=
applylpath
(
root
,
pattern
)
1579
if
not
collected
then
1580
return
dummy
1581
end
1582
local
n
=
#
collected
1583
if
n
=
=
0
then
1584
return
dummy
1585
end
1586
if
reverse
then
1587
local
c
=
n
+
1
1588
return
function
(
)
1589
if
c
>
1
then
1590
c
=
c
-
1
1591
local
e
=
collected
[
c
]
1592
local
r
=
e
.
__p__
1593
return
r
,
r
.
dt
,
e
.
ni
1594
end
1595
end
1596
else
1597
local
c
=
0
1598
return
function
(
)
1599
if
c
<
n
then
1600
c
=
c
+
1
1601
local
e
=
collected
[
c
]
1602
local
r
=
e
.
__p__
1603
return
r
,
r
.
dt
,
e
.
ni
1604
end
1605
end
1606
end
1607
end
1608 1609
function
xml
.
collected
(
root
,
pattern
,
reverse
)
-- e
1610
local
collected
=
applylpath
(
root
,
pattern
)
1611
if
not
collected
then
1612
return
dummy
1613
end
1614
local
n
=
#
collected
1615
if
n
=
=
0
then
1616
return
dummy
1617
end
1618
if
reverse
then
1619
local
c
=
n
+
1
1620
return
function
(
)
1621
if
c
>
1
then
1622
c
=
c
-
1
1623
return
collected
[
c
]
1624
end
1625
end
1626
else
1627
local
c
=
0
1628
return
function
(
)
1629
if
c
<
n
then
1630
c
=
c
+
1
1631
return
collected
[
c
]
1632
end
1633
end
1634
end
1635
end
1636 1637
-- handy
1638 1639
function
xml
.
inspect
(
collection
,
pattern
)
1640
pattern
=
pattern
or
"
.
"
1641
for
e
in
xml
.
collected
(
collection
,
pattern
or
"
.
"
)
do
1642
report_lpath
(
"
pattern: %s\n\n%s\n
"
,
pattern
,
xml
.
tostring
(
e
)
)
1643
end
1644
end
1645 1646
-- texy (see xfdf):
1647 1648
local
function
split
(
e
)
-- todo: use helpers / lpeg
1649
local
dt
=
e
.
dt
1650
if
dt
then
1651
for
i
=
1
,
#
dt
do
1652
local
dti
=
dt
[
i
]
1653
if
type
(
dti
)
=
=
"
string
"
then
1654
dti
=
gsub
(
dti
,
"
^[\n\r]*(.-)[\n\r]*
"
,
"
%1
"
)
1655
dti
=
gsub
(
dti
,
"
[\n\r]+
"
,
"
\n\n
"
)
1656
dt
[
i
]
=
dti
1657
else
1658
split
(
dti
)
1659
end
1660
end
1661
end
1662
return
e
1663
end
1664 1665
function
xml
.
finalizers
.
paragraphs
(
c
)
1666
for
i
=
1
,
#
c
do
1667
split
(
c
[
i
]
)
1668
end
1669
return
c
1670
end
1671 1672
-- local lpegmatch = lpeg.match
1673
-- local w = lpeg.patterns.whitespace
1674
-- local p = w^0 * lpeg.Cf(lpeg.Ct("") * lpeg.Cg(lpeg.C((1-w)^1) * lpeg.Cc(true) * w^0)^1,rawset)
1675 1676
-- function xml.functions.classes(e,class) -- cache
1677
-- class = class and e.at[class] or e.at.class
1678
-- if class then
1679
-- return lpegmatch(p,class)
1680
-- else
1681
-- return { }
1682
-- end
1683
-- end
1684 1685
-- local gmatch = string.gmatch
1686 1687
-- function xml.functions.hasclass(e,c,class)
1688
-- class = class and e.at[class] or e.at.class
1689
-- if class and class ~= "" then
1690
-- if class == c then
1691
-- return true
1692
-- else
1693
-- for s in gmatch(class,"%S+") do
1694
-- if s == c then
1695
-- return true
1696
-- end
1697
-- end
1698
-- end
1699
-- end
1700
-- return false
1701
-- end
1702