rattler 0.3.0 → 0.4.0

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 (182) hide show
  1. data/README.rdoc +57 -37
  2. data/features/command_line/dest_option.feature +8 -21
  3. data/features/command_line/lib_option.feature +37 -0
  4. data/features/command_line/parser_generator.feature +7 -4
  5. data/features/grammar/back_reference.feature +37 -0
  6. data/features/grammar/fail.feature +3 -3
  7. data/features/grammar/labels.feature +11 -3
  8. data/features/grammar/list_matching.feature +14 -5
  9. data/features/grammar/literal.feature +30 -4
  10. data/features/grammar/nonterminal.feature +1 -1
  11. data/features/grammar/ordered_choice.feature +2 -2
  12. data/features/grammar/skip_operator.feature +1 -1
  13. data/features/grammar/symantic_action.feature +7 -7
  14. data/features/grammar/whitespace.feature +2 -2
  15. data/features/step_definitions/grammar_steps.rb +2 -2
  16. data/lib/rattler/back_end.rb +1 -0
  17. data/lib/rattler/back_end/compiler.rb +19 -20
  18. data/lib/rattler/back_end/optimizer.rb +100 -0
  19. data/lib/rattler/back_end/optimizer/composite_reducing.rb +18 -0
  20. data/lib/rattler/back_end/optimizer/flatten_choice.rb +31 -0
  21. data/lib/rattler/back_end/optimizer/flatten_sequence.rb +59 -0
  22. data/lib/rattler/back_end/optimizer/flattening.rb +17 -0
  23. data/lib/rattler/back_end/optimizer/inline_regular_rules.rb +46 -0
  24. data/lib/rattler/back_end/optimizer/join_match_capturing_sequence.rb +71 -0
  25. data/lib/rattler/back_end/optimizer/join_match_choice.rb +37 -0
  26. data/lib/rattler/back_end/optimizer/join_match_matching_sequence.rb +38 -0
  27. data/lib/rattler/back_end/optimizer/join_match_sequence.rb +17 -0
  28. data/lib/rattler/back_end/optimizer/join_predicate_bare_match.rb +68 -0
  29. data/lib/rattler/back_end/optimizer/join_predicate_match.rb +17 -0
  30. data/lib/rattler/back_end/optimizer/join_predicate_nested_match.rb +37 -0
  31. data/lib/rattler/back_end/optimizer/join_predicate_or_bare_match.rb +68 -0
  32. data/lib/rattler/back_end/optimizer/join_predicate_or_match.rb +17 -0
  33. data/lib/rattler/back_end/optimizer/join_predicate_or_nested_match.rb +36 -0
  34. data/lib/rattler/back_end/optimizer/match_joining.rb +60 -0
  35. data/lib/rattler/back_end/optimizer/optimization.rb +94 -0
  36. data/lib/rattler/back_end/optimizer/optimization_context.rb +72 -0
  37. data/lib/rattler/back_end/optimizer/optimization_sequence.rb +37 -0
  38. data/lib/rattler/back_end/optimizer/optimize_children.rb +46 -0
  39. data/lib/rattler/back_end/optimizer/reduce_repeat_match.rb +44 -0
  40. data/lib/rattler/back_end/optimizer/remove_meaningless_wrapper.rb +32 -0
  41. data/lib/rattler/back_end/optimizer/simplify_redundant_repeat.rb +43 -0
  42. data/lib/rattler/back_end/optimizer/simplify_token_match.rb +38 -0
  43. data/lib/rattler/back_end/parser_generator.rb +21 -14
  44. data/lib/rattler/back_end/parser_generator/apply_generator.rb +35 -35
  45. data/lib/rattler/back_end/parser_generator/assert_generator.rb +29 -30
  46. data/lib/rattler/back_end/parser_generator/back_reference_generator.rb +93 -0
  47. data/lib/rattler/back_end/parser_generator/choice_generator.rb +33 -49
  48. data/lib/rattler/back_end/parser_generator/direct_action_generator.rb +14 -14
  49. data/lib/rattler/back_end/parser_generator/disallow_generator.rb +29 -30
  50. data/lib/rattler/back_end/parser_generator/dispatch_action_generator.rb +11 -13
  51. data/lib/rattler/back_end/parser_generator/expr_generator.rb +36 -56
  52. data/lib/rattler/back_end/parser_generator/fail_generator.rb +18 -18
  53. data/lib/rattler/back_end/parser_generator/group_match.rb +18 -0
  54. data/lib/rattler/back_end/parser_generator/group_match_generator.rb +76 -0
  55. data/lib/rattler/back_end/parser_generator/label_generator.rb +25 -6
  56. data/lib/rattler/back_end/parser_generator/list1_generator.rb +7 -7
  57. data/lib/rattler/back_end/parser_generator/list_generating.rb +19 -20
  58. data/lib/rattler/back_end/parser_generator/list_generator.rb +5 -5
  59. data/lib/rattler/back_end/parser_generator/match_generator.rb +52 -52
  60. data/lib/rattler/back_end/parser_generator/one_or_more_generator.rb +6 -6
  61. data/lib/rattler/back_end/parser_generator/optional_generator.rb +30 -29
  62. data/lib/rattler/back_end/parser_generator/predicate_propogating.rb +8 -8
  63. data/lib/rattler/back_end/parser_generator/repeat_generating.rb +23 -25
  64. data/lib/rattler/back_end/parser_generator/rule_generator.rb +27 -79
  65. data/lib/rattler/back_end/parser_generator/rule_set_generator.rb +102 -0
  66. data/lib/rattler/back_end/parser_generator/sequence_generator.rb +49 -41
  67. data/lib/rattler/back_end/parser_generator/skip_generator.rb +14 -20
  68. data/lib/rattler/back_end/parser_generator/skip_propogating.rb +4 -4
  69. data/lib/rattler/back_end/parser_generator/sub_generating.rb +6 -0
  70. data/lib/rattler/back_end/parser_generator/token_generator.rb +12 -12
  71. data/lib/rattler/back_end/parser_generator/token_propogating.rb +2 -2
  72. data/lib/rattler/back_end/parser_generator/zero_or_more_generator.rb +4 -4
  73. data/lib/rattler/grammar.rb +4 -3
  74. data/lib/rattler/grammar/analysis.rb +91 -0
  75. data/lib/rattler/grammar/grammar.rb +37 -25
  76. data/lib/rattler/grammar/grammar_parser.rb +19 -11
  77. data/lib/rattler/grammar/metagrammar.rb +569 -800
  78. data/lib/rattler/grammar/rattler.rtlr +162 -144
  79. data/lib/rattler/parsers.rb +5 -1
  80. data/lib/rattler/parsers/action_code.rb +29 -15
  81. data/lib/rattler/parsers/apply.rb +5 -5
  82. data/lib/rattler/parsers/assert.rb +4 -18
  83. data/lib/rattler/parsers/back_reference.rb +46 -0
  84. data/lib/rattler/parsers/choice.rb +6 -39
  85. data/lib/rattler/parsers/combinator_parser.rb +32 -0
  86. data/lib/rattler/parsers/combining.rb +3 -29
  87. data/lib/rattler/parsers/direct_action.rb +27 -30
  88. data/lib/rattler/parsers/disallow.rb +4 -18
  89. data/lib/rattler/parsers/dispatch_action.rb +30 -25
  90. data/lib/rattler/parsers/label.rb +9 -18
  91. data/lib/rattler/parsers/list.rb +3 -34
  92. data/lib/rattler/parsers/list1.rb +4 -36
  93. data/lib/rattler/parsers/list_parser.rb +64 -0
  94. data/lib/rattler/parsers/match.rb +7 -42
  95. data/lib/rattler/parsers/node_code.rb +44 -0
  96. data/lib/rattler/parsers/one_or_more.rb +7 -27
  97. data/lib/rattler/parsers/optional.rb +5 -25
  98. data/lib/rattler/parsers/parser.rb +16 -44
  99. data/lib/rattler/parsers/parser_dsl.rb +13 -3
  100. data/lib/rattler/parsers/predicate.rb +4 -12
  101. data/lib/rattler/parsers/rule.rb +18 -19
  102. data/lib/rattler/parsers/rule_set.rb +63 -0
  103. data/lib/rattler/parsers/sequence.rb +12 -46
  104. data/lib/rattler/parsers/skip.rb +12 -26
  105. data/lib/rattler/parsers/token.rb +6 -21
  106. data/lib/rattler/parsers/zero_or_more.rb +6 -26
  107. data/lib/rattler/runner.rb +66 -28
  108. data/lib/rattler/runtime/extended_packrat_parser.rb +26 -20
  109. data/lib/rattler/runtime/packrat_parser.rb +17 -21
  110. data/lib/rattler/runtime/parser.rb +12 -2
  111. data/lib/rattler/runtime/recursive_descent_parser.rb +3 -11
  112. data/lib/rattler/util.rb +2 -1
  113. data/lib/rattler/util/graphviz.rb +29 -0
  114. data/lib/rattler/util/graphviz/digraph_builder.rb +71 -0
  115. data/lib/rattler/util/graphviz/node_builder.rb +84 -0
  116. data/lib/rattler/util/node.rb +37 -19
  117. data/lib/rattler/util/parser_spec_helper.rb +61 -35
  118. data/spec/rattler/back_end/compiler_spec.rb +6 -860
  119. data/spec/rattler/back_end/optimizer/flatten_choice_spec.rb +70 -0
  120. data/spec/rattler/back_end/optimizer/flatten_sequence_spec.rb +130 -0
  121. data/spec/rattler/back_end/optimizer/inline_regular_rules_spec.rb +80 -0
  122. data/spec/rattler/back_end/optimizer/join_match_capturing_sequence_spec.rb +241 -0
  123. data/spec/rattler/back_end/optimizer/join_match_choice_spec.rb +100 -0
  124. data/spec/rattler/back_end/optimizer/join_match_matching_sequence_spec.rb +112 -0
  125. data/spec/rattler/back_end/optimizer/join_predicate_bare_match_spec.rb +194 -0
  126. data/spec/rattler/back_end/optimizer/join_predicate_nested_match_spec.rb +180 -0
  127. data/spec/rattler/back_end/optimizer/join_predicate_or_bare_match_spec.rb +153 -0
  128. data/spec/rattler/back_end/optimizer/join_predicate_or_nested_match_spec.rb +153 -0
  129. data/spec/rattler/back_end/optimizer/reduce_repeat_match_spec.rb +98 -0
  130. data/spec/rattler/back_end/optimizer/simplify_redundant_repeat_spec.rb +226 -0
  131. data/spec/rattler/back_end/optimizer/simplify_token_match_spec.rb +85 -0
  132. data/spec/rattler/back_end/parser_generator/apply_generator_spec.rb +38 -33
  133. data/spec/rattler/back_end/parser_generator/assert_generator_spec.rb +38 -33
  134. data/spec/rattler/back_end/parser_generator/back_reference_generator_spec.rb +181 -0
  135. data/spec/rattler/back_end/parser_generator/choice_generator_spec.rb +38 -33
  136. data/spec/rattler/back_end/parser_generator/direct_action_generator_spec.rb +38 -33
  137. data/spec/rattler/back_end/parser_generator/disallow_generator_spec.rb +38 -33
  138. data/spec/rattler/back_end/parser_generator/dispatch_action_generator_spec.rb +38 -33
  139. data/spec/rattler/back_end/parser_generator/group_match_generator_spec.rb +185 -0
  140. data/spec/rattler/back_end/parser_generator/label_generator_spec.rb +38 -33
  141. data/spec/rattler/back_end/parser_generator/list1_generator_spec.rb +10 -5
  142. data/spec/rattler/back_end/parser_generator/list_generator_spec.rb +10 -5
  143. data/spec/rattler/back_end/parser_generator/match_generator_spec.rb +38 -33
  144. data/spec/rattler/back_end/parser_generator/one_or_more_generator_spec.rb +38 -33
  145. data/spec/rattler/back_end/parser_generator/optional_generator_spec.rb +38 -33
  146. data/spec/rattler/back_end/parser_generator/rule_generator_spec.rb +13 -46
  147. data/spec/rattler/back_end/parser_generator/rule_set_generator_spec.rb +97 -0
  148. data/spec/rattler/back_end/parser_generator/sequence_generator_spec.rb +38 -33
  149. data/spec/rattler/back_end/parser_generator/skip_generator_spec.rb +38 -33
  150. data/spec/rattler/back_end/parser_generator/token_generator_spec.rb +38 -33
  151. data/spec/rattler/back_end/parser_generator/zero_or_more_generator_spec.rb +39 -34
  152. data/spec/rattler/back_end/shared_compiler_examples.rb +885 -0
  153. data/spec/rattler/grammar/analysis_spec.rb +167 -0
  154. data/spec/rattler/grammar/grammar_parser_spec.rb +169 -179
  155. data/spec/rattler/grammar/grammar_spec.rb +24 -21
  156. data/spec/rattler/parsers/action_code_spec.rb +64 -19
  157. data/spec/rattler/parsers/apply_spec.rb +9 -9
  158. data/spec/rattler/parsers/back_reference_spec.rb +38 -0
  159. data/spec/rattler/parsers/combinator_parser_spec.rb +14 -0
  160. data/spec/rattler/parsers/direct_action_spec.rb +16 -2
  161. data/spec/rattler/parsers/dispatch_action_spec.rb +15 -32
  162. data/spec/rattler/parsers/fail_spec.rb +6 -4
  163. data/spec/rattler/parsers/label_spec.rb +10 -28
  164. data/spec/rattler/parsers/node_code_spec.rb +48 -0
  165. data/spec/rattler/parsers/parser_dsl_spec.rb +1 -1
  166. data/spec/rattler/parsers/rule_set_spec.rb +35 -0
  167. data/spec/rattler/parsers/sequence_spec.rb +15 -24
  168. data/spec/rattler/runtime/extended_packrat_parser_spec.rb +22 -17
  169. data/spec/rattler/runtime/packrat_parser_spec.rb +1 -1
  170. data/spec/rattler/runtime/parse_node_spec.rb +15 -19
  171. data/spec/rattler/runtime/recursive_descent_parser_spec.rb +1 -1
  172. data/spec/rattler/runtime/shared_parser_examples.rb +61 -28
  173. data/spec/rattler/util/graphviz/node_builder_spec.rb +84 -0
  174. data/spec/rattler/util/node_spec.rb +92 -65
  175. data/spec/rattler_spec.rb +16 -16
  176. data/spec/support/combinator_parser_spec_helper.rb +19 -18
  177. data/spec/support/compiler_spec_helper.rb +56 -87
  178. data/spec/support/runtime_parser_spec_helper.rb +6 -14
  179. metadata +117 -22
  180. data/features/grammar/regex.feature +0 -24
  181. data/lib/rattler/parsers/match_joining.rb +0 -67
  182. data/lib/rattler/parsers/rules.rb +0 -43
