-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial transfer of former stringex matcher
- Loading branch information
Showing
7 changed files
with
395 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,32 @@ | ||
# go-matcher | ||
Tideland Go Matcher | ||
# Tideland Go Matcher | ||
|
||
[](https://github.com/tideland/go-matcher) | ||
[](https://raw.githubusercontent.com/tideland/go-matcher/master/LICENSE) | ||
[](https://github.com/tideland/go-matcher/blob/master/go.mod) | ||
[](https://pkg.go.dev/mod/tideland.dev/go/matcher?tab=packages) | ||
[](https://github.com/tideland/go-matcher/actions/) | ||
[](https://goreportcard.com/report/tideland.dev/go/matcher) | ||
|
||
## Description | ||
|
||
The **Tideland Go Matcher** provides a simple pattern matching. It matches | ||
the following pattterns: | ||
|
||
- ? matches one char | ||
- * matches a group of chars | ||
- [abc] matches any of the chars inside the brackets | ||
- [a-z] matches any of the chars of the range | ||
- [^abc] matches any but the chars inside the brackets | ||
- \ escapes any of the pattern chars | ||
|
||
## Examples | ||
|
||
```go | ||
if matcher.Matches("g*e g?", "Google Go", matcher.IgnoreCase) { ... } | ||
|
||
if matcher.Matches("[oO][kK]", "ok", matcher.ValidateCase) { .... } | ||
``` | ||
|
||
## Contributors | ||
|
||
- Frank Mueller (https://github.com/themue / https://github.com/tideland / https://themue.dev) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// Tideland Go Matcher | ||
// | ||
// Copyright (C) 2019-2023 Frank Mueller / Tideland / Oldenburg / Germany | ||
// | ||
// All rights reserved. Use of this source code is governed | ||
// by the new BSD license. | ||
|
||
// Package matcher provides the matching of simple patterns against strings. | ||
// It matches the following pattterns: | ||
// | ||
// - ? matches one char | ||
// - * matches a group of chars | ||
// - [abc] matches any of the chars inside the brackets | ||
// - [a-z] matches any of the chars of the range | ||
// - [^abc] matches any but the chars inside the brackets | ||
// - \ escapes any of the pattern chars | ||
package matcher // import "tideland.dev/go/matcher" | ||
|
||
// EOF |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module tideland.dev/go/matcher | ||
|
||
go 1.20 | ||
|
||
require tideland.dev/go/audit v0.7.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
tideland.dev/go/audit v0.7.0 h1:lr4LkNu7i5qLJuqQ6lUfnt0J09anZNfrdXdB1I9JlTs= | ||
tideland.dev/go/audit v0.7.0/go.mod h1:Jua+IB3KgAC7fbuZ1YHT7gKhwpiTOcn3Q7AOCQsrro8= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
// Tideland Go Matcher | ||
// | ||
// Copyright (C) 2019-2023 Frank Mueller / Tideland / Oldenburg / Germany | ||
// | ||
// All rights reserved. Use of this source code valuePos governed | ||
// by the new BSD license. | ||
|
||
package matcher // import "tideland.dev/go/matcher" | ||
|
||
//-------------------- | ||
// IMPORTS | ||
//-------------------- | ||
|
||
import ( | ||
"strings" | ||
) | ||
|
||
//-------------------- | ||
// MATCHER | ||
//-------------------- | ||
|
||
const ( | ||
IgnoreCase bool = true | ||
ValidateCase bool = false | ||
|
||
matchSuccess int = iota | ||
matchCont | ||
matchFail | ||
) | ||
|
||
// matcher is a helper type for string pattern matching. | ||
type matcher struct { | ||
patternRunes []rune | ||
patternLen int | ||
patternPos int | ||
valueRunes []rune | ||
valueLen int | ||
valuePos int | ||
} | ||
|
||
// newMatcher creates the helper type for string pattern matching. | ||
func newMatcher(pattern, value string, ignoreCase bool) *matcher { | ||
if ignoreCase { | ||
return newMatcher(strings.ToLower(pattern), strings.ToLower(value), false) | ||
} | ||
prs := append([]rune(pattern), '\u0000') | ||
vrs := append([]rune(value), '\u0000') | ||
return &matcher{ | ||
patternRunes: prs, | ||
patternLen: len(prs) - 1, | ||
patternPos: 0, | ||
valueRunes: vrs, | ||
valueLen: len(vrs) - 1, | ||
valuePos: 0, | ||
} | ||
} | ||
|
||
// matches checks if the value matches the pattern. | ||
func (m *matcher) matches() bool { | ||
// Loop over the pattern. | ||
for m.patternLen > 0 { | ||
switch m.processPatternRune() { | ||
case matchSuccess: | ||
return true | ||
case matchFail: | ||
return false | ||
|
||
} | ||
m.patternPos++ | ||
m.patternLen-- | ||
if m.valueLen == 0 { | ||
for m.patternRunes[m.patternPos] == '*' { | ||
m.patternPos++ | ||
m.patternLen-- | ||
} | ||
break | ||
} | ||
} | ||
if m.patternLen == 0 && m.valueLen == 0 { | ||
return true | ||
} | ||
return false | ||
} | ||
|
||
// processPatternRune handles the current leading pattern rune. | ||
func (m *matcher) processPatternRune() int { | ||
switch m.patternRunes[m.patternPos] { | ||
case '*': | ||
return m.processAsterisk() | ||
case '?': | ||
return m.processQuestionMark() | ||
case '[': | ||
return m.processOpenBracket() | ||
case '\\': | ||
m.processBackslash() | ||
fallthrough | ||
default: | ||
return m.processDefault() | ||
} | ||
} | ||
|
||
// processAsterisk handles groups of characters. | ||
func (m *matcher) processAsterisk() int { | ||
for m.patternRunes[m.patternPos+1] == '*' { | ||
m.patternPos++ | ||
m.patternLen-- | ||
} | ||
if m.patternLen == 1 { | ||
return matchSuccess | ||
} | ||
for m.valueLen > 0 { | ||
patternCopy := make([]rune, len(m.patternRunes[m.patternPos+1:])) | ||
valueCopy := make([]rune, len(m.valueRunes[m.valuePos:])) | ||
copy(patternCopy, m.patternRunes[m.patternPos+1:]) | ||
copy(valueCopy, m.valueRunes[m.valuePos:]) | ||
pam := newMatcher(string(patternCopy), string(valueCopy), false) | ||
if pam.matches() { | ||
return matchSuccess | ||
} | ||
m.valuePos++ | ||
m.valueLen-- | ||
} | ||
return matchFail | ||
} | ||
|
||
// processQuestionMark handles a single character. | ||
func (m *matcher) processQuestionMark() int { | ||
if m.valueLen == 0 { | ||
return matchFail | ||
} | ||
m.valuePos++ | ||
m.valueLen-- | ||
return matchCont | ||
} | ||
|
||
// processOpenBracket handles an open bracket for a group of characters. | ||
func (m *matcher) processOpenBracket() int { | ||
m.patternPos++ | ||
m.patternLen-- | ||
not := (m.patternRunes[m.patternPos] == '^') | ||
match := false | ||
if not { | ||
m.patternPos++ | ||
m.patternLen-- | ||
} | ||
group: | ||
for { | ||
switch { | ||
case m.patternRunes[m.patternPos] == '\\': | ||
m.patternPos++ | ||
m.patternLen-- | ||
if m.patternRunes[m.patternPos] == m.valueRunes[m.valuePos] { | ||
match = true | ||
} | ||
case m.patternRunes[m.patternPos] == ']': | ||
break group | ||
case m.patternLen == 0: | ||
m.patternPos-- | ||
m.patternLen++ | ||
break group | ||
case m.patternRunes[m.patternPos+1] == '-' && m.patternLen >= 3: | ||
start := m.patternRunes[m.patternPos] | ||
end := m.patternRunes[m.patternPos+2] | ||
vr := m.valueRunes[m.valuePos] | ||
if start > end { | ||
start, end = end, start | ||
} | ||
m.patternPos += 2 | ||
m.patternLen -= 2 | ||
if vr >= start && vr <= end { | ||
match = true | ||
} | ||
default: | ||
if m.patternRunes[m.patternPos] == m.valueRunes[m.valuePos] { | ||
match = true | ||
} | ||
} | ||
m.patternPos++ | ||
m.patternLen-- | ||
} | ||
if not { | ||
match = !match | ||
} | ||
if !match { | ||
return matchFail | ||
} | ||
m.valuePos++ | ||
m.valueLen-- | ||
return matchCont | ||
} | ||
|
||
// processBackslash handles escaping via baskslash. | ||
func (m *matcher) processBackslash() int { | ||
if m.patternLen >= 2 { | ||
m.patternPos++ | ||
m.patternLen-- | ||
} | ||
return matchCont | ||
} | ||
|
||
// processDefault handles any other rune. | ||
func (m *matcher) processDefault() int { | ||
if m.patternRunes[m.patternPos] != m.valueRunes[m.valuePos] { | ||
return matchFail | ||
} | ||
m.valuePos++ | ||
m.valueLen-- | ||
return matchCont | ||
} | ||
|
||
// Matches checks if the pattern matches a given value. | ||
func Matches(pattern, value string, ignoreCase bool) bool { | ||
m := newMatcher(pattern, value, ignoreCase) | ||
return m.matches() | ||
} | ||
|
||
// EOF |
Oops, something went wrong.