mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Remove unused journey code
Journey was integrated into Action Dispatch at 56fee39
, but from that
time, almost all NFA namespace code were not used.
This commit is contained in:
parent
41758964e9
commit
639f5fda30
8 changed files with 6 additions and 437 deletions
|
@ -3,5 +3,3 @@
|
|||
require "action_dispatch/journey/router"
|
||||
require "action_dispatch/journey/gtg/builder"
|
||||
require "action_dispatch/journey/gtg/simulator"
|
||||
require "action_dispatch/journey/nfa/builder"
|
||||
require "action_dispatch/journey/nfa/simulator"
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "action_dispatch/journey/nfa/transition_table"
|
||||
require "action_dispatch/journey/gtg/transition_table"
|
||||
|
||||
module ActionDispatch
|
||||
module Journey # :nodoc:
|
||||
module NFA # :nodoc:
|
||||
class Visitor < Visitors::Visitor # :nodoc:
|
||||
def initialize(tt)
|
||||
@tt = tt
|
||||
@i = -1
|
||||
end
|
||||
|
||||
def visit_CAT(node)
|
||||
left = visit(node.left)
|
||||
right = visit(node.right)
|
||||
|
||||
@tt.merge(left.last, right.first)
|
||||
|
||||
[left.first, right.last]
|
||||
end
|
||||
|
||||
def visit_GROUP(node)
|
||||
from = @i += 1
|
||||
left = visit(node.left)
|
||||
to = @i += 1
|
||||
|
||||
@tt.accepting = to
|
||||
|
||||
@tt[from, left.first] = nil
|
||||
@tt[left.last, to] = nil
|
||||
@tt[from, to] = nil
|
||||
|
||||
[from, to]
|
||||
end
|
||||
|
||||
def visit_OR(node)
|
||||
from = @i += 1
|
||||
children = node.children.map { |c| visit(c) }
|
||||
to = @i += 1
|
||||
|
||||
children.each do |child|
|
||||
@tt[from, child.first] = nil
|
||||
@tt[child.last, to] = nil
|
||||
end
|
||||
|
||||
@tt.accepting = to
|
||||
|
||||
[from, to]
|
||||
end
|
||||
|
||||
def terminal(node)
|
||||
from_i = @i += 1 # new state
|
||||
to_i = @i += 1 # new state
|
||||
|
||||
@tt[from_i, to_i] = node
|
||||
@tt.accepting = to_i
|
||||
@tt.add_memo(to_i, node.memo)
|
||||
|
||||
[from_i, to_i]
|
||||
end
|
||||
end
|
||||
|
||||
class Builder # :nodoc:
|
||||
def initialize(ast)
|
||||
@ast = ast
|
||||
end
|
||||
|
||||
def transition_table
|
||||
tt = TransitionTable.new
|
||||
Visitor.new(tt).accept(@ast)
|
||||
tt
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -9,17 +9,6 @@ module ActionDispatch
|
|||
" #{from} -> #{to} [label=\"#{sym || 'ε'}\"];"
|
||||
}
|
||||
|
||||
# memo_nodes = memos.values.flatten.map { |n|
|
||||
# label = n
|
||||
# if Journey::Route === n
|
||||
# label = "#{n.verb.source} #{n.path.spec}"
|
||||
# end
|
||||
# " #{n.object_id} [label=\"#{label}\", shape=box];"
|
||||
# }
|
||||
# memo_edges = memos.flat_map { |k, memos|
|
||||
# (memos || []).map { |v| " #{k} -> #{v.object_id};" }
|
||||
# }.uniq
|
||||
|
||||
<<-eodot
|
||||
digraph nfa {
|
||||
rankdir=LR;
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "strscan"
|
||||
|
||||
module ActionDispatch
|
||||
module Journey # :nodoc:
|
||||
module NFA # :nodoc:
|
||||
class MatchData # :nodoc:
|
||||
attr_reader :memos
|
||||
|
||||
def initialize(memos)
|
||||
@memos = memos
|
||||
end
|
||||
end
|
||||
|
||||
class Simulator # :nodoc:
|
||||
attr_reader :tt
|
||||
|
||||
def initialize(transition_table)
|
||||
@tt = transition_table
|
||||
end
|
||||
|
||||
def simulate(string)
|
||||
input = StringScanner.new(string)
|
||||
state = tt.eclosure(0)
|
||||
until input.eos?
|
||||
sym = input.scan(%r([/.?]|[^/.?]+))
|
||||
state = tt.eclosure(tt.move(state, sym))
|
||||
end
|
||||
|
||||
acceptance_states = state.find_all { |s|
|
||||
tt.accepting?(tt.eclosure(s).sort.last)
|
||||
}
|
||||
|
||||
return if acceptance_states.empty?
|
||||
|
||||
memos = acceptance_states.flat_map { |x| tt.memo(x) }.compact
|
||||
|
||||
MatchData.new(memos)
|
||||
end
|
||||
|
||||
alias :=~ :simulate
|
||||
alias :match :simulate
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,119 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "action_dispatch/journey/nfa/dot"
|
||||
|
||||
module ActionDispatch
|
||||
module Journey # :nodoc:
|
||||
module NFA # :nodoc:
|
||||
class TransitionTable # :nodoc:
|
||||
include Journey::NFA::Dot
|
||||
|
||||
attr_accessor :accepting
|
||||
attr_reader :memos
|
||||
|
||||
def initialize
|
||||
@table = Hash.new { |h, f| h[f] = {} }
|
||||
@memos = {}
|
||||
@accepting = nil
|
||||
@inverted = nil
|
||||
end
|
||||
|
||||
def accepting?(state)
|
||||
accepting == state
|
||||
end
|
||||
|
||||
def accepting_states
|
||||
[accepting]
|
||||
end
|
||||
|
||||
def add_memo(idx, memo)
|
||||
@memos[idx] = memo
|
||||
end
|
||||
|
||||
def memo(idx)
|
||||
@memos[idx]
|
||||
end
|
||||
|
||||
def []=(i, f, s)
|
||||
@table[f][i] = s
|
||||
end
|
||||
|
||||
def merge(left, right)
|
||||
@memos[right] = @memos.delete(left)
|
||||
@table[right] = @table.delete(left)
|
||||
end
|
||||
|
||||
def states
|
||||
(@table.keys + @table.values.flat_map(&:keys)).uniq
|
||||
end
|
||||
|
||||
# Returns set of NFA states to which there is a transition on ast symbol
|
||||
# +a+ from some state +s+ in +t+.
|
||||
def following_states(t, a)
|
||||
Array(t).flat_map { |s| inverted[s][a] }.uniq
|
||||
end
|
||||
|
||||
# Returns set of NFA states to which there is a transition on ast symbol
|
||||
# +a+ from some state +s+ in +t+.
|
||||
def move(t, a)
|
||||
Array(t).map { |s|
|
||||
inverted[s].keys.compact.find_all { |sym|
|
||||
sym === a
|
||||
}.map { |sym| inverted[s][sym] }
|
||||
}.flatten.uniq
|
||||
end
|
||||
|
||||
def alphabet
|
||||
inverted.values.flat_map(&:keys).compact.uniq.sort_by(&:to_s)
|
||||
end
|
||||
|
||||
# Returns a set of NFA states reachable from some NFA state +s+ in set
|
||||
# +t+ on nil-transitions alone.
|
||||
def eclosure(t)
|
||||
stack = Array(t)
|
||||
seen = {}
|
||||
children = []
|
||||
|
||||
until stack.empty?
|
||||
s = stack.pop
|
||||
next if seen[s]
|
||||
|
||||
seen[s] = true
|
||||
children << s
|
||||
|
||||
stack.concat(inverted[s][nil])
|
||||
end
|
||||
|
||||
children.uniq
|
||||
end
|
||||
|
||||
def transitions
|
||||
@table.flat_map { |to, hash|
|
||||
hash.map { |from, sym| [from, sym, to] }
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
def inverted
|
||||
return @inverted if @inverted
|
||||
|
||||
@inverted = Hash.new { |h, from|
|
||||
h[from] = Hash.new { |j, s| j[s] = [] }
|
||||
}
|
||||
|
||||
@table.each { |to, hash|
|
||||
hash.each { |from, sym|
|
||||
if sym
|
||||
sym = Nodes::Symbol === sym ? sym.regexp : sym.left
|
||||
end
|
||||
|
||||
@inverted[from][sym] << to
|
||||
}
|
||||
}
|
||||
|
||||
@inverted
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -40,10 +40,10 @@ module ActionDispatch
|
|||
/articles/:id(.:format)
|
||||
}
|
||||
|
||||
sim = NFA::Simulator.new table
|
||||
sim = Simulator.new table
|
||||
|
||||
match = sim.match "/articles/new"
|
||||
assert_equal 2, match.memos.length
|
||||
memos = sim.memos "/articles/new"
|
||||
assert_equal 2, memos.length
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -54,10 +54,10 @@ module ActionDispatch
|
|||
/articles/new(.:format)
|
||||
}
|
||||
|
||||
sim = NFA::Simulator.new table
|
||||
sim = Simulator.new table
|
||||
|
||||
match = sim.match "/articles/new"
|
||||
assert_equal 2, match.memos.length
|
||||
memos = sim.memos "/articles/new"
|
||||
assert_equal 2, memos.length
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_unit"
|
||||
|
||||
module ActionDispatch
|
||||
module Journey
|
||||
module NFA
|
||||
class TestSimulator < ActiveSupport::TestCase
|
||||
def test_simulate_simple
|
||||
sim = simulator_for ["/foo"]
|
||||
assert_match sim, "/foo"
|
||||
end
|
||||
|
||||
def test_simulate_simple_no_match
|
||||
sim = simulator_for ["/foo"]
|
||||
assert_no_match sim, "foo"
|
||||
end
|
||||
|
||||
def test_simulate_simple_no_match_too_long
|
||||
sim = simulator_for ["/foo"]
|
||||
assert_no_match sim, "/foo/bar"
|
||||
end
|
||||
|
||||
def test_simulate_simple_no_match_wrong_string
|
||||
sim = simulator_for ["/foo"]
|
||||
assert_no_match sim, "/bar"
|
||||
end
|
||||
|
||||
def test_simulate_regex
|
||||
sim = simulator_for ["/:foo/bar"]
|
||||
assert_match sim, "/bar/bar"
|
||||
assert_match sim, "/foo/bar"
|
||||
end
|
||||
|
||||
def test_simulate_or
|
||||
sim = simulator_for ["/foo", "/bar"]
|
||||
assert_match sim, "/bar"
|
||||
assert_match sim, "/foo"
|
||||
assert_no_match sim, "/baz"
|
||||
end
|
||||
|
||||
def test_simulate_optional
|
||||
sim = simulator_for ["/foo(/bar)"]
|
||||
assert_match sim, "/foo"
|
||||
assert_match sim, "/foo/bar"
|
||||
assert_no_match sim, "/foo/"
|
||||
end
|
||||
|
||||
def test_matchdata_has_memos
|
||||
paths = %w{ /foo /bar }
|
||||
parser = Journey::Parser.new
|
||||
asts = paths.map { |x|
|
||||
ast = parser.parse x
|
||||
ast.each { |n| n.memo = ast }
|
||||
ast
|
||||
}
|
||||
|
||||
expected = asts.first
|
||||
|
||||
builder = Builder.new Nodes::Or.new asts
|
||||
|
||||
sim = Simulator.new builder.transition_table
|
||||
|
||||
md = sim.match "/foo"
|
||||
assert_equal [expected], md.memos
|
||||
end
|
||||
|
||||
def test_matchdata_memos_on_merge
|
||||
parser = Journey::Parser.new
|
||||
routes = [
|
||||
"/articles(.:format)",
|
||||
"/articles/new(.:format)",
|
||||
"/articles/:id/edit(.:format)",
|
||||
"/articles/:id(.:format)",
|
||||
].map { |path|
|
||||
ast = parser.parse path
|
||||
ast.each { |n| n.memo = ast }
|
||||
ast
|
||||
}
|
||||
|
||||
asts = routes.dup
|
||||
|
||||
ast = Nodes::Or.new routes
|
||||
|
||||
nfa = Journey::NFA::Builder.new ast
|
||||
sim = Simulator.new nfa.transition_table
|
||||
md = sim.match "/articles"
|
||||
assert_equal [asts.first], md.memos
|
||||
end
|
||||
|
||||
def simulator_for(paths)
|
||||
parser = Journey::Parser.new
|
||||
asts = paths.map { |x| parser.parse x }
|
||||
builder = Builder.new Nodes::Or.new asts
|
||||
Simulator.new builder.transition_table
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,74 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "abstract_unit"
|
||||
|
||||
module ActionDispatch
|
||||
module Journey
|
||||
module NFA
|
||||
class TestTransitionTable < ActiveSupport::TestCase
|
||||
def setup
|
||||
@parser = Journey::Parser.new
|
||||
end
|
||||
|
||||
def test_eclosure
|
||||
table = tt "/"
|
||||
assert_equal [0], table.eclosure(0)
|
||||
|
||||
table = tt ":a|:b"
|
||||
assert_equal 3, table.eclosure(0).length
|
||||
|
||||
table = tt "(:a|:b)"
|
||||
assert_equal 5, table.eclosure(0).length
|
||||
assert_equal 5, table.eclosure([0]).length
|
||||
end
|
||||
|
||||
def test_following_states_one
|
||||
table = tt "/"
|
||||
|
||||
assert_equal [1], table.following_states(0, "/")
|
||||
assert_equal [1], table.following_states([0], "/")
|
||||
end
|
||||
|
||||
def test_following_states_group
|
||||
table = tt "a|b"
|
||||
states = table.eclosure 0
|
||||
|
||||
assert_equal 1, table.following_states(states, "a").length
|
||||
assert_equal 1, table.following_states(states, "b").length
|
||||
end
|
||||
|
||||
def test_following_states_multi
|
||||
table = tt "a|a"
|
||||
states = table.eclosure 0
|
||||
|
||||
assert_equal 2, table.following_states(states, "a").length
|
||||
assert_equal 0, table.following_states(states, "b").length
|
||||
end
|
||||
|
||||
def test_following_states_regexp
|
||||
table = tt "a|:a"
|
||||
states = table.eclosure 0
|
||||
|
||||
assert_equal 1, table.following_states(states, "a").length
|
||||
assert_equal 1, table.following_states(states, /[^\.\/\?]+/).length
|
||||
assert_equal 0, table.following_states(states, "b").length
|
||||
end
|
||||
|
||||
def test_alphabet
|
||||
table = tt "a|:a"
|
||||
assert_equal [/[^\.\/\?]+/, "a"], table.alphabet
|
||||
|
||||
table = tt "a|a"
|
||||
assert_equal ["a"], table.alphabet
|
||||
end
|
||||
|
||||
private
|
||||
def tt(string)
|
||||
ast = @parser.parse string
|
||||
builder = Builder.new ast
|
||||
builder.transition_table
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue