{
package main
}

Grammar ← __ initializer:( Initializer __ )? rules:( Rule __ )+ {
    pos := c.astPos()

    // create the grammar, assign its initializer
    g := ast.NewGrammar(pos)
    initSlice := toIfaceSlice(initializer)
    if len(initSlice) > 0 {
        g.Init = initSlice[0].(*ast.CodeBlock)
    }

    rulesSlice := toIfaceSlice(rules)
    g.Rules = make([]*ast.Rule, len(rulesSlice))
    for i, duo := range rulesSlice {
        g.Rules[i] = duo.([]interface{})[0].(*ast.Rule)
    }

    return g, nil
}

Initializer ← code:CodeBlock EOS {
    return code, nil
}

Rule ← name:IdentifierName __ display:( StringLiteral __ )? RuleDefOp __ expr:Expression EOS {
    pos := c.astPos()

    rule := ast.NewRule(pos, name.(*ast.Identifier))
    displaySlice := toIfaceSlice(display)
    if len(displaySlice) > 0 {
        rule.DisplayName = displaySlice[0].(*ast.StringLit)
    }
    rule.Expr = expr.(ast.Expression)

    return rule, nil
}

Expression ← ChoiceExpr

ChoiceExpr ← first:ActionExpr rest:( __ "/" __ ActionExpr )* {
    restSlice := toIfaceSlice(rest)
    if len(restSlice) == 0 {
        return first, nil
    }

    pos := c.astPos()
    choice := ast.NewChoiceExpr(pos)
    choice.Alternatives = []ast.Expression{first.(ast.Expression)}
    for _, sl := range restSlice {
        choice.Alternatives = append(choice.Alternatives, sl.([]interface{})[3].(ast.Expression))
    }
    return choice, nil
}

ActionExpr ← expr:SeqExpr code:( __ CodeBlock )? {
    if code == nil {
        return expr, nil
    }

    pos := c.astPos()
    act := ast.NewActionExpr(pos)
    act.Expr = expr.(ast.Expression)
    codeSlice := toIfaceSlice(code)
    act.Code = codeSlice[1].(*ast.CodeBlock)

    return act, nil
}

SeqExpr ← first:LabeledExpr rest:( __ LabeledExpr )* {
    restSlice := toIfaceSlice(rest)
    if len(restSlice) == 0 {
        return first, nil
    }
    seq := ast.NewSeqExpr(c.astPos())
    seq.Exprs = []ast.Expression{first.(ast.Expression)}
    for _, sl := range restSlice {
        seq.Exprs = append(seq.Exprs, sl.([]interface{})[1].(ast.Expression))
    }
    return seq, nil
}

LabeledExpr ← label:Identifier __ ':' __ expr:PrefixedExpr {
    pos := c.astPos()
    lab := ast.NewLabeledExpr(pos)
    lab.Label = label.(*ast.Identifier)
    lab.Expr = expr.(ast.Expression)
    return lab, nil
} / PrefixedExpr

PrefixedExpr ← op:PrefixedOp __ expr:SuffixedExpr {
    pos := c.astPos()
    opStr := op.(string)
    if opStr == "&" {
        and := ast.NewAndExpr(pos)
        and.Expr = expr.(ast.Expression)
        return and, nil
    }
    not := ast.NewNotExpr(pos)
    not.Expr = expr.(ast.Expression)
    return not, nil
} / SuffixedExpr

PrefixedOp ← ( '&' / '!' ) {
    return string(c.text), nil
}

SuffixedExpr ← expr:PrimaryExpr __ op:SuffixedOp {
    pos := c.astPos()
    opStr := op.(string)
    switch opStr {
    case "?":
        zero := ast.NewZeroOrOneExpr(pos)
        zero.Expr = expr.(ast.Expression)
        return zero, nil
    case "*":
        zero := ast.NewZeroOrMoreExpr(pos)
        zero.Expr = expr.(ast.Expression)
        return zero, nil
    case "+":
        one := ast.NewOneOrMoreExpr(pos)
        one.Expr = expr.(ast.Expression)
        return one, nil
    default:
        return nil, errors.New("unknown operator: " + opStr)
    }
} / PrimaryExpr 

SuffixedOp ← ( '?' / '*' / '+' ) {
    return string(c.text), nil
}

