package scotch.compiler.syntax.definition; import static lombok.AccessLevel.PACKAGE; import static scotch.compiler.syntax.TypeError.typeError; import static scotch.compiler.syntax.builder.BuilderUtil.require; import static scotch.compiler.syntax.reference.DefinitionReference.valueRef; import static scotch.util.Either.right; import java.util.Optional; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.ToString; import scotch.compiler.analyzer.DependencyAccumulator; import scotch.compiler.analyzer.NameAccumulator; import scotch.compiler.analyzer.OperatorAccumulator; import scotch.compiler.analyzer.PatternAnalyzer; import scotch.compiler.analyzer.PrecedenceParser; import scotch.compiler.analyzer.NameQualifier; import scotch.compiler.analyzer.TypeChecker; import scotch.compiler.intermediate.IntermediateGenerator; import scotch.compiler.syntax.builder.SyntaxBuilder; import scotch.compiler.syntax.reference.DefinitionReference; import scotch.compiler.syntax.reference.ValueReference; import scotch.compiler.syntax.value.Value; import scotch.compiler.text.SourceLocation; import scotch.util.Either; import scotch.symbol.Symbol; import scotch.compiler.syntax.type.Type; @AllArgsConstructor(access = PACKAGE) @EqualsAndHashCode(callSuper = false) @ToString(exclude = "sourceLocation") public class ValueDefinition extends Definition { public static Builder builder() { return new Builder(); } private final SourceLocation sourceLocation; private final Symbol symbol; private final Value body; @Override public Definition accumulateDependencies(DependencyAccumulator state) { return state.scoped(this, () -> { state.pushSymbol(symbol); try { return withBody(body.accumulateDependencies(state)); } finally { state.popSymbol(); } }); } @Override public Definition accumulateNames(NameAccumulator state) { state.defineValue(symbol, getType()); state.specialize(getType()); return state.scoped(this, () -> withBody(body.accumulateNames(state))); } @Override public Optional<Symbol> asSymbol() { return Optional.of(symbol); } @Override public Either<Definition, ValueDefinition> asValue() { return right(this); } @Override public Definition checkTypes(TypeChecker state) { return state.enclose(this, () -> { Value checkedBody = body.checkTypes(state); Type type = state.getType(this) .unify(checkedBody.getType(), state.scope()) .orElseGet(unification -> { state.error(typeError(unification, sourceLocation)); return checkedBody.getType(); }); Type generatedType = state.scope().generate(type); ValueDefinition result = withBody(checkedBody.withType(generatedType)); state.redefine(result); return state.bind(result); }); } @Override public Definition defineOperators(OperatorAccumulator state) { return state.scoped(this, () -> withBody(body.defineOperators(state))); } @Override public Optional<DefinitionReference> generateIntermediateCode(IntermediateGenerator generator) { return generator.scoped(this, () -> generator.defineValue(symbol, body.getType(), body.generateIntermediateCode(generator))); } public Value getBody() { return body; } public String getMethodName() { return symbol.unqualify().getMethodName(); } @Override public ValueReference getReference() { return valueRef(symbol); } public SourceLocation getSourceLocation() { return sourceLocation; } public Symbol getSymbol() { return symbol; } public Type getType() { return body.getType(); } @Override public Optional<Definition> parsePrecedence(PrecedenceParser state) { return Optional.of(state.named(symbol, () -> state.scoped(this, () -> withBody(body.parsePrecedence(state).unwrap())))); } @Override public Definition qualifyNames(NameQualifier state) { return state.named(symbol, () -> state.scoped(this, () -> { Type qualifiedType = getType().qualifyNames(state); state.redefineValue(symbol, qualifiedType); return new ValueDefinition( sourceLocation, symbol, body.qualifyNames(state) ); })); } @Override public Definition reducePatterns(PatternAnalyzer state) { return state.scoped(this, () -> withBody(body.reducePatterns(state))); } public ValueDefinition withBody(Value body) { return new ValueDefinition(sourceLocation, symbol, body); } public ValueDefinition withSourceLocation(SourceLocation sourceLocation) { return new ValueDefinition(sourceLocation, symbol, body); } public static class Builder implements SyntaxBuilder<ValueDefinition> { private Optional<Symbol> symbol; private Optional<Value> body; private Optional<SourceLocation> sourceLocation; private Builder() { symbol = Optional.empty(); body = Optional.empty(); sourceLocation = Optional.empty(); } @Override public ValueDefinition build() { return Definitions.value( require(sourceLocation, "Source location"), require(symbol, "Value symbol"), require(body, "Value body").collapse() ); } public Builder withBody(Value body) { this.body = Optional.of(body); return this; } @Override public Builder withSourceLocation(SourceLocation sourceLocation) { this.sourceLocation = Optional.of(sourceLocation); return this; } public Builder withSymbol(Symbol symbol) { this.symbol = Optional.of(symbol); return this; } } }