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
|
require 'journey/nfa/dot'
module Journey
module GTG
class TransitionTable
include Journey::NFA::Dot
attr_reader :memos
def initialize
@regexp_states = Hash.new { |h,k| h[k] = {} }
@string_states = Hash.new { |h,k| h[k] = {} }
@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
move_string(t, a).concat move_regexp(t, a)
end
def to_json
require 'json'
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
JSON.dump({
: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.shuffle.first(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 }.shuffle.first
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 for 1.9 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
case sym
when String
@string_states[from][sym] = to
when Regexp
@regexp_states[from][sym] = to
else
raise ArgumentError, 'unknown symbol: %s' % sym.class
end
end
def states
ss = @string_states.keys + @string_states.values.map(&:values).flatten
rs = @regexp_states.keys + @regexp_states.values.map(&:values).flatten
(ss + rs).uniq
end
def transitions
@string_states.map { |from, hash|
hash.map { |s, to| [from, s, to] }
}.flatten(1) + @regexp_states.map { |from, hash|
hash.map { |s, to| [from, s, to] }
}.flatten(1)
end
private
def move_regexp t, a
return [] if t.empty?
t.map { |s|
@regexp_states[s].map { |re,v| re === a ? v : nil }
}.flatten.compact.uniq
end
def move_string t, a
return [] if t.empty?
t.map { |s| @string_states[s][a] }.compact
end
end
end
end
|