package scotch.compiler.syntax.definition;
import static java.util.stream.Collectors.toList;
import static scotch.compiler.syntax.builder.BuilderUtil.require;
import static scotch.compiler.syntax.reference.DefinitionReference.rootRef;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import com.google.common.collect.ImmutableList;
import lombok.ToString;
import scotch.compiler.analyzer.DependencyAccumulator;
import scotch.compiler.analyzer.NameAccumulator;
import scotch.compiler.analyzer.NameQualifier;
import scotch.compiler.analyzer.OperatorAccumulator;
import scotch.compiler.analyzer.PatternAnalyzer;
import scotch.compiler.analyzer.PrecedenceParser;
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.text.SourceLocation;
@ToString(exclude = "sourceLocation")
public class RootDefinition extends Definition {
public static Builder builder() {
return new Builder();
}
private final SourceLocation sourceLocation;
private final List<DefinitionReference> definitions;
RootDefinition(SourceLocation sourceLocation, List<DefinitionReference> definitions) {
this.sourceLocation = sourceLocation;
this.definitions = ImmutableList.copyOf(definitions);
}
@Override
public Definition accumulateDependencies(DependencyAccumulator state) {
return state.scoped(this, () -> withDefinitions(state.accumulateDependencies(definitions)));
}
@Override
public Definition accumulateNames(NameAccumulator state) {
return state.scoped(this, () -> withDefinitions(state.accumulateNames(definitions)));
}
@Override
public Definition checkTypes(TypeChecker state) {
return state.keep(this);
}
@Override
public Definition defineOperators(OperatorAccumulator state) {
return state.scoped(this, () -> withDefinitions(state.defineDefinitionOperators(definitions)));
}
@Override
public boolean equals(Object o) {
return o == this || o instanceof RootDefinition && Objects.equals(definitions, ((RootDefinition) o).definitions);
}
@Override
public Optional<DefinitionReference> generateIntermediateCode(IntermediateGenerator generator) {
return generator.scoped(this, () -> generator.defineRoot(definitions.stream()
.map(generator::generateIntermediateCode)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(toList())));
}
@Override
public DefinitionReference getReference() {
return rootRef();
}
@Override
public SourceLocation getSourceLocation() {
return sourceLocation;
}
@Override
public int hashCode() {
return Objects.hash(definitions);
}
@Override
public Optional<Definition> parsePrecedence(PrecedenceParser state) {
return Optional.of(state.scoped(this, () -> withDefinitions(state.mapOptional(definitions, Definition::parsePrecedence))));
}
@Override
public Definition qualifyNames(NameQualifier state) {
return state.scoped(this, () -> withDefinitions(state.qualifyDefinitionNames(definitions)));
}
@Override
public Definition reducePatterns(PatternAnalyzer state) {
return state.scoped(this, () -> withDefinitions(state.reducePatterns(definitions)));
}
public RootDefinition withDefinitions(List<DefinitionReference> definitions) {
return new RootDefinition(sourceLocation, definitions);
}
public static class Builder implements SyntaxBuilder<RootDefinition> {
private List<DefinitionReference> definitions;
private Optional<SourceLocation> sourceLocation;
private Builder() {
definitions = new ArrayList<>();
sourceLocation = Optional.empty();
}
@Override
public RootDefinition build() {
return Definitions.root(require(sourceLocation, "Source location"), definitions);
}
public Builder withModule(DefinitionReference module) {
definitions.add(module);
return this;
}
@Override
public Builder withSourceLocation(SourceLocation sourceLocation) {
this.sourceLocation = Optional.of(sourceLocation);
return this;
}
}
}