File Coverage

lib/Parse/Gnaw.pm
Criterion Covered Total %
statement 270 363 74.3
branch 79 126 62.7
condition 4 6 66.6
subroutine 27 28 96.4
pod 17 17 100.0
total 397 540 73.5


line stmt bran cond sub pod time code
1              
2 41     41   991236 no warnings 'once';
  41         97  
  41         1670  
3              
4             package Parse::Gnaw;
5              
6 29     29   779 use 5.006;
  29         98  
  29         1018  
7 29     29   160 use strict;
  29         59  
  29         942  
8 29     29   142 use warnings FATAL => 'all';
  29         57  
  29         1050  
9              
10 29     29   1039 use Data::Dumper;
  29         10688  
  29         1870  
11 29     29   161 use Carp ('cluck','confess');
  29         58  
  29         1666  
12 29     29   37243 use Storable qw(nstore dclone retrieve);
  29         99401  
  29         49718  
13              
14             our $VERSION = '0.601';
15              
16             # this package doesn't play nice.
17             # it uses eval("") to create variables in the caller's namespace.
18             # if the caller uses these variables, they might get warnings about
19             # some variable only used once.
20             #
21             # to disable that warning, we need to do a
22             # no warnings 'once';
23             # except that's lexical and we can't do that here
24             #
25             # on the other hand, if we have an import function and from import call this:
26             # warnings->unimport("once");
27             # then the no warnings gets pulled into the calling package.
28             #
29             # We want to use the Exporter.pm module, but we can't "use" it like this:
30             # use Exporter
31             # because that creates a conflict when we declare our own import method.
32             # So, instead, we put Exporter in our @ISA and then we can define our import method.
33             #
34             # then from inside import, we call export_to_level to do the importing stuff for us
35             # that Exporter normally does, but now we're doing it inside an import sub
36             # which then allows us to call
37             # warnings->unimport("once");
38             #
39             # see"
40             # http://perldoc.perl.org/Exporter.html#Exporting-without-using-Exporter%27s-import-method
41             # and
42             # http://mail.pm.org/mailman/private/boston-pm/2013-May/014850.html
43             # From: "Ben Tilly"
44             # Date: Sat, May 4, 2013 6:46 pm
45             # If your module has an import method, and in that method calls
46             # warnings->unimport("once") then the unimport should be lexically
47             # scoped to where your package was used.
48              
49              
50             our @ISA = qw(Exporter);
51             our @EXPORT = qw ( rule predeclare lit call cc notcc thrifty alt );
52              
53             sub import {
54 26     26   686 warnings->unimport("once");
55 26         453 strict->unimport("vars");
56 26         15502 Parse::Gnaw->export_to_level(1,@_);
57             }
58              
59              
60             our $debug=0;
61              
62             sub format_package{
63 264     264 1 348 my $callerindex = 0;
64 264         301 while(1){
65 1044         5841 my @caller=caller($callerindex++);
66 1044         3349 my $package =$caller[0];
67 1044 100       3119 if($package =~ m{Parse::Gnaw}){
68              
69             } else {
70 264         837 return $package;
71             }
72             }
73             }
74              
75             sub format_filename{
76 264     264 1 344 my $callerindex = 0;
77 264         284 while(1){
78 1044         4528 my @caller=caller($callerindex++);
79 1044         3070 my $package =$caller[0];
80 1044         1163 my $filename=$caller[1];
81 1044 100       2985 if($package =~ m{Parse::Gnaw}){
82              
83             } else {
84 264         913 return $filename;
85             }
86             }
87              
88             }
89              
90             sub format_linenum{
91 264     264 1 305 my $callerindex = 0;
92 264         278 while(1){
93 1044         4291 my @caller=caller($callerindex++);
94 1044         3007 my $package =$caller[0];
95 1044         1071 my $linenum =$caller[2];
96 1044 100       3008 if($package =~ m{Parse::Gnaw}){
97              
98             } else {
99 264         747 return $linenum;
100             }
101             }
102             }
103              
104              
105             sub eval_string{
106 327     327 1 485 my $string=shift(@_);
107              
108 327 50       649 if($debug){
109 0         0 my @caller=caller(1);
110 0         0 my $filename=$caller[1];
111 0         0 my $linenum =$caller[2];
112 0         0 print "eval_string('$string') called from $filename, line $linenum\n";
113             }
114              
115 327         326 my $eval_return;
116              
117 327     22   15080 eval($string);
  22     15   205  
  22         45  
  22         1065  
  15         85  
  15         31  
  15         427  
118 327 50       1040 if($@){
119 0         0 die $@;
120             }
121              
122 327         707 return $eval_return;
123             }
124              
125              
126             sub get_ref_to_rulebook{
127 166     166 1 284 my($package,$createifnotexist)=@_;
128            
129 166 50       372 if($debug){
130 0         0 my @caller=caller(1);
131 0         0 my $filename=$caller[1];
132 0         0 my $linenum =$caller[2];
133 0         0 print "called get_ref_to_rulebook($package) from $filename at $linenum\n";
134             }
135              
136 166         561 my $retval = eval_string("\$eval_return = \$".$package."::rulebook;");
137              
138 166 100 66     929 if(defined($retval) and (ref($retval) eq 'HASH')){
139 144         330 return $retval;
140             }
141              
142 22 50       86 if($createifnotexist){
143 22         117 $retval=eval_string('$'.$package."::rulebook={}; \$eval_return = \$".$package.'::rulebook;');
144 22         77 return $retval;
145             }
146              
147 0         0 return;
148             }
149              
150              
151             sub get_ref_to_rulename{
152 70     70 1 148 my($package,$rulename,$createifnotexist)=@_;
153            
154 70 50       173 if($debug){
155 0         0 my @caller=caller(1);
156 0         0 my $filename=$caller[1];
157 0         0 my $linenum =$caller[2];
158 0         0 print "called get_ref_to_rulename($package,$rulename) from $filename at $linenum\n";
159             }
160              
161 70         215 my $package_rulename = $package.'::'.$rulename;
162              
163 70         224 my $retval = eval_string("no warnings 'once'; \$eval_return = \$".$package_rulename.";");
164              
165 70 100 66     264 if(defined($retval) and (ref($retval) eq 'ARRAY')){
166 1         3 return $retval;
167             }
168              
169 69 50       168 if($createifnotexist){
170 69         249 my $ruleref=eval_string('$'.$package_rulename."=[]; \$eval_return = \$".$package_rulename.";");
171              
172             # put it in the rulebook.
173 69         164 my $bookref = get_ref_to_rulebook($package,1);
174 69         175 $bookref->{$rulename}=$ruleref;
175              
176 69         181 return $ruleref;
177             }
178              
179 0         0 return;
180             }
181              
182              
183              
184             sub process_first_arguments_and_return_hash_ref{
185              
186             #print Dumper \@_; warn "process_first_arguments_and_return_hash_ref arguments (above)";
187              
188              
189             # first parameter string is same as payload=> key in hash
190             # need to know for error checking this:
191             # ('myrule',{method=>'rule', payload=>'myrule'})
192             # and need to know if hash doesn't exist or doesn't have the key for first parameter.
193             # i.e. this
194             # ('myrule')
195             # needs to return this:
196             # {payload=>'myrule'}
197             #
198             # on the other hand, if we're processing inputs to the lit() function, then first parameter is the literal
199             # lit('a',{payload=>'a'});
200             #
201             # the methodname is the subroutine name to call to execute the grammar.
202             # the methodname for a rule is 'rule'
203             # the methodname for a literal is 'lit'
204             # the methodname for a thrifty quantifier is 'thrifty'
205             #
206             # every hash methodname -> methodvalue should have a corresponding
207             # payload -> loadvalue combination.
208             # for example a literal might look like this: { methodname=>'lit', payload=>'a' };
209             # the methodname tells us it is a 'lit'. payload tells us we're looking for the letter 'a'.
210 264     264 1 474 my $methodname=shift(@_);
211              
212             # passing in a reference so we can shift data off the array, and affect the array in the caller space as well.
213 264         339 my $argref = shift(@_);
214 264 50       1166 unless(ref($argref) eq 'ARRAY'){
215 0         0 confess "ERROR: called process_first_arguments_and_return_hash_ref, second argument should be an array reference, found $argref instead ";
216             }
217              
218              
219 264         293 my $parm_payload;
220 264 50       676 if(not(ref($argref->[0]))){
221 264         479 $parm_payload=shift(@$argref);
222             }
223              
224 264         527 my $package = format_package();
225 264         1582 my $source_filename = format_filename();
226 264         539 my $source_linenum = format_linenum();
227              
228 264         307 my $info_href;
229 264 100       678 if(ref($argref->[0]) eq 'HASH'){
230 206         324 my $orig_href=shift(@$argref);
231 206         5503 $info_href = dclone $orig_href;
232             } else {
233 58         115 $info_href={};
234             }
235              
236 264 50       555 if(defined($parm_payload)){
237 264 100       578 if(exists($info_href->{payload})){
238              
239             # passed in process ( 'a' { payload=>'a' } ) both 'a's must match.
240 80         146 my $hash_payload=$info_href->{payload};
241 80 50       254 unless($parm_payload eq $hash_payload){
242 0         0 print Dumper $info_href;
243 0         0 confess "ERROR: process_first_arguments_and_return_hash_ref parm_payload does not equal hash_payload $methodname ($parm_payload ne $hash_payload)";
244             }
245             } else {
246             # passed in parm_payload and do not have hash_payload. So, put it in hash.
247             # process ('a', {} )
248 184         434 $info_href->{payload}=$parm_payload;
249             }
250             } else {
251             # parm_payload is NOT passed in as string, MUST be defined in hash
252             # if we don't say process ('a', {} ), then we must say process ( { payload => 'a' } )
253 0 0       0 unless(exists($info_href->{payload})){
254 0         0 confess("ERROR: process_first_arguments_and_return_hash_ref without providing a $methodname anywhere");
255             }
256              
257             }
258              
259             # handle the rest of the defaults;
260 264 100       819 unless(exists($info_href->{package})){$info_href->{package} =$package;}
  70         271  
261 264 100       675 unless(exists($info_href->{filename})){$info_href->{filename} =$source_filename;;}
  70         131  
262 264 100       564 unless(exists($info_href->{linenum})){$info_href->{linenum} =$source_linenum;}
  70         204  
263 264 100       537 unless(exists($info_href->{methodname})){$info_href->{methodname} =$methodname;}
  184         320  
264              
265 264         725 return $info_href;
266             }
267              
268              
269             sub copy_location_info_and_make_new_hash_ref{
270 114     114 1 152 my($orig_href)=@_;
271              
272             # first copy over only the keys we want. this is a one-deep copy.
273             # if any hash values point to other references, those need to a deep copy.
274 114         173 my $one_deep_copy={};
275              
276 114         214 foreach my $key ('package', 'filename', 'linenum'){
277 342         849 $one_deep_copy->{$key}=$orig_href->{$key}
278             }
279              
280             # make a deep copy of just these keys
281 114         3839 my $full_separate_copy = dclone $one_deep_copy;
282              
283 114         378 return $full_separate_copy;
284             }
285              
286              
287              
288             #######################################################################
289             #######################################################################
290             #######################################################################
291             sub rule {
292             #######################################################################
293             #######################################################################
294             #######################################################################
295 70     70 1 4413 my $argref=[@_];
296              
297              
298 70 50       210 if($debug){print "called rule, \@_ is: "; print Dumper \@_; warn " ";}
  0         0  
  0         0  
  0         0  
299              
300              
301 70         183 my $info_href=process_first_arguments_and_return_hash_ref('rule', $argref);
302 70 50       187 if($debug){print "called rule ";print Dumper $info_href; warn " ";}
  0         0  
  0         0  
  0         0  
303              
304 70         125 my $rulename = $info_href->{payload};
305 70         130 my $package = $info_href->{package};
306 70         98 my $filename = $info_href->{filename};
307 70         110 my $linenum = $info_href->{linenum};
308              
309 70 100       194 unless(exists($info_href->{quantifier})){
310 59         134 $info_href->{quantifier}='';
311             }
312              
313 70 50       232 if($rulename =~ m{\:\:}){
314 0         0 confess "ERROR: called rule and passed in a package name rule '$rulename'. Rulenames should not contain '::'";
315             }
316            
317 70         195 my $rulebook = get_ref_to_rulebook($package,1);
318              
319 70 100       244 if(exists($rulebook->{$rulename})){
320 3         9 my $oldruleinfo=$rulebook->{$rulename}->[0];
321              
322             #print Dumper $oldruleinfo; die;
323 3         6 my $hash_info = $oldruleinfo->[2];
324 3         29 my $oldmethod= $hash_info->{methodname};
325 3 100       15 if($oldmethod eq 'predeclare'){
326              
327             } else {
328              
329 1         14 warn "warning: redefining rule '$rulename' for package '$package'";
330              
331             # element ->[0] in rule array is the 'rule' method. element ->[1] in 'rule' method is the info_href.
332 1         71 print "original rule: "; print Dumper $rulebook->{$rulename}->[0]->[1];
  1         8  
333 1         102 print "new rule: "; print Dumper $info_href;
  1         3  
334             }
335             }
336            
337 70         252 my $currentrule = get_ref_to_rulename($package,$rulename,1);
338              
339             # empty out the array for the rule
340 70         144 @$currentrule = ();
341              
342             # first index into rule array is a "ruleinfo" marker to indicate info about this rule
343             # such as rulename, where it came from, and other information.
344 70         206 push(@$currentrule, ['rule',$rulename, $info_href]);
345              
346              
347             # now go through the subrules and format them properly.
348             # a big thing to do is convert strings like 'a' into [ 'lit', 'a', {info} ]
349             # this allows a rule to be a lot less verbose.
350 70         104 my $index=-1;
351 70         178 while(@$argref){
352 153         170 $index++;
353              
354 153 50       320 if($debug){warn "shifting element of 'rule', index $index";}
  0         0  
355              
356 153         224 my $subrule=shift(@$argref);
357              
358 153         197 my $isnumber=0;
359 153         208 my $isstring=0;
360 153         219 my $isarray=0;
361 153         165 my $ishash=0;
362              
363 153         239 my $ref=ref($subrule);
364 153 100       368 if($ref){
365 60 50       140 if($ref eq 'ARRAY'){
    0          
366 60         92 $isarray=1;
367             } elsif($ref eq 'HASH'){
368 0         0 $ishash=1;
369             }
370             }else{
371 26     26   286 no warnings 'numeric';
  26         61  
  26         65501  
372 93 50       459 if($subrule eq $subrule+0){
373 0         0 $isnumber=1;
374             }else{
375 93         194 $isstring=1;
376             }
377             }
378              
379 153         237 my @subrules=();
380              
381             # if subrule is 'a', convert that to a literal subrule.
382 153 100       351 if($isstring){
    50          
383 93 50       202 if($debug){warn "subrule is string '$subrule'";}
  0         0  
384              
385             # make a copy of hash ref and use that for lit() otherwise the original info_href gets tainted.
386 93         195 my $location_href=copy_location_info_and_make_new_hash_ref($info_href);
387 93         283 @subrules = lit($subrule, $location_href);
388              
389            
390             # if subrule is an array reference, then fill in the hash ref with any info the caller didn't have.
391             } elsif($isarray){
392 60 50       142 if($debug){warn "subrule is array "; print Dumper $subrule; warn " ";}
  0         0  
  0         0  
  0         0  
393 60         142 my ($method,$payload,$subinfo)=@$subrule;
394 60         167 $subinfo=process_first_arguments_and_return_hash_ref($method,[$payload,$subinfo]);
395 60         229 @subrules = ( [$method,$payload,$subinfo] );
396              
397             # if its a hashref, then 'method' key points to a value like 'lit'.
398             # and 'lit' will poitn to the actual payload such as 'a'.
399             # and the rest will contain whatever location info caller passed in.
400             #} elsif($ishash){
401             # my $method=$subrule->{method};
402             # my $payload=$subrule->{$method};
403             # my $subinfo = process_first_arguments_and_return_hash_ref($method,[$payload,$subrule]);
404             # @subrules = ( [$method,$payload,$subinfo] );
405              
406             } else {
407 0         0 print "\n\n\n";
408 0         0 print Dumper $subrule;
409 0         0 print "\n\n\n";
410              
411 0         0 confess "ERROR: dont know how to handle subrule '$subrule' at $filename, $linenum ";
412             }
413              
414 153         562 push(@$currentrule, @subrules);
415             }
416              
417             # now fragment the rule so we can reorder how its called:
418 70         219 fragment_a_rule($currentrule);
419             }
420              
421              
422              
423              
424              
425              
426             # each rule may be split up into fragments
427             # myrule : 'a' 'b' 'c'
428             # might get split up into
429             # myrule : 'a' myrule_fragment_2
430             # myrule_fragment_2 : 'b' myrule_fragment_3
431             # myrule_fragment_3 : 'c'
432             # need to keep count of how many fragments so the rulenames for each fragment is unique
433             my $rulefragcntr={};
434              
435             sub fragment_suffix(){'_rulefragment_'}
436              
437             sub fragment_a_rule{
438 70     70 1 109 my ($currentrule)=@_;
439 70         154 my @subrules = @$currentrule;
440 70         124 @$currentrule=();
441              
442 70         101 my $first_subrule=$subrules[0];
443              
444 70         105 my $hash_info = $first_subrule->[2];
445              
446 70         125 my $rulename=$hash_info->{payload};
447              
448 70         195 while(@subrules){
449 198         316 my $subrule = shift(@subrules);
450 198         277 push(@$currentrule, $subrule);
451              
452 198 100       567 return if(scalar(@subrules)==0);
453              
454 149         236 my $subinfo = $subrule->[2];
455 149         195 my $method = $subrule->[0];
456 149 100       313 my $iscall = ($method eq 'call') ? 1 : '';
457              
458 149         190 my $last_subrule= (scalar(@subrules)==0);
459            
460              
461 149 100       526 if($iscall){
462              
463             # its a rule call.
464             # will still call the rule, but want to append a "then_call" attribute
465             # everything AFTER the call will go into a new rule fragment.
466             # will put a then_call to that fragment.
467              
468 21         41 my $fragment_suffix=fragment_suffix();
469 21         31 my $rulename_without_suffix = $rulename;
470 21         281 $rulename_without_suffix=~s{$fragment_suffix\d+}{};
471              
472 21         53 my $package = $subinfo->{package};
473 21         46 my $key_for_rule_fragment_counter = $package.'::'.$rulename_without_suffix;
474 21 100       72 unless(exists($rulefragcntr->{$key_for_rule_fragment_counter})){
475 15         38 $rulefragcntr->{$key_for_rule_fragment_counter}=0;
476             }
477 21         48 $rulefragcntr->{$key_for_rule_fragment_counter}=$rulefragcntr->{$key_for_rule_fragment_counter}+1;
478 21         33 my $rule_fragment_count = $rulefragcntr->{$key_for_rule_fragment_counter};
479              
480 21         52 my $fragrulename = $rulename_without_suffix.$fragment_suffix.$rule_fragment_count;
481              
482 21         47 my $hashforfragcall = copy_location_info_and_make_new_hash_ref( $subinfo );
483 21         45 delete($hashforfragcall->{payload});
484              
485             # now that we've copied the subinfo from the call,
486             # mark the subinfo then_call attribute
487 21         48 $subinfo->{then_call}=$fragrulename;
488              
489             # whatever is left goes into the rule fragment.
490 21         134 rule($fragrulename, $hashforfragcall, @subrules);
491 21         209 @subrules=();
492             }
493             }
494              
495             }
496              
497              
498             # lit('hello') will turn into 5 individual lits 'h', 'e', 'l', 'l', 'o'.
499             # if you don't want to split them up into individual letters, use term() function instead.
500             #
501             # FYI: can call this with lit('a', {hashref with location info});
502             #
503             # could conceivably also call it with lit('a', {lit=>'a'}) though that would be a bit weird.
504             #
505             # could even call it with lit({method=>'lit', lit=>'a', etc})
506             sub lit{
507 93     93 1 213 my $argref=[@_];
508              
509 93 50       231 if($debug){print "called lit, \@_ is: "; print Dumper \@_; warn " ";}
  0         0  
  0         0  
  0         0  
510              
511 93         211 my $info_href=process_first_arguments_and_return_hash_ref('lit', $argref);
512 93 50       254 if($debug){print "called lit ";print Dumper $info_href; warn " ";}
  0         0  
  0         0  
  0         0  
513              
514 93         157 my $lit = $info_href->{payload};
515              
516 93         271 my @letters=split(//,$lit);
517              
518 93         122 my @retval;
519              
520 93         170 foreach my $letter (@letters){
521 98         1514 my $dclone_href = dclone $info_href;
522 98         415 push(@retval, ['lit', $letter, $dclone_href]);
523             }
524            
525              
526 93         507 return (@retval);
527             }
528              
529              
530             sub predeclare {
531             #######################################################################
532             #######################################################################
533             #######################################################################
534 2     2 1 405 my $argref=[@_];
535              
536              
537 2 50       13 if($debug){print "called predeclare, \@_ is: "; print Dumper \@_; warn " ";}
  0         0  
  0         0  
  0         0  
538              
539              
540 2         11 my $info_href=process_first_arguments_and_return_hash_ref('predeclare', $argref);
541 2 50       8 if($debug){print "called predeclare ";print Dumper $info_href; warn " ";}
  0         0  
  0         0  
  0         0  
542              
543 2         6 my $rulename = $info_href->{payload};
544 2         4 my $package = $info_href->{package};
545 2         11 my $filename = $info_href->{filename};
546 2         12 my $linenum = $info_href->{linenum};
547              
548 2 50       9 unless(exists($info_href->{quantifier})){
549 2         7 $info_href->{quantifier}='';
550             }
551              
552 2 50       9 if($rulename =~ m{\:\:}){
553 0         0 confess "ERROR: called rule and passed in a package name rule '$rulename'. Rulenames should not contain '::'";
554             }
555            
556 2         12 my $rulebook = get_ref_to_rulebook($package,1);
557              
558 2         15 $rulebook->{$rulename}=[['predeclare', $rulename, $info_href]];
559             }
560              
561             #######################################################################
562             #######################################################################
563             #######################################################################
564             sub call{
565             #######################################################################
566             #######################################################################
567             #######################################################################
568 25     25 1 689 my $argref=[@_];
569              
570 25 50       69 if($debug){print "called 'call', \@_ is: "; print Dumper \@_; warn " ";}
  0         0  
  0         0  
  0         0  
571              
572 25         59 my $info_href=process_first_arguments_and_return_hash_ref('call', $argref);
573 25 50       98 if($debug){print "called 'call' ";print Dumper $info_href; warn " ";}
  0         0  
  0         0  
  0         0  
574              
575 25         57 my $ruletocall = $info_href->{payload};
576            
577 25         49 my $package = $info_href->{package};
578              
579 25         68 my $rulebook = get_ref_to_rulebook($package,1);
580 25 100       98 unless(exists($rulebook->{$ruletocall})){
581 1         4 my $msg="WARNING: call passed a nonexistent rulename '$ruletocall'";
582 1         185 print "$msg\n";
583 1         25 cluck($msg);
584              
585             }
586              
587 25         971 return ['call', $ruletocall, $info_href ];
588             }
589              
590              
591              
592              
593              
594              
595             my $thriftycounter=0;
596              
597             #######################################################################
598             #######################################################################
599             #######################################################################
600             sub thrifty{
601             #######################################################################
602             #######################################################################
603             #######################################################################
604 11     11 1 3615 my $argref=[@_];
605              
606              
607             #print "called thrifty ";print Dumper \@_; warn " ";
608 11         32 my $min_max=pop(@$argref);
609              
610 11 100       62 if(ref($min_max) eq 'HASH'){
611             # do nothing, assume user passed in {min=>8, max=>33}
612             } else {
613             # user didn't pass in a hash. Create a hash, cause we need a hash.
614 6         10 my ($min, $max);
615              
616             # if its an array, assume its [min,max]
617 6 50       22 if(ref($min_max) eq 'ARRAY'){
618 0         0 ($min,$max)=@$min_max;
619              
620             # else, its a string, try to deal with various formats
621             }else{
622 6 50       31 if($min_max =~ m{\A(\d+)?\,(\d+)?\Z}){
    50          
    0          
    0          
623 0         0 ($min,$max)=($1,$2);
624             } elsif($min_max eq '+'){
625 6         15 ($min,$max)=(1,-999);
626             } elsif($min_max eq '*'){
627 0         0 ($min,$max)=(0,-999);
628             } elsif($min_max eq '?'){
629 0         0 ($min,$max)=(0,1);
630             } else {
631 0         0 die "ERROR: thrifty can't handle min-max indicator '$min_max' ";
632             }
633             }
634              
635             # now that we've extracted min/max from array or string, create a hash.
636 6         27 $min_max={min=>$min,max=>$max};
637             }
638              
639 11         40 my $thrifty_rule_name = "thrifty_".(++$thriftycounter);
640              
641 11         33 $min_max->{quantifier}='thrifty';
642              
643             # now call the process function to fill in info that might be missing, like filename and linenum.
644             # this call needs min_max to be a hash.
645 11         63 $min_max=process_first_arguments_and_return_hash_ref('rule', [$thrifty_rule_name, $min_max]);
646              
647 11 50       49 if($debug){print "in THRIFTY "; print Dumper $min_max; warn " ";}
  0         0  
  0         0  
  0         0  
648              
649             # remainder of @_ is the stuff for the thrifty rule.
650             # create a new rule and put the quantify stuff in it.
651 11         45 rule($thrifty_rule_name, $min_max, @$argref);
652              
653             # return a call to newly created thrifty rule.
654 11         44 my $retval = call($thrifty_rule_name, $min_max);
655              
656 11         73 return $retval;
657             }
658              
659              
660              
661              
662              
663              
664              
665              
666              
667              
668              
669              
670              
671              
672              
673              
674             sub cc{
675 2     2 1 418 my $argref=[@_];
676              
677 2         13 my $info_href=process_first_arguments_and_return_hash_ref('cc', $argref);
678 2 50       7 if($debug){print "called cc ";print Dumper $info_href; warn " ";}
  0         0  
  0         0  
  0         0  
679              
680             # charclass is a string of characters in the class, such as 'aeiou'.
681             # want to turn that into a hashref where the keys are the characters
682             # value doesn't matter, just make it a count
683              
684 2         5 my $charclass=$info_href->{payload};
685              
686 2         4 my $hash_of_letters={};
687 2         12 my @chars = split(//,$charclass);
688 2         5 foreach my $char (@chars){
689 10         24 $hash_of_letters->{$char}++;
690 10 50       34 if($hash_of_letters->{$char}>1){
691 0         0 print Dumper $info_href;
692 0         0 die "ERROR: called cc with duplicates in charclass '$charclass', duplicate is '$char'"; }
693             }
694              
695 2         7 $info_href->{hash_of_letters}=$hash_of_letters;
696              
697 2         6 my $retval = ['cc', $charclass, $info_href ];
698              
699 2         14 print Dumper $retval;
700              
701 2         404 return $retval;
702             }
703              
704             sub notcc{
705 1     1 1 651 my $argref=[@_];
706              
707 1         6 my $info_href=process_first_arguments_and_return_hash_ref('notcc', $argref);
708 1 50       4 if($debug){print "called notcc ";print Dumper $info_href; warn " ";}
  0         0  
  0         0  
  0         0  
709              
710             # charclass is a string of characters in the class, such as 'aeiou'.
711             # want to turn that into a hashref where the keys are the characters
712             # value doesn't matter, just make it a count
713              
714 1         3 my $charclass=$info_href->{payload};
715              
716 1         2 my $hash_of_letters={};
717 1         5 my @chars = split(//,$charclass);
718 1         2 foreach my $char (@chars){
719 5         13 $hash_of_letters->{$char}++;
720 5 50       15 if($hash_of_letters->{$char}>1){
721 0         0 print Dumper $info_href;
722 0         0 die "ERROR: called notcc with duplicates in charclass '$charclass', duplicate is '$char'"; }
723             }
724              
725 1         2 $info_href->{hash_of_letters}=$hash_of_letters;
726              
727 1         3 my $retval = ['notcc', $charclass, $info_href ];
728              
729 1         6 print Dumper $retval;
730              
731 1         1141 return $retval;
732             }
733              
734              
735              
736              
737             my $alternatecounter=0;
738              
739             # alt( [ 'a','b'], ['c','d'], ['e','f'] );
740             sub alt{
741 0     0 1 0 my $argref=['alternates', @_];
742              
743 0         0 my $info_href=process_first_arguments_and_return_hash_ref('alt', $argref);
744 0 0       0 if($debug){print "called alternation ";print Dumper $info_href; warn " ";}
  0         0  
  0         0  
  0         0  
745              
746 0         0 $info_href->{alternates}=[];
747              
748 0         0 while(@$argref){
749            
750 0         0 my $arr_ref=shift(@$argref);
751              
752             # should pass in a list of array refs. turn each one into a rule.
753 0 0       0 unless(ref($arr_ref) eq 'ARRAY'){
754 0         0 confess "ERROR: alternate should be passed a list of array references, each containing an alternate rule description. got '$arr_ref' instead";
755             }
756              
757 0         0 my $alternate_rule_name = "alternate_".(++$alternatecounter);
758            
759 0         0 push(@{$info_href->{alternates}}, $alternate_rule_name);
  0         0  
760              
761             # create a new rule and put the quantify stuff in it.
762 0         0 rule($alternate_rule_name, @$arr_ref);
763              
764              
765             }
766              
767              
768 0         0 my $retval = ['alt', 'alternates', $info_href ];
769              
770 0         0 print Dumper $retval;
771              
772 0         0 return $retval;
773             }
774              
775              
776              
777             1; # End of Parse::Gnaw
778              
779