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
|
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 File.dirname(__FILE__), "..", "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]
# Annoying hack warnings
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
|