55require_relative 'namespace'
66require_relative 'xmltokens'
77require_relative 'attribute'
8- require_relative 'syncenumerator'
98require_relative 'parsers/xpathparser'
109
1110class Object
@@ -141,7 +140,7 @@ def match(path_stack, nodeset)
141140 when Array # nodeset
142141 unnode ( result )
143142 else
144- result
143+ [ result ]
145144 end
146145 end
147146
@@ -341,26 +340,24 @@ def expr( path_stack, nodeset, context=nil )
341340 var_name = path_stack . shift
342341 return [ @variables [ var_name ] ]
343342
344- # :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq
345- # TODO: Special case for :or and :and -- not evaluate the right
346- # operand if the left alone determines result (i.e. is true for
347- # :or and false for :and).
348- when :eq , :neq , :lt , :lteq , :gt , :gteq , :or
343+ when :eq , :neq , :lt , :lteq , :gt , :gteq
349344 left = expr ( path_stack . shift , nodeset . dup , context )
350345 right = expr ( path_stack . shift , nodeset . dup , context )
351346 res = equality_relational_compare ( left , op , right )
352347 trace ( op , left , right , res ) if @debug
353348 return res
354349
350+ when :or
351+ left = expr ( path_stack . shift , nodeset . dup , context )
352+ return true if Functions . boolean ( left )
353+ right = expr ( path_stack . shift , nodeset . dup , context )
354+ return Functions . boolean ( right )
355+
355356 when :and
356- left = expr ( path_stack . shift , nodeset . dup , context )
357- return [ ] unless left
358- if left . respond_to? ( :inject ) and !left . inject ( false ) { |a , b | a | b }
359- return [ ]
360- end
361- right = expr ( path_stack . shift , nodeset . dup , context )
362- res = equality_relational_compare ( left , op , right )
363- return res
357+ left = expr ( path_stack . shift , nodeset . dup , context )
358+ return false unless Functions . boolean ( left )
359+ right = expr ( path_stack . shift , nodeset . dup , context )
360+ return Functions . boolean ( right )
364361
365362 when :div , :mod , :mult , :plus , :minus
366363 left = expr ( path_stack . shift , nodeset , context )
@@ -397,31 +394,34 @@ def expr( path_stack, nodeset, context=nil )
397394 when :function
398395 func_name = path_stack . shift . tr ( '-' , '_' )
399396 arguments = path_stack . shift
400- subcontext = context ? nil : { :size => nodeset . size }
401-
402- res = [ ]
403- cont = context
404- nodeset . each_with_index do |node , i |
405- if subcontext
406- if node . is_a? ( XPathNode )
407- subcontext [ :node ] = node . raw_node
408- subcontext [ :index ] = node . position
409- else
410- subcontext [ :node ] = node
411- subcontext [ :index ] = i
412- end
413- cont = subcontext
414- end
415- arg_clone = arguments . dclone
416- args = arg_clone . collect do |arg |
417- result = expr ( arg , [ node ] , cont )
418- result = unnode ( result ) if result . is_a? ( Array )
419- result
397+
398+ if nodeset . size != 1
399+ message = "[BUG] Node set size must be 1 for function call: "
400+ message += "<#{ func_name } >: <#{ nodeset . inspect } >: "
401+ message += "<#{ arguments . inspect } >"
402+ raise message
403+ end
404+
405+ node = nodeset . first
406+ if context
407+ target_context = context
408+ else
409+ target_context = { :size => nodeset . size }
410+ if node . is_a? ( XPathNode )
411+ target_context [ :node ] = node . raw_node
412+ target_context [ :index ] = node . position
413+ else
414+ target_context [ :node ] = node
415+ target_context [ :index ] = 1
420416 end
421- Functions . context = cont
422- res << Functions . send ( func_name , *args )
423417 end
424- return res
418+ args = arguments . dclone . collect do |arg |
419+ result = expr ( arg , nodeset , target_context )
420+ result = unnode ( result ) if result . is_a? ( Array )
421+ result
422+ end
423+ Functions . context = target_context
424+ return Functions . send ( func_name , *args )
425425
426426 else
427427 raise "[BUG] Unexpected path: <#{ op . inspect } >: <#{ path_stack . inspect } >"
@@ -806,31 +806,28 @@ def norm b
806806 end
807807 end
808808
809- def equality_relational_compare ( set1 , op , set2 )
809+ def equality_relational_compare ( set1 , op , set2 )
810810 set1 = unnode ( set1 ) if set1 . is_a? ( Array )
811811 set2 = unnode ( set2 ) if set2 . is_a? ( Array )
812+
812813 if set1 . kind_of? Array and set2 . kind_of? Array
813- if set1 . size == 0 or set2 . size == 0
814- nd = set1 . size ==0 ? set2 : set1
815- rv = nd . collect { |il | compare ( il , op , nil ) }
816- return rv
817- else
818- res = [ ]
819- SyncEnumerator . new ( set1 , set2 ) . each { |i1 , i2 |
820- i1 = norm ( i1 )
821- i2 = norm ( i2 )
822- res << compare ( i1 , op , i2 )
823- }
824- return res
814+ # If both objects to be compared are node-sets, then the
815+ # comparison will be true if and only if there is a node in the
816+ # first node-set and a node in the second node-set such that the
817+ # result of performing the comparison on the string-values of
818+ # the two nodes is true.
819+ set1 . product ( set2 ) . any? do |node1 , node2 |
820+ node_string1 = Functions . string ( node1 )
821+ node_string2 = Functions . string ( node2 )
822+ compare ( node_string1 , op , node_string2 )
825823 end
826- end
827- # If one is nodeset and other is number, compare number to each item
828- # in nodeset s.t. number op number(string(item))
829- # If one is nodeset and other is string, compare string to each item
830- # in nodeset s.t. string op string(item)
831- # If one is nodeset and other is boolean, compare boolean to each item
832- # in nodeset s.t. boolean op boolean(item)
833- if set1 . kind_of? Array or set2 . kind_of? Array
824+ elsif set1 . kind_of? Array or set2 . kind_of? Array
825+ # If one is nodeset and other is number, compare number to each item
826+ # in nodeset s.t. number op number(string(item))
827+ # If one is nodeset and other is string, compare string to each item
828+ # in nodeset s.t. string op string(item)
829+ # If one is nodeset and other is boolean, compare boolean to each item
830+ # in nodeset s.t. boolean op boolean(item)
834831 if set1 . kind_of? Array
835832 a = set1
836833 b = set2
@@ -841,15 +838,23 @@ def equality_relational_compare( set1, op, set2 )
841838
842839 case b
843840 when true , false
844- return unnode ( a ) { |v | compare ( Functions ::boolean ( v ) , op , b ) }
841+ each_unnode ( a ) . any? do |unnoded |
842+ compare ( Functions . boolean ( unnoded ) , op , b )
843+ end
845844 when Numeric
846- return unnode ( a ) { |v | compare ( Functions ::number ( v ) , op , b ) }
847- when /^\d +(\. \d +)?$/
848- b = Functions ::number ( b )
849- return unnode ( a ) { |v | compare ( Functions ::number ( v ) , op , b ) }
845+ each_unnode ( a ) . any? do |unnoded |
846+ compare ( Functions . number ( unnoded ) , op , b )
847+ end
848+ when /\A \d +(\. \d +)?\z /
849+ b = Functions . number ( b )
850+ each_unnode ( a ) . any? do |unnoded |
851+ compare ( Functions . number ( unnoded ) , op , b )
852+ end
850853 else
851- b = Functions ::string ( b )
852- return unnode ( a ) { |v | compare ( Functions ::string ( v ) , op , b ) }
854+ b = Functions ::string ( b )
855+ each_unnode ( a ) . any? do |unnoded |
856+ compare ( Functions ::string ( unnoded ) , op , b )
857+ end
853858 end
854859 else
855860 # If neither is nodeset,
@@ -880,13 +885,12 @@ def equality_relational_compare( set1, op, set2 )
880885 set2 = Functions ::number ( set2 )
881886 end
882887 end
883- return compare ( set1 , op , set2 )
888+ compare ( set1 , op , set2 )
884889 end
885- return false
886890 end
887891
888- def compare a , op , b
889- case op
892+ def compare ( a , operator , b )
893+ case operator
890894 when :eq
891895 a == b
892896 when :neq
@@ -899,22 +903,27 @@ def compare a, op, b
899903 a > b
900904 when :gteq
901905 a >= b
902- when :and
903- a and b
904- when :or
905- a or b
906906 else
907- false
907+ message = "[BUG] Unexpected compare operator: " +
908+ "<#{ operator . inspect } >: <#{ a . inspect } >: <#{ b . inspect } >"
909+ raise message
908910 end
909911 end
910912
911- def unnode ( nodeset )
912- nodeset . collect do |node |
913+ def each_unnode ( nodeset )
914+ return to_enum ( __method__ , nodeset ) unless block_given?
915+ nodeset . each do |node |
913916 if node . is_a? ( XPathNode )
914917 unnoded = node . raw_node
915918 else
916919 unnoded = node
917920 end
921+ yield ( unnoded )
922+ end
923+ end
924+
925+ def unnode ( nodeset )
926+ each_unnode ( nodeset ) . collect do |unnoded |
918927 unnoded = yield ( unnoded ) if block_given?
919928 unnoded
920929 end
0 commit comments