1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
# frozen_string_literal: true
require "action_dispatch/journey/nfa/dot"
module ActionDispatch
module Journey # :nodoc:
module GTG # :nodoc:
class TransitionTable # :nodoc:
include Journey::NFA::Dot
attr_reader :memos
def initialize
@regexp_states = {}
@string_states = {}
@accepting = {}
@memos = Hash.new { |h, k| h[k] = [] }
end
def add_accepting(state)
@accepting[state] = true
end
def accepting_states
@accepting.keys
end
def accepting?(state)
@accepting[state]
end
def add_memo(idx, memo)
@memos[idx] << memo
end
def memo(idx)
@memos[idx]
end
def eclosure(t)
Array(t)
end
def move(t, a)
return [] if t.empty?
regexps = []
t.map { |s|
if states = @regexp_states[s]
regexps.concat states.map { |re, v| re === a ? v : nil }
end
if states = @string_states[s]
states[a]
end
}.compact.concat regexps
end
def as_json(options = nil)
simple_regexp = Hash.new { |h, k| h[k] = {} }
@regexp_states.each do |from, hash|
hash.each do |re, to|
simple_regexp[from][re.source] = to
end
end
{
regexp_states: simple_regexp,
string_states: @string_states,
accepting: @accepting
}
end
def to_svg
svg = IO.popen("dot -Tsvg", "w+") { |f|
f.write(to_dot)
f.close_write
f.readlines
}
3.times { svg.shift }
svg.join.sub(/width="[^"]*"/, "").sub(/height="[^"]*"/, "")
end
def visualizer(paths, title = "FSM")
viz_dir = File.join __dir__, "..", "visualizer"
fsm_js = File.read File.join(viz_dir, "fsm.js")
fsm_css = File.read File.join(viz_dir, "fsm.css")
erb = File.read File.join(viz_dir, "index.html.erb")
states = "function tt() { return #{to_json}; }"
fun_routes = paths.sample(3).map do |ast|
ast.map { |n|
case n
when Nodes::Symbol
case n.left
when ":id" then rand(100).to_s
when ":format" then %w{ xml json }.sample
else
"omg"
end
when Nodes::Terminal then n.symbol
else
nil
end
}.compact.join
end
stylesheets = [fsm_css]
svg = to_svg
javascripts = [states, fsm_js]
fun_routes = fun_routes
stylesheets = stylesheets
svg = svg
javascripts = javascripts
require "erb"
template = ERB.new erb
template.result(binding)
end
def []=(from, to, sym)
to_mappings = states_hash_for(sym)[from] ||= {}
to_mappings[sym] = to
end
def states
ss = @string_states.keys + @string_states.values.flat_map(&:values)
rs = @regexp_states.keys + @regexp_states.values.flat_map(&:values)
(ss + rs).uniq
end
def transitions
@string_states.flat_map { |from, hash|
hash.map { |s, to| [from, s, to] }
} + @regexp_states.flat_map { |from, hash|
hash.map { |s, to| [from, s, to] }
}
end
private
def states_hash_for(sym)
case sym
when String
@string_states
when Regexp
@regexp_states
else
raise ArgumentError, "unknown symbol: %s" % sym.class
end
end
end
end
end
end
|