rattler 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -2,59 +2,92 @@ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
2
 
3
3
  shared_examples_for 'a recursive descent parser' do
4
4
  include RuntimeParserSpecHelper
5
-
5
+
6
+ describe '#parse' do
7
+
8
+ let(:grammar) { define_grammar do
9
+ rule(:word) { match(/\w+/) }
10
+ end }
11
+
12
+ it 'applies the start rule to the source' do
13
+ parsing('Hello World!').should result_in('Hello').at(5)
14
+ end
15
+
16
+ end
17
+ end
18
+
19
+ shared_examples_for 'a generated recursive descent parser' do
20
+ include RuntimeParserSpecHelper
21
+
22
+ it_behaves_like 'a recursive descent parser'
23
+
24
+ let(:parser_class) do
25
+ Rattler::BackEnd::Compiler.compile_parser described_class, grammar
26
+ end
27
+
6
28
  describe '#match' do
7
-
29
+
30
+ let(:grammar) { define_grammar do
31
+ rule(:word) { match(/\w+/) }
32
+ end }
33
+
8
34
  it 'dispatches to the correct match method' do
9
- given_rules { rule(:foo) { match(/\w+/) } }.
10
- parsing('Hello World!').as(:foo).should result_in('Hello').at(5)
35
+ matching('Hello World!').as(:word).should result_in('Hello').at(5)
11
36
  end
12
-
37
+
13
38
  it 'registers parse errors' do
14
- given_rules { rule(:foo) { match(/d+/) } }.
15
- parsing('Hello World!').as(:foo).
16
- should fail.at(0).with_message('foo expected')
39
+ matching('@').as(:word).
40
+ should fail.at(0).with_message('word expected')
17
41
  end
18
-
19
- it 'supports recursive rules' do
20
- given_rules do
42
+
43
+ context 'given recursive rules' do
44
+
45
+ let(:grammar) { define_grammar do
21
46
  rule :a do
22
47
  ( match(/\d/) & match(:a) \
23
48
  | match(/\d/) )
24
49
  end
25
- end.
26
- parsing('451a').as(:a).should result_in(['4', ['5', '1']]).at(3)
50
+ end }
51
+
52
+ it 'applies the rules recursively' do
53
+ matching('1234abc').as(:a).should result_in(['1', ['2', ['3', '4']]]).at(4)
54
+ end
27
55
  end
28
-
29
56
  end
30
-
57
+
31
58
  end
32
59
 
33
- shared_examples_for 'a packrat parser' do
60
+ shared_examples_for 'a generated packrat parser' do
34
61
  include RuntimeParserSpecHelper
35
62
 
36
- it_behaves_like 'a recursive descent parser'
63
+ it_behaves_like 'a generated recursive descent parser'
64
+
65
+ let(:parser_class) do
66
+ Rattler::BackEnd::Compiler.compile_parser described_class, grammar
67
+ end
37
68
 
38
69
  describe '#match' do
39
70
 
40
- it 'memoizes parse results' do
41
- example = given_rules do
71
+ context 'given recursive rules' do
72
+
73
+ let(:grammar) { define_grammar do
42
74
  rule :a do
43
75
  ( match(:b) & match('a') \
44
- | match(:b) & match('b') )
76
+ | match(:b) & eof )
45
77
  end
46
78
  rule :b do
47
- match('b')
79
+ match('b') & disallow(:a)
48
80
  end
49
- end.
50
- parsing('bb').as(:a)
51
- example.parser.should_receive(:match_b).and_return do
52
- example.parser.pos = example.parser.pos + 1
53
- 'b'
81
+ end }
82
+
83
+ let(:example) { matching('bag').as(:a) }
84
+
85
+ it 'memoizes parse results' do
86
+ example.parser.should_receive(:match_b!).
87
+ and_return { example.parser.pos += 1; 'b' }
88
+ example.should result_in(['b', 'a']).at(2)
54
89
  end
55
- example.should result_in(['b', 'b']).at(2)
56
90
  end
57
-
58
91
  end
59
92
 
60
93
  end
