treetop 1.1.4 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. data/README +1 -1
  2. data/Rakefile +3 -2
  3. data/doc/contributing_and_planned_features.markdown +1 -1
  4. data/doc/using_in_ruby.markdown +2 -2
  5. data/examples/lambda_calculus/arithmetic.rb +551 -0
  6. data/examples/lambda_calculus/arithmetic_test.rb +1 -1
  7. data/examples/lambda_calculus/lambda_calculus.rb +39 -72
  8. data/examples/lambda_calculus/lambda_calculus_test.rb +2 -2
  9. data/examples/lambda_calculus/test_helper.rb +3 -3
  10. data/lib/treetop.rb +3 -0
  11. data/lib/treetop/bootstrap_gen_1_metagrammar.rb +22 -14
  12. data/lib/treetop/compiler.rb +1 -2
  13. data/lib/treetop/compiler/grammar_compiler.rb +12 -5
  14. data/lib/treetop/compiler/metagrammar.rb +931 -558
  15. data/lib/treetop/compiler/metagrammar.treetop +26 -6
  16. data/lib/treetop/compiler/node_classes/anything_symbol.rb +10 -2
  17. data/lib/treetop/compiler/node_classes/atomic_expression.rb +4 -0
  18. data/lib/treetop/compiler/node_classes/character_class.rb +10 -1
  19. data/lib/treetop/compiler/node_classes/choice.rb +2 -4
  20. data/lib/treetop/compiler/node_classes/parsing_expression.rb +8 -17
  21. data/lib/treetop/compiler/node_classes/parsing_rule.rb +3 -3
  22. data/lib/treetop/compiler/node_classes/predicate.rb +1 -1
  23. data/lib/treetop/compiler/node_classes/repetition.rb +3 -4
  24. data/lib/treetop/compiler/node_classes/sequence.rb +4 -4
  25. data/lib/treetop/compiler/node_classes/terminal.rb +11 -1
  26. data/lib/treetop/compiler/ruby_builder.rb +2 -2
  27. data/lib/treetop/ruby_extensions.rb +1 -1
  28. data/lib/treetop/runtime.rb +0 -3
  29. data/lib/treetop/runtime/compiled_parser.rb +42 -34
  30. data/lib/treetop/runtime/node_cache.rb +1 -1
  31. data/lib/treetop/runtime/syntax_node.rb +51 -32
  32. data/lib/treetop/runtime/terminal_parse_failure.rb +7 -24
  33. data/lib/treetop/runtime/terminal_syntax_node.rb +7 -2
  34. metadata +12 -7
  35. data/examples/TALK +0 -33
  36. data/lib/treetop/compiler/load_grammar.rb +0 -7
  37. data/lib/treetop/compiler/metagrammar. +0 -0
  38. data/lib/treetop/runtime/parse_failure.rb +0 -32
  39. data/lib/treetop/runtime/parse_result.rb +0 -30
@@ -297,19 +297,31 @@ module Treetop
297
297
  end
298
298
 
299
299
  rule terminal
300
- single_quoted_string / double_quoted_string / character_class / anything_symbol
300
+ quoted_string / character_class / anything_symbol
301
+ end
302
+
303
+ rule quoted_string
304
+ (single_quoted_string / double_quoted_string) {
305
+ def string
306
+ super.text_value
307
+ end
308
+ }
301
309
  end
302
310
 
303
311
  rule double_quoted_string
304
- '"' (!'"' ("\\\\" / '\"' / .))* '"' <Terminal>
312
+ '"' string:(!'"' ("\\\\" / '\"' / .))* '"' <Terminal>
305
313
  end
306
314
 
307
315
  rule single_quoted_string
308
- "'" (!"'" ("\\\\" / "\\'" / .))* "'" <Terminal>
316
+ "'" string:(!"'" ("\\\\" / "\\'" / .))* "'" <Terminal>
309
317
  end
310
318
 