PrimaryExpr ← LitMatcher / CharClassMatcher / AnyMatcher / RuleRefExpr / SemanticPredExpr / "(" __ expr:Expression __ ")" {
    return expr, nil
}
RuleRefExpr ← name:IdentifierName !( __ ( StringLiteral __ )? RuleDefOp ) {
    ref := ast.NewRuleRefExpr(c.astPos())
    ref.Name = name.(*ast.Identifier)
    return ref, nil
}
SemanticPredExpr ← op:SemanticPredOp __ code:CodeBlock {
    opStr := op.(string)
    if opStr == "&" {
        and := ast.NewAndCodeExpr(c.astPos())
        and.Code = code.(*ast.CodeBlock)
        return and, nil
    }
    not := ast.NewNotCodeExpr(c.astPos())
    not.Code = code.(*ast.CodeBlock)
    return not, nil
}
SemanticPredOp ← ( '&' / '!' ) {
    return string(c.text), nil
}

RuleDefOp ← '=' / "<-" / '\u2190' / '\u27f5'

SourceChar ← .
Comment ← MultiLineComment / SingleLineComment
MultiLineComment ← "/*" ( !"*/" SourceChar )* "*/"
MultiLineCommentNoLineTerminator ← "/*" ( !( "*/" / EOL ) SourceChar )* "*/"
SingleLineComment ← "//" ( !EOL SourceChar )*

Identifier ← IdentifierName
IdentifierName ← IdentifierStart IdentifierPart* {
    return ast.NewIdentifier(c.astPos(), string(c.text)), nil
}
IdentifierStart ← [a-z_]i
IdentifierPart ← IdentifierStart / [0-9]

LitMatcher ← lit:StringLiteral ignore:"i"? {
    rawStr := lit.(*ast.StringLit).Val
	s, err := strconv.Unquote(rawStr)
    if err != nil {
        return nil, err
    }
    m := ast.NewLitMatcher(c.astPos(), s)
    m.IgnoreCase = ignore != nil
    return m, nil
}
StringLiteral ← ( '"' DoubleStringChar* '"' / "'" SingleStringChar "'" / '`' RawStringChar '`' ) {
    return ast.NewStringLit(c.astPos(), string(c.text)), nil
}
DoubleStringChar ← !( '"' / "\\" / EOL ) SourceChar / "\\" DoubleStringEscape
SingleStringChar ← !( "'" / "\\" / EOL ) SourceChar / "\\" SingleStringEscape
RawStringChar ← !'`' SourceChar

DoubleStringEscape ← "'" / CommonEscapeSequence
SingleStringEscape ← '"' / CommonEscapeSequence

CommonEscapeSequence ← SingleCharEscape / OctalEscape / HexEscape / LongUnicodeEscape / ShortUnicodeEscape
SingleCharEscape ← 'a' / 'b' / 'n' / 'f' / 'r' / 't' / 'v' / '\\'
OctalEscape ← OctalDigit OctalDigit OctalDigit
HexEscape ← 'x' HexDigit HexDigit
LongUnicodeEscape ← 'U' HexDigit HexDigit HexDigit HexDigit HexDigit HexDigit HexDigit HexDigit
ShortUnicodeEscape ← 'u' HexDigit HexDigit HexDigit HexDigit

OctalDigit ← [0-7]
DecimalDigit ← [0-9]
HexDigit ← [0-9a-f]i

CharClassMatcher ← '[' ( ClassCharRange / ClassChar / "\\" UnicodeClassEscape )* ']' 'i'? {
    pos := c.astPos()
    cc := ast.NewCharClassMatcher(pos, string(c.text))
    return cc, nil
}
ClassCharRange ← ClassChar '-' ClassChar
ClassChar ← !( "]" / "\\" / EOL ) SourceChar / "\\" CharClassEscape
CharClassEscape ← ']' / CommonEscapeSequence

UnicodeClassEscape ← 'p' ( SingleCharUnicodeClass / '{' UnicodeClass '}' )
SingleCharUnicodeClass ← [LMNCPZS]
UnicodeClass ← [a-z_]i+

AnyMatcher ← "." {
    any := ast.NewAnyMatcher(c.astPos(), ".")
    return any, nil
}

CodeBlock ← "{" Code "}" {
    pos := c.astPos()
    cb := ast.NewCodeBlock(pos, string(c.text))
    return cb, nil
}

Code ← ( ( ![{}] SourceChar )+ / "{" Code "}" )*

__ ← ( Whitespace / EOL / Comment )*
_ ← ( Whitespace / MultiLineCommentNoLineTerminator )*

Whitespace ← [ \t\r]
EOL ← '\n'
EOS ← __ ';' / _ SingleLineComment? EOL / __ EOF

EOF ← !.