@@ -0,0 +1,84 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
2
+ require 'set'
3
+
4
+ describe Rattler::Util::GraphViz::NodeBuilder do
5
+
6
+ describe '#array_like?' do
7
+
8
+ context 'given an array' do
9
+ it 'returns true' do
10
+ subject.array_like?(['a']).should be_true
11
+ end
12
+ end
13
+
14
+ context 'given a hash' do
15
+ it 'returns true' do
16
+ subject.array_like?({:a => 'a'}).should be_true
17
+ end
18
+ end
19
+
20
+ context 'given a string' do
21
+ it 'returns false' do
22
+ subject.array_like?('a').should be_false
23
+ end
24
+ end
25
+ end
26
+
27
+ describe '#record_like?' do
28
+
29
+ context 'given a hash of simple values' do
30
+ it 'returns true' do
31
+ subject.record_like?({:a => 'a', :b => 'b'}).should be_true
32
+ end
33
+ end
34
+
35
+ context 'given a hash with compound values' do
36
+ it 'returns false' do
37
+ subject.record_like?({:a => ['a1', 'a2'], :b => 'b'}).should be_false
38
+ end
39
+ end
40
+
41
+ context 'given an array' do
42
+ it 'returns false' do
43
+ subject.record_like?(['a']).should be_false
44
+ end
45
+ end
46
+ end
47
+
48
+ describe '#each_child_of' do
49
+
50
+ context 'given an array' do
51
+
52
+ let(:object) { ['a', 'b', 'c'] }
53
+
54
+ it 'iterates over the members' do
55
+ children = []
56
+ subject.each_child_of(object) {|_| children << _ }
57
+ children.should == ['a', 'b', 'c']
58
+ end
59
+ end
60
+
61
+ context 'given a hash with compound values' do
62
+
63
+ let(:object) { {:a => ['a1', 'a2'], :b => 'b'} }
64
+
65
+ it 'iterates over the pairs' do
66
+ children = Set[]
67
+ subject.each_child_of(object) {|_| children << _ }
68
+ children.should == Set[[:a, ['a1', 'a2']], [:b, 'b']]
69
+ end
70
+ end
71
+
72
+ context 'given a hash with simple values' do
73
+
74
+ let(:object) { {:a => 'a', :b => 'b'} }
75
+
76
+ it 'does nothing' do
77
+ children = Set[]
78
+ subject.each_child_of(object) {|_| children << _}
79
+ children.should be_empty
80
+ end
81
+ end
82
+ end
83
+
84
+ end
@@ -3,100 +3,100 @@ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
3
3
  include Rattler::Util
4
4
 
5
5
  describe Node do
6
-
6
+
7
7
  describe '#children' do
8
-
8
+
9
9
  context 'when the Node has children' do
10
10
  subject do
11
11
  Node.new(Node.new(:name => 'foo'), Node.new(:name => 'bar'))
12
12
  end
13
-
13
+
14
14
  it 'returns the children' do
15
15
  subject.children.
16
16
  should == [Node.new(:name => 'foo'), Node.new(:name => 'bar')]
17
17
  end
18
18
  end
19
-
19
+
20
20
  context 'when the Node has no children' do
21
-
21
+
22
22
  subject { Node.new }
23
-
23
+
24
24
  it 'returns an empty array' do
25
25
  subject.children.should == []
26
26
  end
27
27
  end
28
-
28
+
29
29
  context 'when the Node has children and attributes' do
30
30
  subject do
31
31
  Node.new(Node.new(:name => 'foo'), Node.new(:name => 'bar'), :foo => 'bar')
32
32
  end
33
-
33
+
34
34
  it 'returns the children' do
35
35
  subject.children.
36
36
  should == [Node.new(:name => 'foo'), Node.new(:name => 'bar')]
37
37
  end
38
38
  end
39
-
39
+
40
40
  context 'when the Node has only attributes' do
41
-
41
+
42
42
  subject { Node.new(:foo => 'bar', :bar => nil) }
43
-
43
+
44
44
  it 'returns an empty array' do
45
45
  subject.children.should == []
46
46
  end
47
47
  end
48
48
  end
49
-
49
+
50
50
  describe '#attrs' do
51
51
  context 'when the Node has attributes' do
52
-
52
+
53
53
  subject { Node.new(:foo => 'bar', :bar => nil) }
54
-
54
+
55
55
  it 'returns a Hash of the attributes' do
56
56
  subject.attrs.should == { :foo => 'bar', :bar => nil }
57
57
  end
58
58
  end
59
-
59
+
60
60
  context 'when the Node has no attributes' do
61
-
61
+
62
62
  subject { Node.new }
63
-
63
+
64
64
  it 'returns an empty Hash' do
65
65
  subject.attrs.should == {}
66
66
  end
67
67
  end
68
-
68
+
69
69
  context 'when the Node has children and attributes' do
70
70
  subject do
71
71
  Node.new(Node.new(:name => 'foo'), :foo => 'bar', :bar => nil)
72
72
  end
73
-
73
+
74
74
  it 'returns a Hash of the attributes' do
75
75
  subject.attrs.should == { :foo => 'bar', :bar => nil }
76
76
  end
77
77
  end
78
-
78
+
79
79
  context 'when the Node has only children' do
80
80
  subject do
81
81
  Node.new(Node.new(:name => 'foo'), Node.new(:name => 'bar'))
82
82
  end
83
-
83
+
84
84
  it 'returns an empty Hash' do
85
85
  subject.attrs.should == {}
