Package org.openquark.cal.compiler

Source Code of org.openquark.cal.compiler.ExpressionGenerator

/*
* 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.
*/


/*
* ExpressionGenerator.java
* Created: Sep 12 2000
* By: Bo Ilic
*/
package org.openquark.cal.compiler;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import org.openquark.cal.compiler.Expression.ErrorInfo;
import org.openquark.cal.compiler.Expression.Let.LetDefn;
import org.openquark.cal.compiler.Expression.Switch.SwitchAlt;
import org.openquark.cal.internal.module.Cal.Collections.CAL_List_internal;
import org.openquark.cal.internal.module.Cal.Core.CAL_Bits_internal;
import org.openquark.cal.internal.module.Cal.Core.CAL_Debug_internal;
import org.openquark.cal.internal.module.Cal.Core.CAL_Dynamic_internal;
import org.openquark.cal.internal.module.Cal.Core.CAL_Exception_internal;
import org.openquark.cal.internal.module.Cal.Core.CAL_Prelude_internal;
import org.openquark.cal.internal.module.Cal.Core.CAL_Record_internal;
import org.openquark.cal.internal.module.Cal.Utilities.CAL_QuickCheck_internal;
import org.openquark.cal.module.Cal.Core.CAL_Prelude;


/**
* Takes a possibly simplified AST produced by the parser and type checker, and converts
* to our own internal Expression format.
*
* Creation date: (9/12/00 11:24:51 AM)
* @author Bo Ilic
*/
final class ExpressionGenerator {
   
    private static final SortedMap<FieldName, String> NO_MATCHING_FIELDS = Collections.unmodifiableSortedMap(new TreeMap<FieldName, String>());
    private static final SortedMap<Integer, String> NO_POSITIONAL_FIELDS = Collections.unmodifiableSortedMap(new TreeMap<Integer, String>());
   
    private final CALCompiler compiler;

    /** The name of the module in which to generate the expression. */
    private final ModuleName currentModuleName;
   
    private final ModuleTypeInfo currentModuleTypeInfo;
   
    /** whether expression generation is for an adjunct or not */
    private final boolean isAdjunct;
      
    /**
     * Counts the number of letNonRecs,letRecs (with a single binding) and
     * letRecs (with a multiple binding) in the current module, as well as
     * running totals for the workspace.    
     */
    private static final boolean GENERATE_LET_STATISTICS = false;
    private int nLetNonRecs;
    private int nLetRecsMultiple;
    private int nLetRecsSingle;   
    private static int totalNLetNonRecs;   
    private static int totalNLetRecsMultiple;
    private static int totalNLetRecsSingle;

    /**
     * Constructs an ExpressionGenerator.
     * @param compiler
     * @param currentModuleTypeInfo
     * @param isAdjunct
     */
    ExpressionGenerator(CALCompiler compiler, ModuleTypeInfo currentModuleTypeInfo, boolean isAdjunct) {
        if (compiler == null || currentModuleTypeInfo == null) {
            throw new NullPointerException();           
        }
       
        this.compiler = compiler;
        this.currentModuleTypeInfo = currentModuleTypeInfo;
        this.currentModuleName = currentModuleTypeInfo.getModuleName();
        this.isAdjunct = isAdjunct;
    }
   
    private void generateBuiltInFunction(final QualifiedName functionName) {
        generateBuiltInFunction(functionName, null);
    }

    /**
     * Creates a CoreFunction for a built-in function. This is necessary
     * when built-in functions are applied to less than their full number of arguments. For example:
     * "main = sin 2;" works without having these extra supercombinator activation records, but
     * "main = mySin 2; mySin = sin;" fails with a missing "sin" label error. The solution is to create
     * core functions representing definitions of the form:
     * sin x = sin x;
     * This is not a recursive definition! The right hand side is fully applied, so it will be treated
     * as a primitive op by the code generator.
     *
     * Creation date: (2/22/01 11:02:56 AM)
     *
     * @param functionName name of the built-in function (assumed to belong to the current module)
     * @param maybeArgStrictness  strictness annotations for the built-in function. If null, all args are assumed to be strict
     *    and the function is assumed *not* to have a class context i.e. no dictionary args.
     */
    private void generateBuiltInFunction(final QualifiedName functionName, final boolean[] maybeArgStrictness) {
             
        final Function function = currentModuleTypeInfo.getFunction(functionName.getUnqualifiedName());      
     
        final int arity = function.getTypeExpr().getArity();      
        final int nTotalArgs;
       
        final boolean[] argStrictness;
        if (maybeArgStrictness == null) {           
            nTotalArgs = arity;
            argStrictness = new boolean[nTotalArgs];           
            //all parameters assumed to be strict
            Arrays.fill(argStrictness, true);
        } else {
            argStrictness = maybeArgStrictness;
            if (argStrictness.length >= arity) {
                // If >, Assume that there will be dictionary arguments
                nTotalArgs = argStrictness.length;
            } else {
                throw new IllegalArgumentException();
            }
        }
        String[] argNames = new String[nTotalArgs];
        TypeExpr[] argTypes = new TypeExpr[nTotalArgs];
        TypeExpr[] typePieces = function.getTypeExpr().getTypePieces();
       
        for (int argN = 0; argN < (nTotalArgs-arity); ++argN) {
            argNames[argN] = "$x" + argN;
        }
       
        for (int argN = (nTotalArgs-arity), start = argN; argN < nTotalArgs; ++argN) {           
            argNames[argN] = "$x" + argN;           
            argTypes[argN] = typePieces[argN - start];           
        }
       
        TypeExpr resultType = typePieces[typePieces.length-1];

        CoreFunction coreFunction = CoreFunction.makePrimitiveCoreFunction(function.getName(), argNames, argStrictness, argTypes, resultType, compiler.getCurrentModuleTimeStamp());
              
        //Record the number of actual arguments of the first expression.      
        Expression expr = new Expression.Var(function);

        for (int argN = 0; argN < arity; ++argN) {

            expr = new Expression.Appl(expr, new Expression.Var(QualifiedName.make(currentModuleName, "$x" + argN)));
        }

        coreFunction.setExpression(expr);
       
        storeCoreFunction(coreFunction);       
    }
   
    /**
     * Generate the expression for unsafeCoerce. Operationally it is just the identity function,
     * but it will not be accepted by the type checker so it can't be written directly in CAL.    
     */
    private void generateUnsafeCoerce() {
     
         QualifiedName qualifiedSCName = CAL_Prelude.Functions.unsafeCoerce;

         String[] argNames = new String[]{"$x"};
         //unsafeCoerce is strict.
         boolean[] argStrictness = new boolean[]{true};
         TypeExpr[] argTypes = new TypeExpr[]{TypeExpr.makeParametricType()};
         TypeExpr resultType = TypeExpr.makeParametricType();
        
         CoreFunction coreFunction = CoreFunction.makeCALCoreFunction(qualifiedSCName, argNames, argStrictness, argTypes, resultType, compiler.getCurrentModuleTimeStamp());
              
         coreFunction.setExpression(new Expression.Var(QualifiedName.make(currentModuleName, "$x")));
       
         storeCoreFunction(coreFunction);
     }  
   
    /**
     * Generate the expression for Prelude.if.  We need a functional form for this
     * so that it can be used in a lazy context.  The definition is if x y z = if x y z.
     * This is not a recursive definition! The right hand side is fully applied, so it will be treated
     * as a primitive op by the code generator.
     */
    private void generateFunctionalIf() {
        QualifiedName qualifiedSCName = QualifiedName.make(currentModuleName, "if");

        String[] argNames = new String[]{"$cond", "$then", "$else"};
        //unsafeCoerce is strict.
        boolean[] argStrictness = new boolean[]{true, false, false};
        TypeExpr typeVar = TypeExpr.makeParametricType();
        TypeExpr booleanType = compiler.getTypeChecker().getTypeConstants().getBooleanType();
        TypeExpr[] argTypes = new TypeExpr[] {booleanType, typeVar, typeVar};
       
        CoreFunction coreFunction = CoreFunction.makeCALCoreFunction (qualifiedSCName, argNames, argStrictness, argTypes, typeVar, compiler.getCurrentModuleTimeStamp());
             
        Expression left = new Expression.Var (qualifiedSCName);
        for (int i = 0; i < argNames.length; ++i) {
            Expression.Var var = new Expression.Var (QualifiedName.make(currentModuleName, argNames[i]));
            left = new Expression.Appl (left, var);
        }
        coreFunction.setExpression(left);
      
        storeCoreFunction(coreFunction);
    }

    /**
     * Creates a CoreFunction for a foreign function. This is necessary
     * when foreign functions are applied to less than their full number of arguments.
     * @param function entity of the foreign function
     * @throws IllegalArgumentException if functionEntity is not a foreign function
     * @see #generateBuiltInFunction(QualifiedName)
     */
    private void generateForeignFunction (Function function) {
                           
        int arity = function.getTypeExpr().getArity();      
       
        String functionName = function.getName().getUnqualifiedName();

        QualifiedName qualifiedFunctionName = QualifiedName.make(currentModuleName, functionName);
       
        String[] argNames = new String[arity];
        boolean[] argStrictness = new boolean[arity];
        TypeExpr[] typePieces = function.getTypeExpr().getTypePieces();
        TypeExpr resultType = typePieces[typePieces.length-1];
        TypeExpr[] argTypes = new TypeExpr[arity];
        for (int argN = 0; argN < arity; ++argN) {
            //all parameters of foreign functions are strict
            argNames[argN] = "$x" + argN;
            argStrictness[argN] = true;
            argTypes[argN] = typePieces[argN];           
        }

        CoreFunction coreFunction = CoreFunction.makeForeignCoreFunction(qualifiedFunctionName, argNames, argStrictness, argTypes, resultType, compiler.getCurrentModuleTimeStamp());
              
        //Record the number of actual arguments of the first expression.      
        Expression expr = new Expression.Var(function);

        for (int argN = 0; argN < arity; ++argN) {

            expr = new Expression.Appl(expr, new Expression.Var(QualifiedName.make(currentModuleName, "$x" + argN)));
        }

        coreFunction.setExpression(expr);
       
        storeCoreFunction(coreFunction);
    }
   
    /**
     * Call when the core function has been fully initialized. It is now ready to be used for code generation
     * and packaging within a module.
     * @param coreFunction
     */
    private void storeCoreFunction(CoreFunction coreFunction) {
             
        Packager packager = compiler.getPackager();
        if (packager != null) {
            try {
                packager.store(coreFunction);
            } catch (Packager.PackagerException e) {
                // Couldn't package this supercombinator
                compiler.logMessage(new CompilerMessage(new MessageKind.Error.UnableToPackageSuperCombinator(this.toString())));
            }
        } else {
            // Otherwise emit an INFO error saying what we compiled
            compiler.logMessage(new CompilerMessage(coreFunction.dump()));
        }
    }

