/* * ****************************************************************************** * 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.parser; import static com.google.common.base.Preconditions.checkState; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import com.google.common.base.Joiner; import com.google.common.collect.Lists; import de.monticore.ast.ASTNode; import de.monticore.codegen.GeneratorHelper; import de.monticore.codegen.mc2cd.MCGrammarSymbolTableHelper; import de.monticore.grammar.MCGrammarInfo; import de.monticore.grammar.HelperGrammar; import de.monticore.grammar.PredicatePair; import de.monticore.grammar.grammar._ast.ASTAlt; import de.monticore.grammar.grammar._ast.ASTBlock; 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.ASTConstantsGrammar; import de.monticore.grammar.grammar._ast.ASTFollowOption; 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.ASTNonTerminal; import de.monticore.grammar.grammar._ast.ASTProd; import de.monticore.grammar.grammar._ast.ASTTerminal; import de.monticore.grammar.grammar_withconcepts._ast.ASTAction; import de.monticore.grammar.grammar_withconcepts._ast.ASTExpressionPredicate; import de.monticore.grammar.grammar_withconcepts._ast.ASTGrammar_WithConceptsNode; import de.monticore.grammar.grammar_withconcepts._ast.ASTJavaCode; import de.monticore.grammar.prettyprint.Grammar_WithConceptsPrettyPrinter; import de.monticore.grammar.symboltable.MCGrammarSymbol; import de.monticore.grammar.symboltable.MCGrammarSymbolReference; import de.monticore.grammar.symboltable.MCProdSymbol; import de.monticore.java.javadsl._ast.ASTBlockStatement; import de.monticore.java.javadsl._ast.ASTClassMemberDeclaration; import de.monticore.prettyprint.IndentPrinter; import de.se_rwth.commons.JavaNamesHelper; import de.se_rwth.commons.Names; import de.se_rwth.commons.StringTransformations; import de.se_rwth.commons.logging.Log; /** * This is a helper class for the parser generation */ public class ParserGeneratorHelper { public static final String MONTICOREANYTHING = "MONTICOREANYTHING"; public static final String RIGHTASSOC = "<assoc=right>"; public static final String ANTLR_CONCEPT = "antlr"; private static Grammar_WithConceptsPrettyPrinter prettyPrinter; private ASTMCGrammar astGrammar; private String qualifiedGrammarName; private MCGrammarSymbol grammarSymbol; private MCGrammarInfo grammarInfo; private Map<ASTNode, String> tmpVariables = new HashMap<>(); private int tmp_counter = 0; /** * Constructor for de.monticore.codegen.parser.ParserGeneratorHelper */ public ParserGeneratorHelper(ASTMCGrammar ast, MCGrammarInfo grammarInfo) { Log.errorIfNull(ast); this.astGrammar = ast; this.qualifiedGrammarName = astGrammar.getPackage().isEmpty() ? astGrammar.getName() : Joiner.on('.').join(Names.getQualifiedName(astGrammar.getPackage()), astGrammar.getName()); this.grammarInfo = grammarInfo; this.grammarSymbol = grammarInfo.getGrammarSymbol(); checkState(qualifiedGrammarName.equals(grammarSymbol.getFullName())); } /** * @return grammarSymbol */ public MCGrammarSymbol getGrammarSymbol() { return this.grammarSymbol; } /** * @return the qualified grammar's name */ public String getQualifiedGrammarName() { return qualifiedGrammarName; } /** * @return the name of the start rule */ public String getStartRuleName() { if (grammarSymbol.getStartProd().isPresent()) { return grammarSymbol.getStartProd().get().getName(); } return ""; } /** * @return the qualified 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 ""; } /** * @return the package for the generated parser files */ public String getParserPackage() { return getQualifiedGrammarName().toLowerCase() + "." + ParserGenerator.PARSER_PACKAGE; } /** * @return the name for a lexsymbol that should be used in an Antlr-File */ public String getLexSymbolName(String constName) { Log.errorIfNull(constName); return grammarInfo.getLexNamer().getLexName(grammarSymbol, constName); } /** * Get all used LexSymbols, different form information in AST, as inherited * ones are integrated as well * * @return Keys of all lex symbols */ public Set<String> getLexSymbolsWithInherited() { return grammarInfo.getLexNamer().getLexnames(); } /** * checks if parser must be generated for this rule * * @param rule * @return */ public boolean generateParserForRule(MCProdSymbol rule) { boolean generateParserForRule = false; String ruleName = rule.getName(); if (rule.isClass()) { generateParserForRule = true; } if (rule.isAbstract() || rule.isInterface()) { List<PredicatePair> subRules = grammarInfo.getSubRulesForParsing(ruleName); generateParserForRule = !subRules.isEmpty(); } return generateParserForRule; } /** * Gets all interface rules which were not excluded from the generation * * @return List of interface rules */ public List<MCProdSymbol> getInterfaceRulesToGenerate() { List<MCProdSymbol> interfaceRules = Lists.newArrayList(); for (MCProdSymbol ruleSymbol : grammarSymbol.getProdsWithInherited() .values()) { if (ruleSymbol.isAbstract() || ruleSymbol.isInterface()) { List<PredicatePair> subRules = grammarInfo .getSubRulesForParsing(ruleSymbol.getName()); if (!subRules.isEmpty()) { interfaceRules.add(ruleSymbol); } } } return interfaceRules; } /** * Gets all non external idents * * @return List of ident types */ public List<MCProdSymbol> getIdentsToGenerate() { return grammarSymbol.getProdsWithInherited().values().stream() .filter(r -> r.isLexerProd() && !r.isExternal()).collect(Collectors.toList()); } public static String getConvertFunction(MCProdSymbol symbol) { Optional<ASTLexProd> ast = symbol.getAstNode().filter(ASTLexProd.class::isInstance) .map(ASTLexProd.class::cast); return ast.isPresent()? HelperGrammar.createConvertFunction(ast.get(), prettyPrinter):""; } /** * Gets parser rules * * @return List of ident types */ public List<ASTProd> getParserRulesToGenerate() { // Iterate over all Rules return grammarSymbol.getProdsWithInherited().values().stream() .filter(r -> (r.isParserProd() || r.isEnum())).map(r -> r.getAstNode()) .filter(Optional::isPresent).map(Optional::get).filter(ASTProd.class::isInstance) .map(ASTProd.class::cast).collect(Collectors.toList()); } public List<ASTLexProd> getLexerRulesToGenerate() { // Iterate over all LexRules List<ASTLexProd> prods = Lists.newArrayList(); MCProdSymbol mcanything = null; final Map<String, MCProdSymbol> rules = new LinkedHashMap<>(); // Don't use grammarSymbol.getRulesWithInherited because of changed order for (final MCProdSymbol ruleSymbol : grammarSymbol.getProds()) { rules.put(ruleSymbol.getName(), ruleSymbol); } for (int i = grammarSymbol.getSuperGrammars().size() - 1; i >= 0; i--) { rules.putAll(grammarSymbol.getSuperGrammarSymbols().get(i).getProdsWithInherited()); } for (Entry<String, MCProdSymbol> ruleSymbol : rules.entrySet()) { if (ruleSymbol.getValue().isLexerProd()) { MCProdSymbol lexProd = ruleSymbol.getValue(); // MONTICOREANYTHING must be last rule if (lexProd.getName().equals(MONTICOREANYTHING)) { mcanything = lexProd; } else { prods.add((ASTLexProd) lexProd.getAstNode().get()); } } } if (mcanything != null) { prods.add((ASTLexProd) mcanything.getAstNode().get()); } return prods; } public String getConstantNameForConstant(ASTConstant x) { String name; if (x.getHumanName().isPresent()) { name = x.getHumanName().get(); } else { name = grammarInfo.getLexNamer().getConstantName(x.getName()); } return name.toUpperCase(); } public String getTmpVarName(ASTNode a) { if (!tmpVariables.containsKey(a)) { tmpVariables.put(a, getNewTmpVar()); } return tmpVariables.get(a); } private String getNewTmpVar() { return "tmp" + (Integer.valueOf(tmp_counter++)).toString(); } public void resetTmpVarNames() { tmpVariables.clear(); tmp_counter = 0; } // ---------------------------------------------------- /** * The result is true iff ASTTerminal is iterated * * @param ast ASTConstantGroup to be evaluated * @return true iff ASTConstantGroup is iterated */ public static boolean isIterated(ASTTerminal ast) { return ast.getIteration() == ASTConstantsGrammar.PLUS || ast .getIteration() == ASTConstantsGrammar.STAR; } /** * The result is true iff ASTOrGroup is iterated * * @param ast ASTOrGroup to be evaluated * @return true iff ASTOrGroup is iterated */ public static boolean isIterated(ASTBlock ast) { return ast.getIteration() == ASTConstantsGrammar.PLUS || ast .getIteration() == ASTConstantsGrammar.STAR; } /** * Returns the name of a rule * * @param ast rule * @return Name of a rule */ public static String getRuleName(ASTClassProd ast) { return ast.getName(); } /** * Creates usage name from a NtSym usually from its attribute or creates name * * @param ast * @return */ public static String getUsuageName(ASTNonTerminal ast) { // Use Nonterminal name as attribute name starting with lower case latter if (ast.getUsageName().isPresent()) { return ast.getUsageName().get(); } else { return StringTransformations.uncapitalize(ast.getName()); } } public static boolean isIterated(ASTNonTerminal ast) { return ast.getIteration() == ASTConstantsGrammar.PLUS || ast .getIteration() == ASTConstantsGrammar.STAR; } public static String getTypeNameForEnum(String surroundtype, ASTConstantGroup ast) { return new StringBuilder("[enum.").append(surroundtype).append(".") .append(ast.getUsageName()).toString(); } /** * Printable representation of iteration * * @param i Value from AST * @return String representing value i */ public static String printIteration(int i) { switch (i) { case ASTConstantsGrammar.PLUS: return "+"; case ASTConstantsGrammar.STAR: return "*"; case ASTConstantsGrammar.QUESTION: return "?"; default: return ""; } } public static String getDefinedType(ASTClassProd rule) { return rule.getName(); } /** * Returns Human-Readable, antlr conformed name for a rulename * * @param ast rule name * @return Human-Readable, antlr conformed rule name */ public static String getRuleNameForAntlr(ASTNonTerminal ast) { return getRuleNameForAntlr(ast.getName()); } /** * Returns Human-Readable, antlr conformed name for a rulename * * @param rulename rule name * @return Human-Readable, antlr conformed rule name */ public static String getRuleNameForAntlr(String rulename) { return JavaNamesHelper.getNonReservedName(rulename .toLowerCase()); } /** * Returns Human-Readable, antlr conformed name for a rulename * * @param rule rule name * @return Human-Readable, antlr conformed rule name */ public static String getRuleNameForAntlr(ASTClassProd rule) { return getRuleNameForAntlr(getRuleName(rule)); } public String getTmpVarNameForAntlrCode(ASTNonTerminal node) { Optional<MCProdSymbol> prod = MCGrammarSymbolTableHelper.getEnclosingRule(node); if (!prod.isPresent()) { Log.error("0xA1006 ASTNonterminal " + node.getName() + "(usageName: " + node.getUsageName() + ") can't be resolved."); return ""; } return getTmpVarName(node); } public String getTmpVarNameForAntlrCode(ASTLexNonTerminal node) { Optional<MCProdSymbol> prod = MCGrammarSymbolTableHelper.getEnclosingRule(node); if (!prod.isPresent()) { Log.error("0xA1007 ASTNonterminal " + node.getName() + " can't be resolved."); return ""; } return getTmpVarName(node); } public String getTmpVarNameForAntlrCode(String name, ASTNode node) { Optional<MCProdSymbol> prod = MCGrammarSymbolTableHelper.getEnclosingRule(node); if (!prod.isPresent()) { Log.error("0xA1008 ASTNonterminal " + name + " can't be resolved."); return ""; } return getTmpVarName(node); } public Optional<ASTAlt> getAlternativeForFollowOption(String prodName) { return astGrammar.getGrammarOptions().isPresent() ? astGrammar.getGrammarOptions().get().getFollowOptions().stream() .filter(f -> f.getProdName().equals(prodName)).map(ASTFollowOption::getAlt).findFirst() : Optional.empty(); } public List<ASTAlt> getAlternatives(ASTClassProd ast) { if (!ast.getAlts().isEmpty()) { return ast.getAlts(); } for (MCGrammarSymbolReference g : grammarSymbol.getSuperGrammars()) { final Optional<MCProdSymbol> ruleByName = g.getReferencedSymbol().getProdWithInherited(ast.getName()); if (ruleByName.isPresent() && ruleByName.get().isClass()) { Optional<ASTNode> astProd = ruleByName.get().getAstNode(); if (astProd.isPresent() && astProd.get() instanceof ASTClassProd) { return ((ASTClassProd)astProd.get()).getAlts(); } } } return Lists.newArrayList(); } public static String getASTClassName(MCProdSymbol rule) { return MCGrammarSymbolTableHelper.getQualifiedName(rule); } public static Grammar_WithConceptsPrettyPrinter getPrettyPrinter() { if (prettyPrinter == null) { prettyPrinter = new Grammar_WithConceptsPrettyPrinter(new IndentPrinter()); } return prettyPrinter; } /** * Neue Zwischenknoten: Action = Statements; ExpressionPredicate = Expression; * ASTScript = MCStatement; ASTAntlrCode = MemberDeclarations; ASTActionAntlr * = MemberDeclarations; * * @param node * @return */ public static String getText(ASTNode node) { Log.errorIfNull(node); if (node instanceof ASTAction) { StringBuilder buffer = new StringBuilder(); for (ASTBlockStatement action : ((ASTAction) node).getBlockStatements()) { buffer.append(getPrettyPrinter().prettyprint(action)); } return buffer.toString(); } if (node instanceof ASTJavaCode) { StringBuilder buffer = new StringBuilder(); for (ASTClassMemberDeclaration action : ((ASTJavaCode) node).getClassMemberDeclarations()) { buffer.append(getPrettyPrinter().prettyprint(action)); } return buffer.toString(); } if (node instanceof ASTExpressionPredicate) { String exprPredicate = getPrettyPrinter() .prettyprint(((ASTExpressionPredicate) node).getExpression()); Log.debug("ASTExpressionPredicate:\n" + exprPredicate, ParserGenerator.LOG); return exprPredicate; } if (node instanceof ASTGrammar_WithConceptsNode) { String output = getPrettyPrinter().prettyprint((ASTGrammar_WithConceptsNode) node); Log.debug("ASTGrammar_WithConceptsNode:\n" + output, ParserGenerator.LOG); return output; } return ""; } public static String getParseRuleName(MCProdSymbol rule) { return JavaNamesHelper.getNonReservedName(StringTransformations.uncapitalize(rule.getName())); } public static String getMCParserWrapperName(MCProdSymbol rule) { return StringTransformations.capitalize(JavaNamesHelper.getNonReservedName(rule.getName())); } /** * @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 "int"; // TODO GV: // return getConstantType(); } return MCGrammarSymbolTableHelper.getQualifiedName(symbol.getAstNode().get(), symbol, GeneratorHelper.AST_PREFIX, ""); } public static String getDefaultValue(MCProdSymbol symbol) { String name = getQualifiedName(symbol); if ("int".equals(name)) { return "0"; } if ("boolean".equals(name)) { return "false"; } 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 formatAttributeValue(Optional<Integer> value) { if (!value.isPresent()) { return "undef"; } else if (value.get() == GeneratorHelper.STAR) { return "*"; } return Integer.toString(value.get()); } }