]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/chr/CHRRule.java
Merge "Warn for existential variables in head pattern referred only once"
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / elaboration / chr / CHRRule.java
1 package org.simantics.scl.compiler.elaboration.chr;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5
6 import org.simantics.scl.compiler.compilation.CompilationContext;
7 import org.simantics.scl.compiler.elaboration.chr.plan.CHRSearchPlan;
8 import org.simantics.scl.compiler.elaboration.chr.planning.QueryPlanningContext;
9 import org.simantics.scl.compiler.elaboration.chr.relations.CHRConstraint;
10 import org.simantics.scl.compiler.elaboration.contexts.SimplificationContext;
11 import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;
12 import org.simantics.scl.compiler.elaboration.contexts.TypingContext;
13 import org.simantics.scl.compiler.elaboration.expressions.EAsPattern;
14 import org.simantics.scl.compiler.elaboration.expressions.EVariable;
15 import org.simantics.scl.compiler.elaboration.expressions.Expression;
16 import org.simantics.scl.compiler.elaboration.expressions.ExpressionVisitor;
17 import org.simantics.scl.compiler.elaboration.expressions.Variable;
18 import org.simantics.scl.compiler.elaboration.expressions.printing.ExpressionToStringVisitor;
19 import org.simantics.scl.compiler.elaboration.expressions.visitors.StandardExpressionVisitor;
20 import org.simantics.scl.compiler.errors.Locations;
21 import org.simantics.scl.compiler.internal.parsing.Symbol;
22 import org.simantics.scl.compiler.types.Types;
23 import org.simantics.scl.compiler.types.kinds.Kinds;
24
25 import gnu.trove.map.hash.TObjectIntHashMap;
26 import gnu.trove.set.hash.TIntHashSet;
27
28 public class CHRRule extends Symbol {
29     public CHRRuleset parentRuleset;
30     public int priority;
31     public CHRQuery head;
32     public CHRQuery body;
33     public Variable[] existentialVariables;
34     
35     // Analysis
36     //public int firstPriorityExecuted;
37     public int lastPriorityExecuted;
38     
39     // Plans
40     public ArrayList<CHRSearchPlan> plans = new ArrayList<CHRSearchPlan>();
41     
42     // Code generation, move to CHRPriority
43     public String containerClassName;
44     
45     public CHRRule(long location, CHRQuery head, CHRQuery body, Variable[] existentialVariables) {
46         this.location = location;
47         this.head = head;
48         this.body = body;
49         this.existentialVariables = existentialVariables;
50     }
51     
52     public CHRRule(long location, CHRQuery head, CHRQuery body) {
53         this(location, head, body, null);
54     }
55
56     public void resolve(TranslationContext context) {
57         context.pushExistentialFrame();
58         head.resolve(context);
59         context.disallowNewExistentials();
60         body.resolve(context);
61         existentialVariables = context.popExistentialFrame();
62         
63         warnForExistentialsUsedOnlyOnce(context);
64     }
65
66     private static final Object NEVER_USED = new Object();
67     
68     private void warnForExistentialsUsedOnlyOnce(TranslationContext context) {
69         // Initialize the hash map
70         HashMap<Variable, Object> usageCount = new HashMap<>(existentialVariables.length);
71         for(Variable var : existentialVariables)
72             if(!var.getName().equals("_"))
73                 usageCount.put(var, NEVER_USED);
74     
75         // Collect variable uses
76         ExpressionVisitor visitor = new StandardExpressionVisitor() {
77             private void handle(Expression expression, Variable variable) {
78                 Object object = usageCount.remove(variable);
79                 if(object == NEVER_USED)
80                     usageCount.put(variable, expression);
81             }
82             @Override
83             public void visit(EVariable expression) {
84                 if(expression.variable != null)
85                     handle(expression, expression.variable);
86             }
87             @Override
88             public void visit(EAsPattern expression) {
89                 expression.pattern.accept(this);
90                 handle(expression, expression.var);
91             }
92         };
93         head.accept(visitor);
94         body.accept(visitor);
95         
96         // Report as warnings
97         usageCount.forEach((variable, expression_) -> {
98             if(!(expression_ instanceof Expression))
99                 return; // Should never happen
100             Expression expression = (Expression)expression_;
101             if(context.isExpandedFromWildcard(expression))
102                 return;
103             
104             context.getErrorLog().logWarning(expression.location,
105                     "Existential variable " + variable.getName() + " is referred only once. Replace by _ if this is a wildcard.");
106         });
107         
108     }
109
110     public void checkType(TypingContext context) {
111         for(Variable variable : existentialVariables)
112             variable.setType(Types.metaVar(Kinds.STAR));
113         head.checkType(context);
114         body.checkType(context);
115     }
116     
117     public void collectVars(TObjectIntHashMap<Variable> allVars, TIntHashSet vars) {
118         head.collectVars(allVars, vars);
119         body.collectVars(allVars, vars);
120     }
121
122     public void setLocationDeep(long loc) {
123         if(location == Locations.NO_LOCATION) {
124             this.location = loc;
125             head.setLocationDeep(loc);
126             body.setLocationDeep(loc);
127         }
128     }
129     
130     public void simplify(SimplificationContext context) {
131         head.simplify(context);
132         body.simplify(context);
133     }
134
135     public void compile(CompilationContext compilationContext, CHRConstraint initConstraint) {
136         boolean hasLocalActiveLiteral = false;
137         for(int i=0;i<head.literals.length;++i) {
138             CHRLiteral literal = head.literals[i];
139             if(literal.passive)
140                 continue;
141             CHRConstraint constraint = (CHRConstraint)literal.relation;
142             
143             Variable activeFact = new Variable("activeFact", constraint.factType);
144             QueryPlanningContext context = new QueryPlanningContext(compilationContext, existentialVariables);
145             if(!head.createQueryPlan(context, new EVariable(activeFact), i, initConstraint))
146                 return;
147             body.createEnforcePlan(context, priority);
148             addPlan(new CHRSearchPlan(constraint, activeFact, context.getPlanOps()));
149             
150             if(constraint.parentRuleset == parentRuleset)
151                 hasLocalActiveLiteral = true;
152         }
153         if(!hasLocalActiveLiteral) {
154             Variable activeFact = new Variable("activeFact", initConstraint.factType);
155             QueryPlanningContext context = new QueryPlanningContext(compilationContext, existentialVariables);
156             if(!head.createQueryPlan(context, new EVariable(activeFact), -1, initConstraint))
157                 return;
158             body.createEnforcePlan(context, priority);
159             /*System.out.println(this);
160             for(PlanOp planOp : context.getPlanOps())
161                 System.out.println("    " + planOp);*/
162             addPlan(new CHRSearchPlan(initConstraint, activeFact, context.getPlanOps()));
163         }
164     }
165     
166     private void addPlan(CHRSearchPlan plan) {
167         plans.add(plan);
168     }
169
170     public String toString() {
171         StringBuilder b = new StringBuilder();
172         ExpressionToStringVisitor visitor = new ExpressionToStringVisitor(b);
173         visitor.visit(this);
174         return b.toString();
175     }
176     
177 }