/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- 
 * $Id: EC.jj,v 1.17 2000/09/09 19:51:48 metlov Exp $
 *
 * This file is part of the Java Expressions Library (JEL).
 *   For more information about JEL visit :
 *    http://galaxy.fzu.cz/JEL/
 *
 * (c) 1998 -- 2000 by Konstantin Metlov(metlov@fzu.cz);
 *
 * JEL is Distributed under the terms of GNU General Public License.
 *    This code comes with ABSOLUTELY NO WARRANTY.
 *  For license details see COPYING file in this directory.
 */

/*
 * This is the grammar definition for the expression language,
 * compiled by JEL. Requires JavaCC to rebuild parser.
 */
options {
    LOOKAHEAD=1;
    CHOICE_AMBIGUITY_CHECK=2;
    OTHER_AMBIGUITY_CHECK=2;
    OUTPUT_DIRECTORY="generated";
    ERROR_REPORTING=true;
    STATIC=false;
}

PARSER_BEGIN(EC)
package gnu.jel.generated;

import gnu.jel.*;
import gnu.jel.reflect.Member;
import java.io.StringReader;
import gnu.jel.debug.Debug;
import java.util.Stack;

public class EC { /* EC stands for ExpressionCompiler */
  TypesStack typesStk=null;
  Stack paramOPs=null;
  OPlist list=null;
  Token errToken=null;
  Library lib=null;

  public OPlist parse(Class resType, Library lib) 
    throws CompilationException {
    
    // checks of the assumptions made later
    if (Debug.enabled)
      Debug.assert(((MINUS-PLUS)==1) && ((NE-EQ)==1) && ((GE-LT)==1) && 
                   ((GT-GE)==1) && ((LE-GT)==1) && ((RS-LS)==1) &&
                   ((RUS-RS)==1) && ((DIVIDE-MULTIPLY)==1) && 
                   ((REMAINDER-DIVIDE)==1) && ((LOGCOMPL-BWCOMPL)==1));
    
    this.lib=lib;
    typesStk=new TypesStack();
    paramOPs=new Stack();
    list=new OPlist();
    try {
      expression(resType);
    } catch (CompilationException exc) {
      if (Debug.enabled)
        Debug.assert(errToken!=null,"Error token is not set.");
      exc.setColumn(errToken.beginColumn);
      throw exc;
    } catch (ParseException pe) {
      throw new CompilationException(pe.currentToken.next.beginColumn,
                                     pe.currentToken.next.kind==0?1:2,
                                     pe.currentToken.next);
    } catch (TokenMgrError tme) {
      throw new CompilationException(tme.col,tme.isEOF?1:3,
                                     new Character(tme.encountered));
    };
    return list;
  }


  private void notePar() {
    if (list.size()>0)
      paramOPs.push(list.getLast());
    else
      paramOPs.push(null);
  }

  private void genDVCall(String varName)
    throws CompilationException {
    String typeName=lib.getDVTypeName(varName);
    notePar(); // placeholder for "this" reference
    int paramsStart=typesStk.size();
    list.addLast(new OPload(typesStk,varName));
    notePar();
    functionCall(null,"get"+typeName+"Property",paramsStart);
  }


  private void functionCall(Class resolveInClass,String name,int paramsStart) 
    throws CompilationException {
    // collect params
    int np=typesStk.size()-paramsStart;
    Class[] params=new Class[np];
    OP[] paramsOPs=new OP[np];
    for(int i=np-1;i>=0;i--) {
      params[i]=typesStk.pop();
      paramsOPs[i]=(OP)paramOPs.pop();
    };
    
    // find method
    Member m=null; 
    if (resolveInClass!=null) {
      m=lib.getMember(resolveInClass,name,params);
    } else
      m=lib.getMember(name,params);
    
    
    // put "this" pointer in place
    if (resolveInClass==null) {
      OP thisOP=(OP)paramOPs.pop();
      if ((m.getModifiers() & 0x0008)==0) {
        // insert loading of "this" pointer
        OP op=new OPcall(typesStk,1,(new Object[0]).getClass());
        if (thisOP==null) 
          list.addFirst(op); 
        else
          list.addAfter(thisOP,op); 
        thisOP=op;
        paramOPs.push(op);
        int classID=lib.getDynamicMethodClassID(m);
        op=new OPload(typesStk,Integer.TYPE,new Integer(classID));
        list.addAfter(thisOP,op); thisOP=op;
        paramOPs.push(op);
        op=new OPbinary(typesStk,paramOPs,19,list);
        list.addAfter(thisOP,op); thisOP=op;
        paramOPs.push(thisOP);
      };
    };
    
    // restore params & param ops
    for(int i=0;i<np;i++) {
      typesStk.push(params[i]);
      paramOPs.push(paramsOPs[i]);
    };
    list.addLast(new OPcall(typesStk,paramOPs,m,list,
                            lib.isStateless(m)));
  }

  
  private void binOP(int code,Token errToken) throws CompilationException {
    this.errToken=errToken;
    notePar();
    list.addLast(new OPbinary(typesStk,paramOPs,code,list));
  }
};
PARSER_END(EC)

