/* This file is part of the source code for 3D-XplorMath-J, Version 1.0 (January 2008).
* Copyright (c) 2008 The 3D-XplorMath Consortium (http://3d-xplormath.org).
* This source code is released under a BSD License, which allows redistribution
* in source and binary form, with or without modification, provided copyright
* and license information are included, and with no warranty or guarantee of
* any kind. For details, see http://3d-xplormath.org/j/source/BSDLicense.txt
*/
package vmm.functions;
import java.util.ArrayList;
/**
* Package-private class that represents the compiled version of an expression
* or function that has been parsed by a {@link Parser}. A ProgFuntion is essentially
* a list of commands that are carried out to evaluate an expression or function.
* A ProgFunction can only be created by an object belonging to the static
* nested class {@link ProgFunction.Builder}. (Note that an "expression" is taken
* to be a function of zero arguments and so is not given any special treatment.)
*
A ProgFunction is evaluated using an {@link EvalStack} which contains all
* the intermediate results used in the computation. It also contains a copy of
* the arguments to the function.
*/
final class ProgFunction {
private ProgCommand[] commands;
private int argCount;
private Type argType;
private Type type;
private ProgFunction() {
}
/**
* Returns the number of arguemnts of this ProgFunction. When a ProgFunction is applied
* to a stack, it expects this many arguments to have been placed on the stack. This
* will be zero for an expression that has no arguments.
*/
int getArgCount() {
return argCount;
}
/**
* Returns the type of this function, which will be either Type.REAL or Type.COMPLEX.
* The arguements of this function are of this type. There is no support for
* having different arguments be of different type.
*/
Type getArgType() {
return argType;
}
/**
* Gets the return type of this function, which will be of type Type.REAL or Type.COMPLEX.
* After the function has been applied to a stack, the arguemnts of the function (if any)
* will be replaced by a value of this type that represents the value of the function for
* those arguemnts.
*/
Type getType() {
return type;
}
/**
* Apply this function to a stack. The arguments, if any, should already have been pushed
* onto the stack. After the evaluation, the arguements have been removed from the stack
* and have been replaced by the value of the function.
*/
void apply(EvalStack stack) {
if (argCount == 0) {
for (ProgCommand command : commands)
command.apply(stack);
}
else {
int sizeOfArgBlock = argType == Type.COMPLEX ? 2*argCount : argCount;
stack.startFunctionCall(sizeOfArgBlock);
for (ProgCommand command : commands)
command.apply(stack);
if (type == Type.COMPLEX)
stack.endComplexValuedFunction(sizeOfArgBlock);
else
stack.endRealValuedFunction(sizeOfArgBlock);
}
}
/**
* An object of this type is used (by a {@link Parser}) to create a ProgFunction.
* This is the only way to create a ProgFunction. The builder provides methods
* for adding various types of commands to the ProgFunction that it is building.
*/
static class Builder {
private ProgFunction func;
private ArrayList commands;
private ArrayList currentSubProg;
private ArrayList> pendingSubProgs;
private ArrayList> subProgs;
private ArrayList constants;
private void add(ProgCommand command) {
assert func != null;
assert command != null;
if (currentSubProg != null)
currentSubProg.add(command);
else
commands.add(command);
// System.out.println("added " + command);
}
void start(int argCount, Type argType) { // Begin creating a function with specified number and type of arguemts.
assert func == null;
func = new ProgFunction();
func.argType = argType;
func.argCount = argCount;
commands = new ArrayList();
}
void addStackOp(StackOp op) { // Add a command to the function that represents applying a specified StackOp
add(func.new StackOpRef(op));
}
void addArgumentReference(int argumentNumber) { // Add a command that represents one of the arguments to the function
add(func.new ArgRef(argumentNumber));
}
void addConditional(int trueCaseProgNum, int falseCaseProgNum, boolean trueCaseNeedsRealToComplex) {
// Add a command representing a conditional expression. The trueCaseProgNum and
// falseCaseProgNum were returned by the startSubProg() method. If
// trueCaseNeedsRealToComplex than a REAL_TO_COMPLEX StackOp command is added to the
// end of the trueCase ProgFunction.
if (trueCaseNeedsRealToComplex)
subProgs.get(trueCaseProgNum).add(func.new StackOpRef(StackOp.REAL_TO_COMPLEX));
add(func.new Conditional(subProgs.get(trueCaseProgNum),subProgs.get(falseCaseProgNum)));
}
void addFunctionRef(ProgFunction func) { // Add a command representing calling a ProgFunction
add(func.new FunctionRef(func));
}
void addRealConstant(double x) { // Add a command representing a real constant.
if (constants == null)
constants = new ArrayList();
for (ConstRef c : constants) {
if ( !(c instanceof ComplexConstRef) && c.re == x ) {
add(c);
return;
}
}
ConstRef c = func.new ConstRef(x);
constants.add(c);
add(c);
}
void addComplexConstant(double re, double im) { // Add a command representing a Complex constant.
if (constants == null)
constants = new ArrayList();
for (ConstRef c : constants) {
if ( (c instanceof ComplexConstRef) && c.re == re && ((ComplexConstRef)c).im == im ) {
add(c);
return;
}
}
ComplexConstRef c = func.new ComplexConstRef(re,im);
constants.add(c);
add(c);
}
void addVariableRef(Variable v) { // Add a command representing a real variable.
add(func.new VarRef(v));
}
void addComplexVariableRef(ComplexVariable v) { // Add a command representing a Complex variable.
add(func.new ComplexVarRef(v));
}
int startSubProg() { // Start parsing the true case or false case in a conditional; the return value identifies the sub-program.
assert(func != null);
if (currentSubProg != null) {
pendingSubProgs = new ArrayList>();
pendingSubProgs.add(currentSubProg);
}
currentSubProg = new ArrayList();
if (subProgs == null)
subProgs = new ArrayList>();
subProgs.add(currentSubProg);
return subProgs.size() - 1;
}
void finishSubProg() { // Finish parsing the sub-expression that was started in startSubProg().
assert(currentSubProg != null);
if (pendingSubProgs != null && pendingSubProgs.size() > 0)
currentSubProg = pendingSubProgs.remove(pendingSubProgs.size()-1);
else
currentSubProg = null;
}
ProgFunction finish(Type valueType) { // Finish parsing the function and specify the type of value that it computes.
assert func != null;
assert currentSubProg == null;
func.type = valueType;
func.commands = new ProgCommand[commands.size()];
commands.toArray(func.commands);
ProgFunction f = func;
reset();
return f;
}
void reset() { // Throws away the function that is being built, so that Builder can start on a new function.
func = null;
commands = null;
subProgs = null;
currentSubProg = null;
pendingSubProgs = null;
constants = null;
}
}
abstract private class ProgCommand { // The command in the ProgFunction belong to this class
abstract void apply(EvalStack stack);
}
private class ArgRef extends ProgCommand { // A ProgCommand representing a reference to an argument of a function
int argNum;
ArgRef(int argNum) {
this.argNum = argNum;
}
void apply(EvalStack stack) {
if (argType == Type.COMPLEX)
stack.fetchComplex(argNum*2);
else
stack.fetch(argNum);
}
}
private class StackOpRef extends ProgCommand { // A ProgCommand representing a StackOp
StackOp op;
StackOpRef(StackOp op) {
this.op = op;
}
void apply(EvalStack stack) {
stack.apply(op);
}
}
private class Conditional extends ProgCommand { // A ProgCommand representing the ternary conditional operator ?:
ProgCommand[] trueCase, falseCase;
Conditional(ArrayList trueCase, ArrayList falseCase) {
this.trueCase = new ProgCommand[trueCase.size()];
trueCase.toArray(this.trueCase);
this.falseCase = new ProgCommand[falseCase.size()];
falseCase.toArray(this.falseCase);
}
void apply(EvalStack stack) {
if (stack.pop() == 0) {
for (ProgCommand c : falseCase)
c.apply(stack);
}
else {
for (ProgCommand c : trueCase)
c.apply(stack);
}
}
}
private class ConstRef extends ProgCommand { // A ProgCommand representing a real constant.
double re;
ConstRef(double x) {
re = x;
}
void apply(EvalStack stack) {
stack.push(re);
}
}
private class ComplexConstRef extends ConstRef { // A ProgCommand representing a complex constant.
double im;
ComplexConstRef(double re, double im) {
super(re);
this.im = im;
}
void apply(EvalStack stack) {
stack.push(re,im);
}
}
private class VarRef extends ProgCommand { // A ProgCommand representing a reference to a real Variable
Variable variable;
VarRef(Variable v) {
variable = v;
}
void apply(EvalStack stack) {
stack.push(variable.getVal());
}
}
private class ComplexVarRef extends ProgCommand { // A ProgCommand representing a reference to a ComplexVariable
ComplexVariable variable;
ComplexVarRef(ComplexVariable v) {
variable = v;
}
void apply(EvalStack stack) {
stack.push(variable.getRe(),variable.getIm());
}
}
private class FunctionRef extends ProgCommand { // A ProgCommand representing a ProgFunction
ProgFunction func;
FunctionRef(ProgFunction f) {
func = f;
}
void apply(EvalStack stack) {
func.apply(stack);
}
}
}