@@ -0,0 +1,71 @@
1
+ #
2
+ # = rattler/util/graphviz/digraph_builder.rb
3
+ #
4
+ # Author:: Jason Arhart
5
+ # Documentation:: Author
6
+ #
7
+
8
+ begin
9
+ require 'graphviz'
10
+ rescue LoadError => e
11
+ abort "I need the ruby-graphviz gem and GraphViz installed and in the PATH.\n"
12
+ end
13
+
14
+ require 'rattler/util/graphviz'
15
+
16
+ module Rattler::Util::GraphViz
17
+ #
18
+ # +DigraphBuilder+ is used to build GraphViz objects representing trees of
19
+ # nodes.
20
+ #
21
+ # @author Jason Arhart
22
+ #
23
+ class DigraphBuilder
24
+
25
+ # Return a new +GraphViz+ digraph object representing +root+.
26
+ #
27
+ # @return a new +GraphViz+ digraph object representing +root+
28
+ def self.digraph(root, name='G')
29
+ self.new(root, name).digraph
30
+ end
31
+
32
+ # Create a new digraph builder for +root+.
33
+ def initialize(root, name='G')
34
+ @root = root
35
+ @g = ::GraphViz.digraph(name)
36
+ @nodes = {}
37
+ @node_serial = 0
38
+ @node_builder = NodeBuilder.new
39
+ end
40
+
41
+ # @return a new +GraphViz+ digraph object representing the root object
42
+ def digraph
43
+ @digraph ||= begin
44
+ node(@root)
45
+ @g
46
+ end
47
+ end
48
+
49
+ # Return a <tt>GraphViz::Node</tt> object for +o+. Multiple requests with
50
+ # the same object return the same node object.
51
+ #
52
+ # @return a <tt>GraphViz::Node</tt> object for +o+
53
+ def node(o)
54
+ @nodes.fetch(o.object_id) do
55
+ new_node = @g.add_node new_node_name, @node_builder.node_options(o)
56
+ @nodes[o.object_id] = new_node
57
+ @node_builder.each_child_of(o) {|_| new_node << node(_) }
58
+ new_node
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def new_node_name
65
+ name = "n#{@node_serial}"
66
+ @node_serial += 1
67
+ name
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,84 @@
1
+ #
2
+ # = rattler/util/graphviz/node_builder.rb
3
+ #
4
+ # Author:: Jason Arhart
5
+ # Documentation:: Author
6
+ #
7
+ require 'rattler/util/graphviz'
8
+
9
+ module Rattler::Util::GraphViz
10
+ #
11
+ # +NodeBuilder+ is used by +DigraphBuilder+ to build nodes for a GraphViz
12
+ # digraph object representing a tree of nodes.
13
+ #
14
+ # @author Jason Arhart
15
+ #
16
+ class NodeBuilder
17
+
18
+ # Yield any children of +o+ that should be represented as separate nodes in
19
+ # the graph.
20
+ def each_child_of(o)
21
+ o.each {|_| yield _ } if array_like? o and not record_like? o
22
+ end
23
+
24
+ # Return the options for a node representing +o+.
25
+ # @return the options for a node representing +o+.
26
+ def node_options(o)
27
+ { :shape => node_shape(o), :label => node_label(o) }
28
+ end
29
+
30
+ # Return the shape option for a node representing +o+.
31
+ # @return the shape option for a node representing +o+.
32
+ def node_shape(o)
33
+ case o
34
+ when Hash, Array
35
+ 'circle'
36
+ when String, Numeric, Symbol
37
+ 'plaintext'
38
+ else
39
+ 'Mrecord'
40
+ end
41
+ end
42
+
43
+ # Return the label option for a node representing +o+.
44
+ # @return the label option for a node representing +o+.
45
+ def node_label(o)
46
+ if o.is_a? ::Rattler::Util::Node
47
+ record_label(o, o.attrs)
48
+ elsif record_like? o
49
+ record_label(o, o)
50
+ elsif array_like? o
51
+ type_label(o)
52
+ else
53
+ o.inspect
54
+ end
55
+ end
56
+
57
+ def type_label(o)
58
+ case o
59
+ when Hash then '\\{\\}'
60
+ when Array then '\\[\\]'
61
+ else o.respond_to?(:name) ? o.name : o.class.name
62
+ end
63
+ end
64
+
65
+ def array_like?(o)
66
+ o.respond_to? :each and
67
+ not o.respond_to? :to_str
68
+ end
69
+
70
+ def record_like?(o)
71
+ o.respond_to? :each_pair and
72
+ o.none? {|k, v| array_like? v or record_like? v }
73
+ end
74
+
75
+ def record_label(o, h)
76
+ '{' + ([type_label(o)] + hash_content_labels(h)).join('|') + '}'
77
+ end
78
+
79
+ def hash_content_labels(h)
80
+ h.map {|pair| '{' + pair.map {|_| _.inspect }.join('|') + '}' }
81
+ end
82
+
83
+ end
84
+ end
@@ -16,7 +16,7 @@ module Rattler::Util
16
16
  #
