package scotch.compiler.syntax.definition;
import static scotch.compiler.syntax.builder.BuilderUtil.require;
import static scotch.compiler.syntax.reference.DefinitionReference.operatorRef;
import static scotch.symbol.Operator.operator;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalInt;
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;
import scotch.symbol.Operator;
import scotch.symbol.Symbol;
import scotch.symbol.Value.Fixity;
@ToString(exclude = "sourceLocation")
public class OperatorDefinition extends Definition {
public static Builder builder() {
return new Builder();
}
private final SourceLocation sourceLocation;
private final Symbol symbol;
private final Fixity fixity;
private final int precedence;
OperatorDefinition(SourceLocation sourceLocation, Symbol symbol, Fixity fixity, int precedence) {
this.sourceLocation = sourceLocation;
this.symbol = symbol;
this.fixity = fixity;
this.precedence = precedence;
}
@Override
public Definition accumulateDependencies(DependencyAccumulator state) {
return state.keep(this);
}
@Override
public Definition accumulateNames(NameAccumulator state) {
return state.keep(this);
}
@Override
public Definition checkTypes(TypeChecker state) {
return state.keep(this);
}
@Override
public Definition defineOperators(OperatorAccumulator state) {
return state.scoped(this, () -> {
state.defineOperator(symbol, getOperator());
return this;
});
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (o instanceof OperatorDefinition) {
OperatorDefinition other = (OperatorDefinition) o;
return Objects.equals(symbol, other.symbol)
&& Objects.equals(fixity, other.fixity)
&& Objects.equals(precedence, other.precedence);
} else {
return false;
}
}
@Override
public Optional<DefinitionReference> generateIntermediateCode(IntermediateGenerator generator) {
return Optional.empty();
}
public Operator getOperator() {
return operator(fixity, precedence);
}
@Override
public DefinitionReference getReference() {
return operatorRef(symbol);
}
@Override
public SourceLocation getSourceLocation() {
return sourceLocation;
}
public Symbol getSymbol() {
return symbol;
}
@Override
public int hashCode() {
return Objects.hash(symbol, fixity, precedence);
}
@Override
public Optional<Definition> parsePrecedence(PrecedenceParser state) {
return Optional.of(state.keep(this));
}
@Override
public Definition qualifyNames(NameQualifier state) {
return state.keep(this);
}
@Override
public Definition reducePatterns(PatternAnalyzer state) {
return state.keep(this);
}
public static class Builder implements SyntaxBuilder<OperatorDefinition> {
private Optional<Symbol> symbol;
private Optional<Fixity> fixity;
private OptionalInt precedence;
private Optional<SourceLocation> sourceLocation;
private Builder() {
symbol = Optional.empty();
fixity = Optional.empty();
precedence = OptionalInt.empty();
sourceLocation = Optional.empty();
}
@Override
public OperatorDefinition build() {
return Definitions.operatorDef(
require(sourceLocation, "Source location"),
require(symbol, "Operator symbol"),
require(fixity, "Operator fixity"),
require(precedence, "Operator precedence")
);
}
public Builder withFixity(Fixity fixity) {
this.fixity = Optional.of(fixity);
return this;
}
public Builder withPrecedence(int precedence) {
this.precedence = OptionalInt.of(precedence);
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;
}
}
}