1 /* 2 * Copyright (c) 2012-2018 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 7 module antlr.v4.runtime.RuleContext; 8 9 import antlr.v4.runtime.InterfaceRecognizer; 10 import antlr.v4.runtime.InterfaceRuleContext; 11 import antlr.v4.runtime.ParserRuleContext; 12 import antlr.v4.runtime.Token; 13 import antlr.v4.runtime.atn.ATN; 14 import antlr.v4.runtime.misc; 15 import antlr.v4.runtime.tree.ParseTree; 16 import antlr.v4.runtime.tree.RuleNode; 17 import antlr.v4.runtime.tree.Trees; 18 import std.array; 19 import std.conv; 20 21 /** 22 * A rule context is a record of a single rule invocation. 23 * 24 * We form a stack of these context objects using the parent 25 * pointer. A parent pointer of null indicates that the current 26 * context is the bottom of the stack. The ParserRuleContext subclass 27 * as a children list so that we can turn this data structure into a 28 * tree. 29 * 30 * The root node always has a null pointer and invokingState of -1. 31 * 32 * Upon entry to parsing, the first invoked rule function creates a 33 * context object (asubclass specialized for that rule such as 34 * SContext) and makes it the root of a parse tree, recorded by field 35 * Parser._ctx. 36 * 37 * public final SContext s() throws RecognitionException { 38 * SContext _localctx = new SContext(_ctx, getState()); <-- create new node 39 * enterRule(_localctx, 0, RULE_s); <-- push it 40 * ... 41 * exitRule(); <-- pop back to _localctx 42 * return _localctx; 43 * } 44 * 45 * A subsequent rule invocation of r from the start rule s pushes a 46 * new context object for r whose parent points at s and use invoking 47 * state is the state with r emanating as edge label. 48 * 49 * The invokingState fields from a context object to the root 50 * together form a stack of rule indication states where the root 51 * (bottom of the stack) has a -1 sentinel value. If we invoke start 52 * symbol s then call r1, which calls r2, the would look like 53 * this: 54 * 55 * SContext[-1] <- root node (bottom of the stack) 56 * R1Context[p] <- p in rule s called r1 57 * R2Context[q] <- q in rule r1 called r2 58 * 59 * So the top of the stack, _ctx, represents a call to the current 60 * rule and it holds the return address from another rule that invoke 61 * to this rule. To invoke a rule, we must always have a current context. 62 * 63 * The parent contexts are useful for computing lookahead sets and 64 * getting error information. 65 * 66 * These objects are used during parsing and prediction. 67 * For the special case of parsers, we use the subclass 68 * ParserRuleContext. 69 * 70 * @see ParserRuleContext 71 */ 72 class RuleContext : RuleNode, InterfaceRuleContext 73 { 74 75 public static const ParserRuleContext EMPTY = new ParserRuleContext(); 76 77 public RuleContext parent; 78 79 public int invokingState = -1; 80 81 public this() 82 { 83 } 84 85 public this(RuleContext parent, int invokingState) 86 { 87 this.parent = parent; 88 this.invokingState = invokingState; 89 } 90 91 public int depth() 92 { 93 int n = 0; 94 RuleContext p = this; 95 while (p) { 96 p = p.parent; 97 n++; 98 } 99 return n; 100 } 101 102 /** 103 * A context is empty if there is no invoking state; meaning nobody called 104 * current context. 105 */ 106 public bool isEmpty() 107 { 108 return invokingState == -1; 109 } 110 111 /** 112 * satisfy the ParseTree / SyntaxTree interface 113 */ 114 public Interval getSourceInterval() 115 { 116 return cast(Interval)Interval.INVALID; 117 } 118 119 public RuleContext getRuleContext() 120 { 121 return this; 122 } 123 124 public RuleContext getPayload() 125 { 126 return this; 127 } 128 129 /** 130 * Return the combined text of all child nodes. This method only considers 131 * tokens which have been added to the parse tree. 132 * <p> 133 * Since tokens on hidden channels (e.g. whitespace or comments) are not 134 * added to the parse trees, they will not appear in the output of this 135 * method. 136 */ 137 public string getText() 138 { 139 if (getChildCount() == 0) { 140 return ""; 141 } 142 auto builder = appender!(string); 143 for (int i = 0; i < getChildCount(); i++) { 144 builder.put(getChild(i).getText()); 145 } 146 return builder.data; 147 } 148 149 public int getRuleIndex() 150 { 151 return -1; 152 } 153 154 /** 155 * For rule associated with this parse tree internal node, return 156 * the outer alternative number used to match the input. Default 157 * implementation does not compute nor store this alt num. Create 158 * a subclass of ParserRuleContext with backing field and set 159 * option contextSuperClass. 160 * to set it. 161 */ 162 public int getAltNumber() 163 { 164 return ATN.INVALID_ALT_NUMBER; 165 } 166 167 /** 168 * Set the outer alternative number for this context node. Default 169 * implementation does nothing to avoid backing field overhead for 170 * trees that don't need it. Create 171 * a subclass of ParserRuleContext with backing field and set 172 * option contextSuperClass. 173 */ 174 public void setAltNumber(int altNumber) 175 { 176 } 177 178 public ParseTree getChild(int i) 179 { 180 return null; 181 } 182 183 public int getChildCount() 184 { 185 return 0; 186 } 187 188 public U accept(U)(ParseTreeVisitor!U visitor) 189 { 190 return visitor.visitChildren(this); 191 } 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 * We have to know the recognizer so we can get rule names. 197 */ 198 public string toStringTree(InterfaceRecognizer recog) 199 { 200 return Trees.toStringTree(cast(ParseTree)this, recog); 201 } 202 203 public string toStringTree(string[] ruleNames) 204 { 205 return Trees.toStringTree(cast(ParseTree)this, ruleNames); 206 } 207 208 public string toStringTree() 209 { 210 return toStringTree([]); 211 } 212 213 /** 214 * @uml 215 * @override 216 */ 217 public override string toString() 218 { 219 return toString([]); 220 } 221 222 public string toString(InterfaceRecognizer recog) 223 { 224 return toString(recog, ParserRuleContext.EMPTY); 225 } 226 227 public string toString(string[] ruleNames) 228 { 229 return toString(ruleNames, null); 230 } 231 232 /** 233 * recog null unless ParserRuleContext, in which case we use subclass toString(...) 234 */ 235 public string toString(InterfaceRecognizer recog, RuleContext stop) 236 { 237 string[] ruleNames = recog !is null ? recog.getRuleNames() : null; 238 string[] ruleNamesList = ruleNames !is null ? ruleNames : null; 239 return toString(ruleNamesList) ~ stop.toString; 240 241 } 242 243 public string toString(string[] ruleNames, RuleContext stop) 244 { 245 auto buf = appender!(string); 246 RuleContext p = this; 247 buf.put("["); 248 while (p !is null && p != stop) { 249 if (ruleNames.length == 0) { 250 if (!p.isEmpty) { 251 buf.put(to!string(p.invokingState)); 252 } 253 } 254 else { 255 int ruleIndex = p.getRuleIndex; 256 string ruleName = ruleIndex >= 0 && ruleIndex < ruleNames.length ? ruleNames[ruleIndex] : to!string(ruleIndex); 257 buf.put(ruleName); 258 } 259 if (p.parent !is null && (ruleNames.length || !p.parent.isEmpty)) { 260 buf.put(" "); 261 } 262 p = p.parent; 263 } 264 buf.put("]"); 265 return buf.data; 266 } 267 268 public RuleContext getParent() 269 { 270 return parent; 271 } 272 273 /** 274 * since 4.7. {@see ParseTree#setParent} comment 275 */ 276 public void setParent(RuleContext parent) 277 { 278 this.parent = parent; 279 } 280 281 } 282 283 version(unittest) { 284 import dshould : be, equal, not, should; 285 import unit_threaded; 286 287 class Test { 288 @Tags("ruleCont", "reg") 289 @("simpleRuleContext") 290 unittest { 291 auto rcp = new RuleContext(null, -1); 292 auto rc = new RuleContext(rcp, -1); 293 rc.should.not.be(null); 294 rcp.depth.should.equal(1); 295 rc.isEmpty.should.equal(true); 296 rc.parent.isEmpty.should.equal(true); 297 rc.getParent.isEmpty.should.equal(true); 298 rc.depth.should.equal(2); 299 rc.toString.should.equal("[]"); 300 rc.toStringTree.should.equal("[]"); 301 rc.getAltNumber.should.equal(0); 302 } 303 304 @Tags("ruleContVoc", "reg") 305 @("ruleContextWithVocabulary") 306 unittest { 307 class RuleContextT : RuleContext { 308 public this(RuleContext parent, int invokingState) 309 { 310 super(parent, invokingState); 311 } 312 313 override public int getRuleIndex() 314 { 315 return invokingState; 316 } 317 } 318 auto rcp = new RuleContext(null, 1); 319 auto rc = new RuleContext(rcp, 0); 320 rc.should.not.be(null); 321 rc.toString(["A1", "B1"]).should.equal("[-1 -1]"); 322 323 rcp = new RuleContextT(null, 1); 324 rc = new RuleContextT(rcp, 0); 325 rc.should.not.be(null); 326 rc.toString(["A1", "B1"]).should.equal("[A1 B1]"); 327 } 328 } 329 }