17
17
  class Node
18
18
  include Enumerable
19
-
19
+
20
20
  # Create a +Node+ object.
21
21
  #
22
22
  # @return [Node]
@@ -32,7 +32,7 @@ module Rattler::Util
32
32
  def self.[](*args)
33
33
  self.new(*args)
34
34
  end
35
-
35
+
36
36
  # Create a +Node+ object.
37
37
  #
38
38
  # @overload initialize()
@@ -47,7 +47,7 @@ module Rattler::Util
47
47
  @attrs = args.last.respond_to?(:to_hash) ? args.pop : {}
48
48
  @__children__ = args
49
49
  end
50
-
50
+
51
51
  # Return an array of the node's children
52
52
  #
53
53
  # @return [Array] the node's children
@@ -62,7 +62,7 @@ module Rattler::Util
62
62
  []
63
63
  end
64
64
  end
65
-
65
+
66
66
  # Return the node's child at +index+, or the first/only child if no index
67
67
  # is given.
68
68
  #
@@ -72,14 +72,14 @@ module Rattler::Util
72
72
  def child(index = 0)
73
73
  children[index]
74
74
  end
75
-
75
+
76
76
  # Return a the node's attributes.
77
77
  #
78
78
  # @return [Hash] the node's attributes
79
79
  def attrs
