Removed internal state of XPath::Evaluator.
Come to think of it it might actually be easier to implement the evaluator as an actual VM. That is, instead of directly running on the AST it runs on some flavour of bytecode. Alternatively it runs directly on the AST but behaves more like a (stack based) VM. This would most likely be easier than passing a cursor to every node processing method.
This commit is contained in:
parent
54f0355ea9
commit
266c66569e
|
@ -10,42 +10,87 @@ module Oga
|
||||||
#
|
#
|
||||||
def initialize(document)
|
def initialize(document)
|
||||||
@document = document
|
@document = document
|
||||||
@cursor = @document
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_absolute(node)
|
def process(node, cursor)
|
||||||
if @cursor.is_a?(XML::Node)
|
return if node.nil?
|
||||||
@cursor = @cursor.root_node
|
|
||||||
|
node = node.to_ast
|
||||||
|
|
||||||
|
on_handler = :"on_#{node.type}"
|
||||||
|
|
||||||
|
if respond_to?(on_handler)
|
||||||
|
new_node = send(on_handler, node, cursor)
|
||||||
|
else
|
||||||
|
new_node = handler_missing(node)
|
||||||
end
|
end
|
||||||
|
|
||||||
return process_all(node.children)
|
node = new_node if new_node
|
||||||
|
|
||||||
|
return node
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_path(node)
|
def process_all(nodes, cursor)
|
||||||
|
return nodes.to_a.map do |node|
|
||||||
|
process(node, cursor)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def evaluate(string)
|
||||||
|
ast = Parser.new(string).parse
|
||||||
|
cursor = move_cursor(@document)
|
||||||
|
|
||||||
|
return process(ast, cursor)
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_absolute(node, cursor)
|
||||||
|
if @document.is_a?(XML::Node)
|
||||||
|
cursor = move_cursor(@document.root_node)
|
||||||
|
else
|
||||||
|
cursor = move_cursor(@document)
|
||||||
|
end
|
||||||
|
|
||||||
|
return process_all(node.children, cursor)
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_path(node, cursor)
|
||||||
test, children = *node
|
test, children = *node
|
||||||
|
|
||||||
current = process(test)
|
nodes = process(test, cursor)
|
||||||
|
|
||||||
if current
|
unless nodes.empty?
|
||||||
@cursor = current
|
nodes = process_all(children, nodes)
|
||||||
current = process(children)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return current
|
return nodes
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_test(node)
|
def on_test(node, cursor)
|
||||||
nodes = []
|
|
||||||
ns, name = *node
|
ns, name = *node
|
||||||
|
nodes = XML::NodeSet.new
|
||||||
|
|
||||||
@cursor.children.each do |child|
|
cursor.each do |xml_node|
|
||||||
if child.is_a?(XML::Element) and child.name == name and child.namespace == ns
|
next unless xml_node.is_a?(XML::Element)
|
||||||
nodes << child
|
|
||||||
|
if xml_node.name == name and xml_node.namespace == ns
|
||||||
|
nodes << xml_node
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return nodes
|
return nodes
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def move_cursor(location)
|
||||||
|
if location.is_a?(XML::Document)
|
||||||
|
return location.children
|
||||||
|
|
||||||
|
elsif location.is_a?(XML::Node)
|
||||||
|
return XML::NodeSet.new([location])
|
||||||
|
|
||||||
|
else
|
||||||
|
return location
|
||||||
|
end
|
||||||
|
end
|
||||||
end # Evaluator
|
end # Evaluator
|
||||||
end # XPath
|
end # XPath
|
||||||
end # Oga
|
end # Oga
|
||||||
|
|
Loading…
Reference in New Issue