diff --git a/builtins.go b/builtins.go index 6ecbd989..102a7627 100644 --- a/builtins.go +++ b/builtins.go @@ -1428,6 +1428,56 @@ func builtinStrReplace(i *interpreter, strv, fromv, tov value) (value, error) { return makeValueString(strings.Replace(sStr, sFrom, sTo, -1)), nil } +func builtinRegexMatch(i *interpreter, regexv, strv value) (value, error) { + regex, err := i.getString(regexv) + if err != nil { + return nil, err + } + + str, err := i.getString(strv) + if err != nil { + return nil, err + } + + sRegex := regex.getGoString() + sStr := str.getGoString() + + match, err := regexp.MatchString(sRegex, sStr) + if err != nil { + return nil, err + } + + return makeValueBoolean(match), nil +} + +func builtinRegexSubst(i *interpreter, regexv, srcv, replv value) (value, error) { + regex, err := i.getString(regexv) + if err != nil { + return nil, err + } + + src, err := i.getString(srcv) + if err != nil { + return nil, err + } + + repl, err := i.getString(replv) + if err != nil { + return nil, err + } + + sRegex := regex.getGoString() + sSrc := src.getGoString() + sRepl := repl.getGoString() + + r, err := regexp.Compile(sRegex) + if err != nil { + return nil, err + } + + return makeValueString(r.ReplaceAllString(sSrc, sRepl)), nil +} + func builtinIsEmpty(i *interpreter, strv value) (value, error) { str, err := i.getString(strv) if err != nil { @@ -2865,6 +2915,8 @@ var funcBuiltins = buildBuiltinMap([]builtin{ &ternaryBuiltin{name: "splitLimit", function: builtinSplitLimit, params: ast.Identifiers{"str", "c", "maxsplits"}}, &ternaryBuiltin{name: "splitLimitR", function: builtinSplitLimitR, params: ast.Identifiers{"str", "c", "maxsplits"}}, &ternaryBuiltin{name: "strReplace", function: builtinStrReplace, params: ast.Identifiers{"str", "from", "to"}}, + &binaryBuiltin{name: "regexMatch", function: builtinRegexMatch, params: ast.Identifiers{"regex", "str"}}, + &ternaryBuiltin{name: "regexSubst", function: builtinRegexSubst, params: ast.Identifiers{"regex", "src", "repl"}}, &unaryBuiltin{name: "isEmpty", function: builtinIsEmpty, params: ast.Identifiers{"str"}}, &binaryBuiltin{name: "equalsIgnoreCase", function: builtinEqualsIgnoreCase, params: ast.Identifiers{"str1", "str2"}}, &unaryBuiltin{name: "trim", function: builtinTrim, params: ast.Identifiers{"str"}}, diff --git a/linter/internal/types/stdlib.go b/linter/internal/types/stdlib.go index b1afb2d0..a3994032 100644 --- a/linter/internal/types/stdlib.go +++ b/linter/internal/types/stdlib.go @@ -92,6 +92,8 @@ func prepareStdlib(g *typeGraph) { "splitLimit": g.newSimpleFuncType(arrayOfString, "str", "c", "maxsplits"), "splitLimitR": g.newSimpleFuncType(arrayOfString, "str", "c", "maxsplits"), "strReplace": g.newSimpleFuncType(stringType, "str", "from", "to"), + "regexMatch": g.newSimpleFuncType(boolType, "regex", "str"), + "regexSubst": g.newSimpleFuncType(stringType, "regex", "src", "repl"), "asciiUpper": g.newSimpleFuncType(stringType, "str"), "asciiLower": g.newSimpleFuncType(stringType, "str"), "stringChars": g.newSimpleFuncType(stringType, "str"), diff --git a/testdata/builtinRegexMatch.golden b/testdata/builtinRegexMatch.golden new file mode 100644 index 00000000..27ba77dd --- /dev/null +++ b/testdata/builtinRegexMatch.golden @@ -0,0 +1 @@ +true diff --git a/testdata/builtinRegexMatch.jsonnet b/testdata/builtinRegexMatch.jsonnet new file mode 100644 index 00000000..fa48582d --- /dev/null +++ b/testdata/builtinRegexMatch.jsonnet @@ -0,0 +1 @@ +std.regexMatch('\\d+', 'abc123def') diff --git a/testdata/builtinRegexMatch.linter.golden b/testdata/builtinRegexMatch.linter.golden new file mode 100644 index 00000000..e69de29b diff --git a/testdata/builtinRegexMatch2.golden b/testdata/builtinRegexMatch2.golden new file mode 100644 index 00000000..c508d536 --- /dev/null +++ b/testdata/builtinRegexMatch2.golden @@ -0,0 +1 @@ +false diff --git a/testdata/builtinRegexMatch2.jsonnet b/testdata/builtinRegexMatch2.jsonnet new file mode 100644 index 00000000..9a0e00e1 --- /dev/null +++ b/testdata/builtinRegexMatch2.jsonnet @@ -0,0 +1 @@ +std.regexMatch('\\d+', 'abcdef') diff --git a/testdata/builtinRegexMatch2.linter.golden b/testdata/builtinRegexMatch2.linter.golden new file mode 100644 index 00000000..e69de29b diff --git a/testdata/builtinRegexSubst.golden b/testdata/builtinRegexSubst.golden new file mode 100644 index 00000000..1b00ed28 --- /dev/null +++ b/testdata/builtinRegexSubst.golden @@ -0,0 +1 @@ +"abc[123]def[456]ghi" diff --git a/testdata/builtinRegexSubst.jsonnet b/testdata/builtinRegexSubst.jsonnet new file mode 100644 index 00000000..08856105 --- /dev/null +++ b/testdata/builtinRegexSubst.jsonnet @@ -0,0 +1 @@ +std.regexSubst('([0-9]+)', 'abc123def456ghi', '[$1]') diff --git a/testdata/builtinRegexSubst.linter.golden b/testdata/builtinRegexSubst.linter.golden new file mode 100644 index 00000000..e69de29b diff --git a/testdata/builtinRegexSubst2.golden b/testdata/builtinRegexSubst2.golden new file mode 100644 index 00000000..cd4bc1ab --- /dev/null +++ b/testdata/builtinRegexSubst2.golden @@ -0,0 +1 @@ +"hello world" diff --git a/testdata/builtinRegexSubst2.jsonnet b/testdata/builtinRegexSubst2.jsonnet new file mode 100644 index 00000000..57a2764d --- /dev/null +++ b/testdata/builtinRegexSubst2.jsonnet @@ -0,0 +1 @@ +std.regexSubst('Hello', 'hello world', 'Hi') diff --git a/testdata/builtinRegexSubst2.linter.golden b/testdata/builtinRegexSubst2.linter.golden new file mode 100644 index 00000000..e69de29b diff --git a/testdata/stdlib_smoke_test.golden b/testdata/stdlib_smoke_test.golden index 3b2e8639..aa90e4ac 100644 --- a/testdata/stdlib_smoke_test.golden +++ b/testdata/stdlib_smoke_test.golden @@ -165,6 +165,8 @@ 4, 5 ], + "regexMatch": true, + "regexSubst": "abc[123]def[456]ghi", "repeat": "foofoofoo", "reverse": [ "a", diff --git a/testdata/stdlib_smoke_test.jsonnet b/testdata/stdlib_smoke_test.jsonnet index ca9ccf8b..8334c7c1 100644 --- a/testdata/stdlib_smoke_test.jsonnet +++ b/testdata/stdlib_smoke_test.jsonnet @@ -76,6 +76,8 @@ splitLimit: std.splitLimit(str="a,b,c", c=",", maxsplits=1), splitLimitR: std.splitLimitR(str="a,b,c", c=",", maxsplits=1), strReplace: std.strReplace(str="aaa", from="aa", to="bb"), + regexMatch: std.regexMatch(regex="\\d+", str="abc123def"), + regexSubst: std.regexSubst(regex="([0-9]+)", src="abc123def456ghi", repl="[$1]"), asciiUpper: std.asciiUpper(str="Blah"), asciiLower: std.asciiLower(str="Blah"), stringChars: std.stringChars(str="blah"),