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.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 // Class ArrayPredictionContext
17 /**
18  * Array of prediction contexts
19  */
20 class ArrayPredictionContext : PredictionContext
21 {
22 
23     /**
24      * Parent can be null only if full ctx mode and we make an array
25      * from {@link #EMPTY} and non-empty. We merge {@link #EMPTY} by using null parent and
26      * returnState == {@link #EMPTY_RETURN_STATE}.
27      */
28     public PredictionContext[] parents;
29 
30     /**
31      * Sorted for merge, no duplicates; if present,
32      * {@link #EMPTY_RETURN_STATE} is always last.
33      * @uml
34      * @final
35      */
36     public int[] returnStates;
37 
38     public this(SingletonPredictionContext a)
39     {
40         PredictionContext[] parents;
41         parents ~= a.parent;
42         int[] returnStates;
43         returnStates ~= a.returnState;
44         this(parents, returnStates);
45     }
46 
47     public this(PredictionContext[] parents, int[] returnStates)
48     {
49         super(calculateHashCode(parents, returnStates)); // caching the hash code
50         assert(parents && parents.length > 0);
51         assert(returnStates && returnStates.length > 0);
52         debug {
53             import std.stdio;
54             writefln("CREATE ARRAY: %s, %s", parents, returnStates);
55         }
56         this.parents = parents;
57         this.returnStates = returnStates;
58     }
59 
60     /**
61      * @uml
62      * @override
63      */
64     public override bool isEmpty()
65     {
66         // since EMPTY_RETURN_STATE can only appear in the last position, we
67         // don't need to verify that size==1
68         return returnStates[0] == EMPTY_RETURN_STATE;
69     }
70 
71     /**
72      * @uml
73      * @override
74      */
75     public override size_t size()
76     {
77         return returnStates.length;
78     }
79 
80     /**
81      * @uml
82      * @override
83      */
84     public override PredictionContext getParent(int index)
85     {
86         return parents[index];
87     }
88 
89     /**
90      * @uml
91      * @override
92      */
93     public override int getReturnState(int index)
94     {
95         return returnStates[index];
96     }
97 
98     /**
99      * @uml
100      * @override
101      */
102     public override bool opEquals(Object o)
103     {
104         if (!cast(ArrayPredictionContext)o) {
105             return false;
106         }
107 
108         if (this.toHash != o.toHash) {
109             return false; // can't be same if hash is different
110         }
111         auto aObject = cast(ArrayPredictionContext)o;
112         import std.algorithm.comparison : equal;
113         return equal(parents, aObject.parents) &&
114             equal(returnStates, aObject.returnStates);
115     }
116 
117     /**
118      * @uml
119      * @override
120      */
121     public override string toString()
122     {
123         if (isEmpty)
124             return "[]";
125         string[] buf;
126         foreach (int i, el; returnStates) {
127             if (el == EMPTY_RETURN_STATE) {
128                 buf ~= "$";
129                 continue;
130             }
131             buf ~= to!string(el);
132             if (parents[i]) {
133                 buf ~= " " ~ parents[i].toString;
134             }
135             else {
136                 buf ~= "null";
137             }
138         }
139         return "[" ~ join(buf, ", ") ~ "]";
140     }
141 
142 }
143 
144 version(unittest) {
145     import dshould : be, equal, not, should;
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 }