/* * ****************************************************************************** * 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.symboltable; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.common.base.Strings.nullToEmpty; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.regex.Pattern; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import de.monticore.ast.ASTNode; import de.monticore.codegen.GeneratorHelper; import de.monticore.codegen.cd2java.visitor.VisitorGeneratorHelper; import de.monticore.codegen.mc2cd.MCGrammarSymbolTableHelper; import de.monticore.grammar.grammar._ast.ASTMCGrammar; import de.monticore.grammar.symboltable.MCGrammarSymbol; import de.monticore.grammar.symboltable.MCProdComponentSymbol; import de.monticore.grammar.symboltable.MCProdSymbol; import de.monticore.io.paths.IterablePath; import de.monticore.symboltable.GlobalScope; import de.monticore.umlcd4a.cd4analysis._ast.ASTCDCompilationUnit; import de.monticore.umlcd4a.symboltable.CDSymbol; import de.se_rwth.commons.JavaNamesHelper; import de.se_rwth.commons.Names; import de.se_rwth.commons.logging.Log; /** * @author Pedram Mir Seyed Nazari */ public class SymbolTableGeneratorHelper extends GeneratorHelper { public static final String NAME_NONTERMINAL = "Name"; private final String qualifiedGrammarName; private final ASTMCGrammar astGrammar; private final MCGrammarSymbol grammarSymbol; // TODO PN refactor public SymbolTableGeneratorHelper( ASTMCGrammar ast, GlobalScope globalScope, ASTCDCompilationUnit astCd) { super(astCd, globalScope); Log.errorIfNull(ast); this.astGrammar = ast; this.qualifiedGrammarName = astGrammar.getPackage().isEmpty() ? astGrammar.getName() : Joiner.on('.').join(Names.getQualifiedName(astGrammar.getPackage()), astGrammar.getName()); grammarSymbol = globalScope.<MCGrammarSymbol> resolve( qualifiedGrammarName, MCGrammarSymbol.KIND).orElse(null); Log.errorIfNull(grammarSymbol, "0xA4036 Grammar " + qualifiedGrammarName + " can't be resolved in the scope " + globalScope); checkState(qualifiedGrammarName.equals(grammarSymbol.getFullName())); } public MCGrammarSymbol getGrammarSymbol() { return grammarSymbol; } /** * @return the package for the generated symbol table files */ public String getTargetPackage() { return getQualifiedGrammarName().toLowerCase() + "." + SymbolTableGenerator.PACKAGE; } /** * @return the qualified grammar's name */ public String getQualifiedGrammarName() { return qualifiedGrammarName; } public static String getQualifiedSymbolType(String packageName, String symbolName) { return getPackageName(packageName, SymbolTableGenerator.PACKAGE) + "." + symbolName; } /** * @return the name of the top ast, i.e., the ast of the start rule. */ public String getQualifiedStartRuleName() { if (grammarSymbol.getStartProd().isPresent()) { return MCGrammarSymbolTableHelper .getQualifiedName(grammarSymbol.getStartProd().get()); } return ""; } public boolean isStartRule(MCProdSymbol ruleSymbol) { return grammarSymbol.getStartProd().isPresent() && grammarSymbol.getStartProd().get().equals(ruleSymbol); } /** * @return all rules using the nonterminal <code>Name</code>. If a usage name * is specified, it must be <code>name</code> (case insenstive), e.g. * <code>name:Name</code> or <code>Name:Name</code>. */ public Collection<MCProdSymbol> getAllSymbolDefiningRules() { final Set<MCProdSymbol> ruleSymbolsWithName = new LinkedHashSet<>(); for (final MCProdSymbol rule : grammarSymbol.getProds()) { if (rule.isSymbolDefinition()) { ruleSymbolsWithName.add(rule); } } return ImmutableList.copyOf(ruleSymbolsWithName); } public Collection<MCProdSymbol> getAllScopeSpanningRules() { final Set<MCProdSymbol> rules = new LinkedHashSet<>(); for (final MCProdSymbol rule : grammarSymbol.getProds()) { if (!rule.isSymbolDefinition() && spansScope(rule)) { rules.add(rule); } } return ImmutableList.copyOf(rules); } public Map<String, String> ruleComponents2JavaFields(MCProdSymbol ruleSymbol) { Log.errorIfNull(ruleSymbol); // fieldName -> fieldType final Map<String, String> fields = new HashMap<>(); for (MCProdComponentSymbol componentSymbol : ruleSymbol.getProdComponents()) { checkArgument(componentSymbol.getAstNode().isPresent()); if (componentSymbol.isNonterminal()) { nonterminal2JavaField(componentSymbol, fields); } else if (componentSymbol.isConstant()) { constant2JavaField(componentSymbol, fields); } else if (componentSymbol.isConstantGroup()) { String attrName = MCGrammarSymbolTableHelper.getConstantName(componentSymbol).orElse(""); if (canBeTransformedToValidJavaName(attrName)) { boolean iterated = MCGrammarSymbolTableHelper.isConstGroupIterated(componentSymbol); String constGroupType = iterated? "int" : "boolean"; fields.put(attrName, constGroupType); } } else if (componentSymbol.isTerminal()) { // ignore terminals } else { // TODO PN remove this exception throw new RuntimeException( "0xA4078 TODO PN implement in " + SymbolTableGeneratorHelper.class.getSimpleName()); } } return fields; } private void nonterminal2JavaField(MCProdComponentSymbol componentSymbol, Map<String, String> fields) { final Optional<String> componentName = getRuleComponentName(componentSymbol); if (componentName.isPresent()) { if (componentSymbol.isSymbolReference()) { // the case: Transition = from:Name@State ..., i.e., a reference to // another symbol fields.put(componentName.get(), componentSymbol.getReferencedSymbolName().get() + "Symbol"); } } // TODO PN else, do something? } private void constant2JavaField(MCProdComponentSymbol componentSymbol, Map<String, String> fields) { final Optional<String> componentName = getRuleComponentName(componentSymbol); if (componentName.isPresent()) { fields.put(componentName.get(), "boolean"); } } private Optional<String> getRuleComponentName(MCProdComponentSymbol componentSymbol) { if (canBeTransformedToValidJavaName(componentSymbol.getName())) { return Optional.of(componentSymbol.getName()); } else if (canBeTransformedToValidJavaName(componentSymbol.getUsageName())) { return Optional.of(componentSymbol.getUsageName()); } return Optional.empty(); } public Map<String, String> symbolRuleComponents2JavaFields(MCProdSymbol ruleSymbol) { Log.errorIfNull(ruleSymbol); // fieldName -> fieldType final Map<String, String> fields = new HashMap<>(); for (MCProdComponentSymbol componentSymbol : ruleSymbol.getProdComponents()) { checkArgument(componentSymbol.getAstNode().isPresent()); if (componentSymbol.isNonterminal()) { symbolNonTerminal2JavaField(componentSymbol, fields); } } return fields; } private void symbolNonTerminal2JavaField(MCProdComponentSymbol componentSymbol, Map<String, String> fields) { final Optional<String> componentName = getRuleComponentName(componentSymbol); if (componentName.isPresent() && componentSymbol.getReferencedProd().isPresent()) { // the case: Automaton = Name ... State* ..., i.e., the containment of // another symbol final Optional<MCProdSymbol> referencedRule = grammarSymbol .getProd(componentSymbol.getReferencedProd().get().getName()); if (referencedRule.isPresent() && referencedRule.get().isSymbolDefinition()) { fields.put(componentName.get(), referencedRule.get().getName() + "Symbol"); } } } public Map<String, String> symbolReferenceRuleComponents2JavaFields(MCProdSymbol ruleSymbol) { Log.errorIfNull(ruleSymbol); // fieldName -> fieldType final Map<String, String> fields = new HashMap<>(); for (MCProdComponentSymbol componentSymbol : ruleSymbol.getProdComponents()) { checkArgument(componentSymbol.getAstNode().isPresent()); if (componentSymbol.isNonterminal()) { nonterminal2JavaField(componentSymbol, fields); } } return fields; } public Map<String, String> ruleComponentsWithoutSymbolReferences2JavaFields( final MCProdSymbol ruleSymbol) { final Map<String, String> all = ruleComponents2JavaFields(ruleSymbol); final Map<String, String> symbolReferences = symbolReferenceRuleComponents2JavaFields( ruleSymbol); final Map<String, String> withoutSymbolReferences = new LinkedHashMap<>(); for (Map.Entry<String, String> entry : all.entrySet()) { if (!symbolReferences.containsKey(entry.getKey())) { withoutSymbolReferences.put(entry.getKey(), entry.getValue()); } } return withoutSymbolReferences; } public Map<String, String> nonSymbolFields(MCProdSymbol ruleSymbol) { return null; // TODO PN implement } public static String getterPrefix(final String type) { if ("boolean".equals(type) || "Boolean".equals(type)) { return "is"; } return "get"; } /** * Returns true, if <code>name</code> is a valid Java name or can be * transformed to a valid Java name using * {@link JavaNamesHelper#getNonReservedName(String)}. For example, * <code>final</code> is no valid Java name, but can be transformed to one, * e.g., <code>r__final</code>. However, <code>+-</code> is neither a valid * Java nor can it be transformed to one. * * @param name * @return true, if <code>name</code> is a valid Java name or can be * transformed to a valid Java name using * {@link JavaNamesHelper#getNonReservedName(String)}. */ private boolean canBeTransformedToValidJavaName(String name) { return isValidName(nonReservedName(name)); } public static String nonReservedName(final String name) { return JavaNamesHelper.getNonReservedName(name); } // TODO PN move to JavaNamesHelper private static final Set<String> KEYWORDS = new LinkedHashSet<>(Arrays.asList( "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized", "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte", "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch", "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally", "long", "strictfp", "volatile", "const", "float", "native", "super", "while")); private static final Pattern JAVA_SIMPLE_NAME_PATTERN = Pattern .compile("[A-Za-z_$]+[a-zA-Z0-9_$]*"); public static boolean isValidName(final String name) { if ("".equals(nullToEmpty(name))) { return false; } return !KEYWORDS.contains(name) && JAVA_SIMPLE_NAME_PATTERN.matcher(name).matches(); } public static String getGeneratedErrorCode(ASTNode ast) { return GeneratorHelper.getGeneratedErrorCode(ast); } // TODO refactor public String getVisitorType() { return VisitorGeneratorHelper.getVisitorType(getCdName()); } // TODO refactor public String getCommonDelegatorVisitorType() { return "Common" + VisitorGeneratorHelper.getDelegatorVisitorType(getCdName()); } // TODO refactor public String getDelegatorVisitorType() { return VisitorGeneratorHelper.getDelegatorVisitorType(getCdName()); } // TODO refactor public static String getQualifiedSymTabCreatorType(String packageName, String cdName) { return getPackageName(packageName, getVisitorPackageSuffix()) + "." + getSymTabCreatorType(cdName); } // TODO refactor public static String getSymTabCreatorType(String cdName) { return cdName + "SymbolTableCreator"; } // TODO refactor public String getQualifiedVisitorNameAsJavaName(CDSymbol cd) { return VisitorGeneratorHelper.qualifiedJavaTypeToName(getQualifiedVisitorType(cd)); } // TODO refactor public static String getQualifiedSymTabCreator(String qualifiedLanguageName) { String packageName = getCdPackage(qualifiedLanguageName); String cdName = getCdName(qualifiedLanguageName); return getQualifiedSymTabCreatorType(packageName, cdName); } // TODO refactor public String getQualifiedVisitorType(CDSymbol cd) { return VisitorGeneratorHelper.getQualifiedVisitorType(cd.getFullName()); } public boolean spansScope(final MCProdSymbol rule) { for (MCProdComponentSymbol ruleComponent : rule.getProdComponents()) { if (ruleComponent.getReferencedProd().isPresent()) { // the case: Automaton = Name ... State* ..., i.e., the containment of // another symbol final Optional<MCProdSymbol> referencedRule = getGrammarSymbol() .getProd(ruleComponent.getReferencedProd().get().getName()); if (referencedRule.isPresent() && referencedRule.get().isSymbolDefinition()) { return true; } } } return false; } public boolean isSymbol(final MCProdSymbol rule) { return rule.isSymbolDefinition(); } public boolean isScopeSpanningSymbol(final MCProdSymbol rule) { return isSymbol(rule) && spansScope(rule); } public boolean isNamed(final MCProdSymbol rule) { for (MCProdComponentSymbol comp : rule.getProdComponents()) { // TODO check full name? if (comp.getName().equals(NAME_NONTERMINAL) && (isNullOrEmpty(comp.getUsageName()) || comp.getUsageName().equalsIgnoreCase(NAME_NONTERMINAL))) { return true; } } return false; } public boolean existsHandwrittenSymbolClass(MCProdSymbol ruleSymbol, IterablePath handCodedPath) { return existsHandwrittenClass(Names.getSimpleName(ruleSymbol.getName() + "Symbol"), getTargetPackage(), handCodedPath); } }