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