/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
    Author:    Jacob Hookom
    Email:    jacob at hookom.net
*/

/* == Option Declaration == */
options
{
    STATIC=false;
    NODE_PREFIX="Ast";
    VISITOR_EXCEPTION="javax.el.ELException";
    VISITOR=false;
    MULTI=true;
    NODE_DEFAULT_VOID=true;
    JAVA_UNICODE_ESCAPE=false;
    UNICODE_INPUT=true;
    BUILD_NODE_FILES=true;
}

/* == Parser Declaration == */
PARSER_BEGIN( ELParser )
package org.apache.el.parser;
import java.io.StringReader;
import javax.el.ELException;
public class ELParser {

    public static Node parse(String ref) throws ELException {
        try {
            return (new ELParser(new StringReader(ref))).CompositeExpression();
        } catch (ParseException pe) {
            throw new ELException(pe.getMessage());
        }
    }
}
PARSER_END( ELParser )

/*
 * CompositeExpression
 * Allow most flexible parsing, restrict by examining
 * type of returned node
 */
AstCompositeExpression CompositeExpression() #CompositeExpression : {}
{
    (DeferredExpression() |
     DynamicExpression() |
     LiteralExpression())* <EOF> { return jjtThis; }
}

/*
 * LiteralExpression
 * Non-EL Expression blocks
 */
void LiteralExpression() #LiteralExpression : { Token t = null; }
{
    t=<LITERAL_EXPRESSION> { jjtThis.setImage(t.image); }
}

/*
 * DeferredExpression
 * #{...} Expressions
 */
void DeferredExpression() #DeferredExpression : {}
{
    <START_DEFERRED_EXPRESSION> Expression() <RBRACE>
}

/*
 * DynamicExpression
 * ${...} Expressions
 */
void DynamicExpression() #DynamicExpression : {}
{
    <START_DYNAMIC_EXPRESSION> Expression() <RBRACE>
}

/*
 * Expression
 * EL Expression Language Root
 */
void Expression() : {}
{
    Semicolon()
}

/*
 * Semicolon
 */
