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