View Javadoc
1   package org.sentrysoftware.jawk.backend;
2   
3   /*-
4    * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
5    * Jawk
6    * ჻჻჻჻჻჻
7    * Copyright (C) 2006 - 2023 Sentry Software
8    * ჻჻჻჻჻჻
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Lesser Public License for more details.
18   *
19   * You should have received a copy of the GNU General Lesser Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
23   */
24  
25  import java.io.FileOutputStream;
26  import java.io.IOException;
27  import java.io.PrintStream;
28  import java.io.StringReader;
29  import java.util.ArrayList;
30  import java.util.Arrays;
31  import java.util.Collections;
32  import java.util.Enumeration;
33  import java.util.HashMap;
34  import java.util.List;
35  import java.util.Locale;
36  import java.util.Map;
37  import java.util.Random;
38  import java.util.Set;
39  import java.util.StringTokenizer;
40  import java.util.regex.Matcher;
41  import java.util.regex.Pattern;
42  
43  import org.sentrysoftware.jawk.ExitException;
44  import org.sentrysoftware.jawk.ext.JawkExtension;
45  import org.sentrysoftware.jawk.frontend.AwkSyntaxTree;
46  import org.sentrysoftware.jawk.intermediate.Address;
47  import org.sentrysoftware.jawk.intermediate.AwkTuples;
48  import org.sentrysoftware.jawk.intermediate.Position;
49  import org.sentrysoftware.jawk.intermediate.PositionForInterpretation;
50  import org.sentrysoftware.jawk.intermediate.UninitializedObject;
51  import org.sentrysoftware.jawk.jrt.AssocArray;
52  import org.sentrysoftware.jawk.jrt.AwkRuntimeException;
53  import org.sentrysoftware.jawk.jrt.BlockManager;
54  import org.sentrysoftware.jawk.jrt.BlockObject;
55  import org.sentrysoftware.jawk.jrt.CharacterTokenizer;
56  import org.sentrysoftware.jawk.jrt.JRT;
57  import org.sentrysoftware.jawk.jrt.KeyList;
58  import org.sentrysoftware.jawk.jrt.KeyListImpl;
59  import org.sentrysoftware.jawk.jrt.ConditionPair;
60  import org.sentrysoftware.jawk.jrt.RegexTokenizer;
61  import org.sentrysoftware.jawk.jrt.SingleCharacterTokenizer;
62  import org.sentrysoftware.jawk.jrt.VariableManager;
63  import org.sentrysoftware.jawk.util.ArrayStackImpl;
64  import org.sentrysoftware.jawk.util.AwkLogger;
65  import org.sentrysoftware.jawk.util.AwkSettings;
66  import org.sentrysoftware.jawk.util.LinkedListStackImpl;
67  import org.sentrysoftware.jawk.util.MyStack;
68  import org.sentrysoftware.jawk.util.ScriptSource;
69  import org.sentrysoftware.printf4j.Printf4J;
70  import org.slf4j.Logger;
71  
72  /**
73   * The Jawk interpreter.
74   * <p>
75   * It takes tuples constructed by the intermediate step
76   * and executes each tuple in accordance to their instruction semantics.
77   * The tuples correspond to the Awk script compiled by the parser.
78   * The interpreter consists of an instruction processor (interpreter),
79   * a runtime stack, and machinery to support the instruction set
80   * contained within the tuples.
81   *
82   * <p>
83   * The interpreter runs completely independent of the frontend/intermediate step.
84   * In fact, an intermediate file produced by Jawk is sufficient to
85   * execute on this interpreter. The binding data-structure is
86   * the AwkSettings, which can contain options pertinent to
87   * the interpreter. For example, the interpreter must know about
88   * the -v command line argument values, as well as the file/variable list
89   * parameter values (ARGC/ARGV) after the script on the command line.
90   * However, if programmatic access to the AVM is required, meaningful
91   * AwkSettings are not required.
92   *
93   * <p>
94   * Semantic analysis has occurred prior to execution of the interpreter.
95   * Therefore, the interpreter throws AwkRuntimeExceptions upon most
96   * errors/conditions. It can also throw a <code>java.lang.Error</code> if an
97   * interpreter error is encountered.
98   *
99   * @author Danny Daglas
100  */
101 public class AVM implements AwkInterpreter, VariableManager {
102 
103 	private static final Logger LOG = AwkLogger.getLogger(AVM.class);
104 	private static final boolean IS_WINDOWS = (System.getProperty("os.name").indexOf("Windows") >= 0);
105 
106 	private RuntimeStack runtime_stack = new RuntimeStack();
107 
108 	// 16 slots by default
109 	// (could be a parameter)
110 	//private Deque<Object> operand_stack = new ArrayDeque<Object>(16);
111 	//private MyStack<Object> operand_stack = new LinkedListStackImpl<Object>();
112 	private MyStack<Object> operand_stack = new ArrayStackImpl<Object>();
113 	private java.util.List<String> arguments;
114 	private boolean sorted_array_keys;
115 	private Map<String, Object> initial_variables;
116 	private String initial_fs_value;
117 	private boolean trap_illegal_format_exceptions;
118 	private JRT jrt;
119 	final private Locale locale;
120 	private Map<String, JawkExtension> extensions;
121 
122 	// stack methods
123 	//private Object pop() { return operand_stack.removeFirst(); }
124 	//private void push(Object o) { operand_stack.addLast(o); }
125 	private Object pop() { return operand_stack.pop(); }
126 	private void push(Object o) { operand_stack.push(o); }
127 
128 	private final AwkSettings settings;
129 
130 	/**
131 	 * Construct the interpreter.
132 	 * <p>
133 	 * Provided to allow programmatic construction of the interpreter
134 	 * outside of the framework which is used by Jawk.
135 	 */
136 	public AVM() {
137 		settings = null;
138 		arguments = new ArrayList<String>();
139 		sorted_array_keys = false;
140 		initial_variables = new HashMap<String, Object>();
141 		initial_fs_value = null;
142 		trap_illegal_format_exceptions = false;
143 		jrt = new JRT(this);	// this = VariableManager
144 		locale = Locale.getDefault();
145 		this.extensions = Collections.emptyMap();
146 	}
147 
148 	/**
149 	 * Construct the interpreter, accepting parameters which may have been
150 	 * set on the command-line arguments to the JVM.
151 	 *
152 	 * @param parameters The parameters affecting the behavior of the
153 	 *	interpreter.
154 	 * @param extensions Map of the extensions to load
155 	 */
156 	public AVM(final AwkSettings parameters, final Map<String, JawkExtension> extensions) {
157 		if (parameters == null) {
158 			throw new IllegalArgumentException("AwkSettings can not be null");
159 		}
160 		this.settings = parameters;
161 		locale = settings.getLocale();
162 		arguments = parameters.getNameValueOrFileNames();
163 		sorted_array_keys = parameters.isUseSortedArrayKeys();
164 		initial_variables = parameters.getVariables();
165 		initial_fs_value = parameters.getFieldSeparator();
166 		trap_illegal_format_exceptions = parameters.isCatchIllegalFormatExceptions();
167 		jrt = new JRT(this);	// this = VariableManager
168 		this.extensions = extensions;
169 		for (JawkExtension ext : extensions.values()) {
170 			ext.init(this, jrt, settings);	// this = VariableManager
171 		}
172 	}
173 
174 	private long nf_offset = NULL_OFFSET;
175 	private long nr_offset = NULL_OFFSET;
176 	private long fnr_offset = NULL_OFFSET;
177 	private long fs_offset = NULL_OFFSET;
178 	private long rs_offset = NULL_OFFSET;
179 	private long ofs_offset = NULL_OFFSET;
180 	private long ors_offset = NULL_OFFSET;
181 	private long rstart_offset = NULL_OFFSET;
182 	private long rlength_offset = NULL_OFFSET;
183 	private long filename_offset = NULL_OFFSET;
184 	private long subsep_offset = NULL_OFFSET;
185 	private long convfmt_offset = NULL_OFFSET;
186 	private long ofmt_offset = NULL_OFFSET;
187 	private long environ_offset = NULL_OFFSET;
188 	private long argc_offset = NULL_OFFSET;
189 	private long argv_offset = NULL_OFFSET;
190 
191 	private static final Integer ZERO = Integer.valueOf(0);
192 	private static final Integer ONE = Integer.valueOf(1);
193 
194 	private Random random_number_generator;
195 	private int oldseed;
196 
197 	private Address exit_address = null;
198 	
199 	/**
200 	 * <code>true</code> if execution position is within an END block;
201 	 * <code>false</code> otherwise.
202 	 */
203 	private boolean within_end_blocks = false;
204 
205 	/**
206 	 * Exit code set by the <code>exit NN</code> command (0 by default)
207 	 */
208 	private int exit_code = 0;
209 	
210 	/**
211 	 * Whether <code>exit</code> has been called and we should throw ExitException
212 	 */
213 	private boolean throw_exit_exception = false;
214 	
215 	/**
216 	 * Maps global variable names to their global array offsets.
217 	 * It is useful when passing variable assignments from the file-list
218 	 * portion of the command-line arguments.
219 	 */
220 	private Map<String, Integer> global_variable_offsets;
221 	/**
222 	 * Indicates whether the variable, by name, is a scalar
223 	 * or not. If not, then it is an Associative Array.
224 	 */
225 	private Map<String, Boolean> global_variable_arrays;
226 	private Set<String> function_names;
227 
228 	private static int parseIntField(Object obj, PositionForInterpretation position) {
229 
230 		int fieldVal;
231 
232 		if (obj instanceof Number) {
233 			fieldVal = ((Number) obj).intValue();
234 		} else {
235 			try {
236 				fieldVal = (int) Double.parseDouble(obj.toString());
237 			} catch (NumberFormatException nfe) {
238 				throw new AwkRuntimeException(position.lineNumber(), "Field $(" + obj.toString() + ") is incorrect.");
239 			}
240 		}
241 
242 		return fieldVal;
243 	}
244 
245 	private void setNumOnJRT(int fieldNum, double num) {
246 
247 		String numString;
248 		if (num == (long) num) {
249 			numString = Long.toString((long) num);
250 		} else {
251 			numString = Double.toString(num);
252 		}
253 
254 		// same code as _ASSIGN_AS_INPUT_FIELD_
255 		if (fieldNum == 0) {
256 			jrt.setInputLine(numString.toString());
257 			jrt.jrtParseFields();
258 		} else {
259 			jrt.jrtSetInputField(numString, fieldNum);
260 		}
261 	}
262 
263 	private String execSubOrGSub(PositionForInterpretation position, int gsubArgPos) {
264 
265 		String newString;
266 
267 		// arg[gsubArgPos] = is_gsub
268 		// stack[0] = original field value
269 		// stack[1] = replacement string
270 		// stack[2] = ere
271 		boolean is_gsub = position.boolArg(gsubArgPos);
272 		String convfmt = getCONVFMT().toString();
273 		String orig = JRT.toAwkString(pop(), convfmt, locale);
274 		String repl = JRT.toAwkString(pop(), convfmt, locale);
275 		String ere = JRT.toAwkString(pop(), convfmt, locale);
276 		if (is_gsub) {
277 			newString = replaceAll(orig, ere, repl);
278 		} else {
279 			newString = replaceFirst(orig, ere, repl);
280 		}
281 
282 		return newString;
283 	}
284 
285 	/**
286 	 * {@inheritDoc}
287 	 *
288 	 * Traverse the tuples, executing their associated opcodes to provide
289 	 * an execution platform for Jawk scripts.
290 	 * @throws IOException in case of I/O problems (with getline typically)
291 	 */
292 	@Override
293 	public void interpret(AwkTuples tuples)
294 			throws ExitException, IOException
295 	{
296 		Map<String, Pattern> regexps = new HashMap<String, Pattern>();
297 		Map<Integer, ConditionPair> condition_pairs = new HashMap<Integer, ConditionPair>();
298 
299 		global_variable_offsets = tuples.getGlobalVariableOffsetMap();
300 		global_variable_arrays = tuples.getGlobalVariableAarrayMap();
301 		function_names = tuples.getFunctionNameSet();
302 
303 		PositionForInterpretation position = (PositionForInterpretation) tuples.top();
304 
305 		try {
306 			while (!position.isEOF()) {
307 				//System_out.println("--> "+position);
308 				int opcode = position.opcode();
309 				// switch on OPCODE
310 				switch (opcode) {
311 					case AwkTuples._PRINT_: {
312 						// arg[0] = # of items to print on the stack
313 						// stack[0] = item 1
314 						// stack[1] = item 2
315 						// etc.
316 						long num_args = position.intArg(0);
317 						printTo(settings.getOutputStream(), num_args);
318 						position.next();
319 						break;
320 					}
321 					case AwkTuples._PRINT_TO_FILE_: {
322 						// arg[0] = # of items to print on the stack
323 						// arg[1] = true=append, false=overwrite
324 						// stack[0] = output filename
325 						// stack[1] = item 1
326 						// stack[2] = item 2
327 						// etc.
328 						long num_args = position.intArg(0);
329 						boolean append = position.boolArg(1);
330 						String key = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
331 						PrintStream ps = jrt.getOutputFiles().get(key);
332 						if (ps == null) {
333 							try {
334 								jrt.getOutputFiles().put(key, ps = new PrintStream(new FileOutputStream(key, append), true));	// true = autoflush
335 							} catch (IOException ioe) {
336 								throw new AwkRuntimeException(position.lineNumber(), "Cannot open " + key + " for writing: " + ioe);
337 							}
338 						}
339 						printTo(ps, num_args);
340 						position.next();
341 						break;
342 					}
343 					case AwkTuples._PRINT_TO_PIPE_: {
344 						// arg[0] = # of items to print on the stack
345 						// stack[0] = command to execute
346 						// stack[1] = item 1
347 						// stack[2] = item 2
348 						// etc.
349 						long num_args = position.intArg(0);
350 						String cmd = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
351 						PrintStream ps = jrt.jrtSpawnForOutput(cmd);
352 						printTo(ps, num_args);
353 						position.next();
354 						break;
355 					}
356 					case AwkTuples._PRINTF_: {
357 						// arg[0] = # of items to print on the stack (includes format string)
358 						// stack[0] = format string
359 						// stack[1] = item 1
360 						// etc.
361 						long num_args = position.intArg(0);
362 						printfTo(settings.getOutputStream(), num_args);
363 						position.next();
364 						break;
365 					}
366 					case AwkTuples._PRINTF_TO_FILE_: {
367 						// arg[0] = # of items to print on the stack (includes format string)
368 						// arg[1] = true=append, false=overwrite
369 						// stack[0] = output filename
370 						// stack[1] = format string
371 						// stack[2] = item 1
372 						// etc.
373 						long num_args = position.intArg(0);
374 						boolean append = position.boolArg(1);
375 						String key = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
376 						PrintStream ps = jrt.getOutputFiles().get(key);
377 						if (ps == null) {
378 							try {
379 								jrt.getOutputFiles().put(key, ps = new PrintStream(new FileOutputStream(key, append), true));	// true = autoflush
380 							} catch (IOException ioe) {
381 								throw new AwkRuntimeException(position.lineNumber(), "Cannot open " + key + " for writing: " + ioe);
382 							}
383 						}
384 						printfTo(ps, num_args);
385 						position.next();
386 						break;
387 					}
388 					case AwkTuples._PRINTF_TO_PIPE_: {
389 						// arg[0] = # of items to print on the stack (includes format string)
390 						// stack[0] = command to execute
391 						// stack[1] = format string
392 						// stack[2] = item 1
393 						// etc.
394 						long num_args = position.intArg(0);
395 						String cmd = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
396 						PrintStream ps = jrt.jrtSpawnForOutput(cmd);
397 						printfTo(ps, num_args);
398 						position.next();
399 						break;
400 					}
401 					case AwkTuples._SPRINTF_: {
402 						// arg[0] = # of sprintf arguments
403 						// stack[0] = arg1 (format string)
404 						// stack[1] = arg2
405 						// etc.
406 						long num_args = position.intArg(0);
407 						push(sprintfFunction(num_args));
408 						position.next();
409 						break;
410 					}
411 					case AwkTuples._LENGTH_: {
412 
413 						// arg[0] = 0==use $0, otherwise, use the stack element
414 						// stack[0] = element to measure (only if arg[0] != 0)
415 
416 						// print items from the top of the stack
417 						// # of items
418 						long num = position.intArg(0);
419 						if (num == 0) {
420 							// display $0
421 							push(jrt.jrtGetInputField(0).toString().length());
422 						} else {
423 							push(pop().toString().length());
424 						}
425 						position.next();
426 						break;
427 					}
428 					case AwkTuples._PUSH_: {
429 						// arg[0] = constant to push onto the stack
430 						push(position.arg(0));
431 						position.next();
432 						break;
433 					}
434 					case AwkTuples._POP_: {
435 						// stack[0] = item to pop from the stack
436 						pop();
437 						position.next();
438 						break;
439 					}
440 					case AwkTuples._IFFALSE_: {
441 						// arg[0] = address to jump to if top of stack is false
442 						// stack[0] = item to check
443 
444 						// if int, then check for 0
445 						// if double, then check for 0
446 						// if String, then check for "" or double value of "0"
447 						boolean jump = !jrt.toBoolean(pop());
448 						if (jump) {
449 							position.jump(position.addressArg());
450 						} else {
451 							position.next();
452 						}
453 						break;
454 					}
455 					case AwkTuples._TO_NUMBER_: {
456 						// stack[0] = item to convert to a number
457 
458 						// if int, then check for 0
459 						// if double, then check for 0
460 						// if String, then check for "" or double value of "0"
461 						boolean val = jrt.toBoolean(pop());
462 						push(val ? ONE : ZERO);
463 						position.next();
464 						break;
465 					}
466 					case AwkTuples._IFTRUE_: {
467 						// arg[0] = address to jump to if top of stack is true
468 						// stack[0] = item to check
469 
470 						// if int, then check for 0
471 						// if double, then check for 0
472 						// if String, then check for "" or double value of "0"
473 						boolean jump = jrt.toBoolean(pop());
474 						if (jump) {
475 							position.jump(position.addressArg());
476 						} else {
477 							position.next();
478 						}
479 						break;
480 					}
481 					case AwkTuples._NOT_: {
482 						// stack[0] = item to logically negate
483 
484 						// if int, then check for 0
485 						// if double, then check for 0
486 						// if String, then check for "" or double value of "0"
487 						Object o = pop();
488 						boolean result;
489 						if (o instanceof Integer) {
490 							result = ((Integer)o).intValue() != 0;
491 						} else if (o instanceof Long) {
492 							result = ((Long)o).longValue() != 0;
493 						} else if (o instanceof Double) {
494 							result = ((Double)o).doubleValue() != 0;
495 						} else if (o instanceof String) {
496 							result = (o.toString().length() > 0);
497 						} else if (o instanceof UninitializedObject) {
498 							result = false;
499 						} else {
500 							throw new Error("Unknown operand_stack type: "+o.getClass()+" for value "+o);
501 						}
502 						if (result) {
503 							push(0);
504 						} else {
505 							push(1);
506 						}
507 						position.next();
508 						break;
509 					}
510 					case AwkTuples._NEGATE_: {
511 						// stack[0] = item to numerically negate
512 
513 						double d = JRT.toDouble(pop());
514 						if (d == (long) d) {
515 							push((long) -d);
516 						} else {
517 							push(-d);
518 						}
519 						position.next();
520 						break;
521 					}
522 					case AwkTuples._UNARY_PLUS_: {
523 						// stack[0] = item to convert to a number
524 						double d = JRT.toDouble(pop());
525 						if (d == (long) d) {
526 							push((long)d);
527 						} else {
528 							push(d);
529 						}
530 						position.next();
531 						break;
532 					}
533 					case AwkTuples._GOTO_: {
534 						// arg[0] = address
535 
536 						position.jump(position.addressArg());
537 						break;
538 					}
539 					case AwkTuples._NOP_: {
540 						// do nothing, just advance the position
541 						position.next();
542 						break;
543 					}
544 					case AwkTuples._CONCAT_: {
545 						// stack[0] = string1
546 						// stack[1] = string2
547 						String convfmt = getCONVFMT().toString();
548 						String s2 = JRT.toAwkString(pop(), convfmt, locale);
549 						String s1 = JRT.toAwkString(pop(), convfmt, locale);
550 						String result_string = s1 + s2;
551 						push(result_string);
552 						position.next();
553 						break;
554 					}
555 					case AwkTuples._ASSIGN_: {
556 						// arg[0] = offset
557 						// arg[1] = is_global
558 						// stack[0] = value
559 						Object value = pop();
560 						boolean is_global = position.boolArg(1);
561 						assign(position.intArg(0), value, is_global, position);
562 						position.next();
563 						break;
564 					}
565 					case AwkTuples._ASSIGN_ARRAY_: {
566 						// arg[0] = offset
567 						// arg[1] = is_global
568 						// stack[0] = array index
569 						// stack[1] = value
570 						Object arr_idx = pop();
571 						Object rhs = pop();
572 						if (rhs == null) {
573 							rhs = BLANK;
574 						}
575 						long offset = position.intArg(0);
576 						boolean is_global = position.boolArg(1);
577 						assignArray(offset, arr_idx, rhs, is_global);
578 						position.next();
579 						break;
580 					}
581 					case AwkTuples._PLUS_EQ_ARRAY_:
582 					case AwkTuples._MINUS_EQ_ARRAY_:
583 					case AwkTuples._MULT_EQ_ARRAY_:
584 					case AwkTuples._DIV_EQ_ARRAY_:
585 					case AwkTuples._MOD_EQ_ARRAY_:
586 					case AwkTuples._POW_EQ_ARRAY_: {
587 						// arg[0] = offset
588 						// arg[1] = is_global
589 						// stack[0] = array index
590 						// stack[1] = value
591 						Object arr_idx = pop();
592 						Object rhs = pop();
593 						if (rhs == null) {
594 							rhs = BLANK;
595 						}
596 						long offset = position.intArg(0);
597 						boolean is_global = position.boolArg(1);
598 
599 						double val = JRT.toDouble(rhs);
600 
601 						// from _DEREF_ARRAY_
602 						// stack[0] = AssocArray
603 						// stack[1] = array index
604 						Object o1 = runtime_stack.getVariable(offset, is_global);	// map
605 						if (o1 == null || o1 instanceof UninitializedObject) {
606 							runtime_stack.setVariable(offset, o1 = new AssocArray(sorted_array_keys), is_global);
607 						} else {
608 							assert o1 instanceof AssocArray;
609 						}
610 
611 						AssocArray array = (AssocArray) o1;
612 						Object o = array.get(arr_idx);
613 						assert o != null;
614 						double orig_val = JRT.toDouble(o);
615 
616 						double new_val;
617 
618 						switch (opcode) {
619 							case AwkTuples._PLUS_EQ_ARRAY_:
620 								new_val = orig_val + val;
621 								break;
622 							case AwkTuples._MINUS_EQ_ARRAY_:
623 								new_val = orig_val - val;
624 								break;
625 							case AwkTuples._MULT_EQ_ARRAY_:
626 								new_val = orig_val * val;
627 								break;
628 							case AwkTuples._DIV_EQ_ARRAY_:
629 								new_val = orig_val / val;
630 								break;
631 							case AwkTuples._MOD_EQ_ARRAY_:
632 								new_val = orig_val % val;
633 								break;
634 							case AwkTuples._POW_EQ_ARRAY_:
635 								new_val = Math.pow(orig_val, val);
636 								break;
637 							default:
638 								throw new Error("Invalid op code here: " + opcode);
639 						}
640 
641 						if (new_val == (long) new_val) {
642 							assignArray(offset, arr_idx, (long) new_val, is_global);
643 						} else {
644 							assignArray(offset, arr_idx, new_val, is_global);
645 						}
646 						position.next();
647 						break;
648 					}
649 					case AwkTuples._ASSIGN_AS_INPUT_: {
650 						// stack[0] = value
651 						jrt.setInputLine(pop().toString());
652 						jrt.jrtParseFields();
653 						push(jrt.getInputLine());
654 						position.next();
655 						break;
656 					}
657 					case AwkTuples._ASSIGN_AS_INPUT_FIELD_: {
658 						// stack[0] = field number
659 						// stack[1] = value
660 						Object field_num_obj = pop();
661 						int field_num;
662 						if (field_num_obj instanceof Number) {
663 							field_num = ((Number) field_num_obj).intValue();
664 						} else {
665 							try {
666 								field_num = Integer.parseInt(field_num_obj.toString());
667 							} catch (NumberFormatException nfe) {
668 								field_num = 0;
669 							}
670 						}
671 						String value = pop().toString();
672 						push(value);	// leave the result on the stack
673 						if (field_num == 0) {
674 							jrt.setInputLine(value);
675 							jrt.jrtParseFields();
676 						} else {
677 							jrt.jrtSetInputField(value, field_num);
678 						}
679 						position.next();
680 						break;
681 					}
682 					case AwkTuples._PLUS_EQ_:
683 					case AwkTuples._MINUS_EQ_:
684 					case AwkTuples._MULT_EQ_:
685 					case AwkTuples._DIV_EQ_:
686 					case AwkTuples._MOD_EQ_:
687 					case AwkTuples._POW_EQ_: {
688 						// arg[0] = offset
689 						// arg[1] = is_global
690 						// stack[0] = value
691 						boolean is_global = position.boolArg(1);
692 						Object o1 = runtime_stack.getVariable(position.intArg(0), is_global);
693 						if (o1 == null) {
694 							o1 = BLANK;
695 						}
696 						Object o2 = pop();
697 						double d1 = JRT.toDouble(o1);
698 						double d2 = JRT.toDouble(o2);
699 						double ans;
700 						switch (opcode) {
701 							case AwkTuples._PLUS_EQ_:
702 								ans = d1 + d2;
703 								break;
704 							case AwkTuples._MINUS_EQ_:
705 								ans = d1 - d2;
706 								break;
707 							case AwkTuples._MULT_EQ_:
708 								ans = d1 * d2;
709 								break;
710 							case AwkTuples._DIV_EQ_:
711 								ans = d1 / d2;
712 								break;
713 							case AwkTuples._MOD_EQ_:
714 								ans = d1 % d2;
715 								break;
716 							case AwkTuples._POW_EQ_:
717 								ans = Math.pow(d1, d2);
718 								break;
719 							default:
720 								throw new Error("Invalid opcode here: " + opcode);
721 						}
722 						if (ans == (long) ans) {
723 							push((long) ans);
724 							runtime_stack.setVariable(position.intArg(0), (int) ans, is_global);
725 						} else {
726 							push(ans);
727 							runtime_stack.setVariable(position.intArg(0), ans, is_global);
728 						}
729 						position.next();
730 						break;
731 					}
732 					case AwkTuples._PLUS_EQ_INPUT_FIELD_:
733 					case AwkTuples._MINUS_EQ_INPUT_FIELD_:
734 					case AwkTuples._MULT_EQ_INPUT_FIELD_:
735 					case AwkTuples._DIV_EQ_INPUT_FIELD_:
736 					case AwkTuples._MOD_EQ_INPUT_FIELD_:
737 					case AwkTuples._POW_EQ_INPUT_FIELD_: {
738 						// stack[0] = dollar_field_number
739 						// stack[1] = inc value
740 
741 						// same code as _GET_INPUT_FIELD_:
742 						int fieldnum = parseIntField(pop(), position);
743 						double incval = JRT.toDouble(pop());
744 
745 						// except here, get the number, and add the incvalue
746 						Object num_obj = jrt.jrtGetInputField(fieldnum);
747 						double num;
748 						switch (opcode) {
749 							case AwkTuples._PLUS_EQ_INPUT_FIELD_:
750 								num = JRT.toDouble(num_obj) + incval;
751 								break;
752 							case AwkTuples._MINUS_EQ_INPUT_FIELD_:
753 								num = JRT.toDouble(num_obj) - incval;
754 								break;
755 							case AwkTuples._MULT_EQ_INPUT_FIELD_:
756 								num = JRT.toDouble(num_obj) * incval;
757 								break;
758 							case AwkTuples._DIV_EQ_INPUT_FIELD_:
759 								num = JRT.toDouble(num_obj) / incval;
760 								break;
761 							case AwkTuples._MOD_EQ_INPUT_FIELD_:
762 								num = JRT.toDouble(num_obj) % incval;
763 								break;
764 							case AwkTuples._POW_EQ_INPUT_FIELD_:
765 								num = Math.pow(JRT.toDouble(num_obj), incval);
766 								break;
767 							default:
768 								throw new Error("Invalid opcode here: " + opcode);
769 						}
770 						setNumOnJRT(fieldnum, num);
771 
772 						// put the result value on the stack
773 						push(num);
774 						position.next();
775 
776 						break;
777 					}
778 					case AwkTuples._INC_: {
779 						// arg[0] = offset
780 						// arg[1] = is_global
781 						inc(position.intArg(0), position.boolArg(1));
782 						position.next();
783 						break;
784 					}
785 					case AwkTuples._DEC_: {
786 						// arg[0] = offset
787 						// arg[1] = is_global
788 						dec(position.intArg(0), position.boolArg(1));
789 						position.next();
790 						break;
791 					}
792 					case AwkTuples._POSTINC_: {
793 						// arg[0] = offset
794 						// arg[1] = is_global
795 						pop();
796 						push(inc(position.intArg(0), position.boolArg(1)));
797 						position.next();
798 						break;
799 					}
800 					case AwkTuples._POSTDEC_: {
801 						// arg[0] = offset
802 						// arg[1] = is_global
803 						pop();
804 						push(dec(position.intArg(0), position.boolArg(1)));
805 						position.next();
806 						break;
807 					}
808 					case AwkTuples._INC_ARRAY_REF_: {
809 						// arg[0] = offset
810 						// arg[1] = is_global
811 						// stack[0] = array index
812 						boolean is_global = position.boolArg(1);
813 						Object o1 = runtime_stack.getVariable(position.intArg(0), is_global);
814 						if (o1 == null || o1 instanceof UninitializedObject) {
815 							runtime_stack.setVariable(position.intArg(0), o1 = new AssocArray(sorted_array_keys), is_global);
816 						}
817 						AssocArray aa = (AssocArray) o1;
818 						Object key = pop();
819 						Object o = aa.get(key);
820 						assert o != null;
821 						double ans = JRT.toDouble(o) + 1;
822 						if (ans == (long) ans) {
823 							aa.put(key, (long) ans);
824 						} else {
825 							aa.put(key, ans);
826 						}
827 						position.next();
828 						break;
829 					}
830 					case AwkTuples._DEC_ARRAY_REF_: {
831 						// arg[0] = offset
832 						// arg[1] = is_global
833 						// stack[0] = array index
834 						boolean is_global = position.boolArg(1);
835 						Object o1 = runtime_stack.getVariable(position.intArg(0), is_global);
836 						if (o1 == null || o1 instanceof UninitializedObject) {
837 							runtime_stack.setVariable(position.intArg(0), o1 = new AssocArray(sorted_array_keys), is_global);
838 						}
839 						AssocArray aa = (AssocArray) o1;
840 						Object key = pop();
841 						Object o = aa.get(key);
842 						assert o != null;
843 						double ans = JRT.toDouble(o) - 1;
844 						if (ans == (long) ans) {
845 							aa.put(key, (long) ans);
846 						} else {
847 							aa.put(key, ans);
848 						}
849 						position.next();
850 						break;
851 					}
852 					case AwkTuples._INC_DOLLAR_REF_: {
853 						// stack[0] = dollar index (field number)
854 						// same code as _GET_INPUT_FIELD_:
855 						int fieldnum = parseIntField(pop(), position);
856 						// except here, get the number, and add one
857 						//push(avmGetInputField(fieldnum));
858 						Object num_obj = jrt.jrtGetInputField(fieldnum);
859 						double num = JRT.toDouble(num_obj) + 1;
860 						setNumOnJRT(fieldnum, num);
861 
862 						position.next();
863 						break;
864 					}
865 					case AwkTuples._DEC_DOLLAR_REF_: {
866 						// stack[0] = dollar index (field number)
867 						// same code as _GET_INPUT_FIELD_:
868 						int fieldnum = parseIntField(pop(), position);
869 						// except here, get the number, and add one
870 						//push(avmGetInputField(fieldnum));
871 						Object num_obj = jrt.jrtGetInputField(fieldnum);
872 						double num = JRT.toDouble(num_obj) - 1;
873 						setNumOnJRT(fieldnum, num);
874 
875 						position.next();
876 						break;
877 					}
878 					case AwkTuples._DEREFERENCE_: {
879 						// arg[0] = offset
880 						// arg[1] = is_global
881 						boolean is_global = position.boolArg(2);
882 						Object o = runtime_stack.getVariable(position.intArg(0), is_global);
883 						if (o == null) {
884 							if (position.boolArg(1)) {
885 								// is_array
886 								push(runtime_stack.setVariable(position.intArg(0), new AssocArray(sorted_array_keys), is_global));
887 							} else {
888 								push(runtime_stack.setVariable(position.intArg(0), BLANK, is_global));
889 							}
890 						} else {
891 							push(o);
892 						}
893 						position.next();
894 						break;
895 					}
896 					case AwkTuples._DEREF_ARRAY_: {
897 						// stack[0] = array index
898 						// stack[1] = AssocArray
899 						Object idx = pop();	// idx
900 						Object array = pop();	// map
901 						if (!(array instanceof AssocArray)) {
902 							throw new AwkRuntimeException("Attempting to index a non-associative-array.");
903 						}
904 						Object o = ((AssocArray) array).get(idx);
905 						assert o != null;
906 						push(o);
907 						position.next();
908 						break;
909 					}
910 					case AwkTuples._SRAND_: {
911 						// arg[0] = num_args (where 0 = no args, anything else = one argument)
912 						// stack[0] = seed (only if num_args != 0)
913 						long numargs = position.intArg(0);
914 						int seed;
915 						if (numargs == 0) {
916 							// use the time of day for the seed
917 							seed = JRT.timeSeed();
918 						} else {
919 							Object o = pop();
920 							if (o instanceof Double) {
921 								seed = ((Double) o).intValue();
922 							} else if (o instanceof Long) {
923 								seed = ((Long) o).intValue();
924 							} else if (o instanceof Integer) {
925 								seed = ((Integer) o).intValue();
926 							} else {
927 								try {
928 									seed = Integer.parseInt(o.toString());
929 								} catch (NumberFormatException nfe) {
930 									seed = 0;
931 								}
932 							}
933 						}
934 						random_number_generator = new Random(seed);
935 						push(oldseed);
936 						oldseed = seed;
937 						position.next();
938 						break;
939 					}
940 					case AwkTuples._RAND_: {
941 						if (random_number_generator == null) {
942 							int seed = JRT.timeSeed();
943 							random_number_generator = new Random(seed);
944 							oldseed = seed;
945 						}
946 						push(random_number_generator.nextDouble());
947 						position.next();
948 						break;
949 					}
950 					case AwkTuples._INTFUNC_:
951 					case AwkTuples._CAST_INT_: {
952 						// stack[0] = arg to int() function
953 						push((long) JRT.toDouble(pop()));
954 						position.next();
955 						break;
956 					}
957 					case AwkTuples._SQRT_: {
958 						// stack[0] = arg to sqrt() function
959 						push(Math.sqrt(JRT.toDouble(pop())));
960 						position.next();
961 						break;
962 					}
963 					case AwkTuples._LOG_: {
964 						// stack[0] = arg to log() function
965 						push(Math.log(JRT.toDouble(pop())));
966 						position.next();
967 						break;
968 					}
969 					case AwkTuples._EXP_: {
970 						// stack[0] = arg to exp() function
971 						push(Math.exp(JRT.toDouble(pop())));
972 						position.next();
973 						break;
974 					}
975 					case AwkTuples._SIN_: {
976 						// stack[0] = arg to sin() function
977 						push(Math.sin(JRT.toDouble(pop())));
978 						position.next();
979 						break;
980 					}
981 					case AwkTuples._COS_: {
982 						// stack[0] = arg to cos() function
983 						push(Math.cos(JRT.toDouble(pop())));
984 						position.next();
985 						break;
986 					}
987 					case AwkTuples._ATAN2_: {
988 						// stack[0] = 2nd arg to atan2() function
989 						// stack[1] = 1st arg to atan2() function
990 						double d2 = JRT.toDouble(pop());
991 						double d1 = JRT.toDouble(pop());
992 						push(Math.atan2(d1, d2));
993 						position.next();
994 						break;
995 					}
996 					case AwkTuples._MATCH_: {
997 						// stack[0] = 2nd arg to match() function
998 						// stack[1] = 1st arg to match() function
999 						String convfmt = getCONVFMT().toString();
1000 						String ere = JRT.toAwkString(pop(), convfmt, locale);
1001 						String s = JRT.toAwkString(pop(), convfmt, locale);
1002 
1003 						// check if IGNORECASE set
1004 						int flags = 0;
1005 
1006 						if (global_variable_offsets.containsKey("IGNORECASE")) {
1007 							Integer offset_obj = global_variable_offsets.get("IGNORECASE");
1008 							Object ignorecase = runtime_stack.getVariable(offset_obj, true);
1009 
1010 							if (JRT.toDouble(ignorecase) != 0) {
1011 								flags |= Pattern.CASE_INSENSITIVE;
1012 							}
1013 						}
1014 
1015 						Pattern pattern = Pattern.compile(ere, flags);
1016 						Matcher matcher = pattern.matcher(s);
1017 						boolean result = matcher.find();
1018 						if (result) {
1019 							assign(rstart_offset, matcher.start() + 1, true, position);
1020 							assign(rlength_offset, matcher.end() - matcher.start(), true, position);
1021 							pop();
1022 							// end up with RSTART on the stack
1023 						} else {
1024 							assign(rstart_offset, ZERO, true, position);
1025 							assign(rlength_offset, -1, true, position);
1026 							pop();
1027 							// end up with RSTART on the stack
1028 						}
1029 						position.next();
1030 						break;
1031 					}
1032 					case AwkTuples._INDEX_: {
1033 						// stack[0] = 2nd arg to index() function
1034 						// stack[1] = 1st arg to index() function
1035 						String convfmt = getCONVFMT().toString();
1036 						String s2 = JRT.toAwkString(pop(), convfmt, locale);
1037 						String s1 = JRT.toAwkString(pop(), convfmt, locale);
1038 						push(s1.indexOf(s2) + 1);
1039 						position.next();
1040 						break;
1041 					}
1042 					case AwkTuples._SUB_FOR_DOLLAR_0_: {
1043 						// arg[0] = is_global
1044 						// stack[0] = replacement string
1045 						// stack[1] = ere
1046 						boolean is_gsub = position.boolArg(0);
1047 						String convfmt = getCONVFMT().toString();
1048 						String repl = JRT.toAwkString(pop(), convfmt, locale);
1049 						String ere = JRT.toAwkString(pop(), convfmt, locale);
1050 						String orig = JRT.toAwkString(jrt.jrtGetInputField(0), convfmt, locale);
1051 						String newstring;
1052 						if (is_gsub) {
1053 							newstring = replaceAll(orig, ere, repl);
1054 						} else {
1055 							newstring = replaceFirst(orig, ere, repl);
1056 						}
1057 						// assign it to "$0"
1058 						jrt.setInputLine(newstring);
1059 						jrt.jrtParseFields();
1060 						position.next();
1061 						break;
1062 					}
1063 					case AwkTuples._SUB_FOR_DOLLAR_REFERENCE_: {
1064 						// arg[0] = is_global
1065 						// stack[0] = field num
1066 						// stack[1] = original field value
1067 						// stack[2] = replacement string
1068 						// stack[3] = ere
1069 						boolean is_gsub = position.boolArg(0);
1070 						String convfmt = getCONVFMT().toString();
1071 						int fieldNum = (int) JRT.toDouble(pop());
1072 						String orig = JRT.toAwkString(pop(), convfmt, locale);
1073 						String repl = JRT.toAwkString(pop(), convfmt, locale);
1074 						String ere = JRT.toAwkString(pop(), convfmt, locale);
1075 						String newstring;
1076 						if (is_gsub) {
1077 							newstring = replaceAll(orig, ere, repl);
1078 						} else {
1079 							newstring = replaceFirst(orig, ere, repl);
1080 						}
1081 						// assign it to "$0"
1082 						if (fieldNum == 0) {
1083 							jrt.setInputLine(newstring);
1084 							jrt.jrtParseFields();
1085 						} else {
1086 							jrt.jrtSetInputField(newstring, fieldNum);
1087 						}
1088 						position.next();
1089 						break;
1090 					}
1091 					case AwkTuples._SUB_FOR_VARIABLE_: {
1092 						// arg[0] = offset
1093 						// arg[1] = is_global
1094 						// arg[2] = is_gsub
1095 						// stack[0] = original variable value
1096 						// stack[1] = replacement string
1097 						// stack[2] = ere
1098 						long offset = position.intArg(0);
1099 						boolean is_global = position.boolArg(1);
1100 						String newString = execSubOrGSub(position, 2);
1101 						// assign it to "offset/global"
1102 						assign(offset, newString, is_global, position);
1103 						pop();
1104 						position.next();
1105 						break;
1106 					}
1107 					case AwkTuples._SUB_FOR_ARRAY_REFERENCE_: {
1108 						// arg[0] = offset
1109 						// arg[1] = is_global
1110 						// arg[2] = is_gsub
1111 						// stack[0] = original variable value
1112 						// stack[1] = replacement string
1113 						// stack[2] = ere
1114 						// stack[3] = array index
1115 						// ARRAY reference offset/is_global
1116 						long offset = position.intArg(0);
1117 						boolean is_global = position.boolArg(1);
1118 						Object arr_idx = pop();
1119 						String newString = execSubOrGSub(position, 2);
1120 						// assign it to "offset/arr_idx/global"
1121 						assignArray(offset, arr_idx, newString, is_global);
1122 						pop();
1123 						position.next();
1124 						break;
1125 					}
1126 					case AwkTuples._SPLIT_: {
1127 						// arg[0] = num args
1128 						// stack[0] = field_sep (only if num args == 3)
1129 						// stack[1] = array
1130 						// stack[2] = string
1131 						String convfmt = getCONVFMT().toString();
1132 						long numargs = position.intArg(0);
1133 						String fs_string;
1134 						if (numargs == 2) {
1135 							fs_string = JRT.toAwkString(getFS(), convfmt, locale);
1136 						} else if (numargs == 3) {
1137 							fs_string = JRT.toAwkString(pop(), convfmt, locale);
1138 						} else {
1139 							throw new Error("Invalid # of args. split() requires 2 or 3. Got: " + numargs);
1140 						}
1141 						Object o = pop();
1142 						if (!(o instanceof AssocArray)) {
1143 							throw new AwkRuntimeException(position.lineNumber(), o + " is not an array.");
1144 						}
1145 						String s = JRT.toAwkString(pop(), convfmt, locale);
1146 						Enumeration<Object> tokenizer;
1147 						if (fs_string.equals(" ")) {
1148 							tokenizer = new StringTokenizer(s);
1149 						} else if (fs_string.length() == 1) {
1150 							tokenizer = new SingleCharacterTokenizer(s, fs_string.charAt(0));
1151 						} else if (fs_string.isEmpty()) {
1152 							tokenizer = new CharacterTokenizer(s);
1153 						} else {
1154 							tokenizer = new RegexTokenizer(s, fs_string);
1155 						}
1156 
1157 						AssocArray assoc_array = (AssocArray) o;
1158 						assoc_array.clear();
1159 						int cnt = 0;
1160 						while (tokenizer.hasMoreElements()) {
1161 							assoc_array.put(++cnt, tokenizer.nextElement());
1162 						}
1163 						push(cnt);
1164 						position.next();
1165 						break;
1166 					}
1167 					case AwkTuples._SUBSTR_: {
1168 						// arg[0] = num args
1169 						// stack[0] = length (only if num args == 3)
1170 						// stack[1] = start pos
1171 						// stack[2] = string
1172 						long numargs = position.intArg(0);
1173 						int startPos, length;
1174 						String s;
1175 						if (numargs == 3)
1176 						{
1177 							length = (int) JRT.toLong(pop());
1178 							startPos = (int) JRT.toDouble(pop());
1179 							s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1180 						} else if (numargs == 2) {
1181 							startPos = (int) JRT.toDouble(pop());
1182 							s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1183 							length = s.length() - startPos + 1;
1184 						} else {
1185 							throw new Error("numargs for _SUBSTR_ must be 2 or 3. It is " + numargs);
1186 						}
1187 						if (startPos <= 0) {
1188 							startPos = 1;
1189 						}
1190 						if (length <= 0 || startPos > s.length()) {
1191 							push(BLANK);
1192 						} else {
1193 							if (startPos + length > s.length()) {
1194 								push(s.substring(startPos - 1));
1195 							} else {
1196 								push(s.substring(startPos - 1, startPos + length - 1));
1197 							}
1198 						}
1199 						position.next();
1200 						break;
1201 					}
1202 					case AwkTuples._TOLOWER_: {
1203 						// stack[0] = string
1204 						push(JRT.toAwkString(pop(), getCONVFMT().toString(), locale).toLowerCase());
1205 						position.next();
1206 						break;
1207 					}
1208 					case AwkTuples._TOUPPER_: {
1209 						// stack[0] = string
1210 						push(JRT.toAwkString(pop(), getCONVFMT().toString(), locale).toUpperCase());
1211 						position.next();
1212 						break;
1213 					}
1214 					case AwkTuples._SYSTEM_: {
1215 						// stack[0] = command string
1216 						String s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1217 						push(JRT.jrtSystem(s));
1218 						position.next();
1219 						break;
1220 					}
1221 					case AwkTuples._SWAP_: {
1222 						// stack[0] = item1
1223 						// stack[1] = item2
1224 						swapOnStack();
1225 						position.next();
1226 						break;
1227 					}
1228 					case AwkTuples._CMP_EQ_: {
1229 						// stack[0] = item2
1230 						// stack[1] = item1
1231 						Object o2 = pop();
1232 						Object o1 = pop();
1233 						push(JRT.compare2(o1, o2, 0) ? ONE : ZERO);
1234 						position.next();
1235 						break;
1236 					}
1237 					case AwkTuples._CMP_LT_: {
1238 						// stack[0] = item2
1239 						// stack[1] = item1
1240 						Object o2 = pop();
1241 						Object o1 = pop();
1242 						push(JRT.compare2(o1, o2, -1) ? ONE : ZERO);
1243 						position.next();
1244 						break;
1245 					}
1246 					case AwkTuples._CMP_GT_: {
1247 						// stack[0] = item2
1248 						// stack[1] = item1
1249 						Object o2 = pop();
1250 						Object o1 = pop();
1251 						push(JRT.compare2(o1, o2, 1) ? ONE : ZERO);
1252 						position.next();
1253 						break;
1254 					}
1255 					case AwkTuples._MATCHES_: {
1256 						// stack[0] = item2
1257 						// stack[1] = item1
1258 						Object o2 = pop();
1259 						Object o1 = pop();
1260 						// use o1's string value
1261 						String s = o1.toString();
1262 						// assume o2 is a regexp
1263 						if (o2 instanceof Pattern) {
1264 							Pattern p = (Pattern) o2;
1265 							Matcher m = p.matcher(s);
1266 							// m.matches() matches the ENTIRE string
1267 							// m.find() is more appropriate
1268 							boolean result = m.find();
1269 							push(result ? 1 : 0);
1270 						} else {
1271 							String r = JRT.toAwkString(o2, getCONVFMT().toString(), locale);
1272 							boolean result = Pattern.compile(r).matcher(s).find();
1273 							push(result ? 1 : 0);
1274 						}
1275 						position.next();
1276 						break;
1277 					}
1278 					case AwkTuples._SLEEP_: {
1279 						// arg[0] = num_args
1280 						// if (num_args==1)
1281 						// 	stack[0] = # of seconds
1282 						// else
1283 						// 	nothing on the stack
1284 						//int seconds = (int) JRT.toDouble(pop());
1285 						long seconds;
1286 						long numargs = position.intArg(0);
1287 						if (numargs == 0) {
1288 							seconds = 1;
1289 						} else {
1290 							seconds = (long) JRT.toDouble(pop());
1291 						}
1292 						try {
1293 							Thread.sleep(seconds * 1000);
1294 						} catch (InterruptedException ie) {
1295 							throw new AwkRuntimeException(position.lineNumber(), "Caught exception while waiting for process exit: " + ie);
1296 						}
1297 						position.next();
1298 						break;
1299 					}
1300 					case AwkTuples._DUMP_: {
1301 						// arg[0] = num_args
1302 						// if (num_args==0)
1303 						// 	all Jawk global variables
1304 						// else
1305 						// 	args are assoc arrays to display
1306 						//int seconds = (int) JRT.toDouble(pop());
1307 						long numargs = position.intArg(0);
1308 						AssocArray[] aa_array;
1309 						if (numargs == 0) {
1310 							aa_array = null;
1311 						} else {
1312 							aa_array = new AssocArray[(int) numargs];
1313 							for (int i = 0; i < numargs; ++i) {
1314 								aa_array[i] = (AssocArray) pop();
1315 							}
1316 						}
1317 						avmDump(aa_array);
1318 						position.next();
1319 						break;
1320 					}
1321 					case AwkTuples._ADD_: {
1322 						// stack[0] = item2
1323 						// stack[1] = item1
1324 						Object o2 = pop();
1325 						Object o1 = pop();
1326 						double d1 = JRT.toDouble(o1);
1327 						double d2 = JRT.toDouble(o2);
1328 						double ans = d1 + d2;
1329 						if (ans == (long) ans) {
1330 							push((long) ans);
1331 						} else {
1332 							push(ans);
1333 						}
1334 						position.next();
1335 						break;
1336 					}
1337 					case AwkTuples._SUBTRACT_: {
1338 						// stack[0] = item2
1339 						// stack[1] = item1
1340 						Object o2 = pop();
1341 						Object o1 = pop();
1342 						double d1 = JRT.toDouble(o1);
1343 						double d2 = JRT.toDouble(o2);
1344 						double ans = d1 - d2;
1345 						if (ans == (long) ans) {
1346 							push((long) ans);
1347 						} else {
1348 							push(ans);
1349 						}
1350 						position.next();
1351 						break;
1352 					}
1353 					case AwkTuples._MULTIPLY_: {
1354 						// stack[0] = item2
1355 						// stack[1] = item1
1356 						Object o2 = pop();
1357 						Object o1 = pop();
1358 						double d1 = JRT.toDouble(o1);
1359 						double d2 = JRT.toDouble(o2);
1360 						double ans = d1 * d2;
1361 						if (ans == (long) ans) {
1362 							push((long) ans);
1363 						} else {
1364 							push(ans);
1365 						}
1366 						position.next();
1367 						break;
1368 					}
1369 					case AwkTuples._DIVIDE_: {
1370 						// stack[0] = item2
1371 						// stack[1] = item1
1372 						Object o2 = pop();
1373 						Object o1 = pop();
1374 						double d1 = JRT.toDouble(o1);
1375 						double d2 = JRT.toDouble(o2);
1376 						double ans = d1 / d2;
1377 						if (ans == (long) ans) {
1378 							push((long) ans);
1379 						} else {
1380 							push(ans);
1381 						}
1382 						position.next();
1383 						break;
1384 					}
1385 					case AwkTuples._MOD_: {
1386 						// stack[0] = item2
1387 						// stack[1] = item1
1388 						Object o2 = pop();
1389 						Object o1 = pop();
1390 						double d1 = JRT.toDouble(o1);
1391 						double d2 = JRT.toDouble(o2);
1392 						double ans = d1 % d2;
1393 						if (ans == (long) ans) {
1394 							push((long) ans);
1395 						} else {
1396 							push(ans);
1397 						}
1398 						position.next();
1399 						break;
1400 					}
1401 					case AwkTuples._POW_: {
1402 						// stack[0] = item2
1403 						// stack[1] = item1
1404 						Object o2 = pop();
1405 						Object o1 = pop();
1406 						double d1 = JRT.toDouble(o1);
1407 						double d2 = JRT.toDouble(o2);
1408 						double ans = Math.pow(d1, d2);
1409 						if (ans == (long) ans) {
1410 							push((long) ans);
1411 						} else {
1412 							push(ans);
1413 						}
1414 						position.next();
1415 						break;
1416 					}
1417 					case AwkTuples._DUP_: {
1418 						// stack[0] = top of stack item
1419 						Object o = pop();
1420 						push(o);
1421 						push(o);
1422 						position.next();
1423 						break;
1424 					}
1425 					case AwkTuples._KEYLIST_: {
1426 						// stack[0] = AssocArray
1427 						Object o = pop();
1428 						assert o != null;
1429 						if (!(o instanceof AssocArray)) {
1430 							throw new AwkRuntimeException(position.lineNumber(), "Cannot get a keylist (via 'in') of a non associative array. arg = " + o.getClass() + ", " + o);
1431 						}
1432 						AssocArray aa = (AssocArray) o;
1433 						push(new KeyListImpl(aa.keySet()));
1434 						position.next();
1435 						break;
1436 					}
1437 					case AwkTuples._IS_EMPTY_KEYLIST_: {
1438 						// arg[0] = address
1439 						// stack[0] = KeyList
1440 						Object o = pop();
1441 						if (o == null || !(o instanceof KeyList)) {
1442 							throw new AwkRuntimeException(position.lineNumber(), "Cannot get a keylist (via 'in') of a non associative array. arg = " + o.getClass() + ", " + o);
1443 						}
1444 						KeyList keylist = (KeyList) o;
1445 						if (keylist.size() == 0) {
1446 							position.jump(position.addressArg());
1447 						} else {
1448 							position.next();
1449 						}
1450 						break;
1451 					}
1452 					case AwkTuples._GET_FIRST_AND_REMOVE_FROM_KEYLIST_: {
1453 						// stack[0] = KeyList
1454 						Object o = pop();
1455 						if (o == null || !(o instanceof KeyList)) {
1456 							throw new AwkRuntimeException(position.lineNumber(), "Cannot get a keylist (via 'in') of a non associative array. arg = " + o.getClass() + ", " + o);
1457 						}
1458 						// pop off and return the head of the key set
1459 						KeyList keylist = (KeyList) o;
1460 						assert keylist.size() > 0;
1461 						push(keylist.getFirstAndRemove());
1462 						position.next();
1463 						break;
1464 					}
1465 					case AwkTuples._CHECK_CLASS_: {
1466 						// arg[0] = class object
1467 						// stack[0] = item to check
1468 						Object o = pop();
1469 						if (!(position.classArg().isInstance(o))) {
1470 							throw new AwkRuntimeException(position.lineNumber(), "Verification failed. Top-of-stack = " + o.getClass() + " isn't an instance of " + position.classArg());
1471 						}
1472 						push(o);
1473 						position.next();
1474 						break;
1475 					}
1476 					case AwkTuples._CONSUME_INPUT_: {
1477 						// arg[0] = address
1478 						// false = do NOT put result on stack...
1479 						// instead, put it in field vars ($0, $1, ...)
1480 						if (avmConsumeInput(false)) {
1481 							position.next();
1482 						} else {
1483 							position.jump(position.addressArg());
1484 						}
1485 						break;
1486 					}
1487 					case AwkTuples._GETLINE_INPUT_: {
1488 						avmConsumeInputForGetline();
1489 						position.next();
1490 						break;
1491 					}
1492 					case AwkTuples._USE_AS_FILE_INPUT_: {
1493 						// stack[0] = filename
1494 						String s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1495 						avmConsumeFileInputForGetline(s);
1496 						position.next();
1497 						break;
1498 					}
1499 					case AwkTuples._USE_AS_COMMAND_INPUT_: {
1500 						// stack[0] = command line
1501 						String s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1502 						avmConsumeCommandInputForGetline(s);
1503 						position.next();
1504 						break;
1505 					}
1506 					case AwkTuples._NF_OFFSET_: {
1507 						// stack[0] = offset
1508 						nf_offset = position.intArg(0);
1509 						assert nf_offset != NULL_OFFSET;
1510 						assign(nf_offset, 0, true, position);
1511 						pop();			// clean up the stack after the assignment
1512 						position.next();
1513 						break;
1514 					}
1515 					case AwkTuples._NR_OFFSET_: {
1516 						// stack[0] = offset
1517 						nr_offset = position.intArg(0);
1518 						assert nr_offset != NULL_OFFSET;
1519 						assign(nr_offset, 0, true, position);
1520 						pop();			// clean up the stack after the assignment
1521 						position.next();
1522 						break;
1523 					}
1524 					case AwkTuples._FNR_OFFSET_: {
1525 						// stack[0] = offset
1526 						fnr_offset = position.intArg(0);
1527 						assert fnr_offset != NULL_OFFSET;
1528 						assign(fnr_offset, 0, true, position);
1529 						pop();			// clean up the stack after the assignment
1530 						position.next();
1531 						break;
1532 					}
1533 					case AwkTuples._FS_OFFSET_: {
1534 						// stack[0] = offset
1535 						fs_offset = position.intArg(0);
1536 						assert fs_offset != NULL_OFFSET;
1537 						if (initial_fs_value == null) {
1538 							assign(fs_offset, " ", true, position);
1539 						} else {
1540 							assign(fs_offset, initial_fs_value, true, position);
1541 						}
1542 						pop();			// clean up the stack after the assignment
1543 						position.next();
1544 						break;
1545 					}
1546 					case AwkTuples._RS_OFFSET_: {
1547 						// stack[0] = offset
1548 						rs_offset = position.intArg(0);
1549 						assert rs_offset != NULL_OFFSET;
1550 						assign(rs_offset, settings.getDefaultRS(), true, position);
1551 						pop();			// clean up the stack after the assignment
1552 						position.next();
1553 						break;
1554 					}
1555 					case AwkTuples._OFS_OFFSET_: {
1556 						// stack[0] = offset
1557 						ofs_offset = position.intArg(0);
1558 						assert ofs_offset != NULL_OFFSET;
1559 						assign(ofs_offset, " ", true, position);
1560 						pop();			// clean up the stack after the assignment
1561 						position.next();
1562 						break;
1563 					}
1564 					case AwkTuples._ORS_OFFSET_: {
1565 						// stack[0] = offset
1566 						ors_offset = position.intArg(0);
1567 						assert ors_offset != NULL_OFFSET;
1568 						assign(ors_offset, settings.getDefaultORS(), true, position);
1569 						pop();			// clean up the stack after the assignment
1570 						position.next();
1571 						break;
1572 					}
1573 					case AwkTuples._RSTART_OFFSET_: {
1574 						// stack[0] = offset
1575 						rstart_offset = position.intArg(0);
1576 						assert rstart_offset != NULL_OFFSET;
1577 						assign(rstart_offset, "", true, position);
1578 						pop();			// clean up the stack after the assignment
1579 						position.next();
1580 						break;
1581 					}
1582 					case AwkTuples._RLENGTH_OFFSET_: {
1583 						// stack[0] = offset
1584 						rlength_offset = position.intArg(0);
1585 						assert rlength_offset != NULL_OFFSET;
1586 						assign(rlength_offset, "", true, position);
1587 						pop();			// clean up the stack after the assignment
1588 						position.next();
1589 						break;
1590 					}
1591 					case AwkTuples._FILENAME_OFFSET_: {
1592 						// stack[0] = offset
1593 						filename_offset = position.intArg(0);
1594 						assert filename_offset != NULL_OFFSET;
1595 						assign(filename_offset, "", true, position);
1596 						pop();			// clean up the stack after the assignment
1597 						position.next();
1598 						break;
1599 					}
1600 					case AwkTuples._SUBSEP_OFFSET_: {
1601 						// stack[0] = offset
1602 						subsep_offset = position.intArg(0);
1603 						assert subsep_offset != NULL_OFFSET;
1604 						assign(subsep_offset, new String(new byte[] {28}), true, position);
1605 						pop();			// clean up the stack after the assignment
1606 						position.next();
1607 						break;
1608 					}
1609 					case AwkTuples._CONVFMT_OFFSET_: {
1610 						// stack[0] = offset
1611 						convfmt_offset = position.intArg(0);
1612 						assert convfmt_offset != NULL_OFFSET;
1613 						assign(convfmt_offset, "%.6g", true, position);
1614 						pop();			// clean up the stack after the assignment
1615 						position.next();
1616 						break;
1617 					}
1618 					case AwkTuples._OFMT_OFFSET_: {
1619 						// stack[0] = offset
1620 						ofmt_offset = position.intArg(0);
1621 						assert ofmt_offset != NULL_OFFSET;
1622 						assign(ofmt_offset, "%.6g", true, position);
1623 						pop();			// clean up the stack after the assignment
1624 						position.next();
1625 						break;
1626 					}
1627 					case AwkTuples._ENVIRON_OFFSET_: {
1628 						// stack[0] = offset
1629 						//// assignArray(offset, arr_idx, newstring, is_global);
1630 						environ_offset = position.intArg(0);
1631 						assert environ_offset != NULL_OFFSET;
1632 						// set the initial variables
1633 						Map<String, String> env = System.getenv();
1634 						for (Map.Entry<String, String> var : env.entrySet()) {
1635 							assignArray(environ_offset, var.getKey(), var.getValue(), true);
1636 							pop(); // clean up the stack after the assignment
1637 						}
1638 						position.next();
1639 						break;
1640 					}
1641 					case AwkTuples._ARGC_OFFSET_: {
1642 						// stack[0] = offset
1643 						argc_offset = position.intArg(0);
1644 						assert argc_offset != NULL_OFFSET;
1645 						//assign(argc_offset, arguments.size(), true, position);	// true = global
1646 						// +1 to include the "java Awk" (ARGV[0])
1647 						assign(argc_offset, arguments.size() + 1, true, position);	// true = global
1648 						pop();			// clean up the stack after the assignment
1649 						position.next();
1650 						break;
1651 					}
1652 					case AwkTuples._ARGV_OFFSET_: {
1653 						// stack[0] = offset
1654 						argv_offset = position.intArg(0);
1655 						assert argv_offset != NULL_OFFSET;
1656 						// consume argv (looping from 1 to argc)
1657 						int argc = (int) JRT.toDouble(runtime_stack.getVariable(argc_offset, true));	// true = global
1658 						assignArray(argv_offset, 0, "java Awk", true);
1659 						pop();
1660 						for (int i = 1; i < argc; i++) {
1661 							//assignArray(argv_offset, i+1, arguments.get(i), true);
1662 							assignArray(argv_offset, i, arguments.get(i - 1), true);
1663 							pop();			// clean up the stack after the assignment
1664 						}
1665 						position.next();
1666 						break;
1667 					}
1668 					case AwkTuples._GET_INPUT_FIELD_: {
1669 						// stack[0] = field number
1670 						int fieldnum = parseIntField(pop(), position);
1671 						push(jrt.jrtGetInputField(fieldnum));
1672 						position.next();
1673 						break;
1674 					}
1675 					case AwkTuples._APPLY_RS_: {
1676 						assert rs_offset != NULL_OFFSET;
1677 						Object rs_obj = runtime_stack.getVariable(rs_offset, true);	// true = global
1678 						if (jrt.getPartitioningReader() != null) {
1679 							jrt.getPartitioningReader().setRecordSeparator(rs_obj.toString());
1680 						}
1681 						position.next();
1682 						break;
1683 					}
1684 					case AwkTuples._CALL_FUNCTION_: {
1685 						// arg[0] = function address
1686 						// arg[1] = function name
1687 						// arg[2] = # of formal parameters
1688 						// arg[3] = # of actual parameters
1689 						// stack[0] = last actual parameter
1690 						// stack[1] = before-last actual parameter
1691 						// ...
1692 						// stack[n-1] = first actual parameter
1693 						// etc.
1694 						Address func_addr = position.addressArg();
1695 						//String func_name = position.arg(1).toString();
1696 						long num_formal_params = position.intArg(2);
1697 						long num_actual_params = position.intArg(3);
1698 						assert num_formal_params >= num_actual_params;
1699 						runtime_stack.pushFrame(num_formal_params, position.current());
1700 						// Arguments are stacked, so first in the stack is the last for the function 
1701 						for (long i = num_actual_params - 1 ; i >= 0 ; i--) {
1702 							runtime_stack.setVariable(i, pop(), false);	// false = local
1703 						}
1704 						position.jump(func_addr);
1705 						//position.next();
1706 						break;
1707 					}
1708 					case AwkTuples._FUNCTION_: {
1709 						// important for compilation,
1710 						// not needed for interpretation
1711 						// arg[0] = function name
1712 						// arg[1] = # of formal parameters
1713 						position.next();
1714 						break;
1715 					}
1716 					case AwkTuples._SET_RETURN_RESULT_: {
1717 						// stack[0] = return result
1718 						runtime_stack.setReturnValue(pop());
1719 						position.next();
1720 						break;
1721 					}
1722 					case AwkTuples._RETURN_FROM_FUNCTION_: {
1723 						position.jump(runtime_stack.popFrame());
1724 						push(runtime_stack.getReturnValue());
1725 						position.next();
1726 						break;
1727 					}
1728 					case AwkTuples._SET_NUM_GLOBALS_: {
1729 						// arg[0] = # of globals
1730 						assert position.intArg(0) == global_variable_offsets.size();
1731 						runtime_stack.setNumGlobals(position.intArg(0));
1732 
1733 						// now that we have the global variable size,
1734 						// we can allocate the initial variables
1735 
1736 						// assign -v variables (from initial_variables container)
1737 						for (String key : initial_variables.keySet()) {
1738 							if (function_names.contains(key)) {
1739 								throw new IllegalArgumentException("Cannot assign a scalar to a function name (" + key + ").");
1740 							}
1741 							Integer offset_obj = global_variable_offsets.get(key);
1742 							Boolean array_obj = global_variable_arrays.get(key);
1743 							if (offset_obj != null) {
1744 								assert array_obj != null;
1745 								if (array_obj.booleanValue()) {
1746 									throw new IllegalArgumentException("Cannot assign a scalar to a non-scalar variable (" + key + ").");
1747 								} else {
1748 									Object obj = initial_variables.get(key);
1749 									runtime_stack.setFilelistVariable(offset_obj.intValue(), obj);
1750 								}
1751 							}
1752 						}
1753 
1754 						position.next();
1755 						break;
1756 					}
1757 					case AwkTuples._CLOSE_: {
1758 						// stack[0] = file or command line to close
1759 						String s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1760 						push(jrt.jrtClose(s));
1761 						position.next();
1762 						break;
1763 					}
1764 					case AwkTuples._APPLY_SUBSEP_: {
1765 						// arg[0] = # of elements for SUBSEP application
1766 						// stack[0] = first element
1767 						// stack[1] = second element
1768 						// etc.
1769 						long count = position.intArg(0);
1770 						assert count >= 1;
1771 						//String s;
1772 						String convfmt = getCONVFMT().toString();
1773 						if (count == 1) {
1774 							//s = JRT.toAwkString(pop(), convfmt);
1775 						} else {
1776 							StringBuilder sb = new StringBuilder();
1777 							sb.append(JRT.toAwkString(pop(), convfmt, locale));
1778 							String subsep = JRT.toAwkString(runtime_stack.getVariable(subsep_offset, true), convfmt, locale);
1779 							for (int i = 1; i < count; i++) {
1780 								sb.insert(0, subsep);
1781 								sb.insert(0, JRT.toAwkString(pop(), convfmt, locale));
1782 							}
1783 							push(sb.toString());
1784 						}
1785 						position.next();
1786 						break;
1787 					}
1788 					case AwkTuples._DELETE_ARRAY_ELEMENT_: {
1789 						// arg[0] = offset
1790 						// arg[1] = is_global
1791 						// stack[0] = array index
1792 						long offset = position.intArg(0);
1793 						boolean is_global = position.boolArg(1);
1794 						AssocArray aa = (AssocArray) runtime_stack.getVariable(offset, is_global);
1795 						Object key = pop();
1796 						if (aa != null) {
1797 							aa.remove(key);
1798 						}
1799 						position.next();
1800 						break;
1801 					}
1802 					case AwkTuples._DELETE_ARRAY_: {
1803 						// arg[0] = offset
1804 						// arg[1] = is_global
1805 						// (nothing on the stack)
1806 						long offset = position.intArg(0);
1807 						boolean is_global = position.boolArg(1);
1808 						runtime_stack.removeVariable(offset, is_global);
1809 						position.next();
1810 						break;
1811 					}
1812 					case AwkTuples._SET_EXIT_ADDRESS_: {
1813 						// arg[0] = exit address
1814 						exit_address = position.addressArg();
1815 						position.next();
1816 						break;
1817 					}
1818 					case AwkTuples._SET_WITHIN_END_BLOCKS_: {
1819 						// arg[0] = whether within the END blocks section
1820 						within_end_blocks = position.boolArg(0);
1821 						position.next();
1822 						break;
1823 					}
1824 					case AwkTuples._EXIT_WITHOUT_CODE_:
1825 					case AwkTuples._EXIT_WITH_CODE_: {
1826 						if (opcode == AwkTuples._EXIT_WITH_CODE_) {
1827 							// stack[0] = exit code
1828 							exit_code = (int) JRT.toDouble(pop());
1829 						}
1830 						throw_exit_exception = true;
1831 						
1832 						// If in BEGIN or in a rule, jump to the END section
1833 						if (!within_end_blocks) {
1834 							// clear runtime stack
1835 							runtime_stack.popAllFrames();
1836 							// clear operand stack
1837 							operand_stack.clear();
1838 							position.jump(exit_address);
1839 						} else {
1840 							// Exit immediately with ExitException
1841 							jrt.jrtCloseAll();
1842 							// clear operand stack
1843 							operand_stack.clear();
1844 							throw new ExitException(exit_code, "The AWK script requested an exit");
1845 							//position.next();
1846 						}
1847 						break;
1848 					}
1849 					case AwkTuples._REGEXP_: {
1850 						// arg[0] = string representation of regexp
1851 						String key = JRT.toAwkString(position.arg(0), getCONVFMT().toString(), locale);
1852 						Pattern pattern = regexps.get(key);
1853 						if (pattern == null) {
1854 							regexps.put(key, pattern = Pattern.compile(key));
1855 						}
1856 						push(pattern);
1857 						position.next();
1858 						break;
1859 					}
1860 					case AwkTuples._CONDITION_PAIR_: {
1861 						// stack[0] = End condition
1862 						// stack[1] = Start condition
1863 						ConditionPair cp = condition_pairs.get(position.current());
1864 						if (cp == null) {
1865 							cp = new ConditionPair();
1866 							condition_pairs.put(position.current(), cp);
1867 						}
1868 						boolean end = jrt.toBoolean(pop());
1869 						boolean start = jrt.toBoolean(pop());
1870 						push(cp.update(start, end) ? ONE : ZERO);
1871 						position.next();
1872 						break;
1873 					}
1874 					case AwkTuples._IS_IN_: {
1875 						// stack[0] = AssocArray
1876 						// stack[1] = key to check
1877 						Object arr = pop();
1878 						Object arg = pop();
1879 						AssocArray aa = (AssocArray) arr;
1880 						boolean result = aa.isIn(arg);
1881 						push(result ? ONE : ZERO);
1882 						position.next();
1883 						break;
1884 					}
1885 					case AwkTuples._CAST_DOUBLE_: {
1886 						push(JRT.toDouble(pop()));
1887 						position.next();
1888 						break;
1889 					}
1890 					case AwkTuples._CAST_STRING_: {
1891 						push(pop().toString());
1892 						position.next();
1893 						break;
1894 					}
1895 					case AwkTuples._THIS_: {
1896 						// this is in preparation for a function
1897 						// call for the JVM-COMPILED script, only
1898 						// therefore, do NOTHING for the interpreted
1899 						// version
1900 						position.next();
1901 						break;
1902 					}
1903 					case AwkTuples._EXEC_: {
1904 						// stack[0] = Jawk code
1905 
1906 						// TODO FIXME First attempt. It is not complete by a long-shot. Use at your own risk.
1907 
1908 						String awk_code = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1909 						List<ScriptSource> scriptSources = new ArrayList<ScriptSource>(1);
1910 						scriptSources.add(new ScriptSource(ScriptSource.DESCRIPTION_COMMAND_LINE_SCRIPT, new StringReader(awk_code), false));
1911 
1912 						org.sentrysoftware.jawk.frontend.AwkParser ap = new org.sentrysoftware.jawk.frontend.AwkParser(
1913 								//true, true, true, extensions
1914 								settings.isAdditionalFunctions(),
1915 								settings.isAdditionalTypeFunctions(),
1916 								extensions);
1917 						try {
1918 							AwkSyntaxTree ast = ap.parse(scriptSources);
1919 							if (ast != null) {
1920 								ast.semanticAnalysis();
1921 								ast.semanticAnalysis();
1922 								AwkTuples new_tuples = new AwkTuples();
1923 								int result = ast.populateTuples(new_tuples);
1924 								assert result == 0;
1925 								new_tuples.postProcess();
1926 								ap.populateGlobalVariableNameToOffsetMappings(new_tuples);
1927 								AVM new_avm = new AVM(settings, extensions);
1928 								int subScriptExitCode = 0;
1929 								try {
1930 									new_avm.interpret(new_tuples);
1931 								} catch (ExitException ex) {
1932 									subScriptExitCode = ex.getCode();
1933 								}
1934 								push(subScriptExitCode);
1935 							} else {
1936 								push(-1);
1937 							}
1938 						} catch (IOException ioe) {
1939 							throw new AwkRuntimeException(position.lineNumber(), "IO Exception caught : " + ioe);
1940 						}
1941 
1942 						position.next();
1943 						break;
1944 					}
1945 					case AwkTuples._EXTENSION_: {
1946 						// arg[0] = extension keyword
1947 						// arg[1] = # of args on the stack
1948 						// arg[2] = true if parent is NOT an extension function call
1949 						// 		(i.e., initial extension in calling expression)
1950 						// stack[0] = first actual parameter
1951 						// stack[1] = second actual parameter
1952 						// etc.
1953 						String extension_keyword = position.arg(0).toString();
1954 						long num_args = position.intArg(1);
1955 						boolean is_initial = position.boolArg(2);
1956 
1957 						JawkExtension extension = extensions.get(extension_keyword);
1958 						if (extension == null) {
1959 							throw new AwkRuntimeException("Extension for '" + extension_keyword + "' not found.");
1960 						}
1961 
1962 						Object[] args = new Object[(int) num_args];
1963 						for (int i = (int)num_args - 1 ; i >=0 ; i--) {
1964 							args[i] = pop();
1965 						}
1966 
1967 						Object retval = extension.invoke(extension_keyword, args);
1968 
1969 						// block if necessary
1970 						// (convert retval into the return value
1971 						// from the block operation ...)
1972 						if (is_initial && retval != null && retval instanceof BlockObject) {
1973 							retval = new BlockManager().block((BlockObject) retval);
1974 						}
1975 						// (... and proceed)
1976 
1977 						if (retval == null) {
1978 							retval = "";
1979 						} else if (retval instanceof Integer) {
1980 						} else if (retval instanceof Long) {
1981 						} else if (retval instanceof Double) {
1982 						} else if (retval instanceof String) {
1983 						} else if (retval instanceof AssocArray) {
1984 						} else if (retval instanceof BlockObject) {
1985 							// pass a block object through...
1986 						} else {
1987 							// all other extension results are converted
1988 							// to a string (via Object.toString())
1989 							retval = retval.toString();
1990 						}
1991 						push(retval);
1992 
1993 						position.next();
1994 						break;
1995 					}
1996 					default:
1997 						throw new Error("invalid opcode: " + AwkTuples.toOpcodeString(position.opcode()));
1998 				}
1999 			}
2000 			
2001 			// End of the instructions
2002 			jrt.jrtCloseAll();
2003 			
2004 		} catch (RuntimeException re) {
2005 			LOG.error("", re);
2006 			LOG.error("operand_stack = {}", operand_stack);
2007 			LOG.error("position = {}", position);
2008 			LOG.error("line number = {}", position.lineNumber());
2009 
2010 			// clear runtime stack
2011 			runtime_stack.popAllFrames();
2012 			// clear operand stack
2013 			operand_stack.clear();
2014 
2015 			throw re;
2016 		} catch (AssertionError ae) {
2017 			LOG.error("", ae);
2018 			LOG.error("operand_stack = {}", operand_stack);
2019 			try {
2020 				LOG.error("position = {}", position);
2021 			} catch (Throwable t) {
2022 				LOG.error("{ could not report on position", t);
2023 			}
2024 			try {
2025 				LOG.error("line number = {}", position.lineNumber());
2026 			} catch (Throwable t) {
2027 				LOG.error("{ could not report on line number", t);
2028 			}
2029 			throw ae;
2030 		}
2031 		
2032 		// If <code>exit</code> was called, throw an ExitException
2033 		if (throw_exit_exception) {
2034 			throw new ExitException(exit_code, "The AWK script requested an exit");
2035 		}
2036 
2037 	}
2038 
2039 	/**
2040 	 * Close all streams in the runtime
2041 	 */
2042 	public void waitForIO() {
2043 		jrt.jrtCloseAll();
2044 	}
2045 
2046 	private void avmDump(AssocArray[] aa_array) {
2047 		if (aa_array == null) {
2048 			// dump the runtime stack
2049 			Object[] globals = runtime_stack.getNumGlobals();
2050 			for (String name : global_variable_offsets.keySet()) {
2051 				int idx = global_variable_offsets.get(name);
2052 				Object value = globals[idx];
2053 				if (value instanceof AssocArray) {
2054 					AssocArray aa = (AssocArray) value;
2055 					value = aa.mapString();
2056 				}
2057 				LOG.info("{} = {}", name, value);
2058 			}
2059 		} else {
2060 			// dump associative arrays
2061 			for (AssocArray aa : aa_array) {
2062 				LOG.info(aa.mapString());
2063 			}
2064 		}
2065 	}
2066 
2067 	private void printTo(PrintStream ps, long num_args) {
2068 		// print items from the top of the stack
2069 		// # of items
2070 		if (num_args == 0) {
2071 			// display $0
2072 			ps.print(jrt.jrtGetInputField(0));
2073 			ps.print(getORS().toString());
2074 		} else {
2075 			// cache $OFS to separate fields below
2076 			// (no need to execute getOFS for each field)
2077 			String ofs_string = getOFS().toString();
2078 			
2079 			// Arguments are stacked, so we need to reverse order
2080 			Object[] args = new Object[(int)num_args];
2081 			for (int i = (int)num_args - 1 ; i >=0 ; i--) {
2082 				args[i] = pop();
2083 			}
2084 			
2085 			// Now print
2086 			for (int i = 0 ; i < num_args ; i++) {
2087 				ps.print(JRT.toAwkStringForOutput(args[i], getOFMT().toString(), locale));
2088 				// if more elements, display $FS
2089 				if (i < num_args - 1) {
2090 					// use $OFS to separate fields
2091 					ps.print(ofs_string);
2092 				}
2093 			}
2094 			ps.print(getORS().toString());
2095 		}
2096 		// for now, since we are not using Process.waitFor()
2097 		if (IS_WINDOWS) {
2098 			ps.flush();
2099 		}
2100 	}
2101 
2102 	private void printfTo(PrintStream ps, long num_args) {
2103 //		assert num_args > 0;
2104 		ps.print(sprintfFunction(num_args));
2105 		// for now, since we are not using Process.waitFor()
2106 		if (IS_WINDOWS) {
2107 			ps.flush();
2108 		}
2109 	}
2110 
2111 	/**
2112 	 * sprintf() functionality
2113 	 */
2114 	private String sprintfFunction(long num_args) {
2115 
2116 		// Silly case
2117 		if (num_args == 0) return "";
2118 
2119 		// all but the format argument
2120 		Object[] arg_array = new Object[(int) (num_args - 1)];
2121 
2122 		// for each sprintf argument, put it into an
2123 		// array used in the String.format method
2124 		// Arguments are stacked, so we need to reverse their order
2125 		for (int i = (int)num_args - 2 ; i >= 0 ; i--) {
2126 			arg_array[i] = pop();
2127 		}
2128 
2129 		// the format argument!
2130 		String fmt = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
2131 
2132 		if (trap_illegal_format_exceptions) {
2133 			return Printf4J.sprintf(locale, fmt, arg_array);
2134 		} else {
2135 			return JRT.sprintfNoCatch(locale, fmt, arg_array);
2136 		}
2137 	}
2138 
2139 	private StringBuffer replace_first_sb = new StringBuffer();
2140 
2141 	/**
2142 	 * sub() functionality
2143 	 */
2144 	private String replaceFirst(String orig, String ere, String repl) {
2145 		push(JRT.replaceFirst(orig, repl, ere, replace_first_sb));
2146 		return replace_first_sb.toString();
2147 	}
2148 
2149 	private StringBuffer replace_all_sb = new StringBuffer();
2150 
2151 	/**
2152 	 * gsub() functionality
2153 	 */
2154 	private String replaceAll(String orig, String ere, String repl) {
2155 		push(JRT.replaceAll(orig, repl, ere, replace_all_sb));
2156 		return replace_all_sb.toString();
2157 	}
2158 
2159 	/**
2160 	 * Awk variable assignment functionality.
2161 	 */
2162 	private void assign(long l, Object value, boolean is_global, Position position) {
2163 		// check if curr value already refers to an array
2164 		if (runtime_stack.getVariable(l, is_global) instanceof AssocArray) {
2165 			throw new AwkRuntimeException(position.lineNumber(), "cannot assign anything to an unindexed associative array");
2166 		}
2167 		push(value);
2168 		runtime_stack.setVariable(l, value, is_global);
2169 	}
2170 
2171 	/**
2172 	 * Awk array element assignment functionality.
2173 	 */
2174 	private void assignArray(long offset, Object arr_idx, Object rhs, boolean is_global) {
2175 		Object o1 = runtime_stack.getVariable(offset, is_global);
2176 		if (o1 == null || o1.equals(BLANK)) {
2177 			runtime_stack.setVariable(offset, o1 = new AssocArray(sorted_array_keys), is_global);
2178 		}
2179 		assert o1 != null;
2180 		// The only (conceivable) way to contradict
2181 		// the assertion (below) is by passing in
2182 		// a scalar to an unindexed associative array
2183 		// via a -v argument without safeguards to
2184 		// prohibit this.
2185 		// Therefore, guard against this elsewhere, not here.
2186 		//if (! (o1 instanceof AssocArray))
2187 		//	throw new AwkRuntimeException("Attempting to treat a scalar as an array.");
2188 		assert o1 instanceof AssocArray;
2189 		AssocArray array = (AssocArray) o1;
2190 
2191 		// Convert arr_idx to a true integer if it is one
2192 //		String indexString = JRT.toAwkStringForOutput(arr_idx, getCONVFMT().toString());
2193 		array.put(arr_idx, rhs);
2194 		push(rhs);
2195 	}
2196 
2197 	/**
2198 	 * Numerically increases an Awk variable by one; the result
2199 	 * is placed back into that variable.
2200 	 */
2201 	private Object inc(long l, boolean is_global) {
2202 		Object o = runtime_stack.getVariable(l, is_global);
2203 		if (o == null || o instanceof UninitializedObject) {
2204 			runtime_stack.setVariable(l, o = ZERO, is_global);
2205 		}
2206 		runtime_stack.setVariable(l, JRT.inc(o), is_global);
2207 		return o;
2208 	}
2209 
2210 	/**
2211 	 * Numerically decreases an Awk variable by one; the result
2212 	 * is placed back into that variable.
2213 	 */
2214 	private Object dec(long l, boolean is_global) {
2215 		Object o = runtime_stack.getVariable(l, is_global);
2216 		if (o == null) {
2217 			runtime_stack.setVariable(l, o = ZERO, is_global);
2218 		}
2219 		runtime_stack.setVariable(l, JRT.dec(o), is_global);
2220 		return o;
2221 	}
2222 
2223 	/** {@inheritDoc} */
2224 	@Override
2225 	public final Object getRS() {
2226 		assert rs_offset != NULL_OFFSET;
2227 		Object rs_obj = runtime_stack.getVariable(rs_offset, true);	// true = global
2228 		return rs_obj;
2229 	}
2230 
2231 	/** {@inheritDoc} */
2232 	@Override
2233 	public final Object getOFS() {
2234 		assert ofs_offset != NULL_OFFSET;
2235 		Object ofs_obj = runtime_stack.getVariable(ofs_offset, true);	// true = global
2236 		return ofs_obj;
2237 	}
2238 
2239 	public final Object getORS() {
2240 		return runtime_stack.getVariable(ors_offset, true);	// true = global
2241 	}
2242 
2243 	/** {@inheritDoc} */
2244 	@Override
2245 	public final Object getSUBSEP() {
2246 		assert subsep_offset != NULL_OFFSET;
2247 		Object subsep_obj = runtime_stack.getVariable(subsep_offset, true);	// true = global
2248 		return subsep_obj;
2249 	}
2250 
2251 	/**
2252 	 * Performs the global variable assignment within the runtime environment.
2253 	 * These assignments come from the ARGV list (bounded by ARGC), which, in
2254 	 * turn, come from the command-line arguments passed into Awk.
2255 	 *
2256 	 * @param name_value The variable assignment in <i>name=value</i> form.
2257 	 */
2258 	@SuppressWarnings("unused")
2259 	private void setFilelistVariable(String name_value) {
2260 		int eq_idx = name_value.indexOf('=');
2261 		// variable name should be non-blank
2262 		assert eq_idx >= 0;
2263 		if (eq_idx == 0) {
2264 			throw new IllegalArgumentException("Must have a non-blank variable name in a name=value variable assignment argument.");
2265 		}
2266 		String name = name_value.substring(0, eq_idx);
2267 		String value = name_value.substring(eq_idx + 1);
2268 		Object obj;
2269 		try {
2270 			obj = Integer.parseInt(value);
2271 		} catch (NumberFormatException nfe) {
2272 			try {
2273 				obj = Double.parseDouble(value);
2274 			} catch (NumberFormatException nfe2) {
2275 				obj = value;
2276 			}
2277 		}
2278 
2279 		// make sure we're not receiving funcname=value assignments
2280 		if (function_names.contains(name)) {
2281 			throw new IllegalArgumentException("Cannot assign a scalar to a function name (" + name + ").");
2282 		}
2283 
2284 		Integer offset_obj = global_variable_offsets.get(name);
2285 		Boolean array_obj = global_variable_arrays.get(name);
2286 
2287 		if (offset_obj != null) {
2288 			assert array_obj != null;
2289 			if (array_obj.booleanValue()) {
2290 				throw new IllegalArgumentException("Cannot assign a scalar to a non-scalar variable (" + name + ").");
2291 			} else {
2292 				runtime_stack.setFilelistVariable(offset_obj.intValue(), obj);
2293 			}
2294 		}
2295 		// otherwise, do nothing
2296 	}
2297 
2298 	/** {@inheritDoc} */
2299 	@Override
2300 	public final void assignVariable(String name, Object obj) {
2301 		// make sure we're not receiving funcname=value assignments
2302 		if (function_names.contains(name)) {
2303 			throw new IllegalArgumentException("Cannot assign a scalar to a function name (" + name + ").");
2304 		}
2305 
2306 		Integer offset_obj = global_variable_offsets.get(name);
2307 		Boolean array_obj = global_variable_arrays.get(name);
2308 
2309 		if (offset_obj != null) {
2310 			assert array_obj != null;
2311 			if (array_obj.booleanValue()) {
2312 				throw new IllegalArgumentException("Cannot assign a scalar to a non-scalar variable (" + name + ").");
2313 			} else {
2314 				runtime_stack.setFilelistVariable(offset_obj.intValue(), obj);
2315 			}
2316 		}
2317 
2318 	}
2319 
2320 	private void swapOnStack() {
2321 		Object o1 = pop();
2322 		Object o2 = pop();
2323 		push(o1);
2324 		push(o2);
2325 	}
2326 
2327 	private void avmConsumeInputForGetline() throws IOException {
2328 		if (avmConsumeInput(true)) {
2329 			push(1);
2330 		} else {
2331 			push("");
2332 			push(0);
2333 		}
2334 		swapOnStack();
2335 	}
2336 
2337 	private void avmConsumeFileInputForGetline(String filename) throws IOException {
2338 		if (avmConsumeFileInput(filename)) {
2339 			push(1);
2340 		} else {
2341 			push(0);
2342 		}
2343 		swapOnStack();
2344 	}
2345 
2346 	private void avmConsumeCommandInputForGetline(String cmd) throws IOException {
2347 		if (avmConsumeCommandInput(cmd)) {
2348 			push(1);
2349 		} else {
2350 			push(0);
2351 		}
2352 		swapOnStack();
2353 	}
2354 
2355 	private boolean avmConsumeFileInput(String filename)
2356 			throws IOException
2357 	{
2358 		boolean retval = jrt.jrtConsumeFileInput(filename);
2359 		if (retval) {
2360 			push(jrt.getInputLine());
2361 		} else {
2362 			push("");
2363 		}
2364 		return retval;
2365 	}
2366 
2367 	private boolean avmConsumeCommandInput(String cmd)
2368 			throws IOException
2369 	{
2370 		boolean retval = jrt.jrtConsumeCommandInput(cmd);
2371 		if (retval) {
2372 			push(jrt.getInputLine());
2373 		} else {
2374 			push("");
2375 		}
2376 		return retval;
2377 	}
2378 
2379 	private boolean avmConsumeInput(boolean for_getline)
2380 			throws IOException
2381 	{
2382 		boolean retval = jrt.jrtConsumeInput(settings.getInput(), for_getline, locale);
2383 		if (retval && for_getline) {
2384 			push(jrt.getInputLine());
2385 		}
2386 		return retval;
2387 	}
2388 
2389 	/** {@inheritDoc} */
2390 	@Override
2391 	public Object getFS() {
2392 		assert fs_offset != NULL_OFFSET;
2393 		Object fs_string = runtime_stack.getVariable(fs_offset, true);	// true = global
2394 		return fs_string;
2395 	}
2396 
2397 	/** {@inheritDoc} */
2398 	@Override
2399 	public Object getCONVFMT() {
2400 		assert convfmt_offset != NULL_OFFSET : "convfmt_offset not defined";
2401 		Object convfmt_string = runtime_stack.getVariable(convfmt_offset, true);	// true = global
2402 		return convfmt_string;
2403 	}
2404 
2405 	/** {@inheritDoc} */
2406 	@Override
2407 	public void resetFNR() {
2408 		runtime_stack.setVariable(fnr_offset, ZERO, true);
2409 	}
2410 
2411 	/** {@inheritDoc} */
2412 	@Override
2413 	public void incFNR() {
2414 		inc(fnr_offset, true);
2415 	}
2416 
2417 	/** {@inheritDoc} */
2418 	@Override
2419 	public void incNR() {
2420 		inc(nr_offset, true);
2421 	}
2422 
2423 	/** {@inheritDoc} */
2424 	@Override
2425 	public void setNF(Integer I) {
2426 		runtime_stack.setVariable(nf_offset, I, true);
2427 	}
2428 
2429 	/** {@inheritDoc} */
2430 	@Override
2431 	public void setFILENAME(String filename) {
2432 		runtime_stack.setVariable(filename_offset, filename, true);
2433 	}
2434 
2435 	/** {@inheritDoc} */
2436 	@Override
2437 	public Object getARGV() {
2438 		return runtime_stack.getVariable(argv_offset, true);
2439 	}
2440 
2441 	/** {@inheritDoc} */
2442 	@Override
2443 	public Object getARGC() {
2444 		return runtime_stack.getVariable(argc_offset, true);
2445 	}
2446 
2447 	private String getOFMT() {
2448 		assert ofmt_offset != NULL_OFFSET;
2449 		String ofmt_string = runtime_stack.getVariable(ofmt_offset, true).toString();	// true = global
2450 		return ofmt_string;
2451 	}
2452 
2453 	private static final UninitializedObject BLANK = new UninitializedObject();
2454 
2455 	/**
2456 	 * The value of an address which is not yet assigned a tuple index.
2457 	 */
2458 	public static final int NULL_OFFSET = -1;
2459 
2460 	private static class RuntimeStack {
2461 
2462 		private Object[] globals = null;
2463 		private Object[] locals = null;
2464 		private MyStack<Object[]> locals_stack = new ArrayStackImpl<Object[]>();
2465 		private MyStack<Integer> return_indexes = new LinkedListStackImpl<Integer>();
2466 
2467 		@SuppressWarnings("unused")
2468 		public void dump() {
2469 			LOG.info("globals = " + Arrays.toString(globals));
2470 			LOG.info("locals = " + Arrays.toString(locals));
2471 			LOG.info("locals_stack = " + locals_stack);
2472 			LOG.info("return_indexes = " + return_indexes);
2473 		}
2474 
2475 		Object[] getNumGlobals() {
2476 			return globals;
2477 		}
2478 
2479 		/**
2480 		 * Must be one of the first methods executed.
2481 		 */
2482 		void setNumGlobals(long l) {
2483 			assert l >= 0;
2484 			assert globals == null;
2485 			globals = new Object[(int) l];
2486 			for (int i = 0 ; i < l ; i++) {
2487 				globals[i] = null;
2488 			}
2489 			// must accept multiple executions
2490 			//expandFrameIfNecessary(num_globals);
2491 		}
2492 
2493 		/*
2494 		// this assumes globals = Object[0] upon initialization
2495 		private void expandFrameIfNecessary(int num_globals) {
2496 			if (num_globals == globals.length)
2497 				// no need for expansion;
2498 				// do nothing
2499 				return;
2500 			Object[] new_frame = new Object[num_globals];
2501 			for (int i=0;i<globals.length;++i)
2502 				new_frame[i] = globals[i];
2503 			globals = new_frame;
2504 		}
2505 		 */
2506 
2507 		Object getVariable(long offset, boolean is_global) {
2508 			assert globals != null;
2509 			assert offset != NULL_OFFSET;
2510 			if (is_global) {
2511 				return globals[(int) offset];
2512 			} else {
2513 				return locals[(int) offset];
2514 			}
2515 		}
2516 
2517 		Object setVariable(long offset, Object val, boolean is_global) {
2518 			assert globals != null;
2519 			assert offset != NULL_OFFSET;
2520 			if (is_global) {
2521 				return globals[(int) offset] = val;
2522 			} else {
2523 				return locals[(int) offset] = val;
2524 			}
2525 		}
2526 
2527 		// for _DELETE_ARRAY_
2528 		void removeVariable(long offset, boolean is_global) {
2529 			assert globals != null;
2530 			assert offset != NULL_OFFSET;
2531 			if (is_global) {
2532 				assert globals[(int) offset] == null || globals[(int) offset] instanceof AssocArray;
2533 				globals[(int) offset] = null;
2534 			} else {
2535 				assert locals[(int) offset] == null || locals[(int) offset] instanceof AssocArray;
2536 				locals[(int) offset] = null;
2537 			}
2538 		}
2539 
2540 		void setFilelistVariable(int offset, Object value) {
2541 			assert globals != null;
2542 			assert offset != NULL_OFFSET;
2543 			globals[offset] = value;
2544 		}
2545 
2546 		void pushFrame(long num_formal_params, int position_idx) {
2547 			locals_stack.push(locals);
2548 			locals = new Object[(int) num_formal_params];
2549 			return_indexes.push(position_idx);
2550 		}
2551 
2552 		/** returns the position index */
2553 		int popFrame() {
2554 			locals = locals_stack.pop();
2555 			return return_indexes.pop();
2556 		}
2557 
2558 		void popAllFrames() {
2559 			int sz = locals_stack.size();
2560 			while (--sz >= 0) {
2561 				locals = locals_stack.pop();
2562 				return_indexes.pop();
2563 			}
2564 		}
2565 		private Object return_value;
2566 
2567 		void setReturnValue(Object obj) {
2568 			assert return_value == null;
2569 			return_value = obj;
2570 		}
2571 
2572 		Object getReturnValue() {
2573 			Object retval;
2574 			if (return_value == null) {
2575 				retval = BLANK;
2576 			} else {
2577 				retval = return_value;
2578 			}
2579 			return_value = null;
2580 			return retval;
2581 		}
2582 	}
2583 }