Skip to content

Commit

Permalink
fix: Fixes for antlr#3718
Browse files Browse the repository at this point in the history
  o Implement collections with generics to solve hash collisions
  o Fix type casting in LL start parser simulation optimization
  o General minor tidy ups

Acknowledgements to @kaby76 for help with tracing errors

Signed-off-by: Jim.Idle <[email protected]>
Signed-off-by: Eric Vergnaud <[email protected]>
  • Loading branch information
Jim.Idle authored and ericvergnaud committed Sep 9, 2022
1 parent 4e64674 commit ae9bc5f
Show file tree
Hide file tree
Showing 14 changed files with 386 additions and 148 deletions.
16 changes: 12 additions & 4 deletions runtime/Go/antlr/atn_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ type comparable interface {
type ATNConfig interface {
comparable

gequals(other Collectable[ATNConfig]) bool

hash() int

GetState() ATNState
Expand Down Expand Up @@ -47,7 +49,7 @@ type BaseATNConfig struct {
reachesIntoOuterContext int
}

func NewBaseATNConfig7(old *BaseATNConfig) *BaseATNConfig { // TODO: Dup
func NewBaseATNConfig7(old *BaseATNConfig) ATNConfig { // TODO: Dup
return &BaseATNConfig{
state: old.state,
alt: old.alt,
Expand Down Expand Up @@ -134,10 +136,13 @@ func (b *BaseATNConfig) GetReachesIntoOuterContext() int {
func (b *BaseATNConfig) SetReachesIntoOuterContext(v int) {
b.reachesIntoOuterContext = v
}
func (b *BaseATNConfig) equals(o interface{}) bool {
return b.gequals(o.(Collectable[ATNConfig]))
}

// An ATN configuration is equal to another if both have the same state, they
// predict the same alternative, and syntactic/semantic contexts are the same.
func (b *BaseATNConfig) equals(o interface{}) bool {
func (b *BaseATNConfig) gequals(o Collectable[ATNConfig]) bool {
if b == o {
return true
}
Expand All @@ -153,7 +158,7 @@ func (b *BaseATNConfig) equals(o interface{}) bool {
if b.context == nil {
equal = other.context == nil
} else {
equal = b.context.equals(other.context)
equal = b.context.gequals(other.context)
}

var (
Expand Down Expand Up @@ -262,6 +267,10 @@ func (l *LexerATNConfig) hash() int {
}

func (l *LexerATNConfig) equals(other interface{}) bool {
return l.gequals(other.(Collectable[ATNConfig]))
}

func (l *LexerATNConfig) gequals(other Collectable[ATNConfig]) bool {
var othert, ok = other.(*LexerATNConfig)

if l == other {
Expand All @@ -287,7 +296,6 @@ func (l *LexerATNConfig) equals(other interface{}) bool {
return l.BaseATNConfig.equals(othert.BaseATNConfig)
}


func checkNonGreedyDecision(source *LexerATNConfig, target ATNState) bool {
var ds, ok = target.(DecisionState)

Expand Down
31 changes: 29 additions & 2 deletions runtime/Go/antlr/atn_config_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,33 @@ func (b *BaseATNConfigSet) AddAll(coll []ATNConfig) bool {
return false
}

// Compare is a hack function just to verify that adding DFAstares to the known
// set works, so long as comparison of ATNConfigSet s 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.
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 {
return false
}

}
return true
}

func (b *BaseATNConfigSet) Equals(other interface{}) bool {
if b == other {
return true
Expand All @@ -224,12 +251,12 @@ func (b *BaseATNConfigSet) Equals(other interface{}) bool {
other2 := other.(*BaseATNConfigSet)

return b.configs != nil &&
// TODO: b.configs.equals(other2.configs) && // TODO: Is b necessary?
b.fullCtx == other2.fullCtx &&
b.uniqueAlt == other2.uniqueAlt &&
b.conflictingAlts == other2.conflictingAlts &&
b.hasSemanticContext == other2.hasSemanticContext &&
b.dipsIntoOuterContext == other2.dipsIntoOuterContext
b.dipsIntoOuterContext == other2.dipsIntoOuterContext &&
b.Compare(other2)
}

func (b *BaseATNConfigSet) hash() int {
Expand Down
51 changes: 14 additions & 37 deletions runtime/Go/antlr/dfa.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@

package antlr

import (
"sort"
)

type DFA struct {
// atnStartState is the ATN state in which this was created
atnStartState DecisionState

decision int

// states is all the DFA states. Use Map to get the old state back; Set can only
// indicate whether it is there.
states map[int]*DFAState
// indicate whether it is there. Go maps implement key hash collisions and so on and are very
// good, but the DFAState is an object and can't be used directly as the key as it can in say JAva
// amd C#, whereby if the hashcode is the same for two objects, then equals() is called against them
// to see if they really are the same object.
//
//
states *JStore[*DFAState, *DFAStateComparator[*DFAState]]
numstates int

s0 *DFAState

Expand All @@ -29,7 +31,7 @@ func NewDFA(atnStartState DecisionState, decision int) *DFA {
dfa := &DFA{
atnStartState: atnStartState,
decision: decision,
states: make(map[int]*DFAState),
states: NewJStore[*DFAState, *DFAStateComparator[*DFAState]](&DFAStateComparator[*DFAState]{}),
}
if s, ok := atnStartState.(*StarLoopEntryState); ok && s.precedenceRuleDecision {
dfa.precedenceDfa = true
Expand Down Expand Up @@ -92,7 +94,8 @@ func (d *DFA) getPrecedenceDfa() bool {
// true or nil otherwise, and d.precedenceDfa is updated.
func (d *DFA) setPrecedenceDfa(precedenceDfa bool) {
if d.getPrecedenceDfa() != precedenceDfa {
d.setStates(make(map[int]*DFAState))
d.states = NewJStore[*DFAState, *DFAStateComparator[*DFAState]](&DFAStateComparator[*DFAState]{})
d.numstates = 0

if precedenceDfa {
precedenceState := NewDFAState(-1, NewBaseATNConfigSet(false))
Expand All @@ -117,38 +120,12 @@ func (d *DFA) setS0(s *DFAState) {
d.s0 = s
}

func (d *DFA) getState(hash int) (*DFAState, bool) {
s, ok := d.states[hash]
return s, ok
}

func (d *DFA) setStates(states map[int]*DFAState) {
d.states = states
}

func (d *DFA) setState(hash int, state *DFAState) {
d.states[hash] = state
}

func (d *DFA) numStates() int {
return len(d.states)
}

type dfaStateList []*DFAState

func (d dfaStateList) Len() int { return len(d) }
func (d dfaStateList) Less(i, j int) bool { return d[i].stateNumber < d[j].stateNumber }
func (d dfaStateList) Swap(i, j int) { d[i], d[j] = d[j], d[i] }

// sortedStates returns the states in d sorted by their state number.
func (d *DFA) sortedStates() []*DFAState {
vs := make([]*DFAState, 0, len(d.states))

for _, v := range d.states {
vs = append(vs, v)
}

sort.Sort(dfaStateList(vs))
vs := d.states.SortedSlice(func(i, j *DFAState) bool {
return i.stateNumber < j.stateNumber
})

return vs
}
Expand Down
66 changes: 57 additions & 9 deletions runtime/Go/antlr/dfa_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,45 @@ type DFAState struct {
predicates []*PredPrediction
}

type DFAStateComparator[T Collectable[T]] struct {
}

// NB This is the same as normal DFAState
func (c *DFAStateComparator[T]) equals(o1, o2 T) bool {
return o1.gequals(o2)
}

// NB This is the same as normal DFAState
func (c *DFAStateComparator[T]) hash(o T) int {
return o.hash()
}

// equals returns true if this DFA state is equal to another DFA state.
// Two [DFAState] instances are equal if their ATN configuration sets
// are the same. This method is used to see if a state already exists.
//
// Because the number of alternatives and number of ATN configurations are
// finite, there is a finite number of DFA states that can be processed.
// This is necessary to show that the algorithm terminates.
//
// Cannot test the DFA state numbers here because in [ParserATNSimulator.addDFAState]
// we need to know if any other state exists that has this exact set of ATN configurations. The
// [stateNumber] is irrelevant.
func (d *DFAState) gequals(o Collectable[*DFAState]) bool {
if d == o {
return true
}

return d.configs.Equals(o.(*DFAState).configs)
}
func (d *DFAState) equals(o interface{}) bool {
if d == o {
return true
}

return d.configs.Equals(o.(*DFAState).configs)
}

func NewDFAState(stateNumber int, configs ATNConfigSet) *DFAState {
if configs == nil {
configs = NewBaseATNConfigSet(false)
Expand Down Expand Up @@ -141,15 +180,15 @@ func (d *DFAState) setPrediction(v int) {
// Cannot test the DFA state numbers here because in
// ParserATNSimulator.addDFAState we need to know if any other state exists that
// has d exact set of ATN configurations. The stateNumber is irrelevant.
func (d *DFAState) equals(other interface{}) bool {
if d == other {
return true
} else if _, ok := other.(*DFAState); !ok {
return false
}

return d.configs.Equals(other.(*DFAState).configs)
}
// func (d *DFAState) equals(other interface{}) bool {
// if d == other {
// return true
// } else if _, ok := other.(*DFAState); !ok {
// return false
// }
//
// return d.configs.Equals(other.(*DFAState).configs)
//}

func (d *DFAState) String() string {
var s string
Expand All @@ -169,3 +208,12 @@ func (d *DFAState) hash() int {
h = murmurUpdate(h, d.configs.hash())
return murmurFinish(h, 1)
}

func (d *DFAState) Equals(o *DFAState) bool {

if d == o {
return true
}

return d.configs.Equals(o.configs)
}
2 changes: 1 addition & 1 deletion runtime/Go/antlr/go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/antlr/antlr4/runtime/Go/antlr

go 1.16
go 1.18
Loading

0 comments on commit ae9bc5f

Please sign in to comment.