55# http://darkness.codefu.org/wordpress/2003/03/perl-scoping/
66
77package PerlPP ;
8- our $VERSION = ' 0.3.0-alpha ' ;
8+ our $VERSION = ' 0.3.0-pre.2 ' ;
99
1010use v5.10; # provides // - http://perldoc.perl.org/perl5100delta.html
1111use strict;
@@ -46,6 +46,7 @@ package PerlPP;
4646use constant OBMODE_ECHO => 3;
4747use constant OBMODE_COMMAND => 4;
4848use constant OBMODE_COMMENT => 5;
49+ use constant OBMODE_SYSTEM => 6; # an external command being run
4950
5051# Layout of the output-buffer stack.
5152use constant OB_TOP => 0; # top of the stack is [0]: use [un]shift
@@ -58,6 +59,7 @@ package PerlPP;
5859my $Package = ' ' ; # package name for the generated script
5960my $RootSTDOUT ;
6061my $WorkingDir = ' .' ;
62+ my %Opts ; # Parsed command-line options
6163
6264# Vars accessible to, or used by or on behalf of, :macro / :immediate code
6365my @Preprocessors = ();
@@ -259,6 +261,45 @@ sub ExecuteCommand {
259261 }
260262} # ExecuteCommand()
261263
264+ sub GetStatusReport {
265+ # Get a human-readable result string, given $? and $! from a qx//.
266+ # Modified from http://perldoc.perl.org/functions/system.html
267+ my $retval ;
268+ my $status = shift ;
269+ my $errmsg = shift || ' ' ;
270+
271+ if ($status == -1) {
272+ $retval = " failed to execute: $errmsg " ;
273+ } elsif ($status & 127) {
274+ $retval = sprintf (" process died with signal %d , %s coredump" ,
275+ ($status & 127), ($status & 128) ? ' with' : ' without' );
276+ } elsif ($status != 0) {
277+ $retval = sprintf (" process exited with value %d " , $status >> 8);
278+ }
279+ return $retval ;
280+ } # GetStatusReport()
281+
282+ sub ShellOut { # Run an external command
283+ my $cmd = shift =~ s / ^\s +|\s +$// gr ; # trim leading/trailing whitespace
284+ die " No command provided to @{[TAG_OPEN]}!...@{[TAG_CLOSE]}" unless $cmd ;
285+ $cmd = QuoteString $cmd ; # note: cmd is now wrapped in ''
286+
287+ my $error_response = ($Opts {KEEP_GOING } ? ' warn' : ' die' ); # How we will handle errors
288+
289+ print (
290+ qq{ do {
291+ my \$ output = qx${cmd} ;
292+ my \$ status = PerlPP::GetStatusReport(\$ ?, \$ !);
293+ if(\$ status) {
294+ $error_response ("perlpp: command '" . ${cmd} . "' failed: \$ {status}; invoked");
295+ } else {
296+ print \$ output;
297+ }
298+ };
299+ } =~ s / ^\t {2}// gmr # de-indent
300+ );
301+ } # ShellOut()
302+
262303sub OnOpening {
263304 # takes the rest of the string, beginning right after the ? of the tag_open
264305 # returns (withinTag, string still to be processed)
@@ -286,6 +327,8 @@ sub OnOpening {
286327 # OBMODE_CODE
287328 } elsif ( $after =~ / ^(?:\s |$) / ) {
288329 # OBMODE_CODE
330+ } elsif ( $after =~ / ^!/ ) {
331+ $insetMode = OBMODE_SYSTEM;
289332 } elsif ( $after =~ / ^"/ ) {
290333 die " Unexpected end of capturing" ;
291334 } else {
@@ -328,6 +371,8 @@ sub OnClosing {
328371 # Ignore the contents - no operation
329372 } elsif ( $insetMode == OBMODE_CODE ) {
330373 print " $inside \n " ; # \n so you can put comments in your perl code
374+ } elsif ( $insetMode == OBMODE_SYSTEM ) {
375+ ShellOut( $inside );
331376 } else {
332377 print $inside ;
333378 }
@@ -447,19 +492,20 @@ sub OutputResult {
447492 close ( $f ) or die $! ;
448493} # OutputResult()
449494
450- # === Command line ======== ================================================
495+ # === Command line parsing ================================================
451496
452497my %CMDLINE_OPTS = (
453- # hash from internal name to array reference of j
498+ # hash from internal name to array reference of
454499 # [getopt-name, getopt-options, optional default-value]
455500 # They are listed in alphabetical order by option name,
456501 # lowercase before upper, although the code does not require that order.
457502
458- EVAL => [' e' ,' |eval=s' , " " ],
503+ EVAL => [' e' ,' |eval=s' , ' ' ],
459504 DEBUG => [' d' ,' |E|debug' , false],
460505 # -h and --help reserved
461506 # --man reserved
462- # INPUT_FILENAME assigned by parse_command_line_into
507+ # INPUT_FILENAME assigned by parse_command_line_into()
508+ KEEP_GOING => [' k' ,' |keep-going' ,false],
463509 OUTPUT_FILENAME => [' o' ,' |output=s' , " " ],
464510 DEFS => [' D' ,' |define:s%' ], # In %D, and text substitution
465511 SETS => [' s' ,' |set:s%' ], # Extra data in %S, without text substitution
@@ -517,17 +563,16 @@ sub parse_command_line_into {
517563
518564# === Main ================================================================
519565sub Main {
520- my %opts ;
521- parse_command_line_into \%opts ;
566+ parse_command_line_into \%Opts ;
522567
523568 # Preamble
524569
525- $Package = $opts {INPUT_FILENAME };
570+ $Package = $Opts {INPUT_FILENAME };
526571 $Package =~ s / ^.*?([a-z_][a-z_0-9.]*).pl?$/ $1 / i ;
527572 $Package =~ s / [^a-z0-9_]/ _/ gi ;
528573 # $Package is not the whole name, so can start with a number.
529574
530- StartOB();
575+ StartOB(); # Output from here on will be included in the generated script
531576 print " package PPP_${Package} ;\n use 5.010;\n use strict;\n use warnings;\n " ;
532577 print " use constant { true => !!1, false => !!0 };\n " ;
533578
@@ -537,8 +582,8 @@ sub Main {
537582 # as textual representations of expressions.
538583 # The parameters are in %D at runtime.
539584 print " my %D = (\n " ;
540- for my $defname (keys %{$opts {DEFS }}) {
541- my $val = ${$opts {DEFS }}{$defname } // ' true' ;
585+ for my $defname (keys %{$Opts {DEFS }}) {
586+ my $val = ${$Opts {DEFS }}{$defname } // ' true' ;
542587 # just in case it's undef. "true" is the constant in this context
543588 $val = ' true' if $val eq ' ' ;
544589 # "-D foo" (without a value) sets it to _true_ so
@@ -549,42 +594,42 @@ sub Main {
549594 print " );\n " ;
550595
551596 # Save a copy for use at generation time
552- %Defs = map { my $v = eval (${$opts {DEFS }}{$_ });
597+ %Defs = map { my $v = eval (${$Opts {DEFS }}{$_ });
553598 warn " Could not evaluate -D \" $_ \" : $@ " if $@ ;
554599 $_ => ($v // true)
555600 }
556- keys %{$opts {DEFS }};
601+ keys %{$Opts {DEFS }};
557602
558603 # Set up regex for text substitution of Defs.
559604 # Modified from http://www.perlmonks.org/?node_id=989740 by
560605 # AnomalousMonk, http://www.perlmonks.org/?node_id=634253
561- if (%{$opts {DEFS }}) {
606+ if (%{$Opts {DEFS }}) {
562607 my $rx_search =
563- ' \b(' . (join ' |' , map quotemeta , keys %{$opts {DEFS }}) . ' )\b' ;
608+ ' \b(' . (join ' |' , map quotemeta , keys %{$Opts {DEFS }}) . ' )\b' ;
564609 $Defs_RE = qr {$rx_search } ;
565610
566611 # Save the replacement values. If a value cannot be evaluated,
567612 # use the name so the replacement will not change the text.
568613 %Defs_repl_text =
569- map { my $v = eval (${$opts {DEFS }}{$_ });
614+ map { my $v = eval (${$Opts {DEFS }}{$_ });
570615 ($@ || !defined ($v )) ? ($_ => $_ ) : ($_ => (' ' . $v ))
571616 }
572- keys %{$opts {DEFS }};
617+ keys %{$Opts {DEFS }};
573618 }
574619
575620 # Now do SETS: -s or --set, into %S by analogy with -D and %D.
576621
577622 # Save a copy for use at generation time
578- %Sets = map { my $v = eval (${$opts {SETS }}{$_ });
623+ %Sets = map { my $v = eval (${$Opts {SETS }}{$_ });
579624 warn " Could not evaluate -s \" $_ \" : $@ " if $@ ;
580625 $_ => ($v // true)
581626 }
582- keys %{$opts {SETS }};
627+ keys %{$Opts {SETS }};
583628
584629 # Make the copy for runtime
585630 print " my %S = (\n " ;
586- for my $defname (keys %{$opts {SETS }}) {
587- my $val = ${$opts {SETS }}{$defname };
631+ for my $defname (keys %{$Opts {SETS }}) {
632+ my $val = ${$Opts {SETS }}{$defname };
588633 if (!defined ($val )) {
589634 }
590635 $val = ' true' if $val eq ' ' ;
@@ -596,20 +641,20 @@ sub Main {
596641 print " );\n " ;
597642
598643 # Initial code from the command line, if any
599- print $opts {EVAL }, " \n " if $opts {EVAL };
644+ print $Opts {EVAL }, " \n " if $Opts {EVAL };
600645
601646 # The input file
602- ProcessFile( $opts {INPUT_FILENAME } );
647+ ProcessFile( $Opts {INPUT_FILENAME } );
603648
604649 my $script = EndOB(); # The generated Perl script
605650
606651 # --- Run it ---
607- if ( $opts {DEBUG } ) {
652+ if ( $Opts {DEBUG } ) {
608653 print $script ;
609654
610655 } else {
611- StartOB(); # output of the Perl script
612- my $result ; # save any errors from the eval
656+ StartOB(); # Start collecting the output of the Perl script
657+ my $result ; # To save any errors from the eval
613658
614659 # TODO hide %Defs and others of our variables we don't want
615660 # $script to access.
@@ -619,7 +664,7 @@ sub Main {
619664 print STDERR $result ;
620665 exit 1;
621666 } else { # Save successful output
622- OutputResult( \EndOB(), $opts {OUTPUT_FILENAME } );
667+ OutputResult( \EndOB(), $Opts {OUTPUT_FILENAME } );
623668 }
624669 }
625670} # Main()
@@ -682,6 +727,12 @@ =head1 OPTIONS
682727Don't evaluate Perl code, just write the generated code to STDOUT.
683728By analogy with the C<-E > option of gcc.
684729
730+ =item -k, --keep-going
731+
732+ Normally, errors in a C<!command > sequence will terminate further
733+ processing. If B<-k > is given, an error message will be printed to stderr,
734+ but the script will keep running.
735+
685736=item -s, --set B<name > [=B<value > ]
686737
687738As B<-D > , but:
0 commit comments