Repository: benhoyt/goawk (go)

Issue:
Error parsing $++a
Possibly related to https://github.com/benhoyt/goawk/issues/79, but opening a separate issue for visibility: GoAWK errors when parsing `$++a`. Compare:

```
$ echo -e '1 2 3\na b c\nd e f' | gawk '{ print $++a }'
1
b
f
$ echo -e '1 2 3\na b c\nd e f' | goawk '{ print $++a }'
<cmdline>:1:10: expected expression instead of ++
{ print $++a }
         ^
```

Pull request description:
Make constructs like $++lvalue not an error
This matches gawk/mawk/original-awk behaviour.

Add tests, also add test for ++rvalue syntax error.

Fixes #167

Patch:
diff --git a/parser/parser.go b/parser/parser.go
index e8fd6d3..b863df5 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -664,7 +664,7 @@ func (p *parser) mul() ast.Expr {
 
 func (p *parser) pow() ast.Expr {
 	// Note that pow (expr ^ expr) is right-associative
-	expr := p.preIncr()
+	expr := p.postIncr()
 	if p.tok == POW {
 		p.next()
 		right := p.pow()
@@ -673,20 +673,6 @@ func (p *parser) pow() ast.Expr {
 	return expr
 }
 
-func (p *parser) preIncr() ast.Expr {
-	if p.tok == INCR || p.tok == DECR {
-		op := p.tok
-		p.next()
-		exprPos := p.pos
-		expr := p.preIncr()
-		if !ast.IsLValue(expr) {
-			panic(ast.PosErrorf(exprPos, "expected lvalue after ++ or --"))
-		}
-		return &ast.IncrExpr{expr, op, true}
-	}
-	return p.postIncr()
-}
-
 func (p *parser) postIncr() ast.Expr {
 	expr := p.primary()
 	if (p.tok == INCR || p.tok == DECR) && ast.IsLValue(expr) {
@@ -724,6 +710,15 @@ func (p *parser) primary() ast.Expr {
 		op := p.tok
 		p.next()
 		return &ast.UnaryExpr{op, p.pow()}
+	case INCR, DECR:
+		op := p.tok
+		p.next()
+		exprPos := p.pos
+		expr := p.optionalLValue()
+		if expr == nil {
+			panic(ast.PosErrorf(exprPos, "expected lvalue after %s", op))
+		}
+		return &ast.IncrExpr{expr, op, true}
 	case NAME:
 		name := p.val
 		namePos := p.pos


Test patch:
diff --git a/goawk_test.go b/goawk_test.go
index 9411a34..970ca92 100644
--- a/goawk_test.go
+++ b/goawk_test.go
@@ -430,7 +430,7 @@ func TestCommandLine(t *testing.T) {
 		{[]string{"`"}, "", "", "<cmdline>:1:1: unexpected char\n`\n^"},
 		{[]string{"BEGIN {\n\tx*;\n}"}, "", "", "<cmdline>:2:4: expected expression instead of ;\n    x*;\n      ^"},
 		{[]string{"BEGIN {\n\tx*\r\n}"}, "", "", "<cmdline>:2:4: expected expression instead of <newline>\n    x*\n      ^"},
-		{[]string{"-f", "-"}, "\n ++", "", "<stdin>:2:4: expected expression instead of <newline>\n ++\n   ^"},
+		{[]string{"-f", "-"}, "\n $", "", "<stdin>:2:3: expected expression instead of <newline>\n $\n  ^"},
 		{[]string{"-f", "testdata/parseerror/good.awk", "-f", "testdata/parseerror/bad.awk"},
 			"", "", "testdata/parseerror/bad.awk:2:3: expected expression instead of <newline>\nx*\n  ^"},
 		{[]string{"-f", "testdata/parseerror/bad.awk", "-f", "testdata/parseerror/good.awk"},
@@ -467,7 +467,10 @@ func runGoAWK(args []string, stdin string) (stdout, stderr string, err error) {
 }
 
 func runAWKs(t *testing.T, testArgs []string, testStdin, testOutput, testError string) {
+	t.Helper()
+
 	t.Run("awk", func(t *testing.T) {
+		t.Helper()
 		var args []string
 		if strings.Contains(awkExe, "gawk") {
 			args = append(args, "--posix")
@@ -496,6 +499,7 @@ func runAWKs(t *testing.T, testArgs []string, testStdin, testOutput, testError s
 	})
 
 	t.Run("goawk", func(t *testing.T) {
+		t.Helper()
 		stdout, stderr, err := runGoAWK(testArgs, testStdin)
 		if err != nil {
 			stderr = strings.TrimSpace(stderr)
diff --git a/interp/interp_test.go b/interp/interp_test.go
index bf8d39b..ed4fc05 100644
--- a/interp/interp_test.go
+++ b/interp/interp_test.go
@@ -484,6 +484,8 @@ BEGIN {
 	{`function inc(x) { x++; return x }  BEGIN { print inc(3) }`, "", "4\n", "", ""},
 	{`function inca(a, k) { a[k]++ }  BEGIN { b["x"]=7; inca(b, "x"); print b["x"] }`, "", "8\n", "", ""},
 	{`BEGIN { NF++; print NF }`, "", "1\n", "", ""},
+	{"{ print $++n; print $--n }", "x y", "x\nx y\n", "", ""},
+	{"{ print $++a }", "1 2 3\na b c\nd e f\n", "1\nb\nf\n", "", ""},
 
 	// Builtin functions
 	{`BEGIN { print sin(0), sin(0.5), sin(1), sin(-1) }`, "", "0 0.479426 0.841471 -0.841471\n", "", ""},
@@ -840,6 +842,7 @@ BEGIN { foo(5); bar(10) }
 	{`BEGIN { print "\x" }  # !gawk`, "", "", "parse error at 1:18: 1 or 2 hex digits expected", ""},
 	{`BEGIN { print 1&*2 }`, "", "", "parse error at 1:17: unexpected char after '&'", "syntax"},
 	{"BEGIN { ` }", "", "", "parse error at 1:9: unexpected char", "invalid char"},
+	{"BEGIN { ++3 }", "", "", "parse error at 1:11: expected lvalue after ++", "syntax"},
 
 	// Hex floating point and other number conversions
 	{`{ print $1+0 }  # +posix`, `