/*
* ******************************************************************************
* MontiCore Language Workbench
* Copyright (c) 2015, MontiCore, All rights reserved.
*
* This project is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this project. If not, see <http://www.gnu.org/licenses/>.
* ******************************************************************************
*/
package de.monticore.symboltable;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Optional;
import de.monticore.ast.ASTNode;
import de.monticore.symboltable.references.SymbolReference;
import de.se_rwth.commons.logging.Log;
/**
* Base class for all symbol table creators.
*
* @author Pedram Mir Seyed Nazari
*
*/
public abstract class CommonSymbolTableCreator implements SymbolTableCreator {
private final ResolvingConfiguration resolvingConfig;
protected Deque<MutableScope> scopeStack;
/**
* The first scope OTHER THAN THE GLOBAL SCOPE that has been created, i.e., added to the scope
* stack. This information helps to determine the top scope within this creation process.
*/
private MutableScope firstCreatedScope;
public CommonSymbolTableCreator(final ResolvingConfiguration resolvingConfig,
final MutableScope enclosingScope) {
this(resolvingConfig, new ArrayDeque<>());
putOnStack(Log.errorIfNull(enclosingScope));
}
public CommonSymbolTableCreator(final ResolvingConfiguration resolvingConfig,
final Deque<MutableScope> scopeStack) {
this.scopeStack = Log.errorIfNull(scopeStack);
this.resolvingConfig = Log.errorIfNull(resolvingConfig);
}
@Override
public void putSpannedScopeOnStack(ScopeSpanningSymbol symbol) {
Log.errorIfNull(symbol);
putOnStack((MutableScope) symbol.getSpannedScope());
}
@Override
public void putOnStack(MutableScope scope) {
Log.errorIfNull(scope);
setResolvingFiltersForScope(scope);
if (!scope.getEnclosingScope().isPresent() && currentScope().isPresent()) {
scope.setEnclosingScope(currentScope().get());
currentScope().get().addSubScope(scope);
}
else if (scope.getEnclosingScope().isPresent() && currentScope().isPresent()) {
if (scope.getEnclosingScope().get() != currentScope().get()) {
Log.warn("0xA1043 The enclosing scope is not the same as the current scope on the stack.");
}
}
if ((firstCreatedScope == null) && !(scope instanceof GlobalScope)) {
firstCreatedScope = scope;
}
scopeStack.addLast(scope);
}
/**
* Sets the resolving filters that are available for the <code>scope</code>. If no filters are
* explicitly defined for the <code>scope</code>, the filters of the enclosing scope are used.
*
* @param scope the scope
*/
private void setResolvingFiltersForScope(final MutableScope scope) {
// Look for filters that are registered for that scope (in case it is named).
if (scope.getName().isPresent()) {
scope.setResolvingFilters(resolvingConfig.getFilters(scope.getName().get()));
}
// If no resolving filters are defined for the scope, take the ones from the enclosing scope.
if (scope.getResolvingFilters().isEmpty()) {
if (currentScope().isPresent()) {
final Scope enclosingScope = currentScope().get();
scope.setResolvingFilters(enclosingScope.getResolvingFilters());
}
else {
// Scope is the top scope, hence, use the default filters.
scope.setResolvingFilters(resolvingConfig.getDefaultFilters());
}
}
}
/**
* @deprecated use {@link #addToScope(Symbol)} instead
*/
@Override
@Deprecated
public void putInScope(final Symbol symbol) {
addToScope(symbol);
}
@Override
public void addToScope(Symbol symbol) {
if (!(symbol instanceof SymbolReference)){
if (currentScope().isPresent()) {
currentScope().get().add(symbol);
}
else {
Log.warn("0xA50212 Symbol cannot be added to current scope, since no scope exists.");
}
}
}
@Override
public void setLinkBetweenSymbolAndNode(Symbol symbol, ASTNode astNode) {
// symbol -> ast
symbol.setAstNode(astNode);
// ast -> symbol
astNode.setSymbol(symbol);
astNode.setEnclosingScope(symbol.getEnclosingScope());
// ast -> spannedScope
if (symbol instanceof ScopeSpanningSymbol) {
astNode.setSpannedScope(((ScopeSpanningSymbol) symbol).getSpannedScope());
}
}
@Override
public void setLinkBetweenSpannedScopeAndNode(MutableScope scope, ASTNode astNode) {
// scope -> ast
scope.setAstNode(astNode);
// ast -> scope
astNode.setSpannedScope(scope);
}
/**
* @deprecated use {@link #addToScopeAndLinkWithNode(Symbol, ASTNode)} instead
*/
@Override
@Deprecated
public void putInScopeAndLinkWithAst(Symbol symbol, ASTNode astNode) {
addToScopeAndLinkWithNode(symbol, astNode);
}
@Override
public void addToScopeAndLinkWithNode(Symbol symbol, ASTNode astNode) {
addToScope(symbol);
setLinkBetweenSymbolAndNode(symbol, astNode);
if (symbol instanceof ScopeSpanningSymbol) {
putSpannedScopeOnStack((ScopeSpanningSymbol) symbol);
}
}
@Override
public final Optional<? extends MutableScope> removeCurrentScope() {
return Optional.of(scopeStack.pollLast());
}
@Override
public final Optional<? extends MutableScope> currentScope() {
return Optional.ofNullable(scopeStack.peekLast());
}
@Override
public final Optional<? extends ScopeSpanningSymbol> currentSymbol() {
if (currentScope().isPresent()) {
return currentScope().get().getSpanningSymbol();
}
return Optional.empty();
}
@Override
public MutableScope getFirstCreatedScope() {
return firstCreatedScope;
}
@Override
public void setEnclosingScopeOfNodes(ASTNode root) {
EnclosingScopeOfNodesInitializer v = new EnclosingScopeOfNodesInitializer();
v.handle(root);
}
protected void setScopeStack(final Deque<MutableScope> scopeStack) {
this.scopeStack = scopeStack;
}
}