/* * 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. */ /* * FunctionalAgent.java * Created: April 16, 2001 * By: Bo Ilic */ package org.openquark.cal.compiler; import java.io.IOException; import org.openquark.cal.internal.serialization.ModuleSerializationTags; import org.openquark.cal.internal.serialization.RecordInputStream; import org.openquark.cal.internal.serialization.RecordOutputStream; import org.openquark.cal.internal.serialization.RecordInputStream.RecordHeaderInfo; /** * Descibes a function-like entity with CAL. Functions (algebraic, foreign and primitive), class methods and data constructors * all are capable of "function-like" behavior in that they can be called, return a value, have a type etc. * * Within the GemCutter, functional agents are called Gems. * * Note that FunctionalAgent is immutable with respect to external clients. * * @author Bo Ilic */ public abstract class FunctionalAgent extends ScopedEntity { private static final int serializationSchema = 0; /** the type of the functional agent. */ private TypeExpr typeExpr; /** the explicitly specified arguments of this functional agent. Useful for UI. */ private String[] argumentNames; /** * entity form enum pattern. * Creation date: (6/4/01 1:12:46 PM) * @author Bo Ilic */ static final class Form { private static final byte FORM_TYPE_CLASS_METHOD = 1; private static final byte FORM_TYPE_DATA_CONSTRUCTOR = 2; private static final byte FORM_TYPE_FUNCTION = 3; private static final byte FORM_TYPE_PATTERNVAR = 4; private String name; private Form(String name) { this.name = name; } @Override public String toString() { return name; } /** * Write this instance of Form to the RecordOutputStream. * @param s * @throws IOException */ final void write (RecordOutputStream s) throws IOException { if (this == CLASS_METHOD) { s.writeByte(FORM_TYPE_CLASS_METHOD); } else if (this == DATA_CONSTRUCTOR) { s.writeByte(FORM_TYPE_DATA_CONSTRUCTOR); } else if (this == FUNCTION) { s.writeByte(FORM_TYPE_FUNCTION); } else if (this == PATTERNVAR) { s.writeByte(FORM_TYPE_PATTERNVAR); } else { throw new IOException("Unknown FunctionalAgent.Form instance: " + this); } } /** * Load an instance of Form from the RecordInputStream. * Read position is before the record header. * @param s * @return an instance of Form. * @throws IOException */ static final Form load (RecordInputStream s) throws IOException { int key = s.readByte(); switch (key) { case FORM_TYPE_CLASS_METHOD: return CLASS_METHOD; case FORM_TYPE_DATA_CONSTRUCTOR: return DATA_CONSTRUCTOR; case FORM_TYPE_FUNCTION: return FUNCTION; case FORM_TYPE_PATTERNVAR: return PATTERNVAR; default: throw new IOException ("Unable to resolve FunctionalAgent.Form with key: " + key); } } /** top-level or local function. */ public static final Form FUNCTION = new Form("function"); public static final Form DATA_CONSTRUCTOR = new Form("data constructor"); public static final Form CLASS_METHOD = new Form("class method"); /** argument variable of a function or lambda expression or a variable bound by a case expression. */ public static final Form PATTERNVAR = new Form("pattern bound variable"); } /** * Construct an FunctionalAgent. * * @param entityName the name of the entity * @param scope the scope of the entity * @param argumentNames the explicitly specified arguments of the entity. * Can be null for entities with no explicitly specified argument names, * or contain nulls for argument names which aren't specified. * @param typeExpr the type of the entity * @param calDocComment the CALDoc associated with this entity, or null if there is none. */ FunctionalAgent(QualifiedName entityName, Scope scope, String[] argumentNames, TypeExpr typeExpr, CALDocComment calDocComment) { super (entityName, scope, calDocComment); if (typeExpr == null) { throw new NullPointerException ("FunctionalAgent constructor: the argument 'typeExpr' cannot be null."); } this.argumentNames = argumentNames != null ? argumentNames : new String[0]; this.typeExpr = typeExpr; } /** Zero argument constructor used for serialization. */ FunctionalAgent() { } /** * Returns the kind of entity this is. "Form" is used as a synonym for "kind" or "type" * because of the overloaded meanings of the last 2 terms in the type checker! * Creation date: (6/4/01 1:49:57 PM) * @return FunctionalAgent.Form */ abstract public FunctionalAgent.Form getForm(); /** * Returns a copy of the TypeExpr of this entity. For example, if the TypeExpr held * by the entity is (a -> (Int, b)) -> (a, b) then the returned TypeExpr is * (a' -> (Int, b')) -> (a', b'). * * Creation date: (4/16/01 1:47:46 PM) * @return TypeExpr */ public final TypeExpr getTypeExpr() { return getTypeExpr(null); } /** * Returns a copy of the TypeExpr of this entity, with the nonGenericVars in the * TypeExpr copied exactly (the same object). For example, if the TypeExpr held * by the entity is (a -> (Int, b)) -> (a, b) and the nonGeneric vars is just a * then the returned TypeExpr is (a -> (Int, b')) -> (a, b'). * * Creation date: (4/16/01 1:54:13 PM) * @return TypeExpr */ TypeExpr getTypeExpr(NonGenericVars nonGenericVars) { //todoBI it would be nice if when typeCheckingDone is true, then we return //CopyEnv.freshType(typeExpr, null) instead i.e. we can then ensure that the //typeExpr of this entity will not be modified by the caller. Unfortunately, //this doesn't work at this time... //TODO Type expressions are immutable for clients. In the future, we may want //to create a public, immutable, API and an internal API for type expressions. //Doing so would allow us to return typeExpr directly rather than returning a copy, //since clients would not be able to modify the returned type exression. //Also, we would then be able to remove the caching that is done by GemEntity. return CopyEnv.freshType(typeExpr, nonGenericVars); } /** * Returns the typeExpr of the entity without making a copy. This is generally not what * you want to do! It corresponds to treating all type variables that occur in the typeExpr * as non-generic. * * @return TypeExpr */ TypeExpr getTypeExprExact() { //todoBI it would be very nice to try to eliminate this method. //See the comment in getTypeExpr. return typeExpr; } /** * Returns the number of arguments explicitly specified in the definition of this entity, * for example, as specified in the CAL source. * The number of arguments will be less than or equal to the number of actual arguments for the entity. * For example, if a function takes 5 arguments, and has 3 named arguments, then the 4th and 5th argument are unnamed. * For a data constructor, all arguments are nameable. ie. if it has 3 arguments, all 3 are nameable. * However some or all of its arguments may not have names. * @return int number of named arguments */ public final int getNArgumentNames() { return argumentNames.length; } /** * Returns the name of the given argument, explicitly specified in the definition of this entity. * @param argN int index into the named arguments. * @return unqualified name of the named argument, or null if the argument isn't named. */ public final String getArgumentName(int argN) { return argumentNames[argN]; } /** * Returns the nesting level at which the entity is defined. Top level entities are defined at * level 0. * @return int */ int getNestingLevel() { return 0; } /** * Creation date: (6/6/01 2:48:40 PM) * @return boolean returns false if the entity is in the process of being type checked * and thus subject to a change in type. */ boolean isTypeCheckingDone() { return true; } /** * Finished type checking this entity, and so its type should now only * ever be used in a copied form. * Creation date: (6/6/01 2:49:12 PM) */ void setTypeCheckingDone() { if (!isTypeCheckingDone()) { //replace the type expression with the simplest type expression in its //equivalence class. typeExpr = typeExpr.deepPrune(); } } /** * Insert the method's description here. * Creation date: (6/6/01 2:53:31 PM) * @return String */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(super.toString()); sb.append(" :: "); sb.append(typeExpr.toString()); sb.append(" ["); sb.append(getForm().toString()); sb.append("]"); sb.append(" ["); for (int i = 0, nNamedArguments = getNArgumentNames(); i < nNamedArguments; ++i) { sb.append(getArgumentName(i)); if (i < nNamedArguments - 1) { sb.append(' '); } } sb.append("]"); return sb.toString(); } /** * Implemented by concrete classes to serialize to a RecordOutputStream. * @param s * @throws IOException */ @Override abstract void write (RecordOutputStream s) throws IOException; /** * Serialize the content owned by this class to a RecordOutputStream. * @param s * @throws IOException */ @Override void writeContent (RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.FUNCTIONAL_AGENT, serializationSchema); super.writeContent(s); s.writeShortCompressed(argumentNames.length); for (int i = 0; i < argumentNames.length; ++i) { // Write out empty strings for null argument names. String argName = argumentNames[i]; s.writeUTF(argName == null ? "" : argName); } typeExpr.write(s); s.endRecord(); } /** * Read the content of an instance of an FunctionalAgent * @param s * @param mti * @param msgLogger the logger to which to log deserialization messages. * @throws IOException */ @Override void readContent (RecordInputStream s, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { // Look for record header. RecordHeaderInfo rhi = s.findRecord(ModuleSerializationTags.FUNCTIONAL_AGENT); if (rhi == null) { throw new IOException ("Unable to find FunctionalAgent record header."); } DeserializationHelper.checkSerializationSchema(rhi.getSchema(), serializationSchema, mti.getModuleName(), "FunctionalAgent", msgLogger); super.readContent(s, mti, msgLogger); int nArgs = s.readShortCompressed(); argumentNames = new String[nArgs]; for (int i = 0; i < nArgs; ++i) { // Convert empty strings to nulls. String nextString = s.readUTF(); argumentNames[i] = nextString.equals("") ? null : nextString; } typeExpr = TypeExpr.load(s, mti, msgLogger); s.skipRestOfRecord(); } }