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:
Ryuta Kamizono 2020-04-25 00:40:37 +09:00
parent 41758964e9
commit 639f5fda30
8 changed files with 6 additions and 437 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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