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 // Class RuleContext
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 new Interval(-1, -2); // 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 string getText()
139     {
140         if (getChildCount() == 0) {
141             return "";
142         }
143         auto builder = appender!(string);
144         for (int i = 0; i < getChildCount(); i++) {
145             builder.put(getChild(i).getText());
146         }
147         return builder.data;
148     }
149 
150     public int getRuleIndex()
151     {
152         return -1;
153     }
154 
155     /**
156      * For rule associated with this parse tree internal node, return
157      * the outer alternative number used to match the input. Default
158      * implementation does not compute nor store this alt num. Create
159      * a subclass of ParserRuleContext with backing field and set
160      * option contextSuperClass.
161      * to set it.
162      */
163     public int getAltNumber()
164     {
165         return ATN.INVALID_ALT_NUMBER;
166     }
167 
168     /**
169      * Set the outer alternative number for this context node. Default
170      * implementation does nothing to avoid backing field overhead for
171      * trees that don't need it.  Create
172      * a subclass of ParserRuleContext with backing field and set
173      * option contextSuperClass.
174      */
175     public void setAltNumber(int altNumber)
176     {
177     }
178 
179     public ParseTree getChild(int i)
180     {
181         return null;
182     }
183 
184     public int getChildCount()
185     {
186         return 0;
187     }
188 
189     public U accept(U)(ParseTreeVisitor!U visitor)
190     {
191         return visitor.visitChildren(this);
192     }
193 
194     /**
195      * Print out a whole tree, not just a node, in LISP format
196      * (root child1 .. childN). Print just a node if this is a leaf.
197      * We have to know the recognizer so we can get rule names.
198      */
199     public string toStringTree(InterfaceRecognizer recog)
200     {
201         return Trees.toStringTree(cast(ParseTree)this, recog);
202     }
203 
204     public string toStringTree(string[] ruleNames)
205     {
206         return Trees.toStringTree(cast(ParseTree)this, ruleNames);
207     }
208 
209     public string toStringTree()
210     {
211         return toStringTree([]);
212     }
213 
214     /**
215      * @uml
216      * @override
217      */
218     public override string toString()
219     {
220         return toString([]);
221     }
222 
223     public string toString(InterfaceRecognizer recog)
224     {
225         return toString(recog, ParserRuleContext.EMPTY);
226     }
227 
228     public string toString(string[] ruleNames)
229     {
230         return toString(ruleNames, null);
231     }
232 
233     /**
234      * recog null unless ParserRuleContext, in which case we use subclass toString(...)
235      */
236     public string toString(InterfaceRecognizer recog, RuleContext stop)
237     {
238         string[] ruleNames = recog !is null ? recog.getRuleNames() : null;
239         string[] ruleNamesList = ruleNames !is null ? ruleNames : null;
240         return toString(ruleNamesList) ~ stop.toString;
241 
242     }
243 
244     public string toString(string[] ruleNames, RuleContext stop)
245     {
246 	auto buf = appender!(string);
247         RuleContext p = this;
248         buf.put("[");
249         while (p !is null && p != stop) {
250             if (ruleNames.length == 0) {
251                 if (!p.isEmpty) {
252                     buf.put(to!string(p.invokingState));
253                 }
254             }
255             else {
256                 int ruleIndex = p.getRuleIndex;
257                 string ruleName = ruleIndex >= 0 && ruleIndex < ruleNames.length ? ruleNames[ruleIndex] : to!string(ruleIndex);
258                 buf.put(ruleName);
259             }
260             if (p.parent !is null && (ruleNames.length || !p.parent.isEmpty)) {
261                                 buf.put(" ");
262             }
263             p = p.parent;
264         }
265         buf.put("]");
266         return buf.data;
267     }
268 
269     public RuleContext getParent()
270     {
271         return parent;
272     }
273 
274 }
275 
276 version(unittest) {
277     import dshould : be, equal, not, should;
278     import unit_threaded;
279 
280     class Test {
281         @Tags("ruleCont", "reg")
282         @("simpleRuleContext")
283         unittest {
284             auto rcp = new RuleContext(null, -1);
285             auto rc = new RuleContext(rcp, -1);
286             rc.should.not.be(null);
287             rcp.depth.should.equal(1);
288             rc.isEmpty.should.equal(true);
289             rc.parent.isEmpty.should.equal(true);
290             rc.getParent.isEmpty.should.equal(true);
291             rc.depth.should.equal(2);
292             rc.toString.should.equal("[]");
293             rc.toStringTree.should.equal("[]");
294             rc.getAltNumber.should.equal(0);
295         }
296 
297         @Tags("ruleContVoc", "reg")
298         @("ruleContextWithVocabulary")
299         unittest {
300             class RuleContextT : RuleContext {
301                 public this(RuleContext parent, int invokingState)
302                 {
303                     super(parent, invokingState);
304                 }
305 
306                 override public int getRuleIndex()
307                 {
308                     return invokingState;
309                 }
310             }
311             auto rcp = new RuleContext(null, 1);
312             auto rc = new RuleContext(rcp, 0);
313             rc.should.not.be(null);
314             rc.toString(["A1", "B1"]).should.equal("[-1 -1]");
315 
316             rcp = new RuleContextT(null, 1);
317             rc = new RuleContextT(rcp, 0);
318             rc.should.not.be(null);
319             rc.toString(["A1", "B1"]).should.equal("[A1 B1]");
320         }
321     }
322 }