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

go: Performance: Don't compile regex on matcher create #107

Merged
merged 10 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from 4 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
31 changes: 31 additions & 0 deletions go/gherkin_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package gherkin_test

import (
"bytes"
"io"
"testing"

gherkin "github.com/cucumber/gherkin/go/v26"
"github.com/gofrs/uuid"
)

func Benchmark_ParseGherkinDocumentForLanguage(b *testing.B) {
eatGodogContents := `
Feature: eat godogs
In order to be happy
As a hungry gopher
I need to be able to eat godogs
Scenario: Eat 5 out of 12
Given there are 12 godogs
When I eat 5
Then there should be 7 remaining`

r := bytes.NewReader([]byte(eatGodogContents))
buf := new(bytes.Buffer)
newIDFunc := func() string {
return uuid.Must(uuid.NewV4()).String()
}
for i := 0; i < b.N; i++ {
gherkin.ParseGherkinDocumentForLanguage(io.TeeReader(r, buf), gherkin.DefaultDialect, newIDFunc)
}
}
3 changes: 1 addition & 2 deletions go/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ module github.com/cucumber/gherkin/go/v26

require (
github.com/cucumber/messages/go/v21 v21.0.1
github.com/cucumber/messages/go/v22 v22.0.0
mpkorstanje marked this conversation as resolved.
Show resolved Hide resolved
github.com/gofrs/uuid v4.3.1+incompatible
mpkorstanje marked this conversation as resolved.
Show resolved Hide resolved
github.com/stretchr/testify v1.8.2
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gofrs/uuid v4.3.1+incompatible // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Expand Down
5 changes: 0 additions & 5 deletions go/go.sum
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
github.com/cucumber/messages/go/v21 v21.0.1 h1:wzA0LxwjlWQYZd32VTlAVDTkW6inOFmSM+RuOwHZiMI=
github.com/cucumber/messages/go/v21 v21.0.1/go.mod h1:zheH/2HS9JLVFukdrsPWoPdmUtmYQAQPLk7w5vWsk5s=
github.com/cucumber/messages/go/v22 v22.0.0/go.mod h1:aZipXTKc0JnjCsXrJnuZpWhtay93k7Rn3Dee7iyPJjs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI=
github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand All @@ -15,8 +12,6 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
Expand Down
14 changes: 9 additions & 5 deletions go/matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ const (
DocstringAlternativeSeparator = "```"
)

var (
defaultLanguagePattern = regexp.MustCompile("^\\s*#\\s*language\\s*:\\s*([a-zA-Z\\-_]+)\\s*$")
commentDelimiterPattern = regexp.MustCompile(`\s+` + CommentPrefix)
)

type matcher struct {
gdp DialectProvider
defaultLang string
Expand All @@ -35,7 +40,7 @@ func NewMatcher(gdp DialectProvider) Matcher {
defaultLang: DefaultDialect,
lang: DefaultDialect,
dialect: gdp.GetDialect(DefaultDialect),
languagePattern: regexp.MustCompile("^\\s*#\\s*language\\s*:\\s*([a-zA-Z\\-_]+)\\s*$"),
languagePattern: defaultLanguagePattern,
}
}

Expand All @@ -45,7 +50,7 @@ func NewLanguageMatcher(gdp DialectProvider, language string) Matcher {
defaultLang: language,
lang: language,
dialect: gdp.GetDialect(language),
languagePattern: regexp.MustCompile("^\\s*#\\s*language\\s*:\\s*([a-zA-Z\\-_]+)\\s*$"),
languagePattern: defaultLanguagePattern,
}
}

Expand Down Expand Up @@ -95,8 +100,7 @@ func (m *matcher) MatchTagLine(line *Line) (ok bool, token *Token, err error) {
if !line.StartsWith(TagPrefix) {
return
}
commentDelimiter := regexp.MustCompile(`\s+` + CommentPrefix)
uncommentedLine := commentDelimiter.Split(line.TrimmedLineText, 2)[0]
uncommentedLine := commentDelimiterPattern.Split(line.TrimmedLineText, 2)[0]
var tags []*LineSpan
var column = line.Indent() + 1

Expand All @@ -108,7 +112,7 @@ func (m *matcher) MatchTagLine(line *Line) (ok bool, token *Token, err error) {
if len(txt) == 0 {
continue
}
if !regexp.MustCompile(`^\S+$`).MatchString(txt) {
if strings.ContainsAny(txt, "\t\n\f\r ") {
location := &Location{line.LineNumber, column}
msg := "A tag may not contain whitespace"
err = &parseError{msg, location}
Expand Down
35 changes: 35 additions & 0 deletions go/matcher_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package gherkin_test

import (
"testing"

gherkin "github.com/cucumber/gherkin/go/v26"
"github.com/gofrs/uuid"
)

func BenchmarkMatcher_MatchTagLine(b *testing.B) {
builder := gherkin.NewAstBuilder(func() string {
return uuid.Must(uuid.NewV4()).String()
})
parser := gherkin.NewParser(builder)
parser.StopAtFirstError(false)
matcher := gherkin.NewLanguageMatcher(gherkin.DialectsBuiltin(), gherkin.DefaultDialect)

lines := []gherkin.Line{{
LineText: " @hello # there",
AtEof: false,
LineNumber: 0,
TrimmedLineText: "@hello # there",
}, {
LineText: " @yes # there",
AtEof: false,
LineNumber: 0,
TrimmedLineText: "@yes # there",
}}

for i := 0; i < b.N; i++ {
for _, line := range lines {
matcher.MatchTagLine(&line)
}
}
}