1 /* 2 * Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 3 * Use of this file is governed by the BSD 3-clause license that 4 * can be found in the LICENSE.txt file in the project root. 5 */ 6 package org.antlr.v4.runtime; 7 8 import org.antlr.v4.runtime.atn.ATN; 9 import org.antlr.v4.runtime.misc.Interval; 10 import org.antlr.v4.runtime.tree.ParseTree; 11 import org.antlr.v4.runtime.tree.ParseTreeVisitor; 12 import org.antlr.v4.runtime.tree.RuleNode; 13 import org.antlr.v4.runtime.tree.Trees; 14 15 import java.util.Arrays; 16 import java.util.List; 17 18 /** A rule context is a record of a single rule invocation. 19 * 20 * We form a stack of these context objects using the parent 21 * pointer. A parent pointer of null indicates that the current 22 * context is the bottom of the stack. The ParserRuleContext subclass 23 * as a children list so that we can turn this data structure into a 24 * tree. 25 * 26 * The root node always has a null pointer and invokingState of -1. 27 * 28 * Upon entry to parsing, the first invoked rule function creates a 29 * context object (a subclass specialized for that rule such as 30 * SContext) and makes it the root of a parse tree, recorded by field 31 * Parser._ctx. 32 * 33 * public final SContext s() throws RecognitionException { 34 * SContext _localctx = new SContext(_ctx, getState()); <-- create new node 35 * enterRule(_localctx, 0, RULE_s); <-- push it 36 * ... 37 * exitRule(); <-- pop back to _localctx 38 * return _localctx; 39 * } 40 * 41 * A subsequent rule invocation of r from the start rule s pushes a 42 * new context object for r whose parent points at s and use invoking 43 * state is the state with r emanating as edge label. 44 * 45 * The invokingState fields from a context object to the root 46 * together form a stack of rule indication states where the root 47 * (bottom of the stack) has a -1 sentinel value. If we invoke start 48 * symbol s then call r1, which calls r2, the would look like 49 * this: 50 * 51 * SContext[-1] <- root node (bottom of the stack) 52 * R1Context[p] <- p in rule s called r1 53 * R2Context[q] <- q in rule r1 called r2 54 * 55 * So the top of the stack, _ctx, represents a call to the current 56 * rule and it holds the return address from another rule that invoke 57 * to this rule. To invoke a rule, we must always have a current context. 58 * 59 * The parent contexts are useful for computing lookahead sets and 60 * getting error information. 61 * 62 * These objects are used during parsing and prediction. 63 * For the special case of parsers, we use the subclass 64 * ParserRuleContext. 65 * 66 * @see ParserRuleContext 67 */ 68 public class RuleContext implements RuleNode { 69 public static final ParserRuleContext EMPTY = new ParserRuleContext(); 70 71 /** What context invoked this rule? */ 72 public RuleContext parent; 73 74 /** What state invoked the rule associated with this context? 75 * The "return address" is the followState of invokingState 76 * If parent is null, this should be -1 this context object represents 77 * the start rule. 78 */ 79 public int invokingState = -1; 80 RuleContext()81 public RuleContext() {} 82 RuleContext(RuleContext parent, int invokingState)83 public RuleContext(RuleContext parent, int invokingState) { 84 this.parent = parent; 85 //if ( parent!=null ) System.out.println("invoke "+stateNumber+" from "+parent); 86 this.invokingState = invokingState; 87 } 88 depth()89 public int depth() { 90 int n = 0; 91 RuleContext p = this; 92 while ( p!=null ) { 93 p = p.parent; 94 n++; 95 } 96 return n; 97 } 98 99 /** A context is empty if there is no invoking state; meaning nobody called 100 * current context. 101 */ isEmpty()102 public boolean isEmpty() { 103 return invokingState == -1; 104 } 105 106 // satisfy the ParseTree / SyntaxTree interface 107 108 @Override getSourceInterval()109 public Interval getSourceInterval() { 110 return Interval.INVALID; 111 } 112 113 @Override getRuleContext()114 public RuleContext getRuleContext() { return this; } 115 116 @Override getParent()117 public RuleContext getParent() { return parent; } 118 119 @Override getPayload()120 public RuleContext getPayload() { return this; } 121 122 /** Return the combined text of all child nodes. This method only considers 123 * tokens which have been added to the parse tree. 124 * <p> 125 * Since tokens on hidden channels (e.g. whitespace or comments) are not 126 * added to the parse trees, they will not appear in the output of this 127 * method. 128 */ 129 @Override getText()130 public String getText() { 131 if (getChildCount() == 0) { 132 return ""; 133 } 134 135 StringBuilder builder = new StringBuilder(); 136 for (int i = 0; i < getChildCount(); i++) { 137 builder.append(getChild(i).getText()); 138 } 139 140 return builder.toString(); 141 } 142 getRuleIndex()143 public int getRuleIndex() { return -1; } 144 145 /** For rule associated with this parse tree internal node, return 146 * the outer alternative number used to match the input. Default 147 * implementation does not compute nor store this alt num. Create 148 * a subclass of ParserRuleContext with backing field and set 149 * option contextSuperClass. 150 * to set it. 151 * 152 * @since 4.5.3 153 */ getAltNumber()154 public int getAltNumber() { return ATN.INVALID_ALT_NUMBER; } 155 156 /** Set the outer alternative number for this context node. Default 157 * implementation does nothing to avoid backing field overhead for 158 * trees that don't need it. Create 159 * a subclass of ParserRuleContext with backing field and set 160 * option contextSuperClass. 161 * 162 * @since 4.5.3 163 */ setAltNumber(int altNumber)164 public void setAltNumber(int altNumber) { } 165 166 /** @since 4.7. {@see ParseTree#setParent} comment */ 167 @Override setParent(RuleContext parent)168 public void setParent(RuleContext parent) { 169 this.parent = parent; 170 } 171 172 @Override getChild(int i)173 public ParseTree getChild(int i) { 174 return null; 175 } 176 177 @Override getChildCount()178 public int getChildCount() { 179 return 0; 180 } 181 182 @Override accept(ParseTreeVisitor<? extends T> visitor)183 public <T> T accept(ParseTreeVisitor<? extends T> visitor) { return visitor.visitChildren(this); } 184 185 /** Print out a whole tree, not just a node, in LISP format 186 * (root child1 .. childN). Print just a node if this is a leaf. 187 * We have to know the recognizer so we can get rule names. 188 */ 189 @Override toStringTree(Parser recog)190 public String toStringTree(Parser recog) { 191 return Trees.toStringTree(this, recog); 192 } 193 194 /** Print out a whole tree, not just a node, in LISP format 195 * (root child1 .. childN). Print just a node if this is a leaf. 196 */ toStringTree(List<String> ruleNames)197 public String toStringTree(List<String> ruleNames) { 198 return Trees.toStringTree(this, ruleNames); 199 } 200 201 @Override toStringTree()202 public String toStringTree() { 203 return toStringTree((List<String>)null); 204 } 205 206 @Override toString()207 public String toString() { 208 return toString((List<String>)null, (RuleContext)null); 209 } 210 toString(Recognizer<?,?> recog)211 public final String toString(Recognizer<?,?> recog) { 212 return toString(recog, ParserRuleContext.EMPTY); 213 } 214 toString(List<String> ruleNames)215 public final String toString(List<String> ruleNames) { 216 return toString(ruleNames, null); 217 } 218 219 // recog null unless ParserRuleContext, in which case we use subclass toString(...) toString(Recognizer<?,?> recog, RuleContext stop)220 public String toString(Recognizer<?,?> recog, RuleContext stop) { 221 String[] ruleNames = recog != null ? recog.getRuleNames() : null; 222 List<String> ruleNamesList = ruleNames != null ? Arrays.asList(ruleNames) : null; 223 return toString(ruleNamesList, stop); 224 } 225 toString(List<String> ruleNames, RuleContext stop)226 public String toString(List<String> ruleNames, RuleContext stop) { 227 StringBuilder buf = new StringBuilder(); 228 RuleContext p = this; 229 buf.append("["); 230 while (p != null && p != stop) { 231 if (ruleNames == null) { 232 if (!p.isEmpty()) { 233 buf.append(p.invokingState); 234 } 235 } 236 else { 237 int ruleIndex = p.getRuleIndex(); 238 String ruleName = ruleIndex >= 0 && ruleIndex < ruleNames.size() ? ruleNames.get(ruleIndex) : Integer.toString(ruleIndex); 239 buf.append(ruleName); 240 } 241 242 if (p.parent != null && (ruleNames != null || !p.parent.isEmpty())) { 243 buf.append(" "); 244 } 245 246 p = p.parent; 247 } 248 249 buf.append("]"); 250 return buf.toString(); 251 } 252 } 253