| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
############################################################################ |
|
2
|
|
|
|
|
|
|
# Generate flowcharts from Regexp debug dumpes |
|
3
|
|
|
|
|
|
|
# |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
package Graph::Regexp; |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
require 5.008001; |
|
8
|
6
|
|
|
6
|
|
597783
|
use Graph::Easy; |
|
|
6
|
|
|
|
|
1009889
|
|
|
|
6
|
|
|
|
|
262
|
|
|
9
|
6
|
|
|
6
|
|
73
|
use Graph::Easy::Base; |
|
|
6
|
|
|
|
|
12
|
|
|
|
6
|
|
|
|
|
248
|
|
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
$VERSION = 0.05; |
|
12
|
|
|
|
|
|
|
@ISA = qw/Graph::Easy::Base/; |
|
13
|
|
|
|
|
|
|
|
|
14
|
6
|
|
|
6
|
|
31
|
use strict; |
|
|
6
|
|
|
|
|
13
|
|
|
|
6
|
|
|
|
|
227
|
|
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
# Perl 5.8.8, might be different for 5.10? |
|
17
|
6
|
|
|
6
|
|
33
|
use constant MAX_MATCHES => 32767; |
|
|
6
|
|
|
|
|
9
|
|
|
|
6
|
|
|
|
|
1616
|
|
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
############################################################################# |
|
20
|
|
|
|
|
|
|
############################################################################# |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
sub _init |
|
23
|
|
|
|
|
|
|
{ |
|
24
|
20
|
|
|
20
|
|
2265
|
my ($self, $args) = @_; |
|
25
|
|
|
|
|
|
|
|
|
26
|
20
|
|
|
|
|
78
|
$self->{options} = {}; |
|
27
|
20
|
|
50
|
|
|
158
|
$self->{debug} = $args->{debug} || 0; |
|
28
|
20
|
|
|
|
|
84
|
$self->reset(); |
|
29
|
20
|
|
|
|
|
108
|
$self; |
|
30
|
|
|
|
|
|
|
} |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
sub option |
|
33
|
|
|
|
|
|
|
{ |
|
34
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
35
|
0
|
|
|
|
|
0
|
$self->{options}->{$_[0]}; |
|
36
|
|
|
|
|
|
|
} |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
sub graph |
|
39
|
|
|
|
|
|
|
{ |
|
40
|
|
|
|
|
|
|
# decompose regexp dump and return as Graph::Easy object |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# allow Graph::Regexp->graph() calling style |
|
43
|
19
|
|
|
19
|
1
|
178605
|
my $class = 'Graph::Regexp'; |
|
44
|
19
|
50
|
|
|
|
90
|
$class = shift if @_ == 2; $class = ref($class) if ref($class); |
|
|
19
|
100
|
|
|
|
66
|
|
|
45
|
19
|
|
|
|
|
37
|
my $code = shift; |
|
46
|
|
|
|
|
|
|
|
|
47
|
19
|
|
|
|
|
146
|
my $self = $class->new(); |
|
48
|
19
|
|
|
|
|
53
|
$self->reset(); |
|
49
|
19
|
|
|
|
|
71
|
$self->parse($code); |
|
50
|
|
|
|
|
|
|
|
|
51
|
19
|
|
|
|
|
269
|
$self->{graph}; # return the Graph::Easy object |
|
52
|
|
|
|
|
|
|
} |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
sub as_graph |
|
55
|
|
|
|
|
|
|
{ |
|
56
|
|
|
|
|
|
|
# return the internal Graph::Easy object |
|
57
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
58
|
|
|
|
|
|
|
|
|
59
|
0
|
|
|
|
|
0
|
$self->{graph}; |
|
60
|
|
|
|
|
|
|
} |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub as_ascii |
|
63
|
|
|
|
|
|
|
{ |
|
64
|
|
|
|
|
|
|
# return the graph as ASCII |
|
65
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
66
|
|
|
|
|
|
|
|
|
67
|
0
|
|
|
|
|
0
|
$self->{graph}->as_ascii(); |
|
68
|
|
|
|
|
|
|
} |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
BEGIN |
|
71
|
|
|
|
|
|
|
{ |
|
72
|
|
|
|
|
|
|
# make an alias for decompose |
|
73
|
6
|
|
|
6
|
|
24103
|
*decompose = \&parse; |
|
74
|
|
|
|
|
|
|
} |
|
75
|
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
sub parse |
|
77
|
|
|
|
|
|
|
{ |
|
78
|
20
|
|
|
20
|
1
|
2417
|
my ($self, $doc) = @_; |
|
79
|
|
|
|
|
|
|
|
|
80
|
20
|
|
|
|
|
53
|
$self->reset(); # clear data |
|
81
|
|
|
|
|
|
|
|
|
82
|
20
|
50
|
33
|
|
|
143
|
$self->_croak("Expected SCALAR ref, but got " . ref($doc)) |
|
83
|
|
|
|
|
|
|
if ref($doc) && ref($doc) ne 'SCALAR'; |
|
84
|
|
|
|
|
|
|
|
|
85
|
20
|
0
|
33
|
|
|
61
|
$self->_croak("Got filename '$doc', but can't read it: $!") |
|
86
|
|
|
|
|
|
|
if !ref($doc) && !-f $doc; |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
# XXX TODO: filenames |
|
89
|
|
|
|
|
|
|
|
|
90
|
20
|
|
|
|
|
115
|
$self->_parse($$doc); |
|
91
|
|
|
|
|
|
|
|
|
92
|
20
|
|
|
|
|
40
|
$self; |
|
93
|
|
|
|
|
|
|
} |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
sub reset |
|
96
|
|
|
|
|
|
|
{ |
|
97
|
|
|
|
|
|
|
# reset the internal structure |
|
98
|
59
|
|
|
59
|
1
|
79
|
my $self = shift; |
|
99
|
|
|
|
|
|
|
|
|
100
|
59
|
|
|
|
|
91
|
delete $self->{fail}; |
|
101
|
59
|
|
|
|
|
82
|
delete $self->{success}; |
|
102
|
59
|
|
|
|
|
201
|
$self->{graph} = Graph::Easy->new(); |
|
103
|
|
|
|
|
|
|
|
|
104
|
59
|
|
|
|
|
4290
|
$self->{stack} = []; |
|
105
|
59
|
|
|
|
|
1135
|
$self->{entries} = {}; |
|
106
|
|
|
|
|
|
|
|
|
107
|
59
|
|
|
|
|
86
|
$self; |
|
108
|
|
|
|
|
|
|
} |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
sub graph_label |
|
111
|
|
|
|
|
|
|
{ |
|
112
|
|
|
|
|
|
|
# get/set the label of the graph |
|
113
|
0
|
|
|
0
|
1
|
0
|
my ($self) = shift; |
|
114
|
|
|
|
|
|
|
|
|
115
|
0
|
|
|
|
|
0
|
my $g = $self->{graph}; |
|
116
|
0
|
0
|
|
|
|
0
|
if (@_ > 0) |
|
117
|
|
|
|
|
|
|
{ |
|
118
|
0
|
|
|
|
|
0
|
$g->set_attribute('label',$_[0]); |
|
119
|
|
|
|
|
|
|
} |
|
120
|
0
|
|
|
|
|
0
|
$g->label(); |
|
121
|
|
|
|
|
|
|
} |
|
122
|
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
############################################################################# |
|
124
|
|
|
|
|
|
|
############################################################################# |
|
125
|
|
|
|
|
|
|
# main parse routine, recursive |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
sub _setup_nodeclass |
|
128
|
|
|
|
|
|
|
{ |
|
129
|
|
|
|
|
|
|
# add the attributes for one node class |
|
130
|
220
|
|
|
220
|
|
374
|
my ($self, $class, $title, $label) = @_; |
|
131
|
|
|
|
|
|
|
|
|
132
|
220
|
|
|
|
|
320
|
my $g = $self->{graph}; |
|
133
|
|
|
|
|
|
|
|
|
134
|
220
|
|
|
|
|
760
|
$g->set_attribute("node.$class", 'title', $title); |
|
135
|
220
|
|
|
|
|
17140
|
$g->set_attribute("node.$class", 'label', $label); |
|
136
|
|
|
|
|
|
|
} |
|
137
|
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
sub _parse |
|
139
|
|
|
|
|
|
|
{ |
|
140
|
|
|
|
|
|
|
# take the regexp string and decompose it into a tree, then turn this into |
|
141
|
|
|
|
|
|
|
# a graph. |
|
142
|
20
|
|
|
20
|
|
41
|
my ($self, $text) = @_; |
|
143
|
|
|
|
|
|
|
|
|
144
|
20
|
|
|
|
|
40
|
my $g = $self->{graph}; |
|
145
|
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
# add the start node |
|
147
|
20
|
|
|
|
|
88
|
my $root = $g->add_node('0'); |
|
148
|
20
|
|
|
|
|
1312
|
$g->set_attribute('root','0'); # the first node is the root |
|
149
|
20
|
|
|
|
|
2549
|
$root->set_attribute('label','START'); |
|
150
|
20
|
|
|
|
|
1775
|
$root->set_attribute('class','start'); |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# add the final fail and success nodes |
|
153
|
20
|
|
|
|
|
2050
|
$self->{fail} = $g->add_node('FAIL'); |
|
154
|
20
|
|
|
|
|
887
|
$self->{success} = $g->add_node('SUCCESS'); |
|
155
|
20
|
|
|
|
|
946
|
$self->{fail}->set_attribute('class','fail'); |
|
156
|
20
|
|
|
|
|
1638
|
$self->{success}->set_attribute('class','success'); |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
# this is a hack to workaround that Graph::Easy has not yet "end => '0'" for edges |
|
159
|
20
|
|
|
|
|
1769
|
$self->{fail}->set_attribute('origin','SUCCESS'); |
|
160
|
20
|
|
|
|
|
2280
|
$self->{fail}->set_attribute('offset','0,2'); |
|
161
|
|
|
|
|
|
|
|
|
162
|
20
|
|
|
|
|
1863
|
$g->set_attribute('node.nothing', 'label', "\\''"); |
|
163
|
20
|
|
|
|
|
1620
|
$g->set_attribute('node.nothing', 'title', "Nothing (always matches)"); |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
# Special nodes: |
|
166
|
|
|
|
|
|
|
# ^ (BOL) |
|
167
|
|
|
|
|
|
|
# $ (EOL) |
|
168
|
|
|
|
|
|
|
# \z (EOS) |
|
169
|
|
|
|
|
|
|
# \Z (SEOL) |
|
170
|
|
|
|
|
|
|
# \A (SBOL) |
|
171
|
|
|
|
|
|
|
# \b \B (BOUND, NBOUND) |
|
172
|
|
|
|
|
|
|
# \d \D (DIGIT, NDIGIT) |
|
173
|
|
|
|
|
|
|
# \w \W (ALNUM, NALNUM) |
|
174
|
|
|
|
|
|
|
|
|
175
|
20
|
|
|
|
|
1485
|
$self->_setup_nodeclass('bol', 'BOL (Begin Of Line)', '^'); |
|
176
|
20
|
|
|
|
|
1410
|
$self->_setup_nodeclass('eol', 'EOL (End Of Line)', '$'); |
|
177
|
20
|
|
|
|
|
1436
|
$self->_setup_nodeclass('eos', 'EOS (End Of String)', '\\z'); |
|
178
|
20
|
|
|
|
|
1518
|
$self->_setup_nodeclass('seol', 'SEOL (String end or End Of Line)', '\\Z'); |
|
179
|
20
|
|
|
|
|
9910
|
$self->_setup_nodeclass('sbol', 'SBOL (String begin or Begin Of Line)', '\\A'); |
|
180
|
20
|
|
|
|
|
1512
|
$self->_setup_nodeclass('bound', 'BOUND (Boundary)', '\\b'); |
|
181
|
20
|
|
|
|
|
1497
|
$self->_setup_nodeclass('nbound', 'NBOUND (Non-boundary)', '\\B'); |
|
182
|
20
|
|
|
|
|
1462
|
$self->_setup_nodeclass('digit', 'DIGIT (Digit)', '\\d'); |
|
183
|
20
|
|
|
|
|
1463
|
$self->_setup_nodeclass('ndigit', 'NDIGIT (Non-digit)', '\\D'); |
|
184
|
20
|
|
|
|
|
1496
|
$self->_setup_nodeclass('alnum', 'ALNUM (Alphanumeric)', '\\w'); |
|
185
|
20
|
|
|
|
|
1471
|
$self->_setup_nodeclass('nalnum', 'NALNUM (Non-alphanumeric)', '\\W'); |
|
186
|
|
|
|
|
|
|
|
|
187
|
20
|
|
|
|
|
1557
|
$g->set_attributes('node.fail', { fill => 'darkred', color => 'white' } ); |
|
188
|
20
|
|
|
|
|
4404
|
$g->set_attributes('node.success', { fill => 'darkgreen', color => 'white' } ); |
|
189
|
|
|
|
|
|
|
|
|
190
|
20
|
|
|
|
|
3886
|
$g->set_attributes('edge.match', { |
|
191
|
|
|
|
|
|
|
'label' => 'match', |
|
192
|
|
|
|
|
|
|
'color' => 'darkgreen' |
|
193
|
|
|
|
|
|
|
} ); |
|
194
|
20
|
|
|
|
|
3786
|
$g->set_attributes('edge.always', { |
|
195
|
|
|
|
|
|
|
'label' => 'always', |
|
196
|
|
|
|
|
|
|
} ); |
|
197
|
20
|
|
|
|
|
1955
|
$g->set_attributes('edge.fail', { |
|
198
|
|
|
|
|
|
|
'label' => 'fail', |
|
199
|
|
|
|
|
|
|
'color' => 'darkred' |
|
200
|
|
|
|
|
|
|
} ); |
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
# The general family of this object. These are any of: |
|
203
|
|
|
|
|
|
|
# alnum, anchor, anyof, anyof_char, anyof_class, anyof_range, |
|
204
|
|
|
|
|
|
|
# assertion, bol, branch, close, clump, digit, exact, flags, group, groupp, |
|
205
|
|
|
|
|
|
|
# minmod, open, prop, sol, eol, seol, sbol, quant, ref, reg_any, |
|
206
|
|
|
|
|
|
|
# star, plus ... |
|
207
|
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
# first we parse the following text: |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# 1: OPEN1(3) |
|
211
|
|
|
|
|
|
|
# 3: BRANCH(6) |
|
212
|
|
|
|
|
|
|
# 4: EXACT (9) |
|
213
|
|
|
|
|
|
|
# 6: BRANCH(9) |
|
214
|
|
|
|
|
|
|
# 7: EXACT (9) |
|
215
|
|
|
|
|
|
|
# 9: CLOSE1(11) |
|
216
|
|
|
|
|
|
|
# 11: EXACT (13) |
|
217
|
|
|
|
|
|
|
# 13: PLUS(16) |
|
218
|
|
|
|
|
|
|
# 14: EXACT (0) |
|
219
|
|
|
|
|
|
|
# 16: EXACT <1>(18) |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# into entries like: |
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
# { id => 1, level => 0, type => "open", next => 3, id => 1, } |
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
# to preserve the entries in their original order |
|
226
|
20
|
|
|
|
|
3735
|
my $stack = $self->{stack}; |
|
227
|
|
|
|
|
|
|
# to quickly find entries by their id |
|
228
|
20
|
|
|
|
|
41
|
my $entries = $self->{entries}; |
|
229
|
|
|
|
|
|
|
|
|
230
|
20
|
|
|
|
|
85
|
$text =~ s/[\r\n]\z//; |
|
231
|
|
|
|
|
|
|
|
|
232
|
20
|
50
|
|
|
|
78
|
print STDERR "# Input: \n# '$text'\n" if $self->{debug}; |
|
233
|
|
|
|
|
|
|
|
|
234
|
20
|
|
|
|
|
88
|
my @lines = split /\n/, $text; my $index = 0; |
|
|
20
|
|
|
|
|
34
|
|
|
235
|
20
|
|
|
|
|
41
|
for my $line (@lines) |
|
236
|
|
|
|
|
|
|
{ |
|
237
|
|
|
|
|
|
|
# ignore all other lines |
|
238
|
65
|
100
|
|
|
|
283
|
next unless $line =~ /^\s+(\d+):(\s+)[A-Z]/; |
|
239
|
|
|
|
|
|
|
|
|
240
|
63
|
50
|
|
|
|
142
|
print STDERR "# Parsing line: '$line'\n" if $self->{debug} > 1; |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
# level: ' ' => 0, ' ' => 1 etc |
|
243
|
63
|
|
|
|
|
329
|
my $entry = { level => (length($2)-1) / 2, id => $1 }; |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# "7: EXACT (9)" => "EXACT (9)" |
|
246
|
63
|
|
|
|
|
236
|
$line =~ s/^\s+\d+:\s+//; |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# OPEN1(3) or OPEN1 (3) |
|
249
|
63
|
100
|
|
|
|
404
|
if ($line =~ /^([A-Z][A-Z0-9]+)\s*\((\d+)\)/) |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
{ |
|
251
|
44
|
|
|
|
|
124
|
$entry->{class} = lc($1); |
|
252
|
44
|
|
|
|
|
113
|
$entry->{next} = $2; |
|
253
|
44
|
|
|
|
|
82
|
$entry->{exact} = ''; |
|
254
|
|
|
|
|
|
|
} |
|
255
|
|
|
|
|
|
|
# EXACT (16) or EXACT (16) |
|
256
|
|
|
|
|
|
|
elsif ($line =~ /^([A-Z][A-Z0-9-]+)(\s*<(.+)>)?\s*\((\d+)\)/) |
|
257
|
|
|
|
|
|
|
{ |
|
258
|
16
|
|
|
|
|
57
|
$entry->{class} = lc($1); |
|
259
|
16
|
|
|
|
|
33
|
my $t = $3; |
|
260
|
16
|
|
|
|
|
49
|
$entry->{next} = $4; |
|
261
|
16
|
|
|
|
|
107
|
$t =~ s/(\$|\@|\\)/\\$1/g; # quote $, @ and \ |
|
262
|
16
|
|
|
|
|
59
|
$entry->{exact} = "\\\"$t\\\""; |
|
263
|
16
|
|
|
|
|
46
|
$entry->{title} = "EXACT <$t>"; |
|
264
|
|
|
|
|
|
|
} |
|
265
|
|
|
|
|
|
|
# TRIE-EXACT [bo](9) |
|
266
|
|
|
|
|
|
|
elsif ($line =~ /^TRIE-EXACT\s*(\[([^\]]+)\])\s*?\((\d+)\)/) |
|
267
|
|
|
|
|
|
|
{ |
|
268
|
1
|
|
|
|
|
3
|
$entry->{class} = 'trie'; |
|
269
|
1
|
|
|
|
|
6
|
$entry->{title} = "TRIE-EXACT <$1>"; |
|
270
|
1
|
|
|
|
|
3
|
$entry->{exact} = "$1"; |
|
271
|
1
|
|
|
|
|
3
|
$entry->{next} = $2; |
|
272
|
|
|
|
|
|
|
} |
|
273
|
|
|
|
|
|
|
# ANYOF[ab](8) |
|
274
|
|
|
|
|
|
|
elsif ($line =~ /^([A-Z][A-Z0-9-]+)\s*(\[([^\]]+)\])\s*?\((\d+)\)/) |
|
275
|
|
|
|
|
|
|
{ |
|
276
|
2
|
|
|
|
|
8
|
$entry->{class} = lc($1); |
|
277
|
2
|
50
|
|
|
|
8
|
if ($entry->{class} eq 'anyof') |
|
|
|
0
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
{ |
|
279
|
2
|
|
|
|
|
11
|
$entry->{exact} = "[$3]"; |
|
280
|
|
|
|
|
|
|
} |
|
281
|
|
|
|
|
|
|
elsif ($entry->{class} eq 'nothing') |
|
282
|
|
|
|
|
|
|
{ |
|
283
|
0
|
|
|
|
|
0
|
$entry->{exact} = "[$3]"; |
|
284
|
|
|
|
|
|
|
} |
|
285
|
|
|
|
|
|
|
else |
|
286
|
|
|
|
|
|
|
{ |
|
287
|
0
|
|
|
|
|
0
|
$entry->{exact} = "\"$3\""; |
|
288
|
|
|
|
|
|
|
} |
|
289
|
2
|
|
|
|
|
8
|
$entry->{title} = "EXACT <$3>"; |
|
290
|
2
|
|
|
|
|
20
|
$entry->{next} = $4; |
|
291
|
|
|
|
|
|
|
} |
|
292
|
|
|
|
|
|
|
# CURLY {0,1}(22) or CURLY {0,1} (22) |
|
293
|
|
|
|
|
|
|
elsif ($line =~ /^([A-Z][A-Z0-9]+)\s*\{(\d+),(\d+)\}\s*\((\d+)\)/) |
|
294
|
|
|
|
|
|
|
{ |
|
295
|
0
|
|
|
|
|
0
|
$entry->{class} = lc($1); |
|
296
|
0
|
|
|
|
|
0
|
$entry->{next} = $4; |
|
297
|
0
|
|
|
|
|
0
|
$entry->{min} = $2; |
|
298
|
0
|
|
|
|
|
0
|
$entry->{max} = $3; |
|
299
|
0
|
|
|
|
|
0
|
$entry->{exact} = "\{$entry->{min},$entry->{max}\}"; |
|
300
|
|
|
|
|
|
|
} |
|
301
|
|
|
|
|
|
|
# CURLYM[1] {0,1}(22) or CURLY {0,1} (22) or CURLYX[1] {1,2}(22) |
|
302
|
|
|
|
|
|
|
elsif ($line =~ /^([A-Z][A-Z0-9]+)\[[^]]\]\s*\{(\d+),(\d+)\}\s*\((\d+)\)/) |
|
303
|
|
|
|
|
|
|
{ |
|
304
|
0
|
|
|
|
|
0
|
$entry->{class} = lc($1); |
|
305
|
0
|
|
|
|
|
0
|
$entry->{next} = $4; |
|
306
|
0
|
|
|
|
|
0
|
$entry->{min} = $2; |
|
307
|
0
|
|
|
|
|
0
|
$entry->{max} = $3; |
|
308
|
0
|
|
|
|
|
0
|
$entry->{exact} = "\{$entry->{min},$entry->{max}\}"; |
|
309
|
|
|
|
|
|
|
# make curlym, curly and curlyx all "curly" |
|
310
|
0
|
0
|
|
|
|
0
|
$entry->{class} = 'curly' if $entry->{class} =~ /^curly/; |
|
311
|
|
|
|
|
|
|
} |
|
312
|
|
|
|
|
|
|
# PLUS (22) |
|
313
|
|
|
|
|
|
|
elsif ($line =~ /^PLUS\s*\((\d+)\)/) |
|
314
|
|
|
|
|
|
|
{ |
|
315
|
0
|
|
|
|
|
0
|
$entry->{class} = 'plus'; |
|
316
|
0
|
|
|
|
|
0
|
$entry->{next} = $1; |
|
317
|
0
|
|
|
|
|
0
|
$entry->{min} = 1; |
|
318
|
0
|
|
|
|
|
0
|
$entry->{max} = MAX_MATCHES; |
|
319
|
0
|
|
|
|
|
0
|
$entry->{exact} = "\{$entry->{min},$entry->{max}\}"; |
|
320
|
|
|
|
|
|
|
} |
|
321
|
63
|
|
|
|
|
163
|
$entry->{class} =~ s/[0-9]//g; # OPEN1 => open |
|
322
|
63
|
|
|
|
|
126
|
$entry->{index} = $index++; |
|
323
|
|
|
|
|
|
|
|
|
324
|
63
|
|
|
|
|
100
|
push @$stack, $entry; |
|
325
|
63
|
|
|
|
|
163
|
$entries->{ $entry->{id} } = $entry; |
|
326
|
|
|
|
|
|
|
|
|
327
|
63
|
100
|
|
|
|
350
|
next if $entry->{class} =~ /(open|close|branch|end|succeed|curly|minmod|plus|star|whilem)/; |
|
328
|
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
# add the nodes right away |
|
330
|
|
|
|
|
|
|
# print STDERR "# adding node for $line\n"; |
|
331
|
|
|
|
|
|
|
|
|
332
|
35
|
|
|
|
|
186
|
my $n = $g->add_node($entry->{id}); |
|
333
|
35
|
100
|
|
|
|
1804
|
$n->set_attribute('label', $entry->{exact}) if $entry->{exact} ne ''; |
|
334
|
35
|
|
|
|
|
1635
|
$n->set_attribute('class', $entry->{class}); |
|
335
|
35
|
100
|
|
|
|
2831
|
$n->set_attribute('title', $entry->{title}) if $entry->{title}; |
|
336
|
|
|
|
|
|
|
|
|
337
|
35
|
|
|
|
|
1346
|
$entry->{node} = $n; |
|
338
|
|
|
|
|
|
|
} |
|
339
|
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
# empty text => matches always |
|
341
|
20
|
50
|
|
|
|
80
|
if (keys %$entries == 0) |
|
342
|
|
|
|
|
|
|
{ |
|
343
|
0
|
|
|
|
|
0
|
my $edge = $g->add_edge( $root, $self->{success}); |
|
344
|
0
|
|
|
|
|
0
|
$edge->set_attribute('class','always'); |
|
345
|
0
|
|
|
|
|
0
|
return $self; |
|
346
|
|
|
|
|
|
|
} |
|
347
|
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
# Now we take the stack of entries and transform it into a graph by |
|
349
|
|
|
|
|
|
|
# connecting all the nodes with "match" and "fail" edges. |
|
350
|
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
# Notes: |
|
352
|
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
# Each tried (sub)expression in the regexp has exactly two outcomes: |
|
354
|
|
|
|
|
|
|
# 'match' or 'fail'. |
|
355
|
|
|
|
|
|
|
# If a expression consists of more than on part than it is handled |
|
356
|
|
|
|
|
|
|
# like an "and" (first and second part must match). |
|
357
|
|
|
|
|
|
|
# F.i. in "[ab]foo", if [ab] matches, it goes to try "foo", If it |
|
358
|
|
|
|
|
|
|
# it fails, it goes one level up. Likewise for "foo", match goes |
|
359
|
|
|
|
|
|
|
# on to the next part and fail goes up. |
|
360
|
|
|
|
|
|
|
# If we are already at level 0, the entire expression fails. |
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
# Branches try each subexpression in order, that is if one subexpression |
|
363
|
|
|
|
|
|
|
# fails, it goes to the next branch. If any of them matches, it goes |
|
364
|
|
|
|
|
|
|
# on to the next part, and if all of them fail, it goes up. |
|
365
|
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
# /just(another|perl)hacker/ will result in: |
|
367
|
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
# 1: EXACT (3) |
|
369
|
|
|
|
|
|
|
# 3: OPEN1(5) |
|
370
|
|
|
|
|
|
|
# 5: BRANCH(9) |
|
371
|
|
|
|
|
|
|
# 6: EXACT (12) |
|
372
|
|
|
|
|
|
|
# 9: BRANCH(12) |
|
373
|
|
|
|
|
|
|
# 10: EXACT (12) |
|
374
|
|
|
|
|
|
|
# 12: CLOSE1(14) |
|
375
|
|
|
|
|
|
|
# 14: EXACT (17) |
|
376
|
|
|
|
|
|
|
# 17: END(0) |
|
377
|
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
# [ just ] - match -> [ another ] - match -> [ hacker ] - match -> [ success ] |
|
379
|
|
|
|
|
|
|
# | | ^ | |
|
380
|
|
|
|
|
|
|
# | fail | fail | | |
|
381
|
|
|
|
|
|
|
# | | | | fail |
|
382
|
|
|
|
|
|
|
# | [ perl ] - match ------| | |
|
383
|
|
|
|
|
|
|
# | | | |
|
384
|
|
|
|
|
|
|
# | | fail | |
|
385
|
|
|
|
|
|
|
# -------------------------------------------------------------> [ fail ] |
|
386
|
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
# XXX TODO: each OPEN/CLOSE pair should result in a subgroup. This is not |
|
388
|
|
|
|
|
|
|
# yet possible since Graph::Easy doesn't allow nesting yet. |
|
389
|
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
# connect the root node to the first part |
|
391
|
20
|
|
|
|
|
98
|
my $next = $self->_find_node($stack->[0]); |
|
392
|
20
|
|
|
|
|
161
|
my $edge = $g->add_edge( $root, $next); |
|
393
|
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
# The "NOTHING" node has no predecessor and needs to be weeded out: |
|
395
|
|
|
|
|
|
|
# |
|
396
|
|
|
|
|
|
|
# 1: CURLYM[1] {0,32767}(15) |
|
397
|
|
|
|
|
|
|
# 5: BRANCH(8) |
|
398
|
|
|
|
|
|
|
# 6: EXACT (13) |
|
399
|
|
|
|
|
|
|
# 8: BRANCH(11) |
|
400
|
|
|
|
|
|
|
# 9: EXACT (13) |
|
401
|
|
|
|
|
|
|
# 13: SUCCEED(0) |
|
402
|
|
|
|
|
|
|
# 14: NOTHING(15) |
|
403
|
|
|
|
|
|
|
# 15: END(0) |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
########################################################################### |
|
406
|
|
|
|
|
|
|
########################################################################### |
|
407
|
|
|
|
|
|
|
# main conversion loop |
|
408
|
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
# the entry/part we are trying |
|
410
|
20
|
|
|
|
|
1917
|
my $i = 0; |
|
411
|
20
|
|
|
|
|
75
|
while ($i < @$stack) |
|
412
|
|
|
|
|
|
|
{ |
|
413
|
63
|
|
|
|
|
82
|
my $entry = $stack->[$i]; |
|
414
|
|
|
|
|
|
|
|
|
415
|
63
|
100
|
|
|
|
148
|
next unless exists $entry->{node}; |
|
416
|
|
|
|
|
|
|
|
|
417
|
35
|
50
|
66
|
|
|
156
|
if ($entry->{class} eq 'nothing' && $entry->{node}->predecessors() == 0) |
|
418
|
|
|
|
|
|
|
{ |
|
419
|
|
|
|
|
|
|
# a nothing node with no incoming connection, filter it out |
|
420
|
0
|
|
|
|
|
0
|
$g->del_node($entry->{node}); |
|
421
|
0
|
|
|
|
|
0
|
next; |
|
422
|
|
|
|
|
|
|
} |
|
423
|
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
# the "match" egde goes to the next part |
|
425
|
35
|
|
|
|
|
222
|
my $next = $self->_find_next($entry); |
|
426
|
|
|
|
|
|
|
|
|
427
|
35
|
100
|
|
|
|
55
|
my $n = $next; $n = $self->{success} unless defined $n; |
|
|
35
|
|
|
|
|
83
|
|
|
428
|
|
|
|
|
|
|
|
|
429
|
35
|
|
|
|
|
122
|
my $edge = $g->add_edge( $entry->{node}, $n); |
|
430
|
35
|
|
|
|
|
2775
|
$edge->set_attribute('class','match'); |
|
431
|
|
|
|
|
|
|
|
|
432
|
35
|
100
|
|
|
|
3152
|
if ($n == $self->{success}) |
|
433
|
|
|
|
|
|
|
{ |
|
434
|
22
|
|
|
|
|
90
|
$edge->set_attribute('end','back,0'); |
|
435
|
|
|
|
|
|
|
} |
|
436
|
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
# nothing nodes do not have a fail edge, they match always |
|
438
|
35
|
100
|
33
|
|
|
2131
|
if ( ($entry->{class} eq 'nothing') || |
|
|
|
|
66
|
|
|
|
|
|
439
|
|
|
|
|
|
|
(defined $entry->{min} && $entry->{min} == 0) ) |
|
440
|
|
|
|
|
|
|
{ |
|
441
|
3
|
|
|
|
|
9
|
$edge->set_attribute('class','always'); |
|
442
|
3
|
|
|
|
|
257
|
next; |
|
443
|
|
|
|
|
|
|
} |
|
444
|
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
# generate the fail edge: |
|
446
|
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
# if the next node is $self->{success}, then fail must be $self->{fail} |
|
448
|
32
|
|
|
|
|
60
|
my $fail = $self->{fail}; |
|
449
|
|
|
|
|
|
|
# walked over end? |
|
450
|
32
|
100
|
|
|
|
112
|
if (!defined $next) |
|
|
|
50
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
{ |
|
452
|
19
|
|
|
|
|
51
|
$fail = $self->_find_next_branching($entry); |
|
453
|
|
|
|
|
|
|
} |
|
454
|
|
|
|
|
|
|
# otherwise, find the next branching part |
|
455
|
|
|
|
|
|
|
elsif ($next != $self->{success}) |
|
456
|
|
|
|
|
|
|
{ |
|
457
|
13
|
|
|
|
|
45
|
$fail = $self->_find_next_branching($entry); |
|
458
|
|
|
|
|
|
|
} |
|
459
|
|
|
|
|
|
|
|
|
460
|
32
|
|
|
|
|
107
|
$edge = $g->add_edge( $entry->{node}, $fail); |
|
461
|
32
|
|
|
|
|
2102
|
$edge->set_attribute('class','fail'); |
|
462
|
32
|
|
|
|
|
2711
|
$edge->set_attribute('end','back,0'); |
|
463
|
|
|
|
|
|
|
|
|
464
|
63
|
|
|
|
|
2530
|
} continue { $i++; } |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
# if there are no incoming edges to fail, the regexp always matches (like //): |
|
467
|
20
|
100
|
|
|
|
116
|
$g->del_node($self->{fail}) if scalar $self->{fail}->incoming() == 0; |
|
468
|
|
|
|
|
|
|
|
|
469
|
20
|
|
|
|
|
716
|
$self; |
|
470
|
|
|
|
|
|
|
} |
|
471
|
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
sub _find_next_branching |
|
473
|
|
|
|
|
|
|
{ |
|
474
|
|
|
|
|
|
|
# Given an entry on the stack, go backwards to find the |
|
475
|
|
|
|
|
|
|
# last branch, then skip to the next part in that branch. |
|
476
|
|
|
|
|
|
|
# If there is no next part, try one level higher, until |
|
477
|
|
|
|
|
|
|
# we are at the upper-most level. |
|
478
|
32
|
|
|
32
|
|
50
|
my ($self, $entry) = @_; |
|
479
|
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
# Example: |
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
# starting with 14: EXACT (19) |
|
483
|
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
# 1: EXACT <0>(3) |
|
485
|
|
|
|
|
|
|
# 3: OPEN1(5) |
|
486
|
|
|
|
|
|
|
# 5: BRANCH(8) |
|
487
|
|
|
|
|
|
|
# 6: EXACT (35) |
|
488
|
|
|
|
|
|
|
# 8: BRANCH(32) |
|
489
|
|
|
|
|
|
|
# 9: EXACT (11) |
|
490
|
|
|
|
|
|
|
# 11: OPEN2(13) |
|
491
|
|
|
|
|
|
|
# 13: BRANCH(16) 1 # look at next(16) is it a branch? |
|
492
|
|
|
|
|
|
|
# yes it is, so go forward to it |
|
493
|
|
|
|
|
|
|
# 14: EXACT (19) 0 # find 13: BRANCH(16) |
|
494
|
|
|
|
|
|
|
# 16: BRANCH(19) 2 # skip forward |
|
495
|
|
|
|
|
|
|
# 17: EXACT (19) 3 # return this |
|
496
|
|
|
|
|
|
|
# 19: CLOSE2(21) |
|
497
|
|
|
|
|
|
|
# 21: ANYOF[i](35) |
|
498
|
|
|
|
|
|
|
# 32: BRANCH(35) |
|
499
|
|
|
|
|
|
|
# 33: EXACT (35) |
|
500
|
|
|
|
|
|
|
# 35: CLOSE1(37) |
|
501
|
|
|
|
|
|
|
# 37: EXACT (39) |
|
502
|
|
|
|
|
|
|
# 39: END(0) |
|
503
|
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
# starting with 17: EXACT (19) |
|
505
|
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
# 1: EXACT <0>(3) |
|
507
|
|
|
|
|
|
|
# 3: OPEN1(5) |
|
508
|
|
|
|
|
|
|
# 5: BRANCH(8) |
|
509
|
|
|
|
|
|
|
# 6: EXACT (35) |
|
510
|
|
|
|
|
|
|
# 8: BRANCH(32) 2 # look at next(32) is it a branch? |
|
511
|
|
|
|
|
|
|
# yes it is, so go forward to it |
|
512
|
|
|
|
|
|
|
# 9: EXACT (11) |
|
513
|
|
|
|
|
|
|
# 11: OPEN2(13) |
|
514
|
|
|
|
|
|
|
# 13: BRANCH(16) |
|
515
|
|
|
|
|
|
|
# 14: EXACT (19) |
|
516
|
|
|
|
|
|
|
# 16: BRANCH(19) 1 # look at next(16) is it a branch? |
|
517
|
|
|
|
|
|
|
# no, 19 is not, so find 8: BRANCH(32) |
|
518
|
|
|
|
|
|
|
# 17: EXACT (19) 0 # find 16: BRANCH(19) |
|
519
|
|
|
|
|
|
|
# 19: CLOSE2(21) |
|
520
|
|
|
|
|
|
|
# 21: ANYOF[i](35) |
|
521
|
|
|
|
|
|
|
# 32: BRANCH(35) |
|
522
|
|
|
|
|
|
|
# 33: EXACT (35) 3 # return this: |
|
523
|
|
|
|
|
|
|
# 35: CLOSE1(37) |
|
524
|
|
|
|
|
|
|
# 37: EXACT (39) |
|
525
|
|
|
|
|
|
|
# 39: END(0) |
|
526
|
|
|
|
|
|
|
|
|
527
|
32
|
50
|
|
|
|
75
|
print STDERR "# find next branch for $entry->{id}\n" if $self->{debug}; |
|
528
|
|
|
|
|
|
|
|
|
529
|
32
|
|
|
|
|
43
|
my $entries = $self->{entries}; |
|
530
|
32
|
|
|
|
|
47
|
do { |
|
531
|
|
|
|
|
|
|
# find branch one level up |
|
532
|
33
|
|
|
|
|
75
|
my $branch = $self->_find_previous_branch($entry); |
|
533
|
|
|
|
|
|
|
|
|
534
|
33
|
0
|
33
|
|
|
109
|
print STDERR "# prev branch for $entry->{id} should be at $branch->{id}\n" |
|
|
|
|
33
|
|
|
|
|
|
535
|
|
|
|
|
|
|
if $self->{debug} && $branch && defined $branch->{id}; |
|
536
|
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
# no branch above us, fail completely |
|
538
|
33
|
100
|
|
|
|
116
|
return $self->{fail} unless defined $branch; |
|
539
|
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
# skip to next part |
|
541
|
4
|
|
|
|
|
6
|
$entry = $entries->{ $branch->{next} }; |
|
542
|
|
|
|
|
|
|
|
|
543
|
4
|
50
|
|
|
|
9
|
print STDERR "# next branch should be at $entry->{id} ($entry->{class})\n" |
|
544
|
|
|
|
|
|
|
if $self->{debug}; |
|
545
|
|
|
|
|
|
|
|
|
546
|
4
|
100
|
66
|
|
|
27
|
return $self->{fail} if $entry && $entry->{class} eq 'end'; |
|
547
|
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
# loop ends if there is a next part in the current branch |
|
549
|
|
|
|
|
|
|
} while ($entry->{class} ne 'branch'); |
|
550
|
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
# skip over the branch, open etc to the first real part |
|
552
|
2
|
|
|
|
|
6
|
$entry = $self->_find_node($entry); |
|
553
|
|
|
|
|
|
|
|
|
554
|
2
|
50
|
|
|
|
6
|
print STDERR "# next branch is at $entry->{id}\n" |
|
555
|
|
|
|
|
|
|
if $self->{debug}; |
|
556
|
|
|
|
|
|
|
|
|
557
|
2
|
|
|
|
|
5
|
$entry; |
|
558
|
|
|
|
|
|
|
} |
|
559
|
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
sub _find_previous_branch |
|
561
|
|
|
|
|
|
|
{ |
|
562
|
|
|
|
|
|
|
# Given an entry on the stack, go backwards to find the |
|
563
|
|
|
|
|
|
|
# last branch. |
|
564
|
33
|
|
|
33
|
|
41
|
my ($self, $entry) = @_; |
|
565
|
|
|
|
|
|
|
|
|
566
|
33
|
|
|
|
|
47
|
my $entries = $self->{entries}; |
|
567
|
33
|
|
|
|
|
45
|
my $stack = $self->{stack}; |
|
568
|
|
|
|
|
|
|
|
|
569
|
33
|
|
|
|
|
45
|
my $index = $entry->{index}; |
|
570
|
|
|
|
|
|
|
|
|
571
|
33
|
50
|
|
|
|
67
|
print STDERR "# Finding prev branch for entry $entry->{id}\n" |
|
572
|
|
|
|
|
|
|
if $self->{debug}; |
|
573
|
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
# the branch must be this level or lower |
|
575
|
33
|
|
|
|
|
44
|
my $level = $entry->{level}; |
|
576
|
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
# go backwards until we find a BRANCH |
|
578
|
33
|
|
|
|
|
76
|
while ($index > 0) |
|
579
|
|
|
|
|
|
|
{ |
|
580
|
28
|
|
|
|
|
29
|
$index--; |
|
581
|
28
|
|
|
|
|
35
|
my $e = $stack->[$index]; |
|
582
|
|
|
|
|
|
|
|
|
583
|
28
|
0
|
33
|
|
|
68
|
print STDERR "# Found $entry->{id} ($level vs $e->{level}\n" |
|
|
|
|
33
|
|
|
|
|
|
584
|
|
|
|
|
|
|
if $self->{debug} && $entry && $entry->{class} eq 'branch'; |
|
585
|
|
|
|
|
|
|
|
|
586
|
28
|
100
|
100
|
|
|
115
|
return $e if $e->{class} eq 'branch' && $e->{level} <= $level; |
|
587
|
|
|
|
|
|
|
} |
|
588
|
|
|
|
|
|
|
# the part we looked at is in the upper-most level, so there is |
|
589
|
|
|
|
|
|
|
# no next branch part we can skip to, meaning we fail completely. |
|
590
|
29
|
|
|
|
|
270
|
return; |
|
591
|
|
|
|
|
|
|
} |
|
592
|
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
sub _find_node |
|
594
|
|
|
|
|
|
|
{ |
|
595
|
|
|
|
|
|
|
# Given an entry on the stack, skip to next entry if the current |
|
596
|
|
|
|
|
|
|
# isnt a node itself. |
|
597
|
22
|
|
|
22
|
|
42
|
my ($self, $entry) = @_; |
|
598
|
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
# Example: |
|
600
|
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
# 3: OPEN1(5) # open => skip, go to next |
|
602
|
|
|
|
|
|
|
# 5: BRANCH(9) # branch => skip, go to next |
|
603
|
|
|
|
|
|
|
# 6: EXACT (12) # return this |
|
604
|
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
# 1: EXACT (3) # return this |
|
606
|
|
|
|
|
|
|
# 3: OPEN1(5) |
|
607
|
|
|
|
|
|
|
# 5: BRANCH(9) |
|
608
|
|
|
|
|
|
|
# 6: EXACT (12) |
|
609
|
|
|
|
|
|
|
|
|
610
|
22
|
50
|
|
|
|
88
|
print STDERR "# find node for entry $entry->{id}\n" |
|
611
|
|
|
|
|
|
|
if $self->{debug}; |
|
612
|
|
|
|
|
|
|
|
|
613
|
22
|
|
|
|
|
40
|
my $entries = $self->{entries}; |
|
614
|
22
|
|
|
|
|
50
|
my $stack = $self->{stack}; |
|
615
|
22
|
|
|
|
|
74
|
while (!exists $entry->{node}) |
|
616
|
|
|
|
|
|
|
{ |
|
617
|
6
|
50
|
|
|
|
17
|
print STDERR "# at entry $entry->{id}\n" |
|
618
|
|
|
|
|
|
|
if $self->{debug}; |
|
619
|
|
|
|
|
|
|
|
|
620
|
6
|
50
|
|
|
|
28
|
if ($entry->{class} =~ /^(open|branch|plus|star|curly)/) |
|
621
|
|
|
|
|
|
|
{ |
|
622
|
6
|
|
|
|
|
17
|
$entry = $stack->[ $entry->{index} + 1 ]; |
|
623
|
|
|
|
|
|
|
} |
|
624
|
|
|
|
|
|
|
else |
|
625
|
|
|
|
|
|
|
{ |
|
626
|
0
|
|
|
|
|
0
|
$entry = $entries->{ $entry->{next} }; |
|
627
|
|
|
|
|
|
|
} |
|
628
|
6
|
50
|
|
|
|
24
|
return $self->{success} unless ref $entry; # walked over end |
|
629
|
|
|
|
|
|
|
} |
|
630
|
|
|
|
|
|
|
|
|
631
|
22
|
|
|
|
|
59
|
$entry->{node}; |
|
632
|
|
|
|
|
|
|
} |
|
633
|
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
sub _find_next |
|
635
|
|
|
|
|
|
|
{ |
|
636
|
|
|
|
|
|
|
# Given an entry on the stack, find the next entry. |
|
637
|
35
|
|
|
35
|
|
49
|
my ($self, $entry) = @_; |
|
638
|
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
# Example: |
|
640
|
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
# 1: EXACT (3) # go to 3 |
|
642
|
|
|
|
|
|
|
# 3: OPEN1(5) # open => skip, go to next |
|
643
|
|
|
|
|
|
|
# 5: BRANCH(9) # branch => skip, go to next |
|
644
|
|
|
|
|
|
|
# 6: EXACT (12) # return this |
|
645
|
|
|
|
|
|
|
|
|
646
|
35
|
50
|
|
|
|
86
|
print STDERR "# Skipping ahead for $entry->{id}:\n" |
|
647
|
|
|
|
|
|
|
if $self->{debug}; |
|
648
|
35
|
|
|
|
|
55
|
my $entries = $self->{entries}; |
|
649
|
35
|
|
|
|
|
70
|
my $stack = $self->{stack}; |
|
650
|
|
|
|
|
|
|
do |
|
651
|
35
|
|
|
|
|
40
|
{ |
|
652
|
58
|
50
|
|
|
|
159
|
print STDERR "# at entry $entry->{id}\n" |
|
653
|
|
|
|
|
|
|
if $self->{debug}; |
|
654
|
|
|
|
|
|
|
|
|
655
|
58
|
50
|
|
|
|
143
|
if ($entry->{class} =~ /^(open|branch|plus|star|curly)/) |
|
656
|
|
|
|
|
|
|
{ |
|
657
|
0
|
|
|
|
|
0
|
$entry = $stack->[ $entry->{index} + 1 ]; |
|
658
|
|
|
|
|
|
|
} |
|
659
|
|
|
|
|
|
|
else |
|
660
|
|
|
|
|
|
|
{ |
|
661
|
58
|
|
|
|
|
103
|
$entry = $entries->{ $entry->{next} }; |
|
662
|
|
|
|
|
|
|
} |
|
663
|
58
|
100
|
|
|
|
167
|
return unless ref $entry; # walked over end |
|
664
|
|
|
|
|
|
|
|
|
665
|
36
|
50
|
33
|
|
|
170
|
print STDERR "# next $entry->{id}\n" |
|
666
|
|
|
|
|
|
|
if $self->{debug} && ref($entry); |
|
667
|
|
|
|
|
|
|
} while (!exists $entry->{node}); |
|
668
|
|
|
|
|
|
|
|
|
669
|
13
|
50
|
|
|
|
27
|
print STDERR "# return $entry->{id}\n" |
|
670
|
|
|
|
|
|
|
if $self->{debug}; |
|
671
|
|
|
|
|
|
|
|
|
672
|
13
|
|
|
|
|
31
|
$entry->{node}; |
|
673
|
|
|
|
|
|
|
} |
|
674
|
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
1; |
|
676
|
|
|
|
|
|
|
__END__ |