Implement a built-in function for String count

Fixes #6827

Signed-off-by: Manish Giri <manish.giri.me@gmail.com>
This commit is contained in:
Manish Giri
2024-07-03 17:50:34 -05:00
committed by GitHub
parent d93ff255cc
commit c70544045b
5 changed files with 103 additions and 0 deletions
+14
View File
@@ -120,6 +120,7 @@ var DefaultBuiltins = [...]*Builtin{
Lower,
Upper,
Contains,
StringCount,
StartsWith,
EndsWith,
Split,
@@ -1109,6 +1110,19 @@ var Contains = &Builtin{
Categories: stringsCat,
}
var StringCount = &Builtin{
Name: "strings.count",
Description: "Returns the number of non-overlapping instances of a substring in a string.",
Decl: types.NewFunction(
types.Args(
types.Named("search", types.S).Description("string to search in"),
types.Named("substring", types.S).Description("substring to look for"),
),
types.Named("output", types.N).Description("count of occurrences, `0` if not found"),
),
Categories: stringsCat,
}
var StartsWith = &Builtin{
Name: "startswith",
Description: "Returns true if the search string begins with the base string.",
+26
View File
@@ -178,6 +178,7 @@
"startswith",
"strings.any_prefix_match",
"strings.any_suffix_match",
"strings.count",
"strings.render_template",
"strings.replace_n",
"strings.reverse",
@@ -17382,6 +17383,31 @@
},
"wasm": false
},
"strings.count": {
"args": [
{
"description": "string to search in",
"name": "search",
"type": "string"
},
{
"description": "substring to look for",
"name": "substring",
"type": "string"
}
],
"available": [
"edge"
],
"description": "Returns the number of non-overlapping instances of a substring in a string.",
"introduced": "edge",
"result": {
"description": "count of occurrences, `0` if not found",
"name": "output",
"type": "number"
},
"wasm": false
},
"strings.render_template": {
"args": [
{
+17
View File
@@ -3969,6 +3969,23 @@
"type": "function"
}
},
{
"name": "strings.count",
"decl": {
"args": [
{
"type": "string"
},
{
"type": "string"
}
],
"result": {
"type": "number"
},
"type": "function"
}
},
{
"name": "strings.render_template",
"decl": {
+26
View File
@@ -0,0 +1,26 @@
---
cases:
- note: "strings/count_single_word_match"
query: data.test.p = x
modules:
- |
package test
p := strings.count("cheese", "e")
want_result:
- x: 3
- note: "strings/count_multiple_separate_matches"
query: data.test.p = x
modules:
- |
package test
p := strings.count("hello hello hello world", "hello")
want_result:
- x: 3
- note: "strings/count_n_no_match"
query: data.test.p = x
modules:
- |
package test
p := strings.count("dummy", "x")
want_result:
- x: 0
+20
View File
@@ -310,6 +310,25 @@ func builtinContains(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term
return iter(ast.BooleanTerm(strings.Contains(string(s), string(substr))))
}
func builtinStringCount(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
s, err := builtins.StringOperand(operands[0].Value, 1)
if err != nil {
return err
}
substr, err := builtins.StringOperand(operands[1].Value, 2)
if err != nil {
return err
}
baseTerm := string(s)
searchTerm := string(substr)
count := strings.Count(baseTerm, searchTerm)
return iter(ast.IntNumberTerm(count))
}
func builtinStartsWith(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
s, err := builtins.StringOperand(operands[0].Value, 1)
if err != nil {
@@ -570,6 +589,7 @@ func init() {
RegisterBuiltinFunc(ast.IndexOfN.Name, builtinIndexOfN)
RegisterBuiltinFunc(ast.Substring.Name, builtinSubstring)
RegisterBuiltinFunc(ast.Contains.Name, builtinContains)
RegisterBuiltinFunc(ast.StringCount.Name, builtinStringCount)
RegisterBuiltinFunc(ast.StartsWith.Name, builtinStartsWith)
RegisterBuiltinFunc(ast.EndsWith.Name, builtinEndsWith)
RegisterBuiltinFunc(ast.Upper.Name, builtinUpper)