From 5175f8ac486e5a9573f4d4ceed97b18157b90972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gunnar=20Z=C3=B6tl?= Date: Wed, 1 Oct 2025 20:12:10 +0200 Subject: [PATCH 1/9] added regular expression type implemented REGEX object type and regex(string) function --- evaluator/functions.go | 23 +++++++++++++++++++++++ object/object.go | 12 ++++++++++++ 2 files changed, 35 insertions(+) diff --git a/evaluator/functions.go b/evaluator/functions.go index 25cdbd1..8262f25 100644 --- a/evaluator/functions.go +++ b/evaluator/functions.go @@ -16,6 +16,7 @@ import ( "sort" "strconv" "strings" + "regexp" "time" "unicode" @@ -426,6 +427,12 @@ func GetFns() map[string]*object.Builtin { Fn: lastIndexFn, Doc: "returns the last position at which a string is found within another string", }, + // regex("ax+b") + "regex": &object.Builtin{ + Types: []string{object.STRING_OBJ}, + Fn: regexFn, + Doc: "returns a string compiled to a regular expression", + }, // shift([1,2,3]) "shift": &object.Builtin{ Types: []string{object.ARRAY_OBJ}, @@ -1971,6 +1978,22 @@ func lastIndexFn(tok token.Token, env *object.Environment, args ...object.Object return &object.Number{Token: tok, Value: float64(i)} } +// regex("ax+b") +func regexFn(tok token.Token, env *object.Environment, args ...object.Object) object.Object { + err := validateArgs(tok, "regex", args, 1, [][]string{{object.STRING_OBJ}}) + if err != nil { + return err + } + + re, nok := regexp.Compile(args[0].(*object.String).Value) + + if nok != nil { + return newError(tok, "invalid regular expression \"%s\"", nok.Error()) + } + + return &object.Regex{Token: tok, Value: re, Source: args[0].(*object.String).Value} +} + // Clamps start and end arguments to the slice // function. When you slice "abc" you can have // start 10 and end -20... diff --git a/object/object.go b/object/object.go index b4b5776..d3be2cc 100644 --- a/object/object.go +++ b/object/object.go @@ -7,6 +7,7 @@ import ( "sort" "strconv" "strings" + "regexp" "sync" "github.com/abs-lang/abs/ast" @@ -24,6 +25,7 @@ const ( NUMBER_OBJ = "NUMBER" BOOLEAN_OBJ = "BOOLEAN" STRING_OBJ = "STRING" + REGEX_OBJ = "REGEX" RETURN_VALUE_OBJ = "RETURN_VALUE" @@ -229,6 +231,16 @@ func (s *String) HashKey() HashKey { return HashKey{Type: s.Type(), Value: s.Value} } +type Regex struct { + Token token.Token + Value *regexp.Regexp + Source string +} + +func (r *Regex) Type() ObjectType { return REGEX_OBJ } +func (r *Regex) Inspect() string { return r.Source } +func (r *Regex) Json() string { return `"` + strings.ReplaceAll(r.Inspect(), `"`, `\"`) + `"` } + // Function that ensure a mutex // instance is created on the // string From 42f9f23f8aa6701ed08052303d3e159e4ba796b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gunnar=20Z=C3=B6tl?= Date: Wed, 1 Oct 2025 20:42:41 +0200 Subject: [PATCH 2/9] implemented matches(string/regex, string) function --- evaluator/functions.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/evaluator/functions.go b/evaluator/functions.go index 8262f25..82b6a48 100644 --- a/evaluator/functions.go +++ b/evaluator/functions.go @@ -433,6 +433,12 @@ func GetFns() map[string]*object.Builtin { Fn: regexFn, Doc: "returns a string compiled to a regular expression", }, + // matches(pattern, "abc") + "matches": &object.Builtin { + Types: []string{object.STRING_OBJ,object.REGEX_OBJ}, + Fn: matchesFn, + Doc: "checks whether a string matches a pattern", + }, // shift([1,2,3]) "shift": &object.Builtin{ Types: []string{object.ARRAY_OBJ}, @@ -1994,6 +2000,28 @@ func regexFn(tok token.Token, env *object.Environment, args ...object.Object) ob return &object.Regex{Token: tok, Value: re, Source: args[0].(*object.String).Value} } +// matches(pattern, "abc") +func matchesFn(tok token.Token, env *object.Environment, args ...object.Object) object.Object { + err := validateArgs(tok, "last_index", args, 2, [][]string{{object.STRING_OBJ, object.REGEX_OBJ}, {object.STRING_OBJ}}) + if err != nil { + return err + } + + var re *regexp.Regexp + if args[0].Type() == object.REGEX_OBJ { + re = args[0].(*object.Regex).Value; + } else { + var nok error + re, nok = regexp.Compile(args[0].(*object.String).Value) + + if nok != nil { + return newError(tok, "invalid regular expression \"%s\"", nok.Error()) + } + } + + return &object.Boolean{Token: tok, Value: re.MatchString(args[1].(*object.String).Value)} +} + // Clamps start and end arguments to the slice // function. When you slice "abc" you can have // start 10 and end -20... From 142efedc9243b742b109f916986cf7a91955e863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gunnar=20Z=C3=B6tl?= Date: Wed, 1 Oct 2025 21:15:02 +0200 Subject: [PATCH 3/9] impleemented match(pattern, "yxz") function also factored out the distinction between REGEX and STRING object, as this will be used a few more times --- evaluator/functions.go | 63 +++++++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/evaluator/functions.go b/evaluator/functions.go index 82b6a48..7fe88ca 100644 --- a/evaluator/functions.go +++ b/evaluator/functions.go @@ -439,6 +439,12 @@ func GetFns() map[string]*object.Builtin { Fn: matchesFn, Doc: "checks whether a string matches a pattern", }, + // match(pattern, "abc") + "match": &object.Builtin { + Types: []string{object.STRING_OBJ,object.REGEX_OBJ}, + Fn: matchFn, + Doc: "match a pattern against a string and return the first match and all submatches", + }, // shift([1,2,3]) "shift": &object.Builtin{ Types: []string{object.ARRAY_OBJ}, @@ -2000,6 +2006,21 @@ func regexFn(tok token.Token, env *object.Environment, args ...object.Object) ob return &object.Regex{Token: tok, Value: re, Source: args[0].(*object.String).Value} } +func getRegexpObj(obj object.Object) object.Object { + switch (obj.Type()) { + case object.REGEX_OBJ: + return obj + case object.STRING_OBJ: + re, err := regexp.Compile(obj.(*object.String).Value) + if err != nil { + return newError(tok, "%s", err) + } + return &object.Regex{Token: tok, Value: re, Source: obj.(*object.String).Value} + default: + return newError(tok, "invalid argument type"); // should not get here + } +} + // matches(pattern, "abc") func matchesFn(tok token.Token, env *object.Environment, args ...object.Object) object.Object { err := validateArgs(tok, "last_index", args, 2, [][]string{{object.STRING_OBJ, object.REGEX_OBJ}, {object.STRING_OBJ}}) @@ -2007,19 +2028,41 @@ func matchesFn(tok token.Token, env *object.Environment, args ...object.Object) return err } - var re *regexp.Regexp - if args[0].Type() == object.REGEX_OBJ { - re = args[0].(*object.Regex).Value; - } else { - var nok error - re, nok = regexp.Compile(args[0].(*object.String).Value) + re := getRegexpObj(args[0]) + if re.Type() == object.ERROR_OBJ { + return err; + } - if nok != nil { - return newError(tok, "invalid regular expression \"%s\"", nok.Error()) - } + m := re.(*object.Regex).Value.MatchString(args[1].(*object.String).Value) + + return &object.Boolean{Token: tok, Value: m} +} + +// match(pattern, "abc") +func matchFn(tok token.Token, env *object.Environment, args ...object.Object) object.Object { + err := validateArgs(tok, "last_index", args, 2, [][]string{{object.STRING_OBJ, object.REGEX_OBJ}, {object.STRING_OBJ}}) + if err != nil { + return err + } + + re := getRegexpObj(args[0]) + if re.Type() == object.ERROR_OBJ { + return err; + } + + match := re.(*object.Regex).Value.FindStringSubmatch(args[1].(*object.String).Value) + + length := len(match) + if length == 0 { + return NULL; + } + newElements := make([]object.Object, length, length) + + for i, s := range(match) { + newElements[i] = &object.String{Token: tok, Value: s} } - return &object.Boolean{Token: tok, Value: re.MatchString(args[1].(*object.String).Value)} + return &object.Array{Token: tok, Elements: newElements} } // Clamps start and end arguments to the slice From f508e50c6e4306b35b6f3089ad397c0ab99558bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gunnar=20Z=C3=B6tl?= Date: Wed, 1 Oct 2025 22:07:09 +0200 Subject: [PATCH 4/9] implemented match_all(pattern, "abc") function --- evaluator/functions.go | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/evaluator/functions.go b/evaluator/functions.go index 7fe88ca..6afa415 100644 --- a/evaluator/functions.go +++ b/evaluator/functions.go @@ -445,6 +445,12 @@ func GetFns() map[string]*object.Builtin { Fn: matchFn, Doc: "match a pattern against a string and return the first match and all submatches", }, + // match_all(pattern, "abc") + "match_all": &object.Builtin { + Types: []string{object.STRING_OBJ,object.REGEX_OBJ}, + Fn: match_allFn, + Doc: "match a pattern against a string and return the all matches and their submatches", + }, // shift([1,2,3]) "shift": &object.Builtin{ Types: []string{object.ARRAY_OBJ}, @@ -2065,6 +2071,40 @@ func matchFn(tok token.Token, env *object.Environment, args ...object.Object) ob return &object.Array{Token: tok, Elements: newElements} } +// match_all(pattern, "abc") +func match_allFn(tok token.Token, env *object.Environment, args ...object.Object) object.Object { + err := validateArgs(tok, "last_index", args, 2, [][]string{{object.STRING_OBJ, object.REGEX_OBJ}, {object.STRING_OBJ}}) + if err != nil { + return err + } + + re := getRegexpObj(args[0]) + if re.Type() == object.ERROR_OBJ { + return err; + } + + allMatches := re.(*object.Regex).Value.FindAllStringSubmatch(args[1].(*object.String).Value, -1) + + length := len(allMatches) + if length == 0 { + return NULL; + } + newMatches := make([]object.Object, length, length) + + for i, match := range(allMatches) { + length := len(match) + newElements := make([]object.Object, length, length) + + for j, s := range(match) { + newElements[j] = &object.String{Token: tok, Value: s} + } + + newMatches[i] = &object.Array{Token: tok, Elements: newElements} + } + + return &object.Array{Token: tok, Elements: newMatches} +} + // Clamps start and end arguments to the slice // function. When you slice "abc" you can have // start 10 and end -20... From cf43f02ffbdf8761021a543d3dd14f8c2a255e0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gunnar=20Z=C3=B6tl?= Date: Wed, 1 Oct 2025 22:52:18 +0200 Subject: [PATCH 5/9] added tests for regex functions implemented so far --- evaluator/builtin_functions_test.go | 38 +++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/evaluator/builtin_functions_test.go b/evaluator/builtin_functions_test.go index 844dee9..4566cad 100644 --- a/evaluator/builtin_functions_test.go +++ b/evaluator/builtin_functions_test.go @@ -736,6 +736,44 @@ func TestPartition(t *testing.T) { testBuiltinFunction(tests, t) } +func TestMatches(t *testing.T) { + tests := []Tests{ + {`r = regex(""); r.matches("")`, true}, + {`"ax*b".matches("ab")`, true}, + {`r = regex("ax*b"); r.matches("xaby")`, true}, + {`"ax*b".matches("ax")`, false}, + {`r = regex("ax*b"); r.matches("xb")`, false}, + {`"ax+b".matches("ab")`, false}, + } + + testBuiltinFunction(tests, t) +} + +func TestMatch(t *testing.T) { + tests := []Tests{ + {`"..".match("ab")`, []string{"ab"}}, + {`"(.)(.)".match("ab")`, []string{"ab", "a", "b"}}, + {`r = regex("a(y*)b"); r.match("xaybz")`, []string{"ayb", "y"}}, + {`"abc".match("xyz")`, nil}, + {`r = regex("a(y+)b"); r.match("ab")`, nil}, + } + + testBuiltinFunction(tests, t) +} + +// testBuiltinFunction does ot do nested arrays as results, so we get creative +func TestMatchAll(t *testing.T) { + tests := []Tests{ + {`".".match_all("abc")[2]`, []string{"c"}}, + {`r = regex("ax*b"); r.match_all("a ab ax xb axb").len()`, 2}, + {`"a(x*)b".match_all("a ab ax xb axb").len()`, 2}, + {`"a(x*)b".match_all("a ab ax xb axb")[0]`, []string{"ab", ""}}, + {`"a(x*)b".match_all("a ab ax xb axb")[1]`, []string{"axb", "x"}}, + } + + testBuiltinFunction(tests, t) +} + func testBuiltinFunction(tests []Tests, t *testing.T) { for _, tt := range tests { evaluated := testEval(tt.input) From e9743d5851c0ce393a2061931a7e47d9036d5284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gunnar=20Z=C3=B6tl?= Date: Wed, 1 Oct 2025 23:38:25 +0200 Subject: [PATCH 6/9] implemented replace_match function added tests for regex() and replace_match() functions --- evaluator/builtin_functions_test.go | 23 +++++++++++++++++ evaluator/functions.go | 38 +++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/evaluator/builtin_functions_test.go b/evaluator/builtin_functions_test.go index 4566cad..d5c7276 100644 --- a/evaluator/builtin_functions_test.go +++ b/evaluator/builtin_functions_test.go @@ -736,6 +736,17 @@ func TestPartition(t *testing.T) { testBuiltinFunction(tests, t) } +func TestRegex(t *testing.T) { + tests := []Tests{ + {`r = regex(""); r.str()`, ""}, + {`regex("a").str()`, "a"}, + {`r = regex("a(x+)b"); r.str()`, "a(x+)b"}, + {`regex("won't(work")`, ""} + } + + testBuiltinFunction(tests, t) +} + func TestMatches(t *testing.T) { tests := []Tests{ {`r = regex(""); r.matches("")`, true}, @@ -774,6 +785,18 @@ func TestMatchAll(t *testing.T) { testBuiltinFunction(tests, t) } +func TestReplaceMatch(t *testing.T) { + tests := []Tests{ + {`".".replace_match("xyz", ".")`, "..."}, + {`r = regex("x+"); r.replace_match("x xx xxx", f(x) { x.len() })`, "1 2 3"}, + {`".".replace_match("a", f(x) {})`, ""}, + {`r = regex("a(x*)b"); r.replace_match("a ab axb axxxb", ".")`, "a . . ."}, + {`"a(x*)b".replace_match("a ab axb axxxb", f(x) {x.len()})`, "a 2 3 5"}, + } + + testBuiltinFunction(tests, t) +} + func testBuiltinFunction(tests []Tests, t *testing.T) { for _, tt := range tests { evaluated := testEval(tt.input) diff --git a/evaluator/functions.go b/evaluator/functions.go index 6afa415..45b615e 100644 --- a/evaluator/functions.go +++ b/evaluator/functions.go @@ -451,6 +451,12 @@ func GetFns() map[string]*object.Builtin { Fn: match_allFn, Doc: "match a pattern against a string and return the all matches and their submatches", }, + // replace_match(pattern, "abc", "repl") or replace_match(pattern, "abc", fn) + "replace_match": &object.Builtin { + Types: []string{object.STRING_OBJ,object.REGEX_OBJ}, + Fn: replace_matchFn, + Doc: "replace all matches of a pattern in a string", + }, // shift([1,2,3]) "shift": &object.Builtin{ Types: []string{object.ARRAY_OBJ}, @@ -2105,6 +2111,38 @@ func match_allFn(tok token.Token, env *object.Environment, args ...object.Object return &object.Array{Token: tok, Elements: newMatches} } +// replace_match(pattern, "abc", "repl") or replace_match(pattern, "abc", fn) +func replace_matchFn(tok token.Token, env *object.Environment, args ...object.Object) object.Object { + err := validateArgs(tok, "last_index", args, 3, [][]string{{object.STRING_OBJ, object.REGEX_OBJ}, {object.STRING_OBJ}, {object.STRING_OBJ, object.FUNCTION_OBJ}}) + if err != nil { + return err + } + + re := getRegexpObj(args[0]) + if re.Type() == object.ERROR_OBJ { + return err; + } + + var s string; + if args[2].Type() == object.STRING_OBJ { + s = re.(*object.Regex).Value.ReplaceAllString(args[1].(*object.String).Value, args[2].(*object.String).Value) + } else { + replacefn := func(m string) string { + cargs := make([]object.Object, 1, 1) + cargs[0] = &object.String{Token: tok, Value: m} + r := applyFunction(tok, args[2], env, cargs) + if r != nil { + return r.Inspect() + } + return "" + } + + s = re.(*object.Regex).Value.ReplaceAllStringFunc(args[1].(*object.String).Value, replacefn) + } + + return &object.String{Token: tok, Value: s} +} + // Clamps start and end arguments to the slice // function. When you slice "abc" you can have // start 10 and end -20... From 46f6f7592f6b52f082bc61e8ad62f7fdac4a7220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gunnar=20Z=C3=B6tl?= Date: Wed, 1 Oct 2025 23:46:13 +0200 Subject: [PATCH 7/9] added tests for error cases in match functions fixed bug with returning errors from match functions shortened error returned by regex() used isError() function in preference to comparing with object.ERROR_OBJ --- evaluator/builtin_functions_test.go | 7 ++++++- evaluator/functions.go | 18 +++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/evaluator/builtin_functions_test.go b/evaluator/builtin_functions_test.go index d5c7276..b76414a 100644 --- a/evaluator/builtin_functions_test.go +++ b/evaluator/builtin_functions_test.go @@ -741,7 +741,7 @@ func TestRegex(t *testing.T) { {`r = regex(""); r.str()`, ""}, {`regex("a").str()`, "a"}, {`r = regex("a(x+)b"); r.str()`, "a(x+)b"}, - {`regex("won't(work")`, ""} + {`regex(")")`, "error parsing regexp: unexpected ): `)`"}, } testBuiltinFunction(tests, t) @@ -755,6 +755,7 @@ func TestMatches(t *testing.T) { {`"ax*b".matches("ax")`, false}, {`r = regex("ax*b"); r.matches("xb")`, false}, {`"ax+b".matches("ab")`, false}, + {`")".matches("x")`, "error parsing regexp: unexpected ): `)`"}, } testBuiltinFunction(tests, t) @@ -767,6 +768,7 @@ func TestMatch(t *testing.T) { {`r = regex("a(y*)b"); r.match("xaybz")`, []string{"ayb", "y"}}, {`"abc".match("xyz")`, nil}, {`r = regex("a(y+)b"); r.match("ab")`, nil}, + {`")".match("x")`, "error parsing regexp: unexpected ): `)`"}, } testBuiltinFunction(tests, t) @@ -780,6 +782,7 @@ func TestMatchAll(t *testing.T) { {`"a(x*)b".match_all("a ab ax xb axb").len()`, 2}, {`"a(x*)b".match_all("a ab ax xb axb")[0]`, []string{"ab", ""}}, {`"a(x*)b".match_all("a ab ax xb axb")[1]`, []string{"axb", "x"}}, + {`")".match_all("x")`, "error parsing regexp: unexpected ): `)`"}, } testBuiltinFunction(tests, t) @@ -792,6 +795,8 @@ func TestReplaceMatch(t *testing.T) { {`".".replace_match("a", f(x) {})`, ""}, {`r = regex("a(x*)b"); r.replace_match("a ab axb axxxb", ".")`, "a . . ."}, {`"a(x*)b".replace_match("a ab axb axxxb", f(x) {x.len()})`, "a 2 3 5"}, + {`"a(x*)b".replace_match("a ab axb axxxb", "\$1")`, "a x xxx"}, + {`")".replace_match("x", ".")`, "error parsing regexp: unexpected ): `)`"}, } testBuiltinFunction(tests, t) diff --git a/evaluator/functions.go b/evaluator/functions.go index 45b615e..e6c40f7 100644 --- a/evaluator/functions.go +++ b/evaluator/functions.go @@ -2012,7 +2012,7 @@ func regexFn(tok token.Token, env *object.Environment, args ...object.Object) ob re, nok := regexp.Compile(args[0].(*object.String).Value) if nok != nil { - return newError(tok, "invalid regular expression \"%s\"", nok.Error()) + return newError(tok, "%s", nok.Error()) } return &object.Regex{Token: tok, Value: re, Source: args[0].(*object.String).Value} @@ -2041,8 +2041,8 @@ func matchesFn(tok token.Token, env *object.Environment, args ...object.Object) } re := getRegexpObj(args[0]) - if re.Type() == object.ERROR_OBJ { - return err; + if isError(re) { + return re; } m := re.(*object.Regex).Value.MatchString(args[1].(*object.String).Value) @@ -2058,8 +2058,8 @@ func matchFn(tok token.Token, env *object.Environment, args ...object.Object) ob } re := getRegexpObj(args[0]) - if re.Type() == object.ERROR_OBJ { - return err; + if isError(re) { + return re; } match := re.(*object.Regex).Value.FindStringSubmatch(args[1].(*object.String).Value) @@ -2085,8 +2085,8 @@ func match_allFn(tok token.Token, env *object.Environment, args ...object.Object } re := getRegexpObj(args[0]) - if re.Type() == object.ERROR_OBJ { - return err; + if isError(re) { + return re; } allMatches := re.(*object.Regex).Value.FindAllStringSubmatch(args[1].(*object.String).Value, -1) @@ -2119,8 +2119,8 @@ func replace_matchFn(tok token.Token, env *object.Environment, args ...object.Ob } re := getRegexpObj(args[0]) - if re.Type() == object.ERROR_OBJ { - return err; + if isError(re) { + return re; } var s string; From ffd88608b9c85281eecf8758dc8c81a407df07b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gunnar=20Z=C3=B6tl?= Date: Thu, 2 Oct 2025 00:09:03 +0200 Subject: [PATCH 8/9] replace_match() now also supports builtins replace_match can now be called with a builtin as the third argument also, a test for this initial test for regex() now returns the type so we can see that the correct type is actually created --- evaluator/builtin_functions_test.go | 3 ++- evaluator/functions.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/evaluator/builtin_functions_test.go b/evaluator/builtin_functions_test.go index b76414a..4f0b093 100644 --- a/evaluator/builtin_functions_test.go +++ b/evaluator/builtin_functions_test.go @@ -738,7 +738,7 @@ func TestPartition(t *testing.T) { func TestRegex(t *testing.T) { tests := []Tests{ - {`r = regex(""); r.str()`, ""}, + {`r = regex(""); r.type()`, "REGEX"}, {`regex("a").str()`, "a"}, {`r = regex("a(x+)b"); r.str()`, "a(x+)b"}, {`regex(")")`, "error parsing regexp: unexpected ): `)`"}, @@ -795,6 +795,7 @@ func TestReplaceMatch(t *testing.T) { {`".".replace_match("a", f(x) {})`, ""}, {`r = regex("a(x*)b"); r.replace_match("a ab axb axxxb", ".")`, "a . . ."}, {`"a(x*)b".replace_match("a ab axb axxxb", f(x) {x.len()})`, "a 2 3 5"}, + {`"a(x*)b".replace_match("a ab axb axxxb", len)`, "a 2 3 5"}, {`"a(x*)b".replace_match("a ab axb axxxb", "\$1")`, "a x xxx"}, {`")".replace_match("x", ".")`, "error parsing regexp: unexpected ): `)`"}, } diff --git a/evaluator/functions.go b/evaluator/functions.go index e6c40f7..87d4f24 100644 --- a/evaluator/functions.go +++ b/evaluator/functions.go @@ -2113,7 +2113,7 @@ func match_allFn(tok token.Token, env *object.Environment, args ...object.Object // replace_match(pattern, "abc", "repl") or replace_match(pattern, "abc", fn) func replace_matchFn(tok token.Token, env *object.Environment, args ...object.Object) object.Object { - err := validateArgs(tok, "last_index", args, 3, [][]string{{object.STRING_OBJ, object.REGEX_OBJ}, {object.STRING_OBJ}, {object.STRING_OBJ, object.FUNCTION_OBJ}}) + err := validateArgs(tok, "last_index", args, 3, [][]string{{object.STRING_OBJ, object.REGEX_OBJ}, {object.STRING_OBJ}, {object.STRING_OBJ, object.FUNCTION_OBJ, object.BUILTIN_OBJ}}) if err != nil { return err } From 90c92f6f978a088b9efa8261a5280ff9718417a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gunnar=20Z=C3=B6tl?= Date: Thu, 2 Oct 2025 00:11:11 +0200 Subject: [PATCH 9/9] ran go fmt on the files I changed --- evaluator/functions.go | 60 +++++++++++++++++++++--------------------- object/object.go | 12 ++++----- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/evaluator/functions.go b/evaluator/functions.go index 87d4f24..6e18557 100644 --- a/evaluator/functions.go +++ b/evaluator/functions.go @@ -13,10 +13,10 @@ import ( "os/exec" "os/user" "path/filepath" + "regexp" "sort" "strconv" "strings" - "regexp" "time" "unicode" @@ -434,26 +434,26 @@ func GetFns() map[string]*object.Builtin { Doc: "returns a string compiled to a regular expression", }, // matches(pattern, "abc") - "matches": &object.Builtin { - Types: []string{object.STRING_OBJ,object.REGEX_OBJ}, + "matches": &object.Builtin{ + Types: []string{object.STRING_OBJ, object.REGEX_OBJ}, Fn: matchesFn, Doc: "checks whether a string matches a pattern", }, // match(pattern, "abc") - "match": &object.Builtin { - Types: []string{object.STRING_OBJ,object.REGEX_OBJ}, + "match": &object.Builtin{ + Types: []string{object.STRING_OBJ, object.REGEX_OBJ}, Fn: matchFn, Doc: "match a pattern against a string and return the first match and all submatches", }, // match_all(pattern, "abc") - "match_all": &object.Builtin { - Types: []string{object.STRING_OBJ,object.REGEX_OBJ}, + "match_all": &object.Builtin{ + Types: []string{object.STRING_OBJ, object.REGEX_OBJ}, Fn: match_allFn, Doc: "match a pattern against a string and return the all matches and their submatches", }, // replace_match(pattern, "abc", "repl") or replace_match(pattern, "abc", fn) - "replace_match": &object.Builtin { - Types: []string{object.STRING_OBJ,object.REGEX_OBJ}, + "replace_match": &object.Builtin{ + Types: []string{object.STRING_OBJ, object.REGEX_OBJ}, Fn: replace_matchFn, Doc: "replace all matches of a pattern in a string", }, @@ -2019,17 +2019,17 @@ func regexFn(tok token.Token, env *object.Environment, args ...object.Object) ob } func getRegexpObj(obj object.Object) object.Object { - switch (obj.Type()) { - case object.REGEX_OBJ: - return obj - case object.STRING_OBJ: - re, err := regexp.Compile(obj.(*object.String).Value) - if err != nil { - return newError(tok, "%s", err) - } - return &object.Regex{Token: tok, Value: re, Source: obj.(*object.String).Value} - default: - return newError(tok, "invalid argument type"); // should not get here + switch obj.Type() { + case object.REGEX_OBJ: + return obj + case object.STRING_OBJ: + re, err := regexp.Compile(obj.(*object.String).Value) + if err != nil { + return newError(tok, "%s", err) + } + return &object.Regex{Token: tok, Value: re, Source: obj.(*object.String).Value} + default: + return newError(tok, "invalid argument type") // should not get here } } @@ -2042,7 +2042,7 @@ func matchesFn(tok token.Token, env *object.Environment, args ...object.Object) re := getRegexpObj(args[0]) if isError(re) { - return re; + return re } m := re.(*object.Regex).Value.MatchString(args[1].(*object.String).Value) @@ -2059,18 +2059,18 @@ func matchFn(tok token.Token, env *object.Environment, args ...object.Object) ob re := getRegexpObj(args[0]) if isError(re) { - return re; + return re } match := re.(*object.Regex).Value.FindStringSubmatch(args[1].(*object.String).Value) length := len(match) if length == 0 { - return NULL; + return NULL } newElements := make([]object.Object, length, length) - for i, s := range(match) { + for i, s := range match { newElements[i] = &object.String{Token: tok, Value: s} } @@ -2086,22 +2086,22 @@ func match_allFn(tok token.Token, env *object.Environment, args ...object.Object re := getRegexpObj(args[0]) if isError(re) { - return re; + return re } allMatches := re.(*object.Regex).Value.FindAllStringSubmatch(args[1].(*object.String).Value, -1) length := len(allMatches) if length == 0 { - return NULL; + return NULL } newMatches := make([]object.Object, length, length) - for i, match := range(allMatches) { + for i, match := range allMatches { length := len(match) newElements := make([]object.Object, length, length) - for j, s := range(match) { + for j, s := range match { newElements[j] = &object.String{Token: tok, Value: s} } @@ -2120,10 +2120,10 @@ func replace_matchFn(tok token.Token, env *object.Environment, args ...object.Ob re := getRegexpObj(args[0]) if isError(re) { - return re; + return re } - var s string; + var s string if args[2].Type() == object.STRING_OBJ { s = re.(*object.Regex).Value.ReplaceAllString(args[1].(*object.String).Value, args[2].(*object.String).Value) } else { diff --git a/object/object.go b/object/object.go index d3be2cc..ec7ac33 100644 --- a/object/object.go +++ b/object/object.go @@ -4,10 +4,10 @@ import ( "bytes" "fmt" "os/exec" + "regexp" "sort" "strconv" "strings" - "regexp" "sync" "github.com/abs-lang/abs/ast" @@ -25,7 +25,7 @@ const ( NUMBER_OBJ = "NUMBER" BOOLEAN_OBJ = "BOOLEAN" STRING_OBJ = "STRING" - REGEX_OBJ = "REGEX" + REGEX_OBJ = "REGEX" RETURN_VALUE_OBJ = "RETURN_VALUE" @@ -232,14 +232,14 @@ func (s *String) HashKey() HashKey { } type Regex struct { - Token token.Token - Value *regexp.Regexp + Token token.Token + Value *regexp.Regexp Source string } func (r *Regex) Type() ObjectType { return REGEX_OBJ } -func (r *Regex) Inspect() string { return r.Source } -func (r *Regex) Json() string { return `"` + strings.ReplaceAll(r.Inspect(), `"`, `\"`) + `"` } +func (r *Regex) Inspect() string { return r.Source } +func (r *Regex) Json() string { return `"` + strings.ReplaceAll(r.Inspect(), `"`, `\"`) + `"` } // Function that ensure a mutex // instance is created on the