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