537a2aa9dca73a0495b7eb794d8f7c5abf16bef0
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / elaboration / chr / planning / QueryPlanningContext.java
1 package org.simantics.scl.compiler.elaboration.chr.planning;
2
3 import java.util.ArrayList;
4
5 import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
6 import org.simantics.scl.compiler.compilation.CompilationContext;
7 import org.simantics.scl.compiler.elaboration.chr.CHRLiteral;
8 import org.simantics.scl.compiler.elaboration.chr.plan.AccessFactOp;
9 import org.simantics.scl.compiler.elaboration.chr.plan.ClaimOp;
10 import org.simantics.scl.compiler.elaboration.chr.plan.ExecuteOp;
11 import org.simantics.scl.compiler.elaboration.chr.plan.PlanOp;
12 import org.simantics.scl.compiler.elaboration.chr.planning.items.CheckPrePlanItem;
13 import org.simantics.scl.compiler.elaboration.chr.planning.items.EqualsPrePlanItem;
14 import org.simantics.scl.compiler.elaboration.chr.planning.items.GenericPrePlanItem;
15 import org.simantics.scl.compiler.elaboration.chr.planning.items.MemberPrePlanItem;
16 import org.simantics.scl.compiler.elaboration.chr.relations.CHRConstraint;
17 import org.simantics.scl.compiler.elaboration.chr.relations.SpecialCHRRelation;
18 import org.simantics.scl.compiler.elaboration.expressions.EApplyType;
19 import org.simantics.scl.compiler.elaboration.expressions.EConstant;
20 import org.simantics.scl.compiler.elaboration.expressions.EExternalConstant;
21 import org.simantics.scl.compiler.elaboration.expressions.ELiteral;
22 import org.simantics.scl.compiler.elaboration.expressions.EVariable;
23 import org.simantics.scl.compiler.elaboration.expressions.Expression;
24 import org.simantics.scl.compiler.elaboration.expressions.Variable;
25
26 import gnu.trove.impl.Constants;
27 import gnu.trove.map.hash.TObjectIntHashMap;
28 import gnu.trove.procedure.TIntProcedure;
29 import gnu.trove.set.hash.TIntHashSet;
30
31 public class QueryPlanningContext {
32     CompilationContext compilationContext;
33     public PlanPriorityQueue priorityQueue = new PlanPriorityQueue();
34     ArrayList<Variable> variables;
35     TObjectIntHashMap<Variable> variableMap;
36     ArrayList<ArrayList<PrePlanItem>> itemsContainingVariable;
37     ArrayList<PlanOp> planOps = new ArrayList<PlanOp>(); 
38     
39     public QueryPlanningContext(CompilationContext compilationContext, Variable[] variables) {
40         this.compilationContext = compilationContext;
41         this.variables = new ArrayList<Variable>(variables.length*2);
42         this.variableMap = new TObjectIntHashMap<Variable>(variables.length, Constants.DEFAULT_LOAD_FACTOR, -1);
43         itemsContainingVariable = new ArrayList<ArrayList<PrePlanItem>>(variables.length*2);
44         for(Variable variable : variables)
45             addVariable(variable);
46     }
47     
48     private void addVariable(Variable variable) {
49         int id = variables.size();
50         variables.add(variable);
51         variableMap.put(variable, id);
52         itemsContainingVariable.add(new ArrayList<PrePlanItem>(2));
53     }
54
55     public void add(CHRLiteral literal, int secondaryPriority) {
56         if(literal.relation instanceof SpecialCHRRelation) {
57             switch((SpecialCHRRelation)literal.relation) {
58             case CHECK:
59                 addCheck(literal.location, literal.parameters[0], secondaryPriority);
60                 return;
61             case EQUALS:
62                 addGenericEquals(literal.location, literal.parameters[0], literal.parameters[1], secondaryPriority);
63                 return;
64             case MEMBER:
65                 addMember(literal.location, literal.parameters[0], literal.parameters[1], secondaryPriority);
66                 return;
67             case EXECUTE:
68                 throw new InternalCompilerError(literal.location, "EXECUTE constraint is not allowed in query compilation.");
69             }
70         }
71         
72         addGenericConstraint(literal, secondaryPriority);
73     }
74     
75     private TIntHashSet getVars(Expression expression, int initialCapacity) {
76         TIntHashSet variableSet = new TIntHashSet(initialCapacity);
77         expression.collectVars(variableMap, variableSet);
78         return variableSet;
79     }
80     
81     private TIntHashSet[] getVars(Expression[] expressions, int initialCapacity) {
82         TIntHashSet[] variableSets = new TIntHashSet[expressions.length];
83         for(int i=0;i<expressions.length;++i)
84             variableSets[i] = getVars(expressions[i], initialCapacity);
85         return variableSets;
86     }
87     
88     /**
89      * Returns true, if the expression is so simple, it does not involve any computation.
90      */
91     private static boolean isSimpleExpression(Expression expression) {
92         while(expression instanceof EApplyType)
93             expression = ((EApplyType)expression).getExpression();
94         return expression instanceof EVariable 
95                 || expression instanceof EConstant
96                 || expression instanceof ELiteral
97                 || expression instanceof EExternalConstant;
98     }
99     
100     private Expression toSimpleExpression(Expression expression, int secondaryPriority) {
101         if(isSimpleExpression(expression))        
102             return expression;
103         else {
104             Variable temp = new Variable("temp", expression.getType());
105             addVariable(temp);
106             addOneSidedEquals(expression.location, new EVariable(temp), expression, secondaryPriority);
107             return new EVariable(temp);
108         }
109     }
110     
111     private Expression[] toSimpleExpressions(Expression[] expressions, int secondaryPriority) {
112         Expression[] result = new Expression[expressions.length];
113         for(int i=0;i<expressions.length;++i)
114             result[i] = toSimpleExpression(expressions[i], secondaryPriority);
115         return result;
116     }
117
118     private void addGenericConstraint(CHRLiteral literal, int secondaryPriority) {
119         if(literal.killAfterMatch)
120             ((CHRConstraint)literal.relation).setMayBeRemoved();
121         Expression[] parameters = toSimpleExpressions(literal.parameters, secondaryPriority);
122         add(literal.location, new GenericPrePlanItem(literal, literal.relation, parameters, getVars(parameters, 1), secondaryPriority));
123     }
124
125     private void addMember(long location, Expression p1, Expression p2, int secondaryPriority) {
126         Expression expression1 = toSimpleExpression(p1, secondaryPriority);
127         Expression expression2 = toSimpleExpression(p2, secondaryPriority);
128         add(location, new MemberPrePlanItem(expression1, expression2,
129                 getVars(expression1, 1), getVars(expression2, 1), secondaryPriority));
130     }
131     
132     private void addOneSidedEquals(long location, Expression expression1, Expression expression2, int secondaryPriority) {
133         add(location, new EqualsPrePlanItem(expression1, expression2,
134                 getVars(expression1, 1), getVars(expression2, 4), secondaryPriority));
135     }
136
137     private void addGenericEquals(long location, Expression p1, Expression p2, int secondaryPriority) {
138         if(isSimpleExpression(p1))
139             addOneSidedEquals(location, p1, p2, secondaryPriority);
140         else if(isSimpleExpression(p2))
141             addOneSidedEquals(location, p2, p1, secondaryPriority);
142         else {
143             Variable temp = new Variable("temp", p1.getType());
144             addVariable(temp);
145             addOneSidedEquals(p1.location, new EVariable(temp), p1, secondaryPriority);
146             addOneSidedEquals(p2.location, new EVariable(temp), p2, secondaryPriority);
147         }
148     }
149
150     private void addCheck(long location, Expression condition, int secondaryPriority) {
151         TIntHashSet variableSet = new TIntHashSet(4);
152         condition.collectVars(variableMap, variableSet);
153         add(location, new CheckPrePlanItem(condition, variableSet, secondaryPriority));
154     }
155
156     private void add(long location, PrePlanItem item) {
157         priorityQueue.add(item);
158         item.initializeListeners(this);
159         item.location = location;
160     }
161
162     public void listen(TIntHashSet variableSet, PrePlanItem item) {
163         variableSet.forEach(new TIntProcedure() {
164             @Override
165             public boolean execute(int variableId) {
166                 listen(variableId, item);
167                 return true;
168             }
169         });
170     }
171
172     public void listen(int variableId, PrePlanItem item) {
173         itemsContainingVariable.get(variableId).add(item);
174     }
175
176     public boolean createQueryPlan() {
177         while(!priorityQueue.isEmpty()) {
178             PrePlanItem head = priorityQueue.head();
179             if(head.primaryPriority == Double.POSITIVE_INFINITY) {
180                 compilationContext.errorLog.log(head.location, "Cannot solve the query.");
181                 return false;
182             }
183             priorityQueue.pop();
184             head.generate(this);
185         }
186         return true;
187     }
188     
189     public ArrayList<PlanOp> getPlanOps() {
190         return planOps;
191     }
192     
193     private final TIntProcedure BIND_PROCEDURE = new TIntProcedure() {
194         @Override
195         public boolean execute(int variableId) {
196             ArrayList<PrePlanItem> l = itemsContainingVariable.get(variableId);
197             for(PrePlanItem item : l)
198                 item.variableSolved(QueryPlanningContext.this, variableId);
199             l.clear();
200             return true;
201         }
202     };
203
204     public void bind(TIntHashSet variableSet) {
205         variableSet.forEach(BIND_PROCEDURE);
206     }
207
208     public void addPlanOp(PlanOp planOp) {
209         planOps.add(planOp);
210     }
211
212     public CompilationContext getCompilationContext() {
213         return compilationContext;
214     }
215
216     public void activate(CHRLiteral literal, Expression inputFact, int secondaryPriority) {
217         Variable[] variables = new Variable[literal.parameters.length];
218         for(int i=0;i<literal.parameters.length;++i)
219             variables[i] = new Variable("activeFactComponent" + i, literal.parameters[i].getType());
220         if(literal.killAfterMatch)
221             ((CHRConstraint)literal.relation).setMayBeRemoved();
222         planOps.add(new AccessFactOp(literal.location, inputFact, (CHRConstraint)literal.relation, variables, literal.killAfterMatch));
223         for(int i=0;i<literal.parameters.length;++i)
224             addOneSidedEquals(literal.parameters[i].location, new EVariable(variables[i]), literal.parameters[i], secondaryPriority);
225     }
226
227     public void claim(QueryPlanningContext context, CHRLiteral literal) {
228         if(literal.relation instanceof CHRConstraint) {
229             CHRConstraint constraint = (CHRConstraint)literal.relation;
230             addPlanOp(new ClaimOp(literal.location, constraint, literal.parameters));
231         }
232         else if(literal.relation instanceof SpecialCHRRelation) {
233             switch((SpecialCHRRelation)literal.relation) {
234             case EXECUTE:
235                 addPlanOp(new ExecuteOp(literal.location, literal.parameters[0]));
236                 break;
237             default:
238                 context.getCompilationContext().errorLog.log(
239                         literal.location,
240                         "Cannot enforce this constraint.");
241             }
242         }
243     }
244
245 }