SKIP :
{
 	" "
  |	"\r"
  |	"\t"
}

TOKEN : /* OPERATORS */
{
	< PLUS: "+" >
  |	< MINUS: "-" >
  | < DOT: ".">
  |     < BWCOMPL: "~">
  |     < LOGCOMPL: "!">
  |	< MULTIPLY: "*" >
  |	< DIVIDE: "/" >
  |     < REMAINDER: "%" >
  |	< AND: "&" >
  |	< LAND: "&&" >
  |	< OR: "|" >
  |	< LOR: "||" >
  |	< XOR: "^" >
  |     < EQ: "==" >
  |     < NE: "!=" >
  |     < LT: "<" >
  |     < GE: ">=" >
  |     < GT: ">" >
  |     < LE: "<=" >
  |     < LS: "<<" >
  |     < RS: ">>" >
  |     < RUS: ">>>" >
}

TOKEN : /* Boolean Literals */
{
        < FALSE: "false" >
|       < TRUE: "true" >
}

TOKEN :  // Literals (taken from JavaCC Java grammar example)
{
  < INTEGER_LITERAL:
        <DECIMAL_LITERAL> (["l","L"])?
      | <HEX_LITERAL> (["l","L"])?
      | <OCTAL_LITERAL> (["l","L"])?
  >
|
  < #DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])* >
|
  < #HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ >
|
  < #OCTAL_LITERAL: "0" (["0"-"7"])* >
|
  < FLOATING_POINT_LITERAL:
        (["0"-"9"])+ "." (["0"-"9"])* (<EXPONENT>)? (["f","F","d","D"])?
      | "." (["0"-"9"])+ (<EXPONENT>)? (["f","F","d","D"])?
      | (["0"-"9"])+ <EXPONENT> (["f","F","d","D"])?
      | (["0"-"9"])+ (<EXPONENT>)? ["f","F","d","D"]
  >
|
  < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ >
|
  < CHARACTER_LITERAL:
      "'"
      (   (~["'","\\","\n","\r"])
        | ("\\"
            ( ["n","t","b","r","f","\\","'","\""]
            | ["0"-"7"] ( ["0"-"7"] )?
            | ["0"-"3"] ["0"-"7"] ["0"-"7"]
            )
          )
      )
      "'"
  >
| 
  < STRING_LITERAL:
      "\""
      (   (~["\"","\\","\n","\r"])
        | ("\\"
            ( ["n","t","b","r","f","\\","'","\""]
            | ["0"-"7"] ( ["0"-"7"] )?
            | ["0"-"3"] ["0"-"7"] ["0"-"7"]
            )
          )
      )*
      "\""
  >
}

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

void expression(Class resultType) 
  throws CompilationException :
{Token x;}
{
  conditional() (x=<EOF>)
    {
      errToken=x;
      // remove TSB at return if present
      if (typesStk.peekID()==10)
        list.addLast(new OPunary(typesStk,11,null,false));
      
      if (resultType!=null) 
        list.addLast(new OPunary(typesStk,TypesStack.typeID(resultType),
                                 resultType,false));
     
      list.addLast(new OPunary(typesStk,3)); // add the "return" instr.
      
      if (Debug.enabled)
        Debug.assert(typesStk.size()==0,
                     "Words left in stack while parsing.");
    }
}

void conditional() throws CompilationException :
{
  OPlist oldList=null;
  OPlist trueList=null;
  int stackSizeBeforeBranch=0;
  int stackSizeAfterFirstBranch=0;
  Token x;
}
{
  lor() [ x="?" {
    stackSizeBeforeBranch=typesStk.size();
    oldList=list;
    list=new OPlist();
  }
  conditional() 
  ":" {
    stackSizeAfterFirstBranch=typesStk.size();
    trueList=list;
    list=new OPlist();
  }
  conditional() {
    errToken=x;
    if (Debug.enabled)
      Debug.assert((typesStk.size()==stackSizeAfterFirstBranch+1) && 
                   (stackSizeAfterFirstBranch==stackSizeBeforeBranch+1),
                   "Stack in conditional branches is not balanced.");
    OP opcond=new OPcondtnl(typesStk,oldList,trueList,list);
    list=oldList;
    list.addLast(opcond);
  }
  ]
}

