Class: Moxml::XPath::Compiler
- Inherits:
-
Object
- Object
- Moxml::XPath::Compiler
- Defined in:
- lib/moxml/xpath/compiler.rb
Overview
Compiler for transforming XPath AST into executable Ruby code.
This class takes an XPath AST (produced by Parser) and compiles it into a Ruby Proc that can be executed against XML documents. The compilation process:
- Traverse the XPath AST
- Generate Ruby::Node AST representing Ruby code
- Use Ruby::Generator to convert to Ruby source string
- Evaluate source in Context to get a Proc
Constant Summary collapse
- CACHE =
Expression cache
Cache.new
- STAR =
Wildcard for node names/namespace prefixes
"*"- RETURN_NODESET =
Node types that require a NodeSet to push nodes into
%i[path absolute_path relative_path axis predicate].freeze
Class Method Summary collapse
-
.compile_with_cache(ast, namespaces: nil) ⇒ Proc
Compiles and caches an AST.
Instance Method Summary collapse
-
#compile(ast) ⇒ Proc
Compiles an XPath AST into a Ruby Proc.
-
#initialize(namespaces: nil) ⇒ Compiler
constructor
Initialize compiler.
-
#on_binary_op(ast, input, &block) ⇒ Object
Dispatcher for generic binary operator nodes.
-
#on_unary_op(ast, input, &block) ⇒ Object
Dispatcher for generic unary operator nodes.
-
#on_union(ast, input, &block) ⇒ Object
Dispatcher for union nodes (parser creates :union, compiler uses :pipe).
-
#process(ast, input) {|Ruby::Node| ... } ⇒ Ruby::Node
Process a single XPath AST node.
Constructor Details
#initialize(namespaces: nil) ⇒ Compiler
Initialize compiler
50 51 52 53 54 55 |
# File 'lib/moxml/xpath/compiler.rb', line 50 def initialize(namespaces: nil) @namespaces = namespaces @literal_id = 0 @predicate_nodesets = [] @predicate_indexes = [] end |
Class Method Details
.compile_with_cache(ast, namespaces: nil) ⇒ Proc
Compiles and caches an AST
42 43 44 45 |
# File 'lib/moxml/xpath/compiler.rb', line 42 def self.compile_with_cache(ast, namespaces: nil) cache_key = namespaces ? [ast, namespaces] : ast CACHE.get_or_set(cache_key) { new(namespaces: namespaces).compile(ast) } end |
Instance Method Details
#compile(ast) ⇒ Proc
Compiles an XPath AST into a Ruby Proc
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/moxml/xpath/compiler.rb', line 61 def compile(ast) document = literal(:node) matched = matched_literal context_var = context_literal ruby_ast = if return_nodeset?(ast) process(ast, document) { |node| matched.push(node) } else process(ast, document) end proc_ast = literal(:lambda).add_block(document) do # Get context from document context_assign = context_var.assign(document.context) if return_nodeset?(ast) # Create NodeSet using send node: Moxml::NodeSet.new([], context) nodeset_class = const_ref("Moxml", "NodeSet") empty_array = Ruby::Node.new(:array, []) nodeset_new = Ruby::Node.new(:send, [nodeset_class, "new", empty_array, context_var]) body = matched.assign(nodeset_new) .followed_by(ruby_ast) .followed_by(matched) else body = ruby_ast end context_assign.followed_by(body) end generator = Ruby::Generator.new source = generator.process(proc_ast) CONTEXT.evaluate(source) end |
#on_binary_op(ast, input, &block) ⇒ Object
Dispatcher for generic binary operator nodes
111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/moxml/xpath/compiler.rb', line 111 def on_binary_op(ast, input, &block) operator = ast.value # :eq, :lt, :add, :plus, :star, etc. # Map token names to handler method names method_name = case operator when :plus then :add when :minus then :sub when :star then :mul else operator # eq, lt, gt, div, mod, etc. end send(:"on_#{method_name}", ast, input, &block) end |
#on_unary_op(ast, input, &block) ⇒ Object
Dispatcher for generic unary operator nodes
126 127 128 129 |
# File 'lib/moxml/xpath/compiler.rb', line 126 def on_unary_op(ast, input, &block) operator = ast.value # :minus send(:"on_#{operator}", ast, input, &block) end |
#on_union(ast, input, &block) ⇒ Object
Dispatcher for union nodes (parser creates :union, compiler uses :pipe)
132 133 134 |
# File 'lib/moxml/xpath/compiler.rb', line 132 def on_union(ast, input, &block) on_pipe(ast, input, &block) end |
#process(ast, input) {|Ruby::Node| ... } ⇒ Ruby::Node
Process a single XPath AST node
106 107 108 |
# File 'lib/moxml/xpath/compiler.rb', line 106 def process(ast, input, &block) send(:"on_#{ast.type}", ast, input, &block) end |