/*
* ******************************************************************************
* 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.grammar.transformation;
import static de.monticore.grammar.Multiplicity.multiplicityByDuplicates;
import static de.monticore.grammar.Multiplicity.multiplicityOfAttributeInAST;
import java.io.IOException;
import java.io.StringReader;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import de.monticore.codegen.GeneratorHelper;
import de.monticore.grammar.Multiplicity;
import de.monticore.grammar.grammar._ast.ASTASTRule;
import de.monticore.grammar.grammar._ast.ASTAlt;
import de.monticore.grammar.grammar._ast.ASTAttributeInAST;
import de.monticore.grammar.grammar._ast.ASTBlock;
import de.monticore.grammar.grammar._ast.ASTConstantsGrammar;
import de.monticore.grammar.grammar._ast.ASTMCGrammar;
import de.monticore.grammar.grammar._ast.ASTNonTerminal;
import de.monticore.grammar.grammar._ast.ASTNonTerminalSeparator;
import de.monticore.grammar.grammar._ast.ASTProd;
import de.monticore.grammar.grammar_withconcepts._parser.Grammar_WithConceptsParser;
import de.monticore.utils.ASTNodes;
import de.monticore.utils.ASTTraverser;
import de.se_rwth.commons.StringTransformations;
import de.se_rwth.commons.logging.Log;
/**
* Static facade for the transformation of MC AST.
*/
public class GrammarTransformer {
private GrammarTransformer() {
// noninstantiable
}
public static void transform(ASTMCGrammar grammar) {
removeNonTerminalSeparators(grammar);
changeNamesOfMultivaluedAttributes(grammar);
}
/**
* The shortcut NonTerminalSeparator is replaced by the detailed description.
* Example: List(Element || ',')* ==> (List:Element (',' List:Element)+)
*/
public static void removeNonTerminalSeparators(ASTMCGrammar grammar) {
Map<ASTNonTerminalSeparator, ASTAlt> map = new HashMap<ASTNonTerminalSeparator, ASTAlt>();
RuleComponentListFinder componentListTransformer = new RuleComponentListFinder(map);
ASTTraverser traverser = new ASTTraverser(componentListTransformer);
// execute the search
traverser.traverse(grammar);
// execute the transformation
for (Entry<ASTNonTerminalSeparator, ASTAlt> entry : map.entrySet()) {
Log.debug("Find NonTerminalSeparator", GrammarTransformer.class.getName());
Optional<ASTBlock> block = transform(entry.getKey());
if (block.isPresent()) {
ASTAlt parent = entry.getValue();
int ind = parent.getComponents().indexOf(entry.getKey());
if (ind >= 0) {
Log.debug("Remove NonTerminalSeparator", GrammarTransformer.class.getName());
parent.getComponents().remove(ind);
Log.debug("Added new generated block", GrammarTransformer.class.getName());
parent.getComponents().add(ind, block.get());
}
else {
Log.error("0xA1009 Can't transform grammar");
}
}
}
}
/**
* Append suffix 's' to the names of multi-valued att * Append suffix 's' to
* the names of multi-valued attributes (NonTerminals and attributesinAst) if
* no usage names were set. Examples: Name ("." Name&)* ==> names:Name ("."
* names:Name&)* (State | Transition)* ==> (states:State |
* transitions:Transition)*
*/
public static void changeNamesOfMultivaluedAttributes(ASTMCGrammar grammar) {
grammar.getClassProds().forEach(c -> transformNonTerminals(grammar, c));
grammar.getInterfaceProds().forEach(c -> transformNonTerminals(grammar, c));
grammar.getASTRules().forEach(c -> transformAttributesInAST(c));
}
private static void transformNonTerminals(ASTMCGrammar grammar,
ASTProd classProd) {
Set<ASTNonTerminal> components = new LinkedHashSet<>();
ASTNodes.getSuccessors(classProd, ASTNonTerminal.class).stream()
.filter(nonTerminal -> GeneratorHelper.getMultiplicity(grammar,
nonTerminal) == Multiplicity.LIST)
.filter(nonTerminal -> !nonTerminal.getUsageName().isPresent())
.forEach(components::add);
ASTNodes.getSuccessors(classProd, ASTNonTerminal.class).stream()
.filter(nonTerminal -> multiplicityByDuplicates(grammar, nonTerminal) == Multiplicity.LIST)
.filter(nonTerminal -> !nonTerminal.getUsageName().isPresent())
.forEach(components::add);
Collection<String> changedNames = new LinkedHashSet<>();
components.forEach(s -> {
s.setUsageName(StringTransformations.uncapitalize(s.getName()) + 's');
Log.debug("Change the name of " + classProd.getName()
+ " list-attribute: " + s.getName(), GrammarTransformer.class.getName());
changedNames.add(s.getName());
});
// Change corresponding ASTRules
grammar.getASTRules().forEach(
astRule -> {
if (astRule.getType().equals(classProd.getName())) {
astRule.getAttributeInASTs().forEach(
astAttr -> {
String name = astAttr.getGenericType().getTypeName();
if (name.startsWith(GeneratorHelper.AST_PREFIX)) {
name = name.substring(GeneratorHelper.AST_PREFIX.length());
}
if (!astAttr.getName().isPresent() && changedNames.contains(name)) {
astAttr.setName(StringTransformations.uncapitalize(name) + 's');
Log.debug("Change the name of " + classProd.getName()
+ " astRule " + name, GrammarTransformer.class.getName());
}
});
}
});
}
private static void transformAttributesInAST(ASTASTRule astRule) {
ASTNodes
.getSuccessors(astRule, ASTAttributeInAST.class)
.stream()
.filter(attributeInAST -> multiplicityOfAttributeInAST(attributeInAST) == Multiplicity.LIST)
.filter(attributeInAST -> !attributeInAST.getName().isPresent())
.forEach(
attributeInAST -> {
List<String> typeName = attributeInAST.getGenericType().getNames();
attributeInAST.setName(typeName.get(typeName.size() - 1) + 's');
Log.debug("Change the name of ast-rule " + astRule.getType()
+ " list-attribute: " + attributeInAST.getGenericType(),
GrammarTransformer.class.getName());
});
}
/**
* @param nonTerminalSep
* @return
*/
private static Optional<ASTBlock> transform(ASTNonTerminalSeparator nonTerminalSep) {
String name = "";
if (nonTerminalSep.getUsageName().isPresent()) {
name = nonTerminalSep.getUsageName().get() + ":";
}
String plusKeywords = (nonTerminalSep.isPlusKeywords()) ? "&" : "";
String iteration = (nonTerminalSep.getIteration() == ASTConstantsGrammar.STAR) ? "?" : "";
String extendedList = "(%usageName% %nonTerminal% %plusKeywords% (\"%terminal%\" %usageName% %nonTerminal% %plusKeywords%)*)%iterator%";
extendedList = extendedList.replaceAll("%usageName%", name);
extendedList = extendedList.replaceAll("%nonTerminal%", nonTerminalSep.getName());
extendedList = extendedList.replaceAll("%plusKeywords%", plusKeywords);
extendedList = extendedList.replaceAll("%terminal%", nonTerminalSep.getSeparator());
extendedList = extendedList.replaceAll("%iterator%", iteration);
Grammar_WithConceptsParser parser = new Grammar_WithConceptsParser();
Optional<ASTBlock> block = null;
try {
Log.debug("Create ast for " + extendedList, GrammarTransformer.class.getName());
block = parser.parseBlock(new StringReader(extendedList));
if (parser.hasErrors()) {
Log.error("0xA0362 RecognitionException during parsing " + extendedList);
}
}
catch (IOException e) {
Log.error("0xA0361 IOException during parsing " + extendedList);
}
return block;
}
}