void lor() throws CompilationException :
{Token x;}
{
  land() ( x=<LOR> {notePar();} 
  land() {
    binOP(18,x);
  } )*
}

void land() throws CompilationException :
{Token x;}
{
  bor() ( x=<LAND> {notePar();} 
  bor() {binOP(17,x);} )*
}

void bor() throws CompilationException :
{Token x;}
{
  bxor() ( x=<OR> {notePar();} 
  bxor() {binOP(6,x);} )*
}


void bxor() throws CompilationException :
{Token x;}
{
  band() ( x=<XOR> {notePar();} 
  band() {binOP(7,x);} )*
}

void band() throws CompilationException :
{Token x;}
{
  equality() ( x=<AND> {notePar();} 
  equality() {binOP(5,x);} )*
}

void equality() throws CompilationException :
{Token x;}
{
  relation() ( (x=<EQ>|x=<NE>) {notePar();} 
  relation() {binOP(8+x.kind-EQ,x);} )*
}

void relation() throws CompilationException :
{Token x;}
{
  shift() ( (x=<LT>|x=<GE>|x=<GT>|x=<LE>) 
		     {notePar();} 
  shift() {binOP(10+x.kind-LT,x);} )*
  
}

void shift() throws CompilationException :
{Token x;}
{
  sum() ( (x=<LS>|x=<RS>|x=<RUS>) 
		   {notePar();} 
  sum() {binOP(14+x.kind-LS,x);} )*
}

void sum() throws CompilationException :
{Token x;}
{
  term() ((x=<PLUS> | x=<MINUS>) 
           {notePar();} 
  term() {binOP(x.kind-PLUS,x);} )*
}

void term() throws CompilationException :
{Token x;}
{
  unary() ((x=<MULTIPLY> |x=<DIVIDE>|x=<REMAINDER> ) 
           {notePar();} 
  unary() {binOP(2+x.kind-MULTIPLY,x);} )*
}

void unary() throws CompilationException :
{
  Token x;
  Token type_to;
}
{
  x=<MINUS> element() 
  {
    errToken=x;
    list.addLast(new OPunary(typesStk,0));
  }
| (x=<BWCOMPL>|x=<LOGCOMPL>) element()
  {
    errToken=x;
    list.addLast(new OPunary(typesStk,1+x.kind-BWCOMPL));
  }
| LOOKAHEAD(3) "(" (type_to=<ID>) ")" element()
  {
    errToken=type_to;
    int typeID=0;
    while ((typeID<8) &&
           !type_to.image.equals(TypesStack.specialTypes[typeID].toString()))
      typeID++;
    
    if (typeID>=8) // The type name is unknown.
      throw new CompilationException(-1,4,type_to.image);

    list.addLast(new OPunary(typesStk,typeID,null,true));
  }
| element()
}

void element() throws CompilationException :
{
  StringBuffer accumulator=null;
}
{
   <TRUE>  { list.addLast(new OPload(typesStk,Boolean.TRUE));}
 | <FALSE> { list.addLast(new OPload(typesStk,Boolean.FALSE));}
 | 
 ( 
  (
    literal() 
  | "(" conditional() ")"
  | invocation(accumulator=new StringBuffer(), false)
  ) 
   ( 
    <DOT> 
    invocation(accumulator!=null?accumulator:(accumulator=new StringBuffer()),
               true)
   )*

 {
   if ((accumulator!=null) && accumulator.length()>0)
     genDVCall(accumulator.toString());
 }
 )
}

void invocation(StringBuffer accumulator, 
                boolean afterDot) throws CompilationException :
{
  int paramsStart=0;
  Class resolveIn=null;
  Token x,id;
}
{
  id=<ID> 
  {
    if (!afterDot) {
      accumulator.append(id.image);

      // check if such DV exists, if no -- abort match
      if (lib.getDVTypeName(id.image)==null) accumulator.setLength(0);
    } else {
      if (accumulator.length()>0) {
        int oldLen=accumulator.length();
        accumulator.append('.');
        accumulator.append(id.image);
        
        // if the accumulated+'.'+new does not match, load what had 
        // had already matched and abort match
        if (lib.getDVTypeName(accumulator.toString())==null) {
          accumulator.setLength(oldLen); // back up
          genDVCall(accumulator.toString());
          accumulator.setLength(0); // this name will be tried as method
        };
      };
      if (accumulator.length()==0) resolveIn=typesStk.peek();
    }
    if (accumulator.length()==0) {
      // start prepating a call to an object's method x.image
      notePar(); // placeholder for "this" reference
      paramsStart=typesStk.size();
    };
  }
    [ 
     x="(" 
      {
        if (accumulator.length()>0) {
          // error variable must have no parameters
          errToken=x;
          throw new CompilationException(-1,26,null);
          
          // this is the place to hack in the abort of match and check
          // if the last matched name can be called as a method
        };
      }
     [ conditional() {notePar();} ( "," conditional() {notePar();} )* ]
     ")" 
    ]
    
      {
        if (accumulator.length()==0) {
          // generate the method invocation
          errToken=id; 
          functionCall(resolveIn,id.image,paramsStart);
        };
      }

    (
     x="["
      {
        if (accumulator.length()>0) {
          // load the DV
          genDVCall(accumulator.toString());
          accumulator.setLength(0);
        };
        notePar();
      }
     conditional()
     x="]" {binOP(19,x);}
    )*
}

