|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from io import StringIO |
|
from functools import reduce |
|
from antlr4.PredictionContext import PredictionContext, merge |
|
from antlr4.Utils import str_list |
|
from antlr4.atn.ATN import ATN |
|
from antlr4.atn.ATNConfig import ATNConfig |
|
from antlr4.atn.SemanticContext import SemanticContext |
|
from antlr4.error.Errors import UnsupportedOperationException, IllegalStateException |
|
|
|
ATNSimulator = None |
|
|
|
class ATNConfigSet(object): |
|
__slots__ = ( |
|
'configLookup', 'fullCtx', 'readonly', 'configs', 'uniqueAlt', |
|
'conflictingAlts', 'hasSemanticContext', 'dipsIntoOuterContext', |
|
'cachedHashCode' |
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, fullCtx:bool=True): |
|
|
|
|
|
self.configLookup = dict() |
|
|
|
|
|
|
|
self.fullCtx = fullCtx |
|
|
|
|
|
|
|
|
|
|
|
self.readonly = False |
|
|
|
self.configs = [] |
|
|
|
|
|
|
|
self.uniqueAlt = 0 |
|
self.conflictingAlts = None |
|
|
|
|
|
|
|
self.hasSemanticContext = False |
|
self.dipsIntoOuterContext = False |
|
|
|
self.cachedHashCode = -1 |
|
|
|
def __iter__(self): |
|
return self.configs.__iter__() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def add(self, config:ATNConfig, mergeCache=None): |
|
if self.readonly: |
|
raise Exception("This set is readonly") |
|
if config.semanticContext is not SemanticContext.NONE: |
|
self.hasSemanticContext = True |
|
if config.reachesIntoOuterContext > 0: |
|
self.dipsIntoOuterContext = True |
|
existing = self.getOrAdd(config) |
|
if existing is config: |
|
self.cachedHashCode = -1 |
|
self.configs.append(config) |
|
return True |
|
|
|
rootIsWildcard = not self.fullCtx |
|
merged = merge(existing.context, config.context, rootIsWildcard, mergeCache) |
|
|
|
|
|
|
|
existing.reachesIntoOuterContext = max(existing.reachesIntoOuterContext, config.reachesIntoOuterContext) |
|
|
|
if config.precedenceFilterSuppressed: |
|
existing.precedenceFilterSuppressed = True |
|
existing.context = merged |
|
return True |
|
|
|
def getOrAdd(self, config:ATNConfig): |
|
h = config.hashCodeForConfigSet() |
|
l = self.configLookup.get(h, None) |
|
if l is not None: |
|
r = next((cfg for cfg in l if config.equalsForConfigSet(cfg)), None) |
|
if r is not None: |
|
return r |
|
if l is None: |
|
l = [config] |
|
self.configLookup[h] = l |
|
else: |
|
l.append(config) |
|
return config |
|
|
|
def getStates(self): |
|
return set(c.state for c in self.configs) |
|
|
|
def getPredicates(self): |
|
return list(cfg.semanticContext for cfg in self.configs if cfg.semanticContext!=SemanticContext.NONE) |
|
|
|
def get(self, i:int): |
|
return self.configs[i] |
|
|
|
def optimizeConfigs(self, interpreter:ATNSimulator): |
|
if self.readonly: |
|
raise IllegalStateException("This set is readonly") |
|
if len(self.configs)==0: |
|
return |
|
for config in self.configs: |
|
config.context = interpreter.getCachedContext(config.context) |
|
|
|
def addAll(self, coll:list): |
|
for c in coll: |
|
self.add(c) |
|
return False |
|
|
|
def __eq__(self, other): |
|
if self is other: |
|
return True |
|
elif not isinstance(other, ATNConfigSet): |
|
return False |
|
|
|
same = self.configs is not None and \ |
|
self.configs==other.configs and \ |
|
self.fullCtx == other.fullCtx and \ |
|
self.uniqueAlt == other.uniqueAlt and \ |
|
self.conflictingAlts == other.conflictingAlts and \ |
|
self.hasSemanticContext == other.hasSemanticContext and \ |
|
self.dipsIntoOuterContext == other.dipsIntoOuterContext |
|
|
|
return same |
|
|
|
def __hash__(self): |
|
if self.readonly: |
|
if self.cachedHashCode == -1: |
|
self.cachedHashCode = self.hashConfigs() |
|
return self.cachedHashCode |
|
return self.hashConfigs() |
|
|
|
def hashConfigs(self): |
|
return reduce(lambda h, cfg: hash((h, cfg)), self.configs, 0) |
|
|
|
def __len__(self): |
|
return len(self.configs) |
|
|
|
def isEmpty(self): |
|
return len(self.configs)==0 |
|
|
|
def __contains__(self, config): |
|
if self.configLookup is None: |
|
raise UnsupportedOperationException("This method is not implemented for readonly sets.") |
|
h = config.hashCodeForConfigSet() |
|
l = self.configLookup.get(h, None) |
|
if l is not None: |
|
for c in l: |
|
if config.equalsForConfigSet(c): |
|
return True |
|
return False |
|
|
|
def clear(self): |
|
if self.readonly: |
|
raise IllegalStateException("This set is readonly") |
|
self.configs.clear() |
|
self.cachedHashCode = -1 |
|
self.configLookup.clear() |
|
|
|
def setReadonly(self, readonly:bool): |
|
self.readonly = readonly |
|
self.configLookup = None |
|
|
|
def __str__(self): |
|
with StringIO() as buf: |
|
buf.write(str_list(self.configs)) |
|
if self.hasSemanticContext: |
|
buf.write(",hasSemanticContext=") |
|
buf.write(str(self.hasSemanticContext)) |
|
if self.uniqueAlt!=ATN.INVALID_ALT_NUMBER: |
|
buf.write(",uniqueAlt=") |
|
buf.write(str(self.uniqueAlt)) |
|
if self.conflictingAlts is not None: |
|
buf.write(",conflictingAlts=") |
|
buf.write(str(self.conflictingAlts)) |
|
if self.dipsIntoOuterContext: |
|
buf.write(",dipsIntoOuterContext") |
|
return buf.getvalue() |
|
|
|
|
|
class OrderedATNConfigSet(ATNConfigSet): |
|
|
|
def __init__(self): |
|
super().__init__() |
|
|