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.600001';
10              
11 11     11   680656 use 5.010001;
  11         114  
12 11     11   48 use strict;
  11         18  
  11         236  
13 11     11   51 use warnings;
  11         30  
  11         406  
14              
15 11     11   6311 use Getopt::Long 2.5 qw(GetOptionsFromArray);
  11         116062  
  11         259  
16 11     11   6414 use Pod::Usage;
  11         444856  
  11         1325  
17              
18             # === Constants ===========================================================
19              
20 11     11   93 use constant true => !!1;
  11         21  
  11         726  
21 11     11   57 use constant false => !!0;
  11         19  
  11         426  
22              
23 11     11   52 use constant DEBUG => false;
  11         19  
  11         439  
24              
25             # Shell exit codes
26 11     11   53 use constant EXIT_OK => 0; # success
  11         22  
  11         376  
27 11     11   59 use constant EXIT_PROC_ERR => 1; # error during processing
  11         20  
  11         363  
28 11     11   48 use constant EXIT_PARAM_ERR => 2; # couldn't understand the command line
  11         18  
  11         401  
29              
30             # Constants for the parser
31 11     11   51 use constant TAG_OPEN => '<' . '?'; # literal < ? and ? > shouldn't
  11         17  
  11         405  
32 11     11   48 use constant TAG_CLOSE => '?' . '>'; # appear in this file.
  11         20  
  11         567  
33 11     11   53 use constant OPENING_RE => qr/^(.*?)\Q${\(TAG_OPEN)}\E(.*)$/s; # /s states for single-line mode
  11         21  
  11         16  
  11         897  
34 11     11   64 use constant CLOSING_RE => qr/^(.*?)\Q${\(TAG_CLOSE)}\E(.*)$/s;
  11         17  
  11         18  
  11         2247  
35              
36 11         2070 use constant DEFINE_NAME_RE =>
37 11     11   94 qr/^(?[[:alpha:]][[:alnum:]_]*|[[:alpha:]_][[:alnum:]_]+)$/i;
  11         19  
38             # Valid names for -D. TODO expand this to Unicode.
39             # Bare underscore isn't permitted because it's special in perl.
40 11         453 use constant DEFINE_NAME_IN_CONTEXT_RE =>
41 11     11   65 qr/^(?[[:alpha:]][[:alnum:]_]*|[[:alpha:]_][[:alnum:]_]+)\s*+(?.*+)$/i;
  11         21  
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   51 use constant OBMODE_PLAIN => 0; # literal text, not in tag_open/tag_close
  11         18  
  11         364  
46 11     11   47 use constant OBMODE_CAPTURE => 1; # same as OBMODE_PLAIN but with capturing
  11         16  
  11         415  
47 11     11   51 use constant OBMODE_CODE => 2; # perl code
  11         18  
  11         374  
48 11     11   49 use constant OBMODE_ECHO => 3;
  11         16  
  11         363  
49 11     11   207 use constant OBMODE_COMMAND => 4;
  11         29  
  11         427  
50 11     11   52 use constant OBMODE_COMMENT => 5;
  11         16  
  11         403  
51 11     11   51 use constant OBMODE_SYSTEM => 6; # an external command being run
  11         28  
  11         446  
52              
53             # Layout of the output-buffer stack.
54 11     11   52 use constant OB_TOP => 0; # top of the stack is [0]: use [un]shift
  11         30  
  11         466  
55              
56 11     11   55 use constant OB_MODE => 0; # indices of the stack entries
  11         15  
  11         410  
57 11     11   52 use constant OB_CONTENTS => 1;
  11         18  
  11         399  
58 11     11   50 use constant OB_STARTLINE => 2;
  11         15  
  11         438  
59              
60             # What $self is called inside a script package
61 11     11   54 use constant PPP_SELF_INSIDE => 'PSelf';
  11         15  
  11         9786  
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   1145 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 4801 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 1487 my $self = shift;
127              
128 784   100     1601 my $mode = shift // OBMODE_PLAIN;
129 784   100     1566 my $lineno = shift // 1;
130              
131 784 100       877 if ( scalar @{$self->{OutputBuffers}} == 0 ) {
  784         1435  
132 214         392 $| = 1; # flush contents of STDOUT
133 214 50       5150 open( $self->{RootSTDOUT}, ">&STDOUT" ) or die $!; # dup filehandle
134             }
135 784         1094 unshift( @{$self->{OutputBuffers}}, [ $mode, "", $lineno ] );
  784         2039  
