Repository: benhoyt/goawk
Pull request #106 (2022-03-09 20:01:16)

Add support for negative field indexes
This change adds support for negative field indexes to return a field counting from the right, so for example, `$-1` is the last field, `$-2` is the second-to-last, and so on. If *n* is greater than `NF` in the expression `$-n`, GoAWK returns empty string (and does nothing when setting the field).

Usage example:

```
$ echo a b c d e | goawk '{ print $-1, $-2, $-10, $1 }'
e d  a
```

Getting or setting a negative field number is currently an error in original-awk, gawk, and mawk, so as far as I'm concerned this is a backwards-compatible change -- no current AWKs support it (and it's not defined by POSIX), so this is a GoAWK extension.


Patch:
diff --git a/README.md b/README.md
index 4b1a3f8..a275741 100644
--- a/README.md
+++ b/README.md
@@ -79,6 +79,7 @@ Additional features GoAWK has over AWK:
 
 * It's embeddable in your Go programs! You can even call custom Go functions from your AWK scripts.
 * I/O-bound AWK scripts (which is most of them) are significantly faster than `awk`, and on a par with `gawk` and `mawk`.
+* It supports negative field indexes to access fields from the right, for example, `$-1` refers to the last field.
 * The parser supports `'single-quoted strings'` in addition to `"double-quoted strings"`, primarily to make Windows one-liners easier (the Windows `cmd.exe` shell uses `"` as the quote character).
 
 Things AWK has over GoAWK:
diff --git a/benchmarks.txt b/benchmarks.txt
index e40a4ae..a9bb382 100644
--- a/benchmarks.txt
+++ b/benchmarks.txt
@@ -2,205 +2,205 @@ goos: linux
 goarch: amd64
 pkg: github.com/benhoyt/goawk/interp
 cpu: Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz
