journey 1.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. data/.autotest +8 -0
  2. data/.gemtest +0 -0
  3. data/CHANGELOG.rdoc +6 -0
  4. data/Gemfile +11 -0
  5. data/Manifest.txt +50 -0
  6. data/README.rdoc +48 -0
  7. data/Rakefile +31 -0
  8. data/journey.gemspec +42 -0
  9. data/lib/journey.rb +5 -0
  10. data/lib/journey/backwards.rb +5 -0
  11. data/lib/journey/core-ext/hash.rb +11 -0
  12. data/lib/journey/formatter.rb +129 -0
  13. data/lib/journey/gtg/builder.rb +159 -0
  14. data/lib/journey/gtg/simulator.rb +44 -0
  15. data/lib/journey/gtg/transition_table.rb +152 -0
  16. data/lib/journey/nfa/builder.rb +74 -0
  17. data/lib/journey/nfa/dot.rb +34 -0
  18. data/lib/journey/nfa/simulator.rb +45 -0
  19. data/lib/journey/nfa/transition_table.rb +164 -0
  20. data/lib/journey/nodes/node.rb +104 -0
  21. data/lib/journey/parser.rb +204 -0
  22. data/lib/journey/parser.y +47 -0
  23. data/lib/journey/parser_extras.rb +21 -0
  24. data/lib/journey/path/pattern.rb +190 -0
  25. data/lib/journey/route.rb +92 -0
  26. data/lib/journey/router.rb +138 -0
  27. data/lib/journey/router/strexp.rb +22 -0
  28. data/lib/journey/router/utils.rb +57 -0
  29. data/lib/journey/routes.rb +74 -0
  30. data/lib/journey/scanner.rb +58 -0
  31. data/lib/journey/visitors.rb +186 -0
  32. data/lib/journey/visualizer/d3.min.js +2 -0
  33. data/lib/journey/visualizer/fsm.css +34 -0
  34. data/lib/journey/visualizer/fsm.js +134 -0
  35. data/lib/journey/visualizer/index.html.erb +50 -0
  36. data/lib/journey/visualizer/reset.css +48 -0
  37. data/test/gtg/test_builder.rb +77 -0
  38. data/test/gtg/test_transition_table.rb +113 -0
  39. data/test/helper.rb +4 -0
  40. data/test/nfa/test_simulator.rb +96 -0
  41. data/test/nfa/test_transition_table.rb +70 -0
  42. data/test/nodes/test_symbol.rb +15 -0
  43. data/test/path/test_pattern.rb +260 -0
  44. data/test/route/definition/test_parser.rb +108 -0
  45. data/test/route/definition/test_scanner.rb +52 -0
  46. data/test/router/test_strexp.rb +30 -0
  47. data/test/router/test_utils.rb +19 -0
  48. data/test/test_route.rb +95 -0
  49. data/test/test_router.rb +464 -0
  50. data/test/test_routes.rb +51 -0
  51. metadata +168 -0
