treetop 1.4.15 → 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -389,19 +389,19 @@ module Treetop
389
389
  end
390
390
 
391
391
  rule quoted_string
392
- (single_quoted_string / double_quoted_string) {
392
+ qs:(single_quoted_string / double_quoted_string) insens:'i'? <Terminal> {
393
393
  def string
394
- super.text_value
394
+ qs.text_value
395
395
  end
396
396
  }
397
397
  end
398
398
 
399
399
  rule double_quoted_string
400
- '"' string:(!'"' ("\\\\" / '\"' / .))* '"' <Terminal>
400
+ '"' string:(!'"' ("\\\\" / '\"' / .))* '"'
401
401
  end
402
402
 
403
403
  rule single_quoted_string
404
- "'" string:(!"'" ("\\\\" / "\\'" / .))* "'" <Terminal>
404
+ "'" string:(!"'" ("\\\\" / "\\'" / .))* "'"
405
405
  end
406
406
 
407
407
  rule character_class
@@ -4,8 +4,12 @@ module Treetop
4
4
  def compile(address, builder, parent_expression = nil)
5
5
  super
6
6
  builder.if__ "index < input_length" do
7
- assign_result "instantiate_node(#{node_class_name},input, index...(index + 1))"
8
- extend_result_with_inline_module
7
+ if address == 0 || decorated?
8
+ assign_result "instantiate_node(#{node_class_name},input, index...(index + 1))"
9
+ extend_result_with_inline_module
10
+ else
11
+ assign_lazily_instantiated_node
12
+ end
9
13
  builder << "@index += 1"
10
14
  end
11
15
  builder.else_ do
@@ -15,4 +19,4 @@ module Treetop
15
19
  end
16
20
  end
17
21
  end
18
- end
22
+ end
@@ -3,8 +3,8 @@ module Treetop
3
3
  class CharacterClass < AtomicExpression
4
4
  def compile(address, builder, parent_expression = nil)
5
5
  super
6
-
7
- builder.if__ "has_terminal?(#{grounded_regexp(text_value)}, true, index)" do
6
+
7
+ builder.if__ "has_terminal?(@regexps[gr = #{grounded_regexp(text_value)}] ||= Regexp.new(gr), :regexp, index)" do
8
8
  if address == 0 || decorated?
9
9
  assign_result "instantiate_node(#{node_class_name},input, index...(index + 1))"
10
10
  extend_result_with_inline_module
@@ -13,6 +13,8 @@ module Treetop
13
13
  obtain_new_subexpression_address
14
14
  alternatives.first.compile(subexpression_address, builder)
15
15
  builder.if__ subexpression_success? do
16
+ # Undo lazy instantiation:
17
+ builder << "#{subexpression_result_var} = SyntaxNode.new(input, (index-1)...index) if #{subexpression_result_var} == true"
16
18
  assign_result subexpression_result_var
17
19
  extend_result_with_declared_module
18
20
  extend_result_with_inline_module
@@ -28,4 +30,4 @@ module Treetop
28
30
  end
29
31
  end
30
32
  end
31
- end
33
+ end
@@ -33,9 +33,11 @@ module Treetop
33
33
 
34
34
  def generate_cache_lookup(builder)
35
35
  builder.if_ "node_cache[:#{name}].has_key?(index)" do
36
- builder.assign 'cached', "node_cache[:#{name}][index]"
36
+ cache_address = "node_cache[:#{name}][index]"
37
+ builder.assign 'cached', cache_address
37
38
  builder.if_ "cached" do
38
- builder << 'cached = SyntaxNode.new(input, index...(index + 1)) if cached == true'
39
+ # Handle lazily instantiated nodes:
40
+ builder << "#{cache_address} = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true"
39
41
  builder << '@index = cached.interval.end'
40
42
  end
41
43
  builder << 'return cached'
@@ -3,15 +3,21 @@ module Treetop
3
3
  class Terminal < AtomicExpression
4
4
  def compile(address, builder, parent_expression = nil)
5
5
  super
6
- string_length = eval(text_value).length
7
-
8
- builder.if__ "has_terminal?(#{text_value}, false, index)" do
9
- assign_result "instantiate_node(#{node_class_name},input, index...(index + #{string_length}))"
10
- extend_result_with_inline_module
6
+ insensitive = insens.text_value.length > 0
7
+ str = insensitive ? string.downcase : string
8
+ string_length = eval(str).length
9
+
10
+ builder.if__ "has_terminal?(#{str}, #{insensitive ? ':insens' : false}, index)" do
11
+ if address == 0 || decorated? || string_length > 1
12
+ assign_result "instantiate_node(#{node_class_name},input, index...(index + #{string_length}))"
13
+ extend_result_with_inline_module
14
+ else
15
+ assign_lazily_instantiated_node
16
+ end
11
17
  builder << "@index += #{string_length}"
12
18
  end
13
19
  builder.else_ do
14
- builder << "terminal_parse_failure(#{text_value})"
20
+ builder << "terminal_parse_failure(#{str})"
15
21
  assign_result 'nil'
16
22
  end
17
23
  end
@@ -92,12 +92,17 @@ module Treetop
92
92
  end
93
93
  end
94
94
 
95
- def has_terminal?(terminal, regex, index)
96
- if regex
95
+ def has_terminal?(terminal, mode, index)
96
+ case mode
97
+ when :regexp # A Regexp has been passed in, probably a character class
98
+ input.index(terminal, index) == index
99
+ when false # The terminal is a string which must match exactly
100
+ input[index, terminal.size] == terminal
101
+ when :insens # The terminal is a downcased string which must match input downcased
102
+ input[index, terminal.size].downcase == terminal
103
+ when true # Only occurs with old compiled grammars, for character classes
97
104
  rx = @regexps[terminal] ||= Regexp.new(terminal)
98
105
  input.index(rx, index) == index
99
- else
100
- input[index, terminal.size] == terminal
101
106
  end
102
107
  end
103
108
 
@@ -1,8 +1,8 @@
1
1
  module Treetop #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 1
4
- MINOR = 4
5
- TINY = 15
4
+ MINOR = 5
5
+ TINY = 1
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -228,19 +228,19 @@ module CharacterClassSpec
228
228
  end
229
229
 
230
230
  describe "a character class mixed with other expressions" do
231
- testing_expression '[A-Z] "a"'
231
+ testing_expression '[A-Z] "a" "bc"'
232
232
  it "lazily instantiates a node for the character" do
233
- result = parse('Aa')
233
+ result = parse('Aabc')
234
234
  result.instance_variable_get("@elements").should include(true)
235
235
  result.elements.should_not include(true)
236
- result.elements.size.should == 2
236
+ result.elements.size.should == 3
237
237
  end
238
238
  end
239
239
 
240
240
  describe "a character class with a node class declaration mixed with other expressions" do
241
- testing_expression '([A-Z] <CharacterClassSpec::Foo>) "a"'
241
+ testing_expression '([A-Z] <CharacterClassSpec::Foo>) "ab"'
242
242
  it "actively generates a node for the character because it has a node class declared" do
243
- result = parse('Aa')
243
+ result = parse('Aab')
244
244
  result.instance_variable_get("@elements").should_not include(true)
245
245
  result.elements.should_not include(true)
246
246
  result.elements.size.should == 2
@@ -248,9 +248,9 @@ module CharacterClassSpec
248
248
  end
249
249
 
250
250
  describe "a character class with a node module declaration mixed with other expressions" do
251
- testing_expression '([A-Z] <CharacterClassSpec::ModFoo>) "a"'
251
+ testing_expression '([A-Z] <CharacterClassSpec::ModFoo>) "ab"'
252
252
  it "actively generates a node for the character because it has a node module declared" do
253
- result = parse('Aa')
253
+ result = parse('Aab')
254
254
  result.instance_variable_get("@elements").should_not include(true)
255
255
  result.elements.should_not include(true)
256
256
  result.elements.size.should == 2
@@ -258,9 +258,9 @@ module CharacterClassSpec
258
258
  end
259
259
 
260
260
  describe "a character class with an inline block mixed with other expressions" do
261
- testing_expression '([A-Z] { def a_method; end }) "a"'
261
+ testing_expression '([A-Z] { def a_method; end }) "ab"'
262
262
  it "actively generates a node for the character because it has an inline block" do
263
- result = parse('Aa')
263
+ result = parse('Aab')
264
264
  result.instance_variable_get("@elements").should_not include(true)
265
265
  result.elements.should_not include(true)
266
266
  result.elements.size.should == 2
@@ -268,28 +268,28 @@ module CharacterClassSpec
268
268
  end
269
269
 
270
270
  describe "a character class with a label mixed with other expressions" do
271
- testing_expression 'upper:([A-Z]) "b"'
271
+ testing_expression 'upper:([A-Z]) "b" "cd"'
272
272
  it "returns the correct element for the labeled expression" do
273
- result = parse('Ab')
273
+ result = parse('Abcd')
274
274
  result.upper.text_value.should == "A"
275
- result.elements.size.should == 2
275
+ result.elements.size.should == 3
276
276
  end
277
277
  end
278
278
 
279
279
  describe "a character class repetition mixed with other expressions" do
280
- testing_expression '[A-Z]+ "a"'
280
+ testing_expression '[A-Z]+ "a" "bc"'
281
281
  it "lazily instantiates a node for the character" do
282
- result = parse('ABCa')
282
+ result = parse('ABCabc')
283
283
  result.elements[0].instance_variable_get("@elements").should include(true)
284
284
  result.elements[0].elements.should_not include(true)
285
285
  result.elements[0].elements.size.should == 3
286
- result.elements.size.should == 2
287
- result.elements.inspect.should == %Q{[SyntaxNode offset=0, "ABC":\n SyntaxNode offset=0, "A"\n SyntaxNode offset=1, "B"\n SyntaxNode offset=2, "C", SyntaxNode offset=3, "a"]}
286
+ result.elements.size.should == 3
287
+ result.elements.inspect.should == %Q{[SyntaxNode offset=0, "ABC":\n SyntaxNode offset=0, "A"\n SyntaxNode offset=1, "B"\n SyntaxNode offset=2, "C", SyntaxNode offset=3, "a", SyntaxNode offset=4, "bc"]}
288
288
  end
289
289
  end
290
290
 
291
291
  describe "a character class that gets cached because of a choice" do
292
- testing_expression "[A-Z] 'a' / [A-Z]"
292
+ testing_expression "[A-Z] 'a' 'bc' / [A-Z]"
293
293
 
294
294
  it "generates a node for the lazily-instantiated character when it is the primary node" do
295
295
  result = parse('A')
@@ -54,10 +54,10 @@ module ChoiceSpec
54
54
  end
55
55
 
56
56
  describe "A choice between terminals followed by a block" do
57
- testing_expression "('a'/ 'b' / 'c') { def a_method; end }"
57
+ testing_expression "('a'/ 'bb' / [c]) { def a_method; end }"
58
58
 
59
59
  it "extends a match of any of its subexpressions with a module created from the block" do
60
- ['a', 'b', 'c'].each do |letter|
60
+ ['a', 'bb', 'c'].each do |letter|
61
61
  parse(letter).should respond_to(:a_method)
62
62
  end
63
63
  end
@@ -69,10 +69,10 @@ module ChoiceSpec
69
69
  end
70
70
 
71
71
  describe "a choice followed by a declared module" do
72
- testing_expression "('a'/ 'b' / 'c') <ChoiceSpec::TestModule>"
72
+ testing_expression "('a'/ 'bb' / [c]) <ChoiceSpec::TestModule>"
73
73
 
74
74
  it "extends a match of any of its subexpressions with a module created from the block" do
75
- ['a', 'b', 'c'].each do |letter|
75
+ ['a', 'bb', 'c'].each do |letter|
76
76
  parse(letter).should respond_to(:a_method)
77
77
  end
78
78
  end
@@ -29,10 +29,10 @@ module NotPredicateSpec
29
29
  end
30
30
 
31
31
  describe "A !-predicated sequence" do
32
- testing_expression '!("a" "b" "c")'
32
+ testing_expression '!("a" "b" "cc")'
33
33
 
34
34
  it "fails to parse matching input" do
35
- parse('abc').should be_nil
35
+ parse('abcc').should be_nil
36
36
  end
37
37
  end
38
38
  end
@@ -5,7 +5,7 @@ module RepeatedSubruleSpec
5
5
  testing_grammar %{
6
6
  grammar Foo
7
7
  rule foo
8
- a:'a' space b:'b' space 'c'
8
+ a:'a' space b:'b' space 'cc'
9
9
  end
10
10
 
11
11
  rule space
@@ -15,7 +15,7 @@ module RepeatedSubruleSpec
15
15
  }
16
16
 
17
17
  it "should produce a parser having sequence-numbered node accessor methods" do
18
- parse("a b c") do |result|
18
+ parse("a b cc") do |result|
19
19
  result.should_not be_nil
20
20
  result.should respond_to(:space1)
21
21
  result.should respond_to(:space2)
@@ -4,6 +4,50 @@ module TerminalSymbolSpec
4
4
  class Foo < Treetop::Runtime::SyntaxNode
5
5
  end
6
6
 
7
+ describe "a terminal symbol" do
8
+ testing_expression "'Foo'"
9
+
10
+ it "matches the input string" do
11
+ parse "Foo", :index => 0 do |result|
12
+ result.should_not be_nil
13
+ result.interval.should == (0...3)
14
+ result.text_value.should == 'Foo'
15
+ end
16
+ end
17
+
18
+ it "fails to match the input string other than at the start" do
19
+ parse " Foo", :index => 0 do |result|
20
+ result.should be_nil
21
+ end
22
+ end
23
+
24
+ it "fails to match the input string in the wrong case" do
25
+ parse "foo", :index => 0 do |result|
26
+ result.should be_nil
27
+ end
28
+ end
29
+ end
30
+
31
+ describe "a terminal symbol with case-insensitive matching" do
32
+ testing_expression "'Foo'i"
33
+
34
+ it "matches the input string in the same case" do
35
+ parse "Foo", :index => 0 do |result|
36
+ result.should_not be_nil
37
+ result.interval.should == (0...3)
38
+ result.text_value.should == 'Foo'
39
+ end
40
+ end
41
+
42
+ it "matches the input string in varied case" do
43
+ parse "foO", :index => 0 do |result|
44
+ result.should_not be_nil
45
+ result.interval.should == (0...3)
46
+ result.text_value.should == 'foO'
47
+ end
48
+ end
49
+ end
50
+
7
51
  describe "a terminal symbol followed by a node class declaration and a block" do
8
52
  testing_expression "'foo' <TerminalSymbolSpec::Foo> { def a_method; end }"
9
53
 
@@ -47,7 +47,7 @@ module ZeroOrMoreSpec
47
47
  end
48
48
 
49
49
  describe "Zero or more of a choice" do
50
- testing_expression '("a" / "b")*'
50
+ testing_expression '("a" / "bb")*'
51
51
 
52
52
  it "successfully parses matching input" do
53
53
  parse('abba').should_not be_nil
@@ -1,11 +1,11 @@
1
1
  module Test
2
2
  grammar B
3
3
  rule b
4
- 'b'
4
+ 'bb'
5
5
  end
6
6
 
7
7
  rule inherit
8
8
  super 'keyword'
9
9
  end
10
10
  end
11
- end
11
+ end
@@ -16,7 +16,7 @@ module GrammarCompositionSpec
16
16
  end
17
17
 
18
18
  specify "rules in C have access to rules defined in A and B" do
19
- @c.parse('abc').should_not be_nil
19
+ @c.parse('abbc').should_not be_nil
20
20
  end
21
21
 
22
22
  specify "rules in C can override rules in A and B with super semantics" do
@@ -33,7 +33,7 @@ module GrammarCompositionSpec
33
33
  end
34
34
 
35
35
  specify "rules in F have access to rule defined in E" do
36
- @f.parse('abcef').should_not be_nil
36
+ @f.parse('abbcef').should_not be_nil
37
37
  end
38
38
 
39
39
  end
@@ -11,7 +11,7 @@ module CompiledParserSpec
11
11
  end
12
12
 
13
13
  rule b
14
- 'b'
14
+ 'bb'
15
15
  end
16
16
  end
17
17
  }
