/*
* Copyright 2009 www.scribble.org
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.scribble.ast;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.antlr.runtime.tree.CommonTree;
import org.scribble.del.ScribDel;
import org.scribble.main.RuntimeScribbleException;
import org.scribble.main.ScribbleException;
import org.scribble.sesstype.kind.Global;
import org.scribble.sesstype.kind.Local;
import org.scribble.sesstype.kind.ProtocolKind;
import org.scribble.util.ScribUtil;
import org.scribble.visit.AstVisitor;
import org.scribble.visit.Substitutor;
/**
* This is the generic object from which all Scribble model objects
* are derived.
*/
public abstract class ScribNodeBase implements ScribNode
{
protected final CommonTree source; // Consequently, core depends on Antlr
// Used to be for parsed entities; null if not parsed
// Now: for the original parsed entity for error blaming; should not be null unless a purely generated entity
protected ScribDel del;
protected ScribNodeBase(CommonTree source)
{
this.source = source;
}
@Override
public final CommonTree getSource()
{
return this.source;
}
// Internal shallow copy for (immutable) ModelNodes -- does not keep the del (copy is used internally for del setter, and keeping the del needs knowledge of super fields)
// Generally copy is only for shallow copy of object and "immediate" fields (not super fields), cf. ProtocolDecl, ProtocolDeclContext, etc
protected abstract ScribNodeBase copy();
@Override
public abstract ScribNodeBase clone();
@Override
public final ScribDel del()
{
return this.del;
}
@Override
public final ScribNodeBase del(ScribDel del)
{
ScribNodeBase copy = copy();
copy.del = del;
return copy;
}
@Override
public ScribNode accept(AstVisitor nv) throws ScribbleException
{
return nv.visit(null, this);
}
@Override
public ScribNode visitChildren(AstVisitor nv) throws ScribbleException
{
return this;
}
protected ScribNode visitChild(ScribNode child, AstVisitor nv) throws ScribbleException
{
return nv.visit(this, child);
}
public static final <T extends ScribNode> T del(T n, ScribDel del)
{
ScribNodeBase copy = ((ScribNodeBase) n).copy();
copy.del = del;
return ScribUtil.castNodeByClass(n, copy);
}
@Override
public ScribNode substituteNames(Substitutor subs)
{
return this;
}
// FIXME: remove parent parameter, to make uniform with visitChild
// Used when a generic cast would otherwise be needed (non-generic children casts don't need this) -- doesn't check any generic parameters, relies on concrete values being instances of non-parameterised types
// Subtype constraint on visited could still be too restrictive, e.g. AmbigNameNodeDel (although it doesn't matter there), e.g. unfolding continue's into recursion's
protected final static <T extends ScribNode>
T visitChildWithClassEqualityCheck(ScribNode parent, T child, AstVisitor nv) throws ScribbleException
{
ScribNode visited = ((ScribNodeBase) parent).visitChild(child, nv);
// Same subtyping flexibility as standard cast
return ScribUtil.checkNodeClassEquality(child, visited);
}
protected final static <T extends ScribNode>
List<T> visitChildListWithClassEqualityCheck(ScribNode parent, List<T> children, AstVisitor nv)
throws ScribbleException
{
return visitChildListWith(parent, children, nv,
(T t) -> ScribUtil.handleLambdaScribbleException(
() -> ScribNodeBase.visitChildWithClassEqualityCheck(parent, t, nv))); // -> T
}
// Just a list-map with handling for promoted exceptions (via handleLambdaScribbleException) -- could move to Util (where handleLambdaScribbleException is)
private final static <T extends ScribNode, R extends ScribNode>
List<R> visitChildListWith(ScribNode parent, List<T> children, AstVisitor nv, Function<T, R> c)
throws ScribbleException
{
/*List<T> visited = new LinkedList<>();
for (T n : children)
{
visited.add(c.call());
}
return visited;*/
// Maybe this exception hack is not worth it? Better to throw directly as ScribbleException
try
{
return children.stream().map((n) -> c.apply(n)).collect(Collectors.toList());
}
catch (RuntimeScribbleException rse)
{
Throwable cause = rse.getCause();
if (cause instanceof ScribbleException)
{
throw (ScribbleException) cause;
}
throw (RuntimeException) cause;
}
}
// Following would fit better in ProtocolKindNode, except it is an interface
// Takes clazz+kind to handle generic ProtocolKindNodes -- cf. ScribUtil.castNodeByClass, for casting to ground class types
// R is expected to be N<K>, i.e. the generic (ProtocolKindNode) class N parameterised by K
protected final static <N extends ProtocolKindNode<?>, K extends ProtocolKind, R extends ProtocolKindNode<K>>
R visitProtocolKindChildWithCastCheck(
ScribNode parent, ScribNode child, AstVisitor nv, Class<N> clazz, K kind, Function<ScribNode, R> cast)
throws ScribbleException
{
ScribNode visited = ((ScribNodeBase) parent).visitChild(child, nv);
if (!clazz.isAssignableFrom(visited.getClass()))
{
throw new RuntimeException(nv.getClass() + " generic visit error: " + clazz + ", " + visited.getClass());
}
ProtocolKindNode<?> pkn = (ProtocolKindNode<?>) visited;
if ((pkn.isGlobal() && !kind.equals(Global.KIND)) || (pkn.isLocal() && !kind.equals(Local.KIND)))
{
throw new RuntimeException(nv.getClass() + " generic visit error: " + pkn.getClass() + ", " + kind);
}
return cast.apply(pkn);
}
protected final static
<T extends ScribNode, N extends ProtocolKindNode<?>, K extends ProtocolKind, R extends ProtocolKindNode<K>>
List<R> visitProtocolKindChildListWithCastCheck(
ScribNode parent, List<T> children, AstVisitor nv, Class<N> c, K k, Function<ScribNode, R> f)
throws ScribbleException
{
return visitChildListWith(parent, children, nv,
(T t) -> ScribUtil.handleLambdaScribbleException(
() -> ScribNodeBase.visitProtocolKindChildWithCastCheck(parent, t, nv, c, k, f))); // -> R
}
}