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