@@ -25,7 +25,7 @@ module CompiledParserSpec
25
25
  parser.parse('b').should be_nil
26
26
 
27
27
  # Check that the temporary-override works:
28
- parser.parse('b', :root => :b).should_not be_nil
28
+ parser.parse('bb', :root => :b).should_not be_nil
29
29
  parser.parse('a', :root => :b).should be_nil
30
30
 
31
31
  # Check that the temporary-override isn't sticky:
@@ -33,7 +33,7 @@ module CompiledParserSpec
33
33
 
34
34
  # Try a permanent override:
35
35
  parser.root = :b
36
- parser.parse('b').should_not be_nil
36
+ parser.parse('bb').should_not be_nil
37
37
  parser.parse('a').should be_nil
38
38
  end
39
39
 
data/treetop.gemspec CHANGED
@@ -2,15 +2,17 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
+ # stub: treetop 1.5.1 ruby lib
5
6
 
6
7
  Gem::Specification.new do |s|
7
8
  s.name = "treetop"
8
- s.version = "1.4.15"
9
+ s.version = "1.5.1"
9
10
 
10
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib"]
11
13
  s.authors = ["Nathan Sobo", "Clifford Heath"]
12
14
  s.autorequire = "treetop"
13
- s.date = "2013-08-17"
15
+ s.date = "2014-03-12"
14
16
  s.email = "cliffordheath@gmail.com"
