treetop 1.5.3 → 1.6.2
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.
- 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
|