// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. // Use of this file is governed by the BSD 3-clause license that // can be found in the LICENSE.txt file in the project root. package antlr import ( "fmt" "golang.org/x/exp/slices" "strconv" ) var _emptyPredictionContextHash int func init() { _emptyPredictionContextHash = murmurInit(1) _emptyPredictionContextHash = murmurFinish(_emptyPredictionContextHash, 0) } func calculateEmptyHash() int { return _emptyPredictionContextHash } const ( // BasePredictionContextEmptyReturnState represents {@code $} in an array in full context mode, $ // doesn't mean wildcard: // // $ + x = [$,x] // // Here, // // $ = EmptyReturnState BasePredictionContextEmptyReturnState = 0x7FFFFFFF ) // TODO: JI These are meant to be atomics - this does not seem to match the Java runtime here // //goland:noinspection GoUnusedGlobalVariable var ( BasePredictionContextglobalNodeCount = 1 BasePredictionContextid = BasePredictionContextglobalNodeCount ) const ( PredictionContextEmpty = iota PredictionContextSingleton PredictionContextArray ) // PredictionContext is a go idiomatic implementation of PredictionContext that does not rty to // emulate inheritance from Java, and can be used without an interface definition. An interface // is not required because no user code will ever need to implement this interface. type PredictionContext struct { cachedHash int pcType int parentCtx *PredictionContext returnState int parents []*PredictionContext returnStates []int } func NewEmptyPredictionContext() *PredictionContext { nep := &PredictionContext{} nep.cachedHash = calculateEmptyHash() nep.pcType = PredictionContextEmpty nep.returnState = BasePredictionContextEmptyReturnState return nep } func NewBaseSingletonPredictionContext(parent *PredictionContext, returnState int) *PredictionContext { pc := &PredictionContext{} pc.pcType = PredictionContextSingleton pc.returnState = returnState pc.parentCtx = parent if parent != nil { pc.cachedHash = calculateHash(parent, returnState) } else { pc.cachedHash = calculateEmptyHash() } return pc } func SingletonBasePredictionContextCreate(parent *PredictionContext, returnState int) *PredictionContext { if returnState == BasePredictionContextEmptyReturnState && parent == nil { // someone can pass in the bits of an array ctx that mean $ return BasePredictionContextEMPTY } return NewBaseSingletonPredictionContext(parent, returnState) } func NewArrayPredictionContext(parents []*PredictionContext, returnStates []int) *PredictionContext { // Parent can be nil only if full ctx mode and we make an array // from {@link //EMPTY} and non-empty. We merge {@link //EMPTY} by using // nil parent and // returnState == {@link //EmptyReturnState}. hash := murmurInit(1) for _, parent := range parents { hash = murmurUpdate(hash, parent.Hash()) } for _, returnState := range returnStates { hash = murmurUpdate(hash, returnState) } hash = murmurFinish(hash, len(parents)<<1) nec := &PredictionContext{} nec.cachedHash = hash nec.pcType = PredictionContextArray nec.parents = parents nec.returnStates = returnStates return nec } func (p *PredictionContext) Hash() int { return p.cachedHash } func (p *PredictionContext) Equals(other Collectable[*PredictionContext]) bool { switch p.pcType { case PredictionContextEmpty: otherP := other.(*PredictionContext) return other == nil || otherP == nil || otherP.isEmpty() case PredictionContextSingleton: return p.SingletonEquals(other) case PredictionContextArray: return p.ArrayEquals(other) } return false } func (p *PredictionContext) ArrayEquals(o Collectable[*PredictionContext]) bool { if o == nil { return false } other := o.(*PredictionContext) if other == nil || other.pcType != PredictionContextArray { return false } if p.cachedHash != other.Hash() { return false // can't be same if hash is different } // Must compare the actual array elements and not just the array address // return slices.Equal(p.returnStates, other.returnStates) && slices.EqualFunc(p.parents, other.parents, func(x, y *PredictionContext) bool { return x.Equals(y) }) } func (p *PredictionContext) SingletonEquals(other Collectable[*PredictionContext]) bool { if other == nil { return false } otherP := other.(*PredictionContext) if otherP == nil { return false } if p.cachedHash != otherP.Hash() { return false // Can't be same if hash is different } if p.returnState != otherP.getReturnState(0) { return false } // Both parents must be nil if one is if p.parentCtx == nil { return otherP.parentCtx == nil } return p.parentCtx.Equals(otherP.parentCtx) } func (p *PredictionContext) GetParent(i int) *PredictionContext { switch p.pcType { case PredictionContextEmpty: return nil case PredictionContextSingleton: return p.parentCtx case PredictionContextArray: return p.parents[i] } return nil } func (p *PredictionContext) getReturnState(i int) int { switch p.pcType { case PredictionContextArray: return p.returnStates[i] default: return p.returnState } } func (p *PredictionContext) GetReturnStates() []int { switch p.pcType { case PredictionContextArray: return p.returnStates default: return []int{p.returnState} } } func (p *PredictionContext) length() int { switch p.pcType { case PredictionContextArray: return len(p.returnStates) default: return 1 } } func (p *PredictionContext) hasEmptyPath() bool { switch p.pcType { case PredictionContextSingleton: return p.returnState == BasePredictionContextEmptyReturnState } return p.getReturnState(p.length()-1) == BasePredictionContextEmptyReturnState } func (p *PredictionContext) String() string { switch p.pcType { case PredictionContextEmpty: return "$" case PredictionContextSingleton: var up string if p.parentCtx == nil { up = "" } else { up = p.parentCtx.String() } if len(up) == 0 { if p.returnState == BasePredictionContextEmptyReturnState { return "$" } return strconv.Itoa(p.returnState) } return strconv.Itoa(p.returnState) + " " + up case PredictionContextArray: if p.isEmpty() { return "[]" } s := "[" for i := 0; i < len(p.returnStates); i++ { if i > 0 { s = s + ", " } if p.returnStates[i] == BasePredictionContextEmptyReturnState { s = s + "$" continue } s = s + strconv.Itoa(p.returnStates[i]) if !p.parents[i].isEmpty() { s = s + " " + p.parents[i].String() } else { s = s + "nil" } } return s + "]" default: return "unknown" } } func (p *PredictionContext) isEmpty() bool { switch p.pcType { case PredictionContextEmpty: return true case PredictionContextArray: // since EmptyReturnState can only appear in the last position, we // don't need to verify that size==1 return p.returnStates[0] == BasePredictionContextEmptyReturnState default: return false } } func (p *PredictionContext) Type() int { return p.pcType } func calculateHash(parent *PredictionContext, returnState int) int { h := murmurInit(1) h = murmurUpdate(h, parent.Hash()) h = murmurUpdate(h, returnState) return murmurFinish(h, 2) } // Convert a {@link RuleContext} tree to a {@link BasePredictionContext} graph. // Return {@link //EMPTY} if {@code outerContext} is empty or nil. // / func predictionContextFromRuleContext(a *ATN, outerContext RuleContext) *PredictionContext { if outerContext == nil { outerContext = ParserRuleContextEmpty } // if we are in RuleContext of start rule, s, then BasePredictionContext // is EMPTY. Nobody called us. (if we are empty, return empty) if outerContext.GetParent() == nil || outerContext == ParserRuleContextEmpty { return BasePredictionContextEMPTY } // If we have a parent, convert it to a BasePredictionContext graph parent := predictionContextFromRuleContext(a, outerContext.GetParent().(RuleContext)) state := a.states[outerContext.GetInvokingState()] transition := state.GetTransitions()[0] return SingletonBasePredictionContextCreate(parent, transition.(*RuleTransition).followState.GetStateNumber()) } func merge(a, b *PredictionContext, rootIsWildcard bool, mergeCache *JPCMap) *PredictionContext { // Share same graph if both same // if a == b || a.Equals(b) { return a } if a.pcType == PredictionContextSingleton && b.pcType == PredictionContextSingleton { return mergeSingletons(a, b, rootIsWildcard, mergeCache) } // At least one of a or b is array // If one is $ and rootIsWildcard, return $ as wildcard if rootIsWildcard { if a.isEmpty() { return a } if b.isEmpty() { return b } } // Convert either Singleton or Empty to arrays, so that we can merge them // ara := convertToArray(a) arb := convertToArray(b) return mergeArrays(ara, arb, rootIsWildcard, mergeCache) } func convertToArray(pc *PredictionContext) *PredictionContext { switch pc.Type() { case PredictionContextEmpty: return NewArrayPredictionContext([]*PredictionContext{}, []int{}) case PredictionContextSingleton: return NewArrayPredictionContext([]*PredictionContext{pc.GetParent(0)}, []int{pc.getReturnState(0)}) default: // Already an array } return pc } // mergeSingletons merges two Singleton [PredictionContext] instances. // // Stack tops equal, parents merge is same return left graph. //
// //Same stack top, parents differ merge parents giving array node, then
// remainders of those graphs. A new root node is created to point to the
// merged parents.
//
Different stack tops pointing to same parent. Make array node for the
// root where both element in the root point to the same (original)
// parent.
//
Different stack tops pointing to different parents. Make array node for
// the root where each element points to the corresponding original
// parent.
//
These local-context merge operations are used when {@code rootIsWildcard} // is true.
// //{@link //EMPTY} is superset of any graph return {@link //EMPTY}.
//
{@link //EMPTY} and anything is {@code //EMPTY}, so merged parent is
// {@code //EMPTY} return left graph.
//
Special case of last merge if local context.
//
These full-context merge operations are used when {@code rootIsWildcard} // is false.
// // // //Must keep all contexts {@link //EMPTY} in array is a special value (and
// nil parent).
//
Different tops, different parents.
//
Shared top, same parents.
//
Shared top, different parents.
//
Shared top, all shared parents.
//
Equal tops, merge parents and reduce top to
// {@link SingletonBasePredictionContext}.
//