diff --git a/regexp_re2_test.go b/regexp_re2_test.go index 1d6a49a..f66aafa 100644 --- a/regexp_re2_test.go +++ b/regexp_re2_test.go @@ -96,3 +96,26 @@ func TestRE2NamedAscii_Concat(t *testing.T) { t.Fatal("Expected match") } } + +func TestRE2Dollar_Singleline(t *testing.T) { + // PCRE allows for \n after the $ and RE2 doesn't + r := MustCompile(`^ac$\n`, RE2) + if m, _ := r.MatchString("ac"); m { + t.Fatal("Expected no match") + } + if m, _ := r.MatchString("ac\n"); m { + t.Fatal("Expected no match") + } +} + +func TestRE2Dollar_Multiline(t *testing.T) { + r := MustCompile(`^ac$\n`, RE2|Multiline) + if m, _ := r.MatchString("ac"); m { + t.Fatal("Expected no match") + } + if m, err := r.MatchString("ac\n"); err != nil { + t.Fatal(err) + } else if !m { + t.Fatal("Expected match") + } +} diff --git a/regexp_test.go b/regexp_test.go index 2811868..f9031f1 100644 --- a/regexp_test.go +++ b/regexp_test.go @@ -758,6 +758,25 @@ func TestECMANegateRange(t *testing.T) { } } +func TestDollar(t *testing.T) { + // PCRE/C# allow \n to match to $ at end-of-string in singleline mode... + // a weird edge-case kept for compatibility, ECMAScript/RE2 mode don't allow it + re := MustCompile(`ac$`, 0) + if m, err := re.MatchString("ac\n"); err != nil { + t.Fatal(err) + } else if !m { + t.Fatal("Expected match") + } +} +func TestECMADollar(t *testing.T) { + re := MustCompile(`ac$`, ECMAScript) + if m, err := re.MatchString("ac\n"); err != nil { + t.Fatal(err) + } else if m { + t.Fatal("Expected no match") + } +} + func TestThreeByteUnicode_InputOnly(t *testing.T) { // confirm the bmprefix properly ignores 3-byte unicode in the input value // this used to panic diff --git a/runner.go b/runner.go index 2d84a93..1a3fd0f 100644 --- a/runner.go +++ b/runner.go @@ -566,9 +566,22 @@ func (r *runner) execute() error { continue case syntax.EndZ: - if r.rightchars() > 1 || r.rightchars() == 1 && r.charAt(r.textPos()) != '\n' { + rchars := r.rightchars() + if rchars > 1 { break } + // RE2 and EcmaScript define $ as "asserts position at the end of the string" + // PCRE/.NET adds "or before the line terminator right at the end of the string (if any)" + if (r.re.options & (RE2 | ECMAScript)) != 0 { + // RE2/Ecmascript mode + if rchars > 0 { + break + } + } else if rchars == 1 && r.charAt(r.textPos()) != '\n' { + // "regular" mode + break + } + r.advance(0) continue