80
80
  @attrs ||= {}
81
81
  end
82
-
82
+
83
83
  # Return the node's name, which is the node's +name+ attribute if it has
84
84
  # one, otherwise the name of the node's class.
85
85
  #
@@ -87,21 +87,35 @@ module Rattler::Util
87
87
  def name
88
88
  attrs.fetch(:name, self.class.name)
89
89
  end
90
-
90
+
91
91
  # Call _block_ once for each child, passing that child as an argument.
92
92
  #
93
93
  # @yield [child]
94
94
  def each # :yield: child
95
- children.each { |_| yield _ }
95
+ block_given? ? children.each { |_| yield _ } : children.each
96
+ end
97
+
98
+ def with_children(new_children)
99
+ self.class.new(new_children, attrs)
100
+ end
101
+
102
+ alias_method :with_child, :with_children
103
+
104
+ def with_attrs(new_attrs)
105
+ self.with_attrs!(attrs.merge new_attrs)
106
+ end
107
+
108
+ def with_attrs!(new_attrs)
109
+ self.class.new(children, new_attrs)
96
110
  end
97
-
111
+
98
112
  # Return +true+ if the node has no children.
99
113
  #
100
114
  # @return [Boolean] +true+ if the node has no children
101
115
  def empty?
102
116
  children.empty?
