/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

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

        Author: Kin-man Chung (EL 2.2 and EL 3.0)
*/

/* == 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 com.sun.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() <RCURL> 
}

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

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

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

/*
 * Assignment
 * For '=', right associatve, then LambdaExpression or Choice or Assignment
 */
void Assignment() : {}
{
        LOOKAHEAD(3) LambdaExpression() |
        Choice() (<ASSIGN> Assignment() #Assign(2) )?
}

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

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

/*
 * Choice
 * For Choice markup a ? b : c, right associative
 */
void Choice() : {}
{
	Or() (<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 Math
 */
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() #Concat(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()
}

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

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


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

/*
 * Parenthesized Lambda Expression, with optional invokation
 */
void LambdaExpressionOrCall() #LambdaExpression : {}

{
    <LPAREN>
        LambdaParameters() <ARROW>
        (LOOKAHEAD(3) LambdaExpression() | Choice() )
    <RPAREN>
    (MethodArguments())*
}

/*
 * NonLiteral
 * For Grouped Operations, Identifiers, and Functions
 */
void NonLiteral() : {}
{
        LOOKAHEAD(4) LambdaExpressionOrCall()
 	| <LPAREN> Expression() <RPAREN>
 	| LOOKAHEAD(4) Function()
	| Identifier()
        | MapData()
        | ListData()
}

void MapData() #MapData: {}
{
    <START_MAP>
        ( MapEntry() ( <COMMA> MapEntry() )* )?
    <RCURL>
}

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

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

/*
 * 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);
		}
	}
        (MethodArguments())+
}


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

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

/*
 * FloatinPoint
 * 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.Stack<Integer> stack = new java.util.Stack<Integer>();
}

<DEFAULT> TOKEN :
{
  < LITERAL_EXPRESSION:
    ((~["\\", "$", "#"])
      | ("\\" ("\\" | "$" | "#"))
      | ("$" ~["{", "$", "#"])
      | ("#" ~["{", "$", "#"])
    )+
    | "$"
    | "#"
  >
|
  < START_DYNAMIC_EXPRESSION: "${" > {stack.push(DEFAULT);}: IN_EXPRESSION
|
  < START_DEFERRED_EXPRESSION: "#{" > {stack.push(DEFAULT);}: IN_EXPRESSION
}

<DEFAULT> SKIP : { "\\" }

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

<IN_EXPRESSION, IN_MAP> TOKEN :
{
        < START_MAP : "{" > {stack.push(curLexState);}: IN_MAP
|       < RCURL: "}" > {SwitchTo(stack.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: ("\"" ((~["\"","\\"])
		| ("\\" ( ["\\","\""] )))* "\"")
		| ("\'" ((~["\'","\\"])
		| ("\\" ( ["\\","\'"] )))* "\'")
	>
|	< BADLY_ESCAPED_STRING_LITERAL: ("\"" (~["\"","\\"])* ("\\" ( ~["\\","\""] )))
		| ("\'" (~["\'","\\"])* ("\\" ( ~["\\","\'"] )))
	>
|	< TRUE : "true" >
|	< FALSE : "false" >
|	< NULL : "null" >
|	< DOT : "." >
|	< LPAREN : "(" >
|	< RPAREN : ")" >
|	< LBRACK : "[" >
|	< RBRACK : "]" >
|	< COLON : ":" >
|	< COMMA : "," >
|       < SEMICOLON : ";" >
|	< 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>)* >
|	< #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: (~[]) >
}

