Wiki page
[macro] by
mario
2015-03-10 22:36:53.
D 2015-03-10T22:36:53.991
L macro
N text/x-markdown
P b06e60c634a7098585864dc69b276c1b6339710b
U mario
W 4994
Phrep allows both standard/basic macros and [more complex](wiki/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)`.
<div class=note>
Obviously macros <b>don't make sense</b> for functionality that can be readily
expressed with plain PHP functions. Instead their purpose is templating
more lengthy constructs.
</div>
## 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/](dir?name=examples) for more practical variants, with
implicit docblocks and type assertions etc.
<div class=note>
Again, this is a somewhat trivial example. And code templating that
can be done by IDEs and doesn't need project-specific customizations
or rule-based updating should probably not use macro preprocessing.
</div>
# 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 utilizes `var_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 become `array('x2', 'x3', 'x4')`.
* Varargs become more useful with [complex](wiki/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 named `vargs...`
parameter instead).
* Weird partial ([#undef+redefine](https://gcc.gnu.org/onlinedocs/cpp/Undefining-and-Redefining-Macros.html)) macros.
# Differences <kbd>token</kbd> and <kbd>regex</kbd> 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 <kbd>token</kbd> mode looks for correctly paired `(` parens `)` within
any `MACRO(…)` or `MACRO@(…)` reference, and splits up on top-level `,`
comma separators. Therefore the macro arguments may themselves contain
full expressions like `MACRO(strpos($x,$y),cb(1,2,3))`.
* For <kbd>regex</kbd> 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 <kbd>,<kbd> 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.)
Z 5c11f0a11a448227f0d527297c8eba0a