Repository: benhoyt/goawk (go)

Issue:
Regular expression does not match multi-line string
Regular expression does not match multi-line string.

```console
$ goawk 'BEGIN{VAR="a\nb"; print match(VAR, /^a.*b$/)}'
0
$ goawk 'BEGIN{VAR="a\nb"; print match(VAR, /(?s)^a.*b$/)}'
1

$ gawk 'BEGIN{VAR="a\nb"; print match(VAR, /^a.*b$/)}'
1
$ mawk 'BEGIN{VAR="a\nb"; print match(VAR, /^a.*b$/)}'
1
```

Pull request description:
Make regexes match multi-line strings like other AWKs
Fixes #130

Patch:
diff --git a/internal/compiler/compiler.go b/internal/compiler/compiler.go
index 2cf7848..40b2f89 100644
--- a/internal/compiler/compiler.go
+++ b/internal/compiler/compiler.go
@@ -945,11 +945,18 @@ func (c *compiler) regexIndex(r string) int {
 		return index // reuse existing constant
 	}
 	index := len(c.program.Regexes)
-	c.program.Regexes = append(c.program.Regexes, regexp.MustCompile(r))
+	c.program.Regexes = append(c.program.Regexes, regexp.MustCompile(AddRegexFlags(r)))
 	c.indexes.regexes[r] = index
 	return index
 }
 
+// AddRegexFlags add the necessary flags to regex to make it work like other
+// AWKs (exported so we can also use this in the interpreter).
+func AddRegexFlags(regex string) string {
+	// "s" flag lets . match \n (multi-line matching like other AWKs)
+	return "(?s:" + regex + ")"
+}
+
 func (c *compiler) binaryOp(op lexer.Token) {
 	var opcode Opcode
 	switch op {
diff --git a/interp/interp.go b/interp/interp.go
index 010d50d..8a2324b 100644
--- a/interp/interp.go
+++ b/interp/interp.go
@@ -764,7 +764,7 @@ func (p *interp) setSpecial(index int, v value) error {
 	case ast.V_FS:
 		p.fieldSep = p.toString(v)
 		if utf8.RuneCountInString(p.fieldSep) > 1 { // compare to interp.ensureFields
-			re, err := regexp.Compile(p.fieldSep)
+			re, err := regexp.Compile(compiler.AddRegexFlags(p.fieldSep))
 			if err != nil {
 				return newError("invalid regex %q: %s", p.fieldSep, err)
 			}
@@ -786,7 +786,7 @@ func (p *interp) setSpecial(index int, v value) error {
 			sep := regexp.QuoteMeta(p.recordSep) // not strictly necessary as no multi-byte chars are regex meta chars
 			p.recordSepRegex = regexp.MustCompile(sep)
 		default:
-			re, err := regexp.Compile(p.recordSep)
+			re, err := regexp.Compile(compiler.AddRegexFlags(p.recordSep))
 			if err != nil {
 				return newError("invalid regex %q: %s", p.recordSep, err)
 			}
@@ -946,7 +946,7 @@ func (p *interp) compileRegex(regex string) (*regexp.Regexp, error) {
 	if re, ok := p.regexCache[regex]; ok {
 		return re, nil
 	}
-	re, err := regexp.Compile(regex)
+	re, err := regexp.Compile(compiler.AddRegexFlags(regex))
 	if err != nil {
 		return nil, newError("invalid regex %q: %s", regex, err)
 	}


Test patch:
diff --git a/interp/interp_test.go b/interp/interp_test.go
index b039a97..c973cf3 100644
--- a/interp/interp_test.go
+++ b/interp/interp_test.go
@@ -267,6 +267,7 @@ BEGIN {
 	{`{ print /foo/ }`, "food\nfoo\nxfooz\nbar\n", "1\n1\n1\n0\n", "", ""},
 	{`/[a-/`, "foo", "", "parse error at 1:1: error parsing regexp: missing closing ]: `[a-`", "terminated"},
 	{`/=foo/`, "=foo", "=foo\n", "", ""},
+	{`BEGIN { RS="x" } /^a.*c$/`, "a\nb\nc", "a\nb\nc\n", "", ""},
 	{`BEGIN { print "-12"+0, "+12"+0, " \t\r\n7foo"+0, ".5"+0, "5."+0, "+."+0 }`, "", "-12 12 7 0.5 5 0\n", "", ""},
 	{`BEGIN { print "1e3"+0, "1.2e-1"+0, "1e+1"+0, "1e"+0, "1e+"+0 }`, "", "1000 0.12 10 1 1\n", "", ""},
 	{`BEGIN { print -(11102200000000000000000000000000000000 1040000) }  # !gawk - gawk supports big numbers`,
@@ -320,6 +321,8 @@ BEGIN {
 		"id␟name␟age␞1␟Bob \"Billy\" Smith␟42␞2␟Jane\nBrown␟37",
 		"id,name,age\n1,Bob \"Billy\" Smith,42\n2,Jane\nBrown,37\n", "", ""},
 	{`BEGIN { FS="\\" } { print $1, $2 }`, "a\\b", "a b\n", "", ""},
+	{`BEGIN { RS="x"; FS=",.*," } { for (i=1; i<=NF; i++) print $i }`, "one,\n,two", "one\ntwo\n", "", ""},
+	{`BEGIN { FS="x"; RS=",.*," } { print }`, "one,\n,two", "one\ntwo\n", "", ""},
 	{`{ print NF }`, "\na\nc d\ne f g", "0\n1\n2\n3\n", "", ""},
 	{`BEGIN { NR = 123; print NR }`, "", "123\n", "", ""},
 	{`{ print NR, $0 }`, "a\nb\nc", "1 a\n2 b\n3 c\n", "", ""},
@@ -494,6 +497,7 @@ BEGIN {
 	{`BEGIN { print match("x food y", "fo"), RSTART, RLENGTH }`, "", "3 3 2\n", "", ""},
 	{`BEGIN { print match("x food y", "fox"), RSTART, RLENGTH }`, "", "0 0 -1\n", "", ""},
 	{`BEGIN { print match("x food y", /[fod]+/), RSTART, RLENGTH }`, "", "3 3 4\n", "", ""},
+	{`BEGIN { print match("a\nb\nc", /^a.*c$/), RSTART, RLENGTH }`, "", "1 1 5\n", "", ""},
 	{`{ print length, length(), length("buzz"), length("") }`, "foo bar", "7 7 4 0\n", "", ""},
 	{`BEGIN { print index("foo", "f"), index("foo0", 0), index("foo", "o"), index("foo", "x") }`, "", "1 4 2 0\n", "", ""},
 	{`BEGIN { print atan2(1, 0.5), atan2(-1, 0) }`, "", "1.10715 -1.5708\n", "", ""},