File Coverage

blib/lib/Parser/Combinators.pm
Criterion Covered Total %
statement 281 459 61.2
branch 116 236 49.1
condition 2 3 66.6
subroutine 39 62 62.9
pod 0 35 0.0
total 438 795 55.0


line stmt bran cond sub pod time code
1             package Parser::Combinators;
2              
3 2     2   57489 use strict;
  2         6  
  2         72  
4 2     2   71 use 5.010;
  2         8  
5             our $VERSION = '0.05';
6              
7 2     2   14 use Exporter 'import';
  2         22  
  2         213  
8              
9             @Parser::Combinators::EXPORT = qw(
10             sequence
11             choice
12             try
13             maybe
14             regex
15             parens
16             char
17             sepBy
18             oneOf
19             word
20             natural
21             symbol
22             greedyUpto
23             upto
24             many
25             many1
26             whiteSpace
27             comma
28             matches
29             unwrap
30             empty
31             getParseTree
32             bindP
33             returnP
34             $V
35             );
36              
37 2     2   2815 use Data::Dumper;
  2         160311  
  2         10685  
38             our $V= 0;
39              
40             # Forward declarations with prototypes
41              
42             #sub symbol ($);
43             #sub char($);
44             #sub maybe($);
45             #sub sepBy($$);
46             #sub oneOf($);
47              
48              
49             # I want to write the parser using lists, because sequencing is the most common operation.
50             # So I need a function to generate the actual parser from the lists
51             # which is actually a sequence of parsers
52             # The first arg is a list ref, the second arg is an optional code ref to process the returned list of matches
53             sub sequence_ORIG {
54 0     0 0 0 (my $plst, my $proc)=@_;
55 0     0   0 my $gen = sub { (my $str)=@_;
56 0 0       0 say "* sequence( '$str' )" if $V;
57 0         0 my $matches=[];
58 0         0 my $st=1;
59 0         0 my $str2='';
60 0         0 my $ms=undef; # Why not []?
61 0         0 for my $p (@{$plst}) {
  0         0  
62 0 0       0 if (ref($p) eq 'CODE') {
    0          
63 0         0 ($st, $str, $ms)=$p->($str);
64             } elsif (ref($p) eq 'HASH') {
65 0         0 my %hp=%{$p};
  0         0  
66 0         0 (my $k, my $pp) = each %hp;
67 0         0 ($st, $str, my $mms)=$pp->($str);
68 0         0 $ms = {$k => $mms};
69             } else { # assuming it's ARRAY
70 0         0 my $p2 = sequence($p);
71 0         0 ($st, $str, $ms)=$p2->($str)
72             }
73 0 0       0 if (!$st) {
74 0         0 return (0,$str,[]);
75             }
76 0         0 push @{$matches},$ms;# if scalar @{ $ms };
  0         0  
77             }
78 0 0       0 if (defined($proc)) {
79 0 0       0 if( ref($proc) eq 'CODE') {
80 0         0 return (1,$str,$proc->($matches));
81             } else {
82 0 0       0 say 'TROUBLE: <',show($plst),'><',show($proc),'>' if $V;
83 0         0 return (1,$str,$matches);
84             }
85             } else {
86 0         0 return (1,$str,$matches)
87             }
88 0         0 };
89 0         0 return $gen;
90             }
91              
92             sub foldl {
93 19     19 0 42 (my $f, my $acc, my $ls)=@_;
94 19         36 for my $elt (@{$ls}) {
  19         58  
95 51         159 $acc = $f->($acc,$elt);
96             }
97 19         120 return $acc;
98             }
99              
100             sub sequence {
101 11     11 0 28 (my $plst, my $proc)=@_;
102 19     19   6503 my $gen = sub { (my $str)=@_;
103 19 50       2021 say "* sequence( '$str' )" if $V;
104 51         154 my $f = sub { (my $acc, my $p) = @_;
105 51         87 (my $st1, my $str1, my $matches) = @{ $acc };
  51         134  
106 51         196 (my $st2, my $str2, my $ms) = apply($p,$str1);
107 51 100       208 if ($st2*$st1==0) {
108 8         57 return [0,$str1,[]];
109             } else {
110 43         80 return [1,$str2,[ @{$matches},$ms]];
  43         333  
111             }
112 19         154 };
113 19         46 (my $status, $str, my $matches) = @{ foldl($f, [1,$str,[]],$plst) };
  19         111  
114 19 100       103 if ($status == 0) {
    50          
115 4         42 return (0,$str,[]);
116             } elsif (defined($proc)) {
117 0 0       0 if( ref($proc) eq 'CODE') {
118 0         0 return (1,$str,$proc->($matches));
119             } else {
120 0 0       0 say 'TROUBLE: <',show($plst),'><',show($proc),'>' if $V;
121 0         0 return (1,$str,$matches);
122             }
123             } else {
124 15         147 return (1,$str,$matches)
125             }
126 11         85 };
127 11         53 return $gen;
128             }
129              
130             sub sequence_noproc {
131 0     0 0 0 (my $plst )=@_;
132 0     0   0 my $gen = sub { (my $str)=@_;
133 0 0       0 say "* sequence( '$str' )" if $V;
134 0         0 my $f = sub { (my $acc, my $p) = @_;
135 0         0 (my $st1, my $str1, my $matches) = @{ $acc };
  0         0  
136 0         0 (my $st2, my $str2, my $ms) = do {
137 0 0       0 if (ref($p) eq 'CODE') {
    0          
138 0         0 $p->($str1);
139             } elsif (ref($p) eq 'HASH') {
140 0         0 my %hp=%{$p};
  0         0  
141 0         0 (my $k, my $pp) = each %hp;
142 0         0 (my $st, my $str, my $mms)=$pp->($str1);
143 0         0 my $ms = {$k => $mms};
144 0         0 ($st, $str, $ms)
145             } else { # assuming it's ARRAY
146 0         0 my $p2 = sequence($p);
147 0         0 $p2->($str1)
148             }
149             };
150 0 0       0 if ($st2*$st1==0) {
151 0         0 return [0,$str1,[]];
152             } else {
153 0         0 return [1,$str2,[ @{$matches},$ms]];
  0         0  
154             }
155 0         0 };
156 0         0 (my $status, $str, my $matches) = @{ foldl($f, [1,$str,[]],$plst) };
  0         0  
157 0 0       0 if ($status == 0) {
158 0         0 return (0,$str,[]);
159             } else {
160 0         0 return (1,$str,$matches)
161             }
162 0         0 };
163 0         0 return $gen;
164             }
165             # In the best tradition, bind() and return()
166             sub bindP {
167 0     0 0 0 (my $p1,my $p2) =@_;
168 0     0   0 my $gen = sub {(my $str1) =@_;
169 0 0       0 say "* bindP( \'$str1\' )" if $V;
170 0         0 my $matches=undef;
171 0         0 (my $st1,my $str2,my $m1) = $p1->( $str1 );
172 0         0 push @{$matches},$m1;
  0         0  
173 0 0       0 if ($st1) {
174 0         0 say "bind: p1( $str1 ) matched,[$m1] try p2( $str2 )";
175 0         0 (my $st2,my $str3, my $m2) = $p2->( $str2 );
176 0 0       0 if(ref($m2) eq 'ARRAY' ){
    0          
177 0         0 $matches =[ @{$matches},@{$m2} ];
  0         0  
  0         0  
178             } elsif (defined $m2) {
179 0         0 push @{$matches},$m2;
  0         0  
180             }
181 0         0 return ($st2,$str3,$matches);
182             } else {
183 0         0 return (0,$str1,undef);
184             }
185 0         0 };
186 0         0 return $gen;
187             }
188              
189             # Only we can't call it 'return' so let's call it enter :-)
190             sub returnP {
191             sub {
192 0     0   0 (my $str) = @_;
193 0         0 return (0,$str,undef);
194             }
195 0     0 0 0 }
196              
197             # Choice: try every parser in the list until one succeeds or return fail. '<|>' in Parsec
198             # FIXME: Prototype does not guarantee that parens can be omitted. Should make it binary for that.
199             sub choice ($$;@) {
200 1     1 0 4 my @parsers=@_;
201 2     2   5 my $gen = sub { (my $str)= @_;
202 2 50       142 say "* choice('$str')" if $V;
203 2         9 for my $p (@parsers) {
204 3         6 my $status=0;
205 3         27 ($status, $str, my $matches) = apply($p,$str);
206 3 100       13 if ($status) {
207 2 50       140 say "choice: remainder => <$str>" if $V;
208 2 50       13 say "choice: matches => [".show($matches)."]" if $V;
209 2         382 return ($status, $str, $matches);
210             }
211             }
212 0         0 return (0, $str, []);
213 1         6 };
214 1         7 return $gen;
215             }
216             # Normally, when a parser parses a string, it removes the portion that matched. If you want to keep the string, wrap the parser in try()
217             sub try {
218 0     0 0 0 (my $p)=@_;
219             my $gen = sub {
220 0     0   0 (my $str)=@_;
221 0 0       0 say "* try( '$str' )" if $V;
222 0         0 (my $status, my $rest, my $matches)=$p->($str);
223 0 0       0 if ($status) {
224 0 0       0 say "try: remainder => <$rest>" if $V;
225 0 0       0 say "try: matches => [".show($matches)."]" if $V;
226 0         0 return (1, $rest, $matches);
227             } else {
228 0 0       0 say "try: match failed => <$str>" if $V;
229 0         0 return (0, $str, $matches);
230             }
231 0         0 };
232 0         0 return $gen;
233             }
234              
235             # maybe() is like try() but always succeeds
236             # it returns the matches and the consumed string or the orig string and no matches
237 3     3 0 4 sub maybe { (my $p)=@_;
238 9     9   24 my $gen = sub { (my $str)=@_;
239 9 50       820 say "* maybe('$str')" if $V;
240 9         39 (my $status, my $rest, my $matches)=apply($p,$str);
241 9 100       79 if ($status) {
242 6 50       35 say "maybe matches: [".show($matches)."]" if $V;
243 6         905 return (1, $rest, $matches);
244             } else {
245 3 50       217 say "maybe: no matches for <$str>" if $V;
246 3         23 return (1, $str, undef);
247             }
248 3         21 };
249 3         13 return $gen;
250             }
251              
252              
253 3     3 0 7 sub parens { (my $p)= @_;
254 9     9   22 my $gen = sub { (my $str)=@_;
255 9 50       791 say "* parens($str)" if $V;
256 9         22 my $matches=undef;
257 9         32 (my $status, my $str3, my $ch)=char('(')->($str);
258 9 100       100 if ($status==1) {
259 6         13 my $str4 = $str3; $str4=~s/\s*//;
  6         35  
260 6         25 (my $st,my $str4s,$matches)=$p->($str4);
261 6 50       396 say "parens: remainder => <$str4s>" if $V;
262 6 50       30 say "parens: matches => [".show($matches)."]" if $V;
263 6         775 $status*=$st;
264 6 50       20 if ($status==1) {
265 6         21 (my $st, my $str5, my $ch)=char(')')->($str4s);
266             # if ($st==1) {
267             # $str=~s/\s*//;
268             # }
269 6         49 $status*=$st;
270 6 50       20 if ($status==1) { # OK!
271 6         11 my $str6 = $str5; $str6=~s/^\s*//;
  6         33  
272 6 50       391 say "parens: remainder => <$str5>" if $V;
273 6 50       32 say "parens: matches => ".show($matches)."" if $V;
274 6         860 return (1,$str6, $matches);
275             } else { # parse failed on closing paren
276 0         0 return (0,$str5, $matches);
277             }
278             } else { # parse failed on $ref
279 0         0 return (0,$str4, $matches);
280             }
281             } else { # parse failed on opening paren
282 3         16 return (0,$str3,undef);
283             }
284 3         33 };
285 3         26 return $gen;
286             }
287              
288 29     29 0 1255 sub char { (my $ch)=@_;
289 29     29   102 my $gen = sub { (my $str)=@_;
290 29 100       1918 say "* char('$ch', '$str')" if $V;
291 29 100       140 if (substr($str,0,1) eq $ch) {
292 19 100       1560 say "char: matched \'$ch\' " if $V;
293 19 100       1585 say "char: remainder <".substr($str,1).">" if $V;
294 19         137 return (1,substr($str,1),$ch);
295             } else {
296 10         80 return (0,$str,undef);
297             }
298 29         204 };
299 29         145 return $gen;
300             }
301              
302 2     2 0 5 sub sepBy ($$) { (my $sep, my $p)=@_;
303 6     6   18 my $gen = sub { (my $str)=@_;
304 6         19 my $matches=[];
305 6 50       497 say "* sepBy('$sep', '$str')" if $V;
306 6         27 (my $status,my $str1,my $m)=$p->($str);
307 6 50       28 if ($status) {
308 6         13 push @{$matches},$m;
  6         103  
309 6 50       381 say "sepBy: remainder => <$str1>" if $V;
310 6         41 ($status,my $str2,$m)=char($sep)->($str1);
311 6         69 while( $status ) {
312 6         49 my $str2s=$str2;$str2s=~s/^\s*//;
  6         40  
313 6         22 (my $st,my $str3,$m)=$p->($str2s);
314 6         108 push @{$matches},$m;
  6         20  
315 6         23 ($status,$str2,$m)=char($sep)->($str3);
316             }
317 6 50       35 say "sepBy matches => [".show($matches)."]" if $V;
318 6         819 return (1, $str2, $matches);
319             } else { # first match failed.
320 0         0 return (0,$str1,undef);
321             }
322 2         15 };
323 2         9 return $gen;
324             }
325             # This is a lexeme parser, so it skips trailing whitespace
326             # Should be called "identifier" I think
327             sub word {
328 13     13   44 my $gen = sub { (my $str)=@_;
329 13 100       971 say "* word( '$str' )" if $V;
330 13 100       91 if(
331             $str=~/^(\w+)/
332             ) {
333 12         49 my $m=$1;
334 12         30 my $matches=$m;
335 12         302 $str=~s/^$m\s*//;
336 12 100       1064 say "word: remainder => <$str>" if $V;
337 12 100       1102 say "word: matches => [$matches]" if $V;
338 12         83 return (1,$str, $matches);
339             } else {
340 1 50       5 say "word: match failed => <$str>" if $V;
341 1         4 return (0,$str, undef); # assumes $status is 0|1, $str is string, $matches is [string]
342             }
343 6     6 0 1004 };
344 6         32 return $gen;
345             }
346             sub identifier {
347 0     0 0 0 word();
348             }
349              
350             # matches an unsigned integer
351             sub natural {
352 6     6   24 my $gen = sub { (my $str)=@_;
353 6 100       270 say "* natural( '$str' )" if $V;
354 6         13 my $status=0;
355 6 100       33 if(
356             $str=~/^(\d+)/
357             ) {
358 4         13 my $m=$1;
359 4         9 my $matches=$m;
360 4         9 $status=1;
361 4         43 $str=~s/^$m\s*//;
362 4 100       146 say "natural: remainder => <$str>" if $V;
363 4 100       140 say "natural: matches => [$matches]" if $V;
364 4         23 return ($status,$str, $matches);
365             } else {
366 2 100       125 say "natural: match failed => <$str>" if $V;
367 2         10 return ($status,$str, undef); # assumes $status is 0|1, $str is string, $matches is [string]
368             }
369 5     5 0 1132 };
370 5         31 return $gen;
371             }
372              
373             # As in Parsec, parses a literal and removes trailing whitespace
374 11     11 0 2039 sub symbol ($) { (my $lit_str) = @_;
375 11         49 $lit_str=~s/(\W)/\\$1/g;
376 17     17   57 my $gen = sub { (my $str)=@_;
377 17 100       1241 say "* symbol( '$lit_str', '$str' )" if $V;
378 17         46 my $status=0;
379 17 100       579 if(
380             $str=~/^\s*$lit_str\s*/
381             ) {
382 14         47 my $m=$1;
383 14         29 my $matches=$lit_str;
384 14         23 $status=1;
385 14         344 $str=~s/^\s*$lit_str\s*//;
386 14 100       1297 say "symbol: remainder => <$str>" if $V;
387 14 100       908 say "symbol: matches => [$matches]" if $V;
388 14         98 return ($status,$str, $matches);
389             } else {
390 3 100       26 say "symbol: match failed => <$str>" if $V;
391 3         18 return ($status,$str, undef);
392             }
393 11         53 };
394 11         49 return $gen;
395             }
396              
397             # This parser parses anything up to the last occurence ofa given literal and trailing whitespace
398             sub greedyUpto {
399 1     1 0 563 (my $lit_str) = @_;
400 1         10 $lit_str=~s/(\W)/\\$1/g;
401             my $gen = sub {
402 1     1   6 (my $str)=@_;
403 1 50       6 say "* greedyUpto( \'$lit_str\', \'$str\' )" if $V;
404 1 50       35 if(
405             $str=~/^(.*)\s*$lit_str\s*/
406             ) {
407 1         3 my $m=$1;
408 1         9 $m=~s/\s*$//;
409 1 50       6 my $matches= $m eq '' ? undef : $m;
410 1         17 $str=~s/^.*$lit_str\s*//;
411 1 50       14 say "greedyUpto: remainder => <$str>" if $V;
412 1 50       5 say "greedyUpto: matches => [$matches]" if $V;
413 1         6 return (1,$str, $matches);
414             } else {
415 0 0       0 say "greedyUpto: match failed => <$str>" if $V;
416 0         0 return (0,$str, undef);
417             }
418 1         7 };
419 1         4 return $gen;
420             }
421              
422             # This parser parses anything up to the last occurence of a given literal and trailing whitespace
423             sub upto {
424 1     1 0 635 (my $lit_str )= @_;
425 1         9 $lit_str=~s/(\W)/\\$1/g;
426             my $gen = sub {
427 1     1   6 (my $str)=@_;
428 1 50       4 say "upto1 \'$lit_str\': <$str>" if $V;
429 1 50       29 if(
430             $str=~/^(.*?)\s*$lit_str\s*/
431             ) {
432 1         3 my $m=$1;
433 1 50       5 my $matches= $m eq '' ? undef : $m;
434 1         15 $str=~s/^.*?$lit_str\s*//;
435 1 50       5 say "upto: remainder => <$str>" if $V;
436 1 50       6 say "upto: matches => [$matches]" if $V;
437 1         7 return (1,$str, $matches);
438             } else {
439 0 0       0 say "upto: match failed => <$str>" if $V;
440 0         0 return (0,$str, undef);
441             }
442 1         7 };
443 1         6 return $gen;
444             }
445              
446              
447             # `many`, as in Parsec, parses 0 or more the specified parsers
448             sub many {
449 0     0 0 0 (my $parser) = @_;
450             my $gen = sub {
451 0     0   0 (my $str)=@_;
452 0 0       0 print "* many( '$str' )\n" if $V;
453 0         0 (my $status,$str,my $m)=$parser->($str);
454 0 0       0 if ($status) {
455 0         0 my $matches = [$m];
456 0         0 while( $status==1 ) {
457 0         0 ($status,$str,$m)=$parser->($str);
458 0         0 push @{$matches},$m;
  0         0  
459             }
460 0 0       0 print "many: remainder => <$str>\n" if $V;
461 0 0       0 print "many: matches => [".show($matches)."]\n" if $V;
462 0         0 return (1, $str, $matches);
463             } else { # first match failed.
464 0 0       0 print "many: first match failed => <$str>\n" if $V;
465 0         0 return (1, $str,undef);
466             }
467 0         0 };
468 0         0 return $gen;
469             }
470              
471              
472             # `many1`, as in Parsec, parses 1 or more the specified parsers
473             sub many1 {
474 0     0 0 0 (my $parser) = @_;
475             my $gen = sub {
476 0     0   0 (my $str)=@_;
477 0         0 my $matches=[];
478 0 0       0 say "* many( '$str' )" if $V;
479 0         0 (my $status,$str,my $m)=$parser->($str);
480 0 0       0 if ($status) {
481 0         0 push @{$matches},$m;
  0         0  
482 0         0 while( $status==1 ) {
483 0         0 (my $status,$str,$m)=$parser->($str);
484 0         0 push @{$matches},$m;
  0         0  
485             }
486 0 0       0 say "many: remainder => <$str>" if $V;
487 0 0       0 say "many: matches => [".show($matches)."]" if $V;
488             } else { # first match failed.
489 0 0       0 say "many: first match failed => <$str>" if $V;
490 0         0 return (0, $str,undef);
491             }
492 0         0 return (1, $str, $matches);
493 0         0 };
494 0         0 return $gen;
495             }
496              
497             sub comma {
498 7     7   23 my $gen = sub { (my $str) = @_;
499 7 100       427 say "* comma( '$str' )" if $V;
500 7         62 my $st = ($str=~s/^\s*,\s*//);
501 7 100       30 if ($st) {
502 5 100       445 say "comma: match" if $V;
503             } else {
504 2 50       16 say "comma: match failed" if $V;
505             }
506 7         40 return ($st, $str, undef);
507 3     3 0 578 };
508 3         16 return $gen;
509             }
510              
511             sub semi {
512 0     0   0 my $gen = sub { (my $str) = @_;
513 0 0       0 say "* semi( '$str' )" if $V;
514 0         0 my $st = ($str=~s/^\s*;\s*//);
515 0         0 return ($st, $str, undef);
516 0     0 0 0 };
517 0         0 return $gen;
518             }
519              
520              
521              
522             # strip leading whitespace, always success
523             sub whiteSpace {
524 6     6   25 my $gen = sub { (my $str)=@_;
525 6 100       386 say "* whiteSpace( \'$str\' )" if $V;
526 6         40 $str=~s/^(\s*)//;
527 6         24 my $m=$1;
528 6         32 return (1,$str,$m)
529 4     4 0 2148 };
530 4         21 return $gen;
531             }
532              
533 0     0 0 0 sub oneOf { (my $patt_lst) = @_;
534 0     0   0 my $gen = sub { (my $str)= @_;
535 0 0       0 say "* oneOf([".join('|',@{$patt_lst})."],'$str')" if $V;
  0         0  
536 0         0 for my $p (@{$patt_lst}) {
  0         0  
537 0         0 (my $status, $str, my $matches)= symbol($p)->($str);
538 0 0       0 if ($status) {
539 0 0       0 say "choice: remainder => <$str>" if $V;
540 0 0       0 say "choice: matches => [".show($matches)."]" if $V;
541 0         0 return (1, $str, $matches);
542             }
543             }
544 0         0 return (0, $str, undef);
545 0         0 };
546 0         0 return $gen;
547             }
548              
549             # Enough rope: this parser will parse whatever the regex is, stripping trailing whitespace
550 1     1 0 3 sub regex { (my $regex_str) = @_;
551 6     6   15 my $gen = sub { (my $str)=@_;
552 6 50       585 say "* regex( '/$regex_str/', '$str' )" if $V;
553 6         16 my $matches=undef;
554 6 50       80 if(
555             $str=~s/($regex_str)\s*//
556             ) {
557 6         22 my $m=$1;
558 6         12 $matches=$m;
559 6 50       426 say "regex: remainder => <$str>" if $V;
560 6 50       383 say "regex: matches => [$matches]" if $V;
561 6         38 return (1,$str, $matches);
562             } else {
563 0 0       0 say "regex: match failed => <$str>" if $V;
564             }
565 0         0 return (0,$str, $matches); # assumes $status is 0|1, $str is string, $matches is [string]
566 1         7 };
567 1         5 return $gen;
568             }
569              
570 63     63 0 142 sub apply { (my $p, my $str) = @_;
571              
572 63 100       283 if (ref($p) eq 'CODE') {
    100          
    50          
573 39         160 return $p->($str);
574             } elsif (ref($p) eq 'ARRAY') {
575 6         23 return sequence($p)->($str);
576             } elsif (ref($p) eq 'HASH') {
577 18         34 my %hp = %{$p};
  18         99  
578 18         61 (my $k, my $pp) = each %hp;
579 18         72 (my $status, my $str2, my $mms)=$pp->($str);
580 18         76 my $matches = {$k => $mms};
581 18         103 return ($status, $str2, $matches);
582             } else {
583 0         0 die show($p);
584             }
585             }
586              
587              
588             sub matches {
589 0     0 0 0 return @{$_[0]};
  0         0  
590             }
591              
592             sub unwrap {
593 0     0 0 0 (my $elt_in_array)=@_;
594 0         0 my $elt = shift @{$elt_in_array};
  0         0  
595 0         0 return $elt;
596             }
597              
598             sub empty {
599 0     0 0 0 (my $elt_in_array)=@_;
600 0 0       0 return (@{$elt_in_array} ) ? 0 : 1;
  0         0  
601             }
602              
603             # This function returns labeled items in the parse tree.
604             # It is rather aggressive in removing unlabeled items
605 16     16 0 45 sub get_tree_as_lists { (my $list) = @_;
606 16         42 my $hlist=[];
607 16         41 for my $elt (@{$list}) {
  16         57  
608 41 100 66     259 if (ref($elt) eq 'ARRAY' and scalar @{$elt}>0) { # non-empty list
  8 100       49  
609 8         20 push @{ $hlist }, get_tree_as_lists($elt);
  8         63  
610             } elsif (ref($elt) eq 'HASH') { # hash: need to process the rhs of the pair
611 15         33 (my $k, my $v) = each %{$elt};
  15         78  
612 15 100       69 if (ref($v) ne 'ARRAY') { # not an array => wrap in array and redo
    100          
613 6         15 push @{$hlist}, {$k => $v};
  6         49  
614 9         38 } elsif (@{$v}==1) { # a single-elt array
615 2         5 push @{$hlist}, {$k => $v->[0]};
  2         14  
616             } else {
617             my $pv =[
618             map {
619 16 100       165 if (ref($_) eq 'ARRAY') {
    100          
    100          
620 1         5 get_tree_as_lists($_)
621             } elsif ( ref($_) eq 'HASH') {
622 4         27 get_tree_as_lists([$_])
623 10         69 } elsif (defined $_) { $_ }
624 7         17 } @{$v}
  7         25  
625             ];
626 7         24 push @{$hlist}, {$k => $pv};
  7         46  
627             }
628             }
629             }
630 16 100       40 return scalar @{$hlist}==1 ? $hlist->[0] : $hlist;
  16         119  
631             }
632              
633 14     14 0 41 sub is_list_of_objects { (my $mlo) =$_;
634 14 100       53 if (ref($mlo) eq 'ARRAY') {
635 7         14 my @tmlo=@{$mlo};
  7         32  
636 7         22 my @l=grep { ref($_) ne 'HASH' } @tmlo;
  16         91  
637 7 100       79 return scalar(@l)?0:1;
638             } else {
639 7         40 return 0;
640             }
641             }
642              
643 5     5 0 14 sub l2m { (my $hlist) =@_;
644 5         16 my $hmap = {};
645 5         14 my @hmap_vals = map { (my $k, my $v)=%{$_}; $v } @{$hlist};
  14         29  
  14         67  
  14         63  
  5         16  
646 5         14 my @hmap_keys = map { (my $k, my $v)=%{$_}; $k } @{$hlist};
  14         24  
  14         53  
  14         56  
  5         15  
647 5 100       17 my @hmap_rvals = map { is_list_of_objects($_) ? l2m($_) : $_ } @hmap_vals;
  14         47  
648             # my @hmap_keys_vals = map { each %{$_} } @{$hlist}
649 5         21 for my $k ( @hmap_keys ) {
650 14         90 my $rv = shift @hmap_rvals;
651 14         52 $hmap->{$k}=$rv;
652             }
653             # my %{$hmap} = @hmap_keys_vals;
654 5         33 return $hmap;
655             }
656              
657            
658            
659 3     3 0 25 sub getParseTree { (my $m) =@_;
660 3         16 return l2m(get_tree_as_lists($m));
661             }
662              
663             sub run {
664 0     0 0 0 (my $p, my $str) = @_;
665 0         0 (my $st, my $rest, my $m) = apply($p,$str);
666 0         0 getParseTree($m);
667             }
668              
669 26     26 0 71 sub show { (my $data)=@_;
670 26         54 $Data::Dumper::Indent=0;
671 26         48 $Data::Dumper::Terse =1;
672 26         105 return Dumper($data);
673             }
674              
675             1;
676              
677             __END__