1 /*
2  * Copyright (c) 2012-2019 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.atn.ArrayPredictionContext;
8 
9 import antlr.v4.runtime.atn.PredictionContext;
10 import antlr.v4.runtime.atn.SingletonPredictionContext;
11 import std.array;
12 import std.container.array;
13 import std.conv;
14 import std.stdio;
15 
16 /**
17  * Array of prediction contexts
18  */
19 class ArrayPredictionContext : PredictionContext
20 {
21 
22     /**
23      * Parent can be null only if full ctx mode and we make an array
24      * from {@link #EMPTY} and non-empty. We merge {@link #EMPTY} by using null parent and
25      * returnState == {@link #EMPTY_RETURN_STATE}.
26      */
27     public PredictionContext[] parents;
28 
29     /**
30      * Sorted for merge, no duplicates; if present,
31      * {@link #EMPTY_RETURN_STATE} is always last.
32      * @uml
33      * @final
34      */
35     public int[] returnStates;
36 
37     public this(SingletonPredictionContext a)
38     {
39         PredictionContext[] parents;
40         parents ~= a.parent;
41         int[] returnStates;
42         returnStates ~= a.returnState;
43         this(parents, returnStates);
44     }
45 
46     public this(PredictionContext[] parents, int[] returnStates)
47     {
48         super(calculateHashCode(parents, returnStates)); // caching the hash code
49         assert(parents && parents.length > 0);
50         assert(returnStates && returnStates.length > 0);
51         debug {
52             import std.stdio;
53             writefln("CREATE ARRAY: %s, %s", parents, returnStates);
54         }
55         this.parents = parents;
56         this.returnStates = returnStates;
57     }
58 
59     /**
60      * @uml
61      * @override
62      */
63     public override bool isEmpty()
64     {
65         // since EMPTY_RETURN_STATE can only appear in the last position, we
66         // don't need to verify that size==1
67         return returnStates[0] == EMPTY_RETURN_STATE;
68     }
69 
70     /**
71      * @uml
72      * @override
73      */
74     public override size_t size()
75     {
76         return returnStates.length;
77     }
78 
79     /**
80      * @uml
81      * @override
82      */
83     public override PredictionContext getParent(int index)
84     {
85         return parents[index];
86     }
87 
88     /**
89      * @uml
90      * @override
91      */
92     public override int getReturnState(int index)
93     {
94         return returnStates[index];
95     }
96 
97     /**
98      * @uml
99      * @override
100      */
101     public override bool opEquals(Object o)
102     {
103         if (!cast(ArrayPredictionContext)o) {
104             return false;
105         }
106 
107         if (this.toHash != o.toHash) {
108             return false; // can't be same if hash is different
109         }
110         auto aObject = cast(ArrayPredictionContext)o;
111         import std.algorithm.comparison : equal;
112         return equal(parents, aObject.parents) &&
113             equal(returnStates, aObject.returnStates);
114     }
115 
116     /**
117      * @uml
118      * @override
119      */
120     public override string toString()
121     {
122         if (isEmpty)
123             return "[]";
124         string[] buf;
125         foreach (i, el; returnStates) {
126             if (el == EMPTY_RETURN_STATE) {
127                 buf ~= "$";
128                 continue;
129             }
130             buf ~= to!string(el);
131             if (parents[i]) {
132                 buf ~= " " ~ parents[i].toString;
133             }
134             else {
135                 buf ~= "null";
136             }
137         }
138         return "[" ~ join(buf, ", ") ~ "]";
139     }
140 
141 }
142 
143 version(unittest) {
144     import dshould : be, equal, not, should;
145     import unit_threaded;
146 
147     class Test {
148 
149         @Tags("ArrayPredictionContext")
150         @("Empty")
151         unittest {
152             import antlr.v4.runtime.atn.EmptyPredictionContext;
153             auto spc = new EmptyPredictionContext;
154             auto apc = new ArrayPredictionContext(spc);
155             ArrayPredictionContext apcp;
156             apc.toString.should.equal("[]");
157             apc.size.should.equal(1);
158             apc.isEmpty.should.equal(true);
159             static if (size_t.sizeof == 4)
160                 apc.toHash.should.equal(786443632U);
161             else
162                 apc.toHash.should.equal(6723470047294944096UL);
163             apc.getParent(0).should.be(null);
164             apc.getReturnState(0).should.equal(PredictionContext.EMPTY_RETURN_STATE);
165             auto apc1 = new ArrayPredictionContext(spc);
166             apcp = apc;
167             apc.should.equal(apcp);
168             class A {}
169             auto apc2 = new A;
170             apc.should.not.equal(apc2);
171             apc.should.equal(apc1);
172         }
173 
174         @Tags("ArrayPredictionContext")
175         @("Flat")
176         unittest {
177             import antlr.v4.runtime.atn.EmptyPredictionContext;
178             auto spc = new EmptyPredictionContext;
179             auto apc = new ArrayPredictionContext(spc);
180             apc.returnStates = 12 ~ apc.returnStates;
181             apc.toString.should.equal("[12, null, $]");
182             apc.size.should.equal(2);
183             apc.isEmpty.should.equal(false);
184             apc.getParent(0).should.be(null);
185             static if (size_t.sizeof == 4)
186                 apc.toHash.should.equal(786443632U);
187             else
188                 apc.toHash.should.equal(6723470047294944096UL);
189             static if (size_t.sizeof == 4)
190                 apc.calculateHashCode(apc.getParent(0), apc.getReturnState(0))
191                     .should.equal(182986417U);
192             else
193                 apc.calculateHashCode(apc.getParent(0), apc.getReturnState(0))
194                     .should.equal(4292457832056041856UL);
195             apc.getReturnState(0).should.equal(12);
196             auto apc1 = new ArrayPredictionContext(spc);
197             apc.should.not.equal(apc1);
198         }
199 
200         @Tags("ArrayPredictionContext")
201         @("Deep")
202         unittest {
203             import antlr.v4.runtime.atn.EmptyPredictionContext;
204             auto spc = new EmptyPredictionContext;
205             auto apc = new ArrayPredictionContext(spc);
206             auto apc1 = new ArrayPredictionContext(spc);
207             apc.returnStates = 12 ~ apc.returnStates;
208             apc.toString.should.equal("[12, null, $]");
209             apc.size.should.equal(2);
210             apc.isEmpty.should.equal(false);
211             apc.getParent(0).should.be(null);
212             apc.getReturnState(0).should.equal(12);
213             apc.parents[0] = apc1;
214             auto apc2 = new ArrayPredictionContext(apc.parents, apc.returnStates);
215             apc2.toString.should.equal("[12,  [], $]");
216             apc.toString.should.equal("[12,  [], $]");
217             apc.should.not.equal(apc2);
218             apc2.parents = apc1 ~ apc2.parents;
219             apc2.returnStates = 13 ~ apc2.returnStates;
220             apc2.toString.should.equal("[13,  [], 12,  [], $]");
221         }
222     }
223 }