5.2 Types

5.2.1 p_code_point_t

The p_code_point_t type is aliased to a 32-bit unsigned integer. It is used to store decoded code points from the input text and perform lexing based on the grammar's lexer patterns.

5.2.2 p_context_t

Propane defines a p_context_t structure type. The structure is intended to be used opaquely and stores information related to the state of the lexer and parser. A p_context_t instance is allocated and initialied with the p_context_new() function.

5.2.3 p_position_t

The p_position_t structure contains two fields: row and col. These fields contain the 1-based row and column describing a parser position.

For D targets, the p_position_t structure can be checked for validity by querying the valid property.

For C targets, the p_position_t structure can be checked for validity by calling p_position_valid(pos) where pos is a p_position_t structure instance.

For Rust targets, the p_position_t structure can be checked for validity by calling its valid() method (e.g. if pos.valid()).

5.2.4 p_value_t

If tree generation mode is enabled, the p_value_t type is defined to be the type given to the ptype statement in the grammar file.

If tree generation mode is not enabled, there could be more than one ptype given, so the p_value_t type is a union of all possible ptype types. In this case, the API functions p_value() and p_value_XXX() for each given ptype name XXX are generated to return p_value_t instances holding the corresponding ptype.

For Rust targets, p_value_t is an enum rather than a union, and every ptype type must implement Clone and Default (see Rust ptype requirements). Reading a p_value_t with an accessor for a ptype other than the one it currently holds returns Default::default() rather than reinterpreting the stored bytes.

5.2.5 p_token_info_t

The p_token_info_t structure contains the following fields:

For Rust targets, p_token_info_t implements Default, so a token info structure to pass to p_lex() can be created with p_token_info_t::default().

5.2.6 Tree Node Types

If tree generation mode is enabled, a structure type for each rule will be generated. The name of the structure type is given by the name of the rule. Additionally a structure type called Token is generated to represent a tree node which refers to a raw parser token rather than a composite rule.

5.2.6.1 Tree Node Fields

All tree nodes have a position field specifying the text position of the beginning of the matched token or rule, and an end_position field specifying the text position of the end of the matched token or rule. Each of these fields are instances of the p_position_t structure.

A Token node will always have a valid position and end_position. A rule node may not have valid positions if the rule allows for an empty match. In this case the position structure should be checked for validity before using it. For C targets this can be accomplished with if (p_position_valid(node->position)), for D targets with if (node.position.valid), and for Rust targets with if node.position().valid().

A Token node has the following additional fields:

Tree node structures for rules contain generated fields based on the right hand side components specified for all rules of a given name.

In this example:

Start -> Items;

Items -> Item ItemsMore;
Items -> ;

The Start structure will have a field called pItems and another field of the same name but with a positional suffix (pItems1) which both refer to the parsed Items node. Both will be invalid node handles if the parsed Items rule was empty.

Tree node fields are not data members; they are read through the per-language accessors described in the "Parser rule code blocks" section, so this field is read as p_Start_pItems(node) for C, node.pItems() for C++ and Rust, and node.pItems for D.

The Items structure will have fields:

If a rule can be empty (for example in the second Items rule above), then the field referring to that rule's generated tree node will be an invalid node handle if the parser matches the empty rule pattern.

The non-positional tree node field will not be generated if there are multiple positions in which an instance of the node it refers to could be present. For example, in the below rules:

Dual -> One Two;
Dual -> Two One;

The generated Dual structure will contain pOne1, pTwo2, pTwo1, and pOne2 fields. However, a pOne field and pTwo field will not be generated since it would be ambiguous which one was matched.

If the first rule is matched, then pOne1 and pTwo2 will be valid node handles while pTwo1 and pOne2 will be invalid. If the second rule is matched instead, then the opposite would be the case.

Reading a field of an invalid node handle produces another invalid node handle rather than failing. An invalid result therefore propagates along a chain of field accesses, so a walk which descends several levels only has to be checked once, at the end, instead of at every step. Using the tree generation grammar from the tree statement - tree generation mode section:

let token = start.pItems().item().pDual().pOne1().pToken1();
if token.valid()
{
    println!("{}", p_token_names[token.token() as usize]);
}

If the parsed input did not contain a Dual, then pDual() returns an invalid handle and each remaining call in the chain passes that invalid result along. The walk does not fail and does not read invalid memory.

Reading a field which the matched rule did not fill behaves the same way. Every node of a rule set reserves a slot for each field that the rule set can have, so reading pTwo1 from a Dual node when Dual -> One Two was matched returns an invalid handle rather than reading past the end of the node's fields.

This behavior is the same for every target language:

The result at the end of the chain must still be checked before it is used. An invalid handle reports an invalid position and end_position and an n_fields of 0, but its token is token ID 0 and its pvalue is a default-constructed parser value. Neither of those can be distinguished from a node which genuinely holds those values.

If a field alias is present in a rule definition, an additional field will be generated in the tree node with the field alias name. For example:

Exp -> Exp:left plus ExpB:right;

In the generated Exp structure, the fields pExp, pExp1, and left will all refer to the same child node (an instance of the Exp structure), and the fields pExpB, pExpB3, and right will all refer to the same child node (an instance of the ExpB structure).