Repository: benhoyt/goawk (go)

Issue:
{} behaves like 1, END{} doesn't exhaust input
See:
```
$ seq 3 | goawk '{}'
1
2
3
$ seq 3 | { goawk 'END{}'; wc -l;}
3
```
According to POSIX the first command shouldn't print anything, and the second should print 0.

Pull request description:
Fix evaluation of empty {} and END{}
- '{}' shouldn't be equivalent to '{ print $0 }'
- 'END {}' should consume the input

Thanks @oguz-ismail

Fixes #228

Patch:
diff --git a/.gitignore b/.gitignore
index dbe1062..35dd5b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,4 @@ interp/testdata
 scripts/csvbench/bin
 scripts/csvbench/count.csv
 /default.pgo
+/goawk-versions
diff --git a/internal/compiler/compiler.go b/internal/compiler/compiler.go
index 26fc01f..d7beb89 100644
--- a/internal/compiler/compiler.go
+++ b/internal/compiler/compiler.go
@@ -135,6 +135,13 @@ func Compile(resolved *resolver.ResolvedProgram) (compiledProg *Program, err err
 			c := compiler{resolved: resolved, program: p, indexes: indexes}
 			c.stmts(action.Stmts)
 			body = c.finish()
+		} else if len(action.Pattern) == 0 {
+			// No action and no pattern (a bare '{}') should have at least one
+			// opcode, otherwise interpreter will treat it as no action, which
+			// would be evaluated as '{ print $0 }'.
+			c := compiler{resolved: resolved, program: p, indexes: indexes}
+			c.add(Nop)
+			body = c.finish()
 		}
 		p.Actions = append(p.Actions, Action{
 			Pattern: pattern,
@@ -145,7 +152,12 @@ func Compile(resolved *resolver.ResolvedProgram) (compiledProg *Program, err err
 	// Compile END blocks.
 	for _, stmts := range resolved.End {
 		c := compiler{resolved: resolved, program: p, indexes: indexes}
-		c.stmts(stmts)
+		if len(stmts) > 0 {
+			c.stmts(stmts)
+		} else {
+			// Ensure empty 'END {}' isn't treated as no END.
+			c.add(Nop)
+		}
 		p.End = append(p.End, c.finish()...)
 	}
 


Test patch:
diff --git a/interp/interp_test.go b/interp/interp_test.go
index c792d72..124ca30 100644
--- a/interp/interp_test.go
+++ b/interp/interp_test.go
@@ -50,6 +50,7 @@ var interpTests = []interpTest{
 	{`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", "", ""},
+	{`{}`, "1\n2\n3\n", "", "", ""}, // issue #228
 
 	// Patterns
 	{`$0`, "foo\n\nbar", "foo\nbar\n", "", ""},
@@ -1753,6 +1754,32 @@ func TestExit(t *testing.T) {
 	}
 }
 
+// A bare 'END {}' should consume the input (issue #228).
+func TestENDConsumesInput(t *testing.T) {
+	prog, err := parser.ParseProgram([]byte("END {}"), nil)
+	if err != nil {
+		t.Fatalf("error parsing: %v", err)
+	}
+	input := strings.NewReader("1\n2\n3\n")
+	outBuf := &bytes.Buffer{}
+	config := &interp.Config{
+		Stdin:  input,
+		Output: outBuf,
+	}
+	_, err = interp.ExecProgram(prog, config)
+	if err != nil {
+		t.Fatalf("error interpreting: %v", err)
+	}
+	if outBuf.String() != "" {
+		t.Fatalf("output expected/got:\n%q\n%q", "", outBuf.String())
+	}
+	// Ensure there's no input remaining to be read.
+	b, err := io.ReadAll(input)
+	if string(b) != "" {
+		t.Fatalf("input expected/got:\n%q\n%q", "", string(b))
+	}
+}
+
 type csvTest struct {
 	src       string
 	in        string