org.antlr.v4.tool.Rule Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of antlr4 Show documentation
Show all versions of antlr4 Show documentation
The ANTLR 4 grammar compiler.
/*
* Copyright (c) 2012 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD-3-Clause license that
* can be found in the LICENSE.txt file in the project root.
*/
package org.antlr.v4.tool;
import org.antlr.v4.runtime.atn.ATNSimulator;
import org.antlr.v4.runtime.misc.MultiMap;
import org.antlr.v4.runtime.misc.Tuple;
import org.antlr.v4.runtime.misc.Tuple2;
import org.antlr.v4.tool.ast.ActionAST;
import org.antlr.v4.tool.ast.AltAST;
import org.antlr.v4.tool.ast.GrammarAST;
import org.antlr.v4.tool.ast.PredAST;
import org.antlr.v4.tool.ast.RuleAST;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Rule implements AttributeResolver {
/** Rule refs have a predefined set of attributes as well as
* the return values and args.
*
* These must be consistent with ActionTranslator.rulePropToModelMap, ...
*/
public static final AttributeDict predefinedRulePropertiesDict =
new AttributeDict(AttributeDict.DictType.PREDEFINED_RULE);
static {
predefinedRulePropertiesDict.add(new Attribute("parser"));
predefinedRulePropertiesDict.add(new Attribute("text"));
predefinedRulePropertiesDict.add(new Attribute("start"));
predefinedRulePropertiesDict.add(new Attribute("stop"));
predefinedRulePropertiesDict.add(new Attribute("ctx"));
}
public static final Set validLexerCommands = new HashSet();
static {
// CALLS
validLexerCommands.add("mode");
validLexerCommands.add("pushMode");
validLexerCommands.add("type");
validLexerCommands.add("channel");
// ACTIONS
validLexerCommands.add("popMode");
validLexerCommands.add("skip");
validLexerCommands.add("more");
}
public String name;
private String baseContext;
public List modifiers;
public RuleAST ast;
public AttributeDict args;
public AttributeDict retvals;
public AttributeDict locals;
/** In which grammar does this rule live? */
public Grammar g;
/** If we're in a lexer grammar, we might be in a mode */
public String mode;
/** Map a name to an action for this rule like @init {...}.
* The code generator will use this to fill holes in the rule template.
* I track the AST node for the action in case I need the line number
* for errors.
*/
public Map namedActions =
new HashMap();
/** Track exception handlers; points at "catch" node of (catch exception action)
* don't track finally action
*/
public List exceptions = new ArrayList();
/** Track all executable actions other than named actions like @init
* and catch/finally (not in an alt). Also tracks predicates, rewrite actions.
* We need to examine these actions before code generation so
* that we can detect refs to $rule.attr etc...
*
* This tracks per rule; Alternative objs also track per alt.
*/
public List actions = new ArrayList();
public ActionAST finallyAction;
public int numberOfAlts;
public boolean isStartRule = true; // nobody calls us
/** 1..n alts */
public Alternative[] alt;
/** All rules have unique index 0..n-1 */
public int index;
public int actionIndex = -1; // if lexer; 0..n-1 for n actions in a rule
public Rule(Grammar g, String name, RuleAST ast, int numberOfAlts) {
this.g = g;
this.name = name;
this.ast = ast;
this.numberOfAlts = numberOfAlts;
alt = new Alternative[numberOfAlts+1]; // 1..n
for (int i=1; i<=numberOfAlts; i++) alt[i] = new Alternative(this, i);
}
public String getBaseContext() {
if (baseContext != null && !baseContext.isEmpty()) {
return baseContext;
}
String optionBaseContext = ast.getOptionString("baseContext");
if (optionBaseContext != null && !optionBaseContext.isEmpty()) {
return optionBaseContext;
}
int variantDelimiter = name.indexOf(ATNSimulator.RULE_VARIANT_DELIMITER);
if (variantDelimiter >= 0) {
return name.substring(0, variantDelimiter);
}
return name;
}
public void setBaseContext(String baseContext) {
this.baseContext = baseContext;
}
public void defineActionInAlt(int currentAlt, ActionAST actionAST) {
actions.add(actionAST);
alt[currentAlt].actions.add(actionAST);
if ( g.isLexer() ) {
defineLexerAction(actionAST);
}
}
/** Lexer actions are numbered across rules 0..n-1 */
public void defineLexerAction(ActionAST actionAST) {
actionIndex = g.lexerActions.size();
if ( g.lexerActions.get(actionAST)==null ) {
g.lexerActions.put(actionAST, actionIndex);
}
}
public void definePredicateInAlt(int currentAlt, PredAST predAST) {
actions.add(predAST);
alt[currentAlt].actions.add(predAST);
if ( g.sempreds.get(predAST)==null ) {
g.sempreds.put(predAST, g.sempreds.size());
}
}
public Attribute resolveRetvalOrProperty(String y) {
if ( retvals!=null ) {
Attribute a = retvals.get(y);
if ( a!=null ) return a;
}
AttributeDict d = getPredefinedScope(LabelType.RULE_LABEL);
return d.get(y);
}
public Set getTokenRefs() {
Set refs = new HashSet();
for (int i=1; i<=numberOfAlts; i++) {
refs.addAll(alt[i].tokenRefs.keySet());
}
return refs;
}
public Set getElementLabelNames() {
Set refs = new HashSet();
for (int i=1; i<=numberOfAlts; i++) {
refs.addAll(alt[i].labelDefs.keySet());
}
if ( refs.isEmpty() ) return null;
return refs;
}
public MultiMap getElementLabelDefs() {
MultiMap defs =
new MultiMap();
for (int i=1; i<=numberOfAlts; i++) {
for (List pairs : alt[i].labelDefs.values()) {
for (LabelElementPair p : pairs) {
defs.map(p.label.getText(), p);
}
}
}
return defs;
}
public boolean hasAltSpecificContexts() {
return getAltLabels()!=null;
}
/** Used for recursive rules (subclass), which have 1 alt, but many original alts */
public int getOriginalNumberOfAlts() {
return numberOfAlts;
}
/**
* Get {@code #} labels. The keys of the map are the labels applied to outer
* alternatives of a lexer rule, and the values are collections of pairs
* (alternative number and {@link AltAST}) identifying the alternatives with
* this label. Unlabeled alternatives are not included in the result.
*/
public Map>> getAltLabels() {
Map>> labels = new LinkedHashMap>>();
for (int i=1; i<=numberOfAlts; i++) {
GrammarAST altLabel = alt[i].ast.altLabel;
if ( altLabel!=null ) {
List> list = labels.get(altLabel.getText());
if (list == null) {
list = new ArrayList>();
labels.put(altLabel.getText(), list);
}
list.add(Tuple.create(i, alt[i].ast));
}
}
if ( labels.isEmpty() ) return null;
return labels;
}
public List getUnlabeledAltASTs() {
List alts = new ArrayList();
for (int i=1; i<=numberOfAlts; i++) {
GrammarAST altLabel = alt[i].ast.altLabel;
if ( altLabel==null ) alts.add(alt[i].ast);
}
if ( alts.isEmpty() ) return null;
return alts;
}
/** $x Attribute: rule arguments, return values, predefined rule prop.
*/
@Override
public Attribute resolveToAttribute(String x, ActionAST node) {
if ( args!=null ) {
Attribute a = args.get(x); if ( a!=null ) return a;
}
if ( retvals!=null ) {
Attribute a = retvals.get(x); if ( a!=null ) return a;
}
if ( locals!=null ) {
Attribute a = locals.get(x); if ( a!=null ) return a;
}
AttributeDict properties = getPredefinedScope(LabelType.RULE_LABEL);
return properties.get(x);
}
/** $x.y Attribute: x is surrounding rule, label ref (in any alts) */
@Override
public Attribute resolveToAttribute(String x, String y, ActionAST node) {
LabelElementPair anyLabelDef = getAnyLabelDef(x);
if ( anyLabelDef!=null ) {
if ( anyLabelDef.type==LabelType.RULE_LABEL ) {
return g.getRule(anyLabelDef.element.getText()).resolveRetvalOrProperty(y);
}
else {
AttributeDict scope = getPredefinedScope(anyLabelDef.type);
if (scope == null) {
return null;
}
return scope.get(y);
}
}
return null;
}
@Override
public boolean resolvesToLabel(String x, ActionAST node) {
LabelElementPair anyLabelDef = getAnyLabelDef(x);
return anyLabelDef!=null &&
(anyLabelDef.type==LabelType.RULE_LABEL ||
anyLabelDef.type==LabelType.TOKEN_LABEL);
}
@Override
public boolean resolvesToListLabel(String x, ActionAST node) {
LabelElementPair anyLabelDef = getAnyLabelDef(x);
return anyLabelDef!=null &&
(anyLabelDef.type==LabelType.RULE_LIST_LABEL ||
anyLabelDef.type==LabelType.TOKEN_LIST_LABEL);
}
@Override
public boolean resolvesToToken(String x, ActionAST node) {
LabelElementPair anyLabelDef = getAnyLabelDef(x);
if ( anyLabelDef!=null && anyLabelDef.type==LabelType.TOKEN_LABEL ) return true;
return false;
}
@Override
public boolean resolvesToAttributeDict(String x, ActionAST node) {
if ( resolvesToToken(x, node) ) return true;
return false;
}
public Rule resolveToRule(String x) {
if ( x.equals(this.name) ) return this;
LabelElementPair anyLabelDef = getAnyLabelDef(x);
if ( anyLabelDef!=null && anyLabelDef.type==LabelType.RULE_LABEL ) {
return g.getRule(anyLabelDef.element.getText());
}
return g.getRule(x);
}
public LabelElementPair getAnyLabelDef(String x) {
List labels = getElementLabelDefs().get(x);
if ( labels!=null ) return labels.get(0);
return null;
}
public AttributeDict getPredefinedScope(LabelType ltype) {
String grammarLabelKey = g.getTypeString() + ":" + ltype;
return Grammar.grammarAndLabelRefTypeToScope.get(grammarLabelKey);
}
public boolean isFragment() {
if ( modifiers==null ) return false;
for (GrammarAST a : modifiers) {
if ( a.getText().equals("fragment") ) return true;
}
return false;
}
@Override
public int hashCode() { return name.hashCode(); }
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Rule)) {
return false;
}
return name.equals(((Rule)obj).name);
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("Rule{name=").append(name);
if ( args!=null ) buf.append(", args=").append(args);
if ( retvals!=null ) buf.append(", retvals=").append(retvals);
buf.append("}");
return buf.toString();
}
}