Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: More reasonable wins in performance #4224

Merged
merged 2 commits into from
Apr 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ xyz
"""(a:3 x (b:2 y) z)
"""

[skip]
Go
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package main
import (
"test/parser"
"github.com/antlr/antlr4/runtime/Go/antlr/v4"
"fmt"
"os"
"github.com/antlr/antlr4/runtime/Go/antlr/v4"
"os"
"test/parser"
)

<if(parserName)>

import "reflect"

type TreeShapeListener struct {
*parser.Base<grammarName>Listener
}
Expand All @@ -18,8 +21,14 @@ func NewTreeShapeListener() *TreeShapeListener {
func (this *TreeShapeListener) EnterEveryRule(ctx antlr.ParserRuleContext) {
for i := 0; i\<ctx.GetChildCount(); i++ {
child := ctx.GetChild(i)
parentR,ok := child.GetParent().(antlr.RuleNode)
if !ok || parentR.GetBaseRuleContext() != ctx.GetBaseRuleContext() {
parentR, ok := child.GetParent().(antlr.ParserRuleContext)

// Have to use reflect here - we need to compare the underlying pointers, but we
// do not know the types of the underlying structs, just that they will be the same
// type.
parPointer := reflect.ValueOf(parentR).Elem().Addr().Pointer()
ctxPointer := reflect.ValueOf(ctx).Elem().Addr().Pointer()
if !ok || parPointer != ctxPointer {
panic("Invalid parse tree shape detected.")
}
}
Expand Down
30 changes: 15 additions & 15 deletions runtime/Go/antlr/v4/atn_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func NewATNConfig5(state ATNState, alt int, context *PredictionContext, semantic
if semanticContext == nil {
panic("semanticContext cannot be nil") // TODO: Necessary?
}

pac := &ATNConfig{}
pac.state = state
pac.alt = alt
Expand Down Expand Up @@ -83,7 +83,7 @@ func NewATNConfig(c *ATNConfig, state ATNState, context *PredictionContext, sema
}

func (a *ATNConfig) InitATNConfig(c *ATNConfig, state ATNState, alt int, context *PredictionContext, semanticContext SemanticContext) {

a.state = state
a.alt = alt
a.context = context
Expand Down Expand Up @@ -158,7 +158,7 @@ func (a *ATNConfig) Equals(o Collectable[*ATNConfig]) bool {
// predict the same alternative, and syntactic/semantic contexts are the same.
func (a *ATNConfig) PEquals(o Collectable[*ATNConfig]) bool {
var other, ok = o.(*ATNConfig)

if !ok {
return false
}
Expand All @@ -167,22 +167,22 @@ func (a *ATNConfig) PEquals(o Collectable[*ATNConfig]) bool {
} else if other == nil {
return false
}

var equal bool

if a.context == nil {
equal = other.context == nil
} else {
equal = a.context.Equals(other.context)
}

var (
nums = a.state.GetStateNumber() == other.state.GetStateNumber()
alts = a.alt == other.alt
cons = a.semanticContext.Equals(other.semanticContext)
sups = a.precedenceFilterSuppressed == other.precedenceFilterSuppressed
)

return nums && alts && cons && sups && equal
}

Expand All @@ -206,7 +206,7 @@ func (a *ATNConfig) PHash() int {
if a.context != nil {
c = a.context.Hash()
}

h := murmurInit(7)
h = murmurUpdate(h, a.state.GetStateNumber())
h = murmurUpdate(h, a.alt)
Expand All @@ -218,19 +218,19 @@ func (a *ATNConfig) PHash() int {
// String returns a string representation of the ATNConfig, usually used for debugging purposes
func (a *ATNConfig) String() string {
var s1, s2, s3 string

if a.context != nil {
s1 = ",[" + fmt.Sprint(a.context) + "]"
}

if a.semanticContext != SemanticContextNone {
s2 = "," + fmt.Sprint(a.semanticContext)
}

if a.reachesIntoOuterContext > 0 {
s3 = ",up=" + fmt.Sprint(a.reachesIntoOuterContext)
}

return fmt.Sprintf("(%v,%v%v%v%v)", a.state, a.alt, s1, s2, s3)
}

Expand Down Expand Up @@ -313,7 +313,7 @@ func (a *ATNConfig) LEquals(other Collectable[*ATNConfig]) bool {
} else if a.passedThroughNonGreedyDecision != otherT.passedThroughNonGreedyDecision {
return false
}

switch {
case a.lexerActionExecutor == nil && otherT.lexerActionExecutor == nil:
return true
Expand All @@ -324,12 +324,12 @@ func (a *ATNConfig) LEquals(other Collectable[*ATNConfig]) bool {
default:
return false // One but not both, are nil
}

return a.PEquals(otherT)
}

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

return source.passedThroughNonGreedyDecision || (ok && ds.getNonGreedy())
}
78 changes: 39 additions & 39 deletions runtime/Go/antlr/v4/atn_config_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,42 +13,42 @@ import (
// graph-structured stack.
type ATNConfigSet struct {
cachedHash int

// configLookup is used to determine whether two ATNConfigSets 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 that did not match an existing key in configLookup
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 ATNConfigSet 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?
Expand Down Expand Up @@ -83,83 +83,83 @@ func (b *ATNConfigSet) Add(config *ATNConfig, mergeCache *JPCMap) 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 {
b.cachedHash = -1
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 *ATNConfigSet) GetStates() *JStore[ATNState, Comparator[ATNState]] {

// states uses the standard comparator and Hash() provided by the ATNState instance
//
states := NewJStore[ATNState, Comparator[ATNState]](aStateEqInst, ATNStateCollection, "ATNConfigSet.GetStates()")

for i := 0; i < len(b.configs); i++ {
states.Put(b.configs[i].GetState())
}

return states
}

func (b *ATNConfigSet) 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
}

func (b *ATNConfigSet) OptimizeConfigs(interpreter *BaseATNSimulator) {
if b.readOnly {
panic("set is read-only")
}

// Empty indicate no optimization is possible
if b.configLookup == nil || b.configLookup.Len() == 0 {
return
}

for i := 0; i < len(b.configs); i++ {
config := b.configs[i]
config.SetContext(interpreter.getCachedContext(config.GetContext()))
Expand All @@ -170,7 +170,7 @@ func (b *ATNConfigSet) AddAll(coll []*ATNConfig) bool {
for i := 0; i < len(coll); i++ {
b.Add(coll[i], nil)
}

return false
}

Expand All @@ -185,7 +185,7 @@ func (b *ATNConfigSet) Compare(bs *ATNConfigSet) bool {
return false
}
}

return true
}

Expand All @@ -195,7 +195,7 @@ func (b *ATNConfigSet) Equals(other Collectable[ATNConfig]) bool {
} else if _, ok := other.(*ATNConfigSet); !ok {
return false
}

other2 := other.(*ATNConfigSet)
var eca bool
switch {
Expand All @@ -218,10 +218,10 @@ func (b *ATNConfigSet) Hash() int {
if b.cachedHash == -1 {
b.cachedHash = b.hashCodeConfigs()
}

return b.cachedHash
}

return b.hashCodeConfigs()
}

Expand Down Expand Up @@ -257,35 +257,35 @@ func (b *ATNConfigSet) Clear() {
}

func (b *ATNConfigSet) 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
}

Expand Down
2 changes: 1 addition & 1 deletion runtime/Go/antlr/v4/atn_simulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func (b *BaseATNSimulator) getCachedContext(context *PredictionContext) *Predict
if b.sharedContextCache == nil {
return context
}

//visited := NewJMap[*PredictionContext, *PredictionContext, Comparator[*PredictionContext]](pContextEqInst, PredictionVisitedCollection, "Visit map in getCachedContext()")
visited := NewVisitRecord()
return getCachedBasePredictionContext(context, b.sharedContextCache, visited)
Expand Down
Loading