Repository: benhoyt/goawk (go)

Issue:
Proper fuzz testing
Now that Go has builtin fuzz testing in v1.18 we can use https://github.com/benhoyt/goawk/blob/master/interp/fuzz_test.go and get rid of the top-level `fuzz` directory.

However, a lot of things fail because of infinite loops. Can we add a check in `interp.execute` that fails after 1,000,000 (or so) statements, or a timeout? This should probably be behind a build tag and turned off by default, because it will decrease performance significantly.

And then once we've added good fuzz testing, run the fuzzer again and see what it finds.

Pull request description:
Proper Go 1.18 fuzz testing support; add ExecuteContext
Remove old go-fuzz code and add proper Go 1.18 fuzz testing. Also add a new `Interpreter.ExecuteContext` API (for use in the fuzz tests and for other users too).

Note that the `p.checkCtx` test every instruction slows things down by about 1%, but the recent optimizations make that worth it (overall we're getting about a 10% improvement since v1.15.0). See benchmarks at https://github.com/benhoyt/goawk/pull/103/commits/bcd2378f2b0845d910de9139c9797055dce16bc9 for more details.

Also move scripts to `scripts/` directory.

Fixes #92.

Patch:
diff --git a/benchmarks.txt b/benchmarks.txt
index 93db4fa..e40a4ae 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             	 4371464	       259.7 ns/op
-BenchmarkGlobalVars-8             	 4797980	       248.0 ns/op
-BenchmarkGlobalVars-8             	 4710984	       244.8 ns/op
-BenchmarkGlobalVars-8             	 4744932	       245.0 ns/op
-BenchmarkGlobalVars-8             	 4700199	       244.9 ns/op
-BenchmarkLocalVars-8              	 4344748	       270.8 ns/op
-BenchmarkLocalVars-8              	 4361964	       271.0 ns/op
-BenchmarkLocalVars-8              	 4302690	       271.0 ns/op
-BenchmarkLocalVars-8              	 4320506	       271.1 ns/op
-BenchmarkLocalVars-8              	 4289016	       271.8 ns/op
-BenchmarkIncrDecr-8               	 8439381	       134.8 ns/op
-BenchmarkIncrDecr-8               	 8508264	       134.4 ns/op
-BenchmarkIncrDecr-8               	 8462049	       134.7 ns/op
-BenchmarkIncrDecr-8               	 8594487	       135.0 ns/op
-BenchmarkIncrDecr-8               	 8360473	       135.0 ns/op
-BenchmarkSimpleBuiltins-8         	 1702210	       692.9 ns/op
-BenchmarkSimpleBuiltins-8         	 1711676	       693.6 ns/op
-BenchmarkSimpleBuiltins-8         	 1716084	       694.4 ns/op
-BenchmarkSimpleBuiltins-8         	 1722451	       708.3 ns/op
-BenchmarkSimpleBuiltins-8         	 1720766	       696.2 ns/op
-BenchmarkBuiltinMatch-8           	  468158	      2580 ns/op
-BenchmarkBuiltinMatch-8           	  467650	      2568 ns/op
-BenchmarkBuiltinMatch-8           	  445869	      2581 ns/op
-BenchmarkBuiltinMatch-8           	  459672	      2576 ns/op
-BenchmarkBuiltinMatch-8           	  445779	      2572 ns/op
-BenchmarkBuiltinLength-8          	 2716842	       400.5 ns/op
-BenchmarkBuiltinLength-8          	 2966719	       396.9 ns/op
-BenchmarkBuiltinLength-8          	 2997795	       397.2 ns/op
-BenchmarkBuiltinLength-8          	 2977437	       397.5 ns/op
-BenchmarkBuiltinLength-8          	 2950501	       397.1 ns/op
-BenchmarkBuiltinIndex-8           	 1218573	       981.4 ns/op
-BenchmarkBuiltinIndex-8           	 1214140	       980.2 ns/op
-BenchmarkBuiltinIndex-8           	 1218864	       980.1 ns/op
-BenchmarkBuiltinIndex-8           	 1216875	       977.9 ns/op
-BenchmarkBuiltinIndex-8           	 1221342	       981.7 ns/op
-BenchmarkBuiltinSubstr-8          	 1696950	       705.5 ns/op
-BenchmarkBuiltinSubstr-8          	 1689207	       704.8 ns/op
-BenchmarkBuiltinSubstr-8          	 1677028	       705.1 ns/op
-BenchmarkBuiltinSubstr-8          	 1695517	       701.1 ns/op
-BenchmarkBuiltinSubstr-8          	 1665110	       706.0 ns/op
-BenchmarkBuiltinSplitSpace-8      	   34087	     34317 ns/op
-BenchmarkBuiltinSplitSpace-8      	   33998	     34440 ns/op
-BenchmarkBuiltinSplitSpace-8      	   33969	     34429 ns/op
-BenchmarkBuiltinSplitSpace-8      	   34135	     34565 ns/op
-BenchmarkBuiltinSplitSpace-8      	   34350	     34572 ns/op
-BenchmarkBuiltinSplitRegex-8      	   12712	     89855 ns/op
-BenchmarkBuiltinSplitRegex-8      	   12974	     90380 ns/op
-BenchmarkBuiltinSplitRegex-8      	   12639	     94385 ns/op
-BenchmarkBuiltinSplitRegex-8      	   12940	     91230 ns/op
-BenchmarkBuiltinSplitRegex-8      	   12703	     96816 ns/op
-BenchmarkBuiltinSub-8             	   80398	     13663 ns/op
-BenchmarkBuiltinSub-8             	   84315	     14233 ns/op
-BenchmarkBuiltinSub-8             	   74383	     13722 ns/op
-BenchmarkBuiltinSub-8             	   84838	     14105 ns/op
-BenchmarkBuiltinSub-8             	   84944	     13960 ns/op
-BenchmarkBuiltinSubAmpersand-8    	   74740	     14554 ns/op
-BenchmarkBuiltinSubAmpersand-8    	   85614	     13581 ns/op
-BenchmarkBuiltinSubAmpersand-8    	   85485	     14208 ns/op
-BenchmarkBuiltinSubAmpersand-8    	   84147	     14722 ns/op
-BenchmarkBuiltinSubAmpersand-8    	   82761	     13602 ns/op
-BenchmarkBuiltinGsub-8            	   70276	     17000 ns/op
-BenchmarkBuiltinGsub-8            	   61699	     17531 ns/op
-BenchmarkBuiltinGsub-8            	   71220	     16177 ns/op
-BenchmarkBuiltinGsub-8            	   72036	     17041 ns/op
-BenchmarkBuiltinGsub-8            	   71685	     16368 ns/op
-BenchmarkBuiltinGsubAmpersand-8   	   71938	     17426 ns/op
-BenchmarkBuiltinGsubAmpersand-8   	   68680	     16209 ns/op
-BenchmarkBuiltinGsubAmpersand-8   	   71809	     16324 ns/op
-BenchmarkBuiltinGsubAmpersand-8   	   72052	     17998 ns/op
-BenchmarkBuiltinGsubAmpersand-8   	   71821	     16391 ns/op
-BenchmarkBuiltinSprintf-8         	  143167	      7974 ns/op
-BenchmarkBuiltinSprintf-8         	  142041	      7933 ns/op
-BenchmarkBuiltinSprintf-8         	  143452	      7917 ns/op
-BenchmarkBuiltinSprintf-8         	  145059	      7988 ns/op
-BenchmarkBuiltinSprintf-8         	  141703	      7926 ns/op
-BenchmarkRecursiveFunc-8          	   80313	     14513 ns/op
-BenchmarkRecursiveFunc-8          	   80328	     14577 ns/op
-BenchmarkRecursiveFunc-8          	   79768	     14587 ns/op
-BenchmarkRecursiveFunc-8          	   79786	     14458 ns/op
-BenchmarkRecursiveFunc-8          	   80740	     14397 ns/op
-BenchmarkFuncCall-8               	  430056	      2638 ns/op
-BenchmarkFuncCall-8               	  423109	      2630 ns/op
-BenchmarkFuncCall-8               	  455467	      2659 ns/op
-BenchmarkFuncCall-8               	  426019	      2626 ns/op
-BenchmarkFuncCall-8               	  433941	      2670 ns/op
-BenchmarkNativeFunc-8             	  109581	     10564 ns/op
-BenchmarkNativeFunc-8             	  110739	     10569 ns/op
-BenchmarkNativeFunc-8             	  108955	     10629 ns/op
-BenchmarkNativeFunc-8             	  110300	     10535 ns/op
-BenchmarkNativeFunc-8             	  108678	     10577 ns/op
-BenchmarkForLoop-8                	  477658	      2360 ns/op
-BenchmarkForLoop-8                	  447709	      2246 ns/op
-BenchmarkForLoop-8                	  450757	      2254 ns/op
-BenchmarkForLoop-8                	  487446	      2245 ns/op
-BenchmarkForLoop-8                	  492711	      2237 ns/op
-BenchmarkForInLoop-8              	  669613	      1739 ns/op
-BenchmarkForInLoop-8              	  611119	      1692 ns/op
-BenchmarkForInLoop-8              	  604185	      1676 ns/op
-BenchmarkForInLoop-8              	  713817	      1706 ns/op
-BenchmarkForInLoop-8              	  657424	      1716 ns/op
-BenchmarkIfStatement-8            	 8168869	       141.7 ns/op
-BenchmarkIfStatement-8            	 8134052	       142.1 ns/op
-BenchmarkIfStatement-8            	 8244175	       141.7 ns/op
-BenchmarkIfStatement-8            	 7955036	       142.0 ns/op
-BenchmarkIfStatement-8            	 8185912	       141.8 ns/op
-BenchmarkCondExpr-8               	 7799468	       142.1 ns/op
-BenchmarkCondExpr-8               	 7967590	       142.5 ns/op
-BenchmarkCondExpr-8               	 8139943	       142.7 ns/op
-BenchmarkCondExpr-8               	 8131815	       142.5 ns/op
-BenchmarkCondExpr-8               	 8235750	       142.6 ns/op
-BenchmarkSimplePattern-8          	 9737238	       127.4 ns/op
-BenchmarkSimplePattern-8          	 9463335	       125.1 ns/op
-BenchmarkSimplePattern-8          	 9618474	       125.5 ns/op
-BenchmarkSimplePattern-8          	 9226118	       128.1 ns/op
-BenchmarkSimplePattern-8          	 9754975	       125.2 ns/op
-BenchmarkGetField-8               	 3786128	       324.1 ns/op
-BenchmarkGetField-8               	 3873116	       321.0 ns/op
-BenchmarkGetField-8               	 3774313	       325.5 ns/op
-BenchmarkGetField-8               	 3751617	       324.4 ns/op
-BenchmarkGetField-8               	 3791248	       322.5 ns/op
-BenchmarkSetField-8               	 1406820	       831.0 ns/op
-BenchmarkSetField-8               	 1421126	       846.1 ns/op
-BenchmarkSetField-8               	 1409583	       836.8 ns/op
-BenchmarkSetField-8               	 1431890	       839.3 ns/op
-BenchmarkSetField-8               	 1413070	       840.2 ns/op
-BenchmarkRegexMatch-8             	 1152942	      1004 ns/op
-BenchmarkRegexMatch-8             	 1000000	      1006 ns/op
-BenchmarkRegexMatch-8             	 1000000	      1009 ns/op
-BenchmarkRegexMatch-8             	 1000000	      1003 ns/op
-BenchmarkRegexMatch-8             	 1000000	      1002 ns/op
-BenchmarkBinaryOperators-8        	 3032761	       387.0 ns/op
-BenchmarkBinaryOperators-8        	 3043819	       387.5 ns/op
-BenchmarkBinaryOperators-8        	 3057975	       386.7 ns/op
-BenchmarkBinaryOperators-8        	 3055392	       388.2 ns/op
-BenchmarkBinaryOperators-8        	 3037488	       387.3 ns/op
-BenchmarkConcatTwo-8              	11896396	        97.29 ns/op
-BenchmarkConcatTwo-8              	11157866	        96.96 ns/op
-BenchmarkConcatTwo-8              	11935022	        96.83 ns/op
-BenchmarkConcatTwo-8              	11993353	        97.46 ns/op
-BenchmarkConcatTwo-8              	11445412	        96.61 ns/op
-BenchmarkConcatSmall-8            	 3521978	       342.0 ns/op
-BenchmarkConcatSmall-8            	 3507642	       337.2 ns/op
-BenchmarkConcatSmall-8            	 3517893	       336.7 ns/op
-BenchmarkConcatSmall-8            	 3194367	       350.0 ns/op
-BenchmarkConcatSmall-8            	 3291675	       337.5 ns/op
-BenchmarkConcatLarge-8            	    1004	   1035545 ns/op
-BenchmarkConcatLarge-8            	    1005	   1026616 ns/op
-BenchmarkConcatLarge-8            	    1015	   1034306 ns/op
-BenchmarkConcatLarge-8            	    1230	    941629 ns/op
-BenchmarkConcatLarge-8            	    1017	   1023111 ns/op
-BenchmarkComparisons-8            	 3773941	       308.7 ns/op
-BenchmarkComparisons-8            	 4061024	       290.9 ns/op
-BenchmarkComparisons-8            	 4066826	       291.1 ns/op
-BenchmarkComparisons-8            	 4043449	       291.5 ns/op
-BenchmarkComparisons-8            	 3971262	       290.5 ns/op
-BenchmarkArrayOperations-8        	 2205331	       540.3 ns/op
-BenchmarkArrayOperations-8        	 2201574	       538.5 ns/op
-BenchmarkArrayOperations-8        	 2194254	       540.0 ns/op
-BenchmarkArrayOperations-8        	 2208361	       538.9 ns/op
-BenchmarkArrayOperations-8        	 2203152	       541.8 ns/op
-BenchmarkAssign-8                 	 4936855	       238.8 ns/op
-BenchmarkAssign-8                 	 4919559	       238.4 ns/op
-BenchmarkAssign-8                 	 4926692	       239.4 ns/op
-BenchmarkAssign-8                 	 4915062	       238.1 ns/op
-BenchmarkAssign-8                 	 4941153	       237.4 ns/op
-BenchmarkAugAssign-8              	 1273117	       934.6 ns/op
-BenchmarkAugAssign-8              	 1276141	       933.9 ns/op
-BenchmarkAugAssign-8              	 1272656	       932.5 ns/op
-BenchmarkAugAssign-8              	 1272039	       933.6 ns/op
-BenchmarkAugAssign-8              	 1273455	       933.0 ns/op
-BenchmarkPrint-8                  	  547071	      2205 ns/op
-BenchmarkPrint-8                  	  557444	      2209 ns/op
-BenchmarkPrint-8                  	  542422	      2238 ns/op
-BenchmarkPrint-8                  	  542407	      2231 ns/op
-BenchmarkPrint-8                  	  554470	      2267 ns/op
-BenchmarkPrintf-8                 	  262456	      4443 ns/op
-BenchmarkPrintf-8                 	  259258	      4470 ns/op
-BenchmarkPrintf-8                 	  255230	      4470 ns/op
-BenchmarkPrintf-8                 	  263700	      4472 ns/op
-BenchmarkPrintf-8                 	  262808	      4476 ns/op
-BenchmarkRepeatExecProgram-8      	   87577	     14812 ns/op
-BenchmarkRepeatExecProgram-8      	   82438	     15093 ns/op
-BenchmarkRepeatExecProgram-8      	   82806	     15456 ns/op
-BenchmarkRepeatExecProgram-8      	   79336	     14083 ns/op
-BenchmarkRepeatExecProgram-8      	   82671	     13761 ns/op
-BenchmarkRepeatNew-8              	12579566	        89.73 ns/op
-BenchmarkRepeatNew-8              	13093694	        84.74 ns/op
-BenchmarkRepeatNew-8              	13143078	        84.38 ns/op
-BenchmarkRepeatNew-8              	13059631	        84.43 ns/op
-BenchmarkRepeatNew-8              	12768768	        84.52 ns/op
-BenchmarkRepeatIOExecProgram-8    	   49746	     22612 ns/op
-BenchmarkRepeatIOExecProgram-8    	   50871	     22640 ns/op
-BenchmarkRepeatIOExecProgram-8    	   50836	     22655 ns/op
-BenchmarkRepeatIOExecProgram-8    	   50204	     22617 ns/op
-BenchmarkRepeatIOExecProgram-8    	   50152	     22743 ns/op
-BenchmarkRepeatIONew-8            	 1000000	      1114 ns/op
-BenchmarkRepeatIONew-8            	 1000000	      1109 ns/op
-BenchmarkRepeatIONew-8            	 1000000	      1111 ns/op
-BenchmarkRepeatIONew-8            	 1000000	      1109 ns/op
-BenchmarkRepeatIONew-8            	 1000000	      1122 ns/op
+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
 PASS
-ok  	github.com/benhoyt/goawk/interp	320.788s
+ok  	github.com/benhoyt/goawk/interp	319.752s
diff --git a/fuzz/README.txt b/fuzz/README.txt
deleted file mode 100644
index f057bf2..0000000
--- a/fuzz/README.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-To run the fuzzer, first:
-
-    $ go get github.com/dvyukov/go-fuzz
-
-Then build the fuzzer zip file:
-
-    $ go-fuzz-build github.com/benhoyt/goawk/fuzz/interp
-
-And finally run the fuzzer (with 6 parallel processes in this example):
-
-    $ go-fuzz -bin=fuzz-fuzz.zip -workdir=fuzz/interp/workdir -procs=6
diff --git a/fuzz/interp/interp.go b/fuzz/interp/interp.go
deleted file mode 100644
index 4c1ffc4..0000000
--- a/fuzz/interp/interp.go
+++ /dev/null
@@ -1,17 +0,0 @@
-// +build gofuzz
-
-package fuzz
-
-import (
-	"bytes"
-	"github.com/benhoyt/goawk/interp"
-)
-
-func Fuzz(data []byte) int {
-	input := bytes.NewReader([]byte("foo bar\nbaz buz\n"))
-	err := interp.Exec(string(data), " ", input, &bytes.Buffer{})
-	if err != nil {
-		return 0
-	}
-	return 1
-}
diff --git a/fuzz/parser/parser.go b/fuzz/parser/parser.go
deleted file mode 100644
index e1133b2..0000000
--- a/fuzz/parser/parser.go
+++ /dev/null
@@ -1,15 +0,0 @@
-// +build gofuzz
-
-package fuzz
-
-import (
-	"github.com/benhoyt/goawk/parser"
-)
-
-func Fuzz(data []byte) int {
-	if _, err := parser.ParseProgram(data, nil); err != nil {
-		return 0
-	}
-
-	return 1
-}
diff --git a/interp/interp.go b/interp/interp.go
index 87898fd..9278cc6 100644
--- a/interp/interp.go
+++ b/interp/interp.go
@@ -13,6 +13,7 @@ package interp
 
 import (
 	"bufio"
+	"context"
 	"errors"
 	"fmt"
 	"io"
@@ -127,6 +128,12 @@ type interp struct {
 	strs      []string
 	regexes   []*regexp.Regexp
 
+	// Context support (for Interpreter.ExecuteContext)
+	checkCtx bool
+	ctx      context.Context
+	ctxDone  <-chan struct{}
+	ctxOps   int
+
 	// Misc pieces of state
 	random      *rand.Rand
 	randSeed    float64
@@ -361,19 +368,37 @@ func (p *interp) executeAll() (int, error) {
 	// Execute the program: BEGIN, then pattern/actions, then END
 	err := p.execute(p.program.Compiled.Begin)
 	if err != nil && err != errExit {
+		if p.checkCtx {
+			ctxErr := p.checkContextNow()
+			if ctxErr != nil {
+				return 0, ctxErr
+			}
+		}
 		return 0, err
 	}
 	if p.program.Actions == nil && p.program.End == nil {
-		return p.exitStatus, nil
+		return p.exitStatus, nil // only BEGIN specified, don't process input
 	}
 	if err != errExit {
 		err = p.execActions(p.program.Compiled.Actions)
 		if err != nil && err != errExit {
+			if p.checkCtx {
+				ctxErr := p.checkContextNow()
+				if ctxErr != nil {
+					return 0, ctxErr
+				}
+			}
 			return 0, err
 		}
 	}
 	err = p.execute(p.program.Compiled.End)
 	if err != nil && err != errExit {
+		if p.checkCtx {
+			ctxErr := p.checkContextNow()
+			if ctxErr != nil {
+				return 0, ctxErr
+			}
+		}
 		return 0, err
 	}
 	return p.exitStatus, nil
diff --git a/interp/newexecute.go b/interp/newexecute.go
index f94f812..fe46bd9 100644
--- a/interp/newexecute.go
+++ b/interp/newexecute.go
@@ -3,11 +3,14 @@
 package interp
 
 import (
+	"context"
 	"math"
 
 	"github.com/benhoyt/goawk/parser"
 )
 
+const checkContextOps = 1000 // for efficiency, only check context every N instructions
+
 // Interpreter is an interpreter for a specific program, allowing you to
 // efficiently execute the same program over and over with different inputs.
 // Use New to create an Interpreter.
@@ -47,6 +50,7 @@ func New(program *parser.Program) (*Interpreter, error) {
 // parser.ParseProgram, and must not change between calls to Execute.
 func (p *Interpreter) Execute(config *Config) (int, error) {
 	p.interp.resetCore()
+	p.interp.checkCtx = false
 
 	err := p.interp.setExecuteConfig(config)
 	if err != nil {
@@ -131,3 +135,42 @@ func (p *Interpreter) ResetRand() {
 	p.interp.randSeed = 1.0
 	p.interp.random.Seed(int64(math.Float64bits(p.interp.randSeed)))
 }
+
+// ExecuteContext is like Execute, but takes a context to allow the caller to
+// set an execution timeout or cancel the execution. For efficiency, the
+// context is only tested every 1000 virtual machine instructions.
+//
+// Context handling is not preemptive: currently long-running operations like
+// system() won't be interrupted.
+func (p *Interpreter) ExecuteContext(ctx context.Context, config *Config) (int, error) {
+	p.interp.resetCore()
+	p.interp.checkCtx = ctx != context.Background()
+	p.interp.ctx = ctx
+	p.interp.ctxDone = ctx.Done()
+	p.interp.ctxOps = 0
+
+	err := p.interp.setExecuteConfig(config)
+	if err != nil {
+		return 0, err
+	}
+
+	return p.interp.executeAll()
+}
+
+func (p *interp) checkContext() error {
+	p.ctxOps++
+	if p.ctxOps < checkContextOps {
+		return nil
+	}
+	p.ctxOps = 0
+	return p.checkContextNow()
+}
+
+func (p *interp) checkContextNow() error {
+	select {
+	case <-p.ctxDone:
+		return p.ctx.Err()
+	default:
+		return nil
+	}
+}
diff --git a/interp/vm.go b/interp/vm.go
index 1a9ac96..d9969eb 100644
--- a/interp/vm.go
+++ b/interp/vm.go
@@ -32,6 +32,13 @@ func (p *interp) execute(code []compiler.Opcode) error {
 		op := code[ip]
 		ip++
 
+		if p.checkCtx {
+			err := p.checkContext()
+			if err != nil {
+				return err
+			}
+		}
+
 		switch op {
 		case compiler.Num:
 			index := code[ip]
@@ -878,6 +885,7 @@ func (p *interp) execute(code []compiler.Opcode) error {
 			p.replaceTop(num(ret))
 		}
 	}
+
 	return nil
 }
 
diff --git a/benchmark.sh b/scripts/benchmark.sh
similarity index 100%
rename from benchmark.sh
rename to scripts/benchmark.sh
diff --git a/benchmark_awks.py b/scripts/benchmark_awks.py
similarity index 100%
rename from benchmark_awks.py
rename to scripts/benchmark_awks.py
diff --git a/benchstat.sh b/scripts/benchstat.sh
similarity index 100%
rename from benchstat.sh
rename to scripts/benchstat.sh
diff --git a/scripts/fuzz_input.sh b/scripts/fuzz_input.sh
new file mode 100755
index 0000000..49bde8e
--- /dev/null
+++ b/scripts/fuzz_input.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+go1.18rc1 test ./interp -run=^$ -fuzz=Input -parallel=4
diff --git a/scripts/fuzz_source.sh b/scripts/fuzz_source.sh
new file mode 100755
index 0000000..ee87e50
--- /dev/null
+++ b/scripts/fuzz_source.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+go1.18rc1 test ./interp -run=^$ -fuzz=Source -parallel=4


Test patch:
diff --git a/interp/fuzz_test.go b/interp/fuzz_test.go
index 3471932..e402b38 100644
--- a/interp/fuzz_test.go
+++ b/interp/fuzz_test.go
@@ -1,7 +1,4 @@
-// Fuzz tests for the "go test -fuzz" beta: https://go.dev/blog/fuzz-beta
-//
-// These are extremely simple right now, and really only test for parser and
-// interpreter panics.
+// Fuzz tests for use with the Go 1.18 fuzzer.
 
 //go:build go1.18
 // +build go1.18
@@ -9,8 +6,12 @@
 package interp_test
 
 import (
+	"context"
+	"fmt"
+	"io/ioutil"
 	"strings"
 	"testing"
+	"time"
 
 	"github.com/benhoyt/goawk/interp"
 	"github.com/benhoyt/goawk/parser"
@@ -20,41 +21,87 @@ func isFuzzTest(test interpTest) bool {
 	return test.err == "" && test.awkErr == "" && !strings.Contains(test.src, "!fuzz")
 }
 
-func execForFuzz(src, in string) {
-	prog, err := parser.ParseProgram([]byte(src), nil)
-	if err != nil {
-		return
-	}
-	outBuf := &concurrentBuffer{}
-	config := &interp.Config{
-		Stdin:        strings.NewReader(in),
-		Output:       outBuf,
-		Error:        outBuf,
-		NoExec:       true,
-		NoFileWrites: true,
-		NoFileReads:  true,
-	}
-	_, _ = interp.ExecProgram(prog, config)
-}
-
 func FuzzSource(f *testing.F) {
 	for _, test := range interpTests {
 		if isFuzzTest(test) {
 			f.Add(test.src)
 		}
 	}
+
 	f.Fuzz(func(t *testing.T, src string) {
-		execForFuzz(src, "")
+		prog, err := parser.ParseProgram([]byte(src), nil)
+		if err != nil {
+			return
+		}
+		interpreter, err := interp.New(prog)
+		if err != nil {
+			f.Fatalf("interp.New error: %v", err)
+		}
+		config := interp.Config{
+			Stdin:        strings.NewReader("foo bar\nbazz\n"),
+			Output:       ioutil.Discard,
+			Error:        ioutil.Discard,
+			NoExec:       true,
+			NoFileWrites: true,
+			NoFileReads:  true,
+			Environ:      []string{},
+		}
+		ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
+		defer cancel()
+		_, _ = interpreter.ExecuteContext(ctx, &config)
 	})
 }
 
-func FuzzBoth(f *testing.F) {
+func FuzzInput(f *testing.F) {
+	f.Add("")
+	added := make(map[string]bool)
 	for _, test := range interpTests {
-		if isFuzzTest(test) {
-			f.Add(test.src, test.in)
+		if test.in != "" && !added[test.in] {
+			f.Add(test.in)
+			added[test.in] = true
 		}
 	}
-	f.Fuzz(func(t *testing.T, src, in string) {
-		execForFuzz(src, in)
+
+	prog, err := parser.ParseProgram([]byte(`{ print $0, $3, $1, $10 }`), nil)
+	if err != nil {
+		f.Fatalf("parse error: %v", err)
+	}
+
+	interpreter, err := interp.New(prog)
+	if err != nil {
+		f.Fatalf("interp.New error: %v", err)
+	}
+
+	var vars = [][]string{
+		{"FS", " ", "RS", "\n"},
+		{"FS", ",", "RS", "\n"},
+		{"FS", "\t", "RS", "\n"},
+		{"FS", "@+", "RS", "\n"},
+		{"FS", "\n", "RS", ""},
+		{"FS", " ", "RS", "X+"},
+	}
+
+	f.Fuzz(func(t *testing.T, in string) {
+		for _, v := range vars {
+			t.Run(fmt.Sprintf("Vars=%q", v), func(t *testing.T) {
+				interpreter.ResetVars()
+				config := interp.Config{
+					Stdin:        strings.NewReader(in),
+					Output:       ioutil.Discard,
+					Error:        ioutil.Discard,
+					Vars:         v,
+					NoExec:       true,
+					NoFileWrites: true,
+					NoFileReads:  true,
+					Environ:      []string{},
+				}
+				ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
+				defer cancel()
+				_, err := interpreter.ExecuteContext(ctx, &config)
+				if err != nil {
+					t.Fatalf("execute error: %v", err)
+				}
+			})
+		}
 	})
 }
diff --git a/interp/interp_test.go b/interp/interp_test.go
index 017dcaa..a7c0b8b 100644
--- a/interp/interp_test.go
+++ b/interp/interp_test.go
@@ -129,6 +129,7 @@ NR==3, NR==5 { print NR }
 	{`BEGIN { for (i=3; i>0; i--) { printf "%d ", i } }`, "", "3 2 1 ", "", ""},
 	{`BEGIN { for (i=3; i>=0; i--) { printf "%d ", i } }`, "", "3 2 1 0 ", "", ""},
 	{`BEGIN { for (i=0; i<10; i++) { if (i < 5) continue; printf "%d ", i } }`, "", "5 6 7 8 9 ", "", ""},
+	{`BEGIN { for (i=0; i<100; i++) s+=i; print s }`, "", "4950\n", "", ""},
 	{`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", "", ""},
 	{`function alen(a, k, n) { n=0; for (k in a) n++; return n }  BEGIN { a[1]=1; a[2]=1; print alen(a) }`, "", "2\n", "", ""},
diff --git a/interp/newexecute_test.go b/interp/newexecute_test.go
index c6a3429..8fbf6d9 100644
--- a/interp/newexecute_test.go
+++ b/interp/newexecute_test.go
@@ -4,8 +4,11 @@ package interp_test
 
 import (
 	"bytes"
+	"context"
+	"errors"
 	"strings"
 	"testing"
+	"time"
 
 	"github.com/benhoyt/goawk/interp"
 	"github.com/benhoyt/goawk/parser"
@@ -14,14 +17,7 @@ import (
 // This definitely doesn't test that everything was reset, but it's a good start.
 func TestNewExecute(t *testing.T) {
 	source := `{ print NR, OFMT, x, y, a["k"], $1, $3; OFMT="%g"; x++; y++; a["k"]++ }`
-	program, err := parser.ParseProgram([]byte(source), nil)
-	if err != nil {
-		t.Fatalf("error parsing: %v", err)
-	}
-	interpreter, err := interp.New(program)
-	if err != nil {
-		t.Fatalf("error creating interpreter: %v", err)
-	}
+	interpreter := newInterp(t, source)
 
 	// First execution.
 	var output bytes.Buffer
@@ -83,18 +79,10 @@ func TestNewExecute(t *testing.T) {
 
 func TestResetRand(t *testing.T) {
 	source := `BEGIN { print rand(), rand(), rand() }`
-	program, err := parser.ParseProgram([]byte(source), nil)
-	if err != nil {
-		t.Fatalf("error parsing: %v", err)
-	}
-
-	interpreter, err := interp.New(program)
-	if err != nil {
-		t.Fatalf("error creating interpreter: %v", err)
-	}
+	interpreter := newInterp(t, source)
 	var output bytes.Buffer
 
-	_, err = interpreter.Execute(&interp.Config{Output: &output})
+	_, err := interpreter.Execute(&interp.Config{Output: &output})
 	if err != nil {
 		t.Fatalf("error executing: %v", err)
 	}
@@ -121,3 +109,44 @@ func TestResetRand(t *testing.T) {
 		t.Fatalf("expected same random numbers (%q) as original (%q)", withResetRand, original)
 	}
 }
+
+func TestExecuteContextNoError(t *testing.T) {
+	interpreter := newInterp(t, `BEGIN {}`)
+	_, err := interpreter.ExecuteContext(context.Background(), nil)
+	if err != nil {
+		t.Fatalf("execute error: %v", err)
+	}
+}
+
+func TestExecuteContextTimeout(t *testing.T) {
+	interpreter := newInterp(t, `BEGIN { for (i=0; i<100000000; i++) s+=i }`) // would take about 4s
+	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Millisecond)
+	defer cancel()
+	_, err := interpreter.ExecuteContext(ctx, nil)
+	if !errors.Is(err, context.DeadlineExceeded) {
+		t.Fatalf("expected DeadlineExceeded error, got: %v", err)
+	}
+}
+
+func TestExecuteContextCancel(t *testing.T) {
+	interpreter := newInterp(t, `BEGIN { for (i=0; i<100000000; i++) s+=i }`) // would take about 4s
+	ctx, cancel := context.WithCancel(context.Background())
+	cancel() // cancel it right away
+	_, err := interpreter.ExecuteContext(ctx, nil)
+	if !errors.Is(err, context.Canceled) {
+		t.Fatalf("expected Canceled error, got: %v", err)
+	}
+}
+
+func newInterp(t *testing.T, src string) *interp.Interpreter {
+	t.Helper()
+	prog, err := parser.ParseProgram([]byte(src), nil)
+	if err != nil {
+		t.Fatalf("parse error: %v", err)
+	}
+	interpreter, err := interp.New(prog)
+	if err != nil {
+		t.Fatalf("interp.New error: %v", err)
+	}
+	return interpreter
+}