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 // Class RuleContext 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 new Interval(-1, -2); // 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 string getText() 139 { 140 if (getChildCount() == 0) { 141 return ""; 142 } 143 auto builder = appender!(string); 144 for (int i = 0; i < getChildCount(); i++) { 145 builder.put(getChild(i).getText()); 146 } 147 return builder.data; 148 } 149 150 public int getRuleIndex() 151 { 152 return -1; 153 } 154 155 /** 156 * For rule associated with this parse tree internal node, return 157 * the outer alternative number used to match the input. Default 158 * implementation does not compute nor store this alt num. Create 159 * a subclass of ParserRuleContext with backing field and set 160 * option contextSuperClass. 161 * to set it. 162 */ 163 public int getAltNumber() 164 { 165 return ATN.INVALID_ALT_NUMBER; 166 } 167 168 /** 169 * Set the outer alternative number for this context node. Default 170 * implementation does nothing to avoid backing field overhead for 171 * trees that don't need it. Create 172 * a subclass of ParserRuleContext with backing field and set 173 * option contextSuperClass. 174 */ 175 public void setAltNumber(int altNumber) 176 { 177 } 178 179 public ParseTree getChild(int i) 180 { 181 return null; 182 } 183 184 public int getChildCount() 185 { 186 return 0; 187 } 188 189 public U accept(U)(ParseTreeVisitor!U visitor) 190 { 191 return visitor.visitChildren(this); 192 } 193 194 /** 195 * Print out a whole tree, not just a node, in LISP format 196 * (root child1 .. childN). Print just a node if this is a leaf. 197 * We have to know the recognizer so we can get rule names. 198 */ 199 public string toStringTree(InterfaceRecognizer recog) 200 { 201 return Trees.toStringTree(cast(ParseTree)this, recog); 202 } 203 204 public string toStringTree(string[] ruleNames) 205 { 206 return Trees.toStringTree(cast(ParseTree)this, ruleNames); 207 } 208 209 public string toStringTree() 210 { 211 return toStringTree([]); 212 } 213 214 /** 215 * @uml 216 * @override 217 */ 218 public override string toString() 219 { 220 return toString([]); 221 } 222 223 public string toString(InterfaceRecognizer recog) 224 { 225 return toString(recog, ParserRuleContext.EMPTY); 226 } 227 228 public string toString(string[] ruleNames) 229 { 230 return toString(ruleNames, null); 231 } 232 233 /** 234 * recog null unless ParserRuleContext, in which case we use subclass toString(...) 235 */ 236 public string toString(InterfaceRecognizer recog, RuleContext stop) 237 { 238 string[] ruleNames = recog !is null ? recog.getRuleNames() : null; 239 string[] ruleNamesList = ruleNames !is null ? ruleNames : null; 240 return toString(ruleNamesList) ~ stop.toString; 241 242 } 243 244 public string toString(string[] ruleNames, RuleContext stop) 245 { 246 auto buf = appender!(string); 247 RuleContext p = this; 248 buf.put("["); 249 while (p !is null && p != stop) { 250 if (ruleNames.length == 0) { 251 if (!p.isEmpty) { 252 buf.put(to!string(p.invokingState)); 253 } 254 } 255 else { 256 int ruleIndex = p.getRuleIndex; 257 string ruleName = ruleIndex >= 0 && ruleIndex < ruleNames.length ? ruleNames[ruleIndex] : to!string(ruleIndex); 258 buf.put(ruleName); 259 } 260 if (p.parent !is null && (ruleNames.length || !p.parent.isEmpty)) { 261 buf.put(" "); 262 } 263 p = p.parent; 264 } 265 buf.put("]"); 266 return buf.data; 267 } 268 269 public RuleContext getParent() 270 { 271 return parent; 272 } 273 274 } 275 276 version(unittest) { 277 import dshould : be, equal, not, should; 278 import unit_threaded; 279 280 class Test { 281 @Tags("ruleCont", "reg") 282 @("simpleRuleContext") 283 unittest { 284 auto rcp = new RuleContext(null, -1); 285 auto rc = new RuleContext(rcp, -1); 286 rc.should.not.be(null); 287 rcp.depth.should.equal(1); 288 rc.isEmpty.should.equal(true); 289 rc.parent.isEmpty.should.equal(true); 290 rc.getParent.isEmpty.should.equal(true); 291 rc.depth.should.equal(2); 292 rc.toString.should.equal("[]"); 293 rc.toStringTree.should.equal("[]"); 294 rc.getAltNumber.should.equal(0); 295 } 296 297 @Tags("ruleContVoc", "reg") 298 @("ruleContextWithVocabulary") 299 unittest { 300 class RuleContextT : RuleContext { 301 public this(RuleContext parent, int invokingState) 302 { 303 super(parent, invokingState); 304 } 305 306 override public int getRuleIndex() 307 { 308 return invokingState; 309 } 310 } 311 auto rcp = new RuleContext(null, 1); 312 auto rc = new RuleContext(rcp, 0); 313 rc.should.not.be(null); 314 rc.toString(["A1", "B1"]).should.equal("[-1 -1]"); 315 316 rcp = new RuleContextT(null, 1); 317 rc = new RuleContextT(rcp, 0); 318 rc.should.not.be(null); 319 rc.toString(["A1", "B1"]).should.equal("[A1 B1]"); 320 } 321 } 322 }