diff --git a/lib/oga/xpath/evaluator.rb b/lib/oga/xpath/evaluator.rb
index 1d01f0d..dc59e97 100644
--- a/lib/oga/xpath/evaluator.rb
+++ b/lib/oga/xpath/evaluator.rb
@@ -756,6 +756,35 @@ module Oga
end
end
+ ##
+ # Evaluates the `string()` function call.
+ #
+ # This function call converts the given argument *or* the current context
+ # node to a string. If a node set is given then only the first node is
+ # converted to a string.
+ #
+ # @param [Oga::XML::NodeSet] context
+ # @param [Oga::XPath::Node] expression
+ # @return [Oga::XML::NodeSet]
+ #
+ def on_call_string(context, expression = nil)
+ if expression
+ convert = process(expression, context)
+
+ if convert.is_a?(XML::NodeSet)
+ convert = convert.first
+ end
+ else
+ convert = current_node
+ end
+
+ if convert.respond_to?(:text)
+ return convert.text
+ else
+ return convert.to_s
+ end
+ end
+
##
# Processes an `(int)` node. This method simply returns the value as a
# Float.
diff --git a/spec/oga/xpath/evaluator/calls/string_spec.rb b/spec/oga/xpath/evaluator/calls/string_spec.rb
new file mode 100644
index 0000000..b45b543
--- /dev/null
+++ b/spec/oga/xpath/evaluator/calls/string_spec.rb
@@ -0,0 +1,76 @@
+require 'spec_helper'
+
+describe Oga::XPath::Evaluator do
+ context 'string() function' do
+ before do
+ @document = parse(<<-EOF)
+
+ a1
+ b1
+
+
+
+ EOF
+ @evaluator = described_class.new(@document)
+ end
+
+ context 'outside predicates' do
+ example 'convert the node to a string' do
+ @evaluator.evaluate('string(root)')
+ .should == "\n a1\n b1\n \n foobar\n"
+ end
+
+ example 'convert the node to a string' do
+ @evaluator.evaluate('string(root/a)').should == 'a1'
+ end
+
+ example 'convert the node to a string' do
+ @evaluator.evaluate('string(root/b)').should == 'b1'
+ end
+
+ example 'convert the "num" attribute to a string' do
+ @evaluator.evaluate('string(root/b/@num)').should == '10'
+ end
+
+ example 'convert the first node in a set to a string' do
+ @evaluator.evaluate('string(root/*)').should == 'a1'
+ end
+
+ example 'convert an integer to a string' do
+ @evaluator.evaluate('string(10)').should == '10.0'
+ end
+
+ example 'convert a float to a string' do
+ @evaluator.evaluate('string(10.5)').should == '10.5'
+ end
+
+ example 'convert a string to a string' do
+ @evaluator.evaluate('string("foo")').should == 'foo'
+ end
+
+ example 'convert a comment to a string' do
+ @evaluator.evaluate('string(root/c/comment())').should == 'foo'
+ end
+
+ example 'convert a CDATA to a string' do
+ @evaluator.evaluate('string(root/d/node())').should == 'foobar'
+ end
+
+ example 'return an empty string by default' do
+ @evaluator.evaluate('string(foobar)').should == ''
+ end
+ end
+
+ context 'inside predicates' do
+ before do
+ @set = @evaluator.evaluate('root/b[string()]')
+ end
+
+ it_behaves_like :node_set, :length => 1
+
+ example 'return the node' do
+ @set[0].name.should == 'b'
+ end
+ end
+ end
+end