-BenchmarkGlobalVars-8             	 4263327	       267.9 ns/op
-BenchmarkGlobalVars-8             	 4609136	       253.3 ns/op
-BenchmarkGlobalVars-8             	 4607205	       254.1 ns/op
-BenchmarkGlobalVars-8             	 4631146	       253.2 ns/op
-BenchmarkGlobalVars-8             	 4638333	       254.0 ns/op
-BenchmarkLocalVars-8              	 4466588	       261.6 ns/op
-BenchmarkLocalVars-8              	 4522376	       262.0 ns/op
-BenchmarkLocalVars-8              	 4528818	       261.8 ns/op
-BenchmarkLocalVars-8              	 4438172	       261.1 ns/op
-BenchmarkLocalVars-8              	 4493835	       262.0 ns/op
-BenchmarkIncrDecr-8               	 8634733	       135.8 ns/op
-BenchmarkIncrDecr-8               	 8330310	       135.7 ns/op
-BenchmarkIncrDecr-8               	 8427387	       135.9 ns/op
-BenchmarkIncrDecr-8               	 8403655	       135.9 ns/op
-BenchmarkIncrDecr-8               	 8457031	       135.9 ns/op
-BenchmarkSimpleBuiltins-8         	 1735693	       679.1 ns/op
-BenchmarkSimpleBuiltins-8         	 1734067	       682.7 ns/op
-BenchmarkSimpleBuiltins-8         	 1748871	       683.4 ns/op
-BenchmarkSimpleBuiltins-8         	 1747470	       685.2 ns/op
-BenchmarkSimpleBuiltins-8         	 1748572	       681.7 ns/op
-BenchmarkBuiltinMatch-8           	  440961	      2624 ns/op
-BenchmarkBuiltinMatch-8           	  454045	      2643 ns/op
-BenchmarkBuiltinMatch-8           	  434236	      2623 ns/op
-BenchmarkBuiltinMatch-8           	  410077	      2635 ns/op
-BenchmarkBuiltinMatch-8           	  429356	      2637 ns/op
-BenchmarkBuiltinLength-8          	 2692864	       407.3 ns/op
-BenchmarkBuiltinLength-8          	 2922622	       406.4 ns/op
-BenchmarkBuiltinLength-8          	 2885750	       404.9 ns/op
-BenchmarkBuiltinLength-8          	 2902462	       405.6 ns/op
-BenchmarkBuiltinLength-8          	 2924496	       406.3 ns/op
-BenchmarkBuiltinIndex-8           	 1216429	       982.7 ns/op
-BenchmarkBuiltinIndex-8           	 1218434	       980.2 ns/op
-BenchmarkBuiltinIndex-8           	 1221631	       981.6 ns/op
-BenchmarkBuiltinIndex-8           	 1216262	       981.0 ns/op
-BenchmarkBuiltinIndex-8           	 1215754	       976.5 ns/op
-BenchmarkBuiltinSubstr-8          	 1680280	       702.5 ns/op
-BenchmarkBuiltinSubstr-8          	 1704864	       699.0 ns/op
-BenchmarkBuiltinSubstr-8          	 1697030	       698.6 ns/op
-BenchmarkBuiltinSubstr-8          	 1689697	       701.8 ns/op
-BenchmarkBuiltinSubstr-8          	 1686868	       701.7 ns/op
-BenchmarkBuiltinSplitSpace-8      	   33642	     34553 ns/op
-BenchmarkBuiltinSplitSpace-8      	   33950	     37838 ns/op
-BenchmarkBuiltinSplitSpace-8      	   30840	     39356 ns/op
-BenchmarkBuiltinSplitSpace-8      	   27754	     41510 ns/op
-BenchmarkBuiltinSplitSpace-8      	   31758	     35390 ns/op
-BenchmarkBuiltinSplitRegex-8      	   13668	    104644 ns/op
-BenchmarkBuiltinSplitRegex-8      	   13638	     90780 ns/op
-BenchmarkBuiltinSplitRegex-8      	   12883	     98685 ns/op
-BenchmarkBuiltinSplitRegex-8      	   10000	    106095 ns/op
-BenchmarkBuiltinSplitRegex-8      	   13472	     93629 ns/op
-BenchmarkBuiltinSub-8             	   81924	     14067 ns/op
-BenchmarkBuiltinSub-8             	   84368	     13984 ns/op
-BenchmarkBuiltinSub-8             	   77421	     15085 ns/op
-BenchmarkBuiltinSub-8             	   74311	     13685 ns/op
-BenchmarkBuiltinSub-8             	   82861	     14017 ns/op
-BenchmarkBuiltinSubAmpersand-8    	   84949	     14272 ns/op
-BenchmarkBuiltinSubAmpersand-8    	   76710	     13799 ns/op
-BenchmarkBuiltinSubAmpersand-8    	   85435	     14979 ns/op
-BenchmarkBuiltinSubAmpersand-8    	   83442	     14935 ns/op
-BenchmarkBuiltinSubAmpersand-8    	   85035	     13751 ns/op
-BenchmarkBuiltinGsub-8            	   67084	     16214 ns/op
-BenchmarkBuiltinGsub-8            	   69409	     16296 ns/op
-BenchmarkBuiltinGsub-8            	   70615	     17063 ns/op
-BenchmarkBuiltinGsub-8            	   72453	     16444 ns/op
-BenchmarkBuiltinGsub-8            	   70728	     16991 ns/op
-BenchmarkBuiltinGsubAmpersand-8   	   71331	     16283 ns/op
-BenchmarkBuiltinGsubAmpersand-8   	   68366	     16244 ns/op
-BenchmarkBuiltinGsubAmpersand-8   	   65815	     16739 ns/op
-BenchmarkBuiltinGsubAmpersand-8   	   69522	     16557 ns/op
-BenchmarkBuiltinGsubAmpersand-8   	   69364	     16391 ns/op
-BenchmarkBuiltinSprintf-8         	  139731	      7997 ns/op
-BenchmarkBuiltinSprintf-8         	  142776	      8072 ns/op
-BenchmarkBuiltinSprintf-8         	  141777	      8037 ns/op
-BenchmarkBuiltinSprintf-8         	  142446	      8014 ns/op
-BenchmarkBuiltinSprintf-8         	  144211	      8005 ns/op
-BenchmarkRecursiveFunc-8          	   79308	     14279 ns/op
-BenchmarkRecursiveFunc-8          	   79346	     14234 ns/op
-BenchmarkRecursiveFunc-8          	   83191	     14135 ns/op
-BenchmarkRecursiveFunc-8          	   80845	     14148 ns/op
-BenchmarkRecursiveFunc-8          	   82196	     14131 ns/op
-BenchmarkFuncCall-8               	  446318	      2579 ns/op
-BenchmarkFuncCall-8               	  442850	      2620 ns/op
-BenchmarkFuncCall-8               	  445069	      2651 ns/op
-BenchmarkFuncCall-8               	  439562	      2610 ns/op
-BenchmarkFuncCall-8               	  414296	      2585 ns/op
-BenchmarkNativeFunc-8             	  108356	     10673 ns/op
-BenchmarkNativeFunc-8             	  107876	     10541 ns/op
-BenchmarkNativeFunc-8             	  109742	     10675 ns/op
-BenchmarkNativeFunc-8             	  108325	     10655 ns/op
-BenchmarkNativeFunc-8             	  107876	     10631 ns/op
-BenchmarkForLoop-8                	  461509	      2429 ns/op
-BenchmarkForLoop-8                	  525288	      2287 ns/op
-BenchmarkForLoop-8                	  499704	      2287 ns/op
-BenchmarkForLoop-8                	  524025	      2274 ns/op
-BenchmarkForLoop-8                	  476272	      2282 ns/op
-BenchmarkForInLoop-8              	  705500	      1615 ns/op
-BenchmarkForInLoop-8              	  666469	      1614 ns/op
-BenchmarkForInLoop-8              	  756931	      1614 ns/op
-BenchmarkForInLoop-8              	  774727	      1622 ns/op
-BenchmarkForInLoop-8              	  683715	      1576 ns/op
-BenchmarkIfStatement-8            	 8120900	       141.7 ns/op
-BenchmarkIfStatement-8            	 8158304	       141.7 ns/op
-BenchmarkIfStatement-8            	 8129816	       141.3 ns/op
-BenchmarkIfStatement-8            	 8160464	       141.5 ns/op
-BenchmarkIfStatement-8            	 7941661	       142.0 ns/op
-BenchmarkCondExpr-8               	 8110185	       140.6 ns/op
-BenchmarkCondExpr-8               	 8150256	       140.8 ns/op
-BenchmarkCondExpr-8               	 8188604	       140.5 ns/op
-BenchmarkCondExpr-8               	 8267331	       141.1 ns/op
-BenchmarkCondExpr-8               	 8191064	       140.6 ns/op
-BenchmarkSimplePattern-8          	 9132025	       126.1 ns/op
-BenchmarkSimplePattern-8          	 9018183	       127.0 ns/op
-BenchmarkSimplePattern-8          	 9822612	       127.7 ns/op
-BenchmarkSimplePattern-8          	 9707673	       125.6 ns/op
-BenchmarkSimplePattern-8          	 9521788	       128.1 ns/op
-BenchmarkGetField-8               	 3810618	       323.6 ns/op
-BenchmarkGetField-8               	 3755844	       324.6 ns/op
-BenchmarkGetField-8               	 3871728	       331.1 ns/op
-BenchmarkGetField-8               	 3701880	       328.8 ns/op
-BenchmarkGetField-8               	 3642691	       329.8 ns/op
-BenchmarkSetField-8               	 1477261	       800.4 ns/op
-BenchmarkSetField-8               	 1473310	       812.5 ns/op
-BenchmarkSetField-8               	 1461325	       817.6 ns/op
-BenchmarkSetField-8               	 1463655	       804.5 ns/op
-BenchmarkSetField-8               	 1464037	       811.9 ns/op
-BenchmarkRegexMatch-8             	 1000000	      1092 ns/op
-BenchmarkRegexMatch-8             	 1000000	      1025 ns/op
-BenchmarkRegexMatch-8             	 1177221	      1018 ns/op
-BenchmarkRegexMatch-8             	 1000000	      1023 ns/op
-BenchmarkRegexMatch-8             	 1000000	      1019 ns/op
-BenchmarkBinaryOperators-8        	 2881827	       410.8 ns/op
-BenchmarkBinaryOperators-8        	 2884640	       410.0 ns/op
-BenchmarkBinaryOperators-8        	 2904675	       410.5 ns/op
-BenchmarkBinaryOperators-8        	 2893975	       408.4 ns/op
-BenchmarkBinaryOperators-8        	 2889530	       409.4 ns/op
-BenchmarkConcatTwo-8              	11590569	        98.86 ns/op
-BenchmarkConcatTwo-8              	11185783	        97.90 ns/op
-BenchmarkConcatTwo-8              	11919296	        97.48 ns/op
-BenchmarkConcatTwo-8              	11799462	        97.61 ns/op
-BenchmarkConcatTwo-8              	11686958	        97.72 ns/op
-BenchmarkConcatSmall-8            	 3499250	       347.4 ns/op
-BenchmarkConcatSmall-8            	 3510696	       334.6 ns/op
-BenchmarkConcatSmall-8            	 3519835	       336.8 ns/op
-BenchmarkConcatSmall-8            	 3195825	       350.7 ns/op
-BenchmarkConcatSmall-8            	 3152904	       341.4 ns/op
-BenchmarkConcatLarge-8            	    1009	   1048386 ns/op
-BenchmarkConcatLarge-8            	     991	   1059244 ns/op
-BenchmarkConcatLarge-8            	     996	   1058039 ns/op
-BenchmarkConcatLarge-8            	    1020	   1065936 ns/op
-BenchmarkConcatLarge-8            	    1083	    978291 ns/op
-BenchmarkComparisons-8            	 3536655	       335.8 ns/op
-BenchmarkComparisons-8            	 3654572	       322.0 ns/op
-BenchmarkComparisons-8            	 3689509	       304.5 ns/op
-BenchmarkComparisons-8            	 3880083	       305.5 ns/op
-BenchmarkComparisons-8            	 3828115	       305.3 ns/op
-BenchmarkArrayOperations-8        	 2204416	       535.3 ns/op
-BenchmarkArrayOperations-8        	 2214040	       533.8 ns/op
-BenchmarkArrayOperations-8        	 2215861	       535.1 ns/op
-BenchmarkArrayOperations-8        	 2225504	       535.3 ns/op
-BenchmarkArrayOperations-8        	 2210070	       537.0 ns/op
-BenchmarkAssign-8                 	 4829376	       241.6 ns/op
-BenchmarkAssign-8                 	 4856059	       241.9 ns/op
-BenchmarkAssign-8                 	 4842148	       242.2 ns/op
-BenchmarkAssign-8                 	 4856487	       242.0 ns/op
-BenchmarkAssign-8                 	 4881772	       242.1 ns/op
-BenchmarkAugAssign-8              	 1292108	       924.2 ns/op
-BenchmarkAugAssign-8              	 1293535	       922.3 ns/op
-BenchmarkAugAssign-8              	 1281874	       923.2 ns/op
-BenchmarkAugAssign-8              	 1297642	       925.2 ns/op
-BenchmarkAugAssign-8              	 1287924	       916.9 ns/op
-BenchmarkPrint-8                  	  532683	      2195 ns/op
-BenchmarkPrint-8                  	  564078	      2186 ns/op
-BenchmarkPrint-8                  	  574272	      2212 ns/op
-BenchmarkPrint-8                  	  558535	      2213 ns/op
-BenchmarkPrint-8                  	  531136	      2198 ns/op
-BenchmarkPrintf-8                 	  262200	      4481 ns/op
-BenchmarkPrintf-8                 	  254942	      4460 ns/op
-BenchmarkPrintf-8                 	  258392	      4442 ns/op
-BenchmarkPrintf-8                 	  260904	      4432 ns/op
-BenchmarkPrintf-8                 	  269527	      4433 ns/op
-BenchmarkRepeatExecProgram-8      	   85706	     13216 ns/op
-BenchmarkRepeatExecProgram-8      	   86746	     13695 ns/op
-BenchmarkRepeatExecProgram-8      	   87080	     13223 ns/op
-BenchmarkRepeatExecProgram-8      	   87076	     13934 ns/op
-BenchmarkRepeatExecProgram-8      	   82278	     13378 ns/op
-BenchmarkRepeatNew-8              	12374329	        86.92 ns/op
-BenchmarkRepeatNew-8              	13088485	        84.23 ns/op
-BenchmarkRepeatNew-8              	12760605	        84.55 ns/op
-BenchmarkRepeatNew-8              	12798412	        84.40 ns/op
-BenchmarkRepeatNew-8              	12789001	        84.52 ns/op
-BenchmarkRepeatIOExecProgram-8    	   48926	     22787 ns/op
-BenchmarkRepeatIOExecProgram-8    	   49356	     22846 ns/op
-BenchmarkRepeatIOExecProgram-8    	   49848	     22878 ns/op
-BenchmarkRepeatIOExecProgram-8    	   49632	     22733 ns/op
-BenchmarkRepeatIOExecProgram-8    	   49998	     22881 ns/op
-BenchmarkRepeatIONew-8            	 1000000	      1105 ns/op
-BenchmarkRepeatIONew-8            	 1000000	      1107 ns/op
-BenchmarkRepeatIONew-8            	 1000000	      1108 ns/op
-BenchmarkRepeatIONew-8            	 1000000	      1105 ns/op
-BenchmarkRepeatIONew-8            	 1000000	      1121 ns/op
+BenchmarkGlobalVars-8             	 4075256	       268.1 ns/op
+BenchmarkGlobalVars-8             	 4623044	       256.0 ns/op
+BenchmarkGlobalVars-8             	 4641457	       255.5 ns/op
+BenchmarkGlobalVars-8             	 4606293	       256.2 ns/op
+BenchmarkGlobalVars-8             	 4675101	       258.5 ns/op
+BenchmarkLocalVars-8              	 4300752	       277.6 ns/op
+BenchmarkLocalVars-8              	 4339558	       276.8 ns/op
+BenchmarkLocalVars-8              	 4276269	       275.8 ns/op
+BenchmarkLocalVars-8              	 4316472	       277.1 ns/op
+BenchmarkLocalVars-8              	 4319824	       277.2 ns/op
+BenchmarkIncrDecr-8               	 8416693	       138.0 ns/op
+BenchmarkIncrDecr-8               	 8616364	       138.2 ns/op
+BenchmarkIncrDecr-8               	 8575410	       138.5 ns/op
+BenchmarkIncrDecr-8               	 8555124	       138.2 ns/op
+BenchmarkIncrDecr-8               	 8594312	       137.8 ns/op
+BenchmarkSimpleBuiltins-8         	 1716831	       702.4 ns/op
+BenchmarkSimpleBuiltins-8         	 1725399	       698.9 ns/op
+BenchmarkSimpleBuiltins-8         	 1714075	       699.9 ns/op
+BenchmarkSimpleBuiltins-8         	 1720785	       699.3 ns/op
+BenchmarkSimpleBuiltins-8         	 1720160	       699.5 ns/op
+BenchmarkBuiltinMatch-8           	  467084	      2608 ns/op
+BenchmarkBuiltinMatch-8           	  467012	      2616 ns/op
+BenchmarkBuiltinMatch-8           	  452547	      2634 ns/op
+BenchmarkBuiltinMatch-8           	  448473	      2636 ns/op
+BenchmarkBuiltinMatch-8           	  445840	      2640 ns/op
+BenchmarkBuiltinLength-8          	 2683484	       418.6 ns/op
+BenchmarkBuiltinLength-8          	 2911779	       411.9 ns/op
+BenchmarkBuiltinLength-8          	 2893026	       411.8 ns/op
+BenchmarkBuiltinLength-8          	 2891780	       413.3 ns/op
+BenchmarkBuiltinLength-8          	 2890932	       412.0 ns/op
+BenchmarkBuiltinIndex-8           	 1229060	       976.2 ns/op
+BenchmarkBuiltinIndex-8           	 1225165	       978.8 ns/op
+BenchmarkBuiltinIndex-8           	 1219188	       977.9 ns/op
+BenchmarkBuiltinIndex-8           	 1223830	       978.4 ns/op
+BenchmarkBuiltinIndex-8           	 1225833	       976.6 ns/op
+BenchmarkBuiltinSubstr-8          	 1604412	       735.3 ns/op
+BenchmarkBuiltinSubstr-8          	 1624656	       735.9 ns/op
+BenchmarkBuiltinSubstr-8          	 1631421	       737.5 ns/op
+BenchmarkBuiltinSubstr-8          	 1629694	       735.7 ns/op
+BenchmarkBuiltinSubstr-8          	 1631365	       734.6 ns/op
+BenchmarkBuiltinSplitSpace-8      	   34952	     34416 ns/op
+BenchmarkBuiltinSplitSpace-8      	   34904	     34440 ns/op
+BenchmarkBuiltinSplitSpace-8      	   34864	     34402 ns/op
+BenchmarkBuiltinSplitSpace-8      	   34900	     34365 ns/op
+BenchmarkBuiltinSplitSpace-8      	   34938	     34474 ns/op
+BenchmarkBuiltinSplitRegex-8      	   12957	     96728 ns/op
+BenchmarkBuiltinSplitRegex-8      	   10000	    101008 ns/op
+BenchmarkBuiltinSplitRegex-8      	   13686	    109002 ns/op
+BenchmarkBuiltinSplitRegex-8      	   13552	     96655 ns/op
+BenchmarkBuiltinSplitRegex-8      	   10000	    102522 ns/op
+BenchmarkBuiltinSub-8             	   83670	     14483 ns/op
+BenchmarkBuiltinSub-8             	   80293	     14621 ns/op
+BenchmarkBuiltinSub-8             	   85953	     14787 ns/op
+BenchmarkBuiltinSub-8             	   82009	     14345 ns/op
+BenchmarkBuiltinSub-8             	   81778	     14037 ns/op
+BenchmarkBuiltinSubAmpersand-8    	   87823	     14952 ns/op
+BenchmarkBuiltinSubAmpersand-8    	   82266	     14163 ns/op
+BenchmarkBuiltinSubAmpersand-8    	   82502	     14518 ns/op
+BenchmarkBuiltinSubAmpersand-8    	   89666	     13614 ns/op
+BenchmarkBuiltinSubAmpersand-8    	   89136	     14256 ns/op
+BenchmarkBuiltinGsub-8            	   74120	     16630 ns/op
+BenchmarkBuiltinGsub-8            	   59758	     17614 ns/op
+BenchmarkBuiltinGsub-8            	   73873	     17680 ns/op
+BenchmarkBuiltinGsub-8            	   54985	     20752 ns/op
+BenchmarkBuiltinGsub-8            	   55015	     19168 ns/op
+BenchmarkBuiltinGsubAmpersand-8   	   71544	     17509 ns/op
+BenchmarkBuiltinGsubAmpersand-8   	   65523	     18321 ns/op
+BenchmarkBuiltinGsubAmpersand-8   	   62002	     18282 ns/op
+BenchmarkBuiltinGsubAmpersand-8   	   66861	     16991 ns/op
+BenchmarkBuiltinGsubAmpersand-8   	   67010	     16441 ns/op
+BenchmarkBuiltinSprintf-8         	  145122	      8025 ns/op
+BenchmarkBuiltinSprintf-8         	  148687	      8101 ns/op
+BenchmarkBuiltinSprintf-8         	  149292	      8080 ns/op
+BenchmarkBuiltinSprintf-8         	  147423	      8046 ns/op
+BenchmarkBuiltinSprintf-8         	  146047	      8082 ns/op
+BenchmarkRecursiveFunc-8          	   84304	     14180 ns/op
+BenchmarkRecursiveFunc-8          	   81481	     14201 ns/op
+BenchmarkRecursiveFunc-8          	   83458	     14368 ns/op
+BenchmarkRecursiveFunc-8          	   83422	     14334 ns/op
+BenchmarkRecursiveFunc-8          	   83034	     14856 ns/op
+BenchmarkFuncCall-8               	  437306	      2650 ns/op
+BenchmarkFuncCall-8               	  399121	      2602 ns/op
+BenchmarkFuncCall-8               	  466686	      2588 ns/op
+BenchmarkFuncCall-8               	  442496	      2672 ns/op
+BenchmarkFuncCall-8               	  466833	      2618 ns/op
+BenchmarkNativeFunc-8             	  107229	     10713 ns/op
+BenchmarkNativeFunc-8             	  110500	     10727 ns/op
+BenchmarkNativeFunc-8             	  110989	     10778 ns/op
+BenchmarkNativeFunc-8             	  112014	     10735 ns/op
+BenchmarkNativeFunc-8             	  110942	     10711 ns/op
+BenchmarkForLoop-8                	  479324	      2410 ns/op
+BenchmarkForLoop-8                	  455330	      2336 ns/op
+BenchmarkForLoop-8                	  464859	      2329 ns/op
+BenchmarkForLoop-8                	  468164	      2327 ns/op
+BenchmarkForLoop-8                	  478904	      2327 ns/op
+BenchmarkForInLoop-8              	  660457	      1659 ns/op
+BenchmarkForInLoop-8              	  676776	      1641 ns/op
+BenchmarkForInLoop-8              	  674151	      1622 ns/op
+BenchmarkForInLoop-8              	  658274	      1676 ns/op
+BenchmarkForInLoop-8              	  674551	      1618 ns/op
+BenchmarkIfStatement-8            	 8218384	       145.1 ns/op
+BenchmarkIfStatement-8            	 8145972	       145.7 ns/op
+BenchmarkIfStatement-8            	 8027650	       144.8 ns/op
+BenchmarkIfStatement-8            	 8235544	       145.1 ns/op
+BenchmarkIfStatement-8            	 8136636	       146.0 ns/op
+BenchmarkCondExpr-8               	 8082963	       144.8 ns/op
+BenchmarkCondExpr-8               	 8122240	       145.1 ns/op
+BenchmarkCondExpr-8               	 8177277	       145.4 ns/op
+BenchmarkCondExpr-8               	 7887492	       145.2 ns/op
+BenchmarkCondExpr-8               	 8099749	       145.4 ns/op
+BenchmarkSimplePattern-8          	 9528322	       125.4 ns/op
+BenchmarkSimplePattern-8          	 9790114	       123.9 ns/op
+BenchmarkSimplePattern-8          	 9842065	       126.6 ns/op
+BenchmarkSimplePattern-8          	 9320108	       124.2 ns/op
+BenchmarkSimplePattern-8          	 9467245	       125.7 ns/op
+BenchmarkGetField-8               	 3811321	       327.5 ns/op
+BenchmarkGetField-8               	 3821608	       327.4 ns/op
+BenchmarkGetField-8               	 3736053	       327.8 ns/op
+BenchmarkGetField-8               	 3748774	       325.1 ns/op
+BenchmarkGetField-8               	 3705824	       328.3 ns/op
+BenchmarkSetField-8               	 1417530	       829.2 ns/op
+BenchmarkSetField-8               	 1457792	       851.1 ns/op
+BenchmarkSetField-8               	 1413510	       849.1 ns/op
+BenchmarkSetField-8               	 1408370	       845.6 ns/op
+BenchmarkSetField-8               	 1413724	       848.1 ns/op
+BenchmarkRegexMatch-8             	 1000000	      1042 ns/op
+BenchmarkRegexMatch-8             	 1000000	      1028 ns/op
+BenchmarkRegexMatch-8             	 1200997	      1025 ns/op
+BenchmarkRegexMatch-8             	 1000000	      1027 ns/op
+BenchmarkRegexMatch-8             	 1000000	      1000 ns/op
+BenchmarkBinaryOperators-8        	 2761909	       433.7 ns/op
+BenchmarkBinaryOperators-8        	 2762940	       441.2 ns/op
+BenchmarkBinaryOperators-8        	 2758825	       433.4 ns/op
+BenchmarkBinaryOperators-8        	 2733811	       433.8 ns/op
+BenchmarkBinaryOperators-8        	 2764592	       433.7 ns/op
+BenchmarkConcatTwo-8              	12494463	        97.01 ns/op
+BenchmarkConcatTwo-8              	12211786	        97.26 ns/op
+BenchmarkConcatTwo-8              	12352069	        97.80 ns/op
+BenchmarkConcatTwo-8              	12156282	        96.92 ns/op
+BenchmarkConcatTwo-8              	12420493	        97.74 ns/op
+BenchmarkConcatSmall-8            	 3521109	       382.3 ns/op
+BenchmarkConcatSmall-8            	 3577508	       347.2 ns/op
+BenchmarkConcatSmall-8            	 2921392	       395.0 ns/op
+BenchmarkConcatSmall-8            	 3333322	       362.6 ns/op
+BenchmarkConcatSmall-8            	 3554974	       359.1 ns/op
+BenchmarkConcatLarge-8            	    1280	    909816 ns/op
+BenchmarkConcatLarge-8            	    1352	    908561 ns/op
+BenchmarkConcatLarge-8            	    1206	    910679 ns/op
+BenchmarkConcatLarge-8            	    1316	    924500 ns/op
+BenchmarkConcatLarge-8            	    1328	    910896 ns/op
+BenchmarkComparisons-8            	 3447384	       331.3 ns/op
+BenchmarkComparisons-8            	 3862201	       308.7 ns/op
+BenchmarkComparisons-8            	 3839308	       310.3 ns/op
+BenchmarkComparisons-8            	 3850497	       310.0 ns/op
+BenchmarkComparisons-8            	 3840018	       310.3 ns/op
+BenchmarkArrayOperations-8        	 2239652	       534.0 ns/op
+BenchmarkArrayOperations-8        	 2247266	       534.6 ns/op
+BenchmarkArrayOperations-8        	 2244108	       533.3 ns/op
+BenchmarkArrayOperations-8        	 2233255	       532.4 ns/op
+BenchmarkArrayOperations-8        	 2243911	       530.5 ns/op
+BenchmarkAssign-8                 	 4778359	       248.7 ns/op
+BenchmarkAssign-8                 	 4821517	       248.1 ns/op
+BenchmarkAssign-8                 	 4829642	       248.1 ns/op
+BenchmarkAssign-8                 	 4795771	       248.3 ns/op
+BenchmarkAssign-8                 	 4840029	       248.6 ns/op
+BenchmarkAugAssign-8              	 1254536	       951.8 ns/op
+BenchmarkAugAssign-8              	 1267396	       949.1 ns/op
+BenchmarkAugAssign-8              	 1265685	       949.0 ns/op
+BenchmarkAugAssign-8              	 1264654	       953.0 ns/op
+BenchmarkAugAssign-8              	 1266020	       946.2 ns/op
+BenchmarkPrint-8                  	  562316	      2226 ns/op
+BenchmarkPrint-8                  	  568324	      2214 ns/op
+BenchmarkPrint-8                  	  554286	      2209 ns/op
+BenchmarkPrint-8                  	  565864	      2216 ns/op
+BenchmarkPrint-8                  	  574735	      2222 ns/op
+BenchmarkPrintf-8                 	  269350	      4484 ns/op
+BenchmarkPrintf-8                 	  270219	      4488 ns/op
+BenchmarkPrintf-8                 	  267348	      4463 ns/op
+BenchmarkPrintf-8                 	  273658	      4475 ns/op
+BenchmarkPrintf-8                 	  268509	      4479 ns/op
+BenchmarkRepeatExecProgram-8      	   86594	     14564 ns/op
+BenchmarkRepeatExecProgram-8      	   85363	     15414 ns/op
+BenchmarkRepeatExecProgram-8      	   91147	     13883 ns/op
+BenchmarkRepeatExecProgram-8      	   85326	     14576 ns/op
+BenchmarkRepeatExecProgram-8      	   91087	     13833 ns/op
+BenchmarkRepeatNew-8              	12343856	        87.90 ns/op
+BenchmarkRepeatNew-8              	13725634	        84.34 ns/op
+BenchmarkRepeatNew-8              	13482708	        84.25 ns/op
+BenchmarkRepeatNew-8              	13619043	        84.14 ns/op
+BenchmarkRepeatNew-8              	13407644	        84.94 ns/op
+BenchmarkRepeatIOExecProgram-8    	   52796	     22426 ns/op
+BenchmarkRepeatIOExecProgram-8    	   53888	     22444 ns/op
+BenchmarkRepeatIOExecProgram-8    	   53563	     22376 ns/op
+BenchmarkRepeatIOExecProgram-8    	   53539	     22425 ns/op
+BenchmarkRepeatIOExecProgram-8    	   53539	     22360 ns/op
+BenchmarkRepeatIONew-8            	 1000000	      1103 ns/op
+BenchmarkRepeatIONew-8            	 1000000	      1102 ns/op
+BenchmarkRepeatIONew-8            	 1000000	      1103 ns/op
+BenchmarkRepeatIONew-8            	 1000000	      1097 ns/op
+BenchmarkRepeatIONew-8            	 1000000	      1111 ns/op
 PASS
