Refactoring CHR handling code
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / elaboration / chr / CHRRuleset.java
1 package org.simantics.scl.compiler.elaboration.chr;
2
3 import java.util.ArrayList;
4
5 import org.cojen.classfile.TypeDesc;
6 import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
7 import org.simantics.scl.compiler.compilation.CompilationContext;
8 import org.simantics.scl.compiler.constants.BooleanConstant;
9 import org.simantics.scl.compiler.constants.Constant;
10 import org.simantics.scl.compiler.constants.IntegerConstant;
11 import org.simantics.scl.compiler.constants.JavaConstructor;
12 import org.simantics.scl.compiler.constants.JavaMethod;
13 import org.simantics.scl.compiler.constants.NoRepConstant;
14 import org.simantics.scl.compiler.constants.generic.CallJava;
15 import org.simantics.scl.compiler.constants.generic.MethodRef.FieldRef;
16 import org.simantics.scl.compiler.constants.generic.MethodRef.ObjectMethodRef;
17 import org.simantics.scl.compiler.constants.generic.MethodRef.SetFieldRef;
18 import org.simantics.scl.compiler.elaboration.chr.analysis.CHRConstraintGGInfo;
19 import org.simantics.scl.compiler.elaboration.chr.analysis.UsageAnalysis;
20 import org.simantics.scl.compiler.elaboration.chr.plan.CHRSearchPlan;
21 import org.simantics.scl.compiler.elaboration.chr.plan.PlanRealizer;
22 import org.simantics.scl.compiler.elaboration.chr.relations.CHRConstraint;
23 import org.simantics.scl.compiler.elaboration.contexts.SimplificationContext;
24 import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;
25 import org.simantics.scl.compiler.elaboration.contexts.TypingContext;
26 import org.simantics.scl.compiler.elaboration.expressions.Variable;
27 import org.simantics.scl.compiler.elaboration.expressions.block.IncludeStatement;
28 import org.simantics.scl.compiler.environment.AmbiguousNameException;
29 import org.simantics.scl.compiler.errors.Locations;
30 import org.simantics.scl.compiler.internal.codegen.chr.CHRCodeGenerationConstants;
31 import org.simantics.scl.compiler.internal.codegen.chr.CHRRuntimeRulesetCodeGenerator;
32 import org.simantics.scl.compiler.internal.codegen.references.BoundVar;
33 import org.simantics.scl.compiler.internal.codegen.references.IVal;
34 import org.simantics.scl.compiler.internal.codegen.ssa.SSAFunction;
35 import org.simantics.scl.compiler.internal.codegen.types.StandardTypeConstructor;
36 import org.simantics.scl.compiler.internal.codegen.utils.Constants;
37 import org.simantics.scl.compiler.internal.codegen.writer.CodeWriter;
38 import org.simantics.scl.compiler.internal.parsing.Symbol;
39 import org.simantics.scl.compiler.types.TCon;
40 import org.simantics.scl.compiler.types.TVar;
41 import org.simantics.scl.compiler.types.Type;
42 import org.simantics.scl.compiler.types.Types;
43
44 import gnu.trove.map.hash.THashMap;
45 import gnu.trove.map.hash.TObjectIntHashMap;
46 import gnu.trove.set.hash.TIntHashSet;
47
48 public class CHRRuleset extends Symbol {
49     
50     public static final String INIT_CONSTRAINT = "__INIT__";
51     
52     public ArrayList<CHRConstraint> constraints = new ArrayList<CHRConstraint>();
53     public ArrayList<CHRRule> rules = new ArrayList<CHRRule>();
54     public ArrayList<IncludeStatement> includes = new ArrayList<IncludeStatement>();
55     public THashMap<CHRConstraint, IncludeStatement> constraintSourceMap = new THashMap<CHRConstraint, IncludeStatement>();
56     public THashMap<CHRConstraint, CHRConstraintGGInfo> activeConstraintGGInfo = new THashMap<CHRConstraint, CHRConstraintGGInfo>(); // contains also imported constraints
57     public THashMap<IncludeStatement, ArrayList<CHRConstraint>> inverseActiveConstraintSourceMap = new THashMap<IncludeStatement, ArrayList<CHRConstraint>>();
58     
59     public boolean extensible;
60     
61     public CHRConstraint initConstraint;
62     public int priorityCount;
63     
64     public int initialPriorityNumber = 0;
65     
66     public String runtimeRulesetClassName;
67     public TCon runtimeRulesetType;
68     public BoundVar runtimeRulesetVariable;
69     public TypeDesc runtimeRulesetTypeDesc;
70     
71     public static final Constant READ_CURRENT_ID = new CallJava(TVar.EMPTY_ARRAY, Types.PROC, Types.INTEGER, new Type[] {Types.CHRContext},
72             null, new FieldRef(CHRCodeGenerationConstants.CHRContext_name, "currentId", CHRRuntimeRulesetCodeGenerator.FACT_ID_TYPE), null);
73     public static final Constant WRITE_CURRENT_ID = new CallJava(TVar.EMPTY_ARRAY, Types.PROC, Types.UNIT, new Type[] {Types.CHRContext, Types.INTEGER},
74             null, new SetFieldRef(CHRCodeGenerationConstants.CHRContext_name, "currentId", CHRRuntimeRulesetCodeGenerator.FACT_ID_TYPE), null);
75     public static final Constant GENERATE_ID = new CallJava(TVar.EMPTY_ARRAY, Types.PROC, Types.INTEGER, new Type[] {Types.CHRContext},
76             null, new ObjectMethodRef(false, CHRCodeGenerationConstants.CHRContext_name, "generateId", CHRRuntimeRulesetCodeGenerator.FACT_ID_TYPE, Constants.EMPTY_TYPEDESC_ARRAY), null);
77     
78     // FIXME remove and change the parameter of Expression.toVal
79     private CompilationContext cachedContext;
80     
81     // For code generation
82     public CHRRulesetObject rulesetObject;
83     public SSAFunction initializer;
84     public SSAFunction deinitializer;
85     
86     public CHRRuleset() {
87         initConstraint = new CHRConstraint(Locations.NO_LOCATION, INIT_CONSTRAINT, Type.EMPTY_ARRAY);
88         addConstraint(initConstraint);
89     }
90     
91     public void addConstraint(CHRConstraint constraint) {
92         constraints.add(constraint);
93         constraint.setParent(this);
94     }
95     
96     public void resolve(TranslationContext context) {
97         boolean failedIncludes = false;
98         for(IncludeStatement include : includes) {
99             try {
100                 include.ruleset = context.resolveRuleset(include.name.text);
101                 if(include.ruleset == null) {
102                     failedIncludes = true;
103                     context.getErrorLog().log(include.name.location, "Couldn't resolve ruleset " + include.name + ".");
104                     continue;
105                 }
106                 include.value = include.value.resolve(context);
107                 for(CHRConstraint constraint : include.ruleset.constraints) {
108                     context.newCHRConstraint(constraint.name, constraint);
109                     constraintSourceMap.put(constraint, include);
110                 }
111             } catch (AmbiguousNameException e) {
112                 failedIncludes = true;
113                 context.getErrorLog().log(include.name.location, e.getMessage());
114             }
115         }
116         if(failedIncludes)
117             return;
118         for(CHRConstraint constraint : constraints)
119             context.newCHRConstraint(constraint.name, constraint);
120         priorityCount = 0;
121         for(CHRRule rule : rules) {
122             rule.resolve(context);
123             rule.priority = priorityCount++;
124         }
125         /*for(CHRConstraint constraint : constraints) {
126             Variable newVariable = context.newVariable("claim" + constraint.factClassName);
127         }*/
128     }
129
130     public void checkType(TypingContext context) {
131         for(IncludeStatement include : includes)
132             include.value = include.value.checkType(context, include.ruleset.runtimeRulesetType);
133         for(CHRRule rule : rules)
134             rule.checkType(context);
135     }
136
137     public void collectVars(TObjectIntHashMap<Variable> allVars, TIntHashSet vars) {
138         for(IncludeStatement include : includes)
139             include.value.collectVars(allVars, vars);
140         for(CHRRule rule : rules)
141             rule.collectVars(allVars, vars);
142     }
143
144     public void setLocationDeep(long loc) {
145         if(location == Locations.NO_LOCATION) {
146             this.location = loc;
147             for(CHRRule rule : rules)
148                 rule.setLocationDeep(loc);
149         }
150     }
151     
152     public int getMinimumPriority(CHRConstraint constraint) {
153         CHRConstraintGGInfo info = activeConstraintGGInfo.get(constraint);
154         if(info == null)
155             return Integer.MAX_VALUE;
156         else
157             return info.minimumPriority;
158     }
159     
160     public int getAndUpdateNextPriority(CHRConstraint constraint, int nextPriority) {
161         CHRConstraintGGInfo info = activeConstraintGGInfo.get(constraint);
162         if(info == null)
163             return Integer.MAX_VALUE;
164         else {
165             int result = info.nextPriority;
166             info.nextPriority = nextPriority;
167             return result;
168         }
169     }
170
171     private void compile(SimplificationContext context) {
172         try {
173             initializeCodeGeneration(context.getCompilationContext());
174             if(extensible)
175                 applyExtensibleDefaults();
176             UsageAnalysis.analyzeUsage(this);
177             for(CHRRule rule : rules) {
178                 rule.compile(context.getCompilationContext(), initConstraint);
179                 for(CHRSearchPlan plan : rule.plans) {
180                     CHRConstraint constraint = plan.constraint;
181                     if(!activeConstraintGGInfo.containsKey(constraint)) {
182                         activeConstraintGGInfo.put(constraint, new CHRConstraintGGInfo(rule.priority));
183                         IncludeStatement include = constraintSourceMap.get(constraint);
184                         if(include != null) {
185                             ArrayList<CHRConstraint> list = inverseActiveConstraintSourceMap.get(include);
186                             if(list == null) {
187                                 list = new ArrayList<CHRConstraint>(4);
188                                 inverseActiveConstraintSourceMap.put(include, list);
189                             }
190                             list.add(constraint);
191                         }
192                     }
193                 }
194             }
195             // remove init constraint if it is not useful
196             if(getMinimumPriority(initConstraint) == Integer.MAX_VALUE) {
197                 constraints.remove(0);
198                 initConstraint = null;
199             }
200         } catch(Exception e) {
201             throw InternalCompilerError.injectLocation(location, e);
202         }
203     }
204
205     private void applyExtensibleDefaults() {
206         for(CHRConstraint constraint : constraints) {
207             // FIXME Too much indexing!!!
208             int max = 1 << constraint.parameterTypes.length;
209             for(int i=0;i<max;++i)
210                 constraint.getOrCreateIndex(cachedContext, i);
211             constraint.setMayBeRemoved();
212             /*
213             constraint.getOrCreateIndex(cachedContext, 0);
214             if(constraint.parameterTypes.length > 0)
215                 constraint.getOrCreateIndex(cachedContext, 1);*/
216         }
217     }
218
219     private void simplify(SimplificationContext context) {
220         for(IncludeStatement include : includes) {
221             //include.ruleset.simplifyAndCompileIfNeeded(context);
222             include.value = include.value.simplify(context);
223         }
224         for(CHRRule rule : rules)
225             rule.simplify(context);
226     }
227     
228     public void simplifyAndCompileIfNeeded(SimplificationContext context) {
229         if(runtimeRulesetTypeDesc == null) {
230             simplify(context);
231             compile(context);
232         }
233     }
234     
235     public void setRulesetType(TCon type, String className) {
236         this.runtimeRulesetType = type;
237         this.runtimeRulesetClassName = className;
238     }
239     
240     public void initializeCodeGeneration(CompilationContext context) {
241         cachedContext = context; // FIXME remove
242         
243         boolean createTypeDesc = false;
244         if(runtimeRulesetType == null) {
245             String suffix = context.namingPolicy.getFreshClosureClassNameSuffix();
246             setRulesetType(Types.con(context.namingPolicy.getModuleName(), "CHR" + suffix), context.namingPolicy.getModuleClassName() + suffix);
247             createTypeDesc = true;
248         }
249         runtimeRulesetTypeDesc = TypeDesc.forClass(runtimeRulesetClassName);
250         runtimeRulesetVariable = new BoundVar(runtimeRulesetType);
251         for(CHRConstraint constraint : constraints)
252             constraint.initializeCodeGeneration(context, this);
253         if(createTypeDesc && context.module != null) // for unit testing
254             context.module.addTypeDescriptor(runtimeRulesetType.name, new StandardTypeConstructor(runtimeRulesetType, TVar.EMPTY_ARRAY, runtimeRulesetTypeDesc));
255     }
256
257     public static final Constant ACTIVATE = new JavaMethod(true, CHRCodeGenerationConstants.CHRContext_name, "activate", Types.PROC, Types.UNIT, Types.CHRContext, Types.INTEGER);
258     private static final Constant CREATE_CHR_CONTEXT = new JavaConstructor("org/simantics/scl/runtime/chr/CHRContext", Types.PROC, Types.CHRContext);
259     
260     public IVal generateCode(CodeWriter w) {
261         for(IncludeStatement include : includes) {
262             include.storeVar = include.value.toVal(cachedContext, w);
263             initialPriorityNumber = Math.max(initialPriorityNumber, include.ruleset.initialPriorityNumber + include.ruleset.priorityCount);
264         }
265         CHRRulesetObject object = new CHRRulesetObject(runtimeRulesetVariable, this);
266         w.defineObject(object);
267         for(CHRRule rule : rules) {
268             for(CHRSearchPlan plan : rule.plans) {
269                 /*System.out.println("    plan " + plan.priority);
270                 for(PlanOp planOp : plan.ops)
271                     System.out.println("        " + planOp);*/
272                 CodeWriter methodWriter = object.createMethod(w.getModuleWriter(), TVar.EMPTY_ARRAY, Types.PROC, Types.BOOLEAN, new Type[] {Types.CHRContext, plan.constraint.factType});
273                 plan.implementation = methodWriter.getFunction();
274                 IVal[] implementationParameters = methodWriter.getParameters();
275                 plan.activeFact.setVal(implementationParameters[1]);
276                 PlanRealizer realizer = new PlanRealizer(cachedContext, this, runtimeRulesetVariable, implementationParameters[0], plan.ops);
277                 realizer.nextOp(methodWriter);
278                 if(methodWriter.isUnfinished())
279                     methodWriter.return_(rule.location, BooleanConstant.TRUE);
280             }
281         }
282         if(!includes.isEmpty() || extensible) {
283             {
284                 CodeWriter methodWriter = object.createMethod(w.getModuleWriter(), TVar.EMPTY_ARRAY, Types.PROC, Types.UNIT, new Type[] {Types.CHRContext});
285                 initializer = methodWriter.getFunction();
286                 for(IncludeStatement include : includes) {
287                     methodWriter.apply(include.location,
288                             new JavaMethod(true, runtimeRulesetClassName, "register", Types.PROC, Types.UNIT, new Type[] {runtimeRulesetType, Types.CHRContext, include.ruleset.runtimeRulesetType}),
289                             object.getTarget(), methodWriter.getParameters()[0], include.storeVar);
290                 }
291                 if(extensible)
292                         methodWriter.apply(Locations.NO_LOCATION,
293                                 new JavaMethod(true, runtimeRulesetClassName, "register", Types.PROC, Types.UNIT, new Type[] {runtimeRulesetType, Types.CHRContext}),
294                                 object.getTarget(), methodWriter.getParameters()[0]);
295                 methodWriter.return_(location, NoRepConstant.UNIT);
296             }
297             {
298                 CodeWriter methodWriter = object.createMethod(w.getModuleWriter(), TVar.EMPTY_ARRAY, Types.PROC, Types.UNIT, new Type[] {Types.CHRContext});
299                 deinitializer = methodWriter.getFunction();
300                 for(IncludeStatement include : includes) {
301                     methodWriter.apply(include.location,
302                             new JavaMethod(true, runtimeRulesetClassName, "unregister", Types.PROC, Types.UNIT, new Type[] {runtimeRulesetType, Types.CHRContext, include.ruleset.runtimeRulesetType}),
303                             object.getTarget(), methodWriter.getParameters()[0], include.storeVar);
304                 }
305                 if(extensible)
306                         methodWriter.apply(Locations.NO_LOCATION,
307                                 new JavaMethod(true, runtimeRulesetClassName, "unregister", Types.PROC, Types.UNIT, new Type[] {runtimeRulesetType, Types.CHRContext}),
308                                 object.getTarget(), methodWriter.getParameters()[0]);
309                 methodWriter.return_(location, NoRepConstant.UNIT);
310             }
311         }
312         if(initConstraint != null) {
313             IVal chrContext = w.apply(location, CREATE_CHR_CONTEXT);
314             if(initializer != null) {
315                 w.apply(location,
316                         new JavaMethod(true, runtimeRulesetClassName, "initialize", Types.PROC, Types.UNIT, new Type[] {runtimeRulesetType, Types.CHRContext}),
317                         object.getTarget(), chrContext);
318             }
319             IVal initFact = w.apply(location, initConstraint.constructor, w.apply(location, GENERATE_ID, chrContext));
320             w.apply(location, initConstraint.addProcedure, runtimeRulesetVariable, chrContext, initFact);
321             w.apply(location, ACTIVATE, chrContext, new IntegerConstant(Integer.MAX_VALUE));
322             if(deinitializer != null) {
323                 w.apply(location,
324                         new JavaMethod(true, runtimeRulesetClassName, "deinitialize", Types.PROC, Types.UNIT, new Type[] {runtimeRulesetType, Types.CHRContext}),
325                         object.getTarget(), chrContext);
326             }
327         }
328         return runtimeRulesetVariable;
329     }
330
331     public void addRule(CHRRule rule) {
332         rules.add(rule);
333         rule.parentRuleset = this;
334     }
335 }