phrep

Artifact [7a5015c4fd]
Login

Artifact 7a5015c4fdd3efd7136ed607da529881cf7fb511:

Wiki page [directives] by mario 2015-04-28 22:16:29.
D 2015-04-28T22:16:29.150
L directives
N text/x-markdown
P 45b8d3a48fcdb20f09f748b3e4e2a0a5204f5857
U mario
W 7605
[Phrep](wiki/phrep) handles C-style preprocessing *#directives*.

  * They must be written leftmost in source code.
  * Are recognized *context-insensitive*, within or before `<?php` code
    sections.
  * Are mostly equivalent to their C counterparts.
  * Can be prefixed with up to two ␣␣ spaces.
  * May also be doubly-escaped as `// #directive` or `# #directive`.

Conditional `#if`...`#endif` sections can mask both literal output and
other directives. And they can be nested.

All directives are processed in the first file transformation phase, where
output (literal and from `#include` files) simply gets merged. Constant
substitution however happens in the second phase - on the assembled code.

<style>
  .content h2 {
     margin-top: 25pt;
     font-size: 170%;
  }
  .content h2 em {
     font-weight: 200;
     color: #7b6;
  }
  .content h1 {
     margin-top: 25pt;
  }
</style>


## `#define` *const* *value*

Declares a token, constant, standard macro or complex macro.

  * Literal defines without value will just set a constant flag

        #define HAVE_FLAG

    Those are mostly used for conditional sectioning (`#ifdef` checks).

  * Constant declarations obviously can take a value:

        #define PKG_VERSION 1.2.0

    The value can be a raw literal. Non-numeric values are often better
    defined as string right away:

        #define PKG_OPT_A "alpha-1.2"

    This allows easier use directly in PHP code such as `$var = PKG_OPT_A;`

  * [Standard/basic macros](wiki/macro) are simply defined constants with a
    parameter list after the name, and a substitution corpus:

        #define TMUL(x,y) (2*x+3*y)

    When used in PHP code sections, any parameters (values or expressions)
    to the constant name will be packed into the substitution code.

  * [Complex macros](wiki/complex) take an additional `@` to their name:

        #define PHPMUL@(x,y) {return "2*$x+3*$y";}

    Notably now the substitution code is an anonymous function.


## `#undef` *const*

Removes a defined constant or macro name.

  * Usage is quite obviously:

        #undef USE_SQLITE

    Note that it's not an error to undefine a constant that isn't defined.
    Existing constants can just be redefined as well.


## `#ifdef` *const*

Probes for existing flags or constants

  * And obviously starts a conditional section.

        #ifdef USE_DOG
           print "Yes, this is dog.";
        #endif

    Code will only be output until the endif.


## `#ifndef` *const*

Tests if a flag/constant/macro is *not* defined yet.

  * Is often used to conditionally define placeholders:

        #ifndef VERSION
        #define VERSION "0.0.0-dev"
        #endif


## `#if` *expr*

An `#if` directive obviously can also start a conditional section, or a
nested condition.

  * Unlike the basic `#ifdef` or `#ifndef` it can do some operations and
    comparisons.

        #if VERSION > 1.0

  * It may also utilize the `defined()` function to simulate `#ifdef`
    within an expression.

        #if ! defined(USE_FLAG)

    The constant name may be quoted or a literal.

See [expressions](wiki/expressions) on further allowed constructs,
such as `=~` regex comparisons, or `*` `+` `-` arithmetics, or `==`
and `&&` or `||` operations.


## `#elif` *expr*

A conditional else leave can be expressed with `#elif`.

  * An `#elif` allows the same operations as an `#if`

        #ifdef FLAG
           // flag defined
        #elif FLAG == 0
           // it's obviously undefined
        #endif

There can be multiple `#elif`s between the initial `#if` and
the closing `#else`/`#endif`.


## `#else`

If all previous conditional sections failed, then the source/directives
between `#else` and `#endif` will be processed.

An `else` is entirely optional, so a conditional section `#if`..`#endif`
can be completely inactive.


## `#endif`

Ends a conditional section. Note that #if/#endif constructs can be
nested.  Which is why space-indenting `␣#if` directives is useful.


## `#include` *file*

Files and dependencies can be interpolated using an `#include` directive.
It accepts the filename as literal string, or enclosed in `<...>` or `"..."`
or `'...'` quotes.

  * PHP includes have their surrounding `<?php` and `?>` tags stripped.

        <?php
        #include <config.ph>

    So this won't lead to syntactically invalid output, even when includes
    carry PHP open/close tags of their own.

  * Output from some include file is suppressed per default.

        #include "silent.h"

    File extensions `*.h` and `*.def` for instance are commonly used
    by C-style headers, and aren't merged into the output.

Some care is taken to step around commented-out PHP include() statements.

   * Parenthesis as well as a trailing `;` semicolon will make an "#include"
     comment go ignored as non-directive.

   * Some edge cases are hard to detect though. Comment and syntax mashups like
     "`#include 'file.php' or false`" may incorrectly be recognized as directive.
     Therefore future phrep version may prohibit the use of single quotes at least.

   * It's mostly irrelevant in practice. Since most code nowadays just uses
     `include_once()` or `require_once()` anyway.

Files are always searched among the defined include path, as set per `-I`
or `#pragma(dirs=...)` directive.



## `#pragma` *(opt=val)*

The `#pragma` directive sets a few global and/or local
[processing flags](wiki/pragma).

  * For instance suppressing output for the current or subincludes:

        #pragma(output=0)
        #include <hidden.php>

  * The more GCC-compatible syntax is also allowd:
 
        #pragma option value

See [pragma options](wiki/pragma) for a list of available options.


## `#stderr` *msg*

Outputs a warning or error message. This should obviously be used within
conditional sections etc. (Paired with pragma fail even).

*Why?* Unike in CPP this isn't called `#error` or `#warning`, because both
seem entirely too likely to preexist as PHP code comments.


## `#srcout` *code*

Writes literal code to the final output.

  * Purpose is to keep unpreprocessed code running, if macros are used:

        #ifdef RX@
         #srcout  $code = RX@($var =~ $expr);
        #else
         $code = preg_match($expr, $var, $m) && $m && !empty($m[1]) ? intval($m[1]) : NULL;
        #endif

This is a *phrep*-specific extension. Mozilla and CCPP agree on `#literal` for
this purpose. However `#srcout` seems even less likely to accidentially
reside in existing PHP comments.



# Aliases

## `#macro`

Is just an alias for `#define`.

    #macro X(y) (y+CONST_X)

Again see: [macro](wiki/macro).



# Ignored directives

Phrep ignores following directive extensions or variations:

  * `#error` (C)
  * `#warning` (C)
  * `#line` (using `#pragma line` instead)
  * `#include_next` (GCC)
  * `#import` (ObjC)
  * `#using` (MSVC)
  * `#region` (C#)
  * `#endregion` (C#)
  * `#includephp` (from CCPP)
  * `#literal` (CCPP, Mozilla)
  * `#elifdef` (Mozilla)
  * `#elifndef` (Mozilla)
  * `#defeval` (GPP)
  * `#eval` (GPP)
  * `#exec` (GPP)
  * `#ifeq` (GPP)
  * `#ifneq` (GPP)
  * `#mode` (GPP, mostly like #pragma)

Extending the `MacroProccessor` is trivial however. Each new directive just
needs an entry in `RX_DIRECTIVES`, a switch case in `block()`, and a handler
method like `d_import()`.


Z b8204c3d726699777efa2d1a0f8a1a82