    /**
     * Converts a parse tree generated by antlr to our internal representations.
     * Creation date: (6/20/00 10:32:10 AM)
     * @param outerDefnListNode a ParseTreeNode for an outer defn list.    
     */
    void generateCode(ParseTreeNode outerDefnListNode) throws UnableToResolveForeignEntityException {

        try {
            Packager packager = compiler.getPackager();
            packager.switchModule(currentModuleName);
        } catch (Packager.PackagerException e) {
            // Error while packaging module {currentModuleName}.
            compiler.logMessage(new CompilerMessage(new MessageKind.Fatal.ErrorWhilePackagingModule(currentModuleName)));
        }
      
        if (!isAdjunct) {
            generatePreambleCode();
        }

        generateOuterDefnList(outerDefnListNode);
       
        if (ExpressionGenerator.GENERATE_LET_STATISTICS) {
            ExpressionGenerator.totalNLetNonRecs += nLetNonRecs;
            ExpressionGenerator.totalNLetRecsMultiple += nLetRecsMultiple;
            ExpressionGenerator.totalNLetRecsSingle += nLetRecsSingle;
                  
            System.out.println("module " + currentModuleName);
            System.out.println("number of letNonRecs = " + nLetNonRecs);
            System.out.println("number of letRecsMultiple = " + nLetRecsMultiple);
            System.out.println("number of letRecsSingle = " + nLetRecsSingle);
            System.out.println("total of letNonRecs = " + ExpressionGenerator.totalNLetNonRecs);
            System.out.println("total of letRecsMultiple = " + ExpressionGenerator.totalNLetRecsMultiple);
            System.out.println("total of letRecsSingle = " + ExpressionGenerator.totalNLetRecsSingle + '\n');
        }
    }

    /**
     * Converts the various expression parse trees to our internal Expression representation.
     * This is a public method as it is used to generate expression form for the purposes of
     * syntax checking    
     * Creation date: (6/20/00 11:42:12 AM)
     * @param parseTree
     * @return Expression    
     */
    private Expression generateExpr(ParseTreeNode parseTree) {

        int nodeType = parseTree.getType();

        switch (nodeType) {

            case CALTreeParserTokenTypes.VIRTUAL_LET_NONREC:
            {
                ParseTreeNode defnsNode = parseTree.firstChild();
                ParseTreeNode exprNode = defnsNode.nextSibling();
                //note we still need a list in defnsNode because the single definition may have a type
                //declaration so there are 2 items in the list.
                Expression.Let.LetDefn[] letDefnList = generateLetDefnList(defnsNode);
                if (letDefnList.length != 1) {
                    compiler.logMessage(new CompilerMessage(parseTree, new MessageKind.Fatal.MoreThanOneDefinitionInANonRecursiveLet()));   
                }
               
                if (ExpressionGenerator.GENERATE_LET_STATISTICS) {               
                    ++nLetNonRecs;                  
                }
               
                return new Expression.LetNonRec(letDefnList[0], generateExpr(exprNode));
            }
           
            case CALTreeParserTokenTypes.VIRTUAL_LET_REC:
            {
                ParseTreeNode defnsNode = parseTree.firstChild();
                ParseTreeNode exprNode = defnsNode.nextSibling();
                Expression.Let.LetDefn[] letDefnList = generateLetDefnList(defnsNode);
               
                if (ExpressionGenerator.GENERATE_LET_STATISTICS) {               
                    if(letDefnList.length == 1) {
                        ++nLetRecsSingle;                     
                    } else {
                        ++nLetRecsMultiple;                       
                    }
                }
                return new Expression.LetRec(letDefnList, generateExpr(exprNode));
            }

            case CALTreeParserTokenTypes.LAMBDA_DEFN :
            {
                // ExpressionGenerator: Internal Coding Error- unlifted lambda expression.
                compiler.logMessage(new CompilerMessage(parseTree, new MessageKind.Fatal.UnliftedLambdaExpression()));
                break;

                //todoBI should we delete the Expression.Lambda class?
            }

            case CALTreeParserTokenTypes.LITERAL_if :
            {
                ParseTreeNode conditionNode = parseTree.firstChild();
                ParseTreeNode ifTrueNode = conditionNode.nextSibling();
                ParseTreeNode ifFalseNode = ifTrueNode.nextSibling();

                //e = (@ if condition)
                Expression e = new Expression.Appl(new Expression.Var(QualifiedName.make(CAL_Prelude.MODULE_NAME, "if")), generateExpr(conditionNode));
                //returns (@ (@ (@ "if" condExpr) trueExpr) falseExpr)
                return new Expression.Appl(new Expression.Appl(e, generateExpr(ifTrueNode)), generateExpr(ifFalseNode));
            }

            case CALTreeParserTokenTypes.VIRTUAL_DATA_CONSTRUCTOR_CASE:
            {
                return generateDataConstructorCaseExpr(parseTree);             
            }
           
            case CALTreeParserTokenTypes.VIRTUAL_TUPLE_CASE:
            {
                return generateTupleCaseExpr(parseTree);
            }

            case CALTreeParserTokenTypes.VIRTUAL_RECORD_CASE :
            {             
                return generateRecordCaseExpr(parseTree);               
            }

            case CALTreeParserTokenTypes.SELECT_DATA_CONSTRUCTOR_FIELD :
            {
                return generateDataConstructorFieldSelection(parseTree);
            }
           
            case CALTreeParserTokenTypes.APPLICATION :
            {
                // Convert the list of subexpressions into a single subexpression by chaining application
                // subexpressions. If there's only one subexpression in the list then this is the result
                // otherwise, build nested applications
             
                ParseTreeNode firstArgNode = parseTree.firstChild();
                Expression firstArgExpr = generateExpr(firstArgNode);
                if (parseTree.hasExactlyOneChild()){
                    return firstArgExpr;
                }
               
                Expression applicationExpr = firstArgExpr;

                for (final ParseTreeNode exprNode : firstArgNode.nextSiblings()) {

                    applicationExpr = new Expression.Appl(applicationExpr, generateExpr(exprNode));
                }

                return applicationExpr;
            }

            // variables
            case CALTreeParserTokenTypes.QUALIFIED_VAR :
            {
                QualifiedName name = parseTree.toQualifiedName();
               
                // If this is an error call set the error information on the Var expression.
                if (name.equals(CAL_Prelude.Functions.error)) {
                   
                    ErrorInfo errorInfo = parseTree.getErrorInfoForErrorCall();
                    Expression.Var errorVar = new Expression.Var(errorInfo);
                    return new Expression.Appl(errorVar, errorInfo);
                   
                }
               
                if (name.getUnqualifiedName().indexOf('$') == -1) {
                   
                    //not an internal name
                   
                    FunctionalAgent entity = currentModuleTypeInfo.getReachableFunctionOrClassMethod(name);
                    if (entity != null) {
                        return new Expression.Var(entity);
                    }
                   
                    if (!isAdjunct) {
                        throw new IllegalStateException("non-adjunct top-level entities should have EnvEntities...");
                    }
                }
               
                //local function or argument name (does not correspond to a top-level construct.
                return new Expression.Var(name);               
            }

            // data constructors
            case CALTreeParserTokenTypes.QUALIFIED_CONS :
            {      
                DataConstructor dc = getDataConstructor(parseTree.toQualifiedName());
                if (TypeExpr.isEnumType(dc.getTypeConstructor())) {
                    // We optimize enumeration data types by treating them as int.
                    return new Expression.Literal (Integer.valueOf(dc.getOrdinal()));
                } else {
                    return new Expression.Var(dc);
                }
            }

            // literals
            case CALTreeParserTokenTypes.INTEGER_LITERAL :
            case CALTreeParserTokenTypes.FLOAT_LITERAL :
            case CALTreeParserTokenTypes.CHAR_LITERAL :
            case CALTreeParserTokenTypes.STRING_LITERAL :
                return new Expression.Literal(generateLiteral(parseTree));

            case CALTreeParserTokenTypes.TUPLE_CONSTRUCTOR :
            {              
                if (parseTree.hasNoChildren()) {
                    // By definition the Unit data type is an enumeration type so we substitute
                    // an int.
                    DataConstructor dc = getDataConstructor(CAL_Prelude.DataConstructors.Unit);
                    return new Expression.Literal(Integer.valueOf(dc.getOrdinal()));
                }
               
                if (parseTree.hasExactlyOneChild()) {
                    // a parenthesized expression generates the same code as an expression
                    return generateExpr(parseTree.firstChild());
                }
               
                SortedMap<FieldName, Expression> extensionFieldsMap = new TreeMap<FieldName, Expression>();
                int componentN = 1;
               
                for (final ParseTreeNode componentExprNode : parseTree) {

                    extensionFieldsMap.put(FieldName.makeOrdinalField(componentN), generateExpr(componentExprNode));
                    ++componentN;
                }

                return new Expression.RecordExtension(null, extensionFieldsMap);                                             
            }

            case CALTreeParserTokenTypes.LIST_CONSTRUCTOR :
            {
                List<Expression> elementExprList = new ArrayList<Expression>();

                for (final ParseTreeNode elementExprNode : parseTree) {

                    elementExprList.add(generateExpr(elementExprNode));
                }
               
                return generateListExpr(elementExprList);
            }
           
            case CALTreeParserTokenTypes.RECORD_CONSTRUCTOR:
            {
                ParseTreeNode baseRecordNode = parseTree.firstChild();
                baseRecordNode.verifyType(CALTreeParserTokenTypes.BASE_RECORD);          
               
                ParseTreeNode baseRecordExprNode = baseRecordNode.firstChild();
                Expression baseRecordExpr;
                if (baseRecordExprNode != null) {
                    baseRecordExpr = generateExpr(baseRecordExprNode);                   
                } else {
                    baseRecordExpr = null;
                }
               
                ParseTreeNode fieldModificationListNode = baseRecordNode.nextSibling();
                fieldModificationListNode.verifyType(CALTreeParserTokenTypes.FIELD_MODIFICATION_LIST);
               
                             
                SortedMap<FieldName, Expression> extensionFieldsMap = new TreeMap<FieldName, Expression>();              
                SortedMap<FieldName, Expression> updateFieldValuesMap = new TreeMap<FieldName, Expression>();
               
                for (final ParseTreeNode fieldModificationNode : fieldModificationListNode) {
                   
                    fieldModificationNode.verifyType(CALTreeParserTokenTypes.FIELD_EXTENSION, CALTreeParserTokenTypes.FIELD_VALUE_UPDATE);
                   
                    ParseTreeNode fieldNameNode = fieldModificationNode.firstChild();
                  
                    FieldName fieldName = compiler.getTypeChecker().getFieldName(fieldNameNode);
                   
                    ParseTreeNode valueExprNode = fieldNameNode.nextSibling();                   
                    Expression valueExpr = generateExpr(valueExprNode);
                   
                    switch (fieldModificationNode.getType()) {
                        case CALTreeParserTokenTypes.FIELD_EXTENSION:
                        {
                            extensionFieldsMap.put(fieldName, valueExpr);
                            break;
                        }
                        case CALTreeParserTokenTypes.FIELD_VALUE_UPDATE:
                        {
                            updateFieldValuesMap.put(fieldName, valueExpr);
                            break;
                        }
                        default:
                        {
                            fieldModificationNode.unexpectedParseTreeNode();
                            break;
                        }
                    }                         
                }
               
                if (updateFieldValuesMap.isEmpty()) {
                   
                    if (extensionFieldsMap.isEmpty() && baseRecordExpr != null) {
                        return baseRecordExpr;
                    }
                   
                    return new Expression.RecordExtension(baseRecordExpr, extensionFieldsMap);
                }
               
                if (extensionFieldsMap.isEmpty()) {
                    return new Expression.RecordUpdate(baseRecordExpr, updateFieldValuesMap);
                }
               
                return new Expression.RecordExtension(new Expression.RecordUpdate(baseRecordExpr, updateFieldValuesMap), extensionFieldsMap);
            }
           
            case CALTreeParserTokenTypes.SELECT_RECORD_FIELD:
            {              
                //select a field from a record-valued expression e.g. r.field1.
               
                ParseTreeNode exprNode = parseTree.firstChild();
                Expression expr = generateExpr(exprNode);
               
                ParseTreeNode fieldNameNode = exprNode.nextSibling();
                FieldName fieldName = compiler.getTypeChecker().getFieldName(fieldNameNode);               
               
                return new Expression.RecordSelection(expr, fieldName);             
            }
           
            case CALTreeParserTokenTypes.EXPRESSION_TYPE_SIGNATURE:
            {
                return generateExpr(parseTree.firstChild());
            }

            //these operators should be replaced by their functional forms by this point.
            case CALTreeParserTokenTypes.BARBAR :
            case CALTreeParserTokenTypes.AMPERSANDAMPERSAND :
            case CALTreeParserTokenTypes.PLUSPLUS :
            case CALTreeParserTokenTypes.EQUALSEQUALS :
            case CALTreeParserTokenTypes.NOT_EQUALS :
            case CALTreeParserTokenTypes.GREATER_THAN :
            case CALTreeParserTokenTypes.GREATER_THAN_OR_EQUALS :
            case CALTreeParserTokenTypes.LESS_THAN :
            case CALTreeParserTokenTypes.LESS_THAN_OR_EQUALS :
            case CALTreeParserTokenTypes.PLUS :
            case CALTreeParserTokenTypes.MINUS :
            case CALTreeParserTokenTypes.ASTERISK :
            case CALTreeParserTokenTypes.SOLIDUS :
            case CALTreeParserTokenTypes.PERCENT:
            case CALTreeParserTokenTypes.COLON :
            case CALTreeParserTokenTypes.UNARY_MINUS:
            case CALTreeParserTokenTypes.POUND:
            case CALTreeParserTokenTypes.DOLLAR:
            case CALTreeParserTokenTypes.BACKQUOTE:
            default :
            {
                parseTree.unexpectedParseTreeNode();               
                break;
            }
        }

        return null;
    }
   
