/*
* 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.");
}
}
}