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
- CONTEXT =
Shared context for compiled Procs
Context.new
- 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
49 50 51 52 53 54 |
# File 'lib/moxml/xpath/compiler.rb', line 49 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
41 42 43 44 |
# File 'lib/moxml/xpath/compiler.rb', line 41 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
60 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 99 100 101 |
# File 'lib/moxml/xpath/compiler.rb', line 60 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) ensure @literal_id = 0 @predicate_nodesets.clear @predicate_indexes.clear end |
#on_binary_op(ast, input, &block) ⇒ Object
Dispatcher for generic binary operator nodes
114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/moxml/xpath/compiler.rb', line 114 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
129 130 131 132 |
# File 'lib/moxml/xpath/compiler.rb', line 129 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)
135 136 137 |
# File 'lib/moxml/xpath/compiler.rb', line 135 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
109 110 111 |
# File 'lib/moxml/xpath/compiler.rb', line 109 def process(ast, input, &block) send(:"on_#{ast.type}", ast, input, &block) end |