2012-12-19 15:54:47 -05:00
|
|
|
require 'action_dispatch/journey/nfa/dot'
|
|
|
|
|
|
|
|
module ActionDispatch
|
2012-12-19 19:24:25 -05:00
|
|
|
module Journey # :nodoc:
|
|
|
|
module NFA # :nodoc:
|
|
|
|
class TransitionTable # :nodoc:
|
2012-12-19 15:54:47 -05:00
|
|
|
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
|
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
def accepting?(state)
|
2012-12-19 15:54:47 -05:00
|
|
|
accepting == state
|
|
|
|
end
|
|
|
|
|
|
|
|
def accepting_states
|
|
|
|
[accepting]
|
|
|
|
end
|
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
def add_memo(idx, memo)
|
2012-12-19 15:54:47 -05:00
|
|
|
@memos[idx] = memo
|
|
|
|
end
|
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
def memo(idx)
|
2012-12-19 15:54:47 -05:00
|
|
|
@memos[idx]
|
|
|
|
end
|
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
def []=(i, f, s)
|
2012-12-19 15:54:47 -05:00
|
|
|
@table[f][i] = s
|
|
|
|
end
|
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
def merge(left, right)
|
|
|
|
@memos[right] = @memos.delete(left)
|
2012-12-19 15:54:47 -05:00
|
|
|
@table[right] = @table.delete(left)
|
|
|
|
end
|
|
|
|
|
|
|
|
def states
|
2014-03-03 22:23:12 -05:00
|
|
|
(@table.keys + @table.values.flat_map(&:keys)).uniq
|
2012-12-19 15:54:47 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
# Returns set of NFA states to which there is a transition on ast symbol
|
|
|
|
# +a+ from some state +s+ in +t+.
|
2012-12-20 15:42:39 -05:00
|
|
|
def following_states(t, a)
|
2014-03-03 22:23:12 -05:00
|
|
|
Array(t).flat_map { |s| inverted[s][a] }.uniq
|
2012-12-19 15:54:47 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
# Returns set of NFA states to which there is a transition on ast symbol
|
|
|
|
# +a+ from some state +s+ in +t+.
|
2012-12-20 15:42:39 -05:00
|
|
|
def move(t, a)
|
2012-12-19 15:54:47 -05:00
|
|
|
Array(t).map { |s|
|
|
|
|
inverted[s].keys.compact.find_all { |sym|
|
|
|
|
sym === a
|
|
|
|
}.map { |sym| inverted[s][sym] }
|
|
|
|
}.flatten.uniq
|
|
|
|
end
|
|
|
|
|
|
|
|
def alphabet
|
2014-10-27 12:28:53 -04:00
|
|
|
inverted.values.flat_map(&:keys).compact.uniq.sort_by(&:to_s)
|
2012-12-19 15:54:47 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
# Returns a set of NFA states reachable from some NFA state +s+ in set
|
|
|
|
# +t+ on nil-transitions alone.
|
2012-12-20 15:42:39 -05:00
|
|
|
def eclosure(t)
|
2012-12-19 15:54:47 -05:00
|
|
|
stack = Array(t)
|
|
|
|
seen = {}
|
|
|
|
children = []
|
|
|
|
|
|
|
|
until stack.empty?
|
|
|
|
s = stack.pop
|
|
|
|
next if seen[s]
|
|
|
|
|
|
|
|
seen[s] = true
|
|
|
|
children << s
|
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
stack.concat(inverted[s][nil])
|
2012-12-19 15:54:47 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
children.uniq
|
|
|
|
end
|
|
|
|
|
|
|
|
def transitions
|
2014-03-03 22:23:12 -05:00
|
|
|
@table.flat_map { |to, hash|
|
2012-12-19 15:54:47 -05:00
|
|
|
hash.map { |from, sym| [from, sym, to] }
|
2014-03-03 22:23:12 -05:00
|
|
|
}
|
2012-12-19 15:54:47 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
def inverted
|
|
|
|
return @inverted if @inverted
|
|
|
|
|
|
|
|
@inverted = Hash.new { |h, from|
|
|
|
|
h[from] = Hash.new { |j, s| j[s] = [] }
|
|
|
|
}
|
2012-12-19 15:54:47 -05:00
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
@table.each { |to, hash|
|
|
|
|
hash.each { |from, sym|
|
|
|
|
if sym
|
|
|
|
sym = Nodes::Symbol === sym ? sym.regexp : sym.left
|
|
|
|
end
|
2012-12-19 15:54:47 -05:00
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
@inverted[from][sym] << to
|
|
|
|
}
|
2012-12-19 15:54:47 -05:00
|
|
|
}
|
|
|
|
|
2012-12-20 15:42:39 -05:00
|
|
|
@inverted
|
|
|
|
end
|
2012-12-20 17:19:14 -05:00
|
|
|
end
|
2012-12-19 15:54:47 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|