/* * 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. */ /* * BasicOpTuple.java * Creation date: (May 1, 2002) * By: Bo Ilic */ package org.openquark.cal.internal.machine; import org.openquark.cal.compiler.Expression; import org.openquark.cal.compiler.ForeignFunctionInfo; import org.openquark.cal.compiler.QualifiedName; import org.openquark.cal.compiler.UnableToResolveForeignEntityException; import org.openquark.cal.internal.machine.primitiveops.PrimOps; import org.openquark.cal.module.Cal.Core.CAL_Prelude; import org.openquark.cal.runtime.ErrorInfo; /** * Expression tuple for basic operations. * This class is used to recognize fully-saturated calls to built-in or * foreign functions within an expression. */ public final class BasicOpTuple { /** The type of primitive op being invoked (InstructionBase.PRIMOP_* manifest). */ private final int primOp; /** The foreign function info if the basic op is in fact an external function call. Null otherwise. */ private final ForeignFunctionInfo foreignFunctionInfo; /** The information use to identify the source of an error when an exception is thrown. Only for PrimopERROR. * This may be null for the case of internally generated expressions. */ private final ErrorInfo errorInfo; /** The argument expressions. */ private final Expression[] argumentExpressions; /** The name of the operation. */ private final QualifiedName opName; //public static final BasicOpTuple emptyBasicOpTuple = new BasicOpTuple(PrimOps.PRIMOP_NOP, null, null); private BasicOpTuple(int primOp, ForeignFunctionInfo foreignFunctionInfo, ErrorInfo errorInfo, Expression [] argumentExpressions, QualifiedName opName) { this.primOp = primOp; this.foreignFunctionInfo = foreignFunctionInfo; this.errorInfo = errorInfo; this.argumentExpressions = argumentExpressions; this.opName = opName; } /** * @return primitive operator index of the basic op. * Creation date: (May 1, 2002) */ public int getPrimitiveOp () { return primOp; } /** * @return the foreign function info if the basic op is in fact an external function call. * Creation date: (May 3, 2002) */ public ForeignFunctionInfo getForeignFunctionInfo () { return foreignFunctionInfo; } /** * @return The error info if the basic op is in fact an error call. */ public ErrorInfo getErrorInfo() { return errorInfo; } /** * @return the number of expression arguments in the basic op. * Creation date: (May 1, 2002) */ public int getNArguments () { if (argumentExpressions == null) { return -1; } return argumentExpressions.length; } /** * Accessor for the argument expressions in the basic op. * @param argN zero-based expression argument index. * @return Expression the expression argument * Creation date: (May 1, 2002) */ public Expression getArgument (int argN) { return argumentExpressions [argN]; } /** * @return the qualified name of the operation */ public QualifiedName getName () { return opName; } /** * Determine if the expression is a basic operation. * If the expression matches a basic operation, * a BasicOpTuple will be populated with operator parameters and returned. * Otherwise, null is returned. * If the expression corresponds to a foreign function invocation, and the field/method/constructor could not be resolved, * a CodeGenerationException is thrown. * Creation date: (3/24/00 3:53:34 PM) * @param e Expression the expression to test * @return BasicOpTuple instantiated if the expression matched as a basic operation, null otherwise * @throws CodeGenerationException if the expression corresponds to a foreign function invocation, and the field/method/constructor could not be resolved. */ public static BasicOpTuple isBasicOp(Expression e) throws CodeGenerationException { // Look for all the elements of a basic operation // Has to match: EAp(EAp(EVar <op>) e1) e2, for a binary op // Has to match: EAp(EVar <op>) e1, for a unary op // Where e<n> are the argument expressions Expression[] appChain = appChain (e); if (appChain == null) { return null; } int nArguments = appChain.length - 1; Expression.Var var = appChain[0].asVar(); // Check that the full number of arguments are supplied. ForeignFunctionInfo foreignFunctionInfo = var.getForeignFunctionInfo(); PrimOps.PrimOpInfo info = null; info = PrimOps.fetchInfo (var.getName()); int nExpectedArguments; if (info == null && foreignFunctionInfo == null) { //not a built-in primitive or a foreign function call return null; } if (info != null) { nExpectedArguments = info.getArity (); } else if (foreignFunctionInfo != null) { try { nExpectedArguments = foreignFunctionInfo.getNArguments(); } catch (UnableToResolveForeignEntityException ex) { throw new CodeGenerationException("Failed to resolve foreign method, field, or constructor.", ex); } } else { //not a built-in primitive or a foreign function call return null; } if (nExpectedArguments != nArguments) { return null; } Expression[] argumentExpressions = new Expression [nArguments]; for (int i = 0; i < nArguments; ++i) { argumentExpressions[i] = appChain[i+1]; } ErrorInfo errorInfo = null; if (var.getErrorInfo() != null){ Expression.ErrorInfo ei = var.getErrorInfo(); errorInfo = new ErrorInfo(ei.getTopLevelFunctionName(), ei.getLine(), ei.getColumn()); } return new BasicOpTuple (info == null ? PrimOps.PRIMOP_FOREIGN_FUNCTION : info.getCode(), foreignFunctionInfo, errorInfo, argumentExpressions, var.getName()); } /** * Determine if the expression is an application of 'and' or 'or'. * If the expression matches either operation * a BasicOpTuple will be populated with operator parameters and returned. * Otherwise, null is returned. * @param e Expression the expression to test * @return BasicOpTuple instantiated if the expression matched as a basic operation, null otherwise */ public static BasicOpTuple isAndOr(Expression e) { // Has to match: EAp(EAp(EVar <op>) e1) e2, where <op> is 'and' or 'or'. Expression[] appChain = appChain (e); if (appChain == null) { return null; } int nArguments = appChain.length - 1; Expression.Var var = appChain[0].asVar(); // fixup the names for Prelude.and and Prelude.or QualifiedName qn = var.getName(); if (!qn.getModuleName().equals(CAL_Prelude.MODULE_NAME) || (!qn.getUnqualifiedName().equals("and") && !qn.getUnqualifiedName().equals("or"))) { return null; } int opCode; if (qn.getUnqualifiedName().equals("and")) { opCode = PrimOps.PRIMOP_AND; } else { opCode = PrimOps.PRIMOP_OR; } if (nArguments != 2) { return null; } Expression[] argumentExpressions = new Expression [nArguments]; for (int i = 0; i < nArguments; ++i) { argumentExpressions[i] = appChain[i+1]; } return new BasicOpTuple (opCode, null, null, argumentExpressions, qn); } /** * Convert an application chain to an array. * @param root * @return null if this is not an SC application, array of Expression otherwise. */ static private Expression[] appChain (Expression root) { if (root.asAppl() != null) { // Walk down the left branch. Expression c = root; int nArgs = 0; while (c instanceof Expression.Appl) { nArgs++; c = ((Expression.Appl)c).getE1(); } // At this point c should be an Expression.Var if (!(c instanceof Expression.Var)) { return null; } Expression[] chain = new Expression [nArgs + 1]; chain[0] = c; c = root; for (int i = nArgs; i >= 1; i--) { chain[i] = ((Expression.Appl)c).getE2(); c = ((Expression.Appl)c).getE1(); } return chain; } else if (root.asTailRecursiveCall() != null) { return appChain (root.asTailRecursiveCall().getApplForm()); } else if (root.asVar() != null) { return new Expression[]{root}; } return null; } }