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.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 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 debug(ATNConfigSet) 312 auto before = PredictionContext.getAllContextNodes(config.context).length; 313 config.context = interpreter.getCachedContext(config.context); 314 debug(ATNConfigSet) { 315 auto after = PredictionContext.getAllContextNodes(config.context).length; 316 import std.stdio; 317 writefln("ATNConfigSet: configs %s -> %s", before, after); 318 } 319 } 320 } 321 322 public bool addAll(ATNConfig[] coll) 323 { 324 foreach (ATNConfig c; coll) add(c); 325 return false; 326 } 327 328 /** 329 * @uml 330 * @override 331 */ 332 public override bool opEquals(Object o) 333 { 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 null || bObj is null) 360 return false; 361 auto a = cast(ATNConfig) aObj; 362 auto b = cast(ATNConfig) bObj; 363 if (a is b) 364 return true; 365 if (a.semanticContext is b.semanticContext) 366 return a.state.stateNumber == b.state.stateNumber 367 && a.alt == b.alt; 368 if (a.semanticContext is null || b.semanticContext is null) 369 return false; 370 return a.state.stateNumber == b.state.stateNumber 371 && a.alt == b.alt 372 && a.semanticContext.opEquals(b.semanticContext); 373 } 374 375 /** 376 * @uml 377 * @override 378 * @safe 379 * @nothrow 380 */ 381 public override size_t toHash() @safe nothrow 382 { 383 if (readonly_) { 384 if (cachedHashCode == -1) { 385 cachedHashCode = configs.map!(n => n.toHash).sum; 386 } 387 return cachedHashCode; 388 } 389 return configs.map!(n => n.toHash).sum; 390 } 391 392 public int size() 393 { 394 return to!int(configs.length); 395 } 396 397 public bool isEmpty() 398 { 399 return !configs.length; 400 } 401 402 public bool contains(ATNConfig o) 403 { 404 if (configLookup is null) { 405 throw new UnsupportedOperationException("This method is not implemented for readonly sets."); 406 } 407 408 return configLookup.contains(o); 409 } 410 411 public bool containsFast(ATNConfig obj) 412 { 413 if (configLookup is null) { 414 throw new UnsupportedOperationException("This method is not implemented for readonly sets."); 415 } 416 417 return configLookup.containsFast(obj); 418 } 419 420 public void clear() 421 { 422 if (readonly_) throw new IllegalStateException("This set is readonly"); 423 configs.length = 0; 424 cachedHashCode = -1; 425 configLookup.clear; 426 } 427 428 public void readonly(bool readonly) 429 { 430 readonly_ = readonly; 431 configLookup = null; // can't mod, no need for lookup cache 432 } 433 434 /** 435 * @uml 436 * @override 437 */ 438 public override string toString() 439 { 440 auto buf = appender!string; 441 buf.put(to!string(elements)); 442 if (hasSemanticContext) buf.put(format(",hasSemanticContext=%s", hasSemanticContext)); 443 if (uniqueAlt != 0) // ATN.INVALID_ALT_NUMBER 444 buf.put(format(",uniqueAlt=%s", uniqueAlt)); 445 if (!conflictingAlts.isEmpty) 446 buf.put(format(",conflictingAlts=%s", conflictingAlts)); 447 if (dipsIntoOuterContext) buf.put(",dipsIntoOuterContext"); 448 return buf.data; 449 } 450 451 public final bool readonly() 452 { 453 return this.readonly_; 454 } 455 456 } 457 458 version(unittest) { 459 import dshould : be, equal, not, should; 460 import unit_threaded; 461 @Tags("atnConfigSet") 462 @("atnConfigSetTest") 463 unittest { 464 ATNConfigSet atnConfigSet = new ATNConfigSet; 465 atnConfigSet.should.not.be(null); 466 atnConfigSet.readonly.should.equal(false); 467 atnConfigSet.toString.should.equal("[]"); 468 atnConfigSet.isEmpty.should.equal(true); 469 atnConfigSet.toHash.should.equal(0); 470 } 471 }