/*
* ******************************************************************************
* 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;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import de.monticore.ast.ASTNode;
import de.monticore.codegen.mc2cd.MCGrammarSymbolTableHelper;
import de.monticore.codegen.parser.ParserGeneratorHelper;
import de.monticore.grammar.concepts.antlr.antlr._ast.ASTAntlrLexerAction;
import de.monticore.grammar.concepts.antlr.antlr._ast.ASTAntlrParserAction;
import de.monticore.grammar.concepts.antlr.antlr._ast.ASTJavaCodeExt;
import de.monticore.grammar.grammar._ast.ASTClassProd;
import de.monticore.grammar.grammar._ast.ASTConstant;
import de.monticore.grammar.grammar._ast.ASTInterfaceProd;
import de.monticore.grammar.grammar._ast.ASTMCGrammar;
import de.monticore.grammar.grammar._ast.ASTRuleComponent;
import de.monticore.grammar.grammar._ast.ASTRuleReference;
import de.monticore.grammar.grammar._ast.ASTTerminal;
import de.monticore.grammar.symboltable.MCGrammarSymbol;
import de.monticore.grammar.symboltable.MCProdSymbol;
import de.monticore.utils.ASTNodes;
import de.se_rwth.commons.logging.Log;
/**
* Contains information about a grammar which is required for the parser
* generation
*/
public class MCGrammarInfo {
/**
* Keywords of the processed grammar and its super grammars
*/
private Set<String> keywords = Sets.newLinkedHashSet();
/**
* Lexer patterns
*/
private Map<MCGrammarSymbol, List<Pattern>> lexerPatterns = new HashMap<>();
/**
* Additional java code for parser defined in antlr concepts of the processed
* grammar and its super grammars
*/
private List<String> additionalParserJavaCode = new ArrayList<String>();
/**
* Additional java code for lexer defined in antlr concepts of the processed
* grammar and its super grammars
*/
private List<String> additionalLexerJavaCode = new ArrayList<String>();
/**
* Predicates
*/
private ArrayListMultimap<String, PredicatePair> predicats = ArrayListMultimap.create();
/**
* Internal: LexNamer for naming lexer symbols in the antlr source code
*/
private LexNamer lexNamer = new LexNamer();
/**
* The symbol of the processed grammar
*/
private MCGrammarSymbol grammarSymbol;
/**
* The AST of the processed grammar
*/
private ASTMCGrammar astGrammar;
public MCGrammarInfo(MCGrammarSymbol grammarSymbol) {
this.grammarSymbol = grammarSymbol;
if (!grammarSymbol.getAstNode().isPresent()
|| !(grammarSymbol.getAstNode().get() instanceof ASTMCGrammar)) {
Log.error(String.format("0xA2109 ASTNode of the grammar symbol %s is not set.",
grammarSymbol.getName()));
}
astGrammar = (ASTMCGrammar) grammarSymbol.getAstNode().get();
buildLexPatterns();
findAllKeywords();
addSubRules();
addSubRulesToInterface();
addHWAntlrCode();
}
// ------------- Handling of the antlr concept -----------------------------
/**
* Add all sub/superule-relations to the symbol table form the perspective of
* the super rule by using addSubrule
*
* @param classProds Rule
*/
private void addSubRules() {
Set<MCGrammarSymbol> grammarsToHandle = Sets
.newLinkedHashSet(Arrays.asList(grammarSymbol));
grammarsToHandle.addAll(MCGrammarSymbolTableHelper.getAllSuperGrammars(grammarSymbol));
for (MCGrammarSymbol grammar : grammarsToHandle) {
for (ASTClassProd classProd : ((ASTMCGrammar) grammar.getAstNode().get())
.getClassProds()) {
for (ASTRuleReference superRule : classProd.getSuperRule()) {
Optional<MCProdSymbol> prodByName = grammarSymbol
.getProdWithInherited(superRule.getTypeName());
if (prodByName.isPresent()) {
addSubrule(prodByName.get().getName(), HelperGrammar.getRuleName(classProd), superRule);
}
else {
Log.error("0xA2110 Undefined rule: " + superRule.getTypeName(),
superRule.get_SourcePositionStart());
}
}
for (ASTRuleReference ruleref : classProd.getSuperInterfaceRule()) {
Optional<MCProdSymbol> prodByName = grammarSymbol
.getProdWithInherited(ruleref.getTypeName());
if (prodByName.isPresent()) {
addSubrule(prodByName.get().getName(), HelperGrammar.getRuleName(classProd), ruleref);
}
else {
Log.error("0xA2111 Undefined rule: " + ruleref.getTypeName(),
ruleref.get_SourcePositionStart());
}
}
}
}
}
/**
* Add all sub/superule-realtions to the symboltable form the perspective of
* the superrule by using addSubrule
*
* @param interfaceProdList Rule
*/
private void addSubRulesToInterface() {
Set<MCGrammarSymbol> grammarsToHandle = Sets
.newLinkedHashSet(Arrays.asList(grammarSymbol));
grammarsToHandle.addAll(MCGrammarSymbolTableHelper.getAllSuperGrammars(grammarSymbol));
for (MCGrammarSymbol grammar : grammarsToHandle) {
for (ASTInterfaceProd interfaceProd : ((ASTMCGrammar) grammar.getAstNode().get())
.getInterfaceProds()) {
for (ASTRuleReference superRule : interfaceProd.getSuperInterfaceRule()) {
Optional<MCProdSymbol> prodByName = grammar
.getProdWithInherited(superRule.getTypeName());
if (prodByName.isPresent()) {
addSubrule(prodByName.get().getName(), interfaceProd.getName(), superRule);
}
else {
Log.error("0xA2111 Undefined rule: " + superRule.getTypeName(),
superRule.get_SourcePositionStart());
}
}
}
}
}
private void addSubrule(String superrule, String subrule, ASTRuleReference ruleReference) {
ASTRuleComponent component = null;
if (ruleReference.getSemanticpredicateOrAction().isPresent()) {
if (ruleReference.getSemanticpredicateOrAction().get().isPredicate()) {
component = ruleReference.getSemanticpredicateOrAction().get();
}
}
PredicatePair subclassPredicatePair = new PredicatePair(subrule, component);
predicats.put(superrule, subclassPredicatePair);
}
/**
* @return grammarSymbol
*/
public MCGrammarSymbol getGrammarSymbol() {
return this.grammarSymbol;
}
/**
* @param grammarSymbol the grammarSymbol to set
*/
public void setGrammarSymbol(MCGrammarSymbol grammarSymbol) {
this.grammarSymbol = grammarSymbol;
}
/**
* @return java code
*/
public List<String> getAdditionalParserJavaCode() {
return this.additionalParserJavaCode;
}
/**
* @return java code
*/
public List<String> getAdditionalLexerJavaCode() {
return this.additionalLexerJavaCode;
}
private void addHWAntlrCode() {
// Get Antlr hwc
Set<MCGrammarSymbol> grammarsToHandle = Sets
.newLinkedHashSet(Arrays.asList(grammarSymbol));
grammarsToHandle.addAll(MCGrammarSymbolTableHelper.getAllSuperGrammars(grammarSymbol));
for (MCGrammarSymbol grammar : grammarsToHandle) {
if (grammar.getAstNode().isPresent()) {
// Add additional java code for lexer and parser
ASTNodes.getSuccessors(grammar.getAstNode().get(), ASTAntlrParserAction.class).forEach(
a -> addAdditionalParserJavaCode(a.getText()));
ASTNodes.getSuccessors(grammar.getAstNode().get(), ASTAntlrLexerAction.class).forEach(
a -> addAdditionalLexerJavaCode(a.getText()));
}
}
}
/**
* @param action the java code to add
*/
private void addAdditionalParserJavaCode(ASTJavaCodeExt action) {
additionalParserJavaCode.add(ParserGeneratorHelper.getText(action));
}
/**
* @param action the java code to add
*/
private void addAdditionalLexerJavaCode(ASTJavaCodeExt action) {
additionalLexerJavaCode.add(ParserGeneratorHelper.getText(action));
}
// ------------- Handling of keywords -----------------------------
public Set<String> getKeywords() {
return Collections.unmodifiableSet(keywords);
}
/**
* Checks if the terminal or constant <code>name</code> is a and has to be
* defined in the parser.
*
* @param name - rule to check
* @return true, if the terminal or constant <code>name</code> is a and has to
* be defined in the parser.
*/
public boolean isKeyword(String name, MCGrammarSymbol grammar) {
boolean matches = false;
boolean found = false;
// Check with options
if (mustBeKeyword(name, grammar)) {
matches = true;
found = true;
}
// Automatically detect if not specified
if (!found && lexerPatterns.containsKey(grammar)) {
for (Pattern p : lexerPatterns.get(grammar)) {
if (p.matcher(name).matches()) {
matches = true;
Log.debug(name + " is considered as a keyword because it matches " + p + " "
+ "(grammarsymtab)", MCGrammarSymbol.class.getSimpleName());
break;
}
}
}
return matches;
}
public List<PredicatePair> getSubRulesForParsing(String ruleName) {
// Consider superclass
Optional<MCProdSymbol> ruleByName = grammarSymbol.getProdWithInherited(ruleName);
List<PredicatePair> predicateList = Lists.newArrayList();
if (!ruleByName.isPresent()) {
return predicateList;
}
if (predicats.containsKey(ruleName)) {
predicateList.addAll(predicats.get(ruleName));
}
return predicateList;
}
/**
* @return lexNamer
*/
public LexNamer getLexNamer() {
return this.lexNamer;
}
/**
* Iterates over all Rules to find all keywords
*/
private void findAllKeywords() {
for (MCProdSymbol ruleSymbol : grammarSymbol.getProdsWithInherited().values()) {
if (ruleSymbol.isParserProd()) {
Optional<ASTNode> astProd = ruleSymbol.getAstNode();
if (astProd.isPresent() && astProd.get() instanceof ASTClassProd) {
Optional<MCGrammarSymbol> refGrammarSymbol = MCGrammarSymbolTableHelper
.getMCGrammarSymbol(astProd.get());
boolean isRefGrammarSymbol = refGrammarSymbol.isPresent();
for (ASTTerminal keyword : ASTNodes.getSuccessors(astProd.get(), ASTTerminal.class)) {
if (isKeyword(keyword.getName(), grammarSymbol)
|| (isRefGrammarSymbol && isKeyword(keyword.getName(), refGrammarSymbol.get()))) {
keywords.add(keyword.getName());
}
}
for (ASTConstant keyword : ASTNodes.getSuccessors(astProd.get(), ASTConstant.class)) {
if (isKeyword(keyword.getName(), grammarSymbol)
|| (isRefGrammarSymbol && isKeyword(keyword.getName(), refGrammarSymbol.get()))) {
keywords.add(keyword.getName());
}
}
}
}
}
}
private void buildLexPatterns() {
buildLexPatterns(grammarSymbol);
grammarSymbol.getSuperGrammarSymbols().forEach(g -> buildLexPatterns(g));
}
private void buildLexPatterns(MCGrammarSymbol grammar) {
List<Pattern> patterns = lexerPatterns.get(grammar);
if (patterns == null) {
patterns = new ArrayList<>();
lexerPatterns.put(grammar, patterns);
}
for (MCProdSymbol rule : grammar.getProdsWithInherited().values()) {
if (rule.isLexerProd()) {
if (!MCGrammarSymbolTableHelper.isFragment(rule.getAstNode())) {
Optional<Pattern> lexPattern = MCGrammarSymbolTableHelper.calculateLexPattern(
grammar,
rule.getAstNode());
if (lexPattern.isPresent()) {
patterns.add(lexPattern.get());
}
}
}
}
}
private boolean mustBeKeyword(String rule, MCGrammarSymbol grammar) {
return keywords.contains(rule);
}
}