File Coverage

blib/lib/Text/PerlPP.pm
Criterion Covered Total %
statement 447 490 91.2
branch 143 200 71.5
condition 24 32 75.0
subroutine 56 60 93.3
pod 0 21 0.0
total 670 803 83.4


line stmt bran cond sub pod time code
1             #!perl
2             # PerlPP: Perl preprocessor. See the perldoc for usage.
3              
4             package Text::PerlPP;
5              
6             # Semantic versioning, packed per Perl rules. Must always be at least one
7             # digit left of the decimal, and six digits right of the decimal. For
8             # prerelease versions, put an underscore before the last three digits.
9             our $VERSION = '0.600002';
10              
11 11     11   838418 use 5.010001;
  11         162  
12 11     11   58 use strict;
  11         25  
  11         264  
13 11     11   59 use warnings;
  11         50  
  11         522  
14              
15 11     11   8158 use Getopt::Long 2.5 qw(GetOptionsFromArray);
  11         146154  
  11         312  
16 11     11   7617 use Pod::Usage;
  11         555590  
  11         1626  
17              
18             # === Constants ===========================================================
19              
20 11     11   110 use constant true => !!1;
  11         24  
  11         850  
21 11     11   76 use constant false => !!0;
  11         24  
  11         530  
22              
23 11     11   69 use constant DEBUG => false;
  11         23  
  11         497  
24              
25             # Shell exit codes
26 11     11   65 use constant EXIT_OK => 0; # success
  11         24  
  11         450  
27 11     11   87 use constant EXIT_PROC_ERR => 1; # error during processing
  11         18  
  11         453  
28 11     11   58 use constant EXIT_PARAM_ERR => 2; # couldn't understand the command line
  11         26  
  11         587  
29              
30             # Constants for the parser
31 11     11   83 use constant TAG_OPEN => '<' . '?'; # literal < ? and ? > shouldn't
  11         24  
  11         501  
32 11     11   65 use constant TAG_CLOSE => '?' . '>'; # appear in this file.
  11         25  
  11         713  
33 11     11   69 use constant OPENING_RE => qr/^(.*?)\Q${\(TAG_OPEN)}\E(.*)$/s; # /s states for single-line mode
  11         23  
  11         30  
  11         1061  
34 11     11   79 use constant CLOSING_RE => qr/^(.*?)\Q${\(TAG_CLOSE)}\E(.*)$/s;
  11         21  
  11         24  
  11         2844  
35              
36 11         2543 use constant DEFINE_NAME_RE =>
37 11     11   83 qr/^(?[[:alpha:]][[:alnum:]_]*|[[:alpha:]_][[:alnum:]_]+)$/i;
  11         22  
38             # Valid names for -D. TODO expand this to Unicode.
39             # Bare underscore isn't permitted because it's special in perl.
40 11         573 use constant DEFINE_NAME_IN_CONTEXT_RE =>
41 11     11   89 qr/^(?[[:alpha:]][[:alnum:]_]*|[[:alpha:]_][[:alnum:]_]+)\s*+(?.*+)$/i;
  11         27  
42             # A valid name followed by something else. Used for, e.g., :if and :elsif.
43              
44             # Modes - each output buffer has one
45 11     11   96 use constant OBMODE_PLAIN => 0; # literal text, not in tag_open/tag_close
  11         31  
  11         446  
46 11     11   63 use constant OBMODE_CAPTURE => 1; # same as OBMODE_PLAIN but with capturing
  11         19  
  11         429  
47 11     11   56 use constant OBMODE_CODE => 2; # perl code
  11         22  
  11         509  
48 11     11   69 use constant OBMODE_ECHO => 3;
  11         25  
  11         491  
49 11     11   251 use constant OBMODE_COMMAND => 4;
  11         25  
  11         507  
50 11     11   65 use constant OBMODE_COMMENT => 5;
  11         20  
  11         441  
51 11     11   56 use constant OBMODE_SYSTEM => 6; # an external command being run
  11         24  
  11         537  
52              
53             # Layout of the output-buffer stack.
54 11     11   67 use constant OB_TOP => 0; # top of the stack is [0]: use [un]shift
  11         33  
  11         586  
55              
56 11     11   65 use constant OB_MODE => 0; # indices of the stack entries
  11         23  
  11         551  
57 11     11   73 use constant OB_CONTENTS => 1;
  11         22  
  11         468  
58 11     11   58 use constant OB_STARTLINE => 2;
  11         22  
  11         614  
59              
60             # What $self is called inside a script package
61 11     11   69 use constant PPP_SELF_INSIDE => 'PSelf';
  11         20  
  11         12852  