15
17
  s.executables = ["tt"]
16
18
  s.extra_rdoc_files = [
@@ -136,41 +138,37 @@ Gem::Specification.new do |s|
136
138
  ]
137
139
  s.homepage = "https://github.com/cjheath/treetop"
138
140
  s.licenses = ["MIT"]
139
- s.require_paths = ["lib"]
140
- s.rubygems_version = "1.8.24"
141
+ s.rubygems_version = "2.2.2"
141
142
  s.summary = "A Ruby-based text parsing and interpretation DSL"
142
143
 
143
144
  if s.respond_to? :specification_version then
144
- s.specification_version = 3
145
+ s.specification_version = 4
145
146
 
146
147
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
147
- s.add_runtime_dependency(%q<polyglot>, [">= 0"])
148
- s.add_development_dependency(%q<jeweler>, [">= 0"])
149
- s.add_development_dependency(%q<activesupport>, [">= 0"])
150
- s.add_development_dependency(%q<i18n>, ["~> 0.5.0"])
148
+ s.add_runtime_dependency(%q<polyglot>, ["~> 0.3"])
149
+ s.add_development_dependency(%q<jeweler>, ["~> 2.0"])
150
+ s.add_development_dependency(%q<activesupport>, ["~> 4.0"])
151
+ s.add_development_dependency(%q<i18n>, ["~> 0.6"])
151
152
  s.add_development_dependency(%q<rr>, ["~> 1.0"])
