/*
* #%~
* VDM Code Generator
* %%
* Copyright (C) 2008 - 2014 Overture
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #~%
*/
package org.overture.codegen.assistant;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.lang.BooleanUtils;
import org.overture.ast.analysis.AnalysisException;
import org.overture.ast.definitions.PDefinition;
import org.overture.ast.definitions.SClassDefinition;
import org.overture.ast.factory.AstFactory;
import org.overture.ast.intf.lex.ILexNameToken;
import org.overture.ast.patterns.ATuplePattern;
import org.overture.ast.patterns.PPattern;
import org.overture.ast.types.ABracketType;
import org.overture.ast.types.ANamedInvariantType;
import org.overture.ast.types.AOptionalType;
import org.overture.ast.types.AProductType;
import org.overture.ast.types.AQuoteType;
import org.overture.ast.types.ASeq1SeqType;
import org.overture.ast.types.AUnionType;
import org.overture.ast.types.AUnknownType;
import org.overture.ast.types.PType;
import org.overture.ast.types.SSeqTypeBase;
import org.overture.ast.util.PTypeSet;
import org.overture.codegen.ir.INode;
import org.overture.codegen.ir.IRConstants;
import org.overture.codegen.ir.IRInfo;
import org.overture.codegen.ir.SExpIR;
import org.overture.codegen.ir.SObjectDesignatorIR;
import org.overture.codegen.ir.STypeIR;
import org.overture.codegen.ir.SourceNode;
import org.overture.codegen.ir.declarations.AFieldDeclIR;
import org.overture.codegen.ir.declarations.AMethodDeclIR;
import org.overture.codegen.ir.declarations.ARecordDeclIR;
import org.overture.codegen.ir.declarations.SClassDeclIR;
import org.overture.codegen.ir.expressions.AApplyExpIR;
import org.overture.codegen.ir.expressions.SBinaryExpIR;
import org.overture.codegen.ir.statements.AApplyObjectDesignatorIR;
import org.overture.codegen.ir.types.ABoolBasicTypeIR;
import org.overture.codegen.ir.types.ABoolBasicTypeWrappersTypeIR;
import org.overture.codegen.ir.types.ACharBasicTypeIR;
import org.overture.codegen.ir.types.ACharBasicTypeWrappersTypeIR;
import org.overture.codegen.ir.types.AClassTypeIR;
import org.overture.codegen.ir.types.AIntBasicTypeWrappersTypeIR;
import org.overture.codegen.ir.types.AIntNumericBasicTypeIR;
import org.overture.codegen.ir.types.AMethodTypeIR;
import org.overture.codegen.ir.types.ANat1BasicTypeWrappersTypeIR;
import org.overture.codegen.ir.types.ANat1NumericBasicTypeIR;
import org.overture.codegen.ir.types.ANatBasicTypeWrappersTypeIR;
import org.overture.codegen.ir.types.ANatNumericBasicTypeIR;
import org.overture.codegen.ir.types.AObjectTypeIR;
import org.overture.codegen.ir.types.ARatBasicTypeWrappersTypeIR;
import org.overture.codegen.ir.types.ARatNumericBasicTypeIR;
import org.overture.codegen.ir.types.ARealBasicTypeWrappersTypeIR;
import org.overture.codegen.ir.types.ARealNumericBasicTypeIR;
import org.overture.codegen.ir.types.ARecordTypeIR;
import org.overture.codegen.ir.types.ASeqSeqTypeIR;
import org.overture.codegen.ir.types.ASetSetTypeIR;
import org.overture.codegen.ir.types.AStringTypeIR;
import org.overture.codegen.ir.types.ATokenBasicTypeIR;
import org.overture.codegen.ir.types.ATupleTypeIR;
import org.overture.codegen.ir.types.AUnionTypeIR;
import org.overture.codegen.ir.types.AUnknownTypeIR;
import org.overture.codegen.ir.types.SBasicTypeIR;
import org.overture.codegen.ir.types.SMapTypeIR;
import org.overture.codegen.ir.types.SSeqTypeIR;
import org.overture.codegen.ir.types.SSetTypeIR;
import org.overture.codegen.trans.conv.ObjectDesignatorToExpIR;
import org.overture.typechecker.TypeComparator;
import org.overture.typechecker.assistant.definition.PDefinitionAssistantTC;
import org.overture.typechecker.assistant.type.PTypeAssistantTC;
public class TypeAssistantIR extends AssistantBase
{
public TypeAssistantIR(AssistantManager assistantManager)
{
super(assistantManager);
}
public void removeIllegalQuoteTypes(List<PType> types)
{
List<Integer> illegalIndices = new LinkedList<>();
for (int i = 0; i < types.size(); i++)
{
PType t = types.get(i);
if (t instanceof AQuoteType
&& ((AQuoteType) t).getValue().getValue().equals(IRConstants.ILLEGAL_QUOTE_VALUE))
{
illegalIndices.add(i);
}
}
for (int i = illegalIndices.size() - 1; i >= 0; i--)
{
types.remove(i);
}
}
public STypeIR getFieldExpType(IRInfo info, String fieldName,
String fieldModule, SObjectDesignatorIR obj, INode parent)
throws AnalysisException,
org.overture.codegen.ir.analysis.AnalysisException
{
if (parent instanceof AApplyObjectDesignatorIR)
{
AApplyObjectDesignatorIR apply = (AApplyObjectDesignatorIR) parent;
LinkedList<SExpIR> args = apply.getArgs();
if (fieldModule != null)
{
// It is a class
SClassDeclIR clazz = info.getDeclAssistant().findClass(info.getClasses(), fieldModule);
AFieldDeclIR field = info.getDeclAssistant().getFieldDecl(clazz, fieldModule);
if (field != null)
{
return field.getType().clone();
} else
{
// It must be a method
return info.getTypeAssistant().getMethodType(info, fieldModule, fieldName, args);
}
}
}
return getFieldType(info, fieldName, fieldModule, obj);
}
private STypeIR getFieldType(IRInfo info, String fieldName,
String fieldModule, SObjectDesignatorIR obj)
{
if (fieldModule != null)
{
// It is a class
return info.getTypeAssistant().getFieldType(info.getClasses(), fieldModule, fieldName);
} else
{
// It is a record
try
{
ObjectDesignatorToExpIR converter = new ObjectDesignatorToExpIR(info);
SExpIR objExp = obj.apply(converter);
if (objExp.getType() instanceof ARecordTypeIR)
{
STypeIR fieldExpType = info.getTypeAssistant().getFieldType(info.getClasses(), (ARecordTypeIR) objExp.getType(), fieldName);
if (fieldExpType == null)
{
log.error("Could not find field type");
}
return fieldExpType;
}
} catch (org.overture.codegen.ir.analysis.AnalysisException e)
{
}
}
log.error("Could not determine field type");
return new AUnknownTypeIR();
}
public AMethodTypeIR getMethodType(IRInfo info, String fieldModule,
String fieldName, List<SExpIR> args)
throws org.overture.codegen.ir.analysis.AnalysisException
{
SClassDeclIR classDecl = assistantManager.getDeclAssistant().findClass(info.getClasses(), fieldModule);
List<AMethodDeclIR> methods = assistantManager.getDeclAssistant().getAllMethods(classDecl, info.getClasses());
for (AMethodDeclIR method : methods)
{
if (method.getName().equals(fieldName))
{
LinkedList<STypeIR> params = method.getMethodType().getParams();
if (assistantManager.getTypeAssistant().checkArgTypes(info, args, params))
{
return method.getMethodType().clone();
}
}
}
// Union type transformations may ask for the method type of a field to find out
// that it does not exist. Consider for example the (legal) snippet below where
// class A has an operation 'op()' and B is a completely empty class definition
//
// let xs = [new A(), new B()]
// in
// for x in xs do
// x.op();
// If the field does not exist then the method type does not exist
return null;
}
public STypeIR getFieldType(SClassDeclIR classDecl, String fieldName,
List<SClassDeclIR> classes)
{
for (AFieldDeclIR field : assistantManager.getDeclAssistant().getAllFields(classDecl, classes))
{
if (field.getName().equals(fieldName))
{
return field.getType().clone();
}
}
return null;
}
public STypeIR getFieldType(List<SClassDeclIR> classes,
ARecordTypeIR recordType, String memberName)
{
AFieldDeclIR field = assistantManager.getDeclAssistant().getFieldDecl(classes, recordType, memberName);
if (field != null)
{
return field.getType().clone();
}
return null;
}
public List<STypeIR> getFieldTypes(ARecordDeclIR record)
{
List<STypeIR> fieldTypes = new LinkedList<STypeIR>();
for (AFieldDeclIR field : record.getFields())
{
fieldTypes.add(field.getType());
}
return fieldTypes;
}
public STypeIR getFieldType(List<SClassDeclIR> classes, String moduleName,
String fieldName)
{
SClassDeclIR classDecl = assistantManager.getDeclAssistant().findClass(classes, moduleName);
return getFieldType(classDecl, fieldName, classes);
}
public boolean compatible(IRInfo info, STypeIR left, STypeIR right)
{
SourceNode leftSource = left.getSourceNode();
SourceNode rightSource = right.getSourceNode();
if (leftSource == null || rightSource == null)
{
return false;
}
org.overture.ast.node.INode leftType = leftSource.getVdmNode();
org.overture.ast.node.INode rightType = rightSource.getVdmNode();
if (!(leftType instanceof PType) || !(rightType instanceof PType))
{
return false;
}
TypeComparator typeComparator = info.getTcFactory().getTypeComparator();
if (!typeComparator.compatible((PType) leftType, (PType) rightType))
{
return false;
}
return true;
}
public boolean checkArgTypes(IRInfo info, List<SExpIR> args,
List<STypeIR> paramTypes)
throws org.overture.codegen.ir.analysis.AnalysisException
{
if (args.size() != paramTypes.size())
{
return false;
}
for (int i = 0; i < paramTypes.size(); i++)
{
STypeIR paramType = paramTypes.get(i);
STypeIR argType = args.get(i).getType();
if (!compatible(info, paramType, argType))
{
return false;
}
}
return true;
}
public PDefinition getTypeDef(ILexNameToken nameToken,
PDefinitionAssistantTC defAssistant)
{
PDefinition def = (PDefinition) nameToken.getAncestor(PDefinition.class);
if (def == null)
{
return null;
}
SClassDefinition enclosingClass = nameToken.getAncestor(SClassDefinition.class);
if (enclosingClass == null)
{
return null;
}
PDefinition typeDef = defAssistant.findType(def, nameToken, enclosingClass.getName().getModule());
return typeDef;
}
public STypeIR constructSeqType(SSeqTypeBase node, IRInfo question)
throws AnalysisException
{
STypeIR seqOfCg = node.getSeqof().apply(question.getTypeVisitor(), question);
boolean emptyCg = node.getEmpty();
boolean isSeq1 = node instanceof ASeq1SeqType;
// This is a special case since sequence of characters are strings
if (seqOfCg instanceof ACharBasicTypeIR
&& question.getSettings().getCharSeqAsString())
{
AStringTypeIR stringTypeCg = new AStringTypeIR();
stringTypeCg.setSourceNode(new SourceNode(node));
return stringTypeCg;
}
ASeqSeqTypeIR seqType = new ASeqSeqTypeIR();
seqType.setSeqOf(seqOfCg);
seqType.setEmpty(emptyCg);
seqType.setSeq1(isSeq1);
return seqType;
}
public boolean isBasicType(STypeIR type)
{
return type instanceof SBasicTypeIR;
}
public STypeIR getWrapperType(SBasicTypeIR basicType)
{
if (basicType instanceof AIntNumericBasicTypeIR)
{
return new AIntBasicTypeWrappersTypeIR();
} else if (basicType instanceof ANat1NumericBasicTypeIR)
{
return new ANat1BasicTypeWrappersTypeIR();
} else if (basicType instanceof ANatNumericBasicTypeIR)
{
return new ANatBasicTypeWrappersTypeIR();
} else if (basicType instanceof ARatNumericBasicTypeIR)
{
return new ARatBasicTypeWrappersTypeIR();
} else if (basicType instanceof ARealNumericBasicTypeIR)
{
return new ARealBasicTypeWrappersTypeIR();
} else if (basicType instanceof ACharBasicTypeIR)
{
return new ACharBasicTypeWrappersTypeIR();
} else if (basicType instanceof ABoolBasicTypeIR)
{
return new ABoolBasicTypeWrappersTypeIR();
} else if (basicType instanceof ATokenBasicTypeIR)
{
return basicType;
} else
{
return null;
}
}
public AMethodTypeIR consMethodType(PType node, List<PType> paramTypes,
PType resultType, IRInfo question) throws AnalysisException
{
AMethodTypeIR methodType = new AMethodTypeIR();
methodType.setEquivalent(node.clone());
STypeIR resultCg = resultType.apply(question.getTypeVisitor(), question);
methodType.setResult(resultCg);
LinkedList<STypeIR> paramsCg = methodType.getParams();
for (PType paramType : paramTypes)
{
paramsCg.add(paramType.apply(question.getTypeVisitor(), question));
}
return methodType;
}
public boolean isUnionOfType(AUnionType unionType,
Class<? extends PType> type, PTypeAssistantTC typeAssistant)
{
try
{
for (PType t : unionType.getTypes())
{
if (!typeAssistant.isType(t, type))
{
return false;
}
}
} catch (Error t)// Hack for stackoverflowError
{
return false;
}
return true;
}
public boolean isProductOfSameSize(AUnionType unionType,
PTypeAssistantTC typeAssistant)
{
final int NOT_SET = -1;
int commonSize = NOT_SET;
try
{
for (PType t : unionType.getTypes())
{
if (!typeAssistant.isType(t, AProductType.class))
{
return false;
} else
{
AProductType productType = typeAssistant.getProduct(t);
int currentSize = productType.getTypes().size();
if (commonSize == NOT_SET)
{
commonSize = currentSize;
} else
{
if (commonSize != currentSize)
{
return false;
}
}
}
}
} catch (Error t)// Hack for stackoverflowError
{
return false;
}
return true;
}
public PType getType(IRInfo question, AUnionType unionType,
PPattern pattern)
{
PTypeSet possibleTypes = new PTypeSet(question.getTcFactory());
PType patternType = question.getTcFactory().createPPatternAssistant().getPossibleType(pattern);
TypeComparator comp = question.getTcFactory().getTypeComparator();
for (PType t : unionType.getTypes())
{
if (comp.compatible(patternType, t))
{
possibleTypes.add(t);
}
}
if (possibleTypes.isEmpty())
{
log.error("Could not find any possible types for pattern: "
+ pattern);
return null;
} else if (possibleTypes.size() == 1)
{
return possibleTypes.pollFirst();
} else
// More than one possible type
{
unionType.getTypes().clear();
unionType.getTypes().addAll(possibleTypes);
PTypeAssistantTC typeAssistant = question.getTcFactory().createPTypeAssistant();
if (question.getTypeAssistant().isUnionOfType(unionType, AProductType.class, typeAssistant))
{
List<PType> fieldsTypes = new LinkedList<PType>();
int noOfFields = ((ATuplePattern) pattern).getPlist().size();
for (int i = 0; i < noOfFields; i++)
{
List<PType> currentFieldTypes = new LinkedList<PType>();
for (PType currentPossibleType : possibleTypes)
{
AProductType currentProductType = (AProductType) currentPossibleType;
currentFieldTypes.add(currentProductType.getTypes().get(i).clone());
}
fieldsTypes.add(AstFactory.newAUnionType(pattern.getLocation(), currentFieldTypes));
}
return AstFactory.newAProductType(pattern.getLocation(), fieldsTypes);
} else
{
return unionType;
}
}
}
public List<STypeIR> clearObjectTypes(List<STypeIR> types)
{
types = new LinkedList<STypeIR>(types);
List<AObjectTypeIR> objectTypes = new LinkedList<AObjectTypeIR>();
for (STypeIR type : types)
{
if (type instanceof AObjectTypeIR)
{
objectTypes.add((AObjectTypeIR) type);
}
}
types.removeAll(objectTypes);
return types;
}
public List<STypeIR> clearDuplicates(List<STypeIR> types)
{
List<STypeIR> filtered = new LinkedList<STypeIR>();
for (STypeIR type : types)
{
if (!containsType(filtered, type))
{
filtered.add(type);
}
}
return filtered;
}
public boolean isStringType(STypeIR type)
{
return type instanceof AStringTypeIR;
}
public boolean isStringType(SExpIR exp)
{
return exp.getType() instanceof AStringTypeIR;
}
public boolean isMapType(SExpIR exp)
{
return exp.getType() instanceof SMapTypeIR;
}
public boolean isSeqType(SExpIR exp)
{
return exp.getType() instanceof SSeqTypeIR;
}
public boolean isMapApplication(AApplyExpIR applyExp)
{
return isMapType(applyExp.getRoot()) && applyExp.getArgs().size() == 1;
}
public boolean isSeqApplication(AApplyExpIR applyExp)
{
return isSeqType(applyExp.getRoot()) && applyExp.getArgs().size() == 1;
}
public boolean isCharRead(AApplyExpIR applyExp)
{
return isStringType(applyExp.getRoot())
&& applyExp.getArgs().size() == 1;
}
public STypeIR findElementType(STypeIR type)
{
if (type instanceof SSetTypeIR)
{
SSetTypeIR setType = (SSetTypeIR) type;
return setType.getSetOf();
} else if (type instanceof SSeqTypeIR)
{
SSeqTypeIR seqType = (SSeqTypeIR) type;
return seqType.getSeqOf();
}
log.error("Expected set or sequence type in findElementType. Got: "
+ type);
return null;
}
public PType resolve(PType type)
{
while (type instanceof ABracketType
|| type instanceof ANamedInvariantType
|| type instanceof AOptionalType)
{
if (type instanceof ABracketType)
{
type = ((ABracketType) type).getType();
}
if (type instanceof ANamedInvariantType)
{
type = ((ANamedInvariantType) type).getType();
}
if (type instanceof AOptionalType)
{
type = ((AOptionalType) type).getType();
}
}
return type;
}
public SSeqTypeIR getSeqType(AUnionTypeIR unionType)
{
AUnionTypeIR seqOf = new AUnionTypeIR();
seqOf.setTypes(findElementTypes(unionType, new CollectionTypeStrategy()
{
@Override
public boolean isCollectionType(STypeIR type)
{
return type instanceof SSeqTypeIR;
}
@Override
public STypeIR getElementType(STypeIR type)
{
return ((SSeqTypeIR) type).getSeqOf();
}
}));
ASeqSeqTypeIR seqType = new ASeqSeqTypeIR();
seqType.setEmpty(false);
seqType.setSeqOf(seqOf);
return seqType;
}
public SSetTypeIR getSetType(AUnionTypeIR unionType)
{
AUnionTypeIR setOf = new AUnionTypeIR();
setOf.setTypes(findElementTypes(unionType, new CollectionTypeStrategy()
{
@Override
public boolean isCollectionType(STypeIR type)
{
return type instanceof SSetTypeIR;
}
@Override
public STypeIR getElementType(STypeIR type)
{
return ((SSetTypeIR) type).getSetOf();
}
}));
ASetSetTypeIR setType = new ASetSetTypeIR();
setType.setEmpty(false);
setType.setSetOf(setOf);
return setType;
}
public boolean usesUnionType(SBinaryExpIR node)
{
return node.getLeft().getType() instanceof AUnionTypeIR
|| node.getRight().getType() instanceof AUnionTypeIR;
}
public List<STypeIR> findElementTypes(AUnionTypeIR unionType,
CollectionTypeStrategy strategy)
{
List<STypeIR> elementTypes = new LinkedList<STypeIR>();
for (int i = 0; i < unionType.getTypes().size(); i++)
{
STypeIR type = unionType.getTypes().get(i);
if (type instanceof AUnionTypeIR)
{
elementTypes.addAll(findElementTypes((AUnionTypeIR) type, strategy));
} else if (strategy.isCollectionType(type))
{
elementTypes.add(strategy.getElementType(type));
}
}
return elementTypes;
}
public boolean containsType(List<STypeIR> types, STypeIR searchedType)
{
for (STypeIR currentType : types)
{
if (typesEqual(currentType, searchedType))
{
return true;
}
}
return false;
}
private boolean typesEqual(STypeIR left, STypeIR right)
{
if (left instanceof AClassTypeIR && right instanceof AClassTypeIR)
{
AClassTypeIR currentClassType = (AClassTypeIR) left;
AClassTypeIR searchedClassType = (AClassTypeIR) right;
return currentClassType.getName().equals(searchedClassType.getName());
}
if (left instanceof ARecordTypeIR && right instanceof ARecordTypeIR)
{
ARecordTypeIR recordType = (ARecordTypeIR) left;
ARecordTypeIR searchedRecordType = (ARecordTypeIR) right;
return recordType.getName().equals(searchedRecordType.getName());
}
if (left instanceof ATupleTypeIR && right instanceof ATupleTypeIR)
{
ATupleTypeIR currentTupleType = (ATupleTypeIR) left;
ATupleTypeIR searchedTupleType = (ATupleTypeIR) right;
if (currentTupleType.getTypes().size() != searchedTupleType.getTypes().size())
{
return false;
}
LinkedList<STypeIR> leftTypes = currentTupleType.getTypes();
LinkedList<STypeIR> rightTypes = searchedTupleType.getTypes();
for (int i = 0; i < leftTypes.size(); i++)
{
STypeIR currentLeftFieldType = leftTypes.get(i);
STypeIR currentRightFieldType = rightTypes.get(i);
if (!typesEqual(currentLeftFieldType, currentRightFieldType))
{
return false;
}
}
return true;
}
if (left.getClass() == right.getClass())
{
return true;
}
return false;
}
public boolean isNumericType(STypeIR type)
{
return isInt(type) || isRealOrRat(type);
}
public boolean isRealOrRat(STypeIR type)
{
return type instanceof ARatNumericBasicTypeIR
|| type instanceof ARatBasicTypeWrappersTypeIR
|| type instanceof ARealNumericBasicTypeIR
|| type instanceof ARealBasicTypeWrappersTypeIR;
}
public boolean isInt(STypeIR type)
{
return type instanceof AIntNumericBasicTypeIR
|| type instanceof AIntBasicTypeWrappersTypeIR
|| type instanceof ANat1NumericBasicTypeIR
|| type instanceof ANat1BasicTypeWrappersTypeIR
|| type instanceof ANatNumericBasicTypeIR
|| type instanceof ANatBasicTypeWrappersTypeIR;
}
public boolean isWrapperType(STypeIR type)
{
return type instanceof ANatBasicTypeWrappersTypeIR
|| type instanceof ANat1BasicTypeWrappersTypeIR
|| type instanceof ARatBasicTypeWrappersTypeIR
|| type instanceof ARealBasicTypeWrappersTypeIR
|| type instanceof ACharBasicTypeWrappersTypeIR
|| type instanceof ABoolBasicTypeWrappersTypeIR;
}
public boolean isOptional(STypeIR type)
{
return BooleanUtils.isTrue(type.getOptional());
}
public boolean allowsNull(STypeIR type)
{
if (type instanceof AUnionTypeIR)
{
AUnionTypeIR unionType = (AUnionTypeIR) type;
if (BooleanUtils.isTrue(unionType.getOptional()))
{
return true;
}
for (STypeIR t : unionType.getTypes())
{
if (allowsNull(t))
{
return true;
}
}
return false;
} else
{
return /* type instanceof AObjectTypeIR || */type != null
&& (type instanceof AUnknownTypeIR
|| BooleanUtils.isTrue(type.getOptional())
|| isWrapperType(type));
}
}
public PType getVdmType(STypeIR type)
{
SourceNode source = type.getSourceNode();
if (source != null)
{
org.overture.ast.node.INode vdmNode = source.getVdmNode();
if (vdmNode != null)
{
if (vdmNode instanceof PType)
{
return (PType) vdmNode;
}
}
}
log.error("Could not get VDM type of " + type);
return new AUnknownType();
}
}