/*
* ******************************************************************************
* 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 de.monticore.GrammarGlobalScopeTestFactory;
import de.monticore.grammar.grammar._ast.ASTAbstractProd;
import de.monticore.grammar.grammar._ast.ASTClassProd;
import de.monticore.grammar.grammar._ast.ASTExternalProd;
import de.monticore.grammar.grammar._ast.ASTInterfaceProd;
import de.monticore.grammar.grammar._ast.ASTLexProd;
import de.monticore.grammar.grammar._ast.ASTMCGrammar;
import de.monticore.symboltable.Scope;
import de.monticore.symboltable.resolving.ResolvedSeveralEntriesException;
import de.se_rwth.commons.logging.Log;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.Optional;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* @author Pedram Mir Seyed Nazari
*/
public class MontiCoreGrammarSymbolTableCreatorTest {
@BeforeClass
public static void disableFailQuick() {
Log.enableFailQuick(false);
}
@Test
public void testSymbolTableOfGrammarStatechartDSL() {
final Scope globalScope = GrammarGlobalScopeTestFactory.createUsingEssentialMCLanguage();
final Optional<MCGrammarSymbol> grammar =
globalScope.resolve("de.monticore.statechart.Statechart", MCGrammarSymbol.KIND);
assertTrue(grammar.isPresent());
assertTrue(grammar.get().getAstNode().isPresent());
testGrammarSymbolOfStatechart(grammar.get());
}
private void testGrammarSymbolOfStatechart(MCGrammarSymbol grammar) {
assertNotNull(grammar);
assertEquals("de.monticore.statechart.Statechart", grammar.getFullName());
assertEquals("de.monticore.statechart", grammar.getPackageName());
assertTrue(grammar.getKind().isSame(MCGrammarSymbol.KIND));
assertTrue(grammar.getStartProd().isPresent());
assertTrue(grammar.isComponent());
assertEquals(1, grammar.getSuperGrammars().size());
assertEquals(12, grammar.getProds().size());
// AST
assertTrue(grammar.getAstNode().isPresent());
assertTrue(grammar.getAstNode().get() instanceof ASTMCGrammar);
assertSame(grammar.getEnclosingScope(), grammar.getAstNode().get().getEnclosingScope().get());
final MCProdSymbol stateChartProd = grammar.getProd("Statechart").orElse(null);
assertNotNull(stateChartProd);
assertEquals("Statechart", stateChartProd.getName());
assertEquals("de.monticore.statechart.Statechart.Statechart", stateChartProd.getFullName());
assertEquals("de.monticore.statechart", stateChartProd.getPackageName());
assertTrue(stateChartProd.getKind().isSame(MCProdSymbol.KIND));
assertTrue(stateChartProd.isStartProd());
assertTrue(stateChartProd.isClass());
// generic vs. specific
Optional<MCProdSymbol> resolvedStateChartProd = grammar.getSpannedScope().resolve("Statechart", MCProdSymbol.KIND);
assertTrue(resolvedStateChartProd.isPresent());
assertSame(stateChartProd, resolvedStateChartProd.get());
// AST
testLinkBetweenSymbolAndAst(stateChartProd);
final MCProdSymbol entryActionProd = grammar.getProd("EntryAction").orElse(null);
assertNotNull(entryActionProd);
assertEquals("EntryAction", entryActionProd.getName());
assertEquals("de.monticore.statechart.Statechart.EntryAction", entryActionProd.getFullName());
assertFalse(entryActionProd.isStartProd());
testLinkBetweenSymbolAndAst(entryActionProd);
// test prod components
assertEquals(3, entryActionProd.getProdComponents().size());
MCProdComponentSymbol prodComp = entryActionProd.getProdComponent("entry").orElse(null);
assertNotNull(prodComp);
assertEquals("entry", prodComp.getName());
assertEquals("", prodComp.getUsageName());
assertEquals("de.monticore.statechart.Statechart.EntryAction.entry", prodComp.getFullName());
assertEquals("de.monticore.statechart", prodComp.getPackageName());
assertTrue(prodComp.isTerminal());
assertFalse(prodComp.isList());
assertFalse(prodComp.isOptional());
assertSame(entryActionProd.getSpannedScope(), prodComp.getEnclosingScope());
// generic vs specific
Optional<MCProdComponentSymbol> resolvedProdComp = entryActionProd.getSpannedScope().resolve("entry", MCProdComponentSymbol.KIND);
assertTrue(resolvedProdComp.isPresent());
assertSame(prodComp, resolvedProdComp.get());
// AST
testLinkBetweenSymbolAndAst(prodComp);
prodComp = entryActionProd.getProdComponent(":").orElse(null);
assertNotNull(prodComp);
assertEquals(":", prodComp.getName());
assertEquals("", prodComp.getUsageName());
assertTrue(prodComp.isTerminal());
assertFalse(prodComp.isList());
assertFalse(prodComp.isOptional());
assertSame(entryActionProd.getSpannedScope(), prodComp.getEnclosingScope());
prodComp = entryActionProd.getProdComponent("block").orElse(null);
assertNotNull(prodComp);
assertEquals("block", prodComp.getName());
assertEquals("block", prodComp.getUsageName());
assertTrue(prodComp.isNonterminal());
assertTrue(prodComp.getAstNode().isPresent());
assertFalse(prodComp.isList());
assertFalse(prodComp.isOptional());
assertSame(entryActionProd.getSpannedScope(), prodComp.getEnclosingScope());
// reference to defining prod
assertEquals("BlockStatement", prodComp.getReferencedProd().get().getName());
assertTrue(prodComp.getReferencedProd().get().existsReferencedSymbol());
assertTrue(prodComp.getReferencedProd().get().getReferencedSymbol().isExternal());
MCProdSymbol scStructure = grammar.getProd("SCStructure").orElse(null);
assertNotNull(scStructure);
assertEquals("SCStructure", scStructure.getName());
assertTrue(scStructure.isInterface());
assertEquals(0, scStructure.getProdComponents().size());
testLinkBetweenSymbolAndAst(scStructure);
MCProdSymbol abstractAnything = grammar.getProd("AbstractAnything").orElse(null);
assertNotNull(abstractAnything);
assertEquals("AbstractAnything", abstractAnything.getName());
assertEquals("de.monticore.statechart.Statechart.AbstractAnything", abstractAnything.getFullName());
assertFalse(abstractAnything.isInterface());
assertFalse(abstractAnything.isSymbolDefinition());
assertEquals(0, abstractAnything.getProdComponents().size());
testLinkBetweenSymbolAndAst(abstractAnything);
final MCProdSymbol stateProd = grammar.getProd("State").orElse(null);
assertNotNull(stateProd);
assertEquals("State", stateProd.getName());
assertEquals("de.monticore.statechart.Statechart.State", stateProd.getFullName());
assertTrue(stateProd.isClass());
assertEquals(1, stateProd.getSuperInterfaceProds().size());
final MCProdSymbolReference superInterfaceScStructure = stateProd.getSuperInterfaceProds().get(0);
assertTrue(superInterfaceScStructure.existsReferencedSymbol());
assertSame(scStructure, superInterfaceScStructure.getReferencedSymbol());
// TODO PN generic resolving in super prod
// AST
testLinkBetweenSymbolAndAst(stateProd);
MCProdComponentSymbol initialComponent = stateProd.getProdComponent("initial").orElse(null);
assertNotNull(initialComponent);
assertEquals("de.monticore.statechart.Statechart.State.initial", initialComponent.getFullName());
assertEquals("initial", initialComponent.getName());
assertEquals("initial", initialComponent.getUsageName());
MCProdSymbol classBody = grammar.getProd("Classbody").orElse(null);
assertNotNull(classBody);
assertEquals("Classbody", classBody.getName());
assertEquals(0, classBody.getProdComponents().size());
assertTrue(classBody.isExternal());
assertFalse(classBody.isSymbolDefinition());
testLinkBetweenSymbolAndAst(classBody);
MCProdSymbol codeProd = grammar.getProd("Code").orElse(null);
assertNotNull(codeProd);
assertEquals("Code", codeProd.getName());
assertEquals(2, codeProd.getProdComponents().size());
prodComp = codeProd.getProdComponent("body").get();
assertEquals("body", prodComp.getUsageName());
assertTrue(prodComp.getReferencedProd().isPresent());
assertTrue(prodComp.getReferencedProd().get().existsReferencedSymbol());
assertSame(classBody, prodComp.getReferencedProd().get().getReferencedSymbol());
testLinkBetweenSymbolAndAst(codeProd);
}
private void testLinkBetweenSymbolAndAst(MCProdSymbol prodSymbol) {
assertTrue(prodSymbol.getAstNode().isPresent());
assertSame(prodSymbol, prodSymbol.getAstNode().get().getSymbol().get());
assertSame(prodSymbol.getEnclosingScope(), prodSymbol.getAstNode().get().getEnclosingScope().get());
if (prodSymbol.isClass()) {
assertTrue(prodSymbol.getAstNode().get() instanceof ASTClassProd);
}
else if (prodSymbol.isInterface()) {
assertTrue(prodSymbol.getAstNode().get() instanceof ASTInterfaceProd);
}
else if (prodSymbol.isAbstract()) {
assertTrue(prodSymbol.getAstNode().get() instanceof ASTAbstractProd);
}
else if (prodSymbol.isLexerProd()) {
assertTrue(prodSymbol.getAstNode().get() instanceof ASTLexProd);
}
else if (prodSymbol.isExternal()) {
assertTrue(prodSymbol.getAstNode().get() instanceof ASTExternalProd);
}
}
private void testLinkBetweenSymbolAndAst(MCProdComponentSymbol prodCompSymbol) {
assertTrue(prodCompSymbol.getAstNode().isPresent());
assertSame(prodCompSymbol, prodCompSymbol.getAstNode().get().getSymbol().get());
assertSame(prodCompSymbol.getEnclosingScope(), prodCompSymbol.getAstNode().get().getEnclosingScope().get());
}
@Test
public void testGrammarTypeReferences() {
final Scope globalScope = GrammarGlobalScopeTestFactory.createUsingEssentialMCLanguage();
MCGrammarSymbol grammar = globalScope.<MCGrammarSymbol>resolve("de.monticore.TypeReferences",
MCGrammarSymbol.KIND).orElse(null);
assertNotNull(grammar);
assertEquals(5, grammar.getProds().size());
MCProdSymbol c = grammar.getProd("C").orElse(null);
assertNotNull(c);
assertEquals("C", c.getName());
assertTrue(c.isInterface());
assertEquals(0, c.getProdComponents().size());
MCProdSymbol q = grammar.getProd("Q").orElse(null);
assertNotNull(q);
assertEquals("Q", q.getName());
assertTrue(q.isClass());
MCProdSymbol p = grammar.getProd("P").orElse(null);
assertNotNull(p);
}
@Test
public void testSuperGrammar() {
final Scope globalScope = GrammarGlobalScopeTestFactory.createUsingEssentialMCLanguage();
MCGrammarSymbol grammar = globalScope.<MCGrammarSymbol> resolve("de.monticore.statechart.sub.SubStatechart",
MCGrammarSymbol.KIND).orElse(null);
assertNotNull(grammar);
assertEquals("de.monticore.statechart.sub.SubStatechart", grammar.getFullName());
assertTrue(grammar.getStartProd().isPresent());
assertEquals(1, grammar.getSuperGrammars().size());
MCGrammarSymbolReference superGrammarRef = grammar.getSuperGrammars().get(0);
assertEquals("de.monticore.statechart.Statechart", superGrammarRef.getName());
assertTrue(superGrammarRef.existsReferencedSymbol());
testGrammarSymbolOfStatechart(superGrammarRef.getReferencedSymbol());
MCProdSymbol firstProd = grammar.getProd("First").orElse(null);
assertNotNull(firstProd);
assertTrue(firstProd.isStartProd());
assertSame(grammar.getStartProd().get(), firstProd);
MCProdSymbol secondProd = grammar.getProd("Second").orElse(null);
assertNotNull(secondProd);
assertFalse(secondProd.isStartProd());
assertEquals(2, grammar.getProdNames().size());
assertEquals(19, grammar.getProdsWithInherited().size());
// get prod of super grammar
assertFalse(grammar.getProd("State").isPresent());
final MCProdSymbol stateProd = grammar.getProdWithInherited("State").orElse(null);
assertNotNull(stateProd);
assertEquals("de.monticore.statechart.Statechart.State", stateProd.getFullName());
// generic vs. specific search in super grammar
Optional<MCProdSymbol> resolvedProd = grammar.getSpannedScope().resolve("State", MCProdSymbol.KIND);
assertTrue(resolvedProd.isPresent());
assertSame(stateProd, resolvedProd.get());
Optional<MCProdSymbol> resolvedProd2 = firstProd.getEnclosingScope().resolve("State", MCProdSymbol.KIND);
assertTrue(resolvedProd2.isPresent());
assertSame(stateProd, resolvedProd2.get());
}
@Test
public void testMontiCoreGrammar() {
final Scope globalScope = GrammarGlobalScopeTestFactory.createUsingEssentialMCLanguage();
MCGrammarSymbol grammar = globalScope.<MCGrammarSymbol> resolve("mc.grammars.TestGrammar",
MCGrammarSymbol.KIND).orElse(null);
assertNotNull(grammar);
assertEquals("mc.grammars.TestGrammar", grammar.getFullName());
assertEquals(3, countExternalProd(grammar));
assertEquals(5, countInterfaceAndAbstractProds(grammar));
assertEquals(1, grammar.getSuperGrammars().size());
final MCGrammarSymbolReference superGrammarRef = grammar.getSuperGrammars().get(0);
assertTrue(superGrammarRef.existsReferencedSymbol());
final String superGrammarFullName = superGrammarRef.getReferencedSymbol().getFullName();
assertEquals("mc.grammars.literals.TestLiterals", superGrammarFullName);
MCProdSymbol prod = grammar.getProdWithInherited("StringLiteral").orElse(null);
assertNotNull(prod);
assertEquals(superGrammarFullName + ".StringLiteral", prod.getFullName());
}
@Test
public void testNonTerminalsWithSameName() {
final Scope globalScope = GrammarGlobalScopeTestFactory.createUsingEssentialMCLanguage();
MCGrammarSymbol grammar = globalScope.<MCGrammarSymbol>resolve("de.monticore"
+ ".NonTerminalsWithSameName", MCGrammarSymbol.KIND).orElse(null);
assertNotNull(grammar);
assertEquals("de.monticore.NonTerminalsWithSameName", grammar.getFullName());
assertEquals(2, grammar.getProds().size());
MCProdSymbol transition = grammar.getProd("Transition").orElse(null);
assertNotNull(transition);
try {
Optional<MCProdComponentSymbol> r = transition.getSpannedScope().resolve("Arguments", MCProdComponentSymbol.KIND);
assertTrue(r.isPresent());
}
catch(ResolvedSeveralEntriesException e) {
fail("Only one prod component should be resolved instead of " + e.getSymbols().size());
}
}
private int countExternalProd(MCGrammarSymbol grammar) {
int num = 0;
for (MCProdSymbol rule : grammar.getProds()) {
if (rule.isExternal()) {
num++;
}
}
return num;
}
private int countInterfaceAndAbstractProds(MCGrammarSymbol grammar) {
int num = 0;
for (MCProdSymbol rule : grammar.getProds()) {
if (rule.isInterface() || rule.isAbstract()) {
num++;
}
}
return num;
}
@Test
public void testSymbolTableOfAutomaton() {
final Scope globalScope = GrammarGlobalScopeTestFactory.createUsingEssentialMCLanguage();
// test grammar symbol
MCGrammarSymbol grammar =
globalScope.<MCGrammarSymbol>resolve("Automaton", MCGrammarSymbol.KIND).orElse(null);
assertNotNull(grammar);
assertTrue(grammar.getAstNode().isPresent());
grammar.getSpannedScope().resolve("State", MCProdSymbol.KIND);
}
@Test
public void testRuleWithSymbolReference() {
final Scope globalScope = GrammarGlobalScopeTestFactory.createUsingEssentialMCLanguage();
MCGrammarSymbol grammar = globalScope.<MCGrammarSymbol>resolve("de.monticore"
+ ".RuleWithSymbolReference", MCGrammarSymbol.KIND).orElse(null);
assertNotNull(grammar);
assertEquals("de.monticore.RuleWithSymbolReference", grammar.getFullName());
assertEquals(7, grammar.getProds().size());
MCProdSymbol s = grammar.getProd("S").orElse(null);
assertNotNull(s);
assertTrue(s.isSymbolDefinition());
assertEquals("S", s.getProdDefiningSymbolKind().get().getName());
MCProdSymbol t = grammar.getProd("T").orElse(null);
assertTrue(t.isSymbolDefinition());
assertEquals("S", t.getProdDefiningSymbolKind().get().getName());
assertSame(s, t.getProdDefiningSymbolKind().get().getReferencedSymbol());
// The symbol kinds are determined transitively, i.e., A -> T -> S, hence, the symbol kind of prod A is S.
MCProdSymbol a = grammar.getProd("A").orElse(null);
assertTrue(a.isSymbolDefinition());
assertEquals("S", a.getSymbolDefinitionKind().get());
MCProdSymbol b = grammar.getProd("B").orElse(null);
assertFalse(b.isSymbolDefinition());
MCProdComponentSymbol aComponent = b.getProdComponent("an").get();
assertEquals("Name", aComponent.getReferencedProd().get().getName());
assertEquals(a.getName(), aComponent.getReferencedSymbolName().get());
MCProdSymbol e = grammar.getProd("E").orElse(null);
assertTrue(e.isExternal());
assertTrue(e.isSymbolDefinition());
MCProdSymbol r = grammar.getProd("R").orElse(null);
assertTrue(r.isAbstract());
assertFalse(r.isInterface());
assertTrue(r.isSymbolDefinition());
}
}