]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/query/QAtom.java
(refs #7375) Replace collectRefs by CollectRefsVisitor
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / elaboration / query / QAtom.java
1 package org.simantics.scl.compiler.elaboration.query;
2
3 import java.util.ArrayList;
4 import java.util.Set;
5
6 import org.simantics.scl.compiler.elaboration.contexts.ReplaceContext;
7 import org.simantics.scl.compiler.elaboration.contexts.TypingContext;
8 import org.simantics.scl.compiler.elaboration.expressions.ESimpleLet;
9 import org.simantics.scl.compiler.elaboration.expressions.EVariable;
10 import org.simantics.scl.compiler.elaboration.expressions.Expression;
11 import org.simantics.scl.compiler.elaboration.expressions.QueryTransformer;
12 import org.simantics.scl.compiler.elaboration.expressions.Variable;
13 import org.simantics.scl.compiler.elaboration.expressions.VariableProcedure;
14 import org.simantics.scl.compiler.elaboration.java.EqRelation;
15 import org.simantics.scl.compiler.elaboration.query.compilation.ConstraintCollectionContext;
16 import org.simantics.scl.compiler.elaboration.query.compilation.DerivateException;
17 import org.simantics.scl.compiler.elaboration.query.compilation.EnforcingContext;
18 import org.simantics.scl.compiler.elaboration.query.compilation.ExpressionConstraint;
19 import org.simantics.scl.compiler.elaboration.query.compilation.RelationConstraint;
20 import org.simantics.scl.compiler.elaboration.relations.CompositeRelation;
21 import org.simantics.scl.compiler.elaboration.relations.LocalRelation;
22 import org.simantics.scl.compiler.elaboration.relations.SCLRelation;
23 import org.simantics.scl.compiler.errors.Locations;
24 import org.simantics.scl.compiler.types.TVar;
25 import org.simantics.scl.compiler.types.Type;
26 import org.simantics.scl.compiler.types.Types;
27
28 import gnu.trove.map.hash.THashMap;
29 import gnu.trove.map.hash.TIntObjectHashMap;
30 import gnu.trove.map.hash.TObjectIntHashMap;
31 import gnu.trove.set.hash.THashSet;
32 import gnu.trove.set.hash.TIntHashSet;
33
34 public class QAtom extends Query {
35     public SCLRelation relation;
36     public Type[] typeParameters;
37     public Expression[] parameters;
38
39     public QAtom(SCLRelation relation, Expression ... parameters) {
40         this.relation = relation;
41         this.parameters = parameters;
42     }
43
44     public QAtom(SCLRelation relation, Type[] typeParameters, Expression ... parameters) {
45         this.relation = relation;
46         this.typeParameters = typeParameters;
47         this.parameters = parameters;
48     }
49
50     @Override
51     public void collectFreeVariables(THashSet<Variable> vars) {
52         for(Expression parameter : parameters)
53             parameter.collectFreeVariables(vars);
54     }
55
56     @Override
57     public void checkType(TypingContext context) {
58         // Type parameters
59         TVar[] typeVariables = relation.getTypeVariables();
60         typeParameters = new Type[typeVariables.length];
61         for(int i=0;i<typeVariables.length;++i)
62             typeParameters[i] = Types.metaVar(typeVariables[i].getKind());
63
64         // Check parameter types
65         Type[] parameterTypes = relation.getParameterTypes();
66         if(parameterTypes.length != parameters.length)
67             context.getErrorLog().log(location, "Relation is applied with wrong number of parameters.");
68         else
69             for(int i=0;i<parameters.length;++i)
70                 parameters[i] = parameters[i]
71                         .checkType(context, parameterTypes[i].replace(typeVariables, typeParameters));
72     }
73
74     public Expression generateEnforce(EnforcingContext context) {
75         Variable[] variables = new Variable[parameters.length];
76         for(int i=0;i<variables.length;++i)
77             if(parameters[i] instanceof EVariable)
78                 variables[i] = ((EVariable)parameters[i]).getVariable();
79             else
80                 variables[i] = new Variable("p" + i, parameters[i].getType());
81         Expression result = relation.generateEnforce(location, context, typeParameters, variables);
82         for(int i=variables.length-1;i>=0;--i)
83             if(!(parameters[i] instanceof EVariable))
84                 result = new ESimpleLet(
85                         variables[i],
86                         parameters[i],
87                         result
88                         );
89         return result;
90     }
91
92     private static class VariableMaskProcedure implements VariableProcedure {
93         ConstraintCollectionContext context;
94         long requiredVariablesMask = 0L;
95
96         public VariableMaskProcedure(ConstraintCollectionContext context) {
97             this.context = context;
98         }
99
100         @Override
101         public void execute(long location, Variable variable) {
102             int id = context.getVariableMap().get(variable);
103             if(id >= 0)
104                 requiredVariablesMask |= 1L << id;
105         }
106     }
107
108     @Override
109     public void collectConstraints(ConstraintCollectionContext context) {
110         try {
111             // Analyze parameters and find required and optional variables
112             VariableMaskProcedure procedure = new VariableMaskProcedure(context);
113             int[] optionalVariableByParameter = new int[parameters.length];
114             Variable[] varParameters = new Variable[parameters.length];
115             for(int i=0;i<parameters.length;++i) {
116                 Expression parameter = parameters[i];
117                 if(parameter instanceof EVariable) {
118                     Variable variable = ((EVariable)parameter).getVariable();
119                     optionalVariableByParameter[i] = context.getVariableMap().get(variable);
120                     varParameters[i] = variable;
121                 }
122                 else {
123                     Variable temp = new Variable("temp", parameter.getType());
124                     varParameters[i] = temp;
125                     if(parameter.isPattern(0)) {
126                         int tempId = context.addVariable(temp);
127                         context.addConstraint(new ExpressionConstraint(context, temp, parameter, true));
128                         optionalVariableByParameter[i] = tempId;
129                     }
130                     else {
131                         optionalVariableByParameter[i] = -1;
132                         parameter.forVariableUses(procedure);
133                     }
134                 }
135             }
136
137             // Combine required and optional variables
138             TIntHashSet allVariablesSet = new TIntHashSet();
139             for(int v : optionalVariableByParameter)
140                 if(v >= 0)
141                     allVariablesSet.add(v);
142
143             context.addConstraint(new RelationConstraint(allVariablesSet.toArray(), varParameters, this,
144                     optionalVariableByParameter, procedure.requiredVariablesMask));
145         } catch(Exception e) {
146             context.getQueryCompilationContext().getTypingContext().getErrorLog().log(location, e);
147         }
148     }
149
150     @Override
151     public void collectVars(TObjectIntHashMap<Variable> allVars,
152             TIntHashSet vars) {
153         for(Expression parameter : parameters)
154             parameter.collectVars(allVars, vars);
155     }
156
157     @Override
158     public Query replace(ReplaceContext context) {
159         Type[] newTypeParameters;
160         if(typeParameters == null)
161             newTypeParameters = null;
162         else {
163             newTypeParameters = new Type[typeParameters.length];
164             for(int i=0;i<typeParameters.length;++i)
165                 newTypeParameters[i] = typeParameters[i].replace(context.tvarMap);
166         }
167         return new QAtom(relation,
168                 newTypeParameters,
169                 Expression.replace(context, parameters));
170     }
171
172     @SuppressWarnings("unchecked")
173     @Override
174     public Diff[] derivate(THashMap<LocalRelation, Diffable> diffables) throws DerivateException {
175         Diffable diffable = diffables.get(relation);
176         if(diffable == null) {
177             if(relation instanceof CompositeRelation && 
178                     containsReferenceTo((CompositeRelation)relation,
179                             (THashMap<SCLRelation, Diffable>)(THashMap)diffables))
180                 throw new DerivateException(location);
181             return NO_DIFF;
182         }
183         else {
184             Query[] eqs = new Query[parameters.length];
185             for(int i=0;i<parameters.length;++i) {
186                 QAtom eq = new QAtom(EqRelation.INSTANCE, new Expression[] {
187                         new EVariable(diffable.parameters[i]),
188                         parameters[i]
189                 });
190                 eq.setLocationDeep(location);
191                 eq.typeParameters = new Type[] {parameters[i].getType()};
192                 eqs[i] = eq;
193             }
194             return new Diff[] { new Diff(diffable.id, new QConjunction(eqs)) };
195         }
196     }
197
198     private static boolean containsReferenceTo(
199             CompositeRelation relation,
200             THashMap<SCLRelation, Diffable> diffables) {
201         for(SCLRelation r : relation.getSubrelations())
202             if(diffables.containsKey(r))
203                 return true;
204             else if(r instanceof CompositeRelation &&
205                     containsReferenceTo((CompositeRelation)r, diffables))
206                 return true;
207         return false;
208     }
209
210     @Override
211     public Query removeRelations(Set<SCLRelation> relations) {
212         if(relations.contains(relation))
213             return EMPTY_QUERY;
214         else
215             return this;
216     }
217
218     @Override
219     public void setLocationDeep(long loc) {
220         if(location == Locations.NO_LOCATION) {
221             location = loc;
222             for(Expression parameter : parameters)
223                 parameter.setLocationDeep(loc);
224         }
225     }
226
227     @Override
228     public void accept(QueryVisitor visitor) {
229         visitor.visit(this);
230     }
231
232     @Override
233     public void splitToPhases(TIntObjectHashMap<ArrayList<Query>> result) {
234         int phase = relation.getPhase();
235         ArrayList<Query> list = result.get(phase);
236         if(list == null) {
237             list = new ArrayList<Query>();
238             result.put(phase, list);
239         }
240         list.add(this);
241     }
242
243     @Override
244     public Query accept(QueryTransformer transformer) {
245         return transformer.transform(this);
246     }
247
248 }