mirror of
https://github.com/johnkerl/miller.git
synced 2026-07-28 18:21:52 +00:00
Flesh out matches-with-captures
This commit is contained in:
parent
9ea4402933
commit
2c2f7f2291
2 changed files with 126 additions and 30 deletions
|
|
@ -4,6 +4,7 @@ package lib
|
|||
// * cst state for captures array
|
||||
// * reset-hook for start of execution
|
||||
// o UTs for that
|
||||
// * flesh out RegexCaptureBinaryFunctionCallsiteNode to do that
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
@ -125,26 +126,39 @@ func RegexGsubWithCaptures(
|
|||
|
||||
func RegexMatches(input string, sregex string) (matches bool, capturesOneUp []string) {
|
||||
regex := CompileMillerRegexOrDie(sregex)
|
||||
stringMatch := regex.MatchString(input)
|
||||
return stringMatch, nil // TODO
|
||||
return regexMatchesAux(input, regex)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Package-internal/implementation functions
|
||||
|
||||
// xxx:
|
||||
// $ go run foo.go "ab_cde ab_cde" "(ab)_(cde)"
|
||||
// MATRIX [][]int{[]int{0, 6, 0, 2, 3, 6}, []int{8, 14, 8, 10, 11, 14}}
|
||||
// 0 6 "ab_cde"
|
||||
// n:6
|
||||
// 2 0 2 "ab"
|
||||
// 4 3 6 "cde"
|
||||
// 8 14 "ab_cde"
|
||||
// n:6
|
||||
// 2 8 10 "ab"
|
||||
// 4 11 14 "cde"
|
||||
|
||||
func regexSubGsubWithCapturesAux(
|
||||
input string,
|
||||
regex *regexp.Regexp,
|
||||
replacement string,
|
||||
breakOnFirst bool,
|
||||
) string {
|
||||
matrix := regex.FindAllStringIndex(input, -1)
|
||||
matrix := regex.FindAllSubmatchIndex([]byte(input), -1)
|
||||
if matrix == nil || len(matrix) == 0 {
|
||||
return input
|
||||
}
|
||||
|
||||
// xxx instantiate a RegexCaptures object
|
||||
|
||||
// xxx update comment for FindAllSubmatchIndex
|
||||
|
||||
// The key is the Go library's regex.FindAllStringIndex. It gives us start
|
||||
// (inclusive) and end (exclusive) indices for matches.
|
||||
//
|
||||
|
|
@ -176,8 +190,67 @@ func regexSubGsubWithCapturesAux(
|
|||
return buffer.String()
|
||||
}
|
||||
|
||||
// xxx
|
||||
// echo a=ab_cde | mlrgo --oxtab put '
|
||||
// $b = sub($a, "(..)_(...)", "\2-\1");
|
||||
// $c = sub($a, "(..)_(.)(..)", ":\1:\2:\3")
|
||||
// '
|
||||
// xxx comment:
|
||||
// xxx so we early-out
|
||||
//
|
||||
// $ go run foo.go "ab_cde ab_cde" "(ab)_(cde)"
|
||||
// MATRIX [][]int{[]int{0, 6, 0, 2, 3, 6}, []int{8, 14, 8, 10, 11, 14}}
|
||||
// 0 6 "ab_cde"
|
||||
// n:6
|
||||
// 2 0 2 "ab"
|
||||
// 4 3 6 "cde"
|
||||
// 8 14 "ab_cde"
|
||||
// n:6
|
||||
// 2 8 10 "ab"
|
||||
// 4 11 14 "cde"
|
||||
|
||||
func regexMatchesAux(
|
||||
input string,
|
||||
regex *regexp.Regexp,
|
||||
) (bool, []string) {
|
||||
matrix := regex.FindAllSubmatchIndex([]byte(input), -1)
|
||||
if matrix == nil || len(matrix) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// 0 is ""; 1..9 for "\1".."\9"
|
||||
captures := make([]string, 10)
|
||||
|
||||
// xxx update comment for FindAllSubmatchIndex
|
||||
|
||||
// The key is the Go library's regex.FindAllStringIndex. It gives us start
|
||||
// (inclusive) and end (exclusive) indices for matches.
|
||||
//
|
||||
// Example: for pattern "foo" and input "abc foo def foo ghi" we'll have
|
||||
// matrix [[4 7] [12 15]] which indicates matches from positions 4-6 and
|
||||
// 12-14. We simply need to concatenate
|
||||
// * 0-3 "abc " not matching
|
||||
// * 4-6 "foo" matching
|
||||
// * 7-11 " def " not matching
|
||||
// * 12-14 "foo" matching
|
||||
// * 15-18 " ghi" not matching
|
||||
//
|
||||
// Example: with pattern "f.*o" and input "abc foo def foo ghi" we'll have
|
||||
// matrix [[4 15]] so "foo def foo" will be a matched substring.
|
||||
|
||||
// xxx comment first outer-match only
|
||||
row := matrix[0]
|
||||
n := len(row)
|
||||
if n == 2 {
|
||||
// xxx comment no captures
|
||||
return true, nil
|
||||
}
|
||||
|
||||
i := 1
|
||||
for j := 2; j < n; j += 2 {
|
||||
if i > 9 {
|
||||
break
|
||||
}
|
||||
start := row[j]
|
||||
end := row[j+1]
|
||||
captures[i] = input[start:end]
|
||||
i += 1
|
||||
}
|
||||
|
||||
return true, captures
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,9 +39,9 @@ var dataForSubWithCaptures = []tDataForSubGsub{
|
|||
{"abcde", "[a-z]", "X", "Xbcde"},
|
||||
{"abcde", "[A-Z]", "X", "abcde"},
|
||||
|
||||
//{"ab_cde", "(..)_(...)e", "\\2\\1", "cdeab"},
|
||||
//{"ab_cde", "(..)_(...)e", "\\2-\\1", "cde-ab"},
|
||||
//{"ab_cde", "(..)_(...)e", "X\\2Y\\1Z", "XcdeYabZ"},
|
||||
//{"ab_cde", "(..)_(...)", "\\2\\1", "cdeab"},
|
||||
//{"ab_cde", "(..)_(...)", "\\2-\\1", "cde-ab"},
|
||||
//{"ab_cde", "(..)_(...)", "X\\2Y\\1Z", "XcdeYabZ"},
|
||||
}
|
||||
|
||||
var dataForGsubWithoutCaptures = []tDataForSubGsub{
|
||||
|
|
@ -64,66 +64,89 @@ var dataForGsubWithCaptures = []tDataForSubGsub{
|
|||
|
||||
// xxx needs expected-capture data
|
||||
var dataForMatches = []tDataForMatches{
|
||||
{"abcde", "[a-z]", true, nil},
|
||||
{"abcde", "[A-Z]", false, nil},
|
||||
{"abcde", "[a-z]", true, nil},
|
||||
{"...ab_cde...", "(..)_(...)", true, []string{"", "ab", "cde", "", "", "", "", "", "", ""}},
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
func TestRegexSubWithoutCaptures(t *testing.T) {
|
||||
for _, entry := range dataForSubWithoutCaptures {
|
||||
for i, entry := range dataForSubWithoutCaptures {
|
||||
actualOutput := RegexSubWithoutCaptures(entry.input, entry.sregex, entry.replacement)
|
||||
if actualOutput != entry.expectedOutput {
|
||||
t.Fatalf("input \"%s\" sregex \"%s\" replacement \"%s\" expected \"%s\" got \"%s\"\n",
|
||||
entry.input, entry.sregex, entry.replacement, entry.expectedOutput, actualOutput,
|
||||
t.Fatalf("case %d input \"%s\" sregex \"%s\" replacement \"%s\" expected \"%s\" got \"%s\"\n",
|
||||
i, entry.input, entry.sregex, entry.replacement, entry.expectedOutput, actualOutput,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegexSubWithCaptures(t *testing.T) {
|
||||
for _, entry := range dataForSubWithCaptures {
|
||||
for i, entry := range dataForSubWithCaptures {
|
||||
actualOutput := RegexSubWithCaptures(entry.input, entry.sregex, entry.replacement)
|
||||
if actualOutput != entry.expectedOutput {
|
||||
t.Fatalf("input \"%s\" sregex \"%s\" replacement \"%s\" expected \"%s\" got \"%s\"\n",
|
||||
entry.input, entry.sregex, entry.replacement, entry.expectedOutput, actualOutput,
|
||||
t.Fatalf("case %d input \"%s\" sregex \"%s\" replacement \"%s\" expected \"%s\" got \"%s\"\n",
|
||||
i, entry.input, entry.sregex, entry.replacement, entry.expectedOutput, actualOutput,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegexGsubWithoutCaptures(t *testing.T) {
|
||||
for _, entry := range dataForGsubWithoutCaptures {
|
||||
for i, entry := range dataForGsubWithoutCaptures {
|
||||
actualOutput := RegexGsubWithoutCaptures(entry.input, entry.sregex, entry.replacement)
|
||||
if actualOutput != entry.expectedOutput {
|
||||
t.Fatalf("input \"%s\" sregex \"%s\" replacement \"%s\" expected \"%s\" got \"%s\"\n",
|
||||
entry.input, entry.sregex, entry.replacement, entry.expectedOutput, actualOutput,
|
||||
t.Fatalf("case %d input \"%s\" sregex \"%s\" replacement \"%s\" expected \"%s\" got \"%s\"\n",
|
||||
i, entry.input, entry.sregex, entry.replacement, entry.expectedOutput, actualOutput,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegexGsubWithCaptures(t *testing.T) {
|
||||
for _, entry := range dataForGsubWithCaptures {
|
||||
for i, entry := range dataForGsubWithCaptures {
|
||||
actualOutput := RegexGsubWithCaptures(entry.input, entry.sregex, entry.replacement)
|
||||
if actualOutput != entry.expectedOutput {
|
||||
t.Fatalf("input \"%s\" sregex \"%s\" replacement \"%s\" expected \"%s\" got \"%s\"\n",
|
||||
entry.input, entry.sregex, entry.replacement, entry.expectedOutput, actualOutput,
|
||||
t.Fatalf("case %d input \"%s\" sregex \"%s\" replacement \"%s\" expected \"%s\" got \"%s\"\n",
|
||||
i, entry.input, entry.sregex, entry.replacement, entry.expectedOutput, actualOutput,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegexMatches(t *testing.T) {
|
||||
for _, entry := range dataForMatches {
|
||||
for i, entry := range dataForMatches {
|
||||
actualOutput, actualCaptures := RegexMatches(entry.input, entry.sregex)
|
||||
if actualOutput != entry.expectedOutput {
|
||||
t.Fatalf("input \"%s\" sregex \"%s\" expected %v got %v\n",
|
||||
entry.input, entry.sregex, entry.expectedOutput, actualOutput,
|
||||
t.Fatalf("case %d input \"%s\" sregex \"%s\" expected %v got %v\n",
|
||||
i, entry.input, entry.sregex, entry.expectedOutput, actualOutput,
|
||||
)
|
||||
}
|
||||
// xxx compare actual/expected captures
|
||||
// xxx make a comparator function
|
||||
if actualCaptures == nil {
|
||||
if !compareCaptures(actualCaptures, entry.expectedCaptures) {
|
||||
t.Fatalf("case %d input \"%s\" sregex \"%s\" expected captures %#v got %#v\n",
|
||||
i, entry.input, entry.sregex, entry.expectedCaptures, actualCaptures,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func compareCaptures(
|
||||
actualCaptures []string,
|
||||
expectedCaptures []string,
|
||||
) bool {
|
||||
if actualCaptures == nil && expectedCaptures == nil {
|
||||
return true
|
||||
}
|
||||
if actualCaptures == nil || expectedCaptures == nil {
|
||||
return false
|
||||
}
|
||||
if len(actualCaptures) != len(expectedCaptures) {
|
||||
return false
|
||||
}
|
||||
for i, _ := range expectedCaptures {
|
||||
if actualCaptures[i] != expectedCaptures[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue