blob: 0e4e8c438cfe8c005789ba869b9903e157a2085a [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2007, 2019 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.r.core.rsource.ast;
import static org.eclipse.statet.r.core.rsource.IRSourceConstants.STATUS_OK;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.ltk.ast.core.AstNode;
import org.eclipse.statet.ltk.ast.core.impl.AbstractAstNode;
import org.eclipse.statet.r.core.rlang.RTerminal;
/**
* A node of a R AST
*/
@NonNullByDefault
public abstract class RAstNode extends AbstractAstNode
implements AstNode {
interface Assoc {
byte TERM= 1;
byte CONTAINER= 2;
byte NOSTD= 3;
byte LEFTSTD= 4;
byte RIGHTSTD= 5;
}
static final RAstNode[] NO_CHILDREN= new RAstNode[0];
@Nullable RAstNode rParent;
int startOffset;
int endOffset;
int status;
protected RAstNode() {
this.status= STATUS_OK;
}
protected RAstNode(final int status) {
this.status= status;
}
public abstract NodeType getNodeType();
public abstract @Nullable RTerminal getOperator(final int index);
@Override
public final int getStatusCode() {
return this.status;
}
/**
* @return the parent node, if it is an RAstNode too, otherwise <code>null</code>
*/
public @Nullable RAstNode getRParent() {
return this.rParent;
}
@Override
public @Nullable AstNode getParent() {
return this.rParent;
}
public final RAstNode getRRoot() {
RAstNode candidate= this;
RAstNode p;
while ((p= candidate.rParent) != null) {
candidate= p;
}
return candidate;
}
@Override
public abstract boolean hasChildren();
@Override
public abstract int getChildCount();
@Override
public abstract RAstNode getChild(int index);
public abstract RAstNode[] getChildren();
@Override
public abstract int getChildIndex(AstNode child);
@Override
public int getStartOffset() {
return this.startOffset;
}
@Override
public final int getEndOffset() {
return this.endOffset;
}
@Override
public final int getLength() {
return this.endOffset - this.startOffset;
}
int getEqualsIndex(final RAstNode element) {
final RAstNode[] children= getChildren();
int index= 0;
for (final RAstNode child : children) {
if (child == element) {
return index;
}
if (child.equalsSingle(element)) {
index++;
}
}
return -1;
}
public abstract void acceptInR(RAstVisitor visitor) throws InvocationTargetException;
public abstract void acceptInRChildren(RAstVisitor visitor) throws InvocationTargetException;
protected final void acceptChildren(final RAstVisitor visitor, final List<? extends RAstNode> children) throws InvocationTargetException {
for (final RAstNode child : children) {
child.acceptInR(visitor);
}
}
protected final void acceptChildrenExpr(final RAstVisitor visitor, final List<Expression> children) throws InvocationTargetException {
for (final Expression expr : children) {
expr.node.acceptInR(visitor);
}
}
abstract @Nullable Expression getExpr(RAstNode child);
abstract @Nullable Expression getLeftExpr();
abstract @Nullable Expression getRightExpr();
public final boolean equalsIgnoreAst(final Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof RAstNode) || !equalsSingle((RAstNode) obj)) {
return false;
}
RAstNode me= this;
RAstNode other= (RAstNode) obj;
while (me != other) {
if (me.rParent == null || other.rParent == null) {
return (me.rParent == null && other.rParent == null);
}
if ((!me.rParent.equalsSingle(other.rParent))
|| (me.rParent.getEqualsIndex(me) != other.rParent.getEqualsIndex(other))
) {
return false;
}
me= me.rParent;
other= other.rParent;
}
return true;
}
abstract boolean equalsSingle(RAstNode element);
public boolean equalsValue(final RAstNode element) {
if (getNodeType() != element.getNodeType()) {
return false;
}
final int count= getChildCount();
if (count != element.getChildCount()) {
return false;
}
for (int i= 0; i < count; i++) {
if (!getChild(i).equalsValue(element.getChild(i))) {
return false;
}
}
return true;
}
void appendPathElement(final StringBuilder s) {
// if (parent != null) {
// s.append(parent.getEqualsIndex(this));
// }
s.append('$');
s.append(getNodeType().ordinal());
}
public int hashCodeIgnoreAst() {
final StringBuilder path= new StringBuilder();
if (this.rParent != null) {
if (this.rParent.rParent != null) {
path.append(this.rParent.rParent.getNodeType().ordinal());
}
path.append('$');
path.append(this.rParent.getNodeType().ordinal());
}
appendPathElement(path);
return path.toString().hashCode();
}
@Override
public String toString() {
final StringBuilder s= new StringBuilder();
// s.append("«");
s.append(getNodeType().label);
// s.append(" § " + startOffset + "," + endOffset);
// s.append("»");
return s.toString();
}
abstract int getMissingExprStatus(Expression expr);
abstract void updateEndOffset();
}