Phrep allows both standard/basic macros and more complex code substitutions.
Basic macros work pretty much like in CPP. They just take a list of argument names, and a substitution code as value.
Macro declaration:
#define MUL(a,b) (a*b)
And will expand in code references such as:
$var = MUL(7, $x*5);
With their params expanded:
$var = (7*$x*5);
The macro definition doesn't use sigil $
prefixes for the arguments
nor the substitution corpus. But when referencing a macro, either argument
can be a literal PHP expression, including variable names of course.
Which is why parameters often/usually should be parenthesized in substitution
blocks (a)*(b)
.
Code templating
Consider a more realistic example, like templating an object property getter:
Declarare the macro template with
name
argument, and reference it twice in the substitution body:#macro GETTER(name) function get##name() { return $this->##name; }
For code templates such as:
class xyz { private $prop; GETTER(prop) private $y; GETTER(y)
Which expands to a stub getter function for the named property.
See examples/ for more practical variants, with implicit docblocks and type assertions etc.
Code expansion features
The ##
is a macro concatenation operator.
It's less often needed for PHP code, because most syntax constructs already impose word boundaries.
But
param1 ## param2
in the macro substitution block would simply "glue" two argument literals together. (Somewhat like PHPs varvars.)
There's also support for the #
stringification prefix.
It'll expand any
#arg1
in the replacement corpus into a PHP string.For instance
M(abc)
could become"abc"
.In essence it makes these two macro definitions somewhat equivalent:
#define STRINGIFY(x) #x
#define STRINGIFY(x) " ## x ## "
The
#
in phrep actually utilizesvar_export
, so would use single quotes.
Variadic params are supported by declaring a parameter with
#define M(x1,args...)
.
It's mostly useful with the stringify syntax, such that
#args
in the substitution block would becomearray('x2', 'x3', 'x4')
.Varargs become more useful with complex macros though.
Multipass
Macro-in-macro expansion is also supported.
#define RATE(x) MAX(x, CONST*x)
It needs to be enabled however:
#pragma(multipass=5)
And it's only intended for basic macros. (But will implictly also expand preprocessor-defined constants in macros.)
Unsupported features
There are also a few C specifica that don't make sense for PHP source and may remain unsupported:
CPPs
__VA_ARGS__
however isn't understood. (Use a namedvargs...
parameter instead).Weird partial (#undef+redefine) macros.
Differences token and regex mode
Constants and macros are replaced with values or code in the substitution phase. There can be differences in how arguments are passed/interpolated into macros.
The token mode looks for correctly paired
(
parens)
within anyMACRO(…)
orMACRO@(…)
reference, and splits up on top-level,
comma separators. Therefore the macro arguments may themselves contain full expressions likeMACRO(strpos($x,$y),cb(1,2,3))
.For regex lookups, macros can also be used outside of PHP code context. The discovery of
MACRO(…)
references however also checks for correctly paired(
parens)
. And it now takes a little care now to honor strings and comment arguments even. But it's not as precise as the tokenizer mode.
For macro argument splitting on , commas a quick tokenization call is used again, so should work similarily.
Currently neither mode handles comma separators in not-quite-PHP expressions
like MACRO(foo{3,4,5}, $ao[0,1,2])
. (Would only be relevant for complex
macros, or wrapping whole function/class declarations perhaps.)
And empty-parens macros like #define WRONG()
aren't recognized
as macro, and really should just be a plain constant. (Well, actually it's
recognized, but expansion currently disabled.)