package me.tomassetti.turin.parser.ast;
import me.tomassetti.turin.compiler.errorhandling.ErrorCollector;
import me.tomassetti.turin.resolvers.ResolverRegistry;
import me.tomassetti.turin.resolvers.SymbolResolver;
import me.tomassetti.turin.parser.ast.expressions.InvokableExpr;
import me.tomassetti.turin.parser.ast.statements.BlockStatement;
import me.tomassetti.turin.parser.ast.statements.Statement;
import me.tomassetti.turin.symbols.FormalParameter;
import me.tomassetti.turin.symbols.Symbol;
import me.tomassetti.turin.typesystem.TypeUsage;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
/**
* A Node the Abstract Syntax Tree.
* Note that nodes are initially created by the parser but during compilation additional "virtual" nodes could be
* created.
*/
public abstract class Node implements Symbol {
protected Node parent;
private Position position;
private Boolean valid;
@Override
public boolean isNode() {
return true;
}
@Override
public Node asNode() {
return this;
}
///
/// Resolver
///
protected SymbolResolver symbolResolver() {
return ResolverRegistry.INSTANCE.requireResolver(this);
}
///
/// Position
///
public Position getPosition() {
if (position == null) {
throw new IllegalStateException(this.toString()+ " has no position assigned");
}
return position;
}
public void setPosition(Position position) {
this.position = position;
}
///
/// Tree
///
public Node getRoot() {
if (isRoot()) {
return this;
} else {
return getParent().getRoot();
}
}
public boolean isRoot() {
return parent == null;
}
public Node getParent() {
return parent;
}
public abstract Iterable<Node> getChildren();
public void setParent(Node parent){
if (parent == this) {
throw new IllegalArgumentException();
}
this.parent = parent;
}
public <T extends Node> List<T> findAll(Class<T> desiredClass) {
List<T> results = new LinkedList<>();
if (desiredClass.isInstance(this)) {
results.add(desiredClass.cast(this));
}
for (Node child : getChildren()) {
results.addAll(child.findAll(desiredClass));
}
return results;
}
public <N extends Node> N getParentOfType(Class<N> parentClazz) {
if (getParent() == null) {
throw new IllegalStateException("It was expected to be contained in a " + parentClazz.getName());
}
if (parentClazz.isInstance(getParent())) {
return parentClazz.cast(getParent());
}
return getParent().getParentOfType(parentClazz);
}
///
/// Naming
///
public String contextName() {
if (parent == null) {
return "";
}
if (parent instanceof TurinFile) {
TurinFile turinFile = (TurinFile)parent;
return turinFile.getNamespaceDefinition().getName();
}
return parent.contextName();
}
///
/// Typing
///
public TypeUsage calcType() {
throw new UnsupportedOperationException(this.getClass().getCanonicalName());
}
///
/// Symbol resolution
///
public Optional<Symbol> findSymbol(String name, SymbolResolver resolver) {
if (parent == null) {
return Optional.empty();
} else if (parent instanceof BlockStatement) {
if (!(this instanceof Statement)) {
throw new RuntimeException();
}
// This is a peculiar case because we have visibility only on the elements preceding this statement
BlockStatement blockStatement = (BlockStatement)parent;
List<Statement> preceedingStatements = blockStatement.findPreeceding((Statement)this);
for (Statement statement : preceedingStatements) {
Optional<Symbol> result = statement.findSymbol(name, resolver);
if (result.isPresent()) {
return result;
}
}
return parent.findSymbol(name, resolver);
} else {
return parent.findSymbol(name, resolver);
}
}
public final Symbol getField(QualifiedName fieldsPath) {
if (fieldsPath.isSimpleName()) {
return getField(fieldsPath.getName());
} else {
Symbol next = getField(fieldsPath.firstSegment());
return next.getField(fieldsPath.rest());
}
}
public Optional<List<? extends FormalParameter>> findFormalParametersFor(InvokableExpr invokable) {
throw new UnsupportedOperationException(this.getClass().getCanonicalName());
}
/**
* This is intended in a broad way: everything that can be accessed with a dot.
*/
public Symbol getField(String fieldName) {
return calcType().getInstanceField(fieldName, this);
}
///
/// Validation
///
public final boolean validate(SymbolResolver resolver, ErrorCollector errorCollector) {
boolean res = specificValidate(resolver, errorCollector);
// if the node is wrong we do not check its children
if (res) {
for (Node child : getChildren()) {
boolean partial = child.validate(resolver, errorCollector);
if (!partial) {
res = false;
}
}
}
valid = res;
return res;
}
public boolean isValid() {
if (valid == null) {
throw new IllegalStateException("Not validated");
}
return valid;
}
/**
* @return if the node is valid
*/
protected boolean specificValidate(SymbolResolver resolver, ErrorCollector errorCollector) {
// nothing to do
return true;
}
///
/// Support
///
public String describe() {
return this.toString();
}
}