Phrep handles C-style preprocessing #directives.
- They must be written leftmost in source code.
- Are recognized context-insensitive, within or before
<?php
code sections. - Are mostly equivalent to their C counterparts.
- Can be prefixed with up to two ␣␣ spaces.
- May also be doubly-escaped as
// #directive
or# #directive
.
Conditional #if
...#endif
sections can mask both literal output and
other directives. And they can be nested.
All directives are processed in the first file transformation phase, where
output (literal and from #include
files) simply gets merged. Constant
substitution however happens in the second phase - on the assembled code.
#define
const value
Declares a token, constant, standard macro or complex macro.
Literal defines without value will just set a constant flag
#define HAVE_FLAG
Those are mostly used for conditional sectioning (
#ifdef
checks).Constant declarations obviously can take a value:
#define PKG_VERSION 1.2.0
The value can be a raw literal. Non-numeric values are often better defined as string right away:
#define PKG_OPT_A "alpha-1.2"
This allows easier use directly in PHP code such as
$var = PKG_OPT_A;
Standard/basic macros are simply defined constants with a parameter list after the name, and a substitution corpus:
#define TMUL(x,y) (2*x+3*y)
When used in PHP code sections, any parameters (values or expressions) to the constant name will be packed into the substitution code.
Complex macros take an additional
@
to their name:#define PHPMUL@(x,y) {return "2*$x+3*$y";}
Notably now the substitution code is an anonymous function.
#undef
const
Removes a defined constant or macro name.
Usage is quite obviously:
#undef USE_SQLITE
Note that it's not an error to undefine a constant that isn't defined. Existing constants can just be redefined as well.
#ifdef
const
Probes for existing flags or constants
And obviously starts a conditional section.
#ifdef USE_DOG print "Yes, this is dog."; #endif
Code will only be output until the endif.
#ifndef
const
Tests if a flag/constant/macro is not defined yet.
Is often used to conditionally define placeholders:
#ifndef VERSION #define VERSION "0.0.0-dev" #endif
#if
expr
An #if
directive obviously can also start a conditional section, or a
nested condition.
Unlike the basic
#ifdef
or#ifndef
it can do some operations and comparisons.#if VERSION > 1.0
It may also utilize the
defined()
function to simulate#ifdef
within an expression.#if ! defined(USE_FLAG)
The constant name may be quoted or a literal.
See expressions on further allowed constructs,
such as =~
regex comparisons, or *
+
-
arithmetics, or ==
and &&
or ||
operations.
#elif
expr
A conditional else leave can be expressed with #elif
.
An
#elif
allows the same operations as an#if
#ifdef FLAG // flag defined #elif FLAG == 0 // it's obviously undefined #endif
There can be multiple #elif
s between the initial #if
and
the closing #else
/#endif
.
#else
If all previous conditional sections failed, then the source/directives
between #else
and #endif
will be processed.
An else
is entirely optional, so a conditional section #if
..#endif
can be completely inactive.
#endif
Ends a conditional section. Note that #if/#endif constructs can be
nested. Which is why space-indenting ␣#if
directives is useful.
#include
file
Files and dependencies can be interpolated using an #include
directive.
It accepts the filename as literal string, or enclosed in <...>
or "..."
or '...'
quotes.
PHP includes have their surrounding
<?php
and?>
tags stripped.<?php #include <config.ph>
So this won't lead to syntactically invalid output, even when includes carry PHP open/close tags of their own.
Output from some include file is suppressed per default.
#include "silent.h"
File extensions
*.h
and*.def
for instance are commonly used by C-style headers, and aren't merged into the output.
Some care is taken to step around commented-out PHP include() statements.
Parenthesis as well as a trailing
;
semicolon will make an "#include" comment go ignored as non-directive.Some edge cases are hard to detect though. Comment and syntax mashups like "
#include 'file.php' or false
" may incorrectly be recognized as directive. Therefore future phrep version may prohibit the use of single quotes at least.It's mostly irrelevant in practice. Since most code nowadays just uses
include_once()
orrequire_once()
anyway.
Files are always searched among the defined include path, as set per -I
or #pragma(dirs=...)
directive.
#pragma
(opt=val)
The #pragma
directive sets a few global and/or local
processing flags.
For instance suppressing output for the current or subincludes:
#pragma(output=0) #include <hidden.php>
The more GCC-compatible syntax is also allowd:
#pragma option value
See pragma options for a list of available options.
#stderr
msg
Outputs a warning or error message. This should obviously be used within conditional sections etc. (Paired with pragma fail even).
Why? Unike in CPP this isn't called #error
or #warning
, because both
seem entirely too likely to preexist as PHP code comments.
#srcout
code
Writes literal code to the final output.
Purpose is to keep unpreprocessed code running, if macros are used:
#ifdef RX@ #srcout $code = RX@($var =~ $expr); #else $code = preg_match($expr, $var, $m) && $m && !empty($m[1]) ? intval($m[1]) : NULL; #endif
This is a phrep-specific extension. Mozilla and CCPP agree on #literal
for
this purpose. However #srcout
seems even less likely to accidentially
reside in existing PHP comments.
Aliases
#macro
Is just an alias for #define
.
#macro X(y) (y+CONST_X)
Again see: macro.
Ignored directives
Phrep ignores following directive extensions or variations:
#error
(C)#warning
(C)#line
(using#pragma line
instead)#include_next
(GCC)#import
(ObjC)#using
(MSVC)#region
(C#)#endregion
(C#)#includephp
(from CCPP)#literal
(CCPP, Mozilla)#elifdef
(Mozilla)#elifndef
(Mozilla)#defeval
(GPP)#eval
(GPP)#exec
(GPP)#ifeq
(GPP)#ifneq
(GPP)#mode
(GPP, mostly like #pragma)
Extending the MacroProccessor
is trivial however. Each new directive just
needs an entry in RX_DIRECTIVES
, a switch case in block()
, and a handler
method like d_import()
.