line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
|
2
|
42
|
|
|
42
|
|
1260101
|
use strict; |
|
42
|
|
|
|
|
142
|
|
|
42
|
|
|
|
|
1591
|
|
3
|
42
|
|
|
42
|
|
258
|
use warnings; |
|
42
|
|
|
|
|
104
|
|
|
42
|
|
|
|
|
2299
|
|
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
package Text::Parser::RuleSpec 1.000; |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
|
10
|
42
|
|
|
42
|
|
2624
|
use Moose; |
|
42
|
|
|
|
|
1918546
|
|
|
42
|
|
|
|
|
373
|
|
11
|
42
|
|
|
42
|
|
307668
|
use Moose::Exporter; |
|
42
|
|
|
|
|
145
|
|
|
42
|
|
|
|
|
647
|
|
12
|
42
|
|
|
42
|
|
25020
|
use MooseX::ClassAttribute; |
|
42
|
|
|
|
|
3510378
|
|
|
42
|
|
|
|
|
195
|
|
13
|
42
|
|
|
42
|
|
11048875
|
use Text::Parser::Error; |
|
42
|
|
|
|
|
137
|
|
|
42
|
|
|
|
|
463
|
|
14
|
42
|
|
|
42
|
|
32357
|
use Text::Parser::Rule; |
|
42
|
|
|
|
|
144
|
|
|
42
|
|
|
|
|
2018
|
|
15
|
42
|
|
|
42
|
|
33024
|
use List::MoreUtils qw(before_incl after_incl); |
|
42
|
|
|
|
|
598116
|
|
|
42
|
|
|
|
|
319
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
Moose::Exporter->setup_import_methods( |
18
|
|
|
|
|
|
|
with_meta => [ |
19
|
|
|
|
|
|
|
'applies_rule', 'unwraps_lines_using', |
20
|
|
|
|
|
|
|
'disables_superclass_rules', 'applies_cloned_rule', |
21
|
|
|
|
|
|
|
], |
22
|
|
|
|
|
|
|
as_is => ['_check_custom_unwrap_args'], |
23
|
|
|
|
|
|
|
also => 'Moose' |
24
|
|
|
|
|
|
|
); |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
class_has _all_rules => ( |
27
|
|
|
|
|
|
|
is => 'rw', |
28
|
|
|
|
|
|
|
isa => 'HashRef[Text::Parser::Rule]', |
29
|
|
|
|
|
|
|
lazy => 1, |
30
|
|
|
|
|
|
|
default => sub { {} }, |
31
|
|
|
|
|
|
|
traits => ['Hash'], |
32
|
|
|
|
|
|
|
handles => { |
33
|
|
|
|
|
|
|
_add_new_rule => 'set', |
34
|
|
|
|
|
|
|
is_known_rule => 'exists', |
35
|
|
|
|
|
|
|
class_rule_object => 'get', |
36
|
|
|
|
|
|
|
}, |
37
|
|
|
|
|
|
|
); |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
class_has _class_rule_order => ( |
40
|
|
|
|
|
|
|
is => 'rw', |
41
|
|
|
|
|
|
|
isa => 'HashRef[ArrayRef[Str]]', |
42
|
|
|
|
|
|
|
lazy => 1, |
43
|
|
|
|
|
|
|
default => sub { {} }, |
44
|
|
|
|
|
|
|
traits => ['Hash'], |
45
|
|
|
|
|
|
|
handles => { |
46
|
|
|
|
|
|
|
_class_has_rules => 'exists', |
47
|
|
|
|
|
|
|
__cls_rule_order => 'get', |
48
|
|
|
|
|
|
|
_set_class_rule_order => 'set', |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
); |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
class_has _class_rules_in_order => ( |
53
|
|
|
|
|
|
|
is => 'rw', |
54
|
|
|
|
|
|
|
isa => 'HashRef[ArrayRef[Text::Parser::Rule]]', |
55
|
|
|
|
|
|
|
lazy => 1, |
56
|
|
|
|
|
|
|
default => sub { {} }, |
57
|
|
|
|
|
|
|
traits => ['Hash'], |
58
|
|
|
|
|
|
|
handles => { |
59
|
|
|
|
|
|
|
_class_rules => 'get', |
60
|
|
|
|
|
|
|
_set_rules_of_class => 'set', |
61
|
|
|
|
|
|
|
}, |
62
|
|
|
|
|
|
|
); |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
sub class_has_rules { |
66
|
724
|
|
|
724
|
1
|
1585
|
my ( $this_cls, $cls ) = ( shift, shift ); |
67
|
724
|
100
|
|
|
|
1540
|
return 0 if not defined $cls; |
68
|
722
|
100
|
|
|
|
26327
|
return 0 if not $this_cls->_class_has_rules($cls); |
69
|
133
|
|
|
|
|
336
|
return $this_cls->class_rule_order($cls); |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
sub class_rule_order { |
74
|
235
|
|
|
235
|
1
|
2191
|
my ( $class, $cls ) = @_; |
75
|
235
|
100
|
|
|
|
560
|
return () if not defined $cls; |
76
|
234
|
100
|
|
|
|
8860
|
$class->_class_has_rules($cls) ? @{ $class->__cls_rule_order($cls) } : (); |
|
218
|
|
|
|
|
8295
|
|
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
sub class_rules { |
81
|
267
|
|
|
267
|
1
|
596
|
my ( $class, $cls ) = @_; |
82
|
267
|
100
|
|
|
|
552
|
return () if not $class->class_has_rules($cls); |
83
|
66
|
|
|
|
|
101
|
@{ $class->_class_rules($cls) }; |
|
66
|
|
|
|
|
2426
|
|
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub populate_class_rules { |
89
|
30
|
|
|
30
|
1
|
184
|
my ( $class, $cls ) = @_; |
90
|
30
|
100
|
100
|
|
|
1226
|
return if not defined $cls or not $class->_class_has_rules($cls); |
91
|
28
|
|
|
|
|
100
|
my @ord = $class->class_rule_order($cls); |
92
|
|
|
|
|
|
|
$class->_set_rules_of_class( |
93
|
28
|
|
|
|
|
114
|
$cls => [ map { $class->class_rule_object($_) } @ord ] ); |
|
65
|
|
|
|
|
2306
|
|
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
sub applies_rule { |
98
|
39
|
|
|
39
|
1
|
211410
|
my ( $meta, $name ) = ( shift, shift ); |
99
|
39
|
|
|
|
|
171
|
_first_things_on_applies_rule( $meta, $name, @_ ); |
100
|
25
|
|
|
|
|
3705
|
_register_rule( _full_rule_name( $meta, $name ), @_ ); |
101
|
23
|
|
|
|
|
104
|
_set_correct_rule_order( $meta, $name, @_ ); |
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
sub _first_things_on_applies_rule { |
105
|
39
|
|
|
39
|
|
105
|
my ( $meta, $name ) = ( shift, shift ); |
106
|
39
|
|
|
|
|
140
|
_excepts_apply_rule( $meta, $name, @_ ); |
107
|
25
|
|
|
|
|
95
|
_set_default_of_attributes( $meta, auto_split => 1 ); |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
sub _full_rule_name { |
111
|
52
|
|
|
52
|
|
117
|
my ( $meta, $name ) = ( shift, shift ); |
112
|
52
|
|
|
|
|
299
|
return $meta->name . '/' . $name; |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
sub _excepts_apply_rule { |
116
|
39
|
|
|
39
|
|
86
|
my ( $meta, $name ) = ( shift, shift ); |
117
|
39
|
|
|
|
|
116
|
_rulespec_cant_be_in_main( $meta, $name, 'applies_rule' ); |
118
|
37
|
|
|
|
|
117
|
_rule_must_have_name( $meta, $name ); |
119
|
34
|
|
|
|
|
124
|
_check_args_hash_stuff( $meta, "applies_rule $name", @_ ); |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
sub _rulespec_cant_be_in_main { |
123
|
54
|
|
|
54
|
|
137
|
my ( $meta, $name, $funcname ) = ( shift, shift, shift ); |
124
|
54
|
100
|
|
|
|
201
|
my $follow = defined $name ? ": $name" : '.'; |
125
|
54
|
100
|
|
|
|
322
|
parser_exception("$funcname cannot be called in main$follow") |
126
|
|
|
|
|
|
|
if $meta->name eq 'main'; |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
my %rule_options = ( |
130
|
|
|
|
|
|
|
if => 1, |
131
|
|
|
|
|
|
|
do => 1, |
132
|
|
|
|
|
|
|
dont_record => 1, |
133
|
|
|
|
|
|
|
continue_to_next => 1, |
134
|
|
|
|
|
|
|
before => 1, |
135
|
|
|
|
|
|
|
after => 1, |
136
|
|
|
|
|
|
|
); |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
sub _rule_must_have_name { |
139
|
37
|
|
|
37
|
|
79
|
my ( $meta, $name ) = ( shift, shift ); |
140
|
|
|
|
|
|
|
parser_exception("applies_rule requires rule name argument") |
141
|
|
|
|
|
|
|
if not defined $name |
142
|
|
|
|
|
|
|
or ( '' ne ref($name) ) |
143
|
37
|
100
|
100
|
|
|
317
|
or ( exists $rule_options{$name} ); |
|
|
|
100
|
|
|
|
|
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
sub _check_args_hash_stuff { |
147
|
37
|
|
|
37
|
|
83
|
my ( $meta, $funccall ) = ( shift, shift ); |
148
|
37
|
|
|
|
|
108
|
my (%opt) = _check_arg_is_hash( $funccall, @_ ); |
149
|
34
|
|
|
|
|
124
|
_if_empty_prepopulate_rules_from_superclass($meta); |
150
|
34
|
100
|
|
|
|
136
|
_check_location_args( $meta, $funccall, %opt ) |
151
|
|
|
|
|
|
|
if _has_location_opts(%opt); |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
sub _has_location_opts { |
155
|
59
|
|
|
59
|
|
184
|
my (%opt) = @_; |
156
|
59
|
100
|
|
|
|
398
|
exists $opt{before} or exists $opt{after}; |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
sub _check_arg_is_hash { |
160
|
37
|
|
|
37
|
|
550
|
my $funccall = shift; |
161
|
37
|
100
|
100
|
|
|
217
|
parser_exception( |
162
|
|
|
|
|
|
|
"$funccall must be followed by a hash. See documentation.") |
163
|
|
|
|
|
|
|
if not @_ |
164
|
|
|
|
|
|
|
or ( scalar(@_) % 2 ); |
165
|
34
|
|
|
|
|
165
|
return @_; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
sub _check_location_args { |
169
|
9
|
|
|
9
|
|
31
|
my ( $meta, $name, %opt ) = ( shift, shift, @_ ); |
170
|
|
|
|
|
|
|
parser_exception( |
171
|
|
|
|
|
|
|
"\'$name\' call can have \'before\' or \'after\'; not both.") |
172
|
9
|
100
|
100
|
|
|
49
|
if exists $opt{before} and exists $opt{after}; |
173
|
8
|
100
|
|
|
|
22
|
my $loc = exists $opt{before} ? 'before' : 'after'; |
174
|
8
|
|
|
|
|
34
|
my ( $cls, $rule ) = split /\//, $opt{$loc}, 2; |
175
|
8
|
100
|
|
|
|
31
|
parser_exception( |
176
|
|
|
|
|
|
|
"Clause $loc must follow format <classname>/<rulename>: \'$name\'") |
177
|
|
|
|
|
|
|
if not defined $rule; |
178
|
|
|
|
|
|
|
parser_exception("Unknown rule $opt{$loc} in clause $loc: \'$name\'") |
179
|
7
|
100
|
|
|
|
272
|
if not Text::Parser::RuleSpec->is_known_rule( $opt{$loc} ); |
180
|
5
|
|
|
|
|
21
|
my (@r) = Text::Parser::RuleSpec->class_rule_order( $meta->name ); |
181
|
5
|
|
|
|
|
12
|
my $is_super_rule = grep { $_ eq $opt{$loc} } @r; |
|
14
|
|
|
|
|
35
|
|
182
|
5
|
100
|
100
|
|
|
44
|
parser_exception( |
183
|
|
|
|
|
|
|
"Use \'$loc\' clause only with superclass rules ; not this class: \'$name\'" |
184
|
|
|
|
|
|
|
) if $cls eq $meta->name or not $is_super_rule; |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
sub _register_rule { |
188
|
25
|
|
|
25
|
|
63
|
my $key = shift; |
189
|
25
|
100
|
|
|
|
1051
|
parser_exception("name rules uniquely: $key") |
190
|
|
|
|
|
|
|
if Text::Parser::RuleSpec->is_known_rule($key); |
191
|
24
|
|
|
|
|
87
|
my %opts = _get_rule_opts_only(@_); |
192
|
24
|
|
|
|
|
766
|
my $rule = Text::Parser::Rule->new(%opts); |
193
|
23
|
|
|
|
|
865
|
Text::Parser::RuleSpec->_add_new_rule( $key => $rule ); |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
sub _get_rule_opts_only { |
197
|
26
|
|
|
26
|
|
86
|
my (%opt) = @_; |
198
|
26
|
100
|
|
|
|
85
|
delete $opt{before} if exists $opt{before}; |
199
|
26
|
100
|
|
|
|
69
|
delete $opt{after} if exists $opt{after}; |
200
|
26
|
|
|
|
|
103
|
return (%opt); |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
sub _set_default_of_attributes { |
204
|
30
|
|
|
30
|
|
100
|
my ( $meta, %val ) = @_; |
205
|
30
|
|
|
|
|
148
|
while ( my ( $k, $v ) = ( each %val ) ) { |
206
|
30
|
100
|
|
|
|
206
|
_inherit_set_default_mk_ro( $meta, $k, $v ) |
207
|
|
|
|
|
|
|
if not defined $meta->get_attribute($k); |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
sub _inherit_set_default_mk_ro { |
212
|
16
|
|
|
16
|
|
213
|
my ( $meta, $attr, $def ) = ( shift, shift, shift ); |
213
|
16
|
|
|
|
|
85
|
my $old = $meta->find_attribute_by_name($attr); |
214
|
16
|
|
|
|
|
1131
|
my $new = $old->clone_and_inherit_options( default => $def, is => 'ro' ); |
215
|
16
|
|
|
|
|
34200
|
$meta->add_attribute($new); |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
sub _set_correct_rule_order { |
219
|
25
|
|
|
25
|
|
62
|
my ( $meta, $rule_name ) = ( shift, shift ); |
220
|
25
|
|
|
|
|
64
|
my $rname = _full_rule_name( $meta, $rule_name ); |
221
|
25
|
100
|
|
|
|
72
|
return _push_to_class_rules( $meta->name, $rname ) |
222
|
|
|
|
|
|
|
if not _has_location_opts(@_); |
223
|
2
|
|
|
|
|
21
|
_insert_rule_in_order( $meta->name, $rname, @_ ); |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
my %INSERT_RULE_FUNC = ( |
227
|
|
|
|
|
|
|
before => \&_ins_before_rule, |
228
|
|
|
|
|
|
|
after => \&_ins_after_rule, |
229
|
|
|
|
|
|
|
); |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
sub _insert_rule_in_order { |
232
|
2
|
|
|
2
|
|
8
|
my ( $cls, $rname, %opt ) = ( shift, shift, @_ ); |
233
|
2
|
100
|
|
|
|
7
|
my $loc = exists $opt{before} ? 'before' : 'after'; |
234
|
2
|
|
|
|
|
12
|
$INSERT_RULE_FUNC{$loc}->( $cls, $opt{$loc}, $rname ); |
235
|
2
|
|
|
|
|
8
|
Text::Parser::RuleSpec->populate_class_rules($cls); |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
sub _ins_before_rule { |
239
|
1
|
|
|
1
|
|
5
|
my ( $cls, $before, $rname ) = ( shift, shift, shift ); |
240
|
1
|
|
|
|
|
4
|
my (@ord) = Text::Parser::RuleSpec->class_rule_order($cls); |
241
|
1
|
|
|
1
|
|
35
|
my (@ord1) = before_incl { $_ eq $before } @ord; |
|
1
|
|
|
|
|
23
|
|
242
|
1
|
|
|
1
|
|
13
|
my (@ord2) = after_incl { $_ eq $before } @ord; |
|
1
|
|
|
|
|
4
|
|
243
|
1
|
|
|
|
|
3
|
pop @ord1; |
244
|
1
|
|
|
|
|
48
|
Text::Parser::RuleSpec->_set_class_rule_order( |
245
|
|
|
|
|
|
|
$cls => [ @ord1, $rname, @ord2 ] ); |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
sub _ins_after_rule { |
249
|
1
|
|
|
1
|
|
6
|
my ( $cls, $after, $rname ) = ( shift, shift, shift ); |
250
|
1
|
|
|
|
|
4
|
my (@ord) = Text::Parser::RuleSpec->class_rule_order($cls); |
251
|
1
|
|
|
2
|
|
14
|
my (@ord1) = before_incl { $_ eq $after } @ord; |
|
2
|
|
|
|
|
6
|
|
252
|
1
|
|
|
2
|
|
9
|
my (@ord2) = after_incl { $_ eq $after } @ord; |
|
2
|
|
|
|
|
6
|
|
253
|
1
|
|
|
|
|
3
|
shift @ord2; |
254
|
1
|
|
|
|
|
42
|
Text::Parser::RuleSpec->_set_class_rule_order( |
255
|
|
|
|
|
|
|
$cls => [ @ord1, $rname, @ord2 ] ); |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
sub _if_empty_prepopulate_rules_from_superclass { |
259
|
36
|
|
|
36
|
|
91
|
my ( $meta, $cls ) = ( shift, 'Text::Parser::RuleSpec' ); |
260
|
36
|
|
|
|
|
320
|
my @ro = map { $cls->class_rule_order($_) } ( $meta->superclasses ); |
|
38
|
|
|
|
|
2241
|
|
261
|
36
|
100
|
|
|
|
1405
|
$cls->_set_class_rule_order( $meta->name => \@ro ) |
262
|
|
|
|
|
|
|
if not $cls->_class_has_rules( $meta->name ); |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
sub _push_to_class_rules { |
266
|
23
|
|
|
23
|
|
73
|
my ( $class, $cls, $rulename ) = ( 'Text::Parser::RuleSpec', @_ ); |
267
|
23
|
|
|
|
|
80
|
my @ord = $class->class_rule_order($cls); |
268
|
23
|
|
|
|
|
56
|
push @ord, $rulename; |
269
|
23
|
|
|
|
|
891
|
$class->_set_class_rule_order( $cls => \@ord ); |
270
|
23
|
|
|
|
|
107
|
$class->populate_class_rules($cls); |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
sub applies_cloned_rule { |
275
|
6
|
|
|
6
|
1
|
33690
|
my ( $meta, $orule ) = ( shift, shift ); |
276
|
6
|
|
|
|
|
21
|
_first_things_on_applies_cloned_rule( $meta, $orule, @_ ); |
277
|
2
|
|
|
|
|
318
|
my $nrule = _gen_new_rule_name_from( $meta, $orule ); |
278
|
2
|
|
|
|
|
10
|
_register_cloned_rule( _full_rule_name( $meta, $nrule ), |
279
|
|
|
|
|
|
|
_qualified_rulename( $orule, $meta ), @_ ); |
280
|
2
|
|
|
|
|
9
|
_set_correct_rule_order( $meta, $nrule, @_ ); |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
sub _first_things_on_applies_cloned_rule { |
284
|
6
|
|
|
6
|
|
13
|
my ( $meta, $name ) = ( shift, shift ); |
285
|
6
|
|
|
|
|
17
|
_excepts_apply_cloned_rule( $meta, $name, @_ ); |
286
|
2
|
|
|
|
|
10
|
_set_default_of_attributes( $meta, auto_split => 1 ); |
287
|
|
|
|
|
|
|
} |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
sub _excepts_apply_cloned_rule { |
290
|
6
|
|
|
6
|
|
14
|
my ( $meta, $name ) = ( shift, shift ); |
291
|
6
|
|
|
|
|
21
|
_rulespec_cant_be_in_main( $meta, $name, 'applies_cloned_rule' ); |
292
|
6
|
|
|
|
|
18
|
_must_have_named_super( $meta, $name ); |
293
|
3
|
|
|
|
|
17
|
_check_args_hash_stuff( $meta, "applies_cloned_rule $name", @_ ); |
294
|
3
|
100
|
|
|
|
13
|
parser_exception("$name is not an existing rule ; can\'t clone it") |
295
|
|
|
|
|
|
|
if not _is_existing_rule( $name, $meta ); |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
my %clone_options = ( %rule_options, add_precondition => 1, ); |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
sub _must_have_named_super { |
301
|
6
|
|
|
6
|
|
13
|
my ( $meta, $name ) = ( shift, shift ); |
302
|
|
|
|
|
|
|
parser_exception("applies_cloned_rule requires original rule name") |
303
|
|
|
|
|
|
|
if not defined $name |
304
|
|
|
|
|
|
|
or ( '' ne ref($name) ) |
305
|
6
|
100
|
100
|
|
|
55
|
or ( exists $clone_options{$name} ); |
|
|
|
100
|
|
|
|
|
306
|
|
|
|
|
|
|
} |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
sub _is_existing_rule { |
309
|
3
|
|
|
3
|
|
9
|
my ( $rname, $meta ) = ( shift, shift ); |
310
|
3
|
100
|
|
|
|
109
|
return 1 if Text::Parser::RuleSpec->is_known_rule($rname); |
311
|
2
|
100
|
|
|
|
20
|
return 0 if $rname =~ /\//; |
312
|
1
|
|
|
|
|
41
|
return Text::Parser::RuleSpec->is_known_rule( |
313
|
|
|
|
|
|
|
$meta->name . '/' . $rname ); |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
sub _qualified_rulename { |
317
|
2
|
|
|
2
|
|
5
|
my ( $r, $meta ) = ( shift, shift ); |
318
|
2
|
100
|
|
|
|
71
|
return $meta->name . '/' . $r |
319
|
|
|
|
|
|
|
if not Text::Parser::RuleSpec->is_known_rule($r); |
320
|
1
|
|
|
|
|
6
|
return $r; |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
sub _gen_new_rule_name_from { |
324
|
2
|
|
|
2
|
|
5
|
my ( $meta, $oname ) = ( shift, shift ); |
325
|
2
|
|
|
|
|
9
|
my ( $cls, $rname ) = split( /\//, $oname, 2 ); |
326
|
2
|
100
|
|
|
|
6
|
$rname = $cls if not defined $rname; |
327
|
2
|
|
|
|
|
9
|
my $nname = $meta->name . '/' . $rname; |
328
|
2
|
100
|
|
|
|
76
|
return $rname if not Text::Parser::RuleSpec->is_known_rule($nname); |
329
|
1
|
|
|
|
|
3
|
my $incr = 2; |
330
|
1
|
|
|
|
|
38
|
$incr++ while Text::Parser::RuleSpec->is_known_rule("$nname\@$incr"); |
331
|
1
|
|
|
|
|
4
|
return "$rname\@$incr"; |
332
|
|
|
|
|
|
|
} |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
sub _register_cloned_rule { |
335
|
2
|
|
|
2
|
|
4
|
my ( $key, $orule ) = ( shift, shift ); |
336
|
2
|
|
|
|
|
10
|
my %opts = _get_rule_opts_only(@_); |
337
|
2
|
|
|
|
|
73
|
my $o = Text::Parser::RuleSpec->class_rule_object($orule); |
338
|
2
|
|
|
|
|
14
|
my $rule = $o->clone(%opts); |
339
|
2
|
|
|
|
|
80
|
Text::Parser::RuleSpec->_add_new_rule( $key => $rule ); |
340
|
|
|
|
|
|
|
} |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
sub disables_superclass_rules { |
344
|
7
|
|
|
7
|
1
|
36317
|
my $meta = shift; |
345
|
7
|
|
|
|
|
28
|
_rulespec_cant_be_in_main( $meta, undef, 'disables_superclass_rules' ); |
346
|
6
|
|
|
|
|
30
|
_check_disable_rules_args( $meta->name, @_ ); |
347
|
2
|
|
|
|
|
7
|
_find_and_remove_superclass_rules( $meta, @_ ); |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
sub _check_disable_rules_args { |
351
|
6
|
|
|
6
|
|
12
|
my $cls = shift; |
352
|
6
|
100
|
|
|
|
25
|
parser_exception( |
353
|
|
|
|
|
|
|
"No arguments specified in call to disable_superclass_rules") |
354
|
|
|
|
|
|
|
if not @_; |
355
|
5
|
|
|
|
|
13
|
foreach my $a (@_) { |
356
|
7
|
|
|
|
|
18
|
_test_rule_type_and_val( $cls, $a ); |
357
|
|
|
|
|
|
|
} |
358
|
|
|
|
|
|
|
} |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
my %disable_arg_types = ( '' => 1, 'Regexp' => 1, 'CODE' => 1 ); |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
sub _test_rule_type_and_val { |
363
|
7
|
|
|
7
|
|
14
|
my $type_a = ref( $_[1] ); |
364
|
|
|
|
|
|
|
parser_exception( |
365
|
|
|
|
|
|
|
"Rules must be selected by regular expressions or a code") |
366
|
7
|
100
|
|
|
|
25
|
if not exists $disable_arg_types{$type_a}; |
367
|
6
|
100
|
|
|
|
18
|
_test_rule_string_val(@_) if $type_a eq ''; |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
sub _test_rule_string_val { |
371
|
4
|
|
|
4
|
|
12
|
my ( $cls, $a ) = ( shift, shift ); |
372
|
4
|
100
|
|
|
|
22
|
parser_exception( |
373
|
|
|
|
|
|
|
"disable_superclass_rule called with $a ; must be in format <superclass>/<rulename>" |
374
|
|
|
|
|
|
|
) if $a !~ /\//; |
375
|
3
|
|
|
|
|
11
|
my @c = split /\//, $a, 2; |
376
|
3
|
100
|
|
|
|
15
|
parser_exception("Cannot disable rules of same class") if $c[0] eq $cls; |
377
|
|
|
|
|
|
|
} |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
sub _find_and_remove_superclass_rules { |
380
|
2
|
|
|
2
|
|
4
|
my $meta = shift; |
381
|
2
|
|
|
|
|
6
|
_if_empty_prepopulate_rules_from_superclass($meta); |
382
|
2
|
|
|
|
|
11
|
my @ord = _filtered_rules( $meta->name, @_ ); |
383
|
2
|
|
|
|
|
90
|
Text::Parser::RuleSpec->_set_class_rule_order( $meta->name => \@ord ); |
384
|
2
|
|
|
|
|
11
|
Text::Parser::RuleSpec->populate_class_rules( $meta->name ); |
385
|
|
|
|
|
|
|
} |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
sub _filtered_rules { |
388
|
2
|
|
|
2
|
|
5
|
my $cls = shift; |
389
|
2
|
|
|
|
|
3
|
local $_; |
390
|
2
|
100
|
|
|
|
8
|
map { _is_to_be_filtered( $_, @_ ) ? () : $_ } |
|
5
|
|
|
|
|
22
|
|
391
|
|
|
|
|
|
|
( Text::Parser::RuleSpec->class_rule_order($cls) ); |
392
|
|
|
|
|
|
|
} |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
my %test_for_filter_type = ( |
395
|
|
|
|
|
|
|
'' => sub { $_[0] eq $_[1]; }, |
396
|
|
|
|
|
|
|
'Regexp' => sub { $_[0] =~ $_[1]; }, |
397
|
|
|
|
|
|
|
'CODE' => sub { $_[1]->( $_[0] ); }, |
398
|
|
|
|
|
|
|
); |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
sub _is_to_be_filtered { |
401
|
5
|
|
|
5
|
|
11
|
my $r = shift; |
402
|
5
|
|
|
|
|
7
|
foreach my $p (@_) { |
403
|
9
|
|
|
|
|
17
|
my $t = ref $p; |
404
|
9
|
100
|
|
|
|
34
|
return 1 if $test_for_filter_type{$t}->( $r, $p ); |
405
|
|
|
|
|
|
|
} |
406
|
1
|
|
|
|
|
9
|
return 0; |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
sub unwraps_lines_using { |
411
|
2
|
|
|
2
|
1
|
6977
|
my $meta = shift; |
412
|
2
|
|
|
|
|
12
|
_rulespec_cant_be_in_main( $meta, undef, 'unwraps_lines_using' ); |
413
|
1
|
|
|
|
|
4
|
my ( $is_wr, $un_wr ) = _check_custom_unwrap_args(@_); |
414
|
1
|
|
|
|
|
76
|
_set_lws_and_routines( $meta, $is_wr, $un_wr ); |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
sub _check_custom_unwrap_args { |
418
|
8
|
100
|
|
8
|
|
29
|
parser_exception( "Needs exactly 4 arguments ; " . scalar(@_) . " given" ) |
419
|
|
|
|
|
|
|
if @_ != 4; |
420
|
7
|
|
|
|
|
24
|
_test_fields_unwrap_rtn(@_); |
421
|
3
|
|
|
|
|
11
|
my (%opt) = @_; |
422
|
3
|
|
|
|
|
12
|
return ( $opt{is_wrapped}, $opt{unwrap_routine} ); |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
sub _test_fields_unwrap_rtn { |
426
|
7
|
|
|
7
|
|
33
|
my (%opt) = (@_); |
427
|
|
|
|
|
|
|
parser_exception( |
428
|
|
|
|
|
|
|
"unwraps_lines_using must have keys: is_wrapped, unwrap_routine") |
429
|
7
|
100
|
100
|
|
|
48
|
if not( exists $opt{is_wrapped} and exists $opt{unwrap_routine} ); |
430
|
4
|
|
|
|
|
21
|
_is_arg_a_code( $_, %opt ) for (qw(is_wrapped unwrap_routine)); |
431
|
|
|
|
|
|
|
} |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
sub _is_arg_a_code { |
434
|
7
|
|
|
7
|
|
23
|
my ( $arg, %opt ) = (@_); |
435
|
|
|
|
|
|
|
parser_exception( |
436
|
|
|
|
|
|
|
"$arg in call to unwraps_lines_using must be code reference") |
437
|
7
|
100
|
|
|
|
36
|
if 'CODE' ne ref( $opt{$arg} ); |
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
sub _set_lws_and_routines { |
441
|
1
|
|
|
1
|
|
3
|
my ( $meta, $is_wr, $unwr ) = @_; |
442
|
1
|
|
|
|
|
5
|
_set_default_of_attributes( $meta, line_wrap_style => 'custom' ); |
443
|
1
|
|
|
1
|
|
307
|
_set_default_of_attributes( $meta, _is_wrapped => sub { $is_wr; } ); |
|
1
|
|
|
|
|
28
|
|
444
|
1
|
|
|
1
|
|
253
|
_set_default_of_attributes( $meta, _unwrap_routine => sub { $unwr; } ); |
|
1
|
|
|
|
|
24
|
|
445
|
|
|
|
|
|
|
} |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
449
|
|
|
|
|
|
|
|
450
|
42
|
|
|
42
|
|
212386
|
no Moose; |
|
42
|
|
|
|
|
158
|
|
|
42
|
|
|
|
|
445
|
|
451
|
42
|
|
|
42
|
|
13409
|
no MooseX::ClassAttribute; |
|
42
|
|
|
|
|
164
|
|
|
42
|
|
|
|
|
416
|
|
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
1; |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
__END__ |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
=pod |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
=encoding UTF-8 |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
=head1 NAME |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
Text::Parser::RuleSpec - Syntax sugar for rule specification while subclassing Text::Parser or derivatives |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
=head1 VERSION |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
version 1.000 |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
=head1 SYNOPSIS |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
package MyFavorite::Parser; |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
use Text::Parser::RuleSpec; |
474
|
|
|
|
|
|
|
extends 'Text::Parser'; |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
has '+multiline_type' => (default => 'join_next'); |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
unwraps_lines_using ( |
479
|
|
|
|
|
|
|
is_wrapped => sub { |
480
|
|
|
|
|
|
|
my $self = shift; |
481
|
|
|
|
|
|
|
$_ = shift; |
482
|
|
|
|
|
|
|
chomp; |
483
|
|
|
|
|
|
|
m/\s+[~]\s*$/; |
484
|
|
|
|
|
|
|
}, |
485
|
|
|
|
|
|
|
unwrap_routine => sub { |
486
|
|
|
|
|
|
|
my ($self, $last, $current) = @_; |
487
|
|
|
|
|
|
|
chomp $last; |
488
|
|
|
|
|
|
|
$last =~ s/\s+[~]\s*$//g; |
489
|
|
|
|
|
|
|
"$last $current"; |
490
|
|
|
|
|
|
|
}, |
491
|
|
|
|
|
|
|
); |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
applies_rule get_emails => ( |
494
|
|
|
|
|
|
|
if => '$1 eq "EMAIL:"', |
495
|
|
|
|
|
|
|
do => '$2;' |
496
|
|
|
|
|
|
|
); |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
package main; |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
my $parser = MyFavorite::Parser->new(); |
501
|
|
|
|
|
|
|
$parser->read('/path/to/email_lists.txt'); |
502
|
|
|
|
|
|
|
my (@emails) = $parser->get_records(); |
503
|
|
|
|
|
|
|
print "Here are all the emails from the file: @emails\n"; |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
=head1 DESCRIPTION |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
=head2 Primary usage |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
This class enables users to create their own parser classes for a known text file format, and facilitates code-sharing across multiple variants of the same basic text format. The basic steps are as follows: |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
package MyFavorite::Parser; |
512
|
|
|
|
|
|
|
use Text::Parser::RuleSpec; |
513
|
|
|
|
|
|
|
extends 'Text::Parser'; |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
That's it! This is the bare-minimum required to make your own text parser. But it is not particularly useful at this point without any rules of its own. |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
applies_rule comment_char => ( |
518
|
|
|
|
|
|
|
if => '$1 =~ /^#/;', |
519
|
|
|
|
|
|
|
dont_record => 1, |
520
|
|
|
|
|
|
|
); |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
This above rule ignores all comment lines and is added to C<MyFavorite::Parser> class. So now when you create an instance of C<MyFavorite::Parser>, it would automatically run this rule when you call C<L<read|Text::Parser/read>>. |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
We can preset any attributes for this parser class using the familiar L<Moose> functions. Here is an example: |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
has '+line_wrap_style' => ( |
527
|
|
|
|
|
|
|
default => 'trailing_backslash', |
528
|
|
|
|
|
|
|
is => 'ro', |
529
|
|
|
|
|
|
|
); |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
has '+auto_trim' => ( |
532
|
|
|
|
|
|
|
default => 'b', |
533
|
|
|
|
|
|
|
is => 'ro', |
534
|
|
|
|
|
|
|
); |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
=head2 Using attributes for storage |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
Sometimes, you may want to store the parsed information in attributes, instead of records. So for example: |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
has current_section => ( |
541
|
|
|
|
|
|
|
is => 'rw', |
542
|
|
|
|
|
|
|
isa => 'Str|Undef', |
543
|
|
|
|
|
|
|
default => undef, |
544
|
|
|
|
|
|
|
lazy => 1, |
545
|
|
|
|
|
|
|
); |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
has _num_lines_by_section => ( |
548
|
|
|
|
|
|
|
is => 'rw', |
549
|
|
|
|
|
|
|
isa => 'HashRef[Int]', |
550
|
|
|
|
|
|
|
default => sub { {}; }, |
551
|
|
|
|
|
|
|
lazy => 1, |
552
|
|
|
|
|
|
|
handles => { |
553
|
|
|
|
|
|
|
num_lines => 'get', |
554
|
|
|
|
|
|
|
_set_num_lines => 'set', |
555
|
|
|
|
|
|
|
} |
556
|
|
|
|
|
|
|
); |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
applies_rule inc_section_num_lines => ( |
559
|
|
|
|
|
|
|
if => '$1 ne "SECTION"', |
560
|
|
|
|
|
|
|
do => 'my $sec = $this->current_section; |
561
|
|
|
|
|
|
|
my $n = $this->num_lines($sec); |
562
|
|
|
|
|
|
|
$this->_set_num_lines($sec => $n+1);', |
563
|
|
|
|
|
|
|
dont_record => 1, |
564
|
|
|
|
|
|
|
); |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
applies_rule get_section_name => ( |
567
|
|
|
|
|
|
|
if => '$1 eq "SECTION"', |
568
|
|
|
|
|
|
|
do => '$this->current_section($2); $this->_set_num_lines($2 => 0);', |
569
|
|
|
|
|
|
|
dont_record => 1, |
570
|
|
|
|
|
|
|
); |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
In the above example, you can see how the section name we get from one rule is used in a different rule. |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
=head2 Inheriting rules in subclasses |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
We can further subclass a class that C<extends> L<Text::Parser>. Inheriting the rules of the superclass is automatic: |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
package MyParser1; |
579
|
|
|
|
|
|
|
use Text::Parser::RuleSpec; |
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
extends 'Text::Parser'; |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
applies_rule rule1 => ( |
584
|
|
|
|
|
|
|
do => '# something', |
585
|
|
|
|
|
|
|
); |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
package MyParser2; |
588
|
|
|
|
|
|
|
use Text::Parser::RuleSpec; |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
extends 'MyParser1'; |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
applies_rule rule1 => ( |
593
|
|
|
|
|
|
|
do => '# something else', |
594
|
|
|
|
|
|
|
); |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
Now, C<MyParser2> contains two rules: C<MyParser1/rule1> and C<MyParser2/rule1>. Note that both the rules in both classes are called C<rule1> and both will be executed. By default, rules of superclasses will be run before rules in the subclass. The subclass can change this order by explicitly stating that its own C<rule1> is run C<before> the C<rule1> of C<MyParser1>: |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
package MyParser2; |
599
|
|
|
|
|
|
|
use Text::Parser::RuleSpec; |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
extends 'MyParser1'; |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
applies_rule rule1 => ( |
604
|
|
|
|
|
|
|
do => '# something else', |
605
|
|
|
|
|
|
|
before => 'MyParser1/rule1', |
606
|
|
|
|
|
|
|
); |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
A subclass may choose to disable any superclass rules: |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
package MyParser3; |
611
|
|
|
|
|
|
|
use Text::Parser::RuleSpec; |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
extends 'MyParser2'; |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
disables_superclass_rules qr/^MyParser1/; # disables all rules from MyParser1 class |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
Or to clone a rule from either the same class, a superclass, or even from some other random class. |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
package ClonerParser; |
620
|
|
|
|
|
|
|
use Text::Parser::RuleSpec; |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
use Some::Parser; # contains rules: "heading", "section" |
623
|
|
|
|
|
|
|
extends 'MyParser2'; |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
applies_rule my_own_rule => ( |
626
|
|
|
|
|
|
|
if => '# check something', |
627
|
|
|
|
|
|
|
do => '# collect some data', |
628
|
|
|
|
|
|
|
after => 'MyParser2/rule1', |
629
|
|
|
|
|
|
|
); |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
applies_cloned_rule 'MyParser2/rule1' => ( |
632
|
|
|
|
|
|
|
add_precondition => '# Additional condition', |
633
|
|
|
|
|
|
|
do => '# Optionally change the action', |
634
|
|
|
|
|
|
|
# prepend_action => '# Or just prepend something', |
635
|
|
|
|
|
|
|
# append_action => '# Or append something', |
636
|
|
|
|
|
|
|
after => 'MyParser1/rule1', |
637
|
|
|
|
|
|
|
); |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
Imagine this situation: Programmer A writes a text parser for a text format syntax SYNT1, and programmer B notices that the text format he wishes to parse (SYNT2) is similar, except for a few differences. Instead of having to re-write the code from scratch, he can reuse the code from programmer A and modify it exactly as needed. This is especially useful when syntaxes many different text formats are very similar. |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
=head1 METHODS |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
There is no constructor for this module. You cannot create an instance of C<Text::Parser::RuleSpec>. Therefore, all methods here can be called on the C<Text::Parser::RuleSpec> directly. |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
=head2 class_has_rules |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
Takes parser class name and returns a boolean representing if that class has any rules or not. Returns boolean true if the class has any rules, and a boolean false otherwise. |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
print "There are no class rules for MyFavorite::Parser.\n" |
650
|
|
|
|
|
|
|
if not Text::Parser::RuleSpec->class_has_rules('MyFavorite::Parser'); |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
=head2 class_rule_order |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
Takes a single string argument and returns the ordered list of rule names for the class. |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
my (@order) = Text::Parser::RuleSpec->class_rule_order('MyFavorite::Parser'); |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
=head2 class_rule_object |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
This takes a single string argument with the fully qualified rule name, and returns the actual rule object identified by that name. |
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
my $rule = Text::Parser::RuleSpec->class_rule_object('MyFavorite::Parser/rule1'); |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
=head2 class_rules |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
Takes a single string argument and returns the actual rule objects of the given class name. This is a shortcut to first running C<class_rule_order> and then running C<class_rule_object> on each one of them. |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
my (@rules) = Text::Parser::RuleSpec->class_rules('MyFavorite::Parser'); |
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
=head2 is_known_rule |
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
Takes a string argument expected to be fully-qualified name of a rule. Returns a boolean that indicates if such a rule was ever compiled. The fully-qualified name of a rule is of the form C<Some::Class/rule_name>. Any suffixes like C<@2> or C<@3> should be included to check the existence of any cloned rules. |
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
print "Some::Parser::Class/some_rule is a rule\n" |
675
|
|
|
|
|
|
|
if Text::Parser::RuleSpec->is_known_rule('Some::Parser::Class/some_rule'); |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
=head2 populate_class_rules |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
Takes a parser class name as string argument. It populates the class rules according to the latest order of rules. |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
Text::Parser::RuleSpec->populate_class_rules('MyFavorite::Parser'); |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
=head1 FUNCTIONS |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
The following methods are exported into the namespace of your class by default, and may only be called outside the C<main> namespace. |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
=head2 applies_rule |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
Takes one mandatory string argument - a rule name - followed by the options to create a rule. These are the same as the arguments to the C<L<add_rule|Text::Parser/"add_rule">> method of L<Text::Parser> class. Returns nothing. Exceptions will be thrown if any of the required arguments are not provided. |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
applies_rule print_emails => ( |
692
|
|
|
|
|
|
|
if => '$1 eq "EMAIL:"', |
693
|
|
|
|
|
|
|
do => 'print $2;', |
694
|
|
|
|
|
|
|
dont_record => 1, |
695
|
|
|
|
|
|
|
continue_to_next => 1, |
696
|
|
|
|
|
|
|
); |
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
The above call to create a rule C<print_emails> in your class C<MyFavorite::Parser>, will save the rule as C<MyFavorite::Parser/print_emails>. So if you want to clone it in sub-classes or want to insert a rule before or after that in a sub-class, then this is the way to reference the rule. |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
Optionally, one may provide one of C<before> or C<after> clauses to specify when this rule is to be executed. |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
applies_rule check_line_syntax => ( |
703
|
|
|
|
|
|
|
if => '$1 ne "SECTION"', |
704
|
|
|
|
|
|
|
do => '$this->check_syntax($this->current_section, $_);', |
705
|
|
|
|
|
|
|
before => 'Parent::Parser/add_line_to_data_struct', |
706
|
|
|
|
|
|
|
); |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
The above rule will apply C<> |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
Exceptions will be thrown if the C<before> or C<after> rule does not have a class name in it, or if it is the same as the current class, or if the rule is not among the inherited rules so far. Only one of C<before> or C<after> clauses may be provided. |
711
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
=head2 applies_cloned_rule |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
Clones an existing rule to make a replica, but you can add options to change any parameters of the rule. |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
applies_cloned_rule 'Some::SuperClass::Parser/some_rule' => ( |
717
|
|
|
|
|
|
|
add_precondition => '1; # add some tests returning boolean', |
718
|
|
|
|
|
|
|
before => 'MayBe::Another::Superclass::Parser/some_other_rule', |
719
|
|
|
|
|
|
|
## Or even 'Some::SuperClass::Parser/another_rule' |
720
|
|
|
|
|
|
|
do => '## Change the do clause of original rule', |
721
|
|
|
|
|
|
|
); |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
The first argument must be a string containing the rule name to be cloned. You may clone a superclass rule, or even a rule from another class that you have only C<use>d in your code, but are not actually inheriting (using C<extends>). You may even clone a rule from the present class if the rule has been defined already. If the rule name specified contains a class name, then the exact rule is cloned, modified according to other clauses, and inserted into the rule order. But if the rule name specified does not have a classname, then the function looks for a rule with that name in the current class, and clones that one. |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
You may use one of the C<before> or C<after> clauses just like in C<applies_rule>. You may use any of the other rule creation options like C<if>, C<do>, C<continue_to_next>, or C<dont_record>. And you may optionally also use the C<add_precondition> clause. In many cases, you may not need any of the rule-creation options at all and may use only C<add_precondition> or any one of C<before> or C<after> clauses. If you do use any of the rule-creating options like C<do> or C<if>, then it will change those fields of the cloned copy of the original rule. |
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
Note that when you clone a rule, you do not change the original rule itself. You actually make a second copy and modify that. So you retain the original rule along with the clone. |
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
The new cloned rule created is automatically renamed by C<applies_cloned_rule>. If a rule C<Some::Other::Class/my_rule_1> is cloned into your parser class C<MyFavorite::Parser>, then the clone is named C<MyFavorite::Parser/my_rule_1>. This way, the original rule is left unaffected. If such a name already exists, then the clone adds C<@2> suffix to the name, viz., C<MyFavorite::Parser/my_rule_1@2>. If that also exists, it will be called C<MyFavorite::Parser/my_rule_1@3>. And so on it goes on incrementing. |
730
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
=head2 disables_superclass_rules |
732
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
Takes a list of rule names, or regular expression patterns, or subroutine references to identify rules that are to be disabled. You cannot disable rules of the same class. |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
A string argument is expected to contain the full rule-name (including class name) in the format C<My::Parser::Class/my_rule>. The C</> (slash) separating the class name and rule name is mandatory. |
736
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
A regexp argument is tested against the full rule-name. |
738
|
|
|
|
|
|
|
|
739
|
|
|
|
|
|
|
If a subroutine reference is provided, the subroutine is called for each rule in the class, and the rule is disabled if the subroutine returns a true value. |
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
disables_superclass_rules qw(Parent::Parser::Class/parent_rule Another::Class/another_rule); |
742
|
|
|
|
|
|
|
disables_superclass_rules qr/Parent::Parser::Class\/comm.*/; |
743
|
|
|
|
|
|
|
disables_superclass_rules sub { |
744
|
|
|
|
|
|
|
my $rulename = shift; |
745
|
|
|
|
|
|
|
$rulename =~ /[@]/; |
746
|
|
|
|
|
|
|
}; |
747
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
=head2 unwraps_lines_using |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
This function may be used if one wants to specify a custom line-unwrapping routine. Takes a hash argument with mandatory keys as follows: |
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
unwraps_lines_using( |
753
|
|
|
|
|
|
|
is_wrapped => sub { # Should return a boolean for each $line |
754
|
|
|
|
|
|
|
1; |
755
|
|
|
|
|
|
|
}, |
756
|
|
|
|
|
|
|
unwrap_routine => sub { # Should return a string for each $last and $line |
757
|
|
|
|
|
|
|
my ($self, $last, $line) = @_; |
758
|
|
|
|
|
|
|
$last.$line; |
759
|
|
|
|
|
|
|
}, |
760
|
|
|
|
|
|
|
); |
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
For the pair of routines to not cause unexpected C<undef> results, they should return defined values always. To effectively unwrap lines, the C<is_wrapped> routine should return a boolean C<1> when it encounters the continuation character, and C<unwrap_routine> should return a string that appropriately joins the last and current line together. |
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
=head1 SEE ALSO |
765
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
=over 4 |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
=item * |
769
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
L<Text::Parser::Manual::ExtendedAWKSyntax> - Read this manual to learn how to do cool things with this class |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
=item * |
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
L<Text::Parser::Error> - there is a change in how exceptions are thrown by this class. Read this page for more information. |
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
=back |
777
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
=head1 BUGS |
779
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
Please report any bugs or feature requests on the bugtracker website |
781
|
|
|
|
|
|
|
L<http://github.com/balajirama/Text-Parser/issues> |
782
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
When submitting a bug or request, please include a test-file or a |
784
|
|
|
|
|
|
|
patch to an existing test-file that illustrates the bug or desired |
785
|
|
|
|
|
|
|
feature. |
786
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
=head1 AUTHOR |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
Balaji Ramasubramanian <balajiram@cpan.org> |
790
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
This software is copyright (c) 2018-2019 by Balaji Ramasubramanian. |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
796
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
797
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
=cut |
799
|
|
|
|
|
|
|
|