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