311
319
  rule character_class
312
- '[' (!']' ('\]'/.))+ ']' <CharacterClass>
320
+ '[' characters:(!']' ('\]'/.))+ ']' <CharacterClass> {
321
+ def characters
322
+ super.text_value
323
+ end
324
+ }
313
325
  end
314
326
 
315
327
  rule anything_symbol
@@ -377,8 +389,16 @@ module Treetop
377
389
  end
378
390
 
379
391
  rule space
380
- [ \t\n\r]+
392
+ (white / comment_to_eol)+
393
+ end
394
+
395
+ rule comment_to_eol
396
+ '#' (!"\n" .)+
397
+ end
398
+
399
+ rule white
400
+ [ \t\n\r]
381
401
  end
382
402
  end
383
403
  end
384
- end
404
+ end
@@ -3,8 +3,16 @@ module Treetop
3
3
  class AnythingSymbol < AtomicExpression
4
4
  def compile(address, builder, parent_expression = nil)
5
5
  super
6
- assign_result "parse_anything(#{node_class_name}#{optional_arg(inline_module_name)})"
6
+ builder.if__ "index < input_length" do
7
+ assign_result "(#{node_class_name}).new(input, index...(index + 1))"
8
+ extend_result_with_inline_module
9
+ builder << "@index += 1"
10
+ end
11
+ builder.else_ do
12
+ builder << 'terminal_parse_failure("any character")'
13
+ assign_result 'nil'
14
+ end
7
15
  end
8
16
  end
9
17
  end
10
- end
18
+ end
@@ -4,6 +4,10 @@ module Treetop
4
4
  def inline_modules
5
5
  []
6
6
  end
7
+
8
+ def single_quote(string)
9
+ "'#{string.gsub(/'$/, "\\'")}'"
10
+ end
7
11
  end
8
12
  end
9
13
  end
@@ -3,7 +3,16 @@ module Treetop
3
3
  class CharacterClass < AtomicExpression
4
4
  def compile(address, builder, parent_expression = nil)
5
5
  super
6
- assign_result "parse_char_class(/#{text_value}/, '#{elements[1].text_value.gsub(/'$/, "\\\\'")}', #{node_class_name}#{optional_arg(inline_module_name)})"
6
+
7
+ builder.if__ "input.index(/#{text_value}/, index) == index" do
8
+ assign_result "(#{node_class_name}).new(input, index...(index + 1))"
9
+ extend_result_with_inline_module
10
+ builder << "@index += 1"
11
+ end
12
+ builder.else_ do
13
+ "terminal_parse_failure(#{single_quote(characters)})"
14
+ assign_result 'nil'
15
+ end
7
16
  end
8
17
  end
9
18
  end
@@ -4,7 +4,7 @@ module Treetop
4
4
  def compile(address, builder, parent_expression = nil)
5
5
  super
6
6
  begin_comment(self)
7
- use_vars :result, :start_index, :nested_results
7
+ use_vars :result, :start_index
8
8
  compile_alternatives(alternatives)
9
9
  end_comment(self)
10
10
  end
@@ -12,17 +12,15 @@ module Treetop
12
12
  def compile_alternatives(alternatives)
13
13
  obtain_new_subexpression_address
14
14
  alternatives.first.compile(subexpression_address, builder)
15
- accumulate_nested_result
16
15
  builder.if__ subexpression_success? do
17
16
  assign_result subexpression_result_var
18
17
  extend_result_with_declared_module
19
18
  extend_result_with_inline_module
20
- builder << "#{subexpression_result_var}.update_nested_results(#{nested_results_var})"
21
19
  end
22
20
  builder.else_ do
23
21
  if alternatives.size == 1
24
22
  reset_index
25
- assign_failure start_index_var, nested_results_var
23
+ assign_failure start_index_var
26
24
  else
27
25
  compile_alternatives(alternatives[1..-1])
28
26
  end