void Semicolon() : {}
{
    Assignment() ( <SEMICOLON> Assignment() #Semicolon(2) )*
}

/*
 * Assignment
 */
void Assignment() : {}
{
    LOOKAHEAD(4) LambdaExpression() |
    Choice() ( LOOKAHEAD(2) <ASSIGN> Assignment() #Assign(2) )*
}

/*
 * Lambda expression
 */
void LambdaExpression() #LambdaExpression : {}
{
    LambdaParameters() <ARROW> ( LOOKAHEAD(3) LambdaExpression() | Choice() )
}

/*
 * Lambda parameters
 */
void LambdaParameters() #LambdaParameters : {}
{
    Identifier() | <LPAREN> ( Identifier() ( <COMMA> Identifier() )* )? <RPAREN>
}

/*
 * Possible invocation of lambda expression. Invocations must be bracketed but
 * being bracketed does not mean it is an invocation.
 */
void LambdaExpressionOrInvocation() #LambdaExpression : {}
{
    <LPAREN>
        LambdaParameters()
        <ARROW>
        ( LOOKAHEAD(3) LambdaExpression() | Choice() )
    <RPAREN>
    ( MethodParameters() )*
}

/*
 * Choice
 * For Choice markup a ? b : c, then Or
 */
void Choice() : {}
{
    Or() (LOOKAHEAD(3) <QUESTIONMARK> Choice() <COLON> Choice() #Choice(3))*
}

/*
 * Or
 * For 'or' '||', then And
 */
void Or() : {}
{
    And() ((<OR0>|<OR1>) And() #Or(2))*
}

/*
 * And
 * For 'and' '&&', then Equality
 */
void And() : {}
{
    Equality() ((<AND0>|<AND1>) Equality() #And(2))*
}

/*
 * Equality
 * For '==' 'eq' '!=' 'ne', then Compare
 */
void Equality() : {}
{
    Compare()
    (
        ((<EQ0>|<EQ1>) Compare() #Equal(2))
    |
        ((<NE0>|<NE1>) Compare() #NotEqual(2))
    )*
}

/*
 * Compare
 * For a bunch of them, then +=
 */
void Compare() : {}
{
    Concatenation()
    (
        ((<LT0>|<LT1>) Concatenation() #LessThan(2))
    |
        ((<GT0>|<GT1>) Concatenation() #GreaterThan(2))
    |
        ((<LE0>|<LE1>) Concatenation() #LessThanEqual(2))
    |
        ((<GE0>|<GE1>) Concatenation() #GreaterThanEqual(2))
    )*
}

/*
 * Concatenation
 * For +=, then Math
 *
 */
 void Concatenation() : {}
 {
     Math()
     (
         <CONCAT> Math() #Concatenation(2)
     )*
 }

/*
 * Math
 * For '+' '-', then Multiplication
 */
void Math() : {}
{
    Multiplication()
    (
        (<PLUS> Multiplication() #Plus(2))
    |
        (<MINUS> Multiplication() #Minus(2))
    )*
}

/*
 * Multiplication
 * For a bunch of them, then Unary
 */
void Multiplication() : {}
{
    Unary()
    (
        (<MULT> Unary() #Mult(2))
    |
        ((<DIV0>|<DIV1>) Unary() #Div(2))
    |
        ((<MOD0>|<MOD1>) Unary() #Mod(2))
    )*
}

/*
 * Unary
 * For '-' '!' 'not' 'empty', then Value
 */
void Unary() : {}
{
        <MINUS> Unary() #Negative
    |
        (<NOT0>|<NOT1>) Unary() #Not
    |
        <EMPTY> Unary() #Empty
    |
        Value()
}

/*
 * Value
 * Defines Prefix plus zero or more Suffixes
 */
void Value() : {}
{
    (ValuePrefix() (ValueSuffix())*) #Value(>1)
}

/*
 * ValuePrefix
 * For Literals, Variables, and Functions
 */
void ValuePrefix() : {}
{
    Literal()
    | NonLiteral()
}

/*
 * ValueSuffix
 * Either dot or bracket notation
 */
void ValueSuffix() : {}
{
    ( DotSuffix() | BracketSuffix() ) ( MethodParameters())?
}

/*
 * DotSuffix
 * Dot Property
 */
void DotSuffix() #DotSuffix : { Token t = null; }
{
    <DOT> t=<IDENTIFIER> { jjtThis.setImage(t.image); }
}

/*
 * BracketSuffix
 * Sub Expression Suffix
 */
void BracketSuffix() #BracketSuffix : {}
{
    <LBRACK> Expression() <RBRACK>
}

/*
 * MethodParameters
 */
void MethodParameters() #MethodParameters : {}
{
    <LPAREN> ( Expression() ( <COMMA> Expression())* )? <RPAREN>
}

/*
 * NonLiteral
 * For Grouped Operations, Identifiers, and Functions
 */
void NonLiteral() : {}
{
    LOOKAHEAD(5) LambdaExpressionOrInvocation()
    | <LPAREN> Expression() <RPAREN>
    | LOOKAHEAD((<IDENTIFIER> <COLON>)? <IDENTIFIER> <LPAREN>) Function()
    | Identifier()
    | LOOKAHEAD(3)SetData()
    | ListData()
    | MapData()
}

/*
 * Note that both an empty Set and an empty Map are represented by {}. The
 * parser will always parse {} as an empty Set and special handling is required
 * to convert it to an empty Map when appropriate.
 */
void SetData() #SetData: {}
{
    <START_SET_OR_MAP>
        ( Expression() ( <COMMA> Expression() )* )?
    <RBRACE>
}

void ListData() #ListData: {}
{
    <LBRACK>
        ( Expression() ( <COMMA> Expression() )* )?
    <RBRACK>
}

/*
 * Note that both an empty Set and an empty Map are represented by {}. The
 * parser will always parse {} as an empty Set and special handling is required
 * to convert it to an empty Map when appropriate.
 */
void MapData() #MapData: {}
{
    <START_SET_OR_MAP>
        ( MapEntry() ( <COMMA> MapEntry() )* )?
    <RBRACE>
}

void MapEntry() #MapEntry: {}
{
    Expression() <COLON> Expression()
}

/*
 * Identifier
 * Java Language Identifier
 */
void Identifier() #Identifier : { Token t = null; }
{
    t=<IDENTIFIER> { jjtThis.setImage(t.image); }
}

/*
 * Function
 * Namespace:Name(a,b,c)
 */
void Function() #Function :
{
    Token t0 = null;
    Token t1 = null;
}
{
    t0=<IDENTIFIER>  ( <COLON> t1=<IDENTIFIER> )?
    {
        if (t1 != null) {
            jjtThis.setPrefix(t0.image);
            jjtThis.setLocalName(t1.image);
        } else {
            jjtThis.setLocalName(t0.image);
        }
    }
    ( MethodParameters() )+
}

/*
 * Literal
 * Reserved Keywords
 */
void Literal() : {}
{
    Boolean()
    | FloatingPoint()
    | Integer()
    | String()
    | Null()
}

/*
 * Boolean
 * For 'true' 'false'
 */
void Boolean() : {}
{
    <TRUE> #True
    | <FALSE> #False
}

/*
 * FloatingPoint
 * For Decimal and Floating Point Literals
 */
void FloatingPoint() #FloatingPoint : { Token t = null; }
{
    t=<FLOATING_POINT_LITERAL> { jjtThis.setImage(t.image); }
}

/*
 * Integer
 * For Simple Numeric Literals
 */
void Integer() #Integer : { Token t = null; }
{
    t=<INTEGER_LITERAL> { jjtThis.setImage(t.image); }
}

/*
 * String
 * For Quoted Literals
 */
void String() #String : { Token t = null; }
{
    t=<STRING_LITERAL> { jjtThis.setImage(t.image); }
}

/*
 * Null
 * For 'null'
 */
void Null() #Null : {}
{
    <NULL>
}


/* ========================================================================== */
TOKEN_MGR_DECLS:
{
java.util.Deque<Integer> deque = new java.util.ArrayDeque<Integer>();
}
<DEFAULT> TOKEN :
{
  /*
   * The following definition uses + rather than * in two places to prevent
   * LITERAL_EXPRESSION matching the empty string that could result in the
   * Parser entering an infinite loop.
   */
  < LITERAL_EXPRESSION:
    (   (~["$", "#", "\\"])* "\\" (["$", "#"])?
      | (~["$", "#"])* (["$", "#"] ~["{", "$", "#", "\\"])
      | (~["$", "#"])+
    )+
    | "$"
    | "#"
  >
|
  < START_DYNAMIC_EXPRESSION: "${" > {deque.push(DEFAULT);}: IN_EXPRESSION
|
  < START_DEFERRED_EXPRESSION: "#{" > {deque.push(DEFAULT);}: IN_EXPRESSION
}

<IN_EXPRESSION, IN_SET_OR_MAP> SKIP : { " " | "\t" | "\n" | "\r" }

<IN_EXPRESSION, IN_SET_OR_MAP> TOKEN :
{
     < START_SET_OR_MAP : "{" > {deque.push(curLexState);}: IN_SET_OR_MAP
|    < RBRACE: "}" > {SwitchTo(deque.pop());}
|    < INTEGER_LITERAL: ["0"-"9"] (["0"-"9"])* >
|    < FLOATING_POINT_LITERAL: (["0"-"9"])+ "." (["0"-"9"])* (<EXPONENT>)?
        | "." (["0"-"9"])+ (<EXPONENT>)?
        | (["0"-"9"])+ <EXPONENT>
     >
|    < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ >
|    < STRING_LITERAL: ("\"" ((~["\"","\\"])
        | ("\\" ( ["\\","\"","\'"] )))* "\"")
        | ("\'" ((~["\'","\\"])
        | ("\\" ( ["\\","\"","\'"] )))* "\'")
     >
|    < TRUE : "true" >
|    < FALSE : "false" >
|    < NULL : "null" >
|    < DOT : "." >
|    < LPAREN : "(" >
|    < RPAREN : ")" >
|    < LBRACK : "[" >
|    < RBRACK : "]" >
|    < COLON : ":" >
|    < SEMICOLON : ";" >
|    < COMMA : "," >
|    < GT0 : ">" >
|    < GT1 : "gt" >
|    < LT0 : "<" >
|    < LT1 : "lt" >
|    < GE0 : ">=" >
|    < GE1 : "ge" >
|    < LE0 : "<=" >
|    < LE1 : "le" >
|    < EQ0 : "==" >
|    < EQ1 : "eq" >
|    < NE0 : "!=" >
|    < NE1 : "ne" >
|    < NOT0 : "!" >
|    < NOT1 : "not" >
|    < AND0 : "&&" >
|    < AND1 : "and" >
|    < OR0 : "||" >
|    < OR1 : "or" >
|    < EMPTY : "empty" >
|    < INSTANCEOF : "instanceof" >
|    < MULT : "*" >
|    < PLUS : "+" >
|    < MINUS : "-" >
|    < QUESTIONMARK : "?" >
|    < DIV0 : "/" >
|    < DIV1 : "div" >
|    < MOD0 : "%" >
|    < MOD1 : "mod" >
|    < CONCAT : "+=" >
|    < ASSIGN : "=" >
|    < ARROW : "->" >
|    < IDENTIFIER : (<LETTER>|<IMPL_OBJ_START>) (<LETTER>|<DIGIT>)* >
|    < FUNCTIONSUFFIX : (<IDENTIFIER>) >
|    < #IMPL_OBJ_START: "#" >
|    < #LETTER:
        [
        "\u0024",
        "\u0041"-"\u005a",
        "\u005f",
        "\u0061"-"\u007a",
        "\u00c0"-"\u00d6",
        "\u00d8"-"\u00f6",
        "\u00f8"-"\u00ff",
        "\u0100"-"\u1fff",
        "\u3040"-"\u318f",
        "\u3300"-"\u337f",
        "\u3400"-"\u3d2d",
        "\u4e00"-"\u9fff",
        "\uf900"-"\ufaff"
        ]
    >
|    < #DIGIT:
        [
        "\u0030"-"\u0039",
        "\u0660"-"\u0669",
        "\u06f0"-"\u06f9",
        "\u0966"-"\u096f",
        "\u09e6"-"\u09ef",
        "\u0a66"-"\u0a6f",
        "\u0ae6"-"\u0aef",
        "\u0b66"-"\u0b6f",
        "\u0be7"-"\u0bef",
        "\u0c66"-"\u0c6f",
        "\u0ce6"-"\u0cef",
        "\u0d66"-"\u0d6f",
        "\u0e50"-"\u0e59",
        "\u0ed0"-"\u0ed9",
        "\u1040"-"\u1049"
        ]
    >
|    < ILLEGAL_CHARACTER: (~[]) >
}