-ok  	github.com/benhoyt/goawk/interp	319.752s
+ok  	github.com/benhoyt/goawk/interp	319.579s
diff --git a/interp/interp.go b/interp/interp.go
index 9278cc6..7956e2f 100644
--- a/interp/interp.go
+++ b/interp/interp.go
@@ -45,7 +45,7 @@ var (
 )
 
 // Error (actually *Error) is returned by Exec and Eval functions on
-// interpreter error, for example a negative field index.
+// interpreter error, for example FS being set to an invalid regex.
 type Error struct {
 	message string
 }
@@ -661,25 +661,28 @@ func (p *interp) setArrayValue(scope ast.VarScope, arrayIndex int, index string,
 }
 
 // Get the value of given numbered field, equivalent to "$index"
-func (p *interp) getField(index int) (value, error) {
-	if index < 0 {
-		return null(), newError("field index negative: %d", index)
-	}
+func (p *interp) getField(index int) value {
 	if index == 0 {
 		if p.lineIsTrueStr {
-			return str(p.line), nil
+			return str(p.line)
 		} else {
-			return numStr(p.line), nil
+			return numStr(p.line)
 		}
 	}
 	p.ensureFields()
+	if index < 1 {
+		index = len(p.fields) + 1 + index
+		if index < 1 {
+			return str("")
+		}
+	}
 	if index > len(p.fields) {
-		return str(""), nil
+		return str("")
 	}
 	if p.fieldsIsTrueStr[index-1] {
-		return str(p.fields[index-1]), nil
+		return str(p.fields[index-1])
 	} else {
-		return numStr(p.fields[index-1]), nil
+		return numStr(p.fields[index-1])
 	}
 }
 
@@ -689,14 +692,17 @@ func (p *interp) setField(index int, value string) error {
 		p.setLine(value, true)
 		return nil
 	}
-	if index < 0 {
-		return newError("field index negative: %d", index)
-	}
 	if index > maxFieldIndex {
 		return newError("field index too large: %d", index)
 	}
 	// If there aren't enough fields, add empty string fields in between
 	p.ensureFields()
+	if index < 1 {
+		index = len(p.fields) + 1 + index
+		if index < 1 {
+			return nil
+		}
+	}
 	for i := len(p.fields); i < index; i++ {
 		p.fields = append(p.fields, "")
 		p.fieldsIsTrueStr = append(p.fieldsIsTrueStr, true)
diff --git a/interp/vm.go b/interp/vm.go
index d9969eb..2d9e5c3 100644
--- a/interp/vm.go
+++ b/interp/vm.go
@@ -63,19 +63,13 @@ func (p *interp) execute(code []compiler.Opcode) error {
 
 		case compiler.Field:
 			index := p.peekTop()
-			v, err := p.getField(int(index.num()))
-			if err != nil {
-				return err
-			}
+			v := p.getField(int(index.num()))
 			p.replaceTop(v)
 
 		case compiler.FieldInt:
 			index := code[ip]
 			ip++
-			v, err := p.getField(int(index))
-			if err != nil {
-				return err
-			}
+			v := p.getField(int(index))
 			p.push(v)
 
 		case compiler.Global:
@@ -185,11 +179,8 @@ func (p *interp) execute(code []compiler.Opcode) error {
 			amount := code[ip]
 			ip++
 			index := int(p.pop().num())
-			v, err := p.getField(index)
-			if err != nil {
-				return err
-			}
-			err = p.setField(index, p.toString(num(v.num()+float64(amount))))
+			v := p.getField(index)
+			err := p.setField(index, p.toString(num(v.num()+float64(amount))))
 			if err != nil {
 				return err
 			}
@@ -237,10 +228,7 @@ func (p *interp) execute(code []compiler.Opcode) error {
 			ip++
 			right, indexVal := p.popTwo()
 			index := int(indexVal.num())
-			field, err := p.getField(index)
-			if err != nil {
-				return err
-			}
+			field := p.getField(index)
 			v, err := p.augAssignOp(operation, field, right)
 			if err != nil {
 				return err


Test patch:
diff --git a/interp/interp_test.go b/interp/interp_test.go
index a7c0b8b..4e9ce8e 100644
--- a/interp/interp_test.go
+++ b/interp/interp_test.go
@@ -387,7 +387,12 @@ BEGIN {
 	{`{ 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", "field -1"},
+	{`{ print $-1 }`, "a\nb c\nd e f\n", "a\nc\nf\n", "", "field -1"},
+	{`{ print $-2 }`, "a\nb c\nd e f\n", "\nb\ne\n", "", "field -2"},
+	{`{ print $-3 }`, "a\nb c\nd e f\n", "\n\nd\n", "", "field -3"},
+	{`{ $-1="x"; print }`, "a\nb c\nd e f\n", "x\nb x\nd e x\n", "", "field -1"},
+	{`{ $-2="y"; print }`, "a\nb c\nd e f\n", "a\ny c\nd y f\n", "", "field -2"},
+	{`{ $-3="z"; print }`, "a\nb c\nd e f\n", "a\nb c\nz e f\n", "", "field -3"},
 	{`{ NF=-1; }  # !awk - awk allows setting negative NF`,
 		"x", "", "NF set to negative value: -1", "negative value"},
 	{`{ NF=1234567; }`, "x", "", "NF set too large: 1234567", ""},