xmlparser 0.6.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/MANIFEST +112 -0
- data/README +697 -0
- data/README.ja +789 -0
- data/Rakefile +34 -0
- data/ext/encoding.h +91 -0
- data/ext/xmlparser/mkrf_conf.rb +28 -0
- data/ext/xmlparser/xmlparser.c +2226 -0
- data/lib/sax.rb +1 -0
- data/lib/saxdriver.rb +1 -0
- data/lib/wget.rb +47 -0
- data/lib/xml/dom/builder-ja.rb +58 -0
- data/lib/xml/dom/builder.rb +310 -0
- data/lib/xml/dom/core.rb +3276 -0
- data/lib/xml/dom/digest.rb +94 -0
- data/lib/xml/dom/visitor.rb +182 -0
- data/lib/xml/dom2/attr.rb +213 -0
- data/lib/xml/dom2/cdatasection.rb +76 -0
- data/lib/xml/dom2/characterdata.rb +177 -0
- data/lib/xml/dom2/comment.rb +81 -0
- data/lib/xml/dom2/core.rb +19 -0
- data/lib/xml/dom2/document.rb +317 -0
- data/lib/xml/dom2/documentfragment.rb +82 -0
- data/lib/xml/dom2/documenttype.rb +102 -0
- data/lib/xml/dom2/dombuilder.rb +277 -0
- data/lib/xml/dom2/dombuilderfilter.rb +12 -0
- data/lib/xml/dom2/domentityresolver.rb +13 -0
- data/lib/xml/dom2/domentityresolverimpl.rb +37 -0
- data/lib/xml/dom2/domexception.rb +95 -0
- data/lib/xml/dom2/domimplementation.rb +61 -0
- data/lib/xml/dom2/dominputsource.rb +29 -0
- data/lib/xml/dom2/element.rb +533 -0
- data/lib/xml/dom2/entity.rb +110 -0
- data/lib/xml/dom2/entityreference.rb +107 -0
- data/lib/xml/dom2/namednodemap.rb +138 -0
- data/lib/xml/dom2/node.rb +587 -0
- data/lib/xml/dom2/nodelist.rb +231 -0
- data/lib/xml/dom2/notation.rb +86 -0
- data/lib/xml/dom2/processinginstruction.rb +155 -0
- data/lib/xml/dom2/text.rb +128 -0
- data/lib/xml/dom2/xpath.rb +398 -0
- data/lib/xml/encoding-ja.rb +42 -0
- data/lib/xml/parser.rb +13 -0
- data/lib/xml/parserns.rb +236 -0
- data/lib/xml/sax.rb +353 -0
- data/lib/xml/saxdriver.rb +370 -0
- data/lib/xml/xpath.rb +3284 -0
- data/lib/xml/xpath.ry +2352 -0
- data/lib/xmldigest.rb +1 -0
- data/lib/xmltree.rb +1 -0
- data/lib/xmltreebuilder.rb +1 -0
- data/lib/xmltreevisitor.rb +1 -0
- metadata +111 -0
data/lib/xml/xpath.ry
ADDED
@@ -0,0 +1,2352 @@
|
|
1
|
+
#
|
2
|
+
# xpath.ry
|
3
|
+
#
|
4
|
+
# Copyright (C) Ueno Katsuhiro 2000
|
5
|
+
#
|
6
|
+
# $Id: xpath.ry,v 1.2 2003/03/12 06:38:21 yoshidam Exp $
|
7
|
+
#
|
8
|
+
|
9
|
+
class Compiler
|
10
|
+
|
11
|
+
prechigh
|
12
|
+
left '|'
|
13
|
+
right NEG
|
14
|
+
left MUL 'div' 'mod'
|
15
|
+
left '+' '-'
|
16
|
+
left '<' '>' '<=' '>='
|
17
|
+
left '=' '!='
|
18
|
+
left 'and'
|
19
|
+
left 'or'
|
20
|
+
preclow
|
21
|
+
|
22
|
+
options no_result_var
|
23
|
+
|
24
|
+
|
25
|
+
rule
|
26
|
+
|
27
|
+
xPath: # none #
|
28
|
+
{ [] }
|
29
|
+
| expr
|
30
|
+
{
|
31
|
+
expr = val[0].expr('.to_ruby')
|
32
|
+
expr.collect! { |i| i or @context }
|
33
|
+
expr
|
34
|
+
}
|
35
|
+
| PATTERN pattern # for XSLT
|
36
|
+
{
|
37
|
+
expr = val[0].expr('.to_ruby')
|
38
|
+
expr.collect! { |i| i or @context }
|
39
|
+
expr
|
40
|
+
}
|
41
|
+
|
42
|
+
pattern: locationPath
|
43
|
+
| pattern '|' locationPath
|
44
|
+
{ val[0] ** val[2] }
|
45
|
+
|
46
|
+
expr: expr 'or' expr
|
47
|
+
{ val[0].logical_or val[2] }
|
48
|
+
| expr 'and' expr
|
49
|
+
{ val[0].logical_and val[2] }
|
50
|
+
| expr '=' expr
|
51
|
+
{ val[0].eq val[2] }
|
52
|
+
| expr '!=' expr
|
53
|
+
{ val[0].neq val[2] }
|
54
|
+
| expr '<' expr
|
55
|
+
{ val[0].lt val[2] }
|
56
|
+
| expr '>' expr
|
57
|
+
{ val[0].gt val[2] }
|
58
|
+
| expr '<=' expr
|
59
|
+
{ val[0].le val[2] }
|
60
|
+
| expr '>=' expr
|
61
|
+
{ val[0].ge val[2] }
|
62
|
+
| expr '+' expr
|
63
|
+
{ val[0] + val[2] }
|
64
|
+
| expr '-' expr
|
65
|
+
{ val[0] - val[2] }
|
66
|
+
| '-' expr =NEG
|
67
|
+
{ -val[1] }
|
68
|
+
| expr MUL expr
|
69
|
+
{ val[0] * val[2] }
|
70
|
+
| expr 'div' expr
|
71
|
+
{ val[0] / val[2] }
|
72
|
+
| expr 'mod' expr
|
73
|
+
{ val[0] % val[2] }
|
74
|
+
| expr '|' expr
|
75
|
+
{
|
76
|
+
# Why `**' is used for unionizing node-sets is that its
|
77
|
+
# precedence is higher than any other binary operators
|
78
|
+
# in Ruby.
|
79
|
+
val[0] ** val[2]
|
80
|
+
}
|
81
|
+
| locationPath
|
82
|
+
| filterExpr
|
83
|
+
| filterExpr '/' relPath
|
84
|
+
{ val[0] << val[2] }
|
85
|
+
| filterExpr '//' relPath
|
86
|
+
{ val[0].add_step('descendant-or-self') << val[2] }
|
87
|
+
|
88
|
+
filterExpr: Variable
|
89
|
+
{
|
90
|
+
Expression.new [ nil,'.get_variable(',val[0].dump,')' ]
|
91
|
+
}
|
92
|
+
| '(' expr ')'
|
93
|
+
{ val[1].unarize }
|
94
|
+
| Literal
|
95
|
+
{ Expression.new StringConstant.new(val[0]) }
|
96
|
+
| Number
|
97
|
+
{ Expression.new NumberConstant.new(val[0]) }
|
98
|
+
| functionCall
|
99
|
+
{ Expression.new val[0] }
|
100
|
+
| filterExpr predicate
|
101
|
+
{ val[0].add_predicate val[1] }
|
102
|
+
|
103
|
+
functionCall: FuncName '(' arguments ')'
|
104
|
+
{
|
105
|
+
val[2][0,0] = [ nil, ".funcall(#{val[0].dump}" ]
|
106
|
+
val[2].push(')')
|
107
|
+
}
|
108
|
+
|
109
|
+
arguments: # none #
|
110
|
+
{ [] }
|
111
|
+
| expr
|
112
|
+
{ val[0].expr.unshift ', ' }
|
113
|
+
| arguments ',' expr
|
114
|
+
{ val[0].push(', ').concat(val[2].expr) }
|
115
|
+
|
116
|
+
predicate: '['
|
117
|
+
{
|
118
|
+
c = @context
|
119
|
+
@context = c.succ
|
120
|
+
c
|
121
|
+
}
|
122
|
+
expr
|
123
|
+
{
|
124
|
+
c = @context
|
125
|
+
@context = _values[-2]
|
126
|
+
c
|
127
|
+
}
|
128
|
+
']'
|
129
|
+
{
|
130
|
+
expr = val[2]
|
131
|
+
valuetype = expr.value_type
|
132
|
+
value = expr.value
|
133
|
+
if valuetype == :number then
|
134
|
+
if value then
|
135
|
+
f = value.to_f
|
136
|
+
if f > 0 and f.truncate == f then
|
137
|
+
[ ".at(#{f.to_i})" ]
|
138
|
+
else
|
139
|
+
[ '.at(0)' ] # clear
|
140
|
+
end
|
141
|
+
else
|
142
|
+
expr.expr('.to_f').
|
143
|
+
unshift('.at(').push(')')
|
144
|
+
end
|
145
|
+
elsif value then
|
146
|
+
if value.true? then
|
147
|
+
[]
|
148
|
+
else
|
149
|
+
[ '.at(0)' ] # clear
|
150
|
+
end
|
151
|
+
else
|
152
|
+
c = val[3]
|
153
|
+
if valuetype == :ruby_boolean then
|
154
|
+
conv = '.true?'
|
155
|
+
else
|
156
|
+
conv = '.to_predicate'
|
157
|
+
end
|
158
|
+
a = expr.expr(conv)
|
159
|
+
a.collect! { |i| i or c }
|
160
|
+
a.unshift(".predicate { |#{c}| ").push(' }')
|
161
|
+
end
|
162
|
+
}
|
163
|
+
|
164
|
+
locationPath: '/'
|
165
|
+
{ LocationPath.new.absolute! }
|
166
|
+
| '/' relPath
|
167
|
+
{ val[1].absolute! }
|
168
|
+
| '//' relPath
|
169
|
+
{
|
170
|
+
path = LocationPath.new
|
171
|
+
path.absolute!
|
172
|
+
path.add_step('descendant-or-self') << val[1]
|
173
|
+
}
|
174
|
+
| relPath
|
175
|
+
|
176
|
+
relPath: step
|
177
|
+
{ LocationPath.new.add_step(*val[0]) }
|
178
|
+
| relPath '/' step
|
179
|
+
{ val[0].add_step(*val[2]) }
|
180
|
+
| relPath '//' step
|
181
|
+
{
|
182
|
+
val[0].add_step('descendant-or-self').add_step(*val[2])
|
183
|
+
}
|
184
|
+
# XPath does not permit functions here, but XPointer does.
|
185
|
+
| relPath '/' FuncName '('
|
186
|
+
{
|
187
|
+
c = @context
|
188
|
+
@context = c.succ
|
189
|
+
c
|
190
|
+
}
|
191
|
+
arguments
|
192
|
+
{
|
193
|
+
c = @context
|
194
|
+
@context = _values[-2]
|
195
|
+
c
|
196
|
+
}
|
197
|
+
')'
|
198
|
+
{
|
199
|
+
on_error unless is_xpointer?
|
200
|
+
args = val[5]
|
201
|
+
c = val[6]
|
202
|
+
args.collect! { |i| i or c }
|
203
|
+
args[0] = ".funcall(#{val[2].dump}) { |#{c}| ["
|
204
|
+
args.push '] }'
|
205
|
+
val[0].add_predicate args
|
206
|
+
}
|
207
|
+
|
208
|
+
step: '.'
|
209
|
+
{ [ 'self', false, false, false, nil ] }
|
210
|
+
| '..'
|
211
|
+
{ [ 'parent', false, false, false, nil ] }
|
212
|
+
| axisSpec nodeTest predicates
|
213
|
+
{
|
214
|
+
nodetest = val[1]
|
215
|
+
unless nodetest[0] then
|
216
|
+
axis = val[0]
|
217
|
+
if axis != 'attribute' and axis != 'namespace' then
|
218
|
+
nodetest[0] = 'element'
|
219
|
+
end
|
220
|
+
end
|
221
|
+
nodetest[0] = false if nodetest[0] == 'node'
|
222
|
+
nodetest.unshift(val[0]).push(val[2])
|
223
|
+
}
|
224
|
+
|
225
|
+
predicates: # none #
|
226
|
+
| predicates predicate
|
227
|
+
{ (val[0] || []).concat val[1] }
|
228
|
+
|
229
|
+
nodeTest: '*'
|
230
|
+
{ [ false, false, false ] }
|
231
|
+
| Name
|
232
|
+
{
|
233
|
+
if /:/ =~ val[0] then
|
234
|
+
[ false, $', $` ] #' <= for racc
|
235
|
+
else
|
236
|
+
[ false, val[0], nil ]
|
237
|
+
end
|
238
|
+
}
|
239
|
+
| Name ':' '*'
|
240
|
+
{
|
241
|
+
on_error if /:/ =~ val[0]
|
242
|
+
[ false, false, val[0] ]
|
243
|
+
}
|
244
|
+
| NodeType '(' nodeTestArg ')'
|
245
|
+
{
|
246
|
+
nodetype = val[0]
|
247
|
+
arg = val[2]
|
248
|
+
if arg and nodetype != 'processing-instruction' then
|
249
|
+
raise CompileError,
|
250
|
+
"nodetest #{nodetype}() requires no argument"
|
251
|
+
end
|
252
|
+
[ nodetype, arg || false, false ]
|
253
|
+
}
|
254
|
+
|
255
|
+
nodeTestArg: # none #
|
256
|
+
| Literal
|
257
|
+
|
258
|
+
axisSpec: # none #
|
259
|
+
{ 'child' }
|
260
|
+
| '@'
|
261
|
+
{ 'attribute' }
|
262
|
+
| AxisName '::'
|
263
|
+
|
264
|
+
end
|
265
|
+
|
266
|
+
|
267
|
+
---- inner ----
|
268
|
+
|
269
|
+
module CompilePhaseObject
|
270
|
+
|
271
|
+
def invoke_conv(expr, conv_method)
|
272
|
+
return unless conv_method
|
273
|
+
if conv_method == '.to_number' or
|
274
|
+
conv_method == '.to_string' or
|
275
|
+
conv_method == '.to_boolean' then
|
276
|
+
expr.push conv_method, '(', nil, ')'
|
277
|
+
else
|
278
|
+
expr.push conv_method
|
279
|
+
end
|
280
|
+
end
|
281
|
+
private :invoke_conv
|
282
|
+
|
283
|
+
end
|
284
|
+
|
285
|
+
|
286
|
+
module ConstantObject
|
287
|
+
|
288
|
+
include CompilePhaseObject
|
289
|
+
|
290
|
+
def to_string
|
291
|
+
StringConstant.new to_str
|
292
|
+
end
|
293
|
+
|
294
|
+
def to_number
|
295
|
+
NumberConstant.new self
|
296
|
+
end
|
297
|
+
|
298
|
+
def to_boolean
|
299
|
+
if true? then
|
300
|
+
ConstantTrue
|
301
|
+
else
|
302
|
+
ConstantFalse
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
end
|
307
|
+
|
308
|
+
|
309
|
+
module BooleanConstant
|
310
|
+
|
311
|
+
include ConstantObject
|
312
|
+
|
313
|
+
def value_type
|
314
|
+
:boolean
|
315
|
+
end
|
316
|
+
|
317
|
+
def expr(conv_method = nil)
|
318
|
+
if conv_method == '.to_ruby' or conv_method == '.true?' then
|
319
|
+
[ true?.to_s ]
|
320
|
+
else
|
321
|
+
ret = [ nil, '.make_boolean(', true?.to_s, ')' ]
|
322
|
+
invoke_conv ret, conv_method unless conv_method == '.to_boolean'
|
323
|
+
ret
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
end
|
328
|
+
|
329
|
+
class ConstantTrueClass < XPathTrueClass
|
330
|
+
include BooleanConstant
|
331
|
+
@instance = new
|
332
|
+
end
|
333
|
+
|
334
|
+
class ConstantFalseClass < XPathFalseClass
|
335
|
+
include BooleanConstant
|
336
|
+
@instance = new
|
337
|
+
end
|
338
|
+
|
339
|
+
ConstantTrue = ConstantTrueClass.instance
|
340
|
+
ConstantFalse = ConstantFalseClass.instance
|
341
|
+
|
342
|
+
|
343
|
+
|
344
|
+
class NumberConstant < XPathNumber
|
345
|
+
|
346
|
+
include ConstantObject
|
347
|
+
|
348
|
+
def value_type
|
349
|
+
:number
|
350
|
+
end
|
351
|
+
|
352
|
+
def initialize(src)
|
353
|
+
f = src.to_f
|
354
|
+
if src.is_a? ConstantObject and s = dump_float(f) then
|
355
|
+
src = s
|
356
|
+
end
|
357
|
+
@src = [ src ]
|
358
|
+
@precedence = 1
|
359
|
+
super f
|
360
|
+
end
|
361
|
+
|
362
|
+
attr_reader :precedence
|
363
|
+
protected :precedence
|
364
|
+
|
365
|
+
def to_number
|
366
|
+
self
|
367
|
+
end
|
368
|
+
|
369
|
+
|
370
|
+
def expr(conv_method = nil)
|
371
|
+
@src.collect! { |i|
|
372
|
+
if i.is_a? ConstantObject then
|
373
|
+
i.expr '.to_f'
|
374
|
+
else
|
375
|
+
i
|
376
|
+
end
|
377
|
+
}
|
378
|
+
expr = @src
|
379
|
+
expr.flatten!
|
380
|
+
@src = :draff # for debug
|
381
|
+
unless conv_method == '.to_ruby' or conv_method == '.to_f' then
|
382
|
+
expr[0, 0] = [ nil, '.make_number(' ]
|
383
|
+
expr.push(')')
|
384
|
+
invoke_conv expr, conv_method unless conv_method == '.to_number'
|
385
|
+
end
|
386
|
+
expr
|
387
|
+
end
|
388
|
+
|
389
|
+
|
390
|
+
private
|
391
|
+
|
392
|
+
def dump_float(f)
|
393
|
+
if f.finite? and f == eval(s = f.to_s) then
|
394
|
+
s
|
395
|
+
elsif f.infinite? then
|
396
|
+
if f > 0 then
|
397
|
+
'(1.0 / 0.0)'
|
398
|
+
else
|
399
|
+
'(-1.0 / 0.0)'
|
400
|
+
end
|
401
|
+
elsif f.nan? then
|
402
|
+
'(0.0 / 0.0)'
|
403
|
+
else
|
404
|
+
nil
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
|
409
|
+
def concat(op, other, prec)
|
410
|
+
@src.unshift('(').push(')') if @precedence < prec
|
411
|
+
if other.precedence < prec then
|
412
|
+
@src.push(op).push('(').concat(other.expr('.to_f')).push(')')
|
413
|
+
else
|
414
|
+
@src.push(op).concat(other.expr('.to_f'))
|
415
|
+
end
|
416
|
+
@precedence = prec
|
417
|
+
end
|
418
|
+
|
419
|
+
|
420
|
+
public
|
421
|
+
|
422
|
+
def self.def_arithmetic_operator(op, precedence)
|
423
|
+
module_eval <<_, __FILE__, __LINE__ + 1
|
424
|
+
def #{op}(other)
|
425
|
+
super other
|
426
|
+
if s = dump_float(@value) then
|
427
|
+
@src.clear
|
428
|
+
@src.push s
|
429
|
+
else
|
430
|
+
concat ' #{op} ', other, #{precedence}
|
431
|
+
end
|
432
|
+
self
|
433
|
+
end
|
434
|
+
_
|
435
|
+
end
|
436
|
+
|
437
|
+
def_arithmetic_operator '+', 0
|
438
|
+
def_arithmetic_operator '-', 0
|
439
|
+
def_arithmetic_operator '*', 1
|
440
|
+
def_arithmetic_operator '/', 1
|
441
|
+
|
442
|
+
class << self
|
443
|
+
undef def_arithmetic_operator
|
444
|
+
end
|
445
|
+
|
446
|
+
def %(other)
|
447
|
+
orig = @value
|
448
|
+
super other
|
449
|
+
if s = dump_float(@value) then
|
450
|
+
@src.clear
|
451
|
+
@src.push s
|
452
|
+
else
|
453
|
+
f = other.to_f
|
454
|
+
other = -other if orig % f == -@value
|
455
|
+
concat ' % ', other, 1
|
456
|
+
end
|
457
|
+
self
|
458
|
+
end
|
459
|
+
|
460
|
+
def -@
|
461
|
+
super
|
462
|
+
if s = dump_float(@value) then
|
463
|
+
@src.clear
|
464
|
+
@src.push s
|
465
|
+
else
|
466
|
+
if @src.size == 1 then
|
467
|
+
@src.unshift '-'
|
468
|
+
else
|
469
|
+
@src.unshift('-(').push(')')
|
470
|
+
end
|
471
|
+
@precedence = 1
|
472
|
+
end
|
473
|
+
self
|
474
|
+
end
|
475
|
+
|
476
|
+
end
|
477
|
+
|
478
|
+
|
479
|
+
|
480
|
+
class StringConstant < XPathString
|
481
|
+
|
482
|
+
include ConstantObject
|
483
|
+
|
484
|
+
def value_type
|
485
|
+
:string
|
486
|
+
end
|
487
|
+
|
488
|
+
def to_string
|
489
|
+
self
|
490
|
+
end
|
491
|
+
|
492
|
+
def expr(conv_method = nil)
|
493
|
+
if conv_method == '.to_ruby' or conv_method == '.to_str' then
|
494
|
+
[ @value.dump ]
|
495
|
+
else
|
496
|
+
ret = [ nil, '.make_string(', @value.dump, ')' ]
|
497
|
+
invoke_conv ret, conv_method unless conv_method == '.to_string'
|
498
|
+
ret
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
end
|
503
|
+
|
504
|
+
|
505
|
+
|
506
|
+
class Expression
|
507
|
+
|
508
|
+
include CompilePhaseObject
|
509
|
+
|
510
|
+
def initialize(expr)
|
511
|
+
if expr.is_a? ConstantObject then
|
512
|
+
@value = expr
|
513
|
+
else
|
514
|
+
raise "BUG" unless expr.is_a? Array
|
515
|
+
@value = nil
|
516
|
+
@valuetype = nil
|
517
|
+
@expr = expr
|
518
|
+
end
|
519
|
+
@unary = true
|
520
|
+
end
|
521
|
+
|
522
|
+
attr_reader :value
|
523
|
+
|
524
|
+
|
525
|
+
def value_type
|
526
|
+
if @value then
|
527
|
+
@value.value_type
|
528
|
+
else
|
529
|
+
@valuetype
|
530
|
+
end
|
531
|
+
end
|
532
|
+
|
533
|
+
|
534
|
+
def unarize
|
535
|
+
unless @unary then
|
536
|
+
@expr.unshift('(').push(')')
|
537
|
+
@unary = true
|
538
|
+
end
|
539
|
+
self
|
540
|
+
end
|
541
|
+
|
542
|
+
|
543
|
+
def self.def_comparison_operator(name, op)
|
544
|
+
module_eval <<_, __FILE__, __LINE__ + 1
|
545
|
+
def #{name}(other)
|
546
|
+
if @value and other.value then
|
547
|
+
if @value #{op} other.value then
|
548
|
+
@value = ConstantTrue
|
549
|
+
else
|
550
|
+
@value = ConstantFalse
|
551
|
+
end
|
552
|
+
@unary = true
|
553
|
+
else
|
554
|
+
@expr = expr.push(' #{op} ').concat(other.expr)
|
555
|
+
@valuetype = :ruby_boolean
|
556
|
+
@unary = false
|
557
|
+
end
|
558
|
+
self
|
559
|
+
end
|
560
|
+
_
|
561
|
+
end
|
562
|
+
|
563
|
+
def self.def_arithmetic_operator(*ops)
|
564
|
+
ops.each { |op|
|
565
|
+
module_eval <<_, __FILE__, __LINE__ + 1
|
566
|
+
def #{op}(other)
|
567
|
+
if @value and other.value then
|
568
|
+
@value = @value.to_number #{op} other.value.to_number
|
569
|
+
else
|
570
|
+
@expr = expr('.to_number').push(' #{op} ')
|
571
|
+
# not 'to_number', for a little speed up :-)
|
572
|
+
@expr.concat other.expr('.to_f')
|
573
|
+
@valuetype = :number
|
574
|
+
@unary = false
|
575
|
+
end
|
576
|
+
self
|
577
|
+
end
|
578
|
+
_
|
579
|
+
}
|
580
|
+
end
|
581
|
+
|
582
|
+
def_comparison_operator 'eq', '=='
|
583
|
+
def_comparison_operator 'neq', '!='
|
584
|
+
def_comparison_operator 'lt', '<'
|
585
|
+
def_comparison_operator 'gt', '>'
|
586
|
+
def_comparison_operator 'le', '<='
|
587
|
+
def_comparison_operator 'ge', '>='
|
588
|
+
def_arithmetic_operator '+', '-', '*', '/', '%'
|
589
|
+
|
590
|
+
class << self
|
591
|
+
undef def_comparison_operator
|
592
|
+
undef def_arithmetic_operator
|
593
|
+
end
|
594
|
+
|
595
|
+
def -@
|
596
|
+
if @value then
|
597
|
+
@value = -@value.to_number
|
598
|
+
else
|
599
|
+
unarize
|
600
|
+
@expr = expr('.to_number').unshift('-')
|
601
|
+
end
|
602
|
+
self
|
603
|
+
end
|
604
|
+
|
605
|
+
def logical_or(other)
|
606
|
+
if @value and @value.true? then
|
607
|
+
@value = ConstantTrue
|
608
|
+
@unary = true
|
609
|
+
@expr = @valuetype = nil
|
610
|
+
else
|
611
|
+
@expr = expr('.true?').push(' || ').concat(other.expr('.true?'))
|
612
|
+
@valuetype = :ruby_boolean
|
613
|
+
@unary = false
|
614
|
+
end
|
615
|
+
self
|
616
|
+
end
|
617
|
+
|
618
|
+
def logical_and(other)
|
619
|
+
if @value and not @value.true? then
|
620
|
+
@value = ConstantFalse
|
621
|
+
@unary = true
|
622
|
+
@expr = @valuetype = nil
|
623
|
+
else
|
624
|
+
@expr = expr('.true?').push(' && ').concat(other.expr('.true?'))
|
625
|
+
@valuetype = :ruby_boolean
|
626
|
+
@unary = false
|
627
|
+
end
|
628
|
+
self
|
629
|
+
end
|
630
|
+
|
631
|
+
def **(other)
|
632
|
+
@expr = expr.push(' ** ').concat(other.expr)
|
633
|
+
@valuetype = nil
|
634
|
+
@unary = false
|
635
|
+
self
|
636
|
+
end
|
637
|
+
|
638
|
+
|
639
|
+
def add_predicate(pred)
|
640
|
+
unarize
|
641
|
+
@expr = expr.concat(pred)
|
642
|
+
@valuetype = nil
|
643
|
+
self
|
644
|
+
end
|
645
|
+
|
646
|
+
def <<(other)
|
647
|
+
path = other.expr
|
648
|
+
path.shift # nil
|
649
|
+
path.shift # .to_nodeset
|
650
|
+
add_predicate path
|
651
|
+
end
|
652
|
+
|
653
|
+
def add_step(axis)
|
654
|
+
add_predicate [ ".step(:#{axis.tr('-','_')})" ]
|
655
|
+
end
|
656
|
+
|
657
|
+
|
658
|
+
def expr(conv_method = nil)
|
659
|
+
if @value then
|
660
|
+
ret = @value.expr(conv_method)
|
661
|
+
@value = nil
|
662
|
+
elsif @valuetype == :ruby_boolean then
|
663
|
+
ret = @expr
|
664
|
+
unless conv_method == '.to_ruby' or conv_method == '.true?' then
|
665
|
+
ret[0, 0] = [ nil, '.make_boolean(' ]
|
666
|
+
ret.push ')'
|
667
|
+
invoke_conv ret, conv_method unless conv_method == '.to_boolean'
|
668
|
+
end
|
669
|
+
elsif @valuetype == :number and conv_method == '.to_number' then
|
670
|
+
ret = @expr
|
671
|
+
elsif @valuetype == :string and conv_method == '.to_string' then
|
672
|
+
ret = @expr
|
673
|
+
elsif @valuetype == :boolean and conv_method == '.to_boolean' then
|
674
|
+
ret = @expr
|
675
|
+
else
|
676
|
+
if conv_method then
|
677
|
+
unarize
|
678
|
+
invoke_conv @expr, conv_method
|
679
|
+
end
|
680
|
+
ret = @expr
|
681
|
+
end
|
682
|
+
@expr = :draff # for debug
|
683
|
+
ret
|
684
|
+
end
|
685
|
+
|
686
|
+
end
|
687
|
+
|
688
|
+
|
689
|
+
|
690
|
+
class LocationPath
|
691
|
+
|
692
|
+
include CompilePhaseObject
|
693
|
+
|
694
|
+
def initialize
|
695
|
+
@root = false
|
696
|
+
@steps = [] # [ axis, [ tests ], predicates ]
|
697
|
+
end
|
698
|
+
|
699
|
+
attr_reader :root, :steps
|
700
|
+
protected :root, :steps
|
701
|
+
|
702
|
+
def absolute!
|
703
|
+
@root = true
|
704
|
+
self
|
705
|
+
end
|
706
|
+
|
707
|
+
|
708
|
+
def add_step(axis, nodetype = false, localpart = false,
|
709
|
+
namespace = false, predicate = nil)
|
710
|
+
if nodetype == false and localpart == false and namespace == false then
|
711
|
+
append_step axis, [], predicate
|
712
|
+
else
|
713
|
+
append_step axis, [ [ nodetype, localpart, namespace ] ], predicate
|
714
|
+
end
|
715
|
+
self
|
716
|
+
end
|
717
|
+
|
718
|
+
|
719
|
+
def <<(other)
|
720
|
+
raise "BUG" if other.root
|
721
|
+
other = other.steps
|
722
|
+
other.each { |step|
|
723
|
+
if step[0] then
|
724
|
+
append_step(*step)
|
725
|
+
else
|
726
|
+
add_predicate(step[2])
|
727
|
+
end
|
728
|
+
}
|
729
|
+
self
|
730
|
+
end
|
731
|
+
|
732
|
+
|
733
|
+
def add_predicate(pred)
|
734
|
+
@steps.push [ nil, nil, pred ]
|
735
|
+
self
|
736
|
+
end
|
737
|
+
|
738
|
+
|
739
|
+
def **(other)
|
740
|
+
unless other.is_a? LocationPath then
|
741
|
+
ret = nil
|
742
|
+
else
|
743
|
+
othersteps = other.steps
|
744
|
+
size = @steps.size
|
745
|
+
unless size == othersteps.size then
|
746
|
+
othersize = othersteps.size
|
747
|
+
if size >= othersize then
|
748
|
+
ret = (@steps[0, othersize] == othersize and self)
|
749
|
+
else
|
750
|
+
ret = (othersteps[0, size] == @steps and other)
|
751
|
+
end
|
752
|
+
else
|
753
|
+
last = @steps.pop
|
754
|
+
otherlast = othersteps.pop
|
755
|
+
if @steps == othersteps and mix_step(last, otherlast) then
|
756
|
+
ret = self
|
757
|
+
else
|
758
|
+
ret = nil
|
759
|
+
end
|
760
|
+
@steps.push last
|
761
|
+
othersteps.push otherlast
|
762
|
+
end
|
763
|
+
end
|
764
|
+
ret or Expression.new(expr) ** other
|
765
|
+
end
|
766
|
+
|
767
|
+
|
768
|
+
private
|
769
|
+
|
770
|
+
UnifiableAxes = {
|
771
|
+
'descendant' => {
|
772
|
+
'descendant-or-self' => 'descendant',
|
773
|
+
},
|
774
|
+
'descendant-or-self' => {
|
775
|
+
'child' => 'descendant',
|
776
|
+
'descendant' => 'descendant',
|
777
|
+
'descendant-or-self' => 'descendant-or-self',
|
778
|
+
},
|
779
|
+
'ancestor' => {
|
780
|
+
'ancestor-or-self' => 'ancestor',
|
781
|
+
},
|
782
|
+
'ancestor-or-self' => {
|
783
|
+
'parent' => 'ancestor',
|
784
|
+
'ancestor' => 'ancestor',
|
785
|
+
'ancestor-or-self' => 'ancestor-or-self',
|
786
|
+
},
|
787
|
+
'following-sibling' => {
|
788
|
+
'following-sibling' => 'following-sibling',
|
789
|
+
},
|
790
|
+
'preceding-sibling' => {
|
791
|
+
'preceding-sibling' => 'preceding-sibling',
|
792
|
+
},
|
793
|
+
'following' => {
|
794
|
+
'following' => 'following',
|
795
|
+
'following-sibling' => 'following',
|
796
|
+
},
|
797
|
+
'preceding' => {
|
798
|
+
'preceding' => 'preceding',
|
799
|
+
'preceding-sibling' => 'preceding',
|
800
|
+
},
|
801
|
+
'child' => {
|
802
|
+
'following-sibling' => 'child',
|
803
|
+
'preceding-sibling' => 'child',
|
804
|
+
},
|
805
|
+
}
|
806
|
+
UnifiableAxes.default = {}
|
807
|
+
|
808
|
+
|
809
|
+
def append_step(axis, test, predicate)
|
810
|
+
lastaxis, lasttest, lastpred = laststep = @steps.last
|
811
|
+
if axis == 'self' and test.empty? then
|
812
|
+
@steps.push [ nil, nil, predicate ] if predicate
|
813
|
+
elsif lastaxis and lasttest.empty? and
|
814
|
+
not lastpred and not predicate and
|
815
|
+
w = UnifiableAxes[lastaxis][axis] then
|
816
|
+
laststep[0] = w
|
817
|
+
laststep[1] = test
|
818
|
+
else
|
819
|
+
@steps.push [ axis, test, predicate ]
|
820
|
+
end
|
821
|
+
end
|
822
|
+
|
823
|
+
|
824
|
+
def mix_step(step, other)
|
825
|
+
if step[0] and step[0] == other[0] and step[2] == other[2] then
|
826
|
+
step[1].concat other[1]
|
827
|
+
step
|
828
|
+
else
|
829
|
+
nil
|
830
|
+
end
|
831
|
+
end
|
832
|
+
|
833
|
+
|
834
|
+
public
|
835
|
+
|
836
|
+
def expr(conv_method = nil)
|
837
|
+
if @root then
|
838
|
+
expr = [ nil, '.root_nodeset' ]
|
839
|
+
else
|
840
|
+
expr = [ nil, '.to_nodeset' ]
|
841
|
+
end
|
842
|
+
@steps.each { |axis,test,predicate|
|
843
|
+
if axis.nil? then # predicate only
|
844
|
+
expr.concat predicate
|
845
|
+
elsif test.empty? and not predicate then
|
846
|
+
expr.push ".select_all(:#{axis.tr('-','_')})"
|
847
|
+
else
|
848
|
+
expr.push ".step(:#{axis.tr('-','_')})"
|
849
|
+
if test.empty? then
|
850
|
+
expr.push ' { |n| n.select_all'
|
851
|
+
else
|
852
|
+
expr.push ' { |n| n.select { |i| '
|
853
|
+
test.each { |nodetype,localpart,namespace|
|
854
|
+
if nodetype then
|
855
|
+
expr.push "i.node_type == :#{nodetype.tr('-','_')}", ' && '
|
856
|
+
end
|
857
|
+
if localpart then
|
858
|
+
expr.push "i.name_localpart == #{localpart.dump}", ' && '
|
859
|
+
end
|
860
|
+
if namespace.nil? then
|
861
|
+
expr.push 'i.namespace_uri.nil?', ' && '
|
862
|
+
elsif namespace then
|
863
|
+
namespace = namespace.dump
|
864
|
+
expr.push('i.namespace_uri == ', nil,
|
865
|
+
".get_namespace(#{namespace})", ' && ')
|
866
|
+
end
|
867
|
+
expr[-1] = ' or '
|
868
|
+
}
|
869
|
+
expr[-1] = ' }'
|
870
|
+
end
|
871
|
+
expr.concat predicate if predicate
|
872
|
+
expr.push ' }'
|
873
|
+
end
|
874
|
+
}
|
875
|
+
@steps = :draff # for debug
|
876
|
+
invoke_conv expr, conv_method
|
877
|
+
expr
|
878
|
+
end
|
879
|
+
|
880
|
+
|
881
|
+
def value_type
|
882
|
+
nil
|
883
|
+
end
|
884
|
+
|
885
|
+
def value
|
886
|
+
nil
|
887
|
+
end
|
888
|
+
|
889
|
+
def unarize
|
890
|
+
self
|
891
|
+
end
|
892
|
+
|
893
|
+
def self.redirect_to_expr(*ops)
|
894
|
+
ops.each { |op|
|
895
|
+
name = op
|
896
|
+
name = op[1..-1] if op[0] == ?.
|
897
|
+
module_eval <<_, __FILE__, __LINE__ + 1
|
898
|
+
def #{name}(arg) ; Expression.new(expr) #{op} arg ; end
|
899
|
+
_
|
900
|
+
}
|
901
|
+
end
|
902
|
+
|
903
|
+
redirect_to_expr('.eq', '.neq', '.lt', '.gt', '.le', '.ge',
|
904
|
+
'+', '-', '*', '/', '%', '.logical_or', '.logical_and')
|
905
|
+
|
906
|
+
class << self
|
907
|
+
undef redirect_to_expr
|
908
|
+
end
|
909
|
+
|
910
|
+
def -@
|
911
|
+
-Expression.new(expr)
|
912
|
+
end
|
913
|
+
|
914
|
+
end
|
915
|
+
|
916
|
+
|
917
|
+
|
918
|
+
|
919
|
+
Delim = '\\s\\(\\)\\[\\]\\.@,\\/\\|\\*\\+"\'=!<>:'
|
920
|
+
Name = "[^-#{Delim}][^#{Delim}]*"
|
921
|
+
|
922
|
+
Operator = {
|
923
|
+
'@' => true, '::' => true, '(' => true, '[' => true,
|
924
|
+
:MUL => true, 'and' => true, 'or' => true, 'mod' => true, 'div' => true,
|
925
|
+
'/' => true, '//' => true, '|' => true, '+' => true,
|
926
|
+
'-' => true, '=' => true, '!=' => true, '<' => true,
|
927
|
+
'<=' => true, '>' => true, '>=' => true,
|
928
|
+
':' => false
|
929
|
+
# ':' '*' => '*' must not be a MultiplyOperator
|
930
|
+
# ':' 'and' => 'and' must be a OperatorName
|
931
|
+
}
|
932
|
+
|
933
|
+
NodeType = {
|
934
|
+
'comment' => true,
|
935
|
+
'text' => true,
|
936
|
+
'processing-instruction' => true,
|
937
|
+
'node' => true,
|
938
|
+
}
|
939
|
+
|
940
|
+
|
941
|
+
private
|
942
|
+
|
943
|
+
def axis?(s)
|
944
|
+
/\A[-a-zA-Z]+\z/ =~ s
|
945
|
+
end
|
946
|
+
|
947
|
+
def nodetype?(s)
|
948
|
+
NodeType.key? s
|
949
|
+
end
|
950
|
+
|
951
|
+
def tokenize(src)
|
952
|
+
token = []
|
953
|
+
src.scan(/(\.\.?|\/\/?|::?|!=|[<>]=?|[-()\[\].@,|+=*])|
|
954
|
+
("[^"]*"|'[^']*')|(\d+\.?\d*)|
|
955
|
+
(\$?#{Name}(?::#{Name})?)|
|
956
|
+
\s+|./ox) { |delim,literal,number,name| #/
|
957
|
+
if delim then
|
958
|
+
if delim == '*' then
|
959
|
+
delim = :MUL if (prev = token[-1]) and not Operator.key? prev[0]
|
960
|
+
elsif delim == '::' then
|
961
|
+
prev = token[-1]
|
962
|
+
if prev and prev[0] == :Name and axis? prev[1] then
|
963
|
+
prev[0] = :AxisName
|
964
|
+
end
|
965
|
+
elsif delim == '(' then
|
966
|
+
if (prev = token[-1]) and prev[0] == :Name then
|
967
|
+
if nodetype? prev[1] then
|
968
|
+
prev[0] = :NodeType
|
969
|
+
else
|
970
|
+
prev[0] = :FuncName
|
971
|
+
end
|
972
|
+
end
|
973
|
+
end
|
974
|
+
token.push [ delim, delim ]
|
975
|
+
elsif name then
|
976
|
+
prev = token[-1]
|
977
|
+
if name[0] == ?$ then
|
978
|
+
name[0,1] = ''
|
979
|
+
token.push [ :Variable, name ]
|
980
|
+
elsif Operator.key? name and
|
981
|
+
(prev = token[-1]) and not Operator[prev[0]] then
|
982
|
+
token.push [ name, name ]
|
983
|
+
else
|
984
|
+
token.push [ :Name, name ]
|
985
|
+
end
|
986
|
+
elsif number then
|
987
|
+
number << '.0' unless number.include? ?.
|
988
|
+
token.push [ :Number, number ]
|
989
|
+
elsif literal then
|
990
|
+
literal.chop!
|
991
|
+
literal[0,1] = ''
|
992
|
+
token.push [ :Literal, literal ]
|
993
|
+
else
|
994
|
+
s = $&.strip
|
995
|
+
token.push [ s, s ] unless s.empty?
|
996
|
+
end
|
997
|
+
}
|
998
|
+
token
|
999
|
+
end
|
1000
|
+
|
1001
|
+
|
1002
|
+
public
|
1003
|
+
|
1004
|
+
def compile(src, pattern = false)
|
1005
|
+
@token = tokenize(src)
|
1006
|
+
@token.push [ false, :end ]
|
1007
|
+
@token.each { |i| p i } if @yydebug
|
1008
|
+
@token.reverse!
|
1009
|
+
@token.push [ :PATTERN, nil ] if pattern
|
1010
|
+
@context = 'context0'
|
1011
|
+
ret = do_parse
|
1012
|
+
ret = ret.unshift("proc { |context0| ").push(" }").join
|
1013
|
+
print ">>>>\n", ret, "\n<<<<\n" if @yydebug
|
1014
|
+
XPathProc.new eval(ret), src
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
|
1018
|
+
def initialize(debug = false)
|
1019
|
+
super()
|
1020
|
+
@yydebug = debug
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
|
1024
|
+
private
|
1025
|
+
|
1026
|
+
def next_token
|
1027
|
+
@token.pop
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
|
1031
|
+
def is_xpointer?
|
1032
|
+
false
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
|
1036
|
+
def on_error(*args) # tok, val, values
|
1037
|
+
raise CompileError, 'parse error'
|
1038
|
+
end
|
1039
|
+
|
1040
|
+
|
1041
|
+
---- header ----
|
1042
|
+
#
|
1043
|
+
# xpath.rb : generated by racc
|
1044
|
+
#
|
1045
|
+
|
1046
|
+
module XPath
|
1047
|
+
|
1048
|
+
class Error < StandardError ; end
|
1049
|
+
class CompileError < Error ; end
|
1050
|
+
class TypeError < Error ; end
|
1051
|
+
class NameError < Error ; end
|
1052
|
+
class ArgumentError < Error ; end
|
1053
|
+
class InvalidOperation < Error ; end
|
1054
|
+
|
1055
|
+
|
1056
|
+
class XPathProc
|
1057
|
+
|
1058
|
+
def initialize(proc, source)
|
1059
|
+
@proc = proc
|
1060
|
+
@source = source
|
1061
|
+
end
|
1062
|
+
|
1063
|
+
attr_reader :source
|
1064
|
+
|
1065
|
+
def call(context)
|
1066
|
+
@proc.call context
|
1067
|
+
end
|
1068
|
+
|
1069
|
+
end
|
1070
|
+
|
1071
|
+
|
1072
|
+
def self.compile(src, pattern = false)
|
1073
|
+
@compiler = Compiler.new unless defined? @compiler
|
1074
|
+
@compiler.compile src, pattern
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
|
1078
|
+
|
1079
|
+
module XPathObject
|
1080
|
+
|
1081
|
+
def _type
|
1082
|
+
type.name.sub(/\A.*::(?:XPath)?(?=[^:]+\z)/, '')
|
1083
|
+
end
|
1084
|
+
private :_type
|
1085
|
+
|
1086
|
+
def type_error(into)
|
1087
|
+
raise XPath::TypeError, "failed to convert #{_type} into #{into}"
|
1088
|
+
end
|
1089
|
+
private :type_error
|
1090
|
+
|
1091
|
+
|
1092
|
+
def to_str # => to Ruby String
|
1093
|
+
type_error 'String'
|
1094
|
+
end
|
1095
|
+
|
1096
|
+
def to_f # => to Ruby Float
|
1097
|
+
type_error 'Float'
|
1098
|
+
end
|
1099
|
+
|
1100
|
+
def true? # => to Ruby Boolean
|
1101
|
+
type_error 'Boolean'
|
1102
|
+
end
|
1103
|
+
|
1104
|
+
def to_ruby # => to Ruby Object
|
1105
|
+
self
|
1106
|
+
end
|
1107
|
+
|
1108
|
+
def to_predicate # => to Ruby Float, true or false. shouldn't override.
|
1109
|
+
true?
|
1110
|
+
end
|
1111
|
+
|
1112
|
+
|
1113
|
+
def to_string(context) # => to XPath String. shouldn't override.
|
1114
|
+
context.make_string to_str
|
1115
|
+
end
|
1116
|
+
|
1117
|
+
def to_number(context) # => to XPath Number. shouldn't override.
|
1118
|
+
context.make_number to_f
|
1119
|
+
end
|
1120
|
+
|
1121
|
+
def to_boolean(context) # => to XPath Boolean. shouldn't override.
|
1122
|
+
context.make_boolean true?
|
1123
|
+
end
|
1124
|
+
|
1125
|
+
|
1126
|
+
public
|
1127
|
+
|
1128
|
+
# called from compiled XPath expression
|
1129
|
+
|
1130
|
+
def ==(other)
|
1131
|
+
if other.is_a? XPathNodeSet or
|
1132
|
+
other.is_a? XPathBoolean or other.is_a? XPathNumber then
|
1133
|
+
other == self
|
1134
|
+
else
|
1135
|
+
to_str == other.to_str
|
1136
|
+
end
|
1137
|
+
end
|
1138
|
+
|
1139
|
+
def <(other)
|
1140
|
+
if other.is_a? XPathNodeSet then
|
1141
|
+
other > self
|
1142
|
+
else
|
1143
|
+
to_f < other.to_f
|
1144
|
+
end
|
1145
|
+
end
|
1146
|
+
|
1147
|
+
def >(other)
|
1148
|
+
if other.is_a? XPathNodeSet then
|
1149
|
+
other < self
|
1150
|
+
else
|
1151
|
+
to_f > other.to_f
|
1152
|
+
end
|
1153
|
+
end
|
1154
|
+
|
1155
|
+
def <=(other)
|
1156
|
+
if other.is_a? XPathNodeSet then
|
1157
|
+
other >= self
|
1158
|
+
else
|
1159
|
+
to_f <= other.to_f
|
1160
|
+
end
|
1161
|
+
end
|
1162
|
+
|
1163
|
+
def >=(other)
|
1164
|
+
if other.is_a? XPathNodeSet then
|
1165
|
+
other <= self
|
1166
|
+
else
|
1167
|
+
to_f >= other.to_f
|
1168
|
+
end
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
def **(other)
|
1172
|
+
type_error 'NodeSet'
|
1173
|
+
end
|
1174
|
+
|
1175
|
+
def predicate(&block)
|
1176
|
+
type_error 'NodeSet'
|
1177
|
+
end
|
1178
|
+
|
1179
|
+
def at(pos)
|
1180
|
+
type_error 'NodeSet'
|
1181
|
+
end
|
1182
|
+
|
1183
|
+
def funcall(name) # for XPointer
|
1184
|
+
raise XPath::NameError, "undefined function `#{name}' for #{_type}"
|
1185
|
+
end
|
1186
|
+
|
1187
|
+
end
|
1188
|
+
|
1189
|
+
|
1190
|
+
|
1191
|
+
|
1192
|
+
class XPathBoolean
|
1193
|
+
|
1194
|
+
include XPathObject
|
1195
|
+
|
1196
|
+
class << self
|
1197
|
+
attr_reader :instance
|
1198
|
+
private :new
|
1199
|
+
end
|
1200
|
+
|
1201
|
+
def to_str
|
1202
|
+
true?.to_s
|
1203
|
+
end
|
1204
|
+
|
1205
|
+
# def to_f
|
1206
|
+
# def true?
|
1207
|
+
|
1208
|
+
def to_ruby
|
1209
|
+
true?
|
1210
|
+
end
|
1211
|
+
|
1212
|
+
def to_boolean(context)
|
1213
|
+
self
|
1214
|
+
end
|
1215
|
+
|
1216
|
+
def ==(other)
|
1217
|
+
true? == other.true?
|
1218
|
+
end
|
1219
|
+
|
1220
|
+
end
|
1221
|
+
|
1222
|
+
|
1223
|
+
class XPathTrueClass < XPathBoolean
|
1224
|
+
|
1225
|
+
@instance = new
|
1226
|
+
|
1227
|
+
def to_f
|
1228
|
+
1.0
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
def true?
|
1232
|
+
true
|
1233
|
+
end
|
1234
|
+
|
1235
|
+
end
|
1236
|
+
|
1237
|
+
|
1238
|
+
class XPathFalseClass < XPathBoolean
|
1239
|
+
|
1240
|
+
@instance = new
|
1241
|
+
|
1242
|
+
def to_f
|
1243
|
+
0.0
|
1244
|
+
end
|
1245
|
+
|
1246
|
+
def true?
|
1247
|
+
false
|
1248
|
+
end
|
1249
|
+
|
1250
|
+
end
|
1251
|
+
|
1252
|
+
|
1253
|
+
XPathTrue = XPathTrueClass.instance
|
1254
|
+
XPathFalse = XPathFalseClass.instance
|
1255
|
+
|
1256
|
+
|
1257
|
+
|
1258
|
+
|
1259
|
+
class XPathNumber
|
1260
|
+
|
1261
|
+
include XPathObject
|
1262
|
+
|
1263
|
+
def initialize(num)
|
1264
|
+
raise ::TypeError, "must be a Float" unless num.is_a? Float
|
1265
|
+
@value = num
|
1266
|
+
end
|
1267
|
+
|
1268
|
+
def to_str
|
1269
|
+
if @value.nan? then
|
1270
|
+
'NaN'
|
1271
|
+
elsif @value.infinite? then
|
1272
|
+
if @value < 0 then
|
1273
|
+
'-Infinity'
|
1274
|
+
else
|
1275
|
+
'Infinity'
|
1276
|
+
end
|
1277
|
+
else
|
1278
|
+
sprintf("%.100f", @value).gsub(/\.?0+\z/, '') # enough?
|
1279
|
+
end
|
1280
|
+
end
|
1281
|
+
|
1282
|
+
def to_f
|
1283
|
+
@value
|
1284
|
+
end
|
1285
|
+
|
1286
|
+
def true?
|
1287
|
+
@value != 0.0 and not @value.nan?
|
1288
|
+
end
|
1289
|
+
|
1290
|
+
def to_ruby
|
1291
|
+
to_f
|
1292
|
+
end
|
1293
|
+
|
1294
|
+
def to_predicate
|
1295
|
+
to_f
|
1296
|
+
end
|
1297
|
+
|
1298
|
+
def to_number(context)
|
1299
|
+
self
|
1300
|
+
end
|
1301
|
+
|
1302
|
+
|
1303
|
+
def ==(other)
|
1304
|
+
if other.is_a? XPathNodeSet or other.is_a? XPathBoolean then
|
1305
|
+
other == self
|
1306
|
+
else
|
1307
|
+
@value == other.to_f
|
1308
|
+
end
|
1309
|
+
end
|
1310
|
+
|
1311
|
+
def +(other)
|
1312
|
+
@value += other.to_f
|
1313
|
+
self
|
1314
|
+
end
|
1315
|
+
|
1316
|
+
def -(other)
|
1317
|
+
@value -= other.to_f
|
1318
|
+
self
|
1319
|
+
end
|
1320
|
+
|
1321
|
+
def *(other)
|
1322
|
+
@value *= other.to_f
|
1323
|
+
self
|
1324
|
+
end
|
1325
|
+
|
1326
|
+
def /(other)
|
1327
|
+
@value /= other.to_f
|
1328
|
+
self
|
1329
|
+
end
|
1330
|
+
|
1331
|
+
def %(other)
|
1332
|
+
n = other.to_f
|
1333
|
+
f = @value % n
|
1334
|
+
f = -f if @value < 0
|
1335
|
+
f = -f if n < 0
|
1336
|
+
@value = f
|
1337
|
+
self
|
1338
|
+
end
|
1339
|
+
|
1340
|
+
def -@
|
1341
|
+
@value = -@value
|
1342
|
+
self
|
1343
|
+
end
|
1344
|
+
|
1345
|
+
def floor
|
1346
|
+
@value = @value.floor.to_f
|
1347
|
+
self
|
1348
|
+
end
|
1349
|
+
|
1350
|
+
def ceil
|
1351
|
+
@value = @value.ceil.to_f
|
1352
|
+
self
|
1353
|
+
end
|
1354
|
+
|
1355
|
+
def round
|
1356
|
+
f = @value
|
1357
|
+
unless f.nan? or f.infinite? then
|
1358
|
+
if f >= 0.0 then
|
1359
|
+
@value = f.round.to_f
|
1360
|
+
elsif f - f.truncate >= -0.5 then
|
1361
|
+
@value = f.ceil.to_f
|
1362
|
+
else
|
1363
|
+
@value = f.floor.to_f
|
1364
|
+
end
|
1365
|
+
end
|
1366
|
+
self
|
1367
|
+
end
|
1368
|
+
|
1369
|
+
end
|
1370
|
+
|
1371
|
+
|
1372
|
+
|
1373
|
+
|
1374
|
+
class XPathString
|
1375
|
+
|
1376
|
+
include XPathObject
|
1377
|
+
|
1378
|
+
def initialize(str)
|
1379
|
+
raise ::TypeError, "must be a String" unless str.is_a? String
|
1380
|
+
@value = str
|
1381
|
+
end
|
1382
|
+
|
1383
|
+
def to_str
|
1384
|
+
@value
|
1385
|
+
end
|
1386
|
+
|
1387
|
+
def to_f
|
1388
|
+
if /\A\s*(-?\d+\.?\d*)(?:\s|\z)/ =~ @value then
|
1389
|
+
$1.to_f
|
1390
|
+
else
|
1391
|
+
0.0 / 0.0 # NaN
|
1392
|
+
end
|
1393
|
+
end
|
1394
|
+
|
1395
|
+
def true?
|
1396
|
+
not @value.empty?
|
1397
|
+
end
|
1398
|
+
|
1399
|
+
def to_ruby
|
1400
|
+
to_str
|
1401
|
+
end
|
1402
|
+
|
1403
|
+
def to_string(context)
|
1404
|
+
self
|
1405
|
+
end
|
1406
|
+
|
1407
|
+
|
1408
|
+
def concat(s)
|
1409
|
+
@value = @value + s
|
1410
|
+
self
|
1411
|
+
end
|
1412
|
+
|
1413
|
+
def start_with?(s)
|
1414
|
+
/\A#{Regexp.quote(s)}/ =~ @value
|
1415
|
+
end
|
1416
|
+
|
1417
|
+
def contain?(s)
|
1418
|
+
/#{Regexp.quote(s)}/ =~ @value
|
1419
|
+
end
|
1420
|
+
|
1421
|
+
def substring_before(s)
|
1422
|
+
if /#{Regexp.quote(s)}/ =~ @value then
|
1423
|
+
@value = $`
|
1424
|
+
else
|
1425
|
+
@value = ''
|
1426
|
+
end
|
1427
|
+
self
|
1428
|
+
end
|
1429
|
+
|
1430
|
+
def substring_after(s)
|
1431
|
+
if /#{Regexp.quote(s)}/ =~ @value then
|
1432
|
+
@value = $'
|
1433
|
+
else
|
1434
|
+
@value = ''
|
1435
|
+
end
|
1436
|
+
self
|
1437
|
+
end
|
1438
|
+
|
1439
|
+
def substring(start, len)
|
1440
|
+
start = start.round.to_f
|
1441
|
+
if start.infinite? or start.nan? then
|
1442
|
+
@value = ''
|
1443
|
+
elsif len then
|
1444
|
+
len = len.round.to_f
|
1445
|
+
maxlen = start + len
|
1446
|
+
len = maxlen - 1.0 if len >= maxlen
|
1447
|
+
if start <= 1.0 then
|
1448
|
+
start = 0
|
1449
|
+
else
|
1450
|
+
start = start.to_i - 1
|
1451
|
+
end
|
1452
|
+
if len.nan? or len < 1.0 then
|
1453
|
+
@value = ''
|
1454
|
+
elsif len.infinite? then
|
1455
|
+
# @value = @value[start..-1]
|
1456
|
+
/\A[\W\w]{0,#{start}}/ =~ @value
|
1457
|
+
@value = $'
|
1458
|
+
else
|
1459
|
+
# @value = @value[start, len.to_i]
|
1460
|
+
/\A[\W\w]{0,#{start}}([\W\w]{0,#{len.to_i}})/ =~ @value
|
1461
|
+
@value = $1
|
1462
|
+
end
|
1463
|
+
elsif start > 1.0 then
|
1464
|
+
# @value = @value[(start-1)..-1]
|
1465
|
+
/\A[\W\w]{0,#{start.to_i-1}}/ =~ @value
|
1466
|
+
@value = $'
|
1467
|
+
end
|
1468
|
+
raise "BUG" unless @value
|
1469
|
+
self
|
1470
|
+
end
|
1471
|
+
|
1472
|
+
def size
|
1473
|
+
@value.gsub(/[^\Wa-zA-Z_\d]/, ' ').size
|
1474
|
+
end
|
1475
|
+
|
1476
|
+
def normalize_space
|
1477
|
+
@value = @value.strip
|
1478
|
+
@value.gsub!(/\s+/, ' ')
|
1479
|
+
self
|
1480
|
+
end
|
1481
|
+
|
1482
|
+
def translate(from, to)
|
1483
|
+
to = to.split(//)
|
1484
|
+
h = {}
|
1485
|
+
from.split(//).each_with_index { |i,n|
|
1486
|
+
h[i] = to[n] unless h.key? i
|
1487
|
+
}
|
1488
|
+
@value = @value.gsub(/[#{Regexp.quote(h.keys.join)}]/) { |s| h[s] }
|
1489
|
+
self
|
1490
|
+
end
|
1491
|
+
|
1492
|
+
def replace(str)
|
1493
|
+
@value = str
|
1494
|
+
self
|
1495
|
+
end
|
1496
|
+
|
1497
|
+
end
|
1498
|
+
|
1499
|
+
|
1500
|
+
|
1501
|
+
|
1502
|
+
---- footer ----
|
1503
|
+
|
1504
|
+
#
|
1505
|
+
# Client NodeVisitor a NodeAdapter a Node
|
1506
|
+
# | | | |
|
1507
|
+
# |=| | | |
|
1508
|
+
# | |--{visit(node)}-->|=| | |
|
1509
|
+
# | | | |---{accept(self)}----------------->|=|
|
1510
|
+
# | | |=| | | |
|
1511
|
+
# | | | | | |
|
1512
|
+
# | | |=|<------------------{on_**(self)}---|=|
|
1513
|
+
# | | | | | |
|
1514
|
+
# | | | |--{wrap(node)}-->|=| |
|
1515
|
+
# | | | | | | |
|
1516
|
+
# | | | | |=| |
|
1517
|
+
# | |<--[NodeAdapter]--|=| | |
|
1518
|
+
# | | | | |
|
1519
|
+
# | |-----{request}----------------------->|=| |
|
1520
|
+
# | | | | |--{request}--->|=|
|
1521
|
+
# | | | | | | |
|
1522
|
+
# | | | | |<-----[Data]---|=|
|
1523
|
+
# | |<--------------------------[Data]-----|=| |
|
1524
|
+
# | | | | |
|
1525
|
+
# |=| | | |
|
1526
|
+
# | | | |
|
1527
|
+
#
|
1528
|
+
|
1529
|
+
|
1530
|
+
class TransparentNodeVisitor
|
1531
|
+
|
1532
|
+
def visit(node)
|
1533
|
+
node
|
1534
|
+
end
|
1535
|
+
|
1536
|
+
end
|
1537
|
+
|
1538
|
+
|
1539
|
+
class NullNodeAdapter
|
1540
|
+
|
1541
|
+
def node
|
1542
|
+
self
|
1543
|
+
end
|
1544
|
+
|
1545
|
+
def root
|
1546
|
+
nil
|
1547
|
+
end
|
1548
|
+
|
1549
|
+
def parent
|
1550
|
+
nil
|
1551
|
+
end
|
1552
|
+
|
1553
|
+
def children
|
1554
|
+
[]
|
1555
|
+
end
|
1556
|
+
|
1557
|
+
def each_following_siblings
|
1558
|
+
end
|
1559
|
+
|
1560
|
+
def each_preceding_siblings
|
1561
|
+
end
|
1562
|
+
|
1563
|
+
def attributes
|
1564
|
+
[]
|
1565
|
+
end
|
1566
|
+
|
1567
|
+
def namespaces
|
1568
|
+
[]
|
1569
|
+
end
|
1570
|
+
|
1571
|
+
def index
|
1572
|
+
0
|
1573
|
+
end
|
1574
|
+
|
1575
|
+
def node_type
|
1576
|
+
nil
|
1577
|
+
end
|
1578
|
+
|
1579
|
+
def name_localpart
|
1580
|
+
nil
|
1581
|
+
end
|
1582
|
+
|
1583
|
+
def qualified_name
|
1584
|
+
name_localpart
|
1585
|
+
end
|
1586
|
+
|
1587
|
+
def namespace_uri
|
1588
|
+
nil
|
1589
|
+
end
|
1590
|
+
|
1591
|
+
def string_value
|
1592
|
+
''
|
1593
|
+
end
|
1594
|
+
|
1595
|
+
def lang
|
1596
|
+
nil
|
1597
|
+
end
|
1598
|
+
|
1599
|
+
def select_id(*ids)
|
1600
|
+
raise XPath::Error, "selection by ID is not supported"
|
1601
|
+
end
|
1602
|
+
|
1603
|
+
end
|
1604
|
+
|
1605
|
+
|
1606
|
+
|
1607
|
+
|
1608
|
+
class AxisIterator
|
1609
|
+
|
1610
|
+
def reverse_order?
|
1611
|
+
false
|
1612
|
+
end
|
1613
|
+
|
1614
|
+
end
|
1615
|
+
|
1616
|
+
|
1617
|
+
class ReverseAxisIterator < AxisIterator
|
1618
|
+
|
1619
|
+
def reverse_order?
|
1620
|
+
true
|
1621
|
+
end
|
1622
|
+
|
1623
|
+
end
|
1624
|
+
|
1625
|
+
|
1626
|
+
class SelfIterator < AxisIterator
|
1627
|
+
|
1628
|
+
def each(node, visitor)
|
1629
|
+
yield visitor.visit(node)
|
1630
|
+
end
|
1631
|
+
|
1632
|
+
end
|
1633
|
+
|
1634
|
+
|
1635
|
+
class ChildIterator < AxisIterator
|
1636
|
+
|
1637
|
+
def each(node, visitor, &block)
|
1638
|
+
visitor.visit(node).children.each { |i| yield visitor.visit(i) }
|
1639
|
+
end
|
1640
|
+
|
1641
|
+
end
|
1642
|
+
|
1643
|
+
|
1644
|
+
class ParentIterator < AxisIterator
|
1645
|
+
|
1646
|
+
def each(node, visitor)
|
1647
|
+
parent = visitor.visit(node).parent
|
1648
|
+
yield visitor.visit(parent) if parent
|
1649
|
+
end
|
1650
|
+
|
1651
|
+
end
|
1652
|
+
|
1653
|
+
|
1654
|
+
class AncestorIterator < ReverseAxisIterator
|
1655
|
+
|
1656
|
+
def each(node, visitor)
|
1657
|
+
node = visitor.visit(node).parent
|
1658
|
+
while node
|
1659
|
+
i = visitor.visit(node)
|
1660
|
+
parent = i.parent
|
1661
|
+
yield i
|
1662
|
+
node = parent
|
1663
|
+
end
|
1664
|
+
end
|
1665
|
+
|
1666
|
+
end
|
1667
|
+
|
1668
|
+
|
1669
|
+
class AncestorOrSelfIterator < AncestorIterator
|
1670
|
+
|
1671
|
+
def each(node, visitor)
|
1672
|
+
yield visitor.visit(node)
|
1673
|
+
super
|
1674
|
+
end
|
1675
|
+
|
1676
|
+
end
|
1677
|
+
|
1678
|
+
|
1679
|
+
class DescendantIterator < AxisIterator
|
1680
|
+
|
1681
|
+
def each(node, visitor)
|
1682
|
+
stack = visitor.visit(node).children.reverse
|
1683
|
+
while node = stack.pop
|
1684
|
+
i = visitor.visit(node)
|
1685
|
+
stack.concat i.children.reverse
|
1686
|
+
yield i
|
1687
|
+
end
|
1688
|
+
end
|
1689
|
+
|
1690
|
+
end
|
1691
|
+
|
1692
|
+
|
1693
|
+
class DescendantOrSelfIterator < DescendantIterator
|
1694
|
+
|
1695
|
+
def each(node, visitor)
|
1696
|
+
yield visitor.visit(node)
|
1697
|
+
super
|
1698
|
+
end
|
1699
|
+
|
1700
|
+
end
|
1701
|
+
|
1702
|
+
|
1703
|
+
class FollowingSiblingIterator < AxisIterator
|
1704
|
+
|
1705
|
+
def each(node, visitor)
|
1706
|
+
visitor.visit(node).each_following_siblings { |i|
|
1707
|
+
yield visitor.visit(i)
|
1708
|
+
}
|
1709
|
+
end
|
1710
|
+
|
1711
|
+
end
|
1712
|
+
|
1713
|
+
|
1714
|
+
class PrecedingSiblingIterator < ReverseAxisIterator
|
1715
|
+
|
1716
|
+
def each(node, visitor)
|
1717
|
+
visitor.visit(node).each_preceding_siblings { |i|
|
1718
|
+
yield visitor.visit(i)
|
1719
|
+
}
|
1720
|
+
end
|
1721
|
+
|
1722
|
+
end
|
1723
|
+
|
1724
|
+
|
1725
|
+
class FollowingIterator < DescendantOrSelfIterator
|
1726
|
+
|
1727
|
+
def each(node, visitor)
|
1728
|
+
while parent = (a = visitor.visit(node)).parent
|
1729
|
+
a.each_following_siblings { |i| super i, visitor }
|
1730
|
+
node = parent
|
1731
|
+
end
|
1732
|
+
end
|
1733
|
+
|
1734
|
+
end
|
1735
|
+
|
1736
|
+
|
1737
|
+
class PrecedingIterator < ReverseAxisIterator
|
1738
|
+
|
1739
|
+
def each(node, visitor)
|
1740
|
+
while parent = (adaptor = visitor.visit(node)).parent
|
1741
|
+
adaptor.each_preceding_siblings { |i|
|
1742
|
+
stack = visitor.visit(i).children.dup
|
1743
|
+
while node = stack.pop
|
1744
|
+
a = visitor.visit(node)
|
1745
|
+
stack.concat a.children
|
1746
|
+
yield a
|
1747
|
+
end
|
1748
|
+
yield visitor.visit(i)
|
1749
|
+
}
|
1750
|
+
node = parent
|
1751
|
+
end
|
1752
|
+
end
|
1753
|
+
|
1754
|
+
end
|
1755
|
+
|
1756
|
+
|
1757
|
+
class AttributeIterator < AxisIterator
|
1758
|
+
|
1759
|
+
def each(node, visitor)
|
1760
|
+
visitor.visit(node).attributes.each { |i| yield visitor.visit(i) }
|
1761
|
+
end
|
1762
|
+
|
1763
|
+
end
|
1764
|
+
|
1765
|
+
|
1766
|
+
class NamespaceIterator < AxisIterator
|
1767
|
+
|
1768
|
+
def each(node, visitor)
|
1769
|
+
visitor.visit(node).namespaces.each { |i| yield visitor.visit(i) }
|
1770
|
+
end
|
1771
|
+
|
1772
|
+
end
|
1773
|
+
|
1774
|
+
|
1775
|
+
|
1776
|
+
|
1777
|
+
class XPathNodeSet
|
1778
|
+
|
1779
|
+
class LocationStep < XPathNodeSet
|
1780
|
+
|
1781
|
+
def initialize(context)
|
1782
|
+
@context = context
|
1783
|
+
@visitor = context.visitor
|
1784
|
+
@nodes = []
|
1785
|
+
end
|
1786
|
+
|
1787
|
+
def set_iterator(iterator)
|
1788
|
+
@iterator = iterator
|
1789
|
+
end
|
1790
|
+
|
1791
|
+
def reuse(node)
|
1792
|
+
@node = node
|
1793
|
+
@nodes.clear
|
1794
|
+
end
|
1795
|
+
|
1796
|
+
def select
|
1797
|
+
@iterator.each(@node, @visitor) { |i|
|
1798
|
+
node = i.node
|
1799
|
+
@nodes.push node if yield(i)
|
1800
|
+
}
|
1801
|
+
self
|
1802
|
+
end
|
1803
|
+
|
1804
|
+
def select_all
|
1805
|
+
@iterator.each(@node, @visitor) { |i| @nodes.push i.node }
|
1806
|
+
self
|
1807
|
+
end
|
1808
|
+
|
1809
|
+
end
|
1810
|
+
|
1811
|
+
|
1812
|
+
include XPathObject
|
1813
|
+
|
1814
|
+
def initialize(context, *nodes)
|
1815
|
+
@context = context.dup
|
1816
|
+
@visitor = context.visitor
|
1817
|
+
nodes.sort! { |a,b| compare_position a, b }
|
1818
|
+
@nodes = nodes
|
1819
|
+
end
|
1820
|
+
|
1821
|
+
attr_reader :nodes
|
1822
|
+
protected :nodes
|
1823
|
+
|
1824
|
+
|
1825
|
+
def to_str
|
1826
|
+
if @nodes.empty? then
|
1827
|
+
''
|
1828
|
+
else
|
1829
|
+
@visitor.visit(@nodes[0]).string_value
|
1830
|
+
end
|
1831
|
+
end
|
1832
|
+
|
1833
|
+
def to_f
|
1834
|
+
to_string(@context).to_f
|
1835
|
+
end
|
1836
|
+
|
1837
|
+
def true?
|
1838
|
+
not @nodes.empty?
|
1839
|
+
end
|
1840
|
+
|
1841
|
+
def to_ruby
|
1842
|
+
@nodes
|
1843
|
+
end
|
1844
|
+
|
1845
|
+
|
1846
|
+
def self.def_comparison_operator(*ops)
|
1847
|
+
ops.each { |op|
|
1848
|
+
module_eval <<_, __FILE__, __LINE__ + 1
|
1849
|
+
def #{op}(other)
|
1850
|
+
if other.is_a? XPathBoolean then
|
1851
|
+
other #{op} self.to_boolean
|
1852
|
+
else
|
1853
|
+
visitor = @visitor
|
1854
|
+
str = @context.make_string('')
|
1855
|
+
ret = false
|
1856
|
+
@nodes.each { |node|
|
1857
|
+
str.replace visitor.visit(node).string_value
|
1858
|
+
break if ret = (other #{op} str)
|
1859
|
+
}
|
1860
|
+
ret
|
1861
|
+
end
|
1862
|
+
end
|
1863
|
+
_
|
1864
|
+
}
|
1865
|
+
end
|
1866
|
+
|
1867
|
+
def_comparison_operator '==', '<', '>', '<=', '>='
|
1868
|
+
|
1869
|
+
class << self
|
1870
|
+
undef def_comparison_operator
|
1871
|
+
end
|
1872
|
+
|
1873
|
+
def **(other)
|
1874
|
+
super unless other.is_a? XPathNodeSet
|
1875
|
+
merge other.nodes
|
1876
|
+
self
|
1877
|
+
end
|
1878
|
+
|
1879
|
+
|
1880
|
+
def count
|
1881
|
+
@nodes.size
|
1882
|
+
end
|
1883
|
+
|
1884
|
+
def first
|
1885
|
+
@nodes[0]
|
1886
|
+
end
|
1887
|
+
|
1888
|
+
def each(&block)
|
1889
|
+
@nodes.each(&block)
|
1890
|
+
end
|
1891
|
+
|
1892
|
+
|
1893
|
+
def funcall(name) # for XPointer
|
1894
|
+
raise "BUG" unless block_given?
|
1895
|
+
func = ('f_' + name.tr('-', '_')).intern
|
1896
|
+
super unless respond_to? func, true
|
1897
|
+
size = @nodes.size
|
1898
|
+
pos = 1
|
1899
|
+
c = @context.dup
|
1900
|
+
begin
|
1901
|
+
@nodes.collect! { |node|
|
1902
|
+
c.reuse node, pos, size
|
1903
|
+
pos += 1
|
1904
|
+
args = yield(c)
|
1905
|
+
send(func, node, *args)
|
1906
|
+
}
|
1907
|
+
rescue Object::ArgumentError
|
1908
|
+
if $@[1] == "#{__FILE__}:#{__LINE__-3}:in `send'" then
|
1909
|
+
raise XPath::ArgumentError, "#{$!} for `#{name}'"
|
1910
|
+
end
|
1911
|
+
raise
|
1912
|
+
end
|
1913
|
+
self
|
1914
|
+
end
|
1915
|
+
|
1916
|
+
|
1917
|
+
private
|
1918
|
+
|
1919
|
+
def compare_position(node1, node2)
|
1920
|
+
visitor = @visitor
|
1921
|
+
ancestors1 = []
|
1922
|
+
ancestors2 = []
|
1923
|
+
p1 = visitor.visit(node1).parent
|
1924
|
+
while p1
|
1925
|
+
ancestors1.push node1
|
1926
|
+
p1 = visitor.visit(node1 = p1).parent
|
1927
|
+
end
|
1928
|
+
p2 = visitor.visit(node2).parent
|
1929
|
+
while p2
|
1930
|
+
ancestors2.push node2
|
1931
|
+
p2 = visitor.visit(node2 = p2).parent
|
1932
|
+
end
|
1933
|
+
unless node1 == node2 then
|
1934
|
+
raise XPath::Error, "can't compare the positions of given two nodes"
|
1935
|
+
end
|
1936
|
+
n = -1
|
1937
|
+
ancestors1.reverse_each { |node1|
|
1938
|
+
node2 = ancestors2[n]
|
1939
|
+
unless node1 == node2 then
|
1940
|
+
break unless node2
|
1941
|
+
return visitor.visit(node1).index - visitor.visit(node2).index
|
1942
|
+
end
|
1943
|
+
n -= 1
|
1944
|
+
}
|
1945
|
+
ancestors1.size - ancestors2.size
|
1946
|
+
end
|
1947
|
+
|
1948
|
+
|
1949
|
+
def merge(other)
|
1950
|
+
if @nodes.empty? or other.empty? then
|
1951
|
+
@nodes.concat other
|
1952
|
+
elsif (n = compare_position(@nodes.last, other.first)) <= 0 then
|
1953
|
+
@nodes.pop if n == 0
|
1954
|
+
@nodes.concat other
|
1955
|
+
elsif (n = compare_position(other.last, @nodes.first)) <= 0 then
|
1956
|
+
other.pop if n == 0
|
1957
|
+
@nodes = other.concat(@nodes)
|
1958
|
+
else
|
1959
|
+
newnodes = []
|
1960
|
+
nodes = @nodes
|
1961
|
+
until nodes.empty? or other.empty?
|
1962
|
+
n = compare_position(nodes.last, other.last)
|
1963
|
+
if n > 0 then
|
1964
|
+
newnodes.push nodes.pop
|
1965
|
+
elsif n < 0 then
|
1966
|
+
newnodes.push other.pop
|
1967
|
+
else
|
1968
|
+
newnodes.push nodes.pop
|
1969
|
+
other.pop
|
1970
|
+
end
|
1971
|
+
end
|
1972
|
+
newnodes.reverse!
|
1973
|
+
@nodes.concat(other).concat(newnodes)
|
1974
|
+
end
|
1975
|
+
end
|
1976
|
+
|
1977
|
+
|
1978
|
+
IteratorForAxis = {
|
1979
|
+
:self => SelfIterator.new,
|
1980
|
+
:child => ChildIterator.new,
|
1981
|
+
:parent => ParentIterator.new,
|
1982
|
+
:ancestor => AncestorIterator.new,
|
1983
|
+
:ancestor_or_self => AncestorOrSelfIterator.new,
|
1984
|
+
:descendant => DescendantIterator.new,
|
1985
|
+
:descendant_or_self => DescendantOrSelfIterator.new,
|
1986
|
+
:following => FollowingIterator.new,
|
1987
|
+
:preceding => PrecedingIterator.new,
|
1988
|
+
:following_sibling => FollowingSiblingIterator.new,
|
1989
|
+
:preceding_sibling => PrecedingSiblingIterator.new,
|
1990
|
+
:attribute => AttributeIterator.new,
|
1991
|
+
:namespace => NamespaceIterator.new,
|
1992
|
+
}
|
1993
|
+
|
1994
|
+
def get_iterator(axis)
|
1995
|
+
ret = IteratorForAxis[axis]
|
1996
|
+
unless ret then
|
1997
|
+
raise XPath::NameError, "invalid axis `#{axis.id2name.tr('_','-')}'"
|
1998
|
+
end
|
1999
|
+
ret
|
2000
|
+
end
|
2001
|
+
|
2002
|
+
def make_location_step
|
2003
|
+
if defined? @__lstep__ then
|
2004
|
+
@__lstep__
|
2005
|
+
else
|
2006
|
+
@__lstep__ = LocationStep.new(@context)
|
2007
|
+
end
|
2008
|
+
end
|
2009
|
+
|
2010
|
+
|
2011
|
+
public
|
2012
|
+
|
2013
|
+
def step(axis)
|
2014
|
+
iterator = get_iterator(axis)
|
2015
|
+
lstep = make_location_step
|
2016
|
+
lstep.set_iterator iterator
|
2017
|
+
oldnodes = @nodes
|
2018
|
+
@nodes = []
|
2019
|
+
oldnodes.each { |node|
|
2020
|
+
lstep.reuse node
|
2021
|
+
nodes = yield(lstep).nodes
|
2022
|
+
nodes.reverse! if iterator.reverse_order?
|
2023
|
+
merge nodes
|
2024
|
+
}
|
2025
|
+
self
|
2026
|
+
end
|
2027
|
+
|
2028
|
+
|
2029
|
+
def select_all(axis)
|
2030
|
+
iterator = get_iterator(axis)
|
2031
|
+
visitor = @visitor
|
2032
|
+
oldnodes = @nodes
|
2033
|
+
@nodes = []
|
2034
|
+
oldnodes.each { |start|
|
2035
|
+
nodes = []
|
2036
|
+
iterator.each(start, visitor) { |i| nodes.push i.node }
|
2037
|
+
nodes.reverse! if iterator.reverse_order?
|
2038
|
+
merge nodes
|
2039
|
+
}
|
2040
|
+
self
|
2041
|
+
end
|
2042
|
+
|
2043
|
+
|
2044
|
+
def predicate
|
2045
|
+
context = @context
|
2046
|
+
size = @nodes.size
|
2047
|
+
pos = 1
|
2048
|
+
result = nil
|
2049
|
+
newnodes = @nodes.reject { |node|
|
2050
|
+
context.reuse node, pos, size
|
2051
|
+
pos += 1
|
2052
|
+
result = yield(context)
|
2053
|
+
break if result.is_a? Numeric
|
2054
|
+
not result
|
2055
|
+
}
|
2056
|
+
if result.is_a? Numeric then
|
2057
|
+
at result
|
2058
|
+
else
|
2059
|
+
@nodes = newnodes
|
2060
|
+
end
|
2061
|
+
self
|
2062
|
+
end
|
2063
|
+
|
2064
|
+
|
2065
|
+
def at(pos)
|
2066
|
+
n = pos.to_i
|
2067
|
+
if n != pos or n <= 0 then
|
2068
|
+
node = nil
|
2069
|
+
else
|
2070
|
+
node = @nodes[n - 1]
|
2071
|
+
end
|
2072
|
+
@nodes.clear
|
2073
|
+
@nodes.push node if node
|
2074
|
+
self
|
2075
|
+
end
|
2076
|
+
|
2077
|
+
end
|
2078
|
+
|
2079
|
+
|
2080
|
+
|
2081
|
+
class Context
|
2082
|
+
|
2083
|
+
def initialize(node, namespace = nil, variable = nil, visitor = nil)
|
2084
|
+
visitor = TransparentNodeVisitor.new unless visitor
|
2085
|
+
@visitor = visitor
|
2086
|
+
@node = node
|
2087
|
+
@context_position = 1
|
2088
|
+
@context_size = 1
|
2089
|
+
@variables = variable
|
2090
|
+
@namespaces = namespace || {}
|
2091
|
+
end
|
2092
|
+
|
2093
|
+
attr_reader :visitor, :node, :context_position, :context_size
|
2094
|
+
|
2095
|
+
def reuse(node, pos = 1, size = 1)
|
2096
|
+
@variables = nil
|
2097
|
+
@node, @context_position, @context_size = node, pos, size
|
2098
|
+
end
|
2099
|
+
|
2100
|
+
|
2101
|
+
def get_variable(name)
|
2102
|
+
value = @variables && @variables[name] # value should be a XPathObjcect.
|
2103
|
+
raise XPath::NameError, "undefined variable `#{name}'" unless value
|
2104
|
+
value
|
2105
|
+
end
|
2106
|
+
|
2107
|
+
|
2108
|
+
PredefinedNamespace = {
|
2109
|
+
'xml' => 'http://www.w3.org/XML/1998/namespace',
|
2110
|
+
}
|
2111
|
+
|
2112
|
+
def get_namespace(prefix)
|
2113
|
+
ret = @namespaces[prefix] || PredefinedNamespace[prefix]
|
2114
|
+
raise XPath::Error, "undeclared namespace `#{prefix}'" unless ret
|
2115
|
+
ret
|
2116
|
+
end
|
2117
|
+
|
2118
|
+
|
2119
|
+
def make_string(str)
|
2120
|
+
XPathString.new str
|
2121
|
+
end
|
2122
|
+
|
2123
|
+
def make_number(num)
|
2124
|
+
XPathNumber.new num
|
2125
|
+
end
|
2126
|
+
|
2127
|
+
def make_boolean(f)
|
2128
|
+
if f then
|
2129
|
+
XPathTrue
|
2130
|
+
else
|
2131
|
+
XPathFalse
|
2132
|
+
end
|
2133
|
+
end
|
2134
|
+
|
2135
|
+
def make_nodeset(*nodes)
|
2136
|
+
XPathNodeSet.new(self, *nodes)
|
2137
|
+
end
|
2138
|
+
|
2139
|
+
|
2140
|
+
def to_nodeset
|
2141
|
+
make_nodeset @node
|
2142
|
+
end
|
2143
|
+
|
2144
|
+
def root_nodeset
|
2145
|
+
make_nodeset @visitor.visit(@node).root
|
2146
|
+
end
|
2147
|
+
|
2148
|
+
|
2149
|
+
def funcall(name, *args)
|
2150
|
+
begin
|
2151
|
+
send('f_' + name.tr('-', '_'), *args)
|
2152
|
+
rescue Object::NameError
|
2153
|
+
if $@[0] == "#{__FILE__}:#{__LINE__-2}:in `send'" then
|
2154
|
+
raise XPath::NameError, "undefined function `#{name}'"
|
2155
|
+
end
|
2156
|
+
raise
|
2157
|
+
rescue Object::ArgumentError
|
2158
|
+
if $@[1] == "#{__FILE__}:#{__LINE__-7}:in `send'" then
|
2159
|
+
raise XPath::ArgumentError, "#{$!} for `#{name}'"
|
2160
|
+
end
|
2161
|
+
raise
|
2162
|
+
end
|
2163
|
+
end
|
2164
|
+
|
2165
|
+
|
2166
|
+
private
|
2167
|
+
|
2168
|
+
def must(type, *args)
|
2169
|
+
args.each { |i|
|
2170
|
+
unless i.is_a? type then
|
2171
|
+
s = type.name.sub(/\A.*::(?:XPath)?(?=[^:]+\z)/, '')
|
2172
|
+
raise XPath::TypeError, "argument must be #{s}"
|
2173
|
+
end
|
2174
|
+
}
|
2175
|
+
end
|
2176
|
+
|
2177
|
+
def must_be_nodeset(*args)
|
2178
|
+
must XPathNodeSet, *args
|
2179
|
+
end
|
2180
|
+
|
2181
|
+
|
2182
|
+
def f_last
|
2183
|
+
make_number @context_size.to_f
|
2184
|
+
end
|
2185
|
+
|
2186
|
+
def f_position
|
2187
|
+
make_number @context_position.to_f
|
2188
|
+
end
|
2189
|
+
|
2190
|
+
def f_count(nodeset)
|
2191
|
+
must_be_nodeset nodeset
|
2192
|
+
make_number nodeset.count.to_f
|
2193
|
+
end
|
2194
|
+
|
2195
|
+
def f_id(obj)
|
2196
|
+
unless obj.is_a? XPathNodeSet then
|
2197
|
+
ids = obj.to_str.strip.split(/\s+/)
|
2198
|
+
else
|
2199
|
+
ids = []
|
2200
|
+
obj.each { |node| ids.push @visitor.visit(node).string_value }
|
2201
|
+
end
|
2202
|
+
root = @visitor.visit(@node).root
|
2203
|
+
make_nodeset(*@visitor.visit(root).select_id(*ids))
|
2204
|
+
end
|
2205
|
+
|
2206
|
+
def f_local_name(nodeset = nil)
|
2207
|
+
unless nodeset then
|
2208
|
+
n = @node
|
2209
|
+
else
|
2210
|
+
must_be_nodeset nodeset
|
2211
|
+
n = nodeset.first
|
2212
|
+
end
|
2213
|
+
n = @visitor.visit(n) if n
|
2214
|
+
n = n.name_localpart if n
|
2215
|
+
n = '' unless n
|
2216
|
+
make_string n
|
2217
|
+
end
|
2218
|
+
|
2219
|
+
def f_namespace_uri(nodeset = nil)
|
2220
|
+
unless nodeset then
|
2221
|
+
n = @node
|
2222
|
+
else
|
2223
|
+
must_be_nodeset nodeset
|
2224
|
+
n = nodeset.first
|
2225
|
+
end
|
2226
|
+
n = @visitor.visit(n) if n
|
2227
|
+
n = n.namespace_uri if n
|
2228
|
+
n = '' unless n
|
2229
|
+
make_string n
|
2230
|
+
end
|
2231
|
+
|
2232
|
+
def f_name(nodeset = nil)
|
2233
|
+
unless nodeset then
|
2234
|
+
n = @node
|
2235
|
+
else
|
2236
|
+
must_be_nodeset nodeset
|
2237
|
+
n = nodeset.first
|
2238
|
+
end
|
2239
|
+
n = @visitor.visit(n) if n
|
2240
|
+
n = n.qualified_name if n
|
2241
|
+
n = '' unless n
|
2242
|
+
make_string n
|
2243
|
+
end
|
2244
|
+
|
2245
|
+
|
2246
|
+
def f_string(obj = nil)
|
2247
|
+
obj = to_nodeset unless obj
|
2248
|
+
obj.to_string self
|
2249
|
+
end
|
2250
|
+
|
2251
|
+
def f_concat(str, str2, *strs)
|
2252
|
+
s = str2.to_str.dup
|
2253
|
+
strs.each { |i| s << i.to_str }
|
2254
|
+
str.to_string(self).concat(s)
|
2255
|
+
end
|
2256
|
+
|
2257
|
+
def f_starts_with(str, sub)
|
2258
|
+
make_boolean str.to_string(self).start_with?(sub.to_str)
|
2259
|
+
end
|
2260
|
+
|
2261
|
+
def f_contains(str, sub)
|
2262
|
+
make_boolean str.to_string(self).contain?(sub.to_str)
|
2263
|
+
end
|
2264
|
+
|
2265
|
+
def f_substring_before(str, sub)
|
2266
|
+
str.to_string(self).substring_before sub.to_str
|
2267
|
+
end
|
2268
|
+
|
2269
|
+
def f_substring_after(str, sub)
|
2270
|
+
str.to_string(self).substring_after sub.to_str
|
2271
|
+
end
|
2272
|
+
|
2273
|
+
def f_substring(str, start, len = nil)
|
2274
|
+
len = len.to_number(self) if len
|
2275
|
+
str.to_string(self).substring start.to_number(self), len
|
2276
|
+
end
|
2277
|
+
|
2278
|
+
def f_string_length(str = nil)
|
2279
|
+
if str then
|
2280
|
+
str = str.to_string(self)
|
2281
|
+
else
|
2282
|
+
str = make_string(@node.string_value)
|
2283
|
+
end
|
2284
|
+
make_number str.size.to_f
|
2285
|
+
end
|
2286
|
+
|
2287
|
+
def f_normalize_space(str = nil)
|
2288
|
+
if str then
|
2289
|
+
str = str.to_string(self)
|
2290
|
+
else
|
2291
|
+
str = make_string(@node.string_value)
|
2292
|
+
end
|
2293
|
+
str.normalize_space
|
2294
|
+
end
|
2295
|
+
|
2296
|
+
def f_translate(str, from, to)
|
2297
|
+
str.to_string(self).translate from.to_str, to.to_str
|
2298
|
+
end
|
2299
|
+
|
2300
|
+
|
2301
|
+
def f_boolean(obj)
|
2302
|
+
obj.to_boolean self
|
2303
|
+
end
|
2304
|
+
|
2305
|
+
def f_not(bool)
|
2306
|
+
make_boolean(!bool.true?)
|
2307
|
+
end
|
2308
|
+
|
2309
|
+
def f_true
|
2310
|
+
make_boolean true
|
2311
|
+
end
|
2312
|
+
|
2313
|
+
def f_false
|
2314
|
+
make_boolean false
|
2315
|
+
end
|
2316
|
+
|
2317
|
+
def f_lang(str)
|
2318
|
+
lang = @visitor.visit(@node).lang
|
2319
|
+
make_boolean(lang && /\A#{Regexp.quote(str.to_str)}(?:-|\z)/i =~ lang)
|
2320
|
+
end
|
2321
|
+
|
2322
|
+
|
2323
|
+
def f_number(obj = nil)
|
2324
|
+
obj = to_nodeset unless obj
|
2325
|
+
obj.to_number self
|
2326
|
+
end
|
2327
|
+
|
2328
|
+
def f_sum(nodeset)
|
2329
|
+
must_be_nodeset nodeset
|
2330
|
+
sum = 0.0
|
2331
|
+
nodeset.each { |node|
|
2332
|
+
sum += make_string(@visitor.visit(node).string_value).to_f
|
2333
|
+
}
|
2334
|
+
make_number sum
|
2335
|
+
end
|
2336
|
+
|
2337
|
+
def f_floor(num)
|
2338
|
+
num.to_number(self).floor
|
2339
|
+
end
|
2340
|
+
|
2341
|
+
def f_ceiling(num)
|
2342
|
+
num.to_number(self).ceil
|
2343
|
+
end
|
2344
|
+
|
2345
|
+
def f_round(num)
|
2346
|
+
num.to_number(self).round
|
2347
|
+
end
|
2348
|
+
|
2349
|
+
end
|
2350
|
+
|
2351
|
+
|
2352
|
+
end
|