Merge commit 'd186091'
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / elaboration / expressions / Expression.java
1 package org.simantics.scl.compiler.elaboration.expressions;\r
2 \r
3 import java.util.ArrayList;\r
4 \r
5 import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;\r
6 import org.simantics.scl.compiler.common.precedence.Precedence;\r
7 import org.simantics.scl.compiler.constants.NoRepConstant;\r
8 import org.simantics.scl.compiler.elaboration.contexts.EnvironmentalContext;\r
9 import org.simantics.scl.compiler.elaboration.contexts.ReplaceContext;\r
10 import org.simantics.scl.compiler.elaboration.contexts.SimplificationContext;\r
11 import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;\r
12 import org.simantics.scl.compiler.elaboration.contexts.TypingContext;\r
13 import org.simantics.scl.compiler.elaboration.errors.NotPatternException;\r
14 import org.simantics.scl.compiler.elaboration.expressions.lhstype.LhsType;\r
15 import org.simantics.scl.compiler.elaboration.expressions.lhstype.PatternMatchingLhs;\r
16 import org.simantics.scl.compiler.elaboration.expressions.printing.ExpressionToStringVisitor;\r
17 import org.simantics.scl.compiler.elaboration.query.QAtom;\r
18 import org.simantics.scl.compiler.elaboration.relations.SCLRelation;\r
19 import org.simantics.scl.compiler.environment.Environment;\r
20 import org.simantics.scl.compiler.internal.codegen.references.IVal;\r
21 import org.simantics.scl.compiler.internal.codegen.writer.CodeWriter;\r
22 import org.simantics.scl.compiler.internal.elaboration.decomposed.DecomposedExpression;\r
23 import org.simantics.scl.compiler.internal.elaboration.utils.ExpressionDecorator;\r
24 import org.simantics.scl.compiler.internal.interpreted.IExpression;\r
25 import org.simantics.scl.compiler.internal.parsing.Symbol;\r
26 import org.simantics.scl.compiler.top.ExpressionInterpretationContext;\r
27 import org.simantics.scl.compiler.types.TForAll;\r
28 import org.simantics.scl.compiler.types.TFun;\r
29 import org.simantics.scl.compiler.types.TPred;\r
30 import org.simantics.scl.compiler.types.TVar;\r
31 import org.simantics.scl.compiler.types.Type;\r
32 import org.simantics.scl.compiler.types.Types;\r
33 import org.simantics.scl.compiler.types.exceptions.MatchException;\r
34 import org.simantics.scl.compiler.types.kinds.Kinds;\r
35 import org.simantics.scl.compiler.types.util.Typed;\r
36 \r
37 import gnu.trove.map.hash.TObjectIntHashMap;\r
38 import gnu.trove.set.hash.THashSet;\r
39 import gnu.trove.set.hash.TIntHashSet;\r
40 \r
41 public abstract class Expression extends Symbol implements Typed {\r
42     public static final Expression[] EMPTY_ARRAY = new Expression[0];\r
43     \r
44     transient\r
45     private Type type;\r
46     \r
47     public Expression() {\r
48     }\r
49     \r
50     public Expression(long loc) {\r
51         this.location = loc;\r
52     }\r
53         \r
54     @Override\r
55     public Type getType() {\r
56         if(type == null) {\r
57             try {\r
58                 updateType();\r
59             } catch (MatchException e) {\r
60                 throw new InternalCompilerError(e);\r
61             }\r
62             if(type == null)\r
63                 throw new InternalCompilerError(getClass().getSimpleName() + \r
64                         ".updateType couldn't compute its type.");\r
65         }\r
66         return type;\r
67     }\r
68 \r
69     public void setType(Type type) {\r
70         if(type == null)\r
71             throw new NullPointerException();\r
72         this.type = type;\r
73     }\r
74         \r
75         /**\r
76          * Infers the type of the expression without any context. Adds type\r
77      * applications and lambdas if needed.       \r
78          */\r
79     public Expression inferType(TypingContext context) {\r
80         return checkBasicType(context, Types.metaVar(Kinds.STAR));\r
81     }\r
82 \r
83     public Expression checkBasicType(TypingContext context, Type requiredType) {\r
84         return context.subsume(inferType(context), requiredType);\r
85     }\r
86     \r
87     protected Expression applyPUnit(EnvironmentalContext context) {\r
88         Type type = Types.canonical(getType());\r
89         if(type instanceof TFun) {\r
90             TFun fun = (TFun)type;\r
91             if(fun.getCanonicalDomain() == Types.PUNIT) {\r
92                 EApply result = new EApply(location, this, new ELiteral(NoRepConstant.PUNIT));\r
93                 result.effect = fun.getCanonicalEffect();\r
94                 return result;\r
95             }\r
96         }\r
97         return this;\r
98     }\r
99 \r
100     public Expression checkIgnoredType(TypingContext context) {\r
101         Expression expression = inferType(context);\r
102         if(Types.canonical(expression.getType()) != Types.UNIT)\r
103             expression = new ESimpleLet(location, null, expression, new ELiteral(NoRepConstant.PUNIT));\r
104         return expression;\r
105     }\r
106         \r
107         /**\r
108          * Checks the type of the expression against the given type. Adds type\r
109          * applications and lambdas if needed.\r
110          */\r
111         public final Expression checkType(TypingContext context, Type requiredType) {\r
112             //System.out.println("checkType: " + this + " :: " + requiredType);\r
113             if(!context.isInPattern()) {\r
114                 requiredType = Types.canonical(requiredType);\r
115             if(requiredType instanceof TForAll) {\r
116                 TForAll forAll = (TForAll)requiredType;\r
117                 TVar var = forAll.var;\r
118                 TVar newVar = Types.var(var.getKind());\r
119                 requiredType = Types.canonical(forAll.type).replace(var, newVar);\r
120                 return new ELambdaType(new TVar[] {newVar}, checkType(context, requiredType));\r
121             }\r
122             while(requiredType instanceof TFun) {\r
123                 TFun fun = (TFun)requiredType;\r
124                 if(fun.domain instanceof TPred) { // No need to canonicalize\r
125                     ArrayList<Variable> constraints = new ArrayList<Variable>(2);\r
126                     while(true) {\r
127                         constraints.add(new Variable("constraint", fun.domain));\r
128                         requiredType = Types.canonical(fun.range);\r
129                         if(!(requiredType instanceof TFun))\r
130                             break;\r
131                         fun = (TFun)requiredType;\r
132                         if(!(fun.domain instanceof TPred))\r
133                             break;\r
134                     }\r
135                     context.pushConstraintFrame(constraints.toArray(new Variable[constraints.size()]));\r
136                     Expression expression = checkType(context, requiredType);\r
137                     context.popConstraintFrame();\r
138                     for(int i=constraints.size()-1;i>=0;--i)\r
139                         expression = new ESimpleLambda(constraints.get(i), expression);\r
140                     return expression;\r
141                 }\r
142                 else if(fun.domain == Types.PUNIT) {\r
143                     context.pushEffectUpperBound(location, fun.effect);\r
144                     Expression expr = checkType(context, fun.range);\r
145                     context.popEffectUpperBound();       \r
146                     \r
147                     // Wrap\r
148                     Variable var = new Variable("punit", Types.PUNIT);\r
149                     return new ESimpleLambda(location, var, fun.effect, expr);\r
150                 }\r
151                 else\r
152                     break;\r
153             }\r
154             }\r
155             return checkBasicType(context, requiredType); \r
156         }\r
157 \r
158         public abstract void collectRefs(TObjectIntHashMap<Object> allRefs, TIntHashSet refs);\r
159         public abstract void collectVars(TObjectIntHashMap<Variable> allVars, TIntHashSet vars);\r
160         public abstract void forVariables(VariableProcedure procedure);\r
161         \r
162         public Expression decomposeMatching() {\r
163         return this;\r
164     }\r
165 \r
166         public String toString() {\r
167             StringBuilder b = new StringBuilder();\r
168             ExpressionToStringVisitor visitor = new ExpressionToStringVisitor(b);\r
169             accept(visitor);\r
170             return b.toString();\r
171     }\r
172 \r
173         protected abstract void updateType() throws MatchException;\r
174         \r
175         public static class TypeValidationException extends Exception {\r
176         private static final long serialVersionUID = 3181298127162041248L;  \r
177         \r
178         long loc;\r
179 \r
180         public TypeValidationException(long loc) {\r
181             this.loc = loc;\r
182         }\r
183         \r
184         public long getLoc() {\r
185             return loc;\r
186         }\r
187 \r
188         public TypeValidationException(long loc, Throwable cause) {\r
189             super(cause);\r
190             this.loc = loc;\r
191         }\r
192     }\r
193     \r
194     public static void assertEquals(long loc, Type a, Type b) throws TypeValidationException {\r
195         if(!Types.equals(a, b))\r
196             throw new TypeValidationException(loc);\r
197     }\r
198 \r
199         public abstract IVal toVal(Environment env, CodeWriter w);\r
200                 \r
201         public Expression closure(TVar ... vars) {\r
202             if(vars.length == 0)\r
203             return this;\r
204         return new ELambdaType(vars, this);\r
205         }\r
206     \r
207     public abstract void collectFreeVariables(THashSet<Variable> vars);\r
208     \r
209     public Expression simplify(SimplificationContext context) {\r
210         throw new InternalCompilerError(location, getClass().getSimpleName() + " does not support simplify method.");\r
211     }\r
212 \r
213     public abstract Expression resolve(TranslationContext context);\r
214     \r
215     /**\r
216      * Returns head of the pattern.\r
217      */\r
218     public EVar getPatternHead() throws NotPatternException {\r
219         throw new NotPatternException(this);\r
220     }\r
221     \r
222     public LhsType getLhsType() throws NotPatternException {\r
223         throw new NotPatternException(this);\r
224     }\r
225 \r
226     protected void collectVariableNames(PatternMatchingLhs lhsType) throws NotPatternException {\r
227         throw new NotPatternException(this);\r
228     }\r
229 \r
230     public void getParameters(TranslationContext translationContext,\r
231             ArrayList<Expression> parameters) {\r
232         throw new InternalCompilerError("Class " + getClass().getSimpleName() + " does not support getParameters.");        \r
233     }\r
234 \r
235     public Expression resolveAsPattern(TranslationContext context) {\r
236         context.getErrorLog().log(location, "Pattern was expected here.");\r
237         return new EError();\r
238     }\r
239 \r
240     public void removeFreeVariables(THashSet<Variable> vars) {\r
241         throw new InternalCompilerError(getClass().getSimpleName() + " is not a pattern.");\r
242     }\r
243     \r
244     public Expression checkTypeAsPattern(TypingContext context, Type requiredType) {\r
245         if(context.isInPattern())\r
246             throw new InternalCompilerError("Already in a pattern.");\r
247         context.setInPattern(true);\r
248         Expression expression = checkType(context, requiredType);\r
249         context.setInPattern(false);\r
250         return expression;\r
251     }\r
252 \r
253     public THashSet<Variable> getFreeVariables() {\r
254         THashSet<Variable> result = new THashSet<Variable>();\r
255         collectFreeVariables(result);\r
256         return result;\r
257     }    \r
258 \r
259     public static Expression[] concat(Expression[] a, Expression[] b) {\r
260         if(a.length == 0)\r
261             return b;\r
262         if(b.length == 0)\r
263             return a;\r
264         Expression[] result = new Expression[a.length + b.length];\r
265         for(int i=0;i<a.length;++i)\r
266             result[i] = a[i];\r
267         for(int i=0;i<b.length;++i)\r
268             result[i+a.length] = b[i];\r
269         return result;\r
270     }\r
271 \r
272     public Expression replace(ReplaceContext context) {\r
273         throw new InternalCompilerError(getClass().getSimpleName() + " does not support replace.");\r
274     }\r
275     \r
276     public static Expression[] replace(ReplaceContext context, Expression[] expressions) {\r
277         Expression[] result = new Expression[expressions.length];\r
278         for(int i=0;i<expressions.length;++i)\r
279             result[i] = expressions[i].replace(context);\r
280         return result;\r
281     }\r
282     \r
283     public Expression copy() {\r
284         return replace(new ReplaceContext(null));\r
285     }\r
286     \r
287     public Expression copy(TypingContext typingContext) {\r
288         return replace(new ReplaceContext(typingContext));\r
289     }\r
290 \r
291     public abstract void setLocationDeep(long loc);\r
292 \r
293     public Expression replaceInPattern(ReplaceContext context) {\r
294         context.inPattern = true;\r
295         Expression result = replace(context);\r
296         context.inPattern = false;\r
297         return result;\r
298     }\r
299 \r
300     public int getFunctionDefinitionArity() throws NotPatternException {\r
301         throw new NotPatternException(this);\r
302     }\r
303     \r
304     public IVal lambdaToVal(Environment env, CodeWriter w) {\r
305         DecomposedExpression decomposed = DecomposedExpression.decompose(this);\r
306         CodeWriter newW = w.createFunction(decomposed.typeParameters, decomposed.effect, decomposed.returnType, decomposed.parameterTypes);\r
307         IVal[] parameters = newW.getParameters();\r
308         IVal functionVal = newW.getFunction().getTarget();\r
309         for(int i=0;i<parameters.length;++i)\r
310             decomposed.parameters[i].setVal(parameters[i]);\r
311         newW.return_(decomposed.body.toVal(env, newW));\r
312         return functionVal;\r
313     }\r
314     \r
315     public IExpression toIExpression(ExpressionInterpretationContext context) {\r
316         throw new UnsupportedOperationException();\r
317     }\r
318     \r
319     public static IExpression[] toIExpressions(ExpressionInterpretationContext target, Expression[] expressions) {\r
320         IExpression[] result = new IExpression[expressions.length];\r
321         for(int i=0;i<expressions.length;++i)\r
322             result[i] = expressions[i].toIExpression(target);\r
323         return result;\r
324     }\r
325     \r
326     public Expression applyType(Type type) {\r
327         return new EApplyType(location, this, type);\r
328     }\r
329     \r
330     public abstract Expression decorate(ExpressionDecorator decorator);\r
331 \r
332         public boolean isEffectful() {\r
333                 return true;\r
334         }\r
335 \r
336     public boolean isFunctionPattern() {\r
337         return false;\r
338     }\r
339 \r
340     public boolean isConstructorApplication() {\r
341         return false;\r
342     }\r
343     \r
344     public abstract void collectEffects(THashSet<Type> effects);\r
345     \r
346     public Type getEffect() {\r
347         THashSet<Type> effects = new THashSet<Type>();\r
348         collectEffects(effects);\r
349         return Types.union(effects.toArray(new Type[effects.size()]));\r
350     }\r
351     \r
352     public abstract void accept(ExpressionVisitor visitor);\r
353     \r
354     public void collectRelationRefs(\r
355             final TObjectIntHashMap<SCLRelation> allRefs, final TIntHashSet refs) {\r
356         accept(new StandardExpressionVisitor() {\r
357             @Override\r
358             public void visit(QAtom query) {\r
359                 int id = allRefs.get(query.relation);\r
360                 if(id >= 0)\r
361                     refs.add(id);\r
362             }\r
363         });\r
364     }\r
365 \r
366     public boolean isFunctionDefinitionLhs() {\r
367         return false;\r
368     }\r
369 \r
370     public Precedence getPrecedence() {\r
371         return Precedence.DEFAULT;\r
372     }\r
373 \r
374     public boolean isPattern(int arity) {\r
375         return false;\r
376     }\r
377     \r
378     public abstract Expression accept(ExpressionTransformer transformer);\r
379 }\r