File Coverage

blib/lib/MarpaX/Languages/Lua/Parser.pm
Criterion Covered Total %
statement 131 135 97.0
branch 25 36 69.4
condition 4 8 50.0
subroutine 17 17 100.0
pod 2 5 40.0
total 179 201 89.0


line stmt bran cond sub pod time code
1             package MarpaX::Languages::Lua::Parser;
2              
3 3     3   213127 use strict;
  3         29  
  3         94  
4 3     3   17 use warnings;
  3         6  
  3         90  
5 3     3   15 use warnings qw(FATAL utf8); # Fatalize encoding glitches.
  3         6  
  3         142  
6              
7 3     3   1876 use Data::RenderAsTree;
  3         514931  
  3         141  
8              
9 3     3   1935 use Data::Section::Simple 'get_data_section';
  3         2145  
  3         232  
10              
11 3     3   2520 use Log::Handler;
  3         122914  
  3         26  
12              
13 3     3   1926 use Marpa::R2;
  3         435548  
  3         55  
14              
15 3     3   161 use Moo;
  3         8  
  3         30  
16              
17 3     3   2953 use Path::Tiny; # For path().
  3         23158  
  3         237  
18              
19 3     3   34 use Types::Standard qw/Any ArrayRef HashRef Bool Str/;
  3         7  
  3         76  
