gnu.jel.Evaluator Maven / Gradle / Ivy
/* -*- 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 -- 2007 by Konstantin Metlov([email protected]);
*
* 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.
* It is intended for compilation of algebraic expressions involving
* functions.
*
Syntax allows variables, which can be either a public fields of
* certain objects or functions with no arguments. If a method like
* "double x() {};" is defined in the dynamic library class
* (see gnu.jel.Library documentation on how to do this), an
* expression "sin(x)" will call the method "x()" (
* and function Math.sin() ) 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 Math.random()) and will be called at compile time if
* their arguments are known.
*
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 java.lang.Integer,... and
* primitives). Widening type conversions (not leading to precision loss)
* are applied by JEL automatically, narrowing should be explicitly
* requested.
*
There is variant of the "compile" function with three arguments,
* which allows to fix the type of the expression result. For example:
*
* CompiledExpression expression=compile("2*6+6",lib,Double.TYPE);
*
* will produce a compiled expression, whose return type is always
* double. For additional information on how to use this
* feature to eliminate object allocation overhead see
* gnu.jel.CompiledExpression documentation.
*
* Care should be taken during the assembly of static and dynamic libraries
* to avoid conflicts and unexpected return types.
*
*
(c) 1998-2003, by Konstantin Metlov
* 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,"",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.
* This variant of compile allows to store expressions in a
* java.io.OutputStream 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 gnu.jel.ImageLoader.
* @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.
*
This variant of compile allows to store expressions in a
* java.io.OutputStream 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 gnu.jel.ImageLoader.
* @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();
};
};