SourceNode.java
package org.sterling.source.syntax;
import static org.sterling.source.LocationRange.extent;
import static org.sterling.source.syntax.NodeKind.UNDEFINED;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import org.sterling.SterlingException;
import org.sterling.source.LocationAware;
import org.sterling.source.LocationRange;
public class SourceNode implements LocationAware {
public static final SourceNode VOID = new SourceNode();
private final State state;
private SourceNode parent;
public SourceNode(NodeKind kind) {
if (kind.isTerminal()) {
state = new TerminalState(kind);
} else {
state = new NonTerminalState(kind, this);
}
}
private SourceNode() {
state = new VoidState();
}
public <R, S> R accept(SourceVisitor<R, S> visitor, S state) throws SterlingException {
return null;
}
public void addChild(SourceNode child) {
state.addChild(child);
}
public boolean childOf(SourceNode parent) {
return this.parent != null && (this.parent.equals(parent) || this.parent.childOf(parent));
}
public int childrenSize() {
return state.childrenSize();
}
public void clearChildren() {
state.clearChildren();
}
@Override
public boolean equals(Object o) {
return o == this || o instanceof SourceNode && state.equals(((SourceNode) o).state);
}
public SourceNode getChildAt(int index) {
return state.getChildAt(index);
}
public List<SourceNode> getChildren() {
return state.getChildren();
}
public NodeKind getKind() {
return state.getKind();
}
@Override
public LocationRange getRange() {
return state.getRange();
}
public Token getToken() {
return state.getToken();
}
public void setToken(Token token) {
state.setToken(token);
}
@Override
public int hashCode() {
return state.hashCode();
}
public boolean isEmpty() {
return state.isEmpty();
}
public boolean isTerminal() {
return state.getKind().isTerminal();
}
public void prune() {
state.prune();
}
public String toString() {
return state.toString();
}
private interface State extends LocationAware {
void addChild(SourceNode child);
int childrenSize();
void clearChildren();
SourceNode getChildAt(int index);
List<SourceNode> getChildren();
NodeKind getKind();
Token getToken();
void setToken(Token token);
boolean isEmpty();
void prune();
}
private static final class NonTerminalState implements State {
private final NodeKind kind;
private final SourceNode node;
private final List<SourceNode> children;
public NonTerminalState(NodeKind kind, SourceNode node) {
this.kind = kind;
this.node = node;
this.children = new ArrayList<>();
}
@Override
public void addChild(SourceNode child) {
children.add(child);
child.parent = node;
}
@Override
public int childrenSize() {
return children.size();
}
@Override
public void clearChildren() {
for (SourceNode child : children) {
child.parent = null;
}
children.clear();
}
@Override
public boolean equals(Object o) {
if (o instanceof NonTerminalState) {
NonTerminalState other = (NonTerminalState) o;
return Objects.equals(kind, other.kind)
&& Objects.equals(children, other.children);
} else {
return false;
}
}
@Override
public SourceNode getChildAt(int index) {
if (index >= children.size()) {
return VOID;
} else {
return children.get(index);
}
}
@Override
public List<SourceNode> getChildren() {
return new ArrayList<>(children);
}
@Override
public NodeKind getKind() {
return kind;
}
@Override
public LocationRange getRange() {
return extent(children);
}
@Override
public Token getToken() {
throw new IllegalStateException();
}
@Override
public void setToken(Token token) {
throw new IllegalStateException();
}
@Override
public int hashCode() {
return Objects.hash(kind, children);
}
@Override
public boolean isEmpty() {
return children.isEmpty();
}
@Override
public void prune() {
Iterator<SourceNode> iterator = children.iterator();
while (iterator.hasNext()) {
SourceNode node = iterator.next();
node.prune();
if (node.isEmpty()) {
iterator.remove();
}
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append('(');
builder.append(kind);
Iterator<SourceNode> iterator = children.iterator();
if (iterator.hasNext()) {
builder.append(" children=[");
builder.append(iterator.next());
while (iterator.hasNext()) {
builder.append(", ");
builder.append(iterator.next());
}
builder.append(']');
}
builder.append(')');
return builder.toString();
}
}
private static final class TerminalState implements State {
private final NodeKind kind;
private Token token;
public TerminalState(NodeKind kind) {
this.kind = kind;
}
@Override
public void addChild(SourceNode child) {
throw new IllegalStateException();
}
@Override
public int childrenSize() {
throw new IllegalStateException();
}
@Override
public void clearChildren() {
throw new IllegalStateException();
}
@Override
public boolean equals(Object o) {
if (o instanceof TerminalState) {
TerminalState other = (TerminalState) o;
return Objects.equals(kind, other.kind)
&& Objects.equals(token, other.token);
} else {
return false;
}
}
@Override
public SourceNode getChildAt(int index) {
throw new IllegalStateException();
}
@Override
public List<SourceNode> getChildren() {
throw new IllegalStateException();
}
@Override
public NodeKind getKind() {
return kind;
}
@Override
public LocationRange getRange() {
return token.getRange();
}
@Override
public Token getToken() {
return token;
}
@Override
public void setToken(Token token) {
if (!token.is(kind)) {
throw new IllegalArgumentException("Expecting token kind " + kind + " but got " + token.getKind());
}
this.token = token;
}
@Override
public int hashCode() {
return Objects.hash(kind, token);
}
@Override
public boolean isEmpty() {
return token == null;
}
@Override
public void prune() {
// intentionally empty
}
@Override
public String toString() {
if (token == null) {
return "(" + kind + ")";
} else {
return "(" + kind + " token=" + token + ")";
}
}
}
private static final class VoidState implements State {
@Override
public void addChild(SourceNode child) {
throw new IllegalStateException();
}
@Override
public int childrenSize() {
throw new IllegalStateException();
}
@Override
public void clearChildren() {
throw new IllegalStateException();
}
@Override
public SourceNode getChildAt(int index) {
throw new IllegalStateException();
}
@Override
public List<SourceNode> getChildren() {
throw new IllegalStateException();
}
@Override
public NodeKind getKind() {
return UNDEFINED;
}
@Override
public LocationRange getRange() {
return LocationRange.NULL;
}
@Override
public Token getToken() {
throw new IllegalStateException();
}
@Override
public void setToken(Token token) {
throw new IllegalStateException();
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public void prune() {
// intentionally empty
}
}
}