103
117
  end
104
-
118
+
105
119
  # Access the node's children as if the node were an array of its children.
106
120
  #
107
121
  # @overload [](index)
@@ -122,7 +136,7 @@ module Rattler::Util
122
136
  def [](*args)
123
137
  children[*args]
124
138
  end
125
-
139
+
126
140
  # Return +true+ if the node is equal to +other+. Normally this means
127
141
  # +other+ is an instance of the same class or a subclass and has equal
128
142
  # children and attributes.
@@ -133,7 +147,7 @@ module Rattler::Util
133
147
  other.can_equal?(self) and
134
148
  self.same_contents?(other)
135
149
  end
136
-
150
+
137
151
  # Return +true+ if the node has the same value as +other+, i.e. +other+
138
152
  # is an instance of the same class and has equal children and attributes.
139
153
  #
@@ -142,28 +156,28 @@ module Rattler::Util
142
156
  self.class == other.class and
143
157
  self.same_contents?(other)
144
158
  end
145
-
159
+
146
160
  # Allow attributes to be accessed as methods.
147
161
  def method_missing(symbol, *args)
148
- (args.empty? and @attrs.has_key?(symbol)) ? @attrs[symbol] : super
162
+ (args.empty? and attrs.has_key?(symbol)) ? attrs[symbol] : super
149
163
  end