    /**    
     * @param elementExprList (list of Expression objects)
     * @return Expression form of a CAL list literal with expression elements in elementExprList.
     */
    private Expression generateListExpr(List<Expression> elementExprList) {
       
        Expression applicationExpr = new Expression.Var(getDataConstructor(CAL_Prelude.DataConstructors.Nil));
       
        DataConstructor consDataConstructor = getDataConstructor(CAL_Prelude.DataConstructors.Cons);
        for (int i = elementExprList.size() - 1; i >= 0; --i) {
           
            Expression leftExpr = new Expression.Appl(new Expression.Var(consDataConstructor), elementExprList.get(i));
            applicationExpr = new Expression.Appl(leftExpr, applicationExpr);
        }
       
        return applicationExpr;
    }
    
    /**
     * Tuple-cases are a special type of record-case expression and are encoded as record-cases in the Expression format.
     * @param tupleCaseNode
     * @return Expression.RecordCase
     */
    private Expression.RecordCase generateTupleCaseExpr(ParseTreeNode tupleCaseNode) {
       
        tupleCaseNode.verifyType(CALTreeParserTokenTypes.VIRTUAL_TUPLE_CASE);

        ParseTreeNode conditionNode = tupleCaseNode.firstChild();
        Expression conditionExpr = generateExpr(conditionNode);

        ParseTreeNode altListNode = conditionNode.nextSibling();
        altListNode.verifyType(CALTreeParserTokenTypes.ALT_LIST);

        if (!altListNode.hasExactlyOneChild()) {
            //record-case patterns have only 1 alternative. This should be caught earlier in static analysis.
            throw new IllegalArgumentException();
        }

        ParseTreeNode altNode = altListNode.firstChild();
        altNode.verifyType(CALTreeParserTokenTypes.ALT);     
       
        ParseTreeNode patternNode = altNode.firstChild();
        patternNode.verifyType(CALTreeParserTokenTypes.TUPLE_CONSTRUCTOR);
                       
        int componentN = 1;
        SortedMap<FieldName, String> extensionFieldsMap = new TreeMap<FieldName, String>();
       
        for (final ParseTreeNode patternVarNode : patternNode) {              
           
            String patternVarName = null;
           
            switch (patternVarNode.getType()) {
                case CALTreeParserTokenTypes.VAR_ID :
                {
                    patternVarName = patternVarNode.getText();                   
                    break;
                }

                case CALTreeParserTokenTypes.UNDERSCORE :
                {
                    patternVarName = Expression.RecordCase.WILDCARD_VAR;            
                    break;
                }

                default :
                {
                    patternVarNode.unexpectedParseTreeNode();                   
                    return null;
                }
            }
         
            extensionFieldsMap.put(FieldName.makeOrdinalField(componentN), patternVarName);          
           
            ++componentN;
        }  
      
        ParseTreeNode resultExprNode = patternNode.nextSibling();
        Expression resultExpr = generateExpr(resultExprNode);
       
        String baseRecordVarName = null;
        return new Expression.RecordCase(conditionExpr, baseRecordVarName, extensionFieldsMap, resultExpr);       
    }
   
    /**
     * Generates the Expression associated with a record-case.
     * This will have the form:          
     * case conditionExpr of recordPattern -> resultExpr;
     *
     * @param recordCaseNode
     * @return Expression.RecordCase
     */
    private Expression.RecordCase generateRecordCaseExpr(ParseTreeNode recordCaseNode) {
     
        recordCaseNode.verifyType(CALTreeParserTokenTypes.VIRTUAL_RECORD_CASE);

        ParseTreeNode conditionNode = recordCaseNode.firstChild();
        Expression conditionExpr = generateExpr(conditionNode);

        ParseTreeNode altListNode = conditionNode.nextSibling();
        altListNode.verifyType(CALTreeParserTokenTypes.ALT_LIST);

        if (!altListNode.hasExactlyOneChild()) {
            //record-case patterns have only 1 alternative. This should be caught earlier in static analysis.
            throw new IllegalArgumentException();
        }

        ParseTreeNode altNode = altListNode.firstChild();
        altNode.verifyType(CALTreeParserTokenTypes.ALT);

        ParseTreeNode patternNode = altNode.firstChild();
        patternNode.verifyType(CALTreeParserTokenTypes.RECORD_PATTERN);
      
        ParseTreeNode baseRecordPatternNode = patternNode.firstChild();
        baseRecordPatternNode.verifyType(CALTreeParserTokenTypes.BASE_RECORD_PATTERN);

        ParseTreeNode baseRecordPatternVarNode = baseRecordPatternNode.firstChild();
        String baseRecordVarName = null;     
        if (baseRecordPatternVarNode != null) {

            //a record-polymorphic pattern

            switch (baseRecordPatternVarNode.getType()) {
                case CALTreeParserTokenTypes.VAR_ID :
                {
                    baseRecordVarName = baseRecordPatternVarNode.getText();                       
                    break;
                }

                case CALTreeParserTokenTypes.UNDERSCORE :
                {
                    baseRecordVarName = Expression.RecordCase.WILDCARD_VAR;               
                    break;
                }

                default :
                {
                    baseRecordPatternVarNode.unexpectedParseTreeNode();                   
                    return null;
                }
            }          

        }

        ParseTreeNode fieldBindingVarAssignmentListNode = baseRecordPatternNode.nextSibling();
        SortedMap<FieldName, String> extensionFieldsMap = CALTypeChecker.getFieldBindingsMap(fieldBindingVarAssignmentListNode, null, true);
      
        ParseTreeNode resultExprNode = patternNode.nextSibling();
        Expression resultExpr = generateExpr(resultExprNode);
        
        return new Expression.RecordCase(conditionExpr, baseRecordVarName, extensionFieldsMap, resultExpr);
    }
   
    /**
     * Converts the parse tree for a Let definition to our internal representation.
     * Creation date: (6/20/00 10:32:10 AM)
     * @param parseTree
     * @return Expression.Let.LetDefn
     */
    private Expression.Let.LetDefn generateLetDefn(ParseTreeNode parseTree) {

        parseTree.verifyType(CALTreeParserTokenTypes.LET_DEFN);

        ParseTreeNode optionalCALDocNode = parseTree.firstChild();
        optionalCALDocNode.verifyType(CALTreeParserTokenTypes.OPTIONAL_CALDOC_COMMENT);
        TypeExpr varType = optionalCALDocNode.getFunctionTypeForLocalFunctionCALDocComment();
       
        ParseTreeNode varNode = optionalCALDocNode.nextSibling();

        ParseTreeNode varListNode = varNode.nextSibling();
        if (varListNode.firstChild() != null) {
            //local functions should have all been lifted by now.
            throw new IllegalStateException("Internal coding error- cannot generate an Expression for a local function definition.");           
        }

        ParseTreeNode exprNode = varListNode.nextSibling();

        return new Expression.Let.LetDefn(varNode.getText(), generateExpr(exprNode), varType);
    }

