/* * Copyright (c) 2012 Sam Harwell, Tunnel Vision Laboratories LLC * All rights reserved. * * The source code of this document is proprietary work, and is not licensed for * distribution. For information about licensing, contact Sam Harwell at: * sam@tunnelvisionlabs.com */ package org.antlr.works.editor.grammar.completion; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.antlr.v4.runtime.Dependents; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.RuleContext; import org.antlr.v4.runtime.RuleDependencies; import org.antlr.v4.runtime.RuleDependency; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.misc.Tuple; import org.antlr.v4.runtime.misc.Tuple2; import org.antlr.v4.runtime.tree.TerminalNode; import org.antlr.works.editor.grammar.experimental.GrammarParser; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.ActionBlockContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.ArgActionBlockContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.ArgActionParameterContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.ArgActionParameterTypeContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.LabeledAltContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.LabeledElementContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.LexerRuleContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.ParserRuleSpecContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.RulerefContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.TerminalContext; import org.antlr.works.editor.grammar.experimental.generated.GrammarParserBaseListener; /** * * @author Sam Harwell */ public class LabelAnalyzer extends GrammarParserBaseListener { private final Map<String, Token> labels = new HashMap<>(); private final Map<String, Token> unlabeledElements = new HashMap<>(); private final Map<String, Tuple2<Token, ArgActionParameterTypeContext>> arguments = new HashMap<>(); private final Map<String, Tuple2<Token, ArgActionParameterTypeContext>> returnValues = new HashMap<>(); private final Map<String, Tuple2<Token, ArgActionParameterTypeContext>> locals = new HashMap<>(); private final RuleContext finalContext; private Token enclosingRuleName; private boolean caretReached; private boolean inAction; public LabelAnalyzer(RuleContext finalContext) { this.finalContext = finalContext; } public final RuleContext getFinalContext() { return finalContext; } public final Collection<Token> getLabels() { return labels.values(); } public final Collection<Token> getUnlabeledElements() { return unlabeledElements.values(); } public final Collection<Tuple2<Token, ArgActionParameterTypeContext>> getArguments() { return arguments.values(); } public final Collection<Tuple2<Token, ArgActionParameterTypeContext>> getReturnValues() { return returnValues.values(); } public final Collection<Tuple2<Token, ArgActionParameterTypeContext>> getLocals() { return locals.values(); } public final Token getEnclosingRuleName() { return enclosingRuleName; } public final boolean isCaretReached() { return caretReached; } public final boolean isInAction() { return inAction; } @Override public void exitEveryRule(ParserRuleContext ctx) { checkCaretReached(ctx); } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_actionExpression, version=0, dependents=Dependents.PARENTS) public void enterParserRuleSpec(ParserRuleSpecContext ctx) { if (ctx.name != null) { enclosingRuleName = ctx.name; } } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_lexerRule, version=0, dependents=Dependents.PARENTS) public void enterLexerRule(LexerRuleContext ctx) { if (ctx.name != null) { enclosingRuleName = ctx.name; } } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_labeledAlt, version=1, dependents=Dependents.PARENTS) public void enterLabeledAlt(LabeledAltContext ctx) { if (isCaretReached()) { return; } labels.clear(); unlabeledElements.clear(); } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_labeledElement, version=5, dependents={Dependents.PARENTS, Dependents.DESCENDANTS}) public void enterLabeledElement(LabeledElementContext ctx) { if (isCaretReached()) { return; } if (!labels.containsKey(ctx.start.getText())) { labels.put(ctx.start.getText(), ctx.start); } } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_terminal, version=3, dependents={Dependents.PARENTS, Dependents.DESCENDANTS}) public void enterTerminal(TerminalContext ctx) { if (isCaretReached()) { return; } if (isLabeledContext(ctx)) { return; } if (ctx.start.getType() == GrammarParser.TOKEN_REF) { if (!labels.containsKey(ctx.start.getText())) { unlabeledElements.put(ctx.start.getText(), ctx.start); } } } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_ruleref, version=5, dependents={Dependents.PARENTS, Dependents.DESCENDANTS}) public void enterRuleref(RulerefContext ctx) { if (isCaretReached()) { return; } if (isLabeledContext(ctx)) { return; } if (ctx.start.getType() == GrammarParser.RULE_REF) { if (!labels.containsKey(ctx.start.getText())) { unlabeledElements.put(ctx.start.getText(), ctx.start); } } } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_actionBlock, version=5, dependents=Dependents.PARENTS) public void enterActionBlock(ActionBlockContext ctx) { if (isCaretReached()) { return; } inAction = true; } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_actionBlock, version=5, dependents=Dependents.PARENTS) public void exitActionBlock(ActionBlockContext ctx) { checkCaretReached(ctx); if (isCaretReached()) { return; } inAction = false; } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_argActionBlock, version=5, dependents=Dependents.PARENTS) public void enterArgActionBlock(ArgActionBlockContext ctx) { if (isCaretReached()) { return; } inAction = true; } @Override @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_argActionBlock, version=5, dependents=Dependents.PARENTS) public void exitArgActionBlock(ArgActionBlockContext ctx) { checkCaretReached(ctx); if (isCaretReached()) { return; } inAction = false; } @Override @RuleDependencies({ // general dependencies for the target of an arg action parameter @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_argActionParameter, version=7, dependents=Dependents.PARENTS), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_argActionParameters, version=0, dependents=Dependents.PARENTS), // dependencies for specific constructs which allow arg action parameters @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_parserRuleSpec, version=0, dependents=Dependents.SELF), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_ruleReturns, version=0, dependents=Dependents.SELF), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_localsSpec, version=0, dependents=Dependents.SELF), }) public void enterArgActionParameter(ArgActionParameterContext ctx) { checkCaretReached(ctx); if (isCaretReached()) { return; } TerminalNode name = ctx.ARG_ACTION_WORD(); ParserRuleContext parent = ctx.getParent(); if (parent == null) { return; } ParserRuleContext grandParent = parent.getParent(); if (grandParent == null) { return; } if (name != null) { Tuple2<Token, ArgActionParameterTypeContext> parameter = Tuple.create(name.getSymbol(), ctx.argActionParameterType(0)); switch (grandParent.getRuleIndex()) { case GrammarParser.RULE_parserRuleSpec: arguments.put(name.getText(), parameter); break; case GrammarParser.RULE_ruleReturns: returnValues.put(name.getText(), parameter); break; case GrammarParser.RULE_localsSpec: locals.put(name.getText(), parameter); break; default: break; } } } private void checkCaretReached(RuleContext ctx) { if (ctx == getFinalContext()) { caretReached = true; } } @RuleDependencies({ @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_labeledElement, version=5, dependents=Dependents.DESCENDANTS), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_notSet, version=4, dependents=Dependents.DESCENDANTS), }) private static boolean isLabeledContext(ParserRuleContext ctx) { for (RuleContext current = ctx; current != null; current = current.parent) { if (current instanceof GrammarParser.LabeledElementContext) { return true; } else if (current instanceof GrammarParser.NotSetContext) { return true; } } return false; } }