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.ParserRuleContext; 8 9 import antlr.v4.runtime.InterfaceParser; 10 import antlr.v4.runtime.RecognitionException; 11 import antlr.v4.runtime.RuleContext; 12 import antlr.v4.runtime.Token; 13 import antlr.v4.runtime.misc; 14 import antlr.v4.runtime.tree.ErrorNode; 15 import antlr.v4.runtime.tree.ErrorNodeImpl; 16 import antlr.v4.runtime.tree.ParseTree; 17 import antlr.v4.runtime.tree.ParseTreeListener; 18 import antlr.v4.runtime.tree.TerminalNode; 19 import antlr.v4.runtime.tree.TerminalNodeImpl; 20 import std.algorithm; 21 import std.conv; 22 import std.format; 23 import std.variant; 24 25 /** 26 * A rule invocation record for parsing. 27 * 28 * Contains all of the information about the current rule not stored in the 29 * RuleContext. It handles parse tree children list, Any ATN state 30 * tracing, and the default values available for rule invocations: 31 * start, stop, rule index, current alt number. 32 * 33 * Subclasses made for each rule and grammar track the parameters, 34 * return values, locals, and labels specific to that rule. These 35 * are the objects that are returned from rules. 36 * 37 * Note text is not an actual field of a rule return value; it is computed 38 * from start and stop using the input stream's toString() method. I 39 * could add a ctor to this so that we can pass in and store the input 40 * stream, but I'm not sure we want to do that. It would seem to be undefined 41 * to get the .text property anyway if the rule matches tokens from multiple 42 * input streams. 43 * 44 * I do not use getters for fields of objects that are used simply to 45 * group values such as this aggregate. The getters/setters are there to 46 * satisfy the superclass interface. 47 */ 48 class ParserRuleContext : RuleContext 49 { 50 51 /** 52 * @uml 53 * @__gshared 54 */ 55 public static __gshared ParserRuleContext EMPTY = new ParserRuleContext; 56 57 /** 58 * If we are debugging or building a parse tree for a visitor, 59 * we need to track all of the tokens and rule invocations associated 60 * with this rule's context. This is empty for parsing w/o tree constr. 61 * operation because we don't the need to track the details about 62 * how we parse this rule. 63 */ 64 public ParseTree[] children; 65 66 public Token start; 67 68 public Token stop; 69 70 /** 71 * The exception that forced this rule to return. If the rule successfully 72 * completed, this is {@code null}. 73 */ 74 public RecognitionException exception; 75 76 public this() 77 { 78 } 79 80 public this(ParserRuleContext parent, int invokingStateNumber) 81 { 82 super(parent, invokingStateNumber); 83 } 84 85 /** 86 * COPY a ctx (I'm deliberately not using copy constructor) to avoid 87 * confusion with creating node with parent. Does not copy children 88 * (except error leaves). 89 * 90 * This is used in the generated parser code to flip a generic XContext 91 * node for rule X to a YContext for alt label Y. In that sense, it is 92 * not really a generic copy function. 93 * 94 * If we do an error sync() at start of a rule, we might add error nodes 95 * to the generic XContext so this function must copy those nodes to 96 * the YContext as well else they are lost! 97 */ 98 public void copyFrom(ParserRuleContext ctx) 99 { 100 this.parent = ctx.parent; 101 this.invokingState = ctx.invokingState; 102 103 this.start = ctx.start; 104 this.stop = ctx.stop; 105 // copy any error nodes to alt label node 106 if (ctx.children) { 107 this.children.length = 0; 108 // reset parent pointer for any error nodes 109 foreach (ParseTree child; ctx.children) { 110 if (cast(ErrorNode)child) { 111 addChild(cast(ErrorNode)child); 112 } 113 } 114 } 115 } 116 117 public void enterRule(ParseTreeListener listener) 118 { 119 } 120 121 public void exitRule(ParseTreeListener listener) 122 { 123 } 124 125 /** 126 * Add a parse tree node to this as a child. Works for 127 * internal and leaf nodes. Does not set parent link; 128 * other add methods must do that. Other addChild methods 129 * call this. 130 * 131 * We cannot set the parent pointer of the incoming node 132 * because the existing interfaces do not have a setParent() 133 * method and I don't want to break backward compatibility for this. 134 * 135 * @since 4.7 136 */ 137 public ParseTree addAnyChild(ParseTree t) 138 { 139 if (children is null) { 140 ParseTree[] newChildren; 141 children = newChildren; 142 } 143 children ~= t; 144 return t; 145 } 146 147 public RuleContext addChild(RuleContext ruleInvocation) 148 { 149 return cast(RuleContext)addAnyChild(ruleInvocation); 150 } 151 152 /** 153 * Add a token leaf node child and force its parent to be this node. 154 */ 155 public TerminalNode addChild(TerminalNode t) 156 { 157 t.setParent(this); 158 return cast(TerminalNode)addAnyChild(t); 159 } 160 161 /** 162 * Used by enterOuterAlt to toss out a RuleContext previously added as 163 * we entered a rule. If we have # label, we will need to remove 164 * generic ruleContext object. 165 */ 166 public void removeLastChild() 167 { 168 if (children !is null) { 169 children.length--; 170 } 171 } 172 173 public TerminalNode addChild(Token matchedToken) 174 { 175 TerminalNodeImpl t = new TerminalNodeImpl(matchedToken); 176 addChild(t); 177 t.parent = this; 178 return t; 179 } 180 181 public ErrorNode addErrorNode(Token badToken) 182 { 183 ErrorNodeImpl t = new ErrorNodeImpl(badToken); 184 addChild(t); 185 t.parent = this; 186 return t; 187 } 188 189 /** 190 * Override to make type more specific 191 * @uml 192 * @override 193 */ 194 public override ParserRuleContext getParent() 195 { 196 return cast(ParserRuleContext)super.getParent(); 197 } 198 199 /** 200 * @uml 201 * @override 202 */ 203 public override ParseTree getChild(int i) 204 { 205 return children && i >= 0 && 206 i < to!int(children.length) ? children[i] : null; 207 } 208 209 public auto getChild(T)(int i) 210 { 211 if (children is null || i < 0 || i >= children.length) { 212 return null; 213 } 214 215 int j = -1; // what element have we found with ctxType? 216 foreach (o; children) { 217 if (cast(T)o) { 218 j++; 219 if (j == i) { 220 return cast(T)o; 221 } 222 } 223 } 224 return null; 225 } 226 227 public TerminalNode getToken(int ttype, int i) 228 { 229 if (children is null || i < 0 || i >= children.length) { 230 return null; 231 } 232 233 int j = -1; // what token with ttype have we found? 234 foreach (o; children) { 235 if (cast(TerminalNode)o) { 236 TerminalNode tnode = cast(TerminalNode)o; 237 Token symbol = tnode.getSymbol; 238 if (symbol.getType == ttype) { 239 j++; 240 if ( j == i ) { 241 return tnode; 242 } 243 } 244 } 245 } 246 return null; 247 } 248 249 public TerminalNode[] getTokens(int ttype) 250 { 251 TerminalNode[] emptyList; 252 if (children is null) { 253 return emptyList; 254 } 255 256 TerminalNode[] tokens = null; 257 foreach (o; children) { 258 if (cast(TerminalNode)o) { 259 TerminalNode tnode = cast(TerminalNode)o; 260 Token symbol = tnode.getSymbol; 261 if (symbol.getType == ttype) { 262 if (tokens is null) { 263 tokens.length = 0; 264 } 265 tokens ~= tnode; 266 } 267 } 268 } 269 270 if (tokens is null) { 271 return emptyList; 272 } 273 return tokens; 274 } 275 276 public T getRuleContext(T)(int i) 277 { 278 return getChild!T(i); 279 } 280 281 public T[] getRuleContexts(T)() 282 { 283 if (children is null) { 284 T[] l; 285 return l; 286 } 287 T[] contexts = null; 288 foreach (o; children) { 289 if (cast(T)o) { 290 contexts ~= cast(T)o; 291 } 292 } 293 294 if (contexts is null) { 295 T[] l; 296 return l; 297 } 298 return contexts; 299 } 300 301 /** 302 * @uml 303 * @override 304 */ 305 public override int getChildCount() 306 { 307 return children ? to!int(children.length) : 0; 308 } 309 310 /** 311 * @uml 312 * @override 313 */ 314 public override Interval getSourceInterval() 315 { 316 if (start is null) { 317 return cast(Interval)Interval.INVALID; 318 } 319 if (stop is null || stop.getTokenIndex()<start.getTokenIndex()) { 320 return Interval.of(to!int(start.getTokenIndex), to!int(start.getTokenIndex)-1); // empty 321 } 322 return Interval.of(to!int(start.getTokenIndex), to!int(stop.getTokenIndex)); 323 } 324 325 /** 326 * Get the initial token in this context. 327 * Note that the range from start to stop is inclusive, so for rules that do not consume anything 328 * (for example, zero length or error productions) this token may exceed stop. 329 */ 330 public Token getStart() 331 { 332 return start; 333 } 334 335 /** 336 * Get the final token in this context. 337 * Note that the range from start to stop is inclusive, so for rules that do not consume anything 338 * (for example, zero length or error productions) this token may precede start. 339 */ 340 public Token getStop() 341 { 342 return stop; 343 } 344 345 /** 346 * Used for rule context info debugging during parse-time, not so much for ATN debugging 347 */ 348 public string toInfoString(InterfaceParser recognizer) 349 { 350 string[] rules = recognizer.getRuleInvocationStack(this); 351 rules.reverse(); 352 return format("ParserRuleContext{ %1$s " ~ 353 "start=%2$s, stop=%3$s}", rules, 354 start.getText, stop.getText); 355 } 356 357 } 358 359 version (AntlrUnittest) 360 { 361 import dshould; 362 363 @("emptyInstanceParserRuleContext") 364 unittest 365 { 366 auto rpc = ParserRuleContext.EMPTY; 367 rpc.should.not.be(null); 368 rpc.getChildCount.should.equal(0); 369 auto rpc1 = ParserRuleContext.EMPTY; 370 rpc1.should.not.be(null); 371 rpc1.should.be(rpc); 372 rpc.getStart.should.equal(null); 373 rpc.getStop.should.equal(null); 374 rpc.getSourceInterval.toString.should.equal("-1..-2"); 375 rpc.getParent.should.be(null); 376 } 377 }