]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/expressions/EApply.java
2b488068d547108d0341950bd71968463cd6ce75
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / elaboration / expressions / EApply.java
1 package org.simantics.scl.compiler.elaboration.expressions;
2
3 import java.util.ArrayList;
4
5 import org.simantics.scl.compiler.common.names.Name;
6 import org.simantics.scl.compiler.common.names.Names;
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.java.ListConstructor;
17 import org.simantics.scl.compiler.elaboration.macros.MacroRule;
18 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
19 import org.simantics.scl.compiler.errors.Locations;
20 import org.simantics.scl.compiler.internal.codegen.references.IVal;
21 import org.simantics.scl.compiler.internal.codegen.writer.CodeWriter;
22 import org.simantics.scl.compiler.internal.interpreted.IApply;
23 import org.simantics.scl.compiler.internal.interpreted.IExpression;
24 import org.simantics.scl.compiler.internal.interpreted.IListLiteral;
25 import org.simantics.scl.compiler.top.ExpressionInterpretationContext;
26 import org.simantics.scl.compiler.types.Skeletons;
27 import org.simantics.scl.compiler.types.TFun;
28 import org.simantics.scl.compiler.types.Type;
29 import org.simantics.scl.compiler.types.Types;
30 import org.simantics.scl.compiler.types.exceptions.MatchException;
31 import org.simantics.scl.compiler.types.exceptions.UnificationException;
32 import org.simantics.scl.compiler.types.kinds.Kinds;
33 import org.simantics.scl.compiler.types.util.MultiFunction;
34
35 import gnu.trove.map.hash.TObjectIntHashMap;
36 import gnu.trove.set.hash.THashSet;
37 import gnu.trove.set.hash.TIntHashSet;
38
39 public class EApply extends Expression {
40     public Expression function;
41     public Expression[] parameters;
42     Type effect = Types.NO_EFFECTS;
43     
44     public EApply(Expression function, Expression ... parameters) {
45         this.function = function;
46         this.parameters = parameters;
47     }
48     
49     public EApply(Expression function, Expression parameter) {
50         this(function, new Expression[] {parameter});
51     }
52
53     public EApply(long loc, Expression function, Expression ... parameters) {
54         super(loc);
55         this.function = function;
56         this.parameters = parameters;
57     }
58     
59     public EApply(long loc, Type effect, Expression function, Expression ... parameters) {
60         super(loc);
61         this.effect = effect;
62         this.function = function;
63         this.parameters = parameters;
64     }
65     
66     public void set(Expression function, Expression[] parameters) {
67         this.function = function;
68         this.parameters = parameters;
69     }
70
71     public Expression getFunction() {
72         return function;
73     }
74     
75     public Expression[] getParameters() {
76         return parameters;
77     }
78     
79
80     public void collectRefs(TObjectIntHashMap<Object> allRefs, TIntHashSet refs) {
81         function.collectRefs(allRefs, refs);
82         for(Expression parameter : parameters)
83             parameter.collectRefs(allRefs, refs);
84     }
85     
86     public void collectVars(TObjectIntHashMap<Variable> allVars, TIntHashSet vars) {
87         function.collectVars(allVars, vars);
88         for(Expression parameter : parameters)
89             parameter.collectVars(allVars, vars);
90     }
91         
92         @Override
93         protected void updateType() throws MatchException {
94         MultiFunction mfun = Types.matchFunction(function.getType(), parameters.length);
95         /*for(int i=0;i<parameters.length;++i)
96             if(!Types.equals(parameters[i].getType(), mfun.parameterTypes[i]))
97                 throw new MatchException();*/
98         effect = mfun.effect;
99         setType(mfun.returnType);
100         }
101
102         @Override
103         public IVal toVal(CompilationContext context, CodeWriter w) {
104         IVal functionVal = function.toVal(context, w);
105         IVal[] parameterVals = new IVal[parameters.length];
106         for(int i=0;i<parameters.length;++i)
107             parameterVals[i] = parameters[i].toVal(context, w);
108         Type type = getType();
109         effect = Types.simplifyFinalEffect(effect);
110         return w.applyWithEffect(location, effect, type, functionVal, parameterVals);
111     }
112
113     @Override
114     public void collectFreeVariables(THashSet<Variable> vars) {
115         function.collectFreeVariables(vars);
116         for(Expression parameter : parameters)
117             parameter.collectFreeVariables(vars);
118     }
119     
120     private void combineApplications() {
121         if(function instanceof EApply) {
122             EApply apply = (EApply)function;
123             if(Types.canonical(apply.effect) == Types.NO_EFFECTS) {
124                 function = apply.function;
125                 parameters = Expression.concat(apply.parameters, parameters); 
126             } 
127         }
128     }
129
130     @Override
131     public Expression simplify(SimplificationContext context) {
132         function = function.simplify(context);
133         for(int i=0;i<parameters.length;++i)
134             parameters[i] = parameters[i].simplify(context);
135         combineApplications();
136         
137         // Try to apply macro rule
138         if(function instanceof EConstant) {
139             EConstant constant = (EConstant)function;
140             MacroRule rule = constant.value.getMacroRule();
141             if(rule != null) {
142                 Expression simplified = rule.apply(context, constant.typeParameters, this);
143                 if(simplified != null)
144                     // There may be more to simplify after macro application
145                     // However this may cause performance problems (O(n^2) algorithm in pathologic cases)
146                     return simplified.simplify(context);
147             }
148         }
149         
150         return this;
151     }
152     
153     @Override
154     public EVar getPatternHead() throws NotPatternException {
155         return function.getPatternHead();
156     }
157
158     @Override
159     public LhsType getLhsType() throws NotPatternException {
160         LhsType lhsType = function.getLhsType();
161         if(lhsType instanceof PatternMatchingLhs)
162             for(Expression parameter : parameters)
163                 parameter.collectVariableNames((PatternMatchingLhs)lhsType);
164         return lhsType;
165     }
166     
167     @Override
168     public Expression resolve(TranslationContext context) {
169         function = function.resolve(context);
170         for(int i=0;i<parameters.length;++i)
171             parameters[i] = parameters[i].resolve(context);
172         //combineApplications();
173         return this;
174     }
175     
176     @Override
177     public Expression resolveAsPattern(TranslationContext context) {
178         function = function.resolveAsPattern(context);
179         for(int i=0;i<parameters.length;++i)
180             parameters[i] = parameters[i].resolveAsPattern(context);
181         combineApplications();
182         if(!(function instanceof EConstant || function instanceof EError)) {
183             context.getErrorLog().log(location, "Only constants can be applied in patterns.");
184             return new EError();
185         }
186         return this;
187     }
188     
189     @Override
190     public void getParameters(TranslationContext context,
191             ArrayList<Expression> parameters) {
192         function.getParameters(context, parameters);
193         for(Expression parameter : this.parameters)
194             parameters.add(parameter);
195     }
196     
197     @Override
198     public void removeFreeVariables(THashSet<Variable> vars) {
199         function.removeFreeVariables(vars);
200         for(Expression parameter : parameters)
201             parameter.removeFreeVariables(vars);
202     }
203
204     @Override
205     public Expression replace(ReplaceContext context) {
206         return new EApply(
207                 getLocation(),                
208                 effect.replace(context.tvarMap),
209                 function.replace(context),
210                 replace(context, parameters));
211     }
212     
213     @Override
214     public void setLocationDeep(long loc) {
215         if(location == Locations.NO_LOCATION) {
216             location = loc;
217             function.setLocationDeep(loc);
218             for(Expression parameter : parameters)
219                 parameter.setLocationDeep(loc);
220         }
221     }
222     
223     @Override
224     public int getFunctionDefinitionPatternArity() throws NotPatternException {
225         return function.getFunctionDefinitionPatternArity() + parameters.length;
226     }
227     
228     @Override
229     public IExpression toIExpression(ExpressionInterpretationContext target) {
230         IExpression[] parametersI = toIExpressions(target, parameters);
231         
232         Expression function = this.function;
233         while(function instanceof EApplyType)
234             function = ((EApplyType)function).expression;
235         
236         // Special cases
237         if(function instanceof EConstant) {
238             SCLValue functionValue = ((EConstant)function).value;
239             Name name = functionValue.getName();
240             if(name.module.equals("Builtin")) {
241                 IVal val = functionValue.getValue();
242                 if(val instanceof ListConstructor) {
243                     if(((ListConstructor)val).arity == parametersI.length)
244                         return new IListLiteral(parametersI);
245                 }
246             }
247         }
248         //System.out.println("--> " + function + " " + function.getClass().getSimpleName());
249         
250         // The basic case
251         return new IApply(function.toIExpression(target), parametersI);
252     }
253     
254     private void inferType(TypingContext context, boolean ignoreResult) {
255         function = function.inferType(context);
256         function = context.instantiate(function);
257         MultiFunction mfun;
258         try {
259             mfun = Types.unifyFunction(function.getType(), parameters.length);
260         } catch (UnificationException e) {
261             int arity = Types.getArity(function.getType());
262             if(arity == 0)
263                 context.getErrorLog().log(location, "Application of non-function.");
264             else
265                 context.getErrorLog().log(location, "Function of arity " + arity + 
266                         " is applied with " + parameters.length + " parameters.");
267             setType(Types.metaVar(Kinds.STAR));
268             for(int i=0;i<parameters.length;++i)
269                 parameters[i] = parameters[i].inferType(context);
270             return;
271         }
272         if((ignoreResult && Skeletons.canonicalSkeleton(mfun.returnType) instanceof TFun &&
273                 Types.canonical(mfun.effect) == Types.NO_EFFECTS) ||
274                 (context.isInPattern() && Skeletons.canonicalSkeleton(mfun.returnType) instanceof TFun)) {
275             context.getErrorLog().log(location, "The function is applied with too few parameters.");
276         }
277         
278         // Check parameter types
279         for(int i=0;i<parameters.length;++i)
280             parameters[i] = parameters[i].checkType(context, mfun.parameterTypes[i]);
281
282         effect = mfun.effect;
283         
284         context.declareEffect(location, mfun.effect);
285         setType(mfun.returnType);
286     }
287     
288     @Override
289     public Expression inferType(TypingContext context) {
290         if(parameters.length == 2 && function instanceof EConstant && ((EConstant)function).value.getName() == Names.Prelude_dollar)
291             return new EApply(location, parameters[0], parameters[1]).inferType(context);
292         inferType(context, false);
293         return this;
294     }
295     
296     @Override
297     public Expression checkIgnoredType(TypingContext context) {
298         if(parameters.length == 2 && function instanceof EConstant && ((EConstant)function).value.getName() == Names.Prelude_dollar)
299             return new EApply(location, parameters[0], parameters[1]).inferType(context);
300         inferType(context, true);
301         if(Types.canonical(getType()) != Types.UNIT)
302             return new ESimpleLet(location, null, this, new ELiteral(NoRepConstant.PUNIT));
303         return this;
304     }
305     
306     public Type getLocalEffect() {
307         return effect;
308     }
309     
310     public Expression toANormalForm(Expression root) {
311         Expression expression = root;
312         for(int i=parameters.length-1;i>=0;--i) {
313                 Expression parameter = parameters[i];
314                 if(parameter.isEffectful()) {
315                         Variable var = new Variable("aNormalTemp" + i, parameter.getType());
316                         expression = new ESimpleLet(var, parameter, expression);
317                         parameters[i] = new EVariable(var);
318                 }
319         }
320         if(function.isEffectful()) {
321                 Variable var = new Variable("aNormalTempF", function.getType());
322                 expression = new ESimpleLet(var, function, expression);
323                 function = new EVariable(var);
324         }
325         return expression;
326     }
327     
328     @Override
329     public boolean isEffectful() {
330         if(effect != Types.NO_EFFECTS)
331                 return true;            
332         for(Expression parameter : parameters)
333                 if(parameter.isEffectful())
334                         return true;
335         if(function.isEffectful())
336                 return true;
337         return false;
338     }
339     
340     @Override
341     public boolean isFunctionPattern() {
342         return !isConstructorApplication();
343     }
344     
345     @Override
346     public boolean isConstructorApplication() {
347         return function.isConstructorApplication();
348     }
349
350     @Override
351     public void collectEffects(THashSet<Type> effects) {
352         effects.add(effect);
353         function.collectEffects(effects);
354         for(Expression parameter : parameters)
355             parameter.collectEffects(effects);
356     }
357
358     @Override
359     public void accept(ExpressionVisitor visitor) {
360         visitor.visit(this);
361     }
362     
363     @Override
364     public boolean isFunctionDefinitionLhs() {
365         try {
366             EVar patternHead = function.getPatternHead();
367             return !Character.isUpperCase(patternHead.name.charAt(0));
368         } catch(NotPatternException e) {
369             return false;
370         }
371     }
372
373     @Override
374     public void forVariables(VariableProcedure procedure) {
375         function.forVariables(procedure);
376         for(Expression parameter : parameters)
377             parameter.forVariables(procedure);
378     }
379     
380     @Override
381     public boolean isPattern(int arity) {
382         if(!function.isPattern(arity+parameters.length))
383             return false;
384         for(Expression parameter : parameters)
385             if(!parameter.isPattern(0))
386                 return false;
387         return true;
388     }
389
390     @Override
391     public Expression accept(ExpressionTransformer transformer) {
392         return transformer.transform(this);
393     }
394     
395     @Override
396     public boolean equalsExpression(Expression expression) {
397         if(expression.getClass() != getClass())
398             return false;
399         EApply other = (EApply)expression;
400         if(parameters.length != other.parameters.length)
401             return false;
402         if(!function.equalsExpression(other.function))
403             return false;
404         for(int i=0;i<parameters.length;++i)
405             if(!parameters[i].equalsExpression(other.parameters[i]))
406                 return false;
407         return true;
408     }
409 }