Awesome
PerlPP: Perl preprocessor
Translates Text+Perl to Text. It can be used for any kind of text templating, e.g. code generation. No external modules are required, just a single file. Requires Perl 5.10.1+.
PerlPP runs in two passes: it generates a Perl script from your input, and then
it runs the generated script. If you see error at (eval ##)
(for some number ##
), it means there was an error in the generated script.
Installation
Easy way #1: cpanm Text::PerlPP
(using cpanminus).
Easy way #2: copy the perlpp
file from the
latest release
into a directory in your PATH
, and chmod a+x perlpp
.
For development or more information, see the other README.
Usage
Usage: perl perlpp.pl [options] [filename]
Options:
-o, --output filename Output to the file instead of STDOUT.
-D, --define name=value Set $D{name}=value in the generated
code. The hash %D always exists, but
is empty if you haven't specified any
-D options.
Also substitutes _name_ with _value_
in the output file.
_value_ is optional and defaults to
true.
-e, --eval statement Evaluate the statement(s) before any
Perl code of the input files.
-E, --debug Don't evaluate Perl code, just write
it to STDERR.
-k, --keep-going Don't stop on errors in an external command
-s, --set name=value As -D, but generates into %S and does
not substitute in the text body.
-h, --help Usage help.
In a -D option, the value
must be a valid Perl value, e.g., "foo"
for a string. This may require you to escape quotes in the -D argument,
depending on your shell. E.g., if -D foo="bar"
doesn't work, try
-D 'foo="bar"'
(with single quotes around the whole name=value
part).
Syntax of the input file
The syntax is a bit similar to PHP.
Perl code is included between <?
and ?>
tags.
There are several modes, indicated by the character after the <?
:
<? code mode: Perl code is between the tags.
<?= echo mode: prints a Perl expression.
<?: internal-command mode: executed by PerlPP itself.
<?/ code mode, beginning with printing a line break.
<?# comment mode: everything in <?# ... ?> is ignored.
<?! external mode: everything in <?! ... ?> is run as an external command.
The code mode is started by <?
followed by any number of whitespaces
or line breaks.
If there is any non-whitespace character after <?
other than those starting
the special modes, then this tag will be ignored (passed as it is).
For example:
<?x this tag is passed as is ?> because "x" is not a valid mode
produces the result:
<?x this tag is passed as is ?> because "x" is not a valid mode
The Generated Script
The generated script:
- is in its own package, named based on the input filename and a unique number
use
s5.010001
,strict
, andwarnings
- provides constants
true
(=!!1
) andfalse
(=!!0
) (withuse constant
) - declares
my %D
and initializes%D
based on any -D options you provide - declares
my %S
and initializes%S
based on any -s options you provide
Other than that, everything in the script comes from your input file(s). Use the -E option to see the generated script.
Examples
Basic loop
Hello <? print "world"; ?> (again).
<?# I don't appear in the output ?>but I do.
<? for ( my $i = 0; $i < 5; $i++ ) { ?>
number: <?= $i ?>
<? } ?>
Result:
Hello world (again).
but I do.
number: 0
number: 1
number: 2
number: 3
number: 4
Loop with less whitespace
In order to remove empty lines, one might write it like this:
Hello <? print "world"; ?> (again).
<? for ( my $i = 0; $i < 5; $i++ ) { ?>number: <?= $i ?>
<? } ?>
Result:
Hello world (again).
number: 0
number: 1
number: 2
number: 3
number: 4
Line breaks using <?/
The example
foo<? print "bar";?>
produces the output
foobar
Adding the /
, to make
foo<?/ print "bar";?>
produces the output
foo
bar
So <?/ ... ?>
is effectively a shorthand for <? print "\n"; ... ?>
.
External commands using <?!
The example
<?! echo Howdy! ?>
produces the output
Howdy!
If the command returns an error status, perlpp will as well, unless you
specify -k. That way you can use perlpp and external commands in make
and other programs that check exit codes, and not silently lose error
information. For example, running perlpp
on the input:
<?! false ?> More stuff
will give you an error message (from the false
's error return), and will not
print More stuff
. Running perlpp -k
on that same input will give the
error message and will print More stuff
.
Internal Commands
Include
<?:include file.p ?>
<?:include "long file name.p" ?>
Includes source code of another PerlPP file into this position.
Note that this file can be any PerlPP input, so you can also use this to
include plain text files or other literal files.
When using the long form, make sure there is whitespace between the trailing
"
and the closing tag ?>
, as explained below under "Capturing."
Prefix
<?:prefix foo bar ?>
Replaces word prefixes in the following output.
In this case words like fooSomeWord
will become barSomeWord
.
Macro
<?:macro some_perl_code; ?>
will run some_perl_code;
at the time of script generation. Whatever output
the perl code produces will be included verbatim in the script output.
Within some_perl_code
, the current PerlPP instance is available as $PSelf
.
This can be used to dynamically select which files you want to include,
using the provided Include()
method. For example:
<?:macro my $fn="some_name"; $PSelf->Include($fn); ?>
has the same effect as
<?:include some_name ?>
but $fn
can be determined programmatically. Note that defines set with
-D or -s do not take effect until after the script has been
generated, which is after the macro code runs. However, those are available
as hashes $PSelf->{Defs}
and $PSelf->{Sets}
in macro code.
Capturing
Sometimes it is great to get (capture) source text into a Perl string.
"?> start of capturing
<?" end of capturing
There must be no whitespace between the "
and the ?>
or <?
. For example:
<? print "?>That's cool<?" . "?>, really.<?"; ?>
is the same as
<? print 'That\'s cool' . ', really.'; ?>
Captured strings are properly escaped, and can be sequenced like in this example. Moreover, they can be nested!
<?
sub ABC {
for my $c ( "a".."z" ) {
print $c;
}
}
?>
<? ABC(); ?>
<?= uc( "?>alphabet
<? ABC(); ?>
<?" ); ?>
Printed characters from the second ABC()
call are attached to the
string 'alphabet '
, so the result will be
abcdefghijklmnopqrstuvwxyz
ALPHABET
ABCDEFGHIJKLMNOPQRSTUVWXYZ
Capturing works in all modes: code, echo, internal-command, or external-command mode.
C Preprocessor Emulation
The -D switch defines elements of %D
. If you do not specify a
value, the value true
(a constant in the generated script) will be used.
The following commands work mostly analogously to their C preprocessor
counterparts.
<?:define NAME ?>
<?:undef NAME ?>
<?:ifdef NAME ?>
<?:ifndef NAME ?>
<?:else ?>
<?:endif ?>
<?:if NAME CONDITION ?>
<?:elsif NAME CONDITION ?>
(elif
andelseif
are synonyms)
For example:
<?:ifdef NAME ?>
foo
<?:endif ?>
is the same as the more verbose script:
<? if(defined($D{NAME})) { ?>
foo
<? } ?>
If and Elsif
Tests with <?:if NAME ... ?>
and <?:elsif NAME ... ?>
have two restrictions:
- If
$D{NAME}
does not exist, the test will befalse
regardless of the condition...
. - The
...
must be a valid Perl expression when$D{NAME}
is added to the beginning, with no parentheses around it.
For example, <?:if FOO eq "something" ?>
(note the whitespace before ?>
!)
will work fine. However, if you want to test (FOO+1)*3
, you will need
to use the full Perl code <? if( (FOO+1)*3 == 42 ) { ... } ?>
instead of
<?:if ?>
and <?:endif?>
.
Other Features
Custom Preprocessors
It's possible to create your own pre/post-processors in a <?:macro ?>
block
using $PSelf->AddPreprocessor
and $PSelf->AddPostprocessor
.
This feature is used in BigBenBox for
generating code in the C programming language.
Future
Suggestions are welcome.
Highlighting in your editor
Vim
To make highlight PerlPP insets in Vim, add this to ~/.vimrc
autocmd colorscheme * hi PerlPP ctermbg=darkgrey ctermfg=lightgreen
and create corresponding ~/.vim/after/syntax/FILETYPE.vim
syntax region PerlPP start='<?' end='?>' containedin=ALL
FILETYPE can be determined with :set ft?
Copyright
Distributed under the MIT license --- see LICENSE.txt for details. By Andrey Shubin (d-ash) and Chris White (CXW; cxw42); additional contributions by Mohammad S Anwar (MANWAR; manwar).