From 106d83e78067fd30707c31b825fd111983eaf838 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 18 Aug 2015 15:33:47 +0200 Subject: [PATCH] XPath compiler support for sum() --- lib/oga/xpath/compiler.rb | 22 ++++++++++++ spec/oga/xpath/compiler/calls/sum_spec.rb | 41 ++++++++++++++--------- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/lib/oga/xpath/compiler.rb b/lib/oga/xpath/compiler.rb index 422ae70..73bdd28 100644 --- a/lib/oga/xpath/compiler.rb +++ b/lib/oga/xpath/compiler.rb @@ -1103,6 +1103,28 @@ module Oga end end + # @param [Oga::Ruby::Node] input + # @param [AST::Node] arg + # @return [Oga::Ruby::Node] + def on_call_sum(input, arg) + unless return_nodeset?(arg) + raise TypeError, 'sum() can only operate on a path, axis or predicate' + end + + sum_var = unique_literal(:sum) + conversion = literal(Conversion) + + sum_var.assign(literal(0.0)) + .followed_by do + process(arg, input) do |matched_node| + sum_var.assign(sum_var + conversion.to_float(matched_node.text)) + end + end + .followed_by do + block_given? ? sum_var.zero?.not.if_true { yield } : sum_var + end + end + ## # Delegates type tests to specific handlers. # diff --git a/spec/oga/xpath/compiler/calls/sum_spec.rb b/spec/oga/xpath/compiler/calls/sum_spec.rb index 653facb..19e5cc7 100644 --- a/spec/oga/xpath/compiler/calls/sum_spec.rb +++ b/spec/oga/xpath/compiler/calls/sum_spec.rb @@ -4,25 +4,36 @@ describe Oga::XPath::Compiler do describe 'sum() spec' do before do @document = parse('12') + + @a1 = @document.children[0].children[0] end - it 'returns the sum of the node' do - # The test of is "12", which is then converted to a number. - evaluate_xpath(@document, 'sum(root)').should == 12.0 + describe 'at the top-level' do + it 'returns the sum of the node' do + # The test of is "12", which is then converted to a number. + evaluate_xpath(@document, 'sum(root)').should == 12.0 + end + + it 'returns the sum of the child nodes of the node' do + evaluate_xpath(@document, 'sum(root/*)').should == 3.0 + end + + it 'returns zero by default' do + evaluate_xpath(@document, 'sum(foo)').should be_zero + end + + it 'raises a TypeError for non node set arguments' do + block = -> { evaluate_xpath(@document, 'sum("foo")') } + + block.should raise_error(TypeError) + end end - it 'returns the sum of the child nodes of the node' do - evaluate_xpath(@document, 'sum(root/*)').should == 3.0 - end - - it 'returns zero by default' do - evaluate_xpath(@document, 'sum(foo)').should be_zero - end - - it 'raises a TypeError for non node set arguments' do - block = -> { evaluate_xpath(@document, 'sum("foo")') } - - block.should raise_error(TypeError) + describe 'in a predicate' do + it 'returns a NodeSet containing all matching nodes' do + evaluate_xpath(@document, 'root/a[sum(/root/*)]') + .should == node_set(@a1) + end end end end