From 6d3c5c2ce93cbce337338bdc1a4971da72517038 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 23 Feb 2016 22:24:07 +0100 Subject: [PATCH] XPath support for nested pipe operators Basically this will process the left-hand side first, assign the result to a variable and then append this set with the nodes from the right-hand side. Fixes #149 --- lib/oga/xpath/compiler.rb | 22 +++++++----- .../oga/xpath/compiler/operators/pipe_spec.rb | 34 +++++++++++++++++-- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/lib/oga/xpath/compiler.rb b/lib/oga/xpath/compiler.rb index 2fc22cf..ec711b3 100644 --- a/lib/oga/xpath/compiler.rb +++ b/lib/oga/xpath/compiler.rb @@ -590,16 +590,20 @@ module Oga def on_pipe(ast, input, &block) left, right = *ast - union = unique_literal(:union) - conversion = literal(Conversion) + union = unique_literal(:union) - union.assign(literal(XML::NodeSet).new) - .followed_by(process(left, input) { |node| union << node }) - .followed_by(process(right, input) { |node| union << node }) - .followed_by do - # block present means we're in a predicate - block ? conversion.to_boolean(union).if_true(&block) : union - end + # Expressions such as "a | b | c" + if left.type == :pipe + union.assign(process(left, input)) + .followed_by(process(right, input) { |node| union << node }) + .followed_by(union) + # Expressions such as "a | b" + else + union.assign(literal(XML::NodeSet).new) + .followed_by(process(left, input) { |node| union << node }) + .followed_by(process(right, input) { |node| union << node }) + .followed_by(union) + end end # @param [AST::Node] ast diff --git a/spec/oga/xpath/compiler/operators/pipe_spec.rb b/spec/oga/xpath/compiler/operators/pipe_spec.rb index 40bbb65..b9ebbed 100644 --- a/spec/oga/xpath/compiler/operators/pipe_spec.rb +++ b/spec/oga/xpath/compiler/operators/pipe_spec.rb @@ -3,10 +3,12 @@ require 'spec_helper' describe Oga::XPath::Compiler do describe 'pipe operator' do before do - @document = parse('') + @document = parse('') - @a1 = @document.children[0].children[0] - @b1 = @document.children[0].children[1] + @root = @document.children[0] + @a1 = @root.children[0] + @b1 = @root.children[1] + @c1 = @root.children[2] end it 'merges two node sets' do @@ -24,5 +26,31 @@ describe Oga::XPath::Compiler do it 'merges two identical sets' do evaluate_xpath(@document, 'root/a | root/a').should == node_set(@a1) end + + it 'merges three sets' do + evaluate_xpath(@document, 'root/a | root/b | root/c') + .should == node_set(@a1, @b1, @c1) + end + + it 'merges three identical sets' do + evaluate_xpath(@document, 'root/a | root/a | root/a') + .should == node_set(@a1) + end + + it 'merges two non-empty sets in a predicate' do + evaluate_xpath(@document, 'root[a | b]').should == node_set(@root) + end + + it 'merges three non-empty sets in a predicate' do + evaluate_xpath(@document, 'root[a | b | c]').should == node_set(@root) + end + + it 'merges two empty sets in a predicate' do + evaluate_xpath(@document, 'root[x | y]').should == node_set + end + + it 'merges three empty sets in a predicate' do + evaluate_xpath(@document, 'root[x | y | z]').should == node_set + end end end