File: transition_table.rb

package info (click to toggle)
ruby-journey 1.0.4-2.1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, trixie
  • size: 288 kB
  • sloc: ruby: 2,830; javascript: 113; yacc: 42; makefile: 2
file content (153 lines) | stat: -rw-r--r-- 3,721 bytes parent folder | download | duplicates (3)
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