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