150
-
164
+
151
165
  # @private
152
166
  def respond_to?(symbol) #:nodoc:
153
167
  super || @attrs.has_key?(symbol)
154
168
  end
155
-
169
+
156
170
  # @private
157
171
  def can_equal?(other) #:nodoc:
158
172
  self.class == other.class
159
173
  end
160
-
174
+
161
175
  # @private
162
176
  def same_contents?(other) #:nodoc:
163
177
  self.children == other.children and
164
178
  self.attrs == other.attrs
165
179
  end
166
-
180
+
167
181
  # @private
168
182
  def inspect #:nodoc:
169
183
  "#{self.class}[" +
@@ -171,6 +185,10 @@ module Rattler::Util
171
185
  attrs.map {|k, v| k.inspect + '=>' + v.inspect}).join(',') +
172
186
  ']'
173
187
  end
174
-
188
+
189
+ def to_graphviz
190
+ Rattler::Util::GraphViz.digraph(self)
191
+ end
192
+
175
193
  end
176
194
  end
@@ -12,17 +12,17 @@ module Rattler::Util
12
12
  # for parsers.
13
13
  #
14
14
  # @example
15
- #
15
+ #
16
16
  # require 'rattler/grammar/grammar_parser'
17
17
  # require 'rattler/util/parser_spec_helper'
