/*
 * Copyright 2002,2004 The Apache Software Foundation.
 * 
 * Licensed 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.
 */

/**
 *  Jexl : Java Expression Language
 *
 *  @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
 *  @author <a href="mailto:mhw@kremvax.net">Mark H. Wilkinson</a>
 *
 *  @version $Id: Parser.jjt 136562 2004-02-28 13:45:22Z yoavs $
 */

options
{
   MULTI=true;
   STATIC=false;
   VISITOR=true;
   NODE_USES_PARSER=true;
}

PARSER_BEGIN(Parser)

package org.apache.commons.jexl.parser;

import java.io.Reader;
import java.io.ByteArrayInputStream;

public class Parser
{
    public SimpleNode parse(Reader reader)
        throws Exception
    {
        ReInit(reader);

        /*
         *  lets do the 'Unique Init' in here to be
         *  safe - it's a pain to remember
         */

        SimpleNode tree = JexlScript();
        return tree;
    }
}

PARSER_END(Parser)


SKIP : /* WHITE SPACE */
{
    <COMMENT: ( "##" ( ~["\"","\n","\r"] )* ("\n" | "\r") ) >
|
  " "
| "\t"
| "\n"
| "\r"
| "\f"
}

TOKEN : /* LITERALS */
{
  < INTEGER_LITERAL: (<DIGIT>)+ >
|
  < FLOAT_LITERAL: (<DIGIT>)+ "."(<DIGIT>)+ >
}

/*
 * Program structuring syntax follows.
 */

SimpleNode JexlScript() :
{
   String name;
}
{
    (
       Statement()
    )*
    <EOF>
   { return jjtThis;}
}


void Block() :
{}
{
  "{" ( Statement() )* "}"
}

void EmptyFunction() : {}
{
    "empty"
    ( Reference() | "(" Reference() ")" )
}

void SizeFunction() : {}
{
    "size" "(" Reference() ")"
}

void Identifier() :
{
   Token t;
}
{
    t=<IDENTIFIER>
    {
       jjtThis.val = t.image;
    }
}


/*
 * Expression syntax follows.
 */

void Expression() : {}
{
  LOOKAHEAD( PrimaryExpression() "=" )
  Assignment()
|
  ConditionalOrExpression()
}

void Assignment() #Assignment(2) :
{}
{
  PrimaryExpression() "=" Expression()
}

void ConditionalOrExpression() #void :
{}
{
  ConditionalAndExpression()
  (
    "||" ConditionalAndExpression() #OrNode(2)
  |
    "or" ConditionalAndExpression() #OrNode(2)
  )*
}

void ConditionalAndExpression() #void :
{}
{
  InclusiveOrExpression()
  (
    "&&" InclusiveOrExpression() #AndNode(2)
  |
    "and" InclusiveOrExpression() #AndNode(2)
  )*
}

