phrep

Artifact [e9a4a8163d]
Login

Artifact e9a4a8163d8ff97306abae2ab17a1e1f1296e2f0:

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