@@ -41,11 +41,7 @@ module Treetop
41
41
  def accumulator_var
42
42
  var(:accumulator)
43
43
  end
44
-
45
- def nested_results_var
46
- var(:nested_results)
47
- end
48
-
44
+
49
45
  def start_index_var
50
46
  var(:start_index)
51
47
  end
@@ -55,17 +51,13 @@ module Treetop
55
51
  end
56
52
 
57
53
  def subexpression_success?
58
- subexpression_result_var + ".success?"
54
+ subexpression_result_var
59
55
  end
60
56
 
61
57
  def obtain_new_subexpression_address
62
58
  @subexpression_address = builder.next_address
63
59
  end
64
-
65
- def accumulate_nested_result
66
- builder.accumulate nested_results_var, subexpression_result_var
67
- end
68
-
60
+
69
61
  def accumulate_subexpression_result
70
62
  builder.accumulate accumulator_var, subexpression_result_var
71
63
  end
@@ -91,11 +83,11 @@ module Treetop
91
83
  end
92
84
 
93
85
  def epsilon_node
94
- "SyntaxNode.new(input, index...index, #{subexpression_result_var}.nested_failures)"
86
+ "SyntaxNode.new(input, index...index)"
95
87
  end
96
88
 
97
- def assign_failure(start_index_var, nested_results_var)
98
- assign_result("ParseFailure.new(input, #{start_index_var}, #{nested_results_var})")
89
+ def assign_failure(start_index_var)
90
+ assign_result("nil")
99
91
  end
100
92
 
101
93
  def var_initialization
@@ -117,7 +109,6 @@ module Treetop
117
109
  case var_symbol
118
110
  when :result then "r#{address}"
119
111
  when :accumulator then "s#{address}"
120
- when :nested_results then "nr#{address}"
121
112
  when :start_index then "i#{address}"
122
113
  else raise "Unknown var symbol #{var_symbol}."
123
114
  end
@@ -125,7 +116,7 @@ module Treetop
125
116
 
126
117
  def init_value(var_symbol)
127
118
  case var_symbol
128
- when :accumulator, :nested_results then '[]'
119
+ when :accumulator then '[]'
129
120
  when :start_index then 'index'
130
121
  else nil
131
122
  end
@@ -144,4 +135,4 @@ module Treetop
144
135
  end
145
136
  end
146
137
  end
147
- end
138
+ end
@@ -32,9 +32,9 @@ module Treetop
32
32
  end
33
33
 
34
34
  def generate_cache_lookup(builder)
35
- builder.assign 'cached', "node_cache[:#{name}][index]"
36
- builder.if_ 'cached' do
37
- builder << '@index = cached.interval.end'
35
+ builder.if_ "node_cache[:#{name}].has_key?(index)" do
36
+ builder.assign 'cached', "node_cache[:#{name}][index]"
37
+ builder << '@index = cached.interval.end if cached'
38
38
  builder << 'return cached'
39
39
  end
40
40
  end
@@ -13,7 +13,7 @@ module Treetop
13
13
  end
14
14
 
15
15
  def assign_failure
16
- super(start_index_var, "#{subexpression_result_var}.nested_failures")
16
+ super(start_index_var)
17
17
  end
18
18
 
19
19
  def assign_success
@@ -5,12 +5,11 @@ module Treetop
5
5
  super
6
6
  repeated_expression = parent_expression.atomic
7
7
  begin_comment(parent_expression)
8
- use_vars :result, :accumulator, :nested_results, :start_index
8
+ use_vars :result, :accumulator, :start_index
9
9
 
10
10
  builder.loop do
11
11
  obtain_new_subexpression_address
12
12
  repeated_expression.compile(subexpression_address, builder)
13
- accumulate_nested_result
14
13
  builder.if__ subexpression_success? do
15
14
  accumulate_subexpression_result
16
15
  end
@@ -25,7 +24,7 @@ module Treetop
25
24
  end
