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 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/moxml/xpath/compiler.rb', line 60 def compile(ast) document = literal(:node) matched = matched_literal context_var = context_literal # Enable debug output debug = ENV["DEBUG_XPATH"] == "1" if debug puts "\n#{'=' * 60}" puts "COMPILING XPath" puts "=" * 60 puts "AST: #{ast.inspect}" puts end 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) if debug puts "GENERATED RUBY CODE:" puts "-" * 60 puts source puts "=" * 60 puts end 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
132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/moxml/xpath/compiler.rb', line 132 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
147 148 149 150 |
# File 'lib/moxml/xpath/compiler.rb', line 147 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)
153 154 155 |
# File 'lib/moxml/xpath/compiler.rb', line 153 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
127 128 129 |
# File 'lib/moxml/xpath/compiler.rb', line 127 def process(ast, input, &block) send(:"on_#{ast.type}", ast, input, &block) end |