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