Package org.openquark.cal.compiler

Source Code of org.openquark.cal.compiler.Expression$RecordCase$FieldData

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

Related Classes of org.openquark.cal.compiler.Expression$RecordCase$FieldData

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.