//--------------------------------------------------------------------------
//	Copyright (c) 1998-2004, Drew Davidson and Luke Blanshard
//  All rights reserved.
//
//	Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//	Redistributions of source code must retain the above copyright notice,
//  this list of conditions and the following disclaimer.
//	Redistributions in binary form must reproduce the above copyright
//  notice, this list of conditions and the following disclaimer in the
//  documentation and/or other materials provided with the distribution.
//	Neither the name of the Drew Davidson nor the names of its contributors
//  may be used to endorse or promote products derived from this software
//  without specific prior written permission.
//
//	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
//  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
//  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
//  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
//  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
//  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
//  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
//  DAMAGE.
//--------------------------------------------------------------------------

/*
 * This file defines the syntax of OGNL, the Object-Graph Navigation Language.  This
 * language was devised by Drew Davidson, who called it Key-Value Coding Language.  Luke
 * Blanshard then made up the new name and reimplemented it using ANTLR, refining and
 * polishing the language a bit on the way.  Drew maintained the system for a couple of
 * years; then Luke converted the ANTLR grammar to JavaCC, to eliminate the run-time
 * dependency on ANTLR.
 *
 * See package.html for a description of the language.
 */

options {
      // Parser options
    LOOKAHEAD           = 1;
    STATIC              = false;
    JAVA_UNICODE_ESCAPE = true;
    UNICODE_INPUT       = true;

      // Tree options
    MULTI             = true;
    NODE_DEFAULT_VOID = true;
}

PARSER_BEGIN(OgnlParser)

package ognl;

import java.math.*;

/**
 * OgnlParser is a JavaCC parser class; it translates OGNL expressions into abstract
 * syntax trees (ASTs) that can then be interpreted by the getValue and setValue methods.
 */
public class OgnlParser
{
}

PARSER_END(OgnlParser)




/**
 * This is the top-level construct of OGNL.
 */
Node topLevelExpression() : {}
{
    expression() <EOF> { return jjtree.rootNode(); }
}

