Skip to content

Commit

Permalink
[Go] Port locking algorithm from C++ to Go (#3571)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcking committed Mar 19, 2022
1 parent 69b0200 commit 02e5f66
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 171 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,7 @@ PositionAdjustingLexerDef() ::= ""
PositionAdjustingLexer() ::= <<
func (p *PositionAdjustingLexer) NextToken() antlr.Token {
if _, ok := p.Interpreter.(*PositionAdjustingLexerATNSimulator); !ok {
lexerDeserializer := antlr.NewATNDeserializer(nil)
lexerAtn := lexerDeserializer.DeserializeFromUInt16(serializedLexerAtn)
p.Interpreter = NewPositionAdjustingLexerATNSimulator(p, lexerAtn, p.Interpreter.DecisionToDFA(), p.Interpreter.SharedContextCache())
p.Interpreter = NewPositionAdjustingLexerATNSimulator(p, p.Interpreter.ATN(), p.Interpreter.DecisionToDFA(), p.Interpreter.SharedContextCache())
p.Virt = p
}

Expand Down
21 changes: 14 additions & 7 deletions runtime/Go/antlr/atn.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

package antlr

import "sync"

var ATNInvalidAltNumber int

type ATN struct {
Expand Down Expand Up @@ -37,6 +39,10 @@ type ATN struct {
ruleToTokenType []int

states []ATNState

mu sync.Mutex
stateMu sync.RWMutex
edgeMu sync.RWMutex
}

func NewATN(grammarType int, maxTokenType int) *ATN {
Expand All @@ -59,14 +65,15 @@ func (a *ATN) NextTokensInContext(s ATNState, ctx RuleContext) *IntervalSet {
// in s and staying in same rule. Token.EPSILON is in set if we reach end of
// rule.
func (a *ATN) NextTokensNoContext(s ATNState) *IntervalSet {
if s.GetNextTokenWithinRule() != nil {
return s.GetNextTokenWithinRule()
a.mu.Lock()
defer a.mu.Unlock()
iset := s.GetNextTokenWithinRule()
if iset == nil {
iset = a.NextTokensInContext(s, nil)
iset.readOnly = true
s.SetNextTokenWithinRule(iset)
}

s.SetNextTokenWithinRule(a.NextTokensInContext(s, nil))
s.GetNextTokenWithinRule().readOnly = true

return s.GetNextTokenWithinRule()
return iset
}

func (a *ATN) NextTokens(s ATNState, ctx RuleContext) *IntervalSet {
Expand Down
6 changes: 6 additions & 0 deletions runtime/Go/antlr/atn_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ func NewBasicBlockStartState() *BasicBlockStartState {
return &BasicBlockStartState{BaseBlockStartState: b}
}

var _ BlockStartState = &BasicBlockStartState{}

// BlockEndState is a terminal node of a simple (a|b|c) block.
type BlockEndState struct {
*BaseATNState
Expand Down Expand Up @@ -318,6 +320,8 @@ func NewPlusBlockStartState() *PlusBlockStartState {
return &PlusBlockStartState{BaseBlockStartState: b}
}

var _ BlockStartState = &PlusBlockStartState{}

// StarBlockStartState is the block that begins a closure loop.
type StarBlockStartState struct {
*BaseBlockStartState
Expand All @@ -331,6 +335,8 @@ func NewStarBlockStartState() *StarBlockStartState {
return &StarBlockStartState{BaseBlockStartState: b}
}

var _ BlockStartState = &StarBlockStartState{}

type StarLoopbackState struct {
*BaseATNState
}
Expand Down
34 changes: 12 additions & 22 deletions runtime/Go/antlr/dfa.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ package antlr

import (
"sort"
"sync"
"sync/atomic"
"unsafe"
)

type DFA struct {
Expand All @@ -19,23 +16,28 @@ type DFA struct {

// 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
statesMu sync.RWMutex
states map[int]*DFAState

s0 *DFAState

// precedenceDfa is the backing field for isPrecedenceDfa and setPrecedenceDfa.
// True if the DFA is for a precedence decision and false otherwise.
precedenceDfa bool
precedenceDfaMu sync.RWMutex
precedenceDfa bool
}

func NewDFA(atnStartState DecisionState, decision int) *DFA {
return &DFA{
dfa := &DFA{
atnStartState: atnStartState,
decision: decision,
states: make(map[int]*DFAState),
}
if s, ok := atnStartState.(*StarLoopEntryState); ok && s.precedenceRuleDecision {
dfa.precedenceDfa = true
dfa.s0 = NewDFAState(-1, NewBaseATNConfigSet(false))
dfa.s0.isAcceptState = false
dfa.s0.requiresFullContext = false
}
return dfa
}

// getPrecedenceStartState gets the start state for the current precedence and
Expand Down Expand Up @@ -80,8 +82,6 @@ func (d *DFA) setPrecedenceStartState(precedence int, startState *DFAState) {
}

func (d *DFA) getPrecedenceDfa() bool {
d.precedenceDfaMu.RLock()
defer d.precedenceDfaMu.RUnlock()
return d.precedenceDfa
}

Expand All @@ -105,42 +105,32 @@ func (d *DFA) setPrecedenceDfa(precedenceDfa bool) {
d.setS0(nil)
}

d.precedenceDfaMu.Lock()
defer d.precedenceDfaMu.Unlock()
d.precedenceDfa = precedenceDfa
}
}

func (d *DFA) getS0() *DFAState {
return (*DFAState)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.s0))))
return d.s0
}

func (d *DFA) setS0(s *DFAState) {
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.s0)), unsafe.Pointer(s))
d.s0 = s
}

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

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

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

func (d *DFA) numStates() int {
d.statesMu.RLock()
defer d.statesMu.RUnlock()
return len(d.states)
}

Expand Down
14 changes: 1 addition & 13 deletions runtime/Go/antlr/dfa_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package antlr

import (
"fmt"
"sync"
)

// PredPrediction maps a predicate to a predicted alternative.
Expand Down Expand Up @@ -50,8 +49,7 @@ type DFAState struct {

// edges elements point to the target of the symbol. Shift up by 1 so (-1)
// Token.EOF maps to the first element.
edges []*DFAState
edgesMu sync.RWMutex
edges []*DFAState

isAcceptState bool

Expand Down Expand Up @@ -109,32 +107,22 @@ func (d *DFAState) GetAltSet() Set {
}

func (d *DFAState) getEdges() []*DFAState {
d.edgesMu.RLock()
defer d.edgesMu.RUnlock()
return d.edges
}

func (d *DFAState) numEdges() int {
d.edgesMu.RLock()
defer d.edgesMu.RUnlock()
return len(d.edges)
}

func (d *DFAState) getIthEdge(i int) *DFAState {
d.edgesMu.RLock()
defer d.edgesMu.RUnlock()
return d.edges[i]
}

func (d *DFAState) setEdges(newEdges []*DFAState) {
d.edgesMu.Lock()
defer d.edgesMu.Unlock()
d.edges = newEdges
}

func (d *DFAState) setIthEdge(i int, edge *DFAState) {
d.edgesMu.Lock()
defer d.edgesMu.Unlock()
d.edges[i] = edge
}

Expand Down
50 changes: 32 additions & 18 deletions runtime/Go/antlr/lexer_atn_simulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,16 @@ func (l *LexerATNSimulator) Match(input CharStream, mode int) int {

dfa := l.decisionToDFA[mode]

if dfa.getS0() == nil {
var s0 *DFAState
l.atn.stateMu.RLock()
s0 = dfa.getS0()
l.atn.stateMu.RUnlock()

if s0 == nil {
return l.MatchATN(input)
}

return l.execATN(input, dfa.getS0())
return l.execATN(input, s0)
}

func (l *LexerATNSimulator) reset() {
Expand All @@ -117,11 +122,7 @@ func (l *LexerATNSimulator) MatchATN(input CharStream) int {
suppressEdge := s0Closure.hasSemanticContext
s0Closure.hasSemanticContext = false

next := l.addDFAState(s0Closure)

if !suppressEdge {
l.decisionToDFA[l.mode].setS0(next)
}
next := l.addDFAState(s0Closure, suppressEdge)

predict := l.execATN(input, next)

Expand Down Expand Up @@ -203,10 +204,15 @@ func (l *LexerATNSimulator) execATN(input CharStream, ds0 *DFAState) int {
// {@code t}, or {@code nil} if the target state for l edge is not
// already cached
func (l *LexerATNSimulator) getExistingTargetState(s *DFAState, t int) *DFAState {
if s.getEdges() == nil || t < LexerATNSimulatorMinDFAEdge || t > LexerATNSimulatorMaxDFAEdge {
if t < LexerATNSimulatorMinDFAEdge || t > LexerATNSimulatorMaxDFAEdge {
return nil
}

l.atn.edgeMu.RLock()
defer l.atn.edgeMu.RUnlock()
if s.getEdges() == nil {
return nil
}
target := s.getIthEdge(t - LexerATNSimulatorMinDFAEdge)
if LexerATNSimulatorDebug && target != nil {
fmt.Println("reuse state " + strconv.Itoa(s.stateNumber) + " edge to " + strconv.Itoa(target.stateNumber))
Expand Down Expand Up @@ -537,7 +543,7 @@ func (l *LexerATNSimulator) addDFAEdge(from *DFAState, tk int, to *DFAState, cfg
suppressEdge := cfgs.HasSemanticContext()
cfgs.SetHasSemanticContext(false)

to = l.addDFAState(cfgs)
to = l.addDFAState(cfgs, true)

if suppressEdge {
return to
Expand All @@ -551,6 +557,8 @@ func (l *LexerATNSimulator) addDFAEdge(from *DFAState, tk int, to *DFAState, cfg
if LexerATNSimulatorDebug {
fmt.Println("EDGE " + from.String() + " -> " + to.String() + " upon " + strconv.Itoa(tk))
}
l.atn.edgeMu.Lock()
defer l.atn.edgeMu.Unlock()
if from.getEdges() == nil {
// make room for tokens 1..n and -1 masquerading as index 0
from.setEdges(make([]*DFAState, LexerATNSimulatorMaxDFAEdge-LexerATNSimulatorMinDFAEdge+1))
Expand All @@ -564,7 +572,7 @@ func (l *LexerATNSimulator) addDFAEdge(from *DFAState, tk int, to *DFAState, cfg
// configurations already. This method also detects the first
// configuration containing an ATN rule stop state. Later, when
// traversing the DFA, we will know which rule to accept.
func (l *LexerATNSimulator) addDFAState(configs ATNConfigSet) *DFAState {
func (l *LexerATNSimulator) addDFAState(configs ATNConfigSet, suppressEdge bool) *DFAState {

proposed := NewDFAState(-1, configs)
var firstConfigWithRuleStopState ATNConfig
Expand All @@ -585,16 +593,22 @@ func (l *LexerATNSimulator) addDFAState(configs ATNConfigSet) *DFAState {
}
hash := proposed.hash()
dfa := l.decisionToDFA[l.mode]

l.atn.stateMu.Lock()
defer l.atn.stateMu.Unlock()
existing, ok := dfa.getState(hash)
if ok {
return existing
}
newState := proposed
newState.stateNumber = dfa.numStates()
configs.SetReadOnly(true)
newState.configs = configs
dfa.setState(hash, newState)
return newState
proposed = existing
} else {
proposed.stateNumber = dfa.numStates()
configs.SetReadOnly(true)
proposed.configs = configs
dfa.setState(hash, proposed)
}
if !suppressEdge {
dfa.setS0(proposed)
}
return proposed
}

func (l *LexerATNSimulator) getDFA(mode int) *DFA {
Expand Down
Loading

0 comments on commit 02e5f66

Please sign in to comment.