home *** CD-ROM | disk | FTP | other *** search
- # $Id: Preprocessor.pm,v 1.32 2004/05/24 14:02:08 rcaputo Exp $
-
- package POE::Preprocessor;
-
- use strict;
-
- use vars qw($VERSION);
- $VERSION = do {my@r=(q$Revision: 1.32 $=~/\d+/g);sprintf"%d."."%04d"x$#r,@r};
-
- use Carp qw(croak);
- use Filter::Util::Call;
- use Symbol qw(gensym);
- use File::Path qw(mkpath);
- use File::Spec;
-
- sub MAC_PARAMETERS () { 0 }
- sub MAC_CODE () { 1 }
- sub MAC_NAME () { 2 } # only used in temporary %macro
- sub MAC_FILE () { 3 }
- sub MAC_LINE () { 4 } # only used in temporary %macro
-
- sub STATE_PLAIN () { 0x0000 }
- sub STATE_MACRO_DEF () { 0x0001 }
-
- sub COND_FLAG () { 0 }
- sub COND_LINE () { 1 }
- sub COND_INDENT () { 2 }
-
- BEGIN {
- if (defined $ENV{POE_PREPROC_DUMP}) {
- eval "sub DUMP_EXPANDED () { 1 }";
- eval "sub DUMP_BASE () { '$ENV{POE_PREPROC_DUMP}' }";
- }
- else {
- eval "sub DUMP_EXPANDED () { 0 }";
- eval "sub DUMP_BASE () { die }";
- }
- };
-
- #sub DEBUG () { 1 }
- #sub DEBUG_INVOKE () { 1 }
- #sub DEBUG_DEFINE () { 1 }
-
- #sub WARN_DEFINE () { 1 }
-
- BEGIN {
- defined &DEBUG or eval 'sub DEBUG () { 0 }'; # preprocessor
- defined &DEBUG_INVOKE or eval 'sub DEBUG_INVOKE () { 0 }'; # macro invocs
- defined &DEBUG_DEFINE or eval 'sub DEBUG_DEFINE () { 0 }'; # macro defines
- defined &WARN_DEFINE or eval 'sub WARN_DEFINE () { 0 }'; # redefine warning
- };
-
- ### Start of regexp optimizer.
-
- # text_trie_trie is virtually identical to code in Ilya Zakharevich's
- # Text::Trie::Trie function. The minor differences involve hardcoding
- # the minimum substring length to 1 and sorting the output.
-
- sub text_trie_trie {
- my @list = @_;
- return shift if @_ == 1;
- my (@trie, %first);
-
- foreach (@list) {
- my $c = substr $_, 0, 1;
- if (exists $first{$c}) {
- push @{$first{$c}}, $_;
- }
- else {
- $first{$c} = [ $_ ];
- }
- }
-
- foreach (sort keys %first) {
- # Find common substring
- my $substr = $first{$_}->[0];
- (push @trie, $substr), next if @{$first{$_}} == 1;
- my $l = length($substr);
- foreach (@{$first{$_}}) {
- $l-- while substr($_, 0, $l) ne substr($substr, 0, $l);
- }
- $substr = substr $substr, 0, $l;
-
- # Feed the trie.
- @list = map {substr $_, $l} @{$first{$_}};
- push @trie, [$substr, text_trie_trie(@list)];
- }
-
- @trie;
- }
-
- # This is basically Text::Trie::walkTrie, but it's hardcoded to build
- # regular expressions.
-
- sub text_trie_as_regexp {
- my @trie = @_;
- my $num = 0;
- my $regexp = '';
-
- foreach (@trie) {
- $regexp .= '|' if $num++;
- if (ref $_ eq 'ARRAY') {
- $regexp .= $_->[0] . '(?:';
-
- # If the first tail is empty, make the whole group optional.
- my ($tail, $first);
- if (length $_->[1]) {
- $tail = ')';
- $first = 1;
- }
- else {
- $tail = ')?';
- $first = 2;
- }
-
- # Recurse into the group of tails.
- if ($#$_ > 1) {
- $regexp .= text_trie_as_regexp( @{$_}[$first .. $#$_] );
- }
- $regexp .= $tail;
- }
- else {
- $regexp .= $_;
- }
- }
-
- $regexp;
- }
-
- ### End of regexp optimizer.
-
- # These must be accessible from outside the current package.
- use vars qw(%conditional_stacks %excluding_code %exclude_indent);
-
- sub fix_exclude {
- my $package_name = shift;
- $excluding_code{$package_name} = 0;
- if (@{$conditional_stacks{$package_name}}) {
- foreach my $flag (@{$conditional_stacks{$package_name}}) {
- unless ($flag->[COND_FLAG]) {
- $excluding_code{$package_name} = 1;
- $exclude_indent{$package_name} = $flag->[COND_INDENT];
- last;
- }
- }
- }
- }
-
- my (%constants, %macros, %const_regexp, %macro);
-
- sub import {
-
- my $self = shift;
- my %args;
- if(@_ > 1) {
- %args = @_;
- }
-
- # Outer closure to define a unique scope.
- { my $macro_name = '';
- my ($macro_line, $enum_index);
- my ($package_name, $file_name, $line_number) = (caller)[0,1,2];
- my $const_regexp_dirty = 0;
- my $state = STATE_PLAIN;
- my $dump_file;
-
- DUMP_EXPANDED and do {
- my ($vol, $dir, $file) = File::Spec->splitpath($file_name);
- my $dest_dir = File::Spec->catdir(DUMP_BASE, $dir);
- my $dest_filename = File::Spec->catfile($dest_dir, $file);
-
- mkpath($dest_dir, 0, 0777);
- $dump_file = gensym;
- open $dump_file, ">$dest_filename"
- or die "POE::Preprocessor could not create $dest_filename: $!";
- print $dump_file "package $package_name;\n";
- };
-
- # The following block processes inheritance requests for
- # macros/constants and enums. added by sungo 09/2001
- my @isas;
-
- if($args{isa}) {
- if(ref $args{isa} eq 'ARRAY') {
- foreach my $isa (@{$args{isa}}) {
- push @isas, $isa;
- }
- } else {
- push @isas, $args{isa};
- }
- foreach my $isa (@isas) {
- eval "use $isa";
- croak "Unable to load $isa : $@" if $@;
-
- foreach my $const (keys %{$constants{$isa}}) {
- $constants{$package_name}->{$const} =
- $constants{$isa}->{$const};
- $const_regexp_dirty = 1;
- }
-
- foreach my $macro (keys %{$macros{$isa}}) {
- $macros{$package_name}->{$macro} = $macros{$isa}->{$macro};
- }
- }
- }
-
- $conditional_stacks{$package_name} = [ ];
- $excluding_code{$package_name} = 0;
-
- my $set_const = sub {
- my ($name, $value) = @_;
-
- if (WARN_DEFINE && exists $constants{$package_name}->{$name}) {
- warn "const $name redefined at $file_name line $line_number\n"
- unless $constants{$package_name}->{$name} eq $value;
- }
-
- $constants{$package_name}->{$name} = $value;
- $const_regexp_dirty++;
-
- DEBUG_DEFINE and
- warn( ",-----\n",
- "| Defined a constant: $name = $value\n",
- "`-----\n"
- );
- };
-
- # Define the filter sub.
- filter_add
- ( sub {
- my $status = filter_read();
- $line_number++;
-
- ### Handle errors or EOF.
- if ($status <= 0) {
- if (@{$conditional_stacks{$package_name}}) {
- die( "include block never closed. It probably started " .
- "at $file_name line " .
- $conditional_stacks{$package_name}->[0]->[COND_LINE] . "\n"
- );
- }
- DUMP_EXPANDED and close $dump_file;
- return $status;
- }
-
- ### Usurp modified Perl syntax for code inclusion. These
- ### are hardcoded and always handled.
-
- # Only do the conditionals if there's a flag present.
- if (/\#\s*include/) {
-
- # if (...) { # include
- if (/^(\s*)if\s*\((.+)\)\s*\{\s*\#\s*include\s*$/) {
- my $space = (defined $1) ? $1 : '';
- $_ =
- ( $space .
- "BEGIN { push( \@{\$" . __PACKAGE__ .
- "::conditional_stacks{'$package_name'}}, " .
- "[ !!$2, $line_number, '$space' ] ); \&" . __PACKAGE__ .
- "::fix_exclude('$package_name'); }; # $_"
- );
- s/\#\s+/\# /;
-
- # Dummy line in the macro.
- if ($state & STATE_MACRO_DEF) {
- local $_ = $_;
- s/B/\# B/;
- $macro_line++;
- $macro{$package_name}->[MAC_CODE] .= $_;
- DEBUG and
- warn sprintf "%4d M: # mac 1: %s", $line_number, $_;
- }
- else {
- DEBUG and warn sprintf "%4d C: %s", $line_number, $_;
- }
-
- DUMP_EXPANDED and do {
- print $dump_file $_;
- close $dump_file if $status <= 0;
- };
- return $status;
- }
-
- # } # include
- elsif (/^\s*\}\s*\#\s*include\s*$/) {
- s/^(\s*)/$1\# /;
- pop @{$conditional_stacks{$package_name}};
- &fix_exclude($package_name);
-
- unless ($state & STATE_MACRO_DEF) {
- DEBUG and warn sprintf "%4d C: %s", $line_number, $_;
- DUMP_EXPANDED and do {
- print $dump_file $_;
- close $dump_file if $status <= 0;
- };
- return $status;
- }
- }
-
- # } else { # include
- elsif (/^\s*\}\s*else\s*\{\s*\#\s*include\s*$/) {
- unless (@{$conditional_stacks{$package_name}}) {
- die( "else { # include ... without if or unless " .
- "at $file_name line $line_number\n"
- );
- DUMP_EXPANDED and do {
- print $dump_file $_;
- close $dump_file;
- };
- return -1;
- }
-
- s/^(\s*)/$1\# /;
- $conditional_stacks{$package_name}->[-1]->[COND_FLAG] =
- !$conditional_stacks{$package_name}->[-1]->[COND_FLAG];
- &fix_exclude($package_name);
-
- unless ($state & STATE_MACRO_DEF) {
- DEBUG and warn sprintf "%4d C: %s", $line_number, $_;
-
- DUMP_EXPANDED and do {
- print $dump_file $_;
- close $dump_file if $status <= 0;
- };
- return $status;
- }
- }
-
- # unless (...) { # include
- elsif (/^(\s*)unless\s*\((.+)\)\s*\{\s*\#\s*include\s*$/) {
- my $space = (defined $1) ? $1 : '';
- $_ = ( $space .
- "BEGIN { push( \@{\$" . __PACKAGE__ .
- "::conditional_stacks{'$package_name'}}, " .
- "[ !$2, $line_number, '$space' ] ); \&" . __PACKAGE__ .
- "::fix_exclude('$package_name'); }; # $_"
- );
- s/\#\s+/\# /;
-
- # Dummy line in the macro.
- if ($state & STATE_MACRO_DEF) {
- local $_ = $_;
- s/B/\# B/;
- $macro_line++;
- $macro{$package_name}->[MAC_CODE] .= $_;
- DEBUG and
- warn sprintf "%4d M: # mac 2: %s", $line_number, $_;
- }
- else {
- DEBUG and warn sprintf "%4d C: %s", $line_number, $_;
- }
-
- DUMP_EXPANDED and do {
- print $dump_file $_;
- close $dump_file if $status <= 0;
- };
- return $status;
- }
-
- # } elsif (...) { # include
- elsif (/^(\s*)\}\s*elsif\s*\((.+)\)\s*\{\s*\#\s*include\s*$/) {
- unless (@{$conditional_stacks{$package_name}}) {
- die( "Include elsif without include if or unless " .
- "at $file_name line $line_number\n"
- );
- DUMP_EXPANDED and do {
- print $dump_file $_;
- close $dump_file;
- };
- return -1;
- }
-
- my $space = (defined $1) ? $1 : '';
- $_ = ( $space .
- "BEGIN { \$" . __PACKAGE__ .
- "::conditional_stacks{'$package_name'}->[-1] = " .
- "[ !!$2, $line_number, '$space' ]; \&" . __PACKAGE__ .
- "::fix_exclude('$package_name'); }; # $_"
- );
- s/\#\s+/\# /;
-
- # Dummy line in the macro.
- if ($state & STATE_MACRO_DEF) {
- local $_ = $_;
- s/B/\# B/;
- $macro_line++;
- $macro{$package_name}->[MAC_CODE] .= $_;
- DEBUG and
- warn sprintf "%4d M: # mac 3: %s", $line_number, $_;
- }
- else {
- DEBUG and warn sprintf "%4d C: %s", $line_number, $_;
- }
-
- DUMP_EXPANDED and do {
- print $dump_file $_;
- close $dump_file if $status <= 0;
- };
- return $status;
- }
- }
-
- ### Not including code, so comment it out. Don't return
- ### $status here since the code may well be in a macro.
- if ($excluding_code{$package_name}) {
- s{^($exclude_indent{$package_name})?}
- {$exclude_indent{$package_name}\# };
-
- # Kludge: Must thwart macros on this line.
- s/\{\%(.*?)\%\}/MACRO($1)/g;
-
- unless ($state & STATE_MACRO_DEF) {
- DEBUG and warn sprintf "%4d C: %s", $line_number, $_;
- DUMP_EXPANDED and do {
- print $dump_file $_;
- close $dump_file if $status <= 0;
- };
- return $status;
- }
- }
-
- ### Inside a macro definition.
- if ($state & STATE_MACRO_DEF) {
-
- # Close it!
- if (/^\}\s*$/) {
- $state = STATE_PLAIN;
-
- DEBUG_DEFINE and
- warn
- ( ",-----\n",
- "| Defined macro $macro_name\n",
- "| Parameters: ",
- @{$macro{$package_name}->[MAC_PARAMETERS]}, "\n",
- "| Code: {\n",
- $macro{$package_name}->[MAC_CODE],
- "| }\n",
- "`-----\n"
- );
-
- $macro{$package_name}->[MAC_CODE] =~ s/^\s*//;
- $macro{$package_name}->[MAC_CODE] =~ s/\s*$//;
-
- if ( WARN_DEFINE &&
- exists $macros{$package_name}->{$macro_name}
- ) {
- warn( "macro $macro_name redefined at ",
- "$file_name line $line_number\n"
- )
- if ( $macros{$package_name}->{$macro_name}->[MAC_CODE] ne
- $macro{$package_name}->[MAC_CODE]
- );
- }
-
- $macros{$package_name}->{$macro_name} = $macro{$package_name};
-
- $macro_name = '';
- }
-
- # Otherwise append this line to the macro.
- else {
- $macro_line++;
- $macro{$package_name}->[MAC_CODE] .= $_;
- }
-
- # Either way, the code must not go on.
- $_ = "# mac 4: $_";
- DEBUG and warn sprintf "%4d M: %s", $line_number, $_;
-
- DUMP_EXPANDED and do {
- print $dump_file $_;
- close $dump_file if $status <= 0;
- };
- return $status;
- }
-
- ### Ignore everything after __END__ or __DATA__. This works
- ### around a coredump in 5.005_61 through 5.6.0 at the
- ### expense of preprocessing data and documentation.
- if (/^__(END|DATA)__\s*$/) {
- $_ = "# $_";
- DUMP_EXPANDED and do {
- print $dump_file $_;
- close $dump_file;
- };
- return 0;
- }
-
- ### We're done if we're excluding code.
- if ($excluding_code{$package_name}) {
- DUMP_EXPANDED and do {
- print $dump_file $_;
- close $dump_file if $status <= 0;
- };
- return $status;
- }
-
- ### Define an enum.
- if (/^enum(?:\s+(\d+|\+))?\s+(.*?)\s*$/) {
- my $temp_line = $_;
-
- $enum_index = ( (defined $1)
- ? ( ($1 eq '+')
- ? $enum_index
- : $1
- )
- : 0
- );
- foreach (split /\s+/, $2) {
- &{$set_const}($_, $enum_index++);
- }
-
- $_ = "# $temp_line";
-
- DEBUG and warn sprintf "%4d E: %s", $line_number, $_;
-
- DUMP_EXPANDED and do {
- print $dump_file $_;
- close $dump_file if $status <= 0;
- };
- return $status;
- }
-
- ### Define a constant.
- if (/^const\s+(\S+)\s+(.+?)\s*$/i) {
- &{$set_const}($1, $2);
- $_ = "# $_";
- DEBUG and warn sprintf "%4d E: %s", $line_number, $_;
-
- DUMP_EXPANDED and do {
- print $dump_file $_;
- close $dump_file if $status <= 0;
- };
- return $status;
- }
-
- ### Begin a macro definition.
- if (/^macro\s*(\w+)\s*(?:\((.*?)\))?\s*\{\s*$/) {
- $state = STATE_MACRO_DEF;
-
- my $temp_line = $_;
-
- $macro_name = $1;
- $macro_line = 0;
- my @macro_params =
- ( (defined $2)
- ? split(/\s*\,\s*/, $2)
- : ()
- );
-
- $macro{$package_name} =
- [ \@macro_params, # MAC_PARAMETERS
- '', # MAC_CODE
- $macro_name, # MAC_NAME
- $file_name, # MAC_FILE
- $line_number, # MAC_LINE
- ];
-
- $_ = "# $temp_line";
- DEBUG and warn sprintf "%4d D: %s", $line_number, $_;
-
- DUMP_EXPANDED and do {
- print $dump_file $_;
- close $dump_file if $status <= 0;
- };
- return $status;
- }
-
- ### Perform macro substitutions.
- my $substitutions = 0;
- while (/(\{\%\s+(\S+)\s*(.*?)\s*\%\})/gs) {
- my ($name, $params) = ($2, $3);
-
- # Backtrack to the beginning of the substitution so that
- # the newly inserted text may also be checked.
- pos($_) -= length($1);
-
- DEBUG_INVOKE and
- warn ",-----\n| macro invocation: $name $params\n";
-
- if (exists $macros{$package_name}->{$name}) {
-
- my @use_params = split /\s*\,\s*/, $params;
- my @mac_params =
- @{$macros{$package_name}->{$name}->[MAC_PARAMETERS]};
-
- if (@use_params != @mac_params) {
- warn( "macro $name paramter count (",
- scalar(@use_params),
- ") doesn't match defined count (",
- scalar(@mac_params),
- ") at $file_name line $line_number\n"
- );
-
- DUMP_EXPANDED and do {
- print $dump_file $_;
- close $dump_file if $status <= 0;
- };
- return $status;
- }
-
- # Build a new bit of code here.
- my $substitution = $macros{$package_name}->{$name}->[MAC_CODE];
- my $macro_file = $macros{$package_name}->{$name}->[MAC_FILE];
- my $macro_line = $macros{$package_name}->{$name}->[MAC_LINE];
-
- foreach my $mac_param (@mac_params) {
- my $use_param = shift @use_params;
- 1 while ($substitution =~ s/$mac_param/$use_param/g);
- }
-
- unless ($^P) {
- my @sub_lines = split /\n/, $substitution;
- my $sub_line = @sub_lines;
- while ($sub_line--) {
- splice( @sub_lines, $sub_line, 0,
- "# line $line_number " .
- "\"macro $name (defined in $macro_file at line " .
- ($macro_line + $sub_line + 1) . ") " .
- "invoked from $file_name\""
- );
- }
- $substitution = join "\n", @sub_lines;
- }
-
- substr($_, pos($_), length($1)) = $substitution;
- $_ .= "# line " . ($line_number+1) . " \"$file_name\"\n"
- unless $^P;
-
- DEBUG_INVOKE and warn "$_`-----\n";
-
- $substitutions++;
- }
- else {
- die( "macro $name has not been defined ",
- "at $file_name line $line_number\n"
- );
- last;
- }
- }
-
- # Only rebuild the constant regexp if necessary. This
- # prevents redundant regexp rebuilds when defining several
- # constants all together.
- if ($const_regexp_dirty) {
- $const_regexp{$package_name} =
- text_trie_as_regexp
- ( text_trie_trie(keys %{$constants{$package_name}})
- );
- $const_regexp_dirty = 0;
- }
-
- # Perform constant substitutions.
- if (defined $const_regexp{$package_name}) {
- $substitutions +=
- s[\b($const_regexp{$package_name})\b]
- [$constants{$package_name}->{$1}]sg;
- }
-
- # Trace substitutions.
- if (DEBUG) {
- if ($substitutions) {
- foreach my $line (split /\n/) {
- warn sprintf "%4d S: %s\n", $line_number, $line;
- }
- }
- else {
- warn sprintf "%4d |: %s", $line_number, $_;
- }
- }
-
- DUMP_EXPANDED and do {
- print $dump_file $_;
- close $dump_file if $status <= 0;
- };
- return $status;
- }
- );
- }
- }
-
- # Clear a package's macros. Used for destructive testing.
- sub clear_package {
- my ($self, $package) = @_;
- delete $constants{$package};
- delete $macros{$package};
- delete $const_regexp{$package};
- delete $macro{$package};
- }
-
- 1;
-
- __END__
-
- =head1 NAME
-
- POE::Preprocessor - a macro/const/enum preprocessor
-
- =head1 SYNOPSIS
-
- use POE::Preprocessor;
-
- # use POE::Preprocessor ( isa => 'POE::SomeModule' );
-
- macro max (one,two) {
- ((one) > (two) ? (one) : (two))
- }
-
- print {% max $one, $two %}, "\n";
-
- const PI 3.14159265359
-
- print "PI\n"; # Substitutions don't grok Perl!
-
- enum ZERO ONE TWO
- enum 12 TWELVE THIRTEEN FOURTEEN
- enum + FIFTEEN SIXTEEN SEVENTEEN
-
- print "ZERO ONE TWO TWELVE THIRTEEN FOURTEEN FIFTEEN SIXTEEN SEVENTEEN\n";
-
- if ($expression) { # include
- ... lines of code ...
- } # include
-
- unless ($expression) { # include
- ... lines of code ...
- } elsif ($expression) { # include
- ... lines of code ...
- } else { # include
- ... lines of code ...
- } # include
-
- =head1 DESCRIPTION
-
- POE::Preprocessor is a Perl source filter that implements a simple
- macro substitution language. Think of it like compile-time code
- templates.
-
- =head2 Macros
-
- Macros are defined with the C<macro> statement. The syntax is similar
- to Perl subs:
-
- macro macro_name (parameter_0, parameter_1) {
- macro code ... parameter_0 ... parameter_1 ...
- }
-
- The open brace is required to be on the same line as the C<macro>
- statement. The Preprocessor doesn't analyze macro bodies. Instead,
- it assumes that any closing brace in the leftmost column ends an open
- macro.
-
- The parameter list is optional for macros that don't accept
- parameters.
-
- macro macro_name {
- macro code;
- }
-
- Macros are substituted into a program with a syntax borrowed from
- Iaijutsu and altered slightly to jive with Perl's native syntax.
-
- {% macro_name $param_1, 'param two' %}
-
- This is the code the first macro would generate:
-
- macro code ... $param_1 ... 'param two' ...
-
- It's very simplistic. See POE::Kernel for extensive macro use.
-
- =head2 Constants and Enumerations
-
- The C<const> command defines a constant.
-
- const CONSTANT_NAME 'constant value'
- const ANOTHER_CONSTANT 23
-
- Enumerations are defined with the C<emun> command. Enumerations start
- from zero by default:
-
- enum ZEROTH FIRST SECOND ...
-
- If the first parameter of an enumeration is a number, then the
- enumerated constants will start with that value:
-
- enum 10 TENTH ELEVENTH TWELFTH
-
- C<enum> statements may not span lines. If the first enumeration
- parameter is a plus sign, the constants will start where a previous
- C<enum> left off.
-
- enum 13 THIRTEENTH FOURTEENTH FIFTEENTH
- enum + SIXTEENTH SEVENTEENTH EIGHTEENTH
-
- =head2 Conditional Code Inclusion (#ifdef)
-
- The preprocessor supports something like cpp's #if/#else/#endif by
- usurping a bit of Perl's conditional syntax. The following
- conditional statements will be evaluated at compile time if they are
- followed by the comment C<# include>:
-
- if (EXPRESSION) { # include
- BLOCK;
- } elsif (EXPRESSION) { # include
- BLOCK;
- } else { # include
- BLOCK;
- } # include
-
- unless (EXPRESSION) { # include
- BLOCK;
- } # include
-
- The code in each conditional statement's BLOCK will be included or
- excluded in the compiled code depending on the outcome of its
- EXPRESSION.
-
- Conditional includes are nestable, but else and elsif must be on the
- same line as the previous block's closing brace, as they are in the
- previous example.
-
- Conditional includes are experimental pending a decision on how useful
- they are.
-
- =head1 IMPORTING MACROS/CONSTANTS
-
- use POE::Preprocessor ( isa => 'POE::SomeModule' );
-
- This method of calling Preprocessor causes the macros and constants of
- C<POE::SomeModule> to be imported for use in the current namespace.
- These macros and constants can be overriden simply by defining items
- in the current namespace of the same name.
-
- Note: if the macros in C<POE::SomeModule> require additional perl
- modules, any code which imports these macros will need to C<use>
- those modules as well.
-
- =head1 DEBUGGING
-
- POE::Preprocessor has three debugging constants which may be defined
- before the first time POE::Preprocessor is used.
-
- To trace source filtering in general, and to see the resulting code
- and operations performed on each line:
-
- sub POE::Preprocessor::DEBUG () { 1 }
-
- To trace macro invocations as they happen:
-
- sub POE::Preprocessor::DEBUG_INVOKE () { 1 }
-
- To see macro, constant, and enum definitions:
-
- sub POE::Preprocessor::DEBUG_DEFINE () { 1 }
-
- To see warnings when a macro or constant is redefined:
-
- sub POE::Preprocessor::WARN_DEFINE () { 1 }
-
- =head1 ENVIRONMENT
-
- Setting the POE_PREPROC_DUMP environment variable causes
- POE::Preprocessor to write new copies of the modules it expands.
- POE_PREPROC_DUMP should contain the name of a base directory. It
- need not exist. All the expanded files will be written under the
- directory in POE_PREPROC_DUMP.
-
- Note: Some modules such as POE::Kernel alter the macros they load at
- compile time. Versions of these modules created by POE_PREPROC_DUMP
- may only be used in the same situation they were created in. For
- instance, a version of POE::Kernel created with Gtk support macros may
- only be used in Gtk programs from that point on.
-
- Note: Because POE::Preprocessor can only dump source code from the
- point it it is used in a file, it should be the first statement after
- C<package Foo;> in a module. Otherwise code before it will be missing
- from the resulting expanded file. POE::Preprocessor will create a new
- C<package Foo;> line to replace the one that it could not see.
-
- This is good:
-
- package Foo; # Not seen but simulated in the expanded version.
- use POE::Preprocessor;
- use Carp; # Seen and included in the expanded version.
-
- This is bad:
-
- package Foo; # Not seen but simulated in the expanded version.
- use Carp; # Not seen and OMITTED FROM the expanded version.
- use POE::Preprocessor;
-
- =head1 PERLAPP, PERL2EXE, AND PAR SUPPORT
-
- PerlApp, perl2exe, and PAR are program archivers, similar to Java's
- JAR. These utilities bundle your program with its dependencies and
- perhaps a perl executable. The result is a single, large file that
- may be more easily deployable than the usual Perl program.
-
- The archivers find dependencies by looking for C<use> statements. POE
- does a lot of dynamic loading at startup, so its C<use> statements are
- not always detectable.
-
- To work around the issue, add C<use> statements for modules that are
- missing from your bundled distribution.
-
- Things to look for:
-
- PerlApp cannot find modules imported using the L<POE> module. That
- is, this will not work:
-
- use POE qw(A B C);
-
- This is the working equivalent:
-
- # Dynamically required by POE::Kernel.
- use POE::Resource::Aliases;
- use POE::Resource::Events;
- use POE::Resource::Extrefs;
- use POE::Resource::FileHandles;
- use POE::Resource::SIDs;
- use POE::Resource::Sessions;
- use POE::Resource::Signals;
- use POE::Resource::Statistics;
-
- # Dynamically required by POE.pm.
- use POE::Kernel;
- use POE::Session;
-
- use POE::A;
- use POE::B;
- use POE::C;
-
- PerlApp does not support POE::Processor or any other source filter, so
- it's necessary to generate a static version of the files to be
- included. Setting the C<POE_PREPROC_DUMP> environment variable will
- cause POE::Preprocessor to dump a processed version of the file.
-
- set POE_PREPROC_DUMP=c:\rocco\preproc
-
- Run the program. As the program is run, its preprocessed files will
- be placed in subdirectories under L<POE_PREPROC_DUMP>.
-
- perl MyPoeApp.perl
-
- The preprocessed files must now be found, and their parent directory
- must be placed in the PERL5LIB environment variable.
-
- C:\rocco\POE-0.20>dir /b /a:d /s c:\rocco\preproc
-
- Might show these directories:
-
- C:\rocco\preproc\POE
- C:\rocco\preproc\POE\Kernel
-
- The POE modules have been dumped into "C:\rocco\preproc\POE" and its
- subdirectories. For C<use POE::Foo> to find POE/Foo.pm, the
- "c:\rocco\preproc" directory must be prepended to PERL5LIB.
-
- set PERL5LIB=c:\rocco\preproc
-
- PerlApp can finally build an EXE version of the program.
-
- perlapp --exe MyPoeApp.exe --clean MyPoeApp.perl
-
- MyPoeApp.exe should now be a stand-alone executable version of your
- Perl program.
-
- Thanks to...
-
- Zoltan Kandi for testing and documenting this.
-
- Lance Braswell for pointing out the POE::Resource classes need to be
- loaded.
-
- =head1 BUGS
-
- Source filters are line-based, and so is the macro language. The only
- constructs that may span lines are macro definitions, and those *must*
- span lines.
-
- The regular expressions that detect and replace code are simplistic
- and may not do the right things when given challenging Perl syntax to
- parse. For example, constants are replaced within strings.
-
- Substitution is done in two phases: macros first, then constants. It
- would be nicer (and more dangerous) if the phases looped around and
- around until no more substitutions occurred.
-
- The regexp builder makes silly subexpressions like /(?:|m)/. That
- could be done better as /m?/ or /(?:jklm)?/ if the literal is longer
- than a single character.
-
- The "# include" directives are not compatible with the
- POE_PREPROC_DUMP environment variable.
-
- =head1 SEE ALSO
-
- The regexp optimizer is based on code in Ilya Zakharevich's
- Text::Trie.
-
- =head1 AUTHOR & COPYRIGHT
-
- POE::Preprocessor is Copyright 2000 Rocco Caputo. Some parts are
- Copyright 2001 Matt Cashner. All rights reserved. POE::Preprocessor
- is free software; you may redistribute it and/or modify it under
- the same terms as Perl itself.
-
- =cut
-