line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Pegex::Parser; |
2
|
1
|
|
|
1
|
|
6
|
use Pegex::Base; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
6
|
|
3
|
|
|
|
|
|
|
|
4
|
1
|
|
|
1
|
|
444
|
use Pegex::Input; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
33
|
|
5
|
1
|
|
|
1
|
|
515
|
use Pegex::Optimizer; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
34
|
|
6
|
1
|
|
|
1
|
|
8
|
use Scalar::Util; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
1619
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
has grammar => (required => 1); |
9
|
|
|
|
|
|
|
has receiver => (); |
10
|
|
|
|
|
|
|
has input => (); |
11
|
|
|
|
|
|
|
has debug => ( |
12
|
|
|
|
|
|
|
exists($ENV{PERL_PEGEX_DEBUG}) ? $ENV{PERL_PEGEX_DEBUG} : |
13
|
|
|
|
|
|
|
defined($Pegex::Parser::Debug) ? $Pegex::Parser::Debug : |
14
|
|
|
|
|
|
|
0 |
15
|
|
|
|
|
|
|
); |
16
|
|
|
|
|
|
|
sub BUILD { |
17
|
1
|
|
|
1
|
0
|
14
|
my ($self) = @_; |
18
|
1
|
|
50
|
|
|
10
|
$self->{throw_on_error} ||= 1; |
19
|
|
|
|
|
|
|
# $self->{rule} = undef; |
20
|
|
|
|
|
|
|
# $self->{parent} = undef; |
21
|
|
|
|
|
|
|
# $self->{error} = undef; |
22
|
|
|
|
|
|
|
# $self->{position} = undef; |
23
|
|
|
|
|
|
|
# $self->{farthest} = undef; |
24
|
|
|
|
|
|
|
} |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
# XXX Add an optional $position argument. Default to 0. This is the position |
27
|
|
|
|
|
|
|
# to start parsing. Set position and farthest below to this value. Allows for |
28
|
|
|
|
|
|
|
# sub-parsing. Need to somehow return the finishing position of a subparse. |
29
|
|
|
|
|
|
|
# Maybe this all goes in a subparse() method. |
30
|
|
|
|
|
|
|
sub parse { |
31
|
2
|
|
|
2
|
0
|
6
|
my ($self, $input, $start) = @_; |
32
|
|
|
|
|
|
|
|
33
|
2
|
50
|
|
|
|
10
|
$start =~ s/-/_/g if $start; |
34
|
|
|
|
|
|
|
|
35
|
2
|
|
|
|
|
628
|
$self->{position} = 0; |
36
|
2
|
|
|
|
|
5
|
$self->{farthest} = 0; |
37
|
|
|
|
|
|
|
|
38
|
2
|
50
|
|
|
|
18
|
$self->{input} = (not ref $input) |
39
|
|
|
|
|
|
|
? Pegex::Input->new(string => $input) |
40
|
|
|
|
|
|
|
: $input; |
41
|
|
|
|
|
|
|
|
42
|
2
|
50
|
|
|
|
24
|
$self->{input}->open |
43
|
|
|
|
|
|
|
unless $self->{input}{_is_open}; |
44
|
2
|
|
|
|
|
8
|
$self->{buffer} = $self->{input}->read; |
45
|
|
|
|
|
|
|
|
46
|
2
|
50
|
|
|
|
6
|
die "No 'grammar'. Can't parse" |
47
|
|
|
|
|
|
|
unless $self->{grammar}; |
48
|
|
|
|
|
|
|
|
49
|
2
|
|
33
|
|
|
8
|
$self->{grammar}{tree} ||= $self->{grammar}->make_tree; |
50
|
|
|
|
|
|
|
|
51
|
2
|
50
|
33
|
|
|
10
|
my $start_rule_ref = $start || |
52
|
|
|
|
|
|
|
$self->{grammar}{tree}{'+toprule'} || |
53
|
|
|
|
|
|
|
$self->{grammar}{tree}{'TOP'} & 'TOP' or |
54
|
|
|
|
|
|
|
die "No starting rule for Pegex::Parser::parse"; |
55
|
|
|
|
|
|
|
|
56
|
2
|
50
|
|
|
|
14
|
die "No 'receiver'. Can't parse" |
57
|
|
|
|
|
|
|
unless $self->{receiver}; |
58
|
|
|
|
|
|
|
|
59
|
2
|
|
|
|
|
13
|
my $optimizer = Pegex::Optimizer->new( |
60
|
|
|
|
|
|
|
parser => $self, |
61
|
|
|
|
|
|
|
grammar => $self->{grammar}, |
62
|
|
|
|
|
|
|
receiver => $self->{receiver}, |
63
|
|
|
|
|
|
|
); |
64
|
|
|
|
|
|
|
|
65
|
2
|
|
|
|
|
10
|
$optimizer->optimize_grammar($start_rule_ref); |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# Add circular ref and weaken it. |
68
|
2
|
|
|
|
|
6
|
$self->{receiver}{parser} = $self; |
69
|
2
|
|
|
|
|
14
|
Scalar::Util::weaken($self->{receiver}{parser}); |
70
|
|
|
|
|
|
|
|
71
|
2
|
50
|
|
|
|
15
|
if ($self->{receiver}->can("initial")) { |
72
|
0
|
|
|
|
|
0
|
$self->{rule} = $start_rule_ref; |
73
|
0
|
|
|
|
|
0
|
$self->{parent} = {}; |
74
|
0
|
|
|
|
|
0
|
$self->{receiver}->initial(); |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
|
77
|
2
|
50
|
|
|
|
10
|
my $match = $self->debug ? do { |
78
|
0
|
|
|
|
|
0
|
my $method = $optimizer->make_trace_wrapper(\&match_ref); |
79
|
0
|
|
|
|
|
0
|
$self->$method($start_rule_ref, {'+asr' => 0}); |
80
|
|
|
|
|
|
|
} : $self->match_ref($start_rule_ref, {}); |
81
|
|
|
|
|
|
|
|
82
|
2
|
|
|
|
|
21
|
$self->{input}->close; |
83
|
|
|
|
|
|
|
|
84
|
2
|
50
|
33
|
|
|
20
|
if (not $match or $self->{position} < length ${$self->{buffer}}) { |
|
2
|
|
|
|
|
12
|
|
85
|
0
|
|
|
|
|
0
|
$self->throw_error("Parse document failed for some reason"); |
86
|
0
|
|
|
|
|
0
|
return; # In case $self->throw_on_error is off |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
|
89
|
2
|
50
|
|
|
|
24
|
if ($self->{receiver}->can("final")) { |
90
|
2
|
|
|
|
|
4
|
$self->{rule} = $start_rule_ref; |
91
|
2
|
|
|
|
|
5
|
$self->{parent} = {}; |
92
|
2
|
|
|
|
|
10
|
$match = [ $self->{receiver}->final(@$match) ]; |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
|
95
|
2
|
|
|
|
|
229
|
$match->[0]; |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
sub match_next { |
99
|
579
|
|
|
579
|
0
|
684
|
my ($self, $next) = @_; |
100
|
|
|
|
|
|
|
|
101
|
579
|
|
|
|
|
1370
|
my ($rule, $method, $kind, $min, $max, $assertion) = |
102
|
579
|
|
|
|
|
600
|
@{$next}{'rule', 'method', 'kind', '+min', '+max', '+asr'}; |
103
|
|
|
|
|
|
|
|
104
|
579
|
|
|
|
|
997
|
my ($position, $match, $count) = |
105
|
|
|
|
|
|
|
($self->{position}, [], 0); |
106
|
|
|
|
|
|
|
|
107
|
579
|
|
|
|
|
946
|
while (my $return = $method->($self, $rule, $next)) { |
108
|
303
|
50
|
|
|
|
591
|
$position = $self->{position} unless $assertion; |
109
|
303
|
|
|
|
|
269
|
$count++; |
110
|
303
|
|
|
|
|
423
|
push @$match, @$return; |
111
|
303
|
100
|
|
|
|
637
|
last if $max == 1; |
112
|
|
|
|
|
|
|
} |
113
|
579
|
50
|
100
|
|
|
1833
|
if (not $count and $min == 0 and $kind eq 'all') { |
|
|
|
66
|
|
|
|
|
114
|
0
|
|
|
|
|
0
|
$match = [[]]; |
115
|
|
|
|
|
|
|
} |
116
|
579
|
100
|
|
|
|
916
|
if ($max != 1) { |
117
|
20
|
50
|
|
|
|
49
|
if ($next->{-flat}) { |
118
|
0
|
0
|
|
|
|
0
|
$match = [ map { (ref($_) eq 'ARRAY') ? (@$_) : ($_) } @$match ]; |
|
0
|
|
|
|
|
0
|
|
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
else { |
121
|
20
|
|
|
|
|
33
|
$match = [$match] |
122
|
|
|
|
|
|
|
} |
123
|
20
|
50
|
|
|
|
51
|
$self->{farthest} = $position |
124
|
|
|
|
|
|
|
if ($self->{position} = $position) > $self->{farthest}; |
125
|
|
|
|
|
|
|
} |
126
|
579
|
|
66
|
|
|
1804
|
my $result = ($count >= $min and (not $max or $count <= $max)) |
127
|
|
|
|
|
|
|
^ ($assertion == -1); |
128
|
579
|
100
|
100
|
|
|
1585
|
if (not($result) or $assertion) { |
129
|
269
|
50
|
|
|
|
573
|
$self->{farthest} = $position |
130
|
|
|
|
|
|
|
if ($self->{position} = $position) > $self->{farthest}; |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
579
|
100
|
|
|
|
2380
|
($result ? $next->{'-skip'} ? [] : $match : 0); |
|
|
100
|
|
|
|
|
|
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
sub match_rule { |
137
|
0
|
|
|
0
|
0
|
0
|
my ($self, $position, $match) = (@_, []); |
138
|
0
|
|
|
|
|
0
|
$self->{position} = $position; |
139
|
0
|
0
|
|
|
|
0
|
$self->{farthest} = $position |
140
|
|
|
|
|
|
|
if $position > $self->{farthest}; |
141
|
0
|
0
|
|
|
|
0
|
$match = [ $match ] if @$match > 1; |
142
|
0
|
|
|
|
|
0
|
my ($ref, $parent) = @{$self}{'rule', 'parent'}; |
|
0
|
|
|
|
|
0
|
|
143
|
0
|
0
|
|
|
|
0
|
my $rule = $self->{grammar}{tree}{$ref} |
144
|
|
|
|
|
|
|
or die "No rule defined for '$ref'"; |
145
|
|
|
|
|
|
|
|
146
|
0
|
|
|
|
|
0
|
[ $rule->{action}->($self->{receiver}, @$match) ]; |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
sub match_ref { |
150
|
293
|
|
|
293
|
0
|
341
|
my ($self, $ref, $parent) = @_; |
151
|
293
|
50
|
|
|
|
765
|
my $rule = $self->{grammar}{tree}{$ref} |
152
|
|
|
|
|
|
|
or die "No rule defined for '$ref'"; |
153
|
293
|
100
|
|
|
|
515
|
my $match = $self->match_next($rule) or return; |
154
|
148
|
50
|
|
|
|
291
|
return $Pegex::Constant::Dummy unless $rule->{action}; |
155
|
148
|
|
|
|
|
161
|
@{$self}{'rule', 'parent'} = ($ref, $parent); |
|
148
|
|
|
|
|
342
|
|
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# XXX Possible API mismatch. |
158
|
|
|
|
|
|
|
# Not sure if we should "splat" the $match. |
159
|
148
|
|
|
|
|
493
|
[ $rule->{action}->($self->{receiver}, @$match) ]; |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
sub match_rgx { |
163
|
174
|
|
|
174
|
0
|
205
|
my ($self, $regexp) = @_; |
164
|
174
|
|
|
|
|
210
|
my $buffer = $self->{buffer}; |
165
|
|
|
|
|
|
|
|
166
|
174
|
|
|
|
|
344
|
pos($$buffer) = $self->{position}; |
167
|
174
|
100
|
|
|
|
1074
|
$$buffer =~ /$regexp/g or return; |
168
|
|
|
|
|
|
|
|
169
|
85
|
|
|
|
|
132
|
$self->{position} = pos($$buffer); |
170
|
|
|
|
|
|
|
|
171
|
85
|
50
|
|
|
|
214
|
$self->{farthest} = $self->{position} |
172
|
|
|
|
|
|
|
if $self->{position} > $self->{farthest}; |
173
|
|
|
|
|
|
|
|
174
|
1
|
|
|
1
|
|
8
|
no strict 'refs'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
1190
|
|
175
|
85
|
|
|
|
|
297
|
my $captures = [ map $$_, 1..$#+ ]; |
176
|
85
|
50
|
|
|
|
193
|
$captures = [ $captures ] if $#+ > 1; |
177
|
|
|
|
|
|
|
|
178
|
85
|
|
|
|
|
228
|
return $captures; |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
sub match_all { |
182
|
86
|
|
|
86
|
0
|
91
|
my ($self, $list) = @_; |
183
|
86
|
|
|
|
|
88
|
my $position = $self->{position}; |
184
|
86
|
|
|
|
|
96
|
my $set = []; |
185
|
86
|
|
|
|
|
95
|
my $len = 0; |
186
|
86
|
|
|
|
|
119
|
for my $elem (@$list) { |
187
|
183
|
100
|
|
|
|
362
|
if (my $match = $self->match_next($elem)) { |
188
|
139
|
100
|
100
|
|
|
547
|
if (not ($elem->{'+asr'} or $elem->{'-skip'})) { |
189
|
125
|
|
|
|
|
170
|
push @$set, @$match; |
190
|
125
|
|
|
|
|
282
|
$len++; |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
else { |
194
|
44
|
50
|
|
|
|
89
|
$self->{farthest} = $position |
195
|
|
|
|
|
|
|
if ($self->{position} = $position) > $self->{farthest}; |
196
|
44
|
|
|
|
|
124
|
return; |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
} |
199
|
42
|
50
|
|
|
|
115
|
$set = [ $set ] if $len > 1; |
200
|
42
|
|
|
|
|
114
|
return $set; |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
sub match_any { |
204
|
52
|
|
|
52
|
0
|
57
|
my ($self, $list) = @_; |
205
|
52
|
|
|
|
|
70
|
for my $elem (@$list) { |
206
|
103
|
100
|
|
|
|
188
|
if (my $match = $self->match_next($elem)) { |
207
|
30
|
|
|
|
|
82
|
return $match; |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
} |
210
|
22
|
|
|
|
|
57
|
return; |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
sub match_err { |
214
|
0
|
|
|
0
|
0
|
|
my ($self, $error) = @_; |
215
|
0
|
|
|
|
|
|
$self->throw_error($error); |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
sub trace { |
219
|
0
|
|
|
0
|
0
|
|
my ($self, $action) = @_; |
220
|
0
|
0
|
|
|
|
|
my $indent = ($action =~ /^try_/) ? 1 : 0; |
221
|
0
|
|
0
|
|
|
|
$self->{indent} ||= 0; |
222
|
0
|
0
|
|
|
|
|
$self->{indent}-- unless $indent; |
223
|
0
|
|
|
|
|
|
print STDERR ' ' x $self->{indent}; |
224
|
0
|
0
|
|
|
|
|
$self->{indent}++ if $indent; |
225
|
0
|
|
|
|
|
|
my $snippet = substr(${$self->{buffer}}, $self->{position}); |
|
0
|
|
|
|
|
|
|
226
|
0
|
0
|
|
|
|
|
$snippet = substr($snippet, 0, 30) . "..." |
227
|
|
|
|
|
|
|
if length $snippet > 30; |
228
|
0
|
|
|
|
|
|
$snippet =~ s/\n/\\n/g; |
229
|
0
|
0
|
|
|
|
|
print STDERR sprintf("%-30s", $action) . |
230
|
|
|
|
|
|
|
($indent ? " >$snippet<\n" : "\n"); |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
sub throw_error { |
234
|
0
|
|
|
0
|
0
|
|
my ($self, $msg) = @_; |
235
|
0
|
|
|
|
|
|
$@ = $self->{error} = $self->format_error($msg); |
236
|
0
|
0
|
|
|
|
|
return undef unless $self->{throw_on_error}; |
237
|
0
|
|
|
|
|
|
require Carp; |
238
|
0
|
|
|
|
|
|
Carp::croak($self->{error}); |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
sub format_error { |
242
|
0
|
|
|
0
|
0
|
|
my ($self, $msg) = @_; |
243
|
0
|
|
|
|
|
|
my $buffer = $self->{buffer}; |
244
|
0
|
|
|
|
|
|
my $position = $self->{farthest}; |
245
|
0
|
|
|
|
|
|
my $real_pos = $self->{position}; |
246
|
|
|
|
|
|
|
|
247
|
0
|
|
|
|
|
|
my $line = @{[substr($$buffer, 0, $position) =~ /(\n)/g]} + 1; |
|
0
|
|
|
|
|
|
|
248
|
0
|
|
|
|
|
|
my $column = $position - rindex($$buffer, "\n", $position); |
249
|
|
|
|
|
|
|
|
250
|
0
|
0
|
|
|
|
|
my $pretext = substr( |
|
|
0
|
|
|
|
|
|
251
|
|
|
|
|
|
|
$$buffer, |
252
|
|
|
|
|
|
|
$position < 50 ? 0 : $position - 50, |
253
|
|
|
|
|
|
|
$position < 50 ? $position : 50 |
254
|
|
|
|
|
|
|
); |
255
|
0
|
|
|
|
|
|
my $context = substr($$buffer, $position, 50); |
256
|
0
|
|
|
|
|
|
$pretext =~ s/.*\n//gs; |
257
|
0
|
|
|
|
|
|
$context =~ s/\n/\\n/g; |
258
|
|
|
|
|
|
|
|
259
|
0
|
|
|
|
|
|
return <<"..."; |
260
|
|
|
|
|
|
|
Error parsing Pegex document: |
261
|
0
|
|
|
|
|
|
msg: $msg |
262
|
|
|
|
|
|
|
line: $line |
263
|
|
|
|
|
|
|
column: $column |
264
|
|
|
|
|
|
|
context: $pretext$context |
265
|
|
|
|
|
|
|
${\ (' ' x (length($pretext) + 10) . '^')} |
266
|
|
|
|
|
|
|
position: $position ($real_pos pre-lookahead) |
267
|
|
|
|
|
|
|
... |
268
|
|
|
|
|
|
|
} |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
# TODO Move this to a Parser helper role/subclass |
271
|
|
|
|
|
|
|
sub line_column { |
272
|
0
|
|
|
0
|
0
|
|
my ($self, $position) = @_; |
273
|
0
|
|
0
|
|
|
|
$position ||= $self->{position}; |
274
|
0
|
|
|
|
|
|
my $buffer = $self->{buffer}; |
275
|
0
|
|
|
|
|
|
my $line = @{[substr($$buffer, 0, $position) =~ /(\n)/g]} + 1; |
|
0
|
|
|
|
|
|
|
276
|
0
|
|
|
|
|
|
my $column = $position - rindex($$buffer, "\n", $position); |
277
|
0
|
|
|
|
|
|
return [$line, $position]; |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# XXX Need to figure out what uses this. (sample.t) |
281
|
|
|
|
|
|
|
{ |
282
|
|
|
|
|
|
|
package Pegex::Constant; |
283
|
|
|
|
|
|
|
our $Null = []; |
284
|
|
|
|
|
|
|
our $Dummy = []; |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
1; |