package scotch.compiler.syntax.value; import static lombok.AccessLevel.PACKAGE; import static scotch.compiler.intermediate.Intermediates.assign; import static scotch.compiler.syntax.TypeError.typeError; import static scotch.symbol.Symbol.symbol; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; import scotch.compiler.analyzer.DependencyAccumulator; import scotch.compiler.analyzer.NameAccumulator; import scotch.compiler.analyzer.OperatorAccumulator; import scotch.compiler.analyzer.PrecedenceParser; import scotch.compiler.analyzer.NameQualifier; import scotch.compiler.analyzer.TypeChecker; import scotch.compiler.intermediate.IntermediateGenerator; import scotch.compiler.intermediate.IntermediateValue; import scotch.compiler.syntax.pattern.PatternReducer; import scotch.compiler.text.SourceLocation; import scotch.compiler.syntax.type.Type; @AllArgsConstructor(access = PACKAGE) @EqualsAndHashCode(callSuper = false) @ToString(exclude = "sourceLocation") public class Let extends Value { @Getter private final SourceLocation sourceLocation; private final String name; private final Value value; private final Value scope; @Getter private final Type type; @Override public Value accumulateDependencies(DependencyAccumulator state) { return new Let(sourceLocation, name, value.accumulateDependencies(state), scope.accumulateDependencies(state), type); } @Override public Value accumulateNames(NameAccumulator state) { throw new UnsupportedOperationException(); // TODO } @Override public Value bindMethods(TypeChecker typeChecker) { return new Let(sourceLocation, name, value.bindMethods(typeChecker), scope.bindMethods(typeChecker), type); } @Override public Value bindTypes(TypeChecker typeChecker) { return new Let(sourceLocation, name, value.bindTypes(typeChecker), scope.bindTypes(typeChecker), typeChecker.generate(type)); } @Override public Value checkTypes(TypeChecker typeChecker) { typeChecker.addLocal(symbol(name)); Value checkedValue = value.checkTypes(typeChecker); typeChecker.scope().redefineValue(symbol(name), checkedValue.getType()); typeChecker.specialize(checkedValue.getType()); Value checkedScope = scope.checkTypes(typeChecker); return new Let(sourceLocation, name, checkedValue, checkedScope, checkedScope.getType().unify(type, typeChecker).orElseGet(unification -> { typeChecker.error(typeError(unification, scope.getSourceLocation())); return type; })); } @Override public Value defineOperators(OperatorAccumulator state) { throw new UnsupportedOperationException(); // TODO } @Override public IntermediateValue generateIntermediateCode(IntermediateGenerator state) { IntermediateValue checkedScope = scope.generateIntermediateCode(state); state.addArgument(name); IntermediateValue checkedValue = value.generateIntermediateCode(state); return assign(name, checkedValue, checkedScope); } @Override public Value parsePrecedence(PrecedenceParser state) { throw new UnsupportedOperationException(); // TODO } @Override public Value qualifyNames(NameQualifier state) { throw new UnsupportedOperationException(); // TODO } @Override public Value reducePatterns(PatternReducer reducer) { throw new UnsupportedOperationException(); // TODO } @Override public Value withType(Type type) { throw new UnsupportedOperationException(); // TODO } }