void InclusiveOrExpression() #void :
{}
{
  ExclusiveOrExpression()
  ( "|" ExclusiveOrExpression() #BitwiseOrNode(2) )*
}

void ExclusiveOrExpression() #void :
{}
{
  AndExpression()
  ( "^" AndExpression() #BitwiseXorNode(2) )*
}

void AndExpression() #void :
{}
{
  EqualityExpression()
  ( "&" EqualityExpression() #BitwiseAndNode(2) )*
}

void EqualityExpression() #void :
{}
{
  RelationalExpression()
  (
     "==" RelationalExpression() #EQNode(2)
   |
     "eq" RelationalExpression() #EQNode(2)
   |
     "!=" RelationalExpression() #NENode(2)
   |
     "ne" RelationalExpression() #NENode(2)
  )*
}

void RelationalExpression() #void :
{}
{
  AdditiveExpression()
  (
    "<" AdditiveExpression() #LTNode(2)
   |
    "lt" AdditiveExpression() #LTNode(2)
   |
    ">" AdditiveExpression() #GTNode(2)
   |
    "gt" AdditiveExpression() #GTNode(2)
   |
    "<=" AdditiveExpression() #LENode(2)
   |
    "le" AdditiveExpression() #LENode(2)
   |
    ">=" AdditiveExpression() #GENode(2)
   |
    "ge" AdditiveExpression() #GENode(2)
  )*
}

void AdditiveExpression() #void :
{}
{
  MultiplicativeExpression()
  (
    "+" MultiplicativeExpression() #AddNode(2)
   |
    "-" MultiplicativeExpression() #SubtractNode(2)
  )*
}

void MultiplicativeExpression() #void :
{}
{
  UnaryExpression()
  (
    "*" UnaryExpression() #MulNode(2)
   |
    "/" UnaryExpression() #DivNode(2)
   |
    "div" UnaryExpression() #DivNode(2)
   |
    "%" UnaryExpression() #ModNode(2)
   |
    "mod" UnaryExpression() #ModNode(2)
  )*
}

void UnaryExpression() #void :
{}
{
  "-" UnaryExpression() #UnaryMinusNode(1)
|
  "~" UnaryExpression() #BitwiseComplNode(1)
|
  "!" UnaryExpression() #NotNode(1)
|
  "not" UnaryExpression() #NotNode(1)
|
  PrimaryExpression()
}

void PrimaryExpression() #void :
{}
{
  Literal()
|
  Reference()
|
  "(" Expression() ")"
|
  EmptyFunction()
|
  SizeFunction()
}


void Literal() #void :
{
   Token t;
}
{
  IntegerLiteral()
|
  FloatLiteral()
|
  BooleanLiteral()
|
  StringLiteral()
|
  NullLiteral()
}

void NullLiteral() : {}
{
    "null"
}

void BooleanLiteral() #void :
{}
{
  "true" #TrueNode
|
  "false" #FalseNode
}

void IntegerLiteral() :
{
  Token t;
}
{
 (
  t=<INTEGER_LITERAL>
    {
       jjtThis.val = Integer.valueOf(t.image);
    }
 )
}

void FloatLiteral() :
{
  Token t;
}
{
 (
  t=<FLOAT_LITERAL>
    {
       jjtThis.val = Float.valueOf(t.image);
    }
 )
}

void StringLiteral() :
{
   Token t;
}
{
 (
  t=<STRING_LITERAL>
    {
       jjtThis.literal = t.image.substring(1,t.image.length()-1);
    }
 )
}

/*
 * Statement syntax follows.
 */

void Statement() #void :
{}
{
  ";"
|
  Block()
|
  LOOKAHEAD( Reference() ";" ) ReferenceExpression()
|
  LOOKAHEAD( PrimaryExpression() "=" ) StatementExpression()
|
  ExpressionExpression()
|
  IfStatement()
|
  ForeachStatement()
|
  WhileStatement()
}


void ExpressionExpression() : {}
{
    Expression() ";"
}

void StatementExpression() : {}
{
  Assignment() ";"
}

void ReferenceExpression() : {}
{
  Reference() ";"
}

void IfStatement() :{}
{
  "if" "(" Expression() ")" Statement() [ LOOKAHEAD(1) "else" Statement() ]
}


void WhileStatement() :
{}
{
  "while" "(" Expression() ")" Statement()
}

void ForeachStatement() :
{}
{
  "foreach" "(" Reference() "in"  Reference() ")" Statement()
}

void Method() : {}
{
   Identifier() "("[ Parameter() ( "," Parameter() )* ] ")"
}

void ArrayAccess() : {}
{
    Identifier() ("[" ( LOOKAHEAD(3) Expression() | IntegerLiteral() | Reference() ) "]")+
}

void SizeMethod() : {}
{
	"size" "(" ")"
}

void Reference() : {}
{
  (LOOKAHEAD(Identifier() "[" ( Expression() | IntegerLiteral() | Reference()) "]") ArrayAccess() | Identifier())
  (LOOKAHEAD(2) "."
    (
      LOOKAHEAD(Identifier() "[" ( Expression() | IntegerLiteral() | Reference()) "]") ArrayAccess() |
//      (LOOKAHEAD(3) Method() | Identifier() |  IntegerLiteral() )
      (LOOKAHEAD(3) Method() | SizeMethod() | Identifier() |  IntegerLiteral() )

    )
  )*
}


void Parameter() #void: {}
{
   LOOKAHEAD(3) Expression() |  Literal() | Reference()
}

TOKEN : /* IDENTIFIERS */
{
  < IDENTIFIER: <LETTER> (<LETTER>|<DIGIT>)* >
|
  < #LETTER: [ "a"-"z", "A"-"Z", "_", "$" ] >
|
  < #DIGIT: [ "0"-"9"] >
}

TOKEN :
{
    <STRING_LITERAL :
        ("\"" ( ~["\"","\n","\r"] )* "\"" )
     |
        ("\'" ( ~["\'","\n","\r"] )* "\'" )
    >
}