(refs #7375) Replaced collectFreeVariables method by a visitor
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / elaboration / expressions / ESelect.java
1 package org.simantics.scl.compiler.elaboration.expressions;
2
3 import static org.simantics.scl.compiler.elaboration.expressions.Expressions.Just;
4 import static org.simantics.scl.compiler.elaboration.expressions.Expressions.apply;
5 import static org.simantics.scl.compiler.elaboration.expressions.Expressions.let;
6 import static org.simantics.scl.compiler.elaboration.expressions.Expressions.loc;
7 import static org.simantics.scl.compiler.elaboration.expressions.Expressions.newVar;
8 import static org.simantics.scl.compiler.elaboration.expressions.Expressions.seq;
9 import static org.simantics.scl.compiler.elaboration.expressions.Expressions.tuple;
10 import static org.simantics.scl.compiler.elaboration.expressions.Expressions.var;
11
12 import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
13 import org.simantics.scl.compiler.common.names.Names;
14 import org.simantics.scl.compiler.elaboration.contexts.SimplificationContext;
15 import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;
16 import org.simantics.scl.compiler.elaboration.contexts.TypingContext;
17 import org.simantics.scl.compiler.elaboration.query.QExists;
18 import org.simantics.scl.compiler.elaboration.query.Query;
19 import org.simantics.scl.compiler.elaboration.query.compilation.QueryCompilationContext;
20 import org.simantics.scl.compiler.elaboration.query.compilation.QueryCompilationMode;
21 import org.simantics.scl.compiler.elaboration.query.compilation.UnsolvableQueryException;
22 import org.simantics.scl.compiler.errors.Locations;
23 import org.simantics.scl.compiler.internal.parsing.parser.SCLTerminals;
24 import org.simantics.scl.compiler.types.Type;
25 import org.simantics.scl.compiler.types.Types;
26 import org.simantics.scl.compiler.types.exceptions.MatchException;
27 import org.simantics.scl.compiler.types.kinds.Kinds;
28
29 import gnu.trove.map.hash.TObjectIntHashMap;
30 import gnu.trove.set.hash.TIntHashSet;
31
32 public class ESelect extends SimplifiableExpression {
33
34     private final Type ARRAY_LIST = Types.con("ArrayList", "T"); 
35     
36     int selectVariant;
37     public Expression expression;
38     public Query query;
39     public Variable[] variables;
40     
41     public ESelect(int selectVariant, Expression expression, Query query) {
42         this.selectVariant = selectVariant;
43         this.expression = expression;
44         this.query = query;
45     }
46
47     @Override
48     public void collectVars(TObjectIntHashMap<Variable> allVars,
49             TIntHashSet vars) {
50         expression.collectVars(allVars, vars);
51         query.collectVars(allVars, vars);
52     }
53
54     @Override
55     protected void updateType() throws MatchException {
56         setType(selectVariant==SCLTerminals.SELECT_FIRST 
57                 ? Types.apply(Types.MAYBE, expression.getType()) 
58                 : Types.list(expression.getType()));
59     }
60     
61     @Override
62     public Expression checkBasicType(TypingContext context, Type requiredType) {
63         Type componentType;
64         switch(selectVariant) {
65         case SCLTerminals.SELECT:
66         case SCLTerminals.SELECT_DISTINCT:
67             try {
68                 componentType = Types.unifyApply(Types.LIST, requiredType);
69             } catch (MatchException e) {
70                 context.getErrorLog().log(location, "Select expression produces a list of values.");
71                 return new EError(location);
72             }
73             break;
74         case SCLTerminals.SELECT_FIRST:
75             try {
76                 componentType = Types.unifyApply(Types.MAYBE, requiredType);
77             } catch (MatchException e) {
78                 context.getErrorLog().log(location, "Select first expression produces an optional value.");
79                 return new EError(location);
80             }
81             break;
82         default: throw new InternalCompilerError();
83         }
84         for(Variable variable : variables)
85             variable.setType(Types.metaVar(Kinds.STAR));
86         expression.checkType(context, componentType);
87         query.checkType(context);
88         
89         // Compile query
90         Type elType = expression.getType();
91         Expression result;
92         if(selectVariant == SCLTerminals.SELECT_FIRST) {
93             QueryCompilationContext queryCompilationContext =
94                     new QueryCompilationContext(context, QueryCompilationMode.GET_FIRST,
95                             expression.getType(),
96                             Just(expression));
97             try {
98                 new QExists(variables, query).generate(queryCompilationContext);
99             } catch (UnsolvableQueryException e) {
100                 context.getErrorLog().log(getLocation(), "Failed to compile the query.\n" + e.getMessage());
101                 return new EError(getLocation());
102             }
103             result = queryCompilationContext.getContinuation();
104         }
105         else {
106             Variable accumulator = newVar("accum", Types.apply(ARRAY_LIST, elType));
107             result =
108                     apply(context.getCompilationContext(), Types.PROC, Names.ArrayList_freeze, elType,
109                             var(accumulator));
110             Expression innerExpression = 
111                     apply(context.getCompilationContext(), Types.PROC, Names.ArrayList_add, elType,
112                             var(accumulator), expression);
113             try {
114                 QueryCompilationContext queryCompilationContext =
115                         new QueryCompilationContext(context, QueryCompilationMode.ITERATE, null, innerExpression);
116                 new QExists(variables, query).generate(queryCompilationContext);
117                 result = seq(queryCompilationContext.getContinuation(), result);
118             } catch(UnsolvableQueryException e) {
119                 context.getErrorLog().log(getLocation(), "Failed to compile the query.\n" + e.getMessage());
120                 return new EError(getLocation());
121             }
122             result = let(accumulator,
123                     apply(context.getCompilationContext(), Types.PROC, Names.ArrayList_new, elType, tuple()),
124                     result
125                     );
126         }
127         return loc(location, result);
128     }
129
130     @Override
131     public Expression resolve(TranslationContext context) {
132         context.pushExistentialFrame();
133         expression = expression.resolve(context);
134         query = query.resolve(context);
135         variables = context.popExistentialFrame();
136         return this;
137     }
138     
139     @Override
140     public void setLocationDeep(long loc) {
141         if(location == Locations.NO_LOCATION) {
142             location = loc;
143             expression.setLocationDeep(loc);
144             query.setLocationDeep(loc);
145         }
146     }
147     
148     @Override
149     public void accept(ExpressionVisitor visitor) {
150         visitor.visit(this);
151     }
152     
153     @Override
154     public Expression simplify(SimplificationContext context) {
155         throw new UnsupportedOperationException();
156     }
157     
158     @Override
159     public Expression accept(ExpressionTransformer transformer) {
160         return transformer.transform(this);
161     }
162
163 }