XPath compiler support for substring()

This commit is contained in:
Yorick Peterse 2015-08-18 15:15:54 +02:00
parent be05008be9
commit e7e5b123cf
2 changed files with 77 additions and 19 deletions

View File

@ -447,7 +447,7 @@ module Oga
# @return [Oga::Ruby::Node]
#
def on_predicate_index(test, predicate, input)
int1 = literal('1')
int1 = literal(1)
index = to_int(predicate)
index_var = literal(:index)
@ -732,9 +732,9 @@ module Oga
raise TypeError, 'count() can only operate on NodeSet instances'
end
count.assign(literal('0.0'))
count.assign(literal(0.0))
.followed_by do
process(arg, input) { count.assign(count + literal('1')) }
process(arg, input) { count.assign(count + literal(1)) }
end
.followed_by(count)
end
@ -1063,6 +1063,46 @@ module Oga
end
end
# @param [Oga::Ruby::Node] input
# @param [AST::Node] haystack
# @param [AST::Node] start
# @param [AST::Node] length
# @return [Oga::Ruby::Node]
def on_call_substring(input, haystack, start, length = nil)
haystack_var = unique_literal(:haystack)
start_var = unique_literal(:start)
stop_var = unique_literal(:stop)
length_var = unique_literal(:length)
conversion = literal(Conversion)
haystack_var.assign(try_match_first_node(haystack, input))
.followed_by do
start_var.assign(try_match_first_node(start, input))
.followed_by do
start_var.assign(start_var - literal(1))
end
end
.followed_by do
if length
length_var.assign(try_match_first_node(length, input))
.followed_by do
length_int = conversion.to_float(length_var)
.to_i - literal(1)
stop_var.assign(start_var + length_int)
end
else
stop_var.assign(literal(-1))
end
end
.followed_by do
substring = conversion
.to_string(haystack_var)[range(start_var, stop_var)]
block_given? ? substring.empty?.not.if_true { yield } : substring
end
end
##
# Delegates type tests to specific handlers.
#
@ -1108,6 +1148,13 @@ module Oga
Ruby::Node.new(:lit, [value.to_s])
end
# @param [Oga::Ruby::Node] start
# @param [Oga::Ruby::Node] stop
# @return [Oga::Ruby::Node]
def range(start, stop)
Ruby::Node.new(:range, [start, stop])
end
# @param [String] name
# @return [Oga::Ruby::Node]
def unique_literal(name)

View File

@ -4,26 +4,37 @@ describe Oga::XPath::Compiler do
describe 'substring() function' do
before do
@document = parse('<root><a>foobar</a><b>3</b></root>')
@a1 = @document.children[0].children[0]
end
it 'returns the substring of a string' do
evaluate_xpath(@document, 'substring("foo", 2)').should == 'oo'
describe 'at the top-level' do
it 'returns the substring of a string' do
evaluate_xpath(@document, 'substring("foo", 2)').should == 'oo'
end
it 'returns the substring of a string using a custom length' do
evaluate_xpath(@document, 'substring("foo", 2, 1)').should == 'o'
end
it 'returns the substring of a node set' do
evaluate_xpath(@document, 'substring(root/a, 2)').should == 'oobar'
end
it 'returns the substring of a node set with a node set as the length' do
evaluate_xpath(@document, 'substring(root/a, 1, root/b)').should == 'foo'
end
it 'returns an empty string when the source string is empty' do
evaluate_xpath(@document, 'substring("", 1, 3)').should == ''
end
end
it 'returns the substring of a string using a custom length' do
evaluate_xpath(@document, 'substring("foo", 2, 1)').should == 'o'
end
it 'returns the substring of a node set' do
evaluate_xpath(@document, 'substring(root/a, 2)').should == 'oobar'
end
it 'returns the substring of a node set with a node set as the length' do
evaluate_xpath(@document, 'substring(root/a, 1, root/b)').should == 'foo'
end
it 'returns an empty string when the source string is empty' do
evaluate_xpath(@document, 'substring("", 1, 3)').should == ''
describe 'in a predicate' do
it 'returns a NodeSet containing all matching nodes' do
evaluate_xpath(@document, 'root/a[substring("foo", 1, 2)]')
.should == node_set(@a1)
end
end
end
end