phrep

Artifact [0245959c95]
Login

Artifact 0245959c9581a6bea867cbb305293170290607df:

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