treetop 1.1.4 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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