/*
* ******************************************************************************
* 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.codegen.mc2cd;
import static com.google.common.collect.Sets.newLinkedHashSet;
import static de.se_rwth.commons.Util.listTillNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import de.monticore.ModelingLanguage;
import de.monticore.ast.ASTNode;
import de.monticore.codegen.GeneratorHelper;
import de.monticore.grammar.HelperGrammar;
import de.monticore.grammar.RegExpBuilder;
import de.monticore.grammar.grammar._ast.ASTAttributeInAST;
import de.monticore.grammar.grammar._ast.ASTConstant;
import de.monticore.grammar.grammar._ast.ASTConstantGroup;
import de.monticore.grammar.grammar._ast.ASTLexActionOrPredicate;
import de.monticore.grammar.grammar._ast.ASTLexProd;
import de.monticore.grammar.grammar._ast.ASTMCGrammar;
import de.monticore.grammar.prettyprint.Grammar_WithConceptsPrettyPrinter;
import de.monticore.grammar.symboltable.MCGrammarSymbol;
import de.monticore.grammar.symboltable.MCProdAttributeSymbol;
import de.monticore.grammar.symboltable.MCProdComponentSymbol;
import de.monticore.grammar.symboltable.MCProdSymbol;
import de.monticore.grammar.symboltable.MCProdSymbolReference;
import de.monticore.grammar.symboltable.MontiCoreGrammarLanguage;
import de.monticore.grammar.symboltable.MontiCoreGrammarSymbolTableCreator;
import de.monticore.io.paths.ModelPath;
import de.monticore.prettyprint.IndentPrinter;
import de.monticore.symboltable.GlobalScope;
import de.monticore.symboltable.MutableScope;
import de.monticore.symboltable.ResolvingConfiguration;
import de.monticore.symboltable.Scope;
import de.monticore.symboltable.ScopeSpanningSymbol;
import de.monticore.symboltable.Symbol;
import de.se_rwth.commons.StringTransformations;
import de.se_rwth.commons.Util;
import de.se_rwth.commons.logging.Log;
public class MCGrammarSymbolTableHelper {
public static void initializeSymbolTable(ASTMCGrammar rootNode, ModelPath modelPath) {
ModelingLanguage grammarLanguage = new MontiCoreGrammarLanguage();
ResolvingConfiguration resolvingConfiguration = new ResolvingConfiguration();
resolvingConfiguration.addDefaultFilters(grammarLanguage.getResolvingFilters());
Grammar_WithConceptsPrettyPrinter prettyPrinter = new Grammar_WithConceptsPrettyPrinter(
new IndentPrinter());
MutableScope globalScope = new GlobalScope(modelPath, grammarLanguage, resolvingConfiguration);
MontiCoreGrammarSymbolTableCreator symbolTableCreator = new MontiCoreGrammarSymbolTableCreator(
resolvingConfiguration, globalScope, prettyPrinter);
// Create Symbol Table
symbolTableCreator.createFromAST(rootNode);
}
public static Optional<MCProdSymbol> resolveRule(ASTNode astNode, String name) {
Optional<MCGrammarSymbol> grammarSymbol = getMCGrammarSymbol(astNode);
if (!grammarSymbol.isPresent()) {
return Optional.empty();
}
return grammarSymbol.get().getProdWithInherited(name);
}
public static Optional<MCProdSymbol> resolveRuleInSupersOnly(ASTNode astNode, String name) {
Optional<MCGrammarSymbol> grammarSymbol = getMCGrammarSymbol(astNode);
Stream<MCGrammarSymbol> superGrammars = grammarSymbol
.map(symbol -> Util.preOrder(symbol, MCGrammarSymbol::getSuperGrammarSymbols)
.stream())
.orElse(Stream.empty()).skip(1);
return superGrammars.map(superGrammar -> superGrammar.getProd(name))
.filter(mcRuleSymbol -> mcRuleSymbol.isPresent())
.map(Optional::get)
.findFirst();
}
public static Optional<MCGrammarSymbol> getGrammarSymbol(ASTMCGrammar astNode) {
if (!astNode.getSymbol().isPresent()) {
return Optional.empty();
}
if (!(astNode.getSymbol().get() instanceof MCGrammarSymbol)) {
return Optional.empty();
}
return Optional.of((MCGrammarSymbol) astNode.getSymbol().get());
}
public static Optional<MCGrammarSymbol> getMCGrammarSymbol(ASTNode astNode) {
Set<Scope> scopes = getAllScopes(astNode);
for (Scope s : scopes) {
Optional<? extends ScopeSpanningSymbol> symbol = s.getSpanningSymbol();
if (symbol.isPresent() && symbol.get() instanceof MCGrammarSymbol) {
return Optional.of((MCGrammarSymbol) symbol.get());
}
}
return Optional.empty();
// return getAllScopes(astNode).stream()
// .map(Scope::getSpanningSymbol)
// .filter(Optional::isPresent)
// .map(Optional::get)
// .filter(EssentialMCGrammarSymbol.class::isInstance)
// .map(EssentialMCGrammarSymbol.class::cast)
// .findFirst();
}
public static Optional<MCProdSymbol> getEnclosingRule(ASTNode astNode) {
return getAllScopes(astNode).stream()
.map(Scope::getSpanningSymbol)
.filter(Optional::isPresent)
.map(Optional::get)
.filter(MCProdSymbol.class::isInstance)
.map(MCProdSymbol.class::cast)
.findFirst();
}
public static Optional<MCProdSymbol> getEnclosingRule(MCProdComponentSymbol prod) {
return prod.getEnclosingScope().getSpanningSymbol().filter(MCProdSymbol.class::isInstance)
.map(MCProdSymbol.class::cast);
}
/**
* Returns a set of all super grammars of the given grammar (transitively)
*
* @return
*/
public static Set<MCGrammarSymbol> getAllSuperGrammars(
MCGrammarSymbol grammarSymbol) {
Set<MCGrammarSymbol> allSuperGrammars = new LinkedHashSet<>();
Set<MCGrammarSymbol> tmpList = new LinkedHashSet<>();
allSuperGrammars.addAll(grammarSymbol.getSuperGrammarSymbols());
boolean modified = false;
do {
for (MCGrammarSymbol curGrammar : allSuperGrammars) {
tmpList.addAll(curGrammar.getSuperGrammarSymbols());
}
modified = allSuperGrammars.addAll(tmpList);
tmpList.clear();
} while (modified);
return ImmutableSet.copyOf(allSuperGrammars);
}
public static boolean isFragment(Optional<ASTNode> astNode) {
return !astNode.isPresent() || !(astNode.get() instanceof ASTLexProd)
|| ((ASTLexProd) astNode.get()).isFragment();
}
private static Set<Scope> getAllScopes(ASTNode astNode) {
Set<Scope> ret = Sets.newHashSet();
for (Symbol s : getAllSubSymbols(astNode)) {
for (Scope l : listTillNull(s.getEnclosingScope(),
childScope -> childScope.getEnclosingScope().orElse(null))) {
ret.add(l);
}
}
return ret;
// return getAllSubSymbols(astNode).stream()
// .map(Symbol::getEnclosingScope)
// .flatMap(
// scope -> listTillNull(scope, childScope ->
// childScope.getEnclosingScope().orElse(null))
// .stream())
// .collect(Collectors.toSet());
}
private static String getLexString(MCGrammarSymbol grammar, ASTLexProd lexNode) {
StringBuilder builder = new StringBuilder();
RegExpBuilder regExp = new RegExpBuilder(builder, grammar);
regExp.handle(lexNode);
return builder.toString();
}
public static Optional<Pattern> calculateLexPattern(MCGrammarSymbol grammar,
Optional<ASTNode> lexNode) {
Optional<Pattern> ret = Optional.empty();
if (!lexNode.isPresent() || !(lexNode.get() instanceof ASTLexProd)) {
return ret;
}
final String lexString = getLexString(grammar, (ASTLexProd) lexNode.get());
try {
if ("[[]".equals(lexString)) {
return Optional.ofNullable(Pattern.compile("[\\[]"));
}
else {
return Optional.ofNullable(Pattern.compile(lexString));
}
}
catch (PatternSyntaxException e) {
Log.error("0xA0913 Internal error with pattern handling for lex rules. Pattern: " + lexString
+ "\n", e);
}
return ret;
}
private static Set<Symbol> getAllSubSymbols(ASTNode astNode) {
Set<Symbol> symbols = Util.preOrder(astNode, ASTNode::get_Children).stream()
.map(ASTNode::getSymbol)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toSet());
return symbols;
// return Util.preOrder(astNode, ASTNode::get_Children).stream()
// .map(ASTNode::getSymbol)
// .filter(Optional::isPresent)
// .map(Optional::get)
// .collect(Collectors.toSet());
}
// TODO GV:
/**
* Returns the STType associated with this name Use "super." as a prefix to
* explicitly access the type of supergrammars this grammar overriddes.
* Native/external types are types that are not defined in the grammar but are
* refered from it. These types are indicated by the suffix "/" in the grammar
* and refer to regular Java types. To access these type use the prefix "/"
* e.g. "/String" or "/int"
*
* @param name Name of the type
* @return Symboltable entry for this type
*/
public static Optional<MCProdSymbol> getTypeWithInherited(String name,
MCGrammarSymbol gramamrSymbol) {
Optional<MCProdSymbol> ret = Optional.empty();
if (name.startsWith("super.")) {
name = name.substring(6);
}
else {
ret = gramamrSymbol.getProd(name);
}
if (!ret.isPresent()) {
ret = gramamrSymbol.getProdWithInherited(name);
}
return ret;
}
/**
* @return the qualified name for this type
*/
// TODO GV: change implementation
public static String getQualifiedName(MCProdSymbol symbol) {
if (!symbol.getAstNode().isPresent()) {
return "UNKNOWN_TYPE";
}
if (symbol.isLexerProd()) {
return getLexType(symbol.getAstNode());
}
if (symbol.isEnum()) {
return getQualifiedName(symbol.getAstNode().get(), symbol, GeneratorHelper.AST_PREFIX, "");
// return "int";
// TODO GV:
// return getConstantType();
}
return getQualifiedName(symbol.getAstNode().get(), symbol, GeneratorHelper.AST_PREFIX, "");
}
public static String getDefaultValue(MCProdSymbol symbol) {
if (getQualifiedName(symbol).equals("int")) {
return "0";
}
else if (getQualifiedName(symbol).equals("boolean")) {
return "false";
}
else {
return "null";
}
}
private static String getLexType(Optional<ASTNode> node) {
if (node.isPresent()) {
if (node.get() instanceof ASTLexProd) {
return HelperGrammar.createConvertType((ASTLexProd) node.get());
}
if (node.get() instanceof ASTLexActionOrPredicate) {
return "String";
}
}
return "UNKNOWN_TYPE";
}
public static String getQualifiedName(ASTNode astNode, MCProdSymbol symbol, String prefix,
String suffix) {
if (symbol.isExternal()) {
return symbol.getName();
}
else {
Optional<MCGrammarSymbol> grammarSymbol = getMCGrammarSymbol(astNode);
String string = (grammarSymbol.isPresent()
? grammarSymbol.get().getFullName().toLowerCase()
: "")
+ GeneratorHelper.AST_DOT_PACKAGE_SUFFIX_DOT + prefix +
StringTransformations.capitalize(symbol.getName() + suffix);
if (string.startsWith(".")) {
string = string.substring(1);
}
return string;
}
}
public static Optional<String> getConstantName(MCProdComponentSymbol compSymbol) {
if (compSymbol.isConstantGroup() && compSymbol.getAstNode().isPresent()
&& compSymbol.getAstNode().get() instanceof ASTConstantGroup) {
return getConstantGroupName((ASTConstantGroup) compSymbol.getAstNode().get());
}
if (compSymbol.isConstant() && compSymbol.getAstNode().isPresent()
&& compSymbol.getAstNode().get() instanceof ASTConstant) {
return Optional.of(
HelperGrammar.getAttributeNameForConstant((ASTConstant) compSymbol.getAstNode().get()));
}
return Optional.empty();
}
public static Optional<String> getConstantGroupName(ASTConstantGroup ast) {
// setAttributeMinMax(a.getIteration(), att);
if (ast.getUsageName().isPresent()) {
return ast.getUsageName();
}
// derive attribute name from constant entry (but only if we have
// one entry!)
else if (ast.getConstants().size() == 1) {
return Optional.of(HelperGrammar.getAttributeNameForConstant(ast.getConstants().get(0)));
}
Log.error("0xA2345 The name of the constant group could't be ascertained",
ast.get_SourcePositionStart());
return Optional.empty();
}
public void addEnum(String name, String constant) {
// List of enum values for this type
Map<String, Set<String>> possibleValuesForEnum = new HashMap<>();
List<String> enumValues = new ArrayList<>();
Set<String> constantsInGrammar = possibleValuesForEnum.get(name.intern());
if (constantsInGrammar == null) {
constantsInGrammar = new LinkedHashSet<>();
possibleValuesForEnum.put(name.intern(), constantsInGrammar);
}
constantsInGrammar.add(constant.intern());
if (!enumValues.contains(name.intern())) {
enumValues.add(name.intern());
}
}
/**
* TODO: Write me!
*
* @param astNode
* @param currentSymbol
* @return
*/
public static Optional<String> getConstantName(ASTConstantGroup astNode,
Optional<? extends ScopeSpanningSymbol> currentSymbol) {
Optional<String> constName = getConstantGroupName(astNode);
if (!currentSymbol.isPresent() || !(currentSymbol.get() instanceof MCProdSymbol)
|| !constName.isPresent()) {
return constName;
}
return Optional.of(currentSymbol.get().getName() + "." + getConstantGroupName(astNode).get());
}
public static Set<MCProdSymbol> getAllSuperProds(MCProdSymbol prod) {
Set<MCProdSymbol> supersHandled = new LinkedHashSet<>();
List<MCProdSymbol> supersToHandle = new ArrayList<>();
supersToHandle.addAll(getSuperProds(prod));
Set<MCProdSymbol> supersNextRound = new LinkedHashSet<>();
while (!supersToHandle.isEmpty()) {
for (MCProdSymbol superType : supersToHandle) {
if (!supersHandled.contains(superType)) {
supersNextRound.addAll(getSuperProds(superType));
}
supersHandled.add(superType);
}
supersToHandle.clear();
supersToHandle.addAll(supersNextRound);
supersNextRound.clear();
}
return ImmutableSet.copyOf(supersHandled);
}
public static Set<MCProdSymbol> getAllSuperInterfaces(MCProdSymbol prod) {
return getAllSuperProds(prod).stream().filter(p -> p.isInterface()).collect(Collectors.toSet());
}
/**
* TODO: Write me!
*
* @param superType
* @return
*/
public static List<MCProdSymbol> getSuperProds(MCProdSymbol prod) {
List<MCProdSymbol> superTypes = prod.getSuperProds().stream().map(s -> s.getReferencedSymbol())
.collect(Collectors.toList());
superTypes.addAll(prod.getSuperInterfaceProds().stream().map(s -> s.getReferencedSymbol())
.collect(Collectors.toList()));
superTypes.addAll(prod.getAstSuperClasses().stream().filter(s -> s.isProdRef())
.map(s -> s.getProdRef().getReferencedSymbol()).collect(Collectors.toList()));
superTypes.addAll(prod.getAstSuperInterfaces().stream().filter(s -> s.isProdRef())
.map(s -> s.getProdRef().getReferencedSymbol()).collect(Collectors.toList()));
return ImmutableList.copyOf(superTypes);
}
public static boolean isSubtype(MCProdSymbol subType, MCProdSymbol superType) {
return isSubtype(subType, superType, newLinkedHashSet(Arrays.asList(subType)));
}
private static boolean isSubtype(MCProdSymbol subType, MCProdSymbol superType,
Set<MCProdSymbol> handledTypes) {
if (areSameTypes(subType, superType)) {
return true;
}
// Try to find superType in super types of this type
final Collection<MCProdSymbol> allSuperTypes = getAllSuperProds(subType);
if (allSuperTypes.contains(superType)) {
return true;
}
// check transitive sub-type relation
for (MCProdSymbol t : allSuperTypes) {
if (handledTypes.add(superType)) {
boolean subtypeOf = isSubtype(t, superType, handledTypes);
if (subtypeOf) {
return true;
}
}
}
return false;
}
public static boolean areSameTypes(MCProdSymbol type1, MCProdSymbol type2) {
Log.errorIfNull(type1);
Log.errorIfNull(type2);
if (type1 == type2) {
return true;
}
if (!type1.getKind().equals(type2.getKind())) {
return false;
}
return type1.getFullName().equals(type2.getFullName());
// if (!type1.getName().equals(type2.getName())) {
// return false;
// }
// // TODO NN <- PN needed?
// // if (getOriginalLanguage() != type2.getOriginalLanguage()) {
// // return false;
// // }
//
// return
// type1.getGrammarSymbol().getFullName().equals(type2.getGrammarSymbol().getFullName());
}
public static boolean isAssignmentCompatibleOrUndecidable(MCProdSymbol subType,
MCProdSymbol superType) {
return isAssignmentCompatibleOrUndecidable(subType, superType,
newLinkedHashSet(Arrays.asList(subType)));
}
/**
* Returns the type of the collection <code>types</code> that is the sub type
* of all other types in this collection. Else, null is returned.
*
* @param types Collection of types
* @return type that is subtype of all other types or null.
*/
public static Optional<MCProdSymbol> findLeastType(Collection<MCProdSymbol> types) {
for (MCProdSymbol t1 : types) {
boolean isLeastType = true;
for (MCProdSymbol t2 : types) {
if (!isSubtype(t2, t1) && !areSameTypes(t2, t1)) {
isLeastType = false;
break;
}
}
if (isLeastType) {
return Optional.of(t1);
}
}
return Optional.empty();
}
public static boolean isAssignmentCompatibleOrUndecidable(MCProdSymbol subType,
MCProdSymbol superType, Set<MCProdSymbol> handledTypes) {
// Return true if this type or the other type are both external
// TODO GV: check, wenn Java angebunden
if (subType.isExternal()
|| superType.isExternal()) {
return true;
}
// Return true if this type and the other type are the same
if (areSameTypes(subType, superType)) {
return true;
}
// if ((subType.getKindOfType() == KindType.IDENT) &&
// (superType.getKindOfType() == KindType.IDENT)) {
// return subType.getLexType().equals(superType.getLexType());
// }
// Try to find superType in supertypes of this type
Collection<MCProdSymbol> allSuperTypes = getAllSuperProds(subType);
if (allSuperTypes.contains(superType)) {
return true;
}
// check transitive sub-type relation
for (MCProdSymbol t : allSuperTypes) {
if (handledTypes.add(superType)) {
boolean subtypeOf = isAssignmentCompatibleOrUndecidable(t, superType, handledTypes);
if (subtypeOf) {
return true;
}
}
}
return false;
}
/**
* TODO: Write me!
*
* @param mcProdSymbolReference
* @param newOne
* @return
*/
public static boolean isSubType(MCProdSymbolReference ref1, MCProdSymbolReference ref2) {
MCProdSymbol type1 = ref1.getReferencedSymbol();
MCProdSymbol type2 = ref2.getReferencedSymbol();
return areSameTypes(type1, type2) || isSubtype(type1, type2) || isSubtype(type2, type1);
}
/**
* TODO: Write me!
*
* @param prodComponent
* @return
*/
public static boolean isConstGroupIterated(MCProdComponentSymbol prodComponent) {
Preconditions.checkArgument(prodComponent.isConstantGroup());
if (!prodComponent.isList() && prodComponent.getSubProdComponents().size() <= 1) {
return false;
}
prodComponent.getSubProdComponents();
Collection<String> set = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
for (MCProdComponentSymbol component : prodComponent.getSubProdComponents()) {
set.add(component.getName());
}
return set.size() > 1;
}
public static boolean isAttributeDerived(MCProdAttributeSymbol attrSymbol) {
return attrSymbol.getAstNode().isPresent()
&& attrSymbol.getAstNode().get() instanceof ASTAttributeInAST
&& isAttributeDerived((ASTAttributeInAST) attrSymbol.getAstNode().get());
}
/**
* TODO: Write me!
*
* @param ast
* @return
*/
public static boolean isAttributeDerived(ASTAttributeInAST ast) {
return ast.isDerived() && ast.getBody().isPresent();
}
public static boolean isAttributeIterated(MCProdAttributeSymbol attrSymbol) {
return attrSymbol.getAstNode().isPresent()
&& attrSymbol.getAstNode().get() instanceof ASTAttributeInAST
&& isAttributeIterated((ASTAttributeInAST) attrSymbol.getAstNode().get());
}
/**
* TODO: Write me!
*
* @param ast
* @return
*/
public static boolean isAttributeIterated(ASTAttributeInAST ast) {
if (!ast.getCard().isPresent()) {
return false;
}
if (ast.getCard().get().isUnbounded()) {
return true;
}
Optional<Integer> max = getMax(ast);
return max.isPresent() && (max.get() == GeneratorHelper.STAR || max.get() > 1);
}
public static Optional<Integer> getMax(MCProdAttributeSymbol attrSymbol) {
if (!attrSymbol.getAstNode().isPresent()
|| !(attrSymbol.getAstNode().get() instanceof ASTAttributeInAST)) {
return Optional.empty();
}
return getMax((ASTAttributeInAST) attrSymbol.getAstNode().get());
}
public static Optional<Integer> getMax(ASTAttributeInAST ast) {
if (ast.getCard().isPresent()
&& ast.getCard().get().getMax().isPresent()) {
String max = ast.getCard().get().getMax().get();
if ("*".equals(max)) {
return Optional.of(GeneratorHelper.STAR);
}
else {
try {
int x = Integer.parseInt(max);
return Optional.of(x);
}
catch (NumberFormatException ignored) {
Log.warn("0xA0140 Failed to parse an integer value of max of ASTAttributeInAST "
+ ast.getName() + " from string " + max);
}
}
}
return Optional.empty();
}
public static Optional<Integer> getMin(MCProdAttributeSymbol attrSymbol) {
if (!attrSymbol.getAstNode().isPresent()
|| !(attrSymbol.getAstNode().get() instanceof ASTAttributeInAST)) {
return Optional.empty();
}
return getMin((ASTAttributeInAST) attrSymbol.getAstNode().get());
}
public static Optional<Integer> getMin(ASTAttributeInAST ast) {
if (ast.getCard().isPresent()
&& ast.getCard().get().getMin().isPresent()) {
String min = ast.getCard().get().getMin().get();
try {
int x = Integer.parseInt(min);
return Optional.of(x);
}
catch (NumberFormatException ignored) {
Log.warn("0xA0141 Failed to parse an integer value of max of ASTAttributeInAST "
+ ast.getName() + " from string " + min);
}
}
return Optional.empty();
}
// public String getListType() {
// if (isASTNode()) {
// return getQualifiedName() + "List";
// }
// else {
// return "java.util.List<" + getQualifiedName() + ">";
// }
// }
// private String getConstantType() {
// return (this.getEnumValues().size() > 1) ? "int" : "boolean";
// }
}