26
25
 
27
26
  def assign_and_extend_result
28
- assign_result "#{node_class_name}.new(input, #{start_index_var}...index, #{accumulator_var}, #{nested_results_var})"
27
+ assign_result "#{node_class_name}.new(input, #{start_index_var}...index, #{accumulator_var})"
29
28
  extend_result_with_inline_module
30
29
  end
31
30
  end
@@ -44,7 +43,7 @@ module Treetop
44
43
  super
45
44
  builder.if__ "#{accumulator_var}.empty?" do
46
45
  reset_index
47
- assign_failure start_index_var, nested_results_var
46
+ assign_failure start_index_var
48
47
  end
49
48
  builder.else_ do
50
49
  assign_and_extend_result
@@ -4,16 +4,16 @@ module Treetop
4
4
  def compile(address, builder, parent_expression = nil)
5
5
  super
6
6
  begin_comment(self)
7
- use_vars :result, :start_index, :accumulator, :nested_results
7
+ use_vars :result, :start_index, :accumulator
8
8
  compile_sequence_elements(sequence_elements)
9
- builder.if__ "#{accumulator_var}.last.success?" do
9
+ builder.if__ "#{accumulator_var}.last" do
10
10
  assign_result "(#{node_class_name}).new(input, #{start_index_var}...index, #{accumulator_var})"
11
11
  extend_result sequence_element_accessor_module_name if sequence_element_accessor_module_name
12
12
  extend_result_with_inline_module
13
13
  end
14
14
  builder.else_ do
15
15
  reset_index
16
- assign_failure start_index_var, accumulator_var
16
+ assign_failure start_index_var
17
17
  end
18
18
  end_comment(self)
19
19
  end
@@ -65,4 +65,4 @@ module Treetop
65
65
  end
66
66
  end
67
67
  end
68
- end
68
+ end
@@ -3,7 +3,17 @@ module Treetop
3
3
  class Terminal < AtomicExpression
4
4
  def compile(address, builder, parent_expression = nil)
5
5
  super
6
- assign_result "parse_terminal(#{text_value}, #{node_class_name}#{optional_arg(inline_module_name)})"
6
+ string_length = eval(text_value).length
7
+
8
+ builder.if__ "input.index(#{text_value}, index) == index" do
9
+ assign_result "(#{node_class_name}).new(input, index...(index + #{string_length}))"
10
+ extend_result_with_inline_module
11
+ builder << "@index += #{string_length}"
12
+ end
13
+ builder.else_ do
14
+ builder << "terminal_parse_failure(#{text_value})"
15
+ assign_result 'nil'
16
+ end
7
17
  end
8
18
  end
9
19
  end
@@ -16,7 +16,7 @@ module Treetop
16
16
  end
17
17
 
18
18
  def newline
19
- ruby << indent << "\n"
19
+ ruby << "\n"
20
20
  end
21
21
 
22
22
  def indented(depth = 2)
@@ -110,4 +110,4 @@ module Treetop
110
110
  end
111
111
  end
112
112
  end
113
- end
113
+ end
@@ -1,2 +1,2 @@
1
1
  dir = File.dirname(__FILE__)
2
- require "#{dir}/ruby_extensions/string"
2
+ require "#{dir}/ruby_extensions/string"
@@ -1,9 +1,6 @@
1
1
  dir = File.dirname(__FILE__)
2
2
  require "#{dir}/runtime/compiled_parser"
3
- require "#{dir}/runtime/parse_result"
4
3
  require "#{dir}/runtime/syntax_node"
5
- require "#{dir}/runtime/parse_failure"
6
4
  require "#{dir}/runtime/node_cache"
7
5
  require "#{dir}/runtime/parse_cache"
8
- require "#{dir}/runtime/parse_failure"
9
6
  require "#{dir}/runtime/terminal_parse_failure"
@@ -3,11 +3,11 @@ module Treetop
3
3
  class CompiledParser
