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