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.


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: perl [options] [filename]
    -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
    -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
  • uses 5.010001, strict, and warnings
  • provides constants true (=!!1) and false (=!!0) (with use 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.


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 ?>
<? } ?>


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 ?>
<? } ?>


Hello world (again).
number: 0
number: 1
number: 2
number: 3
number: 4

Line breaks using <?/

The example

foo<? print "bar";?>

produces the output


Adding the /, to make

foo<?/ print "bar";?>

produces the output


So <?/ ... ?> is effectively a shorthand for <? print "\n"; ... ?>.

External commands using <?!

The example

<?! echo Howdy! ?>

produces the output


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 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 foo bar ?>

Replaces word prefixes in the following output. In this case words like fooSomeWord will become barSomeWord.


<?: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 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.


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


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 and elseif are synonyms)

For example:

<?:ifdef NAME ?>
<?:endif ?>

is the same as the more verbose script:

<? if(defined($D{NAME})) { ?>
<? } ?>

If and Elsif

Tests with <?:if NAME ... ?> and <?:elsif NAME ... ?> have two restrictions:

  • If $D{NAME} does not exist, the test will be false 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.


Suggestions are welcome.

Highlighting in your editor


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?

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).