github.com/benhoyt/goawk@v1.8.1/interp/interp_test.go (about)

     1  // Tests for GoAWK interpreter.
     2  package interp_test
     3  
     4  import (
     5  	"bytes"
     6  	"errors"
     7  	"flag"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"os"
    11  	"os/exec"
    12  	"reflect"
    13  	"strings"
    14  	"sync"
    15  	"testing"
    16  
    17  	"github.com/benhoyt/goawk/interp"
    18  	"github.com/benhoyt/goawk/parser"
    19  )
    20  
    21  var (
    22  	awkExe string
    23  )
    24  
    25  func TestMain(m *testing.M) {
    26  	flag.StringVar(&awkExe, "awk", "gawk", "awk executable name")
    27  	flag.Parse()
    28  	os.Exit(m.Run())
    29  }
    30  
    31  // Note: a lot of these are really parser tests too.
    32  func TestInterp(t *testing.T) {
    33  	longLine := strings.Repeat("x", 70000)
    34  
    35  	tests := []struct {
    36  		src    string // if this includes "!awk" or "!gawk" those interpreters won't be run
    37  		in     string
    38  		out    string
    39  		err    string // error from GoAWK must equal this
    40  		awkErr string // error from awk/gawk must contain this
    41  	}{
    42  		// BEGIN and END work correctly
    43  		{`BEGIN { print "b" }`, "", "b\n", "", ""},
    44  		{`BEGIN { print "b" }`, "foo", "b\n", "", ""},
    45  		{`END { print "e" }`, "", "e\n", "", ""},
    46  		{`END { print "e" }`, "foo", "e\n", "", ""},
    47  		{`BEGIN { print "b"} END { print "e" }`, "", "b\ne\n", "", ""},
    48  		{`BEGIN { print "b"} END { print "e" }`, "foo", "b\ne\n", "", ""},
    49  		{`BEGIN { print "b"} $0 { print NR } END { print "e" }`, "foo", "b\n1\ne\n", "", ""},
    50  		{`BEGIN { printf "x" }; BEGIN { printf "y" }`, "", "xy", "", ""},
    51  
    52  		// Patterns
    53  		{`$0`, "foo\n\nbar", "foo\nbar\n", "", ""},
    54  		{`{ print $0 }`, "foo\n\nbar", "foo\n\nbar\n", "", ""},
    55  		{`$1=="foo"`, "foo\n\nbar", "foo\n", "", ""},
    56  		{`$1==42`, "foo\n42\nbar", "42\n", "", ""},
    57  		{`$1=="42"`, "foo\n42\nbar", "42\n", "", ""},
    58  		{`/foo/`, "foo\nx\nfood\nxfooz\nbar", "foo\nfood\nxfooz\n", "", ""},
    59  		{`NR==2, NR==4`, "1\n2\n3\n4\n5\n6\n", "2\n3\n4\n", "", ""},
    60  		{`
    61  NR==2, NR==4 { print $0 }
    62  NR==3, NR==5 { print NR }
    63  `, "a\nb\nc\nd\ne\nf\ng", "b\nc\n3\nd\n4\n5\n", "", ""},
    64  
    65  		// print and printf statements
    66  		{`BEGIN { print "x", "y" }`, "", "x y\n", "", ""},
    67  		{`BEGIN { print OFS; OFS = ","; print "x", "y" }`, "", " \nx,y\n", "", ""},
    68  		{`BEGIN { print ORS; ORS = "."; print "x", "y" }`, "", "\n\nx y.", "", ""},
    69  		{`BEGIN { print ORS; ORS = ""; print "x", "y" }`, "", "\n\nx y", "", ""},
    70  		{`{ print; print }`, "foo", "foo\nfoo\n", "", ""},
    71  		{`BEGIN { print; print }`, "", "\n\n", "", ""},
    72  		{`BEGIN { printf "%% %d %x %c %f %s", 42, 42, 42, 42, 42 }`, "", "% 42 2a * 42.000000 42", "", ""},
    73  		{`BEGIN { printf "%3d", 42 }`, "", " 42", "", ""},
    74  		{`BEGIN { printf "%3s", "x" }`, "", "  x", "", ""},
    75  		// {`BEGIN { printf "%.1g", 42 }`, "", "4e+01", "", ""}, // TODO: comment out for now, for some reason gives "4e+001" on Windows
    76  		{`BEGIN { printf "%d", 12, 34 }`, "", "12", "", ""},
    77  		{`BEGIN { printf "%d" }`, "", "", "format error: got 0 args, expected 1", "not enough arg"},
    78  		// Our %c handling is mostly like awk's, except for multiples
    79  		// 256, where awk is weird and we're like mawk
    80  		{`BEGIN { printf "%c", 0 }`, "", "\x00", "", ""},
    81  		{`BEGIN { printf "%c", 127 }`, "", "\x7f", "", ""},
    82  		{`BEGIN { printf "%c", 128 }  # !gawk`, "", "\x80", "", ""},
    83  		{`BEGIN { printf "%c", 255 }  # !gawk`, "", "\xff", "", ""},
    84  		{`BEGIN { printf "%c", 256 }  # !awk !gawk`, "", "\x00", "", ""},
    85  		{`BEGIN { printf "%c", "xyz" }`, "", "x", "", ""},
    86  		{`BEGIN { printf "%c", "" }  # !awk`, "", "\x00", "", ""},
    87  		{`BEGIN { printf }  # !awk - doesn't error on this`, "", "", "parse error at 1:16: expected printf args, got none", "printf: no arguments"},
    88  		{`BEGIN { printf("%%%dd", 4) }`, "", "%4d", "", ""},
    89  
    90  		// if and loop statements
    91  		{`BEGIN { if (1) print "t"; }`, "", "t\n", "", ""},
    92  		{`BEGIN { if (0) print "t"; }`, "", "", "", ""},
    93  		{`BEGIN { if (1) print "t"; else print "f" }`, "", "t\n", "", ""},
    94  		{`BEGIN { if (0) print "t"; else print "f" }`, "", "f\n", "", ""},
    95  		{`BEGIN { for (;;) { print "x"; break } }`, "", "x\n", "", ""},
    96  		{`BEGIN { for (;;) { printf "%d ", i; i++; if (i>2) break; } }`, "", "0 1 2 ", "", ""},
    97  		{`BEGIN { for (i=5; ; ) { printf "%d ", i; i++; if (i>8) break; } }`, "", "5 6 7 8 ", "", ""},
    98  		{`BEGIN { for (i=5; ; i++) { printf "%d ", i; if (i>8) break; } }`, "", "5 6 7 8 9 ", "", ""},
    99  		{`BEGIN { for (i=5; i<8; i++) { printf "%d ", i } }`, "", "5 6 7 ", "", ""},
   100  		{`BEGIN { for (i=0; i<10; i++) { if (i < 5) continue; printf "%d ", i } }`, "", "5 6 7 8 9 ", "", ""},
   101  		{`BEGIN { a[1]=1; a[2]=1; for (k in a) { s++; break } print s }`, "", "1\n", "", ""},
   102  		{`BEGIN { a[1]=1; a[2]=1; a[3]=1; for (k in a) { if (k==2) continue; s++ } print s }`, "", "2\n", "", ""},
   103  		{`BEGIN { while (i<3) { i++; s++; break } print s }`, "", "1\n", "", ""},
   104  		{`BEGIN { while (i<3) { i++; if (i==2) continue; s++ } print s }`, "", "2\n", "", ""},
   105  		{`BEGIN { do { i++; s++; break } while (i<3); print s }`, "", "1\n", "", ""},
   106  		{`BEGIN { do { i++; if (i==2) continue; s++ } while (i<3); print s }`, "", "2\n", "", ""},
   107  		{`BEGIN { a["x"] = 3; a["y"] = 4; for (k in a) x += a[k]; print x }`, "", "7\n", "", ""},
   108  		{`BEGIN { while (i < 5) { print i; i++ } }`, "", "\n1\n2\n3\n4\n", "", ""},
   109  		{`BEGIN { do { print i; i++ } while (i < 5) }`, "", "\n1\n2\n3\n4\n", "", ""},
   110  		{`BEGIN { for (i=0; i<10; i++); printf "x" }`, "", "x", "", ""},
   111  		// regression tests for break and continue with nested loops
   112  		{`
   113  BEGIN {
   114  	for (i = 0; i < 1; i++) {
   115  		for (j = 0; j < 1; j++) {
   116  			print i, j
   117  		}
   118  		break
   119  	}
   120  }
   121  `, "", "0 0\n", "", ""},
   122  		{`
   123  BEGIN {
   124  	for (i = 0; i < 1; i++) {
   125  		for (j = 0; j < 1; j++) {
   126  			print i, j
   127  		}
   128  		continue
   129  	}
   130  }
   131  `, "", "0 0\n", "", ""},
   132  
   133  		// next statement
   134  		{`{ if (NR==2) next; print }`, "a\nb\nc", "a\nc\n", "", ""},
   135  		{`BEGIN { next }`, "", "", "parse error at 1:9: next can't be in BEGIN or END", "BEGIN"},
   136  		{`END { next }`, "", "", "parse error at 1:7: next can't be in BEGIN or END", "END"},
   137  
   138  		// Arrays, "in", and delete
   139  		{`BEGIN { a["x"] = 3; print "x" in a, "y" in a }`, "", "1 0\n", "", ""},
   140  		{`BEGIN { a["x"] = 3; a["y"] = 4; delete a["x"]; for (k in a) print k, a[k] }`, "", "y 4\n", "", ""},
   141  		{`BEGIN { a["x"] = 3; a["y"] = 4; for (k in a) delete a[k]; for (k in a) print k, a[k] }`, "", "", "", ""},
   142  		{`BEGIN { a["x"]; "y" in a; for (k in a) print k, a[k] }`, "", "x \n", "", ""},
   143  		{`BEGIN { a[] }`, "", "", "parse error at 1:11: expected expression instead of ]", "syntax error"},
   144  		{`BEGIN { delete a[] }`, "", "", "parse error at 1:18: expected expression instead of ]", "syntax error"},
   145  		{`BEGIN { a["x"] = 3; a["y"] = 4; delete a; for (k in a) print k, a[k] }`, "", "", "", ""},
   146  
   147  		// Unary expressions: ! + -
   148  		{`BEGIN { print !42, !1, !0, !!42, !!1, !!0 }`, "", "0 0 1 1 1 0\n", "", ""},
   149  		{`BEGIN { print !42, !1, !0, !!42, !!1, !!0 }`, "", "0 0 1 1 1 0\n", "", ""},
   150  		{`BEGIN { print +4, +"3", +0, +-3, -3, - -4, -"3" }`, "", "4 3 0 -3 -3 4 -3\n", "", ""},
   151  		// TODO: {`BEGIN { $0="0"; print !$0 }`, "", "0\n", "", ""},
   152  		{`BEGIN { $0="1"; print !$0 }`, "", "0\n", "", ""},
   153  		{`{ print !$0 }`, "0\n", "1\n", "", ""},
   154  		{`{ print !$0 }`, "1\n", "0\n", "", ""},
   155  		{`!seen[$0]++`, "1\n2\n3\n2\n3\n3\n", "1\n2\n3\n", "", ""},
   156  		{`!seen[$0]--`, "1\n2\n3\n2\n3\n3\n", "1\n2\n3\n", "", ""},
   157  
   158  		// Comparison expressions: == != < <= > >=
   159  		{`BEGIN { print (1==1, 1==0, "1"==1, "1"==1.0) }`, "", "1 0 1 1\n", "", ""},
   160  		{`{ print ($0=="1", $0==1) }`, "1\n1.0\n+1", "1 1\n0 1\n0 1\n", "", ""},
   161  		{`{ print ($1=="1", $1==1) }`, "1\n1.0\n+1", "1 1\n0 1\n0 1\n", "", ""},
   162  		{`BEGIN { print (1!=1, 1!=0, "1"!=1, "1"!=1.0) }`, "", "0 1 0 0\n", "", ""},
   163  		{`{ print ($0!="1", $0!=1) }`, "1\n1.0\n+1", "0 0\n1 0\n1 0\n", "", ""},
   164  		{`{ print ($1!="1", $1!=1) }`, "1\n1.0\n+1", "0 0\n1 0\n1 0\n", "", ""},
   165  		{`BEGIN { print (0<1, 1<1, 2<1, "12"<"2") }`, "", "1 0 0 1\n", "", ""},
   166  		{`{ print ($1<2) }`, "1\n1.0\n+1", "1\n1\n1\n", "", ""},
   167  		{`BEGIN { print (0<=1, 1<=1, 2<=1, "12"<="2") }`, "", "1 1 0 1\n", "", ""},
   168  		{`{ print ($1<=2) }`, "1\n1.0\n+1", "1\n1\n1\n", "", ""},
   169  		{`BEGIN { print (0>1, 1>1, 2>1, "12">"2") }`, "", "0 0 1 0\n", "", ""},
   170  		{`{ print ($1>2) }`, "1\n1.0\n+1", "0\n0\n0\n", "", ""},
   171  		{`BEGIN { print (0>=1, 1>=1, 2>=1, "12">="2") }`, "", "0 1 1 0\n", "", ""},
   172  		{`{ print ($1>=2) }`, "1\n1.0\n+1", "0\n0\n0\n", "", ""},
   173  
   174  		// Short-circuit && and || operators
   175  		{`
   176  function t() { print "t"; return 1 }
   177  function f() { print "f"; return 0 }
   178  BEGIN {
   179  	print f() && f()
   180  	print f() && t()
   181  	print t() && f()
   182  	print t() && t()
   183  }
   184  `, "", "f\n0\nf\n0\nt\nf\n0\nt\nt\n1\n", "", ""},
   185  		{`
   186  function t() { print "t"; return 1 }
   187  function f() { print "f"; return 0 }
   188  BEGIN {
   189  	print f() || f()
   190  	print f() || t()
   191  	print t() || f()
   192  	print t() || t()
   193  }
   194  `, "", "f\nf\n0\nf\nt\n1\nt\n1\nt\n1\n", "", ""},
   195  
   196  		// Other binary expressions: + - * ^ / % CONCAT ~ !~
   197  		{`BEGIN { print 1+2, 1+2+3, 1+-2, -1+2, "1"+"2", 3+.14 }`, "", "3 6 -1 1 3 3.14\n", "", ""},
   198  		{`BEGIN { print 1-2, 1-2-3, 1-+2, -1-2, "1"-"2", 3-.14 }`, "", "-1 -4 -1 -3 -1 2.86\n", "", ""},
   199  		{`BEGIN { print 2*3, 2*3*4, 2*-3, -2*3, "2"*"3", 3*.14 }`, "", "6 24 -6 -6 6 0.42\n", "", ""},
   200  		{`BEGIN { print 2/3, 2/3/4, 2/-3, -2/3, "2"/"3", 3/.14 }`, "", "0.666667 0.166667 -0.666667 -0.666667 0.666667 21.4286\n", "", ""},
   201  		{`BEGIN { print 2%3, 2%3%4, 2%-3, -2%3, "2"%"3", 3%.14 }`, "", "2 2 2 -2 2 0.06\n", "", ""},
   202  		{`BEGIN { print 2^3, 2^3^3, 2^-3, -2^3, "2"^"3", 3^.14 }`, "", "8 134217728 0.125 -8 8 1.16626\n", "", ""},
   203  		{`BEGIN { print 1 2, "x" "yz", 1+2 3+4 }`, "", "12 xyz 37\n", "", ""},
   204  		{`BEGIN { print "food"~/oo/, "food"~/[oO]+d/, "food"~"f", "food"~"F", "food"~0 }`, "", "1 1 1 0 0\n", "", ""},
   205  		{`BEGIN { print "food"!~/oo/, "food"!~/[oO]+d/, "food"!~"f", "food"!~"F", "food"!~0 }`, "", "0 0 0 1 1\n", "", ""},
   206  		{`BEGIN { print 1+2*3/4^5%6 7, (1+2)*3/4^5%6 "7" }`, "", "1.005867 0.008789067\n", "", ""},
   207  
   208  		// Number, string, and regex expressions
   209  		{`BEGIN { print 1, 1., .1, 1e0, -1, 1e }`, "", "1 1 0.1 1 -1 1\n", "", ""},
   210  		{`BEGIN { print '\"' '\'' 'xy' "z" "'" '\"' }`, "", "\"'xyz'\"\n", "", "syntax error"}, // Check support for single-quoted strings
   211  		{`{ print /foo/ }`, "food\nfoo\nxfooz\nbar\n", "1\n1\n1\n0\n", "", ""},
   212  		{`/[a-/`, "foo", "", "parse error at 1:1: error parsing regexp: missing closing ]: `[a-`", "terminated"},
   213  		{`BEGIN { print "-12"+0, "+12"+0, " \t\r\n7foo"+0, ".5"+0, "5."+0, "+."+0 }`, "", "-12 12 7 0.5 5 0\n", "", ""},
   214  		{`BEGIN { print "1e3"+0, "1.2e-1"+0, "1e+1"+0, "1e"+0, "1e+"+0 }`, "", "1000 0.12 10 1 1\n", "", ""},
   215  		{`BEGIN { print -(11102200000000000000000000000000000000 1040000) }  # !gawk - gawk supports big numbers`,
   216  			"", "-inf\n", "", ""},
   217  		{`BEGIN { print atan2(0, 8020020000000e20G-0)}`, "", "0\n", "", ""},
   218  		{`BEGIN { print 1e1000, -1e1000 }  # !gawk`, "", "inf -inf\n", "", ""},
   219  		{`BEGIN { printf "\x0.\x00.\x0A\x10\xff\xFF\x41" }  # !awk`, "", "\x00.\x00.\n\x10\xff\xffA", "", ""},
   220  		{`BEGIN { printf "\x1.\x01.\x0A\x10\xff\xFF\x41" }`, "", "\x01.\x01.\n\x10\xff\xffA", "", ""},
   221  		{`BEGIN { printf "\0\78\7\77\777\0 \141 " }  # !awk`, "", "\x00\a8\a?\xff\x00 a ", "", ""},
   222  		{`BEGIN { printf "\1\78\7\77\777\1 \141 " }`, "", "\x01\a8\a?\xff\x01 a ", "", ""},
   223  
   224  		// Conditional ?: expression
   225  		{`{ print /x/?"t":"f" }`, "x\ny\nxx\nz\n", "t\nf\nt\nf\n", "", ""},
   226  		{`BEGIN { print 1?2?3:4:5, 1?0?3:4:5, 0?2?3:4:5 }`, "", "3 4 5\n", "", ""},
   227  		// TODO: {`BEGIN { $0="0"; print ($0?1:0) }`, "", "1\n", "", ""},
   228  		{`{ print $0?1:0 }`, "0\n", "0\n", "", ""},
   229  		{`{ print $0?1:0 }`, "1\n", "1\n", "", ""},
   230  		{`BEGIN { $0="1"; print ($0?1:0) }`, "", "1\n", "", ""},
   231  		{`BEGIN { print 0?1:0, 1?1:0, ""?1:0, "0"?1:0, "1"?1:0, x?1:0 }`, "", "0 1 0 1 1 0\n", "", ""},
   232  
   233  		// Built-in variables
   234  		// ARGC is tested in goawk_test.go
   235  		{`
   236  BEGIN {
   237  	print CONVFMT, 1.2345678 ""
   238  	CONVFMT = "%.3g"
   239  	print CONVFMT, 1.234567 ""
   240  }`, "", "%.6g 1.23457\n%.3g 1.23\n", "", ""},
   241  		{`BEGIN { FILENAME = "foo"; print FILENAME }`, "", "foo\n", "", ""},
   242  		{`BEGIN { FILENAME = "123.0"; print (FILENAME==123) }`, "", "0\n", "", ""},
   243  		// Other FILENAME behaviour is tested in goawk_test.go
   244  		{`BEGIN { FNR = 123; print FNR }`, "", "123\n", "", ""},
   245  		{`{ print FNR, $0 }`, "a\nb\nc", "1 a\n2 b\n3 c\n", "", ""},
   246  		// Other FNR behaviour is tested in goawk_test.go
   247  		{`BEGIN { print "|" FS "|"; FS="," } { print $1, $2 }`, "a b\na,b\nx,,y", "| |\na b \na b\nx \n", "", ""},
   248  		{`BEGIN { print "|" FS "|"; FS="\\." } { print $1, $2 }`, "a b\na.b\nx..y", "| |\na b \na b\nx \n", "", ""},
   249  		{`BEGIN { FS="\\" } { print $1, $2 }`, "a\\b", "a b\n", "", ""},
   250  		{`{ print NF }`, "\na\nc d\ne f g", "0\n1\n2\n3\n", "", ""},
   251  		{`BEGIN { NR = 123; print NR }`, "", "123\n", "", ""},
   252  		{`{ print NR, $0 }`, "a\nb\nc", "1 a\n2 b\n3 c\n", "", ""},
   253  		{`
   254  BEGIN {
   255  	print OFMT, 1.2345678
   256  	OFMT = "%.3g"
   257  	print OFMT, 1.234567
   258  }`, "", "%.6g 1.23457\n%.3g 1.23\n", "", ""},
   259  		// OFS and ORS are tested above
   260  		{`BEGIN { print RSTART, RLENGTH; RSTART=5; RLENGTH=42; print RSTART, RLENGTH; } `, "",
   261  			"0 0\n5 42\n", "", ""},
   262  		{`BEGIN { print RS }`, "", "\n\n", "", ""},
   263  		{`BEGIN { print RS; RS="|"; print RS }  { print }`, "a b|c d|", "\n\n|\na b\nc d\n", "", ""},
   264  		{`BEGIN { RS=""; FS="\n" }  { printf "%d (%d):\n", NR, NF; for (i=1; i<=NF; i++) print $i }`,
   265  			"a\n\nb\nc",
   266  			"1 (1):\na\n2 (2):\nb\nc\n", "", ""},
   267  		{`BEGIN { RS=""; FS="\n" }  { printf "%d (%d):\n", NR, NF; for (i=1; i<=NF; i++) print $i }`,
   268  			"1\n2\n\na\nb",
   269  			"1 (2):\n1\n2\n2 (2):\na\nb\n", "", ""},
   270  		{`BEGIN { RS=""; FS="\n" }  { printf "%d (%d):\n", NR, NF; for (i=1; i<=NF; i++) print $i }`,
   271  			"a b\nc d\n\ne f\n\n\n  \n\n\ng h\n\n\n",
   272  			"1 (2):\na b\nc d\n2 (1):\ne f\n3 (1):\n  \n4 (1):\ng h\n", "", ""},
   273  		{`BEGIN { RS=""; FS="\n" }  { printf "%d (%d):\n", NR, NF; for (i=1; i<=NF; i++) print $i }`,
   274  			"\n\na b\n\nc d\n",
   275  			"1 (1):\na b\n2 (1):\nc d\n", "", ""},
   276  		{`BEGIN { RS=""; FS="\n" }  { printf "%d (%d):\n", NR, NF; for (i=1; i<=NF; i++) print $i }  # !awk !gawk - they don't handle CR LF with RS==""`,
   277  			"\r\n\r\na b\r\n\r\nc d\r\n",
   278  			"1 (1):\na b\n2 (1):\nc d\n", "", ""},
   279  		{`BEGIN { RS=""; FS="X" }  { printf "%d (%d):\n", NR, NF; for (i=1; i<=NF; i++) printf "%s|", $i }`,
   280  			"aXb\ncXd\n\neXf\n\n\n  \n\n\ngXh\n\n\n",
   281  			"1 (4):\na|b|c|d|2 (2):\ne|f|3 (1):\n  |4 (2):\ng|h|", "", ""},
   282  		{`BEGIN { RS = "" }  { print "got", $0 }`,
   283  			"\n\n\n\n", "", "", ""},
   284  		{`BEGIN { RS="\n" }  { print }`, "a\n\nb\nc", "a\n\nb\nc\n", "", ""},
   285  		{`
   286  BEGIN {
   287  	print SUBSEP
   288  	a[1, 2] = "onetwo"
   289  	print a[1, 2]
   290  	for (k in a) {
   291  		print k, a[k]
   292  	}
   293  	delete a[1, 2]
   294  	SUBSEP = "|"
   295  	print SUBSEP
   296  	a[1, 2] = "onetwo"
   297  	print a[1, 2]
   298  	for (k in a) {
   299  		print k, a[k]
   300  	}
   301  }`, "", "\x1c\nonetwo\n1\x1c2 onetwo\n|\nonetwo\n1|2 onetwo\n", "", ""},
   302  
   303  		// Field expressions and assignment (and interaction with NF)
   304  		{`{ print NF; NF=1; $2="two"; print $0, NF }`, "\n", "0\n two 2\n", "", ""},
   305  		{`{ print NF; NF=2; $2="two"; print $0, NF}`, "\n", "0\n two 2\n", "", ""},
   306  		{`{ print NF; NF=3; $2="two"; print $0, NF}`, "a b c\n", "3\na two c 3\n", "", ""},
   307  		{`{ print; print $1, $3, $NF }`, "a b c d e", "a b c d e\na c e\n", "", ""},
   308  		{`{ print $1,$3; $2="x"; print; print $2 }`, "a b c", "a c\na x c\nx\n", "", ""},
   309  		{`{ print; $0="x y z"; print; print $1, $3 }`, "a b c", "a b c\nx y z\nx z\n", "", ""},
   310  		{`{ print $1^2 }`, "10", "100\n", "", ""},
   311  		{`{ print $-1 }`, "x", "", "field index negative: -1", "field -1"},
   312  		{`{ NF=-1; }  # !awk - awk allows setting negative NF`,
   313  			"x", "", "NF set to negative value: -1", "negative value"},
   314  		{`{ NF=1234567; }`, "x", "", "NF set too large: 1234567", ""},
   315  		{`BEGIN { $1234567=1 }`, "", "", "field index too large: 1234567", ""},
   316  		{`0 in FS  # !awk - doesn't flag this as an error`, "x", "",
   317  			`parse error at 1:6: can't use scalar "FS" as array`, "array"},
   318  		// TODO: I think this is happening because we parse this as ($($0))++ rather than ($($0++))
   319  		// TODO: {`{ $$0++; print $0 }`, "2 3 4", "3\n", "", ""},
   320  		// TODO: {`BEGIN { $0="3 4 5 6 7 8 9"; a=3; print $$a++++; print }`, "", "7\n3 4 6 6 8 8 9\n", "", ""},
   321  
   322  		// Lots of NF tests with different combinations of NF, $, and number
   323  		// of input fields. Some of these cause segmentation faults on awk
   324  		// (but work fine on gawk and mawk).
   325  		{`{ NF=1; $1="x"; print $0; print NF }`, "a", "x\n1\n", "", ""},
   326  		{`{ NF=1; $1="x"; print $0; print NF }`, "a b", "x\n1\n", "", ""},
   327  		{`{ NF=1; $1="x"; print $0; print NF }`, "a b c", "x\n1\n", "", ""},
   328  		{`{ NF=1; $2="x"; print $0; print NF }`, "a", "a x\n2\n", "", ""},
   329  		{`{ NF=1; $2="x"; print $0; print NF }`, "a b", "a x\n2\n", "", ""},
   330  		{`{ NF=1; $2="x"; print $0; print NF }`, "a b c", "a x\n2\n", "", ""},
   331  		{`{ NF=1; $3="x"; print $0; print NF }`, "a", "a  x\n3\n", "", ""},
   332  		{`{ NF=1; $3="x"; print $0; print NF }  # !awk - awk differs from gawk (but gawk seems right)`,
   333  			"a b", "a  x\n3\n", "", ""},
   334  		{`{ NF=1; $3="x"; print $0; print NF }  # !awk - awk differs from gawk (but gawk seems right)`,
   335  			"a b c", "a  x\n3\n", "", ""},
   336  		{`{ NF=2; $1="x"; print $0; print NF }`, "a", "x \n2\n", "", ""},
   337  		{`{ NF=2; $1="x"; print $0; print NF }`, "a b", "x b\n2\n", "", ""},
   338  		{`{ NF=2; $1="x"; print $0; print NF }`, "a b c", "x b\n2\n", "", ""},
   339  		{`{ NF=2; $2="x"; print $0; print NF }`, "a", "a x\n2\n", "", ""},
   340  		{`{ NF=2; $2="x"; print $0; print NF }`, "a b", "a x\n2\n", "", ""},
   341  		{`{ NF=2; $2="x"; print $0; print NF }`, "a b c", "a x\n2\n", "", ""},
   342  		{`{ NF=2; $3="x"; print $0; print NF }`, "a", "a  x\n3\n", "", ""},
   343  		{`{ NF=2; $3="x"; print $0; print NF }`, "a b", "a b x\n3\n", "", ""},
   344  		{`{ NF=2; $3="x"; print $0; print NF }`, "a b c", "a b x\n3\n", "", ""},
   345  		{`{ NF=3; $1="x"; print $0; print NF }  # !awk - segmentation fault`,
   346  			"a", "x  \n3\n", "", ""},
   347  		{`{ NF=3; $1="x"; print $0; print NF }  # !awk - segmentation fault`,
   348  			"a b", "x b \n3\n", "", ""},
   349  		{`{ NF=3; $1="x"; print $0; print NF }`, "a b c", "x b c\n3\n", "", ""},
   350  		{`{ NF=3; $2="x"; print $0; print NF }  # !awk - segmentation fault`,
   351  			"a", "a x \n3\n", "", ""},
   352  		{`{ NF=3; $2="x"; print $0; print NF }  # !awk - segmentation fault`,
   353  			"a b", "a x \n3\n", "", ""},
   354  		{`{ NF=3; $2="x"; print $0; print NF }`, "a b c", "a x c\n3\n", "", ""},
   355  		{`{ NF=3; $3="x"; print $0; print NF }`, "a", "a  x\n3\n", "", ""},
   356  		{`{ NF=3; $3="x"; print $0; print NF }`, "a b", "a b x\n3\n", "", ""},
   357  		{`{ NF=3; $3="x"; print $0; print NF }`, "a b c", "a b x\n3\n", "", ""},
   358  
   359  		// Assignment expressions and vars
   360  		{`BEGIN { print x; x = 4; print x; }`, "", "\n4\n", "", ""},
   361  		{`BEGIN { a["foo"]=1; b[2]="x"; k="foo"; print a[k], b["2"] }`, "", "1 x\n", "", ""},
   362  		{`BEGIN { s+=5; print s; s-=2; print s; s-=s; print s }`, "", "5\n3\n0\n", "", ""},
   363  		{`BEGIN { x=2; x*=x; print x; x*=3; print x }`, "", "4\n12\n", "", ""},
   364  		{`BEGIN { x=6; x/=3; print x; x/=x; print x; x/=.6; print x }`, "", "2\n1\n1.66667\n", "", ""},
   365  		{`BEGIN { x=12; x%=5; print x }`, "", "2\n", "", ""},
   366  		{`BEGIN { x=2; x^=5; print x; x^=0.5; print x }`, "", "32\n5.65685\n", "", ""},
   367  		{`{ $2+=10; print; $3/=2; print }`, "1 2 3", "1 12 3\n1 12 1.5\n", "", ""},
   368  		{`BEGIN { a[2] += 1; a["2"] *= 3; print a[2] }`, "", "3\n", "", ""},
   369  
   370  		// Incr/decr expressions
   371  		{`BEGIN { print x++; print x }`, "", "0\n1\n", "", ""},
   372  		{`BEGIN { print x; print x++; print ++x; print x }`, "", "\n0\n2\n2\n", "", ""},
   373  		{`BEGIN { print x; print x--; print --x; print x }`, "", "\n0\n-2\n-2\n", "", ""},
   374  		{`BEGIN { s++; s++; print s }`, "", "2\n", "", ""},
   375  		{`BEGIN { y=" "; --x[y = y y]; print length(y) }`, "", "2\n", "", ""},
   376  		{`BEGIN { x[y++]++; print y }`, "", "1\n", "", ""},
   377  		{`BEGIN { x[y++] += 3; print y }`, "", "1\n", "", ""},
   378  		{`BEGIN { $(y++)++; print y }`, "", "1\n", "", ""},
   379  
   380  		// Builtin functions
   381  		{`BEGIN { print sin(0), sin(0.5), sin(1), sin(-1) }`, "", "0 0.479426 0.841471 -0.841471\n", "", ""},
   382  		{`BEGIN { print cos(0), cos(0.5), cos(1), cos(-1) }`, "", "1 0.877583 0.540302 0.540302\n", "", ""},
   383  		{`BEGIN { print exp(0), exp(0.5), exp(1), exp(-1) }`, "", "1 1.64872 2.71828 0.367879\n", "", ""},
   384  		{`BEGIN { print log(0), log(0.5), log(1) }`, "", "-inf -0.693147 0\n", "", ""},
   385  		{`BEGIN { print log(-1) }  # !gawk - gawk prints warning for this as well`,
   386  			"", "nan\n", "", ""},
   387  		{`BEGIN { print sqrt(0), sqrt(2), sqrt(4) }`, "", "0 1.41421 2\n", "", ""},
   388  		{`BEGIN { print int(3.5), int("1.9"), int(4), int(-3.6), int("x"), int("") }`, "", "3 1 4 -3 0 0\n", "", ""},
   389  		{`BEGIN { print match("food", "foo"), RSTART, RLENGTH }`, "", "1 1 3\n", "", ""},
   390  		{`BEGIN { print match("x food y", "fo"), RSTART, RLENGTH }`, "", "3 3 2\n", "", ""},
   391  		{`BEGIN { print match("x food y", "fox"), RSTART, RLENGTH }`, "", "0 0 -1\n", "", ""},
   392  		{`BEGIN { print match("x food y", /[fod]+/), RSTART, RLENGTH }`, "", "3 3 4\n", "", ""},
   393  		{`{ print length, length(), length("buzz"), length("") }`, "foo bar", "7 7 4 0\n", "", ""},
   394  		{`BEGIN { print index("foo", "f"), index("foo0", 0), index("foo", "o"), index("foo", "x") }`, "", "1 4 2 0\n", "", ""},
   395  		{`BEGIN { print atan2(1, 0.5), atan2(-1, 0) }`, "", "1.10715 -1.5708\n", "", ""},
   396  		{`BEGIN { print sprintf("%3d", 42) }`, "", " 42\n", "", ""},
   397  		{`BEGIN { print sprintf("%d", 12, 34) }`, "", "12\n", "", ""},
   398  		{`BEGIN { print sprintf("%d") }`, "", "", "format error: got 0 args, expected 1", "not enough arg"},
   399  		{`BEGIN { print sprintf("%d", 12, 34) }`, "", "12\n", "", ""},
   400  		{`BEGIN { print sprintf("% 5d", 42) }`, "", "   42\n", "", ""},
   401  		{`BEGIN { print substr("food", 1) }`, "", "food\n", "", ""},
   402  		{`BEGIN { print substr("food", 1, 2) }`, "", "fo\n", "", ""},
   403  		{`BEGIN { print substr("food", 1, 4) }`, "", "food\n", "", ""},
   404  		{`BEGIN { print substr("food", 1, 8) }`, "", "food\n", "", ""},
   405  		{`BEGIN { print substr("food", 2) }`, "", "ood\n", "", ""},
   406  		{`BEGIN { print substr("food", 2, 2) }`, "", "oo\n", "", ""},
   407  		{`BEGIN { print substr("food", 2, 3) }`, "", "ood\n", "", ""},
   408  		{`BEGIN { print substr("food", 2, 8) }`, "", "ood\n", "", ""},
   409  		{`BEGIN { print substr("food", 0, 8) }`, "", "food\n", "", ""},
   410  		{`BEGIN { print substr("food", -1, 8) }`, "", "food\n", "", ""},
   411  		{`BEGIN { print substr("food", 5, 8) }`, "", "\n", "", ""},
   412  		{`BEGIN { n = split("ab c d ", a); for (i=1; i<=n; i++) print a[i] }`, "", "ab\nc\nd\n", "", ""},
   413  		{`BEGIN { n = split("ab,c,d,", a, ","); for (i=1; i<=n; i++) print a[i] }`, "", "ab\nc\nd\n\n", "", ""},
   414  		{`BEGIN { n = split("ab,c.d,", a, /[,.]/); for (i=1; i<=n; i++) print a[i] }`, "", "ab\nc\nd\n\n", "", ""},
   415  		{`BEGIN { n = split("1 2", a); print (n, a[1], a[2], a[1]==1, a[2]==2) }`, "", "2 1 2 1 1\n", "", ""},
   416  		{`BEGIN { x = "1.2.3"; print sub(/\./, ",", x); print x }`, "", "1\n1,2.3\n", "", ""},
   417  		{`{ print sub(/\./, ","); print $0 }`, "1.2.3", "1\n1,2.3\n", "", ""},
   418  		{`BEGIN { x = "1.2.3"; print gsub(/\./, ",", x); print x }`, "", "2\n1,2,3\n", "", ""},
   419  		{`{ print gsub(/\./, ","); print $0 }`, "1.2.3", "2\n1,2,3\n", "", ""},
   420  		{`{ print gsub(/[0-9]/, "(&)"); print $0 }`, "0123x. 42y", "6\n(0)(1)(2)(3)x. (4)(2)y\n", "", ""},
   421  		{`{ print gsub(/[0-9]+/, "(&)"); print $0 }`, "0123x. 42y", "2\n(0123)x. (42)y\n", "", ""},
   422  		{`{ print gsub(/[0-9]/, "\\&"); print $0 }`, "0123x. 42y", "6\n&&&&x. &&y\n", "", ""},
   423  		{`{ print gsub(/[0-9]/, "\\z"); print $0 }`, "0123x. 42y", "6\n\\z\\z\\z\\zx. \\z\\zy\n", "", ""},
   424  		{`{ print gsub("0", "x\\\\y"); print $0 }  # !awk !gawk -- our behaviour is per POSIX spec (gawk -P and mawk)`,
   425  			"0", "1\nx\\y\n", "", ""},
   426  		{`sub("", "\\e", FS)  # !awk !gawk`, "foo bar\nbaz buz\n", "",
   427  			"invalid regex \"\\\\e \": error parsing regexp: invalid escape sequence: `\\e`", ""},
   428  		{`BEGIN { print tolower("Foo BaR") }`, "", "foo bar\n", "", ""},
   429  		{`BEGIN { print toupper("Foo BaR") }`, "", "FOO BAR\n", "", ""},
   430  		{`
   431  BEGIN {
   432  	srand(1)
   433  	a = rand(); b = rand(); c = rand()
   434  	srand(1)
   435  	x = rand(); y = rand(); z = rand()
   436  	print (a==b, b==c, x==y, y==z)
   437  	print (a==x, b==y, c==z)
   438  }
   439  `, "", "0 0 0 0\n1 1 1\n", "", ""},
   440  		{`
   441  BEGIN {
   442  	for (i = 0; i < 1000; i++) {
   443  		if (rand() < 0.5) n++
   444  	}
   445  	print (n>400)
   446  }
   447  `, "", "1\n", "", ""},
   448  		{`BEGIN { print system("echo foo"); print system("echo bar") }`,
   449  			"", "foo\n0\nbar\n0\n", "", ""},
   450  		{`BEGIN { print system(">&2 echo error") }`,
   451  			"", "error\n0\n", "", ""},
   452  
   453  		// Conditional expressions parse and work correctly
   454  		{`BEGIN { print 0?"t":"f" }`, "", "f\n", "", ""},
   455  		{`BEGIN { print 1?"t":"f" }`, "", "t\n", "", ""},
   456  		{`BEGIN { print (1+2)?"t":"f" }`, "", "t\n", "", ""},
   457  		{`BEGIN { print (1+2?"t":"f") }`, "", "t\n", "", ""},
   458  		{`BEGIN { print(1 ? x="t" : "f"); print x; }`, "", "t\nt\n", "", ""},
   459  
   460  		// Locals vs globals, array params, and recursion
   461  		{`
   462  function f(loc) {
   463  	glob += 1
   464  	loc += 1
   465  	print glob, loc
   466  }
   467  BEGIN {
   468  	glob = 1
   469  	loc = 42
   470  	f(3)
   471  	print loc
   472  	f(4)
   473  	print loc
   474  }
   475  `, "", "2 4\n42\n3 5\n42\n", "", ""},
   476  		{`
   477  function set(a, x, v) {
   478  	a[x] = v
   479  }
   480  function get(a, x) {
   481  	return a[x]
   482  }
   483  BEGIN {
   484  	a["x"] = 1
   485  	set(b, "y", 2)
   486  	for (k in a) print k, a[k]
   487  	print "---"
   488  	for (k in b) print k, b[k]
   489  	print "---"
   490  	print get(a, "x"), get(b, "y")
   491  }
   492  `, "", "x 1\n---\ny 2\n---\n1 2\n", "", ""},
   493  		{`
   494  function fib(n) {
   495  	return n < 3 ? 1 : fib(n-2) + fib(n-1)
   496  }
   497  BEGIN {
   498  	for (i = 1; i <= 7; i++) {
   499  		printf "%d ", fib(i)
   500  	}
   501  }
   502  `, "", "1 1 2 3 5 8 13 ", "", ""},
   503  		{`
   504  function f(a, x) { return a[x] }
   505  function g(b, y) { f(b, y) }
   506  BEGIN { c[1]=2; print f(c, 1); print g(c, 1) }
   507  `, "", "2\n\n", "", ""},
   508  		{`
   509  function g(b, y) { return f(b, y) }
   510  function f(a, x) { return a[x] }
   511  BEGIN { c[1]=2; print f(c, 1); print g(c, 1) }
   512  `, "", "2\n2\n", "", ""},
   513  		{`
   514  function h(b, y) { g(b, y) }
   515  function g(b, y) { f(b, y) }
   516  function f(a, x) { return a[x] }
   517  BEGIN { c[1]=2; print f(c, 1); print g(c, 1) }
   518  `, "", "2\n\n", "", ""},
   519  		{`
   520  function h(b, y) { return g(b, y) }
   521  function g(b, y) { return f(b, y) }
   522  function f(a, x) { return a[x] }
   523  BEGIN { c[1]=2; print f(c, 1); print g(c, 1); print h(c, 1) }
   524  `, "", "2\n2\n2\n", "", ""},
   525  		{`
   526  function get(a, x) { return a[x] }
   527  BEGIN { a[1]=2; print get(a, x); print get(1, 2); }
   528  # !awk - awk doesn't detect this
   529  `, "", "", `parse error at 3:40: can't pass scalar 1 as array param`, "attempt to use scalar"},
   530  		{`
   531  function early() {
   532  	print "x"
   533  	return
   534  	print "y"
   535  }
   536  BEGIN { early() }
   537  `, "", "x\n", "", ""},
   538  		{`BEGIN { return }`, "", "", "parse error at 1:9: return must be inside a function", "return"},
   539  		{`function f() { printf "x" }; BEGIN { f() } `, "", "x", "", ""},
   540  		{`function f(x) { 0 in _; f(_) }  BEGIN { f() }  # !awk !gawk`, "", "",
   541  			`parse error at 1:25: can't pass array "_" as scalar param`, ""},
   542  		{`BEGIN { for (i=0; i<1001; i++) f(); print x }  function f() { x++ }`, "", "1001\n", "", ""},
   543  		{`
   544  function bar(y) { return y[1] }
   545  function foo() { return bar(x) }
   546  BEGIN { x[1] = 42; print foo() }
   547  `, "", "42\n", "", ""},
   548  		// TODO: failing because f1 doesn't use x, so resolver assumes its type is scalar
   549  		// 		{`
   550  		// function f1(x) { }
   551  		// function f2(x, y) { return x[y] }
   552  		// BEGIN { a[1]=2; f1(a); print f2(a, 1) }
   553  		// `, "", "2\n", "", ""},
   554  
   555  		// Type checking / resolver tests
   556  		{`BEGIN { a[x]; a=42 }`, "", "", `parse error at 1:15: can't use array "a" as scalar`, "array"},
   557  		{`BEGIN { s=42; s[x] }`, "", "", `parse error at 1:15: can't use scalar "s" as array`, "array"},
   558  		{`function get(a, k) { return a[k] }  BEGIN { a = 42; print get(a, 1); }  # !awk - doesn't error in awk`,
   559  			"", "", `parse error at 1:59: can't pass scalar "a" as array param`, "attempt to use scalar parameter `a' as an array"},
   560  		{`function get(a, k) { return a+k } BEGIN { a[42]; print get(a, 1); }`,
   561  			"", "", `parse error at 1:56: can't pass array "a" as scalar param`, "array"},
   562  		{`{ f(z) }  function f(x) { print NR }`, "abc", "1\n", "", ""},
   563  		{`function f() { f() }  BEGIN { f() }  # !awk !gawk`, "", "", `calling "f" exceeded maximum call depth of 1000`, ""},
   564  		{`function f(x) { 0 in x }  BEGIN { f(FS) }  # !awk`, "", "", `parse error at 1:35: can't pass scalar "FS" as array param`, "attempt to use scalar parameter `x' as an array"},
   565  		{`
   566  function foo(x) { print "foo", x }
   567  function bar(foo) { print "bar", foo }
   568  BEGIN { foo(5); bar(10) }
   569  `, "", "foo 5\nbar 10\n", "", ""},
   570  		{`
   571  function foo(foo) { print "foo", foo }
   572  function bar(foo) { print "bar", foo }
   573  BEGIN { foo(5); bar(10) }
   574  `, "", "", `parse error at 2:14: can't use function name as parameter name`, "function name"},
   575  		{`function foo() { print foo }  BEGIN { foo() }`,
   576  			"", "", `parse error at 1:46: global var "foo" can't also be a function`, "function"},
   577  		{`function f(x) { print x, x(); }  BEGIN { f() }`, "", "", `parse error at 1:27: can't call local variable "x" as function`, "function"},
   578  
   579  		// Redirected I/O (we give explicit errors, awk and gawk don't)
   580  		// TODO: the following two tests sometimes fail under TravisCI with: "write |1: broken pipe"
   581  		// {`BEGIN { print >"out"; getline <"out" }  # !awk !gawk`, "", "", "can't read from writer stream", ""},
   582  		// {`BEGIN { print |"out"; getline <"out" }  # !awk !gawk`, "", "", "can't read from writer stream", ""},
   583  		{`BEGIN { print >"out"; close("out"); getline <"out"; print >"out" }  # !awk !gawk`, "", "", "can't write to reader stream", ""},
   584  		{`BEGIN { print >"out"; close("out"); getline <"out"; print |"out" }  # !awk !gawk`, "", "", "can't write to reader stream", ""},
   585  		// TODO: currently we support "getline var" but not "getline lvalue"
   586  		// TODO: {`BEGIN { getline a[1]; print a[1] }`, "foo", "foo\n", "", ""},
   587  		// TODO: {`BEGIN { getline $1; print $1 }`, "foo", "foo\n", "", ""},
   588  		{`BEGIN { print "foo" |"sort"; print "bar" |"sort" }`, "", "bar\nfoo\n", "", ""},
   589  		{`BEGIN { print "foo" |">&2 echo error" }`, "", "error\n", "", ""},
   590  		{`BEGIN { "cat" | getline; print }`, "bar", "bar\n", "", ""},
   591  		// TODO: fix test flakiness on Windows (sometimes returns "\nerror\n")
   592  		// {`BEGIN { ">&2 echo error" | getline; print }`, "", "error\n\n", "", ""},
   593  		{`BEGIN { print getline x < "/no/such/file" }`, "", "-1\n", "", ""},
   594  
   595  		// fflush() function - tests parsing and some edge cases, but not
   596  		// actual flushing behavior (that's partially tested in TestFlushes).
   597  		{`BEGIN { print fflush(); print fflush("") }`, "", "0\n0\n", "", ""},
   598  		{`BEGIN { print "x"; print fflush(); print "y"; print fflush("") }`, "", "x\n0\ny\n0\n", "", ""},
   599  		{`BEGIN { print "x" >"out"; print fflush("out"); print "y"; print fflush("") }`, "", "0\ny\n0\n", "", ""},
   600  		{`BEGIN { print fflush("x") }  # !gawk`, "", "error flushing \"x\": not an output file or pipe\n-1\n", "", ""},
   601  		{`BEGIN { "cat" | getline; print fflush("cat") }  # !gawk`, "", "error flushing \"cat\": not an output file or pipe\n-1\n", "", ""},
   602  
   603  		// Greater than operator requires parentheses in print statement,
   604  		// otherwise it's a redirection directive
   605  		{`BEGIN { print "x" > "out" }`, "", "", "", ""},
   606  		{`BEGIN { printf "x" > "out" }`, "", "", "", ""},
   607  		{`BEGIN { print("x" > "out") }`, "", "1\n", "", ""},
   608  		{`BEGIN { printf("x" > "out") }`, "", "1", "", ""},
   609  
   610  		// Grammar should allow blocks wherever statements are allowed
   611  		{`BEGIN { if (1) printf "x"; else printf "y" }`, "", "x", "", ""},
   612  		{`BEGIN { printf "x"; { printf "y"; printf "z" } }`, "", "xyz", "", ""},
   613  
   614  		// Ensure syntax errors result in errors
   615  		// TODO: {`{ $1 = substr($1, 1, 3) print $1 }`, "", "", "ERROR", "syntax error"},
   616  		{`BEGIN { f() }`, "", "", `parse error at 1:9: undefined function "f"`, "defined"},
   617  		{`function f() {} function f() {} BEGIN { }`, "", "", `parse error at 1:26: function "f" already defined`, "define"},
   618  		{`BEGIN { print (1,2),(3,4) }`, "", "", "parse error at 1:15: unexpected comma-separated expression", "syntax"},
   619  		{`BEGIN { print (1,2,(3,4),(5,6)) }`, "", "", "parse error at 1:20: unexpected comma-separated expression", "syntax"},
   620  
   621  		// Ensure very long lines work (> 64KB)
   622  		{`{ print length() }`, longLine, fmt.Sprintf("%d\n", len(longLine)), "", ""},
   623  	}
   624  	for _, test := range tests {
   625  		testName := test.src
   626  		if len(testName) > 70 {
   627  			testName = testName[:70]
   628  		}
   629  
   630  		if awkExe != "" && !strings.Contains(test.src, "!"+awkExe) {
   631  			// Run it through external awk program first
   632  			t.Run("awk_"+testName, func(t *testing.T) {
   633  				srcFile, err := ioutil.TempFile("", "goawktest_")
   634  				if err != nil {
   635  					t.Fatalf("error creating temp file: %v", err)
   636  				}
   637  				defer os.Remove(srcFile.Name())
   638  				_, err = srcFile.Write([]byte(test.src))
   639  				if err != nil {
   640  					t.Fatalf("error writing temp file: %v", err)
   641  				}
   642  				cmd := exec.Command(awkExe, "-f", srcFile.Name(), "-")
   643  				if test.in != "" {
   644  					stdin, err := cmd.StdinPipe()
   645  					if err != nil {
   646  						t.Fatalf("error fetching stdin pipe: %v", err)
   647  					}
   648  					go func() {
   649  						defer stdin.Close()
   650  						stdin.Write([]byte(test.in))
   651  					}()
   652  				}
   653  				out, err := cmd.CombinedOutput()
   654  				if err != nil {
   655  					if test.awkErr != "" {
   656  						if strings.Contains(string(out), test.awkErr) {
   657  							return
   658  						}
   659  						t.Fatalf("expected error %q, got:\n%s", test.awkErr, out)
   660  					} else {
   661  						t.Fatalf("error running %s: %v:\n%s", awkExe, err, out)
   662  					}
   663  				}
   664  				if test.awkErr != "" {
   665  					t.Fatalf(`expected error %q, got ""`, test.awkErr)
   666  				}
   667  				normalized := normalizeNewlines(string(out))
   668  				if normalized != test.out {
   669  					t.Fatalf("expected %q, got %q", test.out, normalized)
   670  				}
   671  			})
   672  		}
   673  
   674  		// Then test it in GoAWK
   675  		t.Run(testName, func(t *testing.T) {
   676  			testGoAWK(t, test.src, test.in, test.out, test.err, nil, nil)
   677  		})
   678  	}
   679  	_ = os.Remove("out")
   680  }
   681  
   682  // Version of bytes.Buffer that's safe for concurrent writes. This
   683  // makes certain tests that write to Output and Error at once (due
   684  // to os/exec) work correctly.
   685  type concurrentBuffer struct {
   686  	buffer bytes.Buffer
   687  	mutex  sync.Mutex
   688  }
   689  
   690  func (b *concurrentBuffer) Write(data []byte) (int, error) {
   691  	b.mutex.Lock()
   692  	defer b.mutex.Unlock()
   693  	return b.buffer.Write(data)
   694  }
   695  
   696  func (b *concurrentBuffer) String() string {
   697  	b.mutex.Lock()
   698  	defer b.mutex.Unlock()
   699  	return b.buffer.String()
   700  }
   701  
   702  func testGoAWK(
   703  	t *testing.T, src, in, out, errStr string,
   704  	funcs map[string]interface{}, configure func(config *interp.Config),
   705  ) {
   706  	parserConfig := &parser.ParserConfig{
   707  		Funcs: funcs,
   708  	}
   709  	prog, err := parser.ParseProgram([]byte(src), parserConfig)
   710  	if err != nil {
   711  		if errStr != "" {
   712  			if err.Error() == errStr {
   713  				return
   714  			}
   715  			t.Fatalf("expected error %q, got %q", errStr, err.Error())
   716  		}
   717  		t.Fatal(err)
   718  	}
   719  	outBuf := &concurrentBuffer{}
   720  	config := &interp.Config{
   721  		Stdin:  strings.NewReader(in),
   722  		Output: outBuf,
   723  		Error:  outBuf,
   724  		Vars:   []string{"_var", "42"},
   725  		Funcs:  funcs,
   726  	}
   727  	if configure != nil {
   728  		configure(config)
   729  	}
   730  	_, err = interp.ExecProgram(prog, config)
   731  	if err != nil {
   732  		if errStr != "" {
   733  			if err.Error() == errStr {
   734  				return
   735  			}
   736  			t.Fatalf("expected error %q, got %q", errStr, err.Error())
   737  		}
   738  		t.Fatal(err)
   739  	}
   740  	if errStr != "" {
   741  		t.Fatalf(`expected error %q, got ""`, errStr)
   742  	}
   743  	normalized := normalizeNewlines(outBuf.String())
   744  	if normalized != out {
   745  		t.Fatalf("expected %q, got %q", out, normalized)
   746  	}
   747  }
   748  
   749  func TestNative(t *testing.T) {
   750  	tests := []struct {
   751  		src   string
   752  		in    string
   753  		out   string
   754  		err   string
   755  		funcs map[string]interface{}
   756  	}{
   757  		{`BEGIN { print foo() }`, "", "", `parse error at 1:15: undefined function "foo"`,
   758  			nil},
   759  		{`BEGIN { print foo() }`, "", "\n", "",
   760  			map[string]interface{}{
   761  				"foo": func() {},
   762  			}},
   763  		{`BEGIN { print foo() }`, "", "FOO\n", "",
   764  			map[string]interface{}{
   765  				"foo": func() string { return "FOO" },
   766  			}},
   767  		{`BEGIN { print foo() }`, "", "BYTES\n", "",
   768  			map[string]interface{}{
   769  				"foo": func() []byte { return []byte("BYTES") },
   770  			}},
   771  		{`BEGIN { print repeat("xy", 5) }`, "", "xyxyxyxyxy\n", "",
   772  			map[string]interface{}{
   773  				"repeat": strings.Repeat,
   774  			}},
   775  		{`BEGIN { print repeat("xy", 5) }`, "", "xyxyxyxyxy\n", "",
   776  			map[string]interface{}{
   777  				"repeat": strings.Repeat,
   778  			}},
   779  		{`
   780  BEGIN {
   781  	print r0()
   782  	print r1(), r1(5)
   783  	print r2(), r2(5)
   784  }`, "", "\n0 25\n0 25\n", "",
   785  			map[string]interface{}{
   786  				"r0": func() {},
   787  				"r1": func(n int) int { return n * n },
   788  				"r2": func(n int) (int, error) {
   789  					return n * n, nil
   790  				},
   791  			}},
   792  		{`
   793  BEGIN {
   794  	print r2()
   795  }`, "", "", "NATIVE ERROR",
   796  			map[string]interface{}{
   797  				"r2": func(n int) (int, error) {
   798  					return n * n, fmt.Errorf("NATIVE ERROR")
   799  				},
   800  			}},
   801  		{`
   802  BEGIN {
   803  	print
   804  	print bool(), bool(0), bool(1), bool(""), bool("0"), bool("x")
   805  	print i(), i(42), i(-5), i(3.75), i(-3.75)
   806  	print i8(), i8(42), i8(-5.6), i8(127), i8(128), i8(255), i8(256)
   807  	print i16(), i16(42), i16(-5.6), i16(32767), i16(32768), i16(65535), i16(65536)
   808  	print i32(), i32(42), i32(-5.6), i32(2147483647), i32(2147483648), i32(4294967295), i32(4294967296)
   809  	print i64(), i64(42), i64(-5.6), i64(2147483647000), i64(-2147483647000)
   810  	print u(), u(42), u(-1)
   811  	print u8(), u8(42), u8(-5.6), u8(127), u8(128), u8(255), u8(256)
   812  	print u16(), u16(42), u16(-1), u16(65535), u16(65536)
   813  	print u32(), u32(42), u32(-1), u32(4294967295), u32(4294967296)
   814  	print u64(), u64(42), u64(-1), u64(4294967296), u64(2147483647000)
   815  	print s() "." s("") "." s("Foo bar") "." s(1234)
   816  	print b() "." b("") "." b("Foo bar") "." b(1234)
   817  }`, "", `
   818  0 0 1 0 1 1
   819  0 42 -5 3 -3
   820  0 42 -5 127 -128 -1 0
   821  0 42 -5 32767 -32768 -1 0
   822  0 42 -5 2147483647 -2147483648 -2147483648 -2147483648
   823  0 42 -5 2147483647000 -2147483647000
   824  0 42 1.84467e+19
   825  0 42 251 127 128 255 0
   826  0 42 65535 65535 0
   827  0 42 4294967295 4294967295 0
   828  0 42 1.84467e+19 4294967296 2147483647000
   829  ..Foo bar.1234
   830  ..Foo bar.1234
   831  `, "",
   832  			map[string]interface{}{
   833  				"bool": func(b bool) bool { return b },
   834  				"i":    func(n int) int { return n },
   835  				"i8":   func(n int8) int8 { return n },
   836  				"i16":  func(n int16) int16 { return n },
   837  				"i32":  func(n int32) int32 { return n },
   838  				"i64":  func(n int64) int64 { return n },
   839  				"u":    func(n uint) uint { return n },
   840  				"u8":   func(n uint8) uint8 { return n },
   841  				"u16":  func(n uint16) uint16 { return n },
   842  				"u32":  func(n uint32) uint32 { return n },
   843  				"u64":  func(n uint64) uint64 { return n },
   844  				"b":    func(b []byte) []byte { return b },
   845  				"s":    func(s string) string { return s },
   846  			}},
   847  		{`
   848  BEGIN {
   849  	print
   850  	print sum(), sum(1), sum(2, 3), sum(4, 5, 6, 7, 8)
   851  	print fmt_ints()
   852  	print fmt_ints("%5d")
   853  	print fmt_ints("%5d", 123)
   854  	print fmt_ints("%d %d", 123, 456)
   855  	print fmt_ints("%d %d %d", 123, 456, 789)
   856  }`, "", `
   857  0 1 5 30
   858  
   859  %!d(MISSING)
   860    123
   861  123 456
   862  123 456 789
   863  `, "",
   864  			map[string]interface{}{
   865  				"sum": func(args ...int) int {
   866  					sum := 0
   867  					for _, a := range args {
   868  						sum += a
   869  					}
   870  					return sum
   871  				},
   872  				"fmt_ints": func(s string, args ...int) string {
   873  					fmtArgs := make([]interface{}, len(args))
   874  					for i, a := range args {
   875  						fmtArgs[i] = a
   876  					}
   877  					return fmt.Sprintf(s, fmtArgs...)
   878  				},
   879  			}},
   880  		{`BEGIN { 0 }`, "", "", `native function "f" is not a function`,
   881  			map[string]interface{}{
   882  				"f": 0,
   883  			}},
   884  		{`BEGIN { 1 }`, "", "", `native function "g" param 0 is not int or string`,
   885  			map[string]interface{}{
   886  				"g": func(s complex64) {},
   887  			}},
   888  		{`BEGIN { 2 }`, "", "", `native function "g" param 2 is not int or string`,
   889  			map[string]interface{}{
   890  				"g": func(x, y int, s []int, t string) {},
   891  			}},
   892  		{`BEGIN { 3 }`, "", "", `native function "h" param 0 is not int or string`,
   893  			map[string]interface{}{
   894  				"h": func(a ...map[string]int) {},
   895  			}},
   896  		{`BEGIN { 4 }`, "", "", `native function "h" param 1 is not int or string`,
   897  			map[string]interface{}{
   898  				"h": func(x int, a ...complex64) {},
   899  			}},
   900  		{`BEGIN { 5 }`, "", "", `native function "r" return value is not int or string`,
   901  			map[string]interface{}{
   902  				"r": func() map[string]int { return nil },
   903  			}},
   904  		{`BEGIN { 6 }`, "", "", `native function "r" first return value is not int or string`,
   905  			map[string]interface{}{
   906  				"r": func() (map[string]int, error) { return nil, nil },
   907  			}},
   908  		{`BEGIN { 7 }`, "", "", `native function "r" second return value is not an error`,
   909  			map[string]interface{}{
   910  				"r": func() (int, int) { return 0, 0 },
   911  			}},
   912  		{`BEGIN { 8 }`, "", "", `native function "r" returns more than two values`,
   913  			map[string]interface{}{
   914  				"r": func() (int, error, int) { return 0, nil, 0 },
   915  			}},
   916  		{`BEGIN { print f(), f(1, 2) }`, "", "", `parse error at 1:20: "f" called with more arguments than declared`,
   917  			map[string]interface{}{
   918  				"f": func(n int) {},
   919  			}},
   920  		{`BEGIN { print split("x y", a) }`, "", "", `can't use keyword "split" as native function name`,
   921  			map[string]interface{}{
   922  				"split": func() {},
   923  			}},
   924  		{`
   925  function foo(n) { return n * 2 }
   926  BEGIN { print foo(42) }
   927  `, "", "84\n", "", map[string]interface{}{
   928  			"foo": func(n int) int { return n / 2 },
   929  		}},
   930  		{`BEGIN { x=3; print foo(x) }`, "", "9\n", ``,
   931  			map[string]interface{}{
   932  				"foo": func(n int) int { return n * n },
   933  			}},
   934  		{`
   935  function bar(n) { return foo(n) }
   936  BEGIN { x=4; y=5; print foo(x), bar(y) }
   937  `, "", "16 25\n", ``,
   938  			map[string]interface{}{
   939  				"foo": func(n int) int { return n * n },
   940  			}},
   941  		{`BEGIN { a["x"]=1; print foo(a) }`, "", "",
   942  			`parse error at 1:25: can't pass array "a" to native function`,
   943  			map[string]interface{}{
   944  				"foo": func(n int) int { return n * n },
   945  			}},
   946  		{`BEGIN { x["x"]=1; print f(x) }  function f(a) { return foo(a) }`, "", "",
   947  			`parse error at 1:25: can't pass array "x" as scalar param`,
   948  			map[string]interface{}{
   949  				"foo": func(n int) int { return n * n },
   950  			}},
   951  		{`function f(a) { return foo(a) }  BEGIN { x["x"]=1; print f(x) }`, "", "",
   952  			`parse error at 1:58: can't pass array "x" as scalar param`,
   953  			map[string]interface{}{
   954  				"foo": func(n int) int { return n * n },
   955  			}},
   956  		{`BEGIN { x["x"]=1; print f(x["x"]) }  function f(a) { return foo(a) }`, "", "1\n", "",
   957  			map[string]interface{}{
   958  				"foo": func(n int) int { return n * n },
   959  			}},
   960  		{`BEGIN { print add(1, add(2, 3)) }`, "", "6\n", "",
   961  			map[string]interface{}{
   962  				"add": func(a, b float64) float64 { return a + b },
   963  			}},
   964  		{`BEGIN { print foo(x) }`, "", "0\n", "",
   965  			map[string]interface{}{
   966  				"foo": func(i int) int { return i },
   967  			}},
   968  		{`BEGIN { print foo(_var) }`, "", "42\n", "",
   969  			map[string]interface{}{
   970  				"foo": func(i int) int { return i },
   971  			}},
   972  		{`function foo(y) { return y/2 }  BEGIN { print foo(_var) }`, "", "21\n", "",
   973  			map[string]interface{}{
   974  				"foo": func(i int) int { return i },
   975  			}},
   976  	}
   977  	for _, test := range tests {
   978  		testName := test.src
   979  		if len(testName) > 70 {
   980  			testName = testName[:70]
   981  		}
   982  		t.Run(testName, func(t *testing.T) {
   983  			testGoAWK(t, test.src, test.in, test.out, test.err, test.funcs, nil)
   984  		})
   985  	}
   986  }
   987  
   988  func TestSafeMode(t *testing.T) {
   989  	tests := []struct {
   990  		src  string
   991  		in   string
   992  		out  string
   993  		err  string
   994  		args []string
   995  	}{
   996  		{`BEGIN { print "hi" >"out" }`, "", "", "can't write to file due to NoFileWrites", nil},
   997  		{`BEGIN { print "hi" >>"out" }`, "", "", "can't write to file due to NoFileWrites", nil},
   998  		{`BEGIN { print "hi" |"sort" }`, "", "", "can't write to pipe due to NoExec", nil},
   999  		{`BEGIN { getline <"in" }`, "", "", "can't read from file due to NoFileReads", nil},
  1000  		{`$0  # no files`, "1\n2\n", "1\n2\n", "", nil},
  1001  		{`$0  # files`, "1\n2\n", "1\n2\n", "can't read from file due to NoFileReads", []string{"f1"}},
  1002  		{`BEGIN { "echo foo" |getline }`, "", "", "can't read from pipe due to NoExec", nil},
  1003  		{`BEGIN { system("echo foo") }`, "", "", "can't call system() due to NoExec", nil},
  1004  	}
  1005  	for _, test := range tests {
  1006  		testName := test.src
  1007  		if len(testName) > 70 {
  1008  			testName = testName[:70]
  1009  		}
  1010  		t.Run(testName, func(t *testing.T) {
  1011  			testGoAWK(t, test.src, test.in, test.out, test.err, nil, func(config *interp.Config) {
  1012  				config.Args = test.args
  1013  				config.NoExec = true
  1014  				config.NoFileWrites = true
  1015  				config.NoFileReads = true
  1016  			})
  1017  		})
  1018  	}
  1019  }
  1020  
  1021  func TestConfigVarsCorrect(t *testing.T) {
  1022  	prog, err := parser.ParseProgram([]byte(`BEGIN { print x }`), nil)
  1023  	if err != nil {
  1024  		t.Fatalf("error parsing: %v", err)
  1025  	}
  1026  	config := &interp.Config{
  1027  		Stdin:  strings.NewReader(""),
  1028  		Output: &bytes.Buffer{},
  1029  		Error:  ioutil.Discard,
  1030  		Vars:   []string{"FS"},
  1031  	}
  1032  	_, err = interp.ExecProgram(prog, config)
  1033  	expected := "length of config.Vars must be a multiple of 2, not 1"
  1034  	if err == nil || err.Error() != expected {
  1035  		t.Fatalf("expected error %q, got: %v", expected, err)
  1036  	}
  1037  }
  1038  
  1039  type mockFlusher struct {
  1040  	bytes.Buffer
  1041  	flushes []string
  1042  }
  1043  
  1044  func (f *mockFlusher) Flush() error {
  1045  	f.flushes = append(f.flushes, normalizeNewlines(f.String()))
  1046  	return nil
  1047  }
  1048  
  1049  func TestFlushes(t *testing.T) {
  1050  	src := `
  1051  BEGIN {
  1052  	print fflush()
  1053  	print "x"
  1054  	print "y"
  1055  	print fflush()
  1056  	print "z"
  1057  	print fflush("")
  1058  }`
  1059  	f := &mockFlusher{}
  1060  	testGoAWK(t, src, "", "", "", nil, func(config *interp.Config) {
  1061  		config.Output = f
  1062  	})
  1063  	// The last one is from GoAWK itself flushing output after finishing
  1064  	expected := []string{"", "0\nx\ny\n", "0\nx\ny\n0\nz\n", "0\nx\ny\n0\nz\n0\n"}
  1065  	if !reflect.DeepEqual(f.flushes, expected) {
  1066  		t.Fatalf("expected flushes %q, got %q", expected, f.flushes)
  1067  	}
  1068  }
  1069  
  1070  type errorFlusher struct {
  1071  	bytes.Buffer
  1072  }
  1073  
  1074  func (f *errorFlusher) Flush() error {
  1075  	return errors.New("that's not good, hackers")
  1076  }
  1077  
  1078  func TestFlushError(t *testing.T) {
  1079  	f := &errorFlusher{}
  1080  	testGoAWK(t, `BEGIN { fflush() }`, "", "", "", nil, func(config *interp.Config) {
  1081  		config.Output = f
  1082  		config.Error = f
  1083  	})
  1084  	expected := "error flushing \"stdout\": that's not good, hackers\n"
  1085  	if f.String() != expected {
  1086  		t.Fatalf("expected %q, got %q", expected, f.String())
  1087  	}
  1088  }
  1089  
  1090  func benchmarkProgram(b *testing.B, funcs map[string]interface{},
  1091  	input, expected, srcFormat string, args ...interface{},
  1092  ) {
  1093  	b.StopTimer()
  1094  	src := fmt.Sprintf(srcFormat, args...)
  1095  	parserConfig := &parser.ParserConfig{
  1096  		Funcs: funcs,
  1097  	}
  1098  	prog, err := parser.ParseProgram([]byte(src), parserConfig)
  1099  	if err != nil {
  1100  		b.Fatalf("error parsing %s: %v", b.Name(), err)
  1101  	}
  1102  	outBuf := &bytes.Buffer{}
  1103  	config := &interp.Config{
  1104  		Stdin:  strings.NewReader(input),
  1105  		Output: outBuf,
  1106  		Error:  ioutil.Discard,
  1107  		Funcs:  funcs,
  1108  	}
  1109  	b.StartTimer()
  1110  	_, err = interp.ExecProgram(prog, config)
  1111  	b.StopTimer()
  1112  	if err != nil {
  1113  		b.Fatalf("error interpreting %s: %v", b.Name(), err)
  1114  	}
  1115  	if expected != "" {
  1116  		expected += "\n"
  1117  	}
  1118  	outStr := strings.Replace(outBuf.String(), "\r\n", "\n", -1)
  1119  	if outStr != expected {
  1120  		b.Fatalf("expected %q, got %q", expected, outStr)
  1121  	}
  1122  }
  1123  
  1124  func BenchmarkGlobalVars(b *testing.B) {
  1125  	benchmarkProgram(b, nil, "", "a 1", `
  1126  BEGIN {
  1127    for (i = 0; i < %d; i++) {
  1128    	x = 1; y = "a"; t = x; x = y; y = t
  1129    	x = 1; y = "a"; t = x; x = y; y = t
  1130    	x = 1; y = "a"; t = x; x = y; y = t
  1131    	x = 1; y = "a"; t = x; x = y; y = t
  1132    	x = 1; y = "a"; t = x; x = y; y = t
  1133    }
  1134    print x, y
  1135  }
  1136  `, b.N)
  1137  }
  1138  
  1139  func BenchmarkLocalVars(b *testing.B) {
  1140  	benchmarkProgram(b, nil, "", "b 2", `
  1141  function f(i, x, y, t) {
  1142    for (i = 0; i < %d; i++) {
  1143    	x = 2; y = "b"; t = x; x = y; y = t
  1144    	x = 2; y = "b"; t = x; x = y; y = t
  1145    	x = 2; y = "b"; t = x; x = y; y = t
  1146    	x = 2; y = "b"; t = x; x = y; y = t
  1147    	x = 2; y = "b"; t = x; x = y; y = t
  1148    }
  1149    print x, y
  1150  }
  1151  
  1152  BEGIN {
  1153    f()
  1154  }
  1155  `, b.N)
  1156  }
  1157  
  1158  func BenchmarkIncrDecr(b *testing.B) {
  1159  	benchmarkProgram(b, nil, "", "0 10", `
  1160  BEGIN {
  1161    for (i = 0; i < %d; i++) {
  1162    	x++; x++; x++; x++; x++; x++; x++; x++; x++; x++
  1163    	y = x
  1164    	x--; x--; x--; x--; x--; x--; x--; x--; x--; x--
  1165    }
  1166    print x, y
  1167  }
  1168  `, b.N)
  1169  }
  1170  
  1171  func BenchmarkSimpleBuiltins(b *testing.B) {
  1172  	benchmarkProgram(b, nil, "", "", `
  1173  BEGIN {
  1174    for (i = 0; i < %d; i++) {
  1175    	sin(0); cos(0); exp(0); log(1); sqrt(2); int("x");
  1176    	sin(0); cos(0); exp(0); log(1); sqrt(2); int("x");
  1177    	sin(0); cos(0); exp(0); log(1); sqrt(2); int("x");
  1178    	sin(0); cos(0); exp(0); log(1); sqrt(2); int("x");
  1179    	sin(0); cos(0); exp(0); log(1); sqrt(2); int("x");
  1180    }
  1181  }
  1182  `, b.N)
  1183  }
  1184  
  1185  func BenchmarkBuiltinMatch(b *testing.B) {
  1186  	benchmarkProgram(b, nil, "", "21", `
  1187  BEGIN {
  1188    s = "The quick brown fox jumps over the lazy dog"
  1189    for (i = 0; i < %d; i++) {
  1190    	match(s, /j[a-z]+p/); match(s, /j[a-z]+p/)
  1191    	match(s, /j[a-z]+p/); match(s, /j[a-z]+p/)
  1192    	match(s, /j[a-z]+p/); match(s, /j[a-z]+p/)
  1193    	match(s, /j[a-z]+p/); match(s, /j[a-z]+p/)
  1194    	match(s, /j[a-z]+p/); x = match(s, /j[a-z]+p/)
  1195    }
  1196    print x
  1197  }
  1198  `, b.N)
  1199  }
  1200  
  1201  func BenchmarkBuiltinLength(b *testing.B) {
  1202  	benchmarkProgram(b, nil, "", "134", `
  1203  BEGIN {
  1204    s = "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog."
  1205    for (i = 0; i < %d; i++) {
  1206    	length(s); length(s); length(s); length(s); length(s);
  1207    	length(s); length(s); length(s); length(s); length(s);
  1208    	length(s); length(s); length(s); length(s); length(s);
  1209    	length(s); length(s); length(s); length(s); length(s);
  1210    	length(s); length(s); length(s); length(s); x = length(s);
  1211    }
  1212    print x
  1213  }
  1214  `, b.N)
  1215  }
  1216  
  1217  func BenchmarkBuiltinIndex(b *testing.B) {
  1218  	benchmarkProgram(b, nil, "", "134", `
  1219  BEGIN {
  1220    s = "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog!?!"
  1221    for (i = 0; i < %d; i++) {
  1222    	index(s, "!?!"); index(s, "!?!"); index(s, "!?!"); index(s, "!?!"); index(s, "!?!")
  1223    	index(s, "!?!"); index(s, "!?!"); index(s, "!?!"); index(s, "!?!"); index(s, "!?!")
  1224    	index(s, "!?!"); index(s, "!?!"); index(s, "!?!"); index(s, "!?!"); index(s, "!?!")
  1225    	index(s, "!?!"); index(s, "!?!"); index(s, "!?!"); index(s, "!?!"); index(s, "!?!")
  1226    	index(s, "!?!"); index(s, "!?!"); index(s, "!?!"); index(s, "!?!"); x = index(s, "!?!")
  1227    }
  1228    print x
  1229  }
  1230  `, b.N)
  1231  }
  1232  
  1233  func BenchmarkBuiltinSubstr(b *testing.B) {
  1234  	benchmarkProgram(b, nil, "", " brown fox", `
  1235  BEGIN {
  1236    s = "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog!?!"
  1237    for (i = 0; i < %d; i++) {
  1238    	substr(s, 100, 10); substr(s, 100, 10); substr(s, 100, 10); substr(s, 100, 10); substr(s, 100, 10)
  1239    	substr(s, 100, 10); substr(s, 100, 10); substr(s, 100, 10); substr(s, 100, 10); substr(s, 100, 10)
  1240    	substr(s, 100, 10); substr(s, 100, 10); substr(s, 100, 10); substr(s, 100, 10); substr(s, 100, 10)
  1241    	substr(s, 100, 10); substr(s, 100, 10); substr(s, 100, 10); substr(s, 100, 10); substr(s, 100, 10)
  1242    	substr(s, 100, 10); substr(s, 100, 10); substr(s, 100, 10); substr(s, 100, 10); x = substr(s, 100, 10)
  1243    }
  1244    print x
  1245  }
  1246  `, b.N)
  1247  }
  1248  
  1249  func BenchmarkBuiltinSplitSpace(b *testing.B) {
  1250  	benchmarkProgram(b, nil, "", "27", `
  1251  BEGIN {
  1252    s = "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog!?!"
  1253    for (i = 0; i < %d; i++) {
  1254    	split(s, a, " "); split(s, a, " "); split(s, a, " ")
  1255    	split(s, a, " "); split(s, a, " "); split(s, a, " ")
  1256    	split(s, a, " "); split(s, a, " "); split(s, a, " ")
  1257    	split(s, a, " "); split(s, a, " "); split(s, a, " ")
  1258    	split(s, a, " "); split(s, a, " "); split(s, a, " ")
  1259    }
  1260    for (k in a) n++
  1261    print n
  1262  }
  1263  `, b.N)
  1264  }
  1265  
  1266  func BenchmarkBuiltinSplitRegex(b *testing.B) {
  1267  	benchmarkProgram(b, nil, "", "22", `
  1268  BEGIN {
  1269    s = "a fox ab fax abc fix a fox ab fax abc fix a fox ab fax abc fix a fox ab fax abc fix a fox ab fax abc fix a fox ab fax abc fix a fox ab fax abc fix"
  1270    for (i = 0; i < %d; i++) {
  1271    	split(s, a, "f[a-z]x"); split(s, a, "f[a-z]x"); split(s, a, "f[a-z]x")
  1272    	split(s, a, "f[a-z]x"); split(s, a, "f[a-z]x"); split(s, a, "f[a-z]x")
  1273    	split(s, a, "f[a-z]x"); split(s, a, "f[a-z]x"); split(s, a, "f[a-z]x")
  1274    	split(s, a, "f[a-z]x"); split(s, a, "f[a-z]x"); split(s, a, "f[a-z]x")
  1275    	split(s, a, "f[a-z]x"); split(s, a, "f[a-z]x"); split(s, a, "f[a-z]x")
  1276    }
  1277    for (k in a) n++
  1278    print n
  1279  }
  1280  `, b.N)
  1281  }
  1282  
  1283  func BenchmarkBuiltinSub(b *testing.B) {
  1284  	benchmarkProgram(b, nil, "", "1 164", `
  1285  BEGIN {
  1286    for (i = 0; i < %d; i++) {
  1287      s = "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog."
  1288    	sub(/f[a-z]x/, "foxes", s); sub(/f[a-z]x/, "foxes", s); sub(/f[a-z]x/, "foxes", s)
  1289    	sub(/f[a-z]x/, "foxes", s); sub(/f[a-z]x/, "foxes", s); sub(/f[a-z]x/, "foxes", s)
  1290    	sub(/f[a-z]x/, "foxes", s); sub(/f[a-z]x/, "foxes", s); sub(/f[a-z]x/, "foxes", s)
  1291    	sub(/f[a-z]x/, "foxes", s); sub(/f[a-z]x/, "foxes", s); sub(/f[a-z]x/, "foxes", s)
  1292    	sub(/f[a-z]x/, "foxes", s); sub(/f[a-z]x/, "foxes", s); x = sub(/f[a-z]x/, "foxes", s)
  1293    }
  1294    print x, length(s)
  1295  }
  1296  `, b.N)
  1297  }
  1298  
  1299  func BenchmarkBuiltinSubAmpersand(b *testing.B) {
  1300  	benchmarkProgram(b, nil, "", "1 164", `
  1301  BEGIN {
  1302    for (i = 0; i < %d; i++) {
  1303      s = "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog."
  1304    	sub(/f[a-z]x/, "&es", s); sub(/f[a-z]x/, "&es", s); sub(/f[a-z]x/, "&es", s)
  1305    	sub(/f[a-z]x/, "&es", s); sub(/f[a-z]x/, "&es", s); sub(/f[a-z]x/, "&es", s)
  1306    	sub(/f[a-z]x/, "&es", s); sub(/f[a-z]x/, "&es", s); sub(/f[a-z]x/, "&es", s)
  1307    	sub(/f[a-z]x/, "&es", s); sub(/f[a-z]x/, "&es", s); sub(/f[a-z]x/, "&es", s)
  1308    	sub(/f[a-z]x/, "&es", s); sub(/f[a-z]x/, "&es", s); x = sub(/f[a-z]x/, "&es", s)
  1309    }
  1310    print x, length(s)
  1311  }
  1312  `, b.N)
  1313  }
  1314  
  1315  func BenchmarkBuiltinGsub(b *testing.B) {
  1316  	benchmarkProgram(b, nil, "", "3 224", `
  1317  BEGIN {
  1318    for (i = 0; i < %d; i++) {
  1319      s = "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog."
  1320    	gsub(/f[a-z]x/, "foxes", s); gsub(/f[a-z]x/, "foxes", s); gsub(/f[a-z]x/, "foxes", s)
  1321    	gsub(/f[a-z]x/, "foxes", s); gsub(/f[a-z]x/, "foxes", s); gsub(/f[a-z]x/, "foxes", s)
  1322    	gsub(/f[a-z]x/, "foxes", s); gsub(/f[a-z]x/, "foxes", s); gsub(/f[a-z]x/, "foxes", s)
  1323    	gsub(/f[a-z]x/, "foxes", s); gsub(/f[a-z]x/, "foxes", s); gsub(/f[a-z]x/, "foxes", s)
  1324    	gsub(/f[a-z]x/, "foxes", s); gsub(/f[a-z]x/, "foxes", s); x = gsub(/f[a-z]x/, "foxes", s)
  1325    }
  1326    print x, length(s)
  1327  }
  1328  `, b.N)
  1329  }
  1330  
  1331  func BenchmarkBuiltinGsubAmpersand(b *testing.B) {
  1332  	benchmarkProgram(b, nil, "", "3 224", `
  1333  BEGIN {
  1334    for (i = 0; i < %d; i++) {
  1335      s = "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog."
  1336    	gsub(/f[a-z]x/, "&es", s); gsub(/f[a-z]x/, "&es", s); gsub(/f[a-z]x/, "&es", s)
  1337    	gsub(/f[a-z]x/, "&es", s); gsub(/f[a-z]x/, "&es", s); gsub(/f[a-z]x/, "&es", s)
  1338    	gsub(/f[a-z]x/, "&es", s); gsub(/f[a-z]x/, "&es", s); gsub(/f[a-z]x/, "&es", s)
  1339    	gsub(/f[a-z]x/, "&es", s); gsub(/f[a-z]x/, "&es", s); gsub(/f[a-z]x/, "&es", s)
  1340    	gsub(/f[a-z]x/, "&es", s); gsub(/f[a-z]x/, "&es", s); x = gsub(/f[a-z]x/, "&es", s)
  1341    }
  1342    print x, length(s)
  1343  }
  1344  `, b.N)
  1345  }
  1346  
  1347  func BenchmarkBuiltinSprintf(b *testing.B) {
  1348  	benchmarkProgram(b, nil, "", "A 123   foo 3.14", `
  1349  BEGIN {
  1350    x = "foo"
  1351    y = 3.14159
  1352    for (i = 0; i < %d; i++) {
  1353    	sprintf("%%c %%d %%5s %%.3g", 65, 123, x, y); sprintf("%%c %%d %%5s %%.3g", 65, 123, x, y); sprintf("%%c %%d %%5s %%.3g", 65, 123, x, y)
  1354    	sprintf("%%c %%d %%5s %%.3g", 65, 123, x, y); sprintf("%%c %%d %%5s %%.3g", 65, 123, x, y); sprintf("%%c %%d %%5s %%.3g", 65, 123, x, y)
  1355    	sprintf("%%c %%d %%5s %%.3g", 65, 123, x, y); sprintf("%%c %%d %%5s %%.3g", 65, 123, x, y); sprintf("%%c %%d %%5s %%.3g", 65, 123, x, y)
  1356    	sprintf("%%c %%d %%5s %%.3g", 65, 123, x, y); sprintf("%%c %%d %%5s %%.3g", 65, 123, x, y); sprintf("%%c %%d %%5s %%.3g", 65, 123, x, y)
  1357    	sprintf("%%c %%d %%5s %%.3g", 65, 123, x, y); sprintf("%%c %%d %%5s %%.3g", 65, 123, x, y); s = sprintf("%%c %%d %%5s %%.3g", 65, 123, x, y)
  1358    }
  1359    print s
  1360  }
  1361  `, b.N)
  1362  }
  1363  
  1364  func BenchmarkRecursiveFunc(b *testing.B) {
  1365  	benchmarkProgram(b, nil, "", "55", `
  1366  function fib(n) {
  1367    if (n <= 2) {
  1368      return 1
  1369    }
  1370    return fib(n-1) + fib(n-2)
  1371  }
  1372  
  1373  BEGIN {
  1374    for (i = 0; i < %d; i++) {
  1375      res = fib(10)
  1376    }
  1377    print res
  1378  }
  1379  `, b.N)
  1380  }
  1381  
  1382  func BenchmarkFuncCall(b *testing.B) {
  1383  	benchmarkProgram(b, nil, "", "75", `
  1384  function add(a, b) {
  1385    return a + b
  1386  }
  1387  
  1388  BEGIN {
  1389    for (i = 0; i < %d; i++) {
  1390      sum = add(0, add(1, add(2, add(3, add(4, 5)))))
  1391      sum = add(sum, add(1, add(2, add(3, add(4, 5)))))
  1392      sum = add(sum, add(1, add(2, add(3, add(4, 5)))))
  1393      sum = add(sum, add(1, add(2, add(3, add(4, 5)))))
  1394      sum = add(sum, add(1, add(2, add(3, add(4, 5)))))
  1395    }
  1396    print sum
  1397  }
  1398  `, b.N)
  1399  }
  1400  
  1401  func BenchmarkNativeFunc(b *testing.B) {
  1402  	funcs := map[string]interface{}{
  1403  		"add": func(a, b float64) float64 { return a + b },
  1404  	}
  1405  	benchmarkProgram(b, funcs, "", "75", `
  1406  BEGIN {
  1407    for (i = 0; i < %d; i++) {
  1408      sum = add(0, add(1, add(2, add(3, add(4, 5)))))
  1409      sum = add(sum, add(1, add(2, add(3, add(4, 5)))))
  1410      sum = add(sum, add(1, add(2, add(3, add(4, 5)))))
  1411      sum = add(sum, add(1, add(2, add(3, add(4, 5)))))
  1412      sum = add(sum, add(1, add(2, add(3, add(4, 5)))))
  1413    }
  1414    print sum
  1415  }
  1416  `, b.N)
  1417  }
  1418  
  1419  func BenchmarkForLoop(b *testing.B) {
  1420  	benchmarkProgram(b, nil, "", "", `
  1421  BEGIN {
  1422    for (i = 0; i < %d; i++) {
  1423    	for (j = 0; j < 100; j++);
  1424    }
  1425  }
  1426  `, b.N)
  1427  }
  1428  
  1429  func BenchmarkForInLoop(b *testing.B) {
  1430  	benchmarkProgram(b, nil, "", "", `
  1431  BEGIN {
  1432    for (j = 0; j < 100; j++) {
  1433    	a[j] = j
  1434    }
  1435    for (i = 0; i < %d; i++) {
  1436      for (k in a);
  1437    }
  1438  }
  1439  `, b.N)
  1440  }
  1441  
  1442  func BenchmarkIfStatement(b *testing.B) {
  1443  	benchmarkProgram(b, nil, "", "0", `
  1444  BEGIN {
  1445    c = 1
  1446    d = 0
  1447    for (i = 0; i < %d; i++) {
  1448    	if (c) { x = 1 } else { x = 0 }
  1449    	if (c) { x = 1 } else { x = 0 }
  1450    	if (c) { x = 1 } else { x = 0 }
  1451    	if (d) { x = 1 } else { x = 0 }
  1452    	if (d) { x = 1 } else { x = 0 }
  1453    	if (d) { x = 1 } else { x = 0 }
  1454    }
  1455    print x
  1456  }
  1457  `, b.N)
  1458  }
  1459  
  1460  func BenchmarkCondExpr(b *testing.B) {
  1461  	benchmarkProgram(b, nil, "", "0", `
  1462  BEGIN {
  1463    c = 1
  1464    d = 0
  1465    for (i = 0; i < %d; i++) {
  1466    	x = c ? 1 : 0
  1467    	x = c ? 1 : 0
  1468    	x = c ? 1 : 0
  1469    	x = d ? 1 : 0
  1470    	x = d ? 1 : 0
  1471    	x = d ? 1 : 0
  1472    }
  1473    print x
  1474  }
  1475  `, b.N)
  1476  }
  1477  
  1478  func BenchmarkSimplePattern(b *testing.B) {
  1479  	b.StopTimer()
  1480  	inputLines := []string{}
  1481  	expectedLines := []string{}
  1482  	for i := 0; i < b.N; i++ {
  1483  		if i != 0 && i%2 == 0 {
  1484  			line := fmt.Sprintf("%d", i)
  1485  			inputLines = append(inputLines, line)
  1486  			expectedLines = append(expectedLines, line)
  1487  		} else {
  1488  			inputLines = append(inputLines, "")
  1489  		}
  1490  	}
  1491  	input := strings.Join(inputLines, "\n")
  1492  	expected := strings.Join(expectedLines, "\n")
  1493  	benchmarkProgram(b, nil, input, expected, "$0")
  1494  }
  1495  
  1496  func BenchmarkGetField(b *testing.B) {
  1497  	b.StopTimer()
  1498  	inputLines := []string{}
  1499  	expectedLines := []string{}
  1500  	for i := 1; i < b.N+1; i++ {
  1501  		inputLines = append(inputLines, fmt.Sprintf("%d %d %d", i, i*2, i*3))
  1502  		expectedLines = append(expectedLines, fmt.Sprintf("%d %d", i, i*3))
  1503  	}
  1504  	input := strings.Join(inputLines, "\n")
  1505  	expected := strings.Join(expectedLines, "\n")
  1506  	benchmarkProgram(b, nil, input, expected, "{ print $1, $3 }")
  1507  }
  1508  
  1509  func BenchmarkSetField(b *testing.B) {
  1510  	benchmarkProgram(b, nil, "1 2 3", "one 2 three", `
  1511  {
  1512    for (i = 0; i < %d; i++) {
  1513      $1 = "one"; $3 = "three"
  1514      $1 = "one"; $3 = "three"
  1515      $1 = "one"; $3 = "three"
  1516      $1 = "one"; $3 = "three"
  1517      $1 = "one"; $3 = "three"
  1518    }
  1519  }
  1520  END {
  1521  	print $0
  1522  }
  1523  `, b.N)
  1524  }
  1525  
  1526  func BenchmarkRegexMatch(b *testing.B) {
  1527  	benchmarkProgram(b, nil, "", "1", `
  1528  BEGIN {
  1529    s = "The quick brown fox jumps over the lazy dog"
  1530    for (i = 0; i < %d; i++) {
  1531    	x = s ~ /j[a-z]+p/
  1532    	x = s ~ /j[a-z]+p/
  1533    	x = s ~ /j[a-z]+p/
  1534    	x = s ~ /j[a-z]+p/
  1535    	x = s ~ /j[a-z]+p/
  1536    }
  1537    print x
  1538  }
  1539  `, b.N)
  1540  }
  1541  
  1542  func BenchmarkBinaryOperators(b *testing.B) {
  1543  	benchmarkProgram(b, nil, "", "5.0293", `
  1544  BEGIN {
  1545    for (i = 0; i < %d; i++) {
  1546      res = (1+2*3/4^5) + (1+2*3/4^5) + (1+2*3/4^5) + (1+2*3/4^5) + (1+2*3/4^5)
  1547    }
  1548    print res
  1549  }
  1550  `, b.N)
  1551  }
  1552  
  1553  func BenchmarkConcatSmall(b *testing.B) {
  1554  	b.StopTimer()
  1555  	benchmarkProgram(b, nil, "", "100", `
  1556  BEGIN {
  1557    x = "0123456789"
  1558    for (i = 0; i < %d; i++) {
  1559    	y = x x x x x x x x x x
  1560    }
  1561    print length(y)
  1562  }
  1563  `, b.N)
  1564  }
  1565  
  1566  func BenchmarkConcatLarge(b *testing.B) {
  1567  	b.StopTimer()
  1568  	benchmarkProgram(b, nil, "", "1000000", `
  1569  BEGIN {
  1570    x = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
  1571    for (i = 0; i < %d; i++) {
  1572    	y = x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x \
  1573    	    x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x
  1574      z = y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y \
  1575          y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y y
  1576    }
  1577    print length(z)
  1578  }
  1579  `, b.N)
  1580  }
  1581  
  1582  func BenchmarkComparisons(b *testing.B) {
  1583  	b.StopTimer()
  1584  	benchmarkProgram(b, nil, "", "1", `
  1585  BEGIN {
  1586    for (i = 0; i < %d; i++) {
  1587    	x = ((((((1 < 2) <= 3) > 4) >= 5) == 6) != 7)
  1588    	x = ((((((1 < 2) <= 3) > 4) >= 5) == 6) != 7)
  1589    	x = ((((((1 < 2) <= 3) > 4) >= 5) == 6) != 7)
  1590    }
  1591    print x
  1592  }
  1593  `, b.N)
  1594  }
  1595  
  1596  func BenchmarkArrayOperations(b *testing.B) {
  1597  	b.StopTimer()
  1598  	benchmarkProgram(b, nil, "", "243", `
  1599  BEGIN {
  1600    for (i = 0; i < %d; i++) {
  1601    	a[0] = 1
  1602    	a[0] = a[0] + a[0] + a[0]
  1603    	a[0] = a[0] + a[0] + a[0]
  1604    	a[0] = a[0] + a[0] + a[0]
  1605    	a[0] = a[0] + a[0] + a[0]
  1606    	a[0] = a[0] + a[0] + a[0]
  1607    }
  1608    print a[0]
  1609  }
  1610  `, b.N)
  1611  }
  1612  
  1613  func BenchmarkAssign(b *testing.B) {
  1614  	b.StopTimer()
  1615  	benchmarkProgram(b, nil, "", "0 1 2 3 4", `
  1616  BEGIN {
  1617    for (i = 0; i < %d; i++) {
  1618    	v=0; w=1; x=2; y=3; z=4
  1619    	v=0; w=1; x=2; y=3; z=4
  1620    	v=0; w=1; x=2; y=3; z=4
  1621    	v=0; w=1; x=2; y=3; z=4
  1622    	v=0; w=1; x=2; y=3; z=4
  1623    }
  1624    print v, w, x, y, z
  1625  }
  1626  `, b.N)
  1627  }
  1628  
  1629  func BenchmarkAugAssign(b *testing.B) {
  1630  	b.StopTimer()
  1631  	benchmarkProgram(b, nil, "", "5 -9 729 32 3.0536 2", `
  1632  BEGIN {
  1633    for (i = 0; i < %d; i++) {
  1634    	a = 0; b = 1; c = 3; d = 1024; e = 2; f = 14
  1635    	a += 1; b -= 2; c *= 3; d /= 2; e ^= 1.1; f %%= 6
  1636    	a += 1; b -= 2; c *= 3; d /= 2; e ^= 1.1; f %%= 6
  1637    	a += 1; b -= 2; c *= 3; d /= 2; e ^= 1.1; f %%= 6
  1638    	a += 1; b -= 2; c *= 3; d /= 2; e ^= 1.1; f %%= 6
  1639    	a += 1; b -= 2; c *= 3; d /= 2; e ^= 1.1; f %%= 6
  1640    }
  1641    print a, b, c, d, e, f
  1642  }
  1643  `, b.N)
  1644  }
  1645  
  1646  func normalizeNewlines(s string) string {
  1647  	return strings.Replace(s, "\r\n", "\n", -1)
  1648  }