/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * $Id: Evaluator.java,v 1.11 2000/11/12 14:59:34 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.
 */

package gnu.jel; 

import gnu.jel.generated.EC;
import gnu.jel.debug.Debug;
import gnu.jel.reflect.Field;
import gnu.jel.reflect.LocalField;
import gnu.jel.reflect.Method;
import gnu.jel.reflect.LocalMethod;
import gnu.jel.reflect.Member;

/**
 * This is the main front end to JEL.
 * <P>It is intended for compilation of algebraic expressions, involving 
 * functions.
 * <P> Syntax supports variables, which are, in fact, functions with no 
 * arguments. If You define such function in the static library -- it is a
 * constant (and will be substituted into expression at compile time).
 * If you define the method , such as "<TT>double x() {}</TT>;" 
 * in the dynamic
 * library class the expression "<TT>sin(x)</TT>" will call the method 
 * "<TT>x()</TT>" ( and 
 * function <TT>Math.sin()</TT> ) each time it is evaluated.
 * <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.
 * <P>The variant of the "compile" function with three arguments allows
 * to fix the type of the expression result. For example :
 * <PRE>
 * CompiledExpression expression=compile("2*6+6",lib,Double.TYPE);
 * </PRE>
 * will produce 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 unsupported return types.
 *
 * <P>(c) 1998, by Konstantin Metlov<BR>
 * Prague, CZ
 * @see gnu.jel.CompiledExpression
 */
public class Evaluator {

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

  static {
    try {
      // prepare eval methods
      Class[] paramsE=new Class[1];
      paramsE[0]=(new Object[0]).getClass();
      for(int i=0;i<9;i++) {
        String name="evaluate";
        Class cls=TypesStack.specialTypes[i];
        if (i!=8) 
          name=name+'_'+cls;
        else 
          cls=(new Object()).getClass();
        eval_methods[i]=new LocalMethod(0x0001,cls,name,paramsE,null);
      };
      
      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.typesStk.pushID(11); // not important what, it must be a reference
      Method supInit=new Method(cmplExpr.getConstructor(new Class[0]));
      cf.code(0xb7);                //| invokespecial
      cf.codeI(cf.getIndex(supInit,10));  //|    super();
      cf.typesStk.pop();
      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.textData.size();
      cf.code(8);                   //    type placeholder
      cf.typesStk.push(Integer.TYPE);
      
      cf.code(0xAC);                //| ireturn
      cf.typesStk.pop();
      
      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#evaluate
   */
  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 Instance of the ExpressionBits class, allowing to store and/or
   * instantiate compiled expression.
   * @exception gnu.jel.CompilationException if the expression is not
   *  syntactically or semantically correct.
   * @see gnu.jel.CompiledExpression#evaluate
   */
  public static byte[] compileBits(String expression, Library lib,
                                   Class resultType) 
    throws CompilationException {
    OPlist code=parse(expression,lib,resultType);
    code.performCF(); // fold constants
    //    System.out.println("CF performed.");
    //    System.out.println(code);
    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#evaluate
   */
  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 Instance of the ExpressionBits class, allowing to store and/or
   * instantiate compiled expression.
   * @exception gnu.jel.CompilationException if the expression is not
   *  syntactically or semantically correct.
   * @see gnu.jel.CompiledExpression#evaluate
   */
  public static byte[] compileBits(String expression, Library lib)
    throws CompilationException {
    return compileBits(expression, lib, null);
  };

  protected static OPlist parse(String expression, Library lib,
                                Class resultType) throws CompilationException {
    return 
      (new EC(new java.io.StringReader(expression))).parse(resultType,lib);
  };

  protected static byte[] getImage(OPlist list) {
    int retID=((OPunary)list.getLast()).resID;

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

    // set return type
    cf.textData.patchAddress(retID_patchback,(byte)retID);

    // add the evaluate method
    cf.newMethod(eval_methods[retID],null);
    list.compile(cf);

    return cf.getImage();
  };
  
};











