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/router"
|
||||||
require "action_dispatch/journey/gtg/builder"
|
require "action_dispatch/journey/gtg/builder"
|
||||||
require "action_dispatch/journey/gtg/simulator"
|
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 || 'ε'}\"];"
|
" #{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
|
<<-eodot
|
||||||
digraph nfa {
|
digraph nfa {
|
||||||
rankdir=LR;
|
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)
|
/articles/:id(.:format)
|
||||||
}
|
}
|
||||||
|
|
||||||
sim = NFA::Simulator.new table
|
sim = Simulator.new table
|
||||||
|
|
||||||
match = sim.match "/articles/new"
|
memos = sim.memos "/articles/new"
|
||||||
assert_equal 2, match.memos.length
|
assert_equal 2, memos.length
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -54,10 +54,10 @@ module ActionDispatch
|
||||||
/articles/new(.:format)
|
/articles/new(.:format)
|
||||||
}
|
}
|
||||||
|
|
||||||
sim = NFA::Simulator.new table
|
sim = Simulator.new table
|
||||||
|
|
||||||
match = sim.match "/articles/new"
|
memos = sim.memos "/articles/new"
|
||||||
assert_equal 2, match.memos.length
|
assert_equal 2, memos.length
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
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