18
- #
18
+ #
19
19
  # describe Rattler::Grammar::GrammarParser do
20
20
  # include Rattler::Util::ParserSpecHelper
21
- #
21
+ #
22
22
  # describe '#match(:var_name)' do
23
23
  # it 'recognizes variable names' do
24
- # parsing(' fooBar ').as(:var_name).should result_in('fooBar').at(7)
25
- # parsing(' FooBar ').as(:var_name).should fail.with_message('variable name expected')
24
+ # matching(' fooBar ').as(:var_name).should result_in('fooBar').at(7)
25
+ # matching(' FooBar ').as(:var_name).should fail.with_message('variable name expected')
26
26
  # end
27
27
  # end
28
28
  # end
@@ -30,39 +30,58 @@ module Rattler::Util
30
30
  # @author Jason Arhart
31
31
  #
32
32
  module ParserSpecHelper
33
-
33
+
34
34
  # Return a parse result to be matched using #result_in or #fail
35
- #
36
- # parsing(source).as(rule_name)
37
- # parsing(source).as(rule_name).from(pos)
35
+ #
36
+ # parsing(source)
37
+ # parsing(source).from(pos)
38
38
  #
39
39
  def parsing(source)
40
- Parsing.new(described_class.new(source))
40
+ Parsing.new(parser(source))
41
+ end
42
+
43
+ # Return a match result to be matched using #result_in or #fail
44
+ #
45
+ # matching(source).as(rule_name)
46
+ # matching(source).as(rule_name).from(pos)
47
+ #
48
+ def matching(source)
49
+ Matching.new(parser(source))
50
+ end
51
+
52
+ def parser(source)
53
+ (self.respond_to?(:parser_class) ? parser_class : described_class).new(source)
41
54
  end
