package scotch.compiler.syntax.value; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; import static scotch.compiler.syntax.builder.BuilderUtil.require; import java.util.ArrayList; import java.util.List; import java.util.Optional; import com.google.common.collect.ImmutableList; import lombok.EqualsAndHashCode; 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.builder.SyntaxBuilder; import scotch.compiler.syntax.pattern.PatternReducer; import scotch.compiler.text.SourceLocation; import scotch.symbol.Symbol; import scotch.compiler.syntax.type.Type; @EqualsAndHashCode(callSuper = false) public class DataConstructor extends Value { public static Builder builder() { return new Builder(); } private final SourceLocation sourceLocation; private final Symbol symbol; private final List<Value> arguments; private final Type type; DataConstructor(SourceLocation sourceLocation, Symbol symbol, Type type, List<Value> arguments) { this.sourceLocation = sourceLocation; this.symbol = symbol; this.arguments = ImmutableList.copyOf(arguments); this.type = type; } @Override public Value accumulateDependencies(DependencyAccumulator state) { return this; } @Override public Value accumulateNames(NameAccumulator state) { return this; } @Override public IntermediateValue generateIntermediateCode(IntermediateGenerator state) { return state.createConstructor( symbol, state.getDataConstructor(symbol).getClassName(), state.getDataConstructor(symbol).getConstructorSignature(), arguments.stream() .map(argument -> argument.generateIntermediateCode(state)) .collect(toList())); } @Override public Value bindMethods(TypeChecker typeChecker) { return withArguments(typeChecker.bindMethods(arguments)); } @Override public Value bindTypes(TypeChecker typeChecker) { return withType(typeChecker.generate(type)) .withArguments(typeChecker.bindTypes(arguments)); } @Override public Value checkTypes(TypeChecker typeChecker) { return withArguments(typeChecker.checkTypes(arguments)); } @Override public Value defineOperators(OperatorAccumulator state) { return withArguments(state.defineValueOperators(arguments)); } private DataConstructor withArguments(List<Value> arguments) { return new DataConstructor(sourceLocation, symbol, type, arguments); } @Override public SourceLocation getSourceLocation() { return sourceLocation; } @Override public Type getType() { return type; } @Override public Value parsePrecedence(PrecedenceParser state) { return withArguments(arguments.stream() .map(argument -> argument.parsePrecedence(state)) .collect(toList())); } @Override public Value qualifyNames(NameQualifier state) { return withArguments(state.qualifyValueNames(arguments)) .withType(type.qualifyNames(state)); } @Override public Value reducePatterns(PatternReducer reducer) { return withArguments(arguments.stream().map(argument -> argument.reducePatterns(reducer)).collect(toList())); } @Override public String toString() { return symbol.toString() + "(" + arguments.stream().map(Object::toString).collect(joining(", ")) + ")"; } @Override public DataConstructor withType(Type type) { return new DataConstructor(sourceLocation, symbol, type, arguments); } public static class Builder implements SyntaxBuilder<DataConstructor> { private Optional<SourceLocation> sourceLocation; private Optional<Symbol> symbol; private List<Value> arguments; private Optional<Type> type; public Builder() { sourceLocation = Optional.empty(); symbol = Optional.empty(); arguments = new ArrayList<>(); type = Optional.empty(); } @Override public DataConstructor build() { return new DataConstructor( require(sourceLocation, "Source location"), require(symbol, "Constructor symbol"), require(type, "Constructor type"), arguments ); } public Builder withArguments(List<Value> arguments) { this.arguments.addAll(arguments); 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; } public Builder withType(Type type) { this.type = Optional.of(type); return this; } } }