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:
Yorick Peterse 2014-07-08 19:19:59 +02:00
parent 54f0355ea9
commit 266c66569e
1 changed files with 61 additions and 16 deletions

View File

@ -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