rattler 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +57 -37
- data/features/command_line/dest_option.feature +8 -21
- data/features/command_line/lib_option.feature +37 -0
- data/features/command_line/parser_generator.feature +7 -4
- data/features/grammar/back_reference.feature +37 -0
- data/features/grammar/fail.feature +3 -3
- data/features/grammar/labels.feature +11 -3
- data/features/grammar/list_matching.feature +14 -5
- data/features/grammar/literal.feature +30 -4
- data/features/grammar/nonterminal.feature +1 -1
- data/features/grammar/ordered_choice.feature +2 -2
- data/features/grammar/skip_operator.feature +1 -1
- data/features/grammar/symantic_action.feature +7 -7
- data/features/grammar/whitespace.feature +2 -2
- data/features/step_definitions/grammar_steps.rb +2 -2
- data/lib/rattler/back_end.rb +1 -0
- data/lib/rattler/back_end/compiler.rb +19 -20
- data/lib/rattler/back_end/optimizer.rb +100 -0
- data/lib/rattler/back_end/optimizer/composite_reducing.rb +18 -0
- data/lib/rattler/back_end/optimizer/flatten_choice.rb +31 -0
- data/lib/rattler/back_end/optimizer/flatten_sequence.rb +59 -0
- data/lib/rattler/back_end/optimizer/flattening.rb +17 -0
- data/lib/rattler/back_end/optimizer/inline_regular_rules.rb +46 -0
- data/lib/rattler/back_end/optimizer/join_match_capturing_sequence.rb +71 -0
- data/lib/rattler/back_end/optimizer/join_match_choice.rb +37 -0
- data/lib/rattler/back_end/optimizer/join_match_matching_sequence.rb +38 -0
- data/lib/rattler/back_end/optimizer/join_match_sequence.rb +17 -0
- data/lib/rattler/back_end/optimizer/join_predicate_bare_match.rb +68 -0
- data/lib/rattler/back_end/optimizer/join_predicate_match.rb +17 -0
- data/lib/rattler/back_end/optimizer/join_predicate_nested_match.rb +37 -0
- data/lib/rattler/back_end/optimizer/join_predicate_or_bare_match.rb +68 -0
- data/lib/rattler/back_end/optimizer/join_predicate_or_match.rb +17 -0
- data/lib/rattler/back_end/optimizer/join_predicate_or_nested_match.rb +36 -0
- data/lib/rattler/back_end/optimizer/match_joining.rb +60 -0
- data/lib/rattler/back_end/optimizer/optimization.rb +94 -0
- data/lib/rattler/back_end/optimizer/optimization_context.rb +72 -0
- data/lib/rattler/back_end/optimizer/optimization_sequence.rb +37 -0
- data/lib/rattler/back_end/optimizer/optimize_children.rb +46 -0
- data/lib/rattler/back_end/optimizer/reduce_repeat_match.rb +44 -0
- data/lib/rattler/back_end/optimizer/remove_meaningless_wrapper.rb +32 -0
- data/lib/rattler/back_end/optimizer/simplify_redundant_repeat.rb +43 -0
- data/lib/rattler/back_end/optimizer/simplify_token_match.rb +38 -0
- data/lib/rattler/back_end/parser_generator.rb +21 -14
- data/lib/rattler/back_end/parser_generator/apply_generator.rb +35 -35
- data/lib/rattler/back_end/parser_generator/assert_generator.rb +29 -30
- data/lib/rattler/back_end/parser_generator/back_reference_generator.rb +93 -0
- data/lib/rattler/back_end/parser_generator/choice_generator.rb +33 -49
- data/lib/rattler/back_end/parser_generator/direct_action_generator.rb +14 -14
- data/lib/rattler/back_end/parser_generator/disallow_generator.rb +29 -30
- data/lib/rattler/back_end/parser_generator/dispatch_action_generator.rb +11 -13
- data/lib/rattler/back_end/parser_generator/expr_generator.rb +36 -56
- data/lib/rattler/back_end/parser_generator/fail_generator.rb +18 -18
- data/lib/rattler/back_end/parser_generator/group_match.rb +18 -0
- data/lib/rattler/back_end/parser_generator/group_match_generator.rb +76 -0
- data/lib/rattler/back_end/parser_generator/label_generator.rb +25 -6
- data/lib/rattler/back_end/parser_generator/list1_generator.rb +7 -7
- data/lib/rattler/back_end/parser_generator/list_generating.rb +19 -20
- data/lib/rattler/back_end/parser_generator/list_generator.rb +5 -5
- data/lib/rattler/back_end/parser_generator/match_generator.rb +52 -52
- data/lib/rattler/back_end/parser_generator/one_or_more_generator.rb +6 -6
- data/lib/rattler/back_end/parser_generator/optional_generator.rb +30 -29
- data/lib/rattler/back_end/parser_generator/predicate_propogating.rb +8 -8
- data/lib/rattler/back_end/parser_generator/repeat_generating.rb +23 -25
- data/lib/rattler/back_end/parser_generator/rule_generator.rb +27 -79
- data/lib/rattler/back_end/parser_generator/rule_set_generator.rb +102 -0
- data/lib/rattler/back_end/parser_generator/sequence_generator.rb +49 -41
- data/lib/rattler/back_end/parser_generator/skip_generator.rb +14 -20
- data/lib/rattler/back_end/parser_generator/skip_propogating.rb +4 -4
- data/lib/rattler/back_end/parser_generator/sub_generating.rb +6 -0
- data/lib/rattler/back_end/parser_generator/token_generator.rb +12 -12
- data/lib/rattler/back_end/parser_generator/token_propogating.rb +2 -2
- data/lib/rattler/back_end/parser_generator/zero_or_more_generator.rb +4 -4
- data/lib/rattler/grammar.rb +4 -3
- data/lib/rattler/grammar/analysis.rb +91 -0
- data/lib/rattler/grammar/grammar.rb +37 -25
- data/lib/rattler/grammar/grammar_parser.rb +19 -11
- data/lib/rattler/grammar/metagrammar.rb +569 -800
- data/lib/rattler/grammar/rattler.rtlr +162 -144
- data/lib/rattler/parsers.rb +5 -1
- data/lib/rattler/parsers/action_code.rb +29 -15
- data/lib/rattler/parsers/apply.rb +5 -5
- data/lib/rattler/parsers/assert.rb +4 -18
- data/lib/rattler/parsers/back_reference.rb +46 -0
- data/lib/rattler/parsers/choice.rb +6 -39
- data/lib/rattler/parsers/combinator_parser.rb +32 -0
- data/lib/rattler/parsers/combining.rb +3 -29
- data/lib/rattler/parsers/direct_action.rb +27 -30
- data/lib/rattler/parsers/disallow.rb +4 -18
- data/lib/rattler/parsers/dispatch_action.rb +30 -25
- data/lib/rattler/parsers/label.rb +9 -18
- data/lib/rattler/parsers/list.rb +3 -34
- data/lib/rattler/parsers/list1.rb +4 -36
- data/lib/rattler/parsers/list_parser.rb +64 -0
- data/lib/rattler/parsers/match.rb +7 -42
- data/lib/rattler/parsers/node_code.rb +44 -0
- data/lib/rattler/parsers/one_or_more.rb +7 -27
- data/lib/rattler/parsers/optional.rb +5 -25
- data/lib/rattler/parsers/parser.rb +16 -44
- data/lib/rattler/parsers/parser_dsl.rb +13 -3
- data/lib/rattler/parsers/predicate.rb +4 -12
- data/lib/rattler/parsers/rule.rb +18 -19
- data/lib/rattler/parsers/rule_set.rb +63 -0
- data/lib/rattler/parsers/sequence.rb +12 -46
- data/lib/rattler/parsers/skip.rb +12 -26
- data/lib/rattler/parsers/token.rb +6 -21
- data/lib/rattler/parsers/zero_or_more.rb +6 -26
- data/lib/rattler/runner.rb +66 -28
- data/lib/rattler/runtime/extended_packrat_parser.rb +26 -20
- data/lib/rattler/runtime/packrat_parser.rb +17 -21
- data/lib/rattler/runtime/parser.rb +12 -2
- data/lib/rattler/runtime/recursive_descent_parser.rb +3 -11
- data/lib/rattler/util.rb +2 -1
- data/lib/rattler/util/graphviz.rb +29 -0
- data/lib/rattler/util/graphviz/digraph_builder.rb +71 -0
- data/lib/rattler/util/graphviz/node_builder.rb +84 -0
- data/lib/rattler/util/node.rb +37 -19
- data/lib/rattler/util/parser_spec_helper.rb +61 -35
- data/spec/rattler/back_end/compiler_spec.rb +6 -860
- data/spec/rattler/back_end/optimizer/flatten_choice_spec.rb +70 -0
- data/spec/rattler/back_end/optimizer/flatten_sequence_spec.rb +130 -0
- data/spec/rattler/back_end/optimizer/inline_regular_rules_spec.rb +80 -0
- data/spec/rattler/back_end/optimizer/join_match_capturing_sequence_spec.rb +241 -0
- data/spec/rattler/back_end/optimizer/join_match_choice_spec.rb +100 -0
- data/spec/rattler/back_end/optimizer/join_match_matching_sequence_spec.rb +112 -0
- data/spec/rattler/back_end/optimizer/join_predicate_bare_match_spec.rb +194 -0
- data/spec/rattler/back_end/optimizer/join_predicate_nested_match_spec.rb +180 -0
- data/spec/rattler/back_end/optimizer/join_predicate_or_bare_match_spec.rb +153 -0
- data/spec/rattler/back_end/optimizer/join_predicate_or_nested_match_spec.rb +153 -0
- data/spec/rattler/back_end/optimizer/reduce_repeat_match_spec.rb +98 -0
- data/spec/rattler/back_end/optimizer/simplify_redundant_repeat_spec.rb +226 -0
- data/spec/rattler/back_end/optimizer/simplify_token_match_spec.rb +85 -0
- data/spec/rattler/back_end/parser_generator/apply_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/assert_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/back_reference_generator_spec.rb +181 -0
- data/spec/rattler/back_end/parser_generator/choice_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/direct_action_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/disallow_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/dispatch_action_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/group_match_generator_spec.rb +185 -0
- data/spec/rattler/back_end/parser_generator/label_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/list1_generator_spec.rb +10 -5
- data/spec/rattler/back_end/parser_generator/list_generator_spec.rb +10 -5
- data/spec/rattler/back_end/parser_generator/match_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/one_or_more_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/optional_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/rule_generator_spec.rb +13 -46
- data/spec/rattler/back_end/parser_generator/rule_set_generator_spec.rb +97 -0
- data/spec/rattler/back_end/parser_generator/sequence_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/skip_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/token_generator_spec.rb +38 -33
- data/spec/rattler/back_end/parser_generator/zero_or_more_generator_spec.rb +39 -34
- data/spec/rattler/back_end/shared_compiler_examples.rb +885 -0
- data/spec/rattler/grammar/analysis_spec.rb +167 -0
- data/spec/rattler/grammar/grammar_parser_spec.rb +169 -179
- data/spec/rattler/grammar/grammar_spec.rb +24 -21
- data/spec/rattler/parsers/action_code_spec.rb +64 -19
- data/spec/rattler/parsers/apply_spec.rb +9 -9
- data/spec/rattler/parsers/back_reference_spec.rb +38 -0
- data/spec/rattler/parsers/combinator_parser_spec.rb +14 -0
- data/spec/rattler/parsers/direct_action_spec.rb +16 -2
- data/spec/rattler/parsers/dispatch_action_spec.rb +15 -32
- data/spec/rattler/parsers/fail_spec.rb +6 -4
- data/spec/rattler/parsers/label_spec.rb +10 -28
- data/spec/rattler/parsers/node_code_spec.rb +48 -0
- data/spec/rattler/parsers/parser_dsl_spec.rb +1 -1
- data/spec/rattler/parsers/rule_set_spec.rb +35 -0
- data/spec/rattler/parsers/sequence_spec.rb +15 -24
- data/spec/rattler/runtime/extended_packrat_parser_spec.rb +22 -17
- data/spec/rattler/runtime/packrat_parser_spec.rb +1 -1
- data/spec/rattler/runtime/parse_node_spec.rb +15 -19
- data/spec/rattler/runtime/recursive_descent_parser_spec.rb +1 -1
- data/spec/rattler/runtime/shared_parser_examples.rb +61 -28
- data/spec/rattler/util/graphviz/node_builder_spec.rb +84 -0
- data/spec/rattler/util/node_spec.rb +92 -65
- data/spec/rattler_spec.rb +16 -16
- data/spec/support/combinator_parser_spec_helper.rb +19 -18
- data/spec/support/compiler_spec_helper.rb +56 -87
- data/spec/support/runtime_parser_spec_helper.rb +6 -14
- metadata +117 -22
- data/features/grammar/regex.feature +0 -24
- data/lib/rattler/parsers/match_joining.rb +0 -67
- 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
|
-
|
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
|
-
|
15
|
-
|
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
|
-
|
20
|
-
|
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
|
-
|
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
|
-
|
41
|
-
|
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) &
|
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
|
-
|
51
|
-
example.
|
52
|
-
|
53
|
-
|
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
|