86
86
  end
87
87
  end
88
88
  end
89
-
89
+
90
90
  describe '#child' do
91
91
  context 'when the Node has children' do
92
92
  subject do
93
93
  Node.new(Node.new(:name => 'foo'), Node.new(:name => 'bar'))
94
94
  end
95
-
95
+
96
96
  it 'returns the first child' do
97
97
  subject.child.should == Node.new(:name => 'foo')
98
98
  end
99
-
99
+
100
100
  context 'given an index' do
101
101
  it 'returns the child at the index' do
102
102
  subject.child(0).should == Node.new(:name => 'foo')
@@ -104,63 +104,63 @@ describe Node do
104
104
  end
105
105
  end
106
106
  end
107
-
107
+
108
108
  context 'when the Node has no children' do
109
-
109
+
110
110
  subject { Node.new }
111
-
111
+
112
112
  it 'returns nil' do
113
113
  subject.child.should be_nil
114
114
  end
115
115
  end
116
116
  end
117
-
117
+
118
118
  describe '#name' do
119
119
  context 'when the Node as a name attribute' do
120
-
120
+
121
121
  subject { Node.new(:name => 'foo' ) }
122
-
122
+
123
123
  it 'uses the name attribute as name' do
124
124
  subject.name.should == 'foo'
125
125
  end
126
126
  end
127
-
127
+
128
128
  context 'when the Node has no name attribute' do
129
-
129
+
130
130
  subject { Node.new }
131
-
131
+
132
132
  it 'uses the class name as name' do
133
133
  subject.name.should == 'Rattler::Util::Node'
134
134
  end
135
135
  end
136
136
  end
137
-
137
+
138
138
  describe '#[]' do
139
139
  context 'when the Node has children' do
140
140
  subject do
141
141
  Node.new(Node.new(:name => 'foo'), Node.new(:name => 'bar'))
142
142
  end
143
-
143
+
144
144
  context 'given a single index' do
145
145
  it 'returns the child at the index' do
146
146
  subject[0].should == Node.new(:name => 'foo')
147
147
  subject[1].should == Node.new(:name => 'bar')
148
148
  end
149
149
  end
150
-
150
+
151
151
  context 'given a negative index' do
152
152
  it 'counts backward from the last child' do
153
153
  subject[-1].should == Node.new(:name => 'bar')
154
154
  subject[-2].should == Node.new(:name => 'foo')
155
155
  end
156
156
  end
157
-
157
+
158
158
  context 'given an out-of-range index' do
159
159
  it 'returns nil' do
160
160
  subject[3].should be_nil
161
161
  end
162
162
  end
163
-
163
+
164
164
  context 'given an index and a length' do
165
165
  it 'returns an array of children starting at the index' do
166
166
  subject[0, 2].should == [Node.new(:name => 'foo'), Node.new(:name => 'bar')]
@@ -168,7 +168,7 @@ describe Node do
168
168
  subject[1, 1].should == [Node.new(:name => 'bar')]
169
169
  end
170
170
  end
171
-
171
+
172
172
  context 'given a negative index and a length' do
173
173
  it 'counts backward from the last child' do
174
174
  subject[-2, 2].should == [Node.new(:name => 'foo'), Node.new(:name => 'bar')]
@@ -176,19 +176,19 @@ describe Node do
176
176
  subject[-1, 1].should == [Node.new(:name => 'bar')]
177
177
  end
178
178
  end
179
-
179
+
180
180
  context 'given an out-of-range index and a length' do
181
181
  it 'returns nil' do
182
182
  subject[3].should be_nil
183
183
  end
184
184
  end
185
-
185
+
186
186
  context 'given an index exactly one past the last child and a length' do
187
187
  it 'returns an empty array' do
188
188
  subject[2, 2].should == []
189
189
  end
190
190
  end
191
-
191
+
192
192
  context 'given a range' do
193
193
  it 'return an array of children indexed by the range' do
194
194
  subject[0..1].should == [Node.new(:name => 'foo'), Node.new(:name => 'bar')]
@@ -196,7 +196,7 @@ describe Node do
196
196
  subject[1..1].should == [Node.new(:name => 'bar')]
197
197
  end
198
198
  end
199
-
199
+
200
200
  context 'given a range with a negative index' do
201
201
  it 'counts backward from the last child' do
202
202
  subject[-2..-1].should == [Node.new(:name => 'foo'), Node.new(:name => 'bar')]
@@ -204,13 +204,13 @@ describe Node do
204
204
  subject[-1..-1].should == [Node.new(:name => 'bar')]
205
205
  end
206
206
  end
207
-
207
+
208
208
  context 'given a range with an out-of-range index' do
209
209
  it 'returns nil' do
210
210
  subject[3..3].should be_nil
211
211
  end
