package org.simantics.scl.compiler.elaboration.expressions; import static org.simantics.scl.compiler.elaboration.expressions.Expressions.Just; import static org.simantics.scl.compiler.elaboration.expressions.Expressions.apply; import static org.simantics.scl.compiler.elaboration.expressions.Expressions.let; import static org.simantics.scl.compiler.elaboration.expressions.Expressions.loc; import static org.simantics.scl.compiler.elaboration.expressions.Expressions.newVar; import static org.simantics.scl.compiler.elaboration.expressions.Expressions.seq; import static org.simantics.scl.compiler.elaboration.expressions.Expressions.tuple; import static org.simantics.scl.compiler.elaboration.expressions.Expressions.var; import org.simantics.scl.compiler.common.exceptions.InternalCompilerError; import org.simantics.scl.compiler.common.names.Names; import org.simantics.scl.compiler.elaboration.contexts.SimplificationContext; import org.simantics.scl.compiler.elaboration.contexts.TranslationContext; import org.simantics.scl.compiler.elaboration.contexts.TypingContext; import org.simantics.scl.compiler.elaboration.query.QExists; import org.simantics.scl.compiler.elaboration.query.Query; import org.simantics.scl.compiler.elaboration.query.compilation.QueryCompilationContext; import org.simantics.scl.compiler.elaboration.query.compilation.QueryCompilationMode; import org.simantics.scl.compiler.elaboration.query.compilation.UnsolvableQueryException; import org.simantics.scl.compiler.errors.Locations; import org.simantics.scl.compiler.internal.parsing.parser.SCLTerminals; import org.simantics.scl.compiler.types.Type; import org.simantics.scl.compiler.types.Types; import org.simantics.scl.compiler.types.exceptions.MatchException; import org.simantics.scl.compiler.types.kinds.Kinds; public class ESelect extends SimplifiableExpression { private final Type ARRAY_LIST = Types.con("ArrayList", "T"); int selectVariant; public Expression expression; public Query query; public Variable[] variables; public ESelect(int selectVariant, Expression expression, Query query) { this.selectVariant = selectVariant; this.expression = expression; this.query = query; } @Override protected void updateType() throws MatchException { setType(selectVariant==SCLTerminals.SELECT_FIRST ? Types.apply(Types.MAYBE, expression.getType()) : Types.list(expression.getType())); } @Override public Expression checkBasicType(TypingContext context, Type requiredType) { Type componentType; switch(selectVariant) { case SCLTerminals.SELECT: case SCLTerminals.SELECT_DISTINCT: try { componentType = Types.unifyApply(Types.LIST, requiredType); } catch (MatchException e) { context.getErrorLog().log(location, "Select expression produces a list of values."); return new EError(location); } break; case SCLTerminals.SELECT_FIRST: try { componentType = Types.unifyApply(Types.MAYBE, requiredType); } catch (MatchException e) { context.getErrorLog().log(location, "Select first expression produces an optional value."); return new EError(location); } break; default: throw new InternalCompilerError(); } for(Variable variable : variables) variable.setType(Types.metaVar(Kinds.STAR)); expression.checkType(context, componentType); query.checkType(context); // Compile query Type elType = expression.getType(); Expression result; if(selectVariant == SCLTerminals.SELECT_FIRST) { QueryCompilationContext queryCompilationContext = new QueryCompilationContext(context, QueryCompilationMode.GET_FIRST, expression.getType(), Just(expression)); try { new QExists(variables, query).generate(queryCompilationContext); } catch (UnsolvableQueryException e) { context.getErrorLog().log(getLocation(), "Failed to compile the query.\n" + e.getMessage()); return new EError(getLocation()); } result = queryCompilationContext.getContinuation(); } else { Variable accumulator = newVar("accum", Types.apply(ARRAY_LIST, elType)); result = apply(context.getCompilationContext(), Types.PROC, Names.ArrayList_freeze, elType, var(accumulator)); Expression innerExpression = apply(context.getCompilationContext(), Types.PROC, Names.ArrayList_add, elType, var(accumulator), expression); try { QueryCompilationContext queryCompilationContext = new QueryCompilationContext(context, QueryCompilationMode.ITERATE, null, innerExpression); new QExists(variables, query).generate(queryCompilationContext); result = seq(queryCompilationContext.getContinuation(), result); } catch(UnsolvableQueryException e) { context.getErrorLog().log(getLocation(), "Failed to compile the query.\n" + e.getMessage()); return new EError(getLocation()); } result = let(accumulator, apply(context.getCompilationContext(), Types.PROC, Names.ArrayList_new, elType, tuple()), result ); } return loc(location, result); } @Override public Expression resolve(TranslationContext context) { context.pushExistentialFrame(); expression = expression.resolve(context); query = query.resolve(context); variables = context.popExistentialFrame(); return this; } @Override public void setLocationDeep(long loc) { if(location == Locations.NO_LOCATION) { location = loc; expression.setLocationDeep(loc); query.setLocationDeep(loc); } } @Override public void accept(ExpressionVisitor visitor) { visitor.visit(this); } @Override public Expression simplify(SimplificationContext context) { throw new UnsupportedOperationException(); } @Override public Expression accept(ExpressionTransformer transformer) { return transformer.transform(this); } }