4
4
  include Treetop::Runtime
5
5
 
6
- attr_accessor :consume_all_input
6
+ attr_reader :input, :index, :terminal_failures, :max_terminal_failure_index
7
7
  attr_writer :root
8
+ attr_accessor :consume_all_input
8
9
  alias :consume_all_input? :consume_all_input
9
- attr_reader :input, :index
10
-
10
+
11
11
  def initialize
12
12
  self.consume_all_input = true
13
13
  end
@@ -16,50 +16,52 @@ module Treetop
16
16
  prepare_to_parse(input)
17
17
  @index = options[:index] if options[:index]
18
18
  result = send("_nt_#{root}")
19
- if consume_all_input? && index != input.size
20
- return ParseFailure.new(input, index, result.nested_failures)
21
- else
22
- return result
23
- end
19
+ return nil if (consume_all_input? && index != input.size)
20
+ return result
24
21
  end
25
-
22
+
23
+ def failure_index
24
+ max_terminal_failure_index
25
+ end
26
+
27
+ def failure_line
28
+ terminal_failures && input.line_of(failure_index)
29
+ end
30
+
31
+ def failure_column
32
+ terminal_failures && input.column_of(failure_index)
33
+ end
34
+
35
+ def failure_reason
36
+ return nil unless (tf = terminal_failures) && tf.size > 0
37
+ "Expected " +
38
+ (tf.size == 1 ?
39
+ tf[0].expected_string :
40
+ "one of #{tf.map{|f| f.expected_string}.uniq*', '}"
41
+ ) +
42
+ " at line #{failure_line}, column #{failure_column} (byte #{failure_index+1})" +
43
+ " after #{input[index...failure_index]}"
44
+ end
45
+
46
+
26
47
  protected
27
48
 
28
- attr_reader :node_cache
49
+ attr_reader :node_cache, :input_length
29
50
  attr_writer :index
30
51
 
31
52
  def prepare_to_parse(input)
32
53
  @input = input
54
+ @input_length = input.length
33
55
  reset_index
34
56
  @node_cache = Hash.new {|hash, key| hash[key] = Hash.new}
57
+ @terminal_failures = []
58
+ @max_terminal_failure_index = 0
35
59
  end
36
60
 
37
61
  def reset_index
38
62
  @index = 0
39
63
  end
40
64
 
41
- def parse_char_class(char_class_re, char_class_string, node_class = SyntaxNode, inline_module = nil)
42
- if input.index(char_class_re, index) == index
43
- result = node_class.new(input, index...(index + 1))
44
- result.extend(inline_module) if inline_module
45
- @index += 1
46
- result
47
- else
48
- terminal_parse_failure("[#{char_class_string}]")
49
- end
50
- end
51
-
52
- def parse_terminal(terminal_string, node_class = SyntaxNode, inline_module = nil)
53
- if input.index(terminal_string, index) == index
54
- result = node_class.new(input, index...(index + terminal_string.length))
55
- result.extend(inline_module) if inline_module
56
- @index += terminal_string.length
57
- result
58
- else
59
- terminal_parse_failure(terminal_string)
60
- end
61
- end
62
-
63
65
  def parse_anything(node_class = SyntaxNode, inline_module = nil)
64
66
  if index < input.length
65
67
  result = node_class.new(input, index...(index + 1))
@@ -72,8 +74,14 @@ module Treetop
72
74
  end
73
75
 
74
76
  def terminal_parse_failure(expected_string)
75
- TerminalParseFailure.new(input, index, expected_string)
77
+ return nil if index < max_terminal_failure_index
78
+ if index > max_terminal_failure_index
79
+ @max_terminal_failure_index = index
80
+ @terminal_failures = []
81
+ end
82
+ terminal_failures << TerminalParseFailure.new(index, expected_string)
83
+ return nil
76
84
  end
77
85
  end
78
86
  end
79
- end
87
+ end