/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * $Id: Evaluator.java 490 2006-10-01 16:08:04Z metlov $
 *
 * This file is part of the Java Expressions Library (JEL).
 *   For more information about JEL visit :
 *    http://kinetic.ac.donetsk.ua/JEL/
 *
 * (c) 1998 -- 2006 by Konstantin Metlov(metlov@kinetic.ac.donetsk.ua);
 *
 * 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.
 */

package gnu.jel; 

//import gnu.jel.generated.EC;
//import gnu.jel.generated.CharStream;
import gnu.jel.debug.Debug;
import java.lang.reflect.Member;

/**
 * This is the main frontend to JEL.
 * <P>It is intended for compilation of algebraic expressions involving 
 * functions.
 * <P> Syntax allows variables, which can be either a public fields of
 * certain objects or functions with no arguments.  If a method like
 * "<TT>double x() {}</TT>;" is defined in the dynamic library class
 * (see <tt>gnu.jel.Library</tt> documentation on how to do this), an
 * expression "<TT>sin(x)</TT>" will call the method "<TT>x()</TT>" (
 * and function <TT>Math.sin()</TT> ) each time it is evaluated.
 * Static methods in namespace are assumed to be stateless (unless
 * this default behaviour is explicitly overridden, as is necessary
 * for <tt>Math.random()</tt>) and will be called at compile time if
 * their arguments are known.
 * <P>It is possible to have any type of intermediate object
 * throughout the calculation as long as types of the function return
 * values and parameters stay compatible. The compiler can
 * do all the type conversions permissible in Java language and more 
 * (e.g. between reflection wrappers <tt>java.lang.Integer</tt>,... and
 * primitives). Widening type conversions (not leading to precision loss)
 * are applied by JEL automatically, narrowing should be explicitly 
 * requested.
 * <P>There is variant of the "compile" function with three arguments,
 * which allows to fix the type of the expression result. For example:
 * <PRE>
 * CompiledExpression expression=compile("2*6+6",lib,Double.TYPE); 
 * </PRE> 
 * will produce a compiled expression, whose return type is always
 * <TT>double</TT>. For additional information on how to use this
 * feature to eliminate object allocation overhead see
 * <TT>gnu.jel.CompiledExpression</TT> documentation.
 * 
 * <P> Care should be taken during the assembly of static and dynamic libraries
 * to avoid conflicts and unexpected return types.
 *
 * <P>(c) 1998-2003, by Konstantin Metlov<BR>
 * Prague, CZ
 * @see gnu.jel.CompiledExpression
 * @see gnu.jel.Library
 */
public class Evaluator {

  protected static ClassFile cf_orig;
  protected static int retID_patchback=0;
  protected static int retIDC_patchback=0;
  protected static LocalMethod[] eval_methods= new LocalMethod[10];

  static {
    try {
      // prepare eval methods
      Class[] paramsE=new Class[1];
      paramsE[0]=(new Object[0]).getClass();
      Class[] excptnsE=new Class[1];
      excptnsE[0]=Class.forName("java.lang.Throwable");

      for(int i=0;i<10;i++) {
        String name="evaluate";
        Class cls=OP.specialTypes[i];
        if (i!=8) 
          name=name+'_'+cls;
        else 
          cls=(new Object()).getClass();
        eval_methods[i]=new LocalMethod(0x0001,cls,name,paramsE,excptnsE);
      };
      
      Class cmplExpr=Class.forName("gnu.jel.CompiledExpression");
      ClassFile cf=new ClassFile(0x0001,"dump",cmplExpr,null,null);
      // public 
      LocalMethod cnstr=
        new LocalMethod(0x0001,Void.TYPE,"<init>",null,null);
      cf.newMethod(cnstr,null);
      cf.code(0x2a);                //| aload_0  ;loads "this"
      cf.noteStk(-1,11); // not important what, it must be a reference
      cf.codeM(cmplExpr.getConstructor(new Class[0]));
      cf.noteStk(11,-1);
      cf.code(0xb1);                //| return void
      
      LocalMethod getType=
      new LocalMethod(0x0001,Integer.TYPE,"getType",null,null);
      cf.newMethod(getType,null);
      cf.code(0x10);                //| bipush
      retID_patchback=cf.tsize;
      cf.code(8);                   //    type placeholder
      cf.noteStk(-1,4); // note "int"
      
      cf.code(0xAC);                //| ireturn
      cf.noteStk(4,-1); // rm "int"

      Class clazz=Class.forName("java.lang.Class");
      Class[] paramsC=new Class[1];
      paramsC[0]=Class.forName("java.lang.String");
      java.lang.reflect.Method forName=clazz.getMethod("forName",paramsC);
      Class[] excptnsC=new Class[1];
      excptnsC[0]=Class.forName("java.lang.ClassNotFoundException");

      LocalMethod getTypeC=
      new LocalMethod(0x0001,clazz,"getTypeC",null,excptnsC);
      cf.newMethod(getTypeC,null);
      cf.code(0x13);                //| ldc_w
      retIDC_patchback=cf.tsize;
      cf.writeShort(0);             //| Cp index to the class name
      cf.noteStk(-1,11);            // note string load
      cf.codeM(forName);            //| Class.forName
      cf.noteStk(11,8);             // string replaced by "class"
      cf.code(0xB0);                //| areturn
      cf.noteStk(8,-1);             // rm "class"

      cf_orig=(ClassFile)cf.clone();
    } catch (Exception exc) {
      if (Debug.enabled) Debug.reportThrowable(exc);
    };
  };
  
