Wiki page
[complex] by
mario
2015-04-28 22:16:20.
D 2015-04-28T22:16:20.392
L complex
N text/x-markdown
P 593943f3390d06012da7259a4b39ebbd252b8c49
U mario
W 5924
[Standard macros](wiki/macro) just interpolate expressions literally. But
phrep also allows for **"complex macros"**, which utilize PHP callbacks for
more clever code generation.
They can be defined by attaching an `@` to macro names. The declaration body
becomes a PHP code generation callback then.
* Declaration:
#define MSTR@(i,str) {return '"' . str_repeat($str,$i) . '"';}
Note that the macro name requires literal parameter names. The PHP
replacement code however uses PHP sigil `$` prefixed variables.
* Macro usage:
$var = MSTR@(3, abc);
* Would be replaced to:
$var = "abcabcabc";
The callback obviously took care of returning the literal `abc` string
enclosed in double quotes for PHP code context.
<div class=note>
You may ignore the following paragraphs. It's actually rather simple and
obvious to utilize.
</div>
# Function body
The substitution code should be enclosed in `{`…`}` curly braces. This
is mostly for legibility / decorative purposes. Technically it's not
required:
#macro XY@(i,str) return $i \
. " . 'and' . " \
. $str;
Multi-line #defines however strictly need backslash-escaped <kbd>\\</kbd>
<kbd>↲</kbd> linebreaks.
# Macro parameters
The macro arguments, `$i` and `$str` here, are stringified expression
literals. The above example macro reference used `3` as integer parameter.
Would the macro have been called as `MSTR@(2+1, abc)`, then `$i` would
contain `2+1` however.
Which is exactly how standard macros receive their parameters. This
approach can incur extra work for code generation callbacks. With some
extra work, it however permits to act on more involving expressions.
For example an `EACH@($a && $b && $c)` macro could explode its argument
just on `&&`, then reshuffle the remaing expressions.
See the [NULLSAFE@ example](dir/examples).
## Variadic functions
You can also declare a variadic parameter.
#define LIT@(x,y,args...) { return var_export($args,1); }
There `$args` could hold an array of stringified extra arguments or
expressions, such as `['arg3', '$expr+4', '"arg5"']`.
<div class=note>
Note the C-style `args...` instead of PHPs `...args`.
</div>
## Token parameters
Apart from named parameters `$i`, `$str` and `$args`, there's always an
implied `$token` parameter to complex macro callbacks as well.
It holds a list of parser tokens from *all* macro arguments.
* [`T_VARIABLE` **$i**, **+**, `T_LNUMBER` **2**, <kbd>,</kbd>,
`T_BOOL_CAST`, `T_VARIABLE` **$bool**, <kbd>,</kbd>,
`T_STRING` **floatval**, `T_WHITESPACE`, **(**, `T_DNUMBER` **1.002**, **)**]
Working on a token string is of course more involving. Therefore you often
want to split out the processing to an externalized helper function even:
#define CONFIGMACRO@(expr) { return \my\macro\func($expr); }
Utilize the [config file](wiki/config-file) for declaring any handler funcs.
They could later be converted back to inline `#defines` and `.def` includes
for distribution.
<div class=note>
Plain macros or using stringified parameters already suffice for most cases.
Complex token transformations only start to make sense if you want to introduce
new syntax variations/shortcuts.
</div>
### Macro helpers
There are a few built-in helper functions for token lists:
* Call `io\Token::args()` to split the linear `$token` list into arguments.
It breaks the token stream on <kbd>,</kbd> comma separators into
sub-arrays, one per macro argument. (This was changed in 0.3.0; as the
presplitted list seemed less commonly needed.)
* With `io\Token::join()` you can stringify the literals from a token list
again.
* And `io\Token::split_expr()` generates a list of expression pairs. Each
array entry holds a conflated literal and a variable (if any exists,
last may be empty).
Which usually looks like:
* [ "raw..."<kbd>,</kbd> `$var` ]
* [ "&literal="<kbd>,</kbd> `$var2` ]
This is used by the `HTML@` macro for example. (Future versions might
allow to separate out more diverse token types as expressions.)
It's also planned to prepare a hook to nikic/PHP-Parser in later versions.
To simplify working with expression trees, instead of plain token lists.
# Why `@` for macro names?
The `@` suffix is used for complex macro names in <kbd>phrep</kbd>. And
that's both for *visualization* and because it's easier to uncover combos
of `T_STRING` and `@` tokens. They also can't overlap with existing code/syntax.
* In plain PHP, the `@` is just the *silencing operator* - and can only
prefix expressions all by itself. → Think of `@$var` more like
`@($var)` with a silently implied `silence@($var)`.
* In <kbd>phrep</kbd> the <b><code>@</code></b> becomes more of a "**shield
operator**". It still masks expressions, but in essence applies a
*decorator*. → Now literally written as `nullsafe@($expr)`.
* Since PHP likely won't be getting decorator wrappers and syntax support
anytime soon, implementing it in <kbd>phrep</kbd> seems like the next best
thing. Should there ever be a decorator interface (with userland AST hooks
even?!), complex MACRO@s could be obsoleted mostly.
### Other uses
Now complex macros can also be utilized to implement some more weird syntax
constructs. For instance they can semi-parse raw expression sections.
* For example a `SQL@( SELECT * FROM x WHERE y = $y )` could be expanded
into a prepared statement list / array / or query building chain even.
* Or provide safer `HTML@( <tag>$var<mix $ing> )` mixing rather easily.
(Utilizes the `io\Token::split_expr()` helper.)
See [examples/config/](dir/examples/config).
Z 7cd2a34fe9e85c0a4e5f296512d7fabf