![JAR search and dependency download from the Maven repository](/logo.png)
com.adobe.xfa.formcalc.Instruction Maven / Gradle / Ivy
Show all versions of aem-sdk-api Show documentation
/*
* ADOBE CONFIDENTIAL
*
* Copyright 2007 Adobe Systems Incorporated All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains the property of
* Adobe Systems Incorporated and its suppliers, if any. The intellectual and
* technical concepts contained herein are proprietary to Adobe Systems
* Incorporated and its suppliers and may be covered by U.S. and Foreign
* Patents, patents in process, and are protected by trade secret or copyright
* law. Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained from
* Adobe Systems Incorporated.
*/
package com.adobe.xfa.formcalc;
import com.adobe.xfa.Obj;
import com.adobe.xfa.ut.MsgFormat;
import com.adobe.xfa.ut.ResId;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.text.Collator;
import java.util.Locale;
/**
* The class Instruction defines all the instructions that the
* FormCalc virtual machine is capable of executing, as a collection
* of static methods. Each instruction corresponds to an opcode of
* this virtual machine.
*
* An execution path of the virtual machine is just a sequence of
* instructions stored in FormCalc's virtual code space. Each sequence
* is terminated with an Stop instruction, to represent the end of an
* execution path.
*
*
All instructions have a common interface, consisting of:
* a the FormCalc YYPARSENAME. Since the FormCalc
* virtual machine is a stack machine, i.e., its fetches its operand(s)
* from a runtime stack, and pushes its result back onto the runtime stack,
* most instructions are single word instructions. A few instructions,
* like Load, In, Out and Call are multi-word instructions -- some of their
* operands are stored in the words following the opcode.
* Other instructions, like If, For and While, also multi-word instructions,
* contain control flow information to the different execution paths
* inherent of conditional and iterative instructions.
*
* @author Mike P. Tardif.
*
* @exclude from published api.
*/
public final class Instruction {
Instruction() {
// empty
}
/**
* A useful constant. Every instruction sequence ends with this instruction.
*/
static final Method gStop = getDeclaredMethod("Stop");
static final Method gOr = getDeclaredMethod("Or");
static final Method gAnd = getDeclaredMethod("And");
static final Method gEq = getDeclaredMethod("Eq");
static final Method gNe = getDeclaredMethod("Ne");
static final Method gGt = getDeclaredMethod("Gt");
static final Method gGe = getDeclaredMethod("Ge");
static final Method gLt = getDeclaredMethod("Lt");
static final Method gLe = getDeclaredMethod("Le");
static final Method gAdd = getDeclaredMethod("Add");
static final Method gSub = getDeclaredMethod("Sub");
static final Method gMul = getDeclaredMethod("Mul");
static final Method gDiv = getDeclaredMethod("Div");
static final Method gUminus = getDeclaredMethod("Uminus");
static final Method gUplus = getDeclaredMethod("Uplus");
static final Method gNot = getDeclaredMethod("Not");
static final Method gNoop = getDeclaredMethod("Noop");
static final Method gDeref = getDeclaredMethod("Deref");
static final Method gLoad = getDeclaredMethod("Load");
static final Method gGbl = getDeclaredMethod("Gbl");
static final Method gFunc = getDeclaredMethod("Func");
static final Method gCall = getDeclaredMethod("Call");
static final Method gRet = getDeclaredMethod("Ret");
static final Method gForm = getDeclaredMethod("Form");
static final Method gAsgn = getDeclaredMethod("Asgn");
static final Method gAsgn2 = getDeclaredMethod("Asgn2");
static final Method gDot = getDeclaredMethod("Dot");
static final Method gDotdot = getDeclaredMethod("Dotdot");
static final Method gDothash = getDeclaredMethod("Dothash");
static final Method gDotstar = getDeclaredMethod("Dotstar");
static final Method gIndex = getDeclaredMethod("Index");
static final Method gList = getDeclaredMethod("List");
static final Method gForeach = getDeclaredMethod("Foreach");
static final Method gFor = getDeclaredMethod("For");
static final Method gWhile = getDeclaredMethod("While");
static final Method gIf = getDeclaredMethod("If");
static final Method gIfFunc = getDeclaredMethod("IfFunc");
static final Method gBreak = getDeclaredMethod("Break");
static final Method gCont = getDeclaredMethod("Cont");
static final Method gEnter = getDeclaredMethod("Enter");
static final Method gExit = getDeclaredMethod("Exit");
private static Method getDeclaredMethod(String name) {
try {
return Instruction.class.getDeclaredMethod(name, CalcParser.class);
}
catch (NoSuchMethodException ignored) {
assert false;
return null;
}
}
/**
* Create storage for this Instruction object. Allocate an
* initial chunk of instruction space.
*
* @param nCodeSize an initial size for instruction space.
* @return integer 1 upon success, and 0 otherwise.
*/
int create(int nCodeSize) {
mnCodeSize = nCodeSize;
moCodeBase = new Object[mnCodeSize];
mnCodePtr = 0;
mnProgEnd = mnProgBase = mnProgCtr = mnCodePtr;
// Javaport: Not required!
// moDebugLineNo = null;
// mnDebugPrevLine = 0;
// mnDebugPrevStoppedAtLine = 0;
// mnDebugStopAtStackDepth = -1; // -1 means don't stop
// mnDebugPollCounter = 0;
return 1;
}
/**
* (Re-)Initialize this Instruction object. When not caching code, the
* instruction code pointer is reset to the instruction space base address.
* The instruction program counter is reset to the instruction code pointer.
*/
void init(CalcParser oParser) {
if (oParser.mbSyntaxErrorSeen)
mnCodePtr = mnProgBase;
else if (oParser.mbWasInSaveMode)
mnCodePtr = mnProgEnd;
else
mnCodePtr = 0;
mnProgCtr = mnCodePtr;
mnProgBase = mnCodePtr;
}
/**
* Get this object's instruction space base address.
*
* @return the instruction space base address.
*/
int getCodeStart() {
return 0;
}
/**
* Get this object's program start address.
*
* @return the address of the start instruction.
*/
int getProgStart() {
return mnProgBase;
}
/**
* Get this object's program start address.
*/
void setProgStart() {
mnProgBase = mnCodePtr;
}
/**
* Get this object's instruction space code size.
*
* @return the instruction space code size.
*/
int getCodeSize() {
return mnCodeSize;
}
/**
* Get the relative address of this object's next address instruction.
* Use this method to get program counter-based relative addresses
* (also called PC-relative addressing).
*
* @return this instruction's relative address.
*/
int generate() {
return mnCodePtr;
}
/**
* Generate the next instruction word in this object's instruction space.
* Because instruction space is allowed to grow as needed, all instruction
* must be position independent, i.e., only PC-relative addresses are allowed.
*
* @param nInstruction an instruction word. If many cases, several
* instruction words need to be generated to complete an instruction.
* @return this instruction's relative address.
*/
int generate(Object nInstruction) {
//
// Grow code space as needed. When full, grow code by two.
//
if (mnCodePtr >= mnCodeSize) {
int nProgEndOffset = mnProgEnd;
int nCodeSize = mnCodePtr;
int nProgSize = mnProgBase;
int nProgCtrSize = mnProgCtr;
mnCodeSize <<= 1;
Object[] oNewBase = new Object[mnCodeSize];
System.arraycopy(moCodeBase, 0, oNewBase, 0, mnCodeSize >> 1);
moCodeBase = oNewBase;
// Javaport: Not required!
// if (moDebugLineNo != null)
// moDebugLineNo = (DebugLineInfo *)
// Realloc(moDebugLineNo, mnCodeSize * sizeof(DebugLineInfo));
mnCodePtr = nCodeSize;
mnProgCtr = nProgCtrSize;
mnProgBase = nProgSize;
mnProgEnd = nProgEndOffset;
}
int pInstruction = mnCodePtr;
moCodeBase[mnCodePtr++] = nInstruction;
return pInstruction;
}
// Javaport: Not required!
// /**
// * Generate the next instruction word, with source line for debugging.
// * It maintains the moDebugLineNo array, which is an array of line
// * numbers parallel to the moCodeBase array.
// *
// * @param nInstruction an instruction word. If many cases, several
// * instruction words need to be generated to complete an instruction.
// * @param nLine line of source code.
// * @return this instruction's relative address.
// */
// int generate(int nInstruction, int nScriptID, int nLine) {
// if (moDebugLineNo == null)
// moDebugLineNo = (DebugLineInfo *) Calloc(mnCodeSize, sizeof(DebugLineInfo));
// int nLastInstr = Generate(nInstruction);
// moDebugLineNo[nLastInstr].mnScriptID = nScriptID;
// moDebugLineNo[nLastInstr].mnLineNo = nLine;
// moDebugLineNo[nLastInstr].bBreakPointSet = false;
// return nLastInstr;
// }
/**
* Execute a sequence of instructions. All sequences are terminated with
* a Stop instruction.
*
* @param oParser the FormCalc parser.
* @param nStartAddr the start address of an instruction sequence.
*/
void execute(CalcParser oParser, int nStartAddr) {
//
// Protect against misuse, e.g.,
// calling execute without having parsed anything.
//
assert(mnCodePtr > 0);
//
// If not debugging Then ...
//
// if (oParser.moDebugHost == null /* || moDebugLineNo == null */) {
mnProgCtr = nStartAddr;
while (moCodeBase[mnProgCtr] != gStop) {
if(null != oParser.moScriptHost && oParser.moScriptHost.cancelActionOccured()){
oParser.mbCancelStatus = true;
if(oParser.mbCancelStatus)
break;
}
else{
Object oObj = moCodeBase[mnProgCtr++];
// assert(oObj instanceof Method);
try {
Method func = (Method) oObj;
func.invoke(null, oParser);
} catch(IllegalAccessException e) {
assert (e != null);
} catch(InvocationTargetException e) {
assert (e != null);
}
}
}
// }
// Javaport: Not required!
// else {
// // Debugging
// if (nStartAddr == GetCodeStart()) {
// mnDebugPrevLine = 0;
// mnDebugPrevStoppedAtLine = 0;
// mnDebugStopAtStackDepth = -1; // -1 means don't stop
// mnDebugPollCounter = 0;
// }
// mnProgCtr = nStartAddr;
// while (moCodeBase[mnProgCtr] != gStop) {
// // Call the Poll() callback periodically.
// if (!(mnDebugPollCounter++ & 1023))
// oParser.moDebugHost.poll();
//
// int nPC = mnProgCtr - GetCodeStart();
// int nLine = moDebugLineNo[nPC].mnLineNo;
//
// // If the current line changes, clear mnDebugPrevStoppedAtLine
// // (otherwise we couldn't break twice on a line inside a loop).
// if (mnDebugPrevLine != nLine)
// mnDebugPrevStoppedAtLine = 0;
//
// // Never stop at the same line twice (multiple
// // instructions share a single source line number).
// // Don't stop on the two expression-separator instructions.
// // This is primarily so that we can step over function calls
// // without stopping twice on the same line of source (since
// // one or both of these instructions will follow the Call
// // instruction and is on the same line).
// if (mnDebugPrevStoppedAtLine != nLine &&
// mnCodeBae[mnProgCtr] != gList &&
// mnCodeBae[mnProgCtr] != gDeref) {
// // If a break-point is hit, OR mnDebugStopAtStackDepth
// // is greater than or equal to the current stack depth,
// // then stop.
// if (moDebugLineNo[nPC].bBreakPointSet ||
// mnDebugStopAtStackDepth >=
// (int)oParser.moFrame.getDepth()) {
//
// mnDebugPrevStoppedAtLine = nLine;
// // reset mnDebugStopAtStackDepth (-1 means don't stop)
// mnDebugStopAtStackDepth = -1;
// int nScriptID = moDebugLineNo[nPC].mnScriptID;
// oParser.moDebugHost.stopped(nScriptID, nLine);
// }
// }
// mnDebugPrevLine = nLine;
// (*(moCodeBase[mnProgCtr++]))(oParser);
// }
// }
}
/**
* Release resources held in this object's instruction space.
* Traverse the entire instruction space, as though it were just a
* sequence of words, deleting symbols from Load instructions,
* and function names form Call instructions.
*
*
Syntax errors are the reason we can't traverse instructions
* in the conventional manner. Upon discovery of a syntax error,
* incomplete instructions with control flow, may exist, but be
* unreachable. The syntax error may have prevented the parser from
* connecting all the sequences. But such sequences may contain
* resources which need freeing. Hence this approach.
*
*
This approach is safe because Load and Call instructions live in
* this processor's code space whereas as symbols and function names
* live in the heap.
*
* @param oParser the FormCalc parser.
* @param nStartAddr the start address of an instruction
* @param bFinal boolean flag indicating finality of release.
* sequence to start releasing.
*/
void release(CalcParser oParser, int nStartAddr) {
int nInstruction = nStartAddr;
Object[] oBase = oParser.moCode.moCodeBase;
while (nInstruction < mnCodePtr) {
//
// Free resources within Load instructions.
//
if (oBase[nInstruction] == gLoad) {
CalcSymbol oSym = (CalcSymbol) oBase[nInstruction + 1];
if (oSym != null) {
switch(oSym.getType()) {
case CalcSymbol.TypeVariable:
case CalcSymbol.TypeParameter:
break;
case CalcSymbol.TypeReference:
break;
default:
CalcSymbol.delete(oSym, oParser);
break;
}
}
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
}
//
// Free resources within Call instructions.
//
else if (oBase[nInstruction] == gCall) {
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
}
//
// Free resources within Form instructions.
//
else if (oBase[nInstruction] == gForm) {
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
}
//
// Ignore all other instructions.
//
else if (oBase[nInstruction] == gFor) {
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
}
else if (oBase[nInstruction] == gForeach) {
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
}
else if (oBase[nInstruction] == gIf) {
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
}
else if (oBase[nInstruction] == gIfFunc) {
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
}
else if (oBase[nInstruction] == gWhile) {
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
}
else if (oBase[nInstruction] == gFunc) {
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
}
else if (oBase[nInstruction] == gGbl) {
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
}
else if (oBase[nInstruction] == gEnter) {
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
}
else if (oBase[nInstruction] == gExit) {
oBase[nInstruction++] = gStop;
oBase[nInstruction++] = gStop;
}
else {
oBase[nInstruction++] = gStop;
}
}
}
/**
* Relocate this object's instruction space. Traverse the entire
* instruction space, preserving and relocating all Gbl and Func
* instruction sequences, and purging most everything else.
*
* @param oParser the FormCalc parser.
*/
void relocate(CalcParser oParser) {
int oDstInstr = 0;
int oSrcInstr = 0;
Object[] oBase = oParser.moCode.moCodeBase;
//
// preserve leading Enter instruction.
//
oBase[oDstInstr++] = oBase[oSrcInstr++];
oBase[oDstInstr++] = oBase[oSrcInstr++];
int nSavedDecl = 0;
while (oSrcInstr < mnCodePtr) {
//
// Relocate Gbl instruction sequences.
//
if (oBase[oSrcInstr] == gGbl) {
int nEnd = oSrcInstr
+ ((Integer) oBase[oSrcInstr + 1]).intValue();
while (oSrcInstr < nEnd)
oBase[oDstInstr++] = oBase[oSrcInstr++];
nSavedDecl++;
}
//
// Relocate Func instruction sequences.
//
else if (oBase[oSrcInstr] == gFunc) {
//
// Adjust relocated function address.
//
CalcSymbol oSym = (CalcSymbol) oBase[oSrcInstr + 1];
if (oSym != null && oSym.getType() == CalcSymbol.TypeFunction)
oSym.setAddr(oDstInstr);
int nEnd = oSrcInstr
+ ((Integer) oBase[oSrcInstr + 2]).intValue();
while (oSrcInstr < nEnd)
oBase[oDstInstr++] = oBase[oSrcInstr++];
nSavedDecl++;
}
//
// Otherwise free resources within Load instructions.
//
else if (oBase[oSrcInstr] == gLoad) {
CalcSymbol oSym = (CalcSymbol) oBase[oSrcInstr + 1];
if (oSym != null && oSym.getType() != CalcSymbol.TypeVariable
&& oSym.getType() != CalcSymbol.TypeReference
&& oSym.getType() != CalcSymbol.TypeParameter)
CalcSymbol.delete(oSym, oParser);
oSrcInstr += 2;
}
//
// And free resources within Call instructions.
//
else if (oBase[oSrcInstr] == gCall) {
oSrcInstr += 3;
}
//
// And free resources within Form instructions.
//
else if (oBase[oSrcInstr] == gForm) {
oSrcInstr += 3;
}
//
// And ignore all other instructions.
//
else {
oSrcInstr++;
}
}
//
// Add trailing Exit instruction.
//
mnProgEnd = oDstInstr;
oBase[oDstInstr++] = gExit;
oBase[oDstInstr++] = Integer.valueOf(1);
//
// Clear out anything remaining.
//
while (oDstInstr < mnCodePtr)
oBase[oDstInstr++] = gStop;
mnCodePtr = mnProgEnd + 2;
//
// Record whether we've relocated something.
//
oParser.mnSavedDecl += (nSavedDecl > 0) ? 1 : 0;
}
/**
* Set or clear a debug break-point.
* It sets or clears a flag in the moDebugLineNo array, which
* is an array of line numbers parallel to the moCodeBase array.
*
* @param nLine line of source code.
* @param bSet true to set a break-point, false to clear it.
* @return true if successful.
*/
public boolean debugBreakPoint(int nScriptID, int nLine, boolean bSet) {
return false;
// Javaport: Not required!
// if (moDebugLineNo == null)
// return false;
// boolean bRC = false;
// int nCodeSize = mnCodePtr;
// for (int i = 0; i < nCodeSize; i++) {
// if (moDebugLineNo[i].mnScriptID == nScriptID) {
// // func basically means "jump over this
// // function declaration". Don't set a break-point on
// // it, because the intent would be to stop inside the
// // function, not when simply passing by a cached function.
// if (moCodeBase[i] == func)
// continue;
// if (moDebugLineNo[i].mnLineNo == nLine) {
// bRC = true;
// moDebugLineNo[i].bBreakPointSet = bSet;
// }
// else if (moDebugLineNo[i].mnLineNo > (int) nLine) {
// break;
// }
// }
// }
// return bRC;
}
/**
* Set internal state to enable either step-over, step-into
* or step-out. The debug-mode execution loop calls the
* debug host's Stopped method when mnDebugStopAtStackDepth
* is greater than or equal to the current stack depth.
*
* @param oParser the FormCalc parser.
* @param eCmd a value of the CalcDebugCommand enum
* representing step-over, step-into or step-out.
* @return true if successful.
*/
public boolean debugCommand(CalcParser oParser, int eCmd) {
// Javaport: Not required!
// int nStackDepth = (int)oParser.moFrame.getDepth();
// if (eCmd == DebugHost.STEP_OVER) {
// mnDebugStopAtStackDepth = nStackDepth;
// return true;
// }
// else if (eCmd == DebugHost.STEP_INTO) {
// mnDebugStopAtStackDepth = nStackDepth + 1;
// return true;
// }
// else if (eCmd == DebugHost.STEP_OUT) {
// mnDebugStopAtStackDepth = nStackDepth - 1;
// return true;
// }
// else if (eCmd == DebugHost.STOP) {
// oParser.mbInterrupted = true;
// return true;
// }
return false;
}
/**
* Stop instruction.
*
* @param oParser the FormCalc parser.
*/
static void Stop(CalcParser oParser) {
// empty
}
/**
* Or instruction.
*
*
A single word instruction. Pop two operands off the stack, or
* their values into a resulting operand that's pushed back onto
* the stack.
*
* @param oParser the FormCalc parser.
*/
static void Or(CalcParser oParser) {
Binary(oParser, gOr);
}
/**
* And instruction.
*
*
A single word instruction. Pop two operands off the stack, and
* their values into a resulting operand that's pushed back onto
* the stack.
*
* @param oParser the FormCalc parser.
*/
static void And(CalcParser oParser) {
Binary(oParser, gAnd);
}
/**
* Eq instruction.
*
*
A single word instruction. Pop two operands off the stack, compare
* their values for equality into a resulting operand that's pushed
* back onto the stack.
*
* @param oParser the FormCalc parser.
*/
static void Eq(CalcParser oParser) {
Relational(oParser, gEq);
}
/**
* Ne instruction.
*
*
A single word instruction. Pop two operands off the stack, compare
* their values for inequality into a resulting operand that's pushed
* back onto the stack.
*
* @param oParser the FormCalc parser.
*/
static void Ne(CalcParser oParser) {
Relational(oParser, gNe);
}
/**
* Gt instruction.
*
*
A single word instruction. Pop two operands off the stack, compare
* their values for greater than into a resulting operand that's pushed
* back onto the stack.
*
* @param oParser the FormCalc parser.
*/
static void Gt(CalcParser oParser) {
Relational(oParser, gGt);
}
/**
* Ge instruction.
*
*
A single word instruction. Pop two operands off the stack, compare
* their values for greater than or equality into a resulting operand
* that's pushed back onto the stack.
*
* @param oParser the FormCalc parser.
*/
static void Ge(CalcParser oParser) {
Relational(oParser, gGe);
}
/**
* Lt instruction.
*
*
A single word instruction. Pop two operands off the stack, compare
* their values for lesser than into a resulting operand that's pushed
* back onto the stack.
*
* @param oParser the FormCalc parser.
*/
static void Lt(CalcParser oParser) {
Relational(oParser, gLt);
}
/**
* Le instruction.
*
*
A single word instruction. Pop two operands off the stack, compare
* their values for lesser than or equality into a resulting operand
* that's pushed back onto the stack.
*
* @param oParser the FormCalc parser.
*/
static void Le(CalcParser oParser) {
Relational(oParser, gLe);
}
/**
* Add instruction.
*
*
A single word instruction. Pop two operands off the stack, add
* their values into a resulting operand that's pushed back onto
* the stack.
*
* @param oParser the FormCalc parser.
*/
static void Add(CalcParser oParser) {
Binary(oParser, gAdd);
}
/**
* Sub instruction.
*
*
A single word instruction. Pop two operands off the stack, subtract
* their values into a resulting operand that's pushed back onto
* the stack.
*
* @param oParser the FormCalc parser.
*/
static void Sub(CalcParser oParser) {
Binary(oParser, gSub);
}
/**
* Mul instruction.
*
*
A single word instruction. Pop two operands off the stack, multiply
* their values into a resulting operand that's pushed back onto
* the stack.
*
* @param oParser the FormCalc parser.
*/
static void Mul(CalcParser oParser) {
Binary(oParser, gMul);
}
/**
* Div instruction.
*
*
A single word instruction. Pop two operands off the stack, divide
* their values into a resulting operand that's pushed back onto
* the stack.
*
* @param oParser the FormCalc parser.
*/
static void Div(CalcParser oParser) {
Binary(oParser, gDiv);
}
/*
* List instruction.
*
*
A single word instruction. Pop two operands off the stack,
* and use the value of the right symbol as the resulting operand
* that's pushed back onto the stack.
*
* @param oParser the FormCalc parser.
*/
static void List(CalcParser oParser) {
Binary(oParser, gList);
}
/**
* Uminus instruction.
*
*
A single word instruction. Pop one operand off the stack, negate
* its value into a resulting operand that's pushed back onto
* the stack.
*
* @param oParser the FormCalc parser.
*/
static void Uminus(CalcParser oParser) {
Unary(oParser, gUminus);
}
/**
* Uplus instruction.
*
*
A single word instruction. Pop one operand off the stack, affirm
* its value into a resulting operand that's pushed back onto
* the stack.
*
* @param oParser the FormCalc parser.
*/
static void Uplus(CalcParser oParser) {
Unary(oParser, gUplus);
}
/**
* Not instruction.
*
*
A single word instruction. Pop one operand off the stack, not
* its value into a resulting operand that's pushed back onto
* the stack.
*
* @param oParser the FormCalc parser.
*/
static void Not(CalcParser oParser) {
Unary(oParser, gNot);
}
/*
* Deref instruction.
*
*
A single word instruction. Pop one operand off the stack, de-reference
* its value into a resulting operand that's pushed back onto
* the stack.
*
* @param oParser the FormCalc parser.
*/
static void Deref(CalcParser oParser) {
Unary(oParser, gDeref);
}
/*
* Noop instruction.
*
*
A single word instruction. Pop one operand off the stack, reference
* its value into a resulting operand that's pushed back onto
* the stack.
*
* @param oParser the FormCalc parser.
*/
static void Noop(CalcParser oParser) {
Unary(oParser, gNoop);
}
/*
* Asgn instruction.
*
*
A single word instruction. Pop two operands off the stack,
* assign the r-value of the first symbol to the l-value of the
* second and copy the r-value of the first as a resulting operand
* that's pushed back onto the stack.
*
* @param oParser the FormCalc parser.
*/
static void Asgn(CalcParser oParser) {
Assign(oParser, gAsgn);
}
/*
* Asgn2 instruction.
*
*
A single word instruction. Pop one operand off the stack,
* assign the r-value of the second symbol to the l-value of the
* first and copy the r-value of the second as a resulting operand
* that's pushed back onto the stack.
*
* @param oParser the FormCalc parser.
*/
static void Asgn2(CalcParser oParser) {
Assign(oParser, gAsgn2);
}
/*
* Dot instruction.
*
*
A single word instruction. Pop two accessor symbols off the stack,
* concatenate their names together (as per the dot notation) into a
* resulting accessor symbol that's pushed back onto the stack.
*
* @param oParser the FormCalc parser.
*/
static void Dot(CalcParser oParser) {
Concat(oParser, gDot);
}
/*
* Dotdot instruction.
*
*
A single word instruction. Pop two accessor symbols off the stack,
* concatenate their names together (as per the dot dot notation) into a
* resulting accessor symbol that's pushed back onto the stack.
*
* @param oParser the FormCalc parser.
*/
static void Dotdot(CalcParser oParser) {
Concat(oParser, gDotdot);
}
/*
* Dothash instruction.
*
*
A single word instruction. Pop two accessor symbols off the stack,
* concatenate their names together (as per the dot hash notation) into a
* resulting accessor symbol that's pushed back onto the stack.
*
* @param oParser the FormCalc parser.
*/
static void Dothash(CalcParser oParser) {
Concat(oParser, gDothash);
}
/*
* Dotstar instruction.
*
*
A single word instruction. Pop one accessor symbol off the stack,
* append a dot star operator (as per the dot star notation) into a
* resulting accessor symbol that's pushed back onto the stack.
*
* @param oParser the FormCalc parser.
*/
static void Dotstar(CalcParser oParser) {
CalcSymbol lSym = oParser.mStack.pop(); // Pop first operand
CalcSymbol oRetSym = null;
try {
if (oParser.mbInThrow
|| oParser.mbInBreak || oParser.mbInContinue) {
oParser.getExceptions(lSym);
oRetSym = new CalcSymbol(0);
}
else {
String lContainer = (lSym.getType() == CalcSymbol.TypeReference)
? "#0" : lSym.getName();
//
// Concatenate the two accessors using the dot notation
// and push the result as an accessor CalcSymbol onto the stack.
//
StringBuilder sResult
= new StringBuilder(lContainer.length() + 2);
sResult.append(lContainer);
sResult.append('.');
sResult.append('*');
oRetSym = new CalcSymbol();
oRetSym.setName(sResult.toString());
if (lSym.getType() == CalcSymbol.TypeReference)
oRetSym.setObjValue(lSym.getObjValue());
oRetSym.setType(CalcSymbol.TypeAccessor);
}
} catch (CalcException e) {
oParser.mbInThrow = true;
oRetSym = e.getSymbol();
}
CalcSymbol.delete(lSym, oParser);
oParser.mStack.push(oRetSym);
}
/*
* Index instruction.
*
*
A single word instruction. Pop two accessor symbols off the stack,
* construct a SOM index expression from their values into a
* resulting accessor symbol that's pushed back onto the stack.
*
* @param oParser the FormCalc parser.
*/
static void Index(CalcParser oParser) {
CalcSymbol rSym = oParser.mStack.pop(); // Pop second operand
CalcSymbol lSym = oParser.mStack.pop(); // Pop first operand
CalcSymbol oRetSym = null;
try {
if (oParser.mbInThrow
|| oParser.mbInBreak || oParser.mbInContinue) {
oParser.getExceptions(rSym);
oParser.getExceptions(lSym);
oRetSym = new CalcSymbol(0);
}
else {
String lAccessor = lSym.getName();
CalcSymbol oSym = new CalcSymbol(rSym);
StringBuilder rIndex = new StringBuilder();
do {
int nIndex;
String sStr = null;
switch (oSym.getType()) {
case CalcSymbol.TypeAccessor:
if ("*".equals(oSym.getName())) {
rIndex.append('*');
}
else {
CalcSymbol oTmpSym = oParser.getOneValue(oSym);
CalcSymbol.delete(oSym, oParser);
oSym = oTmpSym;
continue;
}
break;
case CalcSymbol.TypeReference:
CalcSymbol oTmpSym = oParser.getRefValue(oSym);
CalcSymbol.delete(oSym, oParser);
oSym = oTmpSym;
continue;
case CalcSymbol.TypeDouble:
nIndex = (int) oParser.getNumeric(oSym);
rIndex.append(nIndex);
break;
case CalcSymbol.TypeString:
case CalcSymbol.TypeVariable:
sStr = oSym.getStringValue();
if (sStr != null && FormCalcUtil.strIsNumeric(sStr)) {
rIndex.append(sStr);
int nRadix = rIndex.indexOf(".");
if (nRadix >= 0)
rIndex.setLength(nRadix);
}
else {
nIndex = (int) oParser.getNumeric(oSym);
rIndex.append(nIndex);
}
break;
case CalcSymbol.TypeNull:
rIndex.append('0');
break;
case CalcSymbol.TypeError:
case CalcSymbol.TypeReturn:
CalcException e = new CalcException(oSym);
CalcSymbol.delete(oSym, oParser);
throw e;
default:
assert (true);
break;
}
break;
} while (true);
CalcSymbol.delete(oSym, oParser);
StringBuilder sResult = new StringBuilder(lAccessor.length()
+ 2 + rIndex.length());
sResult.append(lAccessor);
sResult.append('[');
sResult.append(rIndex);
sResult.append(']');
oRetSym = new CalcSymbol();
oRetSym.setName(sResult.toString());
oRetSym.setType(CalcSymbol.TypeAccessor);
}
} catch (CalcException e) {
oParser.mbInThrow = true;
oRetSym = e.getSymbol();
}
CalcSymbol.delete(rSym, oParser);
CalcSymbol.delete(lSym, oParser);
oParser.mStack.push(oRetSym);
}
/*
* Break instruction.
*
*
A single word instruction. Because BreakExpressions return
* a value (all FormCalc Expressions return a value), push a zero
* value onto the stack.
*
* @param oParser the FormCalc parser.
*/
static void Break(CalcParser oParser) {
CalcSymbol oRetSym = null;
//
// If there are any value of the stack Then make sure they are
// fully evaluated before acknowledging the break expression.
//
if (oParser.mStack.getOffset() > 0) {
CalcSymbol oSym = oParser.mStack.pop(); // Pop operand
try {
if (oParser.mbInThrow
|| oParser.mbInBreak || oParser.mbInContinue) {
oParser.getExceptions(oSym);
oRetSym = new CalcSymbol(0);
}
else {
double dVal = oParser.getNumeric(oSym);
oRetSym = new CalcSymbol(dVal);
}
} catch (CalcException e) {
oParser.mbInThrow = true;
oRetSym = e.getSymbol();
}
CalcSymbol.delete(oSym, oParser);
oParser.mStack.push(oRetSym); // Push back operand
}
//
// Push break expression value.
//
oRetSym = new CalcSymbol(0);
oParser.mbInBreak = true;
oParser.mStack.push(oRetSym);
}
/*
* Cont instruction.
*
*
A single word instruction. Because ContinueExpressions return
* a value (all FormCalc Expressions return a value), push a zero
* value onto the stack.
*
* @param oParser the FormCalc parser.
*/
static void Cont(CalcParser oParser) {
CalcSymbol oRetSym = null;
//
// If there are any value of the stack Then make sure they are
// fully evaluated before acknowledging the continue expression.
//
if (oParser.mStack.getOffset() > 0) {
CalcSymbol oSym = oParser.mStack.pop(); // Pop operand
try {
if (oParser.mbInThrow || oParser.mbInBreak
|| oParser.mbInContinue) {
oParser.getExceptions(oSym);
oRetSym = new CalcSymbol(0);
}
else {
double dVal = oParser.getNumeric(oSym);
oRetSym = new CalcSymbol(dVal);
}
} catch (CalcException e) {
oParser.mbInThrow = true;
oRetSym = e.getSymbol();
}
CalcSymbol.delete(oSym, oParser);
oParser.mStack.push(oRetSym); // Push back operand
}
//
// Push break expression value.
//
oRetSym = new CalcSymbol(0);
oParser.mbInContinue = true;
oParser.mStack.push(oRetSym);
}
/*
* Enter instruction.
*
*
A double word instruction. When entering a scope block, activate
* the scope that's is indicated by the second word of this instruction.
*
* @param oParser the FormCalc parser.
*/
static void Enter(CalcParser oParser) {
Object oScope = oParser.moCode.moCodeBase[oParser.moCode.mnProgCtr++];
int nScope = ((Integer) oScope).intValue();
if (oParser.mbInThrow
|| oParser.mbInBreak || oParser.mbInContinue)
return;
oParser.moScope.setActive(nScope);
}
/*
* Exit instruction.
*
*
A double word instruction. When exiting a scope block, de-activate
* the scope that's is indicated by the second word of this instruction.
*
* @param oParser the FormCalc parser.
*/
static void Exit(CalcParser oParser) {
Object oScope = oParser.moCode.moCodeBase[oParser.moCode.mnProgCtr++];
int nScope = ((Integer) oScope).intValue();
if (oParser.mbInThrow
|| oParser.mbInBreak || oParser.mbInContinue)
return;
oParser.moScope.clearActive(nScope);
}
/*
* Load instruction.
*
*
A double word instruction. Push the symbol that's in the
* second word of this instruction onto the stack.
*
* @param oParser the FormCalc parser.
*/
static void Load(CalcParser oParser) {
Object oObj = oParser.moCode.moCodeBase[oParser.moCode.mnProgCtr++];
CalcSymbol oSym = (CalcSymbol) oObj;
//
// For formal parameters, actually retrieve the symbol that
// corresponds to the actual parameter and push it onto the stack.
//
if (oSym.getType() == CalcSymbol.TypeParameter) {
int nStackAddr = oParser.moFrame.peek().getStackAddr();
int nArgCount = oParser.moFrame.peek().getArgCount();
int nStackIdx = oSym.getIdxValue();
oSym = oParser.mStack.peek(nStackAddr - nArgCount + nStackIdx);
oSym = new CalcSymbol(oSym);
}
// else if (oSym.getType() == CalcSymbol.TypeReference) {
// jfObjImpl::addRef(pSym->GetObjValue());
// }
oParser.mStack.push(oSym);
//
// Implement ability to interrupt execution. Currently
// this can only be actived by DebugCommand(CalcDebugStop).
//
if (oParser.mbInterrupted) {
oParser.mbInterrupted = false;
oParser.mbInThrow = true;
//
// Discard the just-pushed value, and
// replace it with the error value.
//
oSym = oParser.mStack.pop();
CalcSymbol.delete(oSym, oParser);
MsgFormat sErrMsg = new MsgFormat(ResId.FC_ERR_INTERRUPTED);
oSym = new CalcSymbol(sErrMsg.toString(), true, 0, 0);
oParser.mStack.push(oSym);
}
}
/*
* Gbl instruction.
*
*
A double word instruction that preceeds every declaration
* of global scope. Its a NOP instruction used to delineate all global
* declarations. Delineating variable/reference declarations makes
* relocating cached code easier.
*
*
The second word of this instruction is the PC-relative address of the
* instruction following the variable declaration.
*
* @param oParser the FormCalc parser.
*/
static void Gbl(CalcParser oParser) {
int nPC = oParser.moCode.mnProgCtr - 1;
//
// If past program start, i.e., not in cached code Then advance
// the program counter to start of the variable assignment, so
// as to execute the variable assignment.
//
if (oParser.moCode.mnProgCtr >= oParser.moCode.mnProgBase) {
oParser.moCode.mnProgCtr = nPC + 2;
}
//
// Else (for cached code) simply advance program counter to end
// of the function definition, so a to ignore the original variable
// assignment.
//
else {
oParser.moCode.mnProgCtr = nPC
+ ((Integer) oParser.moCode.moCodeBase[nPC + 1]).intValue();
}
}
/*
* Func instruction.
*
*
A triple word instruction that preceeds every function declaration.
*
*
The second word of this instruction is a symbol containing the
* function name.
*
*
The third word of this instruction is the PC-relative address of the
* instruction following the function declaration.
*
* @param oParser the FormCalc parser.
*/
static void Func(CalcParser oParser) {
int nPC = oParser.moCode.mnProgCtr - 1;
//
// If past program start, i.e., not in cached code Then execute
// the function body.
//
if (oParser.moCode.mnProgCtr >= oParser.moCode.mnProgBase) {
CalcSymbol oSym = (CalcSymbol) oParser.moCode.moCodeBase[nPC + 1];
CalcSymbol oRetSym = null;
try {
oParser.getExceptions(oSym);
oRetSym = new CalcSymbol(0);
} catch (CalcException e) {
oParser.mbInThrow = true;
oRetSym = e.getSymbol();
}
oParser.mStack.push(oRetSym);
}
//
// Advance program counter to end of the function definition.
//
oParser.moCode.mnProgCtr = nPC
+ ((Integer) oParser.moCode.moCodeBase[nPC + 2]).intValue();
}
/*
* Call instruction.
*
*
A triple word instruction. Pop a number of operand arguments
* off the stack given by the third word of this instruction,
* and call the user-defined or builtin function given
* by the second word of this instruction.
*
*
Push the resulting operand back onto the stack.
*
* @param oParser the FormCalc parser.
*/
static void Call(CalcParser oParser) {
int nPC = oParser.moCode.mnProgCtr - 1;
//
// Get the identifier name and the argument count.
//
String sIdent = (String) oParser.moCode.moCodeBase[nPC + 1];
int nArgs = ((Integer) oParser.moCode.moCodeBase[nPC + 2]).intValue();
//
// Upcase the identifier and check
// if its the name of a builtin function.
//
String sFunction = sIdent.toUpperCase(Locale.US);
CalcSymbol oFuncSym = oParser.mBuiltin.lookup(sFunction);
//
// If not Then check if its the name of a user-defined function.
//
if (oFuncSym == null)
oFuncSym = oParser.moData.lookup(sIdent);
//
// If identifier is name of a builtin function Then ...
//
if (oFuncSym.getType() == CalcSymbol.TypeBuiltin) {
//
// Load the arguments into an array to be passed to the builtin
// function. The function arguments are on the stack in reversed
// order, so populate the array bottom up.
//
CalcSymbol[] oArgs = new CalcSymbol[nArgs];
for (int i = nArgs; i > 0; i--)
oArgs[i - 1] = oParser.mStack.pop();
//
// If a break or continue seen, then just loop through arguments
// looking for exceptions. Be sure to push something back onto
// the stack.
//
if (oParser.mbInThrow
|| oParser.mbInBreak || oParser.mbInContinue) {
CalcSymbol oRetSym = null;
try {
for (int i = nArgs - 1; i >= 0; i--)
oParser.getExceptions(oArgs[i]);
oRetSym = new CalcSymbol(0);
} catch (CalcException e) {
oParser.mbInThrow = true;
oRetSym = e.getSymbol();
}
oParser.mStack.push(oRetSym);
}
//
// Call the builtin function. Remember it is responsible
// for pushing a result onto the stack.
//
else {
oFuncSym.getFuncValue(oParser, oArgs);
}
//
// Free all allocated resources.
//
for (int i = nArgs - 1; i >= 0; i--)
CalcSymbol.delete(oArgs[i], oParser);
oParser.moCode.mnProgCtr = nPC + 3;
}
//
// Else if is identifier is a user function Then ...
//
else if (oFuncSym.getType() == CalcSymbol.TypeFunction) {
//
// If actual parameters dont match formal parameters
// Then do insist on it!
//
if (oFuncSym.getCntValue() != (int) nArgs) {
//
// Pop actual arguments of the stack,
// ignoring exception-valued arguments.
// and free all allocated resources.
//
for (int i = nArgs; i > 0; i--) {
CalcSymbol oArg = oParser.mStack.pop();
CalcSymbol.delete(oArg, oParser);
}
//
// Push error exception onto the stack,
//
oParser.mbInThrow = true;
MsgFormat sErr = new MsgFormat(ResId.FC_ERR_PARAMETER);
CalcSymbol oRetSym = new CalcSymbol(sErr.toString(), true, 0, 0);
oParser.mStack.push(oRetSym);
oParser.moCode.mnProgCtr = nPC + 3;
}
//
// Else if a break or continue seen, then just loop through arguments
// in order, looking for exception-valued arguments. Be sure to
// push something back onto the stack.
//
else if (oParser.mbInThrow
|| oParser.mbInBreak || oParser.mbInContinue) {
//
// Load the arguments into an array. The function arguments
// are on the stack in reversed order, so populate the array
// bottom up.
//
CalcSymbol[] oArgs = new CalcSymbol[nArgs];
for (int i = nArgs; i > 0; i--)
oArgs[i - 1] = oParser.mStack.pop();
CalcSymbol oRetSym = null;
try {
for (int i = nArgs - 1; i >= 0; i--)
oParser.getExceptions(oArgs[i]);
oRetSym = new CalcSymbol(0);
} catch (CalcException e) {
oParser.mbInThrow = true;
oRetSym = e.getSymbol();
}
//
// Free all allocated resources.
//
for (int i = nArgs - 1; i >= 0; i--)
CalcSymbol.delete(oArgs[i], oParser);
oParser.mStack.push(oRetSym);
oParser.moCode.mnProgCtr = nPC + 3;
}
//
// Else ...
//
else {
//
// Initialize and push new stack frame.
//
Frame oFrame = new Frame();
oFrame.setFuncSym(oFuncSym);
oFrame.setStackAddr(oParser.mStack.getOffset() - 1);
oFrame.setReturnAddr(nPC + 3);
oParser.moFrame.push(oFrame);
//
// Invoke the user function by executing code at function's
// start address. User function will execute a Ret instruction
// which will pop arguments, pop the frame and set the program
// counter to the caller's return address.
//
oParser.moCode.execute(oParser, oFuncSym.getAddr() + 3);
}
}
}
/*
* Ret instruction.
*
*
A single word instruction. When exiting a function, the actual
* function arguments are popped of the stack, the stack frame
* is deactivated, the function's return value pushed onto the
* stack, and the program counter reset to the caller's return address.
*
* @param oParser the FormCalc parser.
*/
static void Ret(CalcParser oParser) {
Frame oFrame = oParser.moFrame.pop();
int nArgs = oFrame.getArgCount();
CalcSymbol oRetSym = oParser.mStack.pop();
for (int i = nArgs; i > 0; i--) {
CalcSymbol oArg = oParser.mStack.pop();
CalcSymbol.delete(oArg, oParser);
}
oParser.mStack.push(oRetSym);
oParser.moCode.mnProgCtr = oFrame.getReturnAddr();
}
/*
* Form instruction.
*
*
A triple word instruction. Pop a number of operand arguments
* off the stack given by the third word of this instruction,
* and call the method given by the second word of this instruction.
* Note: methods aren't actually called, just formulated.
*
*
Push the resulting operand back onto the stack.
*
* @param oParser the FormCalc parser.
*/
static void Form(CalcParser oParser) {
int nPC = oParser.moCode.mnProgCtr - 1;
//
// Get the identifier name and the argument count.
//
String sIdent = (String) oParser.moCode.moCodeBase[nPC + 1];
int nArgs = ((Integer) oParser.moCode.moCodeBase[nPC + 2]).intValue();
//
// Load the arguments into an array to be passed to the method
// call. The function arguments are on the stack in reversed
// order, so populate the array bottom up.
//
CalcSymbol[] oArgs = new CalcSymbol[nArgs];
for (int i = nArgs; i > 0; i--)
oArgs[i - 1] = oParser.mStack.pop();
//
// If a break or continue seen, then just loop through arguments
// looking for exceptions. Be sure to push something back onto
// the stack.
//
if (oParser.mbInThrow
|| oParser.mbInBreak || oParser.mbInContinue) {
CalcSymbol oRetSym = null;
try {
for (int i = nArgs - 1; i >= 0; i--)
oParser.getExceptions(oArgs[i]);
oRetSym = new CalcSymbol(0);
} catch (CalcException e) {
oParser.mbInThrow = true;
oRetSym = e.getSymbol();
}
oParser.mStack.push(oRetSym);
}
//
// Otherwise formulate the method call...
//
else {
//
// Concatenate the method arguments in an argument list.
//
Obj[] oObj = new Obj[nArgs + 1];
CalcSymbol oRetSym;
try {
StringBuilder sFormula = new StringBuilder(sIdent);
sFormula.append('(');
for (int i = 0; i < nArgs; i++) {
//
// Loop until all accessor arguments have been evaluated.
//
do {
CalcSymbol[] oSyms = null;
String sStr = null;
switch (oArgs[i].getType()) {
case CalcSymbol.TypeAccessor:
int nSyms = 0;
try {
oSyms = oParser.moScriptHost.getItemValue(
oArgs[i].getName(), oArgs[i].getObjValues());
nSyms = oSyms.length;
CalcSymbol.delete(oArgs[i], oParser);
oArgs[i] = oSyms[0];
Builtins.limitExceptionArgs(oSyms);
for (int j = nSyms - 1; j > 0; j--)
CalcSymbol.delete(oSyms[j], oParser);
} catch (CalcException e) {
for (int j = nSyms - 1; j > 0; j--)
CalcSymbol.delete(oSyms[j], oParser);
throw e;
}
continue;
case CalcSymbol.TypeReference:
sFormula.append('#');
sFormula.append((char) ('0' + i + 1));
oObj[i + 1] = oArgs[i].getObjValue();
break;
case CalcSymbol.TypeString:
case CalcSymbol.TypeVariable:
sStr = oArgs[i].getStringValue();
sFormula.append('\"');
if (sStr != null) {
String sEsc = FormCalcUtil.strToEscStr(sStr);
sFormula.append(sEsc);
}
sFormula.append('\"');
break;
case CalcSymbol.TypeDouble:
sStr = FormCalcUtil.dblToStr(
oArgs[i].getNumericValue(), 11);
StringBuilder sBuf = new StringBuilder(sStr);
FormCalcUtil.trimZeroes(sBuf);
FormCalcUtil.trimRadix(sBuf);
FormCalcUtil.trimSign(sBuf);
sFormula.append('\"');
sFormula.append(sBuf);
sFormula.append('\"');
break;
case CalcSymbol.TypeNull:
sFormula.append("%null%");
break;
case CalcSymbol.TypeReturn:
case CalcSymbol.TypeError:
default:
throw new CalcException(oArgs[i]);
}
break;
} while (true);
if (i < nArgs - 1)
sFormula.append(',');
}
sFormula.append(')');
//
// Push the resulting method call as an accessor CalcSymbol
// onto the stack.
//
oRetSym = new CalcSymbol();
oRetSym.setName(sFormula.toString());
oRetSym.setObjValues(oObj);
oRetSym.setType(CalcSymbol.TypeAccessor);
} catch (CalcException e) {
oParser.mbInThrow = true;
oRetSym = e.getSymbol();
}
oParser.mStack.push(oRetSym);
}
//
// Free all allocated resources.
//
for (int i = nArgs - 1; i >= 0; i--)
CalcSymbol.delete(oArgs[i], oParser);
oParser.moCode.mnProgCtr = nPC + 3;
}
/*
* IfFunc instruction.
*
*
A triple word instruction. Its a long story! In FormCalc, where 'if' is
* both a keyword and the name of a builtin function, keywords have precedence.
* Which means a If instruction has already been generated. This instruction
* takes the contents of an If instruction and executes a call to the builtin
* If function instead.
*
* @param oParser the FormCalc parser.
*/
static void IfFunc(CalcParser oParser) {
int nPC = oParser.moCode.mnProgCtr - 1;
int nArgs = ((Integer) oParser.moCode.moCodeBase[nPC + 1]).intValue();
//
// Do evaluate the expr list, which in this case is the first argument
// of the If function.
//
oParser.moCode.execute(oParser, nPC + 4);
//
// Load the arguments into an array to be passed to the builtin
// function. The function arguments are on the stack in reversed
// order, so populated the array bottom up.
//
CalcSymbol[] oArgs = new CalcSymbol[nArgs];
for (int i = nArgs; i > 0; i--)
oArgs[i - 1] = oParser.mStack.pop();
//
// If a break or continue seen, then just loop through arguments
// looking for exceptions. Be sure to push something back onto
// the stack.
//
if (oParser.mbInThrow
|| oParser.mbInBreak || oParser.mbInContinue) {
CalcSymbol oRetSym = null;
try {
for (int i = nArgs - 1; i >= 0; i--)
oParser.getExceptions(oArgs[i]);
oRetSym = new CalcSymbol(0);
} catch (CalcException e) {
oParser.mbInThrow = true;
oRetSym = e.getSymbol();
}
oParser.mStack.push(oRetSym);
}
//
// Call the builtin if function. It will push a
// result back onto the stack.
//
else {
BuiltinLogical.If(oParser, oArgs);
}
//
// Free all allocated resources.
//
for (int i = nArgs - 1; i >= 0; i--)
CalcSymbol.delete(oArgs[i], oParser);
//
// Advance program counter to end of if function call.
//
oParser.moCode.mnProgCtr = nPC
+ ((Integer) oParser.moCode.moCodeBase[nPC + 3]).intValue();
}
/*
* While instruction.
*
*
A triple word instruction, containing the addresses needed to execute
* a while expression.
*
*
The second word of this instruction is the PC-relative address to the
* Enter instruction, which is the start of this while expression's
* do-part.
*
*
The third word of this instruction is the PC-relative address to the
* Exit instruction, which is the end of this while expression's do-part.
*
*
The instruction following this instruction is always the start of
* this while expression's expr-part.
*
* @param oParser the FormCalc parser.
*/
static void While(CalcParser oParser) {
int nPC = oParser.moCode.mnProgCtr - 1;
if (oParser.mbInThrow
|| oParser.mbInBreak || oParser.mbInContinue) {
oParser.mStack.push(new CalcSymbol());
}
else {
//
// Execute the expr-part and pop the result off the stack.
//
oParser.moCode.execute(oParser, nPC + 3);
CalcSymbol oSym = oParser.mStack.pop();
CalcSymbol oDoSym = new CalcSymbol(0);
try {
double nVal = oParser.getNumeric(oSym);
//
// While the expr-part's result is non-zero Do ...
//
while (nVal != 0.) {
//
// Execute the do-part.
//
oParser.moCode.execute(oParser, nPC
+ ((Integer) oParser.moCode.moCodeBase[nPC + 1]).intValue());
CalcSymbol.delete(oSym, oParser);
oDoSym = oParser.mStack.pop();
String sVal = oParser.getString(oDoSym);
CalcSymbol.delete(oSym, oParser);
oDoSym = new CalcSymbol(sVal);
if (oParser.mbInBreak)
break;
else if (oParser.mbInContinue)
oParser.mbInContinue = false;
//
// Re-execute the expr-part.
//
oParser.moCode.execute(oParser, nPC + 3);
//
// Pop the result off the stack.
//
CalcSymbol.delete(oSym, oParser);
oSym = oParser.mStack.pop();
nVal = oParser.getNumeric(oSym);
}
oParser.mStack.push(oDoSym);
} catch (CalcException e) {
CalcSymbol.delete(oSym, oParser);
oParser.mStack.push(e.getSymbol());
}
CalcSymbol.delete(oSym, oParser);
if (oParser.mbInBreak)
oParser.mbInBreak = false;
}
oParser.moCode.mnProgCtr = nPC
+ ((Integer) oParser.moCode.moCodeBase[nPC + 2]).intValue();
}
/*
* If instruction.
*
*
A quad word instruction, containing the addresses needed to execute
* an if expression.
*
*
The second word of this instruction is the PC-relative address to the
* start of this if expression's then-part.
*
*
The third word of this instruction is the PC-relative address to the
* end of this if expression's else-part.
*
*
The fourth word of this instruction is the PC-relative address to the
* instruction following this if expression's endif-part.
*
*
The instruction following this instruction is always the start of
* this if expression's expr-part.
*
* @param oParser the FormCalc parser.
*/
static void If(CalcParser oParser) {
int nPC = oParser.moCode.mnProgCtr - 1;
if (oParser.mbInThrow
|| oParser.mbInBreak || oParser.mbInContinue) {
oParser.mStack.push(new CalcSymbol());
}
else {
//
// Execute the expr-part and pop the result off the stack.
//
oParser.moCode.execute(oParser, nPC + 4);
CalcSymbol oSym = oParser.mStack.pop();
try {
double nVal = oParser.getNumeric(oSym);
//
// If the result is non-zero then execute the then-part.
//
if (nVal != 0.)
oParser.moCode.execute(oParser, nPC
+ ((Integer) oParser.moCode.moCodeBase[nPC + 1]).intValue());
//
// Else if there's an else-part then execute it.
//
else if (oParser.moCode.moCodeBase[nPC + 2] != gStop)
oParser.moCode.execute(oParser, nPC
+ ((Integer) oParser.moCode.moCodeBase[nPC + 2]).intValue());
//
// Else push the value 0 on the stack.
//
else
oParser.mStack.push(new CalcSymbol(0));
} catch (CalcException e) {
oParser.mbInThrow = true;
oParser.mStack.push(e.getSymbol());
}
CalcSymbol.delete(oSym, oParser);
}
oParser.moCode.mnProgCtr = nPC
+ ((Integer) oParser.moCode.moCodeBase[nPC + 3]).intValue();
}
/*
* For instruction.
*
*
A quint word instruction, containing the addresses needed to execute
* an for expression.
*
*
The second word of this instruction is the PC-relative address to the
* start of this for expression's to-part, which, is always a Load
* instruction of the loop variable.
*
*
The third word of this instruction is the PC-relative address to the
* start of this for expression's step-part, which again, is always a Load
* instruction of the loop variable.
*
*
The fourth word of this instruction is the PC-relative address to the
* start of this for expression's do-part.
*
*
The fifth word of this instruction is the PC-relative address to the
* instruction preceding this for expression's endfor-part. It will always
* be an Exit instruction.
*
*
The instruction following this instruction is always the start of
* this for expression's init-part. It will always be an Enter
* instruction.
*
* @param oParser the FormCalc parser.
*/
static void For(CalcParser oParser) {
int nPC = oParser.moCode.mnProgCtr - 1;
if (oParser.mbInThrow
|| oParser.mbInBreak || oParser.mbInContinue) {
oParser.mStack.push(new CalcSymbol());
}
else {
//
// Execute the init-part and leave the result on the stack.
//
oParser.moCode.execute(oParser, nPC + 5);
//
// Execute the to-part and pop the result off the stack.
//
oParser.moCode.execute(oParser, nPC
+ ((Integer) oParser.moCode.moCodeBase[nPC + 1]).intValue());
CalcSymbol oSym = oParser.mStack.pop();
CalcSymbol oDoSym = new CalcSymbol(0);
try {
double nVal = oParser.getNumeric(oSym);
//
// While the to-part's result is non-zero Do ...
//
while (nVal != 0.) {
//
// Execute the do-part.
//
oParser.moCode.execute(oParser, nPC
+ ((Integer) oParser.moCode.moCodeBase[nPC + 3]).intValue());
CalcSymbol.delete(oSym, oParser);
oDoSym = oParser.mStack.pop();
String sVal = oParser.getString(oDoSym);
CalcSymbol.delete(oSym, oParser);
oDoSym = new CalcSymbol(sVal);
if (oParser.mbInBreak)
break;
else if (oParser.mbInContinue)
oParser.mbInContinue = false;
//
// Execute the step-part.
//
oParser.moCode.execute(oParser, nPC
+ ((Integer) oParser.moCode.moCodeBase[nPC + 2]).intValue());
//
// Re-execute the to-part.
//
oParser.moCode.execute(oParser, nPC
+ ((Integer) oParser.moCode.moCodeBase[nPC + 1]).intValue());
//
// Pop the result off the stack.
//
CalcSymbol.delete(oSym, oParser);
oSym = oParser.mStack.pop();
nVal = oParser.getNumeric(oSym);
}
oParser.mStack.push(oDoSym);
} catch (CalcException e) {
CalcSymbol.delete(oSym, oParser);
oParser.mbInThrow = true;
oParser.mStack.push(e.getSymbol());
}
CalcSymbol.delete(oSym, oParser);
if (oParser.mbInBreak)
oParser.mbInBreak = false;
if (oParser.mbInContinue)
oParser.mbInContinue = false;
}
oParser.moCode.mnProgCtr = nPC
+ ((Integer) oParser.moCode.moCodeBase[nPC + 4]).intValue();
}
/*
* Foreach instruction.
*
*
A quint word instruction, containing argument counts and addresses
* needed to execute an foreach expression.
*
*
The second word of this instruction is a count of the number of
* compile time arguments in this foreach expression's in-part.
*
*
The third word of this instruction is the PC-relative address to the
* start of the arguments in this foreach expression's in-part. The
* instructions comprising the arguments in this foreach's expression's
* in-part are execute once, and all resulting values are pushed onto
* the stack.
*
*
The fourth word of this instruction is the PC-relative address to the
* start of this foreach expression's do-part.
*
*
The fifth word of this instruction is the PC-relative address to the
* instruction preceding this foreach expression's endfor-part. It will
* always be an Exit instruction.
*
*
The instruction following this instruction is always the start of
* this foreach expression's init-part. It will always be an Enter
* instruction, followed by a Load instruction of the loop variable.
* The loop variable is iteratively assigned the values of the arguments
* in the in-part, that are will be residing on top of the stack.
*
* @param oParser the FormCalc parser.
*/
static void Foreach(CalcParser oParser) {
int nPC = oParser.moCode.mnProgCtr - 1;
if (oParser.mbInThrow
|| oParser.mbInBreak || oParser.mbInContinue) {
oParser.mStack.push(new CalcSymbol());
}
else {
int nInArgs = ((Integer) oParser.moCode.moCodeBase[nPC + 1]).intValue();
//
// Make room to NOLOOPARGS error symbol.
//
if (nInArgs == 0)
nInArgs++;
CalcSymbol[] oInSym = new CalcSymbol[nInArgs];
//
// Execute the argument list once and push values onto the stack.
//
oParser.moCode.execute(oParser, nPC
+ ((Integer) oParser.moCode.moCodeBase[nPC + 2]).intValue());
//
// Pop the arguments off the stack into an array to that we can
// iterate through it in reverse order.
//
for (int i = 0; i < nInArgs; ) {
CalcSymbol oArg = oParser.mStack.pop();
//
// If non-accessor Then simply store into the array.
//
if (oArg.getType() != CalcSymbol.TypeAccessor) {
oInSym[i++] = oArg;
}
//
// Else accessor So insert all retuned values into the array.
//
else {
CalcSymbol[] oSyms = null;
try {
oSyms = oParser.moScriptHost.getItemValue(
oArg.getName(), oArg.getObjValues());
int nSyms = oSyms.length;
if (nSyms > 1) {
nInArgs += nSyms - 1;
CalcSymbol[] oNewSym = new CalcSymbol[nInArgs];
System.arraycopy(oInSym, 0, oNewSym, 0, nInArgs - nSyms + 1);
oInSym = oNewSym;
for (int j = 0; j < nSyms; j++)
oInSym[i++] = oSyms[j];
}
else {
oInSym[i++] = oSyms[0];
}
} catch (CalcException e) {
oParser.mbInThrow = true;
oInSym[i++] = e.getSymbol();
}
CalcSymbol.delete(oArg, oParser);
}
}
CalcSymbol oSym = new CalcSymbol(0);
CalcSymbol oDoSym = new CalcSymbol(0);
try {
//
// From the last argument in the array to the first Do ...
//
for (int i = nInArgs - 1; i >= 0; i--) {
oParser.mStack.push(oInSym[i]);
//
// Execute the step-part and pop the result off the stack.
//
oParser.moCode.execute(oParser, nPC + 5);
CalcSymbol.delete(oSym, oParser);
oSym = oParser.mStack.pop();
oParser.getNumeric(oSym);
//
// Execute the do-part.
//
oParser.moCode.execute(oParser, nPC
+ ((Integer) oParser.moCode.moCodeBase[nPC + 3]).intValue());
CalcSymbol.delete(oDoSym, oParser);
oDoSym = oParser.mStack.pop();
String sVal = oParser.getString(oDoSym);
CalcSymbol.delete(oDoSym, oParser);
oDoSym = new CalcSymbol(sVal);
if (oParser.mbInBreak)
break;
else if (oParser.mbInContinue)
oParser.mbInContinue = false;
}
oParser.mStack.push(oDoSym);
} catch (CalcException e) {
CalcSymbol.delete(oSym, oParser);
oParser.mbInThrow = true;
oParser.mStack.push(e.getSymbol());
}
CalcSymbol.delete(oSym, oParser);
if (oParser.mbInBreak)
oParser.mbInBreak = false;
if (oParser.mbInContinue)
oParser.mbInContinue = false;
}
oParser.moCode.mnProgCtr = nPC
+ ((Integer) oParser.moCode.moCodeBase[nPC + 4]).intValue();
}
/**
* Unary operator. A utility function to deal with most unary operators.
*
*
Pop one operands off the stack, apply unary operator on
* its values and push the resulting operand back onto the stack.
*
* @param oParser the FormCalc parser.
* @param oUnop the unary operator method.
*/
static void Unary(CalcParser oParser, Method oUnop) {
CalcSymbol oSym = oParser.mStack.pop(); // Pop operand
CalcSymbol oRetSym = new CalcSymbol();
try {
if (oParser.mbInThrow
|| oParser.mbInBreak || oParser.mbInContinue) {
oParser.getExceptions(oSym);
oRetSym = new CalcSymbol(0);
}
else {
if (oUnop == gUminus) {
double nVal = oParser.getNumeric(oSym);
if (nVal < 0) {
oRetSym = new CalcSymbol(- nVal);
}
else {
StringBuilder sVal = new StringBuilder("-");
sVal.append(FormCalcUtil.dblToStr(nVal, 11));
FormCalcUtil.trimZeroes(sVal);
FormCalcUtil.trimRadix(sVal);
oRetSym = new CalcSymbol(sVal.toString());
}
}
else if (oUnop == gUplus) {
double nVal = oParser.getNumeric(oSym);
if (nVal < 0) {
oRetSym = new CalcSymbol(nVal);
}
else {
StringBuilder sVal = new StringBuilder("+");
sVal.append(FormCalcUtil.dblToStr(nVal, 11));
FormCalcUtil.trimZeroes(sVal);
FormCalcUtil.trimRadix(sVal);
oRetSym = new CalcSymbol(sVal.toString());
}
}
else if (oUnop == gNot) {
double nVal = oParser.getNumeric(oSym);
oRetSym = new CalcSymbol((nVal == 0.) ? 1. : 0.);
}
else if (oUnop == gNoop) {
oRetSym = new CalcSymbol(oSym);
}
else if (oUnop == gDeref) {
oRetSym = new CalcSymbol(oSym);
if (oRetSym.getType() == CalcSymbol.TypeAccessor) {
CalcSymbol oTmpSym = oParser.getOneValue(oRetSym);
CalcSymbol.delete(oRetSym, oParser);
oRetSym = oTmpSym;
}
}
if (oRetSym.getType() == CalcSymbol.TypeError)
oParser.mbInThrow = true;
}
} catch (CalcException e) {
oParser.mbInThrow = true;
oRetSym = e.getSymbol();
}
CalcSymbol.delete(oSym, oParser);
oParser.mStack.push(oRetSym);
}
/**
* Binary operator. A utility function to deal with most binary operators.
*
*
Pop two operands off the stack, apply binary operator on
* their values and push the resulting operand back onto the stack.
*
* @param oParser the FormCalc parser.
* @param oBinop the binary operator method.
*/
private static void Binary(CalcParser oParser, Method oBinop) {
CalcSymbol rSym = oParser.mStack.pop(); // Pop second operand
CalcSymbol lSym = oParser.mStack.pop(); // Pop first operand
CalcSymbol oRetSym = null;
try {
if (oParser.mbInThrow
|| oParser.mbInBreak || oParser.mbInContinue) {
oParser.getExceptions(lSym);
oParser.getExceptions(rSym);
oRetSym = new CalcSymbol(0);
}
else {
if (oBinop != gList) {
int lSymType = lSym.getType();
if (lSymType == CalcSymbol.TypeAccessor) {
CalcSymbol oSym = oParser.getOneValue(lSym);
CalcSymbol.delete(lSym, oParser);
lSym = oSym;
lSymType = lSym.getType();
}
if (lSymType == CalcSymbol.TypeVariable
&& lSym.getStringValue() == null) {
lSymType = CalcSymbol.TypeNull;
}
int rSymType = rSym.getType();
if (rSymType == CalcSymbol.TypeAccessor) {
CalcSymbol oSym = oParser.getOneValue(rSym);
CalcSymbol.delete(rSym, oParser);
rSym = oSym;
rSymType = rSym.getType();
}
if (rSymType == CalcSymbol.TypeVariable
&& rSym.getStringValue() == null) {
rSymType = CalcSymbol.TypeNull;
}
if (lSymType == CalcSymbol.TypeNull
&& rSymType == CalcSymbol.TypeNull) {
oRetSym = new CalcSymbol();
}
else {
double lVal = oParser.getNumeric(lSym);
double rVal = oParser.getNumeric(rSym);
double nRetVal = Double.NaN;
if (oBinop == gOr)
nRetVal = (lVal != 0. || rVal != 0.) ? 1. : 0.;
else if (oBinop == gAnd)
nRetVal = (lVal != 0. && rVal != 0.) ? 1. : 0.;
else if (oBinop == gAdd)
nRetVal = lVal + rVal;
else if (oBinop == gSub)
nRetVal = lVal - rVal;
else if (oBinop == gMul)
nRetVal = lVal * rVal;
else if (oBinop == gDiv)
nRetVal = lVal / rVal;
oRetSym = new CalcSymbol(nRetVal);
if (oRetSym.getType() == CalcSymbol.TypeError)
oParser.mbInThrow = true;
}
}
else /* if (oBinop == gList) */ {
oParser.getExceptions(lSym);
oRetSym = oParser.getOneValue(rSym);
}
}
} catch (CalcException e) {
oParser.mbInThrow = true;
oRetSym = e.getSymbol();
}
CalcSymbol.delete(lSym, oParser);
CalcSymbol.delete(rSym, oParser);
oParser.mStack.push(oRetSym);
}
/**
* Relational operator. A utility function to deal with the relational
* operators.
*
*
Pop two operands off the stack, apply relational operator
* on their values and push the resulting operand back onto the stack.
*
* @param oParser the FormCalc parser.
* @param oRelop the relational operator method.
*/
private static void Relational(CalcParser oParser, Method oRelop) {
CalcSymbol rSym = oParser.mStack.pop(); // Pop second operand
CalcSymbol lSym = oParser.mStack.pop(); // Pop first operand
CalcSymbol oRetSym = null;
try {
if (oParser.mbInThrow
|| oParser.mbInBreak || oParser.mbInContinue) {
oParser.getExceptions(rSym);
oParser.getExceptions(lSym);
oRetSym = new CalcSymbol(0);
}
else {
double nRetVal = 0;
//
// deal with error-valued and return-valued args.
//
CalcSymbol oSymArg[] = { lSym, rSym };
Builtins.limitExceptionArgs(oSymArg);
//
// deal with accessor-valued args.
//
int lSymType = lSym.getType();
if (lSymType == CalcSymbol.TypeAccessor) {
CalcSymbol oSym = oParser.getOneValue(lSym);
CalcSymbol.delete(lSym, oParser);
lSym = oSym;
lSymType = lSym.getType();
}
int rSymType = rSym.getType();
if (rSymType == CalcSymbol.TypeAccessor) {
CalcSymbol oSym = oParser.getOneValue(rSym);
CalcSymbol.delete(rSym, oParser);
rSym = oSym;
rSymType = rSym.getType();
}
//
// Applied a modification to previous fix for Watson 2523207,
// essentially guarding it with a legacy flag, as this fix
// potentially results in form DOM changes.
//
int lActualSymType;
int rActualSymType;
if (oParser.moLegacyScripting.contains(CalcParser.LegacyVersion.V32_SCRIPTING)){
lActualSymType = lSymType;
rActualSymType = rSymType;
}
else {
lActualSymType = oParser.getActualType(lSym).getType();
rActualSymType = oParser.getActualType(rSym).getType();
}
//
// deal with reference args.
//
if ((oRelop == gEq || oRelop == gNe)
&& lSymType == CalcSymbol.TypeReference
&& rSymType == CalcSymbol.TypeReference) {
if (oRelop == gEq)
nRetVal = (lSym.getObjValue() == rSym.getObjValue())
? 1. : 0.;
else if (oRelop == gNe)
nRetVal = (lSym.getObjValue() != rSym.getObjValue())
? 1. : 0.;
}
//
// deal with numeric-valued string args.
//
else if (lSym.isNumeric() && rSym.isNumeric()) {
double rVal = oParser.getNumeric(rSym);
double lVal = oParser.getNumeric(lSym);
if (oRelop == gEq)
nRetVal = (lVal == rVal) ? 1. : 0.;
else if (oRelop == gNe)
nRetVal = (lVal != rVal) ? 1. : 0.;
else if (oRelop == gGt)
nRetVal = (lVal > rVal) ? 1. : 0.;
else if (oRelop == gGe)
nRetVal = (lVal >= rVal) ? 1. : 0.;
else if (oRelop == gLt)
nRetVal = (lVal < rVal) ? 1. : 0.;
else if (oRelop == gLe)
nRetVal = (lVal <= rVal) ? 1. : 0.;
}
//
// deal with string-valued args.
//
else if ((lSymType == CalcSymbol.TypeString
|| lSymType == CalcSymbol.TypeVariable)
&& (rSymType == CalcSymbol.TypeString
|| rSymType == CalcSymbol.TypeVariable)) {
String rStr = oParser.getString(rSym);
String lStr = oParser.getString(lSym);
Collator oCol = Collator.getInstance();
if (oRelop == gEq)
nRetVal = (oCol.compare(lStr, rStr) == 0) ? 1. : 0.;
else if (oRelop == gNe)
nRetVal = (oCol.compare(lStr, rStr) != 0) ? 1. : 0.;
else if (oRelop == gGt)
nRetVal = (oCol.compare(lStr, rStr) > 0) ? 1. : 0.;
else if (oRelop == gGe)
nRetVal = (oCol.compare(lStr, rStr) >= 0) ? 1. : 0.;
else if (oRelop == gLt)
nRetVal = (oCol.compare(lStr, rStr) < 0) ? 1. : 0.;
else if (oRelop == gLe)
nRetVal = (oCol.compare(lStr, rStr) <= 0) ? 1. : 0.;
}
//
// deal with null-valued args.
//
else if (lActualSymType == CalcSymbol.TypeNull
|| rActualSymType == CalcSymbol.TypeNull) {
oParser.getNumeric(rSym);
oParser.getNumeric(lSym);
if (oRelop == gEq)
nRetVal = (lActualSymType == rActualSymType) ? 1. : 0.;
else if (oRelop == gNe)
nRetVal = (lActualSymType != rActualSymType) ? 1. : 0.;
else if (oRelop == gGt)
nRetVal = 0.;
else if (oRelop == gGe)
nRetVal = (lActualSymType == rActualSymType) ? 1. : 0.;
else if (oRelop == gLt)
nRetVal = 0.;
else if (oRelop == gLe)
nRetVal = (lActualSymType == rActualSymType) ? 1. : 0.;
}
else {
double rVal = oParser.getNumeric(rSym);
double lVal = oParser.getNumeric(lSym);
if (oRelop == gEq)
nRetVal = (lVal == rVal) ? 1. : 0.;
else if (oRelop == gNe)
nRetVal = (lVal != rVal) ? 1. : 0.;
else if (oRelop == gGt)
nRetVal = (lVal > rVal) ? 1. : 0.;
else if (oRelop == gGe)
nRetVal = (lVal >= rVal) ? 1. : 0.;
else if (oRelop == gLt)
nRetVal = (lVal < rVal) ? 1. : 0.;
else if (oRelop == gLe)
nRetVal = (lVal <= rVal) ? 1. : 0.;
}
oRetSym = new CalcSymbol(nRetVal);
if (oRetSym.getType() == CalcSymbol.TypeError)
oParser.mbInThrow = true;
}
} catch (CalcException e) {
oParser.mbInThrow = true;
oRetSym = e.getSymbol();
}
CalcSymbol.delete(rSym, oParser);
CalcSymbol.delete(lSym, oParser);
oParser.mStack.push(oRetSym);
}
/**
* Concat operator. A utility function to deal with the dot, dot dot
* and dot hash operators.
*
*
Pop two operands off the stack, apply concatenation operator
* on their names and push the resulting operand back onto the stack.
*
* @param oParser the FormCalc parser.
* @param oCatop the concatenation operator method.
*/
private static void Concat(CalcParser oParser, Method oCatop) {
CalcSymbol rSym = oParser.mStack.pop(); // Pop second operand
CalcSymbol lSym = oParser.mStack.pop(); // Pop first operand
CalcSymbol oRetSym = null;
try {
if (oParser.mbInThrow
|| oParser.mbInBreak || oParser.mbInContinue) {
oParser.getExceptions(rSym);
oParser.getExceptions(lSym);
oRetSym = new CalcSymbol(0);
}
else {
if (rSym.getType() == CalcSymbol.TypeError) {
oParser.mbInThrow = true;
throw new CalcException(rSym);
}
else if (rSym.getType() == CalcSymbol.TypeVariable) {
oParser.mbInThrow = true;
MsgFormat sFmt = new MsgFormat(ResId.FC_ERR_VAR_USED);
sFmt.format(rSym.getName());
CalcSymbol oSym = new CalcSymbol(sFmt.toString(), true, 0, 0);
throw new CalcException(oSym);
}
else if (rSym.getType() == CalcSymbol.TypeReference) {
oParser.mbInThrow = true;
MsgFormat sFmt = new MsgFormat(ResId.FC_ERR_REF_USED);
sFmt.format(rSym.getName());
CalcSymbol oSym = new CalcSymbol(sFmt.toString(), true, 0, 0);
throw new CalcException(oSym);
}
String rContainer = rSym.getName();
String lContainer = (lSym.getType() == CalcSymbol.TypeReference)
? "#0" : lSym.getName();
//
// Concatenate the two containers using the dot notation.
//
StringBuilder sResult = new StringBuilder(lContainer.length()
+ 2 + rContainer.length());
sResult.append(lContainer);
sResult.append('.');
if (oCatop == gDotdot)
sResult.append('.');
else if (oCatop == gDothash)
sResult.append('#');
sResult.append(rContainer);
//
// Push the result as a reference or accessor CalcSymbol
// onto the stack.
//
oRetSym = new CalcSymbol();
oRetSym.setName(sResult.toString());
if (lSym.getType() == CalcSymbol.TypeAccessor) {
if (rSym.getObjs() > 1) {
Obj[] oObj = rSym.getObjValues();
oObj[0] = lSym.getObjValue();
oRetSym.setObjValues(oObj);
}
else {
oRetSym.setObjValue(lSym.getObjValue());
}
}
else if (lSym.getType() == CalcSymbol.TypeReference) {
if (rSym.getObjs() > 1) {
Obj[] oObj = rSym.getObjValues();
oObj[0] = lSym.getObjValue();
oRetSym.setObjValues(oObj);
}
else {
oRetSym.setObjValue(lSym.getObjValue());
}
}
oRetSym.setType(CalcSymbol.TypeAccessor);
}
} catch (CalcException e) {
oParser.mbInThrow = true;
oRetSym = e.getSymbol();
}
CalcSymbol.delete(rSym, oParser);
CalcSymbol.delete(lSym, oParser);
oParser.mStack.push(oRetSym);
}
/**
* Assign operator. A utility function to deal with the assignment
* operators.
*
*
Pop two operands off the stack, apply the assignment operator
* and push the resulting operand back onto the stack.
*
* @param oParser the FormCalc parser.
* @param oAsgnop the assign operator method.
*/
private static void Assign(CalcParser oParser, Method oAsgnop) {
CalcSymbol rSym = new CalcSymbol();
CalcSymbol lSym = new CalcSymbol();
if (oAsgnop == gAsgn) {
rSym = oParser.mStack.pop(); // Pop source value
lSym = oParser.mStack.pop(); // Pop target value
}
else if (oAsgnop == gAsgn2) {
lSym = oParser.mStack.pop(); // Pop target value
rSym = oParser.mStack.pop(); // Pop source value
}
CalcSymbol oRetSym = null;
if (oParser.mbInThrow
|| oParser.mbInBreak || oParser.mbInContinue) {
try {
oParser.getExceptions(rSym);
oParser.getExceptions(lSym);
oRetSym = new CalcSymbol(0);
} catch (CalcException e) {
oParser.mbInThrow = true;
oRetSym = e.getSymbol();
}
}
else {
oRetSym = new CalcSymbol(rSym);
//
// Loop until all RHS accessors have been evaluated.
//
do {
CalcSymbol oVarSym = null;
switch (oRetSym.getType()) {
case CalcSymbol.TypeAccessor:
CalcSymbol oTmpSym = oParser.getOneValue(oRetSym);
CalcSymbol.delete(oRetSym, oParser);
oRetSym = oTmpSym;
continue;
case CalcSymbol.TypeDouble:
oRetSym.setTypeToString();
break;
case CalcSymbol.TypeString:
break;
case CalcSymbol.TypeVariable:
oVarSym = oParser.moData.lookup(oRetSym);
if (oVarSym != oRetSym)
oRetSym.setStringValue(oVarSym.getStringValue());
break;
case CalcSymbol.TypeReturn:
break;
case CalcSymbol.TypeNull:
break;
case CalcSymbol.TypeError:
break;
case CalcSymbol.TypeReference:
break;
default:
assert(true);
break;
}
break;
} while (true);
switch (oRetSym.getType()) {
//
// Having dealt with these above, panic if seen again.
//
case CalcSymbol.TypeAccessor:
case CalcSymbol.TypeDouble:
assert (true);
break;
//
// Assign only non-error/non-return values
//
case CalcSymbol.TypeError:
case CalcSymbol.TypeReturn:
oParser.mbInThrow = true;
break;
case CalcSymbol.TypeVariable:
case CalcSymbol.TypeString:
case CalcSymbol.TypeNull:
if (lSym.getType() == CalcSymbol.TypeReference) {
try {
if (lSym.getObjValue() != null) {
oParser.moScriptHost.putItem(lSym.getObjValues(),
oRetSym);
}
else {
oParser.mbInThrow = true;
MsgFormat sFmt
= new MsgFormat(ResId.FC_ERR_REF_NULL);
sFmt.format(lSym.getName());
oRetSym = new CalcSymbol(sFmt.toString(), true, 0, 0);
}
} catch (CalcException e) {
CalcSymbol.delete(oRetSym, oParser);
oParser.mbInThrow = true;
oRetSym = e.getSymbol();
}
}
else if (lSym.getType() == CalcSymbol.TypeAccessor) {
if (oRetSym.getType() == CalcSymbol.TypeVariable)
oRetSym.setTypeToString();
else if (oRetSym.getType() == CalcSymbol.TypeDouble)
oRetSym.setTypeToString();
else if (oRetSym.getType() == CalcSymbol.TypeString)
oRetSym.setTypeToString();
try {
oParser.moScriptHost.putItemValue(lSym.getName(),
lSym.getObjValues(),
oRetSym);
} catch (CalcException e) {
CalcSymbol.delete(oRetSym, oParser);
oParser.mbInThrow = true;
oRetSym = e.getSymbol();
}
}
else if (lSym.getType() == CalcSymbol.TypeVariable) {
lSym.setStringValue(oRetSym.getStringValue());
}
else if (lSym.getType() == CalcSymbol.TypeError) {
CalcSymbol.delete(oRetSym, oParser);
oRetSym = lSym;
}
break;
case CalcSymbol.TypeReference:
if (lSym.getType() == CalcSymbol.TypeAccessor) {
try {
CalcSymbol oTmpSym = oParser.getRefValue(oRetSym);
CalcSymbol.delete(oRetSym, oParser);
oRetSym = oTmpSym;
oParser.moScriptHost.putItemValue(lSym.getName(),
lSym.getObjValues(),
oRetSym);
} catch (CalcException e) {
CalcSymbol.delete(oRetSym, oParser);
oParser.mbInThrow = true;
oRetSym = e.getSymbol();
}
}
else if (lSym.getType() == CalcSymbol.TypeVariable) {
lSym.setScope(0);
lSym.setStringValue(null);
lSym.setObjValue(oRetSym.getObjValue());
lSym.setType(CalcSymbol.TypeReference);
}
else if (lSym.getType() == CalcSymbol.TypeReference) {
Obj oObj = oRetSym.getObjValue();
lSym.setObjValue(oObj);
if (oObj == null) {
lSym.setScope(oParser.moScope.getScope());
lSym.setStringValue("");
lSym.setType(CalcSymbol.TypeVariable);
}
}
break;
default:
assert (true);
break;
}
}
CalcSymbol.delete(rSym, oParser);
CalcSymbol.delete(lSym, oParser);
oParser.mStack.push(oRetSym);
}
Object[] moCodeBase; // the instruction space code base.
private int mnProgCtr; // the instruction space program counter.
private int mnProgBase; // the instruction space program start base.
private int mnProgEnd; // the instruction space program end base.
private int mnCodePtr; // the instruction space code pointer.
private int mnCodeSize; // the instruction space code size.
// Javaport: Not required!
// private DebugLineInfo moDebugLineNo; // the line number to moCodeBase).
// private int mnDebugPrevLine; // last line executed
// private int mnDebugPrevStoppedAtLine; // last line stopped at.
// private int mnDebugStopAtStackDepth; // stack depth that should trigger a stop
// private int mnDebugPollCounter; // periodically poll debugger interface
}