    /**
     * Converts the parse tree for a series of Let definitions to our internal representation.
     * Creation date: (6/20/00 10:32:10 AM)
     * @param parseTree
     * @return Expression.Let.LetDefn []    
     */
    private Expression.Let.LetDefn[] generateLetDefnList(ParseTreeNode parseTree) {

        parseTree.verifyType(CALTreeParserTokenTypes.LET_DEFN_LIST);

        List<LetDefn> defnsList = new ArrayList<LetDefn>();

        for (final ParseTreeNode defnNode : parseTree) {
                
            if (defnNode.getType() == CALTreeParserTokenTypes.LET_DEFN) {           
                defnsList.add(generateLetDefn(defnNode));
            }
        }

        Expression.Let.LetDefn[] defnsArray = new Expression.Let.LetDefn[defnsList.size()];
        defnsList.toArray(defnsArray);

        return defnsArray;
    }

    /**
     * A helper function for converting a literal in a parse tree node to an object.
     * Creation date: (6/20/00 11:59:46 AM)
     * @return Object
     * @param literalNode
     */
    private Object generateLiteral(ParseTreeNode literalNode) {
                  
        switch (literalNode.getType()) {
            case CALTreeParserTokenTypes.INTEGER_LITERAL :                      
           
                //first see if the literal has been stored as a precomputed value.               
                Number literal = literalNode.getLiteralValueForMaybeMinusIntLiteral();
                if (literal == null) {
                    throw new IllegalStateException("integer literals should be resolved at this point");                   
                }
               
                return literal;              
            }

            case CALTreeParserTokenTypes.FLOAT_LITERAL :
            {
                //first see if the literal has been stored as a precomputed value.               
                Object literal = literalNode.getLiteralValueForFloatLiteral();
                if (literal != null) {
                    return literal;
                }               
               
                // Return the value of the literal
                try {
                    // Return the value of a double
                    return new Double(literalNode.getText());
                } catch (NumberFormatException e) {
                    // We failed to parse the FLOAT_LITERAL as a double!
                    // ExpressionGenerator: unable to parse {literalNode.getText()} to a floating point literal.
                    compiler.logMessage(new CompilerMessage(literalNode, new MessageKind.Error.UnableToParseToFloatingPointLiteral(literalNode.getText())));
                }
                break;
            }

            case CALTreeParserTokenTypes.CHAR_LITERAL :
            {
                try {
                    char c = StringEncoder.unencodeChar(literalNode.getText());
                    return Character.valueOf(c);
                } catch (IllegalArgumentException e) {
                    //the encoded character did not have a valid format. This should never happen for CHAR_LITERAL
                    //values returned from the parser.
                    // ExpressionGenerator: unable to parse {literalNode.getText()} to a character literal.
                    compiler.logMessage(new CompilerMessage(literalNode, new MessageKind.Error.UnableToParseToCharacterLiteral(literalNode.getText())));
                }
                break;
            }
           
            case CALTreeParserTokenTypes.STRING_LITERAL:
            {
                String unencodedString = null;
                try {
                    unencodedString = StringEncoder.unencodeString(literalNode.getText());
                                       
                } catch (IllegalArgumentException e) {
                    //the encoded string did not have a valid format. This should never happen for stringLiteral
                    //values returned from the parser.
                        // ExpressionGenerator: unable to parse {literalNode.getText()} to a string literal.
                    compiler.logMessage(new CompilerMessage(literalNode, new MessageKind.Error.UnableToParseToStringLiteral(literalNode.getText())));
                }
               
                if (isStringLiteralTooLong(unencodedString)) {
                    //"The string literal {0} is too long."
                    compiler.logMessage(
                        new CompilerMessage(
                            literalNode, new MessageKind.Error.StringLiteralTooLong(literalNode.getText())));   
                }

                return unencodedString;            
            }

            default :
            {
                literalNode.unexpectedParseTreeNode();                          
                break;
            }
        }

