Wrap predicate AST nodes _around_ other nodes.
This means that "foo[1]" uses this AST: (predicate (test nil "foo") (int 1)) Instead of this AST: (test nil "foo" (int 1)) This makes it easier for the XPath evaluator to process predicates correctly.
This commit is contained in:
parent
24350fa457
commit
817a5e075b
|
@ -163,7 +163,7 @@ module Oga
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Processes a node test and optionally a predicate.
|
# Processes a node test.
|
||||||
#
|
#
|
||||||
# @param [AST::Node] ast_node
|
# @param [AST::Node] ast_node
|
||||||
# @param [Oga::XML::NodeSet] context
|
# @param [Oga::XML::NodeSet] context
|
||||||
|
@ -171,20 +171,36 @@ module Oga
|
||||||
#
|
#
|
||||||
def on_test(ast_node, context)
|
def on_test(ast_node, context)
|
||||||
nodes = XML::NodeSet.new
|
nodes = XML::NodeSet.new
|
||||||
predicate = ast_node.children[2]
|
|
||||||
xpath_index = 1
|
|
||||||
|
|
||||||
context.each do |xml_node|
|
context.each do |xml_node|
|
||||||
next unless node_matches?(xml_node, ast_node)
|
nodes << xml_node if node_matches?(xml_node, ast_node)
|
||||||
|
end
|
||||||
|
|
||||||
if predicate
|
return nodes
|
||||||
retval = with_node_set(context) do
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Processes a predicate.
|
||||||
|
#
|
||||||
|
# @param [AST::Node] ast_node
|
||||||
|
# @param [Oga::XML::NodeSet] context
|
||||||
|
# @return [Oga::XML::NodeSet]
|
||||||
|
#
|
||||||
|
def on_predicate(ast_node, context)
|
||||||
|
test, predicate = *ast_node
|
||||||
|
|
||||||
|
initial_nodes = process(test, context)
|
||||||
|
final_nodes = XML::NodeSet.new
|
||||||
|
xpath_index = 1
|
||||||
|
|
||||||
|
initial_nodes.each do |xml_node|
|
||||||
|
retval = with_node_set(initial_nodes) do
|
||||||
process(predicate, XML::NodeSet.new([xml_node]))
|
process(predicate, XML::NodeSet.new([xml_node]))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Numeric values are used as node set indexes.
|
# Numeric values are used as node set indexes.
|
||||||
if retval.is_a?(Numeric)
|
if retval.is_a?(Numeric)
|
||||||
nodes << xml_node if retval.to_i == xpath_index
|
final_nodes << xml_node if retval.to_i == xpath_index
|
||||||
|
|
||||||
# Node sets, strings, booleans, etc
|
# Node sets, strings, booleans, etc
|
||||||
elsif retval
|
elsif retval
|
||||||
|
@ -192,16 +208,13 @@ module Oga
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
nodes << xml_node
|
final_nodes << xml_node
|
||||||
end
|
|
||||||
else
|
|
||||||
nodes << xml_node
|
|
||||||
end
|
end
|
||||||
|
|
||||||
xpath_index += 1
|
xpath_index += 1
|
||||||
end
|
end
|
||||||
|
|
||||||
return nodes
|
return final_nodes
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
|
@ -40,9 +40,8 @@ rule
|
||||||
;
|
;
|
||||||
|
|
||||||
expression_members
|
expression_members
|
||||||
: node_test_as_axis
|
: expression_with_predicate
|
||||||
| operator
|
| operator
|
||||||
| axis
|
|
||||||
| string
|
| string
|
||||||
| number
|
| number
|
||||||
| call
|
| call
|
||||||
|
@ -51,9 +50,14 @@ rule
|
||||||
| variable
|
| variable
|
||||||
;
|
;
|
||||||
|
|
||||||
path_member
|
expression_with_predicate
|
||||||
: node_test_as_axis
|
: node_test_as_axis
|
||||||
|
| node_test_as_axis predicate { s(:predicate, val[0], val[1]) }
|
||||||
| axis
|
| axis
|
||||||
|
| axis predicate { s(:predicate, val[0], val[1]) }
|
||||||
|
|
||||||
|
path_member
|
||||||
|
: expression_with_predicate
|
||||||
| call
|
| call
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -83,7 +87,7 @@ rule
|
||||||
|
|
||||||
node_test
|
node_test
|
||||||
: node_name { s(:test, *val[0]) }
|
: node_name { s(:test, *val[0]) }
|
||||||
| node_name predicate { s(:test, *val[0], val[1]) }
|
#| node_name predicate { s(:predicate, s(:test, *val[0]), val[1]) }
|
||||||
| type_test { val[0] }
|
| type_test { val[0] }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
@ -43,18 +43,13 @@ describe Oga::XPath::Parser do
|
||||||
parse_xpath('div[@class="foo"]/bar()').should == s(
|
parse_xpath('div[@class="foo"]/bar()').should == s(
|
||||||
:path,
|
:path,
|
||||||
s(
|
s(
|
||||||
:axis,
|
:predicate,
|
||||||
'child',
|
s(:axis, 'child', s(:test, nil, 'div')),
|
||||||
s(
|
|
||||||
:test,
|
|
||||||
nil,
|
|
||||||
'div',
|
|
||||||
s(
|
s(
|
||||||
:eq,
|
:eq,
|
||||||
s(:axis, 'attribute', s(:test, nil, 'class')),
|
s(:axis, 'attribute', s(:test, nil, 'class')),
|
||||||
s(:string, 'foo')
|
s(:string, 'foo')
|
||||||
)
|
)
|
||||||
)
|
|
||||||
),
|
),
|
||||||
s(:call, 'bar')
|
s(:call, 'bar')
|
||||||
)
|
)
|
||||||
|
@ -64,14 +59,14 @@ describe Oga::XPath::Parser do
|
||||||
parse_xpath('A[@x]/B[@x]/bar()').should == s(
|
parse_xpath('A[@x]/B[@x]/bar()').should == s(
|
||||||
:path,
|
:path,
|
||||||
s(
|
s(
|
||||||
:axis,
|
:predicate,
|
||||||
'child',
|
s(:axis, 'child', s(:test, nil, 'A')),
|
||||||
s(:test, nil, 'A', s(:axis, 'attribute', s(:test, nil, 'x')))
|
s(:axis, 'attribute', s(:test, nil, 'x'))
|
||||||
),
|
),
|
||||||
s(
|
s(
|
||||||
:axis,
|
:predicate,
|
||||||
'child',
|
s(:axis, 'child', s(:test, nil, 'B')),
|
||||||
s(:test, nil, 'B', s(:axis, 'attribute', s(:test, nil, 'x')))
|
s(:axis, 'attribute', s(:test, nil, 'x'))
|
||||||
),
|
),
|
||||||
s(:call, 'bar')
|
s(:call, 'bar')
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,32 +4,26 @@ describe Oga::XPath::Parser do
|
||||||
context 'predicates' do
|
context 'predicates' do
|
||||||
example 'parse a single predicate' do
|
example 'parse a single predicate' do
|
||||||
parse_xpath('foo[@class="bar"]').should == s(
|
parse_xpath('foo[@class="bar"]').should == s(
|
||||||
:axis,
|
:predicate,
|
||||||
'child',
|
s(:axis, 'child', s(:test, nil, 'foo')),
|
||||||
s(
|
s(
|
||||||
:test,
|
:eq,
|
||||||
nil,
|
s(:axis, 'attribute', s(:test, nil, 'class')),
|
||||||
'foo',
|
s(:string, 'bar')
|
||||||
s(:eq, s(:axis, 'attribute', s(:test, nil, 'class')), s(:string, 'bar'))
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
example 'parse a predicate using the or operator' do
|
example 'parse a predicate using the or operator' do
|
||||||
parse_xpath('foo[@x="bar" or @x="baz"]').should == s(
|
parse_xpath('foo[@x="bar" or @x="baz"]').should == s(
|
||||||
:axis,
|
:predicate,
|
||||||
'child',
|
s(:axis, 'child', s(:test, nil, 'foo')),
|
||||||
s(
|
|
||||||
:test,
|
|
||||||
nil,
|
|
||||||
'foo',
|
|
||||||
s(
|
s(
|
||||||
:or,
|
:or,
|
||||||
s(:eq, s(:axis, 'attribute', s(:test, nil, 'x')), s(:string, 'bar')),
|
s(:eq, s(:axis, 'attribute', s(:test, nil, 'x')), s(:string, 'bar')),
|
||||||
s(:eq, s(:axis, 'attribute', s(:test, nil, 'x')), s(:string, 'baz')),
|
s(:eq, s(:axis, 'attribute', s(:test, nil, 'x')), s(:string, 'baz')),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,9 +8,9 @@ describe Oga::XPath::Parser do
|
||||||
|
|
||||||
example 'parse a variable reference in a predicate' do
|
example 'parse a variable reference in a predicate' do
|
||||||
parse_xpath('foo[$bar]').should == s(
|
parse_xpath('foo[$bar]').should == s(
|
||||||
:axis,
|
:predicate,
|
||||||
'child',
|
s(:axis, 'child', s(:test, nil, 'foo')),
|
||||||
s(:test, nil, 'foo', s(:var, 'bar'))
|
s(:var, 'bar')
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue