mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
mostly implemented depth-first traversal
This commit is contained in:
parent
f092ae544f
commit
c86c37e5f3
4 changed files with 159 additions and 0 deletions
|
@ -3,6 +3,8 @@ module Arel
|
||||||
###
|
###
|
||||||
# Abstract base class for all AST nodes
|
# Abstract base class for all AST nodes
|
||||||
class Node
|
class Node
|
||||||
|
include Enumerable
|
||||||
|
|
||||||
###
|
###
|
||||||
# Factory method to create a Nodes::Not node that has the recipient of
|
# Factory method to create a Nodes::Not node that has the recipient of
|
||||||
# the caller as a child.
|
# the caller as a child.
|
||||||
|
@ -32,6 +34,11 @@ module Arel
|
||||||
viz = Visitors.for engine
|
viz = Visitors.for engine
|
||||||
viz.accept self
|
viz.accept self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Iterate through AST, nodes will be yielded depth-first
|
||||||
|
def each &block
|
||||||
|
Visitors::DepthFirst.new(block).accept self
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
require 'arel/visitors/visitor'
|
require 'arel/visitors/visitor'
|
||||||
|
require 'arel/visitors/depth_first'
|
||||||
require 'arel/visitors/to_sql'
|
require 'arel/visitors/to_sql'
|
||||||
require 'arel/visitors/sqlite'
|
require 'arel/visitors/sqlite'
|
||||||
require 'arel/visitors/postgresql'
|
require 'arel/visitors/postgresql'
|
||||||
|
|
75
lib/arel/visitors/depth_first.rb
Normal file
75
lib/arel/visitors/depth_first.rb
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
module Arel
|
||||||
|
module Visitors
|
||||||
|
class DepthFirst < Arel::Visitors::Visitor
|
||||||
|
def initialize block = nil
|
||||||
|
@block = block || Proc.new
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def binary o
|
||||||
|
visit o.left
|
||||||
|
visit o.right
|
||||||
|
@block.call o
|
||||||
|
end
|
||||||
|
alias :visit_Arel_Nodes_And :binary
|
||||||
|
alias :visit_Arel_Nodes_Assignment :binary
|
||||||
|
alias :visit_Arel_Nodes_Between :binary
|
||||||
|
alias :visit_Arel_Nodes_DoesNotMatch :binary
|
||||||
|
alias :visit_Arel_Nodes_Equality :binary
|
||||||
|
alias :visit_Arel_Nodes_GreaterThan :binary
|
||||||
|
alias :visit_Arel_Nodes_GreaterThanOrEqual :binary
|
||||||
|
alias :visit_Arel_Nodes_In :binary
|
||||||
|
alias :visit_Arel_Nodes_LessThan :binary
|
||||||
|
alias :visit_Arel_Nodes_LessThanOrEqual :binary
|
||||||
|
alias :visit_Arel_Nodes_Matches :binary
|
||||||
|
alias :visit_Arel_Nodes_NotEqual :binary
|
||||||
|
alias :visit_Arel_Nodes_NotIn :binary
|
||||||
|
alias :visit_Arel_Nodes_Or :binary
|
||||||
|
|
||||||
|
def visit_Arel_Attribute o
|
||||||
|
visit o.relation
|
||||||
|
visit o.name
|
||||||
|
@block.call o
|
||||||
|
end
|
||||||
|
alias :visit_Arel_Attributes_Integer :visit_Arel_Attribute
|
||||||
|
alias :visit_Arel_Attributes_Float :visit_Arel_Attribute
|
||||||
|
alias :visit_Arel_Attributes_String :visit_Arel_Attribute
|
||||||
|
alias :visit_Arel_Attributes_Time :visit_Arel_Attribute
|
||||||
|
alias :visit_Arel_Attributes_Boolean :visit_Arel_Attribute
|
||||||
|
alias :visit_Arel_Attributes_Attribute :visit_Arel_Attribute
|
||||||
|
|
||||||
|
def visit_Arel_Table o
|
||||||
|
visit o.name
|
||||||
|
@block.call o
|
||||||
|
end
|
||||||
|
|
||||||
|
def terminal o
|
||||||
|
@block.call o
|
||||||
|
end
|
||||||
|
alias :visit_Arel_Nodes_SqlLiteral :terminal
|
||||||
|
alias :visit_Arel_SqlLiteral :terminal
|
||||||
|
alias :visit_BigDecimal :terminal
|
||||||
|
alias :visit_Date :terminal
|
||||||
|
alias :visit_DateTime :terminal
|
||||||
|
alias :visit_FalseClass :terminal
|
||||||
|
alias :visit_Fixnum :terminal
|
||||||
|
alias :visit_Float :terminal
|
||||||
|
alias :visit_NilClass :terminal
|
||||||
|
alias :visit_String :terminal
|
||||||
|
alias :visit_Symbol :terminal
|
||||||
|
alias :visit_Time :terminal
|
||||||
|
alias :visit_TrueClass :terminal
|
||||||
|
|
||||||
|
def visit_Array o
|
||||||
|
o.each { |i| visit i }
|
||||||
|
@block.call o
|
||||||
|
end
|
||||||
|
|
||||||
|
def visit_Hash o
|
||||||
|
o.each { |k,v| visit(k); visit(v) }
|
||||||
|
@block.call o
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
76
test/visitors/test_depth_first.rb
Normal file
76
test/visitors/test_depth_first.rb
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
require 'helper'
|
||||||
|
|
||||||
|
module Arel
|
||||||
|
module Visitors
|
||||||
|
class TestDepthFirst < MiniTest::Unit::TestCase
|
||||||
|
Collector = Struct.new(:calls) do
|
||||||
|
def call object
|
||||||
|
calls << object
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def setup
|
||||||
|
@collector = Collector.new []
|
||||||
|
@visitor = Visitors::DepthFirst.new @collector
|
||||||
|
end
|
||||||
|
|
||||||
|
[
|
||||||
|
Arel::Nodes::And,
|
||||||
|
Arel::Nodes::Assignment,
|
||||||
|
Arel::Nodes::Between,
|
||||||
|
Arel::Nodes::DoesNotMatch,
|
||||||
|
Arel::Nodes::Equality,
|
||||||
|
Arel::Nodes::GreaterThan,
|
||||||
|
Arel::Nodes::GreaterThanOrEqual,
|
||||||
|
Arel::Nodes::In,
|
||||||
|
Arel::Nodes::LessThan,
|
||||||
|
Arel::Nodes::LessThanOrEqual,
|
||||||
|
Arel::Nodes::Matches,
|
||||||
|
Arel::Nodes::NotEqual,
|
||||||
|
Arel::Nodes::NotIn,
|
||||||
|
Arel::Nodes::Or,
|
||||||
|
].each do |klass|
|
||||||
|
define_method("test_#{klass.name.gsub('::', '_')}") do
|
||||||
|
binary = klass.new(:a, :b)
|
||||||
|
@visitor.accept binary
|
||||||
|
assert_equal [:a, :b, binary], @collector.calls
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
[
|
||||||
|
Arel::Attributes::Integer,
|
||||||
|
Arel::Attributes::Float,
|
||||||
|
Arel::Attributes::String,
|
||||||
|
Arel::Attributes::Time,
|
||||||
|
Arel::Attributes::Boolean,
|
||||||
|
Arel::Attributes::Attribute
|
||||||
|
].each do |klass|
|
||||||
|
define_method("test_#{klass.name.gsub('::', '_')}") do
|
||||||
|
binary = klass.new(:a, :b)
|
||||||
|
@visitor.accept binary
|
||||||
|
assert_equal [:a, :b, binary], @collector.calls
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_table
|
||||||
|
relation = Arel::Table.new(:users)
|
||||||
|
@visitor.accept relation
|
||||||
|
assert_equal ['users', relation], @collector.calls
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_array
|
||||||
|
node = Nodes::Or.new(:a, :b)
|
||||||
|
list = [node]
|
||||||
|
@visitor.accept list
|
||||||
|
assert_equal [:a, :b, node, list], @collector.calls
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_hash
|
||||||
|
node = Nodes::Or.new(:a, :b)
|
||||||
|
hash = { node => node }
|
||||||
|
@visitor.accept hash
|
||||||
|
assert_equal [:a, :b, node, :a, :b, node, hash], @collector.calls
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue