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