go-shout/match.go

126 lines
2.7 KiB
Go

package main
import (
"regexp"
"strings"
)
func matchLine(pattern, actual string) bool {
if !strings.Contains(pattern, "...") {
return pattern == actual
}
parts := strings.Split(pattern, "...")
escaped := make([]string, len(parts))
for i, p := range parts {
escaped[i] = regexp.QuoteMeta(p)
}
re := regexp.MustCompile("^" + strings.Join(escaped, ".*") + "$")
return re.MatchString(actual)
}
func matchOutput(expected, actual []string) bool {
memo := make(map[[2]int]int8) // 0=unknown, 1=true, -1=false
return doMatch(expected, 0, actual, 0, memo)
}
func doMatch(expected []string, ei int, actual []string, ai int, memo map[[2]int]int8) bool {
key := [2]int{ei, ai}
if v := memo[key]; v != 0 {
return v == 1
}
result := doMatchInner(expected, ei, actual, ai, memo)
if result {
memo[key] = 1
} else {
memo[key] = -1
}
return result
}
func doMatchInner(expected []string, ei int, actual []string, ai int, memo map[[2]int]int8) bool {
if ei == len(expected) && ai == len(actual) {
return true
}
if ei == len(expected) {
return false
}
exp := expected[ei]
// Multi-line wildcard
if exp == "..." {
for skip := ai; skip <= len(actual); skip++ {
if doMatch(expected, ei+1, actual, skip, memo) {
return true
}
}
return false
}
if ai == len(actual) {
return false
}
if matchLine(exp, actual[ai]) {
return doMatch(expected, ei+1, actual, ai+1, memo)
}
return false
}
func diff(expected, actual []string) []DiffLine {
var result []DiffLine
ei, ai := 0, 0
for ei < len(expected) || ai < len(actual) {
if ei < len(expected) && expected[ei] == "..." {
nextExp := ""
hasNext := ei+1 < len(expected)
if hasNext {
nextExp = expected[ei+1]
}
if !hasNext {
result = append(result, DiffLine{Kind: "context", Text: "..."})
for ai < len(actual) {
result = append(result, DiffLine{Kind: "context", Text: actual[ai]})
ai++
}
break
}
result = append(result, DiffLine{Kind: "context", Text: "..."})
ei++
for ai < len(actual) && !matchLine(nextExp, actual[ai]) {
result = append(result, DiffLine{Kind: "context", Text: actual[ai]})
ai++
}
continue
}
if ei < len(expected) && ai < len(actual) {
if matchLine(expected[ei], actual[ai]) {
result = append(result, DiffLine{Kind: "equal", Text: actual[ai]})
ei++
ai++
} else {
result = append(result, DiffLine{Kind: "expected", Text: expected[ei]})
result = append(result, DiffLine{Kind: "actual", Text: actual[ai]})
ei++
ai++
}
} else if ei < len(expected) {
result = append(result, DiffLine{Kind: "expected", Text: expected[ei]})
ei++
} else {
result = append(result, DiffLine{Kind: "actual", Text: actual[ai]})
ai++
}
}
return result
}