// sequence (level 14)
void expression() : {}
{
    assignmentExpression() ( "," assignmentExpression() #Sequence(2) )*
}

// assignment expression (level 13)
void assignmentExpression() : {}
{
    conditionalTestExpression() [ "=" assignmentExpression() #Assign(2) ]
}

// conditional test (level 12)
void conditionalTestExpression() : {}
{
    logicalOrExpression()
        [ "?" conditionalTestExpression() ":" conditionalTestExpression() #Test(3) ]
}

// logical or (||)  (level 11)
void logicalOrExpression() : {}
{
    logicalAndExpression() (("||" | "or") logicalAndExpression() #Or(2) )*
}


// logical and (&&)  (level 10)
void logicalAndExpression() : {}
{
    inclusiveOrExpression() (("&&" | "and") inclusiveOrExpression() #And(2) )*
}


// bitwise or non-short-circuiting or (|)  (level 9)
void inclusiveOrExpression() : {}
{
    exclusiveOrExpression() (("|" | "bor") exclusiveOrExpression() #BitOr(2) )*
}


// exclusive or (^)  (level 8)
void exclusiveOrExpression() : {}
{
    andExpression() (("^" | "xor") andExpression() #Xor(2) )*
}


// bitwise or non-short-circuiting and (&)  (level 7)
void andExpression() : {}
{
    equalityExpression() (("&" | "band") equalityExpression() #BitAnd(2) )*
}


// equality/inequality (==/!=) (level 6)
void equalityExpression() : {}
{
    relationalExpression()
    (
        ("==" | "eq") relationalExpression() #Eq(2)
     |
        ("!=" | "neq") relationalExpression() #NotEq(2)
    )*
}


// boolean relational expressions (level 5)
void relationalExpression() : {}
{
    shiftExpression()
    (
        ("<" | "lt") shiftExpression() #Less(2)
     |
        (">" | "gt") shiftExpression() #Greater(2)
     |
        ("<=" | "lte") shiftExpression() #LessEq(2)
     |
        (">=" | "gte") shiftExpression() #GreaterEq(2)
     |
        "in" shiftExpression() #In(2)
     |
        "not" "in" shiftExpression() #NotIn(2)
    )*
}


// bit shift expressions (level 4)
void shiftExpression() : {}
{
    additiveExpression()
    (
        ("<<" | "shl") additiveExpression() #ShiftLeft(2)
     |
        (">>" | "shr") additiveExpression() #ShiftRight(2)
     |
        (">>>" | "ushr") additiveExpression() #UnsignedShiftRight(2)
    )*
}


// binary addition/subtraction (level 3)
void additiveExpression() : {}
{
    multiplicativeExpression()
    (
        "+" multiplicativeExpression() #Add(2)
     |
        "-" multiplicativeExpression() #Subtract(2)
    )*
}


// multiplication/division/remainder (level 2)
void multiplicativeExpression() : {}
{
    unaryExpression()
    (
        "*" unaryExpression() #Multiply(2)
     |
        "/" unaryExpression() #Divide(2)
     |
        "%" unaryExpression() #Remainder(2)
    )*
}

// unary (level 1)
void unaryExpression() : {
    StringBuffer sb;
    Token t;
    ASTInstanceof ionode;
}
{
    (
        "-" unaryExpression() #Negate(1)
     |
        "+" unaryExpression() // Just leave it there
     |
        "~" unaryExpression() #BitNegate(1)
     |
        ("!" | "not") unaryExpression() #Not(1)
     |
        navigationChain()
        [
            "instanceof"
            t = <IDENT>  { sb = new StringBuffer(t.image); ionode = jjtThis; } #Instanceof(1)
            (   "." t = <IDENT>         { sb.append('.').append( t.image ); }
            )*                          { ionode.setTargetType( new String(sb) ); }
        ]
    )
}


// navigation chain: property references, method calls, projections, selections, etc.
void navigationChain() : {}
{
    primaryExpression()
    (   "."
        ( /* Prevent the "eval" ambiguity from issuing a warning; see discussion below. */
            ( LOOKAHEAD(2) methodCall() | propertyName() )
              // Also handle "{", which requires a lookahead of 2.
        |   ( LOOKAHEAD(2) projection() | selection() )
        |   "(" expression() ")"
        ) #Chain(2)

    |   index() #Chain(2)

    |   "(" expression() ")" #Eval(2)

            /* Using parentheses to indicate evaluation of the current
               object makes this language ambiguous, because the
               expression "ident(args)" could be seen as a single
               method call or as a property name followed by an
               evaluation.  We always put the method call first and
               turn off the ambiguity warning; we always want to
               interpret this as a method call. */

    )*
}


void primaryExpression() : {
    Token   t;
    String  className = null;
}
{
    (
        (<CHAR_LITERAL> | <BACK_CHAR_LITERAL> | <STRING_LITERAL> | <INT_LITERAL> | <FLT_LITERAL>)
                                                { jjtThis.setValue( token_source.literalValue ); } #Const(0)
     |
        "true"                                  { jjtThis.setValue( Boolean.TRUE ); }  #Const(0)
     |
        "false"                                 { jjtThis.setValue( Boolean.FALSE ); } #Const(0)
     |
        "null" #Const(0)                        // Null is the default value in an ASTConst
     |
        LOOKAHEAD(2) "#this"                 { jjtThis.setName( "this" ); } #ThisVarRef(0)
     |
        LOOKAHEAD(2) "#root"                 { jjtThis.setName( "root" ); } #RootVarRef(0)
     |
        LOOKAHEAD(2) "#" t=<IDENT>              { jjtThis.setName( t.image ); } #VarRef(0)
     |
        LOOKAHEAD(2) ":" "[" expression() "]"   { jjtThis.setValue( jjtThis.jjtGetChild(0) ); } #Const(1)
     |
        staticReference()
     |
        LOOKAHEAD(2) constructorCall()
     |
          // Prevent the "eval" ambiguity from issuing a warning; see discussion elsewhere.
        ( LOOKAHEAD(2) methodCall() | propertyName() )
     |
        index()
     |
        "(" expression() ")"
     |
        "{" [assignmentExpression() ("," assignmentExpression())*] #List "}"
     |
        LOOKAHEAD(2) ( "#" (className=classReference())? "{" [keyValueExpression() ("," keyValueExpression())*] { jjtThis.setClassName(className); } "}" ) #Map
    )
}

void keyValueExpression() : {}
{
        ( assignmentExpression() (":" assignmentExpression())? ) #KeyValue
}

void staticReference() : {
    String className = "java.lang.Math";
    Token t;
}
{
    className=classReference()
        ( // Prevent the "eval" ambiguity from issuing a warning; see discussion elsewhere.
            LOOKAHEAD(2)
            staticMethodCall( className )
         |
            t=<IDENT>               { jjtThis.init( className, t.image ); } #StaticField(0)
        )
}

String classReference(): {
    String      result = "java.lang.Math";
}
{
    "@" ( result=className() )? "@" { return result; }
}

String className(): {
    Token t;
    StringBuffer result;
}
{
    t=<IDENT>               { result = new StringBuffer( t.image ); }
    ( "." t=<IDENT>         { result.append('.').append( t.image ); }
    )*                      { return new String(result); }
}

void constructorCall() #Ctor : {
    String className;
    Token t;
    StringBuffer sb;
}
{
    "new" className=className()
        (
            LOOKAHEAD(2) (
                "(" [ assignmentExpression() ( "," assignmentExpression() )* ] ")"
                    {
                        jjtThis.setClassName(className);
                    }
            )
            |
            LOOKAHEAD(2) (
                "[" "]" "{" [assignmentExpression() ("," assignmentExpression())*] #List "}"
                    {
                        jjtThis.setClassName(className);
                        jjtThis.setArray(true);
                    }
            )
            |
            LOOKAHEAD(2) (
                "[" assignmentExpression() "]"
                    {
                        jjtThis.setClassName(className);
                        jjtThis.setArray(true);
                    }
            )
        )
}

void propertyName() #Property : {
    Token t;
}
{
    t=<IDENT> { jjtThis.setValue( t.image ); } #Const
}

void staticMethodCall( String className ) #StaticMethod : {
    Token t;
}
{
    t=<IDENT> "(" [ assignmentExpression() ( "," assignmentExpression() )* ] ")"
                                        { jjtThis.init( className, t.image ); }
}

void methodCall() #Method : {
    Token t;
}
{
    t=<IDENT> "(" [ assignmentExpression() ( "," assignmentExpression() )* ] ")"
                                        { jjtThis.setMethodName( t.image ); }
}

/**
 * Apply an expression to all elements of a collection, creating a new collection
 * as the result.
 */
void projection() #Project : {}
{
    "{" expression() "}"
}

void selection() : {}
{
        LOOKAHEAD(2) selectAll()
    |
        LOOKAHEAD(2) selectFirst()
    |
        LOOKAHEAD(2) selectLast()
}

/**
 * Apply a boolean expression to all elements of a collection, creating a new collection
 * containing those elements for which the expression returned true.
 */
void selectAll() #Select : {}
{
    "{" "?" expression() "}"
}

/**
 * Apply a boolean expression to all elements of a collection, creating a new collection
 * containing those elements for the first element for which the expression returned true.
 */
void selectFirst() #SelectFirst : {}
{
    "{" "^" expression() "}"
}

/**
 * Apply a boolean expression to all elements of a collection, creating a new collection
 * containing those elements for the first element for which the expression returned true.
 */
void selectLast() #SelectLast : {}
{
    "{" "$" expression() "}"
}

void index() #Property : {}
{
    "[" expression() "]" { jjtThis.setIndexedAccess(true); }
 |
    <DYNAMIC_SUBSCRIPT> { jjtThis.setValue( token_source.literalValue ); } #Const
    {
        jjtThis.setIndexedAccess(true);
    }
}

// LEXER PRODUCTIONS

TOKEN_MGR_DECLS:
{
      /** Holds the last value computed by a constant token. */
    Object literalValue;
      /** Holds the last character escaped or in a character literal. */
    private char charValue;
      /** Holds char literal start token. */
    private char charLiteralStartQuote;
      /** Holds the last string literal parsed. */
    private StringBuffer stringBuffer;

      /** Converts an escape sequence into a character value. */
    private char escapeChar()
    {
        int ofs = image.length() - 1;
        switch ( image.charAt(ofs) ) {
            case 'n':   return '\n';
            case 'r':   return '\r';
            case 't':   return '\t';
            case 'b':   return '\b';
            case 'f':   return '\f';
            case '\\':  return '\\';
            case '\'':  return '\'';
            case '\"':  return '\"';
        }

          // Otherwise, it's an octal number.  Find the backslash and convert.
        while ( image.charAt(--ofs) != '\\' )
          {}
        int value = 0;
        while ( ++ofs < image.length() )
            value = (value << 3) | (image.charAt(ofs) - '0');
        return (char) value;
    }

    private Object makeInt()
    {
        Object  result;
        String  s = image.toString();
        int     base = 10;

        if ( s.charAt(0) == '0' )
            base = (s.length() > 1 && (s.charAt(1) == 'x' || s.charAt(1) == 'X'))? 16 : 8;
        if ( base == 16 )
            s = s.substring(2); // Trim the 0x off the front
        switch ( s.charAt(s.length()-1) ) {
            case 'l': case 'L':
                result = Long.valueOf( s.substring(0,s.length()-1), base );
                break;

            case 'h': case 'H':
                result = new BigInteger( s.substring(0,s.length()-1), base );
                break;

            default:
                result = Integer.valueOf( s, base );
                break;
        }
        return result;
    }

    private Object makeFloat()
    {
        String s = image.toString();
        switch ( s.charAt(s.length()-1) ) {
            case 'f': case 'F':
                return Float.valueOf( s );

            case 'b': case 'B':
                return new BigDecimal( s.substring(0,s.length()-1) );

            case 'd': case 'D':
            default:
                return Double.valueOf( s );
        }
    }
}

// Whitespace -- ignored
SKIP:
{  " " | "\t" | "\f" | "\r" | "\n" }

// An identifier.
TOKEN:
{
    < IDENT: <LETTER> (<LETTER>|<DIGIT>)* >
 |
    < #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"
      ] >
}

/**
 * Token for "dynamic subscripts", which are one of: [^], [|], [$], and [*].  The
 * appropriate constant from the DynamicSubscript class is stored in the token manager's
 * "value" field.
 */
TOKEN:
{
    < DYNAMIC_SUBSCRIPT: "[" ["^","|","$","*"] "]" >
        {
            switch (image.charAt(1)) {
              case '^': literalValue = DynamicSubscript.first; break;
              case '|': literalValue = DynamicSubscript.mid;   break;
              case '$': literalValue = DynamicSubscript.last;  break;
              case '*': literalValue = DynamicSubscript.all;   break;
          }
        }
}

/**
 * Character and string literals, whose object value is stored in the token manager's
 * "literalValue" field.
 */
MORE:
{
    "`"     : WithinBackCharLiteral
 |
    "'"     { stringBuffer = new StringBuffer(); }: WithinCharLiteral
 |
    "\""    { stringBuffer = new StringBuffer(); }: WithinStringLiteral
}

<WithinCharLiteral> MORE:
{
    < ESC: "\\" ( ["n","r","t","b","f","\\","'","`","\""]
                | (["0"-"3"])? ["0"-"7"] (["0"-"7"])?
                )
    >
        { charValue = escapeChar(); stringBuffer.append(charValue); }
 |
    < (~["'","\\"]) >
        { charValue = image.charAt( image.length()-1 ); stringBuffer.append(charValue); }
}

<WithinCharLiteral> TOKEN:
{
    < CHAR_LITERAL: "'">
        {
            if (stringBuffer.length() == 1) {
                literalValue = new Character( charValue );
            } else {
                literalValue = new String( stringBuffer );
            }
        }
        : DEFAULT
}

<WithinBackCharLiteral> MORE:
{
    < BACK_CHAR_ESC: <ESC> >
        { charValue = escapeChar(); }
 |
    < (~["`","\\"]) >
        { charValue = image.charAt( image.length()-1 ); }
}

<WithinBackCharLiteral> TOKEN:
{
    < BACK_CHAR_LITERAL: "`">
        { literalValue = new Character( charValue ); }: DEFAULT
}

<WithinStringLiteral> MORE:
{
    < STRING_ESC: <ESC> >
        { stringBuffer.append( escapeChar() ); }
 |
    < (~["\"","\\"]) >
        { stringBuffer.append( image.charAt(image.length()-1) ); }
}

<WithinStringLiteral> TOKEN:
{
    <STRING_LITERAL: "\"">
        { literalValue = new String( stringBuffer ); }
        : DEFAULT
}

/**
 * Integer or real Numeric literal, whose object value is stored in the token manager's
 * "literalValue" field.
 */
TOKEN:
{
    < INT_LITERAL:
        ( "0" (["0"-"7"])* | ["1"-"9"] (["0"-"9"])* | "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ )
        (["l","L","h","H"])?
    >
        { literalValue =
        makeInt(); }
 |
    < FLT_LITERAL:
        ( <DEC_FLT> (<EXPONENT>)? (<FLT_SUFF>)?
        | <DEC_DIGITS> <EXPONENT> (<FLT_SUFF>)?
        | <DEC_DIGITS> <FLT_SUFF>
        )
    >
        { literalValue = makeFloat(); }

 |  < #DEC_FLT: (["0"-"9"])+ "." (["0"-"9"])* | "." (["0"-"9"])+ >
 |  < #DEC_DIGITS: (["0"-"9"])+ >
 |  < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ >
 |  < #FLT_SUFF: ["d","D","f","F","b","B"] >
}

