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