package net.bioclipse.spectrum.graph2d;
import java.awt.*;
import java.util.*;
import java.lang.*;
/*
**************************************************************************
**
** Class ParseFunction
**
**************************************************************************
** Copyright (C) 1996 Leigh Brookshaw
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**************************************************************************/
/**
* This class will parse a function definition and solve it returning the
* value. The function may have upto 3 independent variables in it
* (x,y,z).
* <p>
* <b>Known Bugs</B> This class is not fool proof. If the answer is wrong
* then use the parenthesis to force the order of evaluation. The most
* likely place this will be needed is in the use of the power command.
* The exponent is not evaluated correctly if it begins with a
* unary operator.
*
* <h3>List of recognised commands</h3>
* <ul>
* <li> ( ) parenthesis , comma
* <li> +, -, unary -, unary +
* <li> *, /
* <li> ^ (raise to a power)
* <li> pi, e, All the constants in class SpecialFunction
* <li> log
* <li> sin, cos, tan
* <li> asin, acos, atan
* <li> sqrt
* <li> rand
* <li> exp
* <li> remainder
* <li> atan2
* <li> All the functions in class SpecialFunction
* <li> Independent variables x,y,z
* <li> Scientific notation using "e", "E", "d", "D".
* </ul>
*
* @version $Revision: 1.8 $, $Date: 1996/08/12 23:37:08 $
* @author Leigh Brookshaw
*/
public class ParseFunction extends ScanString {
/*
**********************
** Constants
*********************/
/*
** Specifiy the integer values associated with keywords. Every integer
** in this list is associated with keyword used in the string
*/
static final int GROUP = 1;
static final int ENDGROUP = 2;
static final int ADD = 3;
static final int SUBTRACT = 4;
static final int DIVIDE = 5;
static final int MULTIPLY = 6;
static final int LOG = 7;
static final int POWER = 8;
static final int PI = 9;
static final int E = 10;
static final int SIN = 11;
static final int COS = 12;
static final int TAN = 13;
static final int X = 14;
static final int Y = 15;
static final int Z = 16;
static final int ASIN = 17;
static final int ACOS = 18;
static final int ATAN = 19;
static final int RAD = 20;
static final int SQRT = 21;
static final int RANDOM = 22;
static final int LOG10 = 23;
static final int EXP = 24;
static final int REMAINDER = 25;
static final int COMMA = 26;
static final int ATAN2 = 27;
static final int J0 = 28;
static final int J1 = 29;
static final int JN = 30;
static final int SINH = 31;
static final int COSH = 32;
static final int TANH = 33;
static final int ASINH = 34;
static final int ACOSH = 35;
static final int ATANH = 36;
static final int Y0 = 37;
static final int Y1 = 38;
static final int YN = 39;
static final int FAC = 40;
static final int GAMMA = 41;
static final int ERF = 42;
static final int ERFC = 43;
static final int NORMAL = 44;
static final int POISSONC = 45;
static final int POISSON = 46;
static final int CHISQC = 47;
static final int CHISQ = 48;
static final int IGAM = 49;
static final int IGAMC = 50;
/*
** Physical Constants in SI units
*/
static final int BOLTZMAN = 101;
static final int ECHARGE = 102;
static final int EMASS = 103;
static final int PMASS = 104;
static final int GRAV = 105;
static final int PLANCK = 106;
static final int LIGHTSPEED = 107;
static final int STEFANBOLTZ = 108;
static final int AVOGADRO = 109;
static final int GASCONSTANT = 110;
static final int GRAVACC = 111;
/*
*********************
**
** Private Variables
**
*********************/
/*
** The root node after parsing the string
*/
private Node root;
/*
** Boolean flags set when any of the independent variable are encountered
*/
private boolean independent_x;
private boolean independent_y;
private boolean independent_z;
/*
** Internal values for the independent variables
*/
private double x;
private double y;
private double z;
/**
* Debug variable. If set true debug output is printed.
*/
public boolean debug;
/*
************************
**
** Constructors
**
**********************/
/**
* Instantiate the class
*/
public ParseFunction() {
root = null;
independent_x = false;
independent_y = false;
independent_z = false;
debug = false;
x = 0.0;
y = 0.0;
z = 0.0;
addKeyWord(",", COMMA);
addKeyWord("(", GROUP);
addKeyWord(")", ENDGROUP);
addKeyWord("+", ADD);
addKeyWord("-", SUBTRACT);
addKeyWord("/", DIVIDE);
addKeyWord("*", MULTIPLY);
addKeyWord("log", LOG);
addKeyWord("^", POWER);
addKeyWord("pi", PI);
addKeyWord("e", E);
addKeyWord("sin", SIN);
addKeyWord("cos", COS);
addKeyWord("tan", TAN);
addKeyWord("x", X);
addKeyWord("y", Y);
addKeyWord("z", Z);
addKeyWord("asin", ASIN);
addKeyWord("acos", ACOS);
addKeyWord("atan", ATAN);
addKeyWord("rad", RAD);
addKeyWord("sqrt", SQRT);
addKeyWord("rand", RANDOM);
addKeyWord("log10", LOG10);
addKeyWord("exp", EXP);
addKeyWord("rem", REMAINDER);
addKeyWord("atan2", ATAN2);
addKeyWord("j0", J0);
addKeyWord("j1", J1);
addKeyWord("jn", JN);
addKeyWord("sinh", SINH);
addKeyWord("cosh", COSH);
addKeyWord("tanh", TANH);
addKeyWord("asinh", ASINH);
addKeyWord("acosh", ACOSH);
addKeyWord("atanh", ATANH);
addKeyWord("y0", Y0);
addKeyWord("y1", Y1);
addKeyWord("yn", YN);
addKeyWord("fac", FAC);
addKeyWord("gamma", GAMMA);
addKeyWord("erf", ERF);
addKeyWord("erfc", ERFC);
addKeyWord("normal", NORMAL);
addKeyWord("poissonc", POISSONC);
addKeyWord("poisson", POISSON);
addKeyWord("chisq", CHISQ);
addKeyWord("chisqc", CHISQC);
addKeyWord("igam", IGAM);
addKeyWord("igamc", IGAMC);
addKeyWord("k", BOLTZMAN);
addKeyWord("ec", ECHARGE);
addKeyWord("me", EMASS);
addKeyWord("mp", PMASS);
addKeyWord("gc", GRAV);
addKeyWord("h", PLANCK);
addKeyWord("c", LIGHTSPEED);
addKeyWord("sigma", STEFANBOLTZ);
addKeyWord("na", AVOGADRO);
addKeyWord("r", GASCONSTANT);
addKeyWord("g", GRAVACC);
}
/**
* Instantiate the class and define the string to parse.
* @param s The string to be parsed.
*/
public ParseFunction(String s) {
this();
setString(s);
}
/*
******************
**
** Public Methods
**
*****************/
/**
* Parse the string.
* @param s The string to parse
* @return true if it was successful, false otherwise.
*/
public boolean parse(String s) {
setString(s);
return parse();
}
/**
* Parse the previously set string
* @return true if it was successful, false otherwise.
*/
public boolean parse() {
root = new Node();
if( parseString(root) != EOS) return false;
if(debug) {
System.out.println("Before Reordering:");
root.print(5);
}
reOrderNodes(root);
if(debug) {
System.out.println("After Reordering:");
root.print(5);
}
return true;
}
/**
* Return the solution of the function given the independent values
* @param x indpendent x value
* @param y indpendent y value
* @param z indpendent z value
* @return solution of the function
*/
public double getResult(double x, double y, double z)
throws Exception {
this.x = x;
this.y = y;
this.z = z;
return evaluate(root);
}
/**
* Return the solution of the function given the independent values
* @param x indpendent x value
* @param y indpendent y value
* @return solution of the function
*/
public double getResult(double x, double y)
throws Exception {
this.x = x;
this.y = y;
return evaluate(root);
}
/**
* Return the solution of the function given the independent values
* @param x indpendent x value
* @return solution of the function
*/
public double getResult(double x) throws Exception {
this.x = x;
return evaluate(root);
}
/**
* Return the solution of the function if it has no independent values
* or they have already been set using the set methods
* @return solution of the function
* @see ParseFunction.setX()
* @see ParseFunction.setY()
* @see ParseFunction.setZ()
*/
public double getResult() throws Exception {
return evaluate(root);
}
/**
* Return an array of solutions given an array of x values
* @param n number of values to process in the input array
* @param x Array containing the x values.
* @return Array containing the solutions.
* @exception Exception
* Generic exception if the array index n<=0, or x is null.
*/
public double[] getResults(int n, double x[]) throws Exception {
if(n <= 0) throw new Exception("Array index error");
if(x == null) throw new Exception("X Array error");
double array[] = new double[n];
for(int i=0; i<n; i++) {
this.x = x[i];
array[i] = evaluate(root);
}
return array;
}
/**
* Return an array of solutions given an array of x values and y values
* @param n number of values to process in the input array
* @param x Array containing the x values.
* @param y Array containing the y values.
* @return Array containing the solutions.
* @exception Exception
* Generic exception if the array index n<=0, or x is null, or y is null.
*/
public double[] getResults(int n, double x[], double y[])
throws Exception {
if(n <= 0) throw new Exception("Array index error");
if(x == null) throw new Exception("X Array error");
if(y == null) throw new Exception("Y Array error");
double array[] = new double[n];
for(int i=0; i<n; i++) {
this.x = x[i];
this.y = y[i];
array[i] = evaluate(root);
}
return array;
}
/**
* Return an array of solutions given an array of x values, y values
* and z values.
* @param n number of values to process in the input array
* @param x Array containing the x values.
* @param y Array containing the y values.
* @return Array containing the solutions.
* @exception Exception
* Generic exception if the array index n<=0, or x is null,
* or y is null, or z is null.
*/
public double[] getResults(int n, double x[], double y[], double z[])
throws Exception {
if(n <= 0) throw new Exception("Array index error");
if(x == null) throw new Exception("X Array error");
if(y == null) throw new Exception("Y Array error");
if(z == null) throw new Exception("Z Array error");
double array[] = new double[n];
for(int i=0; i<n; i++) {
this.x = x[i];
this.y = y[i];
this.z = z[i];
array[i] = evaluate(root);
}
return array;
}
/**
* Return a boolean array with index 0 true if the independent
* variable x was found in the function, index 1 true if
* y was found, and index 2 true if z was found.
*/
public boolean[] getVariables() {
boolean b[] = new boolean[3];
b[0] = independent_x;
b[1] = independent_y;
b[2] = independent_z;
return b;
}
/**
* Set the value of the independent variable X.
*/
public void setX( double x ) { this.x = x; }
/**
* Set the value of the independent variable Y.
*/
public void setY( double y ) { this.y = y; }
/**
* Set the value of the independent variable Z.
*/
public void setZ( double z ) { this.z = z; }
/*
*******************
**
** Private Methods
**
******************/
/*
** Parse the string and build the node link list.
** This builds up a simple Left-Right binary node tree.
**
** A GROUP token (start of parenthesis) forces a new group node
** that starts an independent branch of the tree with the contents
** of the group linked to the left node of the group node.
** Intrinsic functions behave like a GROUP token they start an independent
** branch of the tree with the contents of the group linked to the
** left node of the intrinsic function.
** Intrinsic functions that have to be passed 2 parameters have the
** second parameter linked to there right node.
** (This has to be modified for functions containing more than 2 parameters
** currently the code cannot deal with more than 2 parameters)
*/
private int parseString(Node node) {
Node left;
Node right;
int token;
int t;
// Get the next token in the string
token = nextWord();
// Do some preliminary branching.
if(token == ERROR) {
System.out.println("Error parsing \""+sval+"\"");
return ERROR;
} else
if( token != EOS && debug ) {
System.out.println("Parse: "+sval+"\t Token: "+token);
} else
if( token == EOS && debug ) {
System.out.println("Parse: EOS");
}
// Main token switch
switch(token) {
/*
** Number token or constant tokens.
** Place the value in the node and recurse on this
** terminal node. It will be used by an operator.
*/
case NUMBER:
node.type = Node.VALUE;
node.value = nval;
return parseString(node);
case PI:
node.type = Node.VALUE;
node.value = Math.PI;
return parseString(node);
case E:
node.type = Node.VALUE;
node.value = Math.E;
return parseString(node);
case BOLTZMAN:
node.type = Node.VALUE;
node.value = SpecialFunction.BOLTZMAN;
return parseString(node);
case ECHARGE:
node.type = Node.VALUE;
node.value = SpecialFunction.ECHARGE;
return parseString(node);
case EMASS:
node.type = Node.VALUE;
node.value = SpecialFunction.EMASS;
return parseString(node);
case PMASS:
node.type = Node.VALUE;
node.value = SpecialFunction.PMASS;
return parseString(node);
case GRAV:
node.type = Node.VALUE;
node.value = SpecialFunction.GRAV;
return parseString(node);
case PLANCK:
node.type = Node.VALUE;
node.value = SpecialFunction.PLANCK;
return parseString(node);
case LIGHTSPEED:
node.type = Node.VALUE;
node.value = SpecialFunction.LIGHTSPEED;
return parseString(node);
case STEFANBOLTZ:
node.type = Node.VALUE;
node.value = SpecialFunction.STEFANBOLTZ;
return parseString(node);
case AVOGADRO:
node.type = Node.VALUE;
node.value = SpecialFunction.AVOGADRO;
return parseString(node);
case GASCONSTANT:
node.type = Node.VALUE;
node.value = SpecialFunction.GASCONSTANT;
return parseString(node);
case GRAVACC:
node.type = Node.VALUE;
node.value = SpecialFunction.GRAVACC;
return parseString(node);
case RAD:
node.type = Node.VALUE;
node.value = Math.PI/180.0;
return parseString(node);
case RANDOM:
node.type = Node.VALUE;
node.value = Math.random();
return parseString(node);
/*
** Independent variables behave like constant nodes except
** they are flagged as INDEPENDENT nodes. Then we recurse using this
** this node as it has to be used by an operator
*/
case X: case Y: case Z:
node.op = token;
node.type = Node.INDEPENDENT;
if(token == X) independent_x = true;
else
if(token == Y) independent_y = true;
else
if(token == Z) independent_z = true;
return parseString(node);
/*
** Terminal tokens
*/
case ENDGROUP: case EOS: case COMMA:
break;
/*
** beginning of a group '('. Parse the string until the
** corresponding endgroup is encountered ')'. The created
** node list is attached to the group nodes left
** node. Then continue the process by continuing to parse
** the string after the endgroup.
*/
case GROUP:
left = new Node();
if(parseString(left) == ENDGROUP) {
node.left = left;
node.type = Node.GROUP;
node.precedence = Node.P5;
token = parseString(node);
} else {
System.out.println("Parse Failed: missing parentheses");
token = ERROR;
}
break;
/*
** Binary and Unary Operators.
** The existing node goes to the left and everything
** on the right gets attached to the right node.
**
** A unary operator is recognised by the empty
** node parsed ie nothing exists on the left.
*/
case ADD: case SUBTRACT: case MULTIPLY: case DIVIDE: case POWER:
right = new Node();
t = parseString(right);
if(t != ERROR) {
if( (token == SUBTRACT || token == ADD )
&& node.type == Node.NULL ) {
//System.out.println("...Unary Operator");
node.right = right;
node.precedence = Node.P4;
node.op = token;
node.type = Node.OP;
} else {
//System.out.println("...Binary Operator");
left = new Node(node);
node.left = left;
node.right = right;
node.op = token;
node.type = Node.OP;
switch (token) {
case ADD: case SUBTRACT:
node.precedence = Node.P1;
break;
case MULTIPLY: case DIVIDE:
node.precedence = Node.P2;
break;
case POWER:
node.precedence = Node.P3;
break;
}
}
}
token = t;
break;
/*
** Single parameter intrinsic functions behave excacty like
** parenthesis.
*/
case SIN: case COS: case TAN:
case ASIN: case ACOS: case ATAN:
case LOG: case SQRT: case LOG10:
case EXP: case REMAINDER:
case J0: case J1: case Y0: case Y1:
case SINH: case COSH: case TANH:
case ASINH: case ACOSH: case ATANH:
case FAC: case GAMMA: case ERF: case ERFC:
case NORMAL:
node.op = token;
node.type = Node.INTRINSIC;
node.precedence = Node.P0;
token = nextWord();
if(token != GROUP ) {
System.out.println(
"Parse Failed: intrinsic function is missing \"(\"");
token = ERROR;
} else {
left = new Node();
if(parseString(left) == ENDGROUP) {
node.left = left;
token = parseString(node);
} else {
System.out.println(
"Parse Failed: intrinsic function is missing \")\"");
token = ERROR;
}
}
break;
/*
** 2 parameter intrinsic functions
** First parameter on attached to the left node,
** second parameter attached to the right.
*/
case ATAN2: case JN: case YN: case IGAM: case IGAMC:
case CHISQ: case CHISQC: case POISSON: case POISSONC:
node.op = token;
node.type = Node.INTRINSIC;
node.precedence = Node.P0;
token = nextWord();
if(debug) System.out.println("Parse: "+sval);
if(token != GROUP ) {
System.out.println(
"Parse Failed: intrinsic function is missing \"(\"");
token = ERROR;
} else {
Node param1 = new Node();
if(parseString(param1) == COMMA) {
Node param2 = new Node();
if(parseString(param2) == ENDGROUP) {
node.right = param2;
node.left = param1;
token = parseString(node);
} else {
System.out.println(
"Parse Failed: intrinsic function is missing \")\"");
token = ERROR;
}
} else {
System.out.println(
"Parse Failed: intrinsic function is missing \",\"");
token = ERROR;
}
}
break;
default:
break;
}
return token;
}
/*
** Starting at the passed node evaluate everything below this node
*/
private double evaluate(Node node) throws Exception {
double value = 0.0;
if( node == null) {
throw new
Exception("evaluate: Failed because of null node!");
}
switch (node.type) {
case Node.GROUP:
value = evaluate(node.left);
break;
case Node.OP:
value = evaluateOp(node);
break;
case Node.INTRINSIC:
value = evaluateIntrinsic(node);
break;
case Node.VALUE:
value = node.value;
break;
case Node.INDEPENDENT:
if(node.op == X) value = x;
else
if(node.op == Y) value = y;
else
if(node.op == Z) value = z;
break;
default:
throw new Exception("evaluate: Unknown type!");
}
return value;
}
/*
** Parsed node is an Operator node. Evaluate both the left and right
** nodes and then excecute the operator on the values.
*/
private double evaluateOp(Node node) throws Exception {
double value = 0.0;
switch (node.op) {
case ADD:
if(node.left != null) value = evaluate(node.left);
value += evaluate(node.right);
break;
case SUBTRACT:
if(node.left != null) value = evaluate(node.left);
value -= evaluate(node.right);
break;
case DIVIDE:
value = evaluate(node.left);
value /= evaluate(node.right);
break;
case MULTIPLY:
value = evaluate(node.left);
value *= evaluate(node.right);
break;
case POWER:
value = Math.pow(evaluate(node.left),
evaluate(node.right));
break;
default:
throw new Exception(
"evaluate: Failed because of Unknown operator!");
}
return value;
}
/*
** Parsed node is an instrinsic function. Evaluate the parameters
** then call the intrinsic and return the result
*/
private double evaluateIntrinsic(Node node) throws Exception {
double value = 0.0;
switch (node.op) {
case SIN:
value = Math.sin(evaluate(node.left));
break;
case COS:
value = Math.cos(evaluate(node.left));
break;
case TAN:
value = Math.tan(evaluate(node.left));
break;
case ASIN:
value = Math.asin(evaluate(node.left));
break;
case ACOS:
value = Math.acos(evaluate(node.left));
break;
case ATAN:
value = Math.atan(evaluate(node.left));
break;
case LOG:
value = Math.log(evaluate(node.left));
break;
case SQRT:
value = Math.sqrt(evaluate(node.left));
break;
case LOG10:
value = Math.log(evaluate(node.left))/
Math.E;
case EXP:
value = Math.exp(evaluate(node.left));
break;
case J0:
value = SpecialFunction.j0(
evaluate(node.left));
break;
case J1:
value = SpecialFunction.j1(
evaluate(node.left));
break;
case Y0:
value = SpecialFunction.y0(
evaluate(node.left));
break;
case Y1:
value = SpecialFunction.y1(
evaluate(node.left));
break;
case FAC:
value = SpecialFunction.fac(
evaluate(node.left));
break;
case GAMMA:
value = SpecialFunction.gamma(
evaluate(node.left));
break;
case SINH:
value = SpecialFunction.sinh(
evaluate(node.left));
break;
case COSH:
value = SpecialFunction.cosh(
evaluate(node.left));
break;
case TANH:
value = SpecialFunction.tanh(
evaluate(node.left));
break;
case ASINH:
value = SpecialFunction.asinh(
evaluate(node.left));
break;
case ACOSH:
value = SpecialFunction.acosh(
evaluate(node.left));
break;
case ATANH:
value = SpecialFunction.atanh(
evaluate(node.left));
break;
case ERF:
value = SpecialFunction.erf(
evaluate(node.left));
break;
case ERFC:
value = SpecialFunction.erfc(
evaluate(node.left));
break;
case NORMAL:
value = SpecialFunction.normal(
evaluate(node.left));
break;
case POISSON:
value = SpecialFunction.poisson(
(int)(evaluate(node.left)+0.01),
evaluate(node.right));
break;
case POISSONC:
value = SpecialFunction.poissonc(
(int)(evaluate(node.left)+0.01),
evaluate(node.right));
break;
case CHISQ:
value = SpecialFunction.chisq(
evaluate(node.left),
evaluate(node.right));
break;
case CHISQC:
value = SpecialFunction.chisqc(
evaluate(node.left),
evaluate(node.right));
break;
case IGAM:
value = SpecialFunction.igam(
evaluate(node.left),
evaluate(node.right));
break;
case IGAMC:
value = SpecialFunction.igamc(
evaluate(node.left),
evaluate(node.right));
break;
case ATAN2:
value = Math.atan2(evaluate(node.left),
evaluate(node.right));
break;
case JN:
value = SpecialFunction.jn(
(int)(evaluate(node.left)+0.01),
evaluate(node.right));
break;
case YN:
value = SpecialFunction.yn(
(int)(evaluate(node.left)+0.01),
evaluate(node.right));
break;
default:
throw new Exception(
"evaluate: Failed because of an unknown intrinsic!");
}
return value;
}
/*
** The basic parsing mechanism failes to recognise precedence.
** this method reorganises the nodes into ascending precedence.
** That is nodes with higher precedence are pushed further down the tree
** this means they get evaluated first.
**
** Precedence
** Grouping
** Unary minus/plus
** Power
** Multiplication/Division
** Addition/Subtraction
** Variables, Intrinsic Functions, Constants
*/
private void reOrderNodes(Node node) {
Node right = null;
Node left = null;
if(node == null) return;
right = node.right;
left = node.left;
if(right != null && right.type == Node.GROUP) {
reOrderNodes(right);
} else
if(left != null && left.type == Node.GROUP) {
reOrderNodes(left);
}
if( node.type == Node.GROUP ) {
reOrderNodes(left);
} else
if( node.type == Node.OP && right.type == Node.OP ) {
if(node.precedence >= right.precedence) {
Node newnode = new Node(node);
newnode.right = right.left;
node.replace(right);
node.left = newnode;
right = null;
reOrderNodes(node);
}
}
}
}
class Node extends Object {
public static final int OP = 0;
public static final int VALUE = 1;
public static final int INTRINSIC = 2;
public static final int NULL = 3;
public static final int INDEPENDENT = 4;
public static final int GROUP = 5;
public static final int PARAMETER = 6;
public static final int P0 = 0;
public static final int P1 = 1;
public static final int P2 = 2;
public static final int P3 = 3;
public static final int P4 = 4;
public static final int P5 = 5;
int type;
Node left;
Node right;
int op;
double value;
int precedence;
public Node() {
type = NULL;
left = null;
right = null;
op = NULL;
value = 0.0;
precedence = P0;
}
public Node(Node n) {
replace(n);
}
public void replace(Node n ) {
if(n == null) return;
op = n.op;
type = n.type;
left = n.left;
right = n.right;
value = n.value;
precedence = n.precedence;
}
public void indent(int ind) {
for (int i = 0; i < ind; i++) System.out.print(" ");
}
public void print(int indentLevel) {
char l[] = new char[1];
indent(indentLevel);
System.out.println("NODE type=" + type);
indent(indentLevel);
System.out.println(" prec="+ precedence);
indent(indentLevel);
switch (type) {
case Node.VALUE:
System.out.println(" value=" + value);
break;
case Node.INDEPENDENT:
System.out.println(" variable=" + op);
break;
default:
System.out.println(" op=" + op);
if(left != null) left.print(indentLevel + 5);
if(right != null) right.print(indentLevel + 5);
break;
}
}
}