diff --git a/runtime-testsuite/resources/org/antlr/v4/test/runtime/descriptors/Performance/DropLoopEntryBranchInLRRule_4.txt b/runtime-testsuite/resources/org/antlr/v4/test/runtime/descriptors/Performance/DropLoopEntryBranchInLRRule_4.txt index fe9248c8fd..8bf8b21bc0 100644 --- a/runtime-testsuite/resources/org/antlr/v4/test/runtime/descriptors/Performance/DropLoopEntryBranchInLRRule_4.txt +++ b/runtime-testsuite/resources/org/antlr/v4/test/runtime/descriptors/Performance/DropLoopEntryBranchInLRRule_4.txt @@ -56,4 +56,5 @@ Python3 JavaScript TypeScript PHP +Go diff --git a/runtime/Go/antlr/v4/array_prediction_context.go b/runtime/Go/antlr/v4/array_prediction_context.go index a24350200e..9ad7eceb66 100644 --- a/runtime/Go/antlr/v4/array_prediction_context.go +++ b/runtime/Go/antlr/v4/array_prediction_context.go @@ -63,7 +63,7 @@ func (a *ArrayPredictionContext) getReturnState(index int) int { // Equals is the default comparison function for ArrayPredictionContext when no specialized // implementation is needed for a collection -func (a *ArrayPredictionContext) Equals(o interface{}) bool { +func (a *ArrayPredictionContext) Equals(o Collectable[PredictionContext]) bool { if a == o { return true } @@ -76,7 +76,7 @@ func (a *ArrayPredictionContext) Equals(o interface{}) bool { } // Must compare the actual array elements and not just the array address - // + // TODO: The hash hashes in all the return states anyway, to we maybe don't need to compare them here? return slices.Equal(a.returnStates, other.returnStates) && slices.EqualFunc(a.parents, other.parents, func(x, y PredictionContext) bool { return x.Equals(y) diff --git a/runtime/Go/antlr/v4/atn_config.go b/runtime/Go/antlr/v4/atn_config.go index 5dd1205675..ecb251e278 100644 --- a/runtime/Go/antlr/v4/atn_config.go +++ b/runtime/Go/antlr/v4/atn_config.go @@ -14,33 +14,33 @@ import ( // state. The semantic context is the tree of semantic predicates encountered // before reaching an ATN state. type ATNConfig interface { - + // Equals compares this ATNConfig to another for equality Equals(o Collectable[ATNConfig]) bool - + // Hash returns the hash code for this ATNConfig for use in maps and comparisons Hash() int - + // GetState returns the ATN state associated with this configuration GetState() ATNState // GetAlt returns the alternative associated with this configuration GetAlt() int // GetSemanticContext returns the semantic context associated with this configuration GetSemanticContext() SemanticContext - + // GetContext returns the rule invocation stack associated with this configuration GetContext() PredictionContext // SetContext sets the rule invocation stack associated with this configuration SetContext(PredictionContext) - + // GetReachesIntoOuterContext returns the count of references to an outer context from this configuration GetReachesIntoOuterContext() int // SetReachesIntoOuterContext sets the count of references to an outer context from this configuration SetReachesIntoOuterContext(int) - + // String returns a string representation of the configuration String() string - + getPrecedenceFilterSuppressed() bool setPrecedenceFilterSuppressed(bool) } @@ -78,7 +78,7 @@ func NewBaseATNConfig5(state ATNState, alt int, context PredictionContext, seman if semanticContext == nil { panic("semanticContext cannot be nil") // TODO: Necessary? } - + return &BaseATNConfig{state: state, alt: alt, context: context, semanticContext: semanticContext} } @@ -108,15 +108,15 @@ func NewBaseATNConfig(c ATNConfig, state ATNState, context PredictionContext, se if semanticContext == nil { panic("semanticContext cannot be nil") // TODO: Remove this - probably put here for some bug that is now fixed } - + b := &BaseATNConfig{} b.InitBaseATNConfig(c, state, c.GetAlt(), context, semanticContext) - + return b } func (b *BaseATNConfig) InitBaseATNConfig(c ATNConfig, state ATNState, alt int, context PredictionContext, semanticContext SemanticContext) { - + b.state = state b.alt = alt b.context = context @@ -179,28 +179,28 @@ func (b *BaseATNConfig) Equals(o Collectable[ATNConfig]) bool { } else if o == nil { return false } - + var other, ok = o.(*BaseATNConfig) - + if !ok { return false } - + var equal bool - + if b.context == nil { equal = other.context == nil } else { equal = b.context.Equals(other.context) } - + var ( nums = b.state.GetStateNumber() == other.state.GetStateNumber() alts = b.alt == other.alt cons = b.semanticContext.Equals(other.semanticContext) sups = b.precedenceFilterSuppressed == other.precedenceFilterSuppressed ) - + return nums && alts && cons && sups && equal } @@ -211,7 +211,7 @@ func (b *BaseATNConfig) Hash() int { if b.context != nil { c = b.context.Hash() } - + h := murmurInit(7) h = murmurUpdate(h, b.state.GetStateNumber()) h = murmurUpdate(h, b.alt) @@ -223,19 +223,19 @@ func (b *BaseATNConfig) Hash() int { // String returns a string representation of the BaseATNConfig, usually used for debugging purposes func (b *BaseATNConfig) String() string { var s1, s2, s3 string - + if b.context != nil { s1 = ",[" + fmt.Sprint(b.context) + "]" } - + if b.semanticContext != SemanticContextNone { s2 = "," + fmt.Sprint(b.semanticContext) } - + if b.reachesIntoOuterContext > 0 { s3 = ",up=" + fmt.Sprint(b.reachesIntoOuterContext) } - + return fmt.Sprintf("(%v,%v%v%v%v)", b.state, b.alt, s1, s2, s3) } @@ -249,7 +249,7 @@ type LexerATNConfig struct { } func NewLexerATNConfig6(state ATNState, alt int, context PredictionContext) *LexerATNConfig { - + return &LexerATNConfig{ BaseATNConfig: BaseATNConfig{ state: state, @@ -274,7 +274,7 @@ func NewLexerATNConfig5(state ATNState, alt int, context PredictionContext, lexe func NewLexerATNConfig4(c *LexerATNConfig, state ATNState) *LexerATNConfig { lac := &LexerATNConfig{ - + lexerActionExecutor: c.lexerActionExecutor, passedThroughNonGreedyDecision: checkNonGreedyDecision(c, state), } @@ -340,32 +340,27 @@ func (l *LexerATNConfig) Equals(other Collectable[ATNConfig]) bool { return true } var otherT, ok = other.(*LexerATNConfig) - - if l == other { - return true - } else if !ok { + if !ok { return false } else if l.passedThroughNonGreedyDecision != otherT.passedThroughNonGreedyDecision { return false } - - var b bool - - if l.lexerActionExecutor != nil { - b = !l.lexerActionExecutor.Equals(otherT.lexerActionExecutor) - } else { - b = otherT.lexerActionExecutor != nil + + switch { + case l.lexerActionExecutor == nil && otherT.lexerActionExecutor == nil: + case l.lexerActionExecutor != nil && otherT.lexerActionExecutor != nil: + if !l.lexerActionExecutor.Equals(otherT.lexerActionExecutor) { + return false + } + default: + return false // One but not both, are nil } - - if b { - return false - } - + return l.BaseATNConfig.Equals(&otherT.BaseATNConfig) } func checkNonGreedyDecision(source *LexerATNConfig, target ATNState) bool { var ds, ok = target.(DecisionState) - + return source.passedThroughNonGreedyDecision || (ok && ds.getNonGreedy()) } diff --git a/runtime/Go/antlr/v4/atn_config_set.go b/runtime/Go/antlr/v4/atn_config_set.go index 7331cbc8d7..f6780cda1e 100644 --- a/runtime/Go/antlr/v4/atn_config_set.go +++ b/runtime/Go/antlr/v4/atn_config_set.go @@ -13,36 +13,36 @@ type ATNConfigSet interface { Equals(o Collectable[ATNConfig]) bool Add(ATNConfig, *DoubleDict) bool AddAll([]ATNConfig) bool - + GetStates() *JStore[ATNState, Comparator[ATNState]] GetPredicates() []SemanticContext GetItems() []ATNConfig - + OptimizeConfigs(interpreter *BaseATNSimulator) - + Length() int IsEmpty() bool Contains(ATNConfig) bool ContainsFast(ATNConfig) bool Clear() String() string - + HasSemanticContext() bool SetHasSemanticContext(v bool) - + ReadOnly() bool SetReadOnly(bool) - + GetConflictingAlts() *BitSet SetConflictingAlts(*BitSet) - + Alts() *BitSet - + FullContext() bool - + GetUniqueAlt() int SetUniqueAlt(int) - + GetDipsIntoOuterContext() bool SetDipsIntoOuterContext(bool) } @@ -51,45 +51,45 @@ type ATNConfigSet interface { // about its elements and can combine similar configurations using a // graph-structured stack. type BaseATNConfigSet struct { - + // TODO: Is this actually valid? JI cachedHash int - + // configLookup is used to determine whether two BaseATNConfigSets are equal. We // need all configurations with the same (s, i, _, semctx) to be equal. A key // effectively doubles the number of objects associated with ATNConfigs. All // keys are hashed by (s, i, _, pi), not including the context. Wiped out when // read-only because a set becomes a DFA state. configLookup *JStore[ATNConfig, Comparator[ATNConfig]] - + // configs is the added elements. configs []ATNConfig - + // TODO: These fields make me pretty uncomfortable, but it is nice to pack up // info together because it saves re-computation. Can we track conflicts as they // are added to save scanning configs later? conflictingAlts *BitSet - + // dipsIntoOuterContext is used by parsers and lexers. In a lexer, it indicates // we hit a pred while computing a closure operation. Do not make a DFA state // from the BaseATNConfigSet in this case. TODO: How is this used by parsers? dipsIntoOuterContext bool - + // fullCtx is whether it is part of a full context LL prediction. Used to // determine how to merge $. It is a wildcard with SLL, but not for an LL // context merge. fullCtx bool - + // Used in parser and lexer. In lexer, it indicates we hit a pred // while computing a closure operation. Don't make a DFA state from this set. hasSemanticContext bool - + // readOnly is whether it is read-only. Do not // allow any code to manipulate the set if true because DFA states will point at // sets and those must not change. It not, protect other fields; conflictingAlts // in particular, which is assigned after readOnly. readOnly bool - + // TODO: These fields make me pretty uncomfortable, but it is nice to pack up // info together because it saves re-computation. Can we track conflicts as they // are added to save scanning configs later? @@ -124,17 +124,17 @@ func (b *BaseATNConfigSet) Add(config ATNConfig, mergeCache *DoubleDict) bool { if b.readOnly { panic("set is read-only") } - + if config.GetSemanticContext() != SemanticContextNone { b.hasSemanticContext = true } - + if config.GetReachesIntoOuterContext() > 0 { b.dipsIntoOuterContext = true } - + existing, present := b.configLookup.Put(config) - + // The config was not already in the set // if !present { @@ -142,38 +142,38 @@ func (b *BaseATNConfigSet) Add(config ATNConfig, mergeCache *DoubleDict) bool { b.configs = append(b.configs, config) // Track order here return true } - + // Merge a previous (s, i, pi, _) with it and save the result rootIsWildcard := !b.fullCtx merged := merge(existing.GetContext(), config.GetContext(), rootIsWildcard, mergeCache) - + // No need to check for existing.context because config.context is in the cache, // since the only way to create new graphs is the "call rule" and here. We cache // at both places. existing.SetReachesIntoOuterContext(intMax(existing.GetReachesIntoOuterContext(), config.GetReachesIntoOuterContext())) - + // Preserve the precedence filter suppression during the merge if config.getPrecedenceFilterSuppressed() { existing.setPrecedenceFilterSuppressed(true) } - + // Replace the context because there is no need to do alt mapping existing.SetContext(merged) - + return true } // GetStates returns the set of states represented by all configurations in this config set func (b *BaseATNConfigSet) GetStates() *JStore[ATNState, Comparator[ATNState]] { - + // states uses the standard comparator and Hash() provided by the ATNState instance // states := NewJStore[ATNState, Comparator[ATNState]](aStateEqInst) - + for i := 0; i < len(b.configs); i++ { states.Put(b.configs[i].GetState()) } - + return states } @@ -189,15 +189,15 @@ func (b *BaseATNConfigSet) SetHasSemanticContext(v bool) { func (b *BaseATNConfigSet) GetPredicates() []SemanticContext { predicates := make([]SemanticContext, 0) - + for i := 0; i < len(b.configs); i++ { c := b.configs[i].GetSemanticContext() - + if c != SemanticContextNone { predicates = append(predicates, c) } } - + return predicates } @@ -209,14 +209,14 @@ func (b *BaseATNConfigSet) OptimizeConfigs(interpreter *BaseATNSimulator) { if b.readOnly { panic("set is read-only") } - + if b.configLookup.Len() == 0 { return } - + for i := 0; i < len(b.configs); i++ { config := b.configs[i] - + config.SetContext(interpreter.getCachedContext(config.GetContext())) } } @@ -225,36 +225,27 @@ func (b *BaseATNConfigSet) AddAll(coll []ATNConfig) bool { for i := 0; i < len(coll); i++ { b.Add(coll[i], nil) } - + return false } // Compare is a hack function - O(n squared) at worst - just to verify that adding [DFA] states to the known // set works, so long as comparison of [ATNConfigSet] works. For that to work, we -// need to make sure that the set of ATNConfigs in two sets are equivalent. We can't -// know the order, so we do this inefficient hack. If this proves the point, then -// we can change the config set to a better structure, where w e can perhaps order or hash the set of states +// need to make sure that the set of ATNConfigs in two sets are equivalent. The configs are +// only equal if they are in the same order too as Java uses ArrayList.equals(), which requires +// the same order. // // TODO: JI - Look to change the way config set is implemented. Improve data structure if possible func (b *BaseATNConfigSet) Compare(bs *BaseATNConfigSet) bool { if len(b.configs) != len(bs.configs) { return false } - - for _, c := range b.configs { - found := false - for _, c2 := range bs.configs { - if c.Equals(c2) { - found = true - break - } - } - - if !found { + for i := 0; i < len(b.configs); i++ { + if !b.configs[i].Equals(bs.configs[i]) { return false } - } + return true } @@ -264,13 +255,19 @@ func (b *BaseATNConfigSet) Equals(other Collectable[ATNConfig]) bool { } else if _, ok := other.(*BaseATNConfigSet); !ok { return false } - + other2 := other.(*BaseATNConfigSet) - + var eca bool + switch { + case b.conflictingAlts == nil && other2.conflictingAlts == nil: + eca = true + case b.conflictingAlts != nil && other2.conflictingAlts != nil: + eca = b.conflictingAlts.equals(other2.conflictingAlts) + } return b.configs != nil && b.fullCtx == other2.fullCtx && b.uniqueAlt == other2.uniqueAlt && - b.conflictingAlts == other2.conflictingAlts && + eca && b.hasSemanticContext == other2.hasSemanticContext && b.dipsIntoOuterContext == other2.dipsIntoOuterContext && b.Compare(other2) @@ -281,10 +278,10 @@ func (b *BaseATNConfigSet) Hash() int { if b.cachedHash == -1 { b.cachedHash = b.hashCodeConfigs() } - + return b.cachedHash } - + return b.hashCodeConfigs() } @@ -308,7 +305,7 @@ func (b *BaseATNConfigSet) Contains(item ATNConfig) bool { if b.configLookup == nil { panic("not implemented for read-only sets") } - + return b.configLookup.Contains(item) } @@ -316,7 +313,7 @@ func (b *BaseATNConfigSet) ContainsFast(item ATNConfig) bool { if b.configLookup == nil { panic("not implemented for read-only sets") } - + return b.configLookup.Contains(item) // TODO: containsFast is not implemented for Set } @@ -324,7 +321,7 @@ func (b *BaseATNConfigSet) Clear() { if b.readOnly { panic("set is read-only") } - + b.configs = make([]ATNConfig, 0) b.cachedHash = -1 b.configLookup = NewJStore[ATNConfig, Comparator[ATNConfig]](atnConfCompInst) @@ -364,7 +361,7 @@ func (b *BaseATNConfigSet) ReadOnly() bool { func (b *BaseATNConfigSet) SetReadOnly(readOnly bool) { b.readOnly = readOnly - + if readOnly { b.configLookup = nil // Read only, so no need for the lookup cache } @@ -372,33 +369,33 @@ func (b *BaseATNConfigSet) SetReadOnly(readOnly bool) { func (b *BaseATNConfigSet) String() string { s := "[" - + for i, c := range b.configs { s += c.String() - + if i != len(b.configs)-1 { s += ", " } } - + s += "]" - + if b.hasSemanticContext { s += ",hasSemanticContext=" + fmt.Sprint(b.hasSemanticContext) } - + if b.uniqueAlt != ATNInvalidAltNumber { s += ",uniqueAlt=" + fmt.Sprint(b.uniqueAlt) } - + if b.conflictingAlts != nil { s += ",conflictingAlts=" + b.conflictingAlts.String() } - + if b.dipsIntoOuterContext { s += ",dipsIntoOuterContext" } - + return s } @@ -408,9 +405,9 @@ type OrderedATNConfigSet struct { func NewOrderedATNConfigSet() *OrderedATNConfigSet { b := NewBaseATNConfigSet(false) - + // This set uses the standard Hash() and Equals() from ATNConfig b.configLookup = NewJStore[ATNConfig, Comparator[ATNConfig]](aConfEqInst) - + return &OrderedATNConfigSet{BaseATNConfigSet: b} } diff --git a/runtime/Go/antlr/v4/atn_simulator.go b/runtime/Go/antlr/v4/atn_simulator.go index dbb60ed1e4..38facd56df 100644 --- a/runtime/Go/antlr/v4/atn_simulator.go +++ b/runtime/Go/antlr/v4/atn_simulator.go @@ -23,7 +23,7 @@ func (b *BaseATNSimulator) getCachedContext(context PredictionContext) Predictio return context } - visited := make(map[PredictionContext]PredictionContext) + visited := NewJStore[PredictionContext, Comparator[PredictionContext]](pContextEqInst) return getCachedBasePredictionContext(context, b.sharedContextCache, visited) } diff --git a/runtime/Go/antlr/v4/base_prediction_context.go b/runtime/Go/antlr/v4/base_prediction_context.go index 58c19de28f..bbca66e40e 100644 --- a/runtime/Go/antlr/v4/base_prediction_context.go +++ b/runtime/Go/antlr/v4/base_prediction_context.go @@ -12,7 +12,7 @@ func (b *BasePredictionContext) Hash() int { return b.cachedHash } -func (b *BasePredictionContext) Equals(i interface{}) bool { +func (b *BasePredictionContext) Equals(_ Collectable[PredictionContext]) bool { return false } diff --git a/runtime/Go/antlr/v4/comparators.go b/runtime/Go/antlr/v4/comparators.go index 867cbf4e6d..96cb7b06f7 100644 --- a/runtime/Go/antlr/v4/comparators.go +++ b/runtime/Go/antlr/v4/comparators.go @@ -29,6 +29,7 @@ var ( dfaStateEqInst = &ObjEqComparator[*DFAState]{} semctxEqInst = &ObjEqComparator[SemanticContext]{} atnAltCfgEqInst = &ATNAltConfigComparator[ATNConfig]{} + pContextEqInst = &ObjEqComparator[PredictionContext]{} ) // Equals2 delegates to the Equals() method of type T @@ -68,15 +69,21 @@ func (c *ATNConfigComparator[T]) Equals2(o1, o2 ATNConfig) bool { return o1.GetState().GetStateNumber() == o2.GetState().GetStateNumber() && o1.GetAlt() == o2.GetAlt() && - o1.GetSemanticContext().Equals(o2.GetSemanticContext()) + o1.GetContext().Equals(o2.GetContext()) && + o1.GetSemanticContext().Equals(o2.GetSemanticContext()) && + o1.getPrecedenceFilterSuppressed() == o2.getPrecedenceFilterSuppressed() } // Hash1 is custom hash implementation for ATNConfigs specifically for configLookup func (c *ATNConfigComparator[T]) Hash1(o ATNConfig) int { - hash := 7 - hash = 31*hash + o.GetState().GetStateNumber() - hash = 31*hash + o.GetAlt() - hash = 31*hash + o.GetSemanticContext().Hash() + + hash := murmurInit(7) + hash = murmurUpdate(hash, o.GetState().GetStateNumber()) + hash = murmurUpdate(hash, o.GetAlt()) + hash = murmurUpdate(hash, o.GetContext().Hash()) + hash = murmurUpdate(hash, o.GetSemanticContext().Hash()) + hash = murmurFinish(hash, 4) + return hash } diff --git a/runtime/Go/antlr/v4/empty_prediction_context.go b/runtime/Go/antlr/v4/empty_prediction_context.go index 58ab8ba487..c4d336275e 100644 --- a/runtime/Go/antlr/v4/empty_prediction_context.go +++ b/runtime/Go/antlr/v4/empty_prediction_context.go @@ -47,7 +47,7 @@ func (e *EmptyPredictionContext) Hash() int { return e.cachedHash } -func (e *EmptyPredictionContext) Equals(other interface{}) bool { +func (e *EmptyPredictionContext) Equals(other Collectable[PredictionContext]) bool { return e == other } diff --git a/runtime/Go/antlr/v4/jcollect.go b/runtime/Go/antlr/v4/jcollect.go index 6f426ebd0a..137a287280 100644 --- a/runtime/Go/antlr/v4/jcollect.go +++ b/runtime/Go/antlr/v4/jcollect.go @@ -38,11 +38,11 @@ type JStore[T any, C Comparator[T]] struct { } func NewJStore[T any, C Comparator[T]](comparator Comparator[T]) *JStore[T, C] { - + if comparator == nil { panic("comparator cannot be nil") } - + s := &JStore[T, C]{ store: make(map[int][]T, 1), comparator: comparator, @@ -62,9 +62,9 @@ func NewJStore[T any, C Comparator[T]](comparator Comparator[T]) *JStore[T, C] { // // If the given value is not present in the store, then the value is added to the store and returned as v and exists is set to false. func (s *JStore[T, C]) Put(value T) (v T, exists bool) { - + kh := s.comparator.Hash1(value) - + for _, v1 := range s.store[kh] { if s.comparator.Equals2(value, v1) { return v1, true @@ -79,9 +79,9 @@ func (s *JStore[T, C]) Put(value T) (v T, exists bool) { // which would not generally be useful, but this is a specific thing for ANTLR where the key is // generated using the object we are going to store. func (s *JStore[T, C]) Get(key T) (T, bool) { - + kh := s.comparator.Hash1(key) - + for _, v := range s.store[kh] { if s.comparator.Equals2(key, v) { return v, true @@ -92,7 +92,7 @@ func (s *JStore[T, C]) Get(key T) (T, bool) { // Contains returns true if the given key is present in the store func (s *JStore[T, C]) Contains(key T) bool { - + _, present := s.Get(key) return present } @@ -105,7 +105,7 @@ func (s *JStore[T, C]) SortedSlice(less func(i, j T) bool) []T { sort.Slice(vs, func(i, j int) bool { return less(vs[i], vs[j]) }) - + return vs } @@ -151,7 +151,7 @@ func NewJMap[K, V any, C Comparator[K]](comparator Comparator[K]) *JMap[K, V, C] func (m *JMap[K, V, C]) Put(key K, val V) { kh := m.comparator.Hash1(key) - + m.store[kh] = append(m.store[kh], &entry[K, V]{key, val}) m.len++ } @@ -167,7 +167,7 @@ func (m *JMap[K, V, C]) Values() []V { } func (m *JMap[K, V, C]) Get(key K) (V, bool) { - + var none V kh := m.comparator.Hash1(key) for _, e := range m.store[kh] { diff --git a/runtime/Go/antlr/v4/lexer_action.go b/runtime/Go/antlr/v4/lexer_action.go index 878855c9ab..249afdb37e 100644 --- a/runtime/Go/antlr/v4/lexer_action.go +++ b/runtime/Go/antlr/v4/lexer_action.go @@ -9,25 +9,25 @@ import "strconv" const ( // LexerActionTypeChannel represents a [LexerChannelAction] action. LexerActionTypeChannel = 0 - + // LexerActionTypeCustom represents a [LexerCustomAction] action. LexerActionTypeCustom = 1 - + // LexerActionTypeMode represents a [LexerModeAction] action. LexerActionTypeMode = 2 - + // LexerActionTypeMore represents a [LexerMoreAction] action. LexerActionTypeMore = 3 - + // LexerActionTypePopMode represents a [LexerPopModeAction] action. LexerActionTypePopMode = 4 - + // LexerActionTypePushMode represents a [LexerPushModeAction] action. LexerActionTypePushMode = 5 - + // LexerActionTypeSkip represents a [LexerSkipAction] action. LexerActionTypeSkip = 6 - + // LexerActionTypeType represents a [LexerTypeAction] action. LexerActionTypeType = 7 ) @@ -47,10 +47,10 @@ type BaseLexerAction struct { func NewBaseLexerAction(action int) *BaseLexerAction { la := new(BaseLexerAction) - + la.actionType = action la.isPositionDependent = false - + return la } @@ -67,11 +67,13 @@ func (b *BaseLexerAction) getIsPositionDependent() bool { } func (b *BaseLexerAction) Hash() int { - return b.actionType + h := murmurInit(0) + h = murmurUpdate(h, b.actionType) + return murmurFinish(h, 1) } func (b *BaseLexerAction) Equals(other LexerAction) bool { - return b == other + return b.actionType == other.getActionType() } // LexerSkipAction implements the [BaseLexerAction.Skip] lexer action by calling [Lexer.Skip]. @@ -100,12 +102,16 @@ func (l *LexerSkipAction) String() string { return "skip" } +func (b *LexerSkipAction) Equals(other LexerAction) bool { + return other.getActionType() == LexerActionTypeSkip +} + // Implements the {@code type} lexer action by calling {@link Lexer//setType} // // with the assigned type. type LexerTypeAction struct { *BaseLexerAction - + thetype int } @@ -149,10 +155,10 @@ type LexerPushModeAction struct { } func NewLexerPushModeAction(mode int) *LexerPushModeAction { - + l := new(LexerPushModeAction) l.BaseLexerAction = NewBaseLexerAction(LexerActionTypePushMode) - + l.mode = mode return l } @@ -193,11 +199,11 @@ type LexerPopModeAction struct { } func NewLexerPopModeAction() *LexerPopModeAction { - + l := new(LexerPopModeAction) - + l.BaseLexerAction = NewBaseLexerAction(LexerActionTypePopMode) - + return l } @@ -224,7 +230,7 @@ type LexerMoreAction struct { func NewLexerMoreAction() *LexerMoreAction { l := new(LexerMoreAction) l.BaseLexerAction = NewBaseLexerAction(LexerActionTypeMore) - + return l } @@ -409,14 +415,14 @@ type LexerIndexedCustomAction struct { // the token start index, at which the specified lexerAction should be // executed. func NewLexerIndexedCustomAction(offset int, lexerAction LexerAction) *LexerIndexedCustomAction { - + l := new(LexerIndexedCustomAction) l.BaseLexerAction = NewBaseLexerAction(lexerAction.getActionType()) - + l.offset = offset l.lexerAction = lexerAction l.isPositionDependent = true - + return l } diff --git a/runtime/Go/antlr/v4/lexer_action_executor.go b/runtime/Go/antlr/v4/lexer_action_executor.go index 05024a8e1b..f7fcfc8c75 100644 --- a/runtime/Go/antlr/v4/lexer_action_executor.go +++ b/runtime/Go/antlr/v4/lexer_action_executor.go @@ -19,33 +19,35 @@ type LexerActionExecutor struct { } func NewLexerActionExecutor(lexerActions []LexerAction) *LexerActionExecutor { - + if lexerActions == nil { lexerActions = make([]LexerAction, 0) } - + l := new(LexerActionExecutor) - + l.lexerActions = lexerActions - + // Caches the result of {@link //hashCode} since the hash code is an element // of the performance-critical {@link LexerATNConfig//hashCode} operation. - l.cachedHash = murmurInit(57) + l.cachedHash = murmurInit(0) for _, a := range lexerActions { l.cachedHash = murmurUpdate(l.cachedHash, a.Hash()) } - + l.cachedHash = murmurFinish(l.cachedHash, len(lexerActions)) + return l } // LexerActionExecutorappend creates a [LexerActionExecutor] which executes the actions for // the input [LexerActionExecutor] followed by a specified // [LexerAction]. +// TODO: This does not match the Java code func LexerActionExecutorappend(lexerActionExecutor *LexerActionExecutor, lexerAction LexerAction) *LexerActionExecutor { if lexerActionExecutor == nil { return NewLexerActionExecutor([]LexerAction{lexerAction}) } - + return NewLexerActionExecutor(append(lexerActionExecutor.lexerActions, lexerAction)) } @@ -84,19 +86,19 @@ func (l *LexerActionExecutor) fixOffsetBeforeMatch(offset int) *LexerActionExecu if l.lexerActions[i].getIsPositionDependent() && !ok { if updatedLexerActions == nil { updatedLexerActions = make([]LexerAction, 0) - + for _, a := range l.lexerActions { updatedLexerActions = append(updatedLexerActions, a) } } - + updatedLexerActions[i] = NewLexerIndexedCustomAction(offset, l.lexerActions[i]) } } if updatedLexerActions == nil { return l } - + return NewLexerActionExecutor(updatedLexerActions) } @@ -121,13 +123,13 @@ func (l *LexerActionExecutor) fixOffsetBeforeMatch(offset int) *LexerActionExecu func (l *LexerActionExecutor) execute(lexer Lexer, input CharStream, startIndex int) { requiresSeek := false stopIndex := input.Index() - + defer func() { if requiresSeek { input.Seek(stopIndex) } }() - + for i := 0; i < len(l.lexerActions); i++ { lexerAction := l.lexerActions[i] if la, ok := lexerAction.(*LexerIndexedCustomAction); ok { @@ -148,7 +150,7 @@ func (l *LexerActionExecutor) Hash() int { // TODO: Why is this here? l should not be nil return 61 } - + // TODO: This is created from the action itself when the struct is created - will this be an issue at some point? Java uses the runtime assign hashcode return l.cachedHash } diff --git a/runtime/Go/antlr/v4/prediction_context.go b/runtime/Go/antlr/v4/prediction_context.go index 1ed15bc06a..b472d08447 100644 --- a/runtime/Go/antlr/v4/prediction_context.go +++ b/runtime/Go/antlr/v4/prediction_context.go @@ -11,7 +11,7 @@ import ( // PredictionContext defines the interface that must be implemented by any flavor of prediction context. type PredictionContext interface { Hash() int - Equals(interface{}) bool + Equals(collectable Collectable[PredictionContext]) bool GetParent(int) PredictionContext getReturnState(int) int length() int @@ -53,7 +53,6 @@ func calculateHash(parent PredictionContext, returnState int) int { return murmurFinish(h, 2) } - // Convert a {@link RuleContext} tree to a {@link BasePredictionContext} graph. // Return {@link //EMPTY} if {@code outerContext} is empty or nil. // / @@ -449,25 +448,25 @@ func combineCommonParents(parents []PredictionContext) { } } -func getCachedBasePredictionContext(context PredictionContext, contextCache *PredictionContextCache, visited map[PredictionContext]PredictionContext) PredictionContext { +func getCachedBasePredictionContext(context PredictionContext, contextCache *PredictionContextCache, visited *JStore[PredictionContext, Comparator[PredictionContext]]) PredictionContext { if context.isEmpty() { return context } - existing := visited[context] - if existing != nil { + existing, present := visited.Get(context) + if present { return existing } - existing = contextCache.Get(context) - if existing != nil { - visited[context] = existing + existing, present = contextCache.Get(context) + if present { + _, _ = visited.Put(existing) return existing } changed := false parents := make([]PredictionContext, context.length()) for i := 0; i < len(parents); i++ { parent := getCachedBasePredictionContext(context.GetParent(i), contextCache, visited) - if changed || parent != context.GetParent(i) { + if changed || !parent.Equals(context.GetParent(i)) { if !changed { parents = make([]PredictionContext, context.length()) for j := 0; j < context.length(); j++ { @@ -480,7 +479,7 @@ func getCachedBasePredictionContext(context PredictionContext, contextCache *Pre } if !changed { contextCache.add(context) - visited[context] = context + _, _ = visited.Put(context) return context } var updated PredictionContext @@ -492,8 +491,8 @@ func getCachedBasePredictionContext(context PredictionContext, contextCache *Pre updated = NewArrayPredictionContext(parents, context.(*ArrayPredictionContext).GetReturnStates()) } contextCache.add(updated) - visited[updated] = updated - visited[context] = updated + visited.Put(updated) + visited.Put(context) return updated } diff --git a/runtime/Go/antlr/v4/prediction_context_cache.go b/runtime/Go/antlr/v4/prediction_context_cache.go index 30c9556509..d2520566a9 100644 --- a/runtime/Go/antlr/v4/prediction_context_cache.go +++ b/runtime/Go/antlr/v4/prediction_context_cache.go @@ -6,13 +6,14 @@ var BasePredictionContextEMPTY = NewEmptyPredictionContext() // context cash associated with contexts in DFA states. This cache // can be used for both lexers and parsers. type PredictionContextCache struct { - cache map[PredictionContext]PredictionContext + //cache map[PredictionContext]PredictionContext + cache *JStore[PredictionContext, Comparator[PredictionContext]] } func NewPredictionContextCache() *PredictionContextCache { - t := new(PredictionContextCache) - t.cache = make(map[PredictionContext]PredictionContext) - return t + return &PredictionContextCache{ + cache: NewJStore[PredictionContext, Comparator[PredictionContext]](pContextEqInst), + } } // Add a context to the cache and return it. If the context already exists, @@ -22,18 +23,19 @@ func (p *PredictionContextCache) add(ctx PredictionContext) PredictionContext { if ctx.isEmpty() { return BasePredictionContextEMPTY } - existing := p.cache[ctx] - if existing != nil { - return existing - } - p.cache[ctx] = ctx - return ctx + + // Put will return the existing entry if it is present (note this is done via Equals, not whether it is + // the same pointer), otherwise it will add the new entry and return that. + // + pc, _ := p.cache.Put(ctx) + return pc } -func (p *PredictionContextCache) Get(ctx PredictionContext) PredictionContext { - return p.cache[ctx] +func (p *PredictionContextCache) Get(ctx PredictionContext) (PredictionContext, bool) { + pc, exists := p.cache.Get(ctx) + return pc, exists } func (p *PredictionContextCache) length() int { - return len(p.cache) + return p.cache.Len() } diff --git a/runtime/Go/antlr/v4/singleton_prediction_context.go b/runtime/Go/antlr/v4/singleton_prediction_context.go index 49206cb542..15d4a1644e 100644 --- a/runtime/Go/antlr/v4/singleton_prediction_context.go +++ b/runtime/Go/antlr/v4/singleton_prediction_context.go @@ -57,7 +57,7 @@ func (b *BaseSingletonPredictionContext) Hash() int { return b.cachedHash } -func (b *BaseSingletonPredictionContext) Equals(other interface{}) bool { +func (b *BaseSingletonPredictionContext) Equals(other Collectable[PredictionContext]) bool { if b == other { return true }