lalrpop 0.12.2

convenient LR(1) parser generator
Documentation
use intern::{intern, InternedString};
use grammar::parse_tree::*;
use grammar::pattern::*;
use std::iter::once;
use tok::{self, Tok};
use util::strip;

#[LALR]
grammar<'input>(text: &'input str);

pub Grammar: Grammar =
    <uses:Use*>
    <annotations:Annotation*>
    <lo:@L> "grammar" <hi:@R>
    <tps:GrammarTypeParameters?>
    <parameters:GrammarParameters?>
    <where_clauses:"where"?>
    ";"
    <items:GrammarItem*> => {
        let where_clauses =
            where_clauses.iter()
                         .flat_map(|wc| wc.iter())
                         .map(|s| strip(s))
                         .filter(|s| !s.is_empty())
                         .map(|s| s.to_string())
                         .collect();
        Grammar { prefix: format!("__"), // adjusted by `parse_grammar`
                  span: Span(lo, hi),
                  type_parameters: tps.unwrap_or(vec![]),
                  parameters: parameters.unwrap_or(vec![]),
                  where_clauses: where_clauses,
                  items: uses.into_iter().chain(items).collect(),
                  annotations: annotations }
    };

GrammarTypeParameters: Vec<TypeParameter> =
    "<" <Comma<TypeParameter>> ">";

TypeParameter: TypeParameter = {
    <l:Lifetime> => TypeParameter::Lifetime(l),
    <l:Id> => TypeParameter::Id(l)
};

GrammarParameters: Vec<Parameter> =
    "(" <Comma<GrammarParameter>> ")";

GrammarParameter: Parameter =
    <id:Id> ":" <ty:TypeRef> => Parameter { name: id, ty: ty };

GrammarItem: GrammarItem = {
    Use,
    ExternToken,
    Nonterminal
};

Use: GrammarItem =
    <u:"use"> ";" => GrammarItem::Use(strip(u).to_string());

Nonterminal: GrammarItem =
    <annotations:Annotation*>
    <p:"pub"?> <lo:@L> <n:NonterminalName> <hi:@R>
    <t:(":" <TypeRef>)?> "=" <a:Alternatives> => {
        GrammarItem::Nonterminal(NonterminalData { public: p.is_some(),
                                                   span: Span(lo, hi),
                                                   name: n.0,
                                                   annotations: annotations,
                                                   args: n.1,
                                                   type_decl: t,
                                                   alternatives: a })
    };

Annotation: Annotation =
    "#" "[" <lo:@L> <id:Id> <hi:@R> "]" => {
        Annotation { id_span: Span(lo, hi), id: id }
    };

NonterminalName: (NonterminalString, Vec<NonterminalString>) = {
    <MacroId> "<" <Comma<NotMacroId>> ">",
    <n:NotMacroId> => (n, vec![]),
    <"Escape"> => (NonterminalString(intern(<>)), vec![]),
};

Alternatives: Vec<Alternative> = {
    <a:Alternative> ";" => vec![a],
    "{" <Comma<Alternative>> "}" ";",
};

Alternative: Alternative = {
    <lo:@L> <s:Symbol+> <c:("if" <Cond>)?> <a:Action?> <hi:@R> => {
        Alternative {
            span: Span(lo, hi),
            expr: ExprSymbol { symbols: s },
            condition: c,
            action: a
        }
    },
    <lo:@L> <c:("if" <Cond>)?> <a:Action> <hi:@R> => {
        Alternative {
            span: Span(lo, hi),
            expr: ExprSymbol { symbols: vec![] },
            condition: c,
            action: Some(a)
        }
    },
};

Action: ActionKind = {
    "=>@L" => ActionKind::Lookahead,
    "=>@R" => ActionKind::Lookbehind,
    <c:"=>"> => ActionKind::User(strip(c).to_string()),
    <c:"=>?"> => ActionKind::Fallible(strip(c).to_string()),
};

Cond: Condition =
    <lo:@L> <a:NotMacroId> <op:CondOp> <b:StringLiteral> <hi:@R> => {
        Condition { span:Span(lo, hi), lhs:a, rhs:b, op:op }
    };

CondOp: ConditionOp = {
    "==" => ConditionOp::Equals,
    "!=" => ConditionOp::NotEquals,
    "~~" => ConditionOp::Match,
    "!~" => ConditionOp::NotMatch,
};

ExprSymbol: ExprSymbol =
    Symbol* => ExprSymbol { symbols: <> };

