package scotch.compiler.syntax.definition;
import static java.util.stream.Collectors.joining;
import static scotch.compiler.text.SourceLocation.NULL_SOURCE;
import static scotch.compiler.text.TextUtil.repeat;
import java.util.Collection;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import com.google.common.collect.ImmutableSet;
import scotch.symbol.Symbol;
import scotch.compiler.text.SourceLocation;
public class DependencyCycle {
private final Set<Node> nodes;
public DependencyCycle(Set<Node> nodes) {
this.nodes = ImmutableSet.copyOf(nodes);
}
public static DependencyCycle.Builder builder() {
return new DependencyCycle.Builder();
}
@Override
public boolean equals(Object o) {
return o == this || o instanceof DependencyCycle && Objects.equals(nodes, ((DependencyCycle) o).nodes);
}
@Override
public int hashCode() {
return Objects.hash(nodes);
}
public String prettyPrint() {
return "Dependency cycle detected:\n" + nodes.stream().map(Node::prettyPrint).collect(joining("\n"));
}
public String report(String indent, int indentLevel) {
return nodes.stream()
.map(node -> node.report(indent, indentLevel))
.collect(joining("\n\n"));
}
public static final class Builder {
private final Set<Node> nodes;
private Builder() {
nodes = new HashSet<>();
}
public Builder addNode(DefinitionNode node) {
return addNode(node.getSymbol(), node.getSourceLocation(), node.getDependencies());
}
public Builder addNode(Symbol symbol, Collection<Symbol> dependencies) {
return addNode(symbol, NULL_SOURCE, dependencies);
}
public Builder addNode(Symbol symbol, SourceLocation sourceLocation, Collection<Symbol> dependencies) {
nodes.add(new Node(symbol, sourceLocation, dependencies));
return this;
}
public DependencyCycle build() {
return new DependencyCycle(nodes);
}
}
public static final class Node {
private final Symbol symbol;
private final SourceLocation sourceLocation;
private final Set<Symbol> dependencies;
public Node(Symbol symbol, SourceLocation sourceLocation, Collection<Symbol> dependencies) {
this.symbol = symbol;
this.sourceLocation = sourceLocation;
this.dependencies = ImmutableSet.copyOf(dependencies);
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (o instanceof Node) {
Node other = (Node) o;
return Objects.equals(symbol, other.symbol)
&& Objects.equals(sourceLocation, other.sourceLocation)
&& Objects.equals(dependencies, other.dependencies);
} else {
return false;
}
}
@Override
public int hashCode() {
return Objects.hash(symbol, dependencies);
}
public String prettyPrint() {
return "Method " + symbol.quote() + " depends on ["
+ dependencies.stream().map(Symbol::quote).collect(joining(", "))
+ "]" + " " + sourceLocation.prettyPrint();
}
public String report(String indent, int indentLevel) {
return sourceLocation.report(indent, indentLevel) + "\n"
+ repeat(indent, indentLevel + 1) + "Can't analyze types! Dependency cycle detected:\n"
+ repeat(indent, indentLevel + 2) + "- " + symbol.quote() + "\n"
+ dependencies.stream()
.map(symbol -> repeat(indent, indentLevel + 2) + "- " + symbol.quote())
.collect(joining("\n"));
}
}
}