The HFL Preprocessor

Preprocessing HFL files

  • The HFL compiler has a built-in preprocessor, which can be used to generate different versions of the output based upon preprocessor variables and macros.
  • When it is used, the preprocessor runs as a step before main compilation, and it generates a temporary file that is a modified version of the original. Then the modified version becomes the actual file compiled by the compiler.
  • The preprocessor is mostly a convenience, as almost any editing action that can be done using the preprocessor can be achieved using the features of the HFL language itself. The most common use of the preprocessor is the conditional-compile constructs #ifdef and #ifndef
  • The preprocessor can be useful when creating multiple versions of pages, where the content of the pages can have major portions that are different.
  • An example would be in creating a different version of documentation for Windows, Mac and Linux, because there can be major differences in descriptions, instruction and examples. The different sections for each operating system could be separated by #ifdef statements, so that the source files are easier to read and maintain.
  • The preprocessor involves an extra step in compilation, and can result in a measurable performance loss. Since server-side compilation is generally performance-critical, it is not advisable to use the preprocessor.
  • The preprocessor can be invoked two ways: via the '-pp' command-line option, and via the 'pre_process' block statement.

Preprocessor directives

  • The HFL preprocessor supports the following standard C preprocessor directives. All preprocessor directives must begin at the first character of their lines, with no leading spaces.
  • #define <pp-var> [<value>]
    • Defines a preprocessor variable (not an HFL variable) that can be tested for by preprocessor conditional directives. If the optional <value> is specified, the variable will have that value. Otherwise it will default to the value 1.
    • EXAMPLES:
    • #define VER_DEBUG
      #define ADVANCED_OPTIONS
      #define VERSION WINDOWS
      #define VERSION LINUX

  • #ifdef <pp-var>
    <true-code>
    [#else]
    <false-code>
    #endif
    • Defines a preprocessor conditional inclusion block, that depends upon whether the preprocessor variable <pp-var> is defined or not.
    • If the variable is defined, it will include the block of code of the <true-code> section, up to the next #else or #endif line.
    • Otherwise, if an #else line is specified, it will include the block of code of the <false-code> section, up to the next #endif line.
  • EXAMPLES:
    • #ifdef VER_DEBUG
        // Debug code
        ...
      #endif ADVANCED_OPTIONS

      #ifdef ADVANCED_OPTIONS
        // Advanced option code
        ...
      #else !ADVANCED_OPTIONS
        // Regular code
        ...
      #endif ADVANCED_OPTIONS

  • #ifndef <pp-var>
    <true-code>
    [#else]
    <false-code>
    #endif
    • Defines a preprocessor conditional inclusion block, that depends upon whether the preprocessor variable <pp-var> is not defined or is.
    • If the variable is not defined, it will include the block of code of the <true-code> section, up to the next #else or #endif line.
    • Otherwise, if an #else line is specified, it will include the block of code of the <false-code> section, up to the next #endif line.
  • EXAMPLES:
    • #ifndef VER_DEBUG
        // Non-debug version code
        ...
      #endif ADVANCED_OPTIONS

      #ifndef ADVANCED_OPTIONS
        // Regular code
        ...
      #else ADVANCED_OPTIONS
        // Advanced option code
        ...
      #endif !ADVANCED_OPTIONS

  • #if <pp-expr>
    <true-code>
    [#else]
    <false-code>
    #endif
     
    where:
    <pp-expr>
    :=
    <pp-var> <comp> <value>
    <comp>
    :=
    ==
    |
    !=
    |
    >
    |
    <
    |
    >=
    |
    <=
    • Defines a preprocessor conditional inclusion block, that depends upon the result of evaluating the preprocessor expression <pp-expr>.
    • If the expression tests non-zero and non-empty, it will include the block of code of the <true-code> section, up to the next #else or #endif line.
    • Otherwise, if an #else line is specified, it will include the block of code of the <false-code> section, up to the next #endif line.
  • EXAMPLE:
    • #if VERSION == WINDOWS
        // Code for Windows version
        ...
      #endif VERSION == WINDOWS

      #if VERSION == LINUX
        // Code for Linux version
        ...
      #endif VERSION == LINUX

Invoking the preprocessor from the command line

  • The '-pp' command-line option will cause the preprocessor to be run on the main source file.
  • The preprocessor command-line option has the following syntax:
    • -pp <pp-option>*
       
      where:
      <pp-option>
      :=
      <pp-define>
      | <pp-expand>
      | <pp-expanddef>
      <pp-define>
      :=
      -D.<var>[=<value>]
      <pp-expand>
      :=
      -x | -expand
      <pp-expanddef>
      :=
      -xd | -expanddef
    • The -D. option defines a preprocessor variable, that has the same effect as a '#define' preprocessor statement. If the '= <value>' sub-option is specified, it will assign that value to the preprocessor variable.
    • The -x option specifies that all preprocessor expansions will be performed, including expanding defined preprocessor variables, preprocessor macros, and '#ifdef' statements. This is the normal option to pair with the '-pp' command-line option.
    • The -xd option specifies that preprocessor variables created by either '#define' preprocessor statements or '-D.<var>' command-line options will be expanded within the body of the source text. By default, the preprocessor does no expansions and ignores '#ifdef' statements.
    • EXAMPLES:
    • hflc hello.hfl hello.html -fo.w+-v -pp

      hflc doc.hfl doc.html -fow+-v -pp+-x -D.OS=LINUX

      hflc doc2.hfl doc.html -fo.w+-v -pp+-xd -D.VER=RELEASE

The pre_process block statement

  • The 'pre_process' block statement invokes the preprocessor on the contents of the block. It also causes the preprocessor to run on the main source file.
  • SYNTAX:
    • pre_process { <preproc-line>+ }
       
      where:
      <preproc-line>
      :=
      A single line of text, possibly with
      preprocessor directives or expansions.
  • This is because option files run before the main compilation begins, and thus are the only place where the statement could affect or invoke the preprocessor upon the main content.
  • The lines within the statement's block must conform to the syntax rules for the preprocessor to recognize them. For example, this means that directives such as #define, #ifdef and #ifndef must have no spaces to their left on the line.
    • EXAMPLE:
    • // If creating Linux version, define VER_LINUX and run preprocessor
        if (env("VER_LINUX")) {
          pre_process {
      #define VER_LINUX
            }
          }

Previous: Compiling HFL Files Next: Parse mode