From e04ca9be35eb253e6920853163f0d50140e56242 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 4 Aug 2015 11:53:17 +0200 Subject: [PATCH] XPath compiler Support for following-sibling This is a direct port of the same handler used in the Evaluator class. The code is a bit rough on the edges but this will be cleaned up in upcoming commits. --- lib/oga/xpath/compiler.rb | 54 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/lib/oga/xpath/compiler.rb b/lib/oga/xpath/compiler.rb index 2d5074c..72fb341 100644 --- a/lib/oga/xpath/compiler.rb +++ b/lib/oga/xpath/compiler.rb @@ -74,7 +74,10 @@ module Oga proc_ast = literal('lambda').add_block(document, vars) do if return_nodeset?(ast) + input_assign = original_input_literal.assign(document) + matched.assign(literal(XML::NodeSet).new) + .followed_by(input_assign) .followed_by(ruby_ast) .followed_by(matched) else @@ -268,6 +271,52 @@ module Oga end end + # @param [AST::Node] ast + # @param [Oga::Ruby::Node] input + # @return [Oga::Ruby::Node] + def on_axis_following_sibling(ast, input, &block) + node = node_literal + orig_input = original_input_literal + doc_node = literal('doc_node') + check = literal('check') + parent = literal('parent') + root = literal('root') + + root_assign = orig_input.is_a?(XML::Node) + .if_true { root.assign(orig_input.parent) } + .else { root.assign(orig_input) } + + parent_if = input.is_a?(XML::Node).and(input.parent) + .if_true { parent.assign(input.parent) } + .else { parent.assign(literal('nil')) } + + check_assign = check.assign(literal('false')) + + each_node = root.each_node.add_block(doc_node) do + doc_compare = doc_node.eq(input).if_true do + check.assign(literal('true')) + .followed_by(throw_message(:skip_children)) + end + + next_check = (!check).or(parent != doc_node.parent).if_true do + literal('next') + end + + match_node = backup_variable(node, doc_node) do + process(ast, node, &block).if_true do + yield(node).followed_by(throw_message(:skip_children)) + end + end + + doc_compare.followed_by(next_check) + .followed_by(match_node) + end + + root_assign.followed_by(parent_if) + .followed_by(check_assign) + .followed_by(each_node) + end + # @param [AST::Node] ast # @param [Oga::Ruby::Node] input # @return [Oga::Ruby::Node] @@ -758,6 +807,11 @@ module Oga literal('node') end + # @return [Oga::Ruby::Node] + def original_input_literal + literal('original_input') + end + # @return [Oga::Ruby::Node] def variables_literal literal('variables')