From 7baa24e9b7fc6083eda453637a1f98c96006bad2 Mon Sep 17 00:00:00 2001 From: Ben Hoyt Date: Wed, 18 May 2022 20:38:26 +1200 Subject: [PATCH] Remove awkgo directory (it's now on a branch) --- README.md | 2 +- awkgo/awkgo.go | 194 ------- awkgo/awkgo_test.go | 672 ----------------------- awkgo/compiler.go | 999 ---------------------------------- awkgo/examples/about.awk | 1 - awkgo/examples/about.go | 356 ------------ awkgo/examples/countwords.awk | 9 - awkgo/examples/countwords.go | 361 ------------ awkgo/examples/trickier.awk | 2 - awkgo/examples/trickier.go | 356 ------------ awkgo/helpers.go | 289 ---------- awkgo/run_tests.sh | 2 - awkgo/tests.txt | 428 --------------- awkgo/typer.go | 374 ------------- 14 files changed, 1 insertion(+), 4044 deletions(-) delete mode 100644 awkgo/awkgo.go delete mode 100644 awkgo/awkgo_test.go delete mode 100644 awkgo/compiler.go delete mode 100644 awkgo/examples/about.awk delete mode 100644 awkgo/examples/about.go delete mode 100644 awkgo/examples/countwords.awk delete mode 100644 awkgo/examples/countwords.go delete mode 100644 awkgo/examples/trickier.awk delete mode 100644 awkgo/examples/trickier.go delete mode 100644 awkgo/helpers.go delete mode 100755 awkgo/run_tests.sh delete mode 100644 awkgo/tests.txt delete mode 100644 awkgo/typer.go diff --git a/README.md b/README.md index a49c888e..ab6f25b8 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ This project has a good suite of tests, which include my own intepreter tests, t ## AWKGo -The GoAWK repository also includes the creatively-named AWKGo, an AWK-to-Go compiler. This is experimental and is not subject to the stability requirements of GoAWK itself. You can [read more about AWKGo](https://benhoyt.com/writings/awkgo/) or [browse the AWKGo code](https://github.com/benhoyt/goawk/tree/master/awkgo). +The GoAWK repository also includes the creatively-named AWKGo, an AWK-to-Go compiler. This is experimental and is not subject to the stability requirements of GoAWK itself. You can [read more about AWKGo](https://benhoyt.com/writings/awkgo/) or browse the code on the [`awkgo` branch](https://github.com/benhoyt/goawk/tree/awkgo/awkgo). ## License diff --git a/awkgo/awkgo.go b/awkgo/awkgo.go deleted file mode 100644 index 1c420314..00000000 --- a/awkgo/awkgo.go +++ /dev/null @@ -1,194 +0,0 @@ -// AWKGo: AWK to Go compiler - -/* -NOT SUPPORTED: -- functions -- dynamic typing -- non-literal [s]printf format strings? -- assigning numStr values (but using $0 in conditionals works) -- null values (unset number variable should output "", we output "0") -- print redirection -- getline -- reference to nonexistent array element should create it (POSIX, but yuck) -- some forms of augmented assignment (no reason we can't, just not done yet) -*/ - -package main - -import ( - "bytes" - "fmt" - "github.com/benhoyt/goawk/lexer" - "github.com/benhoyt/goawk/parser" - "os" - "path/filepath" - "runtime" - "strings" - "unicode/utf8" -) - -const ( - version = "v1.0.0" - copyright = "AWKGo " + version + " - Copyright (c) 2021 Ben Hoyt" - shortUsage = "usage: awkgo [-f progfile | 'prog']" - longUsage = `AWKGo arguments: - -f progfile - load AWK source from progfile (multiple allowed) - -h show this usage message - -version - show AWKGo version and exit -` -) - -func main() { - var progFiles []string - - var i int - for i = 1; i < len(os.Args); i++ { - // Stop on explicit end of args or first arg not prefixed with "-" - arg := os.Args[i] - if arg == "--" { - i++ - break - } - if arg == "-" || !strings.HasPrefix(arg, "-") { - break - } - - switch arg { - case "-f": - if i+1 >= len(os.Args) { - errorExitf("flag needs an argument: -f") - } - i++ - progFiles = append(progFiles, os.Args[i]) - case "-h", "--help": - fmt.Printf("%s\n\n%s\n\n%s", copyright, shortUsage, longUsage) - os.Exit(0) - case "-version", "--version": - fmt.Println(version) - os.Exit(0) - default: - switch { - case strings.HasPrefix(arg, "-f"): - progFiles = append(progFiles, arg[2:]) - default: - errorExitf("flag provided but not defined: %s", arg) - } - } - } - - // Any remaining arg is the program source - args := os.Args[i:] - - var src []byte - if len(progFiles) > 0 { - // Read source: the concatenation of all source files specified - buf := &bytes.Buffer{} - progFiles = expandWildcardsOnWindows(progFiles) - for _, progFile := range progFiles { - if progFile == "-" { - _, err := buf.ReadFrom(os.Stdin) - if err != nil { - errorExit(err) - } - } else { - f, err := os.Open(progFile) - if err != nil { - errorExit(err) - } - _, err = buf.ReadFrom(f) - if err != nil { - _ = f.Close() - errorExit(err) - } - _ = f.Close() - } - // Append newline to file in case it doesn't end with one - _ = buf.WriteByte('\n') - } - src = buf.Bytes() - } else { - if len(args) < 1 { - errorExitf(shortUsage) - } - src = []byte(args[0]) - args = args[1:] - } - - if len(args) > 0 { - errorExitf("additional arguments not allowed: %v\n%s", strings.Join(args, " "), shortUsage) - } - - prog, err := parser.ParseProgram(src, nil) - if err != nil { - errMsg := fmt.Sprintf("%s", err) - if err, ok := err.(*parser.ParseError); ok { - showSourceLine(src, err.Position, len(errMsg)) - } - errorExitf("%s", errMsg) - } - - err = compile(prog, os.Stdout) - if err != nil { - errorExitf("%v", err) - } -} - -// For parse errors, show source line and position of error, eg: -// -// ----------------------------------------------------- -// BEGIN { x*; } -// ^ -// ----------------------------------------------------- -// parse error at 1:11: expected expression instead of ; -// -func showSourceLine(src []byte, pos lexer.Position, dividerLen int) { - divider := strings.Repeat("-", dividerLen) - if divider != "" { - fmt.Fprintln(os.Stderr, divider) - } - lines := bytes.Split(src, []byte{'\n'}) - srcLine := string(lines[pos.Line-1]) - numTabs := strings.Count(srcLine[:pos.Column-1], "\t") - runeColumn := utf8.RuneCountInString(srcLine[:pos.Column-1]) - fmt.Fprintln(os.Stderr, strings.Replace(srcLine, "\t", " ", -1)) - fmt.Fprintln(os.Stderr, strings.Repeat(" ", runeColumn)+strings.Repeat(" ", numTabs)+"^") - if divider != "" { - fmt.Fprintln(os.Stderr, divider) - } -} - -func errorExit(err error) { - pathErr, ok := err.(*os.PathError) - if ok && os.IsNotExist(err) { - errorExitf("file %q not found", pathErr.Path) - } - errorExitf("%s", err) -} - -func errorExitf(format string, args ...interface{}) { - fmt.Fprintf(os.Stderr, format+"\n", args...) - os.Exit(1) -} - -func expandWildcardsOnWindows(args []string) []string { - if runtime.GOOS != "windows" { - return args - } - return expandWildcards(args) -} - -// Originally from https://github.com/mattn/getwild (compatible LICENSE). -func expandWildcards(args []string) []string { - result := make([]string, 0, len(args)) - for _, arg := range args { - matches, err := filepath.Glob(arg) - if err == nil && len(matches) > 0 { - result = append(result, matches...) - } else { - result = append(result, arg) - } - } - return result -} diff --git a/awkgo/awkgo_test.go b/awkgo/awkgo_test.go deleted file mode 100644 index 2ba8c4b9..00000000 --- a/awkgo/awkgo_test.go +++ /dev/null @@ -1,672 +0,0 @@ -// This is copied and hacked from goawk/interp/interp_test.go - -//go:build awkgotest -// +build awkgotest - -package main - -import ( - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - "testing" - - "github.com/benhoyt/goawk/parser" -) - -type interpTest struct { - src string - in string - out string - err string -} - -var interpTests = []interpTest{ - // BEGIN and END work correctly - {`BEGIN { print "b" }`, "", "b\n", ""}, - {`BEGIN { print "b" }`, "foo", "b\n", ""}, - {`END { print "e" }`, "", "e\n", ""}, - {`END { print "e" }`, "foo", "e\n", ""}, - {`BEGIN { print "b"} END { print "e" }`, "", "b\ne\n", ""}, - {`BEGIN { print "b"} END { print "e" }`, "foo", "b\ne\n", ""}, - {`BEGIN { print "b"} $0 { print NR } END { print "e" }`, "foo", "b\n1\ne\n", ""}, - {`BEGIN { printf "x" }; BEGIN { printf "y" }`, "", "xy", ""}, - - // Patterns - {`$0`, "foo\n\nbar", "foo\nbar\n", ""}, - {`{ print $0 }`, "foo\n\nbar", "foo\n\nbar\n", ""}, - {`$1=="foo"`, "foo\n\nbar", "foo\n", ""}, - {`$1==42`, "foo\n42\nbar", "42\n", ""}, - {`$1=="42"`, "foo\n42\nbar", "42\n", ""}, - {`/foo/`, "foo\nx\nfood\nxfooz\nbar", "foo\nfood\nxfooz\n", ""}, - {`NR==2, NR==4`, "1\n2\n3\n4\n5\n6\n", "2\n3\n4\n", ""}, - {` -NR==2, NR==4 { print $0 } -NR==3, NR==5 { print NR } -`, "a\nb\nc\nd\ne\nf\ng", "b\nc\n3\nd\n4\n5\n", ""}, - - // print and printf statements - {`BEGIN { print "x", "y" }`, "", "x y\n", ""}, - {`BEGIN { print OFS; OFS = ","; print "x", "y" }`, "", " \nx,y\n", ""}, - {`BEGIN { print ORS; ORS = "."; print "x", "y" }`, "", "\n\nx y.", ""}, - {`BEGIN { print ORS; ORS = ""; print "x", "y" }`, "", "\n\nx y", ""}, - {`{ print; print }`, "foo", "foo\nfoo\n", ""}, - {`BEGIN { print; print }`, "", "\n\n", ""}, - {`BEGIN { printf "%% %d %x %c %f %s", 42, 42, 42, 42, 42 }`, "", "% 42 2a * 42.000000 42", ""}, - {`BEGIN { printf "%3d", 42 }`, "", " 42", ""}, - {`BEGIN { printf "%3s", "x" }`, "", " x", ""}, - // {`BEGIN { printf "%.1g", 42 }`, "", "4e+01", ""}, // Comment out for now, for some reason gives "4e+001" on Windows - {`BEGIN { printf "%d", 12, 34 }`, "", "12", ""}, - {`BEGIN { printf "%d" }`, "", "", `not enough arguments (0) for format string "%d"`}, - // Our %c handling is mostly like awk's, except for multiples - // 256, where awk is weird and we're like mawk - {`BEGIN { printf "%c", 0 }`, "", "\x00", ""}, - {`BEGIN { printf "%c", 127 }`, "", "\x7f", ""}, - {`BEGIN { printf "%c", 128 } # !gawk`, "", "\u0080", ""}, - {`BEGIN { printf "%c", 255 } # !gawk`, "", "\u00ff", ""}, - {`BEGIN { printf "%c", 256 } # !awk !gawk`, "", "\u0100", ""}, - {`BEGIN { printf "%c", "xyz" }`, "", "x", ""}, - {`BEGIN { printf "%c", "" } # !awk`, "", "\x00", ""}, - {`BEGIN { printf } # !awk - doesn't error on this`, "", "", "parse error at 1:16: expected printf args, got none"}, - {`BEGIN { printf("%%%dd", 4) }`, "", "%4d", ""}, - - // if and loop statements - {`BEGIN { if (1) print "t"; }`, "", "t\n", ""}, - {`BEGIN { if (0) print "t"; }`, "", "", ""}, - {`BEGIN { if (1) print "t"; else print "f" }`, "", "t\n", ""}, - {`BEGIN { if (0) print "t"; else print "f" }`, "", "f\n", ""}, - {`BEGIN { for (;;) { print "x"; break } }`, "", "x\n", ""}, - {`BEGIN { for (;;) { printf "%d ", i; i++; if (i>2) break; } }`, "", "0 1 2 ", ""}, - {`BEGIN { for (i=5; ; ) { printf "%d ", i; i++; if (i>8) break; } }`, "", "5 6 7 8 ", ""}, - {`BEGIN { for (i=5; ; i++) { printf "%d ", i; if (i>8) break; } }`, "", "5 6 7 8 9 ", ""}, - {`BEGIN { for (i=5; i<8; i++) { printf "%d ", i } }`, "", "5 6 7 ", ""}, - {`BEGIN { for (i=0; i<10; i++) { if (i < 5) continue; printf "%d ", i } }`, "", "5 6 7 8 9 ", ""}, - {`BEGIN { a[1]=1; a[2]=1; for (k in a) { s++; break } print s }`, "", "1\n", ""}, - {`BEGIN { a[1]=1; a[2]=1; a[3]=1; for (k in a) { if (k==2) continue; s++ } print s }`, "", "2\n", ""}, - {`BEGIN { while (i<3) { i++; s++; break } print s }`, "", "1\n", ""}, - {`BEGIN { while (i<3) { i++; if (i==2) continue; s++ } print s }`, "", "2\n", ""}, - {`BEGIN { do { i++; s++; break } while (i<3); print s }`, "", "1\n", ""}, - {`BEGIN { do { i++; if (i==2) continue; s++ } while (i<3); print s }`, "", "2\n", ""}, - {`BEGIN { a["x"] = 3; a["y"] = 4; for (k in a) x += a[k]; print x }`, "", "7\n", ""}, - {`BEGIN { while (i < 5) { print i; i++ } }`, "", "0\n1\n2\n3\n4\n", ""}, - {`BEGIN { do { print i; i++ } while (i < 5) }`, "", "0\n1\n2\n3\n4\n", ""}, - {`BEGIN { for (i=0; i<10; i++); printf "x" }`, "", "x", ""}, - // regression tests for break and continue with nested loops - {` -BEGIN { - for (i = 0; i < 1; i++) { - for (j = 0; j < 1; j++) { - print i, j - } - break - } -} -`, "", "0 0\n", ""}, - {` -BEGIN { - for (i = 0; i < 1; i++) { - for (j = 0; j < 1; j++) { - print i, j - } - continue - } -} -`, "", "0 0\n", ""}, - - // next statement - {`{ if (NR==2) next; print }`, "a\nb\nc", "a\nc\n", ""}, - //{`{ if (NR==2) f(); print } function f() { next }`, "a\nb\nc", "a\nc\n", "functions not yet supported"}, - {`BEGIN { next }`, "", "", "parse error at 1:9: next can't be inside BEGIN or END"}, - {`END { next }`, "", "", "parse error at 1:7: next can't be inside BEGIN or END"}, - - // Arrays, "in", and delete - {`BEGIN { a["x"] = 3; print "x" in a, "y" in a }`, "", "1 0\n", ""}, - {`BEGIN { a["x"] = 3; a["y"] = 4; delete a["x"]; for (k in a) print k, a[k] }`, "", "y 4\n", ""}, - {`BEGIN { a["x"] = 3; a["y"] = 4; for (k in a) delete a[k]; for (k in a) print k, a[k] }`, "", "", ""}, - //{`BEGIN { a["x"]; "y" in a; for (k in a) print k, a[k] }`, "", "x \n", ""}, - {`BEGIN { a[] }`, "", "", "parse error at 1:11: expected expression instead of ]"}, - {`BEGIN { delete a[] }`, "", "", "parse error at 1:18: expected expression instead of ]"}, - {`BEGIN { a["x"] = 3; a["y"] = 4; delete a; for (k in a) print k, a[k] }`, "", "", ""}, - - // Unary expressions: ! + - - {`BEGIN { print !42, !1, !0, !!42, !!1, !!0 }`, "", "0 0 1 1 1 0\n", ""}, - {`BEGIN { print !42, !1, !0, !!42, !!1, !!0 }`, "", "0 0 1 1 1 0\n", ""}, - {`BEGIN { print +4, +"3", +0, +-3, -3, - -4, -"3" }`, "", "4 3 0 -3 -3 4 -3\n", ""}, - // {`BEGIN { $0="0"; print !$0 }`, "", "0\n", ""}, - {`BEGIN { $0="1"; print !$0 }`, "", "0\n", ""}, - {`{ print !$0 }`, "0\n", "1\n", ""}, - {`{ print !$0 }`, "1\n", "0\n", ""}, - {`!seen[$0]++`, "1\n2\n3\n2\n3\n3\n", "1\n2\n3\n", ""}, - {`!seen[$0]--`, "1\n2\n3\n2\n3\n3\n", "1\n2\n3\n", ""}, - - // Comparison expressions: == != < <= > >= - {`BEGIN { print (1==1, 1==0, "1"==1, "1"==1.0) }`, "", "1 0 1 1\n", ""}, - {`{ print ($0=="1", $0==1) }`, "1\n1.0\n+1", "1 1\n0 1\n0 1\n", ""}, - {`{ print ($1=="1", $1==1) }`, "1\n1.0\n+1", "1 1\n0 1\n0 1\n", ""}, - {`BEGIN { print (1!=1, 1!=0, "1"!=1, "1"!=1.0) }`, "", "0 1 0 0\n", ""}, - {`{ print ($0!="1", $0!=1) }`, "1\n1.0\n+1", "0 0\n1 0\n1 0\n", ""}, - {`{ print ($1!="1", $1!=1) }`, "1\n1.0\n+1", "0 0\n1 0\n1 0\n", ""}, - {`BEGIN { print (0<1, 1<1, 2<1, "12"<"2") }`, "", "1 0 0 1\n", ""}, - {`{ print ($1<2) }`, "1\n1.0\n+1", "1\n1\n1\n", ""}, - {`BEGIN { print (0<=1, 1<=1, 2<=1, "12"<="2") }`, "", "1 1 0 1\n", ""}, - {`{ print ($1<=2) }`, "1\n1.0\n+1", "1\n1\n1\n", ""}, - {`BEGIN { print (0>1, 1>1, 2>1, "12">"2") }`, "", "0 0 1 0\n", ""}, - {`{ print ($1>2) }`, "1\n1.0\n+1", "0\n0\n0\n", ""}, - {`BEGIN { print (0>=1, 1>=1, 2>=1, "12">="2") }`, "", "0 1 1 0\n", ""}, - {`{ print ($1>=2) }`, "1\n1.0\n+1", "0\n0\n0\n", ""}, - - // Short-circuit && and || operators - // {` - //function t() { print "t"; return 1 } - //function f() { print "f"; return 0 } - //BEGIN { - // print f() && f() - // print f() && t() - // print t() && f() - // print t() && t() - //} - //`, "", "f\n0\nf\n0\nt\nf\n0\nt\nt\n1\n", ""}, - // {` - //function t() { print "t"; return 1 } - //function f() { print "f"; return 0 } - //BEGIN { - // print f() || f() - // print f() || t() - // print t() || f() - // print t() || t() - //} - //`, "", "f\nf\n0\nf\nt\n1\nt\n1\nt\n1\n", ""}, - - // Other binary expressions: + - * ^ / % CONCAT ~ !~ - {`BEGIN { print 1+2, 1+2+3, 1+-2, -1+2, "1"+"2", 3+.14 }`, "", "3 6 -1 1 3 3.14\n", ""}, - {`BEGIN { print 1-2, 1-2-3, 1-+2, -1-2, "1"-"2", 3-.14 }`, "", "-1 -4 -1 -3 -1 2.86\n", ""}, - {`BEGIN { print 2*3, 2*3*4, 2*-3, -2*3, "2"*"3", 3*.14 }`, "", "6 24 -6 -6 6 0.42\n", ""}, - {`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", ""}, - {`BEGIN { print 2%3, 2%3%4, 2%-3, -2%3, "2"%"3", 3%.14 }`, "", "2 2 2 -2 2 0.06\n", ""}, - {`BEGIN { print 2^3, 2^3^3, 2^-3, -2^3, "2"^"3", 3^.14 }`, "", "8 134217728 0.125 -8 8 1.16626\n", ""}, - {`BEGIN { print 1 2, "x" "yz", 1+2 3+4 }`, "", "12 xyz 37\n", ""}, - {`BEGIN { print "food"~/oo/, "food"~/[oO]+d/, "food"~"f", "food"~"F", "food"~0 }`, "", "1 1 1 0 0\n", ""}, - {`BEGIN { print "food"!~/oo/, "food"!~/[oO]+d/, "food"!~"f", "food"!~"F", "food"!~0 }`, "", "0 0 0 1 1\n", ""}, - {`BEGIN { print 1+2*3/4^5%6 7, (1+2)*3/4^5%6 "7" }`, "", "1.005867 0.008789067\n", ""}, - - // Number, string, and regex expressions - //{`BEGIN { print 1, 1., .1, 1e0, -1, 1e }`, "", "1 1 0.1 1 -1 1\n", ""}, - {`BEGIN { print '\"' '\'' 'xy' "z" "'" '\"' }`, "", "\"'xyz'\"\n", ""}, // Check support for single-quoted strings - {`{ print /foo/ }`, "food\nfoo\nxfooz\nbar\n", "1\n1\n1\n0\n", ""}, - {`/[a-/`, "foo", "", "parse error at 1:1: error parsing regexp: missing closing ]: `[a-`"}, - {`BEGIN { print "-12"+0, "+12"+0, " \t\r\n7foo"+0, ".5"+0, "5."+0, "+."+0 }`, "", "-12 12 7 0.5 5 0\n", ""}, - {`BEGIN { print "1e3"+0, "1.2e-1"+0, "1e+1"+0, "1e"+0, "1e+"+0 }`, "", "1000 0.12 10 1 1\n", ""}, - {`BEGIN { print -(11102200000000000000000000000000000000 1040000) } # !gawk - gawk supports big numbers`, - "", "-inf\n", ""}, - //{`BEGIN { print atan2(0, 8020020000000e20G-0)}`, "", "0\n", ""}, - //{`BEGIN { print 1e1000, -1e1000 } # !gawk`, "", "inf -inf\n", ""}, - {`BEGIN { printf "\x0.\x00.\x0A\x10\xff\xFF\x41" } # !awk`, "", "\x00.\x00.\n\x10\xff\xffA", ""}, - {`BEGIN { printf "\x1.\x01.\x0A\x10\xff\xFF\x41" }`, "", "\x01.\x01.\n\x10\xff\xffA", ""}, - {`BEGIN { printf "\0\78\7\77\777\0 \141 " } # !awk`, "", "\x00\a8\a?\xff\x00 a ", ""}, - {`BEGIN { printf "\1\78\7\77\777\1 \141 " }`, "", "\x01\a8\a?\xff\x01 a ", ""}, - - // Conditional ?: expression - {`{ print /x/?"t":"f" }`, "x\ny\nxx\nz\n", "t\nf\nt\nf\n", ""}, - {`BEGIN { print 1?2?3:4:5, 1?0?3:4:5, 0?2?3:4:5 }`, "", "3 4 5\n", ""}, - // {`BEGIN { $0="0"; print ($0?1:0) }`, "", "1\n", ""}, - {`{ print $0?1:0 }`, "0\n", "0\n", ""}, - {`{ print $0?1:0 }`, "1\n", "1\n", ""}, - {`BEGIN { $0="1"; print ($0?1:0) }`, "", "1\n", ""}, - {`BEGIN { print 0?1:0, 1?1:0, ""?1:0, "0"?1:0, "1"?1:0 }`, "", "0 1 0 1 1\n", ""}, - - // Built-in variables - // ARGC is tested in goawk_test.go - {` -BEGIN { - print CONVFMT, 1.2345678 "" - CONVFMT = "%.3g" - print CONVFMT, 1.234567 "" -}`, "", "%.6g 1.23457\n%.3g 1.23\n", ""}, - //{`BEGIN { FILENAME = "foo"; print FILENAME }`, "", "foo\n", ""}, - //{`BEGIN { FILENAME = "123.0"; print (FILENAME==123) }`, "", "0\n", ""}, - // Other FILENAME behaviour is tested in goawk_test.go - //{`BEGIN { FNR = 123; print FNR }`, "", "123\n", ""}, - {`{ print FNR, $0 }`, "a\nb\nc", "1 a\n2 b\n3 c\n", ""}, - // Other FNR behaviour is tested in goawk_test.go - {`BEGIN { print "|" FS "|"; FS="," } { print $1, $2 }`, "a b\na,b\nx,,y", "| |\na b \na b\nx \n", ""}, - {`BEGIN { print "|" FS "|"; FS="\\." } { print $1, $2 }`, "a b\na.b\nx..y", "| |\na b \na b\nx \n", ""}, - {`BEGIN { FS="\\" } { print $1, $2 }`, "a\\b", "a b\n", ""}, - {`{ print NF }`, "\na\nc d\ne f g", "0\n1\n2\n3\n", ""}, - //{`BEGIN { NR = 123; print NR }`, "", "123\n", ""}, - {`{ print NR, $0 }`, "a\nb\nc", "1 a\n2 b\n3 c\n", ""}, - {` -BEGIN { - print OFMT, 1.2345678 - OFMT = "%.3g" - print OFMT, 1.234567 -}`, "", "%.6g 1.23457\n%.3g 1.23\n", ""}, - // OFS and ORS are tested above - {`BEGIN { print RSTART, RLENGTH; RSTART=5; RLENGTH=42; print RSTART, RLENGTH; } `, "", - "0 0\n5 42\n", ""}, - //{`BEGIN { print RS }`, "", "\n\n", ""}, - //{`BEGIN { print RS; RS="|"; print RS } { print }`, "a b|c d|", "\n\n|\na b\nc d\n", ""}, - //{`BEGIN { RS=""; FS="\n" } { printf "%d (%d):\n", NR, NF; for (i=1; i<=NF; i++) print $i }`, - // "a\n\nb\nc", - // "1 (1):\na\n2 (2):\nb\nc\n", ""}, - //{`BEGIN { RS=""; FS="\n" } { printf "%d (%d):\n", NR, NF; for (i=1; i<=NF; i++) print $i }`, - // "1\n2\n\na\nb", - // "1 (2):\n1\n2\n2 (2):\na\nb\n", ""}, - //{`BEGIN { RS=""; FS="\n" } { printf "%d (%d):\n", NR, NF; for (i=1; i<=NF; i++) print $i }`, - // "a b\nc d\n\ne f\n\n\n \n\n\ng h\n\n\n", - // "1 (2):\na b\nc d\n2 (1):\ne f\n3 (1):\n \n4 (1):\ng h\n", ""}, - //{`BEGIN { RS=""; FS="\n" } { printf "%d (%d):\n", NR, NF; for (i=1; i<=NF; i++) print $i }`, - // "\n\na b\n\nc d\n", - // "1 (1):\na b\n2 (1):\nc d\n", ""}, - //{`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==""`, - // "\r\n\r\na b\r\n\r\nc d\r\n", - // "1 (1):\na b\n2 (1):\nc d\n", ""}, - //{`BEGIN { RS=""; FS="X" } { printf "%d (%d):\n", NR, NF; for (i=1; i<=NF; i++) printf "%s|", $i }`, - // "aXb\ncXd\n\neXf\n\n\n \n\n\ngXh\n\n\n", - // "1 (4):\na|b|c|d|2 (2):\ne|f|3 (1):\n |4 (2):\ng|h|", ""}, - //{`BEGIN { RS = "" } { print "got", $0 }`, - // "\n\n\n\n", "", ""}, - //{`BEGIN { RS="\n" } { print }`, "a\n\nb\nc", "a\n\nb\nc\n", ""}, - {` -BEGIN { - print SUBSEP - a[1, 2] = "onetwo" - print a[1, 2] - for (k in a) { - print k, a[k] - } - delete a[1, 2] - SUBSEP = "|" - print SUBSEP - a[1, 2] = "onetwo" - print a[1, 2] - for (k in a) { - print k, a[k] - } -}`, "", "\x1c\nonetwo\n1\x1c2 onetwo\n|\nonetwo\n1|2 onetwo\n", ""}, - - // Field expressions and assignment (and interaction with NF) - //{`{ print NF; NF=1; $2="two"; print $0, NF }`, "\n", "0\n two 2\n", ""}, - //{`{ print NF; NF=2; $2="two"; print $0, NF}`, "\n", "0\n two 2\n", ""}, - //{`{ print NF; NF=3; $2="two"; print $0, NF}`, "a b c\n", "3\na two c 3\n", ""}, - {`{ print; print $1, $3, $NF }`, "a b c d e", "a b c d e\na c e\n", ""}, - {`{ print $1,$3; $2="x"; print; print $2 }`, "a b c", "a c\na x c\nx\n", ""}, - {`{ print; $0="x y z"; print; print $1, $3 }`, "a b c", "a b c\nx y z\nx z\n", ""}, - {`{ print $1^2 }`, "10", "100\n", ""}, - //{`{ print $-1 }`, "x", "", "field index negative: -1"}, - //{`{ NF=-1; } # !awk - awk allows setting negative NF`, - // "x", "", "NF set to negative value: -1"}, - //{`{ NF=1234567; }`, "x", "", "NF set too large: 1234567"}, - {`BEGIN { $1234567=1 }`, "", "", "field index too large: 1234567"}, - {`0 in FS # !awk - doesn't flag this as an error`, "x", "", - `parse error at 1:6: can't use scalar "FS" as array`}, - // I think this is happening because we parse this as ($($0))++ rather than ($($0++)) - // {`{ $$0++; print $0 }`, "2 3 4", "3\n", ""}, - // {`BEGIN { $0="3 4 5 6 7 8 9"; a=3; print $$a++++; print }`, "", "7\n3 4 6 6 8 8 9\n", ""}, - - // Lots of NF tests with different combinations of NF, $, and number - // of input fields. Some of these cause segmentation faults on awk - // (but work fine on gawk and mawk). - //{`{ NF=1; $1="x"; print $0; print NF }`, "a", "x\n1\n", ""}, - //{`{ NF=1; $1="x"; print $0; print NF }`, "a b", "x\n1\n", ""}, - //{`{ NF=1; $1="x"; print $0; print NF }`, "a b c", "x\n1\n", ""}, - //{`{ NF=1; $2="x"; print $0; print NF }`, "a", "a x\n2\n", ""}, - //{`{ NF=1; $2="x"; print $0; print NF }`, "a b", "a x\n2\n", ""}, - //{`{ NF=1; $2="x"; print $0; print NF }`, "a b c", "a x\n2\n", ""}, - //{`{ NF=1; $3="x"; print $0; print NF }`, "a", "a x\n3\n", ""}, - //{`{ NF=1; $3="x"; print $0; print NF } # !awk - awk differs from gawk (but gawk seems right)`, - // "a b", "a x\n3\n", ""}, - //{`{ NF=1; $3="x"; print $0; print NF } # !awk - awk differs from gawk (but gawk seems right)`, - // "a b c", "a x\n3\n", ""}, - //{`{ NF=2; $1="x"; print $0; print NF }`, "a", "x \n2\n", ""}, - //{`{ NF=2; $1="x"; print $0; print NF }`, "a b", "x b\n2\n", ""}, - //{`{ NF=2; $1="x"; print $0; print NF }`, "a b c", "x b\n2\n", ""}, - //{`{ NF=2; $2="x"; print $0; print NF }`, "a", "a x\n2\n", ""}, - //{`{ NF=2; $2="x"; print $0; print NF }`, "a b", "a x\n2\n", ""}, - //{`{ NF=2; $2="x"; print $0; print NF }`, "a b c", "a x\n2\n", ""}, - //{`{ NF=2; $3="x"; print $0; print NF }`, "a", "a x\n3\n", ""}, - //{`{ NF=2; $3="x"; print $0; print NF }`, "a b", "a b x\n3\n", ""}, - //{`{ NF=2; $3="x"; print $0; print NF }`, "a b c", "a b x\n3\n", ""}, - //{`{ NF=3; $1="x"; print $0; print NF } # !awk - segmentation fault`, - // "a", "x \n3\n", ""}, - //{`{ NF=3; $1="x"; print $0; print NF } # !awk - segmentation fault`, - // "a b", "x b \n3\n", ""}, - //{`{ NF=3; $1="x"; print $0; print NF }`, "a b c", "x b c\n3\n", ""}, - //{`{ NF=3; $2="x"; print $0; print NF } # !awk - segmentation fault`, - // "a", "a x \n3\n", ""}, - //{`{ NF=3; $2="x"; print $0; print NF } # !awk - segmentation fault`, - // "a b", "a x \n3\n", ""}, - //{`{ NF=3; $2="x"; print $0; print NF }`, "a b c", "a x c\n3\n", ""}, - //{`{ NF=3; $3="x"; print $0; print NF }`, "a", "a x\n3\n", ""}, - //{`{ NF=3; $3="x"; print $0; print NF }`, "a b", "a b x\n3\n", ""}, - //{`{ NF=3; $3="x"; print $0; print NF }`, "a b c", "a b x\n3\n", ""}, - - // Assignment expressions and vars - {`BEGIN { print x; x = 4; print x; }`, "", "0\n4\n", ""}, - {`BEGIN { a["foo"]=1; b[2]="x"; k="foo"; print a[k], b["2"] }`, "", "1 x\n", ""}, - {`BEGIN { s+=5; print s; s-=2; print s; s-=s; print s }`, "", "5\n3\n0\n", ""}, - {`BEGIN { x=2; x*=x; print x; x*=3; print x }`, "", "4\n12\n", ""}, - {`BEGIN { x=6; x/=3; print x; x/=x; print x; x/=.6; print x }`, "", "2\n1\n1.66667\n", ""}, - {`BEGIN { x=12; x%=5; print x }`, "", "2\n", ""}, - {`BEGIN { x=2; x^=5; print x; x^=0.5; print x }`, "", "32\n5.65685\n", ""}, - {`{ $2+=10; print; $3/=2; print }`, "1 2 3", "1 12 3\n1 12 1.5\n", ""}, - {`BEGIN { a[2] += 1; a["2"] *= 3; print a[2] }`, "", "3\n", ""}, - - // Incr/decr expressions - {`BEGIN { print x++; print x }`, "", "0\n1\n", ""}, - {`BEGIN { print x; print x++; print ++x; print x }`, "", "0\n0\n2\n2\n", ""}, - {`BEGIN { print x; print x--; print --x; print x }`, "", "0\n0\n-2\n-2\n", ""}, - {`BEGIN { s++; s++; print s }`, "", "2\n", ""}, - {`BEGIN { y=" "; --x[y = y y]; print length(y) }`, "", "2\n", ""}, - {`BEGIN { x[y++]++; print y }`, "", "1\n", ""}, - {`BEGIN { x[y++] += 3; print y }`, "", "1\n", ""}, - {`BEGIN { $(y++)++; print y }`, "", "1\n", ""}, - - // Builtin functions - {`BEGIN { print sin(0), sin(0.5), sin(1), sin(-1) }`, "", "0 0.479426 0.841471 -0.841471\n", ""}, - {`BEGIN { print cos(0), cos(0.5), cos(1), cos(-1) }`, "", "1 0.877583 0.540302 0.540302\n", ""}, - {`BEGIN { print exp(0), exp(0.5), exp(1), exp(-1) }`, "", "1 1.64872 2.71828 0.367879\n", ""}, - {`BEGIN { print log(0), log(0.5), log(1) }`, "", "-inf -0.693147 0\n", ""}, - {`BEGIN { print log(-1) } # !gawk - gawk prints warning for this as well`, - "", "nan\n", ""}, - {`BEGIN { print sqrt(0), sqrt(2), sqrt(4) }`, "", "0 1.41421 2\n", ""}, - {`BEGIN { print int(3.5), int("1.9"), int(4), int(-3.6), int("x"), int("") }`, "", "3 1 4 -3 0 0\n", ""}, - {`BEGIN { print match("food", "foo"), RSTART, RLENGTH }`, "", "1 1 3\n", ""}, - {`BEGIN { print match("x food y", "fo"), RSTART, RLENGTH }`, "", "3 3 2\n", ""}, - {`BEGIN { print match("x food y", "fox"), RSTART, RLENGTH }`, "", "0 0 -1\n", ""}, - {`BEGIN { print match("x food y", /[fod]+/), RSTART, RLENGTH }`, "", "3 3 4\n", ""}, - {`{ print length, length(), length("buzz"), length("") }`, "foo bar", "7 7 4 0\n", ""}, - {`BEGIN { print index("foo", "f"), index("foo0", 0), index("foo", "o"), index("foo", "x") }`, "", "1 4 2 0\n", ""}, - {`BEGIN { print atan2(1, 0.5), atan2(-1, 0) }`, "", "1.10715 -1.5708\n", ""}, - {`BEGIN { print sprintf("%3d", 42) }`, "", " 42\n", ""}, - {`BEGIN { print sprintf("%d", 12, 34) }`, "", "12\n", ""}, - {`BEGIN { print sprintf("%d") }`, "", "", `not enough arguments (0) for format string "%d"`}, - {`BEGIN { print sprintf("%d", 12, 34) }`, "", "12\n", ""}, - {`BEGIN { print sprintf("% 5d", 42) }`, "", " 42\n", ""}, - {`BEGIN { print substr("food", 1) }`, "", "food\n", ""}, - {`BEGIN { print substr("food", 1, 2) }`, "", "fo\n", ""}, - {`BEGIN { print substr("food", 1, 4) }`, "", "food\n", ""}, - {`BEGIN { print substr("food", 1, 8) }`, "", "food\n", ""}, - {`BEGIN { print substr("food", 2) }`, "", "ood\n", ""}, - {`BEGIN { print substr("food", 2, 2) }`, "", "oo\n", ""}, - {`BEGIN { print substr("food", 2, 3) }`, "", "ood\n", ""}, - {`BEGIN { print substr("food", 2, 8) }`, "", "ood\n", ""}, - {`BEGIN { print substr("food", 0, 8) }`, "", "food\n", ""}, - {`BEGIN { print substr("food", -1, 8) }`, "", "food\n", ""}, - {`BEGIN { print substr("food", 5, 8) }`, "", "\n", ""}, - {`BEGIN { n = split("ab c d ", a); for (i=1; i<=n; i++) print a[i] }`, "", "ab\nc\nd\n", ""}, - {`BEGIN { n = split("ab,c,d,", a, ","); for (i=1; i<=n; i++) print a[i] }`, "", "ab\nc\nd\n\n", ""}, - {`BEGIN { n = split("ab,c.d,", a, /[,.]/); for (i=1; i<=n; i++) print a[i] }`, "", "ab\nc\nd\n\n", ""}, - {`BEGIN { n = split("1 2", a); print (n, a[1], a[2], a[1]==1, a[2]==2) }`, "", "2 1 2 1 1\n", ""}, - {`BEGIN { x = "1.2.3"; print sub(/\./, ",", x); print x }`, "", "1\n1,2.3\n", ""}, - {`{ print sub(/\./, ","); print $0 }`, "1.2.3", "1\n1,2.3\n", ""}, - {`BEGIN { x = "1.2.3"; print gsub(/\./, ",", x); print x }`, "", "2\n1,2,3\n", ""}, - {`{ print gsub(/\./, ","); print $0 }`, "1.2.3", "2\n1,2,3\n", ""}, - {`{ print gsub(/[0-9]/, "(&)"); print $0 }`, "0123x. 42y", "6\n(0)(1)(2)(3)x. (4)(2)y\n", ""}, - {`{ print gsub(/[0-9]+/, "(&)"); print $0 }`, "0123x. 42y", "2\n(0123)x. (42)y\n", ""}, - {`{ print gsub(/[0-9]/, "\\&"); print $0 }`, "0123x. 42y", "6\n&&&&x. &&y\n", ""}, - {`{ print gsub(/[0-9]/, "\\z"); print $0 }`, "0123x. 42y", "6\n\\z\\z\\z\\zx. \\z\\zy\n", ""}, - {`{ print gsub("0", "x\\\\y"); print $0 } # !awk !gawk -- our behaviour is per POSIX spec (gawk -P and mawk)`, - "0", "1\nx\\y\n", ""}, - //{`sub("", "\\e", FS) # !awk !gawk`, "foo bar\nbaz buz\n", "", - // "invalid regex \"\\\\e \": error parsing regexp: invalid escape sequence: `\\e`"}, - {`BEGIN { print tolower("Foo BaR") }`, "", "foo bar\n", ""}, - {`BEGIN { print toupper("Foo BaR") }`, "", "FOO BAR\n", ""}, - {` -BEGIN { - srand(1) - a = rand(); b = rand(); c = rand() - srand(1) - x = rand(); y = rand(); z = rand() - print (a==b, b==c, x==y, y==z) - print (a==x, b==y, c==z) -} -`, "", "0 0 0 0\n1 1 1\n", ""}, - {` -BEGIN { - for (i = 0; i < 1000; i++) { - if (rand() < 0.5) n++ - } - print (n>400) -} -`, "", "1\n", ""}, - {`BEGIN { print system("echo foo"); print system("echo bar") } # !fuzz`, - "", "foo\n0\nbar\n0\n", ""}, - {`BEGIN { print system(">&2 echo error") } # !fuzz`, - "", "error\n0\n", ""}, - - // Conditional expressions parse and work correctly - {`BEGIN { print 0?"t":"f" }`, "", "f\n", ""}, - {`BEGIN { print 1?"t":"f" }`, "", "t\n", ""}, - {`BEGIN { print (1+2)?"t":"f" }`, "", "t\n", ""}, - {`BEGIN { print (1+2?"t":"f") }`, "", "t\n", ""}, - {`BEGIN { print(1 ? x="t" : "f"); print x; }`, "", "t\nt\n", ""}, - - // Locals vs globals, array params, and recursion - // {` - //function f(loc) { - // glob += 1 - // loc += 1 - // print glob, loc - //} - //BEGIN { - // glob = 1 - // loc = 42 - // f(3) - // print loc - // f(4) - // print loc - //} - //`, "", "2 4\n42\n3 5\n42\n", ""}, - // {` - //function set(a, x, v) { - // a[x] = v - //} - //function get(a, x) { - // return a[x] - //} - //BEGIN { - // a["x"] = 1 - // set(b, "y", 2) - // for (k in a) print k, a[k] - // print "---" - // for (k in b) print k, b[k] - // print "---" - // print get(a, "x"), get(b, "y") - //} - //`, "", "x 1\n---\ny 2\n---\n1 2\n", ""}, - // {` - //function fib(n) { - // return n < 3 ? 1 : fib(n-2) + fib(n-1) - //} - //BEGIN { - // for (i = 1; i <= 7; i++) { - // printf "%d ", fib(i) - // } - //} - //`, "", "1 1 2 3 5 8 13 ", ""}, - // {` - //function f(a, x) { return a[x] } - //function g(b, y) { f(b, y) } - //BEGIN { c[1]=2; print f(c, 1); print g(c, 1) } - //`, "", "2\n\n", ""}, - // {` - //function g(b, y) { return f(b, y) } - //function f(a, x) { return a[x] } - //BEGIN { c[1]=2; print f(c, 1); print g(c, 1) } - //`, "", "2\n2\n", ""}, - // {` - //function h(b, y) { g(b, y) } - //function g(b, y) { f(b, y) } - //function f(a, x) { return a[x] } - //BEGIN { c[1]=2; print f(c, 1); print g(c, 1) } - //`, "", "2\n\n", ""}, - // {` - //function h(b, y) { return g(b, y) } - //function g(b, y) { return f(b, y) } - //function f(a, x) { return a[x] } - //BEGIN { c[1]=2; print f(c, 1); print g(c, 1); print h(c, 1) } - //`, "", "2\n2\n2\n", ""}, - // {` - //function get(a, x) { return a[x] } - //BEGIN { a[1]=2; print get(a, x); print get(1, 2); } - //# !awk - awk doesn't detect this - //`, "", "", `parse error at 3:40: can't pass scalar 1 as array param`}, - // {` - //function early() { - // print "x" - // return - // print "y" - //} - //BEGIN { early() } - //`, "", "x\n", ""}, - // {`BEGIN { return }`, "", "", "parse error at 1:9: return must be inside a function"}, - // {`function f() { printf "x" }; BEGIN { f() } `, "", "x", ""}, - // {`function f(x) { 0 in _; f(_) } BEGIN { f() } # !awk !gawk`, "", "", - // `parse error at 1:25: can't pass array "_" as scalar param`}, - // {`BEGIN { for (i=0; i<1001; i++) f(); print x } function f() { x++ }`, "", "1001\n", ""}, - // {` - //function bar(y) { return y[1] } - //function foo() { return bar(x) } - //BEGIN { x[1] = 42; print foo() } - //`, "", "42\n", ""}, - // failing because f1 doesn't use x, so resolver assumes its type is scalar - // {` - // function f1(x) { } - // function f2(x, y) { return x[y] } - // BEGIN { a[1]=2; f1(a); print f2(a, 1) } - // `, "", "2\n", ""}, - - // Type checking / resolver tests - {`BEGIN { a[x]; a=42 }`, "", "", `parse error at 1:15: can't use array "a" as scalar`}, - {`BEGIN { s=42; s[x] }`, "", "", `parse error at 1:15: can't use scalar "s" as array`}, - // {`function get(a, k) { return a[k] } BEGIN { a = 42; print get(a, 1); } # !awk - doesn't error in awk`, - // "", "", `parse error at 1:59: can't pass scalar "a" as array param`}, - // {`function get(a, k) { return a+k } BEGIN { a[42]; print get(a, 1); }`, - // "", "", `parse error at 1:56: can't pass array "a" as scalar param`}, - // {`{ f(z) } function f(x) { print NR }`, "abc", "1\n", ""}, - // {`function f() { f() } BEGIN { f() } # !awk !gawk`, "", "", `calling "f" exceeded maximum call depth of 1000`}, - // {`function f(x) { 0 in x } BEGIN { f(FS) } # !awk`, "", "", `parse error at 1:35: can't pass scalar "FS" as array param`}, - // {` - //function foo(x) { print "foo", x } - //function bar(foo) { print "bar", foo } - //BEGIN { foo(5); bar(10) } - //`, "", "foo 5\nbar 10\n", ""}, - // {` - //function foo(foo) { print "foo", foo } - //function bar(foo) { print "bar", foo } - //BEGIN { foo(5); bar(10) } - //`, "", "", `parse error at 2:14: can't use function name as parameter name`}, - // {`function foo() { print foo } BEGIN { foo() }`, - // "", "", `parse error at 1:46: global var "foo" can't also be a function`}, - // {`function f(x) { print x, x(); } BEGIN { f() }`, "", "", `parse error at 1:27: can't call local variable "x" as function`}, - - // Redirected I/O (we give explicit errors, awk and gawk don't) - // the following two tests sometimes fail under TravisCI with: "write |1: broken pipe" - // {`BEGIN { print >"out"; getline <"out" } # !awk !gawk`, "", "", "can't read from writer stream"}, - // {`BEGIN { print |"out"; getline <"out" } # !awk !gawk`, "", "", "can't read from writer stream"}, - //{`BEGIN { print >"out"; close("out"); getline <"out"; print >"out" } # !awk !gawk`, "", "", "can't write to reader stream"}, - //{`BEGIN { print >"out"; close("out"); getline <"out"; print |"out" } # !awk !gawk`, "", "", "can't write to reader stream"}, - // currently we support "getline var" but not "getline lvalue" - // {`BEGIN { getline a[1]; print a[1] }`, "foo", "foo\n", ""}, - // {`BEGIN { getline $1; print $1 }`, "foo", "foo\n", ""}, - //{`BEGIN { print "foo" |"sort"; print "bar" |"sort" } # !fuzz`, "", "bar\nfoo\n", ""}, - //{`BEGIN { print "foo" |">&2 echo error" } # !fuzz`, "", "error\n", ""}, - //{`BEGIN { "cat" | getline; print } # !fuzz`, "bar", "bar\n", ""}, - // fix test flakiness on Windows (sometimes returns "\nerror\n") - // {`BEGIN { ">&2 echo error" | getline; print }`, "", "error\n\n", ""}, - //{`BEGIN { print getline x < "/no/such/file" } # !fuzz`, "", "-1\n", ""}, - - // Redirecting to or from a filename of "-" means write to stdout or read from stdin - //{`BEGIN { print getline x < "-"; print x }`, "a\nb\n", "1\na\n", ""}, - //{`{ print $0; print getline x <"-"; print x }`, "one\ntwo\n", "one\n0\n\ntwo\n0\n\n", ""}, - //{`BEGIN { print "x" >"-"; print "y" >"-" }`, "", "x\ny\n", ""}, - - // fflush() function - tests parsing and some edge cases, but not - // actual flushing behavior (that's partially tested in TestFlushes). - {`BEGIN { print fflush(); print fflush("") }`, "", "0\n0\n", ""}, - {`BEGIN { print "x"; print fflush(); print "y"; print fflush("") }`, "", "x\n0\ny\n0\n", ""}, - //{`BEGIN { print "x" >"out"; print fflush("out"); print "y"; print fflush("") } # !fuzz`, "", "0\ny\n0\n", ""}, - //{`BEGIN { print fflush("x") } # !gawk`, "", "error flushing \"x\": not an output file or pipe\n-1\n", ""}, - //{`BEGIN { "cat" | getline; print fflush("cat") } # !gawk !fuzz`, "", "error flushing \"cat\": not an output file or pipe\n-1\n", ""}, - - // Greater than operator requires parentheses in print statement, - // otherwise it's a redirection directive - //{`BEGIN { print "x" > "out" } # !fuzz`, "", "", ""}, - //{`BEGIN { printf "x" > "out" } # !fuzz`, "", "", ""}, - //{`BEGIN { print("x" > "out") }`, "", "1\n", ""}, - //{`BEGIN { printf("x" > "out") }`, "", "1", ""}, - - // Grammar should allow blocks wherever statements are allowed - {`BEGIN { if (1) printf "x"; else printf "y" }`, "", "x", ""}, - {`BEGIN { printf "x"; { printf "y"; printf "z" } }`, "", "xyz", ""}, - - // Ensure syntax errors result in errors - // {`{ $1 = substr($1, 1, 3) print $1 }`, "", "", "ERROR"}, - {`BEGIN { f() }`, "", "", `parse error at 1:9: undefined function "f"`}, - {`function f() {} function f() {} BEGIN { }`, "", "", `parse error at 1:26: function "f" already defined`}, - {`BEGIN { print (1,2),(3,4) }`, "", "", "parse error at 1:15: unexpected comma-separated expression"}, - {`BEGIN { print (1,2,(3,4),(5,6)) }`, "", "", "parse error at 1:20: unexpected comma-separated expression"}, -} - -func TestAWKGo(t *testing.T) { - tempDir := t.TempDir() - - for i, test := range interpTests { - testName := test.src - if len(testName) > 70 { - testName = testName[:70] - } - t.Run(testName, func(t *testing.T) { - output, err := os.Create(filepath.Join(tempDir, fmt.Sprintf("test_%d.go", i))) - if err != nil { - t.Fatalf("error creating temp file: %v", err) - } - defer os.Remove(output.Name()) - - prog, err := parser.ParseProgram([]byte(test.src), nil) - if err != nil { - if test.err != "" { - if err.Error() == test.err { - return - } - t.Fatalf("expected error %q, got %q", test.err, err.Error()) - } - t.Fatalf("parse error: %v", err) - } - - err = compile(prog, output) - if err != nil { - if test.err != "" { - if err.Error() == test.err { - return - } - t.Fatalf("expected error %q, got %q", test.err, err.Error()) - } - t.Fatalf("compile error: %v", err) - } - - err = output.Close() - if err != nil { - t.Fatalf("error closing temp file: %v", err) - } - - cmd := exec.Command("go", "run", output.Name()) - cmd.Stdin = strings.NewReader(test.in) - out, err := cmd.CombinedOutput() - if err != nil { - if test.err != "" { - if err.Error() == test.err { - return - } - t.Fatalf("expected error %q, got %q", test.err, err.Error()) - } - t.Fatalf("go run error: %v:\n%s", err, out) - } - if string(out) != test.out { - t.Fatalf("expected %q, got %q", test.out, out) - } - }) - } -} diff --git a/awkgo/compiler.go b/awkgo/compiler.go deleted file mode 100644 index 689f8b2a..00000000 --- a/awkgo/compiler.go +++ /dev/null @@ -1,999 +0,0 @@ -// AWKGo: the main compiler - -package main - -import ( - "bytes" - "errors" - "fmt" - "io" - "math" - "sort" - "strconv" - - "github.com/benhoyt/goawk/internal/ast" - . "github.com/benhoyt/goawk/lexer" - "github.com/benhoyt/goawk/parser" -) - -// compile compiles the parsed AWK program to Go and outputs to writer. -func compile(prog *parser.Program, writer io.Writer) (err error) { - // Both typer and compiler signal errors internally with - // panic(&errorExit{...}), so recover and return an error. - defer func() { - r := recover() - switch r := r.(type) { - case nil: - case *exitError: - err = errors.New(r.message) - default: - panic(r) // another type, re-panic - } - }() - - // Determine the types of variables and expressions. - t := newTyper() - t.program(prog) - - // Do a second typing pass over the program to ensure we detect the types - // of variables assigned after they're used, for example: - // BEGIN { while (i<5) { i++; print i } } - t.program(prog) - - c := &compiler{ - typer: t, - writer: writer, - regexen: make(map[string]int), - } - c.program(prog) - - return nil -} - -type exitError struct { - message string -} - -func (e *exitError) Error() string { - return e.message -} - -func errorf(format string, args ...interface{}) error { - return &exitError{fmt.Sprintf(format, args...)} -} - -type compiler struct { - typer *typer - writer io.Writer - regexen map[string]int -} - -func (c *compiler) output(s string) { - fmt.Fprint(c.writer, s) -} - -func (c *compiler) outputf(format string, args ...interface{}) { - fmt.Fprintf(c.writer, format, args...) -} - -func (c *compiler) program(prog *parser.Program) { - c.output(`package main - -import ( - "bufio" - "fmt" - "math" - "math/rand" - "os" - "os/exec" - "regexp" - "strconv" - "strings" - "time" - "unicode/utf8" -) - -var ( - _output *bufio.Writer - _scanner *bufio.Scanner - _line string - _fields []string - _lineNum int - _seed float64 - _rand *rand.Rand - -`) - - globals := make([]string, 0, len(c.typer.globals)) - for name := range c.typer.globals { - globals = append(globals, name) - } - sort.Strings(globals) - for _, name := range globals { - typ := c.typer.globals[name] - c.outputf("%s %s\n", name, c.goType(typ)) - } - - c.output(`) - -func main() { - _output = bufio.NewWriter(os.Stdout) - defer _output.Flush() - - _scanner = bufio.NewScanner(os.Stdin) - _seed = 1.0 - _rand = rand.New(rand.NewSource(int64(math.Float64bits(_seed)))) - - FS = " " - OFS = " " - ORS = "\n" - OFMT = "%.6g" - CONVFMT = "%.6g" - SUBSEP = "\x1c" - -`) - - for i, action := range prog.Actions { - if len(action.Pattern) == 2 { - // Booleans to keep track of range pattern state - c.outputf("_inRange%d := false\n", i) - } - } - - for _, name := range globals { - typ := c.typer.globals[name] - switch typ { - case typeArrayStr, typeArrayNum: - c.outputf("%s = make(%s)\n", name, c.goType(typ)) - } - } - - for _, stmts := range prog.Begin { - c.output("\n") - c.stmts(stmts) - } - - if c.typer.nextUsed { - c.output("_nextLine:\n") - } - - if len(prog.Actions) > 0 { - c.output(` - for _scanner.Scan() { - _lineNum++ - _line = _scanner.Text() - _fields = _splitHelper(_line, FS) -`) - c.actions(prog.Actions) - c.output(` } - - if _scanner.Err() != nil { - fmt.Fprintln(os.Stderr, _scanner.Err()) - os.Exit(1) - } -`) - } - - for _, stmts := range prog.End { - c.output("\n") - c.stmts(stmts) - } - - c.output("}\n") - - type regex struct { - pattern string - n int - } - var regexen []regex - for pattern, n := range c.regexen { - regexen = append(regexen, regex{pattern, n}) - } - sort.Slice(regexen, func(i, j int) bool { - return regexen[i].n < regexen[j].n - }) - if len(regexen) > 0 { - c.output("\nvar (\n") - for _, r := range regexen { - c.outputf("_re%d = regexp.MustCompile(%q)\n", r.n, r.pattern) - } - c.output(")\n") - } - - c.outputHelpers() -} - -func (c *compiler) actions(actions []ast.Action) { - for i, action := range actions { - c.output("\n") - switch len(action.Pattern) { - case 0: - // No pattern is equivalent to pattern evaluating to true - case 1: - // Single boolean pattern - c.outputf("if %s {\n", c.cond(action.Pattern[0])) - case 2: - // Range pattern (matches between start and stop lines) - c.outputf("if !_inRange%d { _inRange%d = %s }\n", i, i, c.cond(action.Pattern[0])) - c.outputf("if _inRange%d {\n", i) - } - - if len(action.Stmts) == 0 { - // No action is equivalent to { print $0 } - c.output("fmt.Fprint(_output, _line, ORS)\n") - } else { - c.stmts(action.Stmts) - } - - switch len(action.Pattern) { - case 1: - c.output("}\n") - case 2: - c.outputf("\n_inRange%d = !(%s)\n", i, c.cond(action.Pattern[1])) - c.output("}\n") - } - } -} - -func (c *compiler) stmts(stmts ast.Stmts) { - for _, stmt := range stmts { - c.stmt(stmt) - } -} - -func (c *compiler) assign(left, right ast.Expr) string { - switch left := left.(type) { - case *ast.VarExpr: - if left.Scope == ast.ScopeSpecial { - switch left.Index { - case ast.V_NF, ast.V_NR, ast.V_FNR: - panic(errorf("can't assign to special variable %s", left.Name)) - } - } - return fmt.Sprintf("%s = %s", left.Name, c.expr(right)) - case *ast.IndexExpr: - return fmt.Sprintf("%s[%s] = %s", left.Array.Name, c.index(left.Index), c.expr(right)) - case *ast.FieldExpr: - return fmt.Sprintf("_setField(%s, %s)", c.intExpr(left.Index), c.strExpr(right)) - default: - panic(errorf("expected lvalue, not %s", left)) - } -} - -func (c *compiler) stmtNoNewline(stmt ast.Stmt) { - switch s := stmt.(type) { - case *ast.ExprStmt: - switch e := s.Expr.(type) { - case *ast.AssignExpr: - c.output(c.assign(e.Left, e.Right)) - - case *ast.AugAssignExpr: - switch left := e.Left.(type) { - case *ast.VarExpr: - switch e.Op { - case MOD, POW: - c.output(left.Name) - if e.Op == MOD { - c.output(" = math.Mod(") - } else { - c.output(" = math.Pow(") - } - c.outputf("%s, %s)", left.Name, c.numExpr(e.Right)) - default: - c.outputf("%s %s= %s", left.Name, e.Op, c.numExpr(e.Right)) - } - - case *ast.IndexExpr: - switch e.Op { - case MOD, POW: - c.outputf("%s[%s", left.Array.Name, c.index(left.Index)) - if e.Op == MOD { - c.output("] = math.Mod(") - } else { - c.output("] = math.Pow(") - } - c.outputf("%s[%s], %s)", left.Array.Name, c.index(left.Index), c.numExpr(e.Right)) - default: - c.outputf("%s[%s] %s= %s", left.Array.Name, c.index(left.Index), e.Op, c.numExpr(e.Right)) - } - - case *ast.FieldExpr: - // We have to be careful not to evaluate left.Index twice, in - // case it has side effects, as in: $(y++) += 10 - switch e.Op { - case MOD, POW: - c.outputf("func() { _t := %s; _setField(_t, _numToStr(", c.intExpr(left.Index)) - if e.Op == MOD { - c.output("math.Mod(") - } else { - c.output("math.Pow(") - } - c.outputf("_strToNum(_getField(_t)), %s))) }()", c.numExpr(e.Right)) - default: - c.outputf("func() { _t := %s; _setField(_t, _numToStr(_strToNum(_getField(_t)) %s %s)) }()", - c.intExpr(left.Index), e.Op, c.numExpr(e.Right)) - } - } - - case *ast.IncrExpr: - switch left := e.Expr.(type) { - case *ast.VarExpr: - c.outputf("%s%s", left.Name, e.Op) - case *ast.IndexExpr: - c.outputf("%s[%s]%s", left.Array.Name, c.index(left.Index), e.Op) - case *ast.FieldExpr: - // We have to be careful not to evaluate left.Index twice, in - // case it has side effects, as in: $(y++)++ - op := "+" - if e.Op == DECR { - op = "-" - } - c.outputf("func() { _t := %s; _setField(_t, _numToStr(_strToNum(_getField(_t)) %s 1)) }()", - c.intExpr(left.Index), op) - } - - default: - c.outputf("_ = %s", c.expr(s.Expr)) - } - - case *ast.PrintStmt: - if s.Dest != nil { - panic(errorf("print redirection not yet supported")) - } - if c.typer.oFSRSChanged { - c.output("fmt.Fprint(_output, ") - } else { - c.output("fmt.Fprintln(_output, ") - } - if len(s.Args) > 0 { - for i, arg := range s.Args { - if i > 0 { - c.output(", ") - if c.typer.oFSRSChanged { - c.output("OFS, ") - } - } - str := c.expr(arg) - if c.typer.exprs[arg] == typeNum { - str = fmt.Sprintf("_formatNum(%s)", str) - } - c.output(str) - } - } else { - // "print" with no args is equivalent to "print $0" - c.output("_line") - } - if c.typer.oFSRSChanged { - c.output(", ORS") - } - c.output(")") - - case *ast.PrintfStmt: - if s.Dest != nil { - panic(errorf("printf redirection not yet supported")) - } - formatExpr, ok := s.Args[0].(*ast.StrExpr) - if !ok { - panic(errorf("printf currently only supports literal format strings")) - } - args := c.printfArgs(formatExpr.Value, s.Args[1:]) - c.outputf("fmt.Fprintf(_output, %q", formatExpr.Value) - for _, arg := range args { - c.outputf(", %s", arg) - } - c.output(")") - - case *ast.IfStmt: - c.output("if ") - switch cond := s.Cond.(type) { - case *ast.InExpr: - // if _, _ok := a[k]; ok { ... } - c.outputf("_, _ok := %s[%s]; _ok ", cond.Array.Name, c.index(cond.Index)) - default: - c.output(c.cond(s.Cond)) - } - c.output(" {\n") - c.stmts(s.Body) - c.output("}") - if len(s.Else) > 0 { - if _, isIf := s.Else[0].(*ast.IfStmt); isIf && len(s.Else) == 1 { - // Simplify runs if-else if - c.output(" else ") - c.stmt(s.Else[0]) - } else { - c.output(" else {\n") - c.stmts(s.Else) - c.output("}") - } - } - - case *ast.ForStmt: - c.output("for ") - if s.Pre != nil { - _, ok := s.Pre.(*ast.ExprStmt) - if !ok { - panic(errorf(`only expressions are allowed in "for" initializer`)) - } - c.stmtNoNewline(s.Pre) - } - c.output("; ") - if s.Cond != nil { - c.output(c.cond(s.Cond)) - } - c.output("; ") - if s.Post != nil { - _, ok := s.Post.(*ast.ExprStmt) - if !ok { - panic(errorf(`only expressions are allowed in "for" post expression`)) - } - c.stmtNoNewline(s.Post) - } - c.output(" {\n") - c.stmts(s.Body) - c.output("}") - - case *ast.ForInStmt: - c.outputf("for %s = range %s {\n", s.Var.Name, s.Array.Name) - c.stmts(s.Body) - c.output("}") - - case *ast.WhileStmt: - c.outputf("for %s {\n", c.cond(s.Cond)) - c.stmts(s.Body) - c.output("}") - - case *ast.DoWhileStmt: - c.output("for {\n") - c.stmts(s.Body) - c.outputf("if !(%s) {\nbreak\n}\n}", c.cond(s.Cond)) - - case *ast.BreakStmt: - c.output("break") - - case *ast.ContinueStmt: - c.output("continue") - - case *ast.NextStmt: - c.output("goto _nextLine") - - case *ast.ExitStmt: - if s.Status != nil { - c.outputf("os.Exit(%s)", c.intExpr(s.Status)) - } else { - c.output("os.Exit(0)") - } - - case *ast.DeleteStmt: - if len(s.Index) > 0 { - // Delete single key from array - c.outputf("delete(%s, %s)", s.Array.Name, c.index(s.Index)) - } else { - // Delete every element in array - c.outputf("for _k := range %s {\ndelete(%s, _k)\n}", s.Array.Name, s.Array.Name) - } - - case *ast.BlockStmt: - c.output("{\n") - c.stmts(s.Body) - c.output("}") - - default: - panic(errorf("%T not yet supported", s)) - } -} - -func (c *compiler) stmt(stmt ast.Stmt) { - c.stmtNoNewline(stmt) - c.output("\n") -} - -type valueType int - -const ( - typeUnknown valueType = iota - typeStr - typeNum - typeArrayStr - typeArrayNum -) - -func (t valueType) String() string { - switch t { - case typeStr: - return "str" - case typeNum: - return "num" - case typeArrayStr: - return "array of str" - case typeArrayNum: - return "array of num" - default: - return "unknown" - } -} - -func (c *compiler) expr(expr ast.Expr) string { - switch e := expr.(type) { - case *ast.NumExpr: - if e.Value == float64(int(e.Value)) { - return fmt.Sprintf("%d.0", int(e.Value)) - } - if math.IsInf(e.Value, 0) { - panic(errorf("number literal out of range")) - } - return fmt.Sprintf("%g", e.Value) - - case *ast.StrExpr: - return strconv.Quote(e.Value) - - case *ast.FieldExpr: - return "_getField(" + c.intExpr(e.Index) + ")" - - case *ast.VarExpr: - switch e.Scope { - case ast.ScopeSpecial: - return c.special(e.Name, e.Index) - case ast.ScopeGlobal: - return e.Name - default: - panic(errorf("unexpected scope %v", e.Scope)) - } - - case *ast.RegExpr: - return fmt.Sprintf("_boolToNum(%s.MatchString(_line))", c.regexLiteral(e.Regex)) - - case *ast.BinaryExpr: - return c.binaryExpr(e.Op, e.Left, e.Right) - - case *ast.IncrExpr: - exprStr := c.expr(e.Expr) // will be an lvalue (VarExpr, IndexExpr, FieldExpr) - if e.Pre { - // Change ++x expression to: - // func() float64 { x++; return x }() - return fmt.Sprintf("func() float64 { %s%s; return %s }()", - exprStr, e.Op, exprStr) - } else { - // Change x++ expression to: - // func() float64 { _t := x; x++; return _t }() - return fmt.Sprintf("func() float64 { _t := %s; %s%s; return _t }()", - exprStr, exprStr, e.Op) - } - - case *ast.AssignExpr: - right := c.expr(e.Right) - switch l := e.Left.(type) { - case *ast.VarExpr: - return fmt.Sprintf("func () %s { %s = %s; return %s }()", - c.goType(c.typer.exprs[e.Right]), l.Name, right, l.Name) - default: - panic(errorf("lvalue type %T not yet supported", l)) - } - - case *ast.CondExpr: - return fmt.Sprintf("func() %s { if %s { return %s }; return %s }()", - c.goType(c.typer.exprs[e]), c.cond(e.Cond), c.expr(e.True), c.expr(e.False)) - - case *ast.IndexExpr: - switch e.Array.Scope { - case ast.ScopeSpecial: - panic(errorf("special variable %s not yet supported", e.Array.Name)) - case ast.ScopeGlobal: - return e.Array.Name + "[" + c.index(e.Index) + "]" - default: - panic(errorf("unexpected scope %v", e.Array.Scope)) - } - - case *ast.CallExpr: - switch e.Func { - case F_ATAN2: - return "math.Atan2(" + c.numExpr(e.Args[0]) + ", " + c.numExpr(e.Args[1]) + ")" - - case F_COS: - return "math.Cos(" + c.numExpr(e.Args[0]) + ")" - - case F_EXP: - return "math.Exp(" + c.numExpr(e.Args[0]) + ")" - - case F_FFLUSH: - var arg string - if len(e.Args) > 0 { - switch argExpr := e.Args[0].(type) { - case *ast.StrExpr: - arg = argExpr.Value - default: - arg = "not supported" - } - } - if arg != "" { - panic(errorf(`fflush() currently only supports no args or "" arg`)) - } - return "_fflush()" - - case F_INDEX: - return "float64(strings.Index(" + c.strExpr(e.Args[0]) + ", " + c.strExpr(e.Args[1]) + ") + 1)" - - case F_INT: - return "float64(" + c.intExpr(e.Args[0]) + ")" - - case F_LENGTH: - switch len(e.Args) { - case 0: - return "float64(len(_line))" - default: - return "float64(len(" + c.strExpr(e.Args[0]) + "))" - } - - case F_LOG: - return "math.Log(" + c.numExpr(e.Args[0]) + ")" - - case F_MATCH: - if strExpr, ok := e.Args[1].(*ast.StrExpr); ok { - return fmt.Sprintf("_match(%s, %s)", c.strExpr(e.Args[0]), c.regexLiteral(strExpr.Value)) - } - return fmt.Sprintf("_match(%s, _reCompile(%s))", c.strExpr(e.Args[0]), c.strExpr(e.Args[1])) - - case F_RAND: - return "_rand.Float64()" - - case F_SIN: - return "math.Sin(" + c.numExpr(e.Args[0]) + ")" - - case F_SPLIT: - arrayArg := e.Args[1].(*ast.ArrayExpr) - str := fmt.Sprintf("_split(%s, %s, ", c.strExpr(e.Args[0]), arrayArg.Name) - if len(e.Args) == 3 { - str += c.strExpr(e.Args[2]) - } else { - str += "FS" - } - str += ")" - return str - - case F_SPRINTF: - formatExpr, ok := e.Args[0].(*ast.StrExpr) - if !ok { - panic(errorf("sprintf currently only supports literal format strings")) - } - args := c.printfArgs(formatExpr.Value, e.Args[1:]) - str := fmt.Sprintf("fmt.Sprintf(%q", formatExpr.Value) - for _, arg := range args { - str += ", " + arg - } - str += ")" - return str - - case F_SQRT: - return "math.Sqrt(" + c.numExpr(e.Args[0]) + ")" - - case F_SRAND: - if len(e.Args) == 0 { - return "_srandNow()" - } - return "_srand(" + c.numExpr(e.Args[0]) + ")" - - case F_SUB, F_GSUB: - // sub() is actually an assignment to "in" (an lvalue) or $0: - // n = sub(re, repl[, in]) - var reArg string - if strExpr, ok := e.Args[0].(*ast.StrExpr); ok { - reArg = c.regexLiteral(strExpr.Value) - } else { - reArg = fmt.Sprintf("_reCompile(%s)", c.strExpr(e.Args[0])) - } - str := fmt.Sprintf("func() float64 { out, n := _sub(%s, %s, ", reArg, c.strExpr(e.Args[1])) - if len(e.Args) == 3 { - str += c.expr(e.Args[2]) - } else { - str += "_line" - } - if e.Func == F_GSUB { - str += ", true); " - } else { - str += ", false); " - } - if len(e.Args) == 3 { - str += c.assign(e.Args[2], &ast.VarExpr{Name: "out", Scope: ast.ScopeGlobal}) - } else { - str += "_setField(0, out)" - } - str += "; return float64(n) }()" - return str - - case F_SUBSTR: - if len(e.Args) == 2 { - return "_substr(" + c.strExpr(e.Args[0]) + ", " + c.intExpr(e.Args[1]) + ")" - } - return "_substrLength(" + c.strExpr(e.Args[0]) + ", " + c.intExpr(e.Args[1]) + ", " + c.intExpr(e.Args[2]) + ")" - - case F_SYSTEM: - return "_system(" + c.strExpr(e.Args[0]) + ")" - - case F_TOLOWER: - return "strings.ToLower(" + c.expr(e.Args[0]) + ")" - - case F_TOUPPER: - return "strings.ToUpper(" + c.expr(e.Args[0]) + ")" - - default: - panic(errorf("%s() not yet supported", e.Func)) - } - - case *ast.UnaryExpr: - str := c.expr(e.Value) - typ := c.typer.exprs[e.Value] - switch e.Op { - case SUB: - if typ == typeStr { - str = "_strToNum(" + str + ")" - } - return "(-" + str + ")" - - case NOT: - return "_boolToNum(!(" + c.cond(e.Value) + "))" - - case ADD: - if typ == typeStr { - str = "_strToNum(" + str + ")" - } - return "(+" + str + ")" - - default: - panic(errorf("unexpected unary operation: %s", e.Op)) - } - - case *ast.InExpr: - return fmt.Sprintf("func() float64 { _, ok := %s[%s]; if ok { return 1 }; return 0 }()", - e.Array.Name, c.index(e.Index)) - - default: - panic(errorf("%T not yet supported", expr)) - } -} - -func (c *compiler) binaryExpr(op Token, l, r ast.Expr) (str string) { - switch op { - case ADD, SUB, MUL, DIV: - return "(" + c.numExpr(l) + " " + op.String() + " " + c.numExpr(r) + ")" - case MOD: - return "math.Mod(" + c.numExpr(l) + ", " + c.numExpr(r) + ")" - case POW: - return "math.Pow(" + c.numExpr(l) + ", " + c.numExpr(r) + ")" - case CONCAT: - return "(" + c.strExpr(l) + " + " + c.strExpr(r) + ")" - default: - s, ok := c.boolExpr(op, l, r) - if ok { - return "_boolToNum(" + s + ")" - } - panic(errorf("unexpected binary operator %s", op)) - } -} - -func (c *compiler) boolExpr(op Token, l, r ast.Expr) (string, bool) { - switch op { - case EQUALS, LESS, LTE, GREATER, GTE, NOT_EQUALS: - _, leftIsField := l.(*ast.FieldExpr) - _, rightIsField := r.(*ast.FieldExpr) - if leftIsField && rightIsField { - panic(errorf("can't compare two fields directly (%s %s %s); convert one to string or number", l, op, r)) - } - ls := c.expr(l) - rs := c.expr(r) - lt := c.typer.exprs[l] - rt := c.typer.exprs[r] - switch lt { - case typeNum: - switch rt { - case typeNum: - return fmt.Sprintf("(%s %s %s)", ls, op, rs), true - case typeStr: - return fmt.Sprintf("(_numToStr(%s) %s %s)", ls, op, rs), true - } - case typeStr: - switch rt { - case typeNum: - return fmt.Sprintf("(_strToNum(%s) %s %s)", ls, op, rs), true - case typeStr: - return fmt.Sprintf("(%s %s %s)", ls, op, rs), true - } - } - panic(errorf("unexpected types in %s (%s) %s %s (%s)", ls, lt, op, rs, rt)) - case MATCH: - if strExpr, ok := r.(*ast.StrExpr); ok { - return fmt.Sprintf("%s.MatchString(%s)", c.regexLiteral(strExpr.Value), c.strExpr(l)), true - } - return fmt.Sprintf("_reCompile(%s).MatchString(%s)", c.strExpr(l), c.strExpr(r)), true - case NOT_MATCH: - if strExpr, ok := r.(*ast.StrExpr); ok { - return fmt.Sprintf("(!%s.MatchString(%s))", c.regexLiteral(strExpr.Value), c.strExpr(l)), true - } - return fmt.Sprintf("(!_reCompile(%s).MatchString(%s))", c.strExpr(l), c.strExpr(r)), true - case AND, OR: - return fmt.Sprintf("(%s %s %s)", c.cond(l), op, c.cond(r)), true - default: - return "", false - } -} - -func (c *compiler) cond(expr ast.Expr) string { - // If possible, simplify conditional expression to avoid "_boolToNum(b) != 0" - switch e := expr.(type) { - case *ast.BinaryExpr: - str, ok := c.boolExpr(e.Op, e.Left, e.Right) - if ok { - return str - } - case *ast.RegExpr: - return fmt.Sprintf("%s.MatchString(_line)", c.regexLiteral(e.Regex)) - case *ast.FieldExpr: - return fmt.Sprintf("_isFieldTrue(%s)", c.expr(e)) - case *ast.UnaryExpr: - if e.Op == NOT { - return "(!(" + c.cond(e.Value) + "))" - } - } - - str := c.expr(expr) - if c.typer.exprs[expr] == typeStr { - str += ` != ""` - } else { - str += " != 0" - } - return str -} - -func (c *compiler) numExpr(expr ast.Expr) string { - str := c.expr(expr) - if c.typer.exprs[expr] == typeStr { - str = "_strToNum(" + str + ")" - } - return str -} - -func (c *compiler) intExpr(expr ast.Expr) string { - switch e := expr.(type) { - case *ast.NumExpr: - return strconv.Itoa(int(e.Value)) - case *ast.UnaryExpr: - ne, ok := e.Value.(*ast.NumExpr) - if ok && e.Op == SUB { - return "-" + strconv.Itoa(int(ne.Value)) - } - } - return "int(" + c.numExpr(expr) + ")" -} - -func (c *compiler) strExpr(expr ast.Expr) string { - if fieldExpr, ok := expr.(*ast.FieldExpr); ok { - if numExpr, ok := fieldExpr.Index.(*ast.NumExpr); ok && numExpr.Value == 0 { - // Optimize _getField(0) to just _line - return "_line" - } - } - str := c.expr(expr) - if c.typer.exprs[expr] == typeNum { - str = "_numToStr(" + str + ")" - } - return str -} - -func (c *compiler) index(index []ast.Expr) string { - indexStr := "" - for i, e := range index { - if i > 0 { - indexStr += ` + SUBSEP + ` - } - str := c.expr(e) - if c.typer.exprs[e] == typeNum { - str = "_numToStr(" + str + ")" - } - indexStr += str - } - return indexStr -} - -func (c *compiler) special(name string, index int) string { - switch index { - case ast.V_NF: - return "float64(len(_fields))" - case ast.V_NR, ast.V_FNR: - return "float64(_lineNum)" - case ast.V_RLENGTH: - return "RLENGTH" - case ast.V_RSTART: - return "RSTART" - case ast.V_CONVFMT: - return "CONVFMT" - case ast.V_FS: - return "FS" - case ast.V_OFMT: - return "OFMT" - case ast.V_OFS: - return "OFS" - case ast.V_ORS: - return "ORS" - case ast.V_SUBSEP: - return "SUBSEP" - default: - panic(errorf("special variable %s not yet supported", name)) - } -} - -func (c *compiler) goType(typ valueType) string { - switch typ { - case typeNum: - return "float64" - case typeStr: - return "string" - case typeArrayNum: - return "map[string]float64" - case typeArrayStr: - return "map[string]string" - default: - panic(errorf("can't convert type %s to Go type", typ)) - } -} - -func (c *compiler) printfArgs(format string, args []ast.Expr) []string { - argIndex := 0 - nextArg := func() ast.Expr { - if argIndex >= len(args) { - panic(errorf("not enough arguments (%d) for format string %q", len(args), format)) - } - arg := args[argIndex] - argIndex++ - return arg - } - - var argStrs []string - for i := 0; i < len(format); i++ { - if format[i] == '%' { - i++ - if i >= len(format) { - panic(errorf("expected type specifier after %%")) - } - if format[i] == '%' { - continue - } - for i < len(format) && bytes.IndexByte([]byte(" .-+*#0123456789"), format[i]) >= 0 { - if format[i] == '*' { - argStrs = append(argStrs, c.intExpr(nextArg())) - } - i++ - } - if i >= len(format) { - panic(errorf("expected type specifier after %%")) - } - var argStr string - switch format[i] { - case 's': - argStr = c.strExpr(nextArg()) - case 'd', 'i', 'o', 'x', 'X', 'u': - argStr = c.intExpr(nextArg()) - case 'f', 'e', 'E', 'g', 'G': - argStr = "float64(" + c.numExpr(nextArg()) + ")" - case 'c': - arg := nextArg() - if c.typer.exprs[arg] == typeStr { - argStr = fmt.Sprintf("_firstRune(%s)", arg) - } else { - argStr = c.intExpr(arg) - } - default: - panic(errorf("invalid format type %q", format[i])) - } - argStrs = append(argStrs, argStr) - } - } - return argStrs -} - -func (c *compiler) regexLiteral(pattern string) string { - n, ok := c.regexen[pattern] - if !ok { - n = len(c.regexen) + 1 - c.regexen[pattern] = n - } - varName := fmt.Sprintf("_re%d", n) - return varName -} diff --git a/awkgo/examples/about.awk b/awkgo/examples/about.awk deleted file mode 100644 index cc603f42..00000000 --- a/awkgo/examples/about.awk +++ /dev/null @@ -1 +0,0 @@ -/about/ { print $4 } diff --git a/awkgo/examples/about.go b/awkgo/examples/about.go deleted file mode 100644 index f7e1a7ef..00000000 --- a/awkgo/examples/about.go +++ /dev/null @@ -1,356 +0,0 @@ -//go:build awkgoexample -// +build awkgoexample - -package main - -import ( - "bufio" - "fmt" - "math" - "math/rand" - "os" - "os/exec" - "regexp" - "strconv" - "strings" - "time" - "unicode/utf8" -) - -var ( - _output *bufio.Writer - _scanner *bufio.Scanner - _line string - _fields []string - _lineNum int - _seed float64 - _rand *rand.Rand - - CONVFMT string - FS string - OFMT string - OFS string - ORS string - RLENGTH float64 - RSTART float64 - SUBSEP string -) - -func main() { - _output = bufio.NewWriter(os.Stdout) - defer _output.Flush() - - _scanner = bufio.NewScanner(os.Stdin) - _seed = 1.0 - _rand = rand.New(rand.NewSource(int64(math.Float64bits(_seed)))) - - FS = " " - OFS = " " - ORS = "\n" - OFMT = "%.6g" - CONVFMT = "%.6g" - SUBSEP = "\x1c" - - for _scanner.Scan() { - _lineNum++ - _line = _scanner.Text() - _fields = _splitHelper(_line, FS) - - if _re1.MatchString(_line) { - fmt.Fprintln(_output, _getField(4)) - } - } - - if _scanner.Err() != nil { - fmt.Fprintln(os.Stderr, _scanner.Err()) - os.Exit(1) - } -} - -var ( - _re1 = regexp.MustCompile("about") -) - -func _getField(i int) string { - if i < 0 || i > len(_fields) { - return "" - } - if i == 0 { - return _line - } - return _fields[i-1] -} - -func _setField(i int, s string) { - if i == 0 { - _line = s - _fields = _splitHelper(_line, FS) - return - } - for j := len(_fields); j < i; j++ { - _fields = append(_fields, "") - } - _fields[i-1] = s - _line = strings.Join(_fields, OFS) -} - -func _boolToNum(b bool) float64 { - if b { - return 1 - } - return 0 -} - -func _numToStrFormat(format string, n float64) string { - switch { - case math.IsNaN(n): - return "nan" - case math.IsInf(n, 0): - if n < 0 { - return "-inf" - } else { - return "inf" - } - case n == float64(int(n)): - return strconv.Itoa(int(n)) - default: - return fmt.Sprintf(format, n) - } -} - -func _numToStr(n float64) string { - return _numToStrFormat(CONVFMT, n) -} - -func _formatNum(n float64) string { - return _numToStrFormat(OFMT, n) -} - -var asciiSpace = [256]uint8{'\t': 1, '\n': 1, '\v': 1, '\f': 1, '\r': 1, ' ': 1} - -// Like strconv.ParseFloat, but parses at the start of string and -// allows things like "1.5foo". -func _strToNum(s string) float64 { - // Skip whitespace at start - i := 0 - for i < len(s) && asciiSpace[s[i]] != 0 { - i++ - } - start := i - - // Parse mantissa: optional sign, initial digit(s), optional '.', - // then more digits - gotDigit := false - if i < len(s) && (s[i] == '+' || s[i] == '-') { - i++ - } - for i < len(s) && s[i] >= '0' && s[i] <= '9' { - gotDigit = true - i++ - } - if i < len(s) && s[i] == '.' { - i++ - } - for i < len(s) && s[i] >= '0' && s[i] <= '9' { - gotDigit = true - i++ - } - if !gotDigit { - return 0 - } - - // Parse exponent ("1e" and similar are allowed, but ParseFloat - // rejects them) - end := i - if i < len(s) && (s[i] == 'e' || s[i] == 'E') { - i++ - if i < len(s) && (s[i] == '+' || s[i] == '-') { - i++ - } - for i < len(s) && s[i] >= '0' && s[i] <= '9' { - i++ - end = i - } - } - - floatStr := s[start:end] - f, _ := strconv.ParseFloat(floatStr, 64) - return f // Returns infinity in case of "value out of range" error -} - -func _isFieldTrue(s string) bool { - if s == "" { - return false - } - f, err := strconv.ParseFloat(strings.TrimSpace(s), 64) - if err != nil { - return true - } - return f != 0 -} - -var _reCache = make(map[string]*regexp.Regexp) - -func _reCompile(pattern string) *regexp.Regexp { - if re, ok := _reCache[pattern]; ok { - return re - } - re := regexp.MustCompile(pattern) - // Dumb, non-LRU cache: just cache the first N regexes - if len(_reCache) < 100 { - _reCache[pattern] = re - } - return re -} - -func _match(str string, re *regexp.Regexp) float64 { - loc := re.FindStringIndex(str) - if len(loc) > 0 { - RSTART = float64(loc[0] + 1) - RLENGTH = float64(loc[1] - loc[0]) - } else { - RSTART = 0 - RLENGTH = -1 - } - return RSTART -} - -func _srand(seed float64) float64 { - prev := _seed - _seed = seed - _rand.Seed(int64(math.Float64bits(seed))) - return prev -} - -func _srandNow() float64 { - prev := _seed - _rand.Seed(time.Now().UnixNano()) - return prev -} - -func _substrLength(s string, pos, length int) string { - if pos > len(s) { - pos = len(s) + 1 - } - if pos < 1 { - pos = 1 - } - if length < 0 { - length = 0 - } - maxLength := len(s) - pos + 1 - if length > maxLength { - length = maxLength - } - return s[pos-1 : pos-1+length] -} - -func _substr(s string, pos int) string { - if pos > len(s) { - pos = len(s) + 1 - } - if pos < 1 { - pos = 1 - } - length := len(s) - pos + 1 - return s[pos-1 : pos-1+length] -} - -func _firstRune(s string) int { - r, n := utf8.DecodeRuneInString(s) - if n == 0 { - return 0 - } - return int(r) -} - -func _splitHelper(s, fs string) []string { - var parts []string - if fs == " " { - parts = strings.Fields(s) - } else if s == "" { - // NF should be 0 on empty line - } else if utf8.RuneCountInString(fs) <= 1 { - parts = strings.Split(s, fs) - } else { - parts = _reCompile(fs).Split(s, -1) - } - return parts -} - -func _split(s string, a map[string]string, fs string) float64 { - parts := _splitHelper(s, fs) - for k := range a { - delete(a, k) - } - for i, part := range parts { - a[strconv.Itoa(i+1)] = part - } - return float64(len(a)) -} - -func _sub(re *regexp.Regexp, repl, in string, global bool) (out string, num int) { - count := 0 - out = re.ReplaceAllStringFunc(in, func(s string) string { - // Only do the first replacement for sub(), or all for gsub() - if !global && count > 0 { - return s - } - count++ - // Handle & (ampersand) properly in replacement string - r := make([]byte, 0, 64) // Up to 64 byte replacement won't require heap allocation - for i := 0; i < len(repl); i++ { - switch repl[i] { - case '&': - r = append(r, s...) - case '\\': - i++ - if i < len(repl) { - switch repl[i] { - case '&': - r = append(r, '&') - case '\\': - r = append(r, '\\') - default: - r = append(r, '\\', repl[i]) - } - } else { - r = append(r, '\\') - } - default: - r = append(r, repl[i]) - } - } - return string(r) - }) - return out, count -} - -func _system(command string) float64 { - cmd := exec.Command("/bin/sh", "-c", command) - cmd.Stdout = _output - cmd.Stderr = os.Stderr - err := cmd.Start() - if err != nil { - fmt.Fprintln(os.Stderr, err) - return -1 - } - err = cmd.Wait() - if err != nil { - if exitErr, ok := err.(*exec.ExitError); ok { - return float64(exitErr.ProcessState.ExitCode()) - } - fmt.Fprintf(os.Stderr, "unexpected error running command %q: %v\n", command, err) - return -1 - } - return 0 -} - -func _fflush() float64 { - err := _output.Flush() - if err != nil { - fmt.Fprintf(os.Stderr, "error flushing output: %v\n", err) - return -1 - } - return 0 -} diff --git a/awkgo/examples/countwords.awk b/awkgo/examples/countwords.awk deleted file mode 100644 index e00e9d4c..00000000 --- a/awkgo/examples/countwords.awk +++ /dev/null @@ -1,9 +0,0 @@ -{ - for (i = 1; i <= NF; i++) - counts[tolower($i)]++ -} - -END { - for (k in counts) - print k, counts[k] -} diff --git a/awkgo/examples/countwords.go b/awkgo/examples/countwords.go deleted file mode 100644 index 5be6b0bc..00000000 --- a/awkgo/examples/countwords.go +++ /dev/null @@ -1,361 +0,0 @@ -//go:build awkgoexample -// +build awkgoexample - -package main - -import ( - "bufio" - "fmt" - "math" - "math/rand" - "os" - "os/exec" - "regexp" - "strconv" - "strings" - "time" - "unicode/utf8" -) - -var ( - _output *bufio.Writer - _scanner *bufio.Scanner - _line string - _fields []string - _lineNum int - _seed float64 - _rand *rand.Rand - - CONVFMT string - FS string - OFMT string - OFS string - ORS string - RLENGTH float64 - RSTART float64 - SUBSEP string - counts map[string]float64 - i float64 - k string -) - -func main() { - _output = bufio.NewWriter(os.Stdout) - defer _output.Flush() - - _scanner = bufio.NewScanner(os.Stdin) - _seed = 1.0 - _rand = rand.New(rand.NewSource(int64(math.Float64bits(_seed)))) - - FS = " " - OFS = " " - ORS = "\n" - OFMT = "%.6g" - CONVFMT = "%.6g" - SUBSEP = "\x1c" - - counts = make(map[string]float64) - - for _scanner.Scan() { - _lineNum++ - _line = _scanner.Text() - _fields = _splitHelper(_line, FS) - - for i = 1.0; i <= float64(len(_fields)); i++ { - counts[strings.ToLower(_getField(int(i)))]++ - } - } - - if _scanner.Err() != nil { - fmt.Fprintln(os.Stderr, _scanner.Err()) - os.Exit(1) - } - - for k = range counts { - fmt.Fprintln(_output, k, _formatNum(counts[k])) - } -} - -func _getField(i int) string { - if i < 0 || i > len(_fields) { - return "" - } - if i == 0 { - return _line - } - return _fields[i-1] -} - -func _setField(i int, s string) { - if i == 0 { - _line = s - _fields = _splitHelper(_line, FS) - return - } - for j := len(_fields); j < i; j++ { - _fields = append(_fields, "") - } - _fields[i-1] = s - _line = strings.Join(_fields, OFS) -} - -func _boolToNum(b bool) float64 { - if b { - return 1 - } - return 0 -} - -func _numToStrFormat(format string, n float64) string { - switch { - case math.IsNaN(n): - return "nan" - case math.IsInf(n, 0): - if n < 0 { - return "-inf" - } else { - return "inf" - } - case n == float64(int(n)): - return strconv.Itoa(int(n)) - default: - return fmt.Sprintf(format, n) - } -} - -func _numToStr(n float64) string { - return _numToStrFormat(CONVFMT, n) -} - -func _formatNum(n float64) string { - return _numToStrFormat(OFMT, n) -} - -var asciiSpace = [256]uint8{'\t': 1, '\n': 1, '\v': 1, '\f': 1, '\r': 1, ' ': 1} - -// Like strconv.ParseFloat, but parses at the start of string and -// allows things like "1.5foo". -func _strToNum(s string) float64 { - // Skip whitespace at start - i := 0 - for i < len(s) && asciiSpace[s[i]] != 0 { - i++ - } - start := i - - // Parse mantissa: optional sign, initial digit(s), optional '.', - // then more digits - gotDigit := false - if i < len(s) && (s[i] == '+' || s[i] == '-') { - i++ - } - for i < len(s) && s[i] >= '0' && s[i] <= '9' { - gotDigit = true - i++ - } - if i < len(s) && s[i] == '.' { - i++ - } - for i < len(s) && s[i] >= '0' && s[i] <= '9' { - gotDigit = true - i++ - } - if !gotDigit { - return 0 - } - - // Parse exponent ("1e" and similar are allowed, but ParseFloat - // rejects them) - end := i - if i < len(s) && (s[i] == 'e' || s[i] == 'E') { - i++ - if i < len(s) && (s[i] == '+' || s[i] == '-') { - i++ - } - for i < len(s) && s[i] >= '0' && s[i] <= '9' { - i++ - end = i - } - } - - floatStr := s[start:end] - f, _ := strconv.ParseFloat(floatStr, 64) - return f // Returns infinity in case of "value out of range" error -} - -func _isFieldTrue(s string) bool { - if s == "" { - return false - } - f, err := strconv.ParseFloat(strings.TrimSpace(s), 64) - if err != nil { - return true - } - return f != 0 -} - -var _reCache = make(map[string]*regexp.Regexp) - -func _reCompile(pattern string) *regexp.Regexp { - if re, ok := _reCache[pattern]; ok { - return re - } - re := regexp.MustCompile(pattern) - // Dumb, non-LRU cache: just cache the first N regexes - if len(_reCache) < 100 { - _reCache[pattern] = re - } - return re -} - -func _match(str string, re *regexp.Regexp) float64 { - loc := re.FindStringIndex(str) - if len(loc) > 0 { - RSTART = float64(loc[0] + 1) - RLENGTH = float64(loc[1] - loc[0]) - } else { - RSTART = 0 - RLENGTH = -1 - } - return RSTART -} - -func _srand(seed float64) float64 { - prev := _seed - _seed = seed - _rand.Seed(int64(math.Float64bits(seed))) - return prev -} - -func _srandNow() float64 { - prev := _seed - _rand.Seed(time.Now().UnixNano()) - return prev -} - -func _substrLength(s string, pos, length int) string { - if pos > len(s) { - pos = len(s) + 1 - } - if pos < 1 { - pos = 1 - } - if length < 0 { - length = 0 - } - maxLength := len(s) - pos + 1 - if length > maxLength { - length = maxLength - } - return s[pos-1 : pos-1+length] -} - -func _substr(s string, pos int) string { - if pos > len(s) { - pos = len(s) + 1 - } - if pos < 1 { - pos = 1 - } - length := len(s) - pos + 1 - return s[pos-1 : pos-1+length] -} - -func _firstRune(s string) int { - r, n := utf8.DecodeRuneInString(s) - if n == 0 { - return 0 - } - return int(r) -} - -func _splitHelper(s, fs string) []string { - var parts []string - if fs == " " { - parts = strings.Fields(s) - } else if s == "" { - // NF should be 0 on empty line - } else if utf8.RuneCountInString(fs) <= 1 { - parts = strings.Split(s, fs) - } else { - parts = _reCompile(fs).Split(s, -1) - } - return parts -} - -func _split(s string, a map[string]string, fs string) float64 { - parts := _splitHelper(s, fs) - for k := range a { - delete(a, k) - } - for i, part := range parts { - a[strconv.Itoa(i+1)] = part - } - return float64(len(a)) -} - -func _sub(re *regexp.Regexp, repl, in string, global bool) (out string, num int) { - count := 0 - out = re.ReplaceAllStringFunc(in, func(s string) string { - // Only do the first replacement for sub(), or all for gsub() - if !global && count > 0 { - return s - } - count++ - // Handle & (ampersand) properly in replacement string - r := make([]byte, 0, 64) // Up to 64 byte replacement won't require heap allocation - for i := 0; i < len(repl); i++ { - switch repl[i] { - case '&': - r = append(r, s...) - case '\\': - i++ - if i < len(repl) { - switch repl[i] { - case '&': - r = append(r, '&') - case '\\': - r = append(r, '\\') - default: - r = append(r, '\\', repl[i]) - } - } else { - r = append(r, '\\') - } - default: - r = append(r, repl[i]) - } - } - return string(r) - }) - return out, count -} - -func _system(command string) float64 { - cmd := exec.Command("/bin/sh", "-c", command) - cmd.Stdout = _output - cmd.Stderr = os.Stderr - err := cmd.Start() - if err != nil { - fmt.Fprintln(os.Stderr, err) - return -1 - } - err = cmd.Wait() - if err != nil { - if exitErr, ok := err.(*exec.ExitError); ok { - return float64(exitErr.ProcessState.ExitCode()) - } - fmt.Fprintf(os.Stderr, "unexpected error running command %q: %v\n", command, err) - return -1 - } - return 0 -} - -func _fflush() float64 { - err := _output.Flush() - if err != nil { - fmt.Fprintf(os.Stderr, "error flushing output: %v\n", err) - return -1 - } - return 0 -} diff --git a/awkgo/examples/trickier.awk b/awkgo/examples/trickier.awk deleted file mode 100644 index e22f1450..00000000 --- a/awkgo/examples/trickier.awk +++ /dev/null @@ -1,2 +0,0 @@ -$1+0 == $2 { x += ++n } -END { print x } diff --git a/awkgo/examples/trickier.go b/awkgo/examples/trickier.go deleted file mode 100644 index 76b64868..00000000 --- a/awkgo/examples/trickier.go +++ /dev/null @@ -1,356 +0,0 @@ -//go:build awkgoexample -// +build awkgoexample - -package main - -import ( - "bufio" - "fmt" - "math" - "math/rand" - "os" - "os/exec" - "regexp" - "strconv" - "strings" - "time" - "unicode/utf8" -) - -var ( - _output *bufio.Writer - _scanner *bufio.Scanner - _line string - _fields []string - _lineNum int - _seed float64 - _rand *rand.Rand - - CONVFMT string - FS string - OFMT string - OFS string - ORS string - RLENGTH float64 - RSTART float64 - SUBSEP string - n float64 - x float64 -) - -func main() { - _output = bufio.NewWriter(os.Stdout) - defer _output.Flush() - - _scanner = bufio.NewScanner(os.Stdin) - _seed = 1.0 - _rand = rand.New(rand.NewSource(int64(math.Float64bits(_seed)))) - - FS = " " - OFS = " " - ORS = "\n" - OFMT = "%.6g" - CONVFMT = "%.6g" - SUBSEP = "\x1c" - - for _scanner.Scan() { - _lineNum++ - _line = _scanner.Text() - _fields = _splitHelper(_line, FS) - - if _numToStr(_strToNum(_getField(1))+0.0) == _getField(2) { - x += func() float64 { n++; return n }() - } - } - - if _scanner.Err() != nil { - fmt.Fprintln(os.Stderr, _scanner.Err()) - os.Exit(1) - } - - fmt.Fprintln(_output, _formatNum(x)) -} - -func _getField(i int) string { - if i < 0 || i > len(_fields) { - return "" - } - if i == 0 { - return _line - } - return _fields[i-1] -} - -func _setField(i int, s string) { - if i == 0 { - _line = s - _fields = _splitHelper(_line, FS) - return - } - for j := len(_fields); j < i; j++ { - _fields = append(_fields, "") - } - _fields[i-1] = s - _line = strings.Join(_fields, OFS) -} - -func _boolToNum(b bool) float64 { - if b { - return 1 - } - return 0 -} - -func _numToStrFormat(format string, n float64) string { - switch { - case math.IsNaN(n): - return "nan" - case math.IsInf(n, 0): - if n < 0 { - return "-inf" - } else { - return "inf" - } - case n == float64(int(n)): - return strconv.Itoa(int(n)) - default: - return fmt.Sprintf(format, n) - } -} - -func _numToStr(n float64) string { - return _numToStrFormat(CONVFMT, n) -} - -func _formatNum(n float64) string { - return _numToStrFormat(OFMT, n) -} - -var asciiSpace = [256]uint8{'\t': 1, '\n': 1, '\v': 1, '\f': 1, '\r': 1, ' ': 1} - -// Like strconv.ParseFloat, but parses at the start of string and -// allows things like "1.5foo". -func _strToNum(s string) float64 { - // Skip whitespace at start - i := 0 - for i < len(s) && asciiSpace[s[i]] != 0 { - i++ - } - start := i - - // Parse mantissa: optional sign, initial digit(s), optional '.', - // then more digits - gotDigit := false - if i < len(s) && (s[i] == '+' || s[i] == '-') { - i++ - } - for i < len(s) && s[i] >= '0' && s[i] <= '9' { - gotDigit = true - i++ - } - if i < len(s) && s[i] == '.' { - i++ - } - for i < len(s) && s[i] >= '0' && s[i] <= '9' { - gotDigit = true - i++ - } - if !gotDigit { - return 0 - } - - // Parse exponent ("1e" and similar are allowed, but ParseFloat - // rejects them) - end := i - if i < len(s) && (s[i] == 'e' || s[i] == 'E') { - i++ - if i < len(s) && (s[i] == '+' || s[i] == '-') { - i++ - } - for i < len(s) && s[i] >= '0' && s[i] <= '9' { - i++ - end = i - } - } - - floatStr := s[start:end] - f, _ := strconv.ParseFloat(floatStr, 64) - return f // Returns infinity in case of "value out of range" error -} - -func _isFieldTrue(s string) bool { - if s == "" { - return false - } - f, err := strconv.ParseFloat(strings.TrimSpace(s), 64) - if err != nil { - return true - } - return f != 0 -} - -var _reCache = make(map[string]*regexp.Regexp) - -func _reCompile(pattern string) *regexp.Regexp { - if re, ok := _reCache[pattern]; ok { - return re - } - re := regexp.MustCompile(pattern) - // Dumb, non-LRU cache: just cache the first N regexes - if len(_reCache) < 100 { - _reCache[pattern] = re - } - return re -} - -func _match(str string, re *regexp.Regexp) float64 { - loc := re.FindStringIndex(str) - if len(loc) > 0 { - RSTART = float64(loc[0] + 1) - RLENGTH = float64(loc[1] - loc[0]) - } else { - RSTART = 0 - RLENGTH = -1 - } - return RSTART -} - -func _srand(seed float64) float64 { - prev := _seed - _seed = seed - _rand.Seed(int64(math.Float64bits(seed))) - return prev -} - -func _srandNow() float64 { - prev := _seed - _rand.Seed(time.Now().UnixNano()) - return prev -} - -func _substrLength(s string, pos, length int) string { - if pos > len(s) { - pos = len(s) + 1 - } - if pos < 1 { - pos = 1 - } - if length < 0 { - length = 0 - } - maxLength := len(s) - pos + 1 - if length > maxLength { - length = maxLength - } - return s[pos-1 : pos-1+length] -} - -func _substr(s string, pos int) string { - if pos > len(s) { - pos = len(s) + 1 - } - if pos < 1 { - pos = 1 - } - length := len(s) - pos + 1 - return s[pos-1 : pos-1+length] -} - -func _firstRune(s string) int { - r, n := utf8.DecodeRuneInString(s) - if n == 0 { - return 0 - } - return int(r) -} - -func _splitHelper(s, fs string) []string { - var parts []string - if fs == " " { - parts = strings.Fields(s) - } else if s == "" { - // NF should be 0 on empty line - } else if utf8.RuneCountInString(fs) <= 1 { - parts = strings.Split(s, fs) - } else { - parts = _reCompile(fs).Split(s, -1) - } - return parts -} - -func _split(s string, a map[string]string, fs string) float64 { - parts := _splitHelper(s, fs) - for k := range a { - delete(a, k) - } - for i, part := range parts { - a[strconv.Itoa(i+1)] = part - } - return float64(len(a)) -} - -func _sub(re *regexp.Regexp, repl, in string, global bool) (out string, num int) { - count := 0 - out = re.ReplaceAllStringFunc(in, func(s string) string { - // Only do the first replacement for sub(), or all for gsub() - if !global && count > 0 { - return s - } - count++ - // Handle & (ampersand) properly in replacement string - r := make([]byte, 0, 64) // Up to 64 byte replacement won't require heap allocation - for i := 0; i < len(repl); i++ { - switch repl[i] { - case '&': - r = append(r, s...) - case '\\': - i++ - if i < len(repl) { - switch repl[i] { - case '&': - r = append(r, '&') - case '\\': - r = append(r, '\\') - default: - r = append(r, '\\', repl[i]) - } - } else { - r = append(r, '\\') - } - default: - r = append(r, repl[i]) - } - } - return string(r) - }) - return out, count -} - -func _system(command string) float64 { - cmd := exec.Command("/bin/sh", "-c", command) - cmd.Stdout = _output - cmd.Stderr = os.Stderr - err := cmd.Start() - if err != nil { - fmt.Fprintln(os.Stderr, err) - return -1 - } - err = cmd.Wait() - if err != nil { - if exitErr, ok := err.(*exec.ExitError); ok { - return float64(exitErr.ProcessState.ExitCode()) - } - fmt.Fprintf(os.Stderr, "unexpected error running command %q: %v\n", command, err) - return -1 - } - return 0 -} - -func _fflush() float64 { - err := _output.Flush() - if err != nil { - fmt.Fprintf(os.Stderr, "error flushing output: %v\n", err) - return -1 - } - return 0 -} diff --git a/awkgo/helpers.go b/awkgo/helpers.go deleted file mode 100644 index 83f1b120..00000000 --- a/awkgo/helpers.go +++ /dev/null @@ -1,289 +0,0 @@ -package main - -func (c *compiler) outputHelpers() { - c.output(` -func _getField(i int) string { - if i < 0 || i > len(_fields) { - return "" - } - if i == 0 { - return _line - } - return _fields[i-1] -} - -func _setField(i int, s string) { - if i == 0 { - _line = s - _fields = _splitHelper(_line, FS) - return - } - for j := len(_fields); j < i; j++ { - _fields = append(_fields, "") - } - _fields[i-1] = s - _line = strings.Join(_fields, OFS) -} - -func _boolToNum(b bool) float64 { - if b { - return 1 - } - return 0 -} - -func _numToStrFormat(format string, n float64) string { - switch { - case math.IsNaN(n): - return "nan" - case math.IsInf(n, 0): - if n < 0 { - return "-inf" - } else { - return "inf" - } - case n == float64(int(n)): - return strconv.Itoa(int(n)) - default: - return fmt.Sprintf(format, n) - } -} - -func _numToStr(n float64) string { - return _numToStrFormat(CONVFMT, n) -} - -func _formatNum(n float64) string { - return _numToStrFormat(OFMT, n) -} - -var asciiSpace = [256]uint8{'\t': 1, '\n': 1, '\v': 1, '\f': 1, '\r': 1, ' ': 1} - -// Like strconv.ParseFloat, but parses at the start of string and -// allows things like "1.5foo". -func _strToNum(s string) float64 { - // Skip whitespace at start - i := 0 - for i < len(s) && asciiSpace[s[i]] != 0 { - i++ - } - start := i - - // Parse mantissa: optional sign, initial digit(s), optional '.', - // then more digits - gotDigit := false - if i < len(s) && (s[i] == '+' || s[i] == '-') { - i++ - } - for i < len(s) && s[i] >= '0' && s[i] <= '9' { - gotDigit = true - i++ - } - if i < len(s) && s[i] == '.' { - i++ - } - for i < len(s) && s[i] >= '0' && s[i] <= '9' { - gotDigit = true - i++ - } - if !gotDigit { - return 0 - } - - // Parse exponent ("1e" and similar are allowed, but ParseFloat - // rejects them) - end := i - if i < len(s) && (s[i] == 'e' || s[i] == 'E') { - i++ - if i < len(s) && (s[i] == '+' || s[i] == '-') { - i++ - } - for i < len(s) && s[i] >= '0' && s[i] <= '9' { - i++ - end = i - } - } - - floatStr := s[start:end] - f, _ := strconv.ParseFloat(floatStr, 64) - return f // Returns infinity in case of "value out of range" error -} - -func _isFieldTrue(s string) bool { - if s == "" { - return false - } - f, err := strconv.ParseFloat(strings.TrimSpace(s), 64) - if err != nil { - return true - } - return f != 0 -} - -var _reCache = make(map[string]*regexp.Regexp) - -func _reCompile(pattern string) *regexp.Regexp { - if re, ok := _reCache[pattern]; ok { - return re - } - re := regexp.MustCompile(pattern) - // Dumb, non-LRU cache: just cache the first N regexes - if len(_reCache) < 100 { - _reCache[pattern] = re - } - return re -} - -func _match(str string, re *regexp.Regexp) float64 { - loc := re.FindStringIndex(str) - if len(loc) > 0 { - RSTART = float64(loc[0] + 1) - RLENGTH = float64(loc[1] - loc[0]) - } else { - RSTART = 0 - RLENGTH = -1 - } - return RSTART -} - -func _srand(seed float64) float64 { - prev := _seed - _seed = seed - _rand.Seed(int64(math.Float64bits(seed))) - return prev -} - -func _srandNow() float64 { - prev := _seed - _rand.Seed(time.Now().UnixNano()) - return prev -} - -func _substrLength(s string, pos, length int) string { - if pos > len(s) { - pos = len(s) + 1 - } - if pos < 1 { - pos = 1 - } - if length < 0 { - length = 0 - } - maxLength := len(s) - pos + 1 - if length > maxLength { - length = maxLength - } - return s[pos-1 : pos-1+length] -} - -func _substr(s string, pos int) string { - if pos > len(s) { - pos = len(s) + 1 - } - if pos < 1 { - pos = 1 - } - length := len(s) - pos + 1 - return s[pos-1 : pos-1+length] -} - -func _firstRune(s string) int { - r, n := utf8.DecodeRuneInString(s) - if n == 0 { - return 0 - } - return int(r) -} - -func _splitHelper(s, fs string) []string { - var parts []string - if fs == " " { - parts = strings.Fields(s) - } else if s == "" { - // NF should be 0 on empty line - } else if utf8.RuneCountInString(fs) <= 1 { - parts = strings.Split(s, fs) - } else { - parts = _reCompile(fs).Split(s, -1) - } - return parts -} - -func _split(s string, a map[string]string, fs string) float64 { - parts := _splitHelper(s, fs) - for k := range a { - delete(a, k) - } - for i, part := range parts { - a[strconv.Itoa(i+1)] = part - } - return float64(len(a)) -} - -func _sub(re *regexp.Regexp, repl, in string, global bool) (out string, num int) { - count := 0 - out = re.ReplaceAllStringFunc(in, func(s string) string { - // Only do the first replacement for sub(), or all for gsub() - if !global && count > 0 { - return s - } - count++ - // Handle & (ampersand) properly in replacement string - r := make([]byte, 0, 64) // Up to 64 byte replacement won't require heap allocation - for i := 0; i < len(repl); i++ { - switch repl[i] { - case '&': - r = append(r, s...) - case '\\': - i++ - if i < len(repl) { - switch repl[i] { - case '&': - r = append(r, '&') - case '\\': - r = append(r, '\\') - default: - r = append(r, '\\', repl[i]) - } - } else { - r = append(r, '\\') - } - default: - r = append(r, repl[i]) - } - } - return string(r) - }) - return out, count -} - -func _system(command string) float64 { - cmd := exec.Command("/bin/sh", "-c", command) - cmd.Stdout = _output - cmd.Stderr = os.Stderr - err := cmd.Start() - if err != nil { - fmt.Fprintln(os.Stderr, err) - return -1 - } - err = cmd.Wait() - if err != nil { - if exitErr, ok := err.(*exec.ExitError); ok { - return float64(exitErr.ProcessState.ExitCode()) - } - fmt.Fprintf(os.Stderr, "unexpected error running command %q: %v\n", command, err) - return -1 - } - return 0 -} - -func _fflush() float64 { - err := _output.Flush() - if err != nil { - fmt.Fprintf(os.Stderr, "error flushing output: %v\n", err) - return -1 - } - return 0 -} -`) -} diff --git a/awkgo/run_tests.sh b/awkgo/run_tests.sh deleted file mode 100755 index ef4d06ba..00000000 --- a/awkgo/run_tests.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -go test -tags awkgotest ./awkgo -v -count=1 | awk '{ sub(/TestAWKGo[0-9]+/, "TestAWKGo"); sub(/( \(|\t)[0-9]+\.[0-9]+s\)?/, ""); sub(/awkgo_test.go:[0-9]+/, "awkgo_test.go"); print }' >awkgo/tests.txt diff --git a/awkgo/tests.txt b/awkgo/tests.txt deleted file mode 100644 index 84454aed..00000000 --- a/awkgo/tests.txt +++ /dev/null @@ -1,428 +0,0 @@ -=== RUN TestAWKGo -=== RUN TestAWKGo/BEGIN_{_print_"b"_} -=== RUN TestAWKGo/BEGIN_{_print_"b"_}#01 -=== RUN TestAWKGo/END_{_print_"e"_} -=== RUN TestAWKGo/END_{_print_"e"_}#01 -=== RUN TestAWKGo/BEGIN_{_print_"b"}_END_{_print_"e"_} -=== RUN TestAWKGo/BEGIN_{_print_"b"}_END_{_print_"e"_}#01 -=== RUN TestAWKGo/BEGIN_{_print_"b"}_$0_{_print_NR_}_END_{_print_"e"_} -=== RUN TestAWKGo/BEGIN_{_printf_"x"_};_BEGIN_{_printf_"y"_} -=== RUN TestAWKGo/$0 -=== RUN TestAWKGo/{_print_$0_} -=== RUN TestAWKGo/$1=="foo" -=== RUN TestAWKGo/$1==42 -=== RUN TestAWKGo/$1=="42" -=== RUN TestAWKGo//foo/ -=== RUN TestAWKGo/NR==2,_NR==4 -=== RUN TestAWKGo/_NR==2,_NR==4_{_print_$0_}_NR==3,_NR==5_{_print_NR_}_ -=== RUN TestAWKGo/BEGIN_{_print_"x",_"y"_} -=== RUN TestAWKGo/BEGIN_{_print_OFS;_OFS_=_",";_print_"x",_"y"_} -=== RUN TestAWKGo/BEGIN_{_print_ORS;_ORS_=_".";_print_"x",_"y"_} -=== RUN TestAWKGo/BEGIN_{_print_ORS;_ORS_=_"";_print_"x",_"y"_} -=== RUN TestAWKGo/{_print;_print_} -=== RUN TestAWKGo/BEGIN_{_print;_print_} -=== RUN TestAWKGo/BEGIN_{_printf_"%%_%d_%x_%c_%f_%s",_42,_42,_42,_42,_42_} -=== RUN TestAWKGo/BEGIN_{_printf_"%3d",_42_} -=== RUN TestAWKGo/BEGIN_{_printf_"%3s",_"x"_} -=== RUN TestAWKGo/BEGIN_{_printf_"%d",_12,_34_} -=== RUN TestAWKGo/BEGIN_{_printf_"%d"_} -=== RUN TestAWKGo/BEGIN_{_printf_"%c",_0_} -=== RUN TestAWKGo/BEGIN_{_printf_"%c",_127_} -=== RUN TestAWKGo/BEGIN_{_printf_"%c",_128_}__#_!gawk -=== RUN TestAWKGo/BEGIN_{_printf_"%c",_255_}__#_!gawk -=== RUN TestAWKGo/BEGIN_{_printf_"%c",_256_}__#_!awk_!gawk -=== RUN TestAWKGo/BEGIN_{_printf_"%c",_"xyz"_} -=== RUN TestAWKGo/BEGIN_{_printf_"%c",_""_}__#_!awk -=== RUN TestAWKGo/BEGIN_{_printf_}__#_!awk_-_doesn't_error_on_this -=== RUN TestAWKGo/BEGIN_{_printf("%%%dd",_4)_} -=== RUN TestAWKGo/BEGIN_{_if_(1)_print_"t";_} -=== RUN TestAWKGo/BEGIN_{_if_(0)_print_"t";_} -=== RUN TestAWKGo/BEGIN_{_if_(1)_print_"t";_else_print_"f"_} -=== RUN TestAWKGo/BEGIN_{_if_(0)_print_"t";_else_print_"f"_} -=== RUN TestAWKGo/BEGIN_{_for_(;;)_{_print_"x";_break_}_} -=== RUN TestAWKGo/BEGIN_{_for_(;;)_{_printf_"%d_",_i;_i++;_if_(i>2)_break;_}_} -=== RUN TestAWKGo/BEGIN_{_for_(i=5;_;_)_{_printf_"%d_",_i;_i++;_if_(i>8)_break;_}_} -=== RUN TestAWKGo/BEGIN_{_for_(i=5;_;_i++)_{_printf_"%d_",_i;_if_(i>8)_break;_}_} -=== RUN TestAWKGo/BEGIN_{_for_(i=5;_i<8;_i++)_{_printf_"%d_",_i_}_} -=== RUN TestAWKGo/BEGIN_{_for_(i=0;_i<10;_i++)_{_if_(i_<_5)_continue;_printf_"%d_",_i_}_ -=== RUN TestAWKGo/BEGIN_{_a[1]=1;_a[2]=1;_for_(k_in_a)_{_s++;_break_}_print_s_} -=== RUN TestAWKGo/BEGIN_{_a[1]=1;_a[2]=1;_a[3]=1;_for_(k_in_a)_{_if_(k==2)_continue;_s++ -=== RUN TestAWKGo/BEGIN_{_while_(i<3)_{_i++;_s++;_break_}_print_s_} -=== RUN TestAWKGo/BEGIN_{_while_(i<3)_{_i++;_if_(i==2)_continue;_s++_}_print_s_} -=== RUN TestAWKGo/BEGIN_{_do_{_i++;_s++;_break_}_while_(i<3);_print_s_} -=== RUN TestAWKGo/BEGIN_{_do_{_i++;_if_(i==2)_continue;_s++_}_while_(i<3);_print_s_} -=== RUN TestAWKGo/BEGIN_{_a["x"]_=_3;_a["y"]_=_4;_for_(k_in_a)_x_+=_a[k];_print_x_} -=== RUN TestAWKGo/BEGIN_{_while_(i_<_5)_{_print_i;_i++_}_} -=== RUN TestAWKGo/BEGIN_{_do_{_print_i;_i++_}_while_(i_<_5)_} -=== RUN TestAWKGo/BEGIN_{_for_(i=0;_i<10;_i++);_printf_"x"_} -=== RUN TestAWKGo/_BEGIN_{__for_(i_=_0;_i_<_1;_i++)_{___for_(j_=_0;_j_<_1;_j++)_{____pri -=== RUN TestAWKGo/_BEGIN_{__for_(i_=_0;_i_<_1;_i++)_{___for_(j_=_0;_j_<_1;_j++)_{____pri#01 -=== RUN TestAWKGo/{_if_(NR==2)_next;_print_} -=== RUN TestAWKGo/BEGIN_{_next_} -=== RUN TestAWKGo/END_{_next_} -=== RUN TestAWKGo/BEGIN_{_a["x"]_=_3;_print_"x"_in_a,_"y"_in_a_} -=== RUN TestAWKGo/BEGIN_{_a["x"]_=_3;_a["y"]_=_4;_delete_a["x"];_for_(k_in_a)_print_k,_a -=== RUN TestAWKGo/BEGIN_{_a["x"]_=_3;_a["y"]_=_4;_for_(k_in_a)_delete_a[k];_for_(k_in_a) -=== RUN TestAWKGo/BEGIN_{_a[]_} -=== RUN TestAWKGo/BEGIN_{_delete_a[]_} -=== RUN TestAWKGo/BEGIN_{_a["x"]_=_3;_a["y"]_=_4;_delete_a;_for_(k_in_a)_print_k,_a[k]_} -=== RUN TestAWKGo/BEGIN_{_print_!42,_!1,_!0,_!!42,_!!1,_!!0_} -=== RUN TestAWKGo/BEGIN_{_print_!42,_!1,_!0,_!!42,_!!1,_!!0_}#01 -=== RUN TestAWKGo/BEGIN_{_print_+4,_+"3",_+0,_+-3,_-3,_-_-4,_-"3"_} -=== RUN TestAWKGo/BEGIN_{_$0="1";_print_!$0_} -=== RUN TestAWKGo/{_print_!$0_} -=== RUN TestAWKGo/{_print_!$0_}#01 -=== RUN TestAWKGo/!seen[$0]++ -=== RUN TestAWKGo/!seen[$0]-- -=== RUN TestAWKGo/BEGIN_{_print_(1==1,_1==0,_"1"==1,_"1"==1.0)_} -=== RUN TestAWKGo/{_print_($0=="1",_$0==1)_} -=== RUN TestAWKGo/{_print_($1=="1",_$1==1)_} -=== RUN TestAWKGo/BEGIN_{_print_(1!=1,_1!=0,_"1"!=1,_"1"!=1.0)_} -=== RUN TestAWKGo/{_print_($0!="1",_$0!=1)_} -=== RUN TestAWKGo/{_print_($1!="1",_$1!=1)_} -=== RUN TestAWKGo/BEGIN_{_print_(0<1,_1<1,_2<1,_"12"<"2")_} -=== RUN TestAWKGo/{_print_($1<2)_} -=== RUN TestAWKGo/BEGIN_{_print_(0<=1,_1<=1,_2<=1,_"12"<="2")_} -=== RUN TestAWKGo/{_print_($1<=2)_} -=== RUN TestAWKGo/BEGIN_{_print_(0>1,_1>1,_2>1,_"12">"2")_} -=== RUN TestAWKGo/{_print_($1>2)_} -=== RUN TestAWKGo/BEGIN_{_print_(0>=1,_1>=1,_2>=1,_"12">="2")_} -=== RUN TestAWKGo/{_print_($1>=2)_} -=== RUN TestAWKGo/BEGIN_{_print_1+2,_1+2+3,_1+-2,_-1+2,_"1"+"2",_3+.14_} -=== RUN TestAWKGo/BEGIN_{_print_1-2,_1-2-3,_1-+2,_-1-2,_"1"-"2",_3-.14_} -=== RUN TestAWKGo/BEGIN_{_print_2*3,_2*3*4,_2*-3,_-2*3,_"2"*"3",_3*.14_} -=== RUN TestAWKGo/BEGIN_{_print_2/3,_2/3/4,_2/-3,_-2/3,_"2"/"3",_3/.14_} -=== RUN TestAWKGo/BEGIN_{_print_2%3,_2%3%4,_2%-3,_-2%3,_"2"%"3",_3%.14_} -=== RUN TestAWKGo/BEGIN_{_print_2^3,_2^3^3,_2^-3,_-2^3,_"2"^"3",_3^.14_} -=== RUN TestAWKGo/BEGIN_{_print_1_2,_"x"_"yz",_1+2_3+4_} -=== RUN TestAWKGo/BEGIN_{_print_"food"~/oo/,_"food"~/[oO]+d/,_"food"~"f",_"food"~"F",_"f -=== RUN TestAWKGo/BEGIN_{_print_"food"!~/oo/,_"food"!~/[oO]+d/,_"food"!~"f",_"food"!~"F" -=== RUN TestAWKGo/BEGIN_{_print_1+2*3/4^5%6_7,_(1+2)*3/4^5%6_"7"_} -=== RUN TestAWKGo/BEGIN_{_print_'\"'_'\''_'xy'_"z"_"'"_'\"'_} -=== RUN TestAWKGo/{_print_/foo/_} -=== RUN TestAWKGo//[a-/ -=== RUN TestAWKGo/BEGIN_{_print_"-12"+0,_"+12"+0,_"_\t\r\n7foo"+0,_".5"+0,_"5."+0,_"+."+ -=== RUN TestAWKGo/BEGIN_{_print_"1e3"+0,_"1.2e-1"+0,_"1e+1"+0,_"1e"+0,_"1e+"+0_} -=== RUN TestAWKGo/BEGIN_{_print_-(11102200000000000000000000000000000000_1040000)_}__#_! -=== RUN TestAWKGo/BEGIN_{_printf_"\x0.\x00.\x0A\x10\xff\xFF\x41"_}__#_!awk -=== RUN TestAWKGo/BEGIN_{_printf_"\x1.\x01.\x0A\x10\xff\xFF\x41"_} -=== RUN TestAWKGo/BEGIN_{_printf_"\0\78\7\77\777\0_\141_"_}__#_!awk -=== RUN TestAWKGo/BEGIN_{_printf_"\1\78\7\77\777\1_\141_"_} -=== RUN TestAWKGo/{_print_/x/?"t":"f"_} -=== RUN TestAWKGo/BEGIN_{_print_1?2?3:4:5,_1?0?3:4:5,_0?2?3:4:5_} -=== RUN TestAWKGo/{_print_$0?1:0_} -=== RUN TestAWKGo/{_print_$0?1:0_}#01 -=== RUN TestAWKGo/BEGIN_{_$0="1";_print_($0?1:0)_} -=== RUN TestAWKGo/BEGIN_{_print_0?1:0,_1?1:0,_""?1:0,_"0"?1:0,_"1"?1:0_} -=== RUN TestAWKGo/_BEGIN_{__print_CONVFMT,_1.2345678_""__CONVFMT_=_"%.3g"__print_CONVFMT -=== RUN TestAWKGo/{_print_FNR,_$0_} -=== RUN TestAWKGo/BEGIN_{_print_"|"_FS_"|";_FS=","_}_{_print_$1,_$2_} -=== RUN TestAWKGo/BEGIN_{_print_"|"_FS_"|";_FS="\\."_}_{_print_$1,_$2_} -=== RUN TestAWKGo/BEGIN_{_FS="\\"_}_{_print_$1,_$2_} -=== RUN TestAWKGo/{_print_NF_} -=== RUN TestAWKGo/{_print_NR,_$0_} -=== RUN TestAWKGo/_BEGIN_{__print_OFMT,_1.2345678__OFMT_=_"%.3g"__print_OFMT,_1.234567_} -=== RUN TestAWKGo/BEGIN_{_print_RSTART,_RLENGTH;_RSTART=5;_RLENGTH=42;_print_RSTART,_RLE -=== RUN TestAWKGo/_BEGIN_{__print_SUBSEP__a[1,_2]_=_"onetwo"__print_a[1,_2]__for_(k_in_a -=== RUN TestAWKGo/{_print;_print_$1,_$3,_$NF_} -=== RUN TestAWKGo/{_print_$1,$3;_$2="x";_print;_print_$2_} -=== RUN TestAWKGo/{_print;_$0="x_y_z";_print;_print_$1,_$3_} -=== RUN TestAWKGo/{_print_$1^2_} -=== RUN TestAWKGo/BEGIN_{_$1234567=1_} -=== RUN TestAWKGo/0_in_FS__#_!awk_-_doesn't_flag_this_as_an_error -=== RUN TestAWKGo/BEGIN_{_print_x;_x_=_4;_print_x;_} -=== RUN TestAWKGo/BEGIN_{_a["foo"]=1;_b[2]="x";_k="foo";_print_a[k],_b["2"]_} -=== RUN TestAWKGo/BEGIN_{_s+=5;_print_s;_s-=2;_print_s;_s-=s;_print_s_} -=== RUN TestAWKGo/BEGIN_{_x=2;_x*=x;_print_x;_x*=3;_print_x_} -=== RUN TestAWKGo/BEGIN_{_x=6;_x/=3;_print_x;_x/=x;_print_x;_x/=.6;_print_x_} -=== RUN TestAWKGo/BEGIN_{_x=12;_x%=5;_print_x_} -=== RUN TestAWKGo/BEGIN_{_x=2;_x^=5;_print_x;_x^=0.5;_print_x_} -=== RUN TestAWKGo/{_$2+=10;_print;_$3/=2;_print_} -=== RUN TestAWKGo/BEGIN_{_a[2]_+=_1;_a["2"]_*=_3;_print_a[2]_} -=== RUN TestAWKGo/BEGIN_{_print_x++;_print_x_} -=== RUN TestAWKGo/BEGIN_{_print_x;_print_x++;_print_++x;_print_x_} -=== RUN TestAWKGo/BEGIN_{_print_x;_print_x--;_print_--x;_print_x_} -=== RUN TestAWKGo/BEGIN_{_s++;_s++;_print_s_} -=== RUN TestAWKGo/BEGIN_{_y="_";_--x[y_=_y_y];_print_length(y)_} -=== RUN TestAWKGo/BEGIN_{_x[y++]++;_print_y_} -=== RUN TestAWKGo/BEGIN_{_x[y++]_+=_3;_print_y_} -=== RUN TestAWKGo/BEGIN_{_$(y++)++;_print_y_} -=== RUN TestAWKGo/BEGIN_{_print_sin(0),_sin(0.5),_sin(1),_sin(-1)_} -=== RUN TestAWKGo/BEGIN_{_print_cos(0),_cos(0.5),_cos(1),_cos(-1)_} -=== RUN TestAWKGo/BEGIN_{_print_exp(0),_exp(0.5),_exp(1),_exp(-1)_} -=== RUN TestAWKGo/BEGIN_{_print_log(0),_log(0.5),_log(1)_} -=== RUN TestAWKGo/BEGIN_{_print_log(-1)_}__#_!gawk_-_gawk_prints_warning_for_this_as_wel -=== RUN TestAWKGo/BEGIN_{_print_sqrt(0),_sqrt(2),_sqrt(4)_} -=== RUN TestAWKGo/BEGIN_{_print_int(3.5),_int("1.9"),_int(4),_int(-3.6),_int("x"),_int(" -=== RUN TestAWKGo/BEGIN_{_print_match("food",_"foo"),_RSTART,_RLENGTH_} -=== RUN TestAWKGo/BEGIN_{_print_match("x_food_y",_"fo"),_RSTART,_RLENGTH_} -=== RUN TestAWKGo/BEGIN_{_print_match("x_food_y",_"fox"),_RSTART,_RLENGTH_} -=== RUN TestAWKGo/BEGIN_{_print_match("x_food_y",_/[fod]+/),_RSTART,_RLENGTH_} -=== RUN TestAWKGo/{_print_length,_length(),_length("buzz"),_length("")_} -=== RUN TestAWKGo/BEGIN_{_print_index("foo",_"f"),_index("foo0",_0),_index("foo",_"o"),_ -=== RUN TestAWKGo/BEGIN_{_print_atan2(1,_0.5),_atan2(-1,_0)_} -=== RUN TestAWKGo/BEGIN_{_print_sprintf("%3d",_42)_} -=== RUN TestAWKGo/BEGIN_{_print_sprintf("%d",_12,_34)_} -=== RUN TestAWKGo/BEGIN_{_print_sprintf("%d")_} -=== RUN TestAWKGo/BEGIN_{_print_sprintf("%d",_12,_34)_}#01 -=== RUN TestAWKGo/BEGIN_{_print_sprintf("%_5d",_42)_} -=== RUN TestAWKGo/BEGIN_{_print_substr("food",_1)_} -=== RUN TestAWKGo/BEGIN_{_print_substr("food",_1,_2)_} -=== RUN TestAWKGo/BEGIN_{_print_substr("food",_1,_4)_} -=== RUN TestAWKGo/BEGIN_{_print_substr("food",_1,_8)_} -=== RUN TestAWKGo/BEGIN_{_print_substr("food",_2)_} -=== RUN TestAWKGo/BEGIN_{_print_substr("food",_2,_2)_} -=== RUN TestAWKGo/BEGIN_{_print_substr("food",_2,_3)_} -=== RUN TestAWKGo/BEGIN_{_print_substr("food",_2,_8)_} -=== RUN TestAWKGo/BEGIN_{_print_substr("food",_0,_8)_} -=== RUN TestAWKGo/BEGIN_{_print_substr("food",_-1,_8)_} -=== RUN TestAWKGo/BEGIN_{_print_substr("food",_5,_8)_} -=== RUN TestAWKGo/BEGIN_{_n_=_split("ab_c_d_",_a);_for_(i=1;_i<=n;_i++)_print_a[i]_} -=== RUN TestAWKGo/BEGIN_{_n_=_split("ab,c,d,",_a,_",");_for_(i=1;_i<=n;_i++)_print_a[i]_ -=== RUN TestAWKGo/BEGIN_{_n_=_split("ab,c.d,",_a,_/[,.]/);_for_(i=1;_i<=n;_i++)_print_a[ -=== RUN TestAWKGo/BEGIN_{_n_=_split("1_2",_a);_print_(n,_a[1],_a[2],_a[1]==1,_a[2]==2)_} -=== RUN TestAWKGo/BEGIN_{_x_=_"1.2.3";_print_sub(/\./,_",",_x);_print_x_} -=== RUN TestAWKGo/{_print_sub(/\./,_",");_print_$0_} -=== RUN TestAWKGo/BEGIN_{_x_=_"1.2.3";_print_gsub(/\./,_",",_x);_print_x_} -=== RUN TestAWKGo/{_print_gsub(/\./,_",");_print_$0_} -=== RUN TestAWKGo/{_print_gsub(/[0-9]/,_"(&)");_print_$0_} -=== RUN TestAWKGo/{_print_gsub(/[0-9]+/,_"(&)");_print_$0_} -=== RUN TestAWKGo/{_print_gsub(/[0-9]/,_"\\&");_print_$0_} -=== RUN TestAWKGo/{_print_gsub(/[0-9]/,_"\\z");_print_$0_} -=== RUN TestAWKGo/{_print_gsub("0",_"x\\\\y");_print_$0_}__#_!awk_!gawk_--_our_behaviour -=== RUN TestAWKGo/BEGIN_{_print_tolower("Foo_BaR")_} -=== RUN TestAWKGo/BEGIN_{_print_toupper("Foo_BaR")_} -=== RUN TestAWKGo/_BEGIN_{__srand(1)__a_=_rand();_b_=_rand();_c_=_rand()__srand(1)__x_=_ -=== RUN TestAWKGo/_BEGIN_{__for_(i_=_0;_i_<_1000;_i++)_{___if_(rand()_<_0.5)_n++__}__pri -=== RUN TestAWKGo/BEGIN_{_print_system("echo_foo");_print_system("echo_bar")_}__#_!fuzz -=== RUN TestAWKGo/BEGIN_{_print_system(">&2_echo_error")_}__#_!fuzz -=== RUN TestAWKGo/BEGIN_{_print_0?"t":"f"_} -=== RUN TestAWKGo/BEGIN_{_print_1?"t":"f"_} -=== RUN TestAWKGo/BEGIN_{_print_(1+2)?"t":"f"_} -=== RUN TestAWKGo/BEGIN_{_print_(1+2?"t":"f")_} -=== RUN TestAWKGo/BEGIN_{_print(1_?_x="t"_:_"f");_print_x;_} -=== RUN TestAWKGo/BEGIN_{_a[x];_a=42_} -=== RUN TestAWKGo/BEGIN_{_s=42;_s[x]_} -=== RUN TestAWKGo/BEGIN_{_print_fflush();_print_fflush("")_} -=== RUN TestAWKGo/BEGIN_{_print_"x";_print_fflush();_print_"y";_print_fflush("")_} -=== RUN TestAWKGo/BEGIN_{_if_(1)_printf_"x";_else_printf_"y"_} -=== RUN TestAWKGo/BEGIN_{_printf_"x";_{_printf_"y";_printf_"z"_}_} -=== RUN TestAWKGo/BEGIN_{_f()_} -=== RUN TestAWKGo/function_f()_{}_function_f()_{}_BEGIN_{_} -=== RUN TestAWKGo/BEGIN_{_print_(1,2),(3,4)_} -=== RUN TestAWKGo/BEGIN_{_print_(1,2,(3,4),(5,6))_} ---- PASS: TestAWKGo - --- PASS: TestAWKGo/BEGIN_{_print_"b"_} - --- PASS: TestAWKGo/BEGIN_{_print_"b"_}#01 - --- PASS: TestAWKGo/END_{_print_"e"_} - --- PASS: TestAWKGo/END_{_print_"e"_}#01 - --- PASS: TestAWKGo/BEGIN_{_print_"b"}_END_{_print_"e"_} - --- PASS: TestAWKGo/BEGIN_{_print_"b"}_END_{_print_"e"_}#01 - --- PASS: TestAWKGo/BEGIN_{_print_"b"}_$0_{_print_NR_}_END_{_print_"e"_} - --- PASS: TestAWKGo/BEGIN_{_printf_"x"_};_BEGIN_{_printf_"y"_} - --- PASS: TestAWKGo/$0 - --- PASS: TestAWKGo/{_print_$0_} - --- PASS: TestAWKGo/$1=="foo" - --- PASS: TestAWKGo/$1==42 - --- PASS: TestAWKGo/$1=="42" - --- PASS: TestAWKGo//foo/ - --- PASS: TestAWKGo/NR==2,_NR==4 - --- PASS: TestAWKGo/_NR==2,_NR==4_{_print_$0_}_NR==3,_NR==5_{_print_NR_}_ - --- PASS: TestAWKGo/BEGIN_{_print_"x",_"y"_} - --- PASS: TestAWKGo/BEGIN_{_print_OFS;_OFS_=_",";_print_"x",_"y"_} - --- PASS: TestAWKGo/BEGIN_{_print_ORS;_ORS_=_".";_print_"x",_"y"_} - --- PASS: TestAWKGo/BEGIN_{_print_ORS;_ORS_=_"";_print_"x",_"y"_} - --- PASS: TestAWKGo/{_print;_print_} - --- PASS: TestAWKGo/BEGIN_{_print;_print_} - --- PASS: TestAWKGo/BEGIN_{_printf_"%%_%d_%x_%c_%f_%s",_42,_42,_42,_42,_42_} - --- PASS: TestAWKGo/BEGIN_{_printf_"%3d",_42_} - --- PASS: TestAWKGo/BEGIN_{_printf_"%3s",_"x"_} - --- PASS: TestAWKGo/BEGIN_{_printf_"%d",_12,_34_} - --- PASS: TestAWKGo/BEGIN_{_printf_"%d"_} - --- PASS: TestAWKGo/BEGIN_{_printf_"%c",_0_} - --- PASS: TestAWKGo/BEGIN_{_printf_"%c",_127_} - --- PASS: TestAWKGo/BEGIN_{_printf_"%c",_128_}__#_!gawk - --- PASS: TestAWKGo/BEGIN_{_printf_"%c",_255_}__#_!gawk - --- PASS: TestAWKGo/BEGIN_{_printf_"%c",_256_}__#_!awk_!gawk - --- PASS: TestAWKGo/BEGIN_{_printf_"%c",_"xyz"_} - --- PASS: TestAWKGo/BEGIN_{_printf_"%c",_""_}__#_!awk - --- PASS: TestAWKGo/BEGIN_{_printf_}__#_!awk_-_doesn't_error_on_this - --- PASS: TestAWKGo/BEGIN_{_printf("%%%dd",_4)_} - --- PASS: TestAWKGo/BEGIN_{_if_(1)_print_"t";_} - --- PASS: TestAWKGo/BEGIN_{_if_(0)_print_"t";_} - --- PASS: TestAWKGo/BEGIN_{_if_(1)_print_"t";_else_print_"f"_} - --- PASS: TestAWKGo/BEGIN_{_if_(0)_print_"t";_else_print_"f"_} - --- PASS: TestAWKGo/BEGIN_{_for_(;;)_{_print_"x";_break_}_} - --- PASS: TestAWKGo/BEGIN_{_for_(;;)_{_printf_"%d_",_i;_i++;_if_(i>2)_break;_}_} - --- PASS: TestAWKGo/BEGIN_{_for_(i=5;_;_)_{_printf_"%d_",_i;_i++;_if_(i>8)_break;_}_} - --- PASS: TestAWKGo/BEGIN_{_for_(i=5;_;_i++)_{_printf_"%d_",_i;_if_(i>8)_break;_}_} - --- PASS: TestAWKGo/BEGIN_{_for_(i=5;_i<8;_i++)_{_printf_"%d_",_i_}_} - --- PASS: TestAWKGo/BEGIN_{_for_(i=0;_i<10;_i++)_{_if_(i_<_5)_continue;_printf_"%d_",_i_}_ - --- PASS: TestAWKGo/BEGIN_{_a[1]=1;_a[2]=1;_for_(k_in_a)_{_s++;_break_}_print_s_} - --- PASS: TestAWKGo/BEGIN_{_a[1]=1;_a[2]=1;_a[3]=1;_for_(k_in_a)_{_if_(k==2)_continue;_s++ - --- PASS: TestAWKGo/BEGIN_{_while_(i<3)_{_i++;_s++;_break_}_print_s_} - --- PASS: TestAWKGo/BEGIN_{_while_(i<3)_{_i++;_if_(i==2)_continue;_s++_}_print_s_} - --- PASS: TestAWKGo/BEGIN_{_do_{_i++;_s++;_break_}_while_(i<3);_print_s_} - --- PASS: TestAWKGo/BEGIN_{_do_{_i++;_if_(i==2)_continue;_s++_}_while_(i<3);_print_s_} - --- PASS: TestAWKGo/BEGIN_{_a["x"]_=_3;_a["y"]_=_4;_for_(k_in_a)_x_+=_a[k];_print_x_} - --- PASS: TestAWKGo/BEGIN_{_while_(i_<_5)_{_print_i;_i++_}_} - --- PASS: TestAWKGo/BEGIN_{_do_{_print_i;_i++_}_while_(i_<_5)_} - --- PASS: TestAWKGo/BEGIN_{_for_(i=0;_i<10;_i++);_printf_"x"_} - --- PASS: TestAWKGo/_BEGIN_{__for_(i_=_0;_i_<_1;_i++)_{___for_(j_=_0;_j_<_1;_j++)_{____pri - --- PASS: TestAWKGo/_BEGIN_{__for_(i_=_0;_i_<_1;_i++)_{___for_(j_=_0;_j_<_1;_j++)_{____pri#01 - --- PASS: TestAWKGo/{_if_(NR==2)_next;_print_} - --- PASS: TestAWKGo/BEGIN_{_next_} - --- PASS: TestAWKGo/END_{_next_} - --- PASS: TestAWKGo/BEGIN_{_a["x"]_=_3;_print_"x"_in_a,_"y"_in_a_} - --- PASS: TestAWKGo/BEGIN_{_a["x"]_=_3;_a["y"]_=_4;_delete_a["x"];_for_(k_in_a)_print_k,_a - --- PASS: TestAWKGo/BEGIN_{_a["x"]_=_3;_a["y"]_=_4;_for_(k_in_a)_delete_a[k];_for_(k_in_a) - --- PASS: TestAWKGo/BEGIN_{_a[]_} - --- PASS: TestAWKGo/BEGIN_{_delete_a[]_} - --- PASS: TestAWKGo/BEGIN_{_a["x"]_=_3;_a["y"]_=_4;_delete_a;_for_(k_in_a)_print_k,_a[k]_} - --- PASS: TestAWKGo/BEGIN_{_print_!42,_!1,_!0,_!!42,_!!1,_!!0_} - --- PASS: TestAWKGo/BEGIN_{_print_!42,_!1,_!0,_!!42,_!!1,_!!0_}#01 - --- PASS: TestAWKGo/BEGIN_{_print_+4,_+"3",_+0,_+-3,_-3,_-_-4,_-"3"_} - --- PASS: TestAWKGo/BEGIN_{_$0="1";_print_!$0_} - --- PASS: TestAWKGo/{_print_!$0_} - --- PASS: TestAWKGo/{_print_!$0_}#01 - --- PASS: TestAWKGo/!seen[$0]++ - --- PASS: TestAWKGo/!seen[$0]-- - --- PASS: TestAWKGo/BEGIN_{_print_(1==1,_1==0,_"1"==1,_"1"==1.0)_} - --- PASS: TestAWKGo/{_print_($0=="1",_$0==1)_} - --- PASS: TestAWKGo/{_print_($1=="1",_$1==1)_} - --- PASS: TestAWKGo/BEGIN_{_print_(1!=1,_1!=0,_"1"!=1,_"1"!=1.0)_} - --- PASS: TestAWKGo/{_print_($0!="1",_$0!=1)_} - --- PASS: TestAWKGo/{_print_($1!="1",_$1!=1)_} - --- PASS: TestAWKGo/BEGIN_{_print_(0<1,_1<1,_2<1,_"12"<"2")_} - --- PASS: TestAWKGo/{_print_($1<2)_} - --- PASS: TestAWKGo/BEGIN_{_print_(0<=1,_1<=1,_2<=1,_"12"<="2")_} - --- PASS: TestAWKGo/{_print_($1<=2)_} - --- PASS: TestAWKGo/BEGIN_{_print_(0>1,_1>1,_2>1,_"12">"2")_} - --- PASS: TestAWKGo/{_print_($1>2)_} - --- PASS: TestAWKGo/BEGIN_{_print_(0>=1,_1>=1,_2>=1,_"12">="2")_} - --- PASS: TestAWKGo/{_print_($1>=2)_} - --- PASS: TestAWKGo/BEGIN_{_print_1+2,_1+2+3,_1+-2,_-1+2,_"1"+"2",_3+.14_} - --- PASS: TestAWKGo/BEGIN_{_print_1-2,_1-2-3,_1-+2,_-1-2,_"1"-"2",_3-.14_} - --- PASS: TestAWKGo/BEGIN_{_print_2*3,_2*3*4,_2*-3,_-2*3,_"2"*"3",_3*.14_} - --- PASS: TestAWKGo/BEGIN_{_print_2/3,_2/3/4,_2/-3,_-2/3,_"2"/"3",_3/.14_} - --- PASS: TestAWKGo/BEGIN_{_print_2%3,_2%3%4,_2%-3,_-2%3,_"2"%"3",_3%.14_} - --- PASS: TestAWKGo/BEGIN_{_print_2^3,_2^3^3,_2^-3,_-2^3,_"2"^"3",_3^.14_} - --- PASS: TestAWKGo/BEGIN_{_print_1_2,_"x"_"yz",_1+2_3+4_} - --- PASS: TestAWKGo/BEGIN_{_print_"food"~/oo/,_"food"~/[oO]+d/,_"food"~"f",_"food"~"F",_"f - --- PASS: TestAWKGo/BEGIN_{_print_"food"!~/oo/,_"food"!~/[oO]+d/,_"food"!~"f",_"food"!~"F" - --- PASS: TestAWKGo/BEGIN_{_print_1+2*3/4^5%6_7,_(1+2)*3/4^5%6_"7"_} - --- PASS: TestAWKGo/BEGIN_{_print_'\"'_'\''_'xy'_"z"_"'"_'\"'_} - --- PASS: TestAWKGo/{_print_/foo/_} - --- PASS: TestAWKGo//[a-/ - --- PASS: TestAWKGo/BEGIN_{_print_"-12"+0,_"+12"+0,_"_\t\r\n7foo"+0,_".5"+0,_"5."+0,_"+."+ - --- PASS: TestAWKGo/BEGIN_{_print_"1e3"+0,_"1.2e-1"+0,_"1e+1"+0,_"1e"+0,_"1e+"+0_} - --- PASS: TestAWKGo/BEGIN_{_print_-(11102200000000000000000000000000000000_1040000)_}__#_! - --- PASS: TestAWKGo/BEGIN_{_printf_"\x0.\x00.\x0A\x10\xff\xFF\x41"_}__#_!awk - --- PASS: TestAWKGo/BEGIN_{_printf_"\x1.\x01.\x0A\x10\xff\xFF\x41"_} - --- PASS: TestAWKGo/BEGIN_{_printf_"\0\78\7\77\777\0_\141_"_}__#_!awk - --- PASS: TestAWKGo/BEGIN_{_printf_"\1\78\7\77\777\1_\141_"_} - --- PASS: TestAWKGo/{_print_/x/?"t":"f"_} - --- PASS: TestAWKGo/BEGIN_{_print_1?2?3:4:5,_1?0?3:4:5,_0?2?3:4:5_} - --- PASS: TestAWKGo/{_print_$0?1:0_} - --- PASS: TestAWKGo/{_print_$0?1:0_}#01 - --- PASS: TestAWKGo/BEGIN_{_$0="1";_print_($0?1:0)_} - --- PASS: TestAWKGo/BEGIN_{_print_0?1:0,_1?1:0,_""?1:0,_"0"?1:0,_"1"?1:0_} - --- PASS: TestAWKGo/_BEGIN_{__print_CONVFMT,_1.2345678_""__CONVFMT_=_"%.3g"__print_CONVFMT - --- PASS: TestAWKGo/{_print_FNR,_$0_} - --- PASS: TestAWKGo/BEGIN_{_print_"|"_FS_"|";_FS=","_}_{_print_$1,_$2_} - --- PASS: TestAWKGo/BEGIN_{_print_"|"_FS_"|";_FS="\\."_}_{_print_$1,_$2_} - --- PASS: TestAWKGo/BEGIN_{_FS="\\"_}_{_print_$1,_$2_} - --- PASS: TestAWKGo/{_print_NF_} - --- PASS: TestAWKGo/{_print_NR,_$0_} - --- PASS: TestAWKGo/_BEGIN_{__print_OFMT,_1.2345678__OFMT_=_"%.3g"__print_OFMT,_1.234567_} - --- PASS: TestAWKGo/BEGIN_{_print_RSTART,_RLENGTH;_RSTART=5;_RLENGTH=42;_print_RSTART,_RLE - --- PASS: TestAWKGo/_BEGIN_{__print_SUBSEP__a[1,_2]_=_"onetwo"__print_a[1,_2]__for_(k_in_a - --- PASS: TestAWKGo/{_print;_print_$1,_$3,_$NF_} - --- PASS: TestAWKGo/{_print_$1,$3;_$2="x";_print;_print_$2_} - --- PASS: TestAWKGo/{_print;_$0="x_y_z";_print;_print_$1,_$3_} - --- PASS: TestAWKGo/{_print_$1^2_} - --- PASS: TestAWKGo/BEGIN_{_$1234567=1_} - --- PASS: TestAWKGo/0_in_FS__#_!awk_-_doesn't_flag_this_as_an_error - --- PASS: TestAWKGo/BEGIN_{_print_x;_x_=_4;_print_x;_} - --- PASS: TestAWKGo/BEGIN_{_a["foo"]=1;_b[2]="x";_k="foo";_print_a[k],_b["2"]_} - --- PASS: TestAWKGo/BEGIN_{_s+=5;_print_s;_s-=2;_print_s;_s-=s;_print_s_} - --- PASS: TestAWKGo/BEGIN_{_x=2;_x*=x;_print_x;_x*=3;_print_x_} - --- PASS: TestAWKGo/BEGIN_{_x=6;_x/=3;_print_x;_x/=x;_print_x;_x/=.6;_print_x_} - --- PASS: TestAWKGo/BEGIN_{_x=12;_x%=5;_print_x_} - --- PASS: TestAWKGo/BEGIN_{_x=2;_x^=5;_print_x;_x^=0.5;_print_x_} - --- PASS: TestAWKGo/{_$2+=10;_print;_$3/=2;_print_} - --- PASS: TestAWKGo/BEGIN_{_a[2]_+=_1;_a["2"]_*=_3;_print_a[2]_} - --- PASS: TestAWKGo/BEGIN_{_print_x++;_print_x_} - --- PASS: TestAWKGo/BEGIN_{_print_x;_print_x++;_print_++x;_print_x_} - --- PASS: TestAWKGo/BEGIN_{_print_x;_print_x--;_print_--x;_print_x_} - --- PASS: TestAWKGo/BEGIN_{_s++;_s++;_print_s_} - --- PASS: TestAWKGo/BEGIN_{_y="_";_--x[y_=_y_y];_print_length(y)_} - --- PASS: TestAWKGo/BEGIN_{_x[y++]++;_print_y_} - --- PASS: TestAWKGo/BEGIN_{_x[y++]_+=_3;_print_y_} - --- PASS: TestAWKGo/BEGIN_{_$(y++)++;_print_y_} - --- PASS: TestAWKGo/BEGIN_{_print_sin(0),_sin(0.5),_sin(1),_sin(-1)_} - --- PASS: TestAWKGo/BEGIN_{_print_cos(0),_cos(0.5),_cos(1),_cos(-1)_} - --- PASS: TestAWKGo/BEGIN_{_print_exp(0),_exp(0.5),_exp(1),_exp(-1)_} - --- PASS: TestAWKGo/BEGIN_{_print_log(0),_log(0.5),_log(1)_} - --- PASS: TestAWKGo/BEGIN_{_print_log(-1)_}__#_!gawk_-_gawk_prints_warning_for_this_as_wel - --- PASS: TestAWKGo/BEGIN_{_print_sqrt(0),_sqrt(2),_sqrt(4)_} - --- PASS: TestAWKGo/BEGIN_{_print_int(3.5),_int("1.9"),_int(4),_int(-3.6),_int("x"),_int(" - --- PASS: TestAWKGo/BEGIN_{_print_match("food",_"foo"),_RSTART,_RLENGTH_} - --- PASS: TestAWKGo/BEGIN_{_print_match("x_food_y",_"fo"),_RSTART,_RLENGTH_} - --- PASS: TestAWKGo/BEGIN_{_print_match("x_food_y",_"fox"),_RSTART,_RLENGTH_} - --- PASS: TestAWKGo/BEGIN_{_print_match("x_food_y",_/[fod]+/),_RSTART,_RLENGTH_} - --- PASS: TestAWKGo/{_print_length,_length(),_length("buzz"),_length("")_} - --- PASS: TestAWKGo/BEGIN_{_print_index("foo",_"f"),_index("foo0",_0),_index("foo",_"o"),_ - --- PASS: TestAWKGo/BEGIN_{_print_atan2(1,_0.5),_atan2(-1,_0)_} - --- PASS: TestAWKGo/BEGIN_{_print_sprintf("%3d",_42)_} - --- PASS: TestAWKGo/BEGIN_{_print_sprintf("%d",_12,_34)_} - --- PASS: TestAWKGo/BEGIN_{_print_sprintf("%d")_} - --- PASS: TestAWKGo/BEGIN_{_print_sprintf("%d",_12,_34)_}#01 - --- PASS: TestAWKGo/BEGIN_{_print_sprintf("%_5d",_42)_} - --- PASS: TestAWKGo/BEGIN_{_print_substr("food",_1)_} - --- PASS: TestAWKGo/BEGIN_{_print_substr("food",_1,_2)_} - --- PASS: TestAWKGo/BEGIN_{_print_substr("food",_1,_4)_} - --- PASS: TestAWKGo/BEGIN_{_print_substr("food",_1,_8)_} - --- PASS: TestAWKGo/BEGIN_{_print_substr("food",_2)_} - --- PASS: TestAWKGo/BEGIN_{_print_substr("food",_2,_2)_} - --- PASS: TestAWKGo/BEGIN_{_print_substr("food",_2,_3)_} - --- PASS: TestAWKGo/BEGIN_{_print_substr("food",_2,_8)_} - --- PASS: TestAWKGo/BEGIN_{_print_substr("food",_0,_8)_} - --- PASS: TestAWKGo/BEGIN_{_print_substr("food",_-1,_8)_} - --- PASS: TestAWKGo/BEGIN_{_print_substr("food",_5,_8)_} - --- PASS: TestAWKGo/BEGIN_{_n_=_split("ab_c_d_",_a);_for_(i=1;_i<=n;_i++)_print_a[i]_} - --- PASS: TestAWKGo/BEGIN_{_n_=_split("ab,c,d,",_a,_",");_for_(i=1;_i<=n;_i++)_print_a[i]_ - --- PASS: TestAWKGo/BEGIN_{_n_=_split("ab,c.d,",_a,_/[,.]/);_for_(i=1;_i<=n;_i++)_print_a[ - --- PASS: TestAWKGo/BEGIN_{_n_=_split("1_2",_a);_print_(n,_a[1],_a[2],_a[1]==1,_a[2]==2)_} - --- PASS: TestAWKGo/BEGIN_{_x_=_"1.2.3";_print_sub(/\./,_",",_x);_print_x_} - --- PASS: TestAWKGo/{_print_sub(/\./,_",");_print_$0_} - --- PASS: TestAWKGo/BEGIN_{_x_=_"1.2.3";_print_gsub(/\./,_",",_x);_print_x_} - --- PASS: TestAWKGo/{_print_gsub(/\./,_",");_print_$0_} - --- PASS: TestAWKGo/{_print_gsub(/[0-9]/,_"(&)");_print_$0_} - --- PASS: TestAWKGo/{_print_gsub(/[0-9]+/,_"(&)");_print_$0_} - --- PASS: TestAWKGo/{_print_gsub(/[0-9]/,_"\\&");_print_$0_} - --- PASS: TestAWKGo/{_print_gsub(/[0-9]/,_"\\z");_print_$0_} - --- PASS: TestAWKGo/{_print_gsub("0",_"x\\\\y");_print_$0_}__#_!awk_!gawk_--_our_behaviour - --- PASS: TestAWKGo/BEGIN_{_print_tolower("Foo_BaR")_} - --- PASS: TestAWKGo/BEGIN_{_print_toupper("Foo_BaR")_} - --- PASS: TestAWKGo/_BEGIN_{__srand(1)__a_=_rand();_b_=_rand();_c_=_rand()__srand(1)__x_=_ - --- PASS: TestAWKGo/_BEGIN_{__for_(i_=_0;_i_<_1000;_i++)_{___if_(rand()_<_0.5)_n++__}__pri - --- PASS: TestAWKGo/BEGIN_{_print_system("echo_foo");_print_system("echo_bar")_}__#_!fuzz - --- PASS: TestAWKGo/BEGIN_{_print_system(">&2_echo_error")_}__#_!fuzz - --- PASS: TestAWKGo/BEGIN_{_print_0?"t":"f"_} - --- PASS: TestAWKGo/BEGIN_{_print_1?"t":"f"_} - --- PASS: TestAWKGo/BEGIN_{_print_(1+2)?"t":"f"_} - --- PASS: TestAWKGo/BEGIN_{_print_(1+2?"t":"f")_} - --- PASS: TestAWKGo/BEGIN_{_print(1_?_x="t"_:_"f");_print_x;_} - --- PASS: TestAWKGo/BEGIN_{_a[x];_a=42_} - --- PASS: TestAWKGo/BEGIN_{_s=42;_s[x]_} - --- PASS: TestAWKGo/BEGIN_{_print_fflush();_print_fflush("")_} - --- PASS: TestAWKGo/BEGIN_{_print_"x";_print_fflush();_print_"y";_print_fflush("")_} - --- PASS: TestAWKGo/BEGIN_{_if_(1)_printf_"x";_else_printf_"y"_} - --- PASS: TestAWKGo/BEGIN_{_printf_"x";_{_printf_"y";_printf_"z"_}_} - --- PASS: TestAWKGo/BEGIN_{_f()_} - --- PASS: TestAWKGo/function_f()_{}_function_f()_{}_BEGIN_{_} - --- PASS: TestAWKGo/BEGIN_{_print_(1,2),(3,4)_} - --- PASS: TestAWKGo/BEGIN_{_print_(1,2,(3,4),(5,6))_} -PASS -ok github.com/benhoyt/goawk/awkgo diff --git a/awkgo/typer.go b/awkgo/typer.go deleted file mode 100644 index f510c406..00000000 --- a/awkgo/typer.go +++ /dev/null @@ -1,374 +0,0 @@ -// AWKGo: walk parse tree and determine expression and variable types - -package main - -import ( - "github.com/benhoyt/goawk/internal/ast" - . "github.com/benhoyt/goawk/lexer" - "github.com/benhoyt/goawk/parser" -) - -// typer walks the parse tree and builds a mappings of variables and -// expressions to their types. -type typer struct { - globals map[string]valueType - scalarRefs map[string]bool - arrayRefs map[string]bool - exprs map[ast.Expr]valueType - funcName string // function name if inside a func, else "" - nextUsed bool - oFSRSChanged bool -} - -func newTyper() *typer { - t := &typer{ - globals: make(map[string]valueType), - scalarRefs: make(map[string]bool), - arrayRefs: make(map[string]bool), - exprs: make(map[ast.Expr]valueType), - } - t.globals["FS"] = typeStr - t.globals["OFS"] = typeStr - t.globals["ORS"] = typeStr - t.globals["OFMT"] = typeStr - t.globals["CONVFMT"] = typeStr - t.globals["RSTART"] = typeNum - t.globals["RLENGTH"] = typeNum - t.globals["SUBSEP"] = typeStr - return t -} - -func (t *typer) program(prog *parser.Program) { - for _, stmts := range prog.Begin { - t.stmts(stmts) - } - t.actions(prog.Actions) - for _, stmts := range prog.End { - t.stmts(stmts) - } - for range prog.Functions { - panic(errorf("functions not yet supported")) - } - - for name := range t.scalarRefs { - if t.globals[name] == typeUnknown { - panic(errorf("type of %q not known; need assignment?", name)) - } - } - for name := range t.arrayRefs { - if t.globals[name] == typeUnknown { - panic(errorf("type of array %q not known; need array assignment?", name)) - } - } -} - -func (t *typer) stmts(stmts ast.Stmts) { - for _, stmt := range stmts { - t.stmt(stmt) - } -} - -func (t *typer) actions(actions []ast.Action) { - for _, action := range actions { - for _, e := range action.Pattern { - t.expr(e) - } - t.stmts(action.Stmts) - } -} - -func (t *typer) stmt(stmt ast.Stmt) { - switch s := stmt.(type) { - case *ast.PrintStmt: - for _, arg := range s.Args { - t.expr(arg) - } - if s.Dest != nil { - t.expr(s.Dest) - } - - case *ast.PrintfStmt: - for _, arg := range s.Args { - t.expr(arg) - } - if s.Dest != nil { - t.expr(s.Dest) - } - - case *ast.ExprStmt: - t.expr(s.Expr) - - case *ast.IfStmt: - t.expr(s.Cond) - t.stmts(s.Body) - t.stmts(s.Else) - - case *ast.ForStmt: - if s.Pre != nil { - t.stmt(s.Pre) - } - if s.Cond != nil { - t.expr(s.Cond) - } - if s.Post != nil { - t.stmt(s.Post) - } - t.stmts(s.Body) - - case *ast.ForInStmt: - t.setType(s.Var.Name, typeStr) - t.stmts(s.Body) - - case *ast.WhileStmt: - t.expr(s.Cond) - t.stmts(s.Body) - - case *ast.DoWhileStmt: - t.stmts(s.Body) - t.expr(s.Cond) - - case *ast.BreakStmt, *ast.ContinueStmt: - return - - case *ast.NextStmt: - if t.funcName != "" { - panic(errorf(`"next" inside a function not yet supported`)) - } - t.nextUsed = true - return - - case *ast.ExitStmt: - if s.Status != nil { - t.expr(s.Status) - } - - case *ast.DeleteStmt: - for _, index := range s.Index { - t.expr(index) - } - - case *ast.ReturnStmt: - if s.Value != nil { - t.expr(s.Value) - } - - case *ast.BlockStmt: - t.stmts(s.Body) - - default: - panic(errorf("unexpected statement type %T", stmt)) - } -} - -func (t *typer) setType(name string, typ valueType) { - if t.globals[name] == typ { - return - } - if t.globals[name] != typeUnknown { - panic(errorf("variable %q already set to %s, can't set to %s", - name, t.globals[name], typ)) - } - if typ != typeUnknown { - t.globals[name] = typ - } -} - -func (t *typer) expr(expr ast.Expr) (typ valueType) { - defer func() { - if typ != typeUnknown { - t.exprs[expr] = typ - } - }() - - switch e := expr.(type) { - case *ast.FieldExpr: - t.expr(e.Index) - return typeStr - - case *ast.UnaryExpr: - t.expr(e.Value) - return typeNum - - case *ast.BinaryExpr: - t.expr(e.Left) - t.expr(e.Right) - if e.Op == CONCAT { - return typeStr - } - return typeNum - - case *ast.ArrayExpr: - return typeUnknown - - case *ast.InExpr: - for _, index := range e.Index { - t.expr(index) - } - t.expr(e.Array) - return typeNum - - case *ast.CondExpr: - t.expr(e.Cond) - trueType := t.expr(e.True) - falseType := t.expr(e.False) - if trueType != falseType { - panic(errorf("both branches of ?: must yield same type (first is %s, second is %s)", - trueType, falseType)) - } - return trueType - - case *ast.NumExpr: - return typeNum - - case *ast.StrExpr: - return typeStr - - case *ast.RegExpr: - return typeNum - - case *ast.VarExpr: - switch e.Scope { - case ast.ScopeSpecial: - return t.specialType(e.Name, e.Index) - case ast.ScopeGlobal: - t.scalarRefs[e.Name] = true - return t.globals[e.Name] - default: - panic(errorf("unexpected scope %v", e.Scope)) - } - - case *ast.IndexExpr: - t.arrayRefs[e.Array.Name] = true - t.expr(e.Array) - for _, index := range e.Index { - t.expr(index) - } - switch t.globals[e.Array.Name] { - case typeArrayStr: - return typeStr - case typeArrayNum: - return typeNum - } - return typeUnknown - - case *ast.AssignExpr: - rightType := t.expr(e.Right) - switch left := e.Left.(type) { - case *ast.VarExpr: - // x = right - t.setType(left.Name, rightType) - if left.Name == "OFS" || left.Name == "ORS" { - t.oFSRSChanged = true - } - case *ast.IndexExpr: - // m[k] = right - switch rightType { - case typeStr: - t.setType(left.Array.Name, typeArrayStr) - case typeNum: - t.setType(left.Array.Name, typeArrayNum) - } - case *ast.FieldExpr: - // $1 = right - } - t.expr(e.Left) - return rightType - - case *ast.AugAssignExpr: - t.expr(e.Right) - switch left := e.Left.(type) { - case *ast.VarExpr: - // x += right - t.setType(left.Name, typeNum) - if left.Name == "OFS" || left.Name == "ORS" { - t.oFSRSChanged = true - } - case *ast.IndexExpr: - // m[k] += right - t.setType(left.Array.Name, typeArrayNum) - case *ast.FieldExpr: - // $1 += right - } - t.expr(e.Left) - return typeNum - - case *ast.IncrExpr: - switch left := e.Expr.(type) { - case *ast.VarExpr: - // x++ - t.setType(left.Name, typeNum) - if left.Name == "OFS" || left.Name == "ORS" { - t.oFSRSChanged = true - } - case *ast.IndexExpr: - // m[k]++ - t.setType(left.Array.Name, typeArrayNum) - case *ast.FieldExpr: - // $1++ - } - t.expr(e.Expr) - return typeNum - - case *ast.CallExpr: - switch e.Func { - case F_SPLIT: - // split's second arg is an array arg - t.expr(e.Args[0]) - arrayExpr := e.Args[1].(*ast.ArrayExpr) - if t.globals[arrayExpr.Name] != typeUnknown && t.globals[arrayExpr.Name] != typeArrayStr { - panic(errorf("%q already set to %s, can't use as %s in split()", - arrayExpr.Name, t.globals[arrayExpr.Name], typeArrayStr)) - } - t.globals[arrayExpr.Name] = typeArrayStr - if len(e.Args) == 3 { - t.expr(e.Args[2]) - } - return typeNum - case F_SUB, F_GSUB: - t.expr(e.Args[0]) - t.expr(e.Args[1]) - if len(e.Args) == 3 { - // sub and gsub's third arg is actually an lvalue - switch left := e.Args[2].(type) { - case *ast.VarExpr: - t.setType(left.Name, typeStr) - case *ast.IndexExpr: - t.setType(left.Array.Name, typeArrayStr) - } - } - return typeNum - } - for _, arg := range e.Args { - t.expr(arg) - } - switch e.Func { - case F_ATAN2, F_CLOSE, F_COS, F_EXP, F_FFLUSH, F_INDEX, F_INT, F_LENGTH, - F_LOG, F_MATCH, F_RAND, F_SIN, F_SQRT, F_SRAND, F_SYSTEM: - return typeNum - case F_SPRINTF, F_SUBSTR, F_TOLOWER, F_TOUPPER: - return typeStr - default: - panic(errorf("unexpected function %s", e.Func)) - } - - case *ast.UserCallExpr: - panic(errorf("functions not yet supported")) - - case *ast.GetlineExpr: - return typeNum - - default: - panic(errorf("unexpected expression type %T", expr)) - } -} - -func (t *typer) specialType(name string, index int) valueType { - switch index { - case ast.V_NF, ast.V_NR, ast.V_RLENGTH, ast.V_RSTART, ast.V_FNR, ast.V_ARGC: - return typeNum - case ast.V_CONVFMT, ast.V_FILENAME, ast.V_FS, ast.V_OFMT, ast.V_OFS, ast.V_ORS, ast.V_RS, ast.V_SUBSEP: - return typeStr - default: - panic(errorf("unexpected special variable %s", name)) - } -}