  /**
   * Compiles expression, resolving the function names in the library.
   * @param expression is the expression to compile. i.e. "sin(666)" .
   * @param lib Library of functions exported for use in expression.
   * @param resultType identifies the type result should be converted to. Can
   *        be null, in this case the result type is not fixed.
   * @return Instance of the CompiledExpression subclass, implementing
   *   the specified expression evaluation.
   * @exception gnu.jel.CompilationException if the expression is not
   *  syntactically or semantically correct.
   * @see gnu.jel.CompiledExpression
   */
  public static CompiledExpression compile(String expression, Library lib,
                                           Class resultType) 
    throws CompilationException {
    byte[] image=compileBits(expression,lib,resultType);
    try {
      return (CompiledExpression)(ImageLoader.load(image)).newInstance();
    } catch (Exception exc) {
      if (Debug.enabled)
        Debug.reportThrowable(exc);
      return null;
    }
  };
  
  /**
   * Compiles expression, resolving the function names in the library.
   * <P>This variant of compile allows to store expressions in a 
   * <TT>java.io.OutputStream</TT> using Java serialization mechanism.
   * @param expression is the expression to compile. i.e. "sin(666)" .
   * @param lib Library of functions exported for use in expression.
   * @param resultType identifies the type result should be converted to. Can
   *        be null, in this case the result type is not fixed.
   * @return Byte array, representing the expression
   * in a standard Java classfile format. It can be conveniently 
   * loaded (with renaming, if necessary)
   * into Java VM using <tt>gnu.jel.ImageLoader</tt>.
   * @exception gnu.jel.CompilationException if the expression is not
   *  syntactically or semantically correct.
   * @see gnu.jel.CompiledExpression
   * @see gnu.jel.ImageLoader
   */
  public static byte[] compileBits(String expression, Library lib,
                                   Class resultType) 
    throws CompilationException {
    OP code=parse(expression,lib,resultType);
    
    // Perform constants folding
    try {
      code=new OPload(code,code.eval());
    } catch (Exception exc) {
    };

    return getImage(code);
  };

  /**
   * Compiles expression, resolving the function names in the library.
   * @param expression is the expression to compile. i.e. "sin(666)" .
   * @param lib Library of functions exported for use in expression.
   * @return Instance of the CompiledExpression subclass, implementing
   *   the specified expression evaluation.
   * @exception gnu.jel.CompilationException if the expression is not
   *  syntactically or semantically correct.
   * @see gnu.jel.CompiledExpression
   */
  public static CompiledExpression compile(String expression, Library lib)
    throws CompilationException {
    return compile(expression, lib, null);
  };

  /**
   * Compiles expression, resolving the function names in the library.
   * <P>This variant of compile allows to store expressions in a 
   * <TT>java.io.OutputStream</TT> using Java serialization mechanism.
   * @param expression is the expression to compile. i.e. "sin(666)" .
   * @param lib Library of functions exported for use in expression.
   * @return Byte array, representing the expression
   * in a standard Java classfile format. It can be conveniently 
   * loaded (with renaming, if necessary)
   * into Java VM using <tt>gnu.jel.ImageLoader</tt>.
   * @exception gnu.jel.CompilationException if the expression is not
   *  syntactically or semantically correct.
   * @see gnu.jel.CompiledExpression
   * @see gnu.jel.ImageLoader
   */
  public static byte[] compileBits(String expression, Library lib)
    throws CompilationException {
    return compileBits(expression, lib, null);
  };

  static OP parse(String expression, Library lib,
                                Class resultType) throws CompilationException {
    return (new Parser(expression,lib)).parse(resultType);
    //    return (new EC(new CharStream(expression))).parse(resultType,lib);
  };

  static byte[] getImage(OP code) {
    int retID=code.resID;

    ClassFile cf=(ClassFile)cf_orig.clone();

    // set return type
    int otsize=cf.tsize;
    cf.tsize=retID_patchback;
    cf.write((byte)retID);
    cf.tsize=otsize;

    String retName;
    if ((retID<8))
      retName=OP.specialTypes[20+retID].getName();
    else if (retID==9)
      retName="java.lang.Void";
    else 
      retName=code.resType.getName();

    // set return class
    otsize=cf.tsize;
    cf.tsize=retIDC_patchback;
    cf.writeShort(cf.getIndex(retName,8));
    cf.tsize=otsize;
    
    // add the evaluate method
    cf.newMethod(eval_methods[retID],null);
    code.compile(cf);

    return cf.getImage();
  };
  
};











