/* * 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.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import javax.swing.text.JTextComponent; import org.antlr.v4.runtime.Dependents; 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.works.editor.grammar.codemodel.AttributeModel; import org.antlr.works.editor.grammar.codemodel.CodeElementModel; import org.antlr.works.editor.grammar.codemodel.CodeElementPositionRegion; import org.antlr.works.editor.grammar.codemodel.FileModel; import org.antlr.works.editor.grammar.codemodel.LabelModel; import org.antlr.works.editor.grammar.codemodel.PackageModel; import org.antlr.works.editor.grammar.codemodel.RuleModel; import org.antlr.works.editor.grammar.codemodel.TokenVocabModel; import org.antlr.works.editor.grammar.experimental.GrammarParser; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.ActionExpressionContext; import org.antlr.works.editor.grammar.experimental.generated.AbstractGrammarParser.ActionScopeExpressionContext; 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.RuleSpecContext; import org.antlr.works.editor.grammar.experimental.generated.GrammarParserBaseListener; import org.netbeans.api.annotations.common.NonNull; import org.netbeans.api.project.Project; import org.openide.util.Parameters; /** * * @author Sam Harwell */ public class ActionExpressionAnalyzer extends GrammarParserBaseListener { private final FileModel fileModel; private final RuleContext finalContext; private final List<AttributeModel> members = new ArrayList<>(); public ActionExpressionAnalyzer(@NonNull FileModel fileModel, @NonNull RuleContext finalContext) { Parameters.notNull("fileModel", fileModel); Parameters.notNull("finalContext", finalContext); this.fileModel = fileModel; this.finalContext = finalContext; } public List<AttributeModel> getMembers() { return members; } @Override @RuleDependencies({ @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_actionScopeExpression, version=6, dependents=Dependents.ANCESTORS), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_ruleSpec, version=0, dependents=Dependents.SELF), }) public void enterActionScopeExpression(ActionScopeExpressionContext ctx) { if (ctx.op != null && ctx.member == null) { /* action scope expressions are only used for rule references * $ruleName:: */ RuleSpecContext enclosingRule = getEnclosingRuleContext(ctx); Collection<? extends RuleModel> referencedRules = getReferencedRule(enclosingRule, ctx.ref, false); for (RuleModel referencedRule : referencedRules) { // for a scope reference, we can reference the parameters, locals, return values, and labels members.addAll(referencedRule.getParameters()); members.addAll(referencedRule.getReturnValues()); members.addAll(referencedRule.getLocals()); members.addAll(referencedRule.getLabels()); } } } @Override @RuleDependencies({ @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_actionExpression, version=6, dependents=Dependents.ANCESTORS), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_ruleSpec, version=0, dependents=Dependents.SELF), }) public void enterActionExpression(ActionExpressionContext ctx) { if (ctx.op != null && ctx.member == null) { /* action expressions are used for label references (explicit or implicit) * $elementName. (implicit label reference) * $labelName. (explicit label reference) */ RuleSpecContext enclosingRule = getEnclosingRuleContext(ctx); Collection<? extends RuleModel> referencedRules = getReferencedRule(enclosingRule, ctx.ref, true); for (RuleModel referencedRule : referencedRules) { // for a regular reference, we can reference the return values and labels members.addAll(referencedRule.getReturnValues()); members.addAll(referencedRule.getLabels()); members.add(new IntrinsicAttribute("start")); members.add(new IntrinsicAttribute("stop")); members.add(new IntrinsicAttribute("text")); members.add(new IntrinsicAttribute("tree")); members.add(new IntrinsicAttribute("st")); } } } @RuleDependencies({ @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_ruleSpec, version=0, dependents=Dependents.SELF), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_parserRuleSpec, version=0, dependents=Dependents.SELF), @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_lexerRule, version=0, dependents=Dependents.SELF), }) private Token getName(RuleSpecContext rule) { if (rule.getChild(0) instanceof ParserRuleSpecContext) { return ((ParserRuleSpecContext)rule.getChild(0)).name; } else if (rule.getChild(0) instanceof LexerRuleContext) { return ((LexerRuleContext)rule.getChild(0)).name; } else { return null; } } @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_ruleSpec, version=0, dependents=Dependents.SELF) private Collection<? extends RuleModel> getReferencedRule(RuleSpecContext enclosingRule, Token reference, boolean followLabels) { String enclosingRuleName = getName(enclosingRule).getText(); Collection<? extends RuleModel> ruleModels = fileModel.getRules(enclosingRuleName); for (RuleModel ruleModel : ruleModels) { Collection<? extends RuleModel> referencedRule = null; if (followLabels) { /* first try for a label reference. even though labels are not allowed to * alias rule names, we want to minimize the impact of this restriction * on the ability of code completion to provide useful results. */ Collection<? extends LabelModel> label = ruleModel.getLabels(reference.getText().substring(1)); if (!label.isEmpty()) { throw new UnsupportedOperationException("Not implemented yet."); } } if (referencedRule == null) { referencedRule = fileModel.getRules(reference.getText().substring(1)); } if (!referencedRule.isEmpty()) { return referencedRule; } } return null; } @RuleDependency(recognizer=GrammarParser.class, rule=GrammarParser.RULE_ruleSpec, version=7, dependents=Dependents.DESCENDANTS) private static RuleSpecContext getEnclosingRuleContext(RuleContext context) { while (context != null) { if (context instanceof RuleSpecContext) { return (RuleSpecContext)context; } context = context.parent; } return null; } private static class IntrinsicAttribute implements AttributeModel { private final String name; public IntrinsicAttribute(String name) { this.name = name; } @Override public String getName() { return name; } @Override public String getType() { return ""; } @Override public PackageModel getPackage() { return IntrinsicPackage.INSTANCE; } @Override public Collection<? extends CodeElementModel> getMembers() { return Collections.emptyList(); } @Override public Collection<? extends CodeElementModel> getMembers(String name) { return Collections.emptyList(); } @Override public CodeElementPositionRegion getSeek() { return null; } @Override public CodeElementPositionRegion getSpan() { return null; } @Override public JTextComponent navigateTo() { return null; } } private static class IntrinsicPackage implements PackageModel { public static final IntrinsicPackage INSTANCE = new IntrinsicPackage(); @Override public Project getProject() { return null; } @Override public Collection<? extends FileModel> getFiles() { return Collections.emptyList(); } @Override public Collection<? extends TokenVocabModel> getVocabularies() { return Collections.emptyList(); } @Override public PackageModel getPackage() { return this; } @Override public String getName() { return "intrinsic"; } @Override public Collection<? extends CodeElementModel> getMembers() { return Collections.emptyList(); } @Override public Collection<? extends CodeElementModel> getMembers(String name) { return Collections.emptyList(); } @Override public CodeElementPositionRegion getSeek() { return null; } @Override public CodeElementPositionRegion getSpan() { return null; } @Override public JTextComponent navigateTo() { return null; } } }