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 std.typecons : tuple;
146     import unit_threaded;
147 
148     class Test {
149 
150         @Tags("ArrayPredictionContext")
151         @("Empty")
152         unittest {
153             import antlr.v4.runtime.atn.EmptyPredictionContext;
154             auto spc = new EmptyPredictionContext;
155             auto apc = new ArrayPredictionContext(spc);
156             ArrayPredictionContext apcp;
157             apc.toString.should.equal("[]");
158             apc.size.should.equal(1);
159             apc.isEmpty.should.equal(true);
160             static if (size_t.sizeof == 4)
161                 apc.toHash.should.equal(786443632U);
162             else
163                 apc.toHash.should.equal(6723470047294944096UL);
164             apc.getParent(0).should.be(null);
165             apc.getReturnState(0).should.equal(PredictionContext.EMPTY_RETURN_STATE);
166             auto apc1 = new ArrayPredictionContext(spc);
167             apcp = apc;
168             apc.should.equal(apcp);
169             class A {}
170             auto apc2 = new A;
171             apc.should.not.equal(apc2);
172             apc.should.equal(apc1);
173         }
174 
175         @Tags("ArrayPredictionContext")
176         @("Flat")
177         unittest {
178             import antlr.v4.runtime.atn.EmptyPredictionContext;
179             auto spc = new EmptyPredictionContext;
180             auto apc = new ArrayPredictionContext(spc);
181             apc.returnStates = 12 ~ apc.returnStates;
182             apc.toString.should.equal("[12, null, $]");
183             apc.size.should.equal(2);
184             apc.isEmpty.should.equal(false);
185             apc.getParent(0).should.be(null);
186             static if (size_t.sizeof == 4)
187                 apc.toHash.should.equal(786443632U);
188             else
189                 apc.toHash.should.equal(6723470047294944096UL);
190             static if (size_t.sizeof == 4)
191                 apc.calculateHashCode(apc.getParent(0), apc.getReturnState(0))
192                     .should.equal(182986417U);
193             else
194                 apc.calculateHashCode(apc.getParent(0), apc.getReturnState(0))
195                     .should.equal(4292457832056041856UL);
196             apc.getReturnState(0).should.equal(12);
197             auto apc1 = new ArrayPredictionContext(spc);
198             apc.should.not.equal(apc1);
199         }
200 
201         @Tags("ArrayPredictionContext")
202         @("Deep")
203         unittest {
204             import antlr.v4.runtime.atn.EmptyPredictionContext;
205             auto spc = new EmptyPredictionContext;
206             auto apc = new ArrayPredictionContext(spc);
207             auto apc1 = new ArrayPredictionContext(spc);
208             apc.returnStates = 12 ~ apc.returnStates;
209             apc.toString.should.equal("[12, null, $]");
210             apc.size.should.equal(2);
211             apc.isEmpty.should.equal(false);
212             apc.getParent(0).should.be(null);
213             apc.getReturnState(0).should.equal(12);
214             apc.parents[0] = apc1;
215             auto apc2 = new ArrayPredictionContext(apc.parents, apc.returnStates);
216             apc2.toString.should.equal("[12,  [], $]");
217             apc.toString.should.equal("[12,  [], $]");
218             apc.should.not.equal(apc2);
219             apc2.parents = apc1 ~ apc2.parents;
220             apc2.returnStates = 13 ~ apc2.returnStates;
221             apc2.toString.should.equal("[13,  [], 12,  [], $]");
222         }
223     }
224 }