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