Symbol: Symbol = {
    <lo:@L> "<" @L <l:Id> ":" <s:Symbol0> ">" <hi:@R> =>
        Symbol::new(Span(lo, hi), SymbolKind::Name(l, Box::new(s))),

    <lo:@L> "<" <s:Symbol0> ">" <hi:@R> =>
        Symbol::new(Span(lo, hi), SymbolKind::Choose(Box::new(s))),

    Symbol0,
};

Symbol0: Symbol = {
    Symbol1,

    <lhs:Symbol0> <op:RepeatOp> <hi:@R> =>
        Symbol::new(Span(lhs.span.0, hi),
                    SymbolKind::Repeat(Box::new(RepeatSymbol { symbol: lhs, op: op }))),
};

RepeatOp: RepeatOp = {
    "+" => RepeatOp::Plus,
    "*" => RepeatOp::Star,
    "?" => RepeatOp::Question,
};

Symbol1: Symbol =
    <lo:@L> <sk:SymbolKind1> <hi:@R> => Symbol::new(Span(lo, hi), sk);

SymbolKind1: SymbolKind = {
    <name:MacroId> "<" <args:Comma<Symbol>> ">" =>
        SymbolKind::Macro(MacroSymbol { name: name, args: args }),

    QuotedTerminal =>
        SymbolKind::Terminal(<>),

    "Id" =>
        SymbolKind::AmbiguousId(intern(<>)),

    Escape =>
        SymbolKind::Nonterminal(NonterminalString(<>)),

    "(" <ExprSymbol> ")" =>
        SymbolKind::Expr(<>),

    "@L" =>
        SymbolKind::Lookahead,

    "@R" =>
        SymbolKind::Lookbehind,
    
    "!" =>
        SymbolKind::Error,
};

pub TypeRef: TypeRef = {
    "(" <Comma<TypeRef>> ")" =>
        TypeRef::Tuple(<>),

    "#" <Symbol> "#" => {
        TypeRef::OfSymbol(<>.kind)
    },

    "&" <l:Lifetime?> <m:"mut"?> <t:TypeRef> =>
        TypeRef::Ref { lifetime: l,
                       mutable: m.is_some(),
                       referent: Box::new(t) },

    <p:Path> "<" <a:Comma<TypeRefOrLifetime>> ">" =>
        TypeRef::Nominal { path: p, types: a },

    <p:Path> =>
        match p.as_id() {
            Some(id) => TypeRef::Id(id),
            None => TypeRef::Nominal { path: p, types: vec![] }
        },
};

TypeRefOrLifetime: TypeRef = {
    TypeRef,
    Lifetime => TypeRef::Lifetime(<>),
};

Path: Path =
    <a:"::"?> <h:(<Id> "::")*> <t:Id> => {
        Path { absolute: a.is_some(),
               ids: h.into_iter().chain(once(t)).collect() }
    };

ExternToken: GrammarItem = {
    <lo:@L> "extern" <hi:@R> "{" <a0:AssociatedType*> <et:EnumToken> <a1:AssociatedType*> "}" => {
        GrammarItem::ExternToken(ExternToken {
            span: Span(lo, hi),
            associated_types: a0.into_iter().chain(a1).collect(),
            enum_token: Some(et),
        })
    },
    <lo:@L> "extern" <hi:@R> "{" <a0:AssociatedType*> "}" => {
        GrammarItem::ExternToken(ExternToken {
            span: Span(lo, hi),
            associated_types: a0,
            enum_token: None,
        })
    },
};

EnumToken: EnumToken =
    "enum" <lo:@L> <t:TypeRef> <hi:@R> "{"
      <c:Comma<Conversion>>
    "}" => {
        EnumToken {
            type_name: t,
            type_span: Span(lo, hi),
            conversions: c,
        }
    };

AssociatedType: AssociatedType =
    "type" <lo:@L> <n:Id> <hi:@R> "=" <t:TypeRef> ";" => {
        AssociatedType { type_span: Span(lo, hi),
                         type_name: n,
                         type_ref: t }
    };

Conversion: Conversion =
    <lo:@L> <from:Terminal> <start:@L> <p:"=>"> <hi:@R> =>? {
        let pattern = try!(super::parse_pattern(p, start + 2));
        Ok(Conversion { span: Span(lo, hi),
                        from: from,
                        to: pattern })
    };

pub Pattern: Pattern<TypeRef> =
    <lo:@L> <k:PatternKind> <hi:@R> => Pattern { span: Span(lo, hi), kind: k };

