source: extensions/gsdl-video/trunk/installed/cmdline/lib/ruby/1.8/rexml/xpath_parser.rb@ 18425

Last change on this file since 18425 was 18425, checked in by davidb, 15 years ago

Video extension to Greenstone

File size: 24.8 KB
Line 
1require 'rexml/namespace'
2require 'rexml/xmltokens'
3require 'rexml/attribute'
4require 'rexml/syncenumerator'
5require 'rexml/parsers/xpathparser'
6
7class Object
8 def dclone
9 clone
10 end
11end
12class Symbol
13 def dclone ; self ; end
14end
15class Fixnum
16 def dclone ; self ; end
17end
18class Float
19 def dclone ; self ; end
20end
21class Array
22 def dclone
23 klone = self.clone
24 klone.clear
25 self.each{|v| klone << v.dclone}
26 klone
27 end
28end
29
30module REXML
31 # You don't want to use this class. Really. Use XPath, which is a wrapper
32 # for this class. Believe me. You don't want to poke around in here.
33 # There is strange, dark magic at work in this code. Beware. Go back! Go
34 # back while you still can!
35 class XPathParser
36 include XMLTokens
37 LITERAL = /^'([^']*)'|^"([^"]*)"/u
38
39 def initialize( )
40 @parser = REXML::Parsers::XPathParser.new
41 @namespaces = nil
42 @variables = {}
43 end
44
45 def namespaces=( namespaces={} )
46 Functions::namespace_context = namespaces
47 @namespaces = namespaces
48 end
49
50 def variables=( vars={} )
51 Functions::variables = vars
52 @variables = vars
53 end
54
55 def parse path, nodeset
56 #puts "#"*40
57 path_stack = @parser.parse( path )
58 #puts "PARSE: #{path} => #{path_stack.inspect}"
59 #puts "PARSE: nodeset = #{nodeset.inspect}"
60 match( path_stack, nodeset )
61 end
62
63 def get_first path, nodeset
64 #puts "#"*40
65 path_stack = @parser.parse( path )
66 #puts "PARSE: #{path} => #{path_stack.inspect}"
67 #puts "PARSE: nodeset = #{nodeset.inspect}"
68 first( path_stack, nodeset )
69 end
70
71 def predicate path, nodeset
72 path_stack = @parser.parse( path )
73 expr( path_stack, nodeset )
74 end
75
76 def []=( variable_name, value )
77 @variables[ variable_name ] = value
78 end
79
80
81 # Performs a depth-first (document order) XPath search, and returns the
82 # first match. This is the fastest, lightest way to return a single result.
83 #
84 # FIXME: This method is incomplete!
85 def first( path_stack, node )
86 #puts "#{depth}) Entering match( #{path.inspect}, #{tree.inspect} )"
87 return nil if path.size == 0
88
89 case path[0]
90 when :document
91 # do nothing
92 return first( path[1..-1], node )
93 when :child
94 for c in node.children
95 #puts "#{depth}) CHILD checking #{name(c)}"
96 r = first( path[1..-1], c )
97 #puts "#{depth}) RETURNING #{r.inspect}" if r
98 return r if r
99 end
100 when :qname
101 name = path[2]
102 #puts "#{depth}) QNAME #{name(tree)} == #{name} (path => #{path.size})"
103 if node.name == name
104 #puts "#{depth}) RETURNING #{tree.inspect}" if path.size == 3
105 return node if path.size == 3
106 return first( path[3..-1], node )
107 else
108 return nil
109 end
110 when :descendant_or_self
111 r = first( path[1..-1], node )
112 return r if r
113 for c in node.children
114 r = first( path, c )
115 return r if r
116 end
117 when :node
118 return first( path[1..-1], node )
119 when :any
120 return first( path[1..-1], node )
121 end
122 return nil
123 end
124
125
126 def match( path_stack, nodeset )
127 #puts "MATCH: path_stack = #{path_stack.inspect}"
128 #puts "MATCH: nodeset = #{nodeset.inspect}"
129 r = expr( path_stack, nodeset )
130 #puts "MAIN EXPR => #{r.inspect}"
131 r
132 end
133
134 private
135
136
137 # Returns a String namespace for a node, given a prefix
138 # The rules are:
139 #
140 # 1. Use the supplied namespace mapping first.
141 # 2. If no mapping was supplied, use the context node to look up the namespace
142 def get_namespace( node, prefix )
143 if @namespaces
144 return @namespaces[prefix] || ''
145 else
146 return node.namespace( prefix ) if node.node_type == :element
147 return ''
148 end
149 end
150
151
152 # Expr takes a stack of path elements and a set of nodes (either a Parent
153 # or an Array and returns an Array of matching nodes
154 ALL = [ :attribute, :element, :text, :processing_instruction, :comment ]
155 ELEMENTS = [ :element ]
156 def expr( path_stack, nodeset, context=nil )
157 #puts "#"*15
158 #puts "In expr with #{path_stack.inspect}"
159 #puts "Returning" if path_stack.length == 0 || nodeset.length == 0
160 node_types = ELEMENTS
161 return nodeset if path_stack.length == 0 || nodeset.length == 0
162 while path_stack.length > 0
163 #puts "Path stack = #{path_stack.inspect}"
164 #puts "Nodeset is #{nodeset.inspect}"
165 if nodeset.length == 0
166 path_stack.clear
167 return []
168 end
169 case (op = path_stack.shift)
170 when :document
171 nodeset = [ nodeset[0].root_node ]
172 #puts ":document, nodeset = #{nodeset.inspect}"
173
174 when :qname
175 #puts "IN QNAME"
176 prefix = path_stack.shift
177 name = path_stack.shift
178 nodeset.delete_if do |node|
179 # FIXME: This DOUBLES the time XPath searches take
180 ns = get_namespace( node, prefix )
181 #puts "NS = #{ns.inspect}"
182 #puts "node.node_type == :element => #{node.node_type == :element}"
183 if node.node_type == :element
184 #puts "node.name == #{name} => #{node.name == name}"
185 if node.name == name
186 #puts "node.namespace == #{ns.inspect} => #{node.namespace == ns}"
187 end
188 end
189 !(node.node_type == :element and
190 node.name == name and
191 node.namespace == ns )
192 end
193 node_types = ELEMENTS
194
195 when :any
196 #puts "ANY 1: nodeset = #{nodeset.inspect}"
197 #puts "ANY 1: node_types = #{node_types.inspect}"
198 nodeset.delete_if { |node| !node_types.include?(node.node_type) }
199 #puts "ANY 2: nodeset = #{nodeset.inspect}"
200
201 when :self
202 # This space left intentionally blank
203
204 when :processing_instruction
205 target = path_stack.shift
206 nodeset.delete_if do |node|
207 (node.node_type != :processing_instruction) or
208 ( target!='' and ( node.target != target ) )
209 end
210
211 when :text
212 nodeset.delete_if { |node| node.node_type != :text }
213
214 when :comment
215 nodeset.delete_if { |node| node.node_type != :comment }
216
217 when :node
218 # This space left intentionally blank
219 node_types = ALL
220
221 when :child
222 new_nodeset = []
223 nt = nil
224 for node in nodeset
225 nt = node.node_type
226 new_nodeset += node.children if nt == :element or nt == :document
227 end
228 nodeset = new_nodeset
229 node_types = ELEMENTS
230
231 when :literal
232 return path_stack.shift
233
234 when :attribute
235 new_nodeset = []
236 case path_stack.shift
237 when :qname
238 prefix = path_stack.shift
239 name = path_stack.shift
240 for element in nodeset
241 if element.node_type == :element
242 #puts "Element name = #{element.name}"
243 #puts "get_namespace( #{element.inspect}, #{prefix} ) = #{get_namespace(element, prefix)}"
244 attrib = element.attribute( name, get_namespace(element, prefix) )
245 #puts "attrib = #{attrib.inspect}"
246 new_nodeset << attrib if attrib
247 end
248 end
249 when :any
250 #puts "ANY"
251 for element in nodeset
252 if element.node_type == :element
253 new_nodeset += element.attributes.to_a
254 end
255 end
256 end
257 nodeset = new_nodeset
258
259 when :parent
260 #puts "PARENT 1: nodeset = #{nodeset}"
261 nodeset = nodeset.collect{|n| n.parent}.compact
262 #nodeset = expr(path_stack.dclone, nodeset.collect{|n| n.parent}.compact)
263 #puts "PARENT 2: nodeset = #{nodeset.inspect}"
264 node_types = ELEMENTS
265
266 when :ancestor
267 new_nodeset = []
268 for node in nodeset
269 while node.parent
270 node = node.parent
271 new_nodeset << node unless new_nodeset.include? node
272 end
273 end
274 nodeset = new_nodeset
275 node_types = ELEMENTS
276
277 when :ancestor_or_self
278 new_nodeset = []
279 for node in nodeset
280 if node.node_type == :element
281 new_nodeset << node
282 while ( node.parent )
283 node = node.parent
284 new_nodeset << node unless new_nodeset.include? node
285 end
286 end
287 end
288 nodeset = new_nodeset
289 node_types = ELEMENTS
290
291 when :predicate
292 new_nodeset = []
293 subcontext = { :size => nodeset.size }
294 pred = path_stack.shift
295 nodeset.each_with_index { |node, index|
296 subcontext[ :node ] = node
297 #puts "PREDICATE SETTING CONTEXT INDEX TO #{index+1}"
298 subcontext[ :index ] = index+1
299 pc = pred.dclone
300 #puts "#{node.hash}) Recursing with #{pred.inspect} and [#{node.inspect}]"
301 result = expr( pc, [node], subcontext )
302 result = result[0] if result.kind_of? Array and result.length == 1
303 #puts "#{node.hash}) Result = #{result.inspect} (#{result.class.name})"
304 if result.kind_of? Numeric
305 #puts "Adding node #{node.inspect}" if result == (index+1)
306 new_nodeset << node if result == (index+1)
307 elsif result.instance_of? Array
308 if result.size > 0 and result.inject(false) {|k,s| s or k}
309 #puts "Adding node #{node.inspect}" if result.size > 0
310 new_nodeset << node if result.size > 0
311 end
312 else
313 #puts "Adding node #{node.inspect}" if result
314 new_nodeset << node if result
315 end
316 }
317 #puts "New nodeset = #{new_nodeset.inspect}"
318 #puts "Path_stack = #{path_stack.inspect}"
319 nodeset = new_nodeset
320=begin
321 predicate = path_stack.shift
322 ns = nodeset.clone
323 result = expr( predicate, ns )
324 #puts "Result = #{result.inspect} (#{result.class.name})"
325 #puts "nodeset = #{nodeset.inspect}"
326 if result.kind_of? Array
327 nodeset = result.zip(ns).collect{|m,n| n if m}.compact
328 else
329 nodeset = result ? nodeset : []
330 end
331 #puts "Outgoing NS = #{nodeset.inspect}"
332=end
333
334 when :descendant_or_self
335 rv = descendant_or_self( path_stack, nodeset )
336 path_stack.clear
337 nodeset = rv
338 node_types = ELEMENTS
339
340 when :descendant
341 results = []
342 nt = nil
343 for node in nodeset
344 nt = node.node_type
345 results += expr( path_stack.dclone.unshift( :descendant_or_self ),
346 node.children ) if nt == :element or nt == :document
347 end
348 nodeset = results
349 node_types = ELEMENTS
350
351 when :following_sibling
352 #puts "FOLLOWING_SIBLING 1: nodeset = #{nodeset}"
353 results = []
354 for node in nodeset
355 all_siblings = node.parent.children
356 current_index = all_siblings.index( node )
357 following_siblings = all_siblings[ current_index+1 .. -1 ]
358 results += expr( path_stack.dclone, following_siblings )
359 end
360 #puts "FOLLOWING_SIBLING 2: nodeset = #{nodeset}"
361 nodeset = results
362
363 when :preceding_sibling
364 results = []
365 for node in nodeset
366 all_siblings = node.parent.children
367 current_index = all_siblings.index( node )
368 preceding_siblings = all_siblings[ 0 .. current_index-1 ].reverse
369 #results += expr( path_stack.dclone, preceding_siblings )
370 end
371 nodeset = preceding_siblings || []
372 node_types = ELEMENTS
373
374 when :preceding
375 new_nodeset = []
376 for node in nodeset
377 new_nodeset += preceding( node )
378 end
379 #puts "NEW NODESET => #{new_nodeset.inspect}"
380 nodeset = new_nodeset
381 node_types = ELEMENTS
382
383 when :following
384 new_nodeset = []
385 for node in nodeset
386 new_nodeset += following( node )
387 end
388 nodeset = new_nodeset
389 node_types = ELEMENTS
390
391 when :namespace
392 new_nodeset = []
393 prefix = path_stack.shift
394 for node in nodeset
395 if (node.node_type == :element or node.node_type == :attribute)
396 if (node.node_type == :element)
397 namespaces = node.namespaces
398 else
399 namespaces = node.element.namesapces
400 end
401 if (node.namespace == namespaces[prefix])
402 new_nodeset << node
403 end
404 end
405 end
406 nodeset = new_nodeset
407
408 when :variable
409 var_name = path_stack.shift
410 return @variables[ var_name ]
411
412 # :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq
413 # TODO: Special case for :or and :and -- not evaluate the right
414 # operand if the left alone determines result (i.e. is true for
415 # :or and false for :and).
416 when :eq, :neq, :lt, :lteq, :gt, :gteq, :and, :or
417 left = expr( path_stack.shift, nodeset.dup, context )
418 #puts "LEFT => #{left.inspect} (#{left.class.name})"
419 right = expr( path_stack.shift, nodeset.dup, context )
420 #puts "RIGHT => #{right.inspect} (#{right.class.name})"
421 res = equality_relational_compare( left, op, right )
422 #puts "RES => #{res.inspect}"
423 return res
424
425 when :and
426 left = expr( path_stack.shift, nodeset.dup, context )
427 #puts "LEFT => #{left.inspect} (#{left.class.name})"
428 if left == false || left.nil? || !left.inject(false) {|a,b| a | b}
429 return []
430 end
431 right = expr( path_stack.shift, nodeset.dup, context )
432 #puts "RIGHT => #{right.inspect} (#{right.class.name})"
433 res = equality_relational_compare( left, op, right )
434 #puts "RES => #{res.inspect}"
435 return res
436
437 when :div
438 left = Functions::number(expr(path_stack.shift, nodeset, context)).to_f
439 right = Functions::number(expr(path_stack.shift, nodeset, context)).to_f
440 return (left / right)
441
442 when :mod
443 left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
444 right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
445 return (left % right)
446
447 when :mult
448 left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
449 right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
450 return (left * right)
451
452 when :plus
453 left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
454 right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
455 return (left + right)
456
457 when :minus
458 left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
459 right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
460 return (left - right)
461
462 when :union
463 left = expr( path_stack.shift, nodeset, context )
464 right = expr( path_stack.shift, nodeset, context )
465 return (left | right)
466
467 when :neg
468 res = expr( path_stack, nodeset, context )
469 return -(res.to_f)
470
471 when :not
472 when :function
473 func_name = path_stack.shift.tr('-','_')
474 arguments = path_stack.shift
475 #puts "FUNCTION 0: #{func_name}(#{arguments.collect{|a|a.inspect}.join(', ')})"
476 subcontext = context ? nil : { :size => nodeset.size }
477
478 res = []
479 cont = context
480 nodeset.each_with_index { |n, i|
481 if subcontext
482 subcontext[:node] = n
483 subcontext[:index] = i
484 cont = subcontext
485 end
486 arg_clone = arguments.dclone
487 args = arg_clone.collect { |arg|
488 #puts "FUNCTION 1: Calling expr( #{arg.inspect}, [#{n.inspect}] )"
489 expr( arg, [n], cont )
490 }
491 #puts "FUNCTION 2: #{func_name}(#{args.collect{|a|a.inspect}.join(', ')})"
492 Functions.context = cont
493 res << Functions.send( func_name, *args )
494 #puts "FUNCTION 3: #{res[-1].inspect}"
495 }
496 return res
497
498 end
499 end # while
500 #puts "EXPR returning #{nodeset.inspect}"
501 return nodeset
502 end
503
504
505 ##########################################################
506 # FIXME
507 # The next two methods are BAD MOJO!
508 # This is my achilles heel. If anybody thinks of a better
509 # way of doing this, be my guest. This really sucks, but
510 # it is a wonder it works at all.
511 # ########################################################
512
513 def descendant_or_self( path_stack, nodeset )
514 rs = []
515 #puts "#"*80
516 #puts "PATH_STACK = #{path_stack.inspect}"
517 #puts "NODESET = #{nodeset.collect{|n|n.inspect}.inspect}"
518 d_o_s( path_stack, nodeset, rs )
519 #puts "RS = #{rs.collect{|n|n.inspect}.inspect}"
520 document_order(rs.flatten.compact)
521 #rs.flatten.compact
522 end
523
524 def d_o_s( p, ns, r )
525 #puts "IN DOS with #{ns.inspect}; ALREADY HAVE #{r.inspect}"
526 nt = nil
527 ns.each_index do |i|
528 n = ns[i]
529 #puts "P => #{p.inspect}"
530 x = expr( p.dclone, [ n ] )
531 nt = n.node_type
532 d_o_s( p, n.children, x ) if nt == :element or nt == :document and n.children.size > 0
533 r.concat(x) if x.size > 0
534 end
535 end
536
537
538 # Reorders an array of nodes so that they are in document order
539 # It tries to do this efficiently.
540 #
541 # FIXME: I need to get rid of this, but the issue is that most of the XPath
542 # interpreter functions as a filter, which means that we lose context going
543 # in and out of function calls. If I knew what the index of the nodes was,
544 # I wouldn't have to do this. Maybe add a document IDX for each node?
545 # Problems with mutable documents. Or, rewrite everything.
546 def document_order( array_of_nodes )
547 new_arry = []
548 array_of_nodes.each { |node|
549 node_idx = []
550 np = node.node_type == :attribute ? node.element : node
551 while np.parent and np.parent.node_type == :element
552 node_idx << np.parent.index( np )
553 np = np.parent
554 end
555 new_arry << [ node_idx.reverse, node ]
556 }
557 #puts "new_arry = #{new_arry.inspect}"
558 new_arry.sort{ |s1, s2| s1[0] <=> s2[0] }.collect{ |s| s[1] }
559 end
560
561
562 def recurse( nodeset, &block )
563 for node in nodeset
564 yield node
565 recurse( node, &block ) if node.node_type == :element
566 end
567 end
568
569
570
571 # Builds a nodeset of all of the preceding nodes of the supplied node,
572 # in reverse document order
573 # preceding:: includes every element in the document that precedes this node,
574 # except for ancestors
575 def preceding( node )
576 #puts "IN PRECEDING"
577 ancestors = []
578 p = node.parent
579 while p
580 ancestors << p
581 p = p.parent
582 end
583
584 acc = []
585 p = preceding_node_of( node )
586 #puts "P = #{p.inspect}"
587 while p
588 if ancestors.include? p
589 ancestors.delete(p)
590 else
591 acc << p
592 end
593 p = preceding_node_of( p )
594 #puts "P = #{p.inspect}"
595 end
596 acc
597 end
598
599 def preceding_node_of( node )
600 #puts "NODE: #{node.inspect}"
601 #puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}"
602 #puts "PARENT NODE: #{node.parent}"
603 psn = node.previous_sibling_node
604 if psn.nil?
605 if node.parent.nil? or node.parent.class == Document
606 return nil
607 end
608 return node.parent
609 #psn = preceding_node_of( node.parent )
610 end
611 while psn and psn.kind_of? Element and psn.children.size > 0
612 psn = psn.children[-1]
613 end
614 psn
615 end
616
617 def following( node )
618 #puts "IN PRECEDING"
619 acc = []
620 p = next_sibling_node( node )
621 #puts "P = #{p.inspect}"
622 while p
623 acc << p
624 p = following_node_of( p )
625 #puts "P = #{p.inspect}"
626 end
627 acc
628 end
629
630 def following_node_of( node )
631 #puts "NODE: #{node.inspect}"
632 #puts "PREVIOUS NODE: #{node.previous_sibling_node.inspect}"
633 #puts "PARENT NODE: #{node.parent}"
634 if node.kind_of? Element and node.children.size > 0
635 return node.children[0]
636 end
637 return next_sibling_node(node)
638 end
639
640 def next_sibling_node(node)
641 psn = node.next_sibling_node
642 while psn.nil?
643 if node.parent.nil? or node.parent.class == Document
644 return nil
645 end
646 node = node.parent
647 psn = node.next_sibling_node
648 #puts "psn = #{psn.inspect}"
649 end
650 return psn
651 end
652
653 def norm b
654 case b
655 when true, false
656 return b
657 when 'true', 'false'
658 return Functions::boolean( b )
659 when /^\d+(\.\d+)?$/
660 return Functions::number( b )
661 else
662 return Functions::string( b )
663 end
664 end
665
666 def equality_relational_compare( set1, op, set2 )
667 #puts "EQ_REL_COMP(#{set1.inspect} #{op.inspect} #{set2.inspect})"
668 if set1.kind_of? Array and set2.kind_of? Array
669 #puts "#{set1.size} & #{set2.size}"
670 if set1.size == 1 and set2.size == 1
671 set1 = set1[0]
672 set2 = set2[0]
673 elsif set1.size == 0 or set2.size == 0
674 nd = set1.size==0 ? set2 : set1
675 rv = nd.collect { |il| compare( il, op, nil ) }
676 #puts "RV = #{rv.inspect}"
677 return rv
678 else
679 res = []
680 enum = SyncEnumerator.new( set1, set2 ).each { |i1, i2|
681 #puts "i1 = #{i1.inspect} (#{i1.class.name})"
682 #puts "i2 = #{i2.inspect} (#{i2.class.name})"
683 i1 = norm( i1 )
684 i2 = norm( i2 )
685 res << compare( i1, op, i2 )
686 }
687 return res
688 end
689 end
690 #puts "EQ_REL_COMP: #{set1.inspect} (#{set1.class.name}), #{op}, #{set2.inspect} (#{set2.class.name})"
691 #puts "COMPARING VALUES"
692 # If one is nodeset and other is number, compare number to each item
693 # in nodeset s.t. number op number(string(item))
694 # If one is nodeset and other is string, compare string to each item
695 # in nodeset s.t. string op string(item)
696 # If one is nodeset and other is boolean, compare boolean to each item
697 # in nodeset s.t. boolean op boolean(item)
698 if set1.kind_of? Array or set2.kind_of? Array
699 #puts "ISA ARRAY"
700 if set1.kind_of? Array
701 a = set1
702 b = set2
703 else
704 a = set2
705 b = set1
706 end
707
708 case b
709 when true, false
710 return a.collect {|v| compare( Functions::boolean(v), op, b ) }
711 when Numeric
712 return a.collect {|v| compare( Functions::number(v), op, b )}
713 when /^\d+(\.\d+)?$/
714 b = Functions::number( b )
715 #puts "B = #{b.inspect}"
716 return a.collect {|v| compare( Functions::number(v), op, b )}
717 else
718 #puts "Functions::string( #{b}(#{b.class.name}) ) = #{Functions::string(b)}"
719 b = Functions::string( b )
720 return a.collect { |v| compare( Functions::string(v), op, b ) }
721 end
722 else
723 # If neither is nodeset,
724 # If op is = or !=
725 # If either boolean, convert to boolean
726 # If either number, convert to number
727 # Else, convert to string
728 # Else
729 # Convert both to numbers and compare
730 s1 = set1.to_s
731 s2 = set2.to_s
732 #puts "EQ_REL_COMP: #{set1}=>#{s1}, #{set2}=>#{s2}"
733 if s1 == 'true' or s1 == 'false' or s2 == 'true' or s2 == 'false'
734 #puts "Functions::boolean(#{set1})=>#{Functions::boolean(set1)}"
735 #puts "Functions::boolean(#{set2})=>#{Functions::boolean(set2)}"
736 set1 = Functions::boolean( set1 )
737 set2 = Functions::boolean( set2 )
738 else
739 if op == :eq or op == :neq
740 if s1 =~ /^\d+(\.\d+)?$/ or s2 =~ /^\d+(\.\d+)?$/
741 set1 = Functions::number( s1 )
742 set2 = Functions::number( s2 )
743 else
744 set1 = Functions::string( set1 )
745 set2 = Functions::string( set2 )
746 end
747 else
748 set1 = Functions::number( set1 )
749 set2 = Functions::number( set2 )
750 end
751 end
752 #puts "EQ_REL_COMP: #{set1} #{op} #{set2}"
753 #puts ">>> #{compare( set1, op, set2 )}"
754 return compare( set1, op, set2 )
755 end
756 return false
757 end
758
759 def compare a, op, b
760 #puts "COMPARE #{a.inspect}(#{a.class.name}) #{op} #{b.inspect}(#{b.class.name})"
761 case op
762 when :eq
763 a == b
764 when :neq
765 a != b
766 when :lt
767 a < b
768 when :lteq
769 a <= b
770 when :gt
771 a > b
772 when :gteq
773 a >= b
774 when :and
775 a and b
776 when :or
777 a or b
778 else
779 false
780 end
781 end
782 end
783end
Note: See TracBrowser for help on using the repository browser.