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 						// top-of-stack = ere
1048 						// next = repl
1049 						// (use $0 as orig)
1050 						String convfmt = getCONVFMT().toString();
1051 						String repl = JRT.toAwkString(pop(), convfmt, locale);
1052 						String ere = JRT.toAwkString(pop(), convfmt, locale);
1053 						String orig = JRT.toAwkString(jrt.jrtGetInputField(0), convfmt, locale);
1054 						String newstring;
1055 						if (is_gsub) {
1056 							newstring = replaceAll(orig, ere, repl);
1057 						} else {
1058 							newstring = replaceFirst(orig, ere, repl);
1059 						}
1060 						// assign it to "$0"
1061 						jrt.setInputLine(newstring);
1062 						jrt.jrtParseFields();
1063 						position.next();
1064 						break;
1065 					}
1066 					case AwkTuples._SUB_FOR_DOLLAR_REFERENCE_: {
1067 						// arg[0] = is_global
1068 						// stack[0] = original field value
1069 						// stack[1] = replacement string
1070 						// stack[2] = ere
1071 						// stack[3] = field num
1072 						// (use $field_num as orig)
1073 						String newString = execSubOrGSub(position, 0);
1074 						int fieldNum = (int) JRT.toDouble(pop());
1075 						// assign it to "$0"
1076 						if (fieldNum == 0) {
1077 							jrt.setInputLine(newString);
1078 							jrt.jrtParseFields();
1079 						} else {
1080 							jrt.jrtSetInputField(newString, fieldNum);
1081 						}
1082 						position.next();
1083 						break;
1084 					}
1085 					case AwkTuples._SUB_FOR_VARIABLE_: {
1086 						// arg[0] = offset
1087 						// arg[1] = is_global
1088 						// arg[2] = is_gsub
1089 						// stack[0] = original variable value
1090 						// stack[1] = replacement string
1091 						// stack[2] = ere
1092 						long offset = position.intArg(0);
1093 						boolean is_global = position.boolArg(1);
1094 						String newString = execSubOrGSub(position, 2);
1095 						// assign it to "offset/global"
1096 						assign(offset, newString, is_global, position);
1097 						pop();
1098 						position.next();
1099 						break;
1100 					}
1101 					case AwkTuples._SUB_FOR_ARRAY_REFERENCE_: {
1102 						// arg[0] = offset
1103 						// arg[1] = is_global
1104 						// arg[2] = is_gsub
1105 						// stack[0] = original variable value
1106 						// stack[1] = replacement string
1107 						// stack[2] = ere
1108 						// stack[3] = array index
1109 						// ARRAY reference offset/is_global
1110 						long offset = position.intArg(0);
1111 						boolean is_global = position.boolArg(1);
1112 						Object arr_idx = pop();
1113 						String newString = execSubOrGSub(position, 2);
1114 						// assign it to "offset/arr_idx/global"
1115 						assignArray(offset, arr_idx, newString, is_global);
1116 						pop();
1117 						position.next();
1118 						break;
1119 					}
1120 					case AwkTuples._SPLIT_: {
1121 						// arg[0] = num args
1122 						// stack[0] = field_sep (only if num args == 3)
1123 						// stack[1] = array
1124 						// stack[2] = string
1125 						String convfmt = getCONVFMT().toString();
1126 						long numargs = position.intArg(0);
1127 						String fs_string;
1128 						if (numargs == 2) {
1129 							fs_string = JRT.toAwkString(getFS(), convfmt, locale);
1130 						} else if (numargs == 3) {
1131 							fs_string = JRT.toAwkString(pop(), convfmt, locale);
1132 						} else {
1133 							throw new Error("Invalid # of args. split() requires 2 or 3. Got: " + numargs);
1134 						}
1135 						Object o = pop();
1136 						if (!(o instanceof AssocArray)) {
1137 							throw new AwkRuntimeException(position.lineNumber(), o + " is not an array.");
1138 						}
1139 						String s = JRT.toAwkString(pop(), convfmt, locale);
1140 						Enumeration<Object> tokenizer;
1141 						if (fs_string.equals(" ")) {
1142 							tokenizer = new StringTokenizer(s);
1143 						} else if (fs_string.length() == 1) {
1144 							tokenizer = new SingleCharacterTokenizer(s, fs_string.charAt(0));
1145 						} else if (fs_string.isEmpty()) {
1146 							tokenizer = new CharacterTokenizer(s);
1147 						} else {
1148 							tokenizer = new RegexTokenizer(s, fs_string);
1149 						}
1150 
1151 						AssocArray assoc_array = (AssocArray) o;
1152 						assoc_array.clear();
1153 						int cnt = 0;
1154 						while (tokenizer.hasMoreElements()) {
1155 							assoc_array.put(++cnt, tokenizer.nextElement());
1156 						}
1157 						push(cnt);
1158 						position.next();
1159 						break;
1160 					}
1161 					case AwkTuples._SUBSTR_: {
1162 						// arg[0] = num args
1163 						// stack[0] = length (only if num args == 3)
1164 						// stack[1] = start pos
1165 						// stack[2] = string
1166 						long numargs = position.intArg(0);
1167 						int startPos, length;
1168 						String s;
1169 						if (numargs == 3)
1170 						{
1171 							length = (int) JRT.toLong(pop());
1172 							startPos = (int) JRT.toDouble(pop());
1173 							s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1174 						} else if (numargs == 2) {
1175 							startPos = (int) JRT.toDouble(pop());
1176 							s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1177 							length = s.length() - startPos + 1;
1178 						} else {
1179 							throw new Error("numargs for _SUBSTR_ must be 2 or 3. It is " + numargs);
1180 						}
1181 						if (startPos <= 0) {
1182 							startPos = 1;
1183 						}
1184 						if (length <= 0 || startPos > s.length()) {
1185 							push(BLANK);
1186 						} else {
1187 							if (startPos + length > s.length()) {
1188 								push(s.substring(startPos - 1));
1189 							} else {
1190 								push(s.substring(startPos - 1, startPos + length - 1));
1191 							}
1192 						}
1193 						position.next();
1194 						break;
1195 					}
1196 					case AwkTuples._TOLOWER_: {
1197 						// stack[0] = string
1198 						push(JRT.toAwkString(pop(), getCONVFMT().toString(), locale).toLowerCase());
1199 						position.next();
1200 						break;
1201 					}
1202 					case AwkTuples._TOUPPER_: {
1203 						// stack[0] = string
1204 						push(JRT.toAwkString(pop(), getCONVFMT().toString(), locale).toUpperCase());
1205 						position.next();
1206 						break;
1207 					}
1208 					case AwkTuples._SYSTEM_: {
1209 						// stack[0] = command string
1210 						String s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1211 						push(JRT.jrtSystem(s));
1212 						position.next();
1213 						break;
1214 					}
1215 					case AwkTuples._SWAP_: {
1216 						// stack[0] = item1
1217 						// stack[1] = item2
1218 						swapOnStack();
1219 						position.next();
1220 						break;
1221 					}
1222 					case AwkTuples._CMP_EQ_: {
1223 						// stack[0] = item2
1224 						// stack[1] = item1
1225 						Object o2 = pop();
1226 						Object o1 = pop();
1227 						push(JRT.compare2(o1, o2, 0) ? ONE : ZERO);
1228 						position.next();
1229 						break;
1230 					}
1231 					case AwkTuples._CMP_LT_: {
1232 						// stack[0] = item2
1233 						// stack[1] = item1
1234 						Object o2 = pop();
1235 						Object o1 = pop();
1236 						push(JRT.compare2(o1, o2, -1) ? ONE : ZERO);
1237 						position.next();
1238 						break;
1239 					}
1240 					case AwkTuples._CMP_GT_: {
1241 						// stack[0] = item2
1242 						// stack[1] = item1
1243 						Object o2 = pop();
1244 						Object o1 = pop();
1245 						push(JRT.compare2(o1, o2, 1) ? ONE : ZERO);
1246 						position.next();
1247 						break;
1248 					}
1249 					case AwkTuples._MATCHES_: {
1250 						// stack[0] = item2
1251 						// stack[1] = item1
1252 						Object o2 = pop();
1253 						Object o1 = pop();
1254 						// use o1's string value
1255 						String s = o1.toString();
1256 						// assume o2 is a regexp
1257 						if (o2 instanceof Pattern) {
1258 							Pattern p = (Pattern) o2;
1259 							Matcher m = p.matcher(s);
1260 							// m.matches() matches the ENTIRE string
1261 							// m.find() is more appropriate
1262 							boolean result = m.find();
1263 							push(result ? 1 : 0);
1264 						} else {
1265 							String r = JRT.toAwkString(o2, getCONVFMT().toString(), locale);
1266 							boolean result = Pattern.compile(r).matcher(s).find();
1267 							push(result ? 1 : 0);
1268 						}
1269 						position.next();
1270 						break;
1271 					}
1272 					case AwkTuples._SLEEP_: {
1273 						// arg[0] = num_args
1274 						// if (num_args==1)
1275 						// 	stack[0] = # of seconds
1276 						// else
1277 						// 	nothing on the stack
1278 						//int seconds = (int) JRT.toDouble(pop());
1279 						long seconds;
1280 						long numargs = position.intArg(0);
1281 						if (numargs == 0) {
1282 							seconds = 1;
1283 						} else {
1284 							seconds = (long) JRT.toDouble(pop());
1285 						}
1286 						try {
1287 							Thread.sleep(seconds * 1000);
1288 						} catch (InterruptedException ie) {
1289 							throw new AwkRuntimeException(position.lineNumber(), "Caught exception while waiting for process exit: " + ie);
1290 						}
1291 						position.next();
1292 						break;
1293 					}
1294 					case AwkTuples._DUMP_: {
1295 						// arg[0] = num_args
1296 						// if (num_args==0)
1297 						// 	all Jawk global variables
1298 						// else
1299 						// 	args are assoc arrays to display
1300 						//int seconds = (int) JRT.toDouble(pop());
1301 						long numargs = position.intArg(0);
1302 						AssocArray[] aa_array;
1303 						if (numargs == 0) {
1304 							aa_array = null;
1305 						} else {
1306 							aa_array = new AssocArray[(int) numargs];
1307 							for (int i = 0; i < numargs; ++i) {
1308 								aa_array[i] = (AssocArray) pop();
1309 							}
1310 						}
1311 						avmDump(aa_array);
1312 						position.next();
1313 						break;
1314 					}
1315 					case AwkTuples._ADD_: {
1316 						// stack[0] = item2
1317 						// stack[1] = item1
1318 						Object o2 = pop();
1319 						Object o1 = pop();
1320 						double d1 = JRT.toDouble(o1);
1321 						double d2 = JRT.toDouble(o2);
1322 						double ans = d1 + d2;
1323 						if (ans == (long) ans) {
1324 							push((long) ans);
1325 						} else {
1326 							push(ans);
1327 						}
1328 						position.next();
1329 						break;
1330 					}
1331 					case AwkTuples._SUBTRACT_: {
1332 						// stack[0] = item2
1333 						// stack[1] = item1
1334 						Object o2 = pop();
1335 						Object o1 = pop();
1336 						double d1 = JRT.toDouble(o1);
1337 						double d2 = JRT.toDouble(o2);
1338 						double ans = d1 - d2;
1339 						if (ans == (long) ans) {
1340 							push((long) ans);
1341 						} else {
1342 							push(ans);
1343 						}
1344 						position.next();
1345 						break;
1346 					}
1347 					case AwkTuples._MULTIPLY_: {
1348 						// stack[0] = item2
1349 						// stack[1] = item1
1350 						Object o2 = pop();
1351 						Object o1 = pop();
1352 						double d1 = JRT.toDouble(o1);
1353 						double d2 = JRT.toDouble(o2);
1354 						double ans = d1 * d2;
1355 						if (ans == (long) ans) {
1356 							push((long) ans);
1357 						} else {
1358 							push(ans);
1359 						}
1360 						position.next();
1361 						break;
1362 					}
1363 					case AwkTuples._DIVIDE_: {
1364 						// stack[0] = item2
1365 						// stack[1] = item1
1366 						Object o2 = pop();
1367 						Object o1 = pop();
1368 						double d1 = JRT.toDouble(o1);
1369 						double d2 = JRT.toDouble(o2);
1370 						double ans = d1 / d2;
1371 						if (ans == (long) ans) {
1372 							push((long) ans);
1373 						} else {
1374 							push(ans);
1375 						}
1376 						position.next();
1377 						break;
1378 					}
1379 					case AwkTuples._MOD_: {
1380 						// stack[0] = item2
1381 						// stack[1] = item1
1382 						Object o2 = pop();
1383 						Object o1 = pop();
1384 						double d1 = JRT.toDouble(o1);
1385 						double d2 = JRT.toDouble(o2);
1386 						double ans = d1 % d2;
1387 						if (ans == (long) ans) {
1388 							push((long) ans);
1389 						} else {
1390 							push(ans);
1391 						}
1392 						position.next();
1393 						break;
1394 					}
1395 					case AwkTuples._POW_: {
1396 						// stack[0] = item2
1397 						// stack[1] = item1
1398 						Object o2 = pop();
1399 						Object o1 = pop();
1400 						double d1 = JRT.toDouble(o1);
1401 						double d2 = JRT.toDouble(o2);
1402 						double ans = Math.pow(d1, d2);
1403 						if (ans == (long) ans) {
1404 							push((long) ans);
1405 						} else {
1406 							push(ans);
1407 						}
1408 						position.next();
1409 						break;
1410 					}
1411 					case AwkTuples._DUP_: {
1412 						// stack[0] = top of stack item
1413 						Object o = pop();
1414 						push(o);
1415 						push(o);
1416 						position.next();
1417 						break;
1418 					}
1419 					case AwkTuples._KEYLIST_: {
1420 						// stack[0] = AssocArray
1421 						Object o = pop();
1422 						assert o != null;
1423 						if (!(o instanceof AssocArray)) {
1424 							throw new AwkRuntimeException(position.lineNumber(), "Cannot get a keylist (via 'in') of a non associative array. arg = " + o.getClass() + ", " + o);
1425 						}
1426 						AssocArray aa = (AssocArray) o;
1427 						push(new KeyListImpl(aa.keySet()));
1428 						position.next();
1429 						break;
1430 					}
1431 					case AwkTuples._IS_EMPTY_KEYLIST_: {
1432 						// arg[0] = address
1433 						// stack[0] = KeyList
1434 						Object o = pop();
1435 						if (o == null || !(o instanceof KeyList)) {
1436 							throw new AwkRuntimeException(position.lineNumber(), "Cannot get a keylist (via 'in') of a non associative array. arg = " + o.getClass() + ", " + o);
1437 						}
1438 						KeyList keylist = (KeyList) o;
1439 						if (keylist.size() == 0) {
1440 							position.jump(position.addressArg());
1441 						} else {
1442 							position.next();
1443 						}
1444 						break;
1445 					}
1446 					case AwkTuples._GET_FIRST_AND_REMOVE_FROM_KEYLIST_: {
1447 						// stack[0] = KeyList
1448 						Object o = pop();
1449 						if (o == null || !(o instanceof KeyList)) {
1450 							throw new AwkRuntimeException(position.lineNumber(), "Cannot get a keylist (via 'in') of a non associative array. arg = " + o.getClass() + ", " + o);
1451 						}
1452 						// pop off and return the head of the key set
1453 						KeyList keylist = (KeyList) o;
1454 						assert keylist.size() > 0;
1455 						push(keylist.getFirstAndRemove());
1456 						position.next();
1457 						break;
1458 					}
1459 					case AwkTuples._CHECK_CLASS_: {
1460 						// arg[0] = class object
1461 						// stack[0] = item to check
1462 						Object o = pop();
1463 						if (!(position.classArg().isInstance(o))) {
1464 							throw new AwkRuntimeException(position.lineNumber(), "Verification failed. Top-of-stack = " + o.getClass() + " isn't an instance of " + position.classArg());
1465 						}
1466 						push(o);
1467 						position.next();
1468 						break;
1469 					}
1470 					case AwkTuples._CONSUME_INPUT_: {
1471 						// arg[0] = address
1472 						// false = do NOT put result on stack...
1473 						// instead, put it in field vars ($0, $1, ...)
1474 						if (avmConsumeInput(false)) {
1475 							position.next();
1476 						} else {
1477 							position.jump(position.addressArg());
1478 						}
1479 						break;
1480 					}
1481 					case AwkTuples._GETLINE_INPUT_: {
1482 						avmConsumeInputForGetline();
1483 						position.next();
1484 						break;
1485 					}
1486 					case AwkTuples._USE_AS_FILE_INPUT_: {
1487 						// stack[0] = filename
1488 						String s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1489 						avmConsumeFileInputForGetline(s);
1490 						position.next();
1491 						break;
1492 					}
1493 					case AwkTuples._USE_AS_COMMAND_INPUT_: {
1494 						// stack[0] = command line
1495 						String s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1496 						avmConsumeCommandInputForGetline(s);
1497 						position.next();
1498 						break;
1499 					}
1500 					case AwkTuples._NF_OFFSET_: {
1501 						// stack[0] = offset
1502 						nf_offset = position.intArg(0);
1503 						assert nf_offset != NULL_OFFSET;
1504 						assign(nf_offset, 0, true, position);
1505 						pop();			// clean up the stack after the assignment
1506 						position.next();
1507 						break;
1508 					}
1509 					case AwkTuples._NR_OFFSET_: {
1510 						// stack[0] = offset
1511 						nr_offset = position.intArg(0);
1512 						assert nr_offset != NULL_OFFSET;
1513 						assign(nr_offset, 0, true, position);
1514 						pop();			// clean up the stack after the assignment
1515 						position.next();
1516 						break;
1517 					}
1518 					case AwkTuples._FNR_OFFSET_: {
1519 						// stack[0] = offset
1520 						fnr_offset = position.intArg(0);
1521 						assert fnr_offset != NULL_OFFSET;
1522 						assign(fnr_offset, 0, true, position);
1523 						pop();			// clean up the stack after the assignment
1524 						position.next();
1525 						break;
1526 					}
1527 					case AwkTuples._FS_OFFSET_: {
1528 						// stack[0] = offset
1529 						fs_offset = position.intArg(0);
1530 						assert fs_offset != NULL_OFFSET;
1531 						if (initial_fs_value == null) {
1532 							assign(fs_offset, " ", true, position);
1533 						} else {
1534 							assign(fs_offset, initial_fs_value, true, position);
1535 						}
1536 						pop();			// clean up the stack after the assignment
1537 						position.next();
1538 						break;
1539 					}
1540 					case AwkTuples._RS_OFFSET_: {
1541 						// stack[0] = offset
1542 						rs_offset = position.intArg(0);
1543 						assert rs_offset != NULL_OFFSET;
1544 						assign(rs_offset, settings.getDefaultRS(), true, position);
1545 						pop();			// clean up the stack after the assignment
1546 						position.next();
1547 						break;
1548 					}
1549 					case AwkTuples._OFS_OFFSET_: {
1550 						// stack[0] = offset
1551 						ofs_offset = position.intArg(0);
1552 						assert ofs_offset != NULL_OFFSET;
1553 						assign(ofs_offset, " ", true, position);
1554 						pop();			// clean up the stack after the assignment
1555 						position.next();
1556 						break;
1557 					}
1558 					case AwkTuples._ORS_OFFSET_: {
1559 						// stack[0] = offset
1560 						ors_offset = position.intArg(0);
1561 						assert ors_offset != NULL_OFFSET;
1562 						assign(ors_offset, settings.getDefaultORS(), true, position);
1563 						pop();			// clean up the stack after the assignment
1564 						position.next();
1565 						break;
1566 					}
1567 					case AwkTuples._RSTART_OFFSET_: {
1568 						// stack[0] = offset
1569 						rstart_offset = position.intArg(0);
1570 						assert rstart_offset != NULL_OFFSET;
1571 						assign(rstart_offset, "", true, position);
1572 						pop();			// clean up the stack after the assignment
1573 						position.next();
1574 						break;
1575 					}
1576 					case AwkTuples._RLENGTH_OFFSET_: {
1577 						// stack[0] = offset
1578 						rlength_offset = position.intArg(0);
1579 						assert rlength_offset != NULL_OFFSET;
1580 						assign(rlength_offset, "", true, position);
1581 						pop();			// clean up the stack after the assignment
1582 						position.next();
1583 						break;
1584 					}
1585 					case AwkTuples._FILENAME_OFFSET_: {
1586 						// stack[0] = offset
1587 						filename_offset = position.intArg(0);
1588 						assert filename_offset != NULL_OFFSET;
1589 						assign(filename_offset, "", true, position);
1590 						pop();			// clean up the stack after the assignment
1591 						position.next();
1592 						break;
1593 					}
1594 					case AwkTuples._SUBSEP_OFFSET_: {
1595 						// stack[0] = offset
1596 						subsep_offset = position.intArg(0);
1597 						assert subsep_offset != NULL_OFFSET;
1598 						assign(subsep_offset, new String(new byte[] {28}), true, position);
1599 						pop();			// clean up the stack after the assignment
1600 						position.next();
1601 						break;
1602 					}
1603 					case AwkTuples._CONVFMT_OFFSET_: {
1604 						// stack[0] = offset
1605 						convfmt_offset = position.intArg(0);
1606 						assert convfmt_offset != NULL_OFFSET;
1607 						assign(convfmt_offset, "%.6g", true, position);
1608 						pop();			// clean up the stack after the assignment
1609 						position.next();
1610 						break;
1611 					}
1612 					case AwkTuples._OFMT_OFFSET_: {
1613 						// stack[0] = offset
1614 						ofmt_offset = position.intArg(0);
1615 						assert ofmt_offset != NULL_OFFSET;
1616 						assign(ofmt_offset, "%.6g", true, position);
1617 						pop();			// clean up the stack after the assignment
1618 						position.next();
1619 						break;
1620 					}
1621 					case AwkTuples._ENVIRON_OFFSET_: {
1622 						// stack[0] = offset
1623 						//// assignArray(offset, arr_idx, newstring, is_global);
1624 						environ_offset = position.intArg(0);
1625 						assert environ_offset != NULL_OFFSET;
1626 						// set the initial variables
1627 						Map<String, String> env = System.getenv();
1628 						for (Map.Entry<String, String> var : env.entrySet()) {
1629 							assignArray(environ_offset, var.getKey(), var.getValue(), true);
1630 							pop(); // clean up the stack after the assignment
1631 						}
1632 						position.next();
1633 						break;
1634 					}
1635 					case AwkTuples._ARGC_OFFSET_: {
1636 						// stack[0] = offset
1637 						argc_offset = position.intArg(0);
1638 						assert argc_offset != NULL_OFFSET;
1639 						//assign(argc_offset, arguments.size(), true, position);	// true = global
1640 						// +1 to include the "java Awk" (ARGV[0])
1641 						assign(argc_offset, arguments.size() + 1, true, position);	// true = global
1642 						pop();			// clean up the stack after the assignment
1643 						position.next();
1644 						break;
1645 					}
1646 					case AwkTuples._ARGV_OFFSET_: {
1647 						// stack[0] = offset
1648 						argv_offset = position.intArg(0);
1649 						assert argv_offset != NULL_OFFSET;
1650 						// consume argv (looping from 1 to argc)
1651 						int argc = (int) JRT.toDouble(runtime_stack.getVariable(argc_offset, true));	// true = global
1652 						assignArray(argv_offset, 0, "java Awk", true);
1653 						pop();
1654 						for (int i = 1; i < argc; i++) {
1655 							//assignArray(argv_offset, i+1, arguments.get(i), true);
1656 							assignArray(argv_offset, i, arguments.get(i - 1), true);
1657 							pop();			// clean up the stack after the assignment
1658 						}
1659 						position.next();
1660 						break;
1661 					}
1662 					case AwkTuples._GET_INPUT_FIELD_: {
1663 						// stack[0] = field number
1664 						int fieldnum = parseIntField(pop(), position);
1665 						push(jrt.jrtGetInputField(fieldnum));
1666 						position.next();
1667 						break;
1668 					}
1669 					case AwkTuples._APPLY_RS_: {
1670 						assert rs_offset != NULL_OFFSET;
1671 						Object rs_obj = runtime_stack.getVariable(rs_offset, true);	// true = global
1672 						if (jrt.getPartitioningReader() != null) {
1673 							jrt.getPartitioningReader().setRecordSeparator(rs_obj.toString());
1674 						}
1675 						position.next();
1676 						break;
1677 					}
1678 					case AwkTuples._CALL_FUNCTION_: {
1679 						// arg[0] = function address
1680 						// arg[1] = function name
1681 						// arg[2] = # of formal parameters
1682 						// arg[3] = # of actual parameters
1683 						// stack[0] = last actual parameter
1684 						// stack[1] = before-last actual parameter
1685 						// ...
1686 						// stack[n-1] = first actual parameter
1687 						// etc.
1688 						Address func_addr = position.addressArg();
1689 						//String func_name = position.arg(1).toString();
1690 						long num_formal_params = position.intArg(2);
1691 						long num_actual_params = position.intArg(3);
1692 						assert num_formal_params >= num_actual_params;
1693 						runtime_stack.pushFrame(num_formal_params, position.current());
1694 						// Arguments are stacked, so first in the stack is the last for the function 
1695 						for (long i = num_actual_params - 1 ; i >= 0 ; i--) {
1696 							runtime_stack.setVariable(i, pop(), false);	// false = local
1697 						}
1698 						position.jump(func_addr);
1699 						//position.next();
1700 						break;
1701 					}
1702 					case AwkTuples._FUNCTION_: {
1703 						// important for compilation,
1704 						// not needed for interpretation
1705 						// arg[0] = function name
1706 						// arg[1] = # of formal parameters
1707 						position.next();
1708 						break;
1709 					}
1710 					case AwkTuples._SET_RETURN_RESULT_: {
1711 						// stack[0] = return result
1712 						runtime_stack.setReturnValue(pop());
1713 						position.next();
1714 						break;
1715 					}
1716 					case AwkTuples._RETURN_FROM_FUNCTION_: {
1717 						position.jump(runtime_stack.popFrame());
1718 						push(runtime_stack.getReturnValue());
1719 						position.next();
1720 						break;
1721 					}
1722 					case AwkTuples._SET_NUM_GLOBALS_: {
1723 						// arg[0] = # of globals
1724 						assert position.intArg(0) == global_variable_offsets.size();
1725 						runtime_stack.setNumGlobals(position.intArg(0));
1726 
1727 						// now that we have the global variable size,
1728 						// we can allocate the initial variables
1729 
1730 						// assign -v variables (from initial_variables container)
1731 						for (String key : initial_variables.keySet()) {
1732 							if (function_names.contains(key)) {
1733 								throw new IllegalArgumentException("Cannot assign a scalar to a function name (" + key + ").");
1734 							}
1735 							Integer offset_obj = global_variable_offsets.get(key);
1736 							Boolean array_obj = global_variable_arrays.get(key);
1737 							if (offset_obj != null) {
1738 								assert array_obj != null;
1739 								if (array_obj.booleanValue()) {
1740 									throw new IllegalArgumentException("Cannot assign a scalar to a non-scalar variable (" + key + ").");
1741 								} else {
1742 									Object obj = initial_variables.get(key);
1743 									runtime_stack.setFilelistVariable(offset_obj.intValue(), obj);
1744 								}
1745 							}
1746 						}
1747 
1748 						position.next();
1749 						break;
1750 					}
1751 					case AwkTuples._CLOSE_: {
1752 						// stack[0] = file or command line to close
1753 						String s = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1754 						push(jrt.jrtClose(s));
1755 						position.next();
1756 						break;
1757 					}
1758 					case AwkTuples._APPLY_SUBSEP_: {
1759 						// arg[0] = # of elements for SUBSEP application
1760 						// stack[0] = first element
1761 						// stack[1] = second element
1762 						// etc.
1763 						long count = position.intArg(0);
1764 						assert count >= 1;
1765 						//String s;
1766 						String convfmt = getCONVFMT().toString();
1767 						if (count == 1) {
1768 							//s = JRT.toAwkString(pop(), convfmt);
1769 						} else {
1770 							StringBuilder sb = new StringBuilder();
1771 							sb.append(JRT.toAwkString(pop(), convfmt, locale));
1772 							String subsep = JRT.toAwkString(runtime_stack.getVariable(subsep_offset, true), convfmt, locale);
1773 							for (int i = 1; i < count; i++) {
1774 								sb.insert(0, subsep);
1775 								sb.insert(0, JRT.toAwkString(pop(), convfmt, locale));
1776 							}
1777 							push(sb.toString());
1778 						}
1779 						position.next();
1780 						break;
1781 					}
1782 					case AwkTuples._DELETE_ARRAY_ELEMENT_: {
1783 						// arg[0] = offset
1784 						// arg[1] = is_global
1785 						// stack[0] = array index
1786 						long offset = position.intArg(0);
1787 						boolean is_global = position.boolArg(1);
1788 						AssocArray aa = (AssocArray) runtime_stack.getVariable(offset, is_global);
1789 						Object key = pop();
1790 						if (aa != null) {
1791 							aa.remove(key);
1792 						}
1793 						position.next();
1794 						break;
1795 					}
1796 					case AwkTuples._DELETE_ARRAY_: {
1797 						// arg[0] = offset
1798 						// arg[1] = is_global
1799 						// (nothing on the stack)
1800 						long offset = position.intArg(0);
1801 						boolean is_global = position.boolArg(1);
1802 						runtime_stack.removeVariable(offset, is_global);
1803 						position.next();
1804 						break;
1805 					}
1806 					case AwkTuples._SET_EXIT_ADDRESS_: {
1807 						// arg[0] = exit address
1808 						exit_address = position.addressArg();
1809 						position.next();
1810 						break;
1811 					}
1812 					case AwkTuples._SET_WITHIN_END_BLOCKS_: {
1813 						// arg[0] = whether within the END blocks section
1814 						within_end_blocks = position.boolArg(0);
1815 						position.next();
1816 						break;
1817 					}
1818 					case AwkTuples._EXIT_WITHOUT_CODE_:
1819 					case AwkTuples._EXIT_WITH_CODE_: {
1820 						if (opcode == AwkTuples._EXIT_WITH_CODE_) {
1821 							// stack[0] = exit code
1822 							exit_code = (int) JRT.toDouble(pop());
1823 						}
1824 						throw_exit_exception = true;
1825 						
1826 						// If in BEGIN or in a rule, jump to the END section
1827 						if (!within_end_blocks) {
1828 							// clear runtime stack
1829 							runtime_stack.popAllFrames();
1830 							// clear operand stack
1831 							operand_stack.clear();
1832 							position.jump(exit_address);
1833 						} else {
1834 							// Exit immediately with ExitException
1835 							jrt.jrtCloseAll();
1836 							// clear operand stack
1837 							operand_stack.clear();
1838 							throw new ExitException(exit_code, "The AWK script requested an exit");
1839 							//position.next();
1840 						}
1841 						break;
1842 					}
1843 					case AwkTuples._REGEXP_: {
1844 						// arg[0] = string representation of regexp
1845 						String key = JRT.toAwkString(position.arg(0), getCONVFMT().toString(), locale);
1846 						Pattern pattern = regexps.get(key);
1847 						if (pattern == null) {
1848 							regexps.put(key, pattern = Pattern.compile(key));
1849 						}
1850 						push(pattern);
1851 						position.next();
1852 						break;
1853 					}
1854 					case AwkTuples._CONDITION_PAIR_: {
1855 						// stack[0] = End condition
1856 						// stack[1] = Start condition
1857 						ConditionPair cp = condition_pairs.get(position.current());
1858 						if (cp == null) {
1859 							cp = new ConditionPair();
1860 							condition_pairs.put(position.current(), cp);
1861 						}
1862 						boolean end = jrt.toBoolean(pop());
1863 						boolean start = jrt.toBoolean(pop());
1864 						push(cp.update(start, end) ? ONE : ZERO);
1865 						position.next();
1866 						break;
1867 					}
1868 					case AwkTuples._IS_IN_: {
1869 						// stack[0] = AssocArray
1870 						// stack[1] = key to check
1871 						Object arr = pop();
1872 						Object arg = pop();
1873 						AssocArray aa = (AssocArray) arr;
1874 						boolean result = aa.isIn(arg);
1875 						push(result ? ONE : ZERO);
1876 						position.next();
1877 						break;
1878 					}
1879 					case AwkTuples._CAST_DOUBLE_: {
1880 						push(JRT.toDouble(pop()));
1881 						position.next();
1882 						break;
1883 					}
1884 					case AwkTuples._CAST_STRING_: {
1885 						push(pop().toString());
1886 						position.next();
1887 						break;
1888 					}
1889 					case AwkTuples._THIS_: {
1890 						// this is in preparation for a function
1891 						// call for the JVM-COMPILED script, only
1892 						// therefore, do NOTHING for the interpreted
1893 						// version
1894 						position.next();
1895 						break;
1896 					}
1897 					case AwkTuples._EXEC_: {
1898 						// stack[0] = Jawk code
1899 
1900 						// TODO FIXME First attempt. It is not complete by a long-shot. Use at your own risk.
1901 
1902 						String awk_code = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
1903 						List<ScriptSource> scriptSources = new ArrayList<ScriptSource>(1);
1904 						scriptSources.add(new ScriptSource(ScriptSource.DESCRIPTION_COMMAND_LINE_SCRIPT, new StringReader(awk_code), false));
1905 
1906 						org.sentrysoftware.jawk.frontend.AwkParser ap = new org.sentrysoftware.jawk.frontend.AwkParser(
1907 								//true, true, true, extensions
1908 								settings.isAdditionalFunctions(),
1909 								settings.isAdditionalTypeFunctions(),
1910 								extensions);
1911 						try {
1912 							AwkSyntaxTree ast = ap.parse(scriptSources);
1913 							if (ast != null) {
1914 								ast.semanticAnalysis();
1915 								ast.semanticAnalysis();
1916 								AwkTuples new_tuples = new AwkTuples();
1917 								int result = ast.populateTuples(new_tuples);
1918 								assert result == 0;
1919 								new_tuples.postProcess();
1920 								ap.populateGlobalVariableNameToOffsetMappings(new_tuples);
1921 								AVM new_avm = new AVM(settings, extensions);
1922 								int subScriptExitCode = 0;
1923 								try {
1924 									new_avm.interpret(new_tuples);
1925 								} catch (ExitException ex) {
1926 									subScriptExitCode = ex.getCode();
1927 								}
1928 								push(subScriptExitCode);
1929 							} else {
1930 								push(-1);
1931 							}
1932 						} catch (IOException ioe) {
1933 							throw new AwkRuntimeException(position.lineNumber(), "IO Exception caught : " + ioe);
1934 						}
1935 
1936 						position.next();
1937 						break;
1938 					}
1939 					case AwkTuples._EXTENSION_: {
1940 						// arg[0] = extension keyword
1941 						// arg[1] = # of args on the stack
1942 						// arg[2] = true if parent is NOT an extension function call
1943 						// 		(i.e., initial extension in calling expression)
1944 						// stack[0] = first actual parameter
1945 						// stack[1] = second actual parameter
1946 						// etc.
1947 						String extension_keyword = position.arg(0).toString();
1948 						long num_args = position.intArg(1);
1949 						boolean is_initial = position.boolArg(2);
1950 
1951 						JawkExtension extension = extensions.get(extension_keyword);
1952 						if (extension == null) {
1953 							throw new AwkRuntimeException("Extension for '" + extension_keyword + "' not found.");
1954 						}
1955 
1956 						Object[] args = new Object[(int) num_args];
1957 						for (int i = (int)num_args - 1 ; i >=0 ; i--) {
1958 							args[i] = pop();
1959 						}
1960 
1961 						Object retval = extension.invoke(extension_keyword, args);
1962 
1963 						// block if necessary
1964 						// (convert retval into the return value
1965 						// from the block operation ...)
1966 						if (is_initial && retval != null && retval instanceof BlockObject) {
1967 							retval = new BlockManager().block((BlockObject) retval);
1968 						}
1969 						// (... and proceed)
1970 
1971 						if (retval == null) {
1972 							retval = "";
1973 						} else if (retval instanceof Integer) {
1974 						} else if (retval instanceof Long) {
1975 						} else if (retval instanceof Double) {
1976 						} else if (retval instanceof String) {
1977 						} else if (retval instanceof AssocArray) {
1978 						} else if (retval instanceof BlockObject) {
1979 							// pass a block object through...
1980 						} else {
1981 							// all other extension results are converted
1982 							// to a string (via Object.toString())
1983 							retval = retval.toString();
1984 						}
1985 						push(retval);
1986 
1987 						position.next();
1988 						break;
1989 					}
1990 					default:
1991 						throw new Error("invalid opcode: " + AwkTuples.toOpcodeString(position.opcode()));
1992 				}
1993 			}
1994 			
1995 			// End of the instructions
1996 			jrt.jrtCloseAll();
1997 			
1998 		} catch (RuntimeException re) {
1999 			LOG.error("", re);
2000 			LOG.error("operand_stack = {}", operand_stack);
2001 			LOG.error("position = {}", position);
2002 			LOG.error("line number = {}", position.lineNumber());
2003 
2004 			// clear runtime stack
2005 			runtime_stack.popAllFrames();
2006 			// clear operand stack
2007 			operand_stack.clear();
2008 
2009 			throw re;
2010 		} catch (AssertionError ae) {
2011 			LOG.error("", ae);
2012 			LOG.error("operand_stack = {}", operand_stack);
2013 			try {
2014 				LOG.error("position = {}", position);
2015 			} catch (Throwable t) {
2016 				LOG.error("{ could not report on position", t);
2017 			}
2018 			try {
2019 				LOG.error("line number = {}", position.lineNumber());
2020 			} catch (Throwable t) {
2021 				LOG.error("{ could not report on line number", t);
2022 			}
2023 			throw ae;
2024 		}
2025 		
2026 		// If <code>exit</code> was called, throw an ExitException
2027 		if (throw_exit_exception) {
2028 			throw new ExitException(exit_code, "The AWK script requested an exit");
2029 		}
2030 
2031 	}
2032 
2033 	/**
2034 	 * Close all streams in the runtime
2035 	 */
2036 	public void waitForIO() {
2037 		jrt.jrtCloseAll();
2038 	}
2039 
2040 	private void avmDump(AssocArray[] aa_array) {
2041 		if (aa_array == null) {
2042 			// dump the runtime stack
2043 			Object[] globals = runtime_stack.getNumGlobals();
2044 			for (String name : global_variable_offsets.keySet()) {
2045 				int idx = global_variable_offsets.get(name);
2046 				Object value = globals[idx];
2047 				if (value instanceof AssocArray) {
2048 					AssocArray aa = (AssocArray) value;
2049 					value = aa.mapString();
2050 				}
2051 				LOG.info("{} = {}", name, value);
2052 			}
2053 		} else {
2054 			// dump associative arrays
2055 			for (AssocArray aa : aa_array) {
2056 				LOG.info(aa.mapString());
2057 			}
2058 		}
2059 	}
2060 
2061 	private void printTo(PrintStream ps, long num_args) {
2062 		// print items from the top of the stack
2063 		// # of items
2064 		if (num_args == 0) {
2065 			// display $0
2066 			ps.print(jrt.jrtGetInputField(0));
2067 			ps.print(getORS().toString());
2068 		} else {
2069 			// cache $OFS to separate fields below
2070 			// (no need to execute getOFS for each field)
2071 			String ofs_string = getOFS().toString();
2072 			
2073 			// Arguments are stacked, so we need to reverse order
2074 			Object[] args = new Object[(int)num_args];
2075 			for (int i = (int)num_args - 1 ; i >=0 ; i--) {
2076 				args[i] = pop();
2077 			}
2078 			
2079 			// Now print
2080 			for (int i = 0 ; i < num_args ; i++) {
2081 				ps.print(JRT.toAwkStringForOutput(args[i], getOFMT().toString(), locale));
2082 				// if more elements, display $FS
2083 				if (i < num_args - 1) {
2084 					// use $OFS to separate fields
2085 					ps.print(ofs_string);
2086 				}
2087 			}
2088 			ps.print(getORS().toString());
2089 		}
2090 		// for now, since we are not using Process.waitFor()
2091 		if (IS_WINDOWS) {
2092 			ps.flush();
2093 		}
2094 	}
2095 
2096 	private void printfTo(PrintStream ps, long num_args) {
2097 //		assert num_args > 0;
2098 		ps.print(sprintfFunction(num_args));
2099 		// for now, since we are not using Process.waitFor()
2100 		if (IS_WINDOWS) {
2101 			ps.flush();
2102 		}
2103 	}
2104 
2105 	/**
2106 	 * sprintf() functionality
2107 	 */
2108 	private String sprintfFunction(long num_args) {
2109 
2110 		// Silly case
2111 		if (num_args == 0) return "";
2112 
2113 		// all but the format argument
2114 		Object[] arg_array = new Object[(int) (num_args - 1)];
2115 
2116 		// for each sprintf argument, put it into an
2117 		// array used in the String.format method
2118 		// Arguments are stacked, so we need to reverse their order
2119 		for (int i = (int)num_args - 2 ; i >= 0 ; i--) {
2120 			arg_array[i] = pop();
2121 		}
2122 
2123 		// the format argument!
2124 		String fmt = JRT.toAwkString(pop(), getCONVFMT().toString(), locale);
2125 
2126 		if (trap_illegal_format_exceptions) {
2127 			return Printf4J.sprintf(locale, fmt, arg_array);
2128 		} else {
2129 			return JRT.sprintfNoCatch(locale, fmt, arg_array);
2130 		}
2131 	}
2132 
2133 	private StringBuffer replace_first_sb = new StringBuffer();
2134 
2135 	/**
2136 	 * sub() functionality
2137 	 */
2138 	private String replaceFirst(String orig, String ere, String repl) {
2139 		replace_first_sb.setLength(0);
2140 		push(JRT.replaceFirst(orig, repl, ere, replace_first_sb, getCONVFMT().toString(), locale));
2141 		return replace_first_sb.toString();
2142 	}
2143 
2144 	private StringBuffer replace_all_sb = new StringBuffer();
2145 
2146 	/**
2147 	 * gsub() functionality
2148 	 */
2149 	private String replaceAll(String orig, String ere, String repl) {
2150 		replace_all_sb.setLength(0);
2151 		push(JRT.replaceAll(orig, repl, ere, replace_all_sb, getCONVFMT().toString(), locale));
2152 		return replace_all_sb.toString();
2153 	}
2154 
2155 	/**
2156 	 * Awk variable assignment functionality.
2157 	 */
2158 	private void assign(long l, Object value, boolean is_global, Position position) {
2159 		// check if curr value already refers to an array
2160 		if (runtime_stack.getVariable(l, is_global) instanceof AssocArray) {
2161 			throw new AwkRuntimeException(position.lineNumber(), "cannot assign anything to an unindexed associative array");
2162 		}
2163 		push(value);
2164 		runtime_stack.setVariable(l, value, is_global);
2165 	}
2166 
2167 	/**
2168 	 * Awk array element assignment functionality.
2169 	 */
2170 	private void assignArray(long offset, Object arr_idx, Object rhs, boolean is_global) {
2171 		Object o1 = runtime_stack.getVariable(offset, is_global);
2172 		if (o1 == null || o1.equals(BLANK)) {
2173 			runtime_stack.setVariable(offset, o1 = new AssocArray(sorted_array_keys), is_global);
2174 		}
2175 		assert o1 != null;
2176 		// The only (conceivable) way to contradict
2177 		// the assertion (below) is by passing in
2178 		// a scalar to an unindexed associative array
2179 		// via a -v argument without safeguards to
2180 		// prohibit this.
2181 		// Therefore, guard against this elsewhere, not here.
2182 		//if (! (o1 instanceof AssocArray))
2183 		//	throw new AwkRuntimeException("Attempting to treat a scalar as an array.");
2184 		assert o1 instanceof AssocArray;
2185 		AssocArray array = (AssocArray) o1;
2186 
2187 		// Convert arr_idx to a true integer if it is one
2188 //		String indexString = JRT.toAwkStringForOutput(arr_idx, getCONVFMT().toString());
2189 		array.put(arr_idx, rhs);
2190 		push(rhs);
2191 	}
2192 
2193 	/**
2194 	 * Numerically increases an Awk variable by one; the result
2195 	 * is placed back into that variable.
2196 	 */
2197 	private Object inc(long l, boolean is_global) {
2198 		Object o = runtime_stack.getVariable(l, is_global);
2199 		if (o == null || o instanceof UninitializedObject) {
2200 			runtime_stack.setVariable(l, o = ZERO, is_global);
2201 		}
2202 		runtime_stack.setVariable(l, JRT.inc(o), is_global);
2203 		return o;
2204 	}
2205 
2206 	/**
2207 	 * Numerically decreases an Awk variable by one; the result
2208 	 * is placed back into that variable.
2209 	 */
2210 	private Object dec(long l, boolean is_global) {
2211 		Object o = runtime_stack.getVariable(l, is_global);
2212 		if (o == null) {
2213 			runtime_stack.setVariable(l, o = ZERO, is_global);
2214 		}
2215 		runtime_stack.setVariable(l, JRT.dec(o), is_global);
2216 		return o;
2217 	}
2218 
2219 	/** {@inheritDoc} */
2220 	@Override
2221 	public final Object getRS() {
2222 		assert rs_offset != NULL_OFFSET;
2223 		Object rs_obj = runtime_stack.getVariable(rs_offset, true);	// true = global
2224 		return rs_obj;
2225 	}
2226 
2227 	/** {@inheritDoc} */
2228 	@Override
2229 	public final Object getOFS() {
2230 		assert ofs_offset != NULL_OFFSET;
2231 		Object ofs_obj = runtime_stack.getVariable(ofs_offset, true);	// true = global
2232 		return ofs_obj;
2233 	}
2234 
2235 	public final Object getORS() {
2236 		return runtime_stack.getVariable(ors_offset, true);	// true = global
2237 	}
2238 
2239 	/** {@inheritDoc} */
2240 	@Override
2241 	public final Object getSUBSEP() {
2242 		assert subsep_offset != NULL_OFFSET;
2243 		Object subsep_obj = runtime_stack.getVariable(subsep_offset, true);	// true = global
2244 		return subsep_obj;
2245 	}
2246 
2247 	/**
2248 	 * Performs the global variable assignment within the runtime environment.
2249 	 * These assignments come from the ARGV list (bounded by ARGC), which, in
2250 	 * turn, come from the command-line arguments passed into Awk.
2251 	 *
2252 	 * @param name_value The variable assignment in <i>name=value</i> form.
2253 	 */
2254 	@SuppressWarnings("unused")
2255 	private void setFilelistVariable(String name_value) {
2256 		int eq_idx = name_value.indexOf('=');
2257 		// variable name should be non-blank
2258 		assert eq_idx >= 0;
2259 		if (eq_idx == 0) {
2260 			throw new IllegalArgumentException("Must have a non-blank variable name in a name=value variable assignment argument.");
2261 		}
2262 		String name = name_value.substring(0, eq_idx);
2263 		String value = name_value.substring(eq_idx + 1);
2264 		Object obj;
2265 		try {
2266 			obj = Integer.parseInt(value);
2267 		} catch (NumberFormatException nfe) {
2268 			try {
2269 				obj = Double.parseDouble(value);
2270 			} catch (NumberFormatException nfe2) {
2271 				obj = value;
2272 			}
2273 		}
2274 
2275 		// make sure we're not receiving funcname=value assignments
2276 		if (function_names.contains(name)) {
2277 			throw new IllegalArgumentException("Cannot assign a scalar to a function name (" + name + ").");
2278 		}
2279 
2280 		Integer offset_obj = global_variable_offsets.get(name);
2281 		Boolean array_obj = global_variable_arrays.get(name);
2282 
2283 		if (offset_obj != null) {
2284 			assert array_obj != null;
2285 			if (array_obj.booleanValue()) {
2286 				throw new IllegalArgumentException("Cannot assign a scalar to a non-scalar variable (" + name + ").");
2287 			} else {
2288 				runtime_stack.setFilelistVariable(offset_obj.intValue(), obj);
2289 			}
2290 		}
2291 		// otherwise, do nothing
2292 	}
2293 
2294 	/** {@inheritDoc} */
2295 	@Override
2296 	public final void assignVariable(String name, Object obj) {
2297 		// make sure we're not receiving funcname=value assignments
2298 		if (function_names.contains(name)) {
2299 			throw new IllegalArgumentException("Cannot assign a scalar to a function name (" + name + ").");
2300 		}
2301 
2302 		Integer offset_obj = global_variable_offsets.get(name);
2303 		Boolean array_obj = global_variable_arrays.get(name);
2304 
2305 		if (offset_obj != null) {
2306 			assert array_obj != null;
2307 			if (array_obj.booleanValue()) {
2308 				throw new IllegalArgumentException("Cannot assign a scalar to a non-scalar variable (" + name + ").");
2309 			} else {
2310 				runtime_stack.setFilelistVariable(offset_obj.intValue(), obj);
2311 			}
2312 		}
2313 
2314 	}
2315 
2316 	private void swapOnStack() {
2317 		Object o1 = pop();
2318 		Object o2 = pop();
2319 		push(o1);
2320 		push(o2);
2321 	}
2322 
2323 	private void avmConsumeInputForGetline() throws IOException {
2324 		if (avmConsumeInput(true)) {
2325 			push(1);
2326 		} else {
2327 			push("");
2328 			push(0);
2329 		}
2330 		swapOnStack();
2331 	}
2332 
2333 	private void avmConsumeFileInputForGetline(String filename) throws IOException {
2334 		if (avmConsumeFileInput(filename)) {
2335 			push(1);
2336 		} else {
2337 			push(0);
2338 		}
2339 		swapOnStack();
2340 	}
2341 
2342 	private void avmConsumeCommandInputForGetline(String cmd) throws IOException {
2343 		if (avmConsumeCommandInput(cmd)) {
2344 			push(1);
2345 		} else {
2346 			push(0);
2347 		}
2348 		swapOnStack();
2349 	}
2350 
2351 	private boolean avmConsumeFileInput(String filename)
2352 			throws IOException
2353 	{
2354 		boolean retval = jrt.jrtConsumeFileInput(filename);
2355 		if (retval) {
2356 			push(jrt.getInputLine());
2357 		} else {
2358 			push("");
2359 		}
2360 		return retval;
2361 	}
2362 
2363 	private boolean avmConsumeCommandInput(String cmd)
2364 			throws IOException
2365 	{
2366 		boolean retval = jrt.jrtConsumeCommandInput(cmd);
2367 		if (retval) {
2368 			push(jrt.getInputLine());
2369 		} else {
2370 			push("");
2371 		}
2372 		return retval;
2373 	}
2374 
2375 	private boolean avmConsumeInput(boolean for_getline)
2376 			throws IOException
2377 	{
2378 		boolean retval = jrt.jrtConsumeInput(settings.getInput(), for_getline, locale);
2379 		if (retval && for_getline) {
2380 			push(jrt.getInputLine());
2381 		}
2382 		return retval;
2383 	}
2384 
2385 	/** {@inheritDoc} */
2386 	@Override
2387 	public Object getFS() {
2388 		assert fs_offset != NULL_OFFSET;
2389 		Object fs_string = runtime_stack.getVariable(fs_offset, true);	// true = global
2390 		return fs_string;
2391 	}
2392 
2393 	/** {@inheritDoc} */
2394 	@Override
2395 	public Object getCONVFMT() {
2396 		assert convfmt_offset != NULL_OFFSET : "convfmt_offset not defined";
2397 		Object convfmt_string = runtime_stack.getVariable(convfmt_offset, true);	// true = global
2398 		return convfmt_string;
2399 	}
2400 
2401 	/** {@inheritDoc} */
2402 	@Override
2403 	public void resetFNR() {
2404 		runtime_stack.setVariable(fnr_offset, ZERO, true);
2405 	}
2406 
2407 	/** {@inheritDoc} */
2408 	@Override
2409 	public void incFNR() {
2410 		inc(fnr_offset, true);
2411 	}
2412 
2413 	/** {@inheritDoc} */
2414 	@Override
2415 	public void incNR() {
2416 		inc(nr_offset, true);
2417 	}
2418 
2419 	/** {@inheritDoc} */
2420 	@Override
2421 	public void setNF(Integer I) {
2422 		runtime_stack.setVariable(nf_offset, I, true);
2423 	}
2424 
2425 	/** {@inheritDoc} */
2426 	@Override
2427 	public void setFILENAME(String filename) {
2428 		runtime_stack.setVariable(filename_offset, filename, true);
2429 	}
2430 
2431 	/** {@inheritDoc} */
2432 	@Override
2433 	public Object getARGV() {
2434 		return runtime_stack.getVariable(argv_offset, true);
2435 	}
2436 
2437 	/** {@inheritDoc} */
2438 	@Override
2439 	public Object getARGC() {
2440 		return runtime_stack.getVariable(argc_offset, true);
2441 	}
2442 
2443 	private String getOFMT() {
2444 		assert ofmt_offset != NULL_OFFSET;
2445 		String ofmt_string = runtime_stack.getVariable(ofmt_offset, true).toString();	// true = global
2446 		return ofmt_string;
2447 	}
2448 
2449 	private static final UninitializedObject BLANK = new UninitializedObject();
2450 
2451 	/**
2452 	 * The value of an address which is not yet assigned a tuple index.
2453 	 */
2454 	public static final int NULL_OFFSET = -1;
2455 
2456 	private static class RuntimeStack {
2457 
2458 		private Object[] globals = null;
2459 		private Object[] locals = null;
2460 		private MyStack<Object[]> locals_stack = new ArrayStackImpl<Object[]>();
2461 		private MyStack<Integer> return_indexes = new LinkedListStackImpl<Integer>();
2462 
2463 		@SuppressWarnings("unused")
2464 		public void dump() {
2465 			LOG.info("globals = " + Arrays.toString(globals));
2466 			LOG.info("locals = " + Arrays.toString(locals));
2467 			LOG.info("locals_stack = " + locals_stack);
2468 			LOG.info("return_indexes = " + return_indexes);
2469 		}
2470 
2471 		Object[] getNumGlobals() {
2472 			return globals;
2473 		}
2474 
2475 		/**
2476 		 * Must be one of the first methods executed.
2477 		 */
2478 		void setNumGlobals(long l) {
2479 			assert l >= 0;
2480 			assert globals == null;
2481 			globals = new Object[(int) l];
2482 			for (int i = 0 ; i < l ; i++) {
2483 				globals[i] = null;
2484 			}
2485 			// must accept multiple executions
2486 			//expandFrameIfNecessary(num_globals);
2487 		}
2488 
2489 		/*
2490 		// this assumes globals = Object[0] upon initialization
2491 		private void expandFrameIfNecessary(int num_globals) {
2492 			if (num_globals == globals.length)
2493 				// no need for expansion;
2494 				// do nothing
2495 				return;
2496 			Object[] new_frame = new Object[num_globals];
2497 			for (int i=0;i<globals.length;++i)
2498 				new_frame[i] = globals[i];
2499 			globals = new_frame;
2500 		}
2501 		 */
2502 
2503 		Object getVariable(long offset, boolean is_global) {
2504 			assert globals != null;
2505 			assert offset != NULL_OFFSET;
2506 			if (is_global) {
2507 				return globals[(int) offset];
2508 			} else {
2509 				return locals[(int) offset];
2510 			}
2511 		}
2512 
2513 		Object setVariable(long offset, Object val, boolean is_global) {
2514 			assert globals != null;
2515 			assert offset != NULL_OFFSET;
2516 			if (is_global) {
2517 				return globals[(int) offset] = val;
2518 			} else {
2519 				return locals[(int) offset] = val;
2520 			}
2521 		}
2522 
2523 		// for _DELETE_ARRAY_
2524 		void removeVariable(long offset, boolean is_global) {
2525 			assert globals != null;
2526 			assert offset != NULL_OFFSET;
2527 			if (is_global) {
2528 				assert globals[(int) offset] == null || globals[(int) offset] instanceof AssocArray;
2529 				globals[(int) offset] = null;
2530 			} else {
2531 				assert locals[(int) offset] == null || locals[(int) offset] instanceof AssocArray;
2532 				locals[(int) offset] = null;
2533 			}
2534 		}
2535 
2536 		void setFilelistVariable(int offset, Object value) {
2537 			assert globals != null;
2538 			assert offset != NULL_OFFSET;
2539 			globals[offset] = value;
2540 		}
2541 
2542 		void pushFrame(long num_formal_params, int position_idx) {
2543 			locals_stack.push(locals);
2544 			locals = new Object[(int) num_formal_params];
2545 			return_indexes.push(position_idx);
2546 		}
2547 
2548 		/** returns the position index */
2549 		int popFrame() {
2550 			locals = locals_stack.pop();
2551 			return return_indexes.pop();
2552 		}
2553 
2554 		void popAllFrames() {
2555 			int sz = locals_stack.size();
2556 			while (--sz >= 0) {
2557 				locals = locals_stack.pop();
2558 				return_indexes.pop();
2559 			}
2560 		}
2561 		private Object return_value;
2562 
2563 		void setReturnValue(Object obj) {
2564 			assert return_value == null;
2565 			return_value = obj;
2566 		}
2567 
2568 		Object getReturnValue() {
2569 			Object retval;
2570 			if (return_value == null) {
2571 				retval = BLANK;
2572 			} else {
2573 				retval = return_value;
2574 			}
2575 			return_value = null;
2576 			return retval;
2577 		}
2578 	}
2579 }