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.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.ParseTreeVisitor;
17 import antlr.v4.runtime.tree.RuleNode;
18 import antlr.v4.runtime.tree.Trees;
19 import std.array;
20 import std.conv : to;
21 import std.variant;
22 
23 /**
24  * A rule context is a record of a single rule invocation.
25  *
26  * We form a stack of these context objects using the parent
27  * pointer. A parent pointer of null indicates that the current
28  * context is the bottom of the stack. The ParserRuleContext subclass
29  * as a children list so that we can turn this data structure into a
30  * tree.
31  *
32  * The root node always has a null pointer and invokingState of -1.
33  *
34  * Upon entry to parsing, the first invoked rule function creates a
35  * context object (asubclass specialized for that rule such as
36  * SContext) and makes it the root of a parse tree, recorded by field
37  * Parser._ctx.
38  *
39  *  public final SContext s() throws RecognitionException {
40  *      SContext _localctx = new SContext(_ctx, getState()); <-- create new node
41  *      enterRule(_localctx, 0, RULE_s);                     <-- push it
42  *      ...
43  *      exitRule();                                          <-- pop back to _localctx
44  *      return _localctx;
45  *  }
46  *
47  * A subsequent rule invocation of r from the start rule s pushes a
48  * new context object for r whose parent points at s and use invoking
49  * state is the state with r emanating as edge label.
50  *
51  * The invokingState fields from a context object to the root
52  * together form a stack of rule indication states where the root
53  * (bottom of the stack) has a -1 sentinel value. If we invoke start
54  * symbol s then call r1, which calls r2, the  would look like
55  *  this:
56  *
57  *     SContext[-1]   <- root node (bottom of the stack)
58  *     R1Context[p]   <- p in rule s called r1
59  *     R2Context[q]   <- q in rule r1 called r2
60  *
61  * So the top of the stack, _ctx, represents a call to the current
62  * rule and it holds the return address from another rule that invoke
63  * to this rule. To invoke a rule, we must always have a current context.
64  *
65  * The parent contexts are useful for computing lookahead sets and
66  * getting error information.
67  *
68  * These objects are used during parsing and prediction.
69  * For the special case of parsers, we use the subclass
70  * ParserRuleContext.
71  *
72  *  @see ParserRuleContext
73  */
74 class RuleContext : RuleNode, InterfaceRuleContext
75 {
76 
77     public static const ParserRuleContext EMPTY = new ParserRuleContext();
78 
79     public RuleContext parent;
80 
81     public int invokingState = -1;
82 
83     public this()
84     {
85     }
86 
87     public this(RuleContext parent, int invokingState)
88     {
89         this.parent = parent;
90         this.invokingState = invokingState;
91     }
92 
93     public int depth()
94     {
95         int n = 0;
96         RuleContext p = this;
97         while (p) {
98             p = p.parent;
99             n++;
100         }
101         return n;
102     }
103 
104     /**
105      * A context is empty if there is no invoking state; meaning nobody called
106      * current context.
107      */
108     public bool isEmpty()
109     {
110         return invokingState == -1;
111     }
112 
113     /**
114      * satisfy the ParseTree / SyntaxTree interface
115      */
116     public Interval getSourceInterval()
117     {
118         return cast(Interval)Interval.INVALID;
119     }
120 
121     public RuleContext getRuleContext()
122     {
123         return this;
124     }
125 
126     public RuleContext getPayload()
127     {
128         return this;
129     }
130 
131     /**
132      * Return the combined text of all child nodes. This method only considers
133      * tokens which have been added to the parse tree.
134      * <p>
135      * Since tokens on hidden channels (e.g. whitespace or comments) are not
136      * added to the parse trees, they will not appear in the output of this
137      * method.
138      */
139     public Variant getText()
140     {
141         if (getChildCount() == 0) {
142             Variant v = "";
143             return v;
144         }
145         auto builder = appender!(string);
146         for (int i = 0; i < getChildCount(); i++) {
147             builder.put(to!string(getChild(i).getText));
148         }
149         Variant v = builder.data;
150         return v;
151     }
152 
153     public size_t getRuleIndex()
154     {
155         return size_t.max;
156     }
157 
158     /**
159      * For rule associated with this parse tree internal node, return
160      * the outer alternative number used to match the input. Default
161      * implementation does not compute nor store this alt num. Create
162      * a subclass of ParserRuleContext with backing field and set
163      * option contextSuperClass.
164      * to set it.
165      */
166     public int getAltNumber()
167     {
168         return ATN.INVALID_ALT_NUMBER;
169     }
170 
171     /**
172      * Set the outer alternative number for this context node. Default
173      * implementation does nothing to avoid backing field overhead for
174      * trees that don't need it.  Create
175      * a subclass of ParserRuleContext with backing field and set
176      * option contextSuperClass.
177      */
178     public void setAltNumber(int altNumber)
179     {
180     }
181 
182     public ParseTree getChild(int i)
183     {
184         return null;
185     }
186 
187     public int getChildCount()
188     {
189         return 0;
190     }
191 
192     public Variant accept(ParseTreeVisitor visitor)
193     {
194         return visitor.visitChildren(this);
195     }
196 
197     /**
198      * Print out a whole tree, not just a node, in LISP format
199      * (root child1 .. childN). Print just a node if this is a leaf.
200      * We have to know the recognizer so we can get rule names.
201      */
202     public string toStringTree(InterfaceRecognizer recog)
203     {
204         return Trees.toStringTree(cast(ParseTree)this, recog);
205     }
206 
207     public string toStringTree(string[] ruleNames)
208     {
209         return Trees.toStringTree(cast(ParseTree)this, ruleNames);
210     }
211 
212     public string toStringTree()
213     {
214         return toStringTree([]);
215     }
216 
217     /**
218      * @uml
219      * @override
220      */
221     public override string toString()
222     {
223         return toString([]);
224     }
225 
226     public string toString(InterfaceRecognizer recog)
227     {
228         return toString(recog, ParserRuleContext.EMPTY);
229     }
230 
231     public string toString(string[] ruleNames)
232     {
233         return toString(ruleNames, null);
234     }
235 
236     /**
237      * recog null unless ParserRuleContext, in which case we use subclass toString(...)
238      */
239     public string toString(InterfaceRecognizer recog, RuleContext stop)
240     {
241         string[] ruleNames = recog !is null ? recog.getRuleNames() : null;
242         string[] ruleNamesList = ruleNames !is null ? ruleNames : null;
243         return toString(ruleNamesList) ~ stop.toString;
244 
245     }
246 
247     public string toString(string[] ruleNames, RuleContext stop)
248     {
249         auto buf = appender!(string);
250         RuleContext p = this;
251         buf.put("[");
252         while (p !is null && p != stop) {
253             if (ruleNames.length == 0) {
254                 if (!p.isEmpty) {
255                     buf.put(to!string(p.invokingState));
256                 }
257             }
258             else {
259                 auto ruleIndex = p.getRuleIndex;
260                 string ruleName = ruleIndex >= 0 && ruleIndex < ruleNames.length ? ruleNames[ruleIndex] : to!string(ruleIndex);
261                 buf.put(ruleName);
262             }
263             if (p.parent !is null && (ruleNames.length || !p.parent.isEmpty)) {
264                 buf.put(" ");
265             }
266             p = p.parent;
267         }
268         buf.put("]");
269         return buf.data;
270     }
271 
272     public RuleContext getParent()
273     {
274         return parent;
275     }
276 
277     /**
278      * since 4.7. {@see ParseTree#setParent} comment
279      */
280     public void setParent(RuleContext parent)
281     {
282         this.parent = parent;
283     }
284 
285 }
286 
287 version(unittest) {
288     import dshould : be, equal, not, should;
289     import std.typecons : tuple;
290     import unit_threaded;
291 
292     class Test {
293         @Tags("ruleCont", "reg")
294         @("simpleRuleContext")
295         unittest {
296             auto rcp = new RuleContext(null, -1);
297             auto rc = new RuleContext(rcp, -1);
298             rc.should.not.be(null);
299             rcp.depth.should.equal(1);
300             rc.isEmpty.should.equal(true);
301             rc.parent.isEmpty.should.equal(true);
302             rc.getParent.isEmpty.should.equal(true);
303             rc.depth.should.equal(2);
304             rc.toString.should.equal("[]");
305             rc.toStringTree.should.equal("[]");
306             rc.getAltNumber.should.equal(0);
307         }
308 
309         @Tags("ruleContVoc", "reg")
310         @("ruleContextWithVocabulary")
311         unittest {
312             class RuleContextT : RuleContext {
313                 public this(RuleContext parent, int invokingState)
314                 {
315                     super(parent, invokingState);
316                 }
317 
318                 override public size_t getRuleIndex()
319                 {
320                     return invokingState;
321                 }
322             }
323             auto rcp = new RuleContext(null, 1);
324             auto rc = new RuleContext(rcp, 0);
325             rc.should.not.be(null);
326             rc.toString(["A1", "B1"]).should.equal("[" ~ to!string(size_t.max)
327              ~ " " ~ to!string(size_t.max) ~ "]");
328 
329             rcp = new RuleContextT(null, 1);
330             rc = new RuleContextT(rcp, 0);
331             rc.should.not.be(null);
332             rc.toString(["A1", "B1"]).should.equal("[A1 B1]");
333         }
334     }
335 }