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