        return null;
    }
   
    /**
     * Checks to see if the String literal is too long. The restriction here comes from the Java bytecode format
     * where String literals are encoded with a modified UTF-8, and the resulting byte array must have length <= 65535 bytes.
     * (see section 4.4.7 of the JVM spec).
     * <p>
     * This test is strictly speaking not necessary, since we will just get a bytecode verification error later.
     * However, the error message from the compiler is much friendlier.
     *   
     * @param unencodedString
     * @return true if the string literal is too long for encoding into Java bytecodes as a String constant.
     */
    private static boolean isStringLiteralTooLong(final String unencodedString) {
       
        final int maxNModifiedUtf8Bytes = 65535;
       
        final int unicodeStringLength = unencodedString.length();      
       
        //Using modified UTF-8, each char will be converted to at most three bytes,
        //so if this test is true, we can dispense with counting the number of modified UTF-8 bytes.
        if (unicodeStringLength <= maxNModifiedUtf8Bytes / 3) {
            return false;
        }
       
        //Using modified UTF-8, each char will be converted to at least one byte,
        //so if this test is true, we can dispense with counting the number of modified UTF-8 bytes.     
        if (unicodeStringLength > maxNModifiedUtf8Bytes) {          
            return true;
        }
       
        //we can't just use unencodedString.getBytes("UTF-8").length because the JVM uses a modified
        //UTF-8 encoding. In particular, the JVM encoding maps the null char to 2 bytes and SMP characters
        //(which are mapped to 4 bytes in the UTF-8 standard) are not handled as true multi-char values.
  
        int nModifiedUtfBytes = 0;
        for (int i = 0; i < unicodeStringLength; ++i) {
            final char c = unencodedString.charAt(i);
            if (c >= '\u0001' && c <= '\u07FF') {
                ++nModifiedUtfBytes;
            } else if (c == '\u0000' || (c >= '\u0080' && c <= '\u07FF')) {
                nModifiedUtfBytes += 2;
            } else {
                nModifiedUtfBytes += 3;
            }
        }
       
        return nModifiedUtfBytes > maxNModifiedUtf8Bytes;            
    }

    /**
     * Creation date: (7/12/01 11:07:32 AM)
     * @param outerDefnListNode
     */
    private void generateOuterDefnList(ParseTreeNode outerDefnListNode) {

        outerDefnListNode.verifyType(CALTreeParserTokenTypes.OUTER_DEFN_LIST);

        for (final ParseTreeNode parseTree : outerDefnListNode) {

            switch (parseTree.getType()) {

                case CALTreeParserTokenTypes.TOP_LEVEL_FUNCTION_DEFN :
                    generateFunctionDefn(parseTree);
                    break;

                case CALTreeParserTokenTypes.DATA_DECLARATION :
                {
                    ParseTreeNode dataConsListNode = parseTree.getChild(4);
                    dataConsListNode.verifyType(CALTreeParserTokenTypes.DATA_CONSTRUCTOR_DEFN_LIST);
                   
                    for (final ParseTreeNode dataConsNode : dataConsListNode) {

                        dataConsNode.verifyType(CALTreeParserTokenTypes.DATA_CONSTRUCTOR_DEFN);

                        ParseTreeNode dataConsNameNode = dataConsNode.getChild(2);
                        dataConsNameNode.verifyType(CALTreeParserTokenTypes.CONS_ID);
                        QualifiedName dataConsName = QualifiedName.make(currentModuleName, dataConsNameNode.getText());
                      
                        generatePack(getDataConstructor(dataConsName));
                    }

                    break;
                }
              
                case CALTreeParserTokenTypes.TOP_LEVEL_TYPE_DECLARATION :
                //Types created by foreign data declarations do not correspond to Pack instructions.
                //Values of this type can only be created by calls to foreign functions.
                case CALTreeParserTokenTypes.FOREIGN_DATA_DECLARATION:
                    break;

                case CALTreeParserTokenTypes.TYPE_CLASS_DEFN:
                case CALTreeParserTokenTypes.INSTANCE_DEFN:
                    //todoBI
                    break;

                case CALTreeParserTokenTypes.FOREIGN_FUNCTION_DECLARATION :
                {
                    ParseTreeNode externalNameNode = parseTree.getChild(1);
                    externalNameNode.verifyType(CALTreeParserTokenTypes.STRING_LITERAL);

                    ParseTreeNode functionNameNode = externalNameNode.nextSibling().nextSibling().firstChild();
                    functionNameNode.verifyType(CALTreeParserTokenTypes.VAR_ID);

                    String functionName = functionNameNode.getText();
                    Function function = currentModuleTypeInfo.getFunction(functionName);                        

                    generateForeignFunction (function);
                    break;
                }
               
                case CALTreeParserTokenTypes.PRIMITIVE_FUNCTION_DECLARATION:
                {
                    break;
                }

                default :
                {
                    parseTree.unexpectedParseTreeNode();                                  
                    break;
                }
            }
        }
    }

    /**
     * Generates a Pack instruction.
     * In the Core language these take the form:
     * Cons = Pack {1, 2}; -- tag = 1 arity = 2
     * Nil = Pack {2, 0};
     * However, in the typed language, the tag can be any object.
     * Cons = Pack {"Cons", 2};
     * Nil = Pack {"Nil", 0};
     *
     * Creation date: (9/28/00 2:58:43 PM)
     * @param dataConstructor   
     */
    private void generatePack(DataConstructor dataConstructor) {

        CoreFunction coreFunction =
            CoreFunction.makeDataConstructorCoreFunction(dataConstructor, compiler.getCurrentModuleTimeStamp());     
              
        Expression expr = new Expression.PackCons(dataConstructor);                           
             
        coreFunction.setExpression(expr);

        // We're done with this data constructor.
        storeCoreFunction(coreFunction);
    }
   
    /**
     * @param dataConsName name of a data constructor.
     * @return DataConstructor
     */
    private DataConstructor getDataConstructor(QualifiedName dataConsName) {
                                                              
        return currentModuleTypeInfo.getVisibleDataConstructor(dataConsName);           
    }

    /**
     * Generate preambles needed for the built-in functions.
     * Creation date: (4/3/01 1:03:25 PM)
     */
    private void generatePreambleCode() throws UnableToResolveForeignEntityException {
       
        if (currentModuleName.equals(CAL_Prelude_internal.MODULE_NAME)) {
                                     
            //Add instructions for the built-in functions that are implemented as primitives
            //in the run-time and need to support partial application.
            //Omitted here are "if", which is handled as a special case, the operators, which are replaced
            //by function primitives, and constant applicative forms.                     
   
            generateBuiltInFunction(CAL_Prelude_internal.Functions.equalsInt);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.notEqualsInt);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.greaterThanInt);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.greaterThanEqualsInt);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.lessThanInt);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.lessThanEqualsInt);
   
            generateBuiltInFunction(CAL_Prelude_internal.Functions.addInt);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.subtractInt);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.multiplyInt);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.divideInt);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.negateInt);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.remainderInt);
   
            //primitive functions for comparison and arithmetic with floats
   
            generateBuiltInFunction(CAL_Prelude_internal.Functions.equalsFloat);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.notEqualsFloat);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.greaterThanFloat);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.greaterThanEqualsFloat);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.lessThanFloat);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.lessThanEqualsFloat);
   
            generateBuiltInFunction(CAL_Prelude_internal.Functions.addFloat);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.subtractFloat);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.multiplyFloat);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.divideFloat);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.negateFloat);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.remainderFloat);
                    
            //primitive functions for comparison and arithmetic with doubles
   
            generateBuiltInFunction(CAL_Prelude_internal.Functions.equalsDouble);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.notEqualsDouble);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.greaterThanDouble);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.greaterThanEqualsDouble);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.lessThanDouble);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.lessThanEqualsDouble);
   
            generateBuiltInFunction(CAL_Prelude_internal.Functions.addDouble);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.subtractDouble);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.multiplyDouble);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.divideDouble);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.negateDouble);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.remainderDouble);
           
            // primitive functions for comparison and arithmetic with longs
   
            generateBuiltInFunction(CAL_Prelude_internal.Functions.equalsLong);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.notEqualsLong);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.greaterThanLong);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.greaterThanEqualsLong);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.lessThanLong);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.lessThanEqualsLong);
   
            generateBuiltInFunction(CAL_Prelude_internal.Functions.addLong);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.subtractLong);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.multiplyLong);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.divideLong);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.negateLong)
            generateBuiltInFunction(CAL_Prelude_internal.Functions.remainderLong)
   
            // primitive functions for comparison with shorts
   
            generateBuiltInFunction(CAL_Prelude_internal.Functions.equalsShort);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.notEqualsShort);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.greaterThanShort);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.greaterThanEqualsShort);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.lessThanShort);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.lessThanEqualsShort);
   
            // primitive functions for comparison with bytes
   
            generateBuiltInFunction(CAL_Prelude_internal.Functions.equalsByte);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.notEqualsByte);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.greaterThanByte);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.greaterThanEqualsByte);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.lessThanByte);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.lessThanEqualsByte);
                 
            //primitive functions for char comparison
   
            generateBuiltInFunction(CAL_Prelude_internal.Functions.equalsChar);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.notEqualsChar);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.greaterThanChar);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.greaterThanEqualsChar);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.lessThanChar);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.lessThanEqualsChar);
   
            //record functions                                      
            generateBuiltInFunction(CAL_Prelude_internal.Functions.compareRecord, new boolean[]{true, true, true});
            generateBuiltInFunction(CAL_Prelude_internal.Functions.equalsRecord, new boolean[]{true, true, true});           
            generateBuiltInFunction(CAL_Prelude_internal.Functions.recordFromJListPrimitive, new boolean[]{true, true});
            generateBuiltInFunction(CAL_Prelude_internal.Functions.recordFromJMapPrimitive, new boolean[]{true, true});
            generateBuiltInFunction(CAL_Prelude_internal.Functions.recordToJListPrimitive, new boolean[]{true, true});            
            generateBuiltInFunction(CAL_Prelude_internal.Functions.notEqualsRecord, new boolean[]{true, true, true});                      
            generateBuiltInFunction(CAL_Prelude_internal.Functions.recordTypeDictionary, new boolean[]{true, false});
           
           
            generateBuiltInFunction(CAL_Prelude_internal.Functions.objectToCalValue);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.calValueToObject, new boolean[] {false});
            generateBuiltInFunction(CAL_Prelude_internal.Functions.makeComparator);
            generateBuiltInFunction(CAL_Prelude_internal.Functions.makeEquivalenceRelation);
            generateBuiltInFunction(CAL_Prelude.Functions.makeCalFunction);
              
            generateBuiltInFunction(CAL_Prelude.Functions.eager);
                       
            generateBuiltInFunction(CAL_Prelude.Functions.seq, new boolean[]{true, false});
            generateBuiltInFunction(CAL_Prelude.Functions.deepSeq, new boolean[]{true, false});
            generateBuiltInFunction(CAL_Prelude_internal.Functions.ordinalValue, new boolean[]{true});
           
            generateBuiltInFunction(CAL_Prelude.Functions.error, new boolean[]{false, true});
           
            generateBuiltInFunction(CAL_Prelude_internal.Functions.executionContext);
           
            //unsafeCoerce is operationally just the identity function. We need to encode it specially because
            //it won't make it past the type checker!
            generateUnsafeCoerce();
           
            // This is the functional form of the conditional primitive.
            generateFunctionalIf();
           
        } else if (currentModuleName.equals(CAL_Dynamic_internal.MODULE_NAME)) {
          
            generateBuiltInFunction(CAL_Dynamic_internal.Functions.fieldValuesPrimitive);   
            generateBuiltInFunction(CAL_Dynamic_internal.Functions.recordFieldIndex);
            generateBuiltInFunction(CAL_Dynamic_internal.Functions.recordFieldTypePrimitive, new boolean[]{true, false, true});
            generateBuiltInFunction(CAL_Dynamic_internal.Functions.recordFieldValuePrimitive, new boolean[]{true, true});           
            generateBuiltInFunction(CAL_Dynamic_internal.Functions.appendRecordPrimitive, new boolean[]{true, true});
            generateBuiltInFunction(CAL_Dynamic_internal.Functions.insertOrdinalRecordFieldPrimitive, new boolean[]{true, true, false});
            generateBuiltInFunction(CAL_Dynamic_internal.Functions.insertTextualRecordFieldPrimitive, new boolean[]{true, true, false})
           
        } else if (currentModuleName.equals(CAL_Record_internal.MODULE_NAME)) {
           
            generateBuiltInFunction(CAL_Record_internal.Functions.recordToJRecordValuePrimitive, new boolean[]{true, true});
            generateBuiltInFunction(CAL_Record_internal.Functions.strictRecordPrimitive);
            generateBuiltInFunction(CAL_Record_internal.Functions.fieldNamesPrimitive);
            generateBuiltInFunction(CAL_Record_internal.Functions.hasFieldPrimitive);  
            
            generateBuiltInFunction(CAL_Record_internal.Functions.buildListPrimitive, new boolean[]{true,true,true});
            generateBuiltInFunction(CAL_Record_internal.Functions.buildRecordPrimitive, new boolean[]{true,true,true});
           
        } else if (currentModuleName.equals(CAL_List_internal.MODULE_NAME)) {
           
            generateBuiltInFunction(CAL_List_internal.Functions.makeIterator);
           
        } else if (currentModuleName.equals(CAL_Exception_internal.MODULE_NAME)) {
           
            //primThrow and primCatch are both declared having all their arguments non-plinged in order to simplify the implementation
            //of their custom lecc functions. In fact, throw and catch are both strict in their first argument.
            generateBuiltInFunction(CAL_Exception_internal.Functions.primThrow, new boolean[] {false});
            generateBuiltInFunction(CAL_Exception_internal.Functions.primCatch, new boolean[] {false, false});
           
        } else if (currentModuleName.equals(CAL_QuickCheck_internal.MODULE_NAME)) {
           
            generateBuiltInFunction(CAL_QuickCheck_internal.Functions.arbitraryRecordPrimitive, new boolean[]{true,true,true});
            generateBuiltInFunction(CAL_QuickCheck_internal.Functions.coarbitraryRecordPrimitive, new boolean[]{true,true,true});

        } else if (currentModuleName.equals(CAL_Debug_internal.MODULE_NAME)) {

            generateBuiltInFunction(CAL_Debug_internal.Functions.showRecord, new boolean[]{true, true});
           
        } else if (currentModuleName.equals(CAL_Bits_internal.MODULE_NAME))  {
           
            generateBuiltInFunction(CAL_Bits_internal.Functions.bitwiseAndInt);
            generateBuiltInFunction(CAL_Bits_internal.Functions.bitwiseOrInt);
            generateBuiltInFunction(CAL_Bits_internal.Functions.bitwiseXorInt);
            generateBuiltInFunction(CAL_Bits_internal.Functions.complementInt);
            generateBuiltInFunction(CAL_Bits_internal.Functions.shiftLInt);
            generateBuiltInFunction(CAL_Bits_internal.Functions.shiftRInt);
            generateBuiltInFunction(CAL_Bits_internal.Functions.shiftRUnsignedInt);

            generateBuiltInFunction(CAL_Bits_internal.Functions.bitwiseAndLong);
            generateBuiltInFunction(CAL_Bits_internal.Functions.bitwiseOrLong);
            generateBuiltInFunction(CAL_Bits_internal.Functions.bitwiseXorLong);
            generateBuiltInFunction(CAL_Bits_internal.Functions.complementLong);
            generateBuiltInFunction(CAL_Bits_internal.Functions.shiftLLong);
            generateBuiltInFunction(CAL_Bits_internal.Functions.shiftRLong);
            generateBuiltInFunction(CAL_Bits_internal.Functions.shiftRUnsignedLong);
           
        }
       
        generateDerivedForeignInputFunctions();
       
        generateTypeableInstanceFunctions();
    }
   
    /**
     * Every foreign type T that has a derived instance for Inputable defines a hidden function $inputT.
     * This hidden function is defined as follows:
     *
     * if the foreign type T corresponds to one of the primitive unboxed Java types such as int
     * $inputT :: JObject -> T;
     * $inputT !x = Prelude.inputIntFromJObject x;
     * In particular, this will be recognized as an "alias" and treated simply as Prelude.inputIntFromJObject by the runtime.
     * The code shown above would not pass the type checker, which is part of the reason we're generating it directly in
     * the expression generator.
     *
     * in the case where T corresponds to a Java object type, then    
     * $inputT !x = cast<T> x;
     * cast<T> is supported in the Expression format but not directly in the CAL language.    
     */
    private void generateDerivedForeignInputFunctions() throws UnableToResolveForeignEntityException {
                     
        final int nTypeConstructors = currentModuleTypeInfo.getNTypeConstructors();  
       
        for (int i = 0; i < nTypeConstructors; ++i) {
           
            //define left hand side of the CoreFunction definition           
           
            final TypeConstructor typeCons = currentModuleTypeInfo.getNthTypeConstructor(i);
            ForeignTypeInfo foreignTypeInfo = typeCons.getForeignTypeInfo();
            if (foreignTypeInfo != null &&
                typeCons.hasDerivingClause(CAL_Prelude.TypeClasses.Inputable)) {               
               
                final QualifiedName typeConsName = typeCons.getName();           
                final QualifiedName inputableInstanceFunctionName = ClassInstance.makeInternalInstanceMethodName("input", typeConsName);                              
               
                final String[] argNames = {"$x"};
                final boolean[] argStrictness = {true};
                               
                final TypeExpr[] argTypes = {TypeExpr.makeNonParametricType(currentModuleTypeInfo.getVisibleTypeConstructor(CAL_Prelude.TypeConstructors.JObject))};
                final TypeExpr resultType = TypeExpr.makeNonParametricType(typeCons);
               
                //the "isPrimitive" flag is false because these are not declared via a primitive function declaration in a module.
                final CoreFunction coreFunction = CoreFunction.makeCALCoreFunction(inputableInstanceFunctionName, argNames, argStrictness, argTypes, resultType, compiler.getCurrentModuleTimeStamp());
                               
                //define the Expression part of the CoreFunction definition (i.e. the right hand side)           
                                               
                Expression expr = null;
               
                final Class<?> foreignClass = foreignTypeInfo.getForeignType();
               
                if (foreignClass.isPrimitive()) {
                   
                    FunctionalAgent castFunction;               
                    if (foreignClass == char.class) {
                        castFunction = getPreludeFunction("inputChar");  
                       
                    } else if (foreignClass == boolean.class) {
                        castFunction = getPreludeFunction("inputBoolean");
                       
                    } else if (foreignClass == byte.class) {
                        castFunction = getPreludeFunction("inputByte");
                       
                    } else if (foreignClass == short.class) {
                        castFunction = getPreludeFunction("inputShort");
                       
                    } else if (foreignClass == int.class) {
                        castFunction = getPreludeFunction("inputInt");
                       
                    } else if (foreignClass == long.class) {
                        castFunction = getPreludeFunction("inputLong");
                       
                    } else if (foreignClass == float.class) {
                        castFunction = getPreludeFunction("inputFloat");
                       
                    } else if (foreignClass == double.class) {
                        castFunction = getPreludeFunction("inputDouble");
                       
                    } else {
                        //we don't handle void.class- it is an earlier compilation error for this.
                        throw new IllegalStateException();
                    }
                   
                    expr =
                        new Expression.Appl(
                            new Expression.Var(castFunction),
                            new Expression.Var(QualifiedName.make(currentModuleName, "$x")));
                } else {
                   
                    expr = Expression.Cast.make(foreignClass, new Expression.Var(QualifiedName.make(currentModuleName, "$x")));
                }
                                                                                
                coreFunction.setExpression(expr);       
                storeCoreFunction(coreFunction);                  
            }          
        }    
    }
   
    /**    
     * A helper function for retrieving private functions from the Prelude.
     * @param preludeFunctionName    
     * @return FunctionalAgent
     */
    private FunctionalAgent getPreludeFunction(String preludeFunctionName) {
       
        if (currentModuleName.equals(CAL_Prelude.MODULE_NAME)) {
            return currentModuleTypeInfo.getFunction(preludeFunctionName);
        } else {
            return currentModuleTypeInfo.getImportedModule(CAL_Prelude.MODULE_NAME).getFunction(preludeFunctionName);
        }       
    }
   
    /**
     * Every type T constructor is automatically an instance of the type class Typeable.
     * for example, there is an automatic instance declaration added by the compiler for:
     *
     * instance (Typeable a, Typeable b) => Typeable (Either a b) where
     *     typeOf = $typeOfEither;
     *     ;
     *
     * this function defines the hidden internal function $typeOfEither etc.
     *
     * Note: unlike the derived instance functions, the instance functions such as $typeOfEither
     * are defined directly as core functions and do not go through type-checking. This is because
     * we implement them more efficiently than would be possible as CAL functions that need to pass
     * the type-checking phase.         
     */
    private void generateTypeableInstanceFunctions() throws UnableToResolveForeignEntityException {
             
        //typeOfEither was previously defined in the Prelude as:
        //
        //typeOfEither :: (Typeable a, Typeable b) => Either a b -> TypeRep;
        //private typeOfEither x =
        //    let
        //        leftType :: Either a b -> a;
        //        leftType = undefined;
        //        rightType :: Either a b -> b;
        //        rightType = undefined;
        //    in
        //        TypeRep "Cal.Core.Prelude.Either" [typeOf (leftType x), typeOf (rightType x)];
        //
        //we now internally can give a more efficient core language definition (since we
        //don't need the local variables to get past the type checker. Note that however we
        //do the dictionary resoloution ourselves!
        //
        //private $typeOfEither $dict1 $dict2 $x =
        //   Prelude.TypeRep "Cal.Core.Prelude.Either" [Prelude.typeOf $dict1 Prelude.undefined, Prelude.typeOf $dict2 Prelude.undefined]; 
        //
        //because Typeable has a single class method, with no superclasses, Prelude.typeOf $dict1 simple reduces to $dict1, so
        //we actually generate this reduced version.
        //private $typeOfEither $dict1 $dict2 $x =
        //   Prelude.TypeRep "Cal.Core.Prelude.Either" [$dict1 Prelude.undefined, $dict2 Prelude.undefined]; 
        //
        //There are special cases for some of the data constructors of TypeRep.
        //
        //private $typeOfList $dict1 $x  = Prelude.ListTypeRep (Prelude.typeOf $dict1 Prelude.undefined);
        //private $typeOfUnit $x = Prelude.UnitTypeRep;
        //private $typeOfFunction $dict1 $dict2 = Prelude.FunctionTypeRep (Prelude.typeOf $dict1 Prelude.undefined) ((Prelude.typeOf $dict2 Prelude.undefined)
        //private $typeOfBoolean $x = Prelude.BooleanTypeRep;
        //private $typeOfInt $x = Prelude.IntTypeRep;
        //private $typeOfByte $x = Prelude.ByteTypeRep;
        //private $typeOfShort $x = Prelude.ShortTypeRep;
        //private $typeOfLong $x = Prelude.LongTypeRep;
        //private $typeOfFloat $x = Prelude.FloatTypeRep;
        //private $typeOfDouble $x = Prelude.DoubleTypeRep;
        //private $typeOfChar $x = Prelude.CharTypeRep;
        //private $typeOfString $x = Prelude.StringTypeRep;
                       
        final ModuleTypeInfo currentModuleTypeInfo = compiler.getPackager().getModuleTypeInfo(currentModuleName);   
        final int nTypeConstructors = currentModuleTypeInfo.getNTypeConstructors();
        if (nTypeConstructors == 0) {
            return;
        }
       
        //the TypeRep type (and its data constructors) are defined in the Prelude module. Many of these data constructors
        //are private so we need to query for them directly from the Prelude's ModuleTypeInfo
        final boolean isPreludeModule = currentModuleName.equals(CAL_Prelude.MODULE_NAME);
        ModuleTypeInfo preludeModuleTypeInfo;
        if (currentModuleName.equals(CAL_Prelude.MODULE_NAME)) {
            preludeModuleTypeInfo = currentModuleTypeInfo;           
        } else {       
            preludeModuleTypeInfo = currentModuleTypeInfo.getImportedModule(CAL_Prelude.MODULE_NAME);                             
        }       
              
        final Function undefinedFunction = preludeModuleTypeInfo.getFunction("undefined");
        TypeExpr resultType = TypeExpr.makeNonParametricType(preludeModuleTypeInfo.getTypeConstructor("TypeRep"));
       
        for (int i = 0; i < nTypeConstructors; ++i) {
           
            //define left hand side of the CoreFunction definition           
           
            final TypeConstructor typeCons = currentModuleTypeInfo.getNthTypeConstructor(i)
            final QualifiedName typeConsName = typeCons.getName();           
            final QualifiedName typeOfInstanceFunctionName = ClassInstance.makeInternalInstanceMethodName("typeOf", typeConsName);
           
            final int typeArity = typeCons.getTypeArity();
           
            final int arity = typeArity + 1;
           
            final String[] argNames = new String[arity];
            for (int j = 0; j < typeArity; ++j) {
                argNames[j] = "$dict" + j;
            }
            argNames[typeArity] = "$x";
           
            //all args are non-strict
            final boolean[] argStrictness = new boolean[arity];
            Arrays.fill(argStrictness, false);
           
            //all types are non-determined and so null. Note: potentially we could give a type to the last element (the $x in the example)
            //but there is not advantage to that since it is not a strict argument.
            final TypeExpr[] argTypes = new TypeExpr[arity];
           
            final CoreFunction coreFunction = CoreFunction.makeCALCoreFunction (typeOfInstanceFunctionName, argNames, argStrictness, argTypes, resultType, compiler.getCurrentModuleTimeStamp());
           
           
            //define the Expression part of the CoreFunction definition (i.e. the right hand side)           
           
            //[$dict1 Prelude.undefined, $dict2 Prelude.undefined, ...]           
            final List<Expression> elementExprList = new ArrayList<Expression>(typeArity); //List of Expression
            for (int j = 0; j < typeArity; ++ j) {
                elementExprList.add(
                    new Expression.Appl(                       
                        new Expression.Var(QualifiedName.make(currentModuleName, argNames[j])),
                        new Expression.Var(undefinedFunction)));
            }
            final Expression listExpr = generateListExpr(elementExprList);
           
            Expression expr = null;
           
            //There is special handling for some types in the Prelude for efficiency reasons.
            if (isPreludeModule) {
                              
                if (typeConsName.equals(CAL_Prelude.TypeConstructors.Function)) {
                   
                    //private $typeOfFunction $dict1 $dict2 = Prelude.FunctionTypeRep (Prelude.typeOf $dict1 Prelude.undefined) ((Prelude.typeOf $dict2 Prelude.undefined)
                    expr =
                        new Expression.Appl(
                            new Expression.Appl(
                                new Expression.Var(preludeModuleTypeInfo.getDataConstructor(CAL_Prelude_internal.DataConstructors.FunctionTypeRep.getUnqualifiedName())),
                                elementExprList.get(0)),
                            elementExprList.get(1));
                   
                } else if (typeConsName.equals(CAL_Prelude.TypeConstructors.List)) {
                   
                    //private $typeOfList $dict1 $x = Prelude.ListTypeRep (Prelude.typeOf $dict1 Prelude.undefined);
                    expr =
                        new Expression.Appl(
                            new Expression.Var(preludeModuleTypeInfo.getDataConstructor(CAL_Prelude_internal.DataConstructors.ListTypeRep.getUnqualifiedName())),
                            elementExprList.get(0));
                   
                } else if (typeConsName.equals(CAL_Prelude.TypeConstructors.Unit)) {                   
                    //private $typeOfUnit $x = Prelude.UnitTypeRep;
                    expr = new Expression.Var(preludeModuleTypeInfo.getDataConstructor(CAL_Prelude_internal.DataConstructors.UnitTypeRep.getUnqualifiedName()));
                   
                } else if (typeConsName.equals(CAL_Prelude.TypeConstructors.Boolean)) {
                    //private $typeOfBoolean $x = Prelude.BooleanTypeRep;
                    expr = new Expression.Var(preludeModuleTypeInfo.getDataConstructor(CAL_Prelude_internal.DataConstructors.BooleanTypeRep.getUnqualifiedName()));
                } else if (typeConsName.equals(CAL_Prelude.TypeConstructors.Int)) {
                    //private $typeOfInt $x = Prelude.IntTypeRep;
                    expr = new Expression.Var(preludeModuleTypeInfo.getDataConstructor(CAL_Prelude_internal.DataConstructors.IntTypeRep.getUnqualifiedName()));
                } else if (typeConsName.equals(CAL_Prelude.TypeConstructors.Byte)) {
                    //private $typeOfByte $x = Prelude.ByteTypeRep;
                    expr = new Expression.Var(preludeModuleTypeInfo.getDataConstructor(CAL_Prelude_internal.DataConstructors.ByteTypeRep.getUnqualifiedName()));
                } else if (typeConsName.equals(CAL_Prelude.TypeConstructors.Short)) {
                    //private $typeOfShort $x = Prelude.ShortTypeRep;
                    expr = new Expression.Var(preludeModuleTypeInfo.getDataConstructor(CAL_Prelude_internal.DataConstructors.ShortTypeRep.getUnqualifiedName()));
                } else if (typeConsName.equals(CAL_Prelude.TypeConstructors.Long)) {
                    //private $typeOfLong $x = Prelude.LongTypeRep;
                    expr = new Expression.Var(preludeModuleTypeInfo.getDataConstructor(CAL_Prelude_internal.DataConstructors.LongTypeRep.getUnqualifiedName()));
                } else if (typeConsName.equals(CAL_Prelude.TypeConstructors.Float)) {
                    //private $typeOfFloat $x = Prelude.FloatTypeRep;
                    expr = new Expression.Var(preludeModuleTypeInfo.getDataConstructor(CAL_Prelude_internal.DataConstructors.FloatTypeRep.getUnqualifiedName()));
                } else if (typeConsName.equals(CAL_Prelude.TypeConstructors.Double)) {
                    //private $typeOfDouble $x = Prelude.DoubleTypeRep;
                    expr = new Expression.Var(preludeModuleTypeInfo.getDataConstructor(CAL_Prelude_internal.DataConstructors.DoubleTypeRep.getUnqualifiedName()));
                } else if (typeConsName.equals(CAL_Prelude.TypeConstructors.Char)) {
                    //private $typeOfChar $x = Prelude.CharTypeRep;
                    expr = new Expression.Var(preludeModuleTypeInfo.getDataConstructor(CAL_Prelude_internal.DataConstructors.CharTypeRep.getUnqualifiedName()));
                } else if (typeConsName.equals(CAL_Prelude.TypeConstructors.String)) {
                    //private $typeOfString $x = Prelude.StringTypeRep;
                    expr = new Expression.Var(preludeModuleTypeInfo.getDataConstructor(CAL_Prelude_internal.DataConstructors.StringTypeRep.getUnqualifiedName()));
                }
            }

            //Handle the Prelude types not treated above, as well as non-Prelude types.
            //There are some special cases here as well.
            if (expr == null) {               
              
                ForeignTypeInfo foreignTypeInfo = typeCons.getForeignTypeInfo();
                if (foreignTypeInfo != null) {
                   
                    final Class<?> foreignType = foreignTypeInfo.getForeignType();
                   
                    //private $typeOf<?> $x = Prelude.ForeignTypeRep "<?>" (Prelude.executionContext_getForeignClass $ec "<?>" "<foreign name as returned by Class.getName()>");
                    expr =
                        new Expression.Appl(
                            new Expression.Appl(
                                new Expression.Var(preludeModuleTypeInfo.getDataConstructor(CAL_Prelude_internal.DataConstructors.ForeignTypeRep.getUnqualifiedName())),
                                new Expression.Literal(typeConsName.getQualifiedName())),
                                new Expression.Appl(
                                    new Expression.Appl(
                                        new Expression.Appl(
                                            new Expression.Var(preludeModuleTypeInfo.getFunction(CAL_Prelude_internal.Functions.executionContext_getForeignClass.getUnqualifiedName())),
                                            new Expression.Var(preludeModuleTypeInfo.getFunction(CAL_Prelude_internal.Functions.executionContext.getUnqualifiedName()))),
                                        new Expression.Literal(typeConsName.getQualifiedName())),
                                    new Expression.Literal(foreignType.getName())));                       
                               
                } else {                                                          
                    //private $typeOfEither $dict0 $dict1 $x =
                    //    Prelude.AlgebraicTypeRep "Cal.Core.Prelude.Either" (Prelude.listToTypeReps [$dict1 Prelude.undefined, $dict2 Prelude.undefined]);
                    expr =
                        new Expression.Appl(
                            new Expression.Appl(
                                new Expression.Var(preludeModuleTypeInfo.getDataConstructor(CAL_Prelude_internal.DataConstructors.AlgebraicTypeRep.getUnqualifiedName())),
                                new Expression.Literal(typeConsName.getQualifiedName())),
                            new Expression.Appl(
                                new Expression.Var(preludeModuleTypeInfo.getFunction(CAL_Prelude_internal.Functions.listToTypeReps.getUnqualifiedName())),
                                listExpr));                   
                }
            }
           
            if (expr == null) {
                throw new IllegalStateException();
            }
                                
            coreFunction.setExpression(expr);       
            storeCoreFunction(coreFunction);  
        }    
    }
  
    /**
     * Converts a function parse tree generated by antlr to our internal representations.
     * Creation date: (6/20/00 10:32:10 AM)
     * @param functionNode
     */
    private void generateFunctionDefn(ParseTreeNode functionNode) {

        functionNode.verifyType(CALTreeParserTokenTypes.TOP_LEVEL_FUNCTION_DEFN);
        ParseTreeNode functionNameNode = functionNode.getChild(2);
        String functionName = functionNameNode.getText();
       
        /*
        //this is a good place to dump the parse tree of a function immediately
        //prior to expression generation.
                
        if (functionName.equals("overloadingTest21")) {
            try {
           
            functionNode.xmlDumpToFile("d:\\dev\\" + functionName + ".xml");
            } catch (java.io.IOException e) {
                System.out.println(e);
            }
        }
        */

        // Collect formal arguments and add to activation record
        ParseTreeNode paramListNode = functionNameNode.nextSibling();
        paramListNode.verifyType(CALTreeParserTokenTypes.FUNCTION_PARAM_LIST);
        final int nArgs = paramListNode.getNumberOfChildren();
        int argN = 0;
        // This is used to figure out what the 'return' type of the expression is. There
        // was a problem where "Sql.toTypedExpr :: Expr -> TypedExpr a" defined as
        // toTypedExpr = TypedExpr. The old code just called TypeExpr.getReturnType. The
        // correct way is to remove the types corresponding to explicit arguments.
        int explicitListedArgN = 0;
        // The type of the dictionary and lifted arguments are not in the result type of the expression.
        // For all other cases, the types are args are in the result type. The count is needed to
        // adjust the code that determines the return type of the function after all the args
        // are applied.
        int implicitArgN = 0;
        String [] argNames = new String[nArgs];
        boolean [] argStrictness = new boolean[nArgs];
        TypeExpr [] argTypes = new TypeExpr[nArgs];
       
        for (final ParseTreeNode varNode : paramListNode) {

            String argName = varNode.getText();
            if (argName.indexOf('$') == -1) {
                //at this point all arguments must have unique names, or be internal (start with a $).
                //this is a sanity check.
                throw new IllegalStateException();
            }
            argNames[argN] = argName;
           
            argStrictness[argN] = varNode.getType() == CALTreeParserTokenTypes.STRICT_PARAM;
          
            TypeExpr argType = compiler.getTypeChecker().getFunctionBoundNameType(argName);
            if (argType == null && argName.charAt(0) != '$') {
                //the only arguments without types should be the internal arguments
                throw new IllegalStateException();
            }
            explicitListedArgN++;
            if (argType == null || varNode.getIsLiftedArgument()){
                implicitArgN += explicitListedArgN;
                explicitListedArgN = 0;
            }
            argTypes[argN] = argType;
                      
            ++argN;
        }
       
        QualifiedName qualifiedFunctionName = QualifiedName.make(currentModuleName, functionName);

        TypeExpr resultType = null;
        if (nArgs > 0 &&
            functionName.startsWith(ClassInstance.DICTIONARY_FUNCTION_PREFIX) &&
            argNames[nArgs - 1].equals(OverloadingResolver.DICTIONARY_FUNCTION_INDEX)) {
           
            //dictionary functions such as
            //$dictEq#Maybe dictVar !$i = ...
            //don't go through type checking, but nevertheless we know that the index
            //argument $i is strict and of Int type. We set this so that the runtime can unbox this
            //argument.
           
            argTypes[nArgs - 1] = compiler.getTypeChecker().getTypeConstants().getIntType();
            resultType = TypeExpr.makeParametricType();
        }
        else{
            TypeExpr functionType = compiler.getTypeChecker().getFunctionBoundNameType(functionName);
            if (functionType == null){
                // Lambda expressions are not found in the bound names map.
                functionType = paramListNode.getTypeExprForFunctionParamList();
            }

            /**
             * The dictionary and lifted arguments do not show up in the type expression. Account for this
             * when determining the return type.
             */
            if (implicitArgN > 0){
                TypeExpr[] typePieces = functionType.getTypePieces(argTypes.length - implicitArgN);
                resultType = typePieces[typePieces.length - 1];               
             }
            else{
//                resultType = functionType.getResultType();
                /**
                 * Strip off the types associated with the arguments. This is done because the
                 * return type effectively returns the last type in the list. This works find except for
                 * functions defined like,
                 *
                 *      map = prelude.map;
                 *     
                 * There are no arguments to map and the return type is set to [a] and not the correct type.
                 */

                TypeExpr[] typePieces = functionType.getTypePieces(nArgs);
                resultType = typePieces[typePieces.length - 1];
            }

        }

        // Copy the types making sure that type var remain synchronized.
        {
            TypeExpr[] argAndResultTypes = new TypeExpr[argTypes.length + 1];
            for(int i = 0; i < argTypes.length; ++i){
                argAndResultTypes[i] = argTypes[i];
            }
            argAndResultTypes[argTypes.length] = resultType;
           
            argAndResultTypes = TypeExpr.copyTypeExprs(argAndResultTypes);
           
            for(int i = 0; i < argTypes.length; ++i){
                argTypes[i] = argAndResultTypes[i];
            }
            resultType = argAndResultTypes[argTypes.length];
        }       
       
        CoreFunction coreFunction = CoreFunction.makeCALCoreFunction(qualifiedFunctionName, argNames, argStrictness, argTypes, resultType, compiler.getCurrentModuleTimeStamp());
      
        ParseTreeNode exprNode = paramListNode.nextSibling();

        // Compile code generated by expression processing
        // Invoke the SC compilation scheme with the outermost expression
        // Code is compiled into the current code contribution (supercombinator)
        Expression expr = generateExpr(exprNode);
       
        coreFunction.setExpression(expr);

        // We're done with this supercombinator.
        storeCoreFunction(coreFunction);
    }

    /**
     * Converts the AST for a list of switches within a Case expression to our internal representation.
     * @return Expression.Switch.SwitchAlt[]
     * @param altListNode
     */
    private Expression.Switch.SwitchAlt[] generateSwitchAlts(ParseTreeNode altListNode) {
       
        altListNode.verifyType(CALTreeParserTokenTypes.ALT_LIST);
       
        List<SwitchAlt> altsList = new ArrayList<SwitchAlt>();
       
        for (final ParseTreeNode altNode : altListNode) {
           
            altNode.verifyType(CALTreeParserTokenTypes.ALT);
           
            ParseTreeNode patternNode = altNode.firstChild();
            ParseTreeNode exprNode = patternNode.nextSibling();
           
            Expression generatedExpr = generateExpr(exprNode);
           
            switch (patternNode.getType()) {
               
                case CALTreeParserTokenTypes.PATTERN_CONSTRUCTOR :
                {
                    ParseTreeNode qualifiedConsNameListNode = patternNode.firstChild();
                   
                    // Gather the tags for this pattern.
                    List<Object> altTags = new ArrayList<Object>();
                    for (final ParseTreeNode qualifiedConsNameNode : qualifiedConsNameListNode) {
                       
                        QualifiedName dataConsName = qualifiedConsNameNode.toQualifiedName();
                        DataConstructor dataCons = getDataConstructor(dataConsName);
                        altTags.add(dataCons);
                    }
                   
                    ParseTreeNode argBindingsNode = qualifiedConsNameListNode.nextSibling();
                   
                    // Add alts for the bound vars.
                    switch (argBindingsNode.getType()) {
                       
                        case CALTreeParserTokenTypes.PATTERN_VAR_LIST:
                        {
                            altsList.add(new Expression.Switch.SwitchAlt.Positional(altTags, getPositionToVarNameMap(argBindingsNode), generatedExpr));
                            break;
                        }
                        case CALTreeParserTokenTypes.FIELD_BINDING_VAR_ASSIGNMENT_LIST:
                        {
                            Map<FieldName, String> fieldBindingsMap = CALTypeChecker.getFieldBindingsMap(argBindingsNode, null, false);
                           
                            if (fieldBindingsMap != null) {
                                altsList.add(new Expression.Switch.SwitchAlt.Matching(altTags, fieldBindingsMap, generatedExpr));
                            }
                            break;
                        }
                        default:
                        {
                            patternNode.unexpectedParseTreeNode();          
                            break;
                        }
                    }
                   
                    break;
                }
               
                case CALTreeParserTokenTypes.VIRTUAL_UNIT_DATA_CONSTRUCTOR :
                {                                  
                    Object altTag = getDataConstructor(CAL_Prelude.DataConstructors.Unit);
                    altsList.add(new Expression.Switch.SwitchAlt.Matching(altTag, NO_MATCHING_FIELDS, generatedExpr));
                    break;
                }
               
                case CALTreeParserTokenTypes.LIST_CONSTRUCTOR :
                {              
                    Object altTag = getDataConstructor(CAL_Prelude.DataConstructors.Nil);
                    altsList.add(new Expression.Switch.SwitchAlt.Matching(altTag, NO_MATCHING_FIELDS, generatedExpr));
                    break;
                }
               
                case CALTreeParserTokenTypes.COLON :
                {
                    Object altTag = getDataConstructor(CAL_Prelude.DataConstructors.Cons);
                    ParseTreeNode generalizedVarListNode = patternNode;
                    altsList.add(new Expression.Switch.SwitchAlt.Positional(altTag, getPositionToVarNameMap(generalizedVarListNode), generatedExpr));
                    break;
                }
               
                case CALTreeParserTokenTypes.UNDERSCORE:           
                {
                    String altTag = Expression.Switch.SwitchAlt.WILDCARD_TAG;
                    altsList.add(new Expression.Switch.SwitchAlt.Positional(altTag, NO_POSITIONAL_FIELDS, generatedExpr));
                    break;
                }
               
                case CALTreeParserTokenTypes.INT_PATTERN :
                {
                    ParseTreeNode maybeMinusIntListNode = patternNode.firstChild();
                    maybeMinusIntListNode.verifyType(CALTreeParserTokenTypes.MAYBE_MINUS_INT_LIST);
                   
                    List<Object> altTags = new ArrayList<Object>();
                    for (final ParseTreeNode maybeMinusIntNode : maybeMinusIntListNode) {
                       
                        Integer altTag = (Integer)maybeMinusIntNode.getLiteralValueForMaybeMinusIntLiteral();
                        if (altTag == null) {
                            throw new IllegalStateException();
                        }
                       
                        altTags.add(altTag);
                    }
                   
                    altsList.add(new Expression.Switch.SwitchAlt.Matching(altTags, NO_MATCHING_FIELDS, generatedExpr));
                    break;
                }
               
                case CALTreeParserTokenTypes.CHAR_PATTERN :
                {
                    ParseTreeNode charListNode = patternNode.firstChild();
                    charListNode.verifyType(CALTreeParserTokenTypes.CHAR_LIST);
                   
                    List<Object> altTags = new ArrayList<Object>();
                    for (final ParseTreeNode charLiteralNode : charListNode) {
                       
                        Character altTag = charLiteralNode.getCharacterValueForCharLiteral();
                        if (altTag == null) {
                            throw new IllegalStateException();
                        }
                       
                        altTags.add(altTag);
                    }
                   
                    altsList.add(new Expression.Switch.SwitchAlt.Matching(altTags, NO_MATCHING_FIELDS, generatedExpr));
                    break;
                }
               
                case CALTreeParserTokenTypes.INTEGER_LITERAL :
                {
                    //this node does not currently arise in user-written code, but does occur in the
                    //hidden dictionary functions.
                    Object altTag = generateLiteral(patternNode);
                    if (!(altTag instanceof Integer)) {
                        throw new IllegalStateException();
                    }
                    altsList.add(new Expression.Switch.SwitchAlt.Matching(altTag, NO_MATCHING_FIELDS, generatedExpr));
                    break;
                }
               
                default :
                {
                    patternNode.unexpectedParseTreeNode();          
                    break;
                }
            }
        }
       
        Expression.Switch.SwitchAlt[] altsArray = new Expression.Switch.SwitchAlt[altsList.size()];
        return altsList.toArray(altsArray);
    }
   
    /**
     * Generates the Expression format for a data-constructor based switch expression.
     * @param parseTree
     * @return Expression.Switch
     */
    private Expression.Switch generateDataConstructorCaseExpr (ParseTreeNode parseTree) {       
        parseTree.verifyType(CALTreeParserTokenTypes.VIRTUAL_DATA_CONSTRUCTOR_CASE);       
       
        ParseTreeNode exprNode = parseTree.firstChild();
        ParseTreeNode altListNode = exprNode.nextSibling();
        Expression conditionExpr = generateExpr(exprNode);
       
        Expression.Switch.SwitchAlt[] altsArray = generateSwitchAlts(altListNode);
       
        return new Expression.Switch(conditionExpr, altsArray, parseTree.getErrorInfoForErrorCall());
    }
  
    /**
     * Generates the Expression format for a data-constructor field selection.
     * @param parseTree
     * @return Expression.DataConsSelection
     */
    private Expression generateDataConstructorFieldSelection(ParseTreeNode parseTree) {
        parseTree.verifyType(CALTreeParserTokenTypes.SELECT_DATA_CONSTRUCTOR_FIELD);
       
        ParseTreeNode exprNode = parseTree.firstChild();
        ParseTreeNode dcNameNode = exprNode.nextSibling();
        ParseTreeNode fieldNameNode = dcNameNode.nextSibling();      
       
        Expression conditionExpr = generateExpr(exprNode);
        QualifiedName dataConsName = dcNameNode.toQualifiedName();

        DataConstructor dataConstructor = getDataConstructor(dataConsName)
       
        FieldName fieldName = FieldName.make(fieldNameNode.getText());
        int fieldIndex = dataConstructor.getFieldIndex(fieldName);
       
        if (fieldIndex < 0) {
            throw new IllegalStateException("Internal coding error- cannot generate an Expression for a non-existent field name.");
        }             

        return new Expression.DataConsSelection(conditionExpr, dataConstructor, fieldIndex, parseTree.getErrorInfoForErrorCall());
    }

    /**
     * A helper function that converts the AST for a list of vars to mappings for those vars.
     * @return (Integer->String) map from position to var name for all used alt vars.
     * @param generalizedVarListNode the parent of the nodes representing var ids or underscores.
     */
    private SortedMap<Integer, String> getPositionToVarNameMap(ParseTreeNode generalizedVarListNode) {

        SortedMap<Integer, String> positionToVarNameMap = new TreeMap<Integer, String>();
       
        int index = 0;

        for (final ParseTreeNode patternVarNode : generalizedVarListNode) {
               
            switch (patternVarNode.getType()) {
                case CALTreeParserTokenTypes.VAR_ID:
                {
                    positionToVarNameMap.put(Integer.valueOf(index), patternVarNode.getText());
                    break;
                }
               
                case CALTreeParserTokenTypes.UNDERSCORE:                   
                {
                    break;
                }
               
                default:
                {
                    patternVarNode.unexpectedParseTreeNode();                                  
                    break;
                }
            }
            index++;
        }

        return positionToVarNameMap;
    }  
}
TOP

Related Classes of org.openquark.cal.compiler.ExpressionGenerator

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.