File Coverage

blib/lib/MarpaX/Languages/Lua/Parser.pm
Criterion Covered Total %
statement 120 136 88.2
branch 23 36 63.8
condition 4 8 50.0
subroutine 17 17 100.0
pod 2 5 40.0
total 166 202 82.1


line stmt bran cond sub pod time code
1             package MarpaX::Languages::Lua::Parser;
2              
3 2     2   99844 use strict;
  2         4  
  2         47  
4 2     2   10 use warnings;
  2         4  
  2         58  
5 2     2   9 use warnings qw(FATAL utf8); # Fatalize encoding glitches.
  2         7  
  2         81  
6              
7 2     2   1556 use Data::RenderAsTree;
  2         292560  
  2         86  
8              
9 2     2   1559 use Data::Section::Simple 'get_data_section';
  2         1050  
  2         123  
10              
11 2     2   2196 use Log::Handler;
  2         97317  
  2         16  
12              
13 2     2   1661 use Marpa::R2;
  2         296940  
  2         45  
14              
15 2     2   94 use Moo;
  2         4  
  2         21  
16              
17 2     2   1782 use Path::Tiny; # For path().
  2         9364  
  2         141  
18              
19 2     2   24 use Types::Standard qw/Any ArrayRef HashRef Bool Str/;
  2         4  
  2         42  
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.04';
144              
145             # ------------------------------------------------
146              
147             sub BUILD
148             {
149 22     22 0 579 my($self) = @_;
150 22 50   264   231 $SIG{'__WARN__'} = sub { warn $_[0] if $sig_warn_flag};
  264         7265929  
151              
152 22 50       540 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 22         1688 $self -> input_text([path($self -> input_file_name) -> lines_utf8]);
168 22         39715 $self -> grammar
169             (
170             Marpa::R2::Scanless::G -> new({source => \get_data_section('Lua.bnf')})
171             );
172 22         5999303 $self -> recce
173             (
174             Marpa::R2::Scanless::R -> new
175             ({
176             grammar => $self -> grammar,
177             ranking_method => 'high_rule_only',
178             })
179             );
180 22         13746 $self -> renderer
181             (
182             Data::RenderAsTree -> new(clean_nodes => 1)
183             );
184              
185             } # End of BUILD.
186              
187             # --------------------------------------------------
188              
189             sub log
190             {
191 39831     39831 1 4176398 my($self, $level, $s) = @_;
192 39831 50       84869 $level = 'notice' if (! defined $level);
193 39831 50       75753 $s = '' if (! defined $s);
194              
195 39831 50       831241 $self -> logger -> $level($s) if ($self -> logger);
196              
197             } # End of log.
198              
199             # ------------------------------------------------
200              
201             sub process
202             {
203 22     22 0 58 my($self) = @_;
204 22         74 my($input) = join('', @{$self -> input_text});
  22         509  
205 22         526 my($input_ref) = \$input;
206 22         171 my($input_length) = length $input;
207 22         487 my($pos) = $self -> recce -> read($input_ref);
208              
209 22         18957 READ: while (1)
210             {
211             EVENT:
212 2000         568770 for my $event (@{$self -> recce -> events})
  2000         44381  
213             {
214 1980         16687 my($name) = @{$event};
  1980         4216  
215              
216 1980 100       4898 if ($name eq 'multiline string' )
217             {
218 2         45 my($start, $length) = $self -> recce -> pause_span;
219 2         61 my($string_terminator) = $self -> recce -> literal($start, $length);
220 2         29 $string_terminator =~ tr/\[/\]/;
221 2         32 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         8 my($string_length) = $terminator_pos + $length - $start;
228              
229 2         57 $self -> recce -> lexeme_read('multiline string', $start, $string_length);
230              
231 2         254 $pos = $terminator_pos + $length;
232              
233 2         10 next EVENT;
234             }
235              
236 1978 50       4235 if ($name eq 'multiline comment')
237             {
238             # This is a discard event.
239              
240 0         0 my(undef, $start, $end) = @{$event};
  0         0  
241 0         0 my($length) = $end - $start;
242 0         0 my($comment_terminator) = $self -> recce -> literal($start, $length);
243 0         0 $comment_terminator =~ tr/-//;
244 0         0 $comment_terminator =~ tr/\[/\]/;
245 0         0 my($terminator_pos) = index( $$input_ref, $comment_terminator, $start);
246              
247 0 0       0 die "Died looking for $comment_terminator. \n" if ($terminator_pos < 0);
248              
249             # Don't read anything into G1 -- just throw the comment away.
250              
251 0         0 $pos = $terminator_pos + $length;
252              
253 0         0 next EVENT;
254             }
255              
256 1978 100       4049 if ($name eq 'singleline comment')
257             {
258             # This is a discard event.
259              
260 94         117 my(undef, $start, $end) = @{$event};
  94         219  
261 94         244 my($length) = $end-$start;
262 94         302 pos($$input_ref) = $end - 1;
263 94         534 $$input_ref =~ /[\r\n]/gxms;
264 94         199 my($new_pos) = pos($$input_ref);
265              
266 94 50       213 die "Died looking for singleline comment terminator. \n" if (! defined $new_pos);
267              
268 94         118 $pos = $new_pos;
269              
270 94         221 next EVENT;
271             }
272              
273 1884 50       4072 if ($name eq 'Name')
274             {
275             # This is an event to check if a keyword is used as an identifier
276             # and die if it is.
277              
278 1884         35620 my($start, $length) = $self -> recce -> pause_span;
279 1884         49192 my($line, $column) = $self -> recce -> line_column($start);
280 1884         65128 my($literal) = $self -> recce -> literal($start, $length);
281              
282 1884 100       22375 if ( exists $self -> keywords -> { $literal } )
283             {
284 484   100     8829 $self -> recce -> lexeme_read(qq{keyword $literal}, $start, $length)
285             // die $self->input_file_name . qq{ (line, column) = ($line, $column): keyword '$literal' used as \n};
286             }
287             else
288             {
289 1400         26558 $self -> recce -> lexeme_read('Name', $start, $length);
290             }
291              
292 1883         127480 $pos = $self -> recce -> pos();
293              
294 1883         18665 next EVENT;
295             }
296              
297 0         0 die "Unexpected event '$name'\n";
298              
299             }
300              
301 1999 100       6470 last READ if ($pos >= $input_length);
302              
303 1978         36026 $pos = $self -> recce -> resume($pos);
304             }
305              
306             # Warning: Don't use if (my($ambiguous_status) = $self -> recce -> ambiguous),
307             # since then the 'if' always returns true.
308              
309 21 50       416 if (my $ambiguous_status = $self -> recce -> ambiguous)
310             {
311 0         0 die "The Lua source is ambiguous: $ambiguous_status. \n";
312             }
313              
314 21         19324 return $self -> recce -> value;
315              
316             } # End of process.
317              
318             # --------------------------------------------------
319              
320             sub render
321             {
322 21     21 0 62 my($self) = @_;
323 21         62 my($slim_list) = [];
324 21         67 $sig_warn_flag = 0;
325              
326 21         55 my($attributes);
327             my($name);
328 0         0 my($s);
329 0         0 my($type);
330              
331             $self -> renderer -> root -> walk_down
332             ({
333             callback => sub
334             {
335 25018     25018   668804 my($node, $opt) = @_;
336              
337             # Ignore the root, and keep walking.
338              
339 25018 100       64583 return 1 if ($node -> is_root);
340              
341 24997         242372 $name = $node -> name;
342 24997         218927 $name =~ s/^\s*\d+\s=\s(.+)/$1/;
343 24997         95830 $name =~ s/\s\[[A-Z]+\s\d+\]//;
344 24997         75682 $attributes = $node -> attributes;
345 24997         152715 $type = $$attributes{type};
346              
347 24997 100       58374 if ($type eq 'SCALAR')
348             {
349 14813         26856 push @$slim_list, $name;
350              
351 14813         49891 $self -> log(info => ' ' x $$opt{_depth} . $name);
352             }
353              
354 24997         151829 return 1; # Keep walking.
355             },
356 21         562 _depth => 0,
357             });
358              
359 21         1464 $self -> output_tokens($slim_list);
360              
361             } # End of render.
362              
363             # ------------------------------------------------
364              
365             sub run
366             {
367 22     22 1 27860 my($self, %args) = @_;
368 22   33     659 my($file_name) = $args{input_file_name} || $self -> input_file_name;
369 22         270 $sig_warn_flag = 0; # Turn off Perl's warnings for the duration of tree processing.
370              
371 22         117 $self -> value($self -> process($file_name) );
372 21         677464 $self -> renderer -> run(${$self -> value});
  21         606  
373 21         10383725 $self -> log(debug => $_) for @{$self -> renderer -> root -> tree2string({no_attributes => 1 - $self -> attributes})};
  21         561  
374 21         3877 $self -> render;
375              
376 21         1481 $sig_warn_flag = 1; # Turn Perl's warnings back on.
377              
378 21   33     516 my($output_file_name) = $args{output_file_name} || $self -> output_file_name;
379              
380 21 50       810 if ($output_file_name)
381             {
382 21         154 path($output_file_name) -> spew_utf8(map{"$_\n"} @{$self -> output_tokens});
  14813         28120  
  21         1376  
383             }
384              
385             # Return 0 for success and 1 for failure.
386              
387 21         159704 return 0;
388              
389             } # End of run.
390              
391             #-------------------------------------------------
392              
393             1;
394              
395             =pod
396              
397             =head1 NAME
398              
399             C - A Lua source code parser
400              
401             =head1 Synopsis
402              
403             #!/usr/bin/env perl
404              
405             use strict;
406             use warnings;
407              
408             use MarpaX::Languages::Lua::Parser;
409              
410             # ---------------------------------
411              
412             my($input_file_name) = shift || die "Usage: $0 a_lua_source_file_name\n";
413             my($parser) = MarpaX::Languages::Lua::Parser -> new(input_file_name => $input_file_name);
414              
415             $parser -> run;
416              
417             print map{"$_\n"} @{$parser -> output_tokens};
418              
419             This script ships as scripts/synopsis.pl. Run it as:
420              
421             shell> perl -Ilib scripts/synopsis.pl lua.sources/echo.lua
422              
423             See also scripts/parse.file.pl for code which takes command line parameters. For help, run:
424              
425             shell> perl -Ilib scripts/parse.file.pl -h
426              
427             =head1 Description
428              
429             C parses Lua source code files.
430              
431             The result is stored in a tree managed by L.
432              
433             A list of scalar tokens from this tree is stored in an arrayref.
434              
435             See the FAQ question L for details.
436              
437             =head1 Installation
438              
439             Install C as you would for any C module:
440              
441             Run:
442              
443             cpanm MarpaX::Languages::Lua::Parser
444              
445             or run:
446              
447             sudo cpan MarpaX::Languages::Lua::Parser
448              
449             or unpack the distro, and then:
450              
451             perl Makefile.PL
452             make (or dmake or nmake)
453             make test
454             make install
455              
456             =head1 Constructor and Initialization
457              
458             C is called as C<< my($parser) = MarpaX::Languages::Lua::Parser -> new(k1 => v1, k2 => v2, ...) >>.
459              
460             It returns a new object of type C.
461              
462             Key-value pairs accepted in the parameter list (see also the corresponding methods
463             [e.g. L]):
464              
465             =over 4
466              
467             =item o attributes => $Boolean
468              
469             When set to 1, metadata attached to each tree node is included in the output.
470              
471             If you set the L to 'debug', this tree is printed to the log.
472              
473             Default: 0.
474              
475             =item o input_file_name => $string
476              
477             The name the input file to be parsed.
478              
479             This option is mandatory.
480              
481             Default: ''.
482              
483             =item o logger => aLog::HandlerObject
484              
485             By default, an object of type L is created which prints to STDOUT,
486             but given the default setting (maxlevel => 'notice'), nothing is actually printed.
487              
488             See C and C below.
489              
490             Set C to '' (the empty string) to stop a logger being created.
491              
492             Default: undef.
493              
494             =item o maxlevel => logOption1
495              
496             This option affects L objects.
497              
498             See the L docs.
499              
500             Typical values: 'info', 'debug'.
501              
502             See the FAQ question L for details.
503              
504             See also the help output by scripts/parse.file.pl -h.
505              
506             Default: 'notice'.
507              
508             =item o minlevel => logOption2
509              
510             This option affects L object.
511              
512             See the L docs.
513              
514             Default: 'error'.
515              
516             No lower levels are used.
517              
518             =item o output_file_name => $string
519              
520             The name of the text file to be written.
521              
522             If not set, nothing is written.
523              
524             The items written, one per line, are as returned by L.
525              
526             Default: ''.
527              
528             =back
529              
530             =head1 Methods
531              
532             =head2 attributes([$Boolean])
533              
534             Here, the [] indicate an optional parameter.
535              
536             Gets or sets the attributes option.
537              
538             Note: The value passed to L's C method is (1 - $Boolean).
539              
540             See the FAQ question L for details.
541              
542             C is a parameter to L.
543              
544             =head2 input_file_name([$string])
545              
546             Here, the [] indicate an optional parameter.
547              
548             Get or set the name of the file to parse.
549              
550             See lua.sources/*.lua for sample input.
551              
552             Note: C is a parameter to new().
553              
554             =head2 log($level, $s)
555              
556             Calls $self -> logger -> log($level => $s) if ($self -> logger).
557              
558             =head2 logger([$log_object])
559              
560             Here, the [] indicate an optional parameter.
561              
562             Get or set the log object.
563              
564             C<$log_object> must be a L-compatible object.
565              
566             To disable logging, just set logger to the empty string.
567              
568             Note: C is a parameter to new().
569              
570             =head2 maxlevel([$string])
571              
572             Here, the [] indicate an optional parameter.
573              
574             Get or set the value used by the logger object.
575              
576             This option is only used if an object of type L is created. See L.
577              
578             Typical values: 'info', 'debug'.
579              
580             See the FAQ question L for details.
581              
582             Note: C is a parameter to new().
583              
584             =head2 minlevel([$string])
585              
586             Here, the [] indicate an optional parameter.
587              
588             Get or set the value used by the logger object.
589              
590             This option is only used if an object of type L is created. See L.
591              
592             Note: C is a parameter to new().
593              
594             =head2 new()
595              
596             This method is auto-generated by L.
597              
598             =head2 output_file_name([$string])
599              
600             Here, the [] indicate an optional parameter.
601              
602             Get or set the name of the file to write.
603              
604             The tokens written are as returned from L.
605              
606             Note: C is a parameter to new().
607              
608             =head2 output_tokens()
609              
610             Returns an arrayref of tokens output by the parse, one per line. These tokens are pushed onto the
611             stack by walking the tree returned by the renderer, which is an object of type
612             L. The renderer is run by passing it the output from the call to Marpa's
613             C method. See L.
614              
615             If you set the L to 'info', these tokens are printed to the log.
616              
617             See scripts/synopsis.pl for accessing this arrayref.
618              
619             See lua.output/*.txt for sample output.
620              
621             =head2 renderer()
622              
623             Returns the object of type L, which takes the output from the call to Marpa's
624             C method and converts it into an object of type L.
625              
626             If you set the L to 'debug', this tree is printed to the log.
627              
628             =head2 run([%args])
629              
630             The method which does all the work.
631              
632             C<%args> is a hash with this optional (key => value) pair:
633              
634             =over 4
635              
636             =item o input_file_name => $in_file_name
637              
638             =item o output_file_name => $out_file_name
639              
640             =back
641              
642             File names specified in the call to C take precedence over file names specified to L.
643              
644             Returns 0 for a successful parse and 1 for failure.
645              
646             The code dies if L itself can't parse the given input file.
647              
648             Note: C and C are parameters to L.
649              
650             =head1 FAQ
651              
652             =head2 Why did you store Lua's BNF in a __DATA__ section?
653              
654             This avoids problems with single- and double-quotes in the BNF, and the allegedly unknown escape
655             sequences \v etc too.
656              
657             =head2 How do I get output from this module?
658              
659             In various ways:
660              
661             =over 4
662              
663             =item o Call the L method
664              
665             Then, process the arrayref returned.
666              
667             =item o Call the L method
668              
669             This will return an object of type L, and from there you can call that object's
670             C method, to get access to the tree itself. See this module's C method for sample
671             code.
672              
673             =item o Set maxlevel to 'info'.
674              
675             This writes the output tokens to the log, one per line.
676              
677             See the C method for sample code.
678              
679             =item o Set maxlevel to 'debug'.
680              
681             This writes the output tokens to the log, one per line, and also writes to the log the tree
682             returned by passing the return value of Marpa's C method to the renderer. The renderer
683             is an object of type L, and outputs a tree managed by L.
684              
685             See the L method for sample code.
686              
687             =item o Set the output_file_name to a non-empty string
688              
689             In this case the code will walk the tree just mentioned, and output the scalar items, one per line,
690             to this file.
691              
692             =item o All of the above
693              
694             =back
695              
696             =head2 How do I interpret the output?
697              
698             For help with this, try the IRC channel irc.freenode.net#marpa.
699              
700             What that really means is that neither Jeffrey no anyone else imposes any kind of restriction on
701             what you may do with the output, or with how you may interpret it.
702              
703             =head2 Where is Marpa's home page?
704              
705             L.
706              
707             =head1 Machine-Readable Change Log
708              
709             The file Changes was converted into Changelog.ini by L.
710              
711             =head1 Version Numbers
712              
713             Version numbers < 1.00 represent development versions. From 1.00 up, they are production versions.
714              
715             =head1 Repository
716              
717             L
718              
719             =head1 Support
720              
721             Email the author, or log a bug on RT:
722              
723             L.
724              
725             =head1 Credits
726              
727             Jeffrey Kegler wrote the code, and posted a link on the IRC chat channel mentioned above.
728              
729             See L.
730              
731             =head1 Author
732              
733             L was packaged by Ron Savage Iron@savage.net.auE> in 2015.
734              
735             Homepage: L.
736              
737             =head1 Copyright
738              
739             Australian copyright (c) 2015, Ron Savage.
740              
741             All Programs of mine are 'OSI Certified Open Source Software';
742             you can redistribute them and/or modify them under the terms of
743             The Artistic License 2.0, a copy of which is available at:
744             http://www.opensource.org/licenses/index.html
745              
746             =cut
747              
748             __DATA__