Port regextract DSL function from C to Go

This commit is contained in:
John Kerl 2021-02-15 00:55:40 -05:00
parent bc67217bb6
commit 9cc58fa3b5
5 changed files with 66 additions and 9 deletions

View file

@ -335,8 +335,6 @@ regex-match operator: try '$y = ~$x'.`,
// FUNC_CLASS_STRING
// TODO:
// regextract : help: `Example: '$name=regextract($name, "[A-Z]{3}[0-9]{2}")'`,
// regextract_or_else : help: `Example: '$name=regextract_or_else($name, "[A-Z]{3}[0-9]{2}", "default")'`,
// system : help: `Run command string, yielding its stdout minus final carriage return.
{
@ -381,6 +379,20 @@ regex-match operator: try '$y = ~$x'.`,
unaryFunc: types.MlrvalLStrip,
},
{
name: "regextract",
class: FUNC_CLASS_STRING,
help: `Example: '$name=regextract($name, "[A-Z]{3}[0-9]{2}")'`,
binaryFunc: types.MlrvalRegextract,
},
{
name: "regextract_or_else",
class: FUNC_CLASS_STRING,
help: `Example: '$name=regextract_or_else($name, "[A-Z]{3}[0-9]{2}", "default")'`,
ternaryFunc: types.MlrvalRegextractOrElse,
},
{
name: "rstrip",
class: FUNC_CLASS_STRING,

View file

@ -3,6 +3,7 @@ package lib
import (
"errors"
"fmt"
"os"
"regexp"
"strings"
)
@ -14,7 +15,7 @@ import (
// * If the regex_string is of the form a.*b, compiles it using cflags without REG_ICASE.
// * If the regex_string is of the form "a.*b", compiles a.*b using cflags without REG_ICASE.
// * If the regex_string is of the form "a.*b"i, compiles a.*b using cflags with REG_ICASE.
func CompilerMillerRegex(regexString string) (*regexp.Regexp, error) {
func CompileMillerRegex(regexString string) (*regexp.Regexp, error) {
if !strings.HasPrefix(regexString, "\"") {
return regexp.Compile(regexString)
} else {
@ -41,3 +42,12 @@ func CompilerMillerRegex(regexString string) (*regexp.Regexp, error) {
}
}
}
func CompileMillerRegexOrDie(regexString string) *regexp.Regexp {
regex, err := CompileMillerRegex(regexString)
if err != nil {
fmt.Fprint(os.Stderr, err)
os.Exit(1)
}
return regex
}

View file

@ -180,7 +180,7 @@ func NewTransformerHavingFields(
} else {
// Let them type in a.*b if they want, or "a.*b", or "a.*b"i.
// Strip off the leading " and trailing " or "i.
regex, err := lib.CompilerMillerRegex(regexString)
regex, err := lib.CompileMillerRegex(regexString)
if err != nil {
fmt.Fprintf(
os.Stderr,

View file

@ -1,8 +1,9 @@
package types
import (
"regexp"
"strings"
"miller/src/lib"
)
// ================================================================
@ -53,7 +54,7 @@ func MlrvalSub(ma, mb, mc *Mlrval) Mlrval {
return MlrvalFromError()
}
// TODO: better exception-handling
re := regexp.MustCompile(mb.printrep)
re := lib.CompileMillerRegexOrDie(mb.printrep)
onFirst := true
output := re.ReplaceAllStringFunc(ma.printrep, func(s string) string {
@ -90,7 +91,7 @@ func MlrvalGsub(ma, mb, mc *Mlrval) Mlrval {
return MlrvalFromError()
}
// TODO: better exception-handling
re := regexp.MustCompile(mb.printrep)
re := lib.CompileMillerRegexOrDie(mb.printrep)
return MlrvalFromString(
re.ReplaceAllString(ma.printrep, mc.printrep),
)
@ -113,7 +114,7 @@ func MlrvalStringMatchesRegexp(ma, mb *Mlrval) Mlrval {
return MlrvalFromError()
}
// TODO: better exception-handling
re := regexp.MustCompile(mb.printrep)
re := lib.CompileMillerRegexOrDie(mb.printrep)
return MlrvalFromBool(
re.MatchString(ma.printrep),
)
@ -127,3 +128,38 @@ func MlrvalStringDoesNotMatchRegexp(ma, mb *Mlrval) Mlrval {
return matches // error, absent, etc.
}
}
// TODO: find a way to keep and stash a precompiled regex, somewhere in the CST ...
func MlrvalRegextract(ma, mb *Mlrval) Mlrval {
if !ma.IsString() {
return MlrvalFromError()
}
if !mb.IsString() {
return MlrvalFromError()
}
regex := lib.CompileMillerRegexOrDie(mb.printrep)
// TODO: See if we need FindStringIndex or FindStringSubmatch to distinguish from matching "".
output := regex.FindString(ma.printrep)
if output != "" {
return MlrvalFromString(output)
} else {
return MlrvalFromAbsent()
}
}
func MlrvalRegextractOrElse(ma, mb, mc *Mlrval) Mlrval {
if !ma.IsString() {
return MlrvalFromError()
}
if !mb.IsString() {
return MlrvalFromError()
}
regex := lib.CompileMillerRegexOrDie(mb.printrep)
// TODO: See if we need FindStringIndex or FindStringSubmatch to distinguish from matching "".
output := regex.FindString(ma.printrep)
if output != "" {
return MlrvalFromString(output)
} else {
return *mc
}
}

View file

@ -118,7 +118,6 @@ no need to bootstrap a parser for the parser-generator language
* stats1:
o actualize -S and -F -- ?
o regexed
- needs implementation of "a.*b"i
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
! JSON null needs to be passed through as-is ... somehow ... new mlrval type??