Repository: skx/monkey (go)

Issue:
Regex Built In should avoid MustCompile to avoid potential panic
https://github.com/skx/monkey/blob/master/evaluator/stdlib_core.go#L174C59-L175C2

Instead of using MustCompile, you probably should use Compile so you can capture the error and return it. Potentially even caching the regex compiled.


```go
var regexCacheLock sync.Mutex
var regexCache = map[string]*regexp.Regexp{}

func compileRegex(reg string) (*regexp.Regexp, error) {
    regexCacheLock.Lock()
    defer regexCacheLock.Unlock()
  
    reg, ok := regexCache[reg]
    if !ok {
        reg, err = regexp.MustCompile(reg)
        if err != nil {
            return nil, err
        }
        regexCache[reg] = reg
    }
  
    return reg, nil
}

...

// regular expression match
func matchFun(args ...object.Object) object.Object {

    // reg := regexp.MustCompile(args[0].(*object.String).Value)
    // Feel free to replace `compileRegex` with `regexp.Compile` if you dont want to use the cache.
    reg, err := compileRegex(args[0].(*object.String).Value)
    if err != nil {
        return newError("failed to compile regex: %v", err)
    }
```

Pull request description:
94 - Ensure bogus regexps are caught
We previously handled malformed regular expressions when using them as literals, but it was noted by @DeadlySurgeon that the `match` function had no such handling.

This pull-request will close #94 by ensuring that bogus regular expressions are caught even in the `match` function.

Patch:
diff --git a/evaluator/stdlib_core.go b/evaluator/stdlib_core.go
index 3823354..21593ac 100644
--- a/evaluator/stdlib_core.go
+++ b/evaluator/stdlib_core.go
@@ -171,7 +171,11 @@ func matchFun(args ...object.Object) object.Object {
 	//
 	// Compile and match
 	//
-	reg := regexp.MustCompile(args[0].(*object.String).Value)
+	reg, err := regexp.Compile(args[0].(*object.String).Value)
+	if err != nil {
+		return newError("failed to compile regexp %s: %s",
+			args[0].Inspect(), err)
+	}
 	res := reg.FindStringSubmatch(args[1].(*object.String).Value)
 
 	if len(res) > 0 {


Test patch:
diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go
index 454e4b0..757efbc 100644
--- a/evaluator/evaluator_test.go
+++ b/evaluator/evaluator_test.go
@@ -688,3 +688,36 @@ return total;
 	}
 
 }
+
+// TestIssue94 tests #94 - that malformed regexps are caught
+func TestIssue94(t *testing.T) {
+	literal := `
+name = "Steve"
+
+if ( name ~= /+/i ) { puts( "Hello\n" ); }
+`
+	ev1 := testEval(literal)
+	er1, ok1 := ev1.(*object.Error)
+	if !ok1 {
+		t.Errorf("Expected an error with a bogus regexp, got none")
+	}
+
+	if !strings.Contains(er1.Inspect(), "parsing regexp") {
+		t.Errorf("Got an error, but not the right one:%v", er1.Inspect())
+	}
+
+	match := `
+name = "Steve"
+
+if (match( "+", name) ) { puts( "Hello\n" ); }
+`
+	ev2 := testEval(match)
+	er2, ok2 := ev2.(*object.Error)
+	if !ok2 {
+		t.Errorf("Expected an error with a bogus regexp, got none")
+	}
+	if !strings.Contains(er2.Inspect(), "parsing regexp") {
+		t.Errorf("Got an error, but not the right one:%v", er2.Inspect())
+	}
+
+}