treetop 1.4.5 → 1.4.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/README.md +44 -20
  2. data/lib/treetop/compiler/metagrammar.rb +126 -33
  3. data/lib/treetop/compiler/metagrammar.treetop +46 -42
  4. data/lib/treetop/compiler/node_classes/repetition.rb +39 -5
  5. data/lib/treetop/version.rb +1 -1
  6. data/spec/compiler/and_predicate_spec.rb +36 -0
  7. data/spec/compiler/anything_symbol_spec.rb +44 -0
  8. data/spec/compiler/character_class_spec.rb +276 -0
  9. data/spec/compiler/choice_spec.rb +80 -0
  10. data/spec/compiler/circular_compilation_spec.rb +30 -0
  11. data/spec/compiler/failure_propagation_functional_spec.rb +21 -0
  12. data/spec/compiler/grammar_compiler_spec.rb +91 -0
  13. data/spec/compiler/grammar_spec.rb +41 -0
  14. data/spec/compiler/multibyte_chars_spec.rb +38 -0
  15. data/spec/compiler/nonterminal_symbol_spec.rb +40 -0
  16. data/spec/compiler/not_predicate_spec.rb +38 -0
  17. data/spec/compiler/occurrence_range_spec.rb +191 -0
  18. data/spec/compiler/one_or_more_spec.rb +35 -0
  19. data/spec/compiler/optional_spec.rb +37 -0
  20. data/spec/compiler/parenthesized_expression_spec.rb +19 -0
  21. data/spec/compiler/parsing_rule_spec.rb +61 -0
  22. data/spec/compiler/repeated_subrule_spec.rb +29 -0
  23. data/spec/compiler/semantic_predicate_spec.rb +175 -0
  24. data/spec/compiler/sequence_spec.rb +115 -0
  25. data/spec/compiler/terminal_spec.rb +81 -0
  26. data/spec/compiler/terminal_symbol_spec.rb +37 -0
  27. data/spec/compiler/test_grammar.treetop +7 -0
  28. data/spec/compiler/test_grammar.tt +7 -0
  29. data/spec/compiler/test_grammar_do.treetop +7 -0
  30. data/spec/compiler/tt_compiler_spec.rb +215 -0
  31. data/spec/compiler/zero_or_more_spec.rb +56 -0
  32. data/spec/composition/a.treetop +11 -0
  33. data/spec/composition/b.treetop +11 -0
  34. data/spec/composition/c.treetop +10 -0
  35. data/spec/composition/d.treetop +10 -0
  36. data/spec/composition/f.treetop +17 -0
  37. data/spec/composition/grammar_composition_spec.rb +40 -0
  38. data/spec/composition/subfolder/e_includes_c.treetop +15 -0
  39. data/spec/ruby_extensions/string_spec.rb +32 -0
  40. data/spec/runtime/compiled_parser_spec.rb +101 -0
  41. data/spec/runtime/interval_skip_list/delete_spec.rb +147 -0
  42. data/spec/runtime/interval_skip_list/expire_range_spec.rb +349 -0
  43. data/spec/runtime/interval_skip_list/insert_and_delete_node.rb +385 -0
  44. data/spec/runtime/interval_skip_list/insert_spec.rb +660 -0
  45. data/spec/runtime/interval_skip_list/interval_skip_list_spec.graffle +6175 -0
  46. data/spec/runtime/interval_skip_list/interval_skip_list_spec.rb +58 -0
  47. data/spec/runtime/interval_skip_list/palindromic_fixture.rb +23 -0
  48. data/spec/runtime/interval_skip_list/palindromic_fixture_spec.rb +163 -0
  49. data/spec/runtime/interval_skip_list/spec_helper.rb +84 -0
  50. data/spec/runtime/syntax_node_spec.rb +77 -0
  51. data/spec/spec_helper.rb +110 -0
  52. data/treetop.gemspec +18 -0
  53. metadata +70 -9
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ module OneOrMoreSpec
4
+ class Foo < Treetop::Runtime::SyntaxNode
5
+ end
6
+
7
+ describe "one or more of a terminal symbol followed by a node class declaration and a block" do
8
+ testing_expression '"foo"+ <OneOrMoreSpec::Foo> { def a_method; end }'
9
+
10
+ it "fails to parse epsilon, reporting a failure" do
11
+ parse('') do |result|
12
+ result.should be_nil
13
+ terminal_failures = parser.terminal_failures
14
+ terminal_failures.size.should == 1
15
+ failure = terminal_failures.first
16
+ failure.index.should == 0
17
+ failure.expected_string.should == 'foo'
18
+ end
19
+ end
20
+
21
+ it "successfully parses two of that terminal in a row, returning an instance of the declared node class and reporting the failure the third parsing attempt" do
22
+ parse("foofoo") do |result|
23
+ result.should_not be_nil
24
+ result.should be_an_instance_of(Foo)
25
+ result.should respond_to(:a_method)
26
+
27
+ terminal_failures = parser.terminal_failures
28
+ terminal_failures.size.should == 1
29
+ failure = terminal_failures.first
30
+ failure.index.should == 6
31
+ failure.expected_string.should == 'foo'
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ module OptionalSpec
4
+ describe "An optional terminal symbol" do
5
+ testing_expression '"foo"?'
6
+
7
+ it "parses input matching the terminal" do
8
+ parse('foo').should_not be_nil
9
+ end
10
+
11
+ it "parses epsilon, recording a failure" do
12
+ parse('') do |result|
13
+ result.should_not be_nil
14
+ result.interval.should == (0...0)
15
+
16
+ terminal_failures = parser.terminal_failures
17
+ terminal_failures.size.should == 1
18
+ failure = terminal_failures.first
19
+ failure.index.should == 0
20
+ failure.expected_string.should == 'foo'
21
+ end
22
+ end
23
+
24
+ it "parses input not matching the terminal, returning an epsilon result and recording a failure" do
25
+ parse('bar', :consume_all_input => false) do |result|
26
+ result.should_not be_nil
27
+ result.interval.should == (0...0)
28
+
29
+ terminal_failures = parser.terminal_failures
30
+ terminal_failures.size.should == 1
31
+ failure = terminal_failures.first
32
+ failure.index.should == 0
33
+ failure.expected_string.should == 'foo'
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ module ParenthesizedExpressionSpec
4
+ describe "An unadorned expression inside of parentheses" do
5
+ testing_expression '("foo")'
6
+
7
+ it "should behave as normal" do
8
+ parse('foo').should_not be_nil
9
+ end
10
+ end
11
+
12
+ describe "A prefixed-expression inside of parentheses" do
13
+ testing_expression '(!"foo")'
14
+
15
+ it "should behave as normal" do
16
+ parse('foo').should be_nil
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ module ParsingRuleSpec
4
+ describe "a grammar with one parsing rule" do
5
+
6
+ testing_grammar %{
7
+ grammar Foo
8
+ rule bar
9
+ "baz"
10
+ end
11
+ end
12
+ }
13
+
14
+ it "stores and retrieves nodes in its node cache" do
15
+ parser = self.class.const_get(:FooParser).new
16
+ parser.send(:prepare_to_parse, 'baz')
17
+ node_cache = parser.send(:node_cache)
18
+
19
+ node_cache[:bar][0].should be_nil
20
+
21
+ parser._nt_bar
22
+
23
+ cached_node = node_cache[:bar][0]
24
+ cached_node.should be_an_instance_of(Runtime::SyntaxNode)
25
+ cached_node.text_value.should == 'baz'
26
+
27
+ parser.instance_eval { @index = 0 }
28
+ parser._nt_bar.should equal(cached_node)
29
+ parser.index.should == cached_node.interval.end
30
+ end
31
+ end
32
+
33
+
34
+ describe "a grammar with choice that uses the cache and has a subsequent expression" do
35
+ testing_grammar %{
36
+ grammar Logic
37
+ rule expression
38
+ value_plus
39
+ /
40
+ value
41
+ end
42
+
43
+ rule value_plus
44
+ value "something else"
45
+ end
46
+
47
+ rule value
48
+ [a-z]
49
+ /
50
+ "foobar" # the subsequent expression that needs cached.interval.end
51
+ end
52
+ end
53
+ }
54
+
55
+ it "parses a single-character value and generates a node from the cache" do
56
+ result = parse('a')
57
+ result.should be_a(Treetop::Runtime::SyntaxNode)
58
+ result.elements.should be_nil
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ module RepeatedSubruleSpec
4
+ describe "a repeated subrule" do
5
+ testing_grammar %{
6
+ grammar Foo
7
+ rule foo
8
+ a:'a' space b:'b' space 'c'
9
+ end
10
+
11
+ rule space
12
+ ' '
13
+ end
14
+ end
15
+ }
16
+
17
+ it "should produce a parser having sequence-numbered node accessor methods" do
18
+ parse("a b c") do |result|
19
+ result.should_not be_nil
20
+ result.should respond_to(:space1)
21
+ result.should respond_to(:space2)
22
+ result.should_not respond_to(:space)
23
+ result.should respond_to(:a)
24
+ result.should respond_to(:b)
25
+ result.should_not respond_to(:c)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,175 @@
1
+ require 'spec_helper'
2
+
3
+ module SemanticPredicateSpec
4
+ describe "An &-predicate block" do
5
+ testing_expression '& {|| $ok_to_succeed}'
6
+
7
+ it "succeeds if it returns true, returning an epsilon syntax node" do
8
+ $ok_to_succeed = true
9
+ parse('foo', :consume_all_input => false) do |result|
10
+ result.should_not be_nil
11
+ result.interval.should == (0...0)
12
+ end
13
+ end
14
+
15
+ it "fails if it returns false" do
16
+ $ok_to_succeed = false
17
+ parse('foo', :consume_all_input => false) do |result|
18
+ result.should be_nil
19
+ terminal_failures = parser.terminal_failures
20
+ terminal_failures.size.should == 0
21
+ end
22
+ end
23
+
24
+ end
25
+
26
+ describe "A sequence of a terminal and an &-predicate block" do
27
+ testing_expression '"prior " &{|s| $value = s[0].text_value; $ok_to_succeed }'
28
+
29
+ it "matches the input terminal and consumes it if the block returns true, seeing the terminal in the sequence" do
30
+ $ok_to_succeed = true
31
+ $value = nil
32
+ parse('prior foo', :consume_all_input => false) do |result|
33
+ result.should_not be_nil
34
+ result.elements[0].text_value.should == "prior "
35
+ result.text_value.should == 'prior '
36
+ $value.should == 'prior '
37
+ end
38
+ end
39
+
40
+ it "fails if the block returns false, but sees the terminal in the sequence" do
41
+ $ok_to_succeed = false
42
+ $value = nil
43
+ parse('prior foo', :consume_all_input => false) do |result|
44
+ result.should be_nil
45
+ $value.should == 'prior '
46
+ terminal_failures = parser.terminal_failures
47
+ terminal_failures.size.should == 0
48
+ end
49
+ end
50
+
51
+ end
52
+
53
+ describe "A sequence of an optional terminal and an &-predicate block" do
54
+ testing_expression '"prior "? &{|s| $value = s[0].text_value; $ok_to_succeed}'
55
+
56
+ it "matches the input terminal and consumes it if the block returns true" do
57
+ $ok_to_succeed = true
58
+ parse('prior foo', :consume_all_input => false) do |result|
59
+ result.should_not be_nil
60
+ result.elements[0].text_value.should == "prior "
61
+ result.text_value.should == 'prior '
62
+ $value.should == 'prior '
63
+ end
64
+ end
65
+
66
+ it "fails with no terminal_failures if the block returns false" do
67
+ $ok_to_succeed = false
68
+ parse('prior foo', :consume_all_input => false) do |result|
69
+ result.should be_nil
70
+ $value.should == 'prior '
71
+ terminal_failures = parser.terminal_failures
72
+ terminal_failures.size.should == 0
73
+ end
74
+ end
75
+
76
+ it "fail and return the expected optional preceeding terminal as expected input if the block returns false" do
77
+ $ok_to_succeed = false
78
+ parse('foo', :consume_all_input => false) do |result|
79
+ result.should be_nil
80
+ terminal_failures = parser.terminal_failures
81
+ terminal_failures.size.should == 1
82
+ failure = terminal_failures[0]
83
+ failure.index.should == 0
84
+ failure.expected_string.should == 'prior '
85
+ end
86
+ end
87
+
88
+ end
89
+
90
+ describe "A !-predicate block" do
91
+ testing_expression '! {|| $ok_to_succeed}'
92
+
93
+ it "succeeds if it returns false, returning an epsilon syntax node" do
94
+ $ok_to_succeed = false
95
+ parse('foo', :consume_all_input => false) do |result|
96
+ result.should_not be_nil
97
+ result.interval.should == (0...0)
98
+ end
99
+ end
100
+
101
+ it "fails if it returns true" do
102
+ $ok_to_succeed = true
103
+ parse('foo', :consume_all_input => false) do |result|
104
+ result.should be_nil
105
+ terminal_failures = parser.terminal_failures
106
+ terminal_failures.size.should == 0
107
+ end
108
+ end
109
+
110
+ end
111
+
112
+ describe "A sequence of a terminal and an !-predicate block" do
113
+ testing_expression '"prior " !{|s| $value = s[0].text_value; $ok_to_succeed }'
114
+
115
+ it "matches the input terminal and consumes it if the block returns false, seeing the terminal in the sequence" do
116
+ $ok_to_succeed = false
117
+ $value = nil
118
+ parse('prior foo', :consume_all_input => false) do |result|
119
+ result.should_not be_nil
120
+ result.elements[0].text_value.should == "prior "
121
+ result.text_value.should == 'prior '
122
+ $value.should == 'prior '
123
+ end
124
+ end
125
+
126
+ it "fails if the block returns true, but sees the terminal in the sequence" do
127
+ $ok_to_succeed = true
128
+ $value = nil
129
+ parse('prior foo', :consume_all_input => false) do |result|
130
+ result.should be_nil
131
+ $value.should == 'prior '
132
+ terminal_failures = parser.terminal_failures
133
+ terminal_failures.size.should == 0
134
+ end
135
+ end
136
+
137
+ end
138
+
139
+ describe "A sequence of an optional terminal and an !-predicate block" do
140
+ testing_expression '"prior "? !{|s| $value = s[0].text_value; $ok_to_succeed}'
141
+
142
+ it "matches the input terminal and consumes it if the block returns false" do
143
+ $ok_to_succeed = false
144
+ parse('prior foo', :consume_all_input => false) do |result|
145
+ result.should_not be_nil
146
+ result.elements[0].text_value.should == "prior "
147
+ result.text_value.should == 'prior '
148
+ $value.should == 'prior '
149
+ end
150
+ end
151
+
152
+ it "fails with no terminal_failures if the block returns true" do
153
+ $ok_to_succeed = true
154
+ parse('prior foo', :consume_all_input => false) do |result|
155
+ result.should be_nil
156
+ $value.should == 'prior '
157
+ terminal_failures = parser.terminal_failures
158
+ terminal_failures.size.should == 0
159
+ end
160
+ end
161
+
162
+ it "fail and return the expected optional preceeding terminal as expected input if the block returns true" do
163
+ $ok_to_succeed = true
164
+ parse('foo', :consume_all_input => false) do |result|
165
+ result.should be_nil
166
+ terminal_failures = parser.terminal_failures
167
+ terminal_failures.size.should == 1
168
+ failure = terminal_failures[0]
169
+ failure.index.should == 0
170
+ failure.expected_string.should == 'prior '
171
+ end
172
+ end
173
+
174
+ end
175
+ end
@@ -0,0 +1,115 @@
1
+ require 'spec_helper'
2
+
3
+ module SequenceSpec
4
+ class Foo < Treetop::Runtime::SyntaxNode
5
+ end
6
+
7
+ describe "a sequence of labeled terminal symbols followed by a node class declaration and a block" do
8
+ testing_expression 'foo:"foo" bar:"bar" baz:"baz" <SequenceSpec::Foo> { def a_method; end }'
9
+
10
+ it "upon successfully matching input, instantiates an instance of the declared node class with element accessor methods and the method from the inline module" do
11
+ parse('foobarbaz') do |result|
12
+ result.should_not be_nil
13
+ result.should be_an_instance_of(Foo)
14
+ result.should respond_to(:a_method)
15
+ result.foo.text_value.should == 'foo'
16
+ result.bar.text_value.should == 'bar'
17
+ result.baz.text_value.should == 'baz'
18
+ end
19
+ end
20
+
21
+ it "successfully matches at a non-zero index" do
22
+ parse('---foobarbaz', :index => 3) do |result|
23
+ result.should_not be_nil
24
+ result.should be_nonterminal
25
+ (result.elements.map {|elt| elt.text_value}).join.should == 'foobarbaz'
26
+ end
27
+ end
28
+
29
+ it "fails to match non-matching input, recording the parse failure of first non-matching terminal" do
30
+ parse('---foobazbaz', :index => 3) do |result|
31
+ result.should be_nil
32
+ parser.index.should == 3
33
+ terminal_failures = parser.terminal_failures
34
+ terminal_failures.size.should == 1
35
+ failure = terminal_failures.first
36
+ failure.index.should == 6
37
+ failure.expected_string.should == 'bar'
38
+ end
39
+ end
40
+ end
41
+
42
+ module ModFoo
43
+ def mod_method; end
44
+ end
45
+
46
+ describe "a sequence of labeled terminal symbols followed by a node module declaration and a block" do
47
+ testing_expression 'foo:"foo" bar:"bar" baz:"baz" <SequenceSpec::ModFoo> { def a_method; end }'
48
+
49
+ it "upon successfully matching input, instantiates a syntax node and extends it with the declared module, element accessor methods, and the method from the inline module" do
50
+ parse('foobarbaz') do |result|
51
+ result.should_not be_nil
52
+ result.should respond_to(:mod_method)
53
+ result.should be_an_instance_of(Treetop::Runtime::SyntaxNode)
54
+ result.should be_a_kind_of(ModFoo)
55
+ result.should respond_to(:a_method)
56
+ result.foo.text_value.should == 'foo'
57
+ result.bar.text_value.should == 'bar'
58
+ result.baz.text_value.should == 'baz'
59
+ end
60
+ end
61
+ end
62
+
63
+ describe "a sequence of non-terminals" do
64
+ testing_grammar %{
65
+ grammar TestGrammar
66
+ rule sequence
67
+ foo bar baz {
68
+ def baz
69
+ 'override' + super.text_value
70
+ end
71
+ }
72
+ end
73
+
74
+ rule foo 'foo' end
75
+ rule bar 'bar' end
76
+ rule baz 'baz' end
77
+ end
78
+ }
79
+
80
+ it "defines accessors for non-terminals automatically that can be overridden in the inline block" do
81
+ parse('foobarbaz') do |result|
82
+ result.foo.text_value.should == 'foo'
83
+ result.bar.text_value.should == 'bar'
84
+ result.baz.should == 'overridebaz'
85
+ end
86
+ end
87
+ end
88
+
89
+ describe "Compiling a sequence containing various white-space errors" do
90
+ it "should succeed on a valid sequence" do
91
+ compiling_expression('foo:"foo" "bar" <SequenceSpec::Foo> { def a_method; end }').should_not raise_error
92
+ end
93
+
94
+ it "rejects space after a label" do
95
+ compiling_expression('foo :"foo" "bar"').should raise_error(RuntimeError)
96
+ end
97
+
98
+ it "rejects space after label's colon" do
99
+ compiling_expression('foo: "foo" "bar"').should raise_error(RuntimeError)
100
+ end
101
+
102
+ it "rejects missing space after a primary" do
103
+ compiling_expression('foo:"foo""bar"').should raise_error(RuntimeError)
104
+ end
105
+
106
+ it "rejects missing space before node class declaration" do
107
+ compiling_expression('foo:"foo" "bar"<SequenceSpec::Foo>').should raise_error(RuntimeError)
108
+ end
109
+
110
+ it "rejects missing space before inline module" do
111
+ compiling_expression('foo:"foo" "bar" <SequenceSpec::Foo>{def a_method; end}').should raise_error(RuntimeError)
112
+ end
113
+ end
114
+
115
+ end