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.ATNConfigSet;
8 
9 import antlr.v4.runtime.IllegalStateException;
10 import antlr.v4.runtime.UnsupportedOperationException;
11 import antlr.v4.runtime.atn.ATNConfig;
12 import antlr.v4.runtime.atn.ATNState;
13 import antlr.v4.runtime.atn.AbstractConfigHashSet;
14 import antlr.v4.runtime.atn.InterfaceATNSimulator;
15 import antlr.v4.runtime.atn.PredictionContext;
16 import antlr.v4.runtime.atn.SemanticContext;
17 import antlr.v4.runtime.misc.AbstractEqualityComparator;
18 import antlr.v4.runtime.misc;
19 import std.algorithm.iteration : map, sum;
20 import std.array;
21 import std.bitmanip;
22 import std.conv;
23 import std.format;
24 
25 
26 /**
27  * Specialized {@link Set}{@code <}{@link ATNConfig}{@code >} that can track
28  * info about the set, with support for combining similar configurations using a
29  * graph-structured stack.
30  *
31  * The reason that we need this is because we don't want the hash map to use
32  * the standard hash code and equals. We need all configurations with the same
33  * {@code (s,i,_,semctx)} to be equal. Unfortunately, this key effectively doubles
34  * the number of objects associated with ATNConfigs. The other solution is to
35  * use a hash table that lets us specify the equals/hashcode operation.
36  */
37 class ATNConfigSet
38 {
39 
40     // Class ConfigHashSet
41     /**
42      * The reason that we need this is because we don't want the hash map to use
43      * the standard hash code and equals. We need all configurations with the same
44      * {@code (s,i,_,semctx)} to be equal. Unfortunately, this key effectively doubles
45      * the number of objects associated with ATNConfigs. The other solution is to
46      * use a hash table that lets us specify the equals/hashcode operation.
47      */
48     class ConfigHashSet : AbstractConfigHashSet
49     {
50 
51         public this()
52         {
53             super(&ConfigEqualityComparator.hashOf, &ConfigEqualityComparator.opEquals);
54         }
55 
56     }
57     // Singleton ConfigEqualityComparator
58     /**
59      * TODO add class description
60      */
61     static class ConfigEqualityComparator : AbstractEqualityComparator!ATNConfig
62     {
63 
64         /**
65          * The single instance of ConfigEqualityComparator.
66          */
67         private static __gshared ATNConfigSet.ConfigEqualityComparator instance_;
68 
69         /**
70          * @uml
71          * @nothrow
72          * @safe
73          */
74         public static size_t hashOf(Object obj) @safe nothrow
75         {
76             auto o = cast(ATNConfig) obj;
77             size_t hashCode = 7;
78             hashCode = 31 * hashCode + o.state.stateNumber;
79             hashCode = 31 * hashCode + o.alt;
80             if (o.semanticContext)
81                 hashCode = 31 * hashCode + o.semanticContext.toHash;
82             return hashCode;
83         }
84 
85         public static bool opEquals(Object aObj, Object bObj)
86         {
87             if (aObj is null || bObj is null)
88                 return false;
89 
90             auto a = cast(ATNConfig) aObj;
91             auto b = cast(ATNConfig) bObj;
92             if (a is b)
93                 return true;
94 
95             if (a.semanticContext is b.semanticContext)
96                 return a.state.stateNumber == b.state.stateNumber
97                     && a.alt == b.alt;
98             if (a.semanticContext is null || b.semanticContext is null)
99                 return false;
100             return a.state.stateNumber == b.state.stateNumber
101                 && a.alt == b.alt
102                 && a.semanticContext.opEquals(b.semanticContext);
103         }
104 
105         /**
106          * Creates the single instance of ConfigEqualityComparator.
107          */
108         private shared static this()
109         {
110             instance_ = new ConfigEqualityComparator;
111         }
112 
113         /**
114          * Returns: A single instance of ConfigEqualityComparator.
115          */
116         public static ConfigEqualityComparator instance()
117         {
118             return instance_;
119         }
120 
121     }
122 
123     /**
124      * @uml
125      * Indicates that the set of configurations is read-only. Do not
126      * allow any code to manipulate the set; DFA states will point at
127      * the sets and they must not change. This does not protect the other
128      * fields; in particular, conflictingAlts is set after
129      * we've made this readonly.
130      * @read
131      */
132     public bool readonly_ = false;
133 
134     public AbstractConfigHashSet configLookup;
135 
136     /**
137      * @uml
138      * Track the elements as they are added to the set; supports get(i)
139      */
140     public ATNConfig[] configs;
141 
142     /**
143      * @uml
144      * TODO: these fields make me pretty uncomfortable but nice to pack up info together, saves recomputation
145      * TODO: can we track conflicts as they are added to save scanning configs later?
146      */
147     public int uniqueAlt;
148 
149     /**
150      * @uml
151      * Currently this is only used when we detect SLL conflict; this does
152      * not necessarily represent the ambiguous alternatives. In fact,
153      * I should also point out that this seems to include predicated alternatives
154      * that have predicates that evaluate to false. Computed in computeTargetState().
155      */
156     public BitSet conflictingAlts;
157 
158     /**
159      * @uml
160      * Used in parser and lexer. In lexer, it indicates we hit a pred
161      * while computing a closure operation.  Don't make a DFA state from this.
162      */
163     public bool hasSemanticContext;
164 
165     public bool dipsIntoOuterContext;
166 
167     /**
168      * @uml
169      * Indicates that this configuration set is part of a full context
170      * LL prediction. It will be used to determine how to merge $. With SLL
171      * it's a wildcard whereas it is not for LL context merge.
172      * @final
173      */
174     public bool fullCtx;
175 
176     private size_t cachedHashCode = -1;
177 
178     public this(bool fullCtx)
179     {
180         configLookup = new ConfigHashSet();
181         this.fullCtx = fullCtx;
182     }
183 
184     public this()
185     {
186         this(true);
187     }
188 
189     public this(ATNConfigSet old)
190     {
191         this(old.fullCtx);
192         addAll(old.configs);
193         this.uniqueAlt = old.uniqueAlt;
194         this.conflictingAlts = old.conflictingAlts;
195         this.hasSemanticContext = old.hasSemanticContext;
196         this.dipsIntoOuterContext = old.dipsIntoOuterContext;
197     }
198 
199     public bool add(ATNConfig config)
200     {
201         return add(config, null);
202     }
203 
204     /**
205      * Adding a new config means merging contexts with existing configs for
206      * {@code (s, i, pi, _)}, where {@code s} is the
207      * {@link ATNConfig#state}, {@code i} is the {@link ATNConfig#alt}, and
208      * {@code pi} is the {@link ATNConfig#semanticContext}. We use
209      * {@code (s,i,pi)} as key.
210      *
211      * <p>This method updates {@link #dipsIntoOuterContext} and
212      * {@link #hasSemanticContext} when necessary.</p>
213      */
214     public bool add(ATNConfig config, DoubleKeyMap!(PredictionContext, PredictionContext, PredictionContext) mergeCache)
215     {
216         import std.algorithm.comparison : max;
217 
218         if (readonly_)
219             throw new IllegalStateException("This set is readonly");
220 
221         if (config.semanticContext != SemanticContext.NONE) {
222             hasSemanticContext = true;
223         }
224         if (config.getOuterContextDepth > 0) {
225             dipsIntoOuterContext = true;
226         }
227 
228         ATNConfig existing = configLookup.getOrAdd(config);
229 
230         if (existing is config) { // we added this new one
231             cachedHashCode = -1;
232             configs ~= config;  // track order here
233             return true;
234         }
235 
236         // a previous (s,i,pi,_), merge with it and save result
237         bool rootIsWildcard = !fullCtx;
238 
239         PredictionContext merged =
240             PredictionContext.merge(existing.context, config.context, rootIsWildcard, mergeCache);
241         // no need to check for existing.context, config.context in cache
242         // since only way to create new graphs is "call rule" and here. We
243         // cache at both places.
244         existing.reachesIntoOuterContext =
245             max(existing.reachesIntoOuterContext, config.reachesIntoOuterContext);
246 
247         // make sure to preserve the precedence filter suppression during the merge
248         if (config.isPrecedenceFilterSuppressed()) {
249             existing.setPrecedenceFilterSuppressed(true);
250         }
251         existing.context = merged; // replace context; no need to alt mapping
252         return true;
253     }
254 
255     /**
256      * @uml
257      * Return a List holding list of configs
258      */
259     public ATNConfig[] elements()
260     {
261         return configs;
262     }
263 
264     public ATNState[] getStates()
265     {
266         ATNState[] states;
267         foreach (ATNConfig c; configs) {
268             states ~= c.state;
269         }
270         return states;
271     }
272 
273     /**
274      * @uml
275      * Gets the complete set of represented alternatives for the configuration
276      * set.
277      *
278      *  @return the set of represented alternatives in this configuration set
279      */
280     public BitSet getAlts()
281     {
282         BitSet alts;
283         foreach (ATNConfig config; configs) {
284             alts.set(config.alt, true);
285         }
286         return alts;
287     }
288 
289     public SemanticContext[] getPredicates()
290     {
291     SemanticContext[] preds;
292         foreach (ATNConfig c; configs) {
293             if (c.semanticContext != SemanticContext.NONE) {
294                 preds ~= c.semanticContext;
295             }
296         }
297         return preds;
298     }
299 
300     public ATNConfig get(int i)
301     {
302         return configs[i];
303     }
304 
305     public void optimizeConfigs(InterfaceATNSimulator interpreter)
306     {
307     if (readonly_) throw new IllegalStateException("This set is readonly");
308         if (configLookup.isEmpty) return;
309         foreach (ref ATNConfig config; configs) {
310             debug(ATNConfigSet)
311                 auto before = PredictionContext.getAllContextNodes(config.context).length;
312             config.context = interpreter.getCachedContext(config.context);
313             debug(ATNConfigSet) {
314                 auto after = PredictionContext.getAllContextNodes(config.context).length;
315                 import std.stdio;
316                 writefln("ATNConfigSet: configs %s -> %s", before, after);
317             }
318         }
319     }
320 
321     public bool addAll(ATNConfig[] coll)
322     {
323     foreach (ATNConfig c; coll) add(c);
324         return false;
325     }
326 
327     /**
328      * @uml
329      * @override
330      */
331     public override bool opEquals(Object o)
332     {
333         import std.algorithm.comparison : equal;
334 
335         if (o is this) {
336             return true;
337         }
338         else {
339             if (o.classinfo != ATNConfigSet.classinfo) {
340                 return false;
341             }
342         }
343         ATNConfigSet other = cast(ATNConfigSet)o;
344         if (other.size != this.size)
345                     return false;
346         if (equal(this.configs, other.configs) != 0) {  // check stack context
347                 return false;
348         }
349 
350         return fullCtx == other.fullCtx &&
351             uniqueAlt == other.uniqueAlt &&
352             conflictingAlts == other.conflictingAlts &&
353             hasSemanticContext == other.hasSemanticContext &&
354             dipsIntoOuterContext == other.dipsIntoOuterContext;
355     }
356 
357     public static bool equals(Object aObj, Object bObj)
358     {
359         if (aObj is bObj)
360             return true;
361         auto a = cast(ATNConfig) aObj;
362         auto b = cast(ATNConfig) bObj;
363         if (a is null || b is null)
364             return false;
365         if (a.semanticContext is b.semanticContext)
366             return a.state.stateNumber == b.state.stateNumber
367                 && a.alt == b.alt;
368         return a.state.stateNumber == b.state.stateNumber
369             && a.alt == b.alt
370             && a.semanticContext.opEquals(b.semanticContext);
371     }
372 
373     /**
374      * @uml
375      * @override
376      * @safe
377      * @nothrow
378      */
379     public override size_t toHash() @safe nothrow
380     {
381         if (readonly_) {
382             if (cachedHashCode == -1) {
383                 cachedHashCode = configs.map!(n => n.toHash).sum;
384             }
385             return cachedHashCode;
386         }
387         return configs.map!(n => n.toHash).sum;
388     }
389 
390     public int size()
391     {
392         return to!int(configs.length);
393     }
394 
395     public bool isEmpty()
396     {
397     return !configs.length;
398     }
399 
400     public bool contains(ATNConfig o)
401     {
402         if (configLookup is null) {
403             throw new UnsupportedOperationException("This method is not implemented for readonly sets.");
404         }
405 
406         return configLookup.contains(o);
407     }
408 
409     public bool containsFast(ATNConfig obj)
410     {
411         if (configLookup is null) {
412             throw new UnsupportedOperationException("This method is not implemented for readonly sets.");
413         }
414 
415         return configLookup.containsFast(obj);
416     }
417 
418     public void clear()
419     {
420         if (readonly_) throw new IllegalStateException("This set is readonly");
421         configs.length = 0;
422         cachedHashCode = -1;
423         configLookup.clear;
424     }
425 
426     public void readonly(bool readonly)
427     {
428         readonly_ = readonly;
429         configLookup = null; // can't mod, no need for lookup cache
430     }
431 
432     /**
433      * @uml
434      * @override
435      */
436     public override string toString()
437     {
438     auto buf = appender!string;
439         buf.put(to!string(elements));
440         if (hasSemanticContext) buf.put(format(",hasSemanticContext=%s", hasSemanticContext));
441         if (uniqueAlt != 0) // ATN.INVALID_ALT_NUMBER
442             buf.put(format(",uniqueAlt=%s", uniqueAlt));
443         if (!conflictingAlts.isEmpty)
444             buf.put(format(",conflictingAlts=%s", conflictingAlts));
445         if (dipsIntoOuterContext) buf.put(",dipsIntoOuterContext");
446         return buf.data;
447     }
448 
449     public final bool readonly()
450     {
451         return this.readonly_;
452     }
453 
454 }
455 
456 version (AntlrUnittest)
457 {
458     import dshould;
459 
460     @("atnConfigSetTest")
461     unittest
462     {
463         ATNConfigSet atnConfigSet = new ATNConfigSet;
464         atnConfigSet.should.not.be(null);
465         atnConfigSet.readonly.should.equal(false);
466         atnConfigSet.toString.should.equal("[]");
467         atnConfigSet.isEmpty.should.equal(true);
468         atnConfigSet.toHash.should.equal(0);
469     }
470 }