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.

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"'].

Note the C-style `args...` instead of PHPs `...args`.

Token parameters

Apart from named parameters $i, $str and $args, there's always an
implied $token parameter to complex macro callbacks as well.

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 for declaring any handler funcs.
They could later be converted back to inline #defines and .def includes
for distribution.

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.

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 , 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...",$var ]

[ "&literal=",$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 phrep. 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 phrep the @ 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 phrep 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.