20              
21             has attributes =>
22             (
23             default => sub{return 0},
24             is => 'rw',
25             isa => Bool,
26             required => 0,
27             );
28              
29             has grammar =>
30             (
31             default => sub{return ''},
32             is => 'rw',
33             isa => Any,
34             required => 0,
35             );
36              
37             has input_file_name =>
38             (
39             default => sub{return ''},
40             is => 'rw',
41             isa => Str,
42             required => 1,
43             );
44              
45             has input_text =>
46             (
47             default => sub{return []},
48             is => 'rw',
49             isa => ArrayRef,
50             required => 1,
51             );
52              
53             has keywords =>
54             (
55             default => sub
56             {
57             return
58             {
59             map { $_ => 1 }
60             qw
61             {
62             and break do else elseif
63             end false for function if
64             in local nil not or
65             repeat return then true until while
66             }
67             }
68             },
69             is => 'ro',
70             isa => HashRef,
71             required => 0,
72             );
73              
74             has logger =>
75             (
76             default => sub{return undef},
77             is => 'rw',
78             isa => Any,
79             required => 0,
80             );
81              
82             has maxlevel =>
83             (
84             default => sub{return 'notice'},
85             is => 'rw',
86             isa => Str,
87             required => 0,
88             );
89              
90             has minlevel =>
91             (
92             default => sub{return 'error'},
93             is => 'rw',
94             isa => Str,
95             required => 0,
96             );
97              
98             has output_file_name =>
99             (
100             default => sub{return ''},
101             is => 'rw',
102             isa => Str,
103             required => 1,
104             );
105              
106             has output_tokens =>
107             (
108             default => sub{return []},
109             is => 'rw',
110             isa => ArrayRef,
111             required => 1,
112             );
113              
114             has recce =>
115             (
116             default => sub{return ''},
117             is => 'rw',
118             isa => Any,
119             required => 0,
120             );
121              
122             has renderer =>
123             (
124             default => sub{return ''},
125             is => 'rw',
126             isa => Any,
127             required => 0,
128             );
129              
130             has value =>
131             (
132             default => sub{return ''},
133             is => 'rw',
134             isa => Any,
135             required => 0,
136             );
137              
138             # This flag is used to stop Perl issuing deep recursion warnings,
139             # when Tree::DAG_Node's walk_down() traverses the parse tree.
140              
141             my($sig_warn_flag) = 1;
142              
143             our $VERSION = '1.05';
144              
145             # ------------------------------------------------
146              
147             sub BUILD
148             {
149 23     23 0 706 my($self) = @_;
150 23 50   264   220 $SIG{'__WARN__'} = sub { warn $_[0] if $sig_warn_flag};
  264         8196875  
151              
152 23 50       568 if (! defined $self -> logger)
153             {
154 0         0 $self -> logger(Log::Handler -> new);
155 0         0 $self -> logger -> add
156             (
157             screen =>
158             {
159             maxlevel => $self -> maxlevel,
160             message_layout => '%m',
161             minlevel => $self -> minlevel,
162             utf8 => 1,
163             }
164             );
165             }
166              
167 23         649 $self -> input_text([path($self -> input_file_name) -> lines_utf8]);
168 23         38689 $self -> grammar
169             (
170             Marpa::R2::Scanless::G -> new({source => \get_data_section('Lua.bnf')})
171             );
172 23         7017601 $self -> recce
173             (
174             Marpa::R2::Scanless::R -> new
175             ({
176             grammar => $self -> grammar,
177             ranking_method => 'high_rule_only',
178             })
179             );
180 23         20908 $self -> renderer
181             (
182             Data::RenderAsTree -> new(clean_nodes => 1)
183             );
184              
185             } # End of BUILD.
186              
187             # --------------------------------------------------
188              
189             sub log
190             {
191 40018     40018 1 4667299 my($self, $level, $s) = @_;
192 40018 50       78447 $level = 'notice' if (! defined $level);
193 40018 50       70915 $s = '' if (! defined $s);
194              
195 40018 50       657159 $self -> logger -> $level($s) if ($self -> logger);
196              
197             } # End of log.
198              
199             # ------------------------------------------------
200              
201             sub process
202             {
203 23     23 0 80 my($self) = @_;
204 23         81 my($input) = join('', @{$self -> input_text});
  23         513  
205 23         535 my($input_ref) = \$input;
206 23         214 my($input_length) = length $input;
207 23         431 my($pos) = $self -> recce -> read($input_ref);
208              
209 23         28095 READ: while (1)
210             {
211             EVENT:
212 2010         644008 for my $event (@{$self -> recce -> events})
  2010         46921  
213             {
214 1989         22666 my($name) = @{$event};
  1989         4985  
215              
216 1989 100       6005 if ($name eq 'multiline string' )
217             {
218 2         40 my($start, $length) = $self -> recce -> pause_span;
219 2         60 my($string_terminator) = $self -> recce -> literal($start, $length);
220 2         36 $string_terminator =~ tr/\[/\]/;
221 2         37 my($terminator_pos) = index($$input_ref, $string_terminator, $start);
222              
223 2 50       13 die "Died looking for $string_terminator. \n" if ($terminator_pos < 0);
224              
225             # The string terminator has the same length as the start of string marker.
226              
227 2         9 my($string_length) = $terminator_pos + $length - $start;
228              
229 2         37 $self -> recce -> lexeme_read('multiline string', $start, $string_length);
230              
231 2         158 $pos = $terminator_pos + $length;
232              
233 2         8 next EVENT;
234             }
235              
236 1987 100       4752 if ($name eq 'multiline comment')
237             {
238             # This is a discard event.
239              
240 4         7 my(undef, $start, $end) = @{$event};
  4         12  
241 4         8 my($length) = $end - $start;
242 4         69 my($comment_terminator) = $self -> recce -> literal($start, $length);
243 4         65 $comment_terminator = ']' . ('=' x ($length - 4)) . ']';
244 4         23 my($terminator_pos) = index( $$input_ref, $comment_terminator, $start);
245              
246 4 50       11 die "Died looking for $comment_terminator. \n" if ($terminator_pos < 0);
247              
248             # Don't read anything into G1 -- just throw the comment away.
249              
250 4         8 $pos = $terminator_pos + length $comment_terminator;
251              
252 4         12 next EVENT;
253             }
254              
255 1983 100       4513 if ($name eq 'singleline comment')
256             {
257             # This is a discard event.
258              
259 94         196 my(undef, $start, $end) = @{$event};
  94         256  
260 94         232 my($length) = $end-$start;
261 94         399 pos($$input_ref) = $end - 1;
262 94         631 $$input_ref =~ /[\r\n]/gxms;
263 94         334 my($new_pos) = pos($$input_ref);
264              
265 94 50       319 die "Died looking for singleline comment terminator. \n" if (! defined $new_pos);
266              
267 94         210 $pos = $new_pos;
268              
269 94         277 next EVENT;
270             }
271              
272 1889 50       4744 if ($name eq 'Name')
273             {
274             # This is an event to check if a keyword is used as an identifier
275             # and die if it is.
276              
277 1889         31869 my($start, $length) = $self -> recce -> pause_span;
278 1889         52062 my($line, $column) = $self -> recce -> line_column($start);
279 1889         52473 my($literal) = $self -> recce -> literal($start, $length);
280              
281 1889 100       29476 if ( exists $self -> keywords -> { $literal } )
282             {
283 484   100     8056 $self -> recce -> lexeme_read(qq{keyword $literal}, $start, $length)
284             // die $self->input_file_name . qq{ (line, column) = ($line, $column): keyword '$literal' used as \n};
285             }
286             else
287             {
288 1405         23275 $self -> recce -> lexeme_read('Name', $start, $length);
289             }
290              
291 1888         182391 $pos = $self -> recce -> pos();
292              
293 1888         25801 next EVENT;
294             }
295              
296 0         0 die "Unexpected event '$name'\n";
297              
298             }
299              
300 2009 100       7891 last READ if ($pos >= $input_length);
301              
302 1987         34049 $pos = $self -> recce -> resume($pos);
303             }
304              
305             # Warning: Don't use if (my($ambiguous_status) = $self -> recce -> ambiguous),
306             # since then the 'if' always returns true.
307              
308 22 50       791 if (my $ambiguous_status = $self -> recce -> ambiguous)
309             {
310 0         0 die "The Lua source is ambiguous: $ambiguous_status. \n";
311             }
312              
313 22         18065 return $self -> recce -> value;
314              
315             } # End of process.
316              
317             # --------------------------------------------------
318              
319             sub render
320             {
321 22     22 0 82 my($self) = @_;
322 22         159 my($slim_list) = [];
323 22         100 $sig_warn_flag = 0;
324              
325 22         124 my($attributes);
326             my($name);
327 22         0 my($s);
328 22         0 my($type);
329              
330             $self -> renderer -> root -> walk_down
331             ({
332             callback => sub
333             {
334 25136     25136   746020 my($node, $opt) = @_;
335              
336             # Ignore the root, and keep walking.
337              
338 25136 100       53169 return 1 if ($node -> is_root);
339              
340 25114         257625 $name = $node -> name;
341 25114         237760 $name =~ s/^\s*\d+\s=\s(.+)/$1/;
342 25114         98404 $name =~ s/\s\[[A-Z]+\s\d+\]//;
343 25114         63433 $attributes = $node -> attributes;
344 25114         166004 $type = $$attributes{type};
345              
346 25114 100       56637 if ($type eq 'SCALAR')
347             {
348 14882         33521 push @$slim_list, $name;
349              
350 14882         47759 $self -> log(info => ' ' x $$opt{_depth} . $name);
351             }
352              
353 25114         130142 return 1; # Keep walking.
354             },
355 22         660 _depth => 0,
356             });
357              
358 22         1722 $self -> output_tokens($slim_list);
359              
360             } # End of render.
361              
362             # ------------------------------------------------
363              
364             sub run
365             {
366 23     23 1 27141 my($self, %args) = @_;
367 23   33     604 my($file_name) = $args{input_file_name} || $self -> input_file_name;
368 23         311 $sig_warn_flag = 0; # Turn off Perl's warnings for the duration of tree processing.
369              
370 23         156 $self -> value($self -> process($file_name) );
371 22         1051393 $self -> renderer -> run(${$self -> value});
  22         559  
372 22         11916938 $self -> log(debug => $_) for @{$self -> renderer -> root -> tree2string({no_attributes => 1 - $self -> attributes})};
  22         599  
373 22         3593 $self -> render;
374              
375 22         1040 $sig_warn_flag = 1; # Turn Perl's warnings back on.
376              
377 22   33     566 my($output_file_name) = $args{output_file_name} || $self -> output_file_name;
378              
379 22 50       309 if ($output_file_name)
380             {
381 22         204 path($output_file_name) -> spew_utf8(map{"$_\n"} @{$self -> output_tokens});
  14882         32674  
  22         1673  
382             }
383              
384             # Return 0 for success and 1 for failure.
385              
386 22         196744 return 0;
387              
388             } # End of run.
389              
390             #-------------------------------------------------
391              
392             1;
393              
394             =pod
395              
396             =head1 NAME
397              
398             C - A Lua source code parser
399              
400             =head1 Synopsis
401              
402             #!/usr/bin/env perl
403              
404             use strict;
405             use warnings;
406              
407             use MarpaX::Languages::Lua::Parser;
408              
409             # ---------------------------------
410              
411             my($input_file_name) = shift || die "Usage: $0 a_lua_source_file_name\n";
412             my($parser) = MarpaX::Languages::Lua::Parser -> new(input_file_name => $input_file_name);
413              
414             $parser -> run;
415              
416             print map{"$_\n"} @{$parser -> output_tokens};
417              
418             This script ships as scripts/synopsis.pl. Run it as:
419              
420             shell> perl -Ilib scripts/synopsis.pl lua.sources/echo.lua
421              
422             See also scripts/parse.file.pl for code which takes command line parameters. For help, run:
423              
424             shell> perl -Ilib scripts/parse.file.pl -h
425              
426             =head1 Description
427              
428             C parses Lua source code files.
429              
430             The result is stored in a tree managed by L.
431              
432             A list of scalar tokens from this tree is stored in an arrayref.
433              
434             See the FAQ question L for details.
435              
436             =head1 Installation
437              
438             Install C as you would for any C module:
439              
440             Run:
441              
442             cpanm MarpaX::Languages::Lua::Parser
443              
444             or run:
445              
446             sudo cpan MarpaX::Languages::Lua::Parser
447              
448             or unpack the distro, and then:
449              
450             perl Makefile.PL
451             make (or dmake or nmake)
452             make test
453             make install
454              
455             =head1 Constructor and Initialization
456              
457             C is called as C<< my($parser) = MarpaX::Languages::Lua::Parser -> new(k1 => v1, k2 => v2, ...) >>.
458              
459             It returns a new object of type C.
460              
461             Key-value pairs accepted in the parameter list (see also the corresponding methods
462             [e.g. L]):
463              
464             =over 4
465              
466             =item o attributes => $Boolean
467              
468             When set to 1, metadata attached to each tree node is included in the output.
469              
470             If you set the L to 'debug', this tree is printed to the log.
471              
472             Default: 0.
473              
474             =item o input_file_name => $string
475              
476             The name the input file to be parsed.
477              
478             This option is mandatory.
479              
480             Default: ''.
481              
482             =item o logger => aLog::HandlerObject
483              
484             By default, an object of type L is created which prints to STDOUT,
485             but given the default setting (maxlevel => 'notice'), nothing is actually printed.
486              
487             See C and C below.
488              
489             Set C to '' (the empty string) to stop a logger being created.
490              
491             Default: undef.
492              
493             =item o maxlevel => logOption1
494              
495             This option affects L objects.
496              
497             See the L docs.
498              
499             Typical values: 'info', 'debug'.
500              
501             See the FAQ question L for details.
502              
503             See also the help output by scripts/parse.file.pl -h.
504              
505             Default: 'notice'.
506              
507             =item o minlevel => logOption2
508              
509             This option affects L object.
510              
511             See the L docs.
512              
513             Default: 'error'.
514              
515             No lower levels are used.
516              
517             =item o output_file_name => $string
518              
519             The name of the text file to be written.
520              
521             If not set, nothing is written.
522              
523             The items written, one per line, are as returned by L.
524              
525             Default: ''.
526              
527             =back
528              
529             =head1 Methods
530              
531             =head2 attributes([$Boolean])
532              
533             Here, the [] indicate an optional parameter.
534              
535             Gets or sets the attributes option.
536              
537             Note: The value passed to L's C method is (1 - $Boolean).
538              
539             See the FAQ question L for details.
540              
541             C is a parameter to L.
542              
543             =head2 input_file_name([$string])
544              
545             Here, the [] indicate an optional parameter.
546              
547             Get or set the name of the file to parse.
548              
549             See lua.sources/*.lua for sample input.
550              
551             Note: C is a parameter to new().
552              
553             =head2 log($level, $s)
554              
555             Calls $self -> logger -> log($level => $s) if ($self -> logger).
556              
557             =head2 logger([$log_object])
558              
559             Here, the [] indicate an optional parameter.
560              
561             Get or set the log object.
562              
563             C<$log_object> must be a L-compatible object.
564              
565             To disable logging, just set logger to the empty string.
566              
567             Note: C is a parameter to new().
568              
569             =head2 maxlevel([$string])
570              
571             Here, the [] indicate an optional parameter.
572              
573             Get or set the value used by the logger object.
574              
575             This option is only used if an object of type L is created. See L.
576              
577             Typical values: 'info', 'debug'.
578              
579             See the FAQ question L for details.
580              
581             Note: C is a parameter to new().
582              
583             =head2 minlevel([$string])
584              
585             Here, the [] indicate an optional parameter.
586              
587             Get or set the value used by the logger object.
588              
589             This option is only used if an object of type L is created. See L.
590              
591             Note: C is a parameter to new().
592              
593             =head2 new()
594              
595             This method is auto-generated by L.
596              
597             =head2 output_file_name([$string])
598              
599             Here, the [] indicate an optional parameter.
600              
601             Get or set the name of the file to write.
602              
603             The tokens written are as returned from L.
604              
605             Note: C is a parameter to new().
606              
607             =head2 output_tokens()
608              
609             Returns an arrayref of tokens output by the parse, one per line. These tokens are pushed onto the
610             stack by walking the tree returned by the renderer, which is an object of type
611             L. The renderer is run by passing it the output from the call to Marpa's
612             C method. See L.
613              
614             If you set the L to 'info', these tokens are printed to the log.
615              
616             See scripts/synopsis.pl for accessing this arrayref.
617              
618             See lua.output/*.txt for sample output.
619              
620             =head2 renderer()
621              
622             Returns the object of type L, which takes the output from the call to Marpa's
623             C method and converts it into an object of type L.
624              
625             If you set the L to 'debug', this tree is printed to the log.
626              
627             =head2 run([%args])
628              
629             The method which does all the work.
630              
631             C<%args> is a hash with this optional (key => value) pair:
632              
633             =over 4
634              
635             =item o input_file_name => $in_file_name
636              
637             =item o output_file_name => $out_file_name
638              
639             =back
640              
641             File names specified in the call to C take precedence over file names specified to L.
642              
643             Returns 0 for a successful parse and 1 for failure.
644              
645             The code dies if L itself can't parse the given input file.
646              
647             Note: C and C are parameters to L.
648              
649             =head1 FAQ
650              
651             =head2 Why did you store Lua's BNF in a __DATA__ section?
652              
653             This avoids problems with single- and double-quotes in the BNF, and the allegedly unknown escape
654             sequences \v etc too.
655              
656             =head2 How do I get output from this module?
657              
658             In various ways:
659              
660             =over 4
661              
662             =item o Call the L method
663              
664             Then, process the arrayref returned.
665              
666             =item o Call the L method
667              
668             This will return an object of type L, and from there you can call that object's
669             C method, to get access to the tree itself. See this module's C method for sample
670             code.
671              
672             =item o Set maxlevel to 'info'.
673              
674             This writes the output tokens to the log, one per line.
675              
676             See the C method for sample code.
677              
678             =item o Set maxlevel to 'debug'.
679              
680             This writes the output tokens to the log, one per line, and also writes to the log the tree
681             returned by passing the return value of Marpa's C method to the renderer. The renderer
682             is an object of type L, and outputs a tree managed by L.
683              
684             See the L method for sample code.
685              
686             =item o Set the output_file_name to a non-empty string
687              
688             In this case the code will walk the tree just mentioned, and output the scalar items, one per line,
689             to this file.
690              
691             =item o All of the above
692              
693             =back
694              
695             =head2 How do I interpret the output?
696              
697             For help with this, try the IRC channel irc.freenode.net#marpa.
698              
699             What that really means is that neither Jeffrey no anyone else imposes any kind of restriction on
700             what you may do with the output, or with how you may interpret it.
701              
702             =head2 Where is Marpa's home page?
703              
704             L.
705              
706             =head1 Machine-Readable Change Log
707              
708             The file Changes was converted into Changelog.ini by L.
709              
710             =head1 Version Numbers
711              
712             Version numbers < 1.00 represent development versions. From 1.00 up, they are production versions.
713              
714             =head1 Repository
715              
716             L
717              
718             =head1 Support
719              
720             Email the author, or log a bug on RT:
721              
722             L.
723              
724             =head1 Credits
725              
726             Jeffrey Kegler wrote the code, and posted a link on the IRC chat channel mentioned above.
727              
728             See L.
729              
730             =head1 Author
731              
732             L was packaged by Ron Savage Iron@savage.net.auE> in 2015.
733              
734             Homepage: L.
735              
736             =head1 Copyright
737              
738             Australian copyright (c) 2015, Ron Savage.
739              
740             All Programs of mine are 'OSI Certified Open Source Software';
741             you can redistribute them and/or modify them under the terms of
742             The Artistic License 2.0, a copy of which is available at:
743             http://www.opensource.org/licenses/index.html
744              
745             =cut
746              
747             __DATA__