212
212
  end
213
-
213
+
214
214
  context 'given a range starting exactly one past the last child' do
215
215
  it 'returns an empty array' do
216
216
  subject[2..2].should == []
@@ -218,101 +218,128 @@ describe Node do
218
218
  end
219
219
  end
220
220
  end
221
-
221
+
222
222
  describe '#count' do
223
223
  context 'when the Node has children' do
224
224
  subject do
225
225
  Node.new(Node.new(:name => 'foo'), Node.new(:name => 'bar'))
226
226
  end
227
-
227
+
228
228
  it 'returns the number of children' do
229
229
  subject.count.should == 2
230
230
  end
231
231
  end
232
-
232
+
233
233
  context 'when the Node has no children' do
234
234
  subject { Node.new }
235
-
235
+
236
236
  it 'returns 0' do
237
237
  subject.count.should == 0
238
238
  end
239
239
  end
240
240
  end
241
-
241
+
242
242
  describe '#==' do
243
243
  subject { Node.new(Node.new(:name => 'foo'), :foo => 'bar') }
244
-
244
+
245
245
  context 'given a node with the same children and attributes' do
246
246
  it 'returns true' do
247
247
  subject.should == Node.new(Node.new(:name => 'foo'), :foo => 'bar')
248
248
  end
249
249
  end
250
-
250
+
251
251
  context 'given a node with different children' do
252
252
  it 'returns false' do
253
253
  subject.should_not == Node.new(Node.new(:name => 'boo'), :foo => 'bar')
254
254
  end
255
255
  end
256
-
256
+
257
257
  context 'given a node with extra children' do
258
258
  it 'returns false' do
259
259
  subject.should_not ==
260
260
  Node.new(Node.new(:name => 'boo'), Node.new, :foo => 'bar')
261
261
  end
262
262
  end
263
-
263
+
264
264
  context 'given a node with no children' do
265
265
  it 'returns false' do
266
266
  subject.should_not == Node.new(:foo => 'bar')
267
267
  end
268
268
  end
269
-
269
+
270
270
  context 'given a node with the same children and different attributes' do
271
271
  it 'returns false' do
272
272
  subject.should_not == Node.new(Node.new(:name => 'foo'), :foo => 'baz')
273
273
  end
274
274
  end
275
-
275
+
276
276
  context 'given a node with the same children and extra attributes' do
277
277
  it 'returns false' do
278
278
  subject.should_not ==
279
279
  Node.new(Node.new(:name => 'foo'), :foo => 'bar', :bar => 'baz')
280
280
  end
281
281
  end
282
-
282
+
283
283
  context 'given a node with the same children but no attributes' do
284
284
  it 'returns false' do
285
285
  subject.should_not == Node.new(Node.new(:name => 'foo'))
286
286
  end
287
287
  end
288
288
  end
289
-
289
+
290
+ describe '#each' do
291
+
292
+ subject { Node.new(Node.new(:name => 'foo'), Node.new(:name => 'bar')) }
293
+
294
+ let(:expected_items) do
295
+ [Node.new(:name => 'foo'), Node.new(:name => 'bar')]
296
+ end
297
+
298
+ it 'iterates over #children' do
299
+ subject.each {|_| _.should == expected_items.shift }
300
+ end
301
+ end
302
+
303
+ describe '#with_children' do
304
+
305
+ subject { Node.new(Node.new(:name => 'foo'), Node.new(:name => 'bar')) }
306
+
307
+ let :new_children do
308
+ [Node.new(:name => 'boo'), Node.new(:name => 'baz')]
309
+ end
310
+
311
+ it 'returns a new node with the children replaced' do
312
+ subject.with_children(new_children).should ==
313
+ Node.new(Node.new(:name => 'boo'), Node.new(:name => 'baz'))
314
+ end
315
+ end
316
+
290
317
  describe '#method_missing' do
291
-
318
+
292
319
  subject { Node.new(:foo => 'bar', :bar => nil) }
293
-
320
+
294
321
  it 'provides accessor methods for attributes' do
295
322
  subject.foo.should == 'bar'
296
323
  subject.bar.should == nil
297
324
  end
298
325
  end
299
-
326
+
300
327
  describe '#respond_to?' do
301
-
328
+
302
329
  subject { Node.new(:foo => 'bar', :bar => nil) }
303
-
330
+
304
331
  context 'given an attribute name' do
305
332
  it 'returns true' do
306
333
  subject.should respond_to(:foo)
307
334
  subject.should respond_to(:bar)
308
335
  end
309
336
  end
310
-
337
+
311
338
  context 'given a meaningless name' do
312
339
  it 'returns false' do
313
340
  subject.should_not respond_to(:fred)
314
341
  end
315
342
  end
316
343
  end
317
-
344
+
318
345
  end