Phrep handles C-style preprocessing #directives.
- They must be written leftmost in source code.
- Are recognized context-insensitive, within or before
<?phpcode sections. - Are mostly equivalent to their C counterparts.
- Can be prefixed with up to two ␣␣ spaces.
- May also be doubly-escaped as
// #directiveor# #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_FLAGThose are mostly used for conditional sectioning (
#ifdefchecks).Constant declarations obviously can take a value:
#define PKG_VERSION 1.2.0The 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_SQLITENote 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."; #endifCode 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
#ifdefor#ifndefit can do some operations and comparisons.#if VERSION > 1.0It may also utilize the
defined()function to simulate#ifdefwithin 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
#elifallows the same operations as an#if#ifdef FLAG // flag defined #elif FLAG == 0 // it's obviously undefined #endif
There can be multiple #elifs 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
<?phpand?>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
*.hand*.deffor 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 lineinstead)#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().