|
Interpreter |
|
package ij.macro;
import ij.*;
import ij.process.*;
import ij.gui.*;
import ij.plugin.Macro_Runner;
import ij.util.Tools;
import java.awt.*;
import java.util.*;
import java.awt.event.KeyEvent;
import java.io.PrintWriter;
/** This is the recursive descent parser/interpreter for the ImageJ macro language. */
public class Interpreter implements MacroConstants {
static final int STACK_SIZE=1000;
static final int MAX_ARGS=20;
int pc;
int token;
int tokenAddress;
double tokenValue;
String tokenString;
boolean looseSyntax = true;
int lineNumber;
boolean ignoreEOL = true;
boolean statusUpdated;
boolean showingProgress;
boolean keysSet;
boolean checkingType;
int prefixValue;
Variable[] stack;
int topOfStack = -1;
int topOfGlobals = -1;
int startOfLocals = 0;
static Interpreter instance, previousInstance;
static boolean batchMode;
static Vector imageTable; // images opened in batch mode
boolean done;
Program pgm;
Functions func;
boolean inFunction;
String macroName;
String argument;
String returnValue;
boolean calledMacro; // macros envoked by eval() or runMacro()
double[] rgbWeights;
boolean inPrint;
static String additionalFunctions;
/** Interprets the specified string. */
public void run(String macro) {
if (additionalFunctions!=null) {
if (!(macro.endsWith("\n")|| additionalFunctions.startsWith("\n")))
macro = macro + "\n" + additionalFunctions;
else
macro = macro + additionalFunctions;
}
Tokenizer tok = new Tokenizer();
Program pgm = tok.tokenize(macro);
if (pgm.hasVars)
saveGlobals2(pgm);
run(pgm);
}
/** Runs the specified macro, passing it a string
argument and returning a string value. */
public String run(String macro, String arg) {
argument = arg;
calledMacro = true;
if (IJ.getInstance()==null)
setBatchMode(true);
run(macro);
return returnValue;
}
/** Interprets the specified tokenized macro file starting at location 0. */
public void run(Program pgm) {
this.pgm = pgm;
pc = -1;
instance = this;
if (!calledMacro) {
batchMode = false;
imageTable = null;
}
pushGlobals();
if (func==null)
func = new Functions(this, pgm);
func.plot = null;
//IJ.showStatus("interpreting");
while (pgm.code[pc+1]==EOL) pc++; // skip comments
if ((pgm.code[pc+1]&0xff)==MACRO && (pgm.code[pc+2]&0xff)==STRING_CONSTANT) {
// run macro instead of skipping over it
getToken();
getToken();
}
doStatements();
finishUp();
}
/** Interprets the specified tokenized macro starting at the specified location. */
public void runMacro(Program pgm, int macroLoc, String macroName) {
this.pgm = pgm;
this.macroName = macroName;
pc = macroLoc-1;
previousInstance = instance;
instance = this;
batchMode = false;
imageTable = null;
//IJ.showStatus("interpreting");
pushGlobals();
if (func==null)
func = new Functions(this, pgm);
func.plot = null;
if (macroLoc==0)
doStatements();
else
doBlock();
finishUp();
}
/** Saves global variablesk. */
public void saveGlobals(Program pgm) {
saveGlobals2(pgm);
}
void saveGlobals2(Program pgm) {
this.pgm = pgm;
pc = -1;
instance = this;
func = new Functions(this, pgm);
while (!done) {
getToken();
switch (token) {
case VAR: doVar(); break;
case MACRO: skipMacro(); break;
case FUNCTION: skipFunction(); break;
default:
}
}
instance = null;
pgm.saveGlobals(this);
pc = -1;
topOfStack = -1;
done = false;
}
final void getToken() {
if (done)
return;
token = pgm.code[++pc];
//IJ.log(pc+" "+pgm.decodeToken(token));
if (token<=127)
return;
while (token==EOL && ignoreEOL)
token = pgm.code[++pc];
tokenAddress = token>>TOK_SHIFT;
token = token&TOK_MASK;
Symbol sym = pgm.table[tokenAddress];
tokenString = sym.str;
tokenValue = sym.value;
done = token==EOF;
}
final int nextToken() {
return pgm.code[pc+1]&TOK_MASK;
}
final int nextNonEolToken() {
int tok, i=1;
do {
tok = pgm.code[pc+i];
i++;
} while (tok==EOL);
return tok&TOK_MASK;
}
final int nextNextNonEolToken() {
int tok, i=1;
do tok = pgm.code[pc+(i++)]; while (tok==EOL);
do tok = pgm.code[pc+(i++)]; while (tok==EOL);
return tok&TOK_MASK;
}
final void putTokenBack() {
pc--;
if (pc<0)
pc = -1;
}
void doStatements() {
while (!done)
doStatement();
}
final void doStatement() {
getToken();
switch (token) {
case VAR:
doVar();
break;
case PREDEFINED_FUNCTION:
func.doFunction(pgm.table[tokenAddress].type);
break;
case USER_FUNCTION:
runUserFunction();
break;
case RETURN:
doReturn();
break;
case WORD:
doAssignment();
break;
case IF:
doIf();
return;
case ELSE:
error("Else without if");
return;
case FOR:
doFor();
return;
case WHILE:
doWhile();
return;
case DO:
doDo();
return;
case MACRO:
skipMacro();
return;
case FUNCTION:
skipFunction();
return;
case ';':
return;
case '{':
putTokenBack();
doBlock();
return;
case NUMBER:
case NUMERIC_FUNCTION:
case STRING_FUNCTION:
case STRING_CONSTANT:
case '(':
putTokenBack();
inPrint = true;
String s = getString();
inPrint = false;
if (s!=null && !s.equals("NaN")) IJ.log(s);
return;
case EOF: break;
default:
error("Statement cannot begin with '"+pgm.decodeToken(token, tokenAddress)+"'");
}
if (!looseSyntax) {
getToken();
if (token!=';' && !done)
error("';' expected");
}
}
Variable runUserFunction() {
int newPC = (int)tokenValue;
int saveStartOfLocals = startOfLocals;
startOfLocals = topOfStack+1;
int saveTOS = topOfStack;
int nArgs = pushArgs();
int savePC = pc;
Variable value = null;
pc = newPC;
setupArgs(nArgs);
boolean saveInFunction = inFunction;
inFunction = true;
try {
doBlock();
} catch (ReturnException e) {
value = new Variable(0, e.value, e.str, e.array);
}
inFunction = saveInFunction;
pc = savePC;
trimStack(saveTOS, saveStartOfLocals);
return value;
}
/** Push function arguments onto the stack. */
int pushArgs() {
getLeftParen();
int count = 0;
Variable[] args = new Variable[MAX_ARGS];
double value;
if (nextToken()!=')') {
do {
if (count==MAX_ARGS)
error("Too many arguments");
int next = nextToken();
int nextPlus = pgm.code[pc+2]&0xff;
if (next==STRING_CONSTANT || next==STRING_FUNCTION)
args[count] = new Variable(0, 0.0, getString());
else if (next==USER_FUNCTION) {
int savePC = pc;
getToken(); // the function
boolean simpleFunctionCall = isSimpleFunctionCall(false);
pc = savePC;
if (simpleFunctionCall) {
getToken(); // the function
Variable v2 = runUserFunction();
if (v2==null)
error("No return value");
args[count] = v2;
} else
args[count] = new Variable(0, getExpression(), null);
} else if (next==WORD && (nextPlus==',' || nextPlus==')')) {
value = 0.0;
Variable[] array = null;
String str = null;
getToken();
Variable v = lookupVariable();
if (v!=null) {
int type = v.getType();
if (type==Variable.VALUE)
value = v.getValue();
else if (type==Variable.ARRAY)
array = v.getArray();
else
str = v.getString();
}
args[count] = new Variable(0, value, str, array);
} else if (next==WORD && nextPlus=='[' ) {
getToken();
Variable v = lookupVariable();
v = getArrayElement(v);
if (v.getString()!=null)
args[count] = new Variable(0, 0.0, v.getString(), null);
else
args[count] = new Variable(0, v.getValue(), null);
} else
args[count] = new Variable(0, getExpression(), null);
count++;
getToken();
} while (token==',');
putTokenBack();
}
int nArgs = count;
while(count>0)
push(args[--count], this);
getRightParen();
return nArgs;
}
void setupArgs(int nArgs) {
getLeftParen();
int i = topOfStack;
int count = nArgs;
if (nextToken()!=')') {
do {
getToken();
if (i>=0)
stack[i].symTabIndex = tokenAddress;
i--;
count--;
getToken();
} while (token==',');
putTokenBack();
}
if (count!=0)
error(nArgs+" argument"+(nArgs==1?"":"s")+" expected");
getRightParen();
}
// cache exception object for better performance
ReturnException returnException;
// Handle return statement
void doReturn() {
double value = 0.0;
String str = null;
Variable[] array = null;
getToken();
if (token!=';') {
boolean isString = token==STRING_CONSTANT || token==STRING_FUNCTION;
boolean isArrayFunction = token==ARRAY_FUNCTION;
if (token==WORD) {
Variable v = lookupLocalVariable(tokenAddress);
if (v!=null && nextToken()==';') {
array = v.getArray();
isString = v.getString()!=null;
//IJ.log("token==WORD: "+isString+" "+pgm.decodeToken(token, tokenAddress));
}
}
putTokenBack();
if (isString)
str = getString();
else if (isArrayFunction) {
getToken();
array = func.getArrayFunction(pgm.table[tokenAddress].type);
} else if (array==null)
value = getExpression();
}
if (inFunction) {
if (returnException==null)
returnException = new ReturnException();
returnException.value = value;
returnException.str = str;
returnException.array = array;
//throw new ReturnException(value, str, array);
throw returnException;
} else {
finishUp();
if (value!=0.0 || array!=null)
error("Macros can only return strings");
returnValue = str;
done = true;
}
}
void doFor() {
boolean saveLooseSyntax = looseSyntax;
looseSyntax = false;
getToken();
if (token!='(')
error("'(' expected");
getToken(); // skip 'var'
if (token!=VAR)
putTokenBack();
do {
if (nextToken()!=';')
getAssignmentExpression();
getToken();
} while (token==',');
//IJ.log("token: "+pgm.decodeToken(token,tokenAddress));
if (token!=';')
error("';' expected");
int condPC = pc;
int incPC2, startPC=0;
double cond = 1;
while (true) {
if (pgm.code[pc+1]!=';')
cond = getLogicalExpression();
if (startPC==0)
checkBoolean(cond);
getToken();
if (token!=';')
error("';' expected");
int incPC = pc;
// skip to start of code
if (startPC!=0)
pc = startPC;
else {
while (token!=')') {
getToken();
//IJ.log(pgm.decodeToken(token,tokenAddress));
if (token=='{' || token==';' || token=='(' || done)
error("')' expected");
}
}
startPC = pc;
if (cond==1)
doStatement();
else {
skipStatement();
break;
}
pc = incPC; // do increment
do {
if (nextToken()!=')')
getAssignmentExpression();
getToken();
} while (token==',');
pc = condPC;
}
looseSyntax = saveLooseSyntax;
}
void doWhile() {
looseSyntax = false;
int savePC = pc;
boolean isTrue;
do {
pc = savePC;
isTrue = getBoolean();
if (isTrue)
doStatement();
else
skipStatement();
} while (isTrue && !done);
}
void doDo() {
looseSyntax = false;
int savePC = pc;
boolean isTrue;
do {
doStatement();
getToken();
if (token!=WHILE)
error("'while' expected");
isTrue = getBoolean();
if (isTrue)
pc = savePC;
} while (isTrue && !done);
}
final void doBlock() {
getToken();
if (token!='{')
error("'{' expected");
while (!done) {
getToken();
if(token=='}')
break;
putTokenBack();
doStatement();
}
if (token!='}')
error("'}' expected");
}
final void skipStatement() {
getToken();
//IJ.write("skipStatement: " +pgm.decodeToken(token, tokenAddress));
switch (token) {
case PREDEFINED_FUNCTION: case USER_FUNCTION: case VAR:
case WORD: case '(': case PLUS_PLUS: case RETURN:
case NUMERIC_FUNCTION: case STRING_FUNCTION:
skipSimpleStatement();
break;
case IF:
skipParens();
skipStatement();
getToken();
if (token==ELSE)
skipStatement();
else
putTokenBack();
break;
case FOR:
skipParens();
skipStatement();
break;
case WHILE:
skipParens();
skipStatement();
break;
case DO:
skipStatement();
getToken(); // skip 'while'
skipParens();
break;
case ';':
break;
case '{':
putTokenBack();
skipBlock();
break;
default:
error("Skipped statement cannot begin with '"+pgm.decodeToken(token, tokenAddress)+"'");
}
}
final void skipBlock() {
int count = 0;
do {
getToken();
if (token=='{')
count++;
else if (token=='}')
count--;
else if (done) {
error("'}' expected");
return;
}
} while (count>0);
}
final void skipParens() {
int count = 0;
do {
getToken();
if (token=='(')
count++;
else if (token==')')
count--;
else if (done) {
error("')' expected");
return;
}
} while (count>0);
}
final void skipSimpleStatement() {
boolean finished = done;
getToken();
while (!finished && !done) {
if (token==';')
finished = true;
else
getToken();
}
}
/** Skips a user-defined function. */
void skipFunction() {
getToken(); // skip function id
skipParens();
skipBlock();
}
void skipMacro() {
getToken(); // skip macro label
skipBlock();
}
final void doAssignment() {
int next = pgm.code[pc+1]&0xff;
if (next=='[') {
doArrayElementAssignment();
return;
}
int type = getExpressionType();
switch (type) {
case Variable.STRING: doStringAssignment(); break;
case Variable.ARRAY: doArrayAssignment(); break;
case USER_FUNCTION: doUserFunctionAssignment(); break;
case STRING_FUNCTION: doNumericStringAssignment(); break;
default:
putTokenBack();
getAssignmentExpression();
}
}
int getExpressionType() {
int rightSideToken = pgm.code[pc+2];
int tok = rightSideToken&0xff;
if (tok==STRING_CONSTANT)
return Variable.STRING;
if (tok==STRING_FUNCTION) {
int address = rightSideToken>>TOK_SHIFT;
if (pgm.table[address].type==DIALOG) {
int token2 = pgm.code[pc+4];
String name = pgm.table[token2>>TOK_SHIFT].str;
if (name.equals("getNumber") || name.equals("getCheckbox"))
return STRING_FUNCTION;
}
return Variable.STRING;
}
if (tok==ARRAY_FUNCTION)
return Variable.ARRAY;
if (tok==USER_FUNCTION)
return USER_FUNCTION;
if (tok!=WORD)
return Variable.VALUE;
Variable v = lookupVariable(rightSideToken>>TOK_SHIFT);
if (v==null)
return Variable.VALUE;
int type = v.getType();
if (type!=Variable.ARRAY)
return type;
if (pgm.code[pc+3]=='.')
return Variable.VALUE;
if (pgm.code[pc+3]!='[')
return Variable.ARRAY;
int savePC = pc;
getToken(); //"="
getToken(); //the variable
checkingType = true;
int index = getIndex();
checkingType = false;
pc = savePC-1;
getToken();
Variable[] array = v.getArray();
if (index<0 || index>=array.length)
return Variable.VALUE;
return array[index].getType();
}
/** Handles string functions such as Dialog.getNumber() that return a number. */
final void doNumericStringAssignment() {
putTokenBack();
getToken();
Variable v = lookupLocalVariable(tokenAddress);
if (v==null) v = push(tokenAddress, 0.0, null, this);
getToken();
if (token!='=') error("'=' expected");
v.setValue(getExpression());
}
final void doArrayElementAssignment() {
Variable v = lookupLocalVariable(tokenAddress);
if (v==null)
error("Undefined identifier");
if (pgm.code[pc+5]==';'&&(pgm.code[pc+4]==PLUS_PLUS||pgm.code[pc+4]==MINUS_MINUS))
{putTokenBack(); getFactor(); return;}
int index = getIndex();
int expressionType = getExpressionType();
if (expressionType==Variable.ARRAY)
error("Arrays of arrays not supported");
getToken();
int op = token;
if (!(op=='='||op==PLUS_EQUAL||op==MINUS_EQUAL||op==MUL_EQUAL||op==DIV_EQUAL))
{error("'=', '+=', '-=', '*=' or '/=' expected"); return;}
if (op!='=' && (expressionType==Variable.STRING||expressionType==Variable.ARRAY))
{error("'=' expected"); return;}
Variable[] array = v.getArray();
if (array==null)
error("Array expected");
if (index<0 || index>=array.length)
error("Index ("+index+") out of 0-"+(array.length-1)+" range");
int next = nextToken();
switch (expressionType) {
case Variable.STRING:
array[index].setString(getString());
break;
case Variable.ARRAY:
getToken();
if (token==ARRAY_FUNCTION)
array[index].setArray(func.getArrayFunction(pgm.table[tokenAddress].type));
break;
default:
switch (op) {
case '=': array[index].setValue(getExpression()); break;
case PLUS_EQUAL: array[index].setValue(array[index].getValue()+getExpression()); break;
case MINUS_EQUAL: array[index].setValue(array[index].getValue()-getExpression()); break;
case MUL_EQUAL: array[index].setValue(array[index].getValue()*getExpression()); break;
case DIV_EQUAL: array[index].setValue(array[index].getValue()/getExpression()); break;
}
break;
}
}
final void doUserFunctionAssignment() {
//IJ.log("doUserFunctionAssignment0: "+pgm.decodeToken(token, tokenAddress));
putTokenBack();
int savePC = pc;
getToken(); // the variable
getToken(); // '='
getToken(); // the function
boolean simpleAssignment = isSimpleFunctionCall(true);
pc = savePC;
if (!simpleAssignment)
getAssignmentExpression();
else {
getToken();
Variable v1 = lookupLocalVariable(tokenAddress);
if (v1==null)
v1 = push(tokenAddress, 0.0, null, this);
getToken();
if (token!='=')
error("'=' expected");
getToken(); // the function
Variable v2 = runUserFunction();
if (v2==null)
error("No return value");
int type = v2.getType();
if (type==Variable.VALUE)
v1.setValue(v2.getValue());
else if (type==Variable.ARRAY)
v1.setArray(v2.getArray());
else
v1.setString(v2.getString());
}
}
boolean isSimpleFunctionCall(boolean assignment) {
int count = 0;
do {
getToken();
//IJ.log(pgm.decodeToken(token, tokenAddress));
if (token=='(')
count++;
else if (token==')')
count--;
else if (done)
error("')' expected");
} while (count>0);
getToken();
if (assignment)
return token==';';
else
return token==','||token==')';
}
final void doStringAssignment() {
Variable v = lookupLocalVariable(tokenAddress);
if (v==null) {
if (nextToken()=='=')
v = push(tokenAddress, 0.0, null, this);
else
error("Undefined identifier");
}
getToken();
if (token!='=') {
error("'=' expected");
return;
}
v.setString(getString());
}
final void doArrayAssignment() {
Variable v = lookupLocalVariable(tokenAddress);
if (v==null) {
if (nextToken()=='=')
v = push(tokenAddress, 0.0, null, this);
else
error("Undefined identifier");
}
getToken();
if (token!='=') {
error("'=' expected");
return;
}
getToken();
if (token==ARRAY_FUNCTION)
v.setArray(func.getArrayFunction(pgm.table[tokenAddress].type));
else if (token==WORD) {
Variable v2 = lookupVariable();
v.setArray(v2.getArray());
} else
error("Array expected");
}
final void doIf() {
looseSyntax = false;
boolean b = getBoolean();
if (b)
doStatement();
else
skipStatement();
int next = nextNonEolToken();
if (next==';') {
getToken();
next = nextNonEolToken();
}
if (next==ELSE) {
getToken();
if (b)
skipStatement();
else
doStatement();
}
}
final boolean getBoolean() {
getLeftParen();
double value = getLogicalExpression();
checkBoolean(value);
getRightParen();
return value==0.0?false:true;
}
final double getLogicalExpression() {
double v1 = getBooleanExpression();
int next = nextNonEolToken();
if (!(next==LOGICAL_AND || next==LOGICAL_OR))
return v1;
checkBoolean(v1);
getToken();
int op = token;
double v2 = getLogicalExpression();
checkBoolean(v2);
if (op==LOGICAL_AND)
return (int)v1 & (int)v2;
else if (op==LOGICAL_OR)
return (int)v1 | (int)v2;
return v1;
}
final double getBooleanExpression() {
double v1 = 0.0;
String s1 = null;
int next = pgm.code[pc+1];
int tok = next&TOK_MASK;
if (tok==STRING_CONSTANT || tok==STRING_FUNCTION || isString(next))
s1 = getString();
else
v1 = getExpression();
next = nextToken();
if (next>=EQ && next<=LTE) {
getToken();
int op = token;
if (s1!=null)
return compareStrings(s1, getString(), op);
double v2 = getExpression();
switch (op) {
case EQ:
v1 = v1==v2?1.0:0.0;
break;
case NEQ:
v1 = v1!=v2?1.0:0.0;
break;
case GT:
v1 = v1>v2?1.0:0.0;
break;
case GTE:
v1 = v1>=v2?1.0:0.0;
break;
case LT:
v1 = v1<v2?1.0:0.0;
break;
case LTE:
v1 = v1<=v2?1.0:0.0;
break;
}
} else if (s1!=null)
v1 = Tools.parseDouble(s1, 0.0);
return v1;
}
// returns true if the specified token is a string variable
boolean isString(int token) {
if ((token&TOK_MASK)!=WORD) return false;
Variable v = lookupVariable(token>>TOK_SHIFT);
if (v==null) return false;
if (pgm.code[pc+2]=='[') {
Variable[] array = v.getArray();
if (array!=null && array.length>0)
return array[0].getType()==Variable.STRING;
}
return v.getType()==Variable.STRING;
}
double compareStrings(String s1, String s2, int op) {
int result;
if (IJ.isJava2())
result = s1.compareToIgnoreCase(s2);
else
result = s1.toLowerCase().compareTo(s2.toLowerCase());
double v1 = 0.0;
switch (op) {
case EQ:
v1 = result==0?1.0:0.0;
break;
case NEQ:
v1 = result!=0?1.0:0.0;
break;
case GT:
v1 = result>0?1.0:0.0;
break;
case GTE:
v1 = result>=0?1.0:0.0;
break;
case LT:
v1 = result<0?1.0:0.0;
break;
case LTE:
v1 = result<=0?1.0:0.0;
break;
}
return v1;
}
final double getAssignmentExpression() {
int tokPlus2 = pgm.code[pc+2];
if ((pgm.code[pc+1]&0xff)==WORD && (tokPlus2=='='||tokPlus2==PLUS_EQUAL
||tokPlus2==MINUS_EQUAL||tokPlus2==MUL_EQUAL||tokPlus2==DIV_EQUAL)) {
getToken();
Variable v = lookupLocalVariable(tokenAddress);
if (v==null)
v = push(tokenAddress, 0.0, null, this);
getToken();
double value = 0.0;
if (token=='=')
value = getAssignmentExpression();
else {
value = v.getValue();
switch (token) {
case PLUS_EQUAL: value += getAssignmentExpression(); break;
case MINUS_EQUAL: value -= getAssignmentExpression(); break;
case MUL_EQUAL: value *= getAssignmentExpression(); break;
case DIV_EQUAL: value /= getAssignmentExpression(); break;
}
}
v.setValue(value);
return value;
} else
return getLogicalExpression();
}
final void checkBoolean(double value) {
if (!(value==0.0 || value==1.0))
error("Boolean expression expected");
}
void doVar() {
getToken();
while (token==WORD) {
if (nextToken()=='=')
doAssignment();
else {
Variable v = lookupVariable(tokenAddress);
if (v==null)
push(tokenAddress, 0.0, null, this);
}
getToken();
if (token==',')
getToken();
else {
putTokenBack();
break;
}
}
}
final void getLeftParen() {
getToken();
if (token!='(')
error("'(' expected");
}
final void getRightParen() {
getToken();
if (token!=')')
error("')' expected");
}
final void getParens() {
if (nextToken()=='(') {
getLeftParen();
getRightParen();
}
}
final void getComma() {
getToken();
if (token!=',') {
if (looseSyntax)
putTokenBack();
else
error("',' expected");
}
}
void error (String message) {
boolean showMessage = !done;
token = EOF;
tokenString = "";
IJ.showStatus("");
IJ.showProgress(0, 0);
batchMode = false;
imageTable = null;
WindowManager.setTempCurrentImage(null);
if (showMessage) {
String line = getErrorLine();
if (line.length()>120)
line = line.substring(0,119)+"...";
IJ.showMessage("Macro Error", message+" in line "+lineNumber+".\n \n"+line);
throw new RuntimeException(Macro.MACRO_CANCELED);
}
done = true;
}
String getErrorLine() {
int savePC = pc;
lineNumber = 1;
ignoreEOL = false;
pc = -1;
int lineStart = -1;
while(pc<savePC) {
getToken();
if (token==EOL) {
lineNumber++;
lineStart = pc;
}
}
String line = "";
pc = lineStart;
getToken();
String str;
double v;
while (token!=EOL && !done) {
str = pgm.decodeToken(token, tokenAddress);
if (pc==savePC)
str = "<"+str+">";
line += str+" ";
getToken();
}
return line;
}
final String getString() {
String str = getStringTerm();
while (true) {
getToken();
if (token=='+')
str += getStringTerm();
else {
putTokenBack();
break;
}
};
return str;
}
final String getStringTerm() {
String str;
getToken();
switch (token) {
case STRING_CONSTANT:
str = tokenString;
break;
case STRING_FUNCTION:
str = func.getStringFunction(pgm.table[tokenAddress].type);
break;
case USER_FUNCTION:
Variable v = runUserFunction();
if (v==null)
error("No return value");
str = v.getString();
if (str==null) {
double value = v.getValue();
if ((int)value==value)
str = IJ.d2s(value,0);
else
str = ""+value;
}
break;
case WORD:
str = lookupStringVariable();
if (str!=null)
break;
// else fall through
default:
putTokenBack();
double value = getStringExpression();
if ((int)value==value)
str = IJ.d2s(value,0);
else {
str = ""+value;
if (inPrint && value!=Double.POSITIVE_INFINITY && value!=Double.NEGATIVE_INFINITY
&& value!=Double.NaN && (str.length()-str.indexOf('.'))>6 && str.indexOf('E')==-1)
str = IJ.d2s(value, 4);
}
}
return str;
}
final boolean isStringFunction() {
Symbol symbol = pgm.table[tokenAddress];
return symbol.type==D2S;
}
final double getExpression() {
double value = getTerm();
int next;
while (true) {
next = nextNonEolToken();
if (next=='+') {
getToken();
value += getTerm();
} else if (next=='-') {
getToken();
value -= getTerm();
} else
break;
}
return value;
}
final double getTerm() {
double value = getFactor();
boolean done = false;
int next;
while (!done) {
next = nextToken();
switch (next) {
case '*': getToken(); value *= getFactor(); break;
case '/': getToken(); value /= getFactor(); break;
case '%': getToken(); value %= getFactor(); break;
case '&': getToken(); value = (int)value&(int)getFactor(); break;
case '|': getToken(); value = (int)value|(int)getFactor(); break;
case '^': getToken(); value = (int)value^(int)getFactor(); break;
case SHIFT_RIGHT: getToken(); value = (int)value>>(int)getFactor(); break;
case SHIFT_LEFT: getToken(); value = (int)value<<(int)getFactor(); break;
default: done = true; break;
}
}
return value;
}
final double getFactor() {
double value = 0.0;
Variable v = null;
getToken();
switch (token) {
case NUMBER:
value = tokenValue;
break;
case NUMERIC_FUNCTION:
value = func.getFunctionValue(pgm.table[tokenAddress].type);
break;
case STRING_FUNCTION:
String str = func.getStringFunction(pgm.table[tokenAddress].type);
value = Tools.parseDouble(str);
if (Double.isNaN(value))
error("Numeric value expected");
break;
case USER_FUNCTION:
v = runUserFunction();
if (v==null)
error("No return value");
if (v.getString()!=null)
error("Numeric return value expected");
else
value = v.getValue();
break;
case TRUE: value = 1.0; break;
case FALSE: value = 0.0; break;
case PI: value = Math.PI; break;
case NaN: value = Double.NaN; break;
case WORD:
v = lookupVariable();
if (v==null)
return 0.0;
int next = nextToken();
if (next=='[') {
v = getArrayElement(v);
value = v.getValue();
next = nextToken();
} else if (next=='.') {
value = getArrayLength(v);
next = nextToken();
} else {
if (prefixValue!=0 && !checkingType) {
v.setValue(v.getValue()+prefixValue);
prefixValue = 0;
}
value = v.getValue();
}
if (!(next==PLUS_PLUS || next==MINUS_MINUS))
break;
getToken();
if (token==PLUS_PLUS)
v.setValue(v.getValue()+(checkingType?0:1));
else
v.setValue(v.getValue()-(checkingType?0:1));
break;
case (int)'(':
value = getLogicalExpression();
getRightParen();
break;
case PLUS_PLUS:
prefixValue = 1;
value = getFactor();
break;
case MINUS_MINUS:
prefixValue = -1;
value = getFactor();
break;
case '!':
value = getFactor();
if (value==0.0 || value==1.0) {
value = value==0.0?1.0:0.0;
} else
error("Boolean expected");
break;
case '-':
value = -getFactor();
break;
case '~':
value = ~(int)getFactor();
break;
default:
error("Number or numeric function expected");
}
// IJ.log("getFactor: "+value+" "+pgm.decodeToken(preToken,0));
return value;
}
final Variable getArrayElement(Variable v) {
int index = getIndex();
Variable[] array = v.getArray();
if (array==null)
error("Array expected");
if (index<0 || index>=array.length)
error("Index ("+index+") out of 0-"+(array.length-1)+" range");
return array[index];
}
final double getArrayLength(Variable v) {
getToken(); // '.'
getToken();
if (!(token==WORD && tokenString.equals("length")))
error("'length' expected");
Variable[] array = v.getArray();
if (array==null)
error("Array expected");
return array.length;
}
final double getStringExpression() {
double value = getTerm();
while (true) {
getToken();
if (token=='+') {
getToken();
if (token==STRING_CONSTANT || token==STRING_FUNCTION) {
putTokenBack();
putTokenBack();
break;
}
if (token==WORD) {
Variable v = lookupVariable(tokenAddress);
if (v!=null && v.getString()!=null) {
putTokenBack();
putTokenBack();
break;
}
}
putTokenBack();
value += getTerm();
} else if (token=='-')
value -= getTerm();
else {
putTokenBack();
break;
}
};
return value;
}
/** Searches the local and global sections of the stack for.
the specified variable. Returns null if it is not found. */
final Variable lookupLocalVariable(int symTabAddress) {
//IJ.log("lookupLocalVariable: "+topOfStack+" "+startOfLocals+" "+topOfGlobals);
Variable v = null;
for (int i=topOfStack; i>=startOfLocals; i--) {
if (stack[i].symTabIndex==symTabAddress) {
v = stack[i];
break;
}
}
if (v==null) {
for (int i=topOfGlobals; i>=0; i--) {
if (stack[i].symTabIndex==symTabAddress) {
v = stack[i];
break;
}
}
}
return v;
}
/** Searches the entire stack for the specified variable. Returns null if it is not found. */
final Variable lookupVariable(int symTabAddress) {
Variable v = null;
for (int i=topOfStack; i>=0; i--) {
if (stack[i].symTabIndex==symTabAddress) {
v = stack[i];
break;
}
}
return v;
}
Variable push(Variable var, Interpreter interp) {
if (stack==null)
stack = new Variable[STACK_SIZE];
if (topOfStack>=(STACK_SIZE-2))
interp.error("Stack overflow");
else
topOfStack++;
stack[topOfStack] = var;
return var;
}
void pushGlobals() {
if (pgm.globals==null)
return;
if (stack==null)
stack = new Variable[STACK_SIZE];
for (int i=0; i<pgm.globals.length; i++) {
topOfStack++;
stack[topOfStack] = pgm.globals[i];
}
topOfGlobals = topOfStack;
}
/** Creates a Variable and pushes it onto the stack. */
Variable push(int symTabLoc, double value, String str, Interpreter interp) {
Variable var = new Variable(symTabLoc, value, str);
if (stack==null)
stack = new Variable[STACK_SIZE];
if (topOfStack>=(STACK_SIZE-2))
interp.error("Stack overflow");
else
topOfStack++;
stack[topOfStack] = var;
return var;
}
void trimStack(int previousTOS, int previousStartOfLocals) {
for (int i=previousTOS+1; i<=topOfStack; i++)
stack[i] = null;
topOfStack = previousTOS;
startOfLocals = previousStartOfLocals;
//IJ.log("trimStack: "+topOfStack);
}
/** Searches the entire stack for the variable associated with the
current token. Aborts the macro if it is not found. */
final Variable lookupVariable() {
Variable v = null;
if (stack==null) {
undefined();
return v;
}
boolean found = false;
for (int i=topOfStack; i>=0; i--) {
v = stack[i];
//IJ.log(I+" "+v+" "+v.symTabIndex+" "+tokenAddress);
if (v.symTabIndex==tokenAddress) {
found = true;
break;
}
}
if (!found)
undefined();
return v;
}
final String lookupStringVariable() {
if (stack==null) {
undefined();
return "";
}
boolean found = false;
String str = null;
for (int i=topOfStack; i>=0; i--) {
if (stack[i].symTabIndex==tokenAddress) {
Variable v = stack[i];
found = true;
int next = nextToken();
if (next=='[') {
int savePC = pc;
int index = getIndex();
Variable[] array = v.getArray();
if (array==null)
error("Array expected");
if (index<0 || index>=array.length)
error("Index ("+index+") out of 0-"+(array.length-1)+" range");
str = array[index].getString();
if (str==null) {
pc = savePC-1;
getToken();
}
} else if (next=='.')
str = null;
else {
if (v.getArray()!=null)
{getToken(); error("'[' or '.' expected");}
str = v.getString();
}
break;
}
}
if (!found)
undefined();
return str;
}
int getIndex() {
getToken();
if (token!='[')
error("'['expected");
int index = (int)getExpression();
getToken();
if (token!=']')
error("']' expected");
return index;
}
void undefined() {
if (nextToken()=='(')
error("Undefined identifier");
else
error("Undefined variable");
}
void dump() {
getParens();
if (!done) {
pgm.dumpSymbolTable();
pgm.dumpProgram();
dumpStack();
}
}
void dumpStack() {
IJ.log("");
IJ.log("Stack");
if (stack!=null)
for (int i=topOfStack; i>=0; i--)
IJ.log(i+" "+pgm.table[stack[i].symTabIndex].str+" "+stack[i]);
}
void finishUp() {
func.updateDisplay();
if (func.plot!=null) func.plot.show();
instance = null;
if (!calledMacro) {
if (batchMode) showingProgress = true;
batchMode = false;
imageTable = null;
WindowManager.setTempCurrentImage(null);
}
if (showingProgress)
IJ.showProgress(0, 0);
if (keysSet) {
IJ.setKeyUp(KeyEvent.VK_ALT);
IJ.setKeyUp(KeyEvent.VK_SHIFT);
IJ.setKeyUp(KeyEvent.VK_SPACE);
}
if (rgbWeights!=null)
ColorProcessor.setWeightingFactors(rgbWeights[0], rgbWeights[1], rgbWeights[2]);
if (func.writer!=null) func.writer.close();
}
/** Aborts currently running macro. */
public static void abort() {
abort2(instance);
}
/** Aborts the macro that was running when this one started. */
static void abortPrevious() {
if (previousInstance!=null) {
abort2(previousInstance);
IJ.beep();
previousInstance = null;
}
}
private static void abort2(Interpreter interpreter) {
if (interpreter!=null) {
if (!interpreter.calledMacro) {
batchMode = false;
imageTable = null;
}
interpreter.done = true;
IJ.showStatus("Macro aborted");
}
}
public static Interpreter getInstance() {
return instance;
}
static void setBatchMode(boolean b) {
batchMode = b;
if (b==false) imageTable = null;
}
public static boolean isBatchMode() {
return batchMode;
}
public static void addBatchModeImage(ImagePlus imp) {
if (!batchMode || imp==null) return;
if (imageTable==null)
imageTable = new Vector();
//IJ.log("add: "+imp+" "+imageTable.size());
imageTable.addElement(imp);
}
public static void removeBatchModeImage(ImagePlus imp) {
if (imageTable!=null && imp!=null) {
int index = imageTable.indexOf(imp);
//IJ.log("remove: "+imp+" "+imageTable.size());
if (index!=-1)
imageTable.removeElementAt(index);
}
}
public static int[] getBatchModeImageIDs() {
if (!batchMode || imageTable==null)
return new int[0];
int n = imageTable.size();
int[] imageIDs = new int[n];
for (int i=0; i<n; i++) {
ImagePlus imp = (ImagePlus)imageTable.elementAt(i);
imageIDs[i] = imp.getID();
}
return imageIDs;
}
public static int getBatchModeImageCount() {
if (!batchMode || imageTable==null)
return 0;
else
return imageTable.size();
}
public static ImagePlus getBatchModeImage(int id) {
if (!batchMode || imageTable==null)
return null;
for (Enumeration en=Interpreter.imageTable.elements(); en.hasMoreElements();) {
ImagePlus imp = (ImagePlus)en.nextElement();
if (id==imp.getID())
return imp;
}
return null;
}
public static ImagePlus getLastBatchModeImage() {
if (!batchMode || imageTable==null) return null;
int size = imageTable.size();
if (size==0) return null;
return (ImagePlus)imageTable.elementAt(size-1);
}
/** The specified string, if not null, is added to strings passed to the run() method. */
public static void setAdditionalFunctions(String functions) {
additionalFunctions = functions;
}
public static String getAdditionalFunctions() {
return additionalFunctions;
}
} // class Interpreter
|
Interpreter |
|