42
-
55
+
43
56
  # Expect parse to succeed.
44
- #
45
- # parsing(source).as(rule_name).should result_in(result)
46
57
  #
47
- # Passes if parsing _source_ with _parser_class_ succeeds returning
48
- # _result_.
58
+ # parsing(source).should result_in(result)
59
+ #
60
+ # Passes if parsing _source_ succeeds returning _result_.
49
61
  #
50
- # parsing(source).as(rule_name).should result_in(result).at(pos)
62
+ # parsing(source).should result_in(result).at(pos)
51
63
  #
52
- # Passes if parsing _source_ with _parser_class_ succeeds returning
64
+ # Passes if parsing _source_ succeeds returning
53
65
  # _result_ with the parse position at _pos_.
54
66
  #
55
67
  # @return [Matcher]
56
68
  RSpec::Matchers.define :result_in do |expected|
57
69
  match do |target|
58
- (target.result == expected) &&
59
- (!@expected_pos || (target.pos == @expected_pos))
70
+ (target.result == expected) and
71
+ (not @expected_pos or
72
+ target.pos == @expected_pos) and
73
+ (not @expected_bindings or
74
+ @expected_bindings.all?{|k, v| target.scope[k] == v })
60
75
  end
61
-
76
+
62
77
  chain :at do |pos|
63
78
  @expected_pos = pos
64
79
  end
65
-
80
+
81
+ chain :with_scope do |bindings|
82
+ @expected_bindings = bindings
83
+ end
84
+
66
85
  failure_message_for_should do |target|
67
86
  if target.result != expected
68
87
  <<-MESSAGE
@@ -79,15 +98,15 @@ MESSAGE
79
98
  end
80
99
  end
81
100
  end
82
-
101
+
83
102
  # Expect parse to fail.
84
103
  #
85
104
  # parsing(source).as(rule_name).should fail
86
- #
105
+ #
87
106
  # Passes if parsing _source_ with _parser_class_ fails.
88
- #
107
+ #
89
108
  # parsing(source).as(rule_name).should fail.at(pos)
90
- #
109
+ #
91
110
  # Passes if parsing _source_ with _parser_class_ fails with the parse
92
111
  # position at _pos_
93
112
  #
@@ -98,15 +117,15 @@ MESSAGE
98
117
  (!@expected_message || (target.failure.message == @expected_message)) &&
99
118
  (!@expected_pos || (target.failure.pos == @expected_pos))
100
119
  end
101
-
120
+
102
121
  chain :with_message do |message|
103
122
  @expected_message = message
104
123
  end
105
-
124
+
106
125
  chain :at do |pos|
107
126
  @expected_pos = pos
108
127
  end
109
-
128
+
110
129
  failure_message_for_should do |target|
111
130
  if target.result
112
131
  "expected parse to fail but got #{target.result.inspect}"
@@ -125,7 +144,7 @@ MESSAGE
125
144
  end
126
145
  end
127
146
  end
128
-
147
+
129
148
  # @private
130
149
  class Parsing #:nodoc:
131
150
  def initialize(parser)
@@ -136,12 +155,8 @@ MESSAGE
136
155
  @parser.pos = pos
137
156
  self
138
157
  end
139
- def as(rule_name)
140
- @rule_name = rule_name
141
- self
142
- end
143
158
  def result
144
- @result ||= @parser.match(@rule_name)
159
+ @result ||= @parser.parse
145
160
  end
146
161
  def pos
147
162
  @parser.pos
@@ -150,6 +165,17 @@ MESSAGE
150
165
  @parser.failure
151
166
  end
152
167
  end
153
-
168
+
169
+ # @private
170
+ class Matching < Parsing #:nodoc:
171
+ def as(rule_name)
172
+ @rule_name = rule_name
173
+ self
174
+ end
175
+ def result
176
+ @result ||= @parser.match(@rule_name)
177
+ end
178
+ end
179
+
154
180
  end
155
181
  end