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 }