diff --git a/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java b/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java index 9f626af..c831849 100644 --- a/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java +++ b/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java @@ -1,105 +1,715 @@ package algorithms; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + import models.Edge; import models.Node; import models.algebra.Expression; +import models.algebra.Position; import models.algebra.Term; import models.algebra.Type; import models.algebra.Variable; import models.dataConstraintModel.ChannelGenerator; import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.IdentifierTemplate; +import models.dataConstraintModel.StateTransition; import models.dataFlowModel.DataFlowModel; -import models.dataFlowModel.DataflowChannelGenerator; -import models.dataFlowModel.PushPullAttribute; -import models.dataFlowModel.PushPullValue; -import models.dataFlowModel.ResourceDependency; import models.dataFlowModel.ResourceDependencyGraph; import models.dataFlowModel.ResourceNode; public class TypeInference { + static private Map listTypes = new HashMap<>(); + static private Map listComponentTypes = new HashMap<>(); + static private Map, Type> tupleTypes = new HashMap<>(); + static private Map> tupleComponentTypes = new HashMap<>(); + static public void infer(ResourceDependencyGraph graph, DataFlowModel model) { + Map> resources = new HashMap<>(); + Map variables = new HashMap<>(); + Map, Type>>> messages = new HashMap<>(); + Map cons = new HashMap<>(); + Map tuple = new HashMap<>(); + Map> expToResource = new HashMap<>(); + Map> expToVariable = new HashMap<>(); + Map> expToMessage = new HashMap<>(); + Map> expToCons = new HashMap<>(); + Map> expToTuple = new HashMap<>(); + + Map idToRes = new HashMap<>(); + Map> updateFromResource = new HashMap<>(); + Map> updateFromVariable = new HashMap<>(); + Map> updateFromMessage = new HashMap<>(); + Map> updateFromCons = new HashMap<>(); + Map> updateFromTuple = new HashMap<>(); + + listComponentTypes.put(DataConstraintModel.typeList, null); + tupleComponentTypes.put(DataConstraintModel.typeTuple, null); for (Node n: graph.getNodes()) { ResourceNode resource = (ResourceNode) n; - Type resourceType = resource.getIdentifierTemplate().getResourceStateType(); - if (resourceType == null || resourceType == DataConstraintModel.typeList) { - if (resource.getIndegree() == 0) { - for (ChannelGenerator c: model.getIOChannelGenerators()) { - for (ChannelMember cm: ((DataflowChannelGenerator) c).getOutputChannelMembers()) { - if (resource.getIdentifierTemplate().equals(cm.getIdentifierTemplate()) - && cm.getStateTransition().getNextStateExpression() instanceof Term) { - updateResourceType(resource, cm); + idToRes.put(resource.getIdentifierTemplate(), resource); + } + + // Collect type information from the architecture model. + Collection channels = new HashSet<>(model.getIOChannelGenerators()); + channels.addAll(model.getChannelGenerators()); + for (ChannelGenerator c: channels) { + for (ChannelMember cm: c.getChannelMembers()) { + StateTransition st = cm.getStateTransition(); + IdentifierTemplate id = cm.getIdentifierTemplate(); + + // Group expressions by resources. + List sameResource = resources.get(id); + if (sameResource == null) { + sameResource = new ArrayList<>(); + resources.put(id, sameResource); + } + sameResource.add(st.getCurStateExpression()); + sameResource.add(st.getNextStateExpression()); + expToResource.put(System.identityHashCode(st.getCurStateExpression()), sameResource); + expToResource.put(System.identityHashCode(st.getNextStateExpression()), sameResource); + Map updatedExps = getUpdateSet(updateFromResource, sameResource); + Type resType = id.getResourceStateType(); + Expression exp = st.getCurStateExpression(); + Type expType = getExpTypeIfUpdatable(resType, exp); + if (expType != null) { + id.setResourceStateType(expType); + for (Expression resExp: sameResource) { + if (resExp != exp) { + if (resExp instanceof Variable && compareTypes(((Variable) resExp).getType(), expType)) { + ((Variable) resExp).setType(expType); + updatedExps.put(System.identityHashCode(resExp), resExp); + } else if (resExp instanceof Term && compareTypes(((Term) resExp).getType(), expType)) { + ((Term) resExp).setType(expType); + updatedExps.put(System.identityHashCode(resExp), resExp); } } } - } else { - for (Edge e: resource.getInEdges()) { - for (ChannelMember cm : ((ResourceDependency) e).getChannelGenerator().getChannelMembers()) { - if (((PushPullAttribute) ((ResourceDependency) e).getAttribute()).getOptions().get(0) == PushPullValue.PUSH - && cm.getStateTransition().getNextStateExpression() instanceof Term) { - updateResourceType(resource, cm); + } else if (exp instanceof Variable) { + if (compareTypes(((Variable) exp).getType(), resType)) { + ((Variable) exp).setType(resType); + updatedExps.put(System.identityHashCode(exp), exp); + } + } else if (exp instanceof Term) { + if (compareTypes(((Term) exp).getType(), resType)) { + ((Term) exp).setType(resType); + updatedExps.put(System.identityHashCode(exp), exp); + } + } + resType = id.getResourceStateType(); + exp = st.getNextStateExpression(); + expType = getExpTypeIfUpdatable(resType, exp); + if (expType != null) { + id.setResourceStateType(expType); + for (Expression resExp: sameResource) { + if (resExp != exp) { + if (resExp instanceof Variable && compareTypes(((Variable) resExp).getType(), expType)) { + ((Variable) resExp).setType(expType); + updatedExps.put(System.identityHashCode(resExp), resExp); + } else if (resExp instanceof Term && compareTypes(((Term) resExp).getType(), expType)) { + ((Term) resExp).setType(expType); + updatedExps.put(System.identityHashCode(resExp), resExp); } - break; + } + } + } else if (exp instanceof Variable) { + if (compareTypes(((Variable) exp).getType(), resType)) { + ((Variable) exp).setType(resType); + updatedExps.put(System.identityHashCode(exp), exp); + } + } else if (exp instanceof Term) { + if (compareTypes(((Term) exp).getType(), resType)) { + ((Term) exp).setType(resType); + updatedExps.put(System.identityHashCode(exp), exp); + } + } + + // Group expressions by variable. + Map> locals = new HashMap<>(); + Map localTypes = new HashMap<>(); + List allVariables = new ArrayList<>(); + allVariables.addAll(st.getCurStateExpression().getVariables().values()); + allVariables.addAll(st.getMessageExpression().getVariables().values()); + allVariables.addAll(st.getNextStateExpression().getVariables().values()); + for (Variable var: allVariables) { + List sameVariable = locals.get(var.getName()); + if (sameVariable == null) { + sameVariable = new ArrayList<>(); + sameVariable.add(var); + expToVariable.put(System.identityHashCode(var), sameVariable); + locals.put(var.getName(), sameVariable); + localTypes.put(var.getName(), var.getType()); + } else { + sameVariable.add(var); + expToVariable.put(System.identityHashCode(var), sameVariable); + Type varType = localTypes.get(var.getName()); + Map updatedVars = getUpdateSet(updateFromVariable, sameVariable); + if (compareTypes(varType, var.getType())) { + localTypes.put(var.getName(), var.getType()); + for (Variable v: sameVariable) { + if (v != var) { + if (compareTypes(v.getType(), var.getType())) { + v.setType(var.getType()); + updatedVars.put(System.identityHashCode(v), v); + } + } + } + } else if (compareTypes(var.getType(), varType)) { + var.setType(varType); + updatedVars.put(System.identityHashCode(var), var); } } } - for (Edge e: resource.getOutEdges()) { - for (ChannelMember cm : ((ResourceDependency) e).getChannelGenerator().getChannelMembers()) { - if (((PushPullAttribute) ((ResourceDependency) e).getAttribute()).getOptions().get(0) != PushPullValue.PUSH - && cm.getStateTransition().getNextStateExpression() instanceof Term) { - updateResourceType(resource, cm); + for (String varName: locals.keySet()) { + variables.put(System.identityHashCode(locals.get(varName)), localTypes.get(varName)); + } + + // Group expressions by message. + Expression message = st.getMessageExpression(); + if (message instanceof Variable) { + Type msgType = ((Variable) message).getType(); + Map, Type>> msgTypeMap = messages.get(c); + if (msgTypeMap == null) { + msgTypeMap = new HashMap<>(); + messages.put(c, msgTypeMap); + } + Map.Entry, Type> typeAndExps = msgTypeMap.get(0); + if (typeAndExps == null) { + List exps = new ArrayList<>(); + exps.add(message); + typeAndExps = new AbstractMap.SimpleEntry<>(exps, msgType); + msgTypeMap.put(0, typeAndExps); + expToMessage.put(System.identityHashCode(message), exps); + } else { + typeAndExps.getKey().add(message); + expToMessage.put(System.identityHashCode(message), typeAndExps.getKey()); + Map updateExps = getUpdateSet(updateFromMessage, typeAndExps.getKey()); + if (compareTypes(typeAndExps.getValue(), msgType)) { + typeAndExps.setValue(msgType); + for (Expression e: typeAndExps.getKey()) { + if (e != message) { + if (e instanceof Variable) { + ((Variable) e).setType(msgType); + updateExps.put(System.identityHashCode(e), e); + } + } + } + } else if (compareTypes(msgType, typeAndExps.getValue())) { + ((Variable) message).setType(typeAndExps.getValue()); + updateExps.put(System.identityHashCode(message), message); + } + } + } else if (message instanceof Term) { + Map, Type>> msgTypeMap = messages.get(c); + if (msgTypeMap == null) { + msgTypeMap = new HashMap<>(); + messages.put(c, msgTypeMap); + } + for (int i = 0; i < ((Term) message).getArity(); i++) { + Expression arg = ((Term) message).getChild(i); + Type argType = null; + if (arg instanceof Variable) { + argType = ((Variable) arg).getType(); + } else if (arg instanceof Term) { + argType = ((Term) arg).getType(); + } else { + continue; + } + Map.Entry, Type> typeAndExps = msgTypeMap.get(i); + if (typeAndExps == null) { + List exps = new ArrayList<>(); + exps.add(arg); + typeAndExps = new AbstractMap.SimpleEntry<>(exps, argType); + msgTypeMap.put(i, typeAndExps); + expToMessage.put(System.identityHashCode(arg), exps); + } else { + typeAndExps.getKey().add(arg); + expToMessage.put(System.identityHashCode(arg), typeAndExps.getKey()); + Map updateExps = getUpdateSet(updateFromMessage, typeAndExps.getKey()); + if (compareTypes(typeAndExps.getValue(), argType)) { + typeAndExps.setValue(argType); + for (Expression e: typeAndExps.getKey()) { + if (e != arg) { + if (e instanceof Variable) { + ((Variable) e).setType(argType); + updateExps.put(System.identityHashCode(e), e); + } + } + } + } else if (compareTypes(argType, typeAndExps.getValue())) { + if (arg instanceof Variable) { + ((Variable) arg).setType(typeAndExps.getValue()); + updateExps.put(System.identityHashCode(arg), arg); + } else if (arg instanceof Term) { + ((Term) arg).setType(typeAndExps.getValue()); + updateExps.put(System.identityHashCode(arg), arg); + } + } + } + } + } + + Collection terms = new HashSet<>(); + if (st.getCurStateExpression() instanceof Term) { + Map subTerms = ((Term) st.getCurStateExpression()).getSubTerms(Term.class); + terms.addAll(subTerms.values()); + } + if (st.getMessageExpression() instanceof Term) { + Map subTerms = ((Term) st.getMessageExpression()).getSubTerms(Term.class); + terms.addAll(subTerms.values()); + } + if (st.getNextStateExpression() instanceof Term) { + Map subTerms = ((Term) st.getNextStateExpression()).getSubTerms(Term.class); + terms.addAll(subTerms.values()); + } + for (Term t: terms) { + if (t.getSymbol().equals(DataConstraintModel.cons)) { + // If the root symbol of the term is cons. + List consExps = new ArrayList<>(); + consExps.add(t); + expToCons.put(System.identityHashCode(t), consExps); + for (Expression e: t.getChildren()) { + consExps.add(e); + expToCons.put(System.identityHashCode(e), consExps); } - break; + Type newType = getExpTypeIfUpdatable(t.getType(), consExps.get(2)); + if (newType != null) { + // If the type of the second argument is more concrete than the type of the term. + t.setType(newType); + Map updateCons = getUpdateSet(updateFromCons, consExps); + updateCons.put(System.identityHashCode(t), t); + } else { + Type arg2Type = null; + if (consExps.get(2) != null && consExps.get(2) instanceof Variable) { + arg2Type = ((Variable) consExps.get(2)).getType(); + if (compareTypes(arg2Type, t.getType())) { + // If the type of the term is more concrete than the type of the second argument. + ((Variable) consExps.get(2)).setType(t.getType()); + Map updateCons = getUpdateSet(updateFromCons, consExps); + updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); + } + } else if (consExps.get(2) != null && consExps.get(2) instanceof Term) { + arg2Type = ((Term) consExps.get(2)).getType(); + if (compareTypes(arg2Type, t.getType())) { + // If the type of the term is more concrete than the type of the second argument. + ((Term) consExps.get(2)).setType(t.getType()); + Map updateCons = getUpdateSet(updateFromCons, consExps); + updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); + } + } + } + Type newCompType = getExpTypeIfUpdatable(listComponentTypes.get(t.getType()), consExps.get(1)); + if (newCompType != null) { + // If the type of the first argument is more concrete than the type of list component. + Type newListType = listTypes.get(newCompType); + if (newListType == null) { + // Create new list type. + newListType = createNewListType(newCompType, DataConstraintModel.typeList); + } + t.setType(newListType); + Map updateCons = getUpdateSet(updateFromCons, consExps); + updateCons.put(System.identityHashCode(t), t); + if (consExps.get(2) != null && consExps.get(2) instanceof Variable) { + ((Variable) consExps.get(2)).setType(newListType); + updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); + } else if (consExps.get(2) != null && consExps.get(2) instanceof Term) { + ((Term) consExps.get(2)).setType(newListType); + updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); + } + } + cons.put(System.identityHashCode(consExps), t.getType()); + } else if (t.getSymbol().equals(DataConstraintModel.tuple)) { + // If the root symbol of the term is tuple. + List tupleExps = new ArrayList<>(); + List argsTypeList = new ArrayList<>(); + tupleExps.add(t); + expToTuple.put(System.identityHashCode(t), tupleExps); + for (Expression e: t.getChildren()) { + tupleExps.add(e); + expToTuple.put(System.identityHashCode(e), tupleExps); + if (e instanceof Variable) { + argsTypeList.add(((Variable) e).getType()); + } else if (e instanceof Term) { + argsTypeList.add(((Term) e).getType()); + } else { + argsTypeList.add(null); + } + } + if (t.getType() == DataConstraintModel.typeTuple) { + Type newTupleType = tupleTypes.get(argsTypeList); + if (newTupleType == null) { + // Create new tuple type; + newTupleType = createNewTupleType(argsTypeList, DataConstraintModel.typeTuple); + } + t.setType(newTupleType); + Map updateExps = getUpdateSet(updateFromTuple, tupleExps); + updateExps.put(System.identityHashCode(t), t); + } + tuple.put(System.identityHashCode(tupleExps), t.getType()); } } - if (resource.getIdentifierTemplate().getResourceStateType() == null) - resource.getIdentifierTemplate().setResourceStateType(DataConstraintModel.typeInt); + } + } + + // Propagate type information. + while (updateFromResource.size() > 0 || updateFromVariable.size() > 0 || updateFromMessage.size() > 0 || updateFromCons.size() > 0 || updateFromTuple.size() > 0) { + if (updateFromResource.size() > 0) { + Set resourceKeys = updateFromResource.keySet(); + Integer resourceKey = resourceKeys.iterator().next(); + Map resourceValue = updateFromResource.get(resourceKey); + updateFromResource.remove(resourceKey); + for (int i: resourceValue.keySet()) { + Expression resExp = resourceValue.get(i); + updateVaribleTypes(resExp, variables, expToVariable, updateFromVariable); + updateMessageTypes(resExp, messages, expToMessage, updateFromMessage); + updateConsTypes(resExp, cons, expToCons, updateFromCons); + updateTupleTypes(resExp, tuple, expToTuple, updateFromTuple); + } + } + if (updateFromVariable.size() > 0) { + Set variableKeys = updateFromVariable.keySet(); + Integer variableKey = variableKeys.iterator().next(); + Map variableValue = updateFromVariable.get(variableKey); + updateFromVariable.remove(variableKey); + for (int i: variableValue.keySet()) { + Variable var = variableValue.get(i); + updateResourceTypes(var, resources, expToResource, updateFromResource); + updateMessageTypes(var, messages, expToMessage, updateFromMessage); + updateConsTypes(var, cons, expToCons, updateFromCons); + updateTupleTypes(var, tuple, expToTuple, updateFromTuple); + } + } + if (updateFromMessage.size() > 0) { + Set messageKeys = updateFromMessage.keySet(); + Integer messageKey = messageKeys.iterator().next(); + Map messageValue = updateFromMessage.get(messageKey); + updateFromMessage.remove(messageKey); + for (int i: messageValue.keySet()) { + Expression mesExp = messageValue.get(i); + updateResourceTypes(mesExp, resources, expToResource, updateFromResource); + updateVaribleTypes(mesExp, variables, expToVariable, updateFromVariable); + updateConsTypes(mesExp, cons, expToCons, updateFromCons); + updateTupleTypes(mesExp, tuple, expToTuple, updateFromTuple); + } + } + if (updateFromCons.size() > 0) { + Set consKeys = updateFromCons.keySet(); + Integer consKey = consKeys.iterator().next(); + Map consValue = updateFromCons.get(consKey); + updateFromCons.remove(consKey); + for (int i: consValue.keySet()) { + Expression consExp = consValue.get(i); + updateResourceTypes(consExp, resources, expToResource, updateFromResource); + updateVaribleTypes(consExp, variables, expToVariable, updateFromVariable); + updateMessageTypes(consExp, messages, expToMessage, updateFromMessage); + updateTupleTypes(consExp, tuple, expToTuple, updateFromTuple); + } + } + if (updateFromTuple.size() > 0) { + Set tupleKeys = updateFromTuple.keySet(); + Integer tupleKey = tupleKeys.iterator().next(); + Map tupleValue = updateFromTuple.get(tupleKey); + updateFromTuple.remove(tupleKey); + for (int i: tupleValue.keySet()) { + Expression tupleExp = tupleValue.get(i); + updateResourceTypes(tupleExp, resources, expToResource, updateFromResource); + updateVaribleTypes(tupleExp, variables, expToVariable, updateFromVariable); + updateMessageTypes(tupleExp, messages, expToMessage, updateFromMessage); + updateConsTypes(tupleExp, cons, expToCons, updateFromCons); + } + } + } + } + + private static void updateResourceTypes(Expression exp, Map> resources, Map> expToResource, Map> updateFromResource) { + List sameResource = expToResource.get(System.identityHashCode(exp)); + if (sameResource == null) return; + for (IdentifierTemplate id: resources.keySet()) { + if (resources.get(id) == sameResource) { + Type resType = id.getResourceStateType(); + Type newResType = getExpTypeIfUpdatable(resType, exp); + if (newResType != null) { + id.setResourceStateType(newResType); + Map updateExps = getUpdateSet(updateFromResource, sameResource); + for (Expression resExp: sameResource) { + if (resExp != exp) { + if (resExp instanceof Variable) { + ((Variable) resExp).setType(newResType); + updateExps.put(System.identityHashCode(resExp), resExp); + } else if (resExp instanceof Term) { + ((Term) resExp).setType(newResType); + updateExps.put(System.identityHashCode(resExp), resExp); + } + } + } + } } } } - private static void updateResourceType(ResourceNode resource, ChannelMember cm) { - Term rhs = ((Term) cm.getStateTransition().getNextStateExpression()); - if (rhs.getSymbol().equals(DataConstraintModel.cons)) { - Expression listItem = rhs.getChild(0); - if (listItem instanceof Variable) { - Type itemType = null; - if (((Variable) listItem).getType() != null) { - itemType = ((Variable) listItem).getType(); - } else { - if (cm.getStateTransition().getMessageExpression() instanceof Term) { - for (Variable var: ((Term) cm.getStateTransition().getMessageExpression()).getVariables().values()) { - if (((Variable) listItem).getName().equals(var.getName())) { - if (var.getType() != null) { - itemType = var.getType(); - } - } - } - } - if (cm.getStateTransition().getCurStateExpression() instanceof Term) { - for (Variable var: ((Term) cm.getStateTransition().getCurStateExpression()).getVariables().values()) { - if (((Variable) listItem).getName().equals(var.getName())) { - if (var.getType() != null) { - itemType = var.getType(); - } - } - } + private static void updateVaribleTypes(Expression exp, Map variables, Map> expToVariable, Map> updateFromVariable) { + if (exp instanceof Variable) { + Variable var = (Variable) exp; + List sameVariable = expToVariable.get(System.identityHashCode(var)); + if (sameVariable == null) return; + Type varType = variables.get(System.identityHashCode(sameVariable)); + Type newVarType = getExpTypeIfUpdatable(varType, exp); + if (newVarType != null) { + variables.put(System.identityHashCode(sameVariable), newVarType); + Map updateVars = getUpdateSet(updateFromVariable, sameVariable); + for (Variable v: sameVariable) { + if (v != var) { + v.setType(newVarType); + updateVars.put(System.identityHashCode(v), v); } } - if (itemType == DataConstraintModel.typeInt) { - resource.getIdentifierTemplate().setResourceStateType(DataConstraintModel.typeListOfInt); - } else if (itemType == DataConstraintModel.typeFloat) { - resource.getIdentifierTemplate().setResourceStateType(DataConstraintModel.typeListOfFloat); - } else if (itemType == DataConstraintModel.typeDouble) { - resource.getIdentifierTemplate().setResourceStateType(DataConstraintModel.typeListOfDouble); - } else if (itemType == DataConstraintModel.typeString) { - resource.getIdentifierTemplate().setResourceStateType(DataConstraintModel.typeListOfStr); - } else if (itemType == DataConstraintModel.typeList || itemType == DataConstraintModel.typeListOfList) { - resource.getIdentifierTemplate().setResourceStateType(DataConstraintModel.typeListOfList); - } - } else { - resource.getIdentifierTemplate().setResourceStateType(DataConstraintModel.typeList); } } } + + private static void updateMessageTypes(Expression exp, Map, Type>>> messages, Map> expToMessage, Map> updateFromMessage) { + List messageExps = expToMessage.get(System.identityHashCode(exp)); + if (messageExps == null) return; + Type msgType = null; + Map.Entry, Type> expsAndType = null; + for (ChannelGenerator c: messages.keySet()) { + for (int i: messages.get(c).keySet()) { + expsAndType = messages.get(c).get(i); + if (expsAndType.getKey() == messageExps) { + msgType = expsAndType.getValue(); + break; + } + } + if (msgType != null) break; + } + if (msgType == null) return; + Type newMsgType = getExpTypeIfUpdatable(msgType, exp); + if (newMsgType != null) { + expsAndType.setValue(newMsgType); + Map updateExps = getUpdateSet(updateFromMessage, messageExps); + for (Expression e: messageExps) { + if (e != exp) { + if (e instanceof Variable) { + ((Variable) e).setType(newMsgType); + updateExps.put(System.identityHashCode(e), e); + } else if (e instanceof Term) { + ((Term) e).setType(newMsgType); + updateExps.put(System.identityHashCode(e), e); + } + } + } + } + } + + private static void updateConsTypes(Expression exp, Map cons, Map> expToCons, Map> updateFromCons) { + List consComponents = expToCons.get(System.identityHashCode(exp)); + if (consComponents == null) return; + int idx = consComponents.indexOf(exp); + switch (idx) { + case 0: + if (!(exp instanceof Term)) return; + Type listType = cons.get(System.identityHashCode(consComponents)); + Type expType = getExpTypeIfUpdatable(listType, exp); + if (expType != null) { + cons.put(System.identityHashCode(consComponents), expType); + Map updateExps = getUpdateSet(updateFromCons, consComponents); + if (consComponents.get(2) instanceof Variable) { + ((Variable) consComponents.get(2)).setType(expType); + updateExps.put(System.identityHashCode(consComponents.get(2)), consComponents.get(2)); + } else if (consComponents.get(2) instanceof Term) { + ((Term) consComponents.get(2)).setType(expType); + updateExps.put(System.identityHashCode(consComponents.get(2)), consComponents.get(2)); + } + Type compType = listComponentTypes.get(expType); + if (consComponents.get(1) instanceof Variable) { + ((Variable) consComponents.get(1)).setType(compType); + updateExps.put(System.identityHashCode(consComponents.get(1)), consComponents.get(1)); + } else if (consComponents.get(1) instanceof Term) { + ((Term) consComponents.get(1)).setType(compType); + updateExps.put(System.identityHashCode(consComponents.get(1)), consComponents.get(1)); + } + } + break; + case 1: + listType = cons.get(System.identityHashCode(consComponents)); + Type compType = listComponentTypes.get(listType); + Type newCompType = getExpTypeIfUpdatable(compType, exp); + if (newCompType != null) { + Type newListType = listTypes.get(newCompType); + if (newListType == null) { + // Create new list type. + newListType = createNewListType(newCompType, listType); + } + cons.put(System.identityHashCode(consComponents), newListType); + Map updateExps = getUpdateSet(updateFromCons, consComponents); + if (consComponents.get(0) instanceof Term) { + ((Term) consComponents.get(0)).setType(newListType); + updateExps.put(System.identityHashCode(consComponents.get(0)), consComponents.get(0)); + } + if (consComponents.get(2) instanceof Variable) { + ((Variable) consComponents.get(2)).setType(newListType); + updateExps.put(System.identityHashCode(consComponents.get(2)), consComponents.get(2)); + } else if (consComponents.get(2) instanceof Term) { + ((Term) consComponents.get(2)).setType(newListType); + updateExps.put(System.identityHashCode(consComponents.get(2)), consComponents.get(2)); + } + } + break; + case 2: + listType = cons.get(System.identityHashCode(consComponents)); + expType = getExpTypeIfUpdatable(listType, exp); + if (expType != null) { + cons.put(System.identityHashCode(consComponents), expType); + Map updateExps = getUpdateSet(updateFromCons, consComponents); + if (consComponents.get(0) instanceof Term) { + ((Term) consComponents.get(0)).setType(expType); + updateExps.put(System.identityHashCode(consComponents.get(0)), consComponents.get(0)); + } + compType = listComponentTypes.get(expType); + if (consComponents.get(1) instanceof Variable) { + ((Variable) consComponents.get(1)).setType(compType); + updateExps.put(System.identityHashCode(consComponents.get(1)), consComponents.get(1)); + } else if (consComponents.get(1) instanceof Term) { + ((Term) consComponents.get(1)).setType(compType); + updateExps.put(System.identityHashCode(consComponents.get(1)), consComponents.get(1)); + } + } + } + } + + private static Type createNewListType(Type compType, Type parentType) { + String compTypeName = getInterfaceTypeName(compType); + Type newListType = new Type("List", "ArrayList<>", "List<" + compTypeName + ">", parentType); + listTypes.put(compType, newListType); + listComponentTypes.put(newListType, compType); + return newListType; + } + + private static void updateTupleTypes(Expression exp, Map tuple, Map> expToTuple, Map> updateFromTuple) { + List components = expToTuple.get(System.identityHashCode(exp)); + if (components == null) return; + int idx = components.indexOf(exp); + if (idx == 0) { + Type tupleType = tuple.get(System.identityHashCode(components)); + Type newTupleType = getExpTypeIfUpdatable(tupleType, exp); + if (newTupleType != null) { + tuple.put(System.identityHashCode(components), newTupleType); + List componentTypes = tupleComponentTypes.get(newTupleType); + Map updateExps = getUpdateSet(updateFromTuple, components); + for (int i = 0; i < components.size(); i++) { + Expression compExp = components.get(i); + if (compExp instanceof Variable) { + if (compareTypes(((Variable) compExp).getType(), componentTypes.get(i))) { + ((Variable) compExp).setType(componentTypes.get(i)); + updateExps.put(System.identityHashCode(compExp), compExp); + } + }else if (compExp instanceof Term) { + if (compareTypes(((Term) compExp).getType(), componentTypes.get(i))) { + ((Variable) compExp).setType(componentTypes.get(i)); + updateExps.put(System.identityHashCode(compExp), compExp); + } + } + } + } + } else { + Type tupleType = tuple.get(System.identityHashCode(components)); + List componentTypes = tupleComponentTypes.get(tupleType); + Type compType = componentTypes.get(idx - 1); + Type newCompType = getExpTypeIfUpdatable(compType, exp); + if (newCompType != null) { + componentTypes = new ArrayList<>(componentTypes); + componentTypes.set(idx - 1, newCompType); + Type newTupleType = tupleTypes.get(componentTypes); + if (newTupleType == null) { + // Create new tuple type; + newTupleType = createNewTupleType(componentTypes, tupleType); + } + Term t = (Term) components.get(0); + t.setType(newTupleType); + Map updateExps = getUpdateSet(updateFromTuple, components); + updateExps.put(System.identityHashCode(t), t); + tuple.put(System.identityHashCode(components), newTupleType); + } + } + } + + private static Type createNewTupleType(List componentTypes, Type parentTupleType) { + String implTypeName = "AbstractMap.SimpleEntry<>"; + String interfaceTypeName = "Map.Entry<$x>"; + if (componentTypes.size() >= 2) { + implTypeName = implTypeName.replace("$x", getImplementationTypeName(componentTypes.get(0))+ "$x"); + interfaceTypeName = interfaceTypeName.replace("$x", getInterfaceTypeName(componentTypes.get(0)) + "$x"); + for (Type argType: componentTypes.subList(1, componentTypes.size() - 1)) { + implTypeName = implTypeName.replace("$x", ", AbstractMap.SimpleEntry<" + getImplementationTypeName(argType)+ "$x>"); + interfaceTypeName = interfaceTypeName.replace("$x", ", Map.Entry<" + getInterfaceTypeName(argType) + "$x>"); + } + implTypeName = implTypeName.replace("$x", ", " + getImplementationTypeName(componentTypes.get(componentTypes.size() - 1))); + interfaceTypeName = interfaceTypeName.replace("$x", ", " + getInterfaceTypeName(componentTypes.get(componentTypes.size() - 1))); + } + Type newTupleType = new Type("Tuple", implTypeName, interfaceTypeName, parentTupleType); + tupleTypes.put(componentTypes, newTupleType); + tupleComponentTypes.put(newTupleType, componentTypes); + return newTupleType; + } + + private static String getImplementationTypeName(Type type) { + if (type == null) return "Integer"; + return wrapperType(type.getImplementationTypeName()); + } + + private static String getInterfaceTypeName(Type type) { + if (type == null) return "Integer"; + return wrapperType(type.getInterfaceTypeName()); + } + + private static String wrapperType(String implTypeName) { + if (implTypeName.equals(DataConstraintModel.typeInt.getImplementationTypeName())) { + implTypeName = "Integer"; + } else if (implTypeName.equals(DataConstraintModel.typeLong.getImplementationTypeName())) { + implTypeName = "Long"; + } else if (implTypeName.equals(DataConstraintModel.typeFloat.getImplementationTypeName())) { + implTypeName = "Float"; + } else if (implTypeName.equals(DataConstraintModel.typeDouble.getImplementationTypeName())) { + implTypeName = "Double"; + } else if (implTypeName.equals(DataConstraintModel.typeBoolean.getImplementationTypeName())) { + implTypeName = "Boolean"; + } + return implTypeName; + } + + private static > Map getUpdateSet(Map> updateSets, U keySet) { + Map updatedExps = updateSets.get(System.identityHashCode(keySet)); + if (updatedExps == null) { + updatedExps = new HashMap<>(); + updateSets.put(System.identityHashCode(keySet), updatedExps); + } + return updatedExps; + } + + private static Type getExpTypeIfUpdatable(Type originalType, Expression newExp) { + Type expType = null; + if (newExp instanceof Term) { + expType = ((Term) newExp).getType(); + } else if (newExp instanceof Variable) { + expType = ((Variable) newExp).getType(); + } + if (compareTypes(originalType, expType)) { + return expType; + } + return null; + } + + private static boolean compareTypes(Type originalType, Type newType) { + if (originalType == null || (originalType != newType && newType != null && originalType.isAncestorOf(newType))) { + return true; + } + return false; + } }