wast

Macro annotation

Source
macro_rules! annotation {
    ($name:ident) => { ... };
    ($name:ident = $annotation:expr) => { ... };
}
Expand description

A macro, like custom_keyword, to create a type which can be used to parse/peek annotation directives.

Note that when you’re parsing custom annotations it can be somewhat tricky due to the nature that most of them are skipped. You’ll want to be sure to consult the documentation of Parser::register_annotation when using this macro.

§Examples

To see an example of how to use this macro, let’s invent our own syntax for the producers section which looks like:

(@producer "wat" "1.0.2")

Here, for simplicity, we’ll assume everything is a processed-by directive, but you could get much more fancy with this as well.


// First we define the custom annotation keyword we're using, and by
// convention we define it in an `annotation` module.
mod annotation {
    wast::annotation!(producer);
}

struct Producer<'a> {
    name: &'a str,
    version: &'a str,
}

impl<'a> Parse<'a> for Producer<'a> {
    fn parse(parser: Parser<'a>) -> Result<Self> {
        // Remember that parser conventionally parse the *interior* of an
        // s-expression, so we parse our `@producer` annotation and then we
        // parse the payload of our annotation.
        parser.parse::<annotation::producer>()?;
        Ok(Producer {
            name: parser.parse()?,
            version: parser.parse()?,
        })
    }
}

Note though that this is only half of the parser for annotations. The other half is calling the register_annotation method at the right time to ensure the parser doesn’t automatically skip our @producer directive. Note that we can’t call it inside the Parse for Producer definition because that’s too late and the annotation would already have been skipped.

Instead we’ll need to call it from a higher level-parser before the parenthesis have been parsed, like so:

struct Module<'a> {
    fields: Vec<ModuleField<'a>>,
}

impl<'a> Parse<'a> for Module<'a> {
    fn parse(parser: Parser<'a>) -> Result<Self> {
        // .. parse module header here ...

        // register our custom `@producer` annotation before we start
        // parsing the parentheses of each field
        let _r = parser.register_annotation("producer");

        let mut fields = Vec::new();
        while !parser.is_empty() {
            fields.push(parser.parens(|p| p.parse())?);
        }
        Ok(Module { fields })
    }
}

enum ModuleField<'a> {
    Producer(Producer<'a>),
    // ...
}

impl<'a> Parse<'a> for ModuleField<'a> {
    fn parse(parser: Parser<'a>) -> Result<Self> {
        // and here `peek` works and our delegated parsing works because the
        // annotation has been registered.
        if parser.peek::<annotation::producer>()? {
            return Ok(ModuleField::Producer(parser.parse()?));
        }

        // .. typically we'd parse other module fields here...

        Err(parser.error("unknown module field"))
    }
}