treetop 1.5.3 → 1.6.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/Rakefile +9 -3
- data/doc/pitfalls_and_advanced_techniques.markdown +1 -1
- data/doc/sitegen.rb +1 -1
- data/doc/syntactic_recognition.markdown +2 -0
- data/doc/tt.1 +1 -1
- data/lib/treetop/compiler/metagrammar.rb +81 -13
- data/lib/treetop/compiler/metagrammar.treetop +67 -13
- data/lib/treetop/compiler/node_classes/anything_symbol.rb +5 -1
- data/lib/treetop/compiler/node_classes/character_class.rb +6 -2
- data/lib/treetop/compiler/node_classes/choice.rb +9 -5
- data/lib/treetop/compiler/node_classes/nonterminal.rb +2 -2
- data/lib/treetop/compiler/node_classes/parenthesized_expression.rb +5 -1
- data/lib/treetop/compiler/node_classes/parsing_expression.rb +12 -2
- data/lib/treetop/compiler/node_classes/predicate.rb +8 -1
- data/lib/treetop/compiler/node_classes/predicate_block.rb +7 -0
- data/lib/treetop/compiler/node_classes/repetition.rb +31 -11
- data/lib/treetop/compiler/node_classes/sequence.rb +5 -1
- data/lib/treetop/compiler/node_classes/terminal.rb +12 -2
- data/lib/treetop/runtime/compiled_parser.rb +12 -6
- data/lib/treetop/runtime/syntax_node.rb +24 -15
- data/lib/treetop/runtime/terminal_parse_failure.rb +4 -3
- data/lib/treetop/version.rb +2 -2
- data/spec/compiler/and_predicate_spec.rb +1 -1
- data/spec/compiler/anything_symbol_spec.rb +4 -1
- data/spec/compiler/character_class_spec.rb +4 -1
- data/spec/compiler/choice_spec.rb +20 -11
- data/spec/compiler/failure_propagation_functional_spec.rb +1 -1
- data/spec/compiler/grammar_spec.rb +4 -1
- data/spec/compiler/not_predicate_spec.rb +20 -6
- data/spec/compiler/occurrence_range_spec.rb +25 -28
- data/spec/compiler/one_or_more_spec.rb +2 -2
- data/spec/compiler/optional_spec.rb +2 -2
- data/spec/compiler/parenthesized_expression_spec.rb +15 -1
- data/spec/compiler/semantic_predicate_spec.rb +17 -16
- data/spec/compiler/sequence_spec.rb +1 -1
- data/spec/compiler/terminal_spec.rb +8 -1
- data/spec/compiler/terminal_symbol_spec.rb +4 -1
- data/spec/compiler/zero_or_more_spec.rb +4 -2
- data/spec/runtime/compiled_parser_spec.rb +33 -3
- metadata +20 -21
- data/examples/lambda_calculus/lambda_calculus +0 -0
@@ -6,7 +6,7 @@ module Treetop
|
|
6
6
|
builder.if__ "index < input_length" do
|
7
7
|
if address == 0 || decorated?
|
8
8
|
assign_result "instantiate_node(#{node_class_name},input, index...(index + 1))"
|
9
|
-
extend_result_with_inline_module
|
9
|
+
extend_result_with_inline_module parent_expression
|
10
10
|
else
|
11
11
|
assign_lazily_instantiated_node
|
12
12
|
end
|
@@ -17,6 +17,10 @@ module Treetop
|
|
17
17
|
assign_result 'nil'
|
18
18
|
end
|
19
19
|
end
|
20
|
+
|
21
|
+
def expected
|
22
|
+
'"any character"'
|
23
|
+
end
|
20
24
|
end
|
21
25
|
end
|
22
26
|
end
|
@@ -7,18 +7,22 @@ module Treetop
|
|
7
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
|
-
extend_result_with_inline_module
|
10
|
+
extend_result_with_inline_module parent_expression
|
11
11
|
else
|
12
12
|
assign_lazily_instantiated_node
|
13
13
|
end
|
14
14
|
builder << "@index += 1" # Always one character
|
15
15
|
end
|
16
16
|
builder.else_ do
|
17
|
-
builder << "terminal_parse_failure(#{
|
17
|
+
builder << "terminal_parse_failure(#{expected})"
|
18
18
|
assign_result 'nil'
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
+
def expected
|
23
|
+
single_quote('['+characters+']')
|
24
|
+
end
|
25
|
+
|
22
26
|
def grounded_regexp(string)
|
23
27
|
# Double any backslashes, then backslash any single-quotes:
|
24
28
|
"'\\A#{string.gsub(/\\/) { '\\\\' }.gsub(/'/) { "\\'"}}'"
|
@@ -5,11 +5,11 @@ module Treetop
|
|
5
5
|
super
|
6
6
|
begin_comment(self)
|
7
7
|
use_vars :result, :start_index
|
8
|
-
compile_alternatives(alternatives)
|
8
|
+
compile_alternatives(alternatives, parent_expression)
|
9
9
|
end_comment(self)
|
10
10
|
end
|
11
|
-
|
12
|
-
def compile_alternatives(alternatives)
|
11
|
+
|
12
|
+
def compile_alternatives(alternatives, parent_expression)
|
13
13
|
obtain_new_subexpression_address
|
14
14
|
alternatives.first.compile(subexpression_address, builder)
|
15
15
|
builder.if__ subexpression_success? do
|
@@ -17,17 +17,21 @@ module Treetop
|
|
17
17
|
builder << "#{subexpression_result_var} = SyntaxNode.new(input, (index-1)...index) if #{subexpression_result_var} == true"
|
18
18
|
assign_result subexpression_result_var
|
19
19
|
extend_result_with_declared_module
|
20
|
-
extend_result_with_inline_module
|
20
|
+
extend_result_with_inline_module parent_expression
|
21
21
|
end
|
22
22
|
builder.else_ do
|
23
23
|
if alternatives.size == 1
|
24
24
|
reset_index
|
25
25
|
assign_failure start_index_var
|
26
26
|
else
|
27
|
-
compile_alternatives(alternatives[1..-1])
|
27
|
+
compile_alternatives(alternatives[1..-1], parent_expression)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
31
|
+
|
32
|
+
def expected
|
33
|
+
'"(any alternative)"'
|
34
|
+
end
|
31
35
|
end
|
32
36
|
end
|
33
37
|
end
|
@@ -6,8 +6,8 @@ module Treetop
|
|
6
6
|
use_vars :result
|
7
7
|
assign_result text_value == 'super' ? 'super' : "_nt_#{text_value}"
|
8
8
|
extend_result_with_declared_module
|
9
|
-
extend_result_with_inline_module
|
9
|
+
extend_result_with_inline_module parent_expression
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
13
|
-
end
|
13
|
+
end
|
@@ -78,8 +78,14 @@ module Treetop
|
|
78
78
|
extend_result declared_module_name if declared_module_name
|
79
79
|
end
|
80
80
|
|
81
|
-
def extend_result_with_inline_module
|
82
|
-
|
81
|
+
def extend_result_with_inline_module parent_expression = nil
|
82
|
+
# debugger if parent_expression && !parent_expression.respond_to?(:parent_modules)
|
83
|
+
if parent_expression && parent_expression.parent_modules.size > 0
|
84
|
+
parent_expression.parent_modules.each do |inline|
|
85
|
+
extend_result inline.module_name
|
86
|
+
end
|
87
|
+
end
|
88
|
+
extend_result inline_module_name if inline_module_name
|
83
89
|
end
|
84
90
|
|
85
91
|
def reset_index
|
@@ -141,6 +147,10 @@ module Treetop
|
|
141
147
|
def on_one_line(expression)
|
142
148
|
expression.text_value.tr("\n", ' ')
|
143
149
|
end
|
150
|
+
|
151
|
+
def expected
|
152
|
+
nil # Overridden for terminal parse failures
|
153
|
+
end
|
144
154
|
end
|
145
155
|
end
|
146
156
|
end
|
@@ -13,6 +13,7 @@ module Treetop
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def assign_failure
|
16
|
+
reset_index
|
16
17
|
super(start_index_var)
|
17
18
|
end
|
18
19
|
|
@@ -35,11 +36,17 @@ module Treetop
|
|
35
36
|
class NotPredicate < Predicate
|
36
37
|
def when_success
|
37
38
|
assign_failure
|
39
|
+
if (e = parent.atomic.expected)
|
40
|
+
builder << "terminal_parse_failure(#{e}, true)"
|
41
|
+
end
|
38
42
|
end
|
39
43
|
|
40
44
|
def when_failure
|
45
|
+
if (e = parent.atomic.expected)
|
46
|
+
builder << "terminal_failures.pop"
|
47
|
+
end
|
41
48
|
assign_success
|
42
49
|
end
|
43
50
|
end
|
44
51
|
end
|
45
|
-
end
|
52
|
+
end
|
@@ -10,6 +10,13 @@ module Treetop
|
|
10
10
|
p = parent
|
11
11
|
p = p.parent while p && !p.respond_to?(:accumulator_var)
|
12
12
|
assign_result "lambda #{text_value}.call(#{p ? p.accumulator_var : ""})"
|
13
|
+
builder.if_ '!'+result_var do
|
14
|
+
builder << "terminal_parse_failure(#{expected})"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def expected
|
19
|
+
'"<semantic predicate>"' # Should I include (some of) the text_value here?
|
13
20
|
end
|
14
21
|
end
|
15
22
|
end
|
@@ -28,9 +28,9 @@ module Treetop
|
|
28
28
|
parent_expression.inline_module_name
|
29
29
|
end
|
30
30
|
|
31
|
-
def assign_and_extend_result
|
31
|
+
def assign_and_extend_result parent_expression
|
32
32
|
assign_result "instantiate_node(#{node_class_name},input, #{start_index_var}...index, #{accumulator_var})"
|
33
|
-
extend_result_with_inline_module
|
33
|
+
extend_result_with_inline_module parent_expression
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
@@ -38,7 +38,7 @@ module Treetop
|
|
38
38
|
class ZeroOrMore < Repetition
|
39
39
|
def compile(address, builder, parent_expression)
|
40
40
|
super
|
41
|
-
assign_and_extend_result
|
41
|
+
assign_and_extend_result parent_expression
|
42
42
|
end_comment(parent_expression)
|
43
43
|
end
|
44
44
|
|
@@ -55,7 +55,7 @@ module Treetop
|
|
55
55
|
assign_failure start_index_var
|
56
56
|
end
|
57
57
|
builder.else_ do
|
58
|
-
assign_and_extend_result
|
58
|
+
assign_and_extend_result parent_expression
|
59
59
|
end
|
60
60
|
end_comment(parent_expression)
|
61
61
|
end
|
@@ -63,26 +63,46 @@ module Treetop
|
|
63
63
|
def max
|
64
64
|
nil
|
65
65
|
end
|
66
|
+
|
67
|
+
def expected
|
68
|
+
parent_expression.atomic.expected && '"at least one "+'+parent_expression.atomic.expected
|
69
|
+
end
|
66
70
|
end
|
67
71
|
|
68
72
|
class OccurrenceRange < Repetition
|
69
73
|
def compile(address, builder, parent_expression)
|
70
74
|
super
|
71
75
|
|
72
|
-
if min.empty?
|
73
|
-
assign_and_extend_result
|
74
|
-
else
|
76
|
+
if !min.empty? && min.text_value.to_i != 0
|
75
77
|
# We got some, but fewer than we wanted. There'll be a failure reported already
|
76
78
|
builder.if__ "#{accumulator_var}.size < #{min.text_value}" do
|
77
79
|
reset_index
|
78
80
|
assign_failure start_index_var
|
79
|
-
|
81
|
+
end
|
80
82
|
builder.else_ do
|
81
|
-
|
82
|
-
|
83
|
-
|
83
|
+
clean_unsaturated
|
84
|
+
assign_and_extend_result parent_expression
|
85
|
+
end
|
86
|
+
else
|
87
|
+
clean_unsaturated
|
88
|
+
assign_and_extend_result parent_expression
|
89
|
+
end
|
90
|
+
|
84
91
|
end_comment(parent_expression)
|
85
92
|
end
|
93
|
+
|
94
|
+
# remove the last terminal_failure if we merely failed to reach the maximum
|
95
|
+
def clean_unsaturated
|
96
|
+
if !max.empty? && max.text_value.to_i > 0
|
97
|
+
builder.if_ "#{accumulator_var}.size < #{max.text_value}" do
|
98
|
+
builder << 'terminal_failures.pop' # Ignore the last failure.
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def expected
|
104
|
+
parent_expression.atomic.expected && "at least #{min.text_value} "+parent_expression.atomic.expected
|
105
|
+
end
|
86
106
|
end
|
87
107
|
|
88
108
|
end
|
@@ -9,7 +9,7 @@ module Treetop
|
|
9
9
|
builder.if__ "#{accumulator_var}.last" do
|
10
10
|
assign_result "instantiate_node(#{node_class_name},input, #{start_index_var}...index, #{accumulator_var})"
|
11
11
|
extend_result sequence_element_accessor_module_name if sequence_element_accessor_module_name
|
12
|
-
extend_result_with_inline_module
|
12
|
+
extend_result_with_inline_module parent_expression
|
13
13
|
end
|
14
14
|
builder.else_ do
|
15
15
|
reset_index
|
@@ -40,6 +40,10 @@ module Treetop
|
|
40
40
|
def sequence_element_accessor_module_name
|
41
41
|
sequence_element_accessor_module.module_name
|
42
42
|
end
|
43
|
+
|
44
|
+
def expected
|
45
|
+
'"<a sequence>"'
|
46
|
+
end
|
43
47
|
end
|
44
48
|
|
45
49
|
class SequenceElementAccessorModule
|
@@ -25,17 +25,27 @@ module Treetop
|
|
25
25
|
builder.if__ "(match_len = has_terminal?(#{str}, #{mode}, index))" do
|
26
26
|
if address == 0 || decorated? || mode != 'false' || string_length > 1
|
27
27
|
assign_result "instantiate_node(#{node_class_name},input, index...(index + match_len))"
|
28
|
-
|
28
|
+
# debugger if parent_expression and parent_expression.inline_modules.size > 0
|
29
|
+
# extend_result_with_inline_module parent_expression
|
30
|
+
if parent_expression
|
31
|
+
parent_expression.inline_modules.each do |inline|
|
32
|
+
extend_result inline.module_name
|
33
|
+
end
|
34
|
+
end
|
29
35
|
else
|
30
36
|
assign_lazily_instantiated_node
|
31
37
|
end
|
32
38
|
builder << "@index += match_len"
|
33
39
|
end
|
34
40
|
builder.else_ do
|
35
|
-
builder << "terminal_parse_failure(#{
|
41
|
+
builder << "terminal_parse_failure(#{expected})"
|
36
42
|
assign_result 'nil'
|
37
43
|
end
|
38
44
|
end
|
45
|
+
|
46
|
+
def expected
|
47
|
+
single_quote(string)
|
48
|
+
end
|
39
49
|
end
|
40
50
|
end
|
41
51
|
end
|
@@ -17,7 +17,12 @@ module Treetop
|
|
17
17
|
@index = options[:index] if options[:index]
|
18
18
|
result = send("_nt_#{options[:root] || root}")
|
19
19
|
should_consume_all = options.include?(:consume_all_input) ? options[:consume_all_input] : consume_all_input?
|
20
|
-
|
20
|
+
if (should_consume_all && index != input.size)
|
21
|
+
if index > max_terminal_failure_index # Otherwise the failure is already explained
|
22
|
+
terminal_parse_failure('<END OF INPUT>', true)
|
23
|
+
end
|
24
|
+
return nil
|
25
|
+
end
|
21
26
|
return SyntaxNode.new(input, index...(index + 1)) if result == true
|
22
27
|
return result
|
23
28
|
end
|
@@ -34,15 +39,16 @@ module Treetop
|
|
34
39
|
@terminal_failures && input.column_of(failure_index)
|
35
40
|
end
|
36
41
|
|
42
|
+
OtherThan = 'something other than '
|
37
43
|
def failure_reason
|
38
44
|
return nil unless (tf = terminal_failures) && tf.size > 0
|
39
45
|
"Expected " +
|
40
46
|
(tf.size == 1 ?
|
41
|
-
tf[0].expected_string :
|
42
|
-
"one of #{tf.map{|f| f.expected_string}.uniq*', '}"
|
47
|
+
(tf[0].unexpected ? OtherThan : '')+tf[0].expected_string :
|
48
|
+
"one of #{tf.map{|f| (f.unexpected ? OtherThan : '')+f.expected_string}.uniq*', '}"
|
43
49
|
) +
|
44
50
|
" at line #{failure_line}, column #{failure_column} (byte #{failure_index+1})" +
|
45
|
-
" after #{input[index...failure_index]}"
|
51
|
+
(failure_index > 0 ? " after #{input[index...failure_index]}" : '')
|
46
52
|
end
|
47
53
|
|
48
54
|
def terminal_failures
|
@@ -106,13 +112,13 @@ module Treetop
|
|
106
112
|
end
|
107
113
|
end
|
108
114
|
|
109
|
-
def terminal_parse_failure(expected_string)
|
115
|
+
def terminal_parse_failure(expected_string, unexpected = false)
|
110
116
|
return nil if index < max_terminal_failure_index
|
111
117
|
if index > max_terminal_failure_index
|
112
118
|
@max_terminal_failure_index = index
|
113
119
|
@terminal_failures = []
|
114
120
|
end
|
115
|
-
@terminal_failures << [index, expected_string]
|
121
|
+
@terminal_failures << [index, expected_string, unexpected]
|
116
122
|
return nil
|
117
123
|
end
|
118
124
|
end
|
@@ -7,7 +7,9 @@ module Treetop
|
|
7
7
|
def initialize(input, interval, elements = nil)
|
8
8
|
@input = input
|
9
9
|
@interval = interval
|
10
|
-
@elements = elements
|
10
|
+
if (@elements = elements)
|
11
|
+
@elements.each { |e| e.equal?(true) or e.parent = self }
|
12
|
+
end
|
11
13
|
end
|
12
14
|
|
13
15
|
def elements
|
@@ -18,8 +20,8 @@ module Treetop
|
|
18
20
|
if element == true
|
19
21
|
index = last_element ? last_element.interval.last : interval.first
|
20
22
|
element = SyntaxNode.new(input, index...(index + 1))
|
23
|
+
element.parent = self
|
21
24
|
end
|
22
|
-
element.parent = self
|
23
25
|
last_element = element
|
24
26
|
end
|
25
27
|
|
@@ -58,7 +60,7 @@ module Treetop
|
|
58
60
|
end
|
59
61
|
end
|
60
62
|
|
61
|
-
def
|
63
|
+
def inspect_self(indent="")
|
62
64
|
em = extension_modules
|
63
65
|
interesting_methods = methods-[em.last ? em.last.methods : nil]-self.class.instance_methods
|
64
66
|
im = interesting_methods.size > 0 ? " (#{interesting_methods.join(",")})" : ""
|
@@ -70,18 +72,25 @@ module Treetop
|
|
70
72
|
em.map{|m| "+"+m.to_s.sub(/.*:/,'')}*"" +
|
71
73
|
" offset=#{interval.first}" +
|
72
74
|
", #{tv.inspect}" +
|
73
|
-
im
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
75
|
+
im
|
76
|
+
end
|
77
|
+
|
78
|
+
def inspect_children(indent="")
|
79
|
+
return '' unless elements && elements.size > 0
|
80
|
+
":" +
|
81
|
+
elements.map do |e|
|
82
|
+
begin
|
83
|
+
"\n"+e.inspect(indent+" ")
|
84
|
+
rescue # Defend against inspect not taking a parameter
|
85
|
+
"\n"+indent+" "+e.inspect
|
86
|
+
end
|
87
|
+
end.
|
88
|
+
join("")
|
89
|
+
end
|
90
|
+
|
91
|
+
def inspect(indent="")
|
92
|
+
inspect_self(indent) +
|
93
|
+
inspect_children(indent)
|
85
94
|
end
|
86
95
|
|
87
96
|
@@dot_id_counter = 0
|
@@ -1,15 +1,16 @@
|
|
1
1
|
module Treetop
|
2
2
|
module Runtime
|
3
3
|
class TerminalParseFailure
|
4
|
-
attr_reader :index, :expected_string
|
4
|
+
attr_reader :index, :expected_string, :unexpected
|
5
5
|
|
6
|
-
def initialize(index, expected_string)
|
6
|
+
def initialize(index, expected_string, unexpected = false)
|
7
7
|
@index = index
|
8
8
|
@expected_string = expected_string
|
9
|
+
@unexpected = unexpected
|
9
10
|
end
|
10
11
|
|
11
12
|
def to_s
|
12
|
-
|
13
|
+
"String matching #{expected_string} #{@unexpected ? 'not ' : ''}expected."
|
13
14
|
end
|
14
15
|
end
|
15
16
|
end
|