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__ |