/* * 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. */ /* * Expression.java * Created: March 13, 2000 * By: Luke Evans */ package org.openquark.cal.compiler; import java.io.IOException; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import org.openquark.cal.compiler.ExpressionAnalyzer.Visitor; 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; import org.openquark.cal.module.Cal.Core.CAL_Prelude; /** * Warning- this class should only be used by the CAL compiler implementation. It is not part of the * external API of the CAL platform. * <p> * Expressions are generated by the compiler after static analysis, typechecking, lambda lifting, * overload resolution etc. They are an intermediate form used by machines to generate actual * executable code. * * @author LWE */ public abstract class Expression { /** * The array of possible record tags used in calls to {@link RecordInputStream#findRecord(short[])} by * the {@link #load} method. */ private static final short[] EXPRESSION_RECORD_TAGS = new short[] { ModuleSerializationTags.EXPRESSION_APPL, ModuleSerializationTags.EXPRESSION_CAST, ModuleSerializationTags.EXPRESSION_DATACONS_SELECTION, ModuleSerializationTags.EXPRESSION_ERROR_INFO, ModuleSerializationTags.EXPRESSION_LET_REC, ModuleSerializationTags.EXPRESSION_LET_NONREC, ModuleSerializationTags.EXPRESSION_LITERAL, ModuleSerializationTags.EXPRESSION_PACKCONS, ModuleSerializationTags.EXPRESSION_RECORD_CASE, ModuleSerializationTags.EXPRESSION_RECORD_EXTENSION, ModuleSerializationTags.EXPRESSION_RECORD_SELECTION, ModuleSerializationTags.EXPRESSION_SWITCH, ModuleSerializationTags.EXPRESSION_TAIL_RECURSIVE_CALL, ModuleSerializationTags.EXPRESSION_VAR, ModuleSerializationTags.EXPRESSION_RECORD_UPDATE }; /** * ErrorInfos are generated by the compiler during the process of compiling. They are used to * contain information about the position in the source code of a call to error, undefined or assert. * * ErrorInfos are immutable value objects created by the compiler from CAL source, or other * compiler input forms. They are not directly created by clients. * * @author Greg McClement */ public static final class ErrorInfo extends Expression { private static final int serializationSchema = 0; private final QualifiedName topLevelFunctionName; private final int line; private final int column; ErrorInfo(QualifiedName topLevelFunctionName, int line, int column) { if (topLevelFunctionName == null) { throw new IllegalArgumentException("Expression.ErrorInfo.ErrorInfo(QualifiedName, int, int): topLevelFunctionName may not be null."); } this.topLevelFunctionName = topLevelFunctionName; this.line = line; this.column = column; } @Override public String toString() { return "EErr " + topLevelFunctionName + ": line " + line + " column " + column; } public QualifiedName getTopLevelFunctionName(){ return topLevelFunctionName; } public int getLine(){ return line; } public int getColumn(){ return column; } /** * This is a programmatically added object so I didn't think that * the visitor would need to see it. * @param v */ @Override void walk(Visitor v) { } /** * {@inheritDoc} */ @Override public <T, R> R accept(ExpressionVisitor<T, R> visitor, T arg) { return visitor.visitErrorInfo(this, arg); } /** * Write this instance of ErrorInfo to the RecordOutputStream. * @param s * @throws IOException */ @Override final void write (RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.EXPRESSION_ERROR_INFO, serializationSchema); s.writeQualifiedName(topLevelFunctionName); s.writeIntCompressed(line); s.writeIntCompressed(column); s.endRecord (); } /** * Load an instance of ErrorInfo from a RecordInputStream * @param s * @param schema * @param mti * @param msgLogger the logger to which to log deserialization messages. * @return an instance of ErrorInfo * @throws IOException */ public static ErrorInfo load (RecordInputStream s, int schema, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { DeserializationHelper.checkSerializationSchema(schema, serializationSchema, mti.getModuleName(), "Expression.ErrorInfo", msgLogger); QualifiedName topLevelFunctionName = s.readQualifiedName(); int line = s.readIntCompressed(); int column = s.readIntCompressed(); s.skipRestOfRecord(); return new ErrorInfo (topLevelFunctionName, line, column); } } /** * A literal. Contains a literal as recognised by the parser. */ public static final class Literal extends Expression { private static final int serializationSchema = 0; private static final int LITERAL_TYPE_CHARACTER = 0; private static final int LITERAL_TYPE_BOOLEAN = 1; private static final int LITERAL_TYPE_INTEGER = 2; private static final int LITERAL_TYPE_DOUBLE = 3; private static final int LITERAL_TYPE_BYTE = 4; private static final int LITERAL_TYPE_SHORT = 5; private static final int LITERAL_TYPE_FLOAT = 6; private static final int LITERAL_TYPE_LONG = 7; private static final int LITERAL_TYPE_STRING = 8; private static final int LITERAL_TYPE_BIG_INTEGER = 9; private final Object literal; Literal(Object literal) { if (literal == null) { throw new NullPointerException("Expression.Literal constructor: the argument 'literal' cannot be null."); } this.literal = literal; } @Override public String toString() { return "ELit " + literal; } public Object getLiteral() { return literal; } @Override void walk(Visitor v) { v.enterLiteral(this); v.exitLiteral(this); } /** * {@inheritDoc} */ @Override public <T, R> R accept(ExpressionVisitor<T, R> visitor, T arg) { return visitor.visitLiteral(this, arg); } /** * Write this instance of Literal to the RecordOutputStream. * @param s * @throws IOException */ @Override final void write (RecordOutputStream s) throws IOException { s.startRecord (ModuleSerializationTags.EXPRESSION_LITERAL, serializationSchema); Class<? extends Object> valueClass = literal.getClass(); if (valueClass == java.lang.Character.class) { s.writeByte(LITERAL_TYPE_CHARACTER); s.writeChar(((Character)literal).charValue()); } else if (valueClass == java.lang.Boolean.class) { s.writeByte(LITERAL_TYPE_BOOLEAN); s.writeBoolean(((Boolean)literal).booleanValue()); } else if (valueClass == java.lang.Integer.class) { s.writeByte(LITERAL_TYPE_INTEGER); s.writeInt(((Integer)literal).intValue()); } else if (valueClass == java.lang.Double.class) { s.writeByte(LITERAL_TYPE_DOUBLE); s.writeDouble(((Double)literal).doubleValue()); } else if (valueClass == java.lang.Byte.class) { s.writeByte(LITERAL_TYPE_BYTE); s.writeByte(((Byte)literal).byteValue()); } else if (valueClass == java.lang.Short.class) { s.writeByte(LITERAL_TYPE_SHORT); s.writeShort(((Short)literal).shortValue()); } else if (valueClass == java.lang.Float.class) { s.writeByte(LITERAL_TYPE_FLOAT); s.writeFloat(((Float)literal).floatValue()); } else if (valueClass == java.lang.Long.class) { s.writeByte(LITERAL_TYPE_LONG); s.writeLong(((Long)literal).longValue()); } else if (valueClass == String.class) { s.writeByte(LITERAL_TYPE_STRING); s.writeUTF((String)literal); } else if (valueClass == BigInteger.class) { s.writeByte(LITERAL_TYPE_BIG_INTEGER); String ds = ((BigInteger)literal).toString(); s.writeUTF(ds); } else { throw new IOException ("Attemp to write unhandled literal value of type: " + literal.getClass().getName()); } s.endRecord (); } /** * Load an instance of Literal from a RecordInputStream * @param s * @param schema * @param mti * @param msgLogger the logger to which to log deserialization messages. * @return an instance of Literal * @throws IOException */ public static Literal load (RecordInputStream s, int schema, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { DeserializationHelper.checkSerializationSchema(schema, serializationSchema, mti.getModuleName(), "Expression.Literal", msgLogger); byte litType = s.readByte(); Object litVal = null; switch (litType) { case LITERAL_TYPE_CHARACTER: { char c = s.readChar(); litVal = Character.valueOf(c); break; } case LITERAL_TYPE_BOOLEAN: { boolean b = s.readBoolean(); litVal = Boolean.valueOf(b); break; } case LITERAL_TYPE_INTEGER: { int i = s.readInt(); litVal = Integer.valueOf(i); break; } case LITERAL_TYPE_DOUBLE: { double d = s.readDouble(); litVal = Double.valueOf(d); break; } case LITERAL_TYPE_BYTE: { byte b = s.readByte(); litVal = Byte.valueOf(b); break; } case LITERAL_TYPE_SHORT: { short sh = s.readShort(); litVal = Short.valueOf(sh); break; } case LITERAL_TYPE_FLOAT: { float f = s.readFloat(); litVal = Float.valueOf(f); break; } case LITERAL_TYPE_LONG: { long l = s.readLong(); litVal = Long.valueOf(l); break; } case LITERAL_TYPE_STRING: { String st = s.readUTF(); litVal = st; break; } case LITERAL_TYPE_BIG_INTEGER: { String st = s.readUTF(); litVal = (new BigInteger (st)); break; } default: { throw new IOException ("Attempt to read unhandled literal value with type tag " + litType + "."); } } s.skipRestOfRecord(); return new Literal (litVal); } } /** * A variable. Contains the unique name of the variable. Note, Expression.Var * holds both variables (in the sense of CAL syntax) and data constructors. It * is a low level class for use during code generation, and so this distinction * is blurred. */ public static final class Var extends Expression { private static final int serializationSchema = 0; private final QualifiedName name; /** * this field will be non-null if the entity corresponds to a top-level entity in the * ModuleTypeInfo. For example, for a foreign function or a data constructor. * However, for local functions or local variables, it will be null. */ private final FunctionalAgent functionalAgent; /** * Will only be non-null in the case of the Prelude.error function. */ private final ErrorInfo errorInfo; /** * Do not use this constructor for entities that correspond to top-level CAL entities * present in the ModuleTypeInfo e.g. foreign functions, data constructors, etc. * @param name */ Var(QualifiedName name) { if (name == null) { throw new NullPointerException("Expression.Var constructor: the argument 'name' cannot be null."); } String s = name.getUnqualifiedName (); if (Character.isUpperCase (s.charAt(0))) { throw new IllegalArgumentException ("Expression.Var constructor: do not call this constructor for data constructors!"); } //todoBI it would be nice to put this in, but it doesn't hold for adjuncts at the moment... // if (s.indexOf('$') == -1 && !s.equals("if")) { // //System.out.println("Expression.Var constructor- top level name without FunctionalAgent " + name); // //throw new IllegalArgumentException ("Expression.Var constructor: do not call this constructor for top-level entities!"); // } this.name = name; this.functionalAgent = null; this.errorInfo = null; } /** * Special constructor for the Prelude.error function. * @param errorInfo */ Var(ErrorInfo errorInfo) { if (errorInfo == null) { throw new NullPointerException("Expression.Var errorInfo cannot be null."); } this.name = CAL_Prelude.Functions.error; this.functionalAgent = null; this.errorInfo = errorInfo; } Var(FunctionalAgent functionalAgent) { this.name = functionalAgent.getName(); this.functionalAgent = functionalAgent; this.errorInfo = null; } /** * private constructor for use in loading serialized format. * @param name * @param functionalAgent * @param errorInfo */ private Var (QualifiedName name, FunctionalAgent functionalAgent, ErrorInfo errorInfo) { this.name = name; this.functionalAgent = functionalAgent; this.errorInfo = errorInfo; } @Override public String toString() { return "EVar \"" + name.getQualifiedName() + "\""; } public QualifiedName getName() { return name; } public FunctionalAgent getFunctionalAgent (){ return functionalAgent; } public ErrorInfo getErrorInfo(){ return errorInfo; } public ForeignFunctionInfo getForeignFunctionInfo() { if (functionalAgent != null && functionalAgent instanceof Function) { return ((Function)functionalAgent).getForeignFunctionInfo(); } return null; } public DataConstructor getDataConstructor() { if (functionalAgent != null && functionalAgent instanceof DataConstructor) { return (DataConstructor)functionalAgent; } return null; } /** * {@inheritDoc} */ @Override public <T, R> R accept(ExpressionVisitor<T, R> visitor, T arg) { return visitor.visitVar(this, arg); } @Override void walk(Visitor v) { v.enterVar(this); v.exitVar(this); } /** * Write this instance of Var to the RecordOutputStream. * @param s * @throws IOException */ @Override void write (RecordOutputStream s) throws IOException { s.startRecord (ModuleSerializationTags.EXPRESSION_VAR, serializationSchema); boolean flags[] = new boolean [3]; flags[0] = name != null; flags[1] = functionalAgent != null; flags[2] = errorInfo != null; s.writeByte(RecordOutputStream.booleanArrayToBitArray(flags)[0]); if (flags[0]) { s.writeQualifiedName(name); } if (flags[1]) { s.writeQualifiedName(functionalAgent.getName()); } if (flags[2]) { errorInfo.write(s); } s.endRecord (); } /** * Load an instance of Var from a RecordInputStream * @param s * @param schema * @param mti * @param msgLogger the logger to which to log deserialization messages. * @return an instance of Var * @throws IOException */ public static Var load (RecordInputStream s, int schema, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { DeserializationHelper.checkSerializationSchema(schema, serializationSchema, mti.getModuleName(), "Expression.Var", msgLogger); byte flagByte = s.readByte(); QualifiedName qn = null; if (RecordInputStream.booleanFromBitArray(flagByte, 0)) { qn = s.readQualifiedName(); } QualifiedName entityName = null; if (RecordInputStream.booleanFromBitArray(flagByte, 1)) { entityName = s.readQualifiedName(); } ErrorInfo ei = null; if (RecordInputStream.booleanFromBitArray(flagByte, 2)) { ei = (ErrorInfo)Expression.load (s, mti, msgLogger); } s.skipRestOfRecord(); FunctionalAgent ee = null; if (entityName != null) { ModuleTypeInfo typeInfo = mti.getModule().findModule(entityName.getModuleName()).getModuleTypeInfo(); ee = typeInfo.getFunctionalAgent(entityName.getUnqualifiedName()); if (ee == null) { throw new IOException ("Unable to find FunctionalAgent " + entityName + " while loading Expression.Var."); } } return new Var (qn, ee, ei); } } /** * An application. Applies expr1 to expr2. */ public static final class Appl extends Expression { private static final int serializationSchema = 0; private Expression expr1; private Expression expr2; Appl(Expression expr1, Expression expr2) { if (expr1 == null || expr2 == null) { throw new NullPointerException("Expression.Appl constructor: the arguments 'expr1' and 'expr2' cannot be null."); } this.expr1 = expr1; this.expr2 = expr2; } @Override public String toString() { return "EAp (" + expr1 + ") (" + expr2 + ")"; } public Expression getE1() { return expr1; } public Expression getE2() { return expr2; } void setE1 (Expression e) { if (e == null) { throw new IllegalArgumentException("Attempt to set null E1 in Expression.Appl"); } expr1 = e; } void setE2 (Expression e) { if (e == null) { throw new IllegalArgumentException("Attempt to set null E2 in Expression.Appl"); } expr2 = e; } @Override void walk(Visitor v) { v.enterAppl(this); expr1.walk (v); expr2.walk (v); v.exitAppl(this); } /** * {@inheritDoc} */ @Override public <T, R> R accept(ExpressionVisitor<T, R> visitor, T arg) { return visitor.visitApplication(this, arg); } /** * Write this instance of Appl to the RecordOutputStream. * @param s * @throws IOException */ @Override final void write (RecordOutputStream s) throws IOException { s.startRecord (ModuleSerializationTags.EXPRESSION_APPL, serializationSchema); expr1.write (s); expr2.write (s); s.endRecord (); } /** * Load an instance of Appl from a RecordInputStream * @param s * @param schema * @param mti * @param msgLogger the logger to which to log deserialization messages. * @return an instance of Appl * @throws IOException */ public static Appl load (RecordInputStream s, int schema, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { DeserializationHelper.checkSerializationSchema(schema, serializationSchema, mti.getModuleName(), "Expression.Appl", msgLogger); Expression expr1 = Expression.load (s, mti, msgLogger); Expression expr2 = Expression.load (s, mti, msgLogger); s.skipRestOfRecord (); return new Appl (expr1, expr2); } } /** * Base class for recursive or non-recursive lets. */ public abstract static class Let extends Expression { private static final int serializationSchema = 0; private Expression body; Let(Expression body) { if (body == null) { throw new NullPointerException("Expression.Let constructor: the argument 'body' cannot be null."); } this.body = body; } /** * Default constructor for use by serialization code. */ Let () {} public abstract LetDefn[] getDefns(); abstract void setDefns(LetDefn[] defns); public Expression getBody() { return body; } void setBody (Expression e) { if (e == null) { throw new IllegalArgumentException("Attempt to set null body in Expression.Let"); } body = e; } /** * Write this instance of Let to the RecordOutputStream. * @param s * @throws IOException */ void writeContent (RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.EXPRESSION_LET, serializationSchema); body.write (s); s.endRecord (); } /** * Load an instance of ErrorInfo from a RecordInputStream * @param s * @param mti * @param msgLogger the logger to which to log deserialization messages. * @throws IOException */ private void readContent (RecordInputStream s, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { RecordHeaderInfo rhi = s.findRecord(ModuleSerializationTags.EXPRESSION_LET); if (rhi == null) { throw new IOException("Unable to find Expression.Let record header."); } DeserializationHelper.checkSerializationSchema(rhi.getSchema(), serializationSchema, mti.getModuleName(), "Expression.Let", msgLogger); Expression expr = Expression.load (s, mti, msgLogger); this.body = expr; s.skipRestOfRecord(); } public static final class LetDefn { private static final int serializationSchema = 0; private final String var; private Expression expr; /** * The type of the variable being defined in this let definition. */ private TypeExpr varType; int useCount; LetDefn(String var, Expression expr, TypeExpr varType) { if (var == null || expr == null || varType == null) { throw new NullPointerException("Expression.Let.LetDefn constructor: the arguments 'var' and 'expr' cannot be null."); } this.var = var; this.expr = expr; this.varType = varType; } public String getVar() { return var; } public Expression getExpr() { return expr; } public TypeExpr getVarType() { return varType; } void setExpr (Expression e) { if (e == null) { throw new IllegalArgumentException("Attempt to set null expr in Expression.Let.LetDefn"); } expr = e; } public int getUseCount () { return useCount; } void incrementUseCount () { useCount++; } void setUseCount (int i) { this.useCount = i; } @Override public String toString() { return var + ":" + expr; } /** * * @param visitor * @param arg * @return the result of visiting this LetDefn */ public <T, R> R accept(ExpressionVisitor<T, R> visitor, T arg) { return visitor.visitLetDefn(this, arg); } /** * Write this instance of LetDefn to the RecordOutputStream. * @param s * @throws IOException */ final void write (RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.EXPRESSION_LET_DEFN, serializationSchema); s.writeUTF (var); s.writeInt(useCount); expr.write (s); varType.write(s); s.endRecord (); } /** * Load an instance of ErrorInfo from a RecordInputStream * @param s * @param mti * @param msgLogger * @return an instance of ErrorInfo * @throws IOException */ public static LetDefn load (RecordInputStream s, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { RecordHeaderInfo rhi = s.findRecord(ModuleSerializationTags.EXPRESSION_LET_DEFN); if (rhi == null) { throw new IOException ("Unable to find record header for LetDefn"); } DeserializationHelper.checkSerializationSchema(rhi.getSchema(), serializationSchema, mti.getModuleName(), "Expression.Let.LetDefn", msgLogger); String var = s.readUTF (); int useCount = s.readInt (); Expression expr = Expression.load(s, mti, msgLogger); TypeExpr varType = TypeExpr.load(s, mti, msgLogger); s.skipRestOfRecord(); LetDefn letDef = new LetDefn (var, expr, varType); letDef.setUseCount (useCount); return letDef; } } } /** * Used to represent recursive let bindings. The compiler guarantees that all the definitions * appearing in the block are mutually recursive and cannot be split into subgroups. * * @author Bo Ilic */ public static final class LetRec extends Let { private static final int serializationSchema = 0; private LetDefn[] defns; LetRec(LetDefn[] defns, Expression body) { super(body); if (defns == null) { throw new NullPointerException(); } if (defns.length == 0) { throw new IllegalArgumentException(); } this.defns = defns; } /** * Default constructor for use by serialization code. */ LetRec () {} @Override public LetDefn[] getDefns() { return defns; } @Override void setDefns(LetDefn[] defns) { if (defns == null) { throw new IllegalArgumentException("Attempt to set null defns in Expression.LetRec"); } this.defns = defns; } @Override public String toString() { return "ELetRec (" + defnsString() + ") (" + super.body + ")"; } private String defnsString() { StringBuilder sb = new StringBuilder(); // For each definition, add some description int defnsLength = defns.length; for (int i = 0; i < defnsLength; i++) { LetDefn ld = defns[i]; if (i > 0) { sb.append(','); } sb.append(ld); } return sb.toString(); } @Override void walk(Visitor v) { v.enterLetRec(this); for (int i = 0; i < defns.length; ++i) { v.enterLetRecDef(defns[i]); defns[i].getExpr().walk(v); v.exitLetRecDef(defns[i]); } getBody().walk(v); v.exitLetRec(this); } /** * {@inheritDoc} */ @Override public <T, R> R accept(ExpressionVisitor<T, R> visitor, T arg) { return visitor.visitLetRec(this, arg); } /** * Write this instance of LetRec to the RecordOutputStream. * @param s * @throws IOException */ @Override final void write (RecordOutputStream s) throws IOException { s.startRecord (ModuleSerializationTags.EXPRESSION_LET_REC, serializationSchema); super.writeContent (s); s.writeIntCompressed(defns.length); for (int i = 0; i < defns.length; ++i) { defns[i].write (s); } s.endRecord (); } /** * Load an instance of LetRec from a RecordInputStream * @param s * @param schema * @param mti * @param msgLogger the logger to which to log deserialization messages. * @return an instance of LetRec * @throws IOException */ public static LetRec load (RecordInputStream s, int schema, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { DeserializationHelper.checkSerializationSchema(schema, serializationSchema, mti.getModuleName(), "Expression.LetRec", msgLogger); LetRec lnr = new LetRec (); lnr.readContent (s, mti, msgLogger); return lnr; } private void readContent (RecordInputStream s, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { super.readContent (s, mti, msgLogger); int nVars = s.readIntCompressed(); defns = new LetDefn[nVars]; for (int i = 0; i < nVars; ++i) { defns[i] = LetDefn.load(s, mti, msgLogger); } s.skipRestOfRecord(); } } /** * Used to represent non-recursive let definitions. If the definition is of the form "let x = e1 in e2" then * the compiler guarantees that x does not occur in e1. Also this is a *minimal* grouping so there will only * ever be 1 definition in a LetNonRec. * * @author Bo Ilic */ public static final class LetNonRec extends Let { private static final int serializationSchema = 0; private LetDefn defn; LetNonRec (LetDefn defn, Expression body) { super(body); if (defn == null) { throw new NullPointerException(); } this.defn = defn; } private LetNonRec () {} @Override public LetDefn[] getDefns() { return new LetDefn[] {defn}; } @Override void setDefns(LetDefn[] defns) { if (defns == null || defns[0] == null) { throw new IllegalArgumentException("Attempt to set null defn in Expression.LetNonRec"); } defn = defns[0]; } public LetDefn getDefn() { return defn; } @Override public String toString() { return "ELetNonRec (" + defn + ") (" + super.body + ")"; } @Override void walk(Visitor v) { v.enterLetNonRec(this); v.enterLetNonRecDef(defn); defn.getExpr().walk(v); v.exitLetNonRecDef(defn); getBody().walk(v); v.exitLetNonRec(this); } /** * {@inheritDoc} */ @Override public <T, R> R accept(ExpressionVisitor<T, R> visitor, T arg) { return visitor.visitLetNonRec(this, arg); } /** * Write this instance of LetNonRec to the RecordOutputStream. * @param s * @throws IOException */ @Override final void write (RecordOutputStream s) throws IOException { s.startRecord (ModuleSerializationTags.EXPRESSION_LET_NONREC, serializationSchema); super.writeContent (s); defn.write (s); s.endRecord (); } /** * Load an instance of LetNonRec from a RecordInputStream * @param s * @param schema * @param mti * @param msgLogger the logger to which to log deserialization messages. * @return an instance of LetNonRec * @throws IOException */ public static LetNonRec load (RecordInputStream s, int schema, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { DeserializationHelper.checkSerializationSchema(schema, serializationSchema, mti.getModuleName(), "Expression.LetNonRec", msgLogger); LetNonRec lnr = new LetNonRec (); lnr.readContent (s, mti, msgLogger); return lnr; } private void readContent (RecordInputStream s, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { super.readContent (s, mti, msgLogger); defn = LetDefn.load (s, mti, msgLogger); s.skipRestOfRecord(); } } /** * A constructor. * * Note: this does not correspond to the Cons nodes as used in the compiler (which are used * for any symbol in CAL which starts with an upper case letter such as a class method name, module name, data * constructor name. Rather, they are used for creating special functions during code generation, one for each * data constructor. */ public static final class PackCons extends Expression { private static final int serializationSchema = 0; private final DataConstructor dataConstructor; PackCons(DataConstructor dataConstructor) { if (dataConstructor == null) { throw new NullPointerException("Expression.Cons constructor: the argument 'dataConstructor' cannot be null."); } this.dataConstructor = dataConstructor; //System.out.println(toString()); } @Override public String toString() { return "ECons (" + dataConstructor + ")"; } public DataConstructor getDataConstructor() { return dataConstructor; } @Override void walk(Visitor v) { v.enterPackCons(this); v.exitPackCons(this); } /** * {@inheritDoc} */ @Override public <T, R> R accept(ExpressionVisitor<T, R> visitor, T arg) { return visitor.visitPackCons(this, arg); } /** * Write this instance of PackCons to the RecordOutputStream. * @param s * @throws IOException */ @Override final void write (RecordOutputStream s) throws IOException { s.startRecord (ModuleSerializationTags.EXPRESSION_PACKCONS, serializationSchema); s.writeQualifiedName(dataConstructor.getName()); s.endRecord (); } /** * Load an instance of PackCons from a RecordInputStream * @param s * @param schema * @param mti * @param msgLogger the logger to which to log deserialization messages. * @return an instance of PackCons * @throws IOException */ public static PackCons load (RecordInputStream s, int schema, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { DeserializationHelper.checkSerializationSchema(schema, serializationSchema, mti.getModuleName(), "Expression.PackCons", msgLogger); QualifiedName qn = s.readQualifiedName(); s.skipRestOfRecord(); DataConstructor dc = mti.getReachableDataConstructor(qn); if (dc == null) { throw new IOException ("Unable to find data constructor " + qn + " while loading Expression.PackCons."); } return new PackCons (dc); } } /** * A switch. Switches to one of alts depending on swExpr. */ public static final class Switch extends Expression { private static final int serializationSchema = 0; private Expression swExpr; private final SwitchAlt[] alts; private final ErrorInfo errorInfo; Switch(Expression swExpr, SwitchAlt[] alts, ErrorInfo errorInfo) { if (swExpr == null || alts == null) { throw new NullPointerException("Expression.Switch constructor: the arguments 'swExpr' and 'alts' cannot be null."); } this.swExpr = swExpr; this.alts = alts; this.errorInfo = errorInfo; } /** * @return The error information that identifies the position in the source of the expression. Maybe null. */ public ErrorInfo getErrorInfo(){ return errorInfo; } @Override public String toString() { StringBuilder out = new StringBuilder("ESwitch ("); out.append(swExpr.toString()); out.append(", ["); for (int i = 0; i < alts.length; ++i) { if (i > 0) { out.append(", "); } out.append(alts[i].toString()); } out.append("])"); return out.toString(); //return "ESwitch (" + swExpr + "," + alts + ")"; } public Expression getSwitchExpr() { return swExpr; } void setSwitchExpr (Expression e) { if (e == null) { throw new IllegalArgumentException("Attempt to set null swExpr in Expression.Switch"); } swExpr = e; } public SwitchAlt[] getAlts() { return alts; } public SwitchAlt getAlt(int i) { return alts[i]; } public int getNAlts() { return alts.length; } public boolean hasDefaultAlt() { for (int i = 0; i < alts.length; ++i) { if (alts[i].isDefaultAlt()) { return true; } } return false; } public static abstract class SwitchAlt { private static final int serializationSchema = 0; private static final byte ALT_TAG_TYPE_INTEGER = 0; private static final byte ALT_TAG_TYPE_BOOLEAN = 1; private static final byte ALT_TAG_TYPE_CHARACTER = 2; private static final byte ALT_TAG_TYPE_DATACONSTRUCTOR = 3; private static final byte ALT_TAG_TYPE_STRING = 4; /** the altTag to use to represent the wildcard pattern match. */ public static final String WILDCARD_TAG = "_"; /**the varName to use to represent the wildcard pattern match. */ public static final String WILDCARD_VAR = "$_"; /** * The array of possible record tags used in calls to {@link RecordInputStream#findRecord(short[])} by * the {@link #load} method. */ private static final short[] SWITCH_ALT_RECORD_TAGS = new short[]{ ModuleSerializationTags.EXPRESSION_SWITCHALT_MATCHING, ModuleSerializationTags.EXPRESSION_SWITCHALT_POSITIONAL }; /** The tag for the alt. * If the alt represents a data constructor which takes arguments, the tag must be of type DataConstructor. */ private List<Object> altTags; /** The expression for this alt. */ private Expression expr; /** * Write this instance of SwitchAlt to the RecordOutputStream. * @param s * @throws IOException */ abstract void write (RecordOutputStream s) throws IOException; /** * Write this instance of SwitchAlt to the RecordOutputStream. * @param s * @throws IOException */ void writeContent (RecordOutputStream s) throws IOException { s.startRecord (ModuleSerializationTags.EXPRESSION_SWITCHALT, serializationSchema); s.writeIntCompressed(altTags.size()); for (int i = 0; i < altTags.size(); ++i) { Object tag = altTags.get(i); if (tag instanceof Integer) { s.writeByte(ALT_TAG_TYPE_INTEGER); s.writeInt(((Integer)tag).intValue()); } else if (tag instanceof Boolean) { s.writeByte(ALT_TAG_TYPE_BOOLEAN); s.writeBoolean(((Boolean)tag).booleanValue()); } else if (tag instanceof Character) { s.writeByte(ALT_TAG_TYPE_CHARACTER); s.writeChar(((Character)tag).charValue()); } else if (tag instanceof DataConstructor) { s.writeByte(ALT_TAG_TYPE_DATACONSTRUCTOR); s.writeQualifiedName(((DataConstructor)tag).getName()); } else if (tag instanceof String) { s.writeByte(ALT_TAG_TYPE_STRING); s.writeUTF((String)tag); } else { throw new IOException ("Unrecognized SwitchAlt tag type: " + tag.getClass() + "."); } } expr.write (s); s.endRecord (); } /** * Load an instance of SwitchAlt from a RecordInputStream * @param s * @param mti * @param msgLogger the logger to which to log deserialization messages. * @return an instance of SwitchAlt * @throws IOException */ public static SwitchAlt load (RecordInputStream s, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { RecordHeaderInfo rhi = s.findRecord(SWITCH_ALT_RECORD_TAGS); if (rhi == null) { throw new IOException ("Unable to find SwitchAlt record header."); } DeserializationHelper.checkSerializationSchema(rhi.getSchema(), serializationSchema, mti.getModuleName(), "Expression.Switch.SwitchAlt", msgLogger); if (rhi.getRecordTag() == ModuleSerializationTags.EXPRESSION_SWITCHALT_MATCHING) { return SwitchAlt.Matching.load(s, rhi.getSchema(), mti, msgLogger); } else if (rhi.getRecordTag() == ModuleSerializationTags.EXPRESSION_SWITCHALT_POSITIONAL) { return SwitchAlt.Positional.load(s, rhi.getSchema(), mti, msgLogger); } else { throw new IOException ("Unhandled SwitchAlt record tag " + rhi.getRecordTag() + "."); } } private void readContent (RecordInputStream s, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { RecordHeaderInfo rhi = s.findRecord (ModuleSerializationTags.EXPRESSION_SWITCHALT); if (rhi == null) { throw new IOException ("Unable to find SwitchAltRecordHeader."); } DeserializationHelper.checkSerializationSchema(rhi.getSchema(), serializationSchema, mti.getModuleName(), "Expression.Switch.SwitchAlt", msgLogger); int nTags = s.readIntCompressed(); altTags = new ArrayList<Object> (); for (int i = 0; i < nTags; ++i) { byte tagType = s.readByte(); switch (tagType) { case ALT_TAG_TYPE_INTEGER: altTags.add (Integer.valueOf(s.readInt())); break; case ALT_TAG_TYPE_BOOLEAN: altTags.add (Boolean.valueOf(s.readBoolean())); break; case ALT_TAG_TYPE_CHARACTER: altTags.add (Character.valueOf(s.readChar())); break; case ALT_TAG_TYPE_DATACONSTRUCTOR: { QualifiedName qn = s.readQualifiedName(); DataConstructor dc = mti.getReachableDataConstructor(qn); if (dc == null) { throw new IOException ("Unable to find DataConstructor " + qn + " while loading SwitchAlt."); } altTags.add (dc); break; } case ALT_TAG_TYPE_STRING: altTags.add (s.readUTF()); break; default: throw new IOException ("Unhandled tag type " + tagType + " encountered while loading Switch.SwitchAlt."); } } expr = Expression.load (s, mti, msgLogger); s.skipRestOfRecord(); } /** * A switch alt where the variables are specified by position. * @author Edward Lam */ public static final class Positional extends SwitchAlt { private static final int serializationSchema = 0; /** (Integer->String) map from position to var name for all used alt vars.*/ private SortedMap<Integer, String> positionToVarNameMap; Positional(Object altTag, SortedMap<Integer, String> positionToVarNameMap, Expression expr) { this(Collections.singletonList(altTag), positionToVarNameMap, expr); } Positional(List<Object> altTags, SortedMap<Integer, String> positionToVarNameMap, Expression expr) { super (altTags, expr); if (positionToVarNameMap == null) { throw new NullPointerException ("Expression.Switch.SwitchAlt.Matching constructor: the argument 'fieldNameToVarNameMap' cannot be null."); } this.positionToVarNameMap = positionToVarNameMap; } /** * Default constructor for use by serialization code. */ private Positional () {} public SortedMap<Integer, String> getPositionToVarNameMap() { return Collections.unmodifiableSortedMap(positionToVarNameMap); } /** * {@inheritDoc} */ @Override public boolean hasVars() { return !positionToVarNameMap.isEmpty(); } /** * @return the names of the alt variables used in the associated expr. */ @Override public String[] getVarNames() { String[] names = new String[positionToVarNameMap.size()]; int i = 0; for (final Map.Entry<Integer, String> entry : positionToVarNameMap.entrySet()) { names[i++] = entry.getValue(); } return names; } /** * {@inheritDoc} */ @Override public String toArgumentString() { StringBuilder varsDisplay = new StringBuilder(); boolean firstIteration = true; if (positionToVarNameMap.size() > 0){ Integer lastInteger = positionToVarNameMap.lastKey(); for (int i = 0, lastInt = lastInteger.intValue(); i < lastInt + 1; i++) { String varName = positionToVarNameMap.get(Integer.valueOf(i)); if (varName == null) { varName = "_"; } if (firstIteration) { firstIteration = false; } else { varsDisplay.append(" "); } varsDisplay.append(varName); } } return varsDisplay.toString(); } /** * * @param visitor * @param arg * @return the result of visiting this LetDefn */ @Override public <T, R> R accept(ExpressionVisitor<T, R> visitor, T arg) { return visitor.visitSwitchAlt_Positional(this, arg); } /** * Write this instance of SwitchAlt.Positional to the RecordOutputStream. * @param s * @throws IOException */ @Override void write (RecordOutputStream s) throws IOException { s.startRecord (ModuleSerializationTags.EXPRESSION_SWITCHALT_POSITIONAL, serializationSchema); super.writeContent(s); s.writeIntCompressed(positionToVarNameMap.size ()); for (final Map.Entry<Integer, String> entry : positionToVarNameMap.entrySet()){ Integer i = entry.getKey(); String varName = entry.getValue(); s.writeIntCompressed(i.intValue()); s.writeUTF(varName); } s.endRecord (); } /** * Load an instance of SwitchAlt.Positional from a RecordInputStream * @param s * @param schema * @param mti * @param msgLogger the logger to which to log deserialization messages. * @return an instance of SwitchAlt.Positional * @throws IOException */ public static Positional load (RecordInputStream s, int schema, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { DeserializationHelper.checkSerializationSchema(schema, serializationSchema, mti.getModuleName(), "Expression.Switch.SwitchAlt.Positional", msgLogger); Positional m = new Positional (); m.readContent (s, mti, msgLogger); return m; } private void readContent (RecordInputStream s, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { super.readContent (s, mti, msgLogger); int nFields = s.readIntCompressed(); positionToVarNameMap = new TreeMap<Integer, String>(); for (int i = 0; i < nFields; ++i) { int ord = s.readIntCompressed(); String varName = s.readUTF(); positionToVarNameMap.put (Integer.valueOf(ord), varName); } s.skipRestOfRecord(); } } /** * A switch alt where the variables are specified by field name. * @author Edward Lam */ public static final class Matching extends SwitchAlt { private static final int serializationSchema = 0; /** (FieldName->String) map from field name to var name for all used alt vars.*/ private Map<FieldName, String> fieldNameToVarNameMap; Matching(Object altTag, Map<FieldName, String> fieldNameToVarNameMap, Expression expr) { this(Collections.singletonList(altTag), fieldNameToVarNameMap, expr); } Matching(List<Object> altTags, Map<FieldName, String> fieldNameToVarNameMap, Expression expr) { super (altTags, expr); if (fieldNameToVarNameMap == null) { throw new NullPointerException ("Expression.Switch.SwitchAlt.Matching constructor: the argument 'fieldNameToVarNameMap' cannot be null."); } this.fieldNameToVarNameMap = fieldNameToVarNameMap; } /** * Default constructor for use by serialization code. */ private Matching () {} public Map<FieldName, String> getFieldNameToVarNameMap() { return Collections.unmodifiableMap(fieldNameToVarNameMap); } /** * {@inheritDoc} */ @Override public boolean hasVars() { return !fieldNameToVarNameMap.isEmpty(); } /** * @return the names of the alt variables used in the associated expr. */ @Override public String[] getVarNames() { String[] names = new String[fieldNameToVarNameMap.size()]; int i = 0; for (final Map.Entry<FieldName, String> entry : fieldNameToVarNameMap.entrySet()) { names[i++] = entry.getValue(); } return names; } /** * {@inheritDoc} */ @Override public String toArgumentString() { StringBuilder varsDisplay = new StringBuilder(); varsDisplay.append("{"); boolean firstIteration = true; for (final Map.Entry<FieldName, String> entry : fieldNameToVarNameMap.entrySet()) { FieldName fieldName = entry.getKey(); String varName = entry.getValue(); if (firstIteration) { firstIteration = false; } else { varsDisplay.append(", "); } varsDisplay.append(fieldName + "=" + varName); } varsDisplay.append("}"); return varsDisplay.toString(); } /** * * @param visitor * @param arg * @return the result of visiting this LetDefn */ @Override public <T, R> R accept(ExpressionVisitor<T, R> visitor, T arg) { return visitor.visitSwitchAlt_Matching(this, arg); } /** * Write this instance of SwitchAlt.Matching to the RecordOutputStream. * @param s * @throws IOException */ @Override void write (RecordOutputStream s) throws IOException { s.startRecord (ModuleSerializationTags.EXPRESSION_SWITCHALT_MATCHING, serializationSchema); super.writeContent (s); s.writeIntCompressed(fieldNameToVarNameMap.size()); for (final Map.Entry<FieldName, String> entry : fieldNameToVarNameMap.entrySet()) { FieldName fn = entry.getKey(); String varName = entry.getValue(); FieldNameIO.writeFieldName(fn, s); s.writeUTF (varName); } s.endRecord (); } /** * Load an instance of SwitchAlt.Matching from a RecordInputStream * @param s * @param schema * @param mti * @param msgLogger the logger to which to log deserialization messages. * @return an instance of SwitchAlt.Matching * @throws IOException */ public static Matching load (RecordInputStream s, int schema, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { DeserializationHelper.checkSerializationSchema(schema, serializationSchema, mti.getModuleName(), "Expression.Switch.SwitchAlt.Matching", msgLogger); Matching m = new Matching (); m.readContent (s, mti, msgLogger); return m; } private void readContent (RecordInputStream s, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { super.readContent (s, mti, msgLogger); int nFields = s.readIntCompressed(); fieldNameToVarNameMap = new HashMap<FieldName, String>(); for (int i = 0; i < nFields; ++i) { FieldName fn = FieldNameIO.load (s, mti.getModuleName(), msgLogger); String varName = s.readUTF(); fieldNameToVarNameMap.put (fn, varName); } s.skipRestOfRecord(); } } private SwitchAlt(List<Object> altTags, Expression expr) { if (altTags == null || expr == null) { throw new NullPointerException ("Expression.Switch.SwitchAlt constructor: the arguments 'altTags' and 'expr' cannot be null."); } if (altTags.isEmpty()) { throw new IllegalArgumentException("Expression.Switch.SwitchAlt constructor: 'altTags' must not be empty."); } this.altTags = altTags; this.expr = expr; } /** * Default constructor for use by serialization code. */ private SwitchAlt () {} /** * {@inheritDoc} */ @Override public String toString() { StringBuilder altTagsDisplay = new StringBuilder(); if (altTags.size() > 1) { altTagsDisplay.append("("); boolean firstIteration = true; for (final Object altTagObj : altTags ) { String altTag = altTagObj.toString(); if (firstIteration) { firstIteration = false; } else { altTagsDisplay.append(", "); } altTagsDisplay.append(altTag); } altTagsDisplay.append(")"); } else { altTagsDisplay.append(altTags.get(0)); } return altTagsDisplay + " " + toArgumentString() + "-> " + expr; } /** * @return boolean true if this alt is represented in CAL text by the _ pattern i.e. _ -> expr. */ public boolean isDefaultAlt() { return getFirstAltTag().equals(WILDCARD_TAG); } public Object getFirstAltTag() { return altTags.get(0); } public List<Object> getAltTags() { return altTags; } public Expression getAltExpr() { return expr; } void setAltExpr (Expression e) { if (e == null) { throw new IllegalArgumentException("Attempt to set null expr in Expression.Switch.SwitchAlt"); } expr = e; } /** * @return whether the alt has variables which are used in the associated expr. */ public abstract boolean hasVars(); /** * @return the names of the alt variables used in the associated expr. */ public abstract String[] getVarNames(); /** * @return the String to display for the arguments in the toString() representation. */ public abstract String toArgumentString(); void walk(Visitor v) { v.enterSwitchAlt(this); expr.walk (v); v.exitSwitchAlt(this); } /** * * @param visitor * @param arg * @return the result of visiting this LetDefn */ public abstract <T, R> R accept(ExpressionVisitor<T, R> visitor, T arg); } /** * {@inheritDoc} */ @Override public <T, R> R accept(ExpressionVisitor<T, R> visitor, T arg) { return visitor.visitSwitch(this, arg); } @Override void walk(Visitor v) { v.enterSwitch(this); swExpr.walk (v); for (int i = 0, nAlts = alts.length; i < nAlts; ++i) { alts[i].walk(v); } v.exitSwitch(this); } /** * Write this instance of Switch to the RecordOutputStream. * @param s * @throws IOException */ @Override final void write (RecordOutputStream s) throws IOException { s.startRecord (ModuleSerializationTags.EXPRESSION_SWITCH, serializationSchema); boolean flags[] = new boolean [1]; flags [0] = errorInfo != null; byte[] bytes = RecordOutputStream.booleanArrayToBitArray(flags); s.writeByte(bytes[0]); swExpr.write (s); s.writeIntCompressed(alts.length); for (int i = 0; i < alts.length; ++i) { alts[i].write (s); } if (errorInfo != null) { errorInfo.write(s); } s.endRecord (); } /** * Load an instance of Switch from a RecordInputStream * @param s * @param schema * @param mti * @param msgLogger the logger to which to log deserialization messages. * @return an instance of Switch * @throws IOException */ public static Switch load (RecordInputStream s, int schema, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { DeserializationHelper.checkSerializationSchema(schema, serializationSchema, mti.getModuleName(), "Expression.Switch", msgLogger); byte flagsByte = s.readByte(); Expression swExpr = Expression.load (s, mti, msgLogger); int nAlts = s.readIntCompressed(); SwitchAlt alts[] = new SwitchAlt [nAlts]; for (int i = 0; i < nAlts; ++i) { alts[i] = SwitchAlt.load (s, mti, msgLogger); } ErrorInfo errorInfo = null; if (RecordInputStream.booleanFromBitArray(flagsByte, 0)) { errorInfo = (ErrorInfo)Expression.load (s, mti, msgLogger); } s.skipRestOfRecord(); return new Switch (swExpr, alts, errorInfo); } } /** * Class used to represent selection from a data constructor valued expression. * for instance: * [1.0].Cons.head // 1.0 * * @author Edward Lam */ public static final class DataConsSelection extends Expression { private static final int serializationSchema = 0; /** An expression that is guaranteed by the compiler to evaluate to a data constructor value. */ private Expression dcValueExpr; /** The represented data constructor. */ private final DataConstructor dataConstructor; /** The index of the field to extract from dcValueExpr. * eg. in dcValueExpr.DCName.fieldName, the index of fieldName in the data constructor DCName */ private final int fieldIndex; /** Error info for this expression. */ private final ErrorInfo errorInfo; DataConsSelection(Expression dcValueExpr, DataConstructor dataConstructor, int fieldIndex, ErrorInfo errorInfo) { if (dcValueExpr == null || dataConstructor == null) { throw new NullPointerException(); } if (fieldIndex < 0) { throw new IllegalArgumentException("fieldIndex must be >= 0"); } this.dcValueExpr = dcValueExpr; this.dataConstructor = dataConstructor; this.fieldIndex = fieldIndex; this.errorInfo = errorInfo; } public Expression getDCValueExpr() { return this.dcValueExpr; } public void setDCValueExpr(Expression dcValueExpr) { this.dcValueExpr = dcValueExpr; } public DataConstructor getDataConstructor() { return this.dataConstructor; } public int getFieldIndex() { return this.fieldIndex; } /** * @return The error information that identifies the position in the source of the expression. Maybe null. */ public ErrorInfo getErrorInfo(){ return errorInfo; } /** * {@inheritDoc} */ @Override public String toString() { return "EDataConsSelection (" + dcValueExpr + " " + dataConstructor + " " + fieldIndex + ")"; } /** * {@inheritDoc} */ @Override void walk(Visitor v) { v.enterDataConsSelection(this); dcValueExpr.walk(v); v.exitDataConsSelection(this); } /** * {@inheritDoc} */ @Override public <T, R> R accept(ExpressionVisitor<T, R> visitor, T arg) { return visitor.visitDataConsSelection(this, arg); } /** * Write this instance of DataConsSelection to the RecordOutputStream. * @param s * @throws IOException */ @Override final void write (RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.EXPRESSION_DATACONS_SELECTION, serializationSchema); s.writeQualifiedName (dataConstructor.getName()); dcValueExpr.write (s); s.writeIntCompressed (fieldIndex); errorInfo.write (s); s.endRecord (); } /** * Load an instance of DataConsSelection from a RecordInputStream * @param s * @param schema * @param mti * @param msgLogger the logger to which to log deserialization messages. * @return an instance of DataConsSelection * @throws IOException */ public static DataConsSelection load (RecordInputStream s, int schema, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { DeserializationHelper.checkSerializationSchema(schema, serializationSchema, mti.getModuleName(), "Expression.DataConsSelection", msgLogger); QualifiedName dcName = s.readQualifiedName(); DataConstructor dc = mti.getReachableDataConstructor(dcName); if (dc == null) { throw new IOException ("Unable to find data constructor " + dcName + " while loading Expression.DataConsSelection."); } Expression dcValueExpr = Expression.load (s, mti, msgLogger); int fieldIndex = s.readIntCompressed(); ErrorInfo errorInfo = (ErrorInfo)Expression.load (s, mti, msgLogger); s.skipRestOfRecord(); return new DataConsSelection (dcValueExpr, dc, fieldIndex, errorInfo); } } /** * A helper class to represents the ordinal and textual field names in a split * up fashion useful for code generation purposes. * * @author Bo Ilic */ public static final class FieldValueData { /** the ordinal field names (as int values) in ascending ordinal order. May have length 0 but should not be null. */ private final int[] ordinalNames; private final Expression[] ordinalValues; /** the textual field names in ascending alphabetical order. May have length 0 but should not be null. */ private final String[] textualNames; private final Expression[] textualValues; private FieldValueData(int[] ordinalNames, Expression[] ordinalValues, String[] textualNames, Expression[] textualValues) { if (ordinalNames.length != ordinalValues.length || textualNames.length != textualValues.length) { throw new IllegalArgumentException(); } this.ordinalNames = ordinalNames; this.ordinalValues = ordinalValues; this.textualNames = textualNames; this.textualValues = textualValues; } private static FieldValueData make (SortedMap<FieldName, Expression> fieldToValueMap) { int nOrdinalFields = getNOrdinalFields(fieldToValueMap); int nTextualFields = fieldToValueMap.size() - nOrdinalFields; int[] ordinalNames = new int[nOrdinalFields]; Expression[] ordinalValues = new Expression[nOrdinalFields]; String[] textualNames = new String[nTextualFields]; Expression[] textualValues = new Expression[nTextualFields]; int i = 0; for (final Map.Entry<FieldName, Expression> entry : fieldToValueMap.entrySet()) { FieldName fieldName = entry.getKey(); if (i < nOrdinalFields) { ordinalNames[i] = ((FieldName.Ordinal)fieldName).getOrdinal(); ordinalValues[i] = entry.getValue(); } else { textualNames[i - nOrdinalFields] = ((FieldName.Textual)fieldName).getCalSourceForm(); textualValues[i - nOrdinalFields] = entry.getValue(); } ++i; } return new FieldValueData(ordinalNames, ordinalValues, textualNames, textualValues); } private static int getNOrdinalFields(SortedMap<FieldName, Expression> fieldToValueMap) { int nOrdinalFields = 0; for (final FieldName fieldName : fieldToValueMap.keySet()) { if (fieldName instanceof FieldName.Ordinal) { ++nOrdinalFields; } else { //the ordinal fields occur before the textual fields return nOrdinalFields; } } return nOrdinalFields; } public boolean hasTupleOrdinalPart() { int nOrdinalFields = ordinalNames.length; if (nOrdinalFields == 0) { return false; } return ordinalNames[nOrdinalFields - 1] == nOrdinalFields; } public boolean hasOrdinalFields() { return ordinalNames.length > 0; } public int getNOrdinalFields() { return ordinalNames.length; } public int[] getOrdinalNames() { return ordinalNames; } public Expression[] getOrdinalValues() { return ordinalValues; } public boolean hasTextualFields() { return textualNames.length > 0; } public int getNTextualFields() { return textualNames.length; } public String[] getTextualNames() { return textualNames; } public Expression[] getTextualValues() { return textualValues; } } /** * Class used to represent a record update. * Some examples: * {r | field1 := 20.0, field2 := True} * {(1.0, 'a', "abc") | #2 := 'b', #3 := "def"} * * Note that in CAL language syntax field value updates and field extensions are intermixed when using the record modification * operator "|". However, in the desugared Expression format, we consider the field modification as being composed of 2 separate * operations. First, we update the field values (i.e. a RecordUpdate), and then we extend (RecordExtension). * * @author Bo Ilic */ public static final class RecordUpdate extends Expression { private static final int serializationSchema = 0; /** * An expression that when evaluated will result in a record value that will then be updated. * This cannot be null (unlike a record extension). */ private Expression baseRecordExpr; /** * (FieldName -> Expression) Map from the field name to an expression * giving the desired new updated value. */ private final SortedMap<FieldName, Expression> updateFieldValuesMap; RecordUpdate(Expression baseRecordExpr, SortedMap<FieldName, Expression> updateFieldValuesMap) { if (baseRecordExpr == null || updateFieldValuesMap == null) { throw new NullPointerException(); } this.baseRecordExpr = baseRecordExpr; this.updateFieldValuesMap = updateFieldValuesMap; } @Override public String toString() { return "ERecordUpdate {" + baseRecordExpr + " | " + updateFieldValuesMap + "}"; } /** * @return an expression that when evaluated will give the base record that needs updating. Cannot be null. */ public Expression getBaseRecordExpr() { return baseRecordExpr; } void setBaseRecordExpr(Expression expression) { if (expression == null) { throw new NullPointerException(); } this.baseRecordExpr = expression; } /** * @return (FieldName -> Expression) SortedMap from a fieldName in the baseRecordExpr to update, to the * updated value. */ public SortedMap<FieldName, Expression> getUpdateFieldValuesMap() { return Collections.unmodifiableSortedMap(updateFieldValuesMap); } void setFieldValueUpdate(FieldName fieldName, Expression updatedValue) { if (fieldName == null || updatedValue == null) { throw new NullPointerException(); } updateFieldValuesMap.put(fieldName, updatedValue); } public FieldValueData getUpdateFieldsData() { return FieldValueData.make(updateFieldValuesMap); } /** * {@inheritDoc} */ @Override public <T, R> R accept(ExpressionVisitor<T, R> visitor, T arg) { return visitor.visitRecordUpdate(this, arg); } @Override void walk(Visitor v) { v.enterRecordUpdate(this); if (baseRecordExpr != null) { baseRecordExpr.walk(v); } for (final Map.Entry<FieldName, Expression> entry : updateFieldValuesMap.entrySet()) { Expression valueExpr = entry.getValue(); valueExpr.walk (v); } v.exitRecordUpdate(this); } /** * Write this instance of RecordUpdate to the RecordOutputStream. * @param s * @throws IOException */ @Override final void write (RecordOutputStream s) throws IOException { s.startRecord (ModuleSerializationTags.EXPRESSION_RECORD_UPDATE, serializationSchema); baseRecordExpr.write(s); s.writeIntCompressed(updateFieldValuesMap.size()); for (final Map.Entry<FieldName, Expression> entry : updateFieldValuesMap.entrySet()) { FieldName fieldName = entry.getKey(); Expression expr = entry.getValue(); FieldNameIO.writeFieldName(fieldName, s); expr.write (s); } s.endRecord (); } /** * Load an instance of RecordUpdate from a RecordInputStream * @param s * @param schema * @param mti * @param msgLogger the logger to which to log deserialization messages. * @return an instance of RecordExtension * @throws IOException */ public static RecordUpdate load (RecordInputStream s, int schema, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { DeserializationHelper.checkSerializationSchema(schema, serializationSchema, mti.getModuleName(), "Expression.RecordUpdate", msgLogger); Expression baseRecordExpr = Expression.load (s, mti, msgLogger); int nFields = s.readIntCompressed(); SortedMap<FieldName, Expression> updateFieldValuesMap = new TreeMap<FieldName, Expression> (); for (int i = 0; i < nFields; ++i) { FieldName fieldName = FieldNameIO.load(s, mti.getModuleName(), msgLogger); Expression expr = Expression.load (s, mti, msgLogger); updateFieldValuesMap.put (fieldName, expr); } s.skipRestOfRecord(); return new RecordUpdate (baseRecordExpr, updateFieldValuesMap); } } /** * Class used to represent a record extension. * * Some examples: * {r | field1 = 20.0, field2 = True} //a record-polymorphic record extension * {field1 = 20.0, field2 = True} //a non-record-polymorphic record i.e. a record literal * {f x | field1 = 20.0} //f x must result in a record type that doesn't contain field1. * (the compiler assures this). * * Note that RecordExtension is not used to represent record updates. * * @author Bo Ilic */ public static final class RecordExtension extends Expression { private static final int serializationSchema = 0; /** * the base record that this is an extension of. This can be null to indicate a record * literal. If it is non-null, it is an expression that when evaluated will result in a * record value compatible with the extension. */ private Expression baseRecordExpr; /** * (FieldName -> Expression) Map from the field names in the extension to an expression * giving their desired values. */ private final SortedMap<FieldName, Expression> extensionFieldsMap; RecordExtension(Expression baseRecordExpr, SortedMap<FieldName, Expression> extensionFieldsMap) { if (extensionFieldsMap == null) { throw new NullPointerException(); } this.baseRecordExpr = baseRecordExpr; this.extensionFieldsMap = extensionFieldsMap; } @Override public String toString() { return "ERecordExtension {" + baseRecordExpr + " | " + extensionFieldsMap + "}"; } /** * @return the base record that this is an extension of. This can be null to indicate a record * literal. If it is non-null, it is an expression that when evaluated will result in a * record value compatible with the extension. */ public Expression getBaseRecordExpr() { return baseRecordExpr; } void setBaseRecordExpr (Expression e) { if (e == null) { throw new NullPointerException("Attempt to set null baseRecordExpr in Expression.RecordExtension"); } baseRecordExpr = e; } /** * @return (FieldName -> Expression) SortedMap from the field names in the extension to an expression * giving their desired values. */ public SortedMap<FieldName, Expression> getExtensionFieldsMap() { return Collections.unmodifiableSortedMap(extensionFieldsMap); } public FieldValueData getExtensionFieldsData() { return FieldValueData.make(extensionFieldsMap); } void setFieldExtension (FieldName fieldName, Expression e) { if (fieldName == null || e == null){ throw new NullPointerException(); } extensionFieldsMap.put(fieldName, e); } @Override void walk(Visitor v) { v.enterRecordExtension(this); if (baseRecordExpr != null) { baseRecordExpr.walk(v); } for (final Map.Entry<FieldName, Expression> entry : extensionFieldsMap.entrySet()) { Expression valueExpr = entry.getValue(); valueExpr.walk (v); } v.exitRecordExtension(this); } /** * {@inheritDoc} */ @Override public <T, R> R accept(ExpressionVisitor<T, R> visitor, T arg) { return visitor.visitRecordExtension(this, arg); } /** * Write this instance of RecordExtension to the RecordOutputStream. * @param s * @throws IOException */ @Override final void write (RecordOutputStream s) throws IOException { s.startRecord (ModuleSerializationTags.EXPRESSION_RECORD_EXTENSION, serializationSchema); boolean flags[] = new boolean[1]; flags[0] = baseRecordExpr != null; s.writeByte(RecordOutputStream.booleanArrayToBitArray(flags)[0]); if (baseRecordExpr != null) { baseRecordExpr.write(s); } s.writeIntCompressed(extensionFieldsMap.size()); for (final Map.Entry<FieldName, Expression> entry : extensionFieldsMap.entrySet()) { FieldName fieldName = entry.getKey(); Expression expr = entry.getValue(); FieldNameIO.writeFieldName(fieldName, s); expr.write (s); } s.endRecord (); } /** * Load an instance of RecordExtension from a RecordInputStream * @param s * @param schema * @param mti * @param msgLogger the logger to which to log deserialization messages. * @return an instance of RecordExtension * @throws IOException */ public static RecordExtension load (RecordInputStream s, int schema, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { DeserializationHelper.checkSerializationSchema(schema, serializationSchema, mti.getModuleName(), "Expression.RecordExtension", msgLogger); byte flagByte = s.readByte(); Expression baseRecordExpr = null; if (RecordInputStream.booleanFromBitArray(flagByte, 0)) { baseRecordExpr = Expression.load (s, mti, msgLogger); } int nFields = s.readIntCompressed(); SortedMap<FieldName, Expression> extensionFieldsMap = new TreeMap<FieldName, Expression> (); for (int i = 0; i < nFields; ++i) { FieldName fieldName = FieldNameIO.load(s, mti.getModuleName(), msgLogger); Expression expr = Expression.load (s, mti, msgLogger); extensionFieldsMap.put (fieldName, expr); } s.skipRestOfRecord(); return new RecordExtension (baseRecordExpr, extensionFieldsMap); } } /** * Models the "." operator for selecting the value of a field from a record. * For example, * r.field1 //selects field1 from r * {colour = red, height = 6.0}.height //selects height from {colour = red, height = 6.0} * * @author Bo Ilic */ public static final class RecordSelection extends Expression { private static final int serializationSchema = 0; /** An expression that is guaranteed by the compiler to evaluate to a record. */ private Expression recordExpr; /** The name of the field to extract from baseRecordExpr. i.e. recordExpr.fieldName. */ private final FieldName fieldName; RecordSelection(Expression recordExpr, FieldName fieldName) { if (recordExpr == null || fieldName == null) { throw new NullPointerException(); } this.recordExpr = recordExpr; this.fieldName = fieldName; } @Override public String toString() { return "ERecordSelection (" + recordExpr + "." + fieldName.getCalSourceForm() + ")"; } /** * @return An expression that is guaranteed by the compiler to evaluate to a record. */ public Expression getRecordExpr() { return recordExpr; } void setRecordExpr (Expression e) { if (e == null) { throw new IllegalArgumentException("Attempt to set null recordExpr in Expression.RecordSelection"); } recordExpr = e; } /** * @return The name of the field to extract from baseRecordExpr. i.e. recordExpr.fieldName. */ public FieldName getFieldName() { return fieldName; } /** * {@inheritDoc} */ @Override public <T, R> R accept(ExpressionVisitor<T, R> visitor, T arg) { return visitor.visitRecordSelection(this, arg); } @Override void walk(Visitor v) { v.enterRecordSelection(this); recordExpr.walk(v); v.exitRecordSelection(this); } /** * Write this instance of RecordSelection to the RecordOutputStream. * @param s * @throws IOException */ @Override final void write (RecordOutputStream s) throws IOException { s.startRecord (ModuleSerializationTags.EXPRESSION_RECORD_SELECTION, serializationSchema); FieldNameIO.writeFieldName(fieldName, s); recordExpr.write (s); s.endRecord (); } /** * Load an instance of RecordSelection from a RecordInputStream * @param s * @param schema * @param mti * @param msgLogger the logger to which to log deserialization messages. * @return an instance of RecordSelection * @throws IOException */ public static RecordSelection load (RecordInputStream s, int schema, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { DeserializationHelper.checkSerializationSchema(schema, serializationSchema, mti.getModuleName(), "Expression.RecordSelection", msgLogger); FieldName fn = FieldNameIO.load (s, mti.getModuleName(), msgLogger); Expression recordExpr = Expression.load (s, mti, msgLogger); s.skipRestOfRecord(); return new RecordSelection (recordExpr, fn); } } /** * Models the case expression for record patterns. This will have the form: * case conditionExpr of recordPattern -> resultExpr; * * Unlike case expressions for unpacking algebraic data types (i.e. where the patterns involve data constructors) * there is only only recordPattern per case. * Also, record case expressions can be polymorphic in their outermost type e.g. * case r of {s | field1 = x} -> x * * @author Bo Ilic */ public static final class RecordCase extends Expression { private static final int serializationSchema = 0; /** * In the record case expression, case c of ..., this is the "c" part. * It is guaranteed by the compiler to evaluate to a record value of the * appropriate type suitable for pattern decomposition. */ private Expression conditionExpr; /** * name of the variable bound to the base record. Can be WILDCARD_VAR. * A null value indicates a non-record-polymorphic pattern match. * * For example, for the record case expression: * case c of {r | field1 = x, field2, field3 = y, field3 = _} -> x + y * this will be r. * * For the record case expression: * case c of {field1 = x, field2, field3 = y, field3 = _} -> x + y * this will be null. */ private final String baseRecordPatternVarName; /** * (FieldName -> String). SortedMap from the field name to the pattern variable that will be bound during the pattern * match. For example, for the record case expression: * case x of {r | field1 = x, field2, field3 = y, field4 = _} -> x + y * this will be the map [(field1, x), (field2, field2), (field3, y), (field4, WILDCARD_VAR)] * Keys are ordered via the ordering on FieldName. */ private final SortedMap<FieldName, String> fieldBindingVarMap; /** * In the record case expression, case c of p -> e, this is the "e" part. */ private Expression resultExpr; /**the varName to use to represent the wildcard pattern match. */ public static final String WILDCARD_VAR = "$_"; /** * A helper class to represents the ordinal and textual field names in a split * up fashion useful for code generation purposes. * * @author Bo Ilic */ public static final class FieldData { /** the ordinal field names (as int values) in ascending ordinal order. May have length 0 but should not be null. */ private final int[] ordinalNames; /** the textual field names in ascending alphabetical order. May have length 0 but should not be null. */ private final String[] textualNames; private FieldData(int[] ordinalNames, String[] textualNames) { if (ordinalNames == null || textualNames == null) { throw new NullPointerException(); } this.ordinalNames = ordinalNames; this.textualNames = textualNames; } public boolean hasTupleOrdinalPart() { int nOrdinalFields = ordinalNames.length; if (nOrdinalFields == 0) { return false; } return ordinalNames[nOrdinalFields - 1] == nOrdinalFields; } public boolean hasOrdinalFields() { return ordinalNames.length > 0; } public int getNOrdinalFields() { return ordinalNames.length; } public int[] getOrdinalNames() { return ordinalNames; } public boolean hasTextualFields() { return textualNames.length > 0; } public int getNTextualFields() { return textualNames.length; } public String[] getTextualNames() { return textualNames; } } RecordCase(Expression conditionExpr, String baseRecordPatternVarName, SortedMap<FieldName, String> fieldBindingVarMap, Expression resultExpr) { if (conditionExpr == null || fieldBindingVarMap == null || resultExpr == null) { throw new NullPointerException(); } this.conditionExpr = conditionExpr; this.baseRecordPatternVarName = baseRecordPatternVarName; this.fieldBindingVarMap = fieldBindingVarMap; this.resultExpr = resultExpr; } @Override public String toString() { return "ERecordCase (" + conditionExpr + " of {" + baseRecordPatternVarName + " | " + fieldBindingVarMap + "} -> " + resultExpr +")"; } /** * @return In the record case expression, case c of ..., this is the "c" part. */ public Expression getConditionExpr() { return conditionExpr; } void setConditionExpr (Expression e) { if (e == null) { throw new IllegalArgumentException("Attempt to set null conditionExpr in Expression.RecordCase"); } conditionExpr = e; } /** * @return name of the variable bound to the base record. Can be WILDCARD_VAR. * A null value indicates a non-record-polymorphic pattern match. */ public String getBaseRecordPatternVarName() { return baseRecordPatternVarName; } /** * @return (FieldName -> String). SortedMap from the field name to the pattern variable that will be bound during the pattern * match. Keys are ordered via the ordering on FieldName. */ public SortedMap<FieldName, String> getFieldBindingVarMap() { return Collections.unmodifiableSortedMap(fieldBindingVarMap); } public FieldData getBindingFieldsData() { int nOrdinalFields = getNOrdinalFields(); int nTextualFields = fieldBindingVarMap.size() - nOrdinalFields; int[] ordinalNames = new int[nOrdinalFields]; String[] textualNames = new String[nTextualFields]; int i = 0; for (final FieldName fieldName : fieldBindingVarMap.keySet()) { if (i < nOrdinalFields) { ordinalNames[i] = ((FieldName.Ordinal)fieldName).getOrdinal(); } else { textualNames[i - nOrdinalFields] = ((FieldName.Textual)fieldName).getCalSourceForm(); } ++i; } return new FieldData(ordinalNames, textualNames); } private int getNOrdinalFields() { int nOrdinalFields = 0; for (final FieldName fieldName : fieldBindingVarMap.keySet()) { if (fieldName instanceof FieldName.Ordinal) { ++nOrdinalFields; } else { //the ordinal fields occur before the textual fields return nOrdinalFields; } } return nOrdinalFields; } /** * @return In the record case expression, case c of p -> e, this is the "e" part. */ public Expression getResultExpr() { return resultExpr; } void setResultExpr (Expression e) { if (e == null) { throw new IllegalArgumentException("Attempt to set null resultExpr in Expression.RecordCase"); } resultExpr = e; } @Override void walk(Visitor v) { v.enterRecordCase(this); conditionExpr.walk(v); resultExpr.walk(v); v.exitRecordCase(this); } /** * {@inheritDoc} */ @Override public <T, R> R accept(ExpressionVisitor<T, R> visitor, T arg) { return visitor.visitRecordCase(this, arg); } /** * Write this instance of RecordCase to the RecordOutputStream. * @param s * @throws IOException */ @Override final void write (RecordOutputStream s) throws IOException { s.startRecord (ModuleSerializationTags.EXPRESSION_RECORD_CASE, serializationSchema); s.writeUTF (baseRecordPatternVarName); conditionExpr.write (s); resultExpr.write (s); s.writeIntCompressed (fieldBindingVarMap.size()); for (final Map.Entry<FieldName, String> entry : fieldBindingVarMap.entrySet()) { FieldName fn = entry.getKey(); String binding = entry.getValue(); FieldNameIO.writeFieldName(fn, s); s.writeUTF (binding); } s.endRecord (); } /** * Load an instance of RecordCase from a RecordInputStream * @param s * @param schema * @param mti * @param msgLogger the logger to which to log deserialization messages. * @return an instance of RecordCase * @throws IOException */ public static RecordCase load (RecordInputStream s, int schema, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { DeserializationHelper.checkSerializationSchema(schema, serializationSchema, mti.getModuleName(), "Expression.RecordCase", msgLogger); String baseRecordPatternVarName = s.readUTF(); Expression conditionExpr = Expression.load(s, mti, msgLogger); Expression resultExpr = Expression.load(s, mti, msgLogger); int nBindings = s.readIntCompressed(); SortedMap<FieldName, String> fieldBindingVarMap = new TreeMap<FieldName, String> (); for (int i = 0; i < nBindings; ++i) { FieldName fn = FieldNameIO.load(s, mti.getModuleName(), msgLogger); String binding = s.readUTF(); fieldBindingVarMap.put (fn, binding); } s.skipRestOfRecord(); return new RecordCase (conditionExpr, baseRecordPatternVarName, fieldBindingVarMap, resultExpr); } } /** * Represents a tail recursive call. */ public static final class TailRecursiveCall extends Expression { private static final int serializationSchema = 0; /** The arguments of the call. */ private Expression[] arguments; /** The function being tail-called. */ private Expression.Var var; TailRecursiveCall (Expression.Var var, Expression[] arguments) { if (var == null || arguments == null) { throw new NullPointerException("Expression.TailRecursiveCall constructor: the arguments cannot be null."); } this.var = var; this.arguments = arguments; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append ("ERecursiveCall ("); for (int i = 0; i < arguments.length; ++i) { sb.append (arguments[i] + ")"); if (i < arguments.length - 1) { sb.append (" ("); } } return sb.toString (); } public Var getVar() { return var; } public Expression[] getArguments () { return arguments; } void setArguments (Expression[] arguments) { if (arguments == null) { throw new IllegalArgumentException("Attempt to set null arguments in Expression.TailRecursiveCall"); } this.arguments = arguments; } /** * {@inheritDoc} */ @Override public <T, R> R accept(ExpressionVisitor<T, R> visitor, T arg) { return visitor.visitTailRecursiveCall(this, arg); } @Override void walk(Visitor v) { v.enterTailRecursiveCall(this); for (int i = 0, nArgs = arguments.length; i < nArgs; ++i) { arguments[i].walk(v); } v.exitTailRecursiveCall(this); } /** * @return the tail recursive call in the original form of an application node. */ public Expression getApplForm() { Expression e = var; for (int i = 0; i < arguments.length; ++i) { e = new Expression.Appl (e, arguments[i]); } return e; } /** * Write this instance of TailRecursiveCall to the RecordOutputStream. * @param s * @throws IOException */ @Override void write (RecordOutputStream s) throws IOException { s.startRecord (ModuleSerializationTags.EXPRESSION_TAIL_RECURSIVE_CALL, serializationSchema); var.write (s); s.writeIntCompressed(arguments.length); for (int i = 0; i < arguments.length; ++i) { arguments[i].write(s); } s.endRecord (); } /** * Load an instance of TailRecursiveCall from a RecordInputStream * @param s * @param schema * @param mti * @param msgLogger the logger to which to log deserialization messages. * @return an instance of TailRecursiveCall * @throws IOException */ public static TailRecursiveCall load (RecordInputStream s, int schema, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { DeserializationHelper.checkSerializationSchema(schema, serializationSchema, mti.getModuleName(), "Expression.TailRecursiveCall", msgLogger); Expression.Var var = (Var)Expression.load(s, mti, msgLogger); int nArgs = s.readIntCompressed(); Expression arguments[] = new Expression[nArgs]; for (int i = 0; i < nArgs; ++i) { arguments[i] = Expression.load(s, mti, msgLogger); } s.skipRestOfRecord(); return new TailRecursiveCall (var, arguments); } } /** * For use in implementing casting of expressions that evaluate to values of a foreign, non-primitive type, and * downcasting them. Currently used to implement derived instances of Inputable for foreign types in such a * way that if inputting the wrong type of object, you'll get a ClassCastException immediately. * * @author Bo Ilic */ public static final class Cast extends Expression { private static final int serializationSchema = 0; /** Provider for the Class object representing the cast type. */ private final ForeignEntityProvider<Class<?>> castTypeProvider; /** The variable expression to be cast. */ private final Expression.Var varToCast; /** * Private constructor. Instances should be constructed via the factory methods. * @param castTypeProvider a ForeignEntityProvider for providing the Class object representing the cast type. * @param varToCast the variable expression to be cast. */ private Cast(ForeignEntityProvider<Class<?>> castTypeProvider, Expression.Var varToCast) { this.castTypeProvider = castTypeProvider; this.varToCast = varToCast; } /** * Factory method for constructing an instance. * @param castType the Class object representing the cast type. * @param varToCast the variable expression to be cast. * @return an instance of this class. */ static Cast make(Class<?> castType, Expression.Var varToCast) { if (castType == null || varToCast == null) { throw new NullPointerException(); } if (castType.isPrimitive()) { throw new IllegalArgumentException("Expression.Cast: cast type must not be a primitive type"); } return new Cast(ForeignEntityProvider.makeStrict(castType), varToCast); } /** * Factory method for constructing an instance. * @param castTypeProvider a ForeignEntityProvider for providing the Class object representing the cast type. * @param varToCast the variable expression to be cast. * @return an instance of this class. */ static Cast makeWithCastTypeProvider(ForeignEntityProvider<Class<?>> castTypeProvider, Expression.Var varToCast) { if (castTypeProvider == null || varToCast == null) { throw new NullPointerException(); } return new Cast(castTypeProvider, varToCast); } /** @return the provider for the Class object representing the cast type. */ ForeignEntityProvider<Class<?>> getCastTypeProvider() { return castTypeProvider; } /** * @return the Class object representing the cast type. * @throws UnableToResolveForeignEntityException if the class cannot be resolved. */ public Class<?> getCastType() throws UnableToResolveForeignEntityException { return castTypeProvider.get(); } /** @return the variable expression to be cast. */ public Expression.Var getVarToCast() { return varToCast; } /** * {@inheritDoc} */ @Override void walk(Visitor v) { v.enterCast(this); v.exitCast(this); } /** * {@inheritDoc} */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append ("ECast (").append(castTypeProvider).append(' ').append(varToCast).append(')'); return sb.toString (); } /** * {@inheritDoc} */ @Override public <T, R> R accept(ExpressionVisitor<T, R> visitor, T arg) { return visitor.visitCast(this, arg); } /** * Write this instance of Cast to the RecordOutputStream. * @param s * @throws IOException */ @Override final void write (RecordOutputStream s) throws IOException { s.startRecord(ModuleSerializationTags.EXPRESSION_CAST, serializationSchema); try { s.writeUTF(getCastType().getName()); } catch (UnableToResolveForeignEntityException e) { final IllegalStateException illegalStateException = new IllegalStateException("Java entities in the Expression.Cast should have been eagerly resolved during the compilation process"); illegalStateException.initCause(e); throw illegalStateException; } varToCast.write(s); s.endRecord (); } /** * Load an instance of Case from a RecordInputStream * @param s * @param schema * @param mti * @param msgLogger the logger to which to log deserialization messages. * @return an instance of Cast * @throws IOException */ public static Cast load (RecordInputStream s, int schema, final ModuleTypeInfo mti, CompilerMessageLogger msgLogger) throws IOException { DeserializationHelper.checkSerializationSchema(schema, serializationSchema, mti.getModuleName(), "Expression.Cast", msgLogger); final String className = s.readUTF(); Var varToCast = (Var)Expression.load(s, mti, msgLogger); ForeignEntityProvider<Class<?>> castTypeProvider = ForeignEntityProvider.make(msgLogger, new ForeignEntityProvider.Resolver<Class<?>>(className) { @Override public Class<?> resolve(final ForeignEntityProvider.MessageHandler messageHandler) throws UnableToResolveForeignEntityException { final Class<?> castType = DeserializationHelper.classForName(className, mti.getModule().getForeignClassLoader(), "Expression.Cast", null, messageHandler); if (castType.isPrimitive()) { throw new IllegalArgumentException("Expression.Cast: cast type must not be a primitive type"); } return castType; } }); s.skipRestOfRecord(); return makeWithCastTypeProvider(castTypeProvider, varToCast); } } /** * Appl narrowing function * @return if this is an Appl, this cast to Appl, null otherwise. */ public Appl asAppl() { return (this instanceof Appl) ? (Appl) this : null; } /** * PackCons narrowing function * @return if this is an PackCons, this cast to PackCons, null otherwise. */ public PackCons asPackCons() { return (this instanceof PackCons) ? (PackCons) this : null; } /** * Let narrowing function */ public Let asLet() { return (this instanceof Let) ? (Let) this : null; } /** * LetNonRec narrowing function */ public LetNonRec asLetNonRec() { return (this instanceof LetNonRec) ? (LetNonRec) this : null; } /** * LetRec narrowing function */ public LetRec asLetRec() { return (this instanceof LetRec) ? (LetRec) this : null; } /** * Literal narrowing function */ public Literal asLiteral() { return (this instanceof Literal) ? (Literal) this : null; } /** * Switch narrowing function */ public Switch asSwitch() { return (this instanceof Switch) ? (Switch) this : null; } /** * DataConsSelection narrowing function */ public DataConsSelection asDataConsSelection() { return (this instanceof DataConsSelection) ? (DataConsSelection)this : null; } /** * Var narrowing function */ public Var asVar() { return (this instanceof Var) ? (Var) this : null; } /** * RecordUpdate narrowing function */ public RecordUpdate asRecordUpdate() { return (this instanceof RecordUpdate) ? (RecordUpdate) this : null; } /** * RecordExtension narrowing function */ public RecordExtension asRecordExtension() { return (this instanceof RecordExtension) ? (RecordExtension) this : null; } /** * RecordSelection narrowing function */ public RecordSelection asRecordSelection() { return (this instanceof RecordSelection) ? (RecordSelection) this : null; } /** * ErrorInfo narrowing function */ public ErrorInfo asErrorInfo() { return (this instanceof ErrorInfo) ? (ErrorInfo) this : null; } /** * RecordCase narrowing function */ public RecordCase asRecordCase() { return (this instanceof RecordCase) ? (RecordCase) this : null; } /** * TailRecursiveCall narrowing function */ public TailRecursiveCall asTailRecursiveCall () { return (this instanceof TailRecursiveCall) ? (TailRecursiveCall) this : null; } /** * Cast narrowing function */ public Cast asCast () { return (this instanceof Cast) ? (Cast) this : null; } /** * Describe the expression * All expression subtypes should implement toString * Note however, that expressions nest calls to toString which may cause problems with * large expressions */ @Override public abstract String toString(); /** * Method called by the expression walker in the ExpressionAnalyzer class when * using the visitor pattern. * Visiting uses and enter/exit pattern. The enter function for the node is * called, then the sub parts are traversed and then the exit function * is called. * If there are no sub-parts to the expression the exit function will be * called immediately after the enter function. * A purely pre-order traversal (i.e. the node is visited before sub-expressions * are traversed) can be accomplished by using the only the enter methods. * A purely post-order traversal (i.e. the sub-expressions are traversed before * the node is visited) can be accomplished by using only the exit functions. * @param v the Visitor */ abstract void walk(Visitor v); /** * Build a list of all the Expression.Literal objects in the this expression. * @return a list of Expression.Literal objects. */ public List<Expression.Literal> getLiterals () { return ExpressionAnalyzer.literals(this); } /** * Determines if this expression (e) is dependent on * the named variable. * @param varName * @return true if e references var */ public boolean isDependentOn (QualifiedName varName) { return ExpressionAnalyzer.isDependentOn(this, varName); } /** * Accepts the visitation of a visitor, which implements the * ExpressionVisitor interface. This abstract method is to be overridden * by each concrete subclass so that the correct visit method on the * visitor may be called based upon the type of the element being * visited. Each concrete subclass of Expression should correspond * one-to-one with a visit method declaration in the ExpressionVisitor * interface. * <p> * * As the ExpressionVisitor follows a more general visitor pattern * where arguments can be passed into the visit methods and return * values obtained from them, this method passes through the argument * into the visit method, and returns as its return value the return * value of the visit method. * <p> * * Nonetheless, for a significant portion of the common cases, the state of the * visitation can simply be kept as member variables within the visitor itself, * thereby eliminating the need to use the argument and return value of the * visit methods. In these scenarios, the recommended approach is to use * {@link Void} as the type argument for both <code>T</code> and <code>R</code>, and * pass in null as the argument, and return null as the return value. * <p> * * @see ExpressionVisitor * * @param <T> the argument type. If the visitation argument is not used, specify {@link Void}. * @param <R> the return type. If the return value is not used, specify {@link Void}. * * @param visitor * the visitor * @param arg * the argument to be passed to the visitor's visitXXX method * @return the return value of the visitor's visitXXX method */ public abstract <T, R> R accept(ExpressionVisitor<T, R> visitor, T arg); /** * Write this instance of Expression to the RecordOutputStream. * @param s * @throws IOException */ abstract void write (RecordOutputStream s) throws IOException; /** * Load an instance of Expression from a RecordInputStream * @param s * @param moduleTypeInfo * @param msgLogger the logger to which to log deserialization messages. * @return an instance of Expression * @throws IOException */ public static Expression load (RecordInputStream s, ModuleTypeInfo moduleTypeInfo, CompilerMessageLogger msgLogger) throws IOException { RecordHeaderInfo rhi = s.findRecord(EXPRESSION_RECORD_TAGS); if (rhi == null) { throw new IOException("Unable to find Expression record header."); } switch (rhi.getRecordTag()) { case ModuleSerializationTags.EXPRESSION_APPL: return Appl.load (s, rhi.getSchema(), moduleTypeInfo, msgLogger); case ModuleSerializationTags.EXPRESSION_CAST: return Cast.load (s, rhi.getSchema(), moduleTypeInfo, msgLogger); case ModuleSerializationTags.EXPRESSION_DATACONS_SELECTION: return DataConsSelection.load (s, rhi.getSchema(), moduleTypeInfo, msgLogger); case ModuleSerializationTags.EXPRESSION_ERROR_INFO: return ErrorInfo.load (s, rhi.getSchema(), moduleTypeInfo, msgLogger); case ModuleSerializationTags.EXPRESSION_LET_REC: return LetRec.load (s, rhi.getSchema(), moduleTypeInfo, msgLogger); case ModuleSerializationTags.EXPRESSION_LET_NONREC: return LetNonRec.load (s, rhi.getSchema(), moduleTypeInfo, msgLogger); case ModuleSerializationTags.EXPRESSION_LITERAL: return Literal.load (s, rhi.getSchema(), moduleTypeInfo, msgLogger); case ModuleSerializationTags.EXPRESSION_PACKCONS: return PackCons.load (s, rhi.getSchema(), moduleTypeInfo, msgLogger); case ModuleSerializationTags.EXPRESSION_RECORD_CASE: return RecordCase.load (s, rhi.getSchema(), moduleTypeInfo, msgLogger); case ModuleSerializationTags.EXPRESSION_RECORD_UPDATE: return RecordUpdate.load(s, rhi.getSchema(), moduleTypeInfo, msgLogger); case ModuleSerializationTags.EXPRESSION_RECORD_EXTENSION: return RecordExtension.load (s, rhi.getSchema(), moduleTypeInfo, msgLogger); case ModuleSerializationTags.EXPRESSION_RECORD_SELECTION: return RecordSelection.load (s, rhi.getSchema(), moduleTypeInfo, msgLogger); case ModuleSerializationTags.EXPRESSION_SWITCH: return Switch.load (s, rhi.getSchema(), moduleTypeInfo, msgLogger); case ModuleSerializationTags.EXPRESSION_TAIL_RECURSIVE_CALL: return TailRecursiveCall.load (s, rhi.getSchema(), moduleTypeInfo, msgLogger); case ModuleSerializationTags.EXPRESSION_VAR: return Var.load (s, rhi.getSchema(), moduleTypeInfo, msgLogger); default: throw new IOException ("Unrecognized record tag of " + rhi.getRecordTag() + " for Expression."); } } }