PatternKind: PatternKind<TypeRef> = {
    <Path> "(" <Comma<Pattern>> ")" =>
        PatternKind::Enum(<>),

    <p:Path> "{" <a0:(<FieldPattern> ",")*> <a1:FieldPattern?> "}" =>
        PatternKind::Struct(p, a0.into_iter().chain(a1).collect(), false),

    <p:Path> "{" <a0:(<FieldPattern> ",")*> ".." "}" =>
        PatternKind::Struct(p, a0, true),

    "_" =>
        PatternKind::Underscore,

    ".." =>
        PatternKind::DotDot,

    "<" <TypeRef> ">" =>
        PatternKind::Choose(<>),

    "(" <Comma<Pattern>> ")" =>
        PatternKind::Tuple(<>),

    <Path> =>
        PatternKind::Path(<>),
};

FieldPattern: FieldPattern<TypeRef> =
    <lo:@L> <id:Id> <hi:@R> ":" <pat:Pattern> => {
        FieldPattern { field_span: Span(lo, hi),
                       field_name: id,
                       pattern: pat }
    };

MacroId: NonterminalString =
    <i:"MacroId"> => NonterminalString(intern(i));

NotMacroId: NonterminalString =
    <i:"Id"> => NonterminalString(intern(i));

Id: InternedString = {
    <i:"Id"> => intern(i),
    <i:"MacroId"> => intern(i),
};

Escape: InternedString =
    <i:"Escape"> => intern(i);

Lifetime: InternedString =
    <i:"Lifetime"> => intern(i);

Terminal: TerminalString = {
    QuotedTerminal,
    <i:"Id"> => TerminalString::Bare(intern(i)),
};

QuotedTerminal: TerminalString = {
    <s:StringLiteral> => TerminalString::quoted(s),
    <s:RegexLiteral> => TerminalString::regex(s),
};

StringLiteral: InternedString =
    <s:"StringLiteral"> => intern(s);

RegexLiteral: InternedString =
    <s:"RegexLiteral"> => intern(s);

Comma<E>: Vec<E> =
    <v0:(<E> ",")*> <e1:E?> =>
        v0.into_iter().chain(e1).collect();

extern {
    type Location = usize;
    type Error = tok::Error;
    enum Tok<'input> {
        "enum" => Tok::Enum,
        "extern" => Tok::Extern,
        "grammar" => Tok::Grammar,
        "if" => Tok::If,
        "mut" => Tok::Mut,
        "pub" => Tok::Pub,
        "type" => Tok::Type,
        "!" => Tok::Bang,
        "use" => Tok::Use(<&'input str>),
        "where" => Tok::Where(<Vec<&'input str>>),

        "Escape" => Tok::Escape(<&'input str>),
        "Id" => Tok::Id(<&'input str>),
        "MacroId" => Tok::MacroId(<&'input str>),
        "Lifetime" => Tok::Lifetime(<&'input str>),
        "StringLiteral" => Tok::StringLiteral(<&'input str>),
        "RegexLiteral" => Tok::RegexLiteral(<&'input str>),

        "&" => Tok::Ampersand,
        "!=" => Tok::BangEquals,
        "!~" => Tok::BangTilde,
        ":" => Tok::Colon,
        "::" => Tok::ColonColon,
        "," => Tok::Comma,
        ".." => Tok::DotDot,
        "=" => Tok::Equals,
        "==" => Tok::EqualsEquals,
        "=>" => Tok::EqualsGreaterThanCode(<&'input str>),
        "=>?" => Tok::EqualsGreaterThanQuestionCode(<&'input str>),
        "=>@L" => Tok::EqualsGreaterThanLookahead,
        "=>@R" => Tok::EqualsGreaterThanLookbehind,
        ">" => Tok::GreaterThan,
        "#" => Tok::Hash,
        "{" => Tok::LeftBrace,
        "[" => Tok::LeftBracket,
        "(" => Tok::LeftParen,
        "<" => Tok::LessThan,
        "@L" => Tok::Lookahead,
        "@R" => Tok::Lookbehind,
        "+" => Tok::Plus,
        "?" => Tok::Question,
        "}" => Tok::RightBrace,
        "]" => Tok::RightBracket,
        ")" => Tok::RightParen,
        ";" => Tok::Semi,
        "*" => Tok::Star,
        "~~" => Tok::TildeTilde,
        "_" => Tok::Underscore,
    }
}