/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* NPrimitiveFunc.java
* Created: July 28, 2005
* By: Raymond Cypher
*/
package org.openquark.cal.internal.machine.g;
import org.openquark.cal.compiler.QualifiedName;
import org.openquark.cal.internal.machine.g.Executor.GExecutionContext;
import org.openquark.cal.runtime.CALExecutorException;
import org.openquark.cal.runtime.CalValue;
import org.openquark.cal.runtime.MachineConfiguration;
/**
* The base class for primitive functions.
*/
public abstract class NPrimitiveFunc extends NInd {
/** Flag indicating that function tracing and breakpoints are enabled. */
private static final boolean GENERATE_DEBUG_CODE = System.getProperty(MachineConfiguration.MACHINE_DEBUG_CAPABLE_PROP) != null;
private Code unwind = new Code(Instruction.I_Unwind);
/**
* @return the arity of the function implemented.
*/
protected abstract int getArity();
/**
* @return the qualified name of the function being implemented.
*/
protected abstract QualifiedName getName();
/**
* {@inheritDoc}
*/
@Override
public int debug_getNChildren() {
if (hasIndirection()) {
return super.debug_getNChildren();
}
return 0;
}
/**
* {@inheritDoc}
*/
@Override
public CalValue debug_getChild(int childN) {
if (hasIndirection()) {
return super.debug_getChild(childN);
}
throw new IndexOutOfBoundsException();
}
/**
* {@inheritDoc}
*/
@Override
public String debug_getNodeStartText() {
if (hasIndirection()) {
return super.debug_getNodeStartText();
}
return getName().getQualifiedName();
}
/**
* {@inheritDoc}
*/
@Override
public String debug_getNodeEndText() {
if (hasIndirection()) {
return super.debug_getNodeEndText();
}
return "";
}
/**
* {@inheritDoc}
*/
@Override
public String debug_getChildPrefixText(int childN) {
if (hasIndirection()) {
return super.debug_getChildPrefixText(childN);
}
throw new IndexOutOfBoundsException();
}
/**
* The indented string representation of this function node.
* @param indent
* @return indented string.
*/
@Override
public String toString (int indent) {
StringBuilder indentSb = new StringBuilder();
for (int i =0; i < indent; ++i) {
indentSb.append (" ");
}
return indentSb.toString() + idString(0) + " <" + getName() + " / " + getArity() + ">";
}
/**
* Perform the function logic.
* @param arguments - the arguments to the function as an array of Node.
* @param executor
* @return the result of the evaluation
* @throws CALExecutorException
*/
public abstract Node doEvaluation (Node[] arguments, Executor executor) throws CALExecutorException ;
/**
* Does the evaluation and cleanup, delegating to doEvaluation for actual function logic.
* @param executor
* @throws CALExecutorException
*/
private final void evaluateInternal (Executor executor) throws CALExecutorException {
Executor.GStack stack = executor.stack;
GExecutionContext executionContext = (GExecutionContext)executor.getContext();
if (GENERATE_DEBUG_CODE && executionContext.isDebugProcessingNeeded(getName().getQualifiedName())) {
int arity = getArity();
Node args[] = new Node[arity];
// The arguments are on the top of the stack.
for (int i = 0; i < arity; ++i) {
args[i] = stack.get(stack.size() - i - 1);
}
// Let the executionContext do the debug processing.
executionContext.debugProcessing(getName().getQualifiedName(), args);
}
// Pop the arguments off the stack and put in an array.
Node[] args = new Node[getArity()];
for (int i = 0; i < getArity(); ++i) {
args[i] = stack.pop ();
}
// Pass the arguments to doEvaluation() and get the result.
Node result = doEvaluation (args, executor);
// Update the root on the top of the stack.
int updatePosition = stack.size () - 1;
stack.get (updatePosition).setIndirectionNode(result);
stack.set(updatePosition, result);
// Set the code pointer to 'unwind' and return.
executor.setIP (unwind);
}
/*
* Functions to perform state transitions
*/
/**
* Do the Eval state transition.
*/
@Override
protected final void i_eval (Executor e) throws CALExecutorException {
if (getLeafNode() != this) {
e.stack.pop ();
e.stack.push (getLeafNode());
getLeafNode().i_eval(e);
return;
}
e.pushDumpItem();
i_unwind (e);
}
@Override
protected final void i_dispatch (Executor e, int k) throws CALExecutorException {
if (getArity() == 0) {
super.i_dispatch (e, k);
} else
if (getArity() == k) {
// Tail call case.
// <f:S, G[f = FUN k C], Dispatch k:[], D>
// =>
// <S, G, C, D>
// Pop this global node off of the stack.
e.stack.pop ();
// Do the evaluation.
evaluateInternal (e);
} else
if (getArity() < k) {
// <f:n1:n2:...:nk:r:S, G[f=FUN a C], Dispatch k:[], D>
// a < k
// <n1:...:na:va:...:v(k-1):r:S, G[va = HOLE ], C, D>
// [vi = AP v(i-1) ni (a<i<k)]
// [r = AP v(k-1) nk ]
e.stack.pop ();
int si = e.stack.size() - (getArity() + 1);
Node n = e.stack.get (si);
e.stack.set (si, new NInd ());
for (int i = getArity() + 1; i < k; ++i) {
Node na = new NAp (e.stack.get (si--), n);
n = e.stack.get (si);
e.stack.set (si,na);
}
Node na = new NAp (e.stack.get (si--), n);
e.stack.get (si).setIndirectionNode (na);
e.stack.set (si, na);
// Do the evaluation.
evaluateInternal (e);
} else
if (getArity() > k) {
// Two different transitions at this point:
//
// Not enough arguments on stack for f:
// <f:n1:n2:...:nk:r:v(k+1):...:vd:[], G[f = FUN a C], Dispatch k:[], (S, C1):D>
// (k < d < a) =>
// <vd:S, G[v1 = AP f n1 ], C1, D>
// [vi = AP v(i-1) ni (1 < i < k)]
// [r = AP v(k-1) nk ]
//
// Enough arguments on stack for f:
// <f:n1:n2:...:nk:r:v(k+1):...:vd:S, G[f = FUN a C ], Dispatch k:[], D>
// [v(k+1) = AP r n(k+1) ]
// [vi = AP v(i-1) ni ((k+1) < i <=a)]
//
// (k < a) =>
// <n1:n2:...:nk:n(k+1):...:na:va:S, G[v1 = AP f n1 ], C, D>
// [vi = AP v(i-1) ni (1<i<k)]
// [r = AP v(k-1) nk ]
if (e.stack.size() < (getArity() + 2)) {
super.i_dispatch (e, k);
} else {
// System.out.println ("---------------");
// System.out.println (e.showStack (0));
// System.out.println ("---------------");
NAp vi = new NAp (e.stack.pop (), e.stack.peek());
for (int i = 2; i < k; ++i) {
vi = new NAp (vi, e.stack.get (e.stack.size() - i));
}
// root index
int ir = e.stack.size() - (k + 1);
if (k == 1) {
((NInd)e.stack.get(ir)).setIndirectionNode(vi);
} else {
NAp nr = new NAp (vi, e.stack.get (e.stack.size() - k));
((NInd)e.stack.get(ir)).setIndirectionNode(nr);
}
for (int i = k; i < getArity(); ++i) {
e.stack.set (ir, ((NAp)e.stack.get(--ir)).getN2());
}
// System.out.println ("---------------");
// System.out.println (e.showStack (0));
// System.out.println ("---------------");
// Do the evaluation.
evaluateInternal (e);
}
} else {
// default case
super.i_dispatch (e, k);
}
}
/**
* Do Unwind state transition.
*/
@Override
protected final void i_unwind (Executor e) throws CALExecutorException {
Executor.GStack stack = e.stack;
if (getLeafNode() != this) {
stack.pop ();
stack.push (getLeafNode());
//ip.setIP (ip.getIP() - 1);
getLeafNode().i_unwind (e);
return;
}
// Determine the number of necessary args.
int nArgs = getArity ();
int stackSize = stack.size ();
if (stackSize - 1 < nArgs) {
e.popDumpItemBottom ();
} else {
// Re-arrange the stack to expose the arguments.
if (nArgs > 0) {
// Pop off the NGlobal
stack.pop ();
//System.out.println ("\n" + showStack(0) + "\n");
// insert a second instance of the nth node
stackSize--;
Node argN = stack.get (stackSize - nArgs);
stack.add(stackSize - nArgs, argN);
//System.out.println ("\n" + showStack(0) + "\n");
int stackSizeMinusOne = stackSize;
//stackSize increases by one, but it is not neccessary to do this computation
//stackSize++;
for (int i = 0; i < nArgs; ++i) {
int rearrangePosition = stackSizeMinusOne - i;
NAp ap = (NAp)stack.get (rearrangePosition);
stack.set(rearrangePosition, ap.getN2());
//System.out.println ("\n" + showStack(0) + "\n");
}
((NAp)argN).clear();
}
evaluateInternal (e);
}
}
/**
* A helper function used to ensure the continued validity of the hand-written NPrimitiveFunc subclasses.
* @param executor
* @param typeClassName
* @return true if the typeClassName is a type class with only a single class method and
* no superclasses.
*/
protected static boolean isSingleMethodRootClass(Executor executor, QualifiedName typeClassName) {
return (executor.getProgram().getModule(typeClassName.getModuleName()).
getModuleTypeInfo().getTypeClass(typeClassName.getUnqualifiedName())).internal_isSingleMethodRootClass();
}
/**
* A helper function used to ensure the continued validity of the hand-written NPrimitiveFunc subclasses.
* @param executor
* @param classMethodName
* @return the index within the run-time dictionary of the given class method.
*/
protected static int classMethodDictionaryIndex(Executor executor, QualifiedName classMethodName) {
return (executor.getProgram ().getModule(classMethodName.getModuleName()).
getModuleTypeInfo().getClassMethod(classMethodName.getUnqualifiedName())).internal_getDictionaryIndex();
}
}