void literal() throws CompilationException :
{}
{
  <INTEGER_LITERAL> {{
    errToken=token;
    String svalue=token.image.toUpperCase();
    long value=0;
    boolean makelong=svalue.endsWith("L");
    if (makelong) svalue=svalue.substring(0,svalue.length()-1);
    
    try {
      if ( svalue.startsWith("0x") || svalue.startsWith("0X") ) {
	// Hexadecimal number
	svalue=svalue.substring(2);
	value=Long.parseLong(svalue,16);
      } else if (token.image.startsWith("0")) {
	// Octal number
	value=Long.parseLong(svalue,8);
      } else {
	// Decimal number
	value=Long.parseLong(svalue,10);
      };
    } catch (NumberFormatException e) {
      throw new CompilationException(-1,13,token.image); // Overflow ?
    };
    
    Object val=null;
    if (!makelong) { // Check ranges
      if (value<=127) val=new Byte((byte)value);
      else if (value<=32767) val=new Short((short)value);
      else if (value<=2147483647) val=new Integer((int)value);
      else throw new CompilationException(-1,14,token.image);
    } else {
      val=new Long(value);
    };
    
    list.addLast(new OPload(typesStk,val));
  }}
| <FLOATING_POINT_LITERAL> {{
  errToken=token;
  char lc=Character.toUpperCase(token.image.charAt(token.image.length()-1));
  boolean makeFloat = (lc=='F');
  String svalue=token.image;
  if ((lc=='D') || (lc=='F')) {
    svalue=svalue.substring(0,svalue.length()-1);
  };
  Double value=null;
  try {
    value=new Double(svalue);
  } catch (NumberFormatException e) {
    // Overflow ? Will produce infinity.
  };
  if (makeFloat) 
    list.addLast(new OPload(typesStk,new Float(value.floatValue())));
  else
    list.addLast(new OPload(typesStk,value));
  }}
| <CHARACTER_LITERAL> {{
  errToken=token;
  String sval=token.image.substring(1,token.image.length()-1);
  if (sval.length()==1) 
    list.addLast(new OPload(typesStk,new Character(sval.charAt(0))));
  else { // escape or number
    char ec=sval.charAt(1);
    try {
      switch (ec) {
      case 'n': ec='\n'; break;
      case 't': ec='\t'; break;
      case 'b': ec='\b'; break;
      case 'r': ec='\r'; break;
      case 'f': ec='\f'; break;
      case '\\': ec='\\'; break;
      case '\'': ec='\''; break;
      case '\"': ec='"'; break;
      default:
        ec=(char) Integer.parseInt(sval.substring(1),8);
      };
    } catch (NumberFormatException e) {/* Can't be */};
    list.addLast(new OPload(typesStk,new Character(ec)));
  };
  }}
| <STRING_LITERAL> {{
  String sval=token.image.substring(1,token.image.length()-1);
  StringBuffer unescaped=new StringBuffer(sval.length());
  for(int i=0;i<sval.length();i++) {
    char ec=sval.charAt(i);
    if (ec=='\\') { // escape
      ec=sval.charAt(++i);
      switch (ec) {
      case 'n': ec='\n'; break;
      case 't': ec='\t'; break;
      case 'b': ec='\b'; break;
      case 'r': ec='\r'; break;
      case 'f': ec='\f'; break;
      case '\\': ec='\\'; break;
      case '\'': ec='\''; break;
      case '\"': ec='"'; break;
      default:
	int nval=0;
	while ((i<sval.length()) && ((ec=sval.charAt(i))>='0') && (ec<='7')) {
	  nval=nval<<3+(ec-'0');
	  i++;
	};
	i--;
	ec=(char)nval;
      };
    };
    unescaped.append(ec);
  };
  list.addLast(new OPload(typesStk,unescaped.toString()));
  }}
}












