treetop 1.1.4 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +1 -1
- data/Rakefile +3 -2
- data/doc/contributing_and_planned_features.markdown +1 -1
- data/doc/using_in_ruby.markdown +2 -2
- data/examples/lambda_calculus/arithmetic.rb +551 -0
- data/examples/lambda_calculus/arithmetic_test.rb +1 -1
- data/examples/lambda_calculus/lambda_calculus.rb +39 -72
- data/examples/lambda_calculus/lambda_calculus_test.rb +2 -2
- data/examples/lambda_calculus/test_helper.rb +3 -3
- data/lib/treetop.rb +3 -0
- data/lib/treetop/bootstrap_gen_1_metagrammar.rb +22 -14
- data/lib/treetop/compiler.rb +1 -2
- data/lib/treetop/compiler/grammar_compiler.rb +12 -5
- data/lib/treetop/compiler/metagrammar.rb +931 -558
- data/lib/treetop/compiler/metagrammar.treetop +26 -6
- data/lib/treetop/compiler/node_classes/anything_symbol.rb +10 -2
- data/lib/treetop/compiler/node_classes/atomic_expression.rb +4 -0
- data/lib/treetop/compiler/node_classes/character_class.rb +10 -1
- data/lib/treetop/compiler/node_classes/choice.rb +2 -4
- data/lib/treetop/compiler/node_classes/parsing_expression.rb +8 -17
- data/lib/treetop/compiler/node_classes/parsing_rule.rb +3 -3
- data/lib/treetop/compiler/node_classes/predicate.rb +1 -1
- data/lib/treetop/compiler/node_classes/repetition.rb +3 -4
- data/lib/treetop/compiler/node_classes/sequence.rb +4 -4
- data/lib/treetop/compiler/node_classes/terminal.rb +11 -1
- data/lib/treetop/compiler/ruby_builder.rb +2 -2
- data/lib/treetop/ruby_extensions.rb +1 -1
- data/lib/treetop/runtime.rb +0 -3
- data/lib/treetop/runtime/compiled_parser.rb +42 -34
- data/lib/treetop/runtime/node_cache.rb +1 -1
- data/lib/treetop/runtime/syntax_node.rb +51 -32
- data/lib/treetop/runtime/terminal_parse_failure.rb +7 -24
- data/lib/treetop/runtime/terminal_syntax_node.rb +7 -2
- metadata +12 -7
- data/examples/TALK +0 -33
- data/lib/treetop/compiler/load_grammar.rb +0 -7
- data/lib/treetop/compiler/metagrammar. +0 -0
- data/lib/treetop/runtime/parse_failure.rb +0 -32
- 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
|
-
|
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
|
-
|
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
|
-
|
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
|
@@ -3,7 +3,16 @@ module Treetop
|
|
3
3
|
class CharacterClass < AtomicExpression
|
4
4
|
def compile(address, builder, parent_expression = nil)
|
5
5
|
super
|
6
|
-
|
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
|
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
|
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
|
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
|
86
|
+
"SyntaxNode.new(input, index...index)"
|
95
87
|
end
|
96
88
|
|
97
|
-
def assign_failure(start_index_var
|
98
|
-
assign_result("
|
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
|
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.
|
36
|
-
|
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
|
@@ -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, :
|
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}
|
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
|
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
|
7
|
+
use_vars :result, :start_index, :accumulator
|
8
8
|
compile_sequence_elements(sequence_elements)
|
9
|
-
builder.if__ "#{accumulator_var}.last
|
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
|
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
|
-
|
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
|
@@ -1,2 +1,2 @@
|
|
1
1
|
dir = File.dirname(__FILE__)
|
2
|
-
require "#{dir}/ruby_extensions/string"
|
2
|
+
require "#{dir}/ruby_extensions/string"
|
data/lib/treetop/runtime.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|