lalrpop 0.4.0

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

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

pub Grammar: Grammar =
    <uses:Use*> <lo:@L> "grammar" <hi:@R>
    <tps:GrammarTypeParameters?>
    <parameters:GrammarParameters?>
    <where_clauses:"where"?>
    <algorithm:Algorithm?>
    ";"
    <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 { 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(),
                  algorithm: algorithm }
    };

Algorithm: Algorithm =
    "[" <lo:@L> <t:StringLiteral> <hi:@R> "]" => {
        Algorithm {
            span: Span(lo, hi),
            text: t
        }
    };

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 =
    <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,
                                                   args: n.1,
                                                   type_decl: t,
                                                   alternatives: a })
    };

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

Alternatives: Vec<Alternative> = {
    <a:Alternative> => vec![a];
    "{" <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;
};

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 =
    <lo0:@L> "extern" <hi0:@R> "{"
      <a0:AssociatedType*>
      "enum" <lo:@L> <t:TypeRef> <hi:@R> "{"
        <c:Comma<Conversion>>
      "}"
      <a1:AssociatedType*>
    "}" => {
        GrammarItem::ExternToken(ExternToken {
            span: Span(lo0, hi0),
            associated_types: a0.into_iter().chain(a1).collect(),
            enum_token: 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);

StringLiteral: InternedString =
    <s:"StringLiteral"> => 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(..),
        "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>),

        "&" => 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(..),
    }
}