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