diff --git a/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AbstractAnnotationAwareAntlrGrammarGenerator.java b/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AbstractAnnotationAwareAntlrGrammarGenerator.java new file mode 100644 index 0000000000..f846b739ff --- /dev/null +++ b/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AbstractAnnotationAwareAntlrGrammarGenerator.java @@ -0,0 +1,224 @@ +/******************************************************************************* + * Copyright (c) 2016 Avaloq Group AG and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Avaloq Group AG - initial API and implementation + *******************************************************************************/ + +package com.avaloq.tools.ddk.xtext.generator.parser.antlr; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.TreeSet; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.xtend2.lib.StringConcatenation; +import org.eclipse.xtext.EnumRule; +import org.eclipse.xtext.Grammar; +import org.eclipse.xtext.GrammarUtil; +import org.eclipse.xtext.Keyword; +import org.eclipse.xtext.ParserRule; +import org.eclipse.xtext.TerminalRule; +import org.eclipse.xtext.xtext.FlattenedGrammarAccess; +import org.eclipse.xtext.xtext.RuleFilter; +import org.eclipse.xtext.xtext.RuleNames; +import org.eclipse.xtext.xtext.generator.CodeConfig; +import org.eclipse.xtext.xtext.generator.model.IXtextGeneratorFileSystemAccess; +import org.eclipse.xtext.xtext.generator.parser.antlr.AbstractAntlrGrammarWithActionsGenerator; +import org.eclipse.xtext.xtext.generator.parser.antlr.AntlrGrammarGenUtil; +import org.eclipse.xtext.xtext.generator.parser.antlr.AntlrOptions; +import org.eclipse.xtext.xtext.generator.parser.antlr.CombinedGrammarMarker; +import org.eclipse.xtext.xtext.generator.parser.antlr.KeywordHelper; + +import com.avaloq.tools.ddk.xtext.generator.parser.common.GrammarRuleAnnotations; +import com.avaloq.tools.ddk.xtext.generator.parser.common.PredicatesNaming; +import com.google.common.collect.Iterators; +import com.google.inject.Inject; + +// CHECKSTYLE:CONSTANTS-OFF + +@SuppressWarnings({"PMD.UnusedFormalParameter", "nls"}) +public abstract class AbstractAnnotationAwareAntlrGrammarGenerator extends AbstractAntlrGrammarWithActionsGenerator { + + // CHECKSTYLE:CHECK-OFF VisibilityModifier + @Inject + protected GrammarRuleAnnotations annotations; + + @Inject + protected PredicatesNaming predicatesNaming; + // CHECKSTYLE:CHECK-ON VisibilityModifier + + @Inject + private CodeConfig codeConfig; + + private Grammar originalGrammar; + + @Override + public void generate(final Grammar it, final AntlrOptions options, final IXtextGeneratorFileSystemAccess fsa) { + this.keywordHelper = KeywordHelper.getHelper(it); + this.originalGrammar = it; + final RuleFilter filter = new RuleFilter(); + filter.setDiscardUnreachableRules(true); // options.skipUnusedRules + filter.setDiscardTerminalRules(false); // options.skipUnusedRules + final RuleNames ruleNames = RuleNames.getRuleNames(it, true); + final Grammar flattened = new FlattenedGrammarAccess(ruleNames, filter).getFlattenedGrammar(); + new CombinedGrammarMarker(isCombinedGrammar()).attachToEmfObject(flattened); + fsa.generateFile(getGrammarNaming().getParserGrammar(it).getGrammarFileName(), compileParser(flattened, options)); + if (!isCombinedGrammar()) { + fsa.generateFile(getGrammarNaming().getLexerGrammar(it).getGrammarFileName(), compileLexer(flattened, options)); + } + } + + @Override + protected boolean isCombinedGrammar() { + return getGrammarNaming().isCombinedGrammar(originalGrammar); + } + + @Override + protected CharSequence compileLexer(final Grammar it, final AntlrOptions options) { + final StringConcatenation builder = new StringConcatenation(); + builder.append(codeConfig.getFileHeader()); + builder.newLineIfNotEmpty(); + builder.append("lexer grammar "); + builder.append(getGrammarNaming().getLexerGrammar(it).getSimpleName()); + builder.append(";"); + builder.newLineIfNotEmpty(); + builder.append(compileLexerOptions(it, options)); + builder.newLineIfNotEmpty(); + builder.append(compileTokens(it, options)); + builder.newLineIfNotEmpty(); + builder.append(compileLexerHeader(it, options)); + builder.newLineIfNotEmpty(); + builder.append(compileLexerMembers(it, options)); + builder.newLineIfNotEmpty(); + builder.append(compileKeywordRules(it, options)); + builder.newLineIfNotEmpty(); + builder.append(compileTerminalRules(it, options)); + builder.newLineIfNotEmpty(); + return builder; + } + + protected CharSequence compileLexerMembers(final Grammar it, final AntlrOptions options) { + return """ + @members { + protected int getSingleLineCommentRule() { + return RULE_SL_COMMENT; + } + + protected int getMultiLineCommentRule() { + return RULE_ML_COMMENT; + } + + protected int getEndOfFileRule() { + return EOF; + } + } + """; + } + + @Override + protected String compileParserImports(final Grammar it, final AntlrOptions options) { + return """ + import %s; + import com.avaloq.tools.ddk.xtext.parser.antlr.ParserContext; + """.formatted(predicatesNaming.getSemanticPredicatesFullName(GrammarUtil.getGrammar(it))); + } + + protected CharSequence compileParserMemberDeclarations(final Grammar it, final String access) { + return """ + %s %s grammarAccess; + %s %s predicates; + %s ParserContext parserContext; + """.formatted(access, _grammarAccessExtensions.getGrammarAccess(it).getSimpleName(), access, predicatesNaming.getSemanticPredicatesSimpleName(it), access); + } + + protected CharSequence compileParserSetTokenStreamMethod() { + return """ + /** + * Set token stream in parser context. + * @param input Token stream + */ + @Override + public void setTokenStream(TokenStream input) { + super.setTokenStream(input); + if(parserContext != null){ + parserContext.setTokenStream(input); + } + } + """; + } + + @Override + protected CharSequence compileKeywordRules(final Grammar it, final AntlrOptions options) { + // implementation from xtext, but keywords are from the given grammar only (which has been flattened and filtered correctly) + final Set allKeywords = getAllKeywords(it, options); + final List allTerminalRules = GrammarUtil.allTerminalRules(it); + + final List syntheticKwAlternatives = new ArrayList<>(); + for (final String kw : allKeywords) { + final String ruleName = keywordHelper.getRuleName(kw); + syntheticKwAlternatives.add("(FRAGMENT_" + ruleName + ")=> FRAGMENT_" + ruleName + " {$type = " + ruleName + "; }"); + } + for (final TerminalRule terminalRule : allTerminalRules) { + if (!_syntheticTerminalDetector.isSyntheticTerminalRule(terminalRule) && !terminalRule.isFragment()) { + final String ruleName = _grammarAccessExtensions.ruleName(terminalRule); + syntheticKwAlternatives.add("(FRAGMENT_" + ruleName + ")=> FRAGMENT_" + ruleName + " {$type = " + ruleName + "; }"); + } + } + + final StringConcatenation builder = new StringConcatenation(); + if (options.isBacktrackLexer()) { + builder.append("SYNTHETIC_ALL_KEYWORDS :"); + builder.newLine(); + boolean hasElements = false; + for (final String kw : syntheticKwAlternatives) { + if (!hasElements) { + hasElements = true; + } else { + builder.appendImmediate(" |", " "); + } + builder.append(" "); + builder.append(kw, " "); + builder.newLineIfNotEmpty(); + } + builder.append(";"); + builder.newLine(); + for (final String kw : allKeywords) { + builder.append("fragment FRAGMENT_"); + builder.append(keywordHelper.getRuleName(kw)); + builder.append(" : '"); + builder.append(AntlrGrammarGenUtil.toAntlrString(kw)); + builder.append("';"); + builder.newLineIfNotEmpty(); + } + } else { + for (final String rule : allKeywords) { + builder.append(compileRule(rule, it, options)); + builder.newLineIfNotEmpty(); + } + } + return builder; + } + + private static Set getAllKeywords(final Grammar flattenedGrammar, final AntlrOptions options) { + final Set result = new TreeSet<>(KeywordHelper.keywordComparator); + final List parserRules = GrammarUtil.allParserRules(flattenedGrammar); + final List enumRules = GrammarUtil.allEnumRules(flattenedGrammar); + final Iterator iter = Iterators.concat(EcoreUtil.getAllContents(parserRules), EcoreUtil.getAllContents(enumRules)); + Iterators.addAll(result, Iterators.transform( + Iterators.filter(iter, Keyword.class), + kw -> options.isIgnoreCase() ? kw.getValue().toLowerCase(Locale.ENGLISH) : kw.getValue())); + return Collections.unmodifiableSet(result); + } + +} +// CHECKSTYLE:CONSTANTS-ON diff --git a/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AbstractAnnotationAwareAntlrGrammarGenerator.xtend b/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AbstractAnnotationAwareAntlrGrammarGenerator.xtend deleted file mode 100644 index 302df5faed..0000000000 --- a/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AbstractAnnotationAwareAntlrGrammarGenerator.xtend +++ /dev/null @@ -1,159 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ - -package com.avaloq.tools.ddk.xtext.generator.parser.antlr - -import com.avaloq.tools.ddk.xtext.generator.parser.common.GrammarRuleAnnotations -import com.avaloq.tools.ddk.xtext.generator.parser.common.PredicatesNaming -import com.google.common.collect.Iterators -import com.google.inject.Inject -import java.util.Collections -import java.util.Locale -import java.util.TreeSet -import org.eclipse.emf.ecore.EObject -import org.eclipse.emf.ecore.util.EcoreUtil -import org.eclipse.xtext.Grammar -import org.eclipse.xtext.GrammarUtil -import org.eclipse.xtext.Keyword -import org.eclipse.xtext.xtext.FlattenedGrammarAccess -import org.eclipse.xtext.xtext.RuleFilter -import org.eclipse.xtext.xtext.RuleNames -import org.eclipse.xtext.xtext.generator.CodeConfig -import org.eclipse.xtext.xtext.generator.model.IXtextGeneratorFileSystemAccess -import org.eclipse.xtext.xtext.generator.parser.antlr.AbstractAntlrGrammarWithActionsGenerator -import org.eclipse.xtext.xtext.generator.parser.antlr.AntlrOptions -import org.eclipse.xtext.xtext.generator.parser.antlr.CombinedGrammarMarker -import org.eclipse.xtext.xtext.generator.parser.antlr.KeywordHelper - -import static extension org.eclipse.xtext.GrammarUtil.* -import static extension org.eclipse.xtext.xtext.generator.parser.antlr.AntlrGrammarGenUtil.* - -abstract class AbstractAnnotationAwareAntlrGrammarGenerator extends AbstractAntlrGrammarWithActionsGenerator { - @Inject protected extension GrammarRuleAnnotations annotations - @Inject protected extension PredicatesNaming predicatesNaming - @Inject CodeConfig codeConfig - - Grammar originalGrammar - - override generate(Grammar it, AntlrOptions options, IXtextGeneratorFileSystemAccess fsa) { - this.keywordHelper = KeywordHelper.getHelper(it) - this.originalGrammar = it - val RuleFilter filter = new RuleFilter(); - filter.discardUnreachableRules = true // options.skipUnusedRules - filter.discardTerminalRules = false // options.skipUnusedRules - val RuleNames ruleNames = RuleNames.getRuleNames(it, true); - val Grammar flattened = new FlattenedGrammarAccess(ruleNames, filter).getFlattenedGrammar(); - new CombinedGrammarMarker(combinedGrammar).attachToEmfObject(flattened) - fsa.generateFile(grammarNaming.getParserGrammar(it).grammarFileName, flattened.compileParser(options)) - if (!isCombinedGrammar) { - fsa.generateFile(grammarNaming.getLexerGrammar(it).grammarFileName, flattened.compileLexer(options)) - } - } - - protected override isCombinedGrammar() { - grammarNaming.isCombinedGrammar(originalGrammar) - } - - protected override compileLexer(Grammar it, AntlrOptions options) ''' - «codeConfig.fileHeader» - lexer grammar «grammarNaming.getLexerGrammar(it).simpleName»; - «compileLexerOptions(options)» - «compileTokens(options)» - «compileLexerHeader(options)» - «compileLexerMembers(options)» - «compileKeywordRules(options)» - «compileTerminalRules(options)» - ''' - - protected def compileLexerMembers(Grammar it, AntlrOptions options) ''' - @members { - protected int getSingleLineCommentRule() { - return RULE_SL_COMMENT; - } - - protected int getMultiLineCommentRule() { - return RULE_ML_COMMENT; - } - - protected int getEndOfFileRule() { - return EOF; - } - } - ''' - - protected override compileParserImports(Grammar it, AntlrOptions options) ''' - import «grammar.semanticPredicatesFullName»; - import com.avaloq.tools.ddk.xtext.parser.antlr.ParserContext; - ''' - - protected def compileParserMemberDeclarations(Grammar it, String access) ''' - «access» «grammarAccess.simpleName» grammarAccess; - «access» «getSemanticPredicatesSimpleName()» predicates; - «access» ParserContext parserContext; - ''' - - protected def compileParserSetTokenStreamMethod() ''' - /** - * Set token stream in parser context. - * @param input Token stream - */ - @Override - public void setTokenStream(TokenStream input) { - super.setTokenStream(input); - if(parserContext != null){ - parserContext.setTokenStream(input); - } - } - ''' - - protected override compileKeywordRules(Grammar it, AntlrOptions options) { - // implementation from xtext, but keywords are from the given grammar only (which has been flattened and filtered correctly) - val allKeywords = getAllKeywords(options) - val allTerminalRules = allTerminalRules - - val synthetic_kw_alternatives = newArrayList - synthetic_kw_alternatives.addAll(allKeywords.indexed.map[ - val ruleName = keywordHelper.getRuleName(value) - return '''(FRAGMENT_«ruleName»)=> FRAGMENT_«ruleName» {$type = «ruleName»; }''' - ]) - synthetic_kw_alternatives.addAll(allTerminalRules.indexed.map[ - if (!isSyntheticTerminalRule(value) && !value.fragment) { - return '''(FRAGMENT_«value.ruleName»)=> FRAGMENT_«value.ruleName» {$type = «value.ruleName»; }''' - } - ].filterNull.toList) - ''' - «IF options.isBacktrackLexer» - SYNTHETIC_ALL_KEYWORDS : - «FOR kw: synthetic_kw_alternatives SEPARATOR ' |'» - «kw» - «ENDFOR» - ; - «FOR kw: allKeywords» - fragment FRAGMENT_«keywordHelper.getRuleName(kw)» : '«kw.toAntlrString()»'; - «ENDFOR» - «ELSE» - «FOR rule:allKeywords» - «rule.compileRule(it, options)» - «ENDFOR» - «ENDIF» - ''' - } - - private static def getAllKeywords(Grammar flattenedGrammar, AntlrOptions options) { - val result = new TreeSet(KeywordHelper.keywordComparator) - val parserRules=GrammarUtil.allParserRules(flattenedGrammar) - val enumRules=GrammarUtil.allEnumRules(flattenedGrammar) - val iter=Iterators.concat(EcoreUtil.getAllContents(parserRules), EcoreUtil.getAllContents(enumRules)) - Iterators.addAll(result, iter.filter(Keyword).map[if (options.ignoreCase) value.toLowerCase(Locale.ENGLISH) else value]) - Collections.unmodifiableSet(result) - } - -} diff --git a/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AnnotationAwareAntlrContentAssistGrammarGenerator.java b/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AnnotationAwareAntlrContentAssistGrammarGenerator.java new file mode 100644 index 0000000000..a8bee765cf --- /dev/null +++ b/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AnnotationAwareAntlrContentAssistGrammarGenerator.java @@ -0,0 +1,1302 @@ +/******************************************************************************* + * Copyright (c) 2016 Avaloq Group AG and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Avaloq Group AG - initial API and implementation + *******************************************************************************/ + +package com.avaloq.tools.ddk.xtext.generator.parser.antlr; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.xtend2.lib.StringConcatenation; +import org.eclipse.xtext.AbstractElement; +import org.eclipse.xtext.AbstractRule; +import org.eclipse.xtext.Action; +import org.eclipse.xtext.Alternatives; +import org.eclipse.xtext.Assignment; +import org.eclipse.xtext.CrossReference; +import org.eclipse.xtext.EnumLiteralDeclaration; +import org.eclipse.xtext.EnumRule; +import org.eclipse.xtext.Grammar; +import org.eclipse.xtext.GrammarUtil; +import org.eclipse.xtext.Group; +import org.eclipse.xtext.Keyword; +import org.eclipse.xtext.ParserRule; +import org.eclipse.xtext.RuleCall; +import org.eclipse.xtext.TerminalRule; +import org.eclipse.xtext.UnorderedGroup; +import org.eclipse.xtext.xbase.lib.IterableExtensions; +import org.eclipse.xtext.xbase.lib.Pair; +import org.eclipse.xtext.xtext.generator.parser.antlr.AntlrGrammarGenUtil; +import org.eclipse.xtext.xtext.generator.parser.antlr.AntlrOptions; +import org.eclipse.xtext.xtext.generator.parser.antlr.ContentAssistGrammarNaming; +import org.eclipse.xtext.xtext.generator.parser.antlr.GrammarNaming; + +import com.google.common.collect.Iterables; +import com.google.inject.Inject; + +/** + * This implementation is strongly based on AntlrContentAssistGrammarGenerator but with a different base class. + * The following extension is supported: + * + * A datatype grammar rule containing only one ID terminal rule can be annotated + * with @KeywordRule annotation provided a list of words so only these words can + * be accepted by this rule. + * + * Example: + * + * / + * * @KeywordRule(visible, invisible) + * * / + * VisibleKind returns VisibleKind: + * ID + * ; + * + * The above rule will accept only 'visible' and 'invisible' identifiers. + * This rule in ASMD is called a keyword rule because it is intended to replace + * usages of keywords which shall not be reserved words in the language. + * Reserved words are words that are not allowed to be used in identifiers. + * + * The above example can therefore replace the following enumeration: + * + * enum VisibleKind : + * VISIBLE = "visible" + * | INVISIBLE = "invisible" + * ; + * + * Please note that a corresponding value converter is needed. + * + * Implementation remark: + * - This template will insert validating semantic predicates in the rule + * - If the rule is used from an alternative a gated semantic predicate will + * be used in the alternative + * - Error messages will be adjusted correspondingly + */ +// CHECKSTYLE:CONSTANTS-OFF +@SuppressWarnings({"PMD.UnusedFormalParameter", "nls"}) +public class AnnotationAwareAntlrContentAssistGrammarGenerator extends AbstractAnnotationAwareAntlrGrammarGenerator { + + @Inject + private ContentAssistGrammarNaming naming; + + @Override + protected GrammarNaming getGrammarNaming() { + return this.naming; + } + + @Override + protected boolean isParserBackTracking(final Grammar it, final AntlrOptions options) { + return super.isParserBackTracking(it, options) || !GrammarUtil.getAllPredicatedElements(it).isEmpty(); + } + + @Override + protected String compileParserImports(final Grammar it, final AntlrOptions options) { + StringConcatenation builder = new StringConcatenation(); + if (!isCombinedGrammar()) { + builder.append("import java.util.Map;"); + builder.newLine(); + builder.append("import java.util.HashMap;"); + builder.newLine(); + } + builder.newLine(); + builder.append("import java.io.InputStream;"); + builder.newLine(); + builder.append("import org.eclipse.xtext.*;"); + builder.newLine(); + builder.append("import org.eclipse.xtext.parser.*;"); + builder.newLine(); + builder.append("import org.eclipse.xtext.parser.impl.*;"); + builder.newLine(); + builder.append("import org.eclipse.emf.ecore.util.EcoreUtil;"); + builder.newLine(); + builder.append("import org.eclipse.emf.ecore.EObject;"); + builder.newLine(); + builder.append("import org.eclipse.xtext.parser.antlr.XtextTokenStream;"); + builder.newLine(); + builder.append("import org.eclipse.xtext.parser.antlr.XtextTokenStream.HiddenTokens;"); + builder.newLine(); + builder.append("import "); + builder.append(getGrammarNaming().getInternalParserSuperClass(it).getName()); + builder.append(";"); + builder.newLineIfNotEmpty(); + builder.append("import org.eclipse.xtext.ide.editor.contentassist.antlr.internal.DFA;"); + builder.newLine(); + builder.append("import "); + builder.append(_grammarAccessExtensions.getGrammarAccess(it).getName()); + builder.append(";"); + builder.newLineIfNotEmpty(); + builder.append(super.compileParserImports(it, options)); + builder.newLineIfNotEmpty(); + builder.newLine(); + return builder.toString(); + } + + @Override + protected String compileParserMembers(final Grammar it, final AntlrOptions options) { + StringConcatenation builder = new StringConcatenation(); + builder.append("@"); + if (isCombinedGrammar()) { + builder.append("parser::"); + } + builder.append("members {"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(compileParserMemberDeclarations(it, "protected"), " "); + builder.newLineIfNotEmpty(); + if (!isCombinedGrammar()) { + builder.append(" "); + builder.append("private final Map tokenNameToValue = new HashMap();"); + builder.newLine(); + builder.newLine(); + builder.append(" "); + builder.append("{"); + builder.newLine(); + List sortedKeywords = IterableExtensions.sortBy(IterableExtensions.sort(GrammarUtil.getAllKeywords(it)), String::length); + for (final String kw : sortedKeywords) { + builder.append(" "); + builder.append(" "); + builder.append("tokenNameToValue.put(\""); + builder.append(this.keywordHelper.getRuleName(kw), " "); + builder.append("\", \"\'"); + builder.append(AntlrGrammarGenUtil.toStringInAntlrAction(kw).replace("$", "\\u0024"), " "); + builder.append("\'\");"); + builder.newLineIfNotEmpty(); + } + builder.append(" "); + builder.append("}"); + builder.newLine(); + } + builder.newLine(); + builder.append(" "); + builder.append(compileParserSetTokenStreamMethod(), " "); + builder.newLineIfNotEmpty(); + builder.newLine(); + builder.append(" "); + builder.append("public void setPredicates("); + builder.append(this.predicatesNaming.getSemanticPredicatesSimpleName(it), " "); + builder.append(" predicates) {"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("this.predicates = predicates;"); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.newLine(); + builder.append(" "); + builder.append("public void setGrammarAccess("); + builder.append(_grammarAccessExtensions.getGrammarAccess(it).getSimpleName(), " "); + builder.append(" grammarAccess) {"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("this.grammarAccess = grammarAccess;"); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.newLine(); + builder.append("public void setParserContext(ParserContext parserContext) {"); + builder.newLine(); + builder.append(" "); + builder.append("this.parserContext = parserContext;"); + builder.newLine(); + builder.append("}"); + builder.newLine(); + builder.newLine(); + builder.append(" "); + builder.append("@Override"); + builder.newLine(); + builder.append(" "); + builder.append("protected Grammar getGrammar() {"); + builder.newLine(); + builder.append(" "); + builder.append("return grammarAccess.getGrammar();"); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.newLine(); + builder.append(" "); + builder.append("@Override"); + builder.newLine(); + builder.append(" "); + builder.append("protected String getValueForTokenName(String tokenName) {"); + builder.newLine(); + if (isCombinedGrammar()) { + builder.append(" "); + builder.append("return tokenName;"); + builder.newLine(); + } else { + builder.append(" "); + builder.append("String result = tokenNameToValue.get(tokenName);"); + builder.newLine(); + builder.append(" "); + builder.append("if (result == null)"); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("result = tokenName;"); + builder.newLine(); + builder.append(" "); + builder.append("return result;"); + builder.newLine(); + } + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.append("}"); + builder.newLine(); + return builder.toString(); + } + + @Override + protected CharSequence compileRules(final Grammar g, final AntlrOptions options) { + StringConcatenation builder = new StringConcatenation(); + Iterable rules = Iterables.concat(GrammarUtil.allParserRules(g), GrammarUtil.allEnumRules(g)); + Iterable elements = Iterables.concat(rules, GrammarUtil.getAllAlternatives(g)); + elements = Iterables.concat(elements, GrammarUtil.getAllGroups(g)); + elements = Iterables.concat(elements, GrammarUtil.getAllUnorderedGroups(g)); + Iterable filtered = IterableExtensions.filter(Iterables.concat(elements, GrammarUtil.getAllAssignments(g)), + it -> _grammarAccessExtensions.isCalled(GrammarUtil.containingRule(it), g)); + for (final EObject rule : filtered) { + builder.newLine(); + builder.append(compileRule(rule, g, options)); + builder.newLineIfNotEmpty(); + } + if (isCombinedGrammar()) { + builder.append(compileTerminalRules(g, options)); + builder.newLineIfNotEmpty(); + } + return builder; + } + + @Override + @SuppressWarnings("checkstyle:MethodName") + protected CharSequence _compileRule(final ParserRule it, final Grammar grammar, final AntlrOptions options) { + StringConcatenation builder = new StringConcatenation(); + if (AntlrGrammarGenUtil.isValidEntryRule(it)) { + builder.append("// Entry rule "); + builder.append(_grammarAccessExtensions.entryRuleName(it)); + builder.newLineIfNotEmpty(); + builder.append(_grammarAccessExtensions.entryRuleName(it)); + builder.newLineIfNotEmpty(); + if (it.isDefinesHiddenTokens()) { + builder.append("@init {"); + builder.newLine(); + builder.append(" "); + builder.append(compileInitHiddenTokens(it, options), " "); + builder.newLineIfNotEmpty(); + builder.append("}"); + builder.newLine(); + } + builder.append(":"); + builder.newLine(); + builder.append("{ before(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it))); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(_grammarAccessExtensions.ruleName(it), " "); + builder.newLineIfNotEmpty(); + builder.append("{ after(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it))); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("EOF"); + builder.newLine(); + builder.append(";"); + builder.newLine(); + if (it.isDefinesHiddenTokens()) { + builder.append("finally {"); + builder.newLine(); + builder.append(" "); + builder.append(compileRestoreHiddenTokens(it, options), " "); + builder.newLineIfNotEmpty(); + builder.append("}"); + builder.newLine(); + } + } + builder.newLine(); + builder.append("// Rule "); + builder.append(AntlrGrammarGenUtil.getOriginalElement(it).getName()); + builder.newLineIfNotEmpty(); + builder.append(_grammarAccessExtensions.ruleName(it)); + builder.newLineIfNotEmpty(); + if (this.annotations.hasNoBacktrackAnnotation(it)) { + builder.append(" "); + builder.append("// Enclosing rule was annotated with @NoBacktrack"); + builder.newLine(); + builder.append(" "); + builder.append("options { backtrack=false; }"); + builder.newLine(); + } + builder.append(" "); + builder.append("@init {"); + builder.newLine(); + builder.append(" "); + builder.append(compileInitHiddenTokens(it, options), " "); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("int stackSize = keepStackSize();"); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.append(" "); + builder.append(":"); + builder.newLine(); + builder.append(" "); + if (this.annotations.hasValidatingPredicate(it)) { + builder.append(this.annotations.generateValidatingPredicate(it), " "); + } + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(ebnf(it.getAlternatives(), options, false), " "); + builder.newLineIfNotEmpty(); + builder.append(";"); + builder.newLine(); + builder.append("finally {"); + builder.newLine(); + builder.append(" "); + builder.append("restoreStackSize(stackSize);"); + builder.newLine(); + builder.append(" "); + builder.append(compileRestoreHiddenTokens(it, options), " "); + builder.newLineIfNotEmpty(); + builder.append("}"); + builder.newLine(); + return builder; + } + + @Override + @SuppressWarnings("checkstyle:MethodName") + protected CharSequence _compileRule(final EnumRule it, final Grammar grammar, final AntlrOptions options) { + StringConcatenation builder = new StringConcatenation(); + builder.append("// Rule "); + builder.append(AntlrGrammarGenUtil.getOriginalElement(it).getName()); + builder.newLineIfNotEmpty(); + builder.append(_grammarAccessExtensions.ruleName(it)); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("@init {"); + builder.newLine(); + builder.append(" "); + builder.append("int stackSize = keepStackSize();"); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.append(":"); + builder.newLine(); + builder.append(" "); + builder.append(ebnf(it.getAlternatives(), options, false), " "); + builder.newLineIfNotEmpty(); + builder.append(";"); + builder.newLine(); + builder.append("finally {"); + builder.newLine(); + builder.append(" "); + builder.append("restoreStackSize(stackSize);"); + builder.newLine(); + builder.append("}"); + builder.newLine(); + return builder; + } + + @SuppressWarnings("checkstyle:MethodName") + protected CharSequence _compileRule(final Alternatives it, final Grammar grammar, final AntlrOptions options) { + StringConcatenation builder = new StringConcatenation(); + builder.append(AntlrGrammarGenUtil.getContentAssistRuleName(GrammarUtil.containingRule(it))); + builder.append("__"); + builder.append(_grammarAccessExtensions.gaElementIdentifier(AntlrGrammarGenUtil.getOriginalElement(it))); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("@init {"); + builder.newLine(); + builder.append(" "); + builder.append("int stackSize = keepStackSize();"); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.append(":"); + builder.newLine(); + builder.append(" "); + boolean hasElements = false; + for (final AbstractElement element : it.getElements()) { + if (!hasElements) { + hasElements = true; + } else { + builder.appendImmediate("\n|", " "); + } + builder.append(ebnf(element, options, false), " "); + } + builder.newLineIfNotEmpty(); + builder.append(";"); + builder.newLine(); + builder.append("finally {"); + builder.newLine(); + builder.append(" "); + builder.append("restoreStackSize(stackSize);"); + builder.newLine(); + builder.append("}"); + builder.newLine(); + return builder; + } + + @SuppressWarnings("checkstyle:MethodName") + protected CharSequence _compileRule(final Assignment it, final Grammar grammar, final AntlrOptions options) { + StringConcatenation builder = new StringConcatenation(); + builder.append(AntlrGrammarGenUtil.getContentAssistRuleName(GrammarUtil.containingRule(it))); + builder.append("__"); + builder.append(_grammarAccessExtensions.gaElementIdentifier(AntlrGrammarGenUtil.getOriginalElement(it))); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("@init {"); + builder.newLine(); + builder.append(" "); + builder.append("int stackSize = keepStackSize();"); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.append(":"); + builder.newLine(); + builder.append(" "); + builder.append(assignmentEbnf(it.getTerminal(), it, options, false), " "); + builder.newLineIfNotEmpty(); + builder.append(";"); + builder.newLine(); + builder.append("finally {"); + builder.newLine(); + builder.append(" "); + builder.append("restoreStackSize(stackSize);"); + builder.newLine(); + builder.append("}"); + builder.newLine(); + return builder; + } + + @SuppressWarnings("checkstyle:MethodName") + protected CharSequence _compileRule(final UnorderedGroup it, final Grammar grammar, final AntlrOptions options) { + final boolean hasMandatoryContent = IterableExtensions.exists(it.getElements(), element -> !GrammarUtil.isOptionalCardinality(element)); + StringConcatenation builder = new StringConcatenation(); + builder.append(AntlrGrammarGenUtil.getContentAssistRuleName(GrammarUtil.containingRule(it))); + builder.append("__"); + builder.append(_grammarAccessExtensions.gaElementIdentifier(AntlrGrammarGenUtil.getOriginalElement(it))); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("@init {"); + builder.newLine(); + builder.append(" "); + builder.append("int stackSize = keepStackSize();"); + builder.newLine(); + builder.append(" "); + builder.append("getUnorderedGroupHelper().enter(grammarAccess."); + builder.append(_grammarAccessExtensions.gaRuleElementAccessor(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append(");"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.append(":"); + builder.newLine(); + builder.append(" "); + builder.append(AntlrGrammarGenUtil.getContentAssistRuleName(GrammarUtil.containingRule(it)), " "); + builder.append("__"); + builder.append(_grammarAccessExtensions.gaElementIdentifier(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append("__0"); + builder.newLineIfNotEmpty(); + if (hasMandatoryContent) { + builder.append(" "); + builder.append("{getUnorderedGroupHelper().canLeave(grammarAccess."); + builder.append(_grammarAccessExtensions.gaRuleElementAccessor(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append(")}?"); + builder.newLineIfNotEmpty(); + } else { + builder.append(" "); + builder.append("?"); + builder.newLine(); + } + builder.append(";"); + builder.newLine(); + builder.append("finally {"); + builder.newLine(); + builder.append(" "); + builder.append("getUnorderedGroupHelper().leave(grammarAccess."); + builder.append(_grammarAccessExtensions.gaRuleElementAccessor(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append(");"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("restoreStackSize(stackSize);"); + builder.newLine(); + builder.append("}"); + builder.newLine(); + builder.newLine(); + builder.append(ruleImpl(it, grammar, options)); + builder.newLineIfNotEmpty(); + builder.newLine(); + builder.append(ruleImpl(it, grammar, options, 0)); + builder.newLineIfNotEmpty(); + return builder; + } + + @SuppressWarnings("checkstyle:MethodName") + protected CharSequence _compileRule(final Group it, final Grammar grammar, final AntlrOptions options) { + StringConcatenation builder = new StringConcatenation(); + builder.append(ruleImpl(it, grammar, options, 0)); + builder.newLineIfNotEmpty(); + return builder; + } + + protected CharSequence ruleImpl(final UnorderedGroup it, final Grammar grammar, final AntlrOptions options) { + StringConcatenation builder = new StringConcatenation(); + builder.append(AntlrGrammarGenUtil.getContentAssistRuleName(GrammarUtil.containingRule(it))); + builder.append("__"); + builder.append(_grammarAccessExtensions.gaElementIdentifier(AntlrGrammarGenUtil.getOriginalElement(it))); + builder.append("__Impl"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("@init {"); + builder.newLine(); + builder.append(" "); + builder.append("int stackSize = keepStackSize();"); + builder.newLine(); + builder.append(" "); + builder.append("boolean selected = false;"); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.append(":"); + builder.newLine(); + builder.append(" "); + builder.append("("); + builder.newLine(); + boolean hasElements = false; + for (final Pair element : IterableExtensions.indexed(it.getElements())) { + if (!hasElements) { + hasElements = true; + } else { + builder.appendImmediate("|", " "); + } + builder.append(" "); + builder.append("("); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("{getUnorderedGroupHelper().canSelect(grammarAccess."); + builder.append(_grammarAccessExtensions.gaRuleElementAccessor(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append(", "); + builder.append(element.getKey(), " "); + builder.append(")}?=>("); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(" "); + builder.append("{"); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("getUnorderedGroupHelper().select(grammarAccess."); + builder.append(_grammarAccessExtensions.gaRuleElementAccessor(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append(", "); + builder.append(element.getKey(), " "); + builder.append(");"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("{"); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("selected = true;"); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("("); + builder.newLine(); + if (GrammarUtil.isMultipleCardinality(element.getValue())) { + builder.append(" "); + builder.append(" "); + builder.append("("); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append(" "); + builder.append("{ before(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(element.getValue())), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(" "); + builder.append(" "); + builder.append("("); + builder.append(ebnf2(element.getValue(), options, false), " "); + builder.append(")"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(" "); + builder.append(" "); + builder.append("{ after(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(element.getValue())), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(" "); + builder.append(")"); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("("); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append(" "); + builder.append("{ before(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(element.getValue())), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(" "); + builder.append(" "); + builder.append("(("); + builder.append(ebnf2(element.getValue(), options, false), " "); + builder.append(")=>"); + builder.append(ebnf2(element.getValue(), options, false), " "); + builder.append(")*"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(" "); + builder.append(" "); + builder.append("{ after(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(element.getValue())), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(" "); + builder.append(")"); + builder.newLine(); + } else { + builder.append(" "); + builder.append(" "); + builder.append("{ before(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(element.getValue())), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(" "); + builder.append("("); + builder.append(ebnf2(element.getValue(), options, false), " "); + builder.append(")"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(" "); + builder.append("{ after(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(element.getValue())), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + } + builder.append(" "); + builder.append(" "); + builder.append(")"); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append(")"); + builder.newLine(); + builder.append(" "); + builder.append(")"); + builder.newLine(); + } + builder.append(" "); + builder.append(")"); + builder.newLine(); + builder.append(";"); + builder.newLine(); + builder.append("finally {"); + builder.newLine(); + builder.append(" "); + builder.append("if (selected)"); + builder.newLine(); + builder.append(" "); + builder.append("getUnorderedGroupHelper().returnFromSelection(grammarAccess."); + builder.append(_grammarAccessExtensions.gaRuleElementAccessor(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append(");"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("restoreStackSize(stackSize);"); + builder.newLine(); + builder.append("}"); + builder.newLine(); + return builder; + } + + protected CharSequence ruleImpl(final UnorderedGroup it, final Grammar grammar, final AntlrOptions options, final int index) { + StringConcatenation builder = new StringConcatenation(); + builder.append(AntlrGrammarGenUtil.getContentAssistRuleName(GrammarUtil.containingRule(it))); + builder.append("__"); + builder.append(_grammarAccessExtensions.gaElementIdentifier(AntlrGrammarGenUtil.getOriginalElement(it))); + builder.append("__"); + builder.append(index); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("@init {"); + builder.newLine(); + builder.append(" "); + builder.append("int stackSize = keepStackSize();"); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.append(":"); + builder.newLine(); + builder.append(" "); + builder.append(AntlrGrammarGenUtil.getContentAssistRuleName(GrammarUtil.containingRule(it)), " "); + builder.append("__"); + builder.append(_grammarAccessExtensions.gaElementIdentifier(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append("__Impl"); + builder.newLineIfNotEmpty(); + if (it.getElements().size() > index + 1) { + builder.append(" "); + builder.append(AntlrGrammarGenUtil.getContentAssistRuleName(GrammarUtil.containingRule(it)), " "); + builder.append("__"); + builder.append(_grammarAccessExtensions.gaElementIdentifier(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append("__"); + builder.append(index + 1, " "); + builder.append("?"); + builder.newLineIfNotEmpty(); + } + builder.append(";"); + builder.newLine(); + builder.append("finally {"); + builder.newLine(); + builder.append(" "); + builder.append("restoreStackSize(stackSize);"); + builder.newLine(); + builder.append("}"); + builder.newLine(); + builder.newLine(); + if (it.getElements().size() > index + 1) { + builder.append(ruleImpl(it, grammar, options, index + 1)); + builder.newLineIfNotEmpty(); + } + return builder; + } + + protected CharSequence ruleImpl(final Group it, final Grammar grammar, final AntlrOptions options, final int index) { + StringConcatenation builder = new StringConcatenation(); + builder.append(AntlrGrammarGenUtil.getContentAssistRuleName(GrammarUtil.containingRule(it))); + builder.append("__"); + builder.append(_grammarAccessExtensions.gaElementIdentifier(AntlrGrammarGenUtil.getOriginalElement(it))); + builder.append("__"); + builder.append(index); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("@init {"); + builder.newLine(); + builder.append(" "); + builder.append("int stackSize = keepStackSize();"); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.append(":"); + builder.newLine(); + builder.append(" "); + builder.append(AntlrGrammarGenUtil.getContentAssistRuleName(GrammarUtil.containingRule(it)), " "); + builder.append("__"); + builder.append(_grammarAccessExtensions.gaElementIdentifier(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append("__"); + builder.append(index, " "); + builder.append("__Impl"); + builder.newLineIfNotEmpty(); + if (it.getElements().size() > index + 1) { + builder.append(" "); + builder.append(AntlrGrammarGenUtil.getContentAssistRuleName(GrammarUtil.containingRule(it)), " "); + builder.append("__"); + builder.append(_grammarAccessExtensions.gaElementIdentifier(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append("__"); + builder.append(index + 1, " "); + builder.newLineIfNotEmpty(); + } + builder.append(";"); + builder.newLine(); + builder.append("finally {"); + builder.newLine(); + builder.append(" "); + builder.append("restoreStackSize(stackSize);"); + builder.newLine(); + builder.append("}"); + builder.newLine(); + builder.newLine(); + builder.append(AntlrGrammarGenUtil.getContentAssistRuleName(GrammarUtil.containingRule(it))); + builder.append("__"); + builder.append(_grammarAccessExtensions.gaElementIdentifier(AntlrGrammarGenUtil.getOriginalElement(it))); + builder.append("__"); + builder.append(index); + builder.append("__Impl"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("@init {"); + builder.newLine(); + builder.append(" "); + builder.append("int stackSize = keepStackSize();"); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.append(":"); + builder.newLine(); + builder.append(ebnf(it.getElements().get(index), options, false)); + builder.newLineIfNotEmpty(); + builder.append(";"); + builder.newLine(); + builder.append("finally {"); + builder.newLine(); + builder.append(" "); + builder.append("restoreStackSize(stackSize);"); + builder.newLine(); + builder.append("}"); + builder.newLine(); + builder.newLine(); + if (it.getElements().size() > index + 1) { + builder.append(ruleImpl(it, grammar, options, index + 1)); + builder.newLineIfNotEmpty(); + } + return builder; + } + + @Override + protected String ebnf(final AbstractElement it, final AntlrOptions options, final boolean supportsActions) { + StringConcatenation builder = new StringConcatenation(); + if (!GrammarUtil.isOptionalCardinality(it) && GrammarUtil.isMultipleCardinality(it)) { + builder.append("("); + builder.newLine(); + builder.append(" "); + builder.append("("); + builder.newLine(); + builder.append(" "); + builder.append("{ before(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append(paramConfig(it), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("("); + builder.append(ebnf2(it, options, supportsActions), " "); + builder.append(")"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("{ after(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append(paramConfig(it), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(")"); + builder.newLine(); + builder.append(" "); + builder.append("("); + builder.newLine(); + builder.append(" "); + builder.append("{ before(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append(paramConfig(it), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("("); + builder.append(ebnf2(it, options, supportsActions), " "); + builder.append(")*"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("{ after(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append(paramConfig(it), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(")"); + builder.newLine(); + builder.append(")"); + builder.newLine(); + } else { + builder.append("("); + builder.newLine(); + builder.append(" "); + builder.append("{ before(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append(paramConfig(it), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(" "); + if (mustBeParenthesized(it)) { + builder.append("("); + builder.append(ebnf2(it, options, supportsActions), " "); + builder.append(")"); + } else { + builder.append(ebnf2(it, options, supportsActions), " "); + } + builder.append(it.getCardinality(), " "); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("{ after(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append(paramConfig(it), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(")"); + builder.newLine(); + } + return builder.toString(); + } + + protected CharSequence paramConfig(final AbstractElement it) { + StringConcatenation builder = new StringConcatenation(); + if (GrammarUtil.containingRule(it).getAlternatives() == it + && ParserRule.class.isInstance(GrammarUtil.containingRule(it)) + && !((ParserRule) AntlrGrammarGenUtil.getOriginalElement(GrammarUtil.containingRule(it))).getParameters().isEmpty()) { + builder.append(", "); + builder.append(AntlrGrammarGenUtil.getParameterConfig((ParserRule) GrammarUtil.containingRule(it))); + builder.newLineIfNotEmpty(); + } + return builder; + } + + @Override + @SuppressWarnings("checkstyle:MethodName") + protected String _assignmentEbnf(final AbstractElement it, final Assignment assignment, final AntlrOptions options, final boolean supportsActions) { + StringConcatenation builder = new StringConcatenation(); + builder.append("("); + builder.newLine(); + builder.append(" "); + builder.append("{ before(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(ebnf(it, options, supportsActions), " "); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("{ after(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(")"); + builder.newLine(); + return builder.toString(); + } + + @Override + @SuppressWarnings("checkstyle:MethodName") + protected String _assignmentEbnf(final CrossReference it, final Assignment assignment, final AntlrOptions options, final boolean supportsActions) { + StringConcatenation builder = new StringConcatenation(); + builder.append("("); + builder.newLine(); + builder.append(" "); + builder.append("{ before(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(crossrefEbnf(it.getTerminal(), it, supportsActions), " "); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("{ after(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(")"); + builder.newLine(); + return builder.toString(); + } + + @Override + @SuppressWarnings("checkstyle:MethodName") + protected String _assignmentEbnf(final Alternatives it, final Assignment assignment, final AntlrOptions options, final boolean supportsActions) { + StringConcatenation builder = new StringConcatenation(); + builder.append("("); + builder.newLine(); + builder.append(" "); + builder.append("{ before(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("("); + builder.append(AntlrGrammarGenUtil.getContentAssistRuleName(GrammarUtil.containingRule(it)), " "); + builder.append("__"); + builder.append(_grammarAccessExtensions.gaElementIdentifier(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append(")"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("{ after(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(")"); + builder.newLine(); + return builder.toString(); + } + + @Override + @SuppressWarnings("checkstyle:MethodName") + protected String _assignmentEbnf(final RuleCall it, final Assignment assignment, final AntlrOptions options, final boolean supportsActions) { + StringConcatenation builder = new StringConcatenation(); + builder.append("("); + builder.newLine(); + builder.append(" "); + builder.append("{ before(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(_grammarAccessExtensions.ruleName(it.getRule()), " "); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("{ after(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(")"); + builder.newLine(); + return builder.toString(); + } + + @Override + @SuppressWarnings("checkstyle:MethodName") + protected String _crossrefEbnf(final RuleCall it, final CrossReference ref, final boolean supportActions) { + StringConcatenation builder = new StringConcatenation(); + builder.append("("); + builder.newLine(); + builder.append(" "); + builder.append("{ before(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(crossrefEbnf(it.getRule(), it, ref, supportActions), " "); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("{ after(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(")"); + builder.newLine(); + return builder.toString(); + } + + @Override + @SuppressWarnings("checkstyle:MethodName") + protected String _crossrefEbnf(final Keyword it, final CrossReference ref, final boolean supportActions) { + StringConcatenation builder = new StringConcatenation(); + builder.append("("); + builder.newLine(); + builder.append(" "); + builder.append("{ before(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(super._crossrefEbnf(it, ref, supportActions), " "); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("{ after(grammarAccess."); + builder.append(_grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append("); }"); + builder.newLineIfNotEmpty(); + builder.append(")"); + builder.newLine(); + return builder.toString(); + } + + @SuppressWarnings("checkstyle:MethodName") + protected String _crossrefEbnf(final TerminalRule it, final RuleCall call, final CrossReference ref, final boolean supportActions) { + return _grammarAccessExtensions.ruleName(it); + } + + @SuppressWarnings("checkstyle:MethodName") + protected String _crossrefEbnf(final EnumRule it, final RuleCall call, final CrossReference ref, final boolean supportActions) { + return _grammarAccessExtensions.ruleName(it); + } + + @SuppressWarnings("checkstyle:MethodName") + protected String _crossrefEbnf(final AbstractRule it, final RuleCall call, final CrossReference ref, final boolean supportActions) { + if (GrammarUtil.isDatatypeRule(AntlrGrammarGenUtil.getOriginalElement(it))) { + return _grammarAccessExtensions.ruleName(it); + } + throw new IllegalArgumentException(it.getName() + " is not a datatype rule"); + } + + @Override + @SuppressWarnings("checkstyle:MethodName") + protected String _ebnf2(final Alternatives it, final AntlrOptions options, final boolean supportActions) { + StringConcatenation builder = new StringConcatenation(); + builder.append(AntlrGrammarGenUtil.getContentAssistRuleName(GrammarUtil.containingRule(it))); + builder.append("__"); + builder.append(_grammarAccessExtensions.gaElementIdentifier(AntlrGrammarGenUtil.getOriginalElement(it))); + return builder.toString(); + } + + @Override + @SuppressWarnings("checkstyle:MethodName") + protected String _ebnf2(final Assignment it, final AntlrOptions options, final boolean supportActions) { + StringConcatenation builder = new StringConcatenation(); + builder.append(AntlrGrammarGenUtil.getContentAssistRuleName(GrammarUtil.containingRule(it))); + builder.append("__"); + builder.append(_grammarAccessExtensions.gaElementIdentifier(AntlrGrammarGenUtil.getOriginalElement(it))); + return builder.toString(); + } + + @Override + @SuppressWarnings("checkstyle:MethodName") + protected String _ebnf2(final Group it, final AntlrOptions options, final boolean supportActions) { + StringConcatenation builder = new StringConcatenation(); + builder.append(AntlrGrammarGenUtil.getContentAssistRuleName(GrammarUtil.containingRule(it))); + builder.append("__"); + builder.append(_grammarAccessExtensions.gaElementIdentifier(AntlrGrammarGenUtil.getOriginalElement(it))); + builder.append("__0"); + return builder.toString(); + } + + @Override + @SuppressWarnings("checkstyle:MethodName") + protected String _ebnf2(final UnorderedGroup it, final AntlrOptions options, final boolean supportActions) { + StringConcatenation builder = new StringConcatenation(); + builder.append(AntlrGrammarGenUtil.getContentAssistRuleName(GrammarUtil.containingRule(it))); + builder.append("__"); + builder.append(_grammarAccessExtensions.gaElementIdentifier(AntlrGrammarGenUtil.getOriginalElement(it))); + return builder.toString(); + } + + @Override + @SuppressWarnings("checkstyle:MethodName") + protected String _ebnf2(final RuleCall it, final AntlrOptions options, final boolean supportActions) { + return _grammarAccessExtensions.ruleName(it.getRule()); + } + + @Override + protected boolean shouldBeSkipped(final TerminalRule it, final Grammar grammar) { + return false; + } + + @Override + protected CharSequence compileRule(final Object it, final Grammar grammar, final AntlrOptions options) { + if (it instanceof Alternatives) { + return _compileRule((Alternatives) it, grammar, options); + } else if (it instanceof Group) { + return _compileRule((Group) it, grammar, options); + } else if (it instanceof UnorderedGroup) { + return _compileRule((UnorderedGroup) it, grammar, options); + } else if (it instanceof Assignment) { + return _compileRule((Assignment) it, grammar, options); + } else if (it instanceof EnumRule) { + return _compileRule((EnumRule) it, grammar, options); + } else if (it instanceof ParserRule) { + return _compileRule((ParserRule) it, grammar, options); + } else if (it instanceof TerminalRule) { + return _compileRule((TerminalRule) it, grammar, options); + } else if (it instanceof String) { + return _compileRule((String) it, grammar, options); + } else { + throw new IllegalArgumentException("Unhandled parameter types: " + + Arrays. asList(it, grammar, options).toString()); + } + } + + @Override + protected String assignmentEbnf(final AbstractElement it, final Assignment assignment, final AntlrOptions options, final boolean supportsActions) { + if (it instanceof Alternatives) { + return _assignmentEbnf((Alternatives) it, assignment, options, supportsActions); + } else if (it instanceof Group) { + return _assignmentEbnf((Group) it, assignment, options, supportsActions); + } else if (it instanceof Action) { + return _assignmentEbnf((Action) it, assignment, options, supportsActions); + } else if (it instanceof Assignment) { + return _assignmentEbnf((Assignment) it, assignment, options, supportsActions); + } else if (it instanceof CrossReference) { + return _assignmentEbnf((CrossReference) it, assignment, options, supportsActions); + } else if (it instanceof RuleCall) { + return _assignmentEbnf((RuleCall) it, assignment, options, supportsActions); + } else if (it != null) { + return _assignmentEbnf(it, assignment, options, supportsActions); + } else { + throw new IllegalArgumentException("Unhandled parameter types: " + + Arrays. asList(it, assignment, options, supportsActions).toString()); + } + } + + @Override + protected String crossrefEbnf(final AbstractElement it, final CrossReference ref, final boolean supportActions) { + if (it instanceof Alternatives) { + return _crossrefEbnf((Alternatives) it, ref, supportActions); + } else if (it instanceof Keyword) { + return _crossrefEbnf((Keyword) it, ref, supportActions); + } else if (it instanceof RuleCall) { + return _crossrefEbnf((RuleCall) it, ref, supportActions); + } else if (it != null) { + return _crossrefEbnf(it, ref, supportActions); + } else { + throw new IllegalArgumentException("Unhandled parameter types: " + + Arrays. asList(it, ref, supportActions).toString()); + } + } + + @Override + protected String crossrefEbnf(final AbstractRule it, final RuleCall call, final CrossReference ref, final boolean supportActions) { + if (it instanceof EnumRule) { + return _crossrefEbnf((EnumRule) it, call, ref, supportActions); + } else if (it instanceof TerminalRule) { + return _crossrefEbnf((TerminalRule) it, call, ref, supportActions); + } else if (it != null) { + return _crossrefEbnf(it, call, ref, supportActions); + } else { + throw new IllegalArgumentException("Unhandled parameter types: " + + Arrays. asList(it, call, ref, supportActions).toString()); + } + } + + @Override + protected String ebnf2(final AbstractElement it, final AntlrOptions options, final boolean supportActions) { + if (it instanceof Alternatives) { + return _ebnf2((Alternatives) it, options, supportActions); + } else if (it instanceof Group) { + return _ebnf2((Group) it, options, supportActions); + } else if (it instanceof UnorderedGroup) { + return _ebnf2((UnorderedGroup) it, options, supportActions); + } else if (it instanceof Action) { + return _ebnf2((Action) it, options, supportActions); + } else if (it instanceof Assignment) { + return _ebnf2((Assignment) it, options, supportActions); + } else if (it instanceof EnumLiteralDeclaration) { + return _ebnf2((EnumLiteralDeclaration) it, options, supportActions); + } else if (it instanceof Keyword) { + return _ebnf2((Keyword) it, options, supportActions); + } else if (it instanceof RuleCall) { + return _ebnf2((RuleCall) it, options, supportActions); + } else if (it != null) { + return _ebnf2(it, options, supportActions); + } else { + throw new IllegalArgumentException("Unhandled parameter types: " + + Arrays. asList(it, options, supportActions).toString()); + } + } +} +// CHECKSTYLE:CONSTANTS-ON diff --git a/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AnnotationAwareAntlrContentAssistGrammarGenerator.xtend b/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AnnotationAwareAntlrContentAssistGrammarGenerator.xtend deleted file mode 100644 index 977e614c3e..0000000000 --- a/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AnnotationAwareAntlrContentAssistGrammarGenerator.xtend +++ /dev/null @@ -1,489 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ - -package com.avaloq.tools.ddk.xtext.generator.parser.antlr - -import com.google.inject.Inject -import org.eclipse.xtext.AbstractElement -import org.eclipse.xtext.AbstractRule -import org.eclipse.xtext.Alternatives -import org.eclipse.xtext.Assignment -import org.eclipse.xtext.CrossReference -import org.eclipse.xtext.EnumRule -import org.eclipse.xtext.Grammar -import org.eclipse.xtext.Group -import org.eclipse.xtext.Keyword -import org.eclipse.xtext.ParserRule -import org.eclipse.xtext.RuleCall -import org.eclipse.xtext.TerminalRule -import org.eclipse.xtext.UnorderedGroup -import org.eclipse.xtext.xtext.generator.parser.antlr.AntlrOptions -import org.eclipse.xtext.xtext.generator.parser.antlr.ContentAssistGrammarNaming - -import static extension org.eclipse.xtext.GrammarUtil.* -import static extension org.eclipse.xtext.xtext.generator.parser.antlr.AntlrGrammarGenUtil.* - -/** - * This implementation is strongly based on AntlrContentAssistGrammarGenerator but with a different base class. - * The following extension is supported: - * - * A datatype grammar rule containing only one ID terminal rule can be annotated - * with @KeywordRule annotation provided a list of words so only these words can - * be accepted by this rule. - * - * Example: - * - * /** - * * @KeywordRule(visible, invisible) - * * / - * VisibleKind returns VisibleKind: - * ID - * ; - * - * The above rule will accept only 'visible' and 'invisible' identifiers. - * This rule in ASMD is called a keyword rule because it is intended to replace - * usages of keywords which shall not be reserved words in the language. - * Reserved words are words that are not allowed to be used in identifiers. - * - * The above example can therefore replace the following enumeration: - * - * enum VisibleKind : - * VISIBLE = "visible" - * | INVISIBLE = "invisible" - * ; - * - * Please note that a corresponding value converter is needed. - * - * Implementation remark: - * - This template will insert validating semantic predicates in the rule - * - If the rule is used from an alternative a gated semantic predicate will - * be used in the alternative - * - Error messages will be adjusted correspondingly - */ -class AnnotationAwareAntlrContentAssistGrammarGenerator extends AbstractAnnotationAwareAntlrGrammarGenerator { - @Inject - extension ContentAssistGrammarNaming naming - - override protected getGrammarNaming() { - naming - } - - override protected isParserBackTracking(Grammar it, AntlrOptions options) { - super.isParserBackTracking(it, options) || !allPredicatedElements.isEmpty - } - - override protected compileParserImports(Grammar it, AntlrOptions options) ''' - «IF !combinedGrammar» - import java.util.Map; - import java.util.HashMap; - «ENDIF» - - import java.io.InputStream; - import org.eclipse.xtext.*; - import org.eclipse.xtext.parser.*; - import org.eclipse.xtext.parser.impl.*; - import org.eclipse.emf.ecore.util.EcoreUtil; - import org.eclipse.emf.ecore.EObject; - import org.eclipse.xtext.parser.antlr.XtextTokenStream; - import org.eclipse.xtext.parser.antlr.XtextTokenStream.HiddenTokens; - import «grammarNaming.getInternalParserSuperClass(it).name»; - import org.eclipse.xtext.ide.editor.contentassist.antlr.internal.DFA; - import «grammarAccess.name»; - «super.compileParserImports(it, options)» - - ''' - - override protected compileParserMembers(Grammar it, AntlrOptions options) ''' - @«IF combinedGrammar»parser::«ENDIF»members { - «compileParserMemberDeclarations("protected")» - «IF !combinedGrammar» - private final Map tokenNameToValue = new HashMap(); - - { - «FOR kw: allKeywords.sort.sortBy[length]» - tokenNameToValue.put("«keywordHelper.getRuleName(kw)»", "'«kw.toStringInAntlrAction.replace('$', "\\u0024")»'"); - «ENDFOR» - } - «ENDIF» - - «compileParserSetTokenStreamMethod» - - public void setPredicates(«getSemanticPredicatesSimpleName» predicates) { - this.predicates = predicates; - } - - public void setGrammarAccess(«grammarAccess.simpleName» grammarAccess) { - this.grammarAccess = grammarAccess; - } - - public void setParserContext(ParserContext parserContext) { - this.parserContext = parserContext; - } - - @Override - protected Grammar getGrammar() { - return grammarAccess.getGrammar(); - } - - @Override - protected String getValueForTokenName(String tokenName) { - «IF combinedGrammar» - return tokenName; - «ELSE» - String result = tokenNameToValue.get(tokenName); - if (result == null) - result = tokenName; - return result; - «ENDIF» - } - } - ''' - - override protected compileRules(Grammar g, AntlrOptions options) ''' - «FOR rule : (g.allParserRules + g.allEnumRules + g.allAlternatives + g.allGroups + g.allUnorderedGroups + g.allAssignments).filter[containingRule.isCalled(g)]» - - «rule.compileRule(g, options)» - «ENDFOR» - «IF isCombinedGrammar» - «g.compileTerminalRules(options)» - «ENDIF» - ''' - - protected override dispatch compileRule(ParserRule it, Grammar grammar, AntlrOptions options) ''' - «IF isValidEntryRule» - // Entry rule «entryRuleName» - «entryRuleName» - «IF definesHiddenTokens» - @init { - «compileInitHiddenTokens(options)» - } - «ENDIF» - : - { before(grammarAccess.«originalElement.grammarElementAccess»); } - «ruleName» - { after(grammarAccess.«originalElement.grammarElementAccess»); } - EOF - ; - «IF definesHiddenTokens» - finally { - «compileRestoreHiddenTokens(options)» - } - «ENDIF» - «ENDIF» - - // Rule «originalElement.name» - «ruleName» - «IF hasNoBacktrackAnnotation» - // Enclosing rule was annotated with @NoBacktrack - options { backtrack=false; } - «ENDIF» - @init { - «compileInitHiddenTokens(options)» - int stackSize = keepStackSize(); - } - : - «IF hasValidatingPredicate»«generateValidatingPredicate»«ENDIF» - «alternatives.ebnf(options, false)» - ; - finally { - restoreStackSize(stackSize); - «compileRestoreHiddenTokens(options)» - } - ''' - - protected override dispatch compileRule(EnumRule it, Grammar grammar, AntlrOptions options) ''' - // Rule «originalElement.name» - «ruleName()» - @init { - int stackSize = keepStackSize(); - } - : - «alternatives.ebnf(options, false)» - ; - finally { - restoreStackSize(stackSize); - } - ''' - - protected def dispatch compileRule(Alternatives it, Grammar grammar, AntlrOptions options) ''' - «containingRule.contentAssistRuleName»__«originalElement.gaElementIdentifier» - @init { - int stackSize = keepStackSize(); - } - : - «FOR element : elements SEPARATOR '\n|'»«element.ebnf(options, false)»«ENDFOR» - ; - finally { - restoreStackSize(stackSize); - } - ''' - - protected def dispatch compileRule(Assignment it, Grammar grammar, AntlrOptions options) ''' - «containingRule.contentAssistRuleName»__«originalElement.gaElementIdentifier» - @init { - int stackSize = keepStackSize(); - } - : - «terminal.assignmentEbnf(it, options, false)» - ; - finally { - restoreStackSize(stackSize); - } - ''' - - protected def dispatch compileRule(UnorderedGroup it, Grammar grammar, AntlrOptions options) { - val hasMandatoryContent = elements.exists[!isOptionalCardinality] - - ''' - «containingRule.contentAssistRuleName»__«originalElement.gaElementIdentifier()» - @init { - int stackSize = keepStackSize(); - getUnorderedGroupHelper().enter(grammarAccess.«originalElement.gaRuleElementAccessor()»); - } - : - «containingRule.contentAssistRuleName»__«originalElement.gaElementIdentifier()»__0 - «IF hasMandatoryContent» - {getUnorderedGroupHelper().canLeave(grammarAccess.«originalElement.gaRuleElementAccessor()»)}? - «ELSE» - ? - «ENDIF» - ; - finally { - getUnorderedGroupHelper().leave(grammarAccess.«originalElement.gaRuleElementAccessor()»); - restoreStackSize(stackSize); - } - - «ruleImpl(grammar, options)» - - «ruleImpl(grammar, options, 0)» - ''' - } - - protected def dispatch compileRule(Group it, Grammar grammar, AntlrOptions options) ''' - «ruleImpl(grammar, options, 0)» - ''' - - protected def ruleImpl(UnorderedGroup it, Grammar grammar, AntlrOptions options) ''' - «containingRule.contentAssistRuleName»__«originalElement.gaElementIdentifier()»__Impl - @init { - int stackSize = keepStackSize(); - boolean selected = false; - } - : - ( - «FOR element : elements.indexed SEPARATOR '|'» - ( - {getUnorderedGroupHelper().canSelect(grammarAccess.«originalElement.gaRuleElementAccessor()», «element.key»)}?=>( - { - getUnorderedGroupHelper().select(grammarAccess.«originalElement.gaRuleElementAccessor()», «element.key»); - } - { - selected = true; - } - ( - «IF element.value.isMultipleCardinality» - ( - { before(grammarAccess.«element.value.originalElement.grammarElementAccess()»); } - («element.value.ebnf2(options, false)») - { after(grammarAccess.«element.value.originalElement.grammarElementAccess()»); } - ) - ( - { before(grammarAccess.«element.value.originalElement.grammarElementAccess()»); } - ((«element.value.ebnf2(options, false)»)=>«element.value.ebnf2(options, false)»)* - { after(grammarAccess.«element.value.originalElement.grammarElementAccess()»); } - ) - «ELSE» - { before(grammarAccess.«element.value.originalElement.grammarElementAccess()»); } - («element.value.ebnf2(options, false)») - { after(grammarAccess.«element.value.originalElement.grammarElementAccess()»); } - «ENDIF» - ) - ) - ) - «ENDFOR» - ) - ; - finally { - if (selected) - getUnorderedGroupHelper().returnFromSelection(grammarAccess.«originalElement.gaRuleElementAccessor()»); - restoreStackSize(stackSize); - } - ''' - - protected def CharSequence ruleImpl(UnorderedGroup it, Grammar grammar, AntlrOptions options, int index) ''' - «containingRule.contentAssistRuleName»__«originalElement.gaElementIdentifier()»__«index» - @init { - int stackSize = keepStackSize(); - } - : - «containingRule.contentAssistRuleName»__«originalElement.gaElementIdentifier()»__Impl - «IF elements.size > index + 1» - «containingRule.contentAssistRuleName»__«originalElement.gaElementIdentifier()»__«index + 1»? - «ENDIF» - ; - finally { - restoreStackSize(stackSize); - } - - «IF elements.size > index + 1» - «ruleImpl(grammar, options, index + 1)» - «ENDIF» - ''' - - protected def CharSequence ruleImpl(Group it, Grammar grammar, AntlrOptions options, int index) ''' - «containingRule.contentAssistRuleName»__«originalElement.gaElementIdentifier»__«index» - @init { - int stackSize = keepStackSize(); - } - : - «containingRule.contentAssistRuleName»__«originalElement.gaElementIdentifier»__«index»__Impl - «IF elements.size > index + 1» - «containingRule().contentAssistRuleName»__«originalElement.gaElementIdentifier»__«index + 1» - «ENDIF» - ; - finally { - restoreStackSize(stackSize); - } - - «containingRule().contentAssistRuleName»__«originalElement.gaElementIdentifier»__«index»__Impl - @init { - int stackSize = keepStackSize(); - } - : - «elements.get(index).ebnf(options, false)» - ; - finally { - restoreStackSize(stackSize); - } - - «IF elements.size > index + 1» - «ruleImpl(grammar, options, index + 1)» - «ENDIF» - ''' - -// TODO - protected override ebnf(AbstractElement it, AntlrOptions options, boolean supportsActions) ''' - «IF !isOptionalCardinality() && isMultipleCardinality()» - ( - ( - { before(grammarAccess.«originalElement.grammarElementAccess»«paramConfig»); } - («ebnf2(options, supportsActions)») - { after(grammarAccess.«originalElement.grammarElementAccess»«paramConfig»); } - ) - ( - { before(grammarAccess.«originalElement.grammarElementAccess»«paramConfig»); } - («ebnf2(options, supportsActions)»)* - { after(grammarAccess.«originalElement.grammarElementAccess»«paramConfig»); } - ) - ) - «ELSE» - ( - { before(grammarAccess.«originalElement.grammarElementAccess»«paramConfig»); } - «IF mustBeParenthesized()»(«ebnf2(options, supportsActions)»)«ELSE»«ebnf2(options, supportsActions)»«ENDIF»«cardinality» - { after(grammarAccess.«originalElement.grammarElementAccess»«paramConfig»); } - ) - «ENDIF» - ''' - - protected def paramConfig(AbstractElement it) ''' - «IF containingRule.alternatives === it && ParserRule.isInstance(containingRule) && !(containingRule.originalElement as ParserRule).parameters.isEmpty» - , «(containingRule as ParserRule).parameterConfig» - «ENDIF» - ''' - - protected override dispatch assignmentEbnf(AbstractElement it, Assignment assignment, AntlrOptions options, boolean supportsActions) ''' - ( - { before(grammarAccess.«originalElement.grammarElementAccess»); } - «ebnf(options, supportsActions)» - { after(grammarAccess.«originalElement.grammarElementAccess»); } - ) - ''' - - protected override dispatch assignmentEbnf(CrossReference it, Assignment assignment, AntlrOptions options, boolean supportsActions) ''' - ( - { before(grammarAccess.«originalElement.grammarElementAccess»); } - «terminal.crossrefEbnf(it, supportsActions)» - { after(grammarAccess.«originalElement.grammarElementAccess»); } - ) - ''' - - protected override dispatch assignmentEbnf(Alternatives it, Assignment assignment, AntlrOptions options, boolean supportsActions) ''' - ( - { before(grammarAccess.«originalElement.grammarElementAccess»); } - («containingRule.contentAssistRuleName»__«originalElement.gaElementIdentifier») - { after(grammarAccess.«originalElement.grammarElementAccess»); } - ) - ''' - - protected override dispatch assignmentEbnf(RuleCall it, Assignment assignment, AntlrOptions options, boolean supportsActions) ''' - ( - { before(grammarAccess.«originalElement.grammarElementAccess»); } - «rule.ruleName» - { after(grammarAccess.«originalElement.grammarElementAccess»); } - ) - ''' - - protected dispatch override crossrefEbnf(RuleCall it, CrossReference ref, boolean supportActions) ''' - ( - { before(grammarAccess.«originalElement.grammarElementAccess»); } - «rule.crossrefEbnf(it, ref, supportActions)» - { after(grammarAccess.«originalElement.grammarElementAccess»); } - ) - ''' - - protected dispatch override crossrefEbnf(Keyword it, CrossReference ref, boolean supportActions) ''' - ( - { before(grammarAccess.«originalElement.grammarElementAccess»); } - «super._crossrefEbnf(it, ref, supportActions)» - { after(grammarAccess.«originalElement.grammarElementAccess»); } - ) - ''' - - protected dispatch def crossrefEbnf(TerminalRule it, RuleCall call, CrossReference ref, boolean supportActions) { - ruleName - } - - protected dispatch def crossrefEbnf(EnumRule it, RuleCall call, CrossReference ref, boolean supportActions) { - ruleName - } - - protected def dispatch crossrefEbnf(AbstractRule it, RuleCall call, CrossReference ref, boolean supportActions) { - if (originalElement.isDatatypeRule) { - return ruleName - } - throw new IllegalArgumentException(it.name + " is not a datatype rule") - } - - override protected dispatch ebnf2(Alternatives it, AntlrOptions options, boolean supportActions) { - '''«containingRule.contentAssistRuleName»__«originalElement.gaElementIdentifier»''' - } - - override protected dispatch ebnf2(Assignment it, AntlrOptions options, boolean supportActions) { - '''«containingRule.contentAssistRuleName»__«originalElement.gaElementIdentifier»''' - } - - override protected dispatch ebnf2(Group it, AntlrOptions options, boolean supportActions) { - '''«containingRule.contentAssistRuleName»__«originalElement.gaElementIdentifier»__0''' - } - - override protected dispatch ebnf2(UnorderedGroup it, AntlrOptions options, boolean supportActions) { - '''«containingRule.contentAssistRuleName»__«originalElement.gaElementIdentifier»''' - } - - override protected dispatch ebnf2(RuleCall it, AntlrOptions options, boolean supportActions) { - rule.ruleName - } - - override protected shouldBeSkipped(TerminalRule it, Grammar grammar) { - false - } - -} diff --git a/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AnnotationAwareAntlrGrammarGenerator.java b/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AnnotationAwareAntlrGrammarGenerator.java new file mode 100644 index 0000000000..9a88da1ba6 --- /dev/null +++ b/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AnnotationAwareAntlrGrammarGenerator.java @@ -0,0 +1,1099 @@ +/******************************************************************************* + * Copyright (c) 2016 Avaloq Group AG and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Avaloq Group AG - initial API and implementation + *******************************************************************************/ +package com.avaloq.tools.ddk.xtext.generator.parser.antlr; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.xtend2.lib.StringConcatenation; +import org.eclipse.xtext.AbstractElement; +import org.eclipse.xtext.AbstractRule; +import org.eclipse.xtext.Action; +import org.eclipse.xtext.Assignment; +import org.eclipse.xtext.CrossReference; +import org.eclipse.xtext.EcoreUtil2; +import org.eclipse.xtext.EnumLiteralDeclaration; +import org.eclipse.xtext.EnumRule; +import org.eclipse.xtext.Grammar; +import org.eclipse.xtext.GrammarUtil; +import org.eclipse.xtext.Keyword; +import org.eclipse.xtext.ParserRule; +import org.eclipse.xtext.RuleCall; +import org.eclipse.xtext.TerminalRule; +import org.eclipse.xtext.UnorderedGroup; +import org.eclipse.xtext.xbase.lib.IterableExtensions; +import org.eclipse.xtext.xbase.lib.ListExtensions; +import org.eclipse.xtext.xbase.lib.StringExtensions; +import org.eclipse.xtext.xtext.generator.parser.antlr.AntlrGrammarGenUtil; +import org.eclipse.xtext.xtext.generator.parser.antlr.AntlrOptions; +import org.eclipse.xtext.xtext.generator.parser.antlr.GrammarNaming; + +import com.google.common.collect.Iterables; +import com.google.inject.Inject; +import com.google.inject.Singleton; + +// CHECKSTYLE:CONSTANTS-OFF + +/** + * This implementation is strongly based on AntlrGrammarGenerator but with a different base class. + * The following extension is supported: + * + * A datatype grammar rule containing only one ID terminal rule can be annotated + * with @KeywordRule annotation provided a list of words so only these words can + * be accepted by this rule. + * + * Example: + * + * /** + * * @KeywordRule(visible, invisible) + * * / + * VisibleKind returns VisibleKind: + * ID + * ; + * + * The above rule will accept only 'visible' and 'invisible' identifiers. + * This rule in ASMD is called a keyword rule because it is intended to replace + * usages of keywords which shall not be reserved words in the language. + * Reserved words are words that are not allowed to be used in identifiers. + * + * The above example can therefore replace the following enumeration: + * + * enum VisibleKind : + * VISIBLE = "visible" + * | INVISIBLE = "invisible" + * ; + * + * Please note that a corresponding value converter is needed. + * + * Implementation remark: + * - This template will insert validating semantic predicates in the rule + * - If the rule is used from an alternative a gated semantic predicate will + * be used in the alternative + * - Error messages will be adjusted correspondingly + */ +@Singleton +@SuppressWarnings({"checkstyle:MethodName", "PMD.UnusedFormalParameter", "nls"}) +public class AnnotationAwareAntlrGrammarGenerator extends AbstractAnnotationAwareAntlrGrammarGenerator { + + @Inject + private GrammarNaming naming; + + private String lexerSuperClassName = ""; + + public void setLexerSuperClassName(final String lexerSuperClassName) { + this.lexerSuperClassName = lexerSuperClassName; + } + + @Override + protected GrammarNaming getGrammarNaming() { + return this.naming; + } + + @Override + protected String compileParserImports(final Grammar it, final AntlrOptions options) { + final StringConcatenation builder = new StringConcatenation(); + builder.newLine(); + builder.append("import org.eclipse.xtext.*;"); + builder.newLine(); + builder.append("import org.eclipse.xtext.parser.*;"); + builder.newLine(); + builder.append("import org.eclipse.xtext.parser.impl.*;"); + builder.newLine(); + builder.append("import org.eclipse.emf.ecore.util.EcoreUtil;"); + builder.newLine(); + builder.append("import org.eclipse.emf.ecore.EObject;"); + builder.newLine(); + if (!GrammarUtil.allEnumRules(it).isEmpty()) { + builder.append("import org.eclipse.emf.common.util.Enumerator;"); + builder.newLine(); + } + builder.append("import "); + builder.append(this.getGrammarNaming().getInternalParserSuperClass(it).getName()); + builder.append(';'); + builder.newLineIfNotEmpty(); + builder.append("import org.eclipse.xtext.parser.antlr.XtextTokenStream;"); + builder.newLine(); + builder.append("import org.eclipse.xtext.parser.antlr.XtextTokenStream.HiddenTokens;"); + builder.newLine(); + if ((!IterableExtensions.isEmpty(Iterables.filter(Iterables.concat(ListExtensions.map(GrammarUtil.allParserRules(it), (ParserRule it1) -> EcoreUtil2.eAllContentsAsList(it1))), UnorderedGroup.class))) && options.isBacktrack()) { + builder.append("import org.eclipse.xtext.parser.antlr.IUnorderedGroupHelper.UnorderedGroupState;"); + builder.newLine(); + } + builder.append("import org.eclipse.xtext.parser.antlr.AntlrDatatypeRuleToken;"); + builder.newLine(); + builder.append("import "); + builder.append(this._grammarAccessExtensions.getGrammarAccess(it).getName()); + builder.append(';'); + builder.newLineIfNotEmpty(); + builder.append(super.compileParserImports(it, options)); + builder.newLineIfNotEmpty(); + builder.newLine(); + return builder.toString(); + } + + @Override + protected String compileParserMembers(final Grammar it, final AntlrOptions options) { + final StringConcatenation builder = new StringConcatenation(); + builder.newLine(); + builder.append('@'); + if (this.isCombinedGrammar()) { + builder.append("parser::"); + } + builder.append("members {"); + builder.newLineIfNotEmpty(); + builder.newLine(); + if (options.isBacktrack()) { + builder.append("/*"); + builder.newLine(); + builder.append(" "); + builder.append("This grammar contains a lot of empty actions to work around a bug in ANTLR."); + builder.newLine(); + builder.append(" "); + builder.append("Otherwise the ANTLR tool will create synpreds that cannot be compiled in some rare cases."); + builder.newLine(); + builder.append("*/"); + builder.newLine(); + builder.newLine(); + } + builder.append(" "); + builder.append(this.compileParserMemberDeclarations(it, "private"), " "); + builder.newLineIfNotEmpty(); + builder.newLine(); + builder.append(" "); + builder.append("public "); + builder.append(this.naming.getInternalParserClass(it).getSimpleName(), " "); + builder.append("(TokenStream input, "); + builder.append(this._grammarAccessExtensions.getGrammarAccess(it).getSimpleName(), " "); + builder.append(" grammarAccess, ParserContext parserContext, "); + builder.append(this.predicatesNaming.getSemanticPredicatesSimpleName(it), " "); + builder.append(" predicates) {"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("this(input);"); + builder.newLine(); + builder.append(" "); + builder.append("this.grammarAccess = grammarAccess;"); + builder.newLine(); + builder.append(" "); + builder.append("this.predicates = predicates;"); + builder.newLine(); + builder.append(" "); + builder.append("this.parserContext = parserContext;"); + builder.newLine(); + builder.append(" "); + builder.append("parserContext.setTokenStream(input);"); + builder.newLine(); + builder.append(" "); + builder.append("registerRules(grammarAccess.getGrammar());"); + builder.newLine(); + builder.append(" "); + builder.append('}'); + builder.newLine(); + builder.newLine(); + builder.append(" "); + builder.append(this.compileParserSetTokenStreamMethod(), " "); + builder.newLineIfNotEmpty(); + builder.newLine(); + builder.append(" "); + builder.append("@Override"); + builder.newLine(); + builder.append(" "); + builder.append("protected String getFirstRuleName() {"); + builder.newLine(); + builder.append(" "); + builder.append("return \""); + builder.append(AntlrGrammarGenUtil.getOriginalElement(IterableExtensions.head(GrammarUtil.allParserRules(it))).getName(), " "); + builder.append("\";"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append('}'); + builder.newLine(); + builder.newLine(); + builder.append(" "); + builder.append("@Override"); + builder.newLine(); + builder.append(" "); + builder.append("protected "); + builder.append(this._grammarAccessExtensions.getGrammarAccess(it).getSimpleName(), " "); + builder.append(" getGrammarAccess() {"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("return grammarAccess;"); + builder.newLine(); + builder.append(" "); + builder.append('}'); + builder.newLine(); + builder.newLine(); + builder.append('}'); + builder.newLine(); + return builder.toString(); + } + + @Override + protected String compileRuleCatch(final Grammar it, final AntlrOptions options) { + return """ + + @rulecatch { + catch (RecognitionException re) { + recover(input,re); + appendSkippedTokens(); + } + } + """; + } + + @Override + protected boolean shouldBeSkipped(final TerminalRule it, final Grammar grammar) { + return false; + } + + @Override + protected CharSequence _compileRule(final ParserRule it, final Grammar grammar, final AntlrOptions options) { + final StringConcatenation builder = new StringConcatenation(); + if (AntlrGrammarGenUtil.isValidEntryRule(it)) { + builder.append(this.compileEntryRule(it, grammar, options)); + builder.newLineIfNotEmpty(); + } + builder.newLine(); + builder.append(this.compileEBNF(it, options)); + builder.newLineIfNotEmpty(); + return builder; + } + + protected String compileEntryRule(final ParserRule it, final Grammar grammar, final AntlrOptions options) { + final StringConcatenation builder = new StringConcatenation(); + builder.append("// Entry rule "); + builder.append(this._grammarAccessExtensions.entryRuleName(AntlrGrammarGenUtil.getOriginalElement(it))); + builder.newLineIfNotEmpty(); + builder.append(this._grammarAccessExtensions.entryRuleName(AntlrGrammarGenUtil.getOriginalElement(it))); + builder.append(" returns "); + builder.append(this.compileEntryReturns(it, options)); + builder.append(this.compileEntryInit(it, options)); + builder.append(':'); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("{ "); + builder.append(this.newCompositeNode(it), " "); + builder.append(" }"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("iv_"); + builder.append(this._grammarAccessExtensions.ruleName(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append('='); + builder.append(this._grammarAccessExtensions.ruleName(it), " "); + builder.append(AntlrGrammarGenUtil.getDefaultArgumentList(it), " "); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("{ $current=$iv_"); + builder.append(this._grammarAccessExtensions.ruleName(it), " "); + builder.append(".current"); + if (GrammarUtil.isDatatypeRule(AntlrGrammarGenUtil.getOriginalElement(it))) { + builder.append(".getText()"); + } + builder.append("; }"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("EOF;"); + builder.newLine(); + builder.append(this.compileEntryFinally(it, options)); + builder.newLineIfNotEmpty(); + return builder.toString(); + } + + protected String compileEntryReturns(final ParserRule it, final AntlrOptions options) { + if (GrammarUtil.isDatatypeRule(AntlrGrammarGenUtil.getOriginalElement(it))) { + return "[String current=null]"; + } else { + final StringConcatenation builder = new StringConcatenation(); + builder.append('['); + builder.append(this.getCurrentType()); + builder.append(" current=null]"); + return builder.toString(); + } + } + + @Override + protected String compileInit(final AbstractRule it, final AntlrOptions options) { + final StringConcatenation builder = new StringConcatenation(); + if (it instanceof ParserRule) { + builder.append(AntlrGrammarGenUtil.getParameterList((ParserRule) it, !this.isPassCurrentIntoFragment(), this.getCurrentType())); + } + builder.append(" returns "); + builder.append(this.compileReturns(it, options)); + builder.newLineIfNotEmpty(); + if (this.annotations.hasNoBacktrackAnnotation(it)) { + builder.append("// Enclosing rule was annotated with @NoBacktrack"); + builder.newLine(); + builder.append("options { backtrack=false; }"); + builder.newLine(); + } + builder.append("@init {"); + builder.newLine(); + builder.append(" "); + builder.append("enterRule();"); + builder.newLine(); + builder.append(" "); + builder.append(this.compileInitHiddenTokens(it, options), " "); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(this.compileInitUnorderedGroups(it, options), " "); + builder.newLineIfNotEmpty(); + builder.append('}'); + builder.newLine(); + builder.append("@after {"); + builder.newLine(); + builder.append(" "); + builder.append("leaveRule();"); + builder.newLine(); + builder.append('}'); + return builder.toString(); + } + + protected CharSequence compileReturns(final AbstractRule it, final AntlrOptions options) { + if (it instanceof EnumRule) { + return "[Enumerator current=null]"; + } + if (it instanceof ParserRule) { + if (GrammarUtil.isDatatypeRule(AntlrGrammarGenUtil.getOriginalElement((ParserRule) it))) { + return "[AntlrDatatypeRuleToken current=new AntlrDatatypeRuleToken()]"; + } + if (GrammarUtil.isEObjectFragmentRule(AntlrGrammarGenUtil.getOriginalElement((ParserRule) it))) { + final StringConcatenation builder = new StringConcatenation(); + builder.append('['); + builder.append(this.getCurrentType()); + builder.append(" current=in_current]"); + return builder; + } + final StringConcatenation builder1 = new StringConcatenation(); + builder1.append('['); + builder1.append(this.getCurrentType()); + builder1.append(" current=null]"); + return builder1; + } + throw new IllegalStateException("Unexpected rule: " + it); + } + + @Override + protected String _dataTypeEbnf2(final Keyword it, final boolean supportActions) { + if (supportActions) { + final StringConcatenation builder = new StringConcatenation(); + builder.append("kw="); + builder.append(super._dataTypeEbnf2(it, supportActions)); + builder.newLineIfNotEmpty(); + builder.append('{'); + builder.newLine(); + builder.append(" "); + builder.append("$current.merge(kw);"); + builder.newLine(); + builder.append(" "); + builder.append(this.newLeafNode(it, "kw"), " "); + builder.newLineIfNotEmpty(); + builder.append('}'); + builder.newLine(); + return builder.toString(); + } else { + return super._dataTypeEbnf2(it, supportActions); + } + } + + @Override + protected String _ebnf2(final Action it, final AntlrOptions options, final boolean supportActions) { + if (supportActions) { + final StringConcatenation builder = new StringConcatenation(); + if (options.isBacktrack()) { + builder.append('{'); + builder.newLine(); + builder.append(" "); + builder.append("/* */"); + builder.newLine(); + builder.append('}'); + builder.newLine(); + } + builder.append('{'); + builder.newLine(); + builder.append(" "); + builder.append("$current = forceCreateModelElement"); + if (it.getFeature() != null) { + builder.append("And"); + builder.append(StringExtensions.toFirstUpper(this._grammarAccessExtensions.setOrAdd(it)), " "); + } + builder.append('('); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("grammarAccess."); + builder.append(this._grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append(','); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("$current);"); + builder.newLine(); + builder.append('}'); + builder.newLine(); + return builder.toString(); + } else { + return super._ebnf2(it, options, supportActions); + } + } + + @Override + protected String _ebnf2(final Keyword it, final AntlrOptions options, final boolean supportActions) { + if (!supportActions) { + return super._ebnf2(it, options, supportActions); + } else if (GrammarUtil.isAssigned(it)) { + final StringConcatenation builder = new StringConcatenation(); + builder.append(super._ebnf2(it, options, supportActions)); + builder.newLineIfNotEmpty(); + builder.append('{'); + builder.newLine(); + builder.append(" "); + builder.append(this.newLeafNode(it, this._grammarAccessExtensions.localVar(GrammarUtil.containingAssignment(it), it)), " "); + builder.newLineIfNotEmpty(); + builder.append('}'); + builder.newLine(); + return builder.toString(); + } else { + final StringConcatenation builder1 = new StringConcatenation(); + builder1.append(this._grammarAccessExtensions.localVar(it)); + builder1.append('='); + builder1.append(super._ebnf2(it, options, supportActions)); + builder1.newLineIfNotEmpty(); + builder1.append('{'); + builder1.newLine(); + builder1.append(" "); + builder1.append(this.newLeafNode(it, this._grammarAccessExtensions.localVar(it)), " "); + builder1.newLineIfNotEmpty(); + builder1.append('}'); + builder1.newLine(); + return builder1.toString(); + } + } + + @Override + protected String _ebnf2(final EnumLiteralDeclaration it, final AntlrOptions options, final boolean supportActions) { + if (!supportActions) { + return super._ebnf2(it, options, supportActions); + } else { + final StringConcatenation builder = new StringConcatenation(); + builder.append(this._grammarAccessExtensions.localVar(it)); + builder.append('='); + builder.append(super._ebnf2(it, options, supportActions)); + builder.newLineIfNotEmpty(); + builder.append('{'); + builder.newLine(); + builder.append(" "); + builder.append("$current = grammarAccess."); + builder.append(this._grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it)), " "); + builder.append(".getEnumLiteral().getInstance();"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(this.newLeafNode(it, this._grammarAccessExtensions.localVar(it)), " "); + builder.newLineIfNotEmpty(); + builder.append('}'); + builder.newLine(); + return builder.toString(); + } + } + + @Override + protected String crossrefEbnf(final AbstractRule it, final RuleCall call, final CrossReference ref, final boolean supportActions) { + if (supportActions) { + if (it instanceof EnumRule || it instanceof ParserRule) { + final StringConcatenation builder = new StringConcatenation(); + builder.append('{'); + builder.newLine(); + builder.append(" "); + builder.append(this.newCompositeNode(ref), " "); + builder.newLineIfNotEmpty(); + builder.append('}'); + builder.newLine(); + builder.append(this._grammarAccessExtensions.ruleName(it)); + builder.append(AntlrGrammarGenUtil.getArgumentList(call, this.isPassCurrentIntoFragment(), (!supportActions))); + builder.newLineIfNotEmpty(); + builder.append('{'); + builder.newLine(); + builder.append(" "); + builder.append("afterParserOrEnumRuleCall();"); + builder.newLine(); + builder.append('}'); + builder.newLine(); + return builder.toString(); + } else if (it instanceof TerminalRule) { + final StringConcatenation builder1 = new StringConcatenation(); + builder1.append(this._grammarAccessExtensions.localVar(GrammarUtil.containingAssignment(ref))); + builder1.append('='); + builder1.append(this._grammarAccessExtensions.ruleName(it)); + builder1.newLineIfNotEmpty(); + builder1.append('{'); + builder1.newLine(); + builder1.append(" "); + builder1.append(this.newLeafNode(ref, this._grammarAccessExtensions.localVar(GrammarUtil.containingAssignment(ref))), " "); + builder1.newLineIfNotEmpty(); + builder1.append('}'); + builder1.newLine(); + return builder1.toString(); + } else { + throw new IllegalStateException("crossrefEbnf is not supported for " + it); + } + } else { + return super.crossrefEbnf(it, call, ref, supportActions); + } + } + + @Override + protected String _assignmentEbnf(final AbstractElement it, final Assignment assignment, final AntlrOptions options, final boolean supportActions) { + if (supportActions) { + final StringConcatenation builder = new StringConcatenation(); + builder.append(this._grammarAccessExtensions.localVar(assignment, it)); + builder.append('='); + builder.append(super._assignmentEbnf(it, assignment, options, supportActions)); + builder.newLineIfNotEmpty(); + builder.append('{'); + builder.newLine(); + builder.append(" "); + builder.append("if ($current==null) {"); + builder.newLine(); + builder.append(" "); + builder.append("$current = "); + builder.append(this.createModelElement(assignment), " "); + builder.append(';'); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append('}'); + builder.newLine(); + builder.append(" "); + builder.append(this._grammarAccessExtensions.setOrAdd(assignment), " "); + builder.append("WithLastConsumed($current, \""); + builder.append(assignment.getFeature(), " "); + builder.append("\", "); + builder.append(this._grammarAccessExtensions.localVar(assignment, it), " "); + if (GrammarUtil.isBooleanAssignment(assignment)) { + builder.append(" != null"); + } + builder.append(", "); + builder.append(this._grammarAccessExtensions.toStringLiteral(assignment.getTerminal()), " "); + builder.append(");"); + builder.newLineIfNotEmpty(); + builder.append('}'); + builder.newLine(); + return builder.toString(); + } else { + return super._assignmentEbnf(it, assignment, options, supportActions); + } + } + + @Override + protected boolean isPassCurrentIntoFragment() { + return true; + } + + protected CharSequence createModelElement(final EObject grammarElement) { + final StringConcatenation builder = new StringConcatenation(); + builder.append("createModelElement(grammarAccess."); + builder.append(this._grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(GrammarUtil.containingParserRule(grammarElement)))); + builder.append(')'); + return builder; + } + + protected CharSequence createModelElementForParent(final EObject grammarElement) { + final StringConcatenation builder = new StringConcatenation(); + builder.append("createModelElementForParent(grammarAccess."); + builder.append(this._grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(GrammarUtil.containingParserRule(grammarElement)))); + builder.append(')'); + return builder; + } + + protected CharSequence newCompositeNode(final EObject it) { + final StringConcatenation builder = new StringConcatenation(); + builder.append("newCompositeNode(grammarAccess."); + builder.append(this._grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it))); + builder.append(");"); + return builder; + } + + protected CharSequence newLeafNode(final EObject it, final String token) { + final StringConcatenation builder = new StringConcatenation(); + builder.append("newLeafNode("); + builder.append(token); + builder.append(", grammarAccess."); + builder.append(this._grammarAccessExtensions.grammarElementAccess(AntlrGrammarGenUtil.getOriginalElement(it))); + builder.append(");"); + return builder; + } + + /** + * Add gated predicate, if necessary, before the action preceding the rule call. + */ + @Override + protected String _dataTypeEbnf2(final RuleCall it, final boolean supportActions) { + if (supportActions) { + final AbstractRule rule = it.getRule(); + if ((rule instanceof EnumRule && GrammarUtil.isAssigned(it)) || (rule instanceof ParserRule && GrammarUtil.isAssigned(it))) { + return super._dataTypeEbnf2(it, supportActions); + } else if (rule instanceof EnumRule || rule instanceof ParserRule) { + final StringConcatenation builder = new StringConcatenation(); + if (this.annotations.isGatedPredicateRequired(it)) { + builder.append(this.annotations.generateGatedPredicate(it)); + } + builder.newLineIfNotEmpty(); + builder.append('{'); + builder.newLine(); + builder.append(" "); + builder.append(this.newCompositeNode(it), " "); + builder.newLineIfNotEmpty(); + builder.append('}'); + builder.newLine(); + builder.append(this._grammarAccessExtensions.localVar(it)); + builder.append('='); + builder.append(super._dataTypeEbnf2(it, supportActions)); + builder.append(AntlrGrammarGenUtil.getArgumentList(it, this.isPassCurrentIntoFragment(), (!supportActions))); + builder.newLineIfNotEmpty(); + builder.append('{'); + builder.newLine(); + builder.append(" "); + builder.append("$current.merge("); + builder.append(this._grammarAccessExtensions.localVar(it), " "); + builder.append(");"); + builder.newLineIfNotEmpty(); + builder.append('}'); + builder.newLine(); + builder.append('{'); + builder.newLine(); + builder.append(" "); + builder.append("afterParserOrEnumRuleCall();"); + builder.newLine(); + builder.append('}'); + builder.newLine(); + return builder.toString(); + } else if (rule instanceof TerminalRule) { + final StringConcatenation builder1 = new StringConcatenation(); + builder1.append(this._grammarAccessExtensions.localVar(it)); + builder1.append('='); + builder1.append(super._dataTypeEbnf2(it, supportActions)); + builder1.newLineIfNotEmpty(); + builder1.append('{'); + builder1.newLine(); + builder1.append(" "); + builder1.append("$current.merge("); + builder1.append(this._grammarAccessExtensions.localVar(it), " "); + builder1.append(");"); + builder1.newLineIfNotEmpty(); + builder1.append('}'); + builder1.newLine(); + builder1.append('{'); + builder1.newLine(); + builder1.append(" "); + builder1.append(this.newLeafNode(it, this._grammarAccessExtensions.localVar(it)), " "); + builder1.newLineIfNotEmpty(); + builder1.append('}'); + builder1.newLine(); + return builder1.toString(); + } else { + return super._dataTypeEbnf2(it, supportActions); + } + } else { + return super._dataTypeEbnf2(it, supportActions); + } + } + + /** + * Inserts validating predicate only. Gated predicates will be inserted in alternatives if needed. + */ + @Override + protected String compileEBNF(final AbstractRule it, final AntlrOptions options) { + final StringConcatenation builder = new StringConcatenation(); + builder.append("// Rule "); + builder.append(AntlrGrammarGenUtil.getOriginalElement(it).getName()); + builder.newLineIfNotEmpty(); + builder.append(this._grammarAccessExtensions.ruleName(it)); + builder.append(this.compileInit(it, options)); + builder.append(':'); + builder.newLineIfNotEmpty(); + if ((it instanceof ParserRule) && GrammarUtil.isDatatypeRule(AntlrGrammarGenUtil.getOriginalElement(it))) { + builder.append(" "); + if (this.annotations.hasValidatingPredicate(it)) { + builder.append(this.annotations.generateValidatingPredicate(it), " "); + } + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(this.dataTypeEbnf(it.getAlternatives(), true), " "); + builder.newLineIfNotEmpty(); + } else { + builder.append(" "); + builder.append(this.ebnf(it.getAlternatives(), options, true), " "); + builder.newLineIfNotEmpty(); + } + builder.append(';'); + builder.newLine(); + builder.append(this.compileFinally(it, options)); + builder.newLineIfNotEmpty(); + return builder.toString(); + } + + @Override + protected String dataTypeEbnf(final AbstractElement it, final boolean supportActions) { + final StringConcatenation builder = new StringConcatenation(); + if (this.mustBeParenthesized(it)) { + builder.append('('); + builder.newLineIfNotEmpty(); + if (this.annotations.hasNoBacktrackAnnotation(it)) { + builder.append(" "); + builder.append("// Enclosing rule was annotated with @NoBacktrack"); + builder.newLine(); + builder.append(" "); + builder.append("options { backtrack=false; }:"); + builder.newLine(); + } + builder.append(" "); + builder.append(this.dataTypeEbnfPredicate(it), " "); + builder.append(this.dataTypeEbnf2(it, supportActions), " "); + builder.newLineIfNotEmpty(); + builder.append(')'); + } else { + builder.append(this.dataTypeEbnf2(it, supportActions)); + } + builder.append(it.getCardinality()); + builder.newLineIfNotEmpty(); + return builder.toString(); + } + + @Override + protected String ebnf(final AbstractElement it, final AntlrOptions options, final boolean supportActions) { + final StringConcatenation builder = new StringConcatenation(); + if (this.mustBeParenthesized(it)) { + builder.append('('); + builder.newLineIfNotEmpty(); + if (this.annotations.hasNoBacktrackAnnotation(it)) { + builder.append(" "); + builder.append("// Enclosing rule was annotated with @NoBacktrack"); + builder.newLine(); + builder.append(" "); + builder.append("options { backtrack=false; }:"); + builder.newLine(); + } + builder.append(" "); + builder.append(this.ebnfPredicate(it, options), " "); + builder.append(this.ebnf2(it, options, supportActions), " "); + builder.newLineIfNotEmpty(); + builder.append(')'); + } else { + builder.append(this.ebnf2(it, options, supportActions)); + } + builder.append(it.getCardinality()); + builder.newLineIfNotEmpty(); + return builder.toString(); + } + + /** + * Add gated predicate, if necessary, before the action preceding the assignment. + */ + @Override + protected String _assignmentEbnf(final RuleCall it, final Assignment assignment, final AntlrOptions options, final boolean supportActions) { + if (supportActions) { + final AbstractRule rule = it.getRule(); + if (rule instanceof EnumRule || rule instanceof ParserRule) { + final StringConcatenation builder = new StringConcatenation(); + if (this.annotations.isGatedPredicateRequired(it)) { + builder.append(this.annotations.generateGatedPredicate(it)); + } + builder.newLineIfNotEmpty(); + builder.append('{'); + builder.newLine(); + builder.append(" "); + builder.append(this.newCompositeNode(it), " "); + builder.newLineIfNotEmpty(); + builder.append('}'); + builder.newLine(); + builder.append(this._grammarAccessExtensions.localVar(assignment, it)); + builder.append('='); + builder.append(super._assignmentEbnf(it, assignment, options, supportActions)); + builder.newLineIfNotEmpty(); + builder.append('{'); + builder.newLine(); + builder.append(" "); + builder.append("if ($current==null) {"); + builder.newLine(); + builder.append(" "); + builder.append("$current = "); + builder.append(this.createModelElementForParent(assignment), " "); + builder.append(';'); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append('}'); + builder.newLine(); + builder.append(" "); + builder.append(this._grammarAccessExtensions.setOrAdd(assignment), " "); + builder.append('('); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("$current,"); + builder.newLine(); + builder.append(" "); + builder.append("\""); + builder.append(assignment.getFeature(), " "); + builder.append("\","); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(this._grammarAccessExtensions.localVar(assignment, it), " "); + if (GrammarUtil.isBooleanAssignment(assignment)) { + builder.append(" != null"); + } + builder.append(','); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(this._grammarAccessExtensions.toStringLiteral(it), " "); + builder.append(");"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("afterParserOrEnumRuleCall();"); + builder.newLine(); + builder.append('}'); + builder.newLine(); + return builder.toString(); + } else if (rule instanceof TerminalRule) { + final StringConcatenation builder1 = new StringConcatenation(); + builder1.append(this._grammarAccessExtensions.localVar(assignment, it)); + builder1.append('='); + builder1.append(super._assignmentEbnf(it, assignment, options, supportActions)); + builder1.newLineIfNotEmpty(); + builder1.append('{'); + builder1.newLine(); + builder1.append(" "); + builder1.append(this.newLeafNode(it, this._grammarAccessExtensions.localVar(assignment, it)), " "); + builder1.newLineIfNotEmpty(); + builder1.append('}'); + builder1.newLine(); + builder1.append('{'); + builder1.newLine(); + builder1.append(" "); + builder1.append("if ($current==null) {"); + builder1.newLine(); + builder1.append(" "); + builder1.append("$current = "); + builder1.append(this.createModelElement(assignment), " "); + builder1.append(';'); + builder1.newLineIfNotEmpty(); + builder1.append(" "); + builder1.append('}'); + builder1.newLine(); + builder1.append(" "); + builder1.append(this._grammarAccessExtensions.setOrAdd(assignment), " "); + builder1.append("WithLastConsumed("); + builder1.newLineIfNotEmpty(); + builder1.append(" "); + builder1.append("$current,"); + builder1.newLine(); + builder1.append(" "); + builder1.append("\""); + builder1.append(assignment.getFeature(), " "); + builder1.append("\","); + builder1.newLineIfNotEmpty(); + builder1.append(" "); + builder1.append(this._grammarAccessExtensions.localVar(assignment, it), " "); + if (GrammarUtil.isBooleanAssignment(assignment)) { + builder1.append(" != null"); + } + builder1.append(','); + builder1.newLineIfNotEmpty(); + builder1.append(" "); + builder1.append(this._grammarAccessExtensions.toStringLiteral(it), " "); + builder1.append(");"); + builder1.newLineIfNotEmpty(); + builder1.append('}'); + builder1.newLine(); + return builder1.toString(); + } else { + throw new IllegalStateException("assignmentEbnf is not supported for " + it); + } + } else { + return super._assignmentEbnf(it, assignment, options, supportActions); + } + } + + /** + * Add gated predicate, if necessary, before the action preceding the cross reference. + */ + @Override + protected String _assignmentEbnf(final CrossReference it, final Assignment assignment, final AntlrOptions options, final boolean supportActions) { + if (supportActions) { + final StringConcatenation builder = new StringConcatenation(); + if (this.annotations.isGatedPredicateRequired(it)) { + builder.append(this.annotations.generateGatedPredicate(it)); + } + builder.newLineIfNotEmpty(); + if (options.isBacktrack()) { + builder.append('{'); + builder.newLine(); + builder.append(" "); + builder.append("/* */"); + builder.newLine(); + builder.append('}'); + builder.newLine(); + } + builder.append('{'); + builder.newLine(); + builder.append(" "); + builder.append("if ($current==null) {"); + builder.newLine(); + builder.append(" "); + builder.append("$current = "); + builder.append(this.createModelElement(assignment), " "); + builder.append(';'); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append('}'); + builder.newLine(); + builder.append('}'); + builder.newLine(); + builder.append(super._assignmentEbnf(it, assignment, options, supportActions)); + return builder.toString(); + } else { + return super._assignmentEbnf(it, assignment, options, supportActions); + } + } + + /** + * Add gated predicate, if necessary, before the action preceding the rule call. + */ + @Override + protected String _ebnf2(final RuleCall it, final AntlrOptions options, final boolean supportActions) { + if (!supportActions) { + return super._ebnf2(it, options, supportActions); + } else { + final AbstractRule rule = it.getRule(); + if ((rule instanceof EnumRule && GrammarUtil.isAssigned(it)) || (rule instanceof ParserRule && GrammarUtil.isAssigned(it))) { + return super._ebnf2(it, options, supportActions); + } else if (rule instanceof EnumRule || (rule instanceof ParserRule && GrammarUtil.isDatatypeRule(AntlrGrammarGenUtil.getOriginalElement((ParserRule) rule)))) { + final StringConcatenation builder = new StringConcatenation(); + if (this.annotations.isGatedPredicateRequired(it)) { + builder.append(this.annotations.generateGatedPredicate(it)); + } + builder.newLineIfNotEmpty(); + if (options.isBacktrack()) { + builder.append('{'); + builder.newLine(); + builder.append(" "); + builder.append("/* */"); + builder.newLine(); + builder.append('}'); + builder.newLine(); + } + builder.append('{'); + builder.newLine(); + builder.append(" "); + builder.append(this.newCompositeNode(it), " "); + builder.newLineIfNotEmpty(); + builder.append('}'); + builder.newLine(); + builder.append(super._ebnf2(it, options, supportActions)); + builder.newLineIfNotEmpty(); + builder.append('{'); + builder.newLine(); + builder.append(" "); + builder.append("afterParserOrEnumRuleCall();"); + builder.newLine(); + builder.append('}'); + builder.newLine(); + return builder.toString(); + } else if (rule instanceof ParserRule) { + final StringConcatenation builder1 = new StringConcatenation(); + if (this.annotations.isGatedPredicateRequired(it)) { + builder1.append(this.annotations.generateGatedPredicate(it)); + } + builder1.newLineIfNotEmpty(); + if (options.isBacktrack()) { + builder1.append('{'); + builder1.newLine(); + builder1.append(" "); + builder1.append("/* */"); + builder1.newLine(); + builder1.append('}'); + builder1.newLine(); + } + builder1.append('{'); + builder1.newLine(); + if (GrammarUtil.isEObjectFragmentRuleCall(it)) { + builder1.append(" "); + builder1.append("if ($current==null) {"); + builder1.newLine(); + builder1.append(" "); + builder1.append("$current = "); + builder1.append(this.createModelElement(it), " "); + builder1.append(';'); + builder1.newLineIfNotEmpty(); + builder1.append(" "); + builder1.append('}'); + builder1.newLine(); + } + builder1.append(" "); + builder1.append(this.newCompositeNode(it), " "); + builder1.newLineIfNotEmpty(); + builder1.append('}'); + builder1.newLine(); + final String localVar = this._grammarAccessExtensions.localVar(it); + builder1.append(localVar); + builder1.append('='); + builder1.append(super._ebnf2(it, options, supportActions)); + builder1.newLineIfNotEmpty(); + builder1.append('{'); + builder1.newLine(); + builder1.append(" "); + builder1.append("$current = $"); + builder1.append(localVar, " "); + builder1.append(".current;"); + builder1.newLineIfNotEmpty(); + builder1.append(" "); + builder1.append("afterParserOrEnumRuleCall();"); + builder1.newLine(); + builder1.append('}'); + builder1.newLine(); + return builder1.toString(); + } else if (rule instanceof TerminalRule) { + final StringConcatenation builder2 = new StringConcatenation(); + final String localVar = this._grammarAccessExtensions.localVar(it); + builder2.append(localVar); + builder2.append('='); + builder2.append(super._ebnf2(it, options, supportActions)); + builder2.newLineIfNotEmpty(); + builder2.append('{'); + builder2.newLine(); + builder2.append(" "); + builder2.append(this.newLeafNode(it, localVar), " "); + builder2.newLineIfNotEmpty(); + builder2.append('}'); + builder2.newLine(); + return builder2.toString(); + } else { + return super._ebnf2(it, options, supportActions); + } + } + } + + @Override + protected String compileLexerImports(final Grammar it, final AntlrOptions options) { + final StringConcatenation builder = new StringConcatenation(); + builder.newLine(); + builder.append("// Hack: Use our own Lexer superclass by means of import."); + builder.newLine(); + builder.append("// Currently there is no other way to specify the superclass for the lexer."); + builder.newLine(); + if (!this.lexerSuperClassName.isEmpty()) { + builder.append("import "); + builder.append(this.lexerSuperClassName); + builder.append(';'); + builder.newLineIfNotEmpty(); + } else { + builder.append("import "); + builder.append(this.getGrammarNaming().getLexerSuperClass(it)); + builder.append(';'); + builder.newLineIfNotEmpty(); + } + return builder.toString(); + } +} +// CHECKSTYLE:CONSTANTS-ON diff --git a/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AnnotationAwareAntlrGrammarGenerator.xtend b/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AnnotationAwareAntlrGrammarGenerator.xtend deleted file mode 100644 index 7cec9ea56a..0000000000 --- a/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AnnotationAwareAntlrGrammarGenerator.xtend +++ /dev/null @@ -1,544 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ - -package com.avaloq.tools.ddk.xtext.generator.parser.antlr - -import com.google.inject.Inject -import com.google.inject.Singleton -import org.eclipse.emf.ecore.EObject -import org.eclipse.xtend.lib.annotations.Accessors -import org.eclipse.xtext.AbstractElement -import org.eclipse.xtext.AbstractRule -import org.eclipse.xtext.Action -import org.eclipse.xtext.Assignment -import org.eclipse.xtext.CrossReference -import org.eclipse.xtext.EnumLiteralDeclaration -import org.eclipse.xtext.EnumRule -import org.eclipse.xtext.Grammar -import org.eclipse.xtext.Keyword -import org.eclipse.xtext.ParserRule -import org.eclipse.xtext.RuleCall -import org.eclipse.xtext.TerminalRule -import org.eclipse.xtext.UnorderedGroup -import org.eclipse.xtext.xtext.generator.parser.antlr.AntlrOptions -import org.eclipse.xtext.xtext.generator.parser.antlr.GrammarNaming - -import static extension org.eclipse.xtext.EcoreUtil2.* -import static extension org.eclipse.xtext.GrammarUtil.* -import static extension org.eclipse.xtext.xtext.generator.parser.antlr.AntlrGrammarGenUtil.* - -/** - * This implementation is strongly based on AntlrGrammarGenerator but with a different base class. - * The following extension is supported: - * - * A datatype grammar rule containing only one ID terminal rule can be annotated - * with @KeywordRule annotation provided a list of words so only these words can - * be accepted by this rule. - * - * Example: - * - * /** - * * @KeywordRule(visible, invisible) - * * / - * VisibleKind returns VisibleKind: - * ID - * ; - * - * The above rule will accept only 'visible' and 'invisible' identifiers. - * This rule in ASMD is called a keyword rule because it is intended to replace - * usages of keywords which shall not be reserved words in the language. - * Reserved words are words that are not allowed to be used in identifiers. - * - * The above example can therefore replace the following enumeration: - * - * enum VisibleKind : - * VISIBLE = "visible" - * | INVISIBLE = "invisible" - * ; - * - * Please note that a corresponding value converter is needed. - * - * Implementation remark: - * - This template will insert validating semantic predicates in the rule - * - If the rule is used from an alternative a gated semantic predicate will - * be used in the alternative - * - Error messages will be adjusted correspondingly - */ -@Singleton -class AnnotationAwareAntlrGrammarGenerator extends AbstractAnnotationAwareAntlrGrammarGenerator { - - @Inject extension GrammarNaming naming - - @Accessors(PUBLIC_SETTER) String lexerSuperClassName = ""; - - protected override getGrammarNaming() { - naming - } - - protected override compileParserImports(Grammar it, AntlrOptions options) ''' - - import org.eclipse.xtext.*; - import org.eclipse.xtext.parser.*; - import org.eclipse.xtext.parser.impl.*; - import org.eclipse.emf.ecore.util.EcoreUtil; - import org.eclipse.emf.ecore.EObject; - «IF !allEnumRules.empty» - import org.eclipse.emf.common.util.Enumerator; - «ENDIF» - import «grammarNaming.getInternalParserSuperClass(it).name»; - import org.eclipse.xtext.parser.antlr.XtextTokenStream; - import org.eclipse.xtext.parser.antlr.XtextTokenStream.HiddenTokens; - «IF !allParserRules.map[eAllContentsAsList].flatten.filter(UnorderedGroup).empty && options.backtrack» - import org.eclipse.xtext.parser.antlr.IUnorderedGroupHelper.UnorderedGroupState; - «ENDIF» - import org.eclipse.xtext.parser.antlr.AntlrDatatypeRuleToken; - import «grammarAccess.name»; - «super.compileParserImports(it, options)» - - ''' - - protected override compileParserMembers(Grammar it, AntlrOptions options) ''' - - @«IF combinedGrammar»parser::«ENDIF»members { - - «IF options.backtrack» - /* - This grammar contains a lot of empty actions to work around a bug in ANTLR. - Otherwise the ANTLR tool will create synpreds that cannot be compiled in some rare cases. - */ - - «ENDIF» - «compileParserMemberDeclarations("private")» - - public «internalParserClass.simpleName»(TokenStream input, «grammarAccess.simpleName» grammarAccess, ParserContext parserContext, «getSemanticPredicatesSimpleName()» predicates) { - this(input); - this.grammarAccess = grammarAccess; - this.predicates = predicates; - this.parserContext = parserContext; - parserContext.setTokenStream(input); - registerRules(grammarAccess.getGrammar()); - } - - «compileParserSetTokenStreamMethod» - - @Override - protected String getFirstRuleName() { - return "«allParserRules.head.originalElement.name»"; - } - - @Override - protected «grammarAccess.simpleName» getGrammarAccess() { - return grammarAccess; - } - - } - ''' - - protected override compileRuleCatch(Grammar it, AntlrOptions options) ''' - - @rulecatch { - catch (RecognitionException re) { - recover(input,re); - appendSkippedTokens(); - } - } - ''' - - override protected shouldBeSkipped(TerminalRule it, Grammar grammar) { - false - } - - protected override dispatch compileRule(ParserRule it, Grammar grammar, AntlrOptions options) ''' - «IF isValidEntryRule()» - «compileEntryRule(grammar, options)» - «ENDIF» - - «compileEBNF(options)» - ''' - - protected def String compileEntryRule(ParserRule it, Grammar grammar, AntlrOptions options) ''' - // Entry rule «originalElement.entryRuleName» - «originalElement.entryRuleName» returns «compileEntryReturns(options)»«compileEntryInit(options)»: - { «newCompositeNode» } - iv_«originalElement.ruleName»=«ruleName»«defaultArgumentList» - { $current=$iv_«ruleName».current«IF originalElement.datatypeRule».getText()«ENDIF»; } - EOF; - «compileEntryFinally(options)» - ''' - - protected def compileEntryReturns(ParserRule it, AntlrOptions options) { - if (originalElement.datatypeRule) - return '[String current=null]' - else - return '''[«currentType» current=null]''' - } - - - protected override compileInit(AbstractRule it, AntlrOptions options) ''' - «IF it instanceof ParserRule»«getParameterList(!isPassCurrentIntoFragment, currentType)»«ENDIF» returns «compileReturns(options)» - «IF hasNoBacktrackAnnotation» - // Enclosing rule was annotated with @NoBacktrack - options { backtrack=false; } - «ENDIF» - @init { - enterRule(); - «compileInitHiddenTokens(options)» - «compileInitUnorderedGroups(options)» - } - @after { - leaveRule(); - }''' - - protected def compileReturns(AbstractRule it, AntlrOptions options) { - switch it { - EnumRule: - '[Enumerator current=null]' - ParserRule case originalElement.datatypeRule: - '[AntlrDatatypeRuleToken current=new AntlrDatatypeRuleToken()]' - ParserRule case originalElement.isEObjectFragmentRule: - '''[«currentType» current=in_current]''' - ParserRule: - '''[«currentType» current=null]''' - default: - throw new IllegalStateException("Unexpected rule: " + it) - } - } - - protected override String _dataTypeEbnf2(Keyword it, boolean supportActions) { - if (supportActions) ''' - kw=«super._dataTypeEbnf2(it, supportActions)» - { - $current.merge(kw); - «newLeafNode("kw")» - } - ''' - else - super._dataTypeEbnf2(it, supportActions) - } - - protected override String _ebnf2(Action it, AntlrOptions options, boolean supportActions) { - if (supportActions) ''' - «IF options.backtrack» - { - /* */ - } - «ENDIF» - { - $current = forceCreateModelElement«IF feature !== null»And«setOrAdd.toFirstUpper»«ENDIF»( - grammarAccess.«originalElement.grammarElementAccess», - $current); - } - ''' - else - super._ebnf2(it, options, supportActions) - } - - protected override String _ebnf2(Keyword it, AntlrOptions options, boolean supportActions) { - if (!supportActions) - super._ebnf2(it, options, supportActions) - else if (assigned) ''' - «super._ebnf2(it, options, supportActions)» - { - «newLeafNode(containingAssignment.localVar(it))» - } - ''' - else ''' - «localVar»=«super._ebnf2(it, options, supportActions)» - { - «newLeafNode(localVar)» - } - ''' - } - - override protected _ebnf2(EnumLiteralDeclaration it, AntlrOptions options, boolean supportActions) { - if (!supportActions) - super._ebnf2(it, options, supportActions) - else ''' - «localVar»=«super._ebnf2(it, options, supportActions)» - { - $current = grammarAccess.«grammarElementAccess(originalElement)».getEnumLiteral().getInstance(); - «newLeafNode(localVar)» - } - ''' - } - - protected override String crossrefEbnf(AbstractRule it, RuleCall call, CrossReference ref, boolean supportActions) { - if (supportActions) - switch it { - EnumRule, - ParserRule: ''' - { - «ref.newCompositeNode» - } - «ruleName»«call.getArgumentList(isPassCurrentIntoFragment, !supportActions)» - { - afterParserOrEnumRuleCall(); - } - ''' - TerminalRule: ''' - «ref.containingAssignment.localVar»=«ruleName» - { - «ref.newLeafNode(ref.containingAssignment.localVar)» - } - ''' - default: - throw new IllegalStateException("crossrefEbnf is not supported for " + it) - } - else - super.crossrefEbnf(it, call, ref, supportActions) - } - - override protected _assignmentEbnf(AbstractElement it, Assignment assignment, AntlrOptions options, boolean supportActions) { - if (supportActions) ''' - «assignment.localVar(it)»=«super._assignmentEbnf(it, assignment, options, supportActions)» - { - if ($current==null) { - $current = «assignment.createModelElement»; - } - «assignment.setOrAdd»WithLastConsumed($current, "«assignment.feature»", « - assignment.localVar(it)»«IF assignment.isBooleanAssignment» != null«ENDIF - », «assignment.terminal.toStringLiteral»); - } - ''' - else - super._assignmentEbnf(it, assignment, options, supportActions) - } - - override protected isPassCurrentIntoFragment() { - return true - } - - protected def createModelElement(EObject grammarElement) ''' - createModelElement(grammarAccess.«grammarElement.containingParserRule.originalElement.grammarElementAccess»)''' - - protected def createModelElementForParent(EObject grammarElement) ''' - createModelElementForParent(grammarAccess.«grammarElement.containingParserRule.originalElement.grammarElementAccess»)''' - - protected def newCompositeNode(EObject it) '''newCompositeNode(grammarAccess.«originalElement.grammarElementAccess»);''' - - protected def newLeafNode(EObject it, String token) '''newLeafNode(«token», grammarAccess.«originalElement.grammarElementAccess»);''' - - /** - * Add gated predicate, if necessary, before the action preceding the rule call. - */ - protected override String _dataTypeEbnf2(RuleCall it, boolean supportActions) { - if (supportActions) - switch rule { - EnumRule case assigned, - ParserRule case assigned: - super._dataTypeEbnf2(it, supportActions) - EnumRule, - ParserRule: ''' - «IF isGatedPredicateRequired»«generateGatedPredicate»«ENDIF» - { - «newCompositeNode» - } - «localVar»=«super._dataTypeEbnf2(it, supportActions)»«getArgumentList(isPassCurrentIntoFragment, !supportActions)» - { - $current.merge(«localVar»); - } - { - afterParserOrEnumRuleCall(); - } - ''' - TerminalRule: ''' - «localVar»=«super._dataTypeEbnf2(it, supportActions)» - { - $current.merge(«localVar»); - } - { - «newLeafNode(localVar)» - } - ''' - default: - super._dataTypeEbnf2(it, supportActions) - } - else - super._dataTypeEbnf2(it, supportActions) - } - - /** - * Inserts validating predicate only. Gated predicates will be inserted in alternatives if needed. - */ - protected override String compileEBNF(AbstractRule it, AntlrOptions options) ''' - // Rule «originalElement.name» - «ruleName»«compileInit(options)»: - «IF it instanceof ParserRule && originalElement.datatypeRule» - «IF hasValidatingPredicate»«generateValidatingPredicate»«ENDIF» - «dataTypeEbnf(alternatives, true)» - «ELSE» - «ebnf(alternatives, options, true)» - «ENDIF» - ; - «compileFinally(options)» - ''' - - protected override String dataTypeEbnf(AbstractElement it, boolean supportActions) ''' - «IF mustBeParenthesized»( - «IF hasNoBacktrackAnnotation» - // Enclosing rule was annotated with @NoBacktrack - options { backtrack=false; }: - «ENDIF» - «dataTypeEbnfPredicate»«dataTypeEbnf2(supportActions)» - )«ELSE»«dataTypeEbnf2(supportActions)»«ENDIF»«cardinality» - ''' - - protected override String ebnf(AbstractElement it, AntlrOptions options, boolean supportActions) ''' - «IF mustBeParenthesized»( - «IF hasNoBacktrackAnnotation» - // Enclosing rule was annotated with @NoBacktrack - options { backtrack=false; }: - «ENDIF» - «ebnfPredicate(options)»«ebnf2(options, supportActions)» - )«ELSE»«ebnf2(options, supportActions)»«ENDIF»«cardinality» - ''' - - - - /** - * Add gated predicate, if necessary, before the action preceding the assignment. - */ - protected override String _assignmentEbnf(RuleCall it, Assignment assignment, AntlrOptions options, boolean supportActions) { - if (supportActions) - switch rule { - EnumRule, - ParserRule: ''' - «IF isGatedPredicateRequired»«generateGatedPredicate»«ENDIF» - { - «newCompositeNode» - } - «assignment.localVar(it)»=«super._assignmentEbnf(it, assignment, options, supportActions)» - { - if ($current==null) { - $current = «assignment.createModelElementForParent»; - } - «assignment.setOrAdd»( - $current, - "«assignment.feature»", - «assignment.localVar(it)»«IF assignment.isBooleanAssignment» != null«ENDIF», - «toStringLiteral»); - afterParserOrEnumRuleCall(); - } - ''' - TerminalRule: ''' - «assignment.localVar(it)»=«super._assignmentEbnf(it, assignment, options, supportActions)» - { - «newLeafNode(assignment.localVar(it))» - } - { - if ($current==null) { - $current = «assignment.createModelElement»; - } - «assignment.setOrAdd»WithLastConsumed( - $current, - "«assignment.feature»", - «assignment.localVar(it)»«IF assignment.isBooleanAssignment» != null«ENDIF», - «toStringLiteral»); - } - ''' - default: - throw new IllegalStateException("assignmentEbnf is not supported for " + it) - } - else - super._assignmentEbnf(it, assignment, options, supportActions) - } - - /** - * Add gated predicate, if necessary, before the action preceding the cross reference. - */ - protected override _assignmentEbnf(CrossReference it, Assignment assignment, AntlrOptions options, boolean supportActions) { - if (supportActions) ''' - «IF isGatedPredicateRequired»«generateGatedPredicate»«ENDIF» - «IF options.backtrack» - { - /* */ - } - «ENDIF» - { - if ($current==null) { - $current = «assignment.createModelElement»; - } - } - «super._assignmentEbnf(it, assignment, options, supportActions)»''' - else - super._assignmentEbnf(it, assignment, options, supportActions) - } - - /** - * Add gated predicate, if necessary, before the action preceding the rule call. - */ - protected override String _ebnf2(RuleCall it, AntlrOptions options, boolean supportActions) { - if (!supportActions) - super._ebnf2(it, options, supportActions) - else - switch rule : rule { - EnumRule case assigned, - ParserRule case assigned: - super._ebnf2(it, options, supportActions) - EnumRule, - ParserRule case rule.originalElement.datatypeRule: ''' - «IF isGatedPredicateRequired»«generateGatedPredicate»«ENDIF» - «IF options.backtrack» - { - /* */ - } - «ENDIF» - { - «newCompositeNode» - } - «super._ebnf2(it, options, supportActions)» - { - afterParserOrEnumRuleCall(); - } - ''' - ParserRule: ''' - «IF isGatedPredicateRequired»«generateGatedPredicate»«ENDIF» - «IF options.backtrack» - { - /* */ - } - «ENDIF» - { - «IF isEObjectFragmentRuleCall» - if ($current==null) { - $current = «it.createModelElement»; - } - «ENDIF» - «newCompositeNode» - } - «localVar»=«super._ebnf2(it, options, supportActions)» - { - $current = $«localVar».current; - afterParserOrEnumRuleCall(); - } - ''' - TerminalRule: ''' - «localVar»=«super._ebnf2(it, options, supportActions)» - { - «newLeafNode(localVar)» - } - ''' - default: - super._ebnf2(it, options, supportActions) - } - } - - protected override compileLexerImports(Grammar it, AntlrOptions options) ''' - - // Hack: Use our own Lexer superclass by means of import. - // Currently there is no other way to specify the superclass for the lexer. - «IF !lexerSuperClassName.empty» - import «lexerSuperClassName»; - «ELSE» - import «grammarNaming.getLexerSuperClass(it)»; - «ENDIF» - ''' -} \ No newline at end of file diff --git a/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AnnotationAwareXtextAntlrGeneratorFragment2.java b/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AnnotationAwareXtextAntlrGeneratorFragment2.java new file mode 100644 index 0000000000..7d813fb59b --- /dev/null +++ b/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AnnotationAwareXtextAntlrGeneratorFragment2.java @@ -0,0 +1,1065 @@ +/******************************************************************************* + * Copyright (c) 2016 Avaloq Group AG and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Avaloq Group AG - initial API and implementation + *******************************************************************************/ +package com.avaloq.tools.ddk.xtext.generator.parser.antlr; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.antlr.runtime.CharStream; +import org.antlr.runtime.Token; +import org.antlr.runtime.TokenSource; +import org.eclipse.xtend2.lib.StringConcatenation; +import org.eclipse.xtend2.lib.StringConcatenationClient; +import org.eclipse.xtext.AbstractElement; +import org.eclipse.xtext.Grammar; +import org.eclipse.xtext.GrammarUtil; +import org.eclipse.xtext.ParserRule; +import org.eclipse.xtext.parser.antlr.AntlrTokenDefProvider; +import org.eclipse.xtext.parser.antlr.ITokenDefProvider; +import org.eclipse.xtext.parser.antlr.Lexer; +import org.eclipse.xtext.parser.antlr.LexerProvider; +import org.eclipse.xtext.parser.antlr.XtextTokenStream; +import org.eclipse.xtext.xbase.lib.IterableExtensions; +import org.eclipse.xtext.xtext.FlattenedGrammarAccess; +import org.eclipse.xtext.xtext.RuleFilter; +import org.eclipse.xtext.xtext.RuleNames; +import org.eclipse.xtext.xtext.generator.grammarAccess.GrammarAccessExtensions; +import org.eclipse.xtext.xtext.generator.model.FileAccessFactory; +import org.eclipse.xtext.xtext.generator.model.GeneratedJavaFileAccess; +import org.eclipse.xtext.xtext.generator.model.GuiceModuleAccess; +import org.eclipse.xtext.xtext.generator.model.IXtextGeneratorFileSystemAccess; +import org.eclipse.xtext.xtext.generator.model.JavaFileAccess; +import org.eclipse.xtext.xtext.generator.model.ManifestAccess; +import org.eclipse.xtext.xtext.generator.model.TypeReference; +import org.eclipse.xtext.xtext.generator.parser.antlr.AntlrGrammarGenUtil; +import org.eclipse.xtext.xtext.generator.parser.antlr.ContentAssistGrammarNaming; +import org.eclipse.xtext.xtext.generator.parser.antlr.GrammarNaming; +import org.eclipse.xtext.xtext.generator.parser.antlr.XtextAntlrGeneratorFragment2; + +import com.avaloq.tools.ddk.xtext.generator.parser.common.GrammarRuleAnnotations; +import com.avaloq.tools.ddk.xtext.generator.parser.common.GrammarRuleAnnotations.SemanticPredicate; +import com.avaloq.tools.ddk.xtext.generator.parser.common.PredicatesNaming; +import com.avaloq.tools.ddk.xtext.parser.ISemanticPredicates; +import com.avaloq.tools.ddk.xtext.parser.antlr.AbstractContextualAntlrParser; +import com.avaloq.tools.ddk.xtext.parser.antlr.ParserContext; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.google.inject.name.Names; + + +// CHECKSTYLE:OFF +@SuppressWarnings({"PMD.AvoidDuplicateLiterals", "PMD.TooManyMethods", "PMD.LocalVariableNamingConventions", "nls"}) +public class AnnotationAwareXtextAntlrGeneratorFragment2 extends XtextAntlrGeneratorFragment2 { + + private static final String ADDITIONAL_CA_REQUIRED_BUNDLE = "com.avaloq.tools.ddk.xtext"; + + @Inject + private AnnotationAwareAntlrGrammarGenerator productionGenerator; + + @Inject + private AnnotationAwareAntlrContentAssistGrammarGenerator contentAssistGenerator; + + @Inject + private GrammarNaming productionNaming; + + @Inject + private FileAccessFactory fileFactory; + + @Inject + private ContentAssistGrammarNaming contentAssistNaming; + + @Inject + private PredicatesNaming predicatesNaming; + + @Inject + private GrammarAccessExtensions grammarUtil; + + @Inject + private GrammarRuleAnnotations annotations; + + private boolean generateContentAssistIfIdeMissing; + + private boolean removeBacktrackingGuards; + + private int lookaheadThreshold; + + private boolean partialParsing; + + private Set reservedWords = ImmutableSet.of(); + + private Set keywords = ImmutableSet.of(); + + private Set identifierRules = ImmutableSet.of(); + + private String lexerSuperClassName = ""; + + /** + * Suffix used in the naming convention for the classes responsible for semantic predicates. + */ + private static final String CLASS_SUFFIX = "SemanticPredicates"; + + @Override + public void setRemoveBacktrackingGuards(final boolean removeBacktrackingGuards) { + this.removeBacktrackingGuards = removeBacktrackingGuards; + super.setRemoveBacktrackingGuards(removeBacktrackingGuards); + } + + @Override + public void setLookaheadThreshold(final String lookaheadThreshold) { + this.lookaheadThreshold = Integer.parseInt(lookaheadThreshold); + super.setLookaheadThreshold(lookaheadThreshold); + } + + @Override + public void setPartialParsing(final boolean partialParsing) { + this.partialParsing = partialParsing; + super.setPartialParsing(partialParsing); + } + + public void setReservedWords(final String words) { + this.reservedWords = toSet(words); + } + + public void setIdentifierRules(final String rules) { + this.identifierRules = toSet(rules); + } + + public void setKeywords(final String words) { + this.keywords = toSet(words); + } + + public void setLexerSuperClassName(final String className) { + this.lexerSuperClassName = className; + } + + private Set toSet(final String words) { + return Arrays.stream(words.split(",")).map(String::trim).filter(str -> !str.isEmpty()).collect(Collectors.toSet()); + } + + public boolean isGenerateContentAssistIfIdeMissing() { + return this.generateContentAssistIfIdeMissing; + } + + public void setGenerateContentAssistIfIdeMissing(final boolean generateContentAssistIfIdeMissing) { + this.generateContentAssistIfIdeMissing = generateContentAssistIfIdeMissing; + } + + @Override + protected void checkGrammar() { + super.checkGrammar(); + this.annotations.annotateGrammar(getGrammar()); + } + + @Override + protected void doGenerate() { + super.doGenerate(); + // if there is no ide plugin, write the content assist parser to the ui plugin. + if (this.generateContentAssistIfIdeMissing && getProjectConfig().getGenericIde().getSrcGen() == null) { + generateUiContentAssistGrammar(); + generateContentAssistParser().writeTo(getProjectConfig().getEclipsePlugin().getSrcGen()); + if (hasSyntheticTerminalRule()) { + generateContentAssistTokenSource().writeTo(getProjectConfig().getEclipsePlugin().getSrc()); + } + addIdeUiBindingsAndImports(); + } + generateAbstractSemanticPredicate().writeTo(getProjectConfig().getRuntime().getSrcGen()); + } + + @Override + protected void addRuntimeBindingsAndImports() { + super.addRuntimeBindingsAndImports(); + ManifestAccess manifest = getProjectConfig().getRuntime().getManifest(); + if (manifest != null) { + Set exportedPackages = manifest.getExportedPackages(); + String semanticPredicatesPackageName = this.predicatesNaming.getSemanticPredicatesPackageName(getGrammar()); + exportedPackages.add(semanticPredicatesPackageName); + } + } + + @Override + protected void generateContentAssistGrammar() { + generateContentAssistGrammar(getProjectConfig().getGenericIde().getSrcGen()); + } + + protected void generateContentAssistGrammar(final IXtextGeneratorFileSystemAccess fsa) { + final ContentAssistGrammarNaming naming = this.contentAssistNaming; + this.contentAssistGenerator.generate(getGrammar(), getOptions(), fsa); + runAntlr(naming.getParserGrammar(getGrammar()), naming.getLexerGrammar(getGrammar()), fsa); + simplifyUnorderedGroupPredicatesIfRequired(getGrammar(), fsa, naming.getInternalParserClass(getGrammar())); + splitParserAndLexerIfEnabled(fsa, naming.getInternalParserClass(getGrammar()), naming.getLexerClass(getGrammar())); + normalizeTokens(fsa, naming.getLexerGrammar(getGrammar()).getTokensFileName()); + suppressWarnings(fsa, naming.getInternalParserClass(getGrammar()), naming.getLexerClass(getGrammar())); + normalizeLineDelimiters(fsa, naming.getLexerClass(getGrammar()), naming.getInternalParserClass(getGrammar())); + if (this.removeBacktrackingGuards) { + removeBackTrackingGuards(fsa, naming.getInternalParserClass(getGrammar()), this.lookaheadThreshold); + } + } + + @Override + protected void addIdeBindingsAndImports() { + super.addIdeBindingsAndImports(); + ManifestAccess manifest = getProjectConfig().getGenericIde().getManifest(); + if (manifest != null) { + Set requiredBundles = manifest.getRequiredBundles(); + requiredBundles.add(ADDITIONAL_CA_REQUIRED_BUNDLE); + } + } + + protected void generateUiContentAssistGrammar() { + generateContentAssistGrammar(getProjectConfig().getEclipsePlugin().getSrcGen()); + } + + protected void addIdeUiBindingsAndImports() { + final ContentAssistGrammarNaming naming = this.contentAssistNaming; + ManifestAccess manifest = getProjectConfig().getEclipsePlugin().getManifest(); + if (manifest != null) { + Set exportedPackages = manifest.getExportedPackages(); + Iterables.addAll(exportedPackages, List.of( + naming.getLexerClass(getGrammar()).getPackageName(), + naming.getParserClass(getGrammar()).getPackageName(), + naming.getInternalParserClass(getGrammar()).getPackageName())); + Set requiredBundles = manifest.getRequiredBundles(); + Iterables.addAll(requiredBundles, List.of( + "org.antlr.runtime;bundle-version=\"[3.2.0,3.2.1)\"", + ADDITIONAL_CA_REQUIRED_BUNDLE)); + } + GuiceModuleAccess.BindingFactory ideBindings = new GuiceModuleAccess.BindingFactory() + .addConfiguredBinding("ContentAssistLexer", new StringConcatenationClient() { + @Override + protected void appendTo(final StringConcatenationClient.TargetStringConcatenation builder) { + builder.append("binder.bind("); + TypeReference _lexerSuperClass = naming.getLexerSuperClass(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + builder.append(_lexerSuperClass); + builder.append(".class)"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(".annotatedWith("); + builder.append(Names.class, " "); + builder.append(".named("); + TypeReference _typeRef = TypeReference.typeRef("org.eclipse.xtext.ide.LexerIdeBindings"); + builder.append(_typeRef, " "); + builder.append(".CONTENT_ASSIST))"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(".to("); + TypeReference _lexerClass = naming.getLexerClass(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + builder.append(_lexerClass, " "); + builder.append(".class);"); + builder.newLineIfNotEmpty(); + } + }) + .addTypeToType(TypeReference.typeRef("org.eclipse.xtext.ide.editor.contentassist.antlr.IContentAssistParser"), naming.getParserClass(getGrammar())) + .addConfiguredBinding("IProposalConflictHelper", new StringConcatenationClient() { + @Override + protected void appendTo(final StringConcatenationClient.TargetStringConcatenation builder) { + builder.append("binder.bind(org.eclipse.xtext.ide.editor.contentassist.IProposalConflictHelper.class)"); + builder.newLine(); + builder.append(" "); + builder.append(".to(org.eclipse.xtext.ide.editor.contentassist.antlr.AntlrProposalConflictHelper.class);"); + builder.newLine(); + } + }); + if (this.partialParsing) { + ideBindings.addTypeToType( + TypeReference.typeRef("org.eclipse.xtext.ide.editor.contentassist.antlr.ContentAssistContextFactory"), + TypeReference.typeRef("org.eclipse.xtext.ide.editor.contentassist.antlr.PartialContentAssistContextFactory")); + } + if (hasSyntheticTerminalRule()) { + ideBindings.addTypeToType( + TypeReference.typeRef("org.eclipse.xtext.ide.editor.contentassist.CompletionPrefixProvider"), + TypeReference.typeRef("org.eclipse.xtext.ide.editor.contentassist.IndentationAwareCompletionPrefixProvider")); + } + ideBindings.contributeTo(getLanguage().getEclipsePluginGenModule()); + } + + @Override + public JavaFileAccess generateContentAssistParser() { + final ContentAssistGrammarNaming naming = this.contentAssistNaming; + final GeneratedJavaFileAccess file = this.fileFactory.createGeneratedJavaFile(naming.getParserClass(getGrammar())); + StringConcatenationClient client = new StringConcatenationClient() { + @Override + protected void appendTo(final StringConcatenationClient.TargetStringConcatenation builder) { + builder.append("public class "); + String _simpleName = naming.getParserClass(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()).getSimpleName(); + builder.append(_simpleName); + builder.append(" extends "); + TypeReference _parserSuperClass = naming.getParserSuperClass(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar(), AnnotationAwareXtextAntlrGeneratorFragment2.this.partialParsing); + builder.append(_parserSuperClass); + builder.append(" {"); + builder.newLineIfNotEmpty(); + builder.newLine(); + builder.append(" "); + StringConcatenationClient _initNameMappings = AnnotationAwareXtextAntlrGeneratorFragment2.this.initNameMappings(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + builder.append(_initNameMappings, " "); + builder.newLineIfNotEmpty(); + builder.newLine(); + builder.append(" "); + builder.append("@"); + builder.append(Inject.class, " "); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("private "); + TypeReference _grammarAccess = AnnotationAwareXtextAntlrGeneratorFragment2.this.grammarUtil.getGrammarAccess(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + builder.append(_grammarAccess, " "); + builder.append(" grammarAccess;"); + builder.newLineIfNotEmpty(); + builder.newLine(); + builder.append(" "); + builder.append("@"); + builder.append(Inject.class, " "); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("private "); + TypeReference _typeRef = TypeReference.typeRef(ISemanticPredicates.class); + builder.append(_typeRef, " "); + builder.append(" predicates;"); + builder.newLineIfNotEmpty(); + builder.newLine(); + builder.append(" "); + builder.append("/**"); + builder.newLine(); + builder.append(" "); + builder.append("* Creates compilation context."); + builder.newLine(); + builder.append(" "); + builder.append("*"); + builder.newLine(); + builder.append(" "); + builder.append("* @param Input"); + builder.newLine(); + builder.append(" "); + builder.append("* Stream"); + builder.newLine(); + builder.append(" "); + builder.append("* @return Compilation context"); + builder.newLine(); + builder.append(" "); + builder.append("*/"); + builder.newLine(); + builder.append(" "); + builder.append("protected "); + TypeReference _typeRef_1 = TypeReference.typeRef(ParserContext.class); + builder.append(_typeRef_1, " "); + builder.append(" createParserContext() {"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("return new ParserContext();"); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.newLine(); + builder.append(" "); + builder.append("@Override"); + builder.newLine(); + builder.append(" "); + builder.append("protected "); + TypeReference _internalParserClass = naming.getInternalParserClass(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + builder.append(_internalParserClass, " "); + builder.append(" createParser() {"); + builder.newLineIfNotEmpty(); + builder.append(" "); + TypeReference _internalParserClass_1 = naming.getInternalParserClass(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + builder.append(_internalParserClass_1, " "); + builder.append(" result = new "); + TypeReference _internalParserClass_2 = naming.getInternalParserClass(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + builder.append(_internalParserClass_2, " "); + builder.append("(null);"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("result.setGrammarAccess(grammarAccess);"); + builder.newLine(); + builder.append(" "); + builder.append("result.setParserContext(createParserContext());"); + builder.newLine(); + builder.append(" "); + builder.append("result.setPredicates(("); + TypeReference _typeRef_2 = TypeReference.typeRef(AnnotationAwareXtextAntlrGeneratorFragment2.this.predicatesNaming.getSemanticPredicatesFullName(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar())); + builder.append(_typeRef_2, " "); + builder.append(")predicates);"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("return result;"); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.newLine(); + { + boolean _hasSyntheticTerminalRule = AnnotationAwareXtextAntlrGeneratorFragment2.this.hasSyntheticTerminalRule(); + if (_hasSyntheticTerminalRule) { + builder.append(" "); + builder.append("@Override"); + builder.newLine(); + builder.append(" "); + builder.append("protected "); + builder.append(TokenSource.class, " "); + builder.append(" createLexer("); + builder.append(CharStream.class, " "); + builder.append(" stream) {"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(" "); + builder.append("return new "); + TypeReference _tokenSourceClass = naming.getTokenSourceClass(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + builder.append(_tokenSourceClass, " "); + builder.append("(super.createLexer(stream));"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.newLine(); + } + } + builder.append(" "); + builder.append("@Override"); + builder.newLine(); + builder.append(" "); + builder.append("protected String getRuleName("); + builder.append(AbstractElement.class, " "); + builder.append(" element) {"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("return nameMappings.getRuleName(element);"); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.newLine(); + builder.append(" "); + builder.append("@Override"); + builder.newLine(); + builder.append(" "); + builder.append("protected String[] getInitialHiddenTokens() {"); + builder.newLine(); + builder.append(" "); + builder.append("return new String[] { "); + { + List _initialHiddenTokens = AnnotationAwareXtextAntlrGeneratorFragment2.this.grammarUtil.initialHiddenTokens(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + boolean _hasElements = false; + for (final String hidden : _initialHiddenTokens) { + if (!_hasElements) { + _hasElements = true; + } else { + builder.appendImmediate(", ", " "); + } + builder.append("\""); + builder.append(hidden, " "); + builder.append("\""); + } + } + builder.append(" };"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.newLine(); + builder.append(" "); + builder.append("public "); + TypeReference _grammarAccess_1 = AnnotationAwareXtextAntlrGeneratorFragment2.this.grammarUtil.getGrammarAccess(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + builder.append(_grammarAccess_1, " "); + builder.append(" getGrammarAccess() {"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("return this.grammarAccess;"); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.newLine(); + builder.append(" "); + builder.append("public void setGrammarAccess("); + TypeReference _grammarAccess_2 = AnnotationAwareXtextAntlrGeneratorFragment2.this.grammarUtil.getGrammarAccess(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + builder.append(_grammarAccess_2, " "); + builder.append(" grammarAccess) {"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("this.grammarAccess = grammarAccess;"); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.newLine(); + builder.append(" "); + builder.append("public NameMappings getNameMappings() {"); + builder.newLine(); + builder.append(" "); + builder.append("return nameMappings;"); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.newLine(); + builder.append(" "); + builder.append("public void setNameMappings(NameMappings nameMappings) {"); + builder.newLine(); + builder.append(" "); + builder.append("this.nameMappings = nameMappings;"); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.append("}"); + builder.newLine(); + } + }; + file.setContent(client); + return file; + } + + @Override + protected void generateProductionGrammar() { + final GrammarNaming naming = this.productionNaming; + final IXtextGeneratorFileSystemAccess fsa = getProjectConfig().getRuntime().getSrcGen(); + this.productionGenerator.setLexerSuperClassName(this.lexerSuperClassName); + this.productionGenerator.generate(getGrammar(), getOptions(), fsa); + runAntlr(naming.getParserGrammar(getGrammar()), naming.getLexerGrammar(getGrammar()), fsa); + simplifyUnorderedGroupPredicatesIfRequired(getGrammar(), fsa, naming.getInternalParserClass(getGrammar())); + splitParserAndLexerIfEnabled(fsa, naming.getInternalParserClass(getGrammar()), naming.getLexerClass(getGrammar())); + normalizeTokens(fsa, naming.getLexerGrammar(getGrammar()).getTokensFileName()); + suppressWarnings(fsa, naming.getInternalParserClass(getGrammar()), naming.getLexerClass(getGrammar())); + normalizeLineDelimiters(fsa, naming.getInternalParserClass(getGrammar()), naming.getLexerClass(getGrammar())); + + /* filter and ruleNames for flattened grammar */ + final RuleFilter filter = new RuleFilter(); + filter.setDiscardUnreachableRules(true); + filter.setDiscardTerminalRules(false); + final RuleNames ruleNames = RuleNames.getRuleNames(getGrammar(), true); + + String grammarFileName = this.productionGenerator.getGrammarNaming().getParserGrammar(getGrammar()).getGrammarFileName(); + Grammar flattenedGrammar = new FlattenedGrammarAccess(ruleNames, filter).getFlattenedGrammar(); + final KeywordAnalysisHelper keywordAnalysisHelper = new KeywordAnalysisHelper(grammarFileName, flattenedGrammar, this.identifierRules, this.reservedWords, this.keywords, this.grammarUtil); + keywordAnalysisHelper.printReport(fsa.getPath()); + keywordAnalysisHelper.printViolations(fsa.getPath()); + } + + public JavaFileAccess generateAbstractSemanticPredicate() { + final GeneratedJavaFileAccess file = this.fileFactory.createGeneratedJavaFile(TypeReference.typeRef(this.predicatesNaming.getSemanticPredicatesFullName(getGrammar()))); + file.importType(TypeReference.typeRef(this.predicatesNaming.getSemanticPredicatesFullName(getGrammar()))); + file.importType(TypeReference.typeRef(ISemanticPredicates.AbstractSemanticPredicates.class)); + if (!this.annotations.predicates(getGrammar()).isEmpty()) { + file.importType(TypeReference.typeRef(ParserContext.class)); + file.importType(TypeReference.typeRef(Token.class)); + for (final Grammar inheritedGrammar : this.annotations.allInheritedGrammars(getGrammar())) { + file.importType(TypeReference.typeRef(GrammarUtil.getNamespace(inheritedGrammar) + ".grammar." + GrammarUtil.getSimpleName(inheritedGrammar) + CLASS_SUFFIX)); + } + } + file.importType(TypeReference.typeRef(Singleton.class)); + StringConcatenationClient client = new StringConcatenationClient() { + @Override + protected void appendTo(final StringConcatenationClient.TargetStringConcatenation builder) { + builder.append("/**"); + builder.newLine(); + builder.append(" "); + builder.append("* Provides semantic predicates as specified in the grammar. Language may need to override"); + builder.newLine(); + builder.append(" "); + builder.append("* this class in order to provide concrete implementations for predicates."); + builder.newLine(); + builder.append(" "); + builder.append("*/"); + builder.newLine(); + builder.append("@Singleton"); + builder.newLine(); + builder.append("public class "); + String _semanticPredicatesSimpleName = AnnotationAwareXtextAntlrGeneratorFragment2.this.predicatesNaming.getSemanticPredicatesSimpleName(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + builder.append(_semanticPredicatesSimpleName); + builder.append(" extends AbstractSemanticPredicates {"); + builder.newLineIfNotEmpty(); + { + List _predicates = AnnotationAwareXtextAntlrGeneratorFragment2.this.annotations.predicates(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + for (final GrammarRuleAnnotations.SemanticPredicate element : _predicates) { + builder.newLine(); + builder.append(" "); + builder.append("/**"); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("* Predicate for grammar rule "); + String _name = element.getName(); + builder.append(_name, " "); + builder.append("."); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(" "); + builder.append("*"); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("* @param input"); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("* Input from Lexer"); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("* @return {@code true} if the grammar rule is enabled, {@code false} otherwise"); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("*/"); + builder.newLine(); + builder.append(" "); + builder.append("public boolean "); + String _name_1 = element.getName(); + builder.append(_name_1, " "); + builder.append("(ParserContext parserContext) {"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(" "); + String _rulePredicateCondition = AnnotationAwareXtextAntlrGeneratorFragment2.this.getRulePredicateCondition(element); + builder.append(_rulePredicateCondition, " "); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + } + } + builder.newLine(); + { + List _predicates_1 = AnnotationAwareXtextAntlrGeneratorFragment2.this.annotations.predicates(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + for (final GrammarRuleAnnotations.SemanticPredicate element_1 : _predicates_1) { + builder.newLine(); + builder.append(" "); + builder.append("/**"); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("* Message for "); + String _name_2 = element_1.getName(); + builder.append(_name_2, " "); + builder.append(" predicate."); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(" "); + builder.append("*"); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("* @param input"); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("* Input from Lexer"); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("* @return {@code true} if the grammar rule is enabled, {@code false} otherwise"); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("*/"); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("public String "); + String _message = element_1.getMessage(); + builder.append(_message, " "); + builder.append("(Token token) {"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(" "); + String _rulePredicateMessage = AnnotationAwareXtextAntlrGeneratorFragment2.this.getRulePredicateMessage(element_1); + builder.append(_rulePredicateMessage, " "); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(" "); + builder.append("}"); + builder.newLine(); + } + } + builder.append("}"); + builder.newLine(); + } + }; + file.setContent(client); + return file; + } + + /** + * Returns name for the predicate message method. + * + * @param predicate + * Semantic predicate, must not be {@code null} + * @return Content for the predicate message method + */ + public String getRulePredicateMessage(final SemanticPredicate predicate) { + if (predicate.getKeywords() != null) { + StringConcatenation msgBuilder = new StringConcatenation(); + msgBuilder.append("return \"Unexpected: \" + token.getText() + \". Expected: \'"); + String join = Joiner.on("\', \'").join(predicate.getKeywords()); + msgBuilder.append(join); + msgBuilder.append("\'\";"); + msgBuilder.newLineIfNotEmpty(); + return msgBuilder.toString(); + } else { + StringConcatenation defaultBuilder = new StringConcatenation(); + defaultBuilder.append("/* Default message. Intended to be overridden. */"); + defaultBuilder.newLine(); + defaultBuilder.append("return \"Condition "); + String name = predicate.getName(); + defaultBuilder.append(name); + defaultBuilder.append(" is not fullfilled \";"); + defaultBuilder.newLineIfNotEmpty(); + return defaultBuilder.toString(); + } + } + + /** + * Returns predicate condition (default condition). + * + * @param predicate + * Semantic predicate, must not be {@code null} + * @return A string containing the condition for the semantic predicate or {@code null} + */ + public String getRulePredicateCondition(final SemanticPredicate predicate) { + if (predicate.getKeywords() != null) { + final String condition = predicate.getKeywords().stream().map(s -> { + StringConcatenation kwBuilder = new StringConcatenation(); + kwBuilder.append("\""); + kwBuilder.append(s); + kwBuilder.append("\".equalsIgnoreCase(text)"); + return kwBuilder.toString(); + }).collect(Collectors.joining(" || ")); + StringConcatenation condBuilder = new StringConcatenation(); + condBuilder.append("String text = parserContext.getInput().LT(1).getText();"); + condBuilder.newLine(); + condBuilder.append("return "); + condBuilder.append(condition); + condBuilder.append(";"); + condBuilder.newLineIfNotEmpty(); + return condBuilder.toString(); + } else { + return "return " + predicate.getGrammar() + CLASS_SUFFIX + "." + predicate.getName() + "(parserContext);\n"; + } + } + + @Override + public JavaFileAccess generateProductionParser() { + final GrammarNaming naming = this.productionNaming; + final GeneratedJavaFileAccess file = this.fileFactory.createGeneratedJavaFile(naming.getParserClass(getGrammar())); + file.importType(TypeReference.typeRef(this.predicatesNaming.getSemanticPredicatesFullName(getGrammar()))); + file.importType(TypeReference.typeRef(ISemanticPredicates.class)); + StringConcatenationClient client = new StringConcatenationClient() { + @Override + protected void appendTo(final StringConcatenationClient.TargetStringConcatenation builder) { + builder.append("public class "); + String _simpleName = naming.getParserClass(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()).getSimpleName(); + builder.append(_simpleName); + builder.append(" extends "); + builder.append(AbstractContextualAntlrParser.class); + builder.append(" {"); + builder.newLineIfNotEmpty(); + builder.newLine(); + builder.append(" "); + builder.append("@"); + builder.append(Inject.class, " "); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("private "); + TypeReference _grammarAccess = AnnotationAwareXtextAntlrGeneratorFragment2.this.grammarUtil.getGrammarAccess(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + builder.append(_grammarAccess, " "); + builder.append(" grammarAccess;"); + builder.newLineIfNotEmpty(); + builder.newLine(); + builder.append(" "); + builder.append("@"); + builder.append(Inject.class, " "); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("private ISemanticPredicates predicates;"); + builder.newLine(); + builder.newLine(); + builder.append(" "); + builder.append("@Override"); + builder.newLine(); + builder.append(" "); + builder.append("protected void setInitialHiddenTokens("); + builder.append(XtextTokenStream.class, " "); + builder.append(" tokenStream) {"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("tokenStream.setInitialHiddenTokens("); + { + List _initialHiddenTokens = AnnotationAwareXtextAntlrGeneratorFragment2.this.grammarUtil.initialHiddenTokens(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + boolean _hasElements = false; + for (final String hidden : _initialHiddenTokens) { + if (!_hasElements) { + _hasElements = true; + } else { + builder.appendImmediate(", ", " "); + } + builder.append("\""); + builder.append(hidden, " "); + builder.append("\""); + } + } + builder.append(");"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.newLine(); + { + boolean _hasSyntheticTerminalRule = AnnotationAwareXtextAntlrGeneratorFragment2.this.hasSyntheticTerminalRule(); + if (_hasSyntheticTerminalRule) { + builder.append(" "); + builder.append("@Override"); + builder.newLine(); + builder.append(" "); + builder.append("protected "); + builder.append(TokenSource.class, " "); + builder.append(" createLexer("); + builder.append(CharStream.class, " "); + builder.append(" stream) {"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(" "); + builder.append("return new "); + TypeReference _tokenSourceClass = naming.getTokenSourceClass(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + builder.append(_tokenSourceClass, " "); + builder.append("(super.createLexer(stream));"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.newLine(); + builder.append(" "); + builder.append("/**"); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("* Indentation aware languages do not support partial parsing since the lexer is inherently stateful."); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("* Override and return {@code true} if your terminal splitting is stateless."); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("*/"); + builder.newLine(); + builder.append(" "); + builder.append("@Override"); + builder.newLine(); + builder.append(" "); + builder.append("protected boolean isReparseSupported() {"); + builder.newLine(); + builder.append(" "); + builder.append(" "); + builder.append("return false;"); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + } + } + builder.newLine(); + builder.append(" "); + builder.append("@Override"); + builder.newLine(); + builder.append(" "); + builder.append("protected "); + TypeReference _internalParserClass = naming.getInternalParserClass(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + builder.append(_internalParserClass, " "); + builder.append(" createParser("); + builder.append(XtextTokenStream.class, " "); + builder.append(" stream) {"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("return new "); + TypeReference _internalParserClass_1 = naming.getInternalParserClass(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + builder.append(_internalParserClass_1, " "); + builder.append("(stream, getGrammarAccess(), createParserContext(), ("); + String _semanticPredicatesSimpleName = AnnotationAwareXtextAntlrGeneratorFragment2.this.predicatesNaming.getSemanticPredicatesSimpleName(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + builder.append(_semanticPredicatesSimpleName, " "); + builder.append(") predicates);"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.newLine(); + builder.append(" "); + builder.append("@Override"); + builder.newLine(); + builder.append(" "); + builder.append("protected String getDefaultRuleName() {"); + builder.newLine(); + builder.append(" "); + builder.append("return \""); + String _name = AntlrGrammarGenUtil.getOriginalElement(IterableExtensions.head(GrammarUtil.allParserRules(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()))).getName(); + builder.append(_name, " "); + builder.append("\";"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.newLine(); + builder.append(" "); + builder.append("public "); + TypeReference _grammarAccess_1 = AnnotationAwareXtextAntlrGeneratorFragment2.this.grammarUtil.getGrammarAccess(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + builder.append(_grammarAccess_1, " "); + builder.append(" getGrammarAccess() {"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("return this.grammarAccess;"); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.newLine(); + builder.append(" "); + builder.append("public void setGrammarAccess("); + TypeReference _grammarAccess_2 = AnnotationAwareXtextAntlrGeneratorFragment2.this.grammarUtil.getGrammarAccess(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + builder.append(_grammarAccess_2, " "); + builder.append(" grammarAccess) {"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append("this.grammarAccess = grammarAccess;"); + builder.newLine(); + builder.append(" "); + builder.append("}"); + builder.newLine(); + builder.append("}"); + builder.newLine(); + } + }; + file.setContent(client); + return file; + } + + @Override + protected void addUiBindingsAndImports() { + /* Overridden to remove the binding for IContentAssistParser, which we bind at a different place. + * If we would register it here, we would have conflicting bindings for IContentAssistParser. + */ + final ContentAssistGrammarNaming naming = this.contentAssistNaming; + final TypeReference caLexerClass = naming.getLexerClass(getGrammar()); + + ManifestAccess manifest = getProjectConfig().getGenericIde().getManifest(); + if (manifest != null) { + Set exportedPackages = manifest.getExportedPackages(); + Iterables.addAll(exportedPackages, List.of( + caLexerClass.getPackageName(), + naming.getParserClass(getGrammar()).getPackageName(), + naming.getInternalParserClass(getGrammar()).getPackageName())); + } + GuiceModuleAccess.BindingFactory uiBindings = new GuiceModuleAccess.BindingFactory() + .addTypeToType( + TypeReference.typeRef("org.eclipse.xtext.ui.editor.contentassist.IProposalConflictHelper"), + TypeReference.typeRef("org.eclipse.xtext.ui.editor.contentassist.antlr.AntlrProposalConflictHelper")) + .addConfiguredBinding("ContentAssistLexer", new StringConcatenationClient() { + @Override + protected void appendTo(final StringConcatenationClient.TargetStringConcatenation builder) { + builder.append("binder.bind("); + TypeReference _lexerSuperClass = naming.getLexerSuperClass(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + builder.append(_lexerSuperClass); + builder.append(".class)"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(".annotatedWith("); + builder.append(Names.class, " "); + builder.append(".named("); + TypeReference _typeRef = TypeReference.typeRef("org.eclipse.xtext.ide.LexerIdeBindings"); + builder.append(_typeRef, " "); + builder.append(".CONTENT_ASSIST))"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(".to("); + builder.append(caLexerClass, " "); + builder.append(".class);"); + builder.newLineIfNotEmpty(); + } + }) + // registration of the 'ContentAssistLexer' is put in front of the 'HighlightingLexer' + // in order to let 'caLexerClass' get added to the imports, since it is referenced + // several times and the lexer classes' simple names are usually identical + .addConfiguredBinding("HighlightingLexer", new StringConcatenationClient() { + @Override + protected void appendTo(final StringConcatenationClient.TargetStringConcatenation builder) { + builder.append("binder.bind("); + builder.append(Lexer.class); + builder.append(".class)"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(".annotatedWith("); + builder.append(Names.class, " "); + builder.append(".named("); + TypeReference _typeRef = TypeReference.typeRef("org.eclipse.xtext.ide.LexerIdeBindings"); + builder.append(_typeRef, " "); + builder.append(".HIGHLIGHTING))"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(".to("); + TypeReference _lexerClass = AnnotationAwareXtextAntlrGeneratorFragment2.this.productionNaming.getLexerClass(AnnotationAwareXtextAntlrGeneratorFragment2.this.getGrammar()); + builder.append(_lexerClass, " "); + builder.append(".class);"); + builder.newLineIfNotEmpty(); + } + }) + .addConfiguredBinding("HighlightingTokenDefProvider", new StringConcatenationClient() { + @Override + protected void appendTo(final StringConcatenationClient.TargetStringConcatenation builder) { + builder.append("binder.bind("); + builder.append(ITokenDefProvider.class); + builder.append(".class)"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(".annotatedWith("); + builder.append(Names.class, " "); + builder.append(".named("); + TypeReference _typeRef = TypeReference.typeRef("org.eclipse.xtext.ide.LexerIdeBindings"); + builder.append(_typeRef, " "); + builder.append(".HIGHLIGHTING))"); + builder.newLineIfNotEmpty(); + builder.append(" "); + builder.append(".to("); + builder.append(AntlrTokenDefProvider.class, " "); + builder.append(".class);"); + builder.newLineIfNotEmpty(); + } + }) + .addTypeToType( + new TypeReference("org.eclipse.xtext.ui.editor.contentassist", "ContentAssistContext.Factory"), + TypeReference.typeRef("org.eclipse.xtext.ui.editor.contentassist.antlr.DelegatingContentAssistContextFactory")) + .addConfiguredBinding("ContentAssistLexerProvider", new StringConcatenationClient() { + @Override + protected void appendTo(final StringConcatenationClient.TargetStringConcatenation builder) { + builder.append("binder.bind("); + builder.append(caLexerClass); + builder.append(".class).toProvider("); + builder.append(LexerProvider.class); + builder.append(".create("); + builder.append(caLexerClass); + builder.append(".class));"); + builder.newLineIfNotEmpty(); + } + }); + + if (getProjectConfig().getGenericIde().getSrcGen() != null) { + uiBindings.addTypeToType(TypeReference.typeRef("org.eclipse.xtext.ide.editor.contentassist.antlr.IContentAssistParser"), naming.getParserClass(getGrammar())); + } + + if (hasSyntheticTerminalRule()) { + uiBindings.addTypeToType( + TypeReference.typeRef("org.eclipse.xtext.ide.editor.contentassist.CompletionPrefixProvider"), + TypeReference.typeRef("org.eclipse.xtext.ide.editor.contentassist.IndentationAwareCompletionPrefixProvider")); + } + uiBindings.contributeTo(getLanguage().getEclipsePluginGenModule()); + } +} +// CHECKSTYLE:ON diff --git a/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AnnotationAwareXtextAntlrGeneratorFragment2.xtend b/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AnnotationAwareXtextAntlrGeneratorFragment2.xtend deleted file mode 100644 index c7bbdd8c29..0000000000 --- a/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/antlr/AnnotationAwareXtextAntlrGeneratorFragment2.xtend +++ /dev/null @@ -1,530 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016 Avaloq Group AG and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Avaloq Group AG - initial API and implementation - *******************************************************************************/ - -package com.avaloq.tools.ddk.xtext.generator.parser.antlr - -import com.avaloq.tools.ddk.xtext.generator.parser.common.GrammarRuleAnnotations -import com.avaloq.tools.ddk.xtext.generator.parser.common.GrammarRuleAnnotations.SemanticPredicate -import com.avaloq.tools.ddk.xtext.generator.parser.common.PredicatesNaming -import com.avaloq.tools.ddk.xtext.parser.ISemanticPredicates -import com.avaloq.tools.ddk.xtext.parser.antlr.AbstractContextualAntlrParser -import com.avaloq.tools.ddk.xtext.parser.antlr.ParserContext -import com.google.common.base.Joiner -import com.google.common.collect.ImmutableSet -import com.google.inject.Inject -import com.google.inject.Singleton -import com.google.inject.name.Names -import java.util.Arrays -import java.util.Set -import java.util.stream.Collectors -import org.antlr.runtime.CharStream -import org.antlr.runtime.Token -import org.antlr.runtime.TokenSource -import org.eclipse.xtext.AbstractElement -import org.eclipse.xtext.GrammarUtil -import org.eclipse.xtext.parser.antlr.AntlrTokenDefProvider -import org.eclipse.xtext.parser.antlr.ITokenDefProvider -import org.eclipse.xtext.parser.antlr.Lexer -import org.eclipse.xtext.parser.antlr.LexerProvider -import org.eclipse.xtext.parser.antlr.XtextTokenStream -import org.eclipse.xtext.xtext.FlattenedGrammarAccess -import org.eclipse.xtext.xtext.RuleFilter -import org.eclipse.xtext.xtext.RuleNames -import org.eclipse.xtext.xtext.generator.grammarAccess.GrammarAccessExtensions -import org.eclipse.xtext.xtext.generator.model.FileAccessFactory -import org.eclipse.xtext.xtext.generator.model.GuiceModuleAccess -import org.eclipse.xtext.xtext.generator.model.IXtextGeneratorFileSystemAccess -import org.eclipse.xtext.xtext.generator.model.JavaFileAccess -import org.eclipse.xtext.xtext.generator.model.TypeReference -import org.eclipse.xtext.xtext.generator.parser.antlr.ContentAssistGrammarNaming -import org.eclipse.xtext.xtext.generator.parser.antlr.GrammarNaming -import org.eclipse.xtext.xtext.generator.parser.antlr.XtextAntlrGeneratorFragment2 - -import static extension org.eclipse.xtext.GrammarUtil.* -import static extension org.eclipse.xtext.xtext.generator.model.TypeReference.* -import static extension org.eclipse.xtext.xtext.generator.parser.antlr.AntlrGrammarGenUtil.* -import org.eclipse.xtend.lib.annotations.Accessors - -class AnnotationAwareXtextAntlrGeneratorFragment2 extends XtextAntlrGeneratorFragment2 { - - static val ADDITIONAL_CA_REQUIRED_BUNDLE = "com.avaloq.tools.ddk.xtext" - - @Inject AnnotationAwareAntlrGrammarGenerator productionGenerator - @Inject AnnotationAwareAntlrContentAssistGrammarGenerator contentAssistGenerator - @Inject GrammarNaming productionNaming - @Inject FileAccessFactory fileFactory - @Inject ContentAssistGrammarNaming contentAssistNaming - - @Inject extension PredicatesNaming predicatesNaming - @Inject extension GrammarAccessExtensions grammarUtil - @Inject extension GrammarRuleAnnotations annotations - - @Accessors boolean generateContentAssistIfIdeMissing - - boolean removeBacktrackingGuards - int lookaheadThreshold - boolean partialParsing - - Set reservedWords = ImmutableSet.of(); - Set keywords = ImmutableSet.of(); - Set identifierRules = ImmutableSet.of(); - String lexerSuperClassName = ""; - - /** - * Suffix used in the naming convention for the classes responsible for semantic predicates. - */ - static val CLASS_SUFFIX = "SemanticPredicates"; - - override setRemoveBacktrackingGuards(boolean removeBacktrackingGuards) { - this.removeBacktrackingGuards = removeBacktrackingGuards - super.setRemoveBacktrackingGuards(removeBacktrackingGuards) - } - - override setLookaheadThreshold(String lookaheadThreshold) { - this.lookaheadThreshold = Integer.parseInt(lookaheadThreshold) - super.setLookaheadThreshold(lookaheadThreshold) - } - - override setPartialParsing(boolean partialParsing) { - this.partialParsing = partialParsing - super.setPartialParsing(partialParsing) - } - - def setReservedWords(String words) { - reservedWords = toSet(words); - } - - def setIdentifierRules(String rules) { - identifierRules = toSet(rules); - } - - def setKeywords(String words) { - keywords = toSet(words); - } - - def setLexerSuperClassName (String className) { - lexerSuperClassName = className - } - - def private Set toSet(String words) { - Arrays.stream(words.split(",")).map(str | str.trim()).filter(str | !str.isEmpty()).collect(Collectors.toSet()); - } - - protected override void checkGrammar() { - super.checkGrammar(); - getGrammar().annotateGrammar - } - - protected override doGenerate() { - super.doGenerate() - // if there is no ide plugin, write the content assist parser to the ui plugin. - if (generateContentAssistIfIdeMissing && projectConfig.genericIde.srcGen === null) { - generateUiContentAssistGrammar() - generateContentAssistParser().writeTo(projectConfig.eclipsePlugin.srcGen) - if (hasSyntheticTerminalRule()) { - generateContentAssistTokenSource().writeTo(projectConfig.eclipsePlugin.src) - } - addIdeUiBindingsAndImports() - } - - generateAbstractSemanticPredicate().writeTo(projectConfig.runtime.srcGen) - } - - override protected addRuntimeBindingsAndImports() { - super.addRuntimeBindingsAndImports - if (projectConfig.runtime.manifest !== null) { - projectConfig.runtime.manifest=>[ - exportedPackages += #[ - grammar.semanticPredicatesPackageName - ] - ] - } - } - - protected override generateContentAssistGrammar() { - generateContentAssistGrammar(projectConfig.genericIde.srcGen) - } - - protected def generateContentAssistGrammar(IXtextGeneratorFileSystemAccess fsa) { - val extension naming = contentAssistNaming - - contentAssistGenerator.generate(grammar, options, fsa) - - runAntlr(grammar.parserGrammar, grammar.lexerGrammar, fsa) - - simplifyUnorderedGroupPredicatesIfRequired(grammar, fsa, grammar.internalParserClass) - splitParserAndLexerIfEnabled(fsa, grammar.internalParserClass, grammar.lexerClass) - normalizeTokens(fsa, grammar.lexerGrammar.tokensFileName) - suppressWarnings(fsa, grammar.internalParserClass, grammar.lexerClass) - normalizeLineDelimiters(fsa, grammar.lexerClass, grammar.internalParserClass) - if (removeBacktrackingGuards) { - removeBackTrackingGuards(fsa, grammar.internalParserClass, lookaheadThreshold) - } - } - - override protected void addIdeBindingsAndImports() { - super.addIdeBindingsAndImports - if (projectConfig.genericIde.manifest !== null) { - projectConfig.genericIde.manifest=>[ - requiredBundles += ADDITIONAL_CA_REQUIRED_BUNDLE - ] - } - } - - protected def generateUiContentAssistGrammar() { - generateContentAssistGrammar(projectConfig.eclipsePlugin.srcGen) - } - - def protected void addIdeUiBindingsAndImports() { - val extension naming = contentAssistNaming - if (projectConfig.eclipsePlugin.manifest !== null) { - projectConfig.eclipsePlugin.manifest=>[ - exportedPackages += #[ - grammar.lexerClass.packageName, - grammar.parserClass.packageName, - grammar.internalParserClass.packageName - ] - requiredBundles += #[ - "org.antlr.runtime;bundle-version=\"[3.2.0,3.2.1)\"", - ADDITIONAL_CA_REQUIRED_BUNDLE - ] - ] - } - val ideBindings = new GuiceModuleAccess.BindingFactory() - .addConfiguredBinding("ContentAssistLexer", ''' - binder.bind(«grammar.lexerSuperClass».class) - .annotatedWith(«Names».named(«"org.eclipse.xtext.ide.LexerIdeBindings".typeRef».CONTENT_ASSIST)) - .to(«grammar.lexerClass».class); - ''') - .addTypeToType('org.eclipse.xtext.ide.editor.contentassist.antlr.IContentAssistParser'.typeRef, grammar.parserClass) - .addConfiguredBinding("IProposalConflictHelper", ''' - binder.bind(org.eclipse.xtext.ide.editor.contentassist.IProposalConflictHelper.class) - .to(org.eclipse.xtext.ide.editor.contentassist.antlr.AntlrProposalConflictHelper.class); - ''') - if (partialParsing) { - ideBindings.addTypeToType( - "org.eclipse.xtext.ide.editor.contentassist.antlr.ContentAssistContextFactory".typeRef, - "org.eclipse.xtext.ide.editor.contentassist.antlr.PartialContentAssistContextFactory".typeRef - ) - } - if (hasSyntheticTerminalRule) { - ideBindings.addTypeToType( - "org.eclipse.xtext.ide.editor.contentassist.CompletionPrefixProvider".typeRef, - "org.eclipse.xtext.ide.editor.contentassist.IndentationAwareCompletionPrefixProvider".typeRef - ) - } - ideBindings.contributeTo(language.eclipsePluginGenModule) - } - - override JavaFileAccess generateContentAssistParser() { - val extension naming = contentAssistNaming - val file = fileFactory.createGeneratedJavaFile(grammar.parserClass) - file.content = ''' - public class «grammar.parserClass.simpleName» extends «grammar.getParserSuperClass(partialParsing)» { - - «grammar.initNameMappings()» - - @«Inject» - private «grammar.grammarAccess» grammarAccess; - - @«Inject» - private «ISemanticPredicates.typeRef» predicates; - - /** - * Creates compilation context. - * - * @param Input - * Stream - * @return Compilation context - */ - protected «ParserContext.typeRef» createParserContext() { - return new ParserContext(); - } - - @Override - protected «grammar.internalParserClass» createParser() { - «grammar.internalParserClass» result = new «grammar.internalParserClass»(null); - result.setGrammarAccess(grammarAccess); - result.setParserContext(createParserContext()); - result.setPredicates((«grammar.semanticPredicatesFullName.typeRef»)predicates); - return result; - } - - «IF hasSyntheticTerminalRule» - @Override - protected «TokenSource» createLexer(«CharStream» stream) { - return new «grammar.tokenSourceClass»(super.createLexer(stream)); - } - - «ENDIF» - @Override - protected String getRuleName(«AbstractElement» element) { - return nameMappings.getRuleName(element); - } - - @Override - protected String[] getInitialHiddenTokens() { - return new String[] { «FOR hidden : grammar.initialHiddenTokens SEPARATOR ", "»"«hidden»"«ENDFOR» }; - } - - public «grammar.grammarAccess» getGrammarAccess() { - return this.grammarAccess; - } - - public void setGrammarAccess(«grammar.grammarAccess» grammarAccess) { - this.grammarAccess = grammarAccess; - } - - public NameMappings getNameMappings() { - return nameMappings; - } - - public void setNameMappings(NameMappings nameMappings) { - this.nameMappings = nameMappings; - } - } - ''' - return file - } - - protected override generateProductionGrammar() { - val extension naming = productionNaming - val fsa = projectConfig.runtime.srcGen - productionGenerator.lexerSuperClassName = lexerSuperClassName; - productionGenerator.generate(grammar, options, fsa) - - runAntlr(grammar.parserGrammar, grammar.lexerGrammar, fsa) - - simplifyUnorderedGroupPredicatesIfRequired(grammar, fsa, grammar.internalParserClass) - splitParserAndLexerIfEnabled(fsa, grammar.internalParserClass, grammar.lexerClass) - normalizeTokens(fsa, grammar.lexerGrammar.tokensFileName) - suppressWarnings(fsa, grammar.internalParserClass, grammar.lexerClass) - normalizeLineDelimiters(fsa, grammar.internalParserClass, grammar.lexerClass) - - /* filter and ruleNames for flattened grammar */ - val RuleFilter filter = new RuleFilter(); - filter.discardUnreachableRules = true - filter.discardTerminalRules = false - val RuleNames ruleNames = RuleNames.getRuleNames(grammar, true); - - val keywordAnalysisHelper = new KeywordAnalysisHelper(productionGenerator.grammarNaming.getParserGrammar(grammar).grammarFileName, new FlattenedGrammarAccess(ruleNames, filter).getFlattenedGrammar(), identifierRules, reservedWords, keywords, grammarUtil) - keywordAnalysisHelper.printReport(fsa.path); - keywordAnalysisHelper.printViolations(fsa.path); - } - - def JavaFileAccess generateAbstractSemanticPredicate() { - val file = fileFactory.createGeneratedJavaFile(TypeReference.typeRef(grammar.getSemanticPredicatesFullName)) - file.importType(TypeReference.typeRef(grammar.semanticPredicatesFullName)) - file.importType(TypeReference.typeRef(ISemanticPredicates.AbstractSemanticPredicates)) - if(!getGrammar().predicates.isEmpty){ - file.importType(TypeReference.typeRef(ParserContext)) - file.importType(TypeReference.typeRef(Token)) - for (inheritedGrammar : annotations.allInheritedGrammars(getGrammar())) { - file.importType(TypeReference.typeRef(GrammarUtil.getNamespace(inheritedGrammar) + ".grammar." + GrammarUtil.getSimpleName(inheritedGrammar) + CLASS_SUFFIX)) - } - } - file.importType(TypeReference.typeRef(Singleton)) - file.content = ''' - /** - * Provides semantic predicates as specified in the grammar. Language may need to override - * this class in order to provide concrete implementations for predicates. - */ - @Singleton - public class «grammar.getSemanticPredicatesSimpleName()» extends AbstractSemanticPredicates { - «FOR element : getGrammar().predicates» - - /** - * Predicate for grammar rule «element.name». - * - * @param input - * Input from Lexer - * @return {@code true} if the grammar rule is enabled, {@code false} otherwise - */ - public boolean «element.name»(ParserContext parserContext) { - «element.getRulePredicateCondition» - } - «ENDFOR» - - «FOR element : getGrammar().predicates» - - /** - * Message for «element.name» predicate. - * - * @param input - * Input from Lexer - * @return {@code true} if the grammar rule is enabled, {@code false} otherwise - */ - public String «element.message»(Token token) { - «element.getRulePredicateMessage» - } - «ENDFOR» - } - ''' - file - } - - /** - * Returns name for the predicate message method. - * - * @param predicate - * Semantic predicate, must not be {@code null} - * @return Content for the predicate message method - */ - def String getRulePredicateMessage(SemanticPredicate predicate){ - if(predicate.keywords!==null) - ''' - return "Unexpected: " + token.getText() + ". Expected: '«Joiner.on("', '").join(predicate.keywords)»'"; - ''' - else - ''' - /* Default message. Intended to be overridden. */ - return "Condition «predicate.name» is not fullfilled "; - ''' - } - - /** - * Returns predicate condition (default condition). - * - * @param predicate - * Semantic predicate, must not be {@code null} - * @return A string containing the condition for the semantic predicate or {@code null} - */ - def String getRulePredicateCondition(SemanticPredicate predicate) { - if (predicate.keywords !== null) { - val condition = predicate.keywords.stream(). - map([s | '''"«s»".equalsIgnoreCase(text)''']).collect(Collectors.joining(" || ")) - ''' - String text = parserContext.getInput().LT(1).getText(); - return «condition»; - ''' - } else { - return "return " + predicate.grammar + CLASS_SUFFIX + "." + predicate.name + "(parserContext);\n" - } - } - - override JavaFileAccess generateProductionParser() { - val extension naming = productionNaming - val file = fileFactory.createGeneratedJavaFile(grammar.parserClass) - file.importType(TypeReference.typeRef(grammar.semanticPredicatesFullName)) - file.importType(TypeReference.typeRef(ISemanticPredicates)) - - file.content = ''' - public class «grammar.parserClass.simpleName» extends «AbstractContextualAntlrParser» { - - @«Inject» - private «grammar.grammarAccess» grammarAccess; - - @«Inject» - private ISemanticPredicates predicates; - - @Override - protected void setInitialHiddenTokens(«XtextTokenStream» tokenStream) { - tokenStream.setInitialHiddenTokens(«FOR hidden : grammar.initialHiddenTokens SEPARATOR ", "»"«hidden»"«ENDFOR»); - } - - «IF hasSyntheticTerminalRule» - @Override - protected «TokenSource» createLexer(«CharStream» stream) { - return new «grammar.tokenSourceClass»(super.createLexer(stream)); - } - - /** - * Indentation aware languages do not support partial parsing since the lexer is inherently stateful. - * Override and return {@code true} if your terminal splitting is stateless. - */ - @Override - protected boolean isReparseSupported() { - return false; - } - «ENDIF» - - @Override - protected «grammar.internalParserClass» createParser(«XtextTokenStream» stream) { - return new «grammar.internalParserClass»(stream, getGrammarAccess(), createParserContext(), («grammar.semanticPredicatesSimpleName») predicates); - } - - @Override - protected String getDefaultRuleName() { - return "«grammar.allParserRules.head.originalElement.name»"; - } - - public «grammar.grammarAccess» getGrammarAccess() { - return this.grammarAccess; - } - - public void setGrammarAccess(«grammar.grammarAccess» grammarAccess) { - this.grammarAccess = grammarAccess; - } - } - ''' - file - } - - override protected addUiBindingsAndImports() { - /* Overridden to remove the binding for IContentAssistParser, which we bind at a different place. - * If we would register it here, we would have conflicting bindings for IContentAssistParser. - */ - val extension naming = contentAssistNaming - val caLexerClass = grammar.lexerClass - - if (projectConfig.genericIde.manifest !== null) { - projectConfig.genericIde.manifest=>[ - exportedPackages += #[ - caLexerClass.packageName, - grammar.parserClass.packageName, - grammar.internalParserClass.packageName - ] - ] - } - val uiBindings = new GuiceModuleAccess.BindingFactory() - .addTypeToType( - "org.eclipse.xtext.ui.editor.contentassist.IProposalConflictHelper".typeRef, - "org.eclipse.xtext.ui.editor.contentassist.antlr.AntlrProposalConflictHelper".typeRef - ) - .addConfiguredBinding("ContentAssistLexer", ''' - binder.bind(«grammar.lexerSuperClass».class) - .annotatedWith(«Names».named(«"org.eclipse.xtext.ide.LexerIdeBindings".typeRef».CONTENT_ASSIST)) - .to(«caLexerClass».class); - ''') - // registration of the 'ContentAssistLexer' is put in front of the 'HighlightingLexer' - // in order to let 'caLexerClass' get added to the imports, since it is referenced - // several times and the lexer classes' simple names are usually identical - .addConfiguredBinding("HighlightingLexer", ''' - binder.bind(«Lexer».class) - .annotatedWith(«Names».named(«"org.eclipse.xtext.ide.LexerIdeBindings".typeRef».HIGHLIGHTING)) - .to(«productionNaming.getLexerClass(grammar)».class); - ''') - .addConfiguredBinding("HighlightingTokenDefProvider", ''' - binder.bind(«ITokenDefProvider».class) - .annotatedWith(«Names».named(«"org.eclipse.xtext.ide.LexerIdeBindings".typeRef».HIGHLIGHTING)) - .to(«AntlrTokenDefProvider».class); - ''') - .addTypeToType( - new TypeReference("org.eclipse.xtext.ui.editor.contentassist", "ContentAssistContext.Factory"), - "org.eclipse.xtext.ui.editor.contentassist.antlr.DelegatingContentAssistContextFactory".typeRef - ) - .addConfiguredBinding("ContentAssistLexerProvider", ''' - binder.bind(«caLexerClass».class).toProvider(«LexerProvider».create(«caLexerClass».class)); - ''') - - if (projectConfig.genericIde.srcGen !== null) { - uiBindings.addTypeToType("org.eclipse.xtext.ide.editor.contentassist.antlr.IContentAssistParser".typeRef, grammar.parserClass) - } - - if (hasSyntheticTerminalRule) { - uiBindings.addTypeToType( - "org.eclipse.xtext.ide.editor.contentassist.CompletionPrefixProvider".typeRef, - "org.eclipse.xtext.ide.editor.contentassist.IndentationAwareCompletionPrefixProvider".typeRef - ) - } - uiBindings.contributeTo(language.eclipsePluginGenModule) - } -} \ No newline at end of file diff --git a/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/common/GrammarRuleAnnotations.java b/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/common/GrammarRuleAnnotations.java new file mode 100644 index 0000000000..f6bf5982d8 --- /dev/null +++ b/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/common/GrammarRuleAnnotations.java @@ -0,0 +1,741 @@ +/******************************************************************************* + * Copyright (c) 2016 Avaloq Group AG and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Avaloq Group AG - initial API and implementation + *******************************************************************************/ +package com.avaloq.tools.ddk.xtext.generator.parser.common; + +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import org.eclipse.emf.common.notify.Adapter; +import org.eclipse.emf.common.notify.Notifier; +import org.eclipse.emf.common.notify.impl.AdapterImpl; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.xtext.AbstractElement; +import org.eclipse.xtext.AbstractRule; +import org.eclipse.xtext.Action; +import org.eclipse.xtext.Alternatives; +import org.eclipse.xtext.Assignment; +import org.eclipse.xtext.CrossReference; +import org.eclipse.xtext.Grammar; +import org.eclipse.xtext.GrammarUtil; +import org.eclipse.xtext.Group; +import org.eclipse.xtext.RuleCall; +import org.eclipse.xtext.nodemodel.ICompositeNode; +import org.eclipse.xtext.nodemodel.util.NodeModelUtils; +import org.eclipse.xtext.util.internal.EmfAdaptable; +import org.eclipse.xtext.xbase.lib.util.ToStringBuilder; +import org.eclipse.xtext.xtext.RuleWithParameterValues; +import org.eclipse.xtext.xtext.generator.grammarAccess.GrammarAccessExtensions; + +import com.google.common.base.Splitter; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.google.inject.Inject; + +@SuppressWarnings("nls") +public class GrammarRuleAnnotations { + + /** + * Pattern to deactivate backtracking for single rule. + */ + private static final Pattern NO_BACKTRACK_ANNOTATION_PATTERN = Pattern.compile("@NoBacktrack", Pattern.MULTILINE); + + /** + * Pattern to search for keyword rule annotations and extracting list of comma-separated keywords. + */ + private static final Pattern KEYWORD_RULE_ANNOTATION_PATTERN = Pattern.compile("@KeywordRule\\(([\\w\\s,]+)\\)", + Pattern.MULTILINE); + + /** + * Pattern to search for semantic predicate rule annotation that enables the given rule. + */ + private static final Pattern SEMANTIC_PREDICATE_PATTERN = Pattern.compile("@SemanticPredicate\\(([\\s]*[\\w]+)[\\s]*\\)", + Pattern.MULTILINE); + + /** + * Splits comma separated list of keywords. + */ + private static final Splitter KEYWORDS_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings(); + + @Inject + private GrammarAccessExtensions grammarExtensions; + + @EmfAdaptable + public static class NoBacktrack { + public static class NoBacktrackAdapter extends AdapterImpl { + private final GrammarRuleAnnotations.NoBacktrack element; + + public NoBacktrackAdapter(final GrammarRuleAnnotations.NoBacktrack element) { + this.element = element; + } + + public GrammarRuleAnnotations.NoBacktrack get() { + return this.element; + } + + @Override + public boolean isAdapterForType(final Object object) { + return object == GrammarRuleAnnotations.NoBacktrack.class; + } + } + + public static GrammarRuleAnnotations.NoBacktrack findInEmfObject(final Notifier emfObject) { + for (Adapter adapter : emfObject.eAdapters()) { + if (adapter instanceof GrammarRuleAnnotations.NoBacktrack.NoBacktrackAdapter) { + return ((GrammarRuleAnnotations.NoBacktrack.NoBacktrackAdapter) adapter).get(); + } + } + return null; + } + + public static GrammarRuleAnnotations.NoBacktrack removeFromEmfObject(final Notifier emfObject) { + List adapters = emfObject.eAdapters(); + final int max = adapters.size(); + for (int i = 0; i < max; i++) { + Adapter adapter = adapters.get(i); + if (adapter instanceof GrammarRuleAnnotations.NoBacktrack.NoBacktrackAdapter) { + emfObject.eAdapters().remove(i); + return ((GrammarRuleAnnotations.NoBacktrack.NoBacktrackAdapter) adapter).get(); + } + } + return null; + } + + public void attachToEmfObject(final Notifier emfObject) { + NoBacktrack result = findInEmfObject(emfObject); + if (result != null) { + throw new IllegalStateException("The given EMF object already contains an adapter for NoBacktrack"); + } + GrammarRuleAnnotations.NoBacktrack.NoBacktrackAdapter adapter = new GrammarRuleAnnotations.NoBacktrack.NoBacktrackAdapter(this); + emfObject.eAdapters().add(adapter); + } + + @Override + public int hashCode() { + return 1; + } + + @Override + public boolean equals(final Object obj) { + return this == obj || (obj != null && getClass() == obj.getClass()); + } + + @Override + public String toString() { + return new ToStringBuilder(this).toString(); + } + } + + /** + * Gated predicate: {condition}?=> + * Validating predicate: {condition message}? + */ + @EmfAdaptable + public static class SemanticPredicate { + public static class SemanticPredicateAdapter extends AdapterImpl { + private final GrammarRuleAnnotations.SemanticPredicate element; + + public SemanticPredicateAdapter(final GrammarRuleAnnotations.SemanticPredicate element) { + this.element = element; + } + + public GrammarRuleAnnotations.SemanticPredicate get() { + return this.element; + } + + @Override + public boolean isAdapterForType(final Object object) { + return object == GrammarRuleAnnotations.SemanticPredicate.class; + } + } + + private final String name; + + private final String message; + + private final String grammar; + + private final List keywords; + + public static GrammarRuleAnnotations.SemanticPredicate findInEmfObject(final Notifier emfObject) { + for (Adapter adapter : emfObject.eAdapters()) { + if (adapter instanceof GrammarRuleAnnotations.SemanticPredicate.SemanticPredicateAdapter) { + return ((GrammarRuleAnnotations.SemanticPredicate.SemanticPredicateAdapter) adapter).get(); + } + } + return null; + } + + public static GrammarRuleAnnotations.SemanticPredicate removeFromEmfObject(final Notifier emfObject) { + List adapters = emfObject.eAdapters(); + final int max = adapters.size(); + for (int i = 0; i < max; i++) { + Adapter adapter = adapters.get(i); + if (adapter instanceof GrammarRuleAnnotations.SemanticPredicate.SemanticPredicateAdapter) { + emfObject.eAdapters().remove(i); + return ((GrammarRuleAnnotations.SemanticPredicate.SemanticPredicateAdapter) adapter).get(); + } + } + return null; + } + + public void attachToEmfObject(final Notifier emfObject) { + SemanticPredicate result = findInEmfObject(emfObject); + if (result != null) { + throw new IllegalStateException("The given EMF object already contains an adapter for SemanticPredicate"); + } + GrammarRuleAnnotations.SemanticPredicate.SemanticPredicateAdapter adapter = new GrammarRuleAnnotations.SemanticPredicate.SemanticPredicateAdapter(this); + emfObject.eAdapters().add(adapter); + } + + public SemanticPredicate(final String name, final String message, final String grammar, final List keywords) { + this.name = name; + this.message = message; + this.grammar = grammar; + this.keywords = keywords; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((this.name == null) ? 0 : this.name.hashCode()); + result = prime * result + ((this.message == null) ? 0 : this.message.hashCode()); + result = prime * result + ((this.grammar == null) ? 0 : this.grammar.hashCode()); + return prime * result + ((this.keywords == null) ? 0 : this.keywords.hashCode()); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + GrammarRuleAnnotations.SemanticPredicate other = (GrammarRuleAnnotations.SemanticPredicate) obj; + if (this.name == null) { + if (other.name != null) { + return false; + } + } else if (!this.name.equals(other.name)) { + return false; + } + if (this.message == null) { + if (other.message != null) { + return false; + } + } else if (!this.message.equals(other.message)) { + return false; + } + if (this.grammar == null) { + if (other.grammar != null) { + return false; + } + } else if (!this.grammar.equals(other.grammar)) { + return false; + } + if (this.keywords == null) { + if (other.keywords != null) { + return false; + } + } else if (!this.keywords.equals(other.keywords)) { + return false; + } + return true; + } + + @Override + public String toString() { + ToStringBuilder b = new ToStringBuilder(this); + b.add("name", this.name); + b.add("message", this.message); + b.add("grammar", this.grammar); + b.add("keywords", this.keywords); + return b.toString(); + } + + public String getName() { + return this.name; + } + + public String getMessage() { + return this.message; + } + + public String getGrammar() { + return this.grammar; + } + + public List getKeywords() { + return this.keywords; + } + } + + @EmfAdaptable + public static class GrammarAnnotations { + public static class GrammarAnnotationsAdapter extends AdapterImpl { + private final GrammarRuleAnnotations.GrammarAnnotations element; + + public GrammarAnnotationsAdapter(final GrammarRuleAnnotations.GrammarAnnotations element) { + this.element = element; + } + + public GrammarRuleAnnotations.GrammarAnnotations get() { + return this.element; + } + + @Override + public boolean isAdapterForType(final Object object) { + return object == GrammarRuleAnnotations.GrammarAnnotations.class; + } + } + + private final List predicates; + + public static GrammarRuleAnnotations.GrammarAnnotations findInEmfObject(final Notifier emfObject) { + for (Adapter adapter : emfObject.eAdapters()) { + if (adapter instanceof GrammarRuleAnnotations.GrammarAnnotations.GrammarAnnotationsAdapter) { + return ((GrammarRuleAnnotations.GrammarAnnotations.GrammarAnnotationsAdapter) adapter).get(); + } + } + return null; + } + + public static GrammarRuleAnnotations.GrammarAnnotations removeFromEmfObject(final Notifier emfObject) { + List adapters = emfObject.eAdapters(); + final int max = adapters.size(); + for (int i = 0; i < max; i++) { + Adapter adapter = adapters.get(i); + if (adapter instanceof GrammarRuleAnnotations.GrammarAnnotations.GrammarAnnotationsAdapter) { + emfObject.eAdapters().remove(i); + return ((GrammarRuleAnnotations.GrammarAnnotations.GrammarAnnotationsAdapter) adapter).get(); + } + } + return null; + } + + public void attachToEmfObject(final Notifier emfObject) { + GrammarAnnotations result = findInEmfObject(emfObject); + if (result != null) { + throw new IllegalStateException("The given EMF object already contains an adapter for GrammarAnnotations"); + } + GrammarRuleAnnotations.GrammarAnnotations.GrammarAnnotationsAdapter adapter = new GrammarRuleAnnotations.GrammarAnnotations.GrammarAnnotationsAdapter(this); + emfObject.eAdapters().add(adapter); + } + + public GrammarAnnotations(final List predicates) { + this.predicates = predicates; + } + + @Override + public int hashCode() { + final int prime = 31; + return prime * 1 + ((this.predicates == null) ? 0 : this.predicates.hashCode()); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + GrammarRuleAnnotations.GrammarAnnotations other = (GrammarRuleAnnotations.GrammarAnnotations) obj; + if (this.predicates == null) { + if (other.predicates != null) { + return false; + } + } else if (!this.predicates.equals(other.predicates)) { + return false; + } + return true; + } + + @Override + public String toString() { + ToStringBuilder b = new ToStringBuilder(this); + b.add("predicates", this.predicates); + return b.toString(); + } + + public List getPredicates() { + return this.predicates; + } + } + + public boolean hasNoBacktrackAnnotation(final AbstractRule rule) { + return getNoBacktrackAnnotation(rule) != null; + } + + /** + * Checks if the given element is contained in a rule with a NoBacktrack annotation. + * + * @param element + * Grammar element + * @return {@code true} if there is an annotation on the enclosing rule, {@code false} otherwise + */ + public boolean hasNoBacktrackAnnotation(final AbstractElement element) { + return hasNoBacktrackAnnotation(GrammarUtil.containingRule(element)); + } + + public boolean hasSemanticPredicate(final AbstractElement element) { + return findPredicate(element) != null; + } + + public boolean hasValidatingPredicate(final AbstractRule rule) { + return getSemanticPredicateAnnotation(rule) != null; + } + + /** + * Returns disambiguating/validating semantic predicate. + * + * @param rule + * Xtext grammar element + * @return A string containing the semantic predicate or an empty string + */ + public String generateValidatingPredicate(final AbstractRule rule) { + final SemanticPredicate predicate = getSemanticPredicateAnnotation(rule); + if (predicate != null) { + return generateValidatingPredicate(predicate); + } + return ""; + } + + public boolean isGatedPredicateRequired(final AbstractElement element) { + return isStartingWithPredicatedRule(element) && isAlternativeAvailable(element); + } + + /** + * Returns gated semantic predicate. + * + * @param element + * Xtext grammar element + * @return A string containing the semantic predicate or an empty string + */ + public String generateGatedPredicate(final AbstractElement element) { + final SemanticPredicate predicate = findPredicate(element); + if (predicate != null) { + return generateGatedPredicate(predicate); + } + return ""; + } + + public GrammarAnnotations annotateGrammar(final Grammar grammar) { + GrammarAnnotations annotations = GrammarAnnotations.findInEmfObject(grammar); + if (annotations == null) { + annotations = new GrammarAnnotations(grammar.getRules().stream().map(r -> annotateRule(r)).filter(a -> a != null).collect(Collectors.toList())); + annotations.attachToEmfObject(grammar); + + Set inheritedGrammars = allInheritedGrammars(grammar); + for (final Grammar inheritedGrammar : inheritedGrammars) { + GrammarAnnotations inheritedAnnotations = GrammarAnnotations.findInEmfObject(inheritedGrammar); + if (inheritedAnnotations == null) { + inheritedAnnotations = new GrammarAnnotations(inheritedGrammar.getRules().stream().map(r -> annotateRule(r)).filter(a -> a != null).collect(Collectors.toList())); + inheritedAnnotations.attachToEmfObject(inheritedGrammar); + } + annotations.predicates.addAll(inheritedAnnotations.predicates); + } + } + + return annotations; + } + + public Set allInheritedGrammars(final Grammar grammar) { + Set result = Sets.newHashSet(); + for (final AbstractRule rule : GrammarUtil.allParserRules(grammar)) { + Grammar ruleGrammar = GrammarUtil.getGrammar(rule); + if (!ruleGrammar.equals(grammar) && isSemanticPredicate(rule)) { + result.add(ruleGrammar); + } + } + return result; + } + + public List predicates(final Grammar grammar) { + return annotateGrammar(grammar).predicates; + } + + /** + * Checks whether this abstract element leads directly to a keyword rule. + * + * @param element + * Element call + * @return {@code true} if the rule leads to a keyword rule + */ + public boolean isStartingWithPredicatedRule(final AbstractElement element) { + if (element instanceof CrossReference) { + return findPredicate(((CrossReference) element).getTerminal()) != null; + } + return findPredicate(element) != null; + } + + /** + * Finds the predicated rule the given element leads to. + * + * @param element + * Current element + * @return Keyword rule or {@code null} if the given element does not lead to a keyword rule + */ + public SemanticPredicate findPredicate(final AbstractElement element) { + if (element instanceof RuleCall) { + final SemanticPredicate predicate = getSemanticPredicateAnnotation(((RuleCall) element).getRule()); + if (predicate != null) { + return predicate; + } + return findPredicate(((RuleCall) element).getRule().getAlternatives()); + } + if (GrammarUtil.isOptionalCardinality(element)) { + return null; + } + if (element instanceof CrossReference) { + return findPredicate(((CrossReference) element).getTerminal()); + } + if (element instanceof Group) { + final AbstractElement firstNonActionElement = getFirstNonActionElement((Group) element); + if (firstNonActionElement != null) { + return findPredicate(firstNonActionElement); + } + } + if (element instanceof Assignment) { + return findPredicate(((Assignment) element).getTerminal()); + } + return null; + } + + public boolean isRuleWithPredicate(final AbstractRule rule) { + return getSemanticPredicateAnnotation(rule) != null; + } + + public AbstractElement getFirstNonActionElement(final Group group) { + for (final AbstractElement groupElement : group.getElements()) { + if (!(groupElement instanceof Action)) { + return groupElement; + } + } + return null; + } + + /** + * Checks whether there is a viable alternative (i.e. not potentially gated by another gated semantic predicate) for the current element. + * If there is no alternative we should not insert gated semantic predicates, but validating semantic predicates, so we get a better error recovery and + * a correct message. We should not also propagate validating predicates in the callers. If we do so this will damage error recovery in the callers. + * + * @param element + * Grammar element + * @return {@code true} if there is an alternative available and the gated semantic predicate can be inserted + */ + public boolean isAlternativeAvailable(final AbstractElement element) { + if (GrammarUtil.isOptionalCardinality(element)) { + return true; + } + final EObject container = element.eContainer(); + if (container instanceof Assignment) { + return isAlternativeAvailable((AbstractElement) container); + } else if (container instanceof CrossReference) { + return isAlternativeAvailable((AbstractElement) container); + } else if (container instanceof Group) { + if (isFirstNonActionElement((Group) container, element)) { + return isAlternativeAvailable((AbstractElement) container); + } + } else if (container instanceof Alternatives) { + for (final AbstractElement alternative : ((Alternatives) container).getElements()) { + if (!Objects.equals(alternative, element) /* && !isGated(alternative) */) { + return true; + } + } + } + return false; + } + + /** + * Checks if the given grammar element is the first non action element in the given group. + * + * @param group + * where the position is checked, must not be {@code null} + * @param element + * to check, must not be {@code null} + * @return {@code true}, if is first non action element is the given element, {@code false} otherwise + */ + public boolean isFirstNonActionElement(final Group group, final AbstractElement element) { + return Objects.equals(element, getFirstNonActionElement(group)); + } + + public String generateGatedPredicate(final SemanticPredicate predicate) { + return "{predicates." + predicate.getName() + "(parserContext)}?=>"; + } + + public String generateValidatingPredicate(final SemanticPredicate predicate) { + return "{predicates." + predicate.getName() + "(parserContext) /* @ErrorMessage(" + predicate.getMessage() + ") */}?"; + } + + /** + * Translate annotations in comments into predicate annotations. + * + *