136 784         1994 close( STDOUT ); # must be closed before redirecting it to a variable
137 784 50   1   4114 open( STDOUT, ">>", \($self->{OutputBuffers}->[ OB_TOP ]->[ OB_CONTENTS ]) ) or die $!;
  1         6  
  1         1  
  1         15  
138 784         1963 $| = 1; # do not use output buffering
139              
140             printf STDERR "Opened %s buffer %d\n", $OBModeNames[$mode],
141 784         1478 scalar @{$self->{OutputBuffers}} if DEBUG;
142             } #StartOB()
143              
144             sub EndOB {
145 772     772 0 1038 my $self = shift;
146 772         811 my $ob;
147              
148 772         929 $ob = shift( @{$self->{OutputBuffers}} );
  772         1070  
149 772         1191 close( STDOUT );
150 772 100       832 if ( scalar @{$self->{OutputBuffers}} == 0 ) {
  772         1346  
151 202 50       3897 open( STDOUT, ">&", $self->{RootSTDOUT} ) or die $!; # dup filehandle
152 202         664 $| = 0; # return output buffering to the default state
153             } else {
154 570 50       2940 open( STDOUT, ">>", \($self->{OutputBuffers}->[ OB_TOP ]->[ OB_CONTENTS ]) )
155             or die $!;
156             }
157              
158 772         1013 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         1914 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 246 my $self = shift;
182 212         310 return $self->{OutputBuffers}->[ OB_TOP ]->[ OB_STARTLINE ];
183             }
184              
185             sub GetModeOfOB {
186 767     767 0 1013 my $self = shift;
187 767         1304 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   509 my $s = shift;
204              
205 359         604 $s =~ s{\\}{\\\\}g;
206 359         493 $s =~ s{'}{\\'}g;
207 359         1123 return "'" . $s . "'";
208             } #_QuoteString
209              
210             sub PrepareString {
211 353     353 0 471 my $self = shift;
212 353         430 my $s = shift;
213 353         397 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       927 $s =~ s/$self->{Defs_RE}/$self->{Defs_repl_text}->{$1}/g if $self->{Defs_RE};
218              
219             # Replace prefixes
220 353         394 foreach $pref ( keys %{$self->{Prefixes}} ) {
  353         737  
221 1         56 $s =~ s/(^|\W)\Q$pref\E/$1$self->{Prefixes}->{ $pref }/g;
222             }
223              
224             # Quote it for printing
225 353         572 return _QuoteString( $s );
226             }
227              
228             # --- Script-accessible commands ----------------------------------
229              
230             sub ExecuteCommand {
231 84     84 0 103 my $self = shift;
232 84         109 my $cmd = shift;
233 84         113 my $fn;
234             my $dir;
235              
236 84 100       653 if ( $cmd =~ /^include\s++(?:['"](?[^'"]+)['"]|(?\S+))\s*$/i ) {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    100          
    100          
    50          
237 11     11   5043 $self->ProcessFile( $self->{WorkingDir} . "/" . $+{fn} );
  11         3862  
  11         34425  
  4         89  
238              
239             } elsif ( $cmd =~ /^macro\s++(.*+)$/si ) {
240 8         21 $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         26 my $code = qq{ ;
252             {
253             package $self->{Package};
254 8         94 our \$@{[PPP_SELF_INSIDE]};
255             $1
256             };
257             };
258              
259 8 50       28 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         609 eval $code;
264 8         28 my $err = $@; chomp $err;
  8         15  
265 8         67 emit 'print ' . $self->PrepareString( $self->EndOB() ) . ";\n";
266              
267             # Report the error, if any. Under -E, it's a warning.
268 8         40 my $errmsg = "Error: $err\n in immediate " . substr($1, 0, 40) . '...';
269 8 50       25 if($self->{Opts}->{DEBUG}) {
270 0 0       0 warn $errmsg if $err;
271             } else {
272 8 50       26 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         13 my $code = qq{ ;
280             {
281             package $self->{Package};
282 4         17 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         475 eval( $code );
291 4         14 my $err = $@; chomp $err;
  4         7  
292              
293             # Report the error, if any. Under -E, it's a warning.
294 4         19 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       13 die $errmsg if $err;
299             }
300              
301             } elsif ( $cmd =~ /^prefix\s++(\S++)\s++(\S++)\s*+$/i ) {
302 1         9 $self->{Prefixes}->{ $1 } = $2;
303              
304             # Definitions
305             } elsif ( $cmd =~ /^define\s++(.*+)$/i ) { # set in %D
306 4         17 my $test = $1; # Otherwise !~ clobbers it.
307 4 50       17 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         28 my $nm = $+{nm};
312 4         15 my $rest = $+{rest};
313              
314             # Set the default value to true if non provided
315 4         13 $rest =~ s/^\s+|\s+$//g; # trim whitespace
316 4 100       10 $rest='true' if not length($rest); # default to true
317              
318 4         12 emit "\$D\{$nm\} = ($rest) ;\n";
319              
320             } elsif ( $cmd =~ /^undef\s++(?\S++)\s*+$/i ) { # clear from %D
321 2         14 my $nm = $+{nm};
322 2 50       11 die "Invalid name \"$nm\" in \"undef\"" if $nm !~ DEFINE_NAME_RE;
323 2         9 emit "\$D\{$nm\} = undef;\n";
324              
325             # Conditionals
326             } elsif ( $cmd =~ /^ifdef\s++(?\S++)\s*+$/i ) { # test in %D
327 3         15 my $nm = $+{nm}; # Otherwise !~ clobbers it.
328 3 50       13 die "Invalid name \"$nm\" in \"ifdef\"" if $nm !~ DEFINE_NAME_RE;
329 3         9 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         31 my $test = $1; # $1 =~ doesn't work for me
338 15 50       55 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         91 my $ref="\$D\{$+{nm}\}";
343 15         71 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         15 my $cmd = $1;
348 7         9 my $test = $2;
349 7 50       24 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         30 my $ref="\$D\{$+{nm}\}";
354 7         35 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         29 emit "} else {\n";
359              
360             } elsif ( $cmd =~ /^endif\s*+$/i ) { # end of a block
361 18         29 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   18058 my $retval;
372 6         81 my $status = shift;
373 6   50     182 my $errmsg = shift || '';
374              
375 6 50       102 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         24 $retval = sprintf("process exited with value %d", $status >> 8);
382             }
383 6         53 return $retval;
384             } # _GetStatusReport()
385              
386             sub ShellOut { # Run an external command
387 6     6 0 9 my $self = shift;
388 6         15 my $cmd = shift;
389 6         35 $cmd =~ s/^\s+|\s+$//g; # trim leading/trailing whitespace
390 6 50       17 die "No command provided to @{[TAG_OPEN]}!...@{[TAG_CLOSE]}" unless $cmd;
  0         0  
  0         0  
391 6         14 $cmd = _QuoteString $cmd; # note: cmd is now wrapped in ''
392              
393 6 100       23 my $error_response = ($self->{Opts}->{KEEP_GOING} ? 'warn' : 'die'); # How we will handle errors
394              
395 6         24 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         53 $block =~ s/^\t{2}//gm; # de-indent
407 6         14 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 572 my $self = shift;
415 470         789 my ($fname, $lineno) = @_;
416 470         595 $lineno = 0+$lineno;
417 470         739 $fname = '' . $fname;
418              
419 470 100       639 emit "\n#@{[ $self->{Opts}->{DEBUG_LINENOS} ? '#sync' : 'line' ]} $lineno \"$fname\"\n";
  470         1845  
420             } #emit_pound_line()
421              
422             sub OnOpening {
423 213     213 0 266 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         435 my ($after, $lineno) = @_;
428              
429 213         315 my $plain;
430             my $plainMode;
431 213         237 my $insetMode = OBMODE_CODE;
432              
433 213         364 $plainMode = $self->GetModeOfOB();
434 213         337 $plain = $self->EndOB(); # plain text already seen
435              
436 213 100 66     562 if ( $after =~ /^"/ && $plainMode == OBMODE_CAPTURE ) {
437 8         22 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       767 if ( $after =~ /^=/ ) {
    100          
    100          
    100          
    100          
    100          
    50          
443 49         93 $insetMode = OBMODE_ECHO;
444              
445             } elsif ( $after =~ /^:/ ) {
446 84         115 $insetMode = OBMODE_COMMAND;
447              
448             } elsif ( $after =~ /^#/ ) {
449 11         59 $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         13 $insetMode = OBMODE_SYSTEM;
460              
461             } elsif ( $after =~ /^"/ ) {
462 0         0 die "Unexpected end of capturing";
463              
464             } else {
465 1         3 $self->StartOB( $plainMode, $lineno ); # skip non-PerlPP insets
466 1         4 emit $plain . TAG_OPEN;
467 1         4 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       329 if ( $plainMode == OBMODE_CAPTURE ) {
473 2         15 emit $self->PrepareString( $plain ) .
474             ' . do { $' . PPP_SELF_INSIDE . '->StartOB(); ';
475 2         5 $self->StartOB( $plainMode, $lineno ); # wrap the inset in a capturing mode
476             } else {
477 202         370 emit "print " . $self->PrepareString( $plain ) . ";\n";
478             }
479              
480 204         552 $self->StartOB( $insetMode, $lineno ); # contents of the inset
481             }
482 212 50       341 return ( true, "" ) unless $after;
483 212         691 return ( true, substr( $after, 1 ) );
484             } #OnOpening()
485              
486             sub OnClosing {
487 212     212 0 264 my $self = shift;
488 212   50     452 my $end_lineno = shift // 0;
489 212   50     388 my $fname = shift // "";
490              
491 212         235 my $nextMode = OBMODE_PLAIN;
492              
493 212         352 my $start_lineno = $self->GetStartLineOfOB();
494 212         319 my $insetMode = $self->GetModeOfOB();
495 212         294 my $inside = $self->EndOB(); # contents of the inset
496              
497 212 100       429 if ( $inside =~ /"$/ ) {
498 8         32 $self->StartOB( $insetMode, $end_lineno ); # restore contents of the inset
499 8         22 emit substr( $inside, 0, -1 );
500 8         17 $nextMode = OBMODE_CAPTURE;
501              
502             } else {
503 204 100       497 if ( $insetMode == OBMODE_ECHO ) {
    100          
    100          
    100          
    50          
504 49         108 $self->emit_pound_line( $fname, $start_lineno );
505 49         138 emit "print ${inside};\n"; # don't wrap in (), trailing semicolon
506 49         82 $self->emit_pound_line( $fname, $end_lineno );
507              
508             } elsif ( $insetMode == OBMODE_COMMAND ) {
509 84         171 $self->ExecuteCommand( $inside );
510              
511             } elsif ( $insetMode == OBMODE_COMMENT ) {
512             # Ignore the contents - no operation. Do resync, though.
513 11         22 $self->emit_pound_line( $fname, $end_lineno );
514              
515             } elsif ( $insetMode == OBMODE_CODE ) {
516 54         104 $self->emit_pound_line( $fname, $start_lineno );
517 54         137 emit "$inside\n"; # \n so you can put comments in your perl code
518 54         92 $self->emit_pound_line( $fname, $end_lineno );
519              
520             } elsif ( $insetMode == OBMODE_SYSTEM ) {
521 6         16 $self->emit_pound_line( $fname, $start_lineno );
522 6         23 $self->ShellOut( $inside );
523 6         15 $self->emit_pound_line( $fname, $end_lineno );
524              
525             } else {
526 0         0 emit $inside;
527              
528             }
529              
530 204 100       356 if ( $self->GetModeOfOB() == OBMODE_CAPTURE ) { # if the inset is wrapped
531 2         4 emit $self->EndOB() .
532             '$' . PPP_SELF_INSIDE . '->EndOB(); } . '; # end of do { .... } statement
533 2         2 $nextMode = OBMODE_CAPTURE; # back to capturing
534             }
535             }
536 212         358 $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   986 return scalar ( () = $_[0] =~ /\n/g );
544             } #_num_newlines()
545              
546             # Process the contents of a single file
547             sub RunPerlPPOnFileContents {
548 126     126 0 169 my $self = shift;
549 126         158 my $contents_ref = shift; # reference
550 126         173 my $fname = shift;
551 126         249 $self->emit_pound_line( $fname, 1 );
552              
553 126         225 my $withinTag = false;
554 126         144 my $lastPrep;
555              
556 126         158 my $lineno=1; # approximated by the number of newlines we see
557              
558 126         169 $lastPrep = $#{$self->{Preprocessors}};
  126         195  
559 126         260 $self->StartOB(); # plain text
560              
561             # TODO change this to a simple string searching (to speedup)
562             OPENING:
563 562 100       788 if ( $withinTag ) {
564 224 100       808 if ( $$contents_ref =~ CLOSING_RE ) {
565 212         416 emit $1;
566 212         296 $lineno += _num_newlines($1);
567 212         402 $$contents_ref = $2;
568 212         454 $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         312 while ( $lastPrep < $#{$self->{Preprocessors}} ) {
  212         426  
572 0         0 $lastPrep++;
573 0         0 &{$self->{Preprocessors}->[ $lastPrep ]}( $contents_ref );
  0         0  
574             }
575 212         288 $withinTag = false;
576 212         930 goto OPENING;
577             };
578             } else { # look for the next opening tag. $1 is before; $2 is after.
579 338 100       1221 if ( $$contents_ref =~ OPENING_RE ) {
580 213         419 emit $1;
581 213         355 $lineno += _num_newlines($1);
582 213         441 ( $withinTag, $$contents_ref ) = $self->OnOpening( $2, $lineno );
583 213 100       394 if ( $withinTag ) {
584 212         1198 goto OPENING; # $$contents_ref is the rest of the string
585             }
586             }
587             }
588              
589 138 100       223 if ( $withinTag ) { # closer is missing at the end of the file.
590              
591 12         21 $$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       23 $$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         21 $$contents_ref .= TAG_CLOSE;
610              
611 12         27 goto OPENING;
612             }
613              
614 126 50       223 if ( $self->GetModeOfOB() == OBMODE_CAPTURE ) {
615 0         0 die "Unfinished capturing";
616             }
617              
618 126         564 emit $$contents_ref; # tail of a plain text
619              
620             # getting the rest of the plain text
621 126         224 emit "print " . $self->PrepareString( $self->EndOB() ) . ";\n";
622             } #RunPerlPPOnFileContents()
623              
624             # Process a single file
625             sub ProcessFile {
626 126     126 0 181 my $self = shift;
627 126         155 my $fname = shift; # "" or other false value => STDIN
628 126         183 my $wdir = "";
629 126         181 my $contents; # real string of $fname's contents
630             my $proc;
631              
632             # read the whole file
633 126         151 $contents = do {
634 126         155 my $f;
635 126         411 local $/ = undef;
636              
637 126 100       222 if ( $fname ) {
638 23 50       807 open( $f, "<", $fname ) or die "Cannot open '${fname}'";
639 23 50       162 if ( $fname =~ m{^(.*)[\\\/][^\\\/]+$} ) {
640 23         70 $wdir = $self->{WorkingDir};
641 23         73 $self->{WorkingDir} = $1;
642             }
643             } else {
644 103         275 $f = *STDIN;
645             }
646              
647 126         1396 <$f>; # the file will be closed automatically on the scope end
648             };
649              
650 126         200 for $proc ( @{$self->{Preprocessors}} ) {
  126         296  
651 0         0 &$proc( \$contents ); # $contents is modified
652             }
653              
654 126         218 $fname =~ s{"}{-}g; # Prep $fname for #line use -
655             #My impression is #line chokes on embedded "
656 126   100     592 $self->RunPerlPPOnFileContents( \$contents, $fname || '');
657              
658 126 100       299 if ( $wdir ) {
659 23         128 $self->{WorkingDir} = $wdir;
660             }
661             } #ProcessFile()
662              
663             sub Include { # As ProcessFile(), but for use within :macro
664 7     7 0 12 my $self = shift;
665 7         18 emit "print " . $self->PrepareString( $self->EndOB() ) . ";\n";
666             # Close the OB opened by :macro
667 7         165 $self->ProcessFile(shift);
668 7         12 $self->StartOB(); # re-open a plain-text OB
669             } #Include
670              
671             sub FinalizeResult {
672 92     92 0 128 my $self = shift;
673 92         122 my $contents_ref = shift; # reference
674              
675 92         117 for my $proc ( @{$self->{Postprocessors}} ) {
  92         227  
676 0         0 &$proc( $contents_ref );
677             }
678 92         134 return $contents_ref;
679             } #FinalizeResult()
680              
681             sub OutputResult {
682 92     92 0 153 my $self = shift;
683 92         127 my $contents_ref = shift; # reference
684 92         143 my $fname = shift; # "" or other false value => STDOUT
685              
686 92         253 $self->FinalizeResult( $contents_ref );
687              
688 92         106 my $out_fh;
689 92 50       206 if ( $fname ) {
690 0 0       0 open( $out_fh, ">", $fname ) or die $!;
691             } else {
692 92 50       2371 open( $out_fh, ">&STDOUT" ) or die $!;
693             }
694 92         1318 print $out_fh $$contents_ref;
695 92 50       3041 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   235 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   576 local *have = sub { return exists($hrOptsOut->{ $_[0] }); };
  340         760  
735              
736 114         742 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         1302 map { $CMDLINE_OPTS{ $_ }->[0] => $CMDLINE_OPTS{ $_ }[2] }
741 114         3848 grep { (scalar @{$CMDLINE_OPTS{ $_ }})==3 }
  1026         1168  
  1026         1721  
742             keys %CMDLINE_OPTS
743             );
744              
745 114 50       588 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     289 map { $_->[0] . ($_->[1]//'') } values %CMDLINE_OPTS, # options strs
  1026         2417  
755             );
756              
757             # --- TODO clean up the following.
758             my $noexit_on_help =
759 114   100     79474 $hrOptsOut->{ $CMDLINE_OPTS{NOEXIT_ON_HELP}->[0] } // false;
760              
761 114 100       335 if($noexit_on_help) { # Report help during testing
762             # unknown opt --- error out. false => processing should terminate.
763 2 50       6 pod2usage(-verbose => 0, -exitval => 'NOEXIT', %docs), return false unless $ok;
764              
765             # Help, if requested
766 2 50       5 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       229 pod2usage(-verbose => 0, -exitval => EXIT_PARAM_ERR, %docs) unless $ok;
773              
774             # Help, if requested
775 112 50       220 pod2usage(-verbose => 0, -exitval => EXIT_PROC_ERR, %docs) if have('usage');
776 112 50       201 pod2usage(-verbose => 1, -exitval => EXIT_PROC_ERR, %docs) if have('h');
777 112 50       183 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         421 my %revmap = map { $CMDLINE_OPTS{$_}->[0] => $_ } keys %CMDLINE_OPTS;
  1008         1690  
784 112         409 for my $optname (keys %$hrOptsOut) {
785 596         1123 $hrOptsOut->{ $revmap{$optname} } = $hrOptsOut->{ $optname };
786             }
787              
788             # Check the names of any -D flags
789 112         175 for my $k (keys %{$hrOptsOut->{DEFS}}) {
  112         316  
790 32 50       140 die "Invalid -D name \"$k\"" if $k !~ DEFINE_NAME_RE;
791             }
792              
793             # Process other arguments. TODO? support multiple input filenames?
794 112   100     395 $hrOptsOut->{INPUT_FILENAME} = $lrArgs->[0] // "";
795              
796 112         933 return true; # Go ahead and run
797             } #_parse_command_line()
798              
799             # === Main ================================================================
800              
801             sub Main {
802 114 50   114 0 138900 my $self = shift or die("Please use Text::PerlPP->new()->Main");
803 114   50     336 my $lrArgv = shift // [];
804              
805 114 100       282 unless(_parse_command_line( $lrArgv, $self->{Opts} )) {
806 2         57596 return EXIT_OK; # TODO report param err vs. proc err?
807             }
808              
809 112 100       664 if($self->{Opts}->{PRINT_VERSION}) { # print version, raw and dotted
810 2         11 $Text::PerlPP::VERSION =~ m<^([^\.]+)\.(\d{3})(_?)(\d{3})>;
811 2 50       90 printf "PerlPP version %d.%d.%d ($VERSION)%s\n", $1, $2, $4,
812             ($3 ? ' (dev)' : '');
813 2 50       12 if($self->{Opts}->{PRINT_VERSION} > 1) {
814 0         0 print "Script: $0\nText::PerlPP: $INC{'Text/PerlPP.pm'}\n";
815             }
816 2         8 return EXIT_OK;
817             }
818              
819             # Save
820 110         244 push @Instances, $self;
821              
822 110         202 $self->{Package} = $self->{Opts}->{INPUT_FILENAME};
823 110         288 $self->{Package} =~ s/^.*?([a-z_][a-z_0-9.]*).pl?$/$1/i;
824 110         189 $self->{Package} =~ s/[^a-z0-9_]/_/gi;
825             # Not the whole name yet, so can start with a number.
826 110         387 $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   90 no strict 'refs';
  11         21  
  11         7046  
  110         164  
833 110         161 ${ "$self->{Package}::" . PPP_SELF_INSIDE } = $self;
  110         797  
834             }
835              
836             # --- Preamble -----------
837              
838 110         336 $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       265 say "#line 1 \"\"" if($self->{Opts}->{DEBUG_LINENOS});
842 110         640 $self->emit_pound_line( '', 1 );
843              
844             # Open the package
845 110         351 emit "package $self->{Package};\nuse 5.010001;\nuse strict;\nuse warnings;\n";
846 110         244 emit "use constant { true => !!1, false => !!0 };\n";
847 110         311 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         252 emit "my %D = (\n";
855 110         127 for my $defname (keys %{$self->{Opts}->{DEFS}}) {
  110         310  
856 32   50     48 my $val = ${$self->{Opts}->{DEFS}}{$defname} // 'true';
  32         83  
857             # just in case it's undef. "true" is the constant in this context
858 32 100       71 $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         66 emit " $defname => $val,\n";
863             }
864 110         213 emit ");\n";
865              
866             # Save a copy for use at generation time
867 110         207 %{$self->{Defs}} = map { my $v = eval(${$self->{Opts}->{DEFS}}{$_});
  32         34  
  32         1044  
868 32 50       100 warn "Could not evaluate -D \"$_\": $@" if $@;
869 32   100     124 $_ => ($v // true)
870             }
871 110         138 keys %{$self->{Opts}->{DEFS}};
  110         214  
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       152 if(%{$self->{Opts}->{DEFS}}) {
  110         271  
877             my $rx_search =
878 29         36 '\b(' . (join '|', map quotemeta, keys %{$self->{Opts}->{DEFS}}) . ')\b';
  29         118  
879 29         488 $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         76 %{$self->{Defs_repl_text}} =
884 32         38 map { my $v = eval(${$self->{Opts}->{DEFS}}{$_});
  32         766  
885 32 100 66     203 ($@ || !defined($v)) ? ($_ => $_) : ($_ => ('' . $v))
886             }
887 29         66 keys %{$self->{Opts}->{DEFS}};
  29         63  
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         197 %{$self->{Sets}} = map { my $v = eval(${$self->{Opts}->{SETS}}{$_});
  5         7  
  5         227  
894 5 50       19 warn "Could not evaluate -s \"$_\": $@" if $@;
895 5   50     19 $_ => ($v // true)
896             }
897 110         163 keys %{$self->{Opts}->{SETS}};
  110         289  
898              
899             # Make the copy for runtime
900 110         233 emit "my %S = (\n";
901 110         137 for my $defname (keys %{$self->{Opts}->{SETS}}) {
  110         234  
902 5         7 my $val = ${$self->{Opts}->{SETS}}{$defname};
  5         9  
903 5 50       11 if(!defined($val)) {
904             }
905 5 50       18 $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         42 emit " $defname => $val,\n";
910             }
911 110         209 emit ");\n";
912              
913             # --- User input ---------
914              
915             # Initial code from the command line, if any
916 110 100       244 if($self->{Opts}->{EVAL}) {
917 5         12 $self->emit_pound_line( '<-e>', 1 );
918 5         15 emit $self->{Opts}->{EVAL}, "\n";
919             }
920              
921             # The input file
922 110         338 $self->ProcessFile( $self->{Opts}->{INPUT_FILENAME} );
923              
924 110         185 my $script = $self->EndOB(); # The generated Perl script
925              
926             # --- Run it -------------
927 110 100       282 if ( $self->{Opts}->{DEBUG} ) {
928 6         48 print $script;
929              
930             } else {
931 104         247 $self->StartOB(); # Start collecting the output of the Perl script
932 104         126 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         6086 eval( $script ); $result=$@;
  104         24678  
937              
938 104 100       250 if($result) { # Report errors to console and shell
939 12         248 print STDERR $result;
940 12         78 return EXIT_PROC_ERR;
941             } else { # Save successful output
942 92         336 $self->OutputResult( \($self->EndOB()), $self->{Opts}->{OUTPUT_FILENAME} );
943             }
944             }
945 98         412 return EXIT_OK;
946             } #Main()
947              
948             sub new {
949 113     113 0 153213 my $class = shift;
950 113         297 return bless _make_instance(), $class;
951             }
952              
953             1;
954             __END__