4.1 User Code Blocks

User code blocks begin following a "<<" token and end with a ">>" token found at the end of a line. All text lines in the code block are copied verbatim into the output file.

4.1.1 Standalone Code Blocks

C example:

<<
#include <stdio.h>
>>

D example:

<<
import std.stdio;
>>

Standalone code blocks are emitted early in the output file as top-level code outside the context of any function. Standalone code blocks are a good place to include/import any other necessary supporting code modules. They can also define helper functions that can be reused by lexer or parser user code blocks. They are emitted in the order they are defined in the grammar file.

For a C target, the word "header" may immediately follow the "<<" token to cause Propane to emit the code block in the generated header file rather than the generated implementation file. This allows including another header that may be necessary to define any types needed by a ptype directive, for example:

<<header
#include "mytypes.h"
>>

4.1.2 Lexer pattern code blocks

Lexer code blocks appear between << and >> markers following a drop, token, or pattern expression. User code in a lexer code block will be executed when the lexer matches the given pattern. Assignment to the $$ symbol will associate a parser value with the lexed token. This parser value can then be used later in a parser rule.

The input text positions of the matched token can also be accessed from within a lexer code block. Each of these positions is an instance of the p_position_t structure (see p_position_t), which contains 1-based row and col fields. The start position of the matched token is accessed with ${position}, and the end position of the matched token is accessed with ${end_position}.

Example:

token integer /\d+/ <<
  printf("integer token on row %d, col %d\n",
      ${position}.row, ${position}.col);
  $$ = parse_integer(match, match_length);
>>

4.1.2.1 C/C++ lexer code block arguments

The lexer code block is passed the following arguments:

Example:

ptype long;

token integer /\d+/ <<
  long v = 0;
  for (size_t i = 0u; i < match_length; i++)
  {
    v *= 10;
    v += (match[i] - '0');
  }
  $$ = v;
>>

4.1.2.2 D lexer code block arguments

The lexer code block is passed the following arguments:

ptype ulong;

token integer /\d+/ <<
  ulong v;
  foreach (c; match)
  {
    v *= 10;
    v += (c - '0');
  }
  $$ = v;
>>

4.1.3 Parser rule code blocks

Example:

E1 -> E1 plus E2 << $$ = $1 + $3; >>

Parser rule code blocks appear following a rule expression. User code in a parser rule code block will be executed when the parser reduces the given rule. Assignment to the $$ symbol will associate a parser value with the reduced rule. Parser values for the rules or tokens in the rule pattern can be accessed positionally with tokens $1, $2, $3, etc...

The input text positions for the reduced rule and for the individual rule components can also be accessed from within a parser rule code block. Each of these positions is an instance of the p_position_t structure (see p_position_t), which contains 1-based row and col fields.

The start position of the overall reduced rule is accessed with ${$.position}, and the end position of the overall reduced rule is accessed with ${$.end_position}.

The start and end positions of an individual rule component are accessed positionally with ${N.position} and ${N.end_position}, where N is the 1-based index of the component (${1.position} for the first component, ${2.position} for the second, and so on).

Example:

Assignment -> ident equals Expr <<
    printf("assignment on row %d, col %d\n",
        ${$.position}.row, ${$.position}.col);
    printf("target identifier ends on row %d, col %d\n",
        ${1.end_position}.row, ${1.end_position}.col);
    printf("expression starts on row %d, col %d\n",
        ${3.position}.row, ${3.position}.col);
>>

A rule or rule component that allows for an empty match may not have valid positions. In this case the position should be checked for validity before its row and col fields are used (see p_position_valid). For C targets this can be accomplished with if (p_position_valid(${$.position})) and for D targets this can be accomplished with if (${$.position}.valid).

Parser rule code blocks are not available in tree generation mode. In tree generation mode, a full parse tree is automatically constructed in memory for user code to traverse after parsing is complete.