man POE::Preprocessor () - a macro/const/enum preprocessor
NAME
POE::Preprocessor - a macro/const/enum preprocessor
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
DESCRIPTION
POE::Preprocessor is a Perl source filter that implements a simple macro substitution language. Think of it like compile-time code templates.
Macros
Macros are defined with the CWmacro 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 CWmacro 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.
Constants and Enumerations
The CWconst command defines a constant.
const CONSTANT_NAME 'constant value' const ANOTHER_CONSTANT 23
Enumerations are defined with the CWenum 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
CWenum statements may not span lines. If the first enumeration parameter is a plus sign, the constants will start where a previous CWenum left off.
enum 13 THIRTEENTH FOURTEENTH FIFTEENTH enum + SIXTEENTH SEVENTEENTH EIGHTEENTH
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 CW# 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.
IMPORTING MACROS/CONSTANTS
use POE::Preprocessor ( isa => 'POE::SomeModule' );
This method of calling Preprocessor causes the macros and constants of CWPOE::SomeModule to be imported for use in the current namespace. These macros and constants can be overridden simply by defining items in the current namespace of the same name.
Note: if the macros in CWPOE::SomeModule require additional perl modules, any code which imports these macros will need to CWuse those modules as well.
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 }
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 CWpackage Foo; in a module. Otherwise code before it will be missing from the resulting expanded file. POE::Preprocessor will create a new CWpackage 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;
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 CWuse statements. POE does a lot of dynamic loading at startup, so its CWuse statements are not always detectable.
To work around the issue, add CWuse statements for modules that are missing from your bundled distribution.
Things to look for:
PerlApp cannot find modules imported using the 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;
# Choose one of the following, depending on your event loop. use POE::Loop::Select; # Usually this one. use POE::Loop::IO_Poll; use POE::Loop::Event; use POE::Loop::Tk; use POE::Loop::Gtk;
# Dynamically required by POE.pm. use POE::Kernel; use POE::Session;
use POE::A; use POE::B; use POE::C;
We recommend that you use PAR. PAR understands source code filters like POE::Preprocessor. You can skip the rest of this section if you're using it.
PerlApp and Perl2EXE do 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 CWPOE_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 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.30>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 CWuse 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.
Autrijus Tang, for writing PAR.
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.
SEE ALSO
The regexp optimizer is based on code in Ilya Zakharevich's Text::Trie.
PAR is a fine Perl archiver. Use it.
AUTHOR & COPYRIGHT
POE::Preprocessor is Copyright 2000-2004 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.