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 /**
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 !is null && 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 !is null ? 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(start.getTokenIndex(), start.getTokenIndex()-1); // empty
321         }
322         return Interval.of(start.getTokenIndex(), 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(unittest) {
360     import dshould : be, equal, not, should;
361     import unit_threaded;
362     @Tags("parserRC")
363     @("emptyInstanceParserRuleContext")
364     unittest {
365         auto rpc = ParserRuleContext.EMPTY;
366         rpc.should.not.be(null);
367         rpc.getChildCount.should.equal(0);
368         auto rpc1 = ParserRuleContext.EMPTY;
369         rpc1.should.not.be(null);
370         rpc1.should.be(rpc);
371         rpc.getStart.should.equal(null);
372         rpc.getStop.should.equal(null);
373         rpc.getSourceInterval.toString.should.equal("-1..-2");
374         rpc.getParent.should.be(null);
375     }
376 }