Implement matching logic for literals and character classes; add test data
This commit is contained in:
113
main.go
113
main.go
@@ -153,7 +153,101 @@ func buildComponents(pattern string) ([]Component, error) {
|
||||
return comps, nil
|
||||
}
|
||||
|
||||
func matchEngine(pattern string, line string, lineNum int, exp bool) {
|
||||
// ---------- match a single literal (with repeat) ----------
|
||||
func matchLiteral(l Character, runes []rune, pos *int) bool {
|
||||
switch l.Repeats {
|
||||
case NoRepeat:
|
||||
if *pos >= len(runes) || runes[*pos] != l.Character {
|
||||
return false
|
||||
}
|
||||
(*pos)++
|
||||
return true
|
||||
|
||||
case ZeroOrMore:
|
||||
for *pos < len(runes) && runes[*pos] == l.Character {
|
||||
(*pos)++
|
||||
}
|
||||
return true // zero occurrences are fine
|
||||
|
||||
case OneOrMore:
|
||||
if *pos >= len(runes) || runes[*pos] != l.Character {
|
||||
return false
|
||||
}
|
||||
for *pos < len(runes) && runes[*pos] == l.Character {
|
||||
(*pos)++
|
||||
}
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func inClass(rc RenderedCharacterClass, r rune) bool {
|
||||
for _, cr := range rc.Characters {
|
||||
if r == cr {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func matchClass(rc RenderedCharacterClass, runes []rune, pos *int) bool {
|
||||
switch rc.Repeats {
|
||||
case NoRepeat:
|
||||
if *pos >= len(runes) || !inClass(rc, runes[*pos]) {
|
||||
return false
|
||||
}
|
||||
(*pos)++
|
||||
return true
|
||||
|
||||
case ZeroOrMore:
|
||||
for *pos < len(runes) && inClass(rc, runes[*pos]) {
|
||||
(*pos)++
|
||||
}
|
||||
return true // zero occurrences are fine
|
||||
|
||||
case OneOrMore:
|
||||
if *pos >= len(runes) || !inClass(rc, runes[*pos]) {
|
||||
return false
|
||||
}
|
||||
for *pos < len(runes) && inClass(rc, runes[*pos]) {
|
||||
(*pos)++
|
||||
}
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func matchFrom(components []Component, runes []rune, pos int) bool {
|
||||
for _, c := range components {
|
||||
switch c.Ctype {
|
||||
case Literal:
|
||||
if !matchLiteral(c.Literal, runes, &pos) {
|
||||
return false
|
||||
}
|
||||
case Rendered:
|
||||
if !matchClass(c.Rendered, runes, &pos) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func matchComponents(components []Component, text string) bool {
|
||||
runes := []rune(text)
|
||||
for start := 0; start <= len(runes); start++ {
|
||||
if matchFrom(components, runes, start) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func matchEngine(pattern string, line string, lineNum int, exp bool, components []Component) error {
|
||||
if !exp {
|
||||
// sliding window approach for simple full pattern matching
|
||||
for i := 0; i < len(line)-len(pattern); i++ {
|
||||
@@ -163,11 +257,20 @@ func matchEngine(pattern string, line string, lineNum int, exp bool) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
if matchComponents(components, line) {
|
||||
println(lineNum, line)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func processReader(r *os.File, pattern string, exp bool) {
|
||||
var components []Component
|
||||
if exp {
|
||||
components, _ = buildComponents(pattern)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
scanner := bufio.NewScanner(r)
|
||||
lineN := 0
|
||||
@@ -175,10 +278,10 @@ func processReader(r *os.File, pattern string, exp bool) {
|
||||
lineN++
|
||||
text := scanner.Text()
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
go func(ln int) {
|
||||
defer wg.Done()
|
||||
matchEngine(pattern, text, lineN, exp)
|
||||
}()
|
||||
matchEngine(pattern, text, ln, exp, components)
|
||||
}(lineN)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
Reference in New Issue
Block a user