@@ -0,0 +1,104 @@
1
+ require 'journey/visitors'
2
+
3
+ module Journey
4
+ module Nodes
5
+ class Node # :nodoc:
6
+ include Enumerable
7
+
8
+ attr_accessor :left, :memo
9
+
10
+ def initialize left
11
+ @left = left
12
+ @memo = nil
13
+ end
14
+
15
+ def each(&block)
16
+ Visitors::Each.new(block).accept(self)
17
+ end
18
+
19
+ def to_s
20
+ Visitors::String.new.accept(self)
21
+ end
22
+
23
+ def to_dot
24
+ Visitors::Dot.new.accept(self)
25
+ end
26
+
27
+ def to_sym
28
+ name.to_sym
29
+ end
30
+
31
+ def name
32
+ left.tr ':', ''
33
+ end
34
+
35
+ def type
36
+ raise NotImplementedError
37
+ end
38
+ end
39
+
40
+ class Terminal < Node
41
+ alias :symbol :left
42
+ end
43
+
44
+ %w{ Symbol Slash Literal Dot }.each do |t|
45
+ class_eval %{
46
+ class #{t} < Terminal
47
+ def type; :#{t.upcase}; end
48
+ end
49
+ }
50
+ end
51
+
52
+ class Symbol < Terminal
53
+ attr_accessor :regexp
54
+ alias :symbol :regexp
55
+
56
+ DEFAULT_EXP = /[^\.\/\?]+/
57
+ def initialize left
58
+ super
59
+ @regexp = DEFAULT_EXP
60
+ end
61
+
62
+ def default_regexp?
63
+ regexp == DEFAULT_EXP
64
+ end
65
+ end
66
+
67
+ class Unary < Node
68
+ def children; [value] end
69
+ end
70
+
71
+ class Group < Unary
72
+ def type; :GROUP; end
73
+ end
74
+
75
+ class Star < Unary
76
+ def type; :STAR; end
77
+ end
78
+
79
+ class Binary < Node
80
+ attr_accessor :right
81
+
82
+ def initialize left, right
83
+ super(left)
84
+ @right = right
85
+ end
86
+
87
+ def children; [left, right] end
88
+ end
89
+
90
+ class Cat < Binary
91
+ def type; :CAT; end
92
+ end
93
+
94
+ class Or < Node
95
+ attr_reader :children
96
+
97
+ def initialize children
98
+ @children = children
99
+ end
100
+
101
+ def type; :OR; end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,204 @@
1
+ #
2
+ # DO NOT MODIFY!!!!
3
+ # This file is automatically generated by Racc 1.4.6
4
+ # from Racc grammer file "".
5
+ #
6
+
7
+ require 'racc/parser.rb'
8
+
9
+
10
+ require 'journey/parser_extras'
11
+ module Journey
12
+ class Parser < Racc::Parser
13
+ ##### State transition tables begin ###
14
+
15
+ racc_action_table = [
16
+ 17, 22, 13, 15, 14, 7, 15, 16, 8, 19,
17
+ 13, 15, 14, 7, 24, 16, 8, 19, 13, 15,
18
+ 14, 7, nil, 16, 8, 13, 15, 14, 7, nil,
19
+ 16, 8, 13, 15, 14, 7, nil, 16, 8 ]
20
+
21
+ racc_action_check = [
22
+ 1, 17, 1, 1, 1, 1, 8, 1, 1, 1,
23
+ 20, 20, 20, 20, 20, 20, 20, 20, 7, 7,
24
+ 7, 7, nil, 7, 7, 19, 19, 19, 19, nil,
25
+ 19, 19, 0, 0, 0, 0, nil, 0, 0 ]
26
+
27
+ racc_action_pointer = [
28
+ 30, 0, nil, nil, nil, nil, nil, 16, 3, nil,
29
+ nil, nil, nil, nil, nil, nil, nil, 1, nil, 23,
30
+ 8, nil, nil, nil, nil ]
31
+
32
+ racc_action_default = [
33
+ -18, -18, -2, -3, -4, -5, -6, -18, -18, -10,
34
+ -11, -12, -13, -14, -15, -16, -17, -18, -1, -18,
35
+ -18, -9, 25, -8, -7 ]
36
+
37
+ racc_goto_table = [
38
+ 18, 1, 21, nil, nil, nil, nil, nil, 20, nil,
39
+ nil, nil, nil, nil, nil, nil, nil, nil, 23, 18 ]
40
+
41
+ racc_goto_check = [
42
+ 2, 1, 7, nil, nil, nil, nil, nil, 1, nil,
43
+ nil, nil, nil, nil, nil, nil, nil, nil, 2, 2 ]
44
+
45
+ racc_goto_pointer = [
46
+ nil, 1, -1, nil, nil, nil, nil, -6, nil, nil,
47
+ nil ]
48
+
49
+ racc_goto_default = [
50
+ nil, nil, 2, 3, 4, 5, 6, 10, 9, 11,
51
+ 12 ]
52
+
53
+ racc_reduce_table = [
54
+ 0, 0, :racc_error,
55
+ 2, 11, :_reduce_1,
56
+ 1, 11, :_reduce_2,
57
+ 1, 11, :_reduce_none,
58
+ 1, 12, :_reduce_none,
59
+ 1, 12, :_reduce_none,
60
+ 1, 12, :_reduce_none,
61
+ 3, 15, :_reduce_7,
62
+ 3, 13, :_reduce_8,
63
+ 2, 16, :_reduce_9,
64
+ 1, 14, :_reduce_none,
65
+ 1, 14, :_reduce_none,
66
+ 1, 14, :_reduce_none,
67
+ 1, 14, :_reduce_none,
68
+ 1, 19, :_reduce_14,
69
+ 1, 18, :_reduce_15,
70
+ 1, 17, :_reduce_16,
71
+ 1, 20, :_reduce_17 ]
72
+
73
+ racc_reduce_n = 18
74
+
75
+ racc_shift_n = 25
76
+
77
+ racc_token_table = {
78
+ false => 0,
79
+ :error => 1,
80
+ :SLASH => 2,
81
+ :LITERAL => 3,
82
+ :SYMBOL => 4,
83
+ :LPAREN => 5,
84
+ :RPAREN => 6,
85
+ :DOT => 7,
86
+ :STAR => 8,
87
+ :OR => 9 }
88
+
89
+ racc_nt_base = 10
90
+
91
+ racc_use_result_var = true
92
+
93
+ Racc_arg = [
94
+ racc_action_table,
95
+ racc_action_check,
96
+ racc_action_default,
97
+ racc_action_pointer,
98
+ racc_goto_table,
99
+ racc_goto_check,
100
+ racc_goto_default,
101
+ racc_goto_pointer,
102
+ racc_nt_base,
103
+ racc_reduce_table,
104
+ racc_token_table,
105
+ racc_shift_n,
106
+ racc_reduce_n,
107
+ racc_use_result_var ]
108
+
109
+ Racc_token_to_s_table = [
110
+ "$end",
111
+ "error",
112
+ "SLASH",
113
+ "LITERAL",
114
+ "SYMBOL",
115
+ "LPAREN",
116
+ "RPAREN",
117
+ "DOT",
118
+ "STAR",
119
+ "OR",
120
+ "$start",
121
+ "expressions",
122
+ "expression",
123
+ "or",
124
+ "terminal",
125
+ "group",
126
+ "star",
127
+ "literal",
128
+ "symbol",
129
+ "slash",
130
+ "dot" ]
131
+
132
+ Racc_debug_parser = false
133
+
134
+ ##### State transition tables end #####
135
+
136
+ # reduce 0 omitted
137
+
138
+ def _reduce_1(val, _values, result)
139
+ result = Cat.new(val.first, val.last)
140
+ result
141
+ end
142
+
143
+ def _reduce_2(val, _values, result)
144
+ result = val.first
145
+ result
146
+ end
147
+
148
+ # reduce 3 omitted
149
+
150
+ # reduce 4 omitted
151
+
152
+ # reduce 5 omitted
153
+
154
+ # reduce 6 omitted
155
+
156
+ def _reduce_7(val, _values, result)
157
+ result = Group.new(val[1])
158
+ result
159
+ end
160
+
161
+ def _reduce_8(val, _values, result)
162
+ result = Or.new([val.first, val.last])
163
+ result
164
+ end
165
+
166
+ def _reduce_9(val, _values, result)
167
+ result = Star.new(Symbol.new(val.last.left))
168
+ result
169
+ end
170
+
171
+ # reduce 10 omitted
172
+
173
+ # reduce 11 omitted
174
+
175
+ # reduce 12 omitted
176
+
177
+ # reduce 13 omitted
178
+
179
+ def _reduce_14(val, _values, result)
180
+ result = Slash.new('/')
181
+ result
182
+ end
183
+
184
+ def _reduce_15(val, _values, result)
185
+ result = Symbol.new(val.first)
186
+ result
187
+ end
188
+
189
+ def _reduce_16(val, _values, result)
190
+ result = Literal.new(val.first)
191
+ result
192
+ end
193
+
194
+ def _reduce_17(val, _values, result)
195
+ result = Dot.new(val.first)
196
+ result
197
+ end
198
+
199
+ def _reduce_none(val, _values, result)
200
+ val[0]
201
+ end
202
+
203
+ end # class Parser
204
+ end # module Journey
@@ -0,0 +1,47 @@
1
+ class Journey::Parser
2
+
3
+ token SLASH LITERAL SYMBOL LPAREN RPAREN DOT STAR OR
4
+
5
+ rule
6
+ expressions
7
+ : expressions expression { result = Cat.new(val.first, val.last) }
8
+ | expression { result = val.first }
9
+ | or
10
+ ;
11
+ expression
12
+ : terminal
13
+ | group
14
+ | star
15
+ ;
16
+ group
17
+ : LPAREN expressions RPAREN { result = Group.new(val[1]) }
18
+ ;
19
+ or
20
+ : expressions OR expression { result = Or.new([val.first, val.last]) }
21
+ ;
22
+ star
23
+ : STAR literal { result = Star.new(Symbol.new(val.last.left)) }
24
+ ;
25
+ terminal
26
+ : symbol
27
+ | literal
28
+ | slash
29
+ | dot
30
+ ;
31
+ slash
32
+ : SLASH { result = Slash.new('/') }
33
+ ;
34
+ symbol
35
+ : SYMBOL { result = Symbol.new(val.first) }
36
+ ;
37
+ literal
38
+ : LITERAL { result = Literal.new(val.first) }
39
+ dot
40
+ : DOT { result = Dot.new(val.first) }
41
+ ;
42
+
43
+ end
44
+
45
+ ---- header
46
+
47
+ require 'journey/parser_extras'
@@ -0,0 +1,21 @@
1
+ require 'journey/scanner'
2
+ require 'journey/nodes/node'
3
+
4
+ module Journey
5
+ class Parser < Racc::Parser
6
+ include Journey::Nodes
7
+
8
+ def initialize
9
+ @scanner = Scanner.new
10
+ end
11
+
12
+ def parse string
13
+ @scanner.scan_setup string
14
+ do_parse
15
+ end
16
+
17
+ def next_token
18
+ @scanner.next_token
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,190 @@
1
+ module Journey
2
+ module Path
3
+ class Pattern
4
+ attr_reader :spec, :requirements, :anchored
5
+
6
+ def initialize strexp
7
+ parser = Journey::Parser.new
8
+
9
+ @anchored = true
10
+
11
+ case strexp
12
+ when String
13
+ @spec = parser.parse strexp
14
+ @requirements = {}
15
+ @separators = "/.?"
16
+ when Router::Strexp
17
+ @spec = parser.parse strexp.path
18
+ @requirements = strexp.requirements
19
+ @separators = strexp.separators.join
20
+ @anchored = strexp.anchor
21
+ else
22
+ raise "wtf bro: #{strexp}"
23
+ end
24
+
25
+ @names = nil
26
+ @optional_names = nil
27
+ @required_names = nil
28
+ @re = nil
29
+ end
30
+
31
+ def ast
32
+ @spec.grep(Nodes::Symbol).each do |node|
33
+ re = @requirements[node.to_sym]
34
+ node.regexp = re if re
35
+ end
36
+
37
+ @spec.grep(Nodes::Star).each do |node|
38
+ node = node.left
39
+ node.regexp = @requirements[node.to_sym] || /(.+)/
40
+ end
41
+
42
+ @spec
43
+ end
44
+
45
+ def names
46
+ @names ||= spec.grep(Nodes::Symbol).map { |n| n.name }
47
+ end
48
+
49
+ def required_names
50
+ @required_names ||= names - optional_names
51
+ end
52
+
53
+ def optional_names
54
+ @optional_names ||= spec.grep(Nodes::Group).map { |group|
55
+ group.grep(Nodes::Symbol)
56
+ }.flatten.map { |n| n.name }.uniq
57
+ end
58
+
59
+ class RegexpOffsets < Journey::Visitors::Visitor # :nodoc:
60
+ attr_reader :offsets
61
+
62
+ def initialize matchers
63
+ @matchers = matchers
64
+ @capture_count = [0]
65
+ end
66
+
67
+ def visit node
68
+ super
69
+ @capture_count
70
+ end
71
+
72
+ def visit_SYMBOL node
73
+ node = node.to_sym
74
+
75
+ if @matchers.key? node
76
+ re = /#{@matchers[node]}|/
77
+ @capture_count.push((re.match('').length - 1) + (@capture_count.last || 0))
78
+ else
79
+ @capture_count << (@capture_count.last || 0)
80
+ end
81
+ end
82
+ end
83
+
84
+ class AnchoredRegexp < Journey::Visitors::Visitor # :nodoc:
85
+ def initialize separator, matchers
86
+ @separator = separator
87
+ @matchers = matchers
88
+ @separator_re = "([^#{separator}]+)"
89
+ super()
90
+ end
91
+
92
+ def accept node
93
+ %r{\A#{visit node}\Z}
94
+ end
95
+
96
+ def visit_CAT node
97
+ [visit(node.left), visit(node.right)].join
98
+ end
99
+
100
+ def visit_SYMBOL node
101
+ node = node.to_sym
102
+
103
+ return @separator_re unless @matchers.key? node
104
+
105
+ re = @matchers[node]
106
+ # FIXME: is the question mark needed?
107
+ "(#{re}?)"
108
+ end
109
+
110
+ def visit_GROUP node
111
+ "(?:#{visit node.left})?"
112
+ end
113
+
114
+ def visit_LITERAL node
115
+ Regexp.escape node.left
116
+ end
117
+ alias :visit_DOT :visit_LITERAL
118
+
119
+ def visit_SLASH node
120
+ node.left
121
+ end
122
+
123
+ def visit_STAR node
124
+ "(.+)"
125
+ end
126
+ end
127
+
128
+ class UnanchoredRegexp < AnchoredRegexp # :nodoc:
129
+ def accept node
130
+ %r{\A#{visit node}}
131
+ end
132
+ end
133
+
134
+ class MatchData
135
+ attr_reader :names
136
+
137
+ def initialize names, offsets, match
138
+ @names = names
139
+ @offsets = offsets
140
+ @match = match
141
+ end
142
+
143
+ def captures
144
+ (length - 1).times.map { |i| self[i + 1] }
145
+ end
146
+
147
+ def [] x
148
+ idx = @offsets[x - 1] + x
149
+ @match[idx]
150
+ end
151
+
152
+ def length
153
+ @offsets.length
154
+ end
155
+
156
+ def post_match
157
+ @match.post_match
158
+ end
159
+
160
+ def to_s
161
+ @match.to_s
162
+ end
163
+ end
164
+
165
+ def match other
166
+ return unless match = to_regexp.match(other)
167
+ MatchData.new names, offsets, match
168
+ end
169
+ alias :=~ :match
170
+
171
+ def source
172
+ to_regexp.source
173
+ end
174
+
175
+ private
176
+ def to_regexp
177
+ @re ||= regexp_visitor.new(@separators, @requirements).accept spec
178
+ end
179
+
180
+ def regexp_visitor
181
+ @anchored ? AnchoredRegexp : UnanchoredRegexp
182
+ end
183
+
184
+ def offsets
185
+ viz = RegexpOffsets.new @requirements
186
+ viz.accept spec
187
+ end
188
+ end
189
+ end
190
+ end