62              
63             # Debugging info
64             my @OBModeNames = qw(plain capture code echo command comment);
65              
66             # === Globals =============================================================
67              
68             our @Instances; # Hold the instance associated with each package
69              
70             # Make a hashref with all of the globals so state doesn't leak from
71             # one call to Main() to another call to Main().
72             sub _make_instance {
73             return {
74              
75             # Internals
76 113     113   1848 Package => '', # package name for the generated script
77             RootSTDOUT => undef,
78             WorkingDir => '.',
79             Opts => {}, # Parsed command-line options
80              
81             # Vars accessible to, or used by or on behalf of, :macro / :immediate code
82             Preprocessors => [],
83             Postprocessors => [],
84             Prefixes => {}, # set by ExecuteCommand; used by PrepareString
85              
86             # -D definitions. -Dfoo creates $Defs{foo}=>=true and $Defs_repl_text{foo}==''.
87             Defs => {}, # Command-line -D arguments
88             Defs_RE => false, # Regex that matches any -D name
89             Defs_repl_text => {}, # Replacement text for -D names
90              
91             # -s definitions.
92             Sets => {}, # Command-line -s arguments
93              
94             # Output-buffer stack
95             OutputBuffers => [],
96             # Each entry is an array of [mode, text, opening line number]
97              
98             }
99             } #_make_instance
100              
101             # Also, add a variable to the PPP_* pointing to the encapsulated state.
102              
103             # === Internal routines ===================================================
104              
105             # An alias for print(). This is used so that you can find print statements
106             # in the generated script by searching for "print".
107             sub emit {
108 2373     2373 0 5427 print @_;
109             }
110              
111             sub AddPreprocessor {
112 0     0 0 0 my $self = shift;
113 0         0 push( @{$self->{Preprocessors}}, shift );
  0         0  
114             # TODO run it!
115             }
116              
117             sub AddPostprocessor {
118 0     0 0 0 my $self = shift;
119 0         0 push( @{$self->{Postprocessors}}, shift );
  0         0  
120             }
121              
122             # --- Output buffers ----------------------------------------------
123              
124             # Open an output buffer. Default mode is literal text.
125             sub StartOB {
126 784     784 0 1914 my $self = shift;
127              
128 784   100     1904 my $mode = shift // OBMODE_PLAIN;
129 784   100     1968 my $lineno = shift // 1;
130              
131 784 100       1032 if ( scalar @{$self->{OutputBuffers}} == 0 ) {
  784         1732  
132 214         583 $| = 1; # flush contents of STDOUT
133 214 50       5341 open( $self->{RootSTDOUT}, ">&STDOUT" ) or die $!; # dup filehandle
134             }
135 784         1354 unshift( @{$self->{OutputBuffers}}, [ $mode, "", $lineno ] );
  784         2076  
136 784         2653 close( STDOUT ); # must be closed before redirecting it to a variable
137 784 50   1   4395 open( STDOUT, ">>", \($self->{OutputBuffers}->[ OB_TOP ]->[ OB_CONTENTS ]) ) or die $!;
  1         7  
  1         2  
  1         20  
138 784         2589 $| = 1; # do not use output buffering
139              
140             printf STDERR "Opened %s buffer %d\n", $OBModeNames[$mode],
141 784         1556 scalar @{$self->{OutputBuffers}} if DEBUG;
142             } #StartOB()
143              
144             sub EndOB {
145 772     772 0 1126 my $self = shift;
146 772         1095 my $ob;
147              
148 772         983 $ob = shift( @{$self->{OutputBuffers}} );
  772         1300  
149 772         1464 close( STDOUT );
150 772 100       978 if ( scalar @{$self->{OutputBuffers}} == 0 ) {
  772         1529  
151 202 50       4293 open( STDOUT, ">&", $self->{RootSTDOUT} ) or die $!; # dup filehandle
152 202         878 $| = 0; # return output buffering to the default state
153             } else {
154 570 50       2866 open( STDOUT, ">>", \($self->{OutputBuffers}->[ OB_TOP ]->[ OB_CONTENTS ]) )
155             or die $!;
156             }
157              
158 772         1219 if(DEBUG) {
159             printf STDERR "Closed %s buffer %d, contents '%s%s'\n",
160             $OBModeNames[$ob->[ OB_MODE ]],
161             1+@{$self->{OutputBuffers}},
162             substr($ob->[ OB_CONTENTS ], 0, 40),
163             length($ob->[ OB_CONTENTS ])>40 ? '...' : '';
164             }
165              
166 772         2518 return $ob->[ OB_CONTENTS ];
167             } #EndOB
168              
169             sub ReadAndEmptyOB {
170 0     0 0 0 my $self = shift;
171 0         0 my $s;
172              
173 0         0 $s = $self->{OutputBuffers}->[ OB_TOP ]->[ OB_CONTENTS ];
174 0         0 $self->{OutputBuffers}->[ OB_TOP ]->[ OB_CONTENTS ] = "";
175 0         0 return $s;
176             } #ReadAndEmptyOB()
177              
178             # Accessors
179              
180             sub GetStartLineOfOB {
181 212     212 0 323 my $self = shift;
182 212         382 return $self->{OutputBuffers}->[ OB_TOP ]->[ OB_STARTLINE ];
183             }
184              
185             sub GetModeOfOB {
186 767     767 0 1129 my $self = shift;
187 767         1684 return $self->{OutputBuffers}->[ OB_TOP ]->[ OB_MODE ];
188             }
189              
190             # --- String manipulation -----------------------------------------
191              
192             sub _DQuoteString { # wrap $_[0] in double-quotes, escaped properly
193             # Not currently used by PerlPP, but provided for use by scripts.
194             # TODO? inject into the generated script?
195 0     0   0 my $s = shift;
196              
197 0         0 $s =~ s{\\}{\\\\}g;
198 0         0 $s =~ s{"}{\\"}g;
199 0         0 return '"' . $s . '"';
200             } #_DQuoteString
201              
202             sub _QuoteString { # wrap $_[0] in single-quotes, escaped properly
203 359     359   557 my $s = shift;
204              
205 359         707 $s =~ s{\\}{\\\\}g;
206 359         579 $s =~ s{'}{\\'}g;
207 359         1339 return "'" . $s . "'";
208             } #_QuoteString
209              
210             sub PrepareString {
211 353     353 0 585 my $self = shift;
212 353         501 my $s = shift;
213 353         441 my $pref;
214              
215             # Replace -D options. Do this before prefixes so that we don't create
216             # prefix matches. TODO? combine the defs and prefixes into one RE?
217 353 100       952 $s =~ s/$self->{Defs_RE}/$self->{Defs_repl_text}->{$1}/g if $self->{Defs_RE};
218              
219             # Replace prefixes
220 353         479 foreach $pref ( keys %{$self->{Prefixes}} ) {
  353         921  
221 1         150 $s =~ s/(^|\W)\Q$pref\E/$1$self->{Prefixes}->{ $pref }/g;
222             }
223              
224             # Quote it for printing
225 353         695 return _QuoteString( $s );
226             }
227              
228             # --- Script-accessible commands ----------------------------------
229              
230             sub ExecuteCommand {
231 84     84 0 132 my $self = shift;
232 84         120 my $cmd = shift;
233 84         141 my $fn;
234             my $dir;
235              
236 84 100       849 if ( $cmd =~ /^include\s++(?:['"](?[^'"]+)['"]|(?\S+))\s*$/i ) {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    100          
    100          
    50          
237 11     11   6166 $self->ProcessFile( $self->{WorkingDir} . "/" . $+{fn} );
  11         5008  
  11         43608  
  4         73  
238              
239             } elsif ( $cmd =~ /^macro\s++(.*+)$/si ) {
240 8         26 $self->StartOB(); # plain text
241              
242             # Create the execution environment for the macro:
243             # - Run in the script's package. Without `package`, the eval'ed
244             # code runs in Text::PerlPP.
245             # - Make $PSelf available with `our`. Each `eval` gets its own
246             # set of lexical variables, so $PSelf would have to be referred
247             # to with its full package name if we didn't have the `our`.
248             # TODO add a pound line to this eval based on the current line number
249              
250             # NOTE: `package NAME BLOCK` syntax was added in Perl 5.14.0, May 2011.
251 8         30 my $code = qq{ ;
252             {
253             package $self->{Package};
254 8         51 our \$@{[PPP_SELF_INSIDE]};
255             $1
256             };
257             };
258              
259 8 50       32 if($self->{Opts}->{DEBUG}) {
260 0         0 (my $c = $code) =~ s/^/#/gm;
261 0         0 emit "Macro code run:\n$c\n"
262             }
263 8         721 eval $code;
264 8         34 my $err = $@; chomp $err;
  8         19  
265 8         25 emit 'print ' . $self->PrepareString( $self->EndOB() ) . ";\n";
266              
267             # Report the error, if any. Under -E, it's a warning.
268 8         51 my $errmsg = "Error: $err\n in immediate " . substr($1, 0, 40) . '...';
269 8 50       28 if($self->{Opts}->{DEBUG}) {
270 0 0       0 warn $errmsg if $err;
271             } else {
272 8 50       29 die $errmsg if $err;
273             }
274              
275             } elsif ( $cmd =~ /^immediate\s++(.*+)$/si ) {
276             # TODO refactor common code between macro and immediate
277              
278             # TODO add a pound line to this eval
279 4         15 my $code = qq{ ;
280             {
281             package $self->{Package};
282 4         23 our \$@{[PPP_SELF_INSIDE]};
283             $1
284             };
285             };
286 4 50       14 if($self->{Opts}->{DEBUG}) {
287 0         0 (my $c = $code) =~ s/^/#/gm;
288 0         0 emit "Immediate code run:\n$c\n"
289             }
290 4         333 eval( $code );
291 4         18 my $err = $@; chomp $err;
  4         7  
292              
293             # Report the error, if any. Under -E, it's a warning.
294 4         21 my $errmsg = "Error: $err\n in immediate " . substr($1, 0, 40) . '...';
295 4 50       13 if($self->{Opts}->{DEBUG}) {
296 0 0       0 warn $errmsg if $err;
297             } else {
298 4 50       15 die $errmsg if $err;
299             }
300              
301             } elsif ( $cmd =~ /^prefix\s++(\S++)\s++(\S++)\s*+$/i ) {
302 1         11 $self->{Prefixes}->{ $1 } = $2;
303              
304             # Definitions
305             } elsif ( $cmd =~ /^define\s++(.*+)$/i ) { # set in %D
306 4         12 my $test = $1; # Otherwise !~ clobbers it.
307 4 50       20 if( $test !~ DEFINE_NAME_IN_CONTEXT_RE ) {
308 0         0 die "Could not understand \"define\" command \"$test\"." .
309             " Maybe an invalid variable name?";
310             }
311 4         32 my $nm = $+{nm};
312 4         19 my $rest = $+{rest};
313              
314             # Set the default value to true if non provided
315 4         17 $rest =~ s/^\s+|\s+$//g; # trim whitespace
316 4 100       12 $rest='true' if not length($rest); # default to true
317              
318 4         15 emit "\$D\{$nm\} = ($rest) ;\n";
319              
320             } elsif ( $cmd =~ /^undef\s++(?\S++)\s*+$/i ) { # clear from %D
321 2         18 my $nm = $+{nm};
322 2 50       13 die "Invalid name \"$nm\" in \"undef\"" if $nm !~ DEFINE_NAME_RE;
323 2         10 emit "\$D\{$nm\} = undef;\n";
324              
325             # Conditionals
326             } elsif ( $cmd =~ /^ifdef\s++(?\S++)\s*+$/i ) { # test in %D
327 3         17 my $nm = $+{nm}; # Otherwise !~ clobbers it.
328 3 50       15 die "Invalid name \"$nm\" in \"ifdef\"" if $nm !~ DEFINE_NAME_RE;
329 3         11 emit "if(defined(\$D\{$nm\})) {\n"; # Don't need exists()
330              
331             } elsif ( $cmd =~ /^ifndef\s++(?\S++)\s*+$/i ) { # test in %D
332 0         0 my $nm = $+{nm}; # Otherwise !~ clobbers it.
333 0 0       0 die "Invalid name \"$nm\" in \"ifdef\"" if $nm !~ DEFINE_NAME_RE;
334 0         0 emit "if(!defined(\$D\{$nm\})) {\n"; # Don't need exists()
335              
336             } elsif ( $cmd =~ /^if\s++(.*+)$/i ) { # :if - General test of %D values
337 15         44 my $test = $1; # $1 =~ doesn't work for me
338 15 50       73 if( $test !~ DEFINE_NAME_IN_CONTEXT_RE ) {
339 0         0 die "Could not understand \"if\" command \"$test\"." .
340             " Maybe an invalid variable name?";
341             }
342 15         137 my $ref="\$D\{$+{nm}\}";
343 15         96 emit "if(exists($ref) && ( $ref $+{rest} ) ) {\n";
344             # Test exists() first so undef maps to false rather than warning.
345              
346             } elsif ( $cmd =~ /^(elsif|elseif|elif)\s++(.*+)$/ ) { # :elsif with condition
347 7         20 my $cmd = $1;
348 7         14 my $test = $2;
349 7 50       32 if( $test !~ DEFINE_NAME_IN_CONTEXT_RE ) {
350 0         0 die "Could not understand \"$cmd\" command \"$test\"." .
351             " Maybe an invalid variable name?";
352             }
353 7         44 my $ref="\$D\{$+{nm}\}";
354 7         43 emit "} elsif(exists($ref) && ( $ref $+{rest} ) ) {\n";
355             # Test exists() first so undef maps to false rather than warning.
356              
357             } elsif ( $cmd =~ /^else\s*+$/i ) {
358 18         41 emit "} else {\n";
359              
360             } elsif ( $cmd =~ /^endif\s*+$/i ) { # end of a block
361 18         43 emit "}\n";
362              
363             } else {
364 0         0 die "Unknown PerlPP command: ${cmd}";
365             }
366             } #ExecuteCommand()
367              
368             sub _GetStatusReport {
369             # Get a human-readable result string, given $? and $! from a qx//.
370             # Modified from http://perldoc.perl.org/functions/system.html
371 6     6   18752 my $retval;
372 6         127 my $status = shift;
373 6   50     213 my $errmsg = shift || '';
374              
375 6 50       131 if ($status == -1) {
    50          
    100          
376 0         0 $retval = "failed to execute: $errmsg";
377             } elsif ($status & 127) {
378 0 0       0 $retval = sprintf("process died with signal %d, %s coredump",
379             ($status & 127), ($status & 128) ? 'with' : 'without');
380             } elsif($status != 0) {
381 2         28 $retval = sprintf("process exited with value %d", $status >> 8);
382             }
383 6         59 return $retval;
384             } # _GetStatusReport()
385              
386             sub ShellOut { # Run an external command
387 6     6 0 11 my $self = shift;
388 6         16 my $cmd = shift;
389 6         40 $cmd =~ s/^\s+|\s+$//g; # trim leading/trailing whitespace
390 6 50       21 die "No command provided to @{[TAG_OPEN]}!...@{[TAG_CLOSE]}" unless $cmd;
  0         0  
  0         0  
391 6         15 $cmd = _QuoteString $cmd; # note: cmd is now wrapped in ''
392              
393 6 100       20 my $error_response = ($self->{Opts}->{KEEP_GOING} ? 'warn' : 'die'); # How we will handle errors
394              
395 6         26 my $block =
396             qq{do {
397             my \$output = qx${cmd};
398             my \$status = Text::PerlPP::_GetStatusReport(\$?, \$!);
399             if(\$status) {
400             $error_response("perlpp: command '" . ${cmd} . "' failed: \${status}; invoked");
401             } else {
402             print \$output;
403             }
404             };
405             };
406 6         58 $block =~ s/^\t{2}//gm; # de-indent
407 6         26 emit $block;
408             } #ShellOut()
409              
410             # --- Delimiter processing ----------------------------------------
411              
412             # Print a "#line" line. Filename must not contain /"/.
413             sub emit_pound_line {
414 470     470 0 705 my $self = shift;
415 470         985 my ($fname, $lineno) = @_;
416 470         1039 $lineno = 0+$lineno;
417 470         939 $fname = '' . $fname;
418              
419 470 100       819 emit "\n#@{[ $self->{Opts}->{DEBUG_LINENOS} ? '#sync' : 'line' ]} $lineno \"$fname\"\n";
  470         2268  
420             } #emit_pound_line()
421              
422             sub OnOpening {
423 213     213 0 323 my $self = shift;
424             # takes the rest of the string, beginning right after the ? of the tag_open
425             # returns (withinTag, string still to be processed)
426              
427 213         531 my ($after, $lineno) = @_;
428              
429 213         327 my $plain;
430             my $plainMode;
431 213         293 my $insetMode = OBMODE_CODE;
432              
433 213         412 $plainMode = $self->GetModeOfOB();
434 213         402 $plain = $self->EndOB(); # plain text already seen
435              
436 213 100 66     732 if ( $after =~ /^"/ && $plainMode == OBMODE_CAPTURE ) {
437 8         33 emit $self->PrepareString( $plain );
438             # we are still buffering the inset contents,
439             # so we do not have to start it again
440             } else {
441              
442 205 100       987 if ( $after =~ /^=/ ) {
    100          
    100          
    100          
    100          
    100          
    50          
443 49         92 $insetMode = OBMODE_ECHO;
444              
445             } elsif ( $after =~ /^:/ ) {
446 84         135 $insetMode = OBMODE_COMMAND;
447              
448             } elsif ( $after =~ /^#/ ) {
449 11         18 $insetMode = OBMODE_COMMENT;
450              
451             } elsif ( $after =~ m{^\/} ) {
452 1         2 $plain .= "\n"; # newline after what we've already seen
453             # OBMODE_CODE
454              
455             } elsif ( $after =~ /^(?:\s|$)/ ) {
456             # OBMODE_CODE
457              
458             } elsif ( $after =~ /^!/ ) {
459 6         58 $insetMode = OBMODE_SYSTEM;
460              
461             } elsif ( $after =~ /^"/ ) {
462 0         0 die "Unexpected end of capturing";
463              
464             } else {
465 1         4 $self->StartOB( $plainMode, $lineno ); # skip non-PerlPP insets
466 1         6 emit $plain . TAG_OPEN;
467 1         5 return ( false, $after );
468             # Here $after is the entire rest of the input, so it is as if
469             # the TAG_OPEN had never occurred.
470             }
471              
472 204 100       395 if ( $plainMode == OBMODE_CAPTURE ) {
473 2         19 emit $self->PrepareString( $plain ) .
474             ' . do { $' . PPP_SELF_INSIDE . '->StartOB(); ';
475 2         7 $self->StartOB( $plainMode, $lineno ); # wrap the inset in a capturing mode
476             } else {
477 202         458 emit "print " . $self->PrepareString( $plain ) . ";\n";
478             }
479              
480 204         452 $self->StartOB( $insetMode, $lineno ); # contents of the inset
481             }
482 212 50       431 return ( true, "" ) unless $after;
483 212         801 return ( true, substr( $after, 1 ) );
484             } #OnOpening()
485              
486             sub OnClosing {
487 212     212 0 354 my $self = shift;
488 212   50     411 my $end_lineno = shift // 0;
489 212   50     391 my $fname = shift // "";
490              
491 212         326 my $nextMode = OBMODE_PLAIN;
492              
493 212         359 my $start_lineno = $self->GetStartLineOfOB();
494 212         394 my $insetMode = $self->GetModeOfOB();
495 212         357 my $inside = $self->EndOB(); # contents of the inset
496              
497 212 100       506 if ( $inside =~ /"$/ ) {
498 8         41 $self->StartOB( $insetMode, $end_lineno ); # restore contents of the inset
499 8         35 emit substr( $inside, 0, -1 );
500 8         23 $nextMode = OBMODE_CAPTURE;
501              
502             } else {
503 204 100       609 if ( $insetMode == OBMODE_ECHO ) {
    100          
    100          
    100          
    50          
504 49         127 $self->emit_pound_line( $fname, $start_lineno );
505 49         180 emit "print ${inside};\n"; # don't wrap in (), trailing semicolon
506 49         98 $self->emit_pound_line( $fname, $end_lineno );
507              
508             } elsif ( $insetMode == OBMODE_COMMAND ) {
509 84         210 $self->ExecuteCommand( $inside );
510              
511             } elsif ( $insetMode == OBMODE_COMMENT ) {
512             # Ignore the contents - no operation. Do resync, though.
513 11         39 $self->emit_pound_line( $fname, $end_lineno );
514              
515             } elsif ( $insetMode == OBMODE_CODE ) {
516 54         144 $self->emit_pound_line( $fname, $start_lineno );
517 54         170 emit "$inside\n"; # \n so you can put comments in your perl code
518 54         110 $self->emit_pound_line( $fname, $end_lineno );
519              
520             } elsif ( $insetMode == OBMODE_SYSTEM ) {
521 6         19 $self->emit_pound_line( $fname, $start_lineno );
522 6         24 $self->ShellOut( $inside );
523 6         18 $self->emit_pound_line( $fname, $end_lineno );
524              
525             } else {
526 0         0 emit $inside;
527              
528             }
529              
530 204 100       456 if ( $self->GetModeOfOB() == OBMODE_CAPTURE ) { # if the inset is wrapped
531 2         9 emit $self->EndOB() .
532             '$' . PPP_SELF_INSIDE . '->EndOB(); } . '; # end of do { .... } statement
533 2         5 $nextMode = OBMODE_CAPTURE; # back to capturing
534             }
535             }
536 212         465 $self->StartOB( $nextMode ); # plain text
537             } #OnClosing()
538              
539             # --- File processing ---------------------------------------------
540              
541             # Count newlines in a string
542             sub _num_newlines {
543 425     425   1106 return scalar ( () = $_[0] =~ /\n/g );
544             } #_num_newlines()
545              
546             # Process the contents of a single file
547             sub RunPerlPPOnFileContents {
548 126     126 0 202 my $self = shift;
549 126         178 my $contents_ref = shift; # reference
550 126         207 my $fname = shift;
551 126         302 $self->emit_pound_line( $fname, 1 );
552              
553 126         281 my $withinTag = false;
554 126         191 my $lastPrep;
555              
556 126         191 my $lineno=1; # approximated by the number of newlines we see
557              
558 126         170 $lastPrep = $#{$self->{Preprocessors}};
  126         258  
559 126         327 $self->StartOB(); # plain text
560              
561             # TODO change this to a simple string searching (to speedup)
562             OPENING:
563 562 100       992 if ( $withinTag ) {
564 224 100       999 if ( $$contents_ref =~ CLOSING_RE ) {
565 212         498 emit $1;
566 212         355 $lineno += _num_newlines($1);
567 212         474 $$contents_ref = $2;
568 212         524 $self->OnClosing( $lineno, $fname );
569             # that could have been a command, which added new preprocessors
570             # but we don't want to run previously executed preps the second time
571 212         292 while ( $lastPrep < $#{$self->{Preprocessors}} ) {
  212         525  
572 0         0 $lastPrep++;
573 0         0 &{$self->{Preprocessors}->[ $lastPrep ]}( $contents_ref );
  0         0  
574             }
575 212         379 $withinTag = false;
576 212         1116 goto OPENING;
577             };
578             } else { # look for the next opening tag. $1 is before; $2 is after.
579 338 100       1556 if ( $$contents_ref =~ OPENING_RE ) {
580 213         519 emit $1;
581 213         402 $lineno += _num_newlines($1);
582 213         489 ( $withinTag, $$contents_ref ) = $self->OnOpening( $2, $lineno );
583 213 100       492 if ( $withinTag ) {
584 212         1412 goto OPENING; # $$contents_ref is the rest of the string
585             }
586             }
587             }
588              
589 138 100       293 if ( $withinTag ) { # closer is missing at the end of the file.
590              
591 12         27 $$contents_ref .= ' ';
592             # This prevents there from being a double-quote before the
593             # closer, which perlpp would read as the beginning of a capture.
594              
595 12 100       27 $$contents_ref .= "\n;\n" if ( $self->GetModeOfOB() == OBMODE_CODE );
596             # Add semicolons only to plain Perl statements. Don't add them
597             # to external commands, which may not be able to handle them.
598             # In general, the code that is unclosed has to be the end of a
599             # statement. However, we do not add semicolons for commands
600             # because some commands take perl code (`macro`), and some take
601             # non-code (`include`).
602             #
603             # If you want to include a file that ends with a partial
604             # statement, it's up to you to add the closer manually. (You
605             # can still suppress the trailing newline using an unclosed
606             # comment.)
607              
608             # Add the closer
609 12         24 $$contents_ref .= TAG_CLOSE;
610              
611 12         32 goto OPENING;
612             }
613              
614 126 50       269 if ( $self->GetModeOfOB() == OBMODE_CAPTURE ) {
615 0         0 die "Unfinished capturing";
616             }
617              
618 126         323 emit $$contents_ref; # tail of a plain text
619              
620             # getting the rest of the plain text
621 126         296 emit "print " . $self->PrepareString( $self->EndOB() ) . ";\n";
622             } #RunPerlPPOnFileContents()
623              
624             # Process a single file
625             sub ProcessFile {
626 126     126 0 261 my $self = shift;
627 126         218 my $fname = shift; # "" or other false value => STDIN
628 126         239 my $wdir = "";
629 126         199 my $contents; # real string of $fname's contents
630             my $proc;
631              
632             # read the whole file
633 126         178 $contents = do {
634 126         211 my $f;
635 126         478 local $/ = undef;
636              
637 126 100       262 if ( $fname ) {
638 23 50       898 open( $f, "<", $fname ) or die "Cannot open '${fname}'";
639 23 50       202 if ( $fname =~ m{^(.*)[\\\/][^\\\/]+$} ) {
640 23         62 $wdir = $self->{WorkingDir};
641 23         80 $self->{WorkingDir} = $1;
642             }
643             } else {
644 103         301 $f = *STDIN;
645             }
646              
647 126         1561 <$f>; # the file will be closed automatically on the scope end
648             };
649              
650 126         244 for $proc ( @{$self->{Preprocessors}} ) {
  126         343  
651 0         0 &$proc( \$contents ); # $contents is modified
652             }
653              
654 126         281 $fname =~ s{"}{-}g; # Prep $fname for #line use -
655             #My impression is #line chokes on embedded "
656 126   100     710 $self->RunPerlPPOnFileContents( \$contents, $fname || '');
657              
658 126 100       354 if ( $wdir ) {
659 23         166 $self->{WorkingDir} = $wdir;
660             }
661             } #ProcessFile()
662              
663             sub Include { # As ProcessFile(), but for use within :macro
664 7     7 0 13 my $self = shift;
665 7         20 emit "print " . $self->PrepareString( $self->EndOB() ) . ";\n";
666             # Close the OB opened by :macro
667 7         25 $self->ProcessFile(shift);
668 7         15 $self->StartOB(); # re-open a plain-text OB
669             } #Include
670              
671             sub FinalizeResult {
672 92     92 0 153 my $self = shift;
673 92         141 my $contents_ref = shift; # reference
674              
675 92         136 for my $proc ( @{$self->{Postprocessors}} ) {
  92         466  
676 0         0 &$proc( $contents_ref );
677             }
678 92         164 return $contents_ref;
679             } #FinalizeResult()
680              
681             sub OutputResult {
682 92     92 0 180 my $self = shift;
683 92         140 my $contents_ref = shift; # reference
684 92         189 my $fname = shift; # "" or other false value => STDOUT
685              
686 92         325 $self->FinalizeResult( $contents_ref );
687              
688 92         146 my $out_fh;
689 92 50       187 if ( $fname ) {
690 0 0       0 open( $out_fh, ">", $fname ) or die $!;
691             } else {
692 92 50       2440 open( $out_fh, ">&STDOUT" ) or die $!;
693             }
694 92         1668 print $out_fh $$contents_ref;
695 92 50       4043 close( $out_fh ) or die $!;
696             } #OutputResult()
697              
698             # === Command line parsing ================================================
699              
700             my %CMDLINE_OPTS = (
701             # hash from internal name to array reference of
702             # [getopt-name, getopt-options, optional default-value]
703             # They are listed in alphabetical order by option name,
704             # lowercase before upper, although the code does not require that order.
705              
706             DEBUG => ['d','|E|debug', false],
707             DEBUG_LINENOS => ['Elines','',false], # if true, don't add #line markers
708             DEFS => ['D','|define:s%'], # In %D, and text substitution
709             EVAL => ['e','|eval=s', ''],
710             # -h and --help reserved
711             # INPUT_FILENAME assigned by _parse_command_line()
712             KEEP_GOING => ['k','|keep-going',false],
713             # --man reserved
714             OUTPUT_FILENAME => ['o','|output=s', ""],
715             SETS => ['s','|set:s%'], # Extra data in %S, without text substitution
716             # --usage reserved
717             PRINT_VERSION => ['v','|version+'],
718              
719             # Special-case for testing --- don't exit on --help &c.
720             NOEXIT_ON_HELP => ['z_noexit_on_help'],
721              
722             # -? reserved
723             );
724              
725             sub _parse_command_line {
726             # Takes reference to arg list, and reference to hash to populate.
727             # Fills in that hash with the values from the command line, keyed
728             # by the keys in %CMDLINE_OPTS.
729              
730 114     114   294 my ($lrArgs, $hrOptsOut) = @_;
731              
732             # Easier syntax for checking whether optional args were provided.
733             # Syntax thanks to http://www.perlmonks.org/?node_id=696592
734 114     340   693 local *have = sub { return exists($hrOptsOut->{ $_[0] }); };
  340         979  
735              
736 114         546 Getopt::Long::Configure 'gnu_getopt';
737              
738             # Set defaults so we don't have to test them with exists().
739             %$hrOptsOut = ( # map getopt option name to default value
740 570         1541 map { $CMDLINE_OPTS{ $_ }->[0] => $CMDLINE_OPTS{ $_ }[2] }
741 114         4490 grep { (scalar @{$CMDLINE_OPTS{ $_ }})==3 }
  1026         1265  
  1026         2216  
742             keys %CMDLINE_OPTS
743             );
744              
745 114 50       728 my %docs = (-input => (($0 =~ /\bperlpp$/) ? $0 : __FILE__));
746             # The main POD is in the perlpp script at the present time.
747             # However, if we're not running from perlpp, we show the
748             # small POD below, which links to `perldoc perlpp`.
749              
750             # Get options
751             my $ok =
752             GetOptionsFromArray($lrArgs, $hrOptsOut, # destination hash
753             'usage|?', 'h|help', 'man', # options we handle here
754 114   100     366 map { $_->[0] . ($_->[1]//'') } values %CMDLINE_OPTS, # options strs
  1026         3026  
755             );
756              
757             # --- TODO clean up the following.
758             my $noexit_on_help =
759 114   100     96286 $hrOptsOut->{ $CMDLINE_OPTS{NOEXIT_ON_HELP}->[0] } // false;
760              
761 114 100       328 if($noexit_on_help) { # Report help during testing
762             # unknown opt --- error out. false => processing should terminate.
763 2 50       8 pod2usage(-verbose => 0, -exitval => 'NOEXIT', %docs), return false unless $ok;
764              
765             # Help, if requested
766 2 50       6 pod2usage(-verbose => 0, -exitval => 'NOEXIT', %docs), return false if have('usage');
767 2 50       5 pod2usage(-verbose => 1, -exitval => 'NOEXIT', %docs), return false if have('h');
768 0 0       0 pod2usage(-verbose => 2, -exitval => 'NOEXIT', %docs), return false if have('man');
769              
770             } else { # Normal usage
771             # unknown opt --- error out
772 112 50       274 pod2usage(-verbose => 0, -exitval => EXIT_PARAM_ERR, %docs) unless $ok;
773              
774             # Help, if requested
775 112 50       340 pod2usage(-verbose => 0, -exitval => EXIT_PROC_ERR, %docs) if have('usage');
776 112 50       247 pod2usage(-verbose => 1, -exitval => EXIT_PROC_ERR, %docs) if have('h');
777 112 50       233 pod2usage(-verbose => 2, -exitval => EXIT_PROC_ERR, %docs) if have('man');
778             }
779             # ---
780              
781             # Map the option names from GetOptions back to the internal names we use,
782             # e.g., $hrOptsOut->{EVAL} from $hrOptsOut->{e}.
783 112         413 my %revmap = map { $CMDLINE_OPTS{$_}->[0] => $_ } keys %CMDLINE_OPTS;
  1008         1999  
784 112         461 for my $optname (keys %$hrOptsOut) {
785 596         1461 $hrOptsOut->{ $revmap{$optname} } = $hrOptsOut->{ $optname };
786             }
787              
788             # Check the names of any -D flags
789 112         215 for my $k (keys %{$hrOptsOut->{DEFS}}) {
  112         347  
790 32 50       182 die "Invalid -D name \"$k\"" if $k !~ DEFINE_NAME_RE;
791             }
792              
793             # Process other arguments. TODO? support multiple input filenames?
794 112   100     482 $hrOptsOut->{INPUT_FILENAME} = $lrArgs->[0] // "";
795              
796 112         811 return true; # Go ahead and run
797             } #_parse_command_line()
798              
799             # === Main ================================================================
800              
801             sub Main {
802 114 50   114 0 155641 my $self = shift or die("Please use Text::PerlPP->new()->Main");
803 114   50     336 my $lrArgv = shift // [];
804              
805 114 100       299 unless(_parse_command_line( $lrArgv, $self->{Opts} )) {
806 2         70368 return EXIT_OK; # TODO report param err vs. proc err?
807             }
808              
809 112 100       954 if($self->{Opts}->{PRINT_VERSION}) { # print version, raw and dotted
810 2         13 $Text::PerlPP::VERSION =~ m<^([^\.]+)\.(\d{3})(_?)(\d{3})>;
811 2 50       123 printf "PerlPP version %d.%d.%d ($VERSION)%s\n", $1, $2, $4,
812             ($3 ? ' (dev)' : '');
813 2 50       15 if($self->{Opts}->{PRINT_VERSION} > 1) {
814 0         0 print "Script: $0\nText::PerlPP: $INC{'Text/PerlPP.pm'}\n";
815             }
816 2         11 return EXIT_OK;
817             }
818              
819             # Save
820 110         287 push @Instances, $self;
821              
822 110         245 $self->{Package} = $self->{Opts}->{INPUT_FILENAME};
823 110         319 $self->{Package} =~ s/^.*?([a-z_][a-z_0-9.]*).pl?$/$1/i;
824 110         208 $self->{Package} =~ s/[^a-z0-9_]/_/gi;
825             # Not the whole name yet, so can start with a number.
826 110         411 $self->{Package} = "PPP_$self->{Package}$#Instances";
827              
828             # Make $self accessible from inside the package.
829             # This has to happen first so that :macro or :immediate blocks in the
830             # script can access it while the input is being parsed.
831             {
832 11     11   108 no strict 'refs';
  11         36  
  11         8732  
  110         205  
833 110         180 ${ "$self->{Package}::" . PPP_SELF_INSIDE } = $self;
  110         861  
834             }
835              
836             # --- Preamble -----------
837              
838 110         387 $self->StartOB(); # Output from here on will be included in the generated script
839              
840             # Help the user know where to look
841 110 100       340 say "#line 1 \"\"" if($self->{Opts}->{DEBUG_LINENOS});
842 110         445 $self->emit_pound_line( '', 1 );
843              
844             # Open the package
845 110         453 emit "package $self->{Package};\nuse 5.010001;\nuse strict;\nuse warnings;\n";
846 110         333 emit "use constant { true => !!1, false => !!0 };\n";
847 110         433 emit 'our $' . PPP_SELF_INSIDE . ";\n"; # Lexical alias for $self
848              
849             # --- Definitions --------
850              
851             # Transfer parameters from the command line (-D) to the processed file,
852             # as textual representations of expressions.
853             # The parameters are in %D at runtime.
854 110         257 emit "my %D = (\n";
855 110         160 for my $defname (keys %{$self->{Opts}->{DEFS}}) {
  110         406  
856 32   50     55 my $val = ${$self->{Opts}->{DEFS}}{$defname} // 'true';
  32         112  
857             # just in case it's undef. "true" is the constant in this context
858 32 100       110 $val = 'true' if $val eq '';
859             # "-D foo" (without a value) sets it to _true_ so
860             # "if($D{foo})" will work. Getopt::Long gives us '' as the
861             # value in that situation.
862 32         92 emit " $defname => $val,\n";
863             }
864 110         253 emit ");\n";
865              
866             # Save a copy for use at generation time
867 110         249 %{$self->{Defs}} = map { my $v = eval(${$self->{Opts}->{DEFS}}{$_});
  32         94  
  32         1257  
868 32 50       138 warn "Could not evaluate -D \"$_\": $@" if $@;
869 32   100     150 $_ => ($v // true)
870             }
871 110         167 keys %{$self->{Opts}->{DEFS}};
  110         261  
872              
873             # Set up regex for text substitution of Defs.
874             # Modified from http://www.perlmonks.org/?node_id=989740 by
875             # AnomalousMonk, http://www.perlmonks.org/?node_id=634253
876 110 100       182 if(%{$self->{Opts}->{DEFS}}) {
  110         311  
877             my $rx_search =
878 29         63 '\b(' . (join '|', map quotemeta, keys %{$self->{Opts}->{DEFS}}) . ')\b';
  29         176  
879 29         380 $self->{Defs_RE} = qr{$rx_search};
880              
881             # Save the replacement values. If a value cannot be evaluated,
882             # use the name so the replacement will not change the text.
883 29         100 %{$self->{Defs_repl_text}} =
884 32         44 map { my $v = eval(${$self->{Opts}->{DEFS}}{$_});
  32         925  
885 32 100 66     297 ($@ || !defined($v)) ? ($_ => $_) : ($_ => ('' . $v))
886             }
887 29         51 keys %{$self->{Opts}->{DEFS}};
  29         82  
888             }
889              
890             # Now do SETS: -s or --set, into %S by analogy with -D and %D.
891              
892             # Save a copy for use at generation time
893 110         210 %{$self->{Sets}} = map { my $v = eval(${$self->{Opts}->{SETS}}{$_});
  5         10  
  5         265  
894 5 50       24 warn "Could not evaluate -s \"$_\": $@" if $@;
895 5   50     24 $_ => ($v // true)
896             }
897 110         200 keys %{$self->{Opts}->{SETS}};
  110         325  
898              
899             # Make the copy for runtime
900 110         269 emit "my %S = (\n";
901 110         150 for my $defname (keys %{$self->{Opts}->{SETS}}) {
  110         257  
902 5         7 my $val = ${$self->{Opts}->{SETS}}{$defname};
  5         15  
903 5 50       18 if(!defined($val)) {
904             }
905 5 50       21 $val = 'true' if $val eq '';
906             # "-s foo" (without a value) sets it to _true_ so
907             # "if($S{foo})" will work. Getopt::Long gives us '' as the
908             # value in that situation.
909 5         21 emit " $defname => $val,\n";
910             }
911 110         242 emit ");\n";
912              
913             # --- User input ---------
914              
915             # Initial code from the command line, if any
916 110 100       262 if($self->{Opts}->{EVAL}) {
917 5         15 $self->emit_pound_line( '<-e>', 1 );
918 5         16 emit $self->{Opts}->{EVAL}, "\n";
919             }
920              
921             # The input file
922 110         399 $self->ProcessFile( $self->{Opts}->{INPUT_FILENAME} );
923              
924 110         268 my $script = $self->EndOB(); # The generated Perl script
925              
926             # --- Run it -------------
927 110 100       356 if ( $self->{Opts}->{DEBUG} ) {
928 6         62 print $script;
929              
930             } else {
931 104         344 $self->StartOB(); # Start collecting the output of the Perl script
932 104         169 my $result; # To save any errors from the eval
933              
934             # TODO hide %Defs and others of our variables we don't want
935             # $script to access.
936 104         8577 eval( $script ); $result=$@;
  104         29710  
937              
938 104 100       321 if($result) { # Report errors to console and shell
939 12         675 print STDERR $result;
940 12         93 return EXIT_PROC_ERR;
941             } else { # Save successful output
942 92         352 $self->OutputResult( \($self->EndOB()), $self->{Opts}->{OUTPUT_FILENAME} );
943             }
944             }
945 98         572 return EXIT_OK;
946             } #Main()
947              
948             sub new {
949 113     113 0 200125 my $class = shift;
950 113         383 return bless _make_instance(), $class;
951             }
952              
953             1;
954             __END__