Wiki page
[directives] by
mario
2015-04-28 22:16:29.
D 2015-04-28T22:16:29.150
L directives
N text/x-markdown
P 45b8d3a48fcdb20f09f748b3e4e2a0a5204f5857
U mario
W 7605
[Phrep](wiki/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.
<style>
.content h2 {
margin-top: 25pt;
font-size: 170%;
}
.content h2 em {
font-weight: 200;
color: #7b6;
}
.content h1 {
margin-top: 25pt;
}
</style>
## `#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](wiki/macro) 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](wiki/complex) 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](wiki/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()` or `require_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](wiki/pragma).
* 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](wiki/pragma) 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](wiki/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()`.
Z b8204c3d726699777efa2d1a0f8a1a82