package org.simantics.scl.compiler.internal.elaboration.constraints; import java.util.ArrayList; import org.simantics.scl.compiler.constants.Constant; import org.simantics.scl.compiler.elaboration.expressions.EApply; import org.simantics.scl.compiler.elaboration.expressions.EApplyType; import org.simantics.scl.compiler.elaboration.expressions.ELiteral; import org.simantics.scl.compiler.elaboration.expressions.EVariable; import org.simantics.scl.compiler.elaboration.expressions.Expression; import org.simantics.scl.compiler.elaboration.expressions.Variable; import org.simantics.scl.compiler.internal.codegen.utils.TransientClassBuilder; import org.simantics.scl.compiler.top.SCLCompilerConfiguration; import org.simantics.scl.compiler.types.TPred; import org.simantics.scl.compiler.types.Type; import org.simantics.scl.compiler.types.Types; import org.simantics.scl.compiler.types.util.Typed; import org.simantics.scl.runtime.tuple.Tuple0; public class Constraint implements Typed { public static final Constraint[] EMPTY_ARRAY = new Constraint[0]; public static final int STATE_UNSOLVED = 0; public static final int STATE_HAS_INSTANCE = 1; public static final int STATE_HAS_SUBCLASS = 2; public static final int STATE_GIVEN = 3; public static final Expression GIVEN_GENERATOR = new ELiteral(new Constant(Types.UNIT) { @Override public Object realizeValue(TransientClassBuilder classBuilder) { return Tuple0.INSTANCE; } }); public TPred constraint; int state = STATE_UNSOLVED; public final Variable evidence; Expression generator; Type[] generatorParameters; Constraint[] dependsOn; boolean handled; long demandLocation; public Constraint(TPred constraint, Variable evidence, long demandLocation) { if(SCLCompilerConfiguration.DEBUG) { if(constraint == null || evidence == null) throw new NullPointerException(); } this.constraint = constraint; this.evidence = evidence; this.demandLocation = demandLocation; } public void setGenerator(int newState, Expression generator, Type[] generatorParameters, Constraint ... dependsOn) { if(SCLCompilerConfiguration.DEBUG) { if(generator == null) throw new NullPointerException(); for(Type generatorParameter : generatorParameters) if(generatorParameter == null) throw new NullPointerException(); } this.state = newState; this.generator = generator; this.dependsOn = dependsOn; this.generatorParameters = generatorParameters; } public Expression generate(long loc) { Expression result = generator; for(Type p : generatorParameters) result = new EApplyType(loc, result, p); for(Constraint dep : dependsOn) { result = new EApply(loc, result, new EVariable(loc, dep.evidence)); } return result; } public void collect(ConstraintEnvironment environment, ArrayList unsolvedConstraints, ArrayList solvedConstraints) { if(!handled) { switch(state) { case STATE_UNSOLVED: unsolvedConstraints.add(this); break; case STATE_HAS_SUBCLASS: // Try to find constant evidence and prefer that // to subclass { Reduction reduction = environment.reduce(demandLocation, constraint); if(reduction != null && reduction.demands.length == 0) { generator = reduction.generator; generatorParameters = reduction.parameters; dependsOn = Constraint.EMPTY_ARRAY; state = STATE_HAS_INSTANCE; } } // Intentionally no break case STATE_HAS_INSTANCE: for(Constraint dep : dependsOn) dep.collect(environment, unsolvedConstraints, solvedConstraints); solvedConstraints.add(this); break; } handled = true; } } @Override public Type getType() { return constraint; } public long getDemandLocation() { return demandLocation; } }