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 }