/*
* ******************************************************************************
* MontiCore Language Workbench
* Copyright (c) 2016, 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.grammar.symboltable;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.base.Strings.nullToEmpty;
import static de.se_rwth.commons.Names.getQualifiedName;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import com.google.common.collect.Sets;
import de.monticore.ast.ASTNode;
import de.monticore.codegen.mc2cd.MCGrammarSymbolTableHelper;
import de.monticore.grammar.HelperGrammar;
import de.monticore.grammar.Multiplicity;
import de.monticore.grammar.grammar._ast.ASTASTRule;
import de.monticore.grammar.grammar._ast.ASTAbstractProd;
import de.monticore.grammar.grammar._ast.ASTAttributeInAST;
import de.monticore.grammar.grammar._ast.ASTClassProd;
import de.monticore.grammar.grammar._ast.ASTConstant;
import de.monticore.grammar.grammar._ast.ASTConstantGroup;
import de.monticore.grammar.grammar._ast.ASTEnumProd;
import de.monticore.grammar.grammar._ast.ASTExternalProd;
import de.monticore.grammar.grammar._ast.ASTGenericType;
import de.monticore.grammar.grammar._ast.ASTGrammarReference;
import de.monticore.grammar.grammar._ast.ASTInterfaceProd;
import de.monticore.grammar.grammar._ast.ASTLexActionOrPredicate;
import de.monticore.grammar.grammar._ast.ASTLexNonTerminal;
import de.monticore.grammar.grammar._ast.ASTLexProd;
import de.monticore.grammar.grammar._ast.ASTMCGrammar;
import de.monticore.grammar.grammar._ast.ASTMCImportStatement;
import de.monticore.grammar.grammar._ast.ASTNonTerminal;
import de.monticore.grammar.grammar._ast.ASTProd;
import de.monticore.grammar.grammar._ast.ASTRuleReference;
import de.monticore.grammar.grammar._ast.ASTSymbolDefinition;
import de.monticore.grammar.grammar._ast.ASTTerminal;
import de.monticore.grammar.grammar_withconcepts._visitor.Grammar_WithConceptsVisitor;
import de.monticore.grammar.prettyprint.Grammar_WithConceptsPrettyPrinter;
import de.monticore.symboltable.ArtifactScope;
import de.monticore.symboltable.CommonSymbolTableCreator;
import de.monticore.symboltable.ImportStatement;
import de.monticore.symboltable.MutableScope;
import de.monticore.symboltable.ResolvingConfiguration;
import de.monticore.symboltable.Scope;
import de.monticore.symboltable.Symbol;
import de.se_rwth.commons.StringTransformations;
import de.se_rwth.commons.logging.Log;
/**
* @author Pedram Mir Seyed Nazari
*/
public class MontiCoreGrammarSymbolTableCreator extends CommonSymbolTableCreator
implements Grammar_WithConceptsVisitor {
private final Grammar_WithConceptsPrettyPrinter prettyPrinter;
private String packageName = "";
private MCGrammarSymbol grammarSymbol;
private ASTMCGrammar astGrammar;
public MontiCoreGrammarSymbolTableCreator(
ResolvingConfiguration resolvingConfig,
MutableScope enclosingScope,
Grammar_WithConceptsPrettyPrinter prettyPrinter) {
super(resolvingConfig, enclosingScope);
this.prettyPrinter = prettyPrinter;
}
/**
* Creates the symbol table starting from the <code>rootNode</code> and
* returns the first scope that was created.
*
* @param rootNode the root node
* @return the first scope that was created
*/
public Scope createFromAST(ASTMCGrammar rootNode) {
Log.errorIfNull(rootNode);
handle(rootNode);
return getFirstCreatedScope();
}
// PN is tested
@Override
public void visit(ASTMCGrammar astGrammar) {
Log.debug("Building Symboltable for Grammar: " + astGrammar.getName(),
MontiCoreGrammarSymbolTableCreator.class.getSimpleName());
packageName = getQualifiedName(astGrammar.getPackage());
this.astGrammar = astGrammar;
final List<ImportStatement> imports = new ArrayList<>();
if (astGrammar.getImportStatements() != null) {
for (ASTMCImportStatement imp : astGrammar.getImportStatements()) {
imports.add(new ImportStatement(getQualifiedName(imp.getImportList()),
imp.isStar()));
}
}
final ArtifactScope scope = new ArtifactScope(Optional.empty(), packageName, imports);
putOnStack(scope);
grammarSymbol = new MCGrammarSymbol(astGrammar.getName());
grammarSymbol.setComponent(astGrammar.isComponent());
addToScopeAndLinkWithNode(grammarSymbol, astGrammar);
addSuperGrammars(astGrammar, grammarSymbol);
}
@Override
public void endVisit(ASTMCGrammar astGrammar) {
setComponentsCardinality();
setEnclosingScopeOfNodes(astGrammar);
computeStartParserProd(astGrammar);
// remove grammar scope
removeCurrentScope();
// remove artifact scope
removeCurrentScope();
}
@Override
public void visit(ASTInterfaceProd ast) {
MCProdSymbol prodSymbol = new MCProdSymbol(ast.getName());
prodSymbol.setInterface(true);
setSymbolDefinitionIfExists(prodSymbol, ast.getSymbolDefinition().orElse(null));
setSuperProdsAndTypes(prodSymbol, Collections.emptyList(),
Collections.emptyList(), ast.getSuperInterfaceRule(), ast.getASTSuperInterface());
addToScopeAndLinkWithNode(prodSymbol, ast);
}
@Override
public void endVisit(ASTInterfaceProd astInterfaceProd) {
removeCurrentScope();
}
@Override
public void endVisit(ASTConstantGroup astConstantGroup) {
removeCurrentScope();
}
@Override
public void visit(ASTLexProd ast) {
final MCProdSymbol prodSymbol = new MCProdSymbol(ast.getName());
prodSymbol.setLexerProd(true);
addToScopeAndLinkWithNode(prodSymbol, ast);
}
@Override
public void endVisit(ASTLexProd astLexProd) {
removeCurrentScope();
}
@Override
public void visit(ASTClassProd ast) {
final MCProdSymbol prodSymbol = new MCProdSymbol(ast.getName());
setSymbolDefinitionIfExists(prodSymbol, ast.getSymbolDefinition().orElse(null));
setSuperProdsAndTypes(prodSymbol, ast.getSuperRule(),
ast.getASTSuperClass(), ast.getSuperInterfaceRule(), ast.getASTSuperInterface());
addToScopeAndLinkWithNode(prodSymbol, ast);
}
public void endVisit(ASTClassProd astClassProd) {
removeCurrentScope();
}
@Override
public void visit(ASTAbstractProd ast) {
MCProdSymbol prodSymbol = new MCProdSymbol(ast.getName());
prodSymbol.setAbstract(true);
setSymbolDefinitionIfExists(prodSymbol, ast.getSymbolDefinition().orElse(null));
setSuperProdsAndTypes(prodSymbol, ast.getSuperRule(),
ast.getASTSuperClass(), ast.getSuperInterfaceRule(), ast.getASTSuperInterface());
addToScopeAndLinkWithNode(prodSymbol, ast);
}
@Override
public void endVisit(ASTAbstractProd astAbstractProd) {
removeCurrentScope();
}
@Override
public void visit(ASTExternalProd ast) {
final MCProdSymbol prodSymbol = new MCProdSymbol(ast.getName());
prodSymbol.setExternal(true);
setSymbolDefinitionIfExists(prodSymbol, ast.getSymbolDefinition().orElse(null));
addToScopeAndLinkWithNode(prodSymbol, ast);
}
@Override
public void endVisit(ASTExternalProd astExternalProd) {
removeCurrentScope();
}
@Override
public void visit(ASTEnumProd ast) {
final MCProdSymbol prodSymbol = new MCProdSymbol(ast.getName());
prodSymbol.setEnum(true);
addToScopeAndLinkWithNode(prodSymbol, ast);
}
@Override
public void endVisit(ASTEnumProd astEnumProd) {
removeCurrentScope();
}
@Override
public void visit(ASTTerminal ast) {
final String usageName = ast.getUsageName().orElse(null);
final Symbol currentSymbol = currentSymbol().orElse(null);
if (currentSymbol != null) {
final String symbolName = isNullOrEmpty(usageName) ? ast.getName() : usageName;
MCProdComponentSymbol prodComponent = new MCProdComponentSymbol(symbolName);
prodComponent.setUsageName(usageName);
if (currentSymbol instanceof MCProdSymbol) {
MCProdSymbol surroundingProd = (MCProdSymbol) currentSymbol;
setComponentMultiplicity(prodComponent, ast);
prodComponent = surroundingProd.addProdComponent(prodComponent);
}
else {
addToScope(prodComponent);
}
setLinkBetweenSymbolAndNode(prodComponent, ast);
prodComponent.setTerminal(true);
setComponentMultiplicity(prodComponent, ast);
}
}
@Override
public void visit(ASTNonTerminal ast) {
final String usageName = ast.getUsageName().orElse(null);
final Symbol currentSymbol = currentSymbol().orElse(null);
if (currentSymbol != null) {
final String symbolName = isNullOrEmpty(usageName) ? ast.getName() : usageName;
MCProdComponentSymbol prodComponent = new MCProdComponentSymbol(symbolName);
prodComponent.setUsageName(usageName);
MCProdSymbolReference symRef = new MCProdSymbolReference(ast.getName(),
currentScope().orElse(null));
prodComponent.setReferencedProd(symRef);
if (currentSymbol instanceof MCProdSymbol) {
MCProdSymbol surroundingProd = (MCProdSymbol) currentSymbol;
MCProdComponentSymbol prevProdComp = surroundingProd
.getProdComponent(prodComponent.getName()).orElse(null);
Optional<MCProdSymbol> byReference = MCGrammarSymbolTableHelper
.resolveRule(astGrammar, ast.getName());
if (!byReference.isPresent() || !byReference.get().isLexerProd()) {
if (prevProdComp != null && prevProdComp.getReferencedProd().isPresent()) {
boolean sameType = prevProdComp.getReferencedProd().get().getName()
.equals(ast.getName());
if (!sameType) {
boolean subType = MCGrammarSymbolTableHelper
.isSubType(prevProdComp.getReferencedProd().get(),
symRef)
|| MCGrammarSymbolTableHelper
.isSubType(symRef, prevProdComp.getReferencedProd().get());
if (!subType) {
Log.error("0xA4006 The production " + surroundingProd.getName()
+ " must not use the attribute name " + symbolName +
" for different nonterminals.");
}
}
}
}
prodComponent = surroundingProd.addProdComponent(prodComponent);
}
else {
addToScope(prodComponent);
}
setLinkBetweenSymbolAndNode(prodComponent, ast);
prodComponent.setNonterminal(true);
prodComponent.setReferencedSymbolName(ast.getReferencedSymbol().orElse(""));
}
}
@Override
public void visit(ASTASTRule ast) {
final Optional<MCProdSymbol> prodSymbol = grammarSymbol.getProdWithInherited(ast.getType());
if (!prodSymbol.isPresent()) {
Log.error(
"0xA4021 There must not exist an AST rule for the nonterminal " + ast.getType()
+ " because there exists no production defining " + ast.getType(),
ast.get_SourcePositionStart());
}
ast.getAttributeInASTs().forEach(a -> addAttributeInAST(prodSymbol.get(), a));
}
void setComponentMultiplicity(MCProdComponentSymbol prod, ASTNode ast) {
Multiplicity multiplicity = Multiplicity
.determineMultiplicity(astGrammar, ast);
if (multiplicity == Multiplicity.LIST) {
prod.setList(true);
}
else if (multiplicity == Multiplicity.OPTIONAL) {
prod.setOptional(true);
}
}
@Override
public void visit(ASTLexNonTerminal astNode) {
final Optional<MCProdComponentSymbol> sym = addRuleComponent(nullToEmpty(astNode.getName()),
astNode, "");
if (sym.isPresent()) {
sym.get().setLexerNonterminal(true);
}
}
@Override
public void visit(ASTConstantGroup astNode) {
Optional<String> attrName = MCGrammarSymbolTableHelper.getConstantName(astNode,
currentSymbol());
final String usageName = astNode.getUsageName().orElse(null);
final Symbol currentSymbol = currentSymbol().orElse(null);
if (currentSymbol != null && attrName.isPresent()) {
MCProdComponentSymbol prodComponent = new MCProdComponentSymbol(attrName.get());
prodComponent.setConstantGroup(true);
prodComponent.setUsageName(usageName);
if (currentSymbol instanceof MCProdSymbol) {
MCProdSymbol surroundingProd = (MCProdSymbol) currentSymbol;
final String symbolName = isNullOrEmpty(usageName)
? attrName.get()
: usageName;
Optional<MCProdComponentSymbol> prevProdComp = surroundingProd
.getProdComponent(symbolName);
if (prevProdComp.isPresent() && !prevProdComp.get().isConstantGroup()) {
Log.error("0xA4006 The production " + surroundingProd.getName()
+ " must not use the attribute name " + attrName.get() +
" for constant group and nonterminals.");
}
if (prevProdComp.isPresent()) {
prodComponent = prevProdComp.get();
prodComponent.setList(true);
setLinkBetweenSymbolAndNode(prodComponent, astNode);
putSpannedScopeOnStack(prodComponent);
}
else {
final Optional<MCProdComponentSymbol> sym = addRuleComponent(attrName.orElse(""),
astNode, astNode.getUsageName().orElse(null));
if (sym.isPresent()) {
sym.get().setConstantGroup(true);
addToScopeAndLinkWithNode(sym.get(), astNode);
}
}
}
}
}
@Override
public void visit(ASTConstant astNode) {
final Symbol currentSymbol = currentSymbol().orElse(null);
if (currentSymbol != null) {
final String symbolName = astNode.getHumanName().isPresent()
? astNode.getHumanName().get()
: astNode.getName();
MCProdComponentSymbol prodComponent = new MCProdComponentSymbol(symbolName);
prodComponent.setConstant(true);
prodComponent.setUsageName(astNode.getHumanName().orElse(null));
if (currentSymbol instanceof MCProdSymbol) {
MCProdSymbol surroundingProd = (MCProdSymbol) currentSymbol;
prodComponent = surroundingProd.addProdComponent(prodComponent);
}
else if (currentSymbol instanceof MCProdComponentSymbol) {
MCProdComponentSymbol surroundingProd = (MCProdComponentSymbol) currentSymbol;
surroundingProd.addSubProdComponent(prodComponent);
}
else {
addToScope(prodComponent);
}
setLinkBetweenSymbolAndNode(prodComponent, astNode);
}
}
/**
* Create entry for an implicit rule defined in another lexrule by using an
* action and changing the type of the token
*/
@Override
public void visit(ASTLexActionOrPredicate action) {
for (String typeName : HelperGrammar.findImplicitTypes(action, prettyPrinter)) {
// Create rule if needed
Optional<MCProdSymbol> rule = grammarSymbol.getProd(typeName);
if (!rule.isPresent()) {
// Create entry for an implicit rule
final MCProdSymbol prodSymbol = new MCProdSymbol(typeName);
prodSymbol.setLexerProd(true);
}
}
}
private Optional<MCProdComponentSymbol> addRuleComponent(String name, ASTNode node,
String usageName) {
final Symbol currentSymbol = currentSymbol().orElse(null);
if (currentSymbol != null) {
final String symbolName = isNullOrEmpty(usageName) ? name : usageName;
MCProdComponentSymbol prodComponent = new MCProdComponentSymbol(symbolName);
prodComponent.setUsageName(usageName);
if (currentSymbol instanceof MCProdSymbol) {
MCProdSymbol surroundingProd = (MCProdSymbol) currentSymbol;
prodComponent = surroundingProd.addProdComponent(prodComponent);
}
else {
addToScope(prodComponent);
}
setLinkBetweenSymbolAndNode(prodComponent, node);
return Optional.of(prodComponent);
}
return Optional.empty();
}
private void addSuperGrammars(ASTMCGrammar astGrammar, MCGrammarSymbol grammarSymbol) {
for (ASTGrammarReference ref : astGrammar.getSupergrammar()) {
final String superGrammarName = getQualifiedName(ref.getNames());
final MCGrammarSymbolReference superGrammar = new MCGrammarSymbolReference(
superGrammarName, currentScope().orElse(null));
grammarSymbol.addSuperGrammar(superGrammar);
}
}
private void setSuperProdsAndTypes(MCProdSymbol prodSymbol, List<ASTRuleReference> superProds,
List<ASTGenericType> astSuperClasses, List<ASTRuleReference> superInterfaceProds,
List<ASTGenericType> astSuperInterfaces) {
final Scope enclosingScope = currentScope().get();
// A extends B
for (ASTRuleReference astSuperProd : superProds) {
MCProdSymbolReference superProd = new MCProdSymbolReference(astSuperProd.getTypeName(),
enclosingScope);
prodSymbol.addSuperProd(superProd);
}
// A astextends B
for (ASTGenericType astSuperClass : astSuperClasses) {
MCProdOrTypeReference superClass = new MCProdOrTypeReference(astSuperClass.getTypeName(),
enclosingScope);
prodSymbol.addAstSuperClass(superClass);
}
// A implements B
for (ASTRuleReference astInterface : superInterfaceProds) {
MCProdSymbolReference superProd = new MCProdSymbolReference(astInterface.getTypeName(),
enclosingScope);
prodSymbol.addSuperInterfaceProd(superProd);
}
// A astimplements B
for (ASTGenericType astInterface : astSuperInterfaces) {
MCProdOrTypeReference superClass = new MCProdOrTypeReference(astInterface.getTypeName(),
enclosingScope);
prodSymbol.addAstSuperInterface(superClass);
}
}
/**
* Set cardinality of all grammar's nonterminals
*/
private void setComponentsCardinality() {
for (MCProdSymbol prodSymbol : grammarSymbol.getProdsWithInherited().values()) {
Collection<MCProdAttributeSymbol> astAttributes = prodSymbol.getProdAttributes();
for (MCProdComponentSymbol component : prodSymbol.getProdComponents()) {
if (component.isNonterminal()) {
setComponentMultiplicity(component, component.getAstNode().get());
Optional<MCProdAttributeSymbol> attribute = astAttributes.stream()
.filter(a -> a.getName().equals(component.getName())).findAny();
if (attribute.isPresent()) {
Multiplicity multiplicity = Multiplicity
.multiplicityOfAttributeInAST(
(ASTAttributeInAST) attribute.get().getAstNode().get());
component.setList(multiplicity == Multiplicity.LIST);
component.setOptional(multiplicity == Multiplicity.OPTIONAL);
}
}
}
}
}
private void setSymbolDefinitionIfExists(MCProdSymbol prodSymbol,
ASTSymbolDefinition symbolDefinition) {
if (symbolDefinition != null) {
String symbolKindName = prodSymbol.getName();
if (symbolDefinition.getSymbolKind().isPresent()
&& !symbolDefinition.getSymbolKind().get().isEmpty()) {
symbolKindName = symbolDefinition.getSymbolKind().get();
}
MCProdSymbolReference prodReference = new MCProdSymbolReference(symbolKindName,
prodSymbol.getSpannedScope());
prodSymbol.setProdDefiningSymbolKind(prodReference);
}
}
private void computeStartParserProd(ASTMCGrammar astGrammar) {
if (astGrammar.getStartRules().isPresent()) {
String name = astGrammar.getStartRules().get().getRuleReference().getName();
Optional<MCProdSymbol> prod = grammarSymbol.getProdWithInherited(name);
if (!prod.isPresent()) {
Log.error("0xA0243 Rule " + name + " couldn't be found!");
}
else {
prod.get().setStartProd(true);
grammarSymbol.setStartProd(prod.get());
}
}
else {
final Set<ASTProd> firstProductions = Sets.newLinkedHashSet();
// The start rule for parsing is the first occurring Interface-, Abstract-
// or Class-Production in this grammar
if (astGrammar.getClassProds().size() != 0) {
firstProductions.add(astGrammar.getClassProds().get(0));
}
if (astGrammar.getInterfaceProds().size() != 0) {
firstProductions.add(astGrammar.getInterfaceProds().get(0));
}
if (astGrammar.getAbstractProds().size() != 0) {
firstProductions.add(astGrammar.getAbstractProds().get(0));
}
setStartProd(firstProductions);
}
}
/**
* Set start parser production
*/
private void setStartProd(Set<ASTProd> firstProductions) {
// Set start parser rule
ASTProd firstProduction = null;
for (ASTProd prod : firstProductions) {
// TODO: add a common interface to the MC grammar for all these
// productions and remove this hack
if ((firstProduction == null)
|| (firstProduction.get_SourcePositionStart()
.compareTo(prod.get_SourcePositionStart()) > 0)) {
firstProduction = prod;
}
}
if (firstProduction != null) {
Optional<MCProdSymbol> prod = grammarSymbol.getProdWithInherited(firstProduction.getName());
if (!prod.isPresent()) {
Log.error("0xA2174 Prod " + firstProduction.getName() + " couldn't be found! Pos: "
+ firstProduction.get_SourcePositionStart());
}
else {
prod.get().setStartProd(true);
grammarSymbol.setStartProd(prod.get());
}
}
}
/**
* TODO: Write me!
*
* @param mcProdSymbol
* @param astAttribute
*/
private void addAttributeInAST(MCProdSymbol mcProdSymbol, ASTAttributeInAST astAttribute) {
String attributeName = astAttribute.getName()
.orElse(StringTransformations.uncapitalize(astAttribute.getGenericType().getTypeName()));
MCProdAttributeSymbol astAttributeSymbol = new MCProdAttributeSymbol(attributeName);
MCProdOrTypeReference attributeType = new MCProdOrTypeReference(
astAttribute.getGenericType().getTypeName(), mcProdSymbol.getSpannedScope());
astAttributeSymbol.setTypeReference(attributeType);
mcProdSymbol.addProdAttribute(astAttributeSymbol);
//
// Optional<MCProdComponentSymbol> mcComponent =
// mcProdSymbol.getProdComponent(attributeName);
// astAttributeSymbol.setReferencedComponent(mcComponent);
setLinkBetweenSymbolAndNode(astAttributeSymbol, astAttribute);
}
}