5
5
# http://darkness.codefu.org/wordpress/2003/03/perl-scoping/
6
6
7
7
package PerlPP ;
8
- our $VERSION = ' 0.3.0-alpha ' ;
8
+ our $VERSION = ' 0.3.0-pre.2 ' ;
9
9
10
10
use v5.10; # provides // - http://perldoc.perl.org/perl5100delta.html
11
11
use strict;
@@ -46,6 +46,7 @@ package PerlPP;
46
46
use constant OBMODE_ECHO => 3;
47
47
use constant OBMODE_COMMAND => 4;
48
48
use constant OBMODE_COMMENT => 5;
49
+ use constant OBMODE_SYSTEM => 6; # an external command being run
49
50
50
51
# Layout of the output-buffer stack.
51
52
use constant OB_TOP => 0; # top of the stack is [0]: use [un]shift
@@ -58,6 +59,7 @@ package PerlPP;
58
59
my $Package = ' ' ; # package name for the generated script
59
60
my $RootSTDOUT ;
60
61
my $WorkingDir = ' .' ;
62
+ my %Opts ; # Parsed command-line options
61
63
62
64
# Vars accessible to, or used by or on behalf of, :macro / :immediate code
63
65
my @Preprocessors = ();
@@ -259,6 +261,45 @@ sub ExecuteCommand {
259
261
}
260
262
} # ExecuteCommand()
261
263
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
+
262
303
sub OnOpening {
263
304
# takes the rest of the string, beginning right after the ? of the tag_open
264
305
# returns (withinTag, string still to be processed)
@@ -286,6 +327,8 @@ sub OnOpening {
286
327
# OBMODE_CODE
287
328
} elsif ( $after =~ / ^(?:\s |$) / ) {
288
329
# OBMODE_CODE
330
+ } elsif ( $after =~ / ^!/ ) {
331
+ $insetMode = OBMODE_SYSTEM;
289
332
} elsif ( $after =~ / ^"/ ) {
290
333
die " Unexpected end of capturing" ;
291
334
} else {
@@ -328,6 +371,8 @@ sub OnClosing {
328
371
# Ignore the contents - no operation
329
372
} elsif ( $insetMode == OBMODE_CODE ) {
330
373
print " $inside \n " ; # \n so you can put comments in your perl code
374
+ } elsif ( $insetMode == OBMODE_SYSTEM ) {
375
+ ShellOut( $inside );
331
376
} else {
332
377
print $inside ;
333
378
}
@@ -447,19 +492,20 @@ sub OutputResult {
447
492
close ( $f ) or die $! ;
448
493
} # OutputResult()
449
494
450
- # === Command line ======== ================================================
495
+ # === Command line parsing ================================================
451
496
452
497
my %CMDLINE_OPTS = (
453
- # hash from internal name to array reference of j
498
+ # hash from internal name to array reference of
454
499
# [getopt-name, getopt-options, optional default-value]
455
500
# They are listed in alphabetical order by option name,
456
501
# lowercase before upper, although the code does not require that order.
457
502
458
- EVAL => [' e' ,' |eval=s' , " " ],
503
+ EVAL => [' e' ,' |eval=s' , ' ' ],
459
504
DEBUG => [' d' ,' |E|debug' , false],
460
505
# -h and --help reserved
461
506
# --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],
463
509
OUTPUT_FILENAME => [' o' ,' |output=s' , " " ],
464
510
DEFS => [' D' ,' |define:s%' ], # In %D, and text substitution
465
511
SETS => [' s' ,' |set:s%' ], # Extra data in %S, without text substitution
@@ -517,17 +563,16 @@ sub parse_command_line_into {
517
563
518
564
# === Main ================================================================
519
565
sub Main {
520
- my %opts ;
521
- parse_command_line_into \%opts ;
566
+ parse_command_line_into \%Opts ;
522
567
523
568
# Preamble
524
569
525
- $Package = $opts {INPUT_FILENAME };
570
+ $Package = $Opts {INPUT_FILENAME };
526
571
$Package =~ s / ^.*?([a-z_][a-z_0-9.]*).pl?$/ $1 / i ;
527
572
$Package =~ s / [^a-z0-9_]/ _/ gi ;
528
573
# $Package is not the whole name, so can start with a number.
529
574
530
- StartOB();
575
+ StartOB(); # Output from here on will be included in the generated script
531
576
print " package PPP_${Package} ;\n use 5.010;\n use strict;\n use warnings;\n " ;
532
577
print " use constant { true => !!1, false => !!0 };\n " ;
533
578
@@ -537,8 +582,8 @@ sub Main {
537
582
# as textual representations of expressions.
538
583
# The parameters are in %D at runtime.
539
584
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' ;
542
587
# just in case it's undef. "true" is the constant in this context
543
588
$val = ' true' if $val eq ' ' ;
544
589
# "-D foo" (without a value) sets it to _true_ so
@@ -549,42 +594,42 @@ sub Main {
549
594
print " );\n " ;
550
595
551
596
# Save a copy for use at generation time
552
- %Defs = map { my $v = eval (${$opts {DEFS }}{$_ });
597
+ %Defs = map { my $v = eval (${$Opts {DEFS }}{$_ });
553
598
warn " Could not evaluate -D \" $_ \" : $@ " if $@ ;
554
599
$_ => ($v // true)
555
600
}
556
- keys %{$opts {DEFS }};
601
+ keys %{$Opts {DEFS }};
557
602
558
603
# Set up regex for text substitution of Defs.
559
604
# Modified from http://www.perlmonks.org/?node_id=989740 by
560
605
# AnomalousMonk, http://www.perlmonks.org/?node_id=634253
561
- if (%{$opts {DEFS }}) {
606
+ if (%{$Opts {DEFS }}) {
562
607
my $rx_search =
563
- ' \b(' . (join ' |' , map quotemeta , keys %{$opts {DEFS }}) . ' )\b' ;
608
+ ' \b(' . (join ' |' , map quotemeta , keys %{$Opts {DEFS }}) . ' )\b' ;
564
609
$Defs_RE = qr {$rx_search } ;
565
610
566
611
# Save the replacement values. If a value cannot be evaluated,
567
612
# use the name so the replacement will not change the text.
568
613
%Defs_repl_text =
569
- map { my $v = eval (${$opts {DEFS }}{$_ });
614
+ map { my $v = eval (${$Opts {DEFS }}{$_ });
570
615
($@ || !defined ($v )) ? ($_ => $_ ) : ($_ => (' ' . $v ))
571
616
}
572
- keys %{$opts {DEFS }};
617
+ keys %{$Opts {DEFS }};
573
618
}
574
619
575
620
# Now do SETS: -s or --set, into %S by analogy with -D and %D.
576
621
577
622
# Save a copy for use at generation time
578
- %Sets = map { my $v = eval (${$opts {SETS }}{$_ });
623
+ %Sets = map { my $v = eval (${$Opts {SETS }}{$_ });
579
624
warn " Could not evaluate -s \" $_ \" : $@ " if $@ ;
580
625
$_ => ($v // true)
581
626
}
582
- keys %{$opts {SETS }};
627
+ keys %{$Opts {SETS }};
583
628
584
629
# Make the copy for runtime
585
630
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 };
588
633
if (!defined ($val )) {
589
634
}
590
635
$val = ' true' if $val eq ' ' ;
@@ -596,20 +641,20 @@ sub Main {
596
641
print " );\n " ;
597
642
598
643
# Initial code from the command line, if any
599
- print $opts {EVAL }, " \n " if $opts {EVAL };
644
+ print $Opts {EVAL }, " \n " if $Opts {EVAL };
600
645
601
646
# The input file
602
- ProcessFile( $opts {INPUT_FILENAME } );
647
+ ProcessFile( $Opts {INPUT_FILENAME } );
603
648
604
649
my $script = EndOB(); # The generated Perl script
605
650
606
651
# --- Run it ---
607
- if ( $opts {DEBUG } ) {
652
+ if ( $Opts {DEBUG } ) {
608
653
print $script ;
609
654
610
655
} 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
613
658
614
659
# TODO hide %Defs and others of our variables we don't want
615
660
# $script to access.
@@ -619,7 +664,7 @@ sub Main {
619
664
print STDERR $result ;
620
665
exit 1;
621
666
} else { # Save successful output
622
- OutputResult( \EndOB(), $opts {OUTPUT_FILENAME } );
667
+ OutputResult( \EndOB(), $Opts {OUTPUT_FILENAME } );
623
668
}
624
669
}
625
670
} # Main()
@@ -682,6 +727,12 @@ =head1 OPTIONS
682
727
Don't evaluate Perl code, just write the generated code to STDOUT.
683
728
By analogy with the C<-E > option of gcc.
684
729
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
+
685
736
=item -s, --set B<name > [=B<value > ]
686
737
687
738
As B<-D > , but:
0 commit comments