method
expr
v2_6_3 -
Show latest stable
- Class:
REXML::XPathParser
expr( path_stack, nodeset, context=nil )private
Expr takes a stack of path elements and a set of nodes (either a Parent or an Array and returns an Array of matching nodes
# File lib/rexml/xpath_parser.rb, line 164
def expr( path_stack, nodeset, context=nil )
# enter(:expr, path_stack, nodeset)
return nodeset if path_stack.length == 0 || nodeset.length == 0
while path_stack.length > 0
# trace(:while, path_stack, nodeset)
if nodeset.length == 0
path_stack.clear
return []
end
op = path_stack.shift
case op
when :document
first_raw_node = nodeset.first.raw_node
nodeset = [XPathNode.new(first_raw_node.root_node, position: 1)]
when :self
nodeset = step(path_stack) do
[nodeset]
end
when :child
nodeset = step(path_stack) do
child(nodeset)
end
when :literal
# trace(:literal, path_stack, nodeset)
return path_stack.shift
when :attribute
nodeset = step(path_stack, any_type: :attribute) do
nodesets = []
nodeset.each do |node|
raw_node = node.raw_node
next unless raw_node.node_type == :element
attributes = raw_node.attributes
next if attributes.empty?
nodesets << attributes.each_attribute.collect.with_index do |attribute, i|
XPathNode.new(attribute, position: i + 1)
end
end
nodesets
end
when :namespace
pre_defined_namespaces = {
"xml" => "http://www.w3.org/XML/1998/namespace",
}
nodeset = step(path_stack, any_type: :namespace) do
nodesets = []
nodeset.each do |node|
raw_node = node.raw_node
case raw_node.node_type
when :element
if @namespaces
nodesets << pre_defined_namespaces.merge(@namespaces)
else
nodesets << pre_defined_namespaces.merge(raw_node.namespaces)
end
when :attribute
if @namespaces
nodesets << pre_defined_namespaces.merge(@namespaces)
else
nodesets << pre_defined_namespaces.merge(raw_node.element.namespaces)
end
end
end
nodesets
end
when :parent
nodeset = step(path_stack) do
nodesets = []
nodeset.each do |node|
raw_node = node.raw_node
if raw_node.node_type == :attribute
parent = raw_node.element
else
parent = raw_node.parent
end
nodesets << [XPathNode.new(parent, position: 1)] if parent
end
nodesets
end
when :ancestor
nodeset = step(path_stack) do
nodesets = []
# new_nodes = {}
nodeset.each do |node|
raw_node = node.raw_node
new_nodeset = []
while raw_node.parent
raw_node = raw_node.parent
# next if new_nodes.key?(node)
new_nodeset << XPathNode.new(raw_node,
position: new_nodeset.size + 1)
# new_nodes[node] = true
end
nodesets << new_nodeset unless new_nodeset.empty?
end
nodesets
end
when :ancestor_or_self
nodeset = step(path_stack) do
nodesets = []
# new_nodes = {}
nodeset.each do |node|
raw_node = node.raw_node
next unless raw_node.node_type == :element
new_nodeset = [XPathNode.new(raw_node, position: 1)]
# new_nodes[node] = true
while raw_node.parent
raw_node = raw_node.parent
# next if new_nodes.key?(node)
new_nodeset << XPathNode.new(raw_node,
position: new_nodeset.size + 1)
# new_nodes[node] = true
end
nodesets << new_nodeset unless new_nodeset.empty?
end
nodesets
end
when :descendant_or_self
nodeset = step(path_stack) do
descendant(nodeset, true)
end
when :descendant
nodeset = step(path_stack) do
descendant(nodeset, false)
end
when :following_sibling
nodeset = step(path_stack) do
nodesets = []
nodeset.each do |node|
raw_node = node.raw_node
next unless raw_node.respond_to?(:parent)
next if raw_node.parent.nil?
all_siblings = raw_node.parent.children
current_index = all_siblings.index(raw_node)
following_siblings = all_siblings[(current_index + 1)..-1]
next if following_siblings.empty?
nodesets << following_siblings.collect.with_index do |sibling, i|
XPathNode.new(sibling, position: i + 1)
end
end
nodesets
end
when :preceding_sibling
nodeset = step(path_stack, order: :reverse) do
nodesets = []
nodeset.each do |node|
raw_node = node.raw_node
next unless raw_node.respond_to?(:parent)
next if raw_node.parent.nil?
all_siblings = raw_node.parent.children
current_index = all_siblings.index(raw_node)
preceding_siblings = all_siblings[0, current_index].reverse
next if preceding_siblings.empty?
nodesets << preceding_siblings.collect.with_index do |sibling, i|
XPathNode.new(sibling, position: i + 1)
end
end
nodesets
end
when :preceding
nodeset = step(path_stack, order: :reverse) do
unnode(nodeset) do |node|
preceding(node)
end
end
when :following
nodeset = step(path_stack) do
unnode(nodeset) do |node|
following(node)
end
end
when :variable
var_name = path_stack.shift
return [@variables[var_name]]
# :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq
# TODO: Special case for :or and :and -- not evaluate the right
# operand if the left alone determines result (i.e. is true for
# :or and false for :and).
when :eq, :neq, :lt, :lteq, :gt, :gteq, :or
left = expr( path_stack.shift, nodeset.dup, context )
right = expr( path_stack.shift, nodeset.dup, context )
res = equality_relational_compare( left, op, right )
# trace(op, left, right, res)
return res
when :and
left = expr( path_stack.shift, nodeset.dup, context )
return [] unless left
if left.respond_to?(:inject) and !left.inject(false) {|a,b| a | b}
return []
end
right = expr( path_stack.shift, nodeset.dup, context )
res = equality_relational_compare( left, op, right )
return res
when :div, :mod, :mult, :plus, :minus
left = expr(path_stack.shift, nodeset, context)
right = expr(path_stack.shift, nodeset, context)
left = unnode(left) if left.is_a?(Array)
right = unnode(right) if right.is_a?(Array)
left = Functions::number(left)
right = Functions::number(right)
case op
when :div
return left / right
when :mod
return left % right
when :mult
return left * right
when :plus
return left + right
when :minus
return left - right
else
raise "[BUG] Unexpected operator: <#{op.inspect}>"
end
when :union
left = expr( path_stack.shift, nodeset, context )
right = expr( path_stack.shift, nodeset, context )
left = unnode(left) if left.is_a?(Array)
right = unnode(right) if right.is_a?(Array)
return (left | right)
when :neg
res = expr( path_stack, nodeset, context )
res = unnode(res) if res.is_a?(Array)
return -Functions.number(res)
when :not
when :function
func_name = path_stack.shift.tr('-','_')
arguments = path_stack.shift
subcontext = context ? nil : { :size => nodeset.size }
res = []
cont = context
nodeset.each_with_index do |node, i|
if subcontext
if node.is_a?(XPathNode)
subcontext[:node] = node.raw_node
subcontext[:index] = node.position
else
subcontext[:node] = node
subcontext[:index] = i
end
cont = subcontext
end
arg_clone = arguments.dclone
args = arg_clone.collect do |arg|
result = expr( arg, [node], cont )
result = unnode(result) if result.is_a?(Array)
result
end
Functions.context = cont
res << Functions.send( func_name, *args )
end
return res
else
raise "[BUG] Unexpected path: <#{op.inspect}>: <#{path_stack.inspect}>"
end
end # while
return nodeset
# ensure
# leave(:expr, path_stack, nodeset)
end Related methods
- Instance methods
- []=
- first
- get_first
- match
- namespaces=
- parse
- predicate
- variables=
- Class methods
- new
- Private methods
-
child -
compare -
descendant -
descendant_recursive -
enter -
equality_relational_compare -
evaluate_predicate -
expr -
filter_nodeset -
following -
following_node_of -
get_namespace -
leave -
next_sibling_node -
node_test -
norm -
preceding -
preceding_node_of -
sort -
step -
strict? -
trace -
unnode