Node data

When parsing, it's often useful to keep track of the positions of nodes for error handling for example. This can be achieved through the use the NodeData trait and the NodeWrapper struct.

The NodeData trait has two functions; NodeData::start and NodeData::end to get the start and end data of a node.

The NodeWrapper struct contains a node (NodeWrapper::node), and start and end data about that node (NodeWrapper::start, NodeWrapper::end).

Example

#[derive(Parser)]
#[parser(Token, TokType, |t| t.t, usize; ([@Add])*)]
pub struct Root(pub Vec<NodeWrapper<Add, usize>>);

In this example, you'll notice that there is an additional field for the metadata. This indicates that any matches should be wrapped in a NodeWrapper struct with positional data. The type provided is the type returned from the NodeData trait functions1.

You'll also notice that the inner section of the trait is not a Vec<Add>, but Vec<NodeWrapper<Add, usize>>.

Use for one, use for all

If you derive one Parser impl that's wrapped, all Parser impls that can be called from the one using it must also be wrapped.

For example, you could not have the following

#[derive(Parser)]
#[parser(Token, TokType, |t| t.t, usize; ([@Add])*)]
pub struct Root(pub Vec<NodeWrapper<Expr, usize>>);

#[derive(Parser)]
#[parser(Token, TokType, |t| t.t)]
pub enum Expr {
    /* ... */
}

This would cause an error because the Root Parser impl would expect that the Expr Parser impl would also be wrapped so it can access the positional data of an Expr match.

NodeData impl requirement

If you use wrapped matches, you need to ensure that the input type of your parser (Token in the example above) needs to implement NodeData so that Parser impls that match tokens not calls to other types can access positional data.


1

This could most likely be included as a generic in the Parser impl, but is required to ensure the correct NodeData impl is used in the case that there are multiple NodeData impls.