/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.CompPad.model;
import com.CompPad.model.operators.Operator;
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import groovy.lang.Closure;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.List;
/**
* This class encapsulates everything related to the groovy shell which will be
* common to all implementations of comppad - whether as a document, or
* a console application.
* @author trule
*/
public class GroovyEngine {
/* static variables for evaluating postfix operators */
public static final int IGNORE=100;
public static final int PREFIX=1;
public static final int INFIX=2;
public static final int POSTFIX=3;
public static final int LEFTPARENTHESIS = 4;
public static final int RIGHTPARENTHESIS = 5;
public static final int LISTSEPARATOR=6; /* eg a comma. */
/* Make these static so there's only one copy of these hashtables */
public static Hashtable<String,Integer> Types=new Hashtable(); /* */
public static Hashtable<String, Integer> Numargs=new Hashtable(); /* */
public static Hashtable<String,String> Convert=new Hashtable(); /* operator to convert to */
public static Hashtable<String,Integer> Precedence=new Hashtable(); /* Operator Precedence */
public static Hashtable<String,String> Replace=new Hashtable();
private Binding groovyBinding;
private GroovyShell groovyShell;
public GroovyEngine() throws java.lang.Exception {
/* Initialize parameters for postfix conversion and translation to JAVA
* string. Constructor must be non-static to initialize non-static fields */
Replace.put("Symbol_pi","ops.pi()");
Replace.put("e","ops.e()");
/* May want to separate out these initializations into a separate method?
* or at least move to "CompPad" class */
Types.put("(", LEFTPARENTHESIS);
Convert.put("(","(");
Precedence.put("(", -1000);
Numargs.put("(",0);
Types.put("[", LEFTPARENTHESIS);
Convert.put("[","(");
Precedence.put("[", -1000);
Numargs.put("[",0);
Types.put("{", LEFTPARENTHESIS);
Convert.put("{","(");
Precedence.put("{", -1000);
Numargs.put("{",0);
Types.put(")", RIGHTPARENTHESIS);
Convert.put(")",")");
Types.put("]", RIGHTPARENTHESIS);
Convert.put("]",")");
Types.put("}", RIGHTPARENTHESIS);
Convert.put("}",")");
Types.put("right",IGNORE); /* Used to indicate right parenthesis */
Types.put("left",IGNORE); /* Unsed to indicate left parentheses */
Types.put("COMPPAD_OPERATOR_OBJECT",POSTFIX);
Numargs.put("COMPPAD_OPERATOR_OBJECT",1);
Convert.put("COMPPAD_OPERATOR_OBJECT","COMPPAD_OPERATOR_OBJECT");
Precedence.put("COMPPAD_OPERATOR_OBJECT",4); // precedence higher than mult, lower than prefix ops.
/* Note that if the keys don't match on these four items,
* IT CAN DRIVE YOU CRAZY trying to find the problem.
* There must be a better way...*/
Types.put("elwise",POSTFIX);
Numargs.put("elwise",1);
Convert.put("elwise","ops.elwise");
Precedence.put("elwise ",2); // precedence higher than mult, lower than prefix ops.
Types.put("over",INFIX);
Numargs.put("over",2);
Convert.put("over","ops.div");
Precedence.put("over",1);
Types.put("/", INFIX);
Numargs.put("/",2);
Convert.put("/","ops.div");
Precedence.put("/",1);
Types.put("div",INFIX);
Numargs.put("div",2);
Convert.put("div","ops.div");
Precedence.put("div",1);
Types.put("times",INFIX);
Numargs.put("times",2);
Convert.put("times","ops.mult");
Precedence.put("times",1);
Types.put("*",INFIX);
Numargs.put("*",2);
Convert.put("*","ops.mult");
Precedence.put("*",1);
Types.put("cdot",INFIX);
Numargs.put("cdot",2);
Convert.put("cdot","ops.mult");
Precedence.put("cdot",1);
Types.put("^",INFIX);
Numargs.put("^",2);
Convert.put("^","ops.pow");
Precedence.put("^",4);
Types.put("nroot",PREFIX);
Numargs.put("nroot",2);
Convert.put("nroot","ops.nroot");
Precedence.put("nroot",3);
Types.put("+",INFIX);
Numargs.put("+",2);
Convert.put("+","ops.add");
Precedence.put("+",0);
Types.put("-",INFIX);
Numargs.put("-",2);
Convert.put("-","ops.sub");
Precedence.put("-",0);
Types.put("-",INFIX);
Numargs.put("-",2);
Convert.put("-","ops.sub");
Precedence.put("-",0);
Types.put("neg",PREFIX);
Numargs.put("neg",1);
Convert.put("neg","ops.not");
Precedence.put("not",3);
Types.put("negative",PREFIX);
Numargs.put("negative",1);
Convert.put("negative","ops.negative");
Precedence.put("negative",3);
Types.put("<",INFIX);
Numargs.put("<",2);
Convert.put("<", "ops.lt");
Precedence.put("<",-1);
Types.put(">",INFIX);
Numargs.put(">",2);
Convert.put(">", "ops.gt");
Precedence.put(">",-1);
// Types.put("<=",INFIX);
// Numargs.put("<=",2);
// Convert.put("<=", "ops.lte");
// Precedence.put("<=",-1);
Types.put("COMPPAD_LTE",INFIX);
Numargs.put("COMPPAD_LTE",2);
Convert.put("COMPPAD_LTE", "ops.lte");
Precedence.put("COMPPAD_LTE",-1);
// Types.put(">=",INFIX);
// Numargs.put(">=",2);
// Convert.put(">=", "ops.gte");
// Precedence.put(">=",-1);
Types.put("COMPPAD_GTE",INFIX);
Numargs.put("COMPPAD_GTE",2);
Convert.put("COMPPAD_GTE", "ops.gte");
Precedence.put("COMPPAD_GTE",-1);
// Types.put("<>",INFIX);
// Numargs.put("<>",2);
// Convert.put("<>", "ops.neq");
// Precedence.put("<>",-2);
Types.put("COMPPAD_NEQ",INFIX);
Numargs.put("COMPPAD_NEQ",2);
Convert.put("COMPPAD_NEQ", "ops.neq");
Precedence.put("COMPPAD_NEQ",-2);
Types.put("=",INFIX);
Numargs.put("=",2);
Convert.put("=", "ops.eq");
Precedence.put("=",-2);
Types.put("and",INFIX);
Numargs.put("and",2);
Convert.put("and","ops.and");
Precedence.put("and",-6);
Types.put("or",INFIX);
Numargs.put("or",2);
Convert.put("or","ops.or");
Precedence.put("or",-7);
Types.put(":=",INFIX);
Numargs.put(":=",2);
Convert.put(":=","ops.assign");
Precedence.put(":=",-1000);
Types.put("sqrt", PREFIX);
Numargs.put("sqrt",1);
Convert.put("sqrt","ops.sqrt");
Precedence.put("sqrt",3);
Types.put("sin",PREFIX);
Numargs.put("sin",1);
Convert.put("sin","ops.sin");
Precedence.put("sin",3);
Types.put("sin2",PREFIX);
Numargs.put("sin2",1);
Convert.put("sin2","ops.sin2");
Precedence.put("sin2",3);
Types.put("cos",PREFIX);
Numargs.put("cos",1);
Convert.put("cos","ops.cos");
Precedence.put("cos",2);
Types.put("cos2",PREFIX);
Numargs.put("cos2",1);
Convert.put("cos2","ops.cos2");
Precedence.put("cos2",2);
Types.put("tan",PREFIX);
Numargs.put("tan",1);
Convert.put("tan","ops.tan");
Precedence.put("tan",3);
Types.put("tan2",PREFIX);
Numargs.put("tan2",1);
Convert.put("tan2","ops.tan2");
Precedence.put("tan2",3);
Types.put("sec",PREFIX);
Numargs.put("sec",1);
Convert.put("sec","ops.sec");
Precedence.put("sec",3);
Types.put("sec2",PREFIX);
Numargs.put("sec2",1);
Convert.put("sec2","ops.sec2");
Precedence.put("sec2",3);
Types.put("csc",PREFIX);
Numargs.put("csc",1);
Convert.put("csc","ops.csc");
Precedence.put("csc",3);
Types.put("csc2",PREFIX);
Numargs.put("csc2",1);
Convert.put("csc2","ops.csc2");
Precedence.put("csc2",3);
Types.put("cot",PREFIX);
Numargs.put("cot",1);
Convert.put("cot","ops.cot");
Precedence.put("cot",3);
Types.put("cot2",PREFIX);
Numargs.put("cot2",1);
Convert.put("cot2","ops.cot2");
Precedence.put("cot2",3);
Types.put("asin",PREFIX);
Numargs.put("asin",1);
Convert.put("asin","ops.asin");
Precedence.put("asin",3);
Types.put("acos",PREFIX);
Numargs.put("acos",1);
Convert.put("acos","ops.acos");
Precedence.put("acos",3);
Types.put("atan",PREFIX);
Numargs.put("atan",1);
Convert.put("atan","ops.atan");
Precedence.put("atan",3);
Types.put("asec",PREFIX);
Numargs.put("asec",1);
Convert.put("asec","ops.asec");
Precedence.put("asec",3);
Types.put("acsc",PREFIX);
Numargs.put("acsc",1);
Convert.put("acsc","ops.acsc");
Precedence.put("acsc",3);
Types.put("acot",PREFIX);
Numargs.put("acot",1);
Convert.put("acot","ops.acot");
Precedence.put("acot",3);
/* ISO math notation */
Types.put("ln",PREFIX);
Numargs.put("ln",1);
Convert.put("ln", "ops.log");
Precedence.put("ln",3);
Types.put("log_10",PREFIX);
Numargs.put("log_10",1);
Convert.put("log_10","ops.log10");
Precedence.put("log_10",3);
Types.put("round",PREFIX);
Numargs.put("round",2);
Convert.put("round","ops.round");
Precedence.put("round",3);
Types.put("ceil",PREFIX);
Numargs.put("ceil",2);
Convert.put("ceil","ops.ceil");
Precedence.put("ceil",3);
Types.put("floor",PREFIX);
Numargs.put("floor",2);
Convert.put("floor","ops.floor");
Precedence.put("floor",3);
Types.put("rand",PREFIX);
Numargs.put("rand",2);
Convert.put("rand","ops.rand");
Precedence.put("rand",3);
Types.put("zeros",PREFIX);
Numargs.put("zeros",2);
Convert.put("zeros","ops.zeros");
Precedence.put("zeros",3);
Types.put("ones",PREFIX);
Numargs.put("ones",2);
Convert.put("ones","ops.ones");
Precedence.put("ones",3);
Types.put("Re",PREFIX);
Numargs.put("Re",1);
Convert.put("Re", "ops.real");
Precedence.put("Re",3);
Types.put("Im",PREFIX);
Numargs.put("Im",1);
Convert.put("Im", "ops.imaginary");
Precedence.put("Im",3);
Types.put("sgn",PREFIX);
Numargs.put("sgn",1);
Convert.put("sgn", "ops.signum");
Precedence.put("sgn",3);
Types.put("arg",PREFIX);
Numargs.put("arg",1);
Convert.put("arg", "ops.argument");
Precedence.put("arg",3);
Types.put("COMPPAD_CONJUGATE",POSTFIX);
Numargs.put("COMPPAD_CONJUGATE",1);
Convert.put("COMPPAD_CONJUGATE", "ops.conjugate");
Precedence.put("COMPPAD_CONJUGATE",3);
Types.put("COMPPAD_TRANSPOSE", POSTFIX);
Numargs.put("COMPPAD_TRANSPOSE", 1);
Convert.put("COMPPAD_TRANSPOSE","ops.transpose");
Precedence.put("COMPPAD_TRANSPOSE",3);
Types.put("max",PREFIX);
Numargs.put("max",1);
Convert.put("max","ops.max");
Precedence.put("max",3);
Types.put("min",PREFIX);
Numargs.put("min",1);
Convert.put("min","ops.min");
Precedence.put("min",3);
Types.put("sum",PREFIX);
Numargs.put("sum",1);
Convert.put("sum","ops.sum");
Precedence.put("sum",3);
Types.put("prod",PREFIX);
Numargs.put("prod",1);
Convert.put("prod","ops.prod");
Precedence.put("prod",3);
Types.put("rows",PREFIX);
Numargs.put("rows",1);
Convert.put("rows","ops.rows");
Precedence.put("rows",3);
Types.put("columns",PREFIX);
Numargs.put("columns",1);
Convert.put("columns","ops.columns");
Precedence.put("columns",3);
Types.put("size",PREFIX);
Numargs.put("size",1);
Convert.put("size","ops.size");
Precedence.put("size",3);
Types.put("dotslow",INFIX);
Numargs.put("dotslow",2);
Convert.put("dotslow","ops.range");
Precedence.put("dotslow",0);
Types.put("CompPad_Literal",POSTFIX);
Numargs.put("CompPad_Literal",1);
Convert.put("CompPad_Literal","ops.literal"); /* Ignore for now */
Precedence.put("CompPad_Literal",4);
Types.put("COMPPAD_ABS",PREFIX);
Numargs.put("COMPPAD_ABS",1);
Convert.put("COMPPAD_ABS","ops.abs");
Precedence.put("COMPPAD_ABS",3);
Types.put("SIGFIGS",PREFIX);
Numargs.put("SIGFIGS",1);
Convert.put("SIGFIGS","ops.setSigFigs");
Precedence.put("SIGFIGS",3);
Types.put("setDecimalSeparator",PREFIX);
Numargs.put("setDecimalSeparator",1);
Convert.put("setDecimalSeparator","ops.setDecimalSeparator");
Precedence.put("setDecimalSeparator",1);
// Types.put("unitValueOf",PREFIX);
// Numargs.put("unitValueOf",2);
// Convert.put("unitValueOf","ops.unitValueOf");
// Precedence.put("unitValueOf",2);
//
// Types.put("unitSI",PREFIX);
// Numargs.put("unitSI", 1);
// Convert.put("unitSI", "ops.unitSI");
// Precedence.put("unitSI",2);
//
// Types.put("unitNonSI",PREFIX);
// Numargs.put("unitNonSI", 1);
// Convert.put("unitNonSI", "ops.unitNonSI");
// Precedence.put("unitNonSI",2);
Types.put("units",PREFIX);
Numargs.put("units",1);
Convert.put("units","ops.units");
Precedence.put("units",3);
Types.put(",",INFIX);
Numargs.put(",", 2);
Convert.put(",", "ops.addColumn");
Precedence.put(",",-10);
Types.put("##",INFIX);
Numargs.put("##",2);
Convert.put("##", "ops.addRow");
Precedence.put("##", -11);
/* AddColumn needs to bind tighter than addRow, when used with the
* "matrix" keyword */
Types.put("#",INFIX);
Numargs.put("#",2);
Convert.put("#","ops.addColumn");
Precedence.put("#", -10);
Types.put("matrix",PREFIX);
Numargs.put("matrix",1);
Convert.put("matrix","");
Precedence.put("matrix", 3);
/* Subscript array indices */
Types.put("sub",INFIX);
Numargs.put("sub",2);
Convert.put("sub","ops.subscript");
Precedence.put("sub",4);
Types.put("plotxy",PREFIX);
Numargs.put("plotxy",1);
Convert.put("plotxy","ops.plotxy");
Precedence.put("plotxy",3);
groovyBinding = new groovy.lang.Binding();
groovyShell=new GroovyShell(groovyBinding);
groovyShell.evaluate(
"import com.CompPad.model.Operators;ops=Operators;");
List<Field> unitFields = new ArrayList();
javax.measure.unit.SI unitSI = javax.measure.unit.SI.getInstance();
javax.measure.unit.NonSI unitNonSI = javax.measure.unit.NonSI.getInstance();
unitFields = Arrays.asList(javax.measure.unit.SI.class.getFields());
for (Field field:unitFields){
groovyShell.setVariable(field.getName().replace("_", "x"), Operators.units(field.getName()));
}
unitFields=Arrays.asList(javax.measure.unit.NonSI.class.getFields());
for (Field field:unitFields){
groovyShell.setVariable(field.getName().replace("_", "x"), Operators.units(field.getName()));
}
}
//
public Object getVariable(String name){
try{
return groovyBinding.getVariable(name);
}
catch(Exception e){
return null;
}
}
public void setVariable(String name, Object object){
groovyBinding.setVariable(name, object);
}
private java.util.Set getVariableNames(){
return groovyBinding.getVariables().keySet();
}
public boolean hasVariable(String name, Class type){
try{
return groovyBinding.getVariable(name).getClass().getSuperclass()
== type;
}
catch (Exception e){
return false;
}
}
public void evaluate(String s){
groovyShell.evaluate(s);
}
public boolean hasOperator(String name){
// First check if it's in the Types map.
// All these operators will eventually be transitioned to "Operator" classes.
if (name==null){
return false;
}
else if (Types.containsKey(name)){
return true;
}
else if (this.hasVariable(name, Operator.class)){
return true;
}
else if (this.hasVariable(name,Closure.class)){
return true;
}
else{
return false;
}
}
// public boolean hasOperator(String name, Class type){
//
// }
public Integer getType(String name){
if (Types.containsKey(name)){
return Types.get(name);
}
else if (this.hasVariable(name,Closure.class)){
// closures will be treated as prefix (for now)
// although it would be cool to define new infix operators ...
return PREFIX;
}
else{
return null;
}
}
public Integer getNumArgs(String name){
if (Numargs.containsKey(name)){
return Numargs.get(name);
}
else if (this.hasVariable(name,Closure.class)){
return ( (Closure) this.getVariable(name) ).getMaximumNumberOfParameters();
}
else{
return null;
}
}
public Integer getPrecedence(String name){
if (Precedence.containsKey(name)){
return Precedence.get(name);
}
else if (this.hasVariable(name,Closure.class)){
// precedence of prefix operators is null;
return 3;
}
else{
return null;
}
}
public String getReplacement(String name){
if (Replace.containsKey(name)){
return Replace.get(name);
}
else{
return null;
}
}
public String getConvert(String name){
if (Convert.containsKey(name)){
return Convert.get(name);
}
else if (this.hasVariable(name,Closure.class)){
// precedence of prefix operators is null;
return name;
}
else{
return null;
}
}
}