152
- s.add_development_dependency(%q<rspec>, [">= 2.0.0"])
153
- s.add_development_dependency(%q<rake>, [">= 0"])
154
- s.add_runtime_dependency(%q<polyglot>, [">= 0.3.1"])
153
+ s.add_development_dependency(%q<rspec>, ["~> 2"])
154
+ s.add_development_dependency(%q<rake>, ["~> 10"])
155
155
  else
156
- s.add_dependency(%q<polyglot>, [">= 0"])
157
- s.add_dependency(%q<jeweler>, [">= 0"])
158
- s.add_dependency(%q<activesupport>, [">= 0"])
159
- s.add_dependency(%q<i18n>, ["~> 0.5.0"])
156
+ s.add_dependency(%q<polyglot>, ["~> 0.3"])
157
+ s.add_dependency(%q<jeweler>, ["~> 2.0"])
158
+ s.add_dependency(%q<activesupport>, ["~> 4.0"])
159
+ s.add_dependency(%q<i18n>, ["~> 0.6"])
160
160
  s.add_dependency(%q<rr>, ["~> 1.0"])
161
- s.add_dependency(%q<rspec>, [">= 2.0.0"])
162
- s.add_dependency(%q<rake>, [">= 0"])
163
- s.add_dependency(%q<polyglot>, [">= 0.3.1"])
161
+ s.add_dependency(%q<rspec>, ["~> 2"])
162
+ s.add_dependency(%q<rake>, ["~> 10"])
164
163
  end
165
164
  else
166
- s.add_dependency(%q<polyglot>, [">= 0"])
167
- s.add_dependency(%q<jeweler>, [">= 0"])
168
- s.add_dependency(%q<activesupport>, [">= 0"])
169
- s.add_dependency(%q<i18n>, ["~> 0.5.0"])
165
+ s.add_dependency(%q<polyglot>, ["~> 0.3"])
166
+ s.add_dependency(%q<jeweler>, ["~> 2.0"])
167
+ s.add_dependency(%q<activesupport>, ["~> 4.0"])
168
+ s.add_dependency(%q<i18n>, ["~> 0.6"])
170
169
  s.add_dependency(%q<rr>, ["~> 1.0"])
171
- s.add_dependency(%q<rspec>, [">= 2.0.0"])
172
- s.add_dependency(%q<rake>, [">= 0"])
173
- s.add_dependency(%q<polyglot>, [">= 0.3.1"])
170
+ s.add_dependency(%q<rspec>, ["~> 2"])
171
+ s.add_dependency(%q<rake>, ["~> 10"])
174
172
  end
175
173
  end
176
174