+ * Gated vs. Disambiguating predicates On the first look a disambiguating semantic predicate + * would be a right choice. However inserting them would be much more difficult. The reason is that we need + * not only insert them in the beginning of the current rule, but also propagate before actions into alternatives + * that call this rule. We have to do the same for gated predicated, however there is one important difference. + * If we have an alternative like {@code rule: (Keyword1 | Keyword2) Keyword3 ;} then we need to insert the predicate + * only before the first alternative (Keyword1). If we insert disambiguating semantic predicate before both + * the exception the predicate may throw can destroy recovery for the entire rule i.e. will not try going to Keyword3. + * We, however, need validating predicate in the beginning of each rule Keyword2, Keyword3. This requires an + * extra analysis which might be a next step. Gated predicates we can safely insert before Keyword1 and Keyword2. + * This will have no negative impact. We still need validating predicates in Keyword rules themselves. + *

+ * + * @param rule + * the grammar rule to annotate + * @return the attached {@link SemanticPredicate}, or {@code null} if the rule has no predicate annotation + * @throws IllegalArgumentException + * if a keyword annotation is combined with a semantic predicate annotation on the same rule + */ + public SemanticPredicate annotateRule(final AbstractRule rule) { + final String text = getText(rule); + SemanticPredicate predicate = null; + if (text != null) { + final String ruleGrammarName = GrammarUtil.getSimpleName(GrammarUtil.getGrammar(rule)); + predicate = getKeywordRulePredicate(text, rule.getName(), ruleGrammarName); + if (predicate != null) { + predicate.attachToEmfObject(rule); + } + final SemanticPredicate semanticPredicate = getSemanticPredicate(text, ruleGrammarName); + if (semanticPredicate != null) { + if (predicate != null) { + throw new IllegalArgumentException("You may not combine keyword annotations with semantic predicate annotations on one rule: " + rule.getName()); + } + semanticPredicate.attachToEmfObject(rule); + predicate = semanticPredicate; + } + final NoBacktrack noBacktrack = getNoBacktrack(text); + if (noBacktrack != null) { + noBacktrack.attachToEmfObject(rule); + } + } + return predicate; + } + + public SemanticPredicate getSemanticPredicateAnnotation(final AbstractRule rule) { + if (rule != null) { + RuleWithParameterValues ruleWithParameterValues = RuleWithParameterValues.findInEmfObject(rule); + AbstractRule original = ruleWithParameterValues != null ? ruleWithParameterValues.getOriginal() : null; + if (original == null) { + original = rule; + } + return SemanticPredicate.findInEmfObject(original); + } + return null; + } + + public NoBacktrack getNoBacktrackAnnotation(final AbstractRule rule) { + if (rule != null) { + AbstractRule original = RuleWithParameterValues.findInEmfObject(rule).getOriginal(); + if (original == null) { + original = rule; + } + return NoBacktrack.findInEmfObject(original); + } + return null; + } + + /** + * Checks if the given rule contains {@code @KeywordRule(kw1,kw2)} annotation. + * + * @param text + * the rule text to inspect + * @param ruleName + * the name of the rule + * @param grammar + * the simple name of the grammar containing the rule + * @return the corresponding {@link SemanticPredicate}, or {@code null} if no keyword rule annotation is present + */ + public SemanticPredicate getKeywordRulePredicate(final String text, final String ruleName, final String grammar) { + final Matcher matcher = KEYWORD_RULE_ANNOTATION_PATTERN.matcher(text); + if (matcher.find()) { + return new SemanticPredicate( + "is" + ruleName + "Enabled", + "get" + ruleName + "EnabledMessage", + grammar, + Lists.newArrayList(KEYWORDS_SPLITTER.split(matcher.group(1)))); + } + return null; + } + + /** + * Checks if the given rule contains {@code @SemanticPredicate(condition)} annotation. + * + * @param text + * the rule text to inspect + * @param grammar + * the simple name of the grammar containing the rule + * @return the corresponding {@link SemanticPredicate}, or {@code null} if no semantic predicate annotation is present + */ + public SemanticPredicate getSemanticPredicate(final String text, final String grammar) { + final Matcher matcher = SEMANTIC_PREDICATE_PATTERN.matcher(text); + if (matcher.find()) { + return new SemanticPredicate( + matcher.group(1), + "get" + matcher.group(1) + "Message", + grammar, + null); + } + return null; + } + + /** + * Checks if the given rule contains a NoBacktrack annotation. + * + * @param text + * the rule text to inspect + * @return the corresponding {@link NoBacktrack}, or {@code null} if no NoBacktrack annotation is present + */ + public NoBacktrack getNoBacktrack(final String text) { + final Matcher matcher = NO_BACKTRACK_ANNOTATION_PATTERN.matcher(text); + if (matcher.find()) { + return new NoBacktrack(); + } + return null; + } + + public String getText(final AbstractRule rule) { + final ICompositeNode node = NodeModelUtils.getNode(rule); + return node != null ? node.getText() : grammarExtensions.grammarFragmentToString(rule, ""); + } + + /** + * Checks if the given rule contains a keyword rule annotation. + * + * @param rule + * Grammar rule + * @return {@code true} if there is an annotation, {@code false} otherwise + */ + public boolean isSemanticPredicate(final AbstractRule rule) { + String text = getText(rule); + if (text != null) { + final Matcher matcher = SEMANTIC_PREDICATE_PATTERN.matcher(text); + if (matcher.find()) { + return true; + } + } + return false; + } +} diff --git a/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/common/GrammarRuleAnnotations.xtend b/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/common/GrammarRuleAnnotations.xtend deleted file mode 100644 index 0035dd4c2d..0000000000 --- a/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/common/GrammarRuleAnnotations.xtend +++ /dev/null @@ -1,406 +0,0 @@ -/** - * Copyright (c) 2016 Avaloq Group AG and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * Contributors: - * Avaloq Group AG - initial API and implementation - */ - -package com.avaloq.tools.ddk.xtext.generator.parser.common - -import com.google.common.base.Splitter -import com.google.common.collect.Lists -import java.util.List -import java.util.regex.Pattern -import org.eclipse.xtend.lib.annotations.Data -import org.eclipse.xtext.AbstractElement -import org.eclipse.xtext.AbstractRule -import org.eclipse.xtext.Action -import org.eclipse.xtext.Alternatives -import org.eclipse.xtext.Assignment -import org.eclipse.xtext.CrossReference -import org.eclipse.xtext.Grammar -import org.eclipse.xtext.GrammarUtil -import org.eclipse.xtext.Group -import org.eclipse.xtext.RuleCall -import org.eclipse.xtext.nodemodel.util.NodeModelUtils -import org.eclipse.xtext.util.internal.EmfAdaptable -import org.eclipse.xtext.xtext.RuleWithParameterValues -import java.util.stream.Collectors -import java.util.Set -import com.google.common.collect.Sets -import org.eclipse.xtext.xtext.generator.grammarAccess.GrammarAccessExtensions -import com.google.inject.Inject - -class GrammarRuleAnnotations { - - /** - * Pattern to deactivate backtracking for single rule. - */ - static final Pattern NO_BACKTRACK_ANNOTATION_PATTERN = Pattern.compile("@NoBacktrack", Pattern.MULTILINE); // $NON-NLS-1$ - /** - * Pattern to search for keyword rule annotations and extracting list of comma-separated keywords. - */ - static final Pattern KEYWORD_RULE_ANNOTATION_PATTERN = Pattern.compile("@KeywordRule\\(([\\w\\s,]+)\\)", - Pattern.MULTILINE); // $NON-NLS-1$ - /** - * Pattern to search for semantic predicate rule annotation that enables the given rule. - */ - static final Pattern SEMANTIC_PREDICATE_PATTERN = Pattern.compile("@SemanticPredicate\\(([\\s]*[\\w]+)[\\s]*\\)", - Pattern.MULTILINE); // $NON-NLS-1$ - /** - * Splits comma separated list of keywords. - */ - static final Splitter KEYWORDS_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings(); - - @Inject - GrammarAccessExtensions grammarExtensions; - - @EmfAdaptable - @Data - static class NoBacktrack { - } - -/** - * Gated predicate: {condition}?=> - * Validating predicate: {condition message}? - */ - @EmfAdaptable - @Data - static class SemanticPredicate { - val String name - val String message - val String grammar - val List keywords - } - - @EmfAdaptable - @Data - static class GrammarAnnotations { - val List predicates - } - - def boolean hasNoBacktrackAnnotation(AbstractRule rule){ - return getNoBacktrackAnnotation(rule) !== null - } - - /** - * Checks if the given element is contained in a rule with a NoBacktrack annotation. - * - * @param element - * Grammar element - * @return {@code true} if there is an annotation on the enclosing rule, {@code false} otherwise - */ - def boolean hasNoBacktrackAnnotation(AbstractElement element){ - return hasNoBacktrackAnnotation(GrammarUtil.containingRule(element)) - } - - def boolean hasSemanticPredicate(AbstractElement element){ - return findPredicate(element) !== null - } - - def boolean hasValidatingPredicate(AbstractRule rule){ - return getSemanticPredicateAnnotation(rule) !== null - } - - /** - * Returns disambiguating/validating semantic predicate. - * - * @param element - * Xtext grammar element - * @return A string containing the semantic predicate or an empty string - */ - def String generateValidatingPredicate(AbstractRule rule){ - val predicate = getSemanticPredicateAnnotation(rule) - if(predicate !== null) return generateValidatingPredicate(predicate) - return ""; - } - - def boolean isGatedPredicateRequired(AbstractElement element){ - return isStartingWithPredicatedRule(element) && isAlternativeAvailable(element); - } - - /** - * Returns gated semantic predicate. - * - * @param element - * Xtext grammar element - * @return A string containing the semantic predicate or an empty string - */ - def String generateGatedPredicate(AbstractElement element){ - val predicate = findPredicate(element) - if(predicate !== null) return generateGatedPredicate(predicate) - return ""; - } - - def GrammarAnnotations annotateGrammar(Grammar grammar){ - var annotations = GrammarAnnotations.findInEmfObject(grammar) - if (annotations === null) { - annotations = new GrammarAnnotations(grammar.rules.stream().map[r | annotateRule(r)].filter([a | a !== null]).collect(Collectors.toList)) - annotations.attachToEmfObject(grammar) - - var inheritedGrammars = allInheritedGrammars(grammar) - for (inheritedGrammar : inheritedGrammars) { - var inheritedAnnotations = GrammarAnnotations.findInEmfObject(inheritedGrammar) - if (inheritedAnnotations === null) { - inheritedAnnotations = new GrammarAnnotations(inheritedGrammar.rules.stream().map[r | annotateRule(r)].filter([a | a !== null]).collect(Collectors.toList)) - inheritedAnnotations.attachToEmfObject(inheritedGrammar) - } - annotations.predicates.addAll(inheritedAnnotations.predicates) - } - } - - return annotations - } - - def Set allInheritedGrammars(Grammar grammar) { - var result = Sets.newHashSet(); - for (AbstractRule rule : GrammarUtil.allParserRules(grammar)) { - var ruleGrammar = GrammarUtil.getGrammar(rule); - if (!ruleGrammar.equals(grammar) && this.isSemanticPredicate(rule)) { - result.addAll(ruleGrammar) - } - } - return result.toSet; - } - - def List predicates(Grammar grammar){ - return grammar.annotateGrammar.predicates - } - - /** - * Checks whether this abstract element leads directly to a keyword rule. - * - * @param element - * Element call - * @return {@code true} if the rule leads to a keyword rule - */ - def boolean isStartingWithPredicatedRule(AbstractElement element) { - if (element instanceof CrossReference) { - return findPredicate(element.getTerminal()) !== null; - } - return findPredicate(element) !== null; - } - - /** - * Finds the predicated rule the given element leads to. - * - * @param element - * Current element - * @return Keyword rule or {@code null} if the given element does not lead to a keyword rule - */ - def SemanticPredicate findPredicate(AbstractElement element) { - if (element instanceof RuleCall) { - val predicate = getSemanticPredicateAnnotation(element.getRule()) - if (predicate !== null) { - return predicate; - } - return findPredicate(element.getRule().getAlternatives()); - } - if (GrammarUtil.isOptionalCardinality(element)) { - return null; - } - if (element instanceof CrossReference) { - return findPredicate(element.getTerminal()); - } - if (element instanceof Group) { - val firstNonActionElement = getFirstNonActionElement(element); - if (firstNonActionElement !== null) { - return findPredicate(firstNonActionElement); - } - } - if (element instanceof Assignment) { - return findPredicate((element).getTerminal()); - } - return null; - } - - def boolean isRuleWithPredicate(AbstractRule rule){ - return getSemanticPredicateAnnotation(rule) !== null - } - - def AbstractElement getFirstNonActionElement(Group group){ - for (AbstractElement groupElement : group.getElements()) { - if (!(groupElement instanceof Action)) { - return groupElement; - } - } - return null; - } - - /** - * Checks whether there is a viable alternative (i.e. not potentially gated by another gated semantic predicate) for the current element. - * If there is no alternative we should not insert gated semantic predicates, but validating semantic predicates, so we get a better error recovery and - * a correct message. We should not also propagate validating predicates in the callers. If we do so this will damage error recovery in the callers. - * - * @param element - * Grammar element - * @return {@code true} if there is an alternative available and the gated semantic predicate can be inserted - */ - def boolean isAlternativeAvailable(AbstractElement element) { - if (GrammarUtil.isOptionalCardinality(element)) { - return true; - } - val container = element.eContainer(); - if (container instanceof Assignment) { - return isAlternativeAvailable(container); - } else if (container instanceof CrossReference) { - return isAlternativeAvailable(container); - } else if (container instanceof Group) { - if (isFirstNonActionElement(container, element)) { - return isAlternativeAvailable(container); - } - } else if (container instanceof Alternatives) { - for (AbstractElement alternative : container.getElements()) { - if (alternative != element /*&& !isGated(alternative)*/) { - return true; - } - } - } - return false; - } - - /** - * Checks if the given grammar element is the first non action element in the given group. - * - * @param group - * where the position is checked, must not be {@code null} - * @param element - * to check, must not be {@code null} - * @return {@code true}, if is first non action element is the given element, {@code false} otherwise - */ - def boolean isFirstNonActionElement(Group group, AbstractElement element) { - return element == getFirstNonActionElement(group); - } - - - def String generateGatedPredicate(SemanticPredicate predicate) - '''{predicates.«predicate.name»(parserContext)}?=>''' - - def String generateValidatingPredicate(SemanticPredicate predicate) - '''{predicates.«predicate.name»(parserContext) /* @ErrorMessage(«predicate.message») */}?''' - - - /** - * Translate annotations in comments into predicate annotations. - * - *

- * Gated vs. Disambiguating predicates On the first look a disambiguating semantic predicate - * would be a right choice. However inserting them would be much more difficult. The reason is that we need - * not only insert them in the beginning of the current rule, but also propagate before actions into alternatives - * that call this rule. We have to do the same for gated predicated, however there is one important difference. - * If we have an alternative like {@code rule: (Keyword1 | Keyword2) Keyword3 ;} then we need to insert the predicate - * only before the first alternative (Keyword1). If we insert disambiguating semantic predicate before both - * the exception the predicate may throw can destroy recovery for the entire rule i.e. will not try going to Keyword3. - * We, however, need validating predicate in the beginning of each rule Keyword2, Keyword3. This requires an - * extra analysis which might be a next step. Gated predicates we can safely insert before Keyword1 and Keyword2. - * This will have no negative impact. We still need validating predicates in Keyword rules themselves. - *

- */ - def SemanticPredicate annotateRule(AbstractRule rule) { - val text = getText(rule) - var SemanticPredicate predicate = null - if (text !== null) { - val ruleGrammarName = GrammarUtil.getSimpleName(GrammarUtil.getGrammar(rule)) - predicate = getKeywordRulePredicate(text, rule.getName(), ruleGrammarName) - if(predicate !== null){ - predicate.attachToEmfObject(rule) - } - val semanticPredicate = getSemanticPredicate(text, ruleGrammarName) - if(semanticPredicate !== null){ - if(predicate !== null) - throw new IllegalArgumentException("You may not combine keyword annotations with semantic predicate annotations on one rule: " + rule.name) - semanticPredicate.attachToEmfObject(rule) - predicate = semanticPredicate - } - val noBacktrack = getNoBacktrack(text) - noBacktrack?.attachToEmfObject(rule) - } - return predicate - } - - def SemanticPredicate getSemanticPredicateAnnotation(AbstractRule rule) { - if (rule !== null) { - var original = RuleWithParameterValues.findInEmfObject(rule)?.getOriginal() - if (original === null) { - original = rule - } - return SemanticPredicate.findInEmfObject(original) - } - } - - def NoBacktrack getNoBacktrackAnnotation(AbstractRule rule) { - if (rule !== null) { - var original = RuleWithParameterValues.findInEmfObject(rule).getOriginal() - if (original === null) { - original = rule - } - return NoBacktrack.findInEmfObject(original) - } - } - - /** - * Checks if the given rule contains {@code @KeywordRule(kw1,kw2)} annotation. - */ - def SemanticPredicate getKeywordRulePredicate(String text, String ruleName, String grammar){ - val matcher = KEYWORD_RULE_ANNOTATION_PATTERN.matcher(text); - if (matcher.find()) { - return new SemanticPredicate( - "is" + ruleName + "Enabled", - "get" + ruleName + "EnabledMessage", - grammar, - Lists.newArrayList(KEYWORDS_SPLITTER.split(matcher.group(1))) - ) - } - } - - /** - * Checks if the given rule contains {@code @SemanticPredicate(condition)} annotation. - */ - def SemanticPredicate getSemanticPredicate(String text, String grammar){ - val matcher = SEMANTIC_PREDICATE_PATTERN.matcher(text); - if (matcher.find()) { - return new SemanticPredicate( - matcher.group(1), - "get" + matcher.group(1) + "Message", - grammar, - null - ) - } - } - - /** - * Checks if the given rule contains a NoBacktrack annotation. - */ - def NoBacktrack getNoBacktrack(String text){ - val matcher = NO_BACKTRACK_ANNOTATION_PATTERN.matcher(text); - if (matcher.find()) { - return new NoBacktrack - } - } - - def String getText(AbstractRule rule) { - val node = NodeModelUtils.getNode(rule); - return if(node !== null) node.getText() else grammarExtensions.grammarFragmentToString(rule,""); - } - - /** - * Checks if the given rule contains a keyword rule annotation. - * - * @param rule - * Grammar rule - * @return {@code true} if there is an annotation, {@code false} otherwise - */ - def boolean isSemanticPredicate(AbstractRule rule) { - var text = getText(rule); - if (text !== null) { - val matcher = SEMANTIC_PREDICATE_PATTERN.matcher(text); - if (matcher.find()) { - return true; - } - } - return false; - } -} diff --git a/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/common/PredicatesNaming.xtend b/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/common/PredicatesNaming.java similarity index 54% rename from com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/common/PredicatesNaming.xtend rename to com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/common/PredicatesNaming.java index 5488d0147f..38f13db3b4 100644 --- a/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/common/PredicatesNaming.xtend +++ b/com.avaloq.tools.ddk.xtext.generator/src/com/avaloq/tools/ddk/xtext/generator/parser/common/PredicatesNaming.java @@ -9,26 +9,30 @@ * Avaloq Group AG - initial API and implementation *******************************************************************************/ -package com.avaloq.tools.ddk.xtext.generator.parser.common +package com.avaloq.tools.ddk.xtext.generator.parser.common; -import com.google.inject.Inject -import org.eclipse.xtext.Grammar -import org.eclipse.xtext.GrammarUtil -import org.eclipse.xtext.xtext.generator.XtextGeneratorNaming +import org.eclipse.xtext.Grammar; +import org.eclipse.xtext.GrammarUtil; +import org.eclipse.xtext.xtext.generator.XtextGeneratorNaming; -class PredicatesNaming { +import com.google.inject.Inject; - @Inject extension XtextGeneratorNaming naming - def String getSemanticPredicatesFullName(Grammar grammar) { +@SuppressWarnings("nls") +public class PredicatesNaming { + + @Inject + private XtextGeneratorNaming naming; + + public String getSemanticPredicatesFullName(final Grammar grammar) { return getSemanticPredicatesPackageName(grammar) + "." + getSemanticPredicatesSimpleName(grammar); } - def String getSemanticPredicatesPackageName(Grammar grammar) { - return naming.getRuntimeBasePackage(grammar) + ".grammar" + public String getSemanticPredicatesPackageName(final Grammar grammar) { + return naming.getRuntimeBasePackage(grammar) + ".grammar"; } - def String getSemanticPredicatesSimpleName(Grammar grammar) { + public String getSemanticPredicatesSimpleName(final Grammar grammar) { return "Abstract" + GrammarUtil.getSimpleName(grammar) + "SemanticPredicates"; }