View Javadoc
1   package io.jawk.intermediate;
2   
3   /*-
4    * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
5    * Jawk
6    * ჻჻჻჻჻჻
7    * Copyright (C) 2006 - 2026 MetricsHub
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.PrintStream;
26  import java.io.Serializable;
27  import java.util.ArrayDeque;
28  import java.util.ArrayList;
29  import java.util.Arrays;
30  import java.util.Collections;
31  import java.util.Deque;
32  import java.util.HashMap;
33  import java.util.HashSet;
34  import java.util.IdentityHashMap;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.Set;
38  import java.util.function.Supplier;
39  import java.util.regex.Pattern;
40  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
41  import io.jawk.ext.ExtensionFunction;
42  import io.jawk.jrt.JRT;
43  
44  /**
45   * <p>
46   * AwkTuples class.
47   * </p>
48   *
49   * @author Danny Daglas
50   */
51  public class AwkTuples implements Serializable {
52  
53  	private static final long serialVersionUID = 2L;
54  
55  	/** Address manager */
56  	private final AddressManager addressManager = new AddressManager();
57  
58  	// made public to access static members of AwkTuples via Java Reflection
59  
60  	// made public to be accessable via Java Reflection
61  	// (see toOpcodeString() method below)
62  
63  	/**
64  	 * Override add() to populate the line number for each tuple,
65  	 * rather than polluting all the constructors with this assignment.
66  	 */
67  	/**
68  	 * The tuple queue intentionally uses an {@link ArrayList}. The address mapping
69  	 * logic stores tuple indexes (rather than node references) so that jump targets
70  	 * can be serialized and patched efficiently. A linked list would make every
71  	 * lookup O(n) and complicate address reassignment.
72  	 */
73  	private List<Tuple> queue = new ArrayList<Tuple>(100) {
74  		private static final long serialVersionUID = -6334362156408598578L;
75  
76  		@Override
77  		public boolean add(Tuple t) {
78  			t.setLineNumber(linenoStack.peek());
79  			return super.add(t);
80  		}
81  	};
82  
83  	/** Whether tuple post-processing has already been applied. */
84  	private boolean postProcessed;
85  
86  	/** Whether optimization passes have already been applied. */
87  	private boolean optimized;
88  
89  	/** Whether this tuple stream was produced by {@code compileExpression()}. */
90  	private boolean evalTupleStream;
91  
92  	/**
93  	 * <p>
94  	 * toOpcodeString.
95  	 * </p>
96  	 *
97  	 * @param opcode a int
98  	 * @return a {@link java.lang.String} object
99  	 */
100 	public static String toOpcodeString(int opcode) {
101 		return Opcode.fromId(opcode).name();
102 	}
103 
104 	/**
105 	 * <p>
106 	 * pop.
107 	 * </p>
108 	 */
109 	public void pop() {
110 		queue.add(new Tuple.NoOperandTuple(Opcode.POP));
111 	}
112 
113 	/**
114 	 * <p>
115 	 * push.
116 	 * </p>
117 	 *
118 	 * @param o a {@link java.lang.Object} object
119 	 */
120 	public void push(Object o) {
121 		if (o instanceof String) {
122 			queue.add(new Tuple.PushStringTuple(o.toString()));
123 		} else if (o instanceof Integer) {
124 			queue.add(new Tuple.PushLongTuple((long) (Integer) o));
125 		} else if (o instanceof Long) {
126 			queue.add(new Tuple.PushLongTuple((long) (Long) o));
127 		} else if (o instanceof Double) {
128 			queue.add(new Tuple.PushDoubleTuple((Double) o));
129 		}
130 	}
131 
132 	/**
133 	 * <p>
134 	 * ifFalse.
135 	 * </p>
136 	 *
137 	 * @param address a {@link io.jawk.intermediate.Address} object
138 	 */
139 	public void ifFalse(Address address) {
140 		queue.add(new Tuple.AddressTuple(Opcode.IFFALSE, address));
141 	}
142 
143 	/**
144 	 * <p>
145 	 * toNumber.
146 	 * </p>
147 	 */
148 	public void toNumber() {
149 		queue.add(new Tuple.NoOperandTuple(Opcode.TO_NUMBER));
150 	}
151 
152 	/**
153 	 * <p>
154 	 * ifTrue.
155 	 * </p>
156 	 *
157 	 * @param address a {@link io.jawk.intermediate.Address} object
158 	 */
159 	public void ifTrue(Address address) {
160 		queue.add(new Tuple.AddressTuple(Opcode.IFTRUE, address));
161 	}
162 
163 	/**
164 	 * <p>
165 	 * gotoAddress.
166 	 * </p>
167 	 *
168 	 * @param address a {@link io.jawk.intermediate.Address} object
169 	 */
170 	public void gotoAddress(Address address) {
171 		queue.add(new Tuple.AddressTuple(Opcode.GOTO, address));
172 	}
173 
174 	/**
175 	 * <p>
176 	 * createAddress.
177 	 * </p>
178 	 *
179 	 * @param label a {@link java.lang.String} object
180 	 * @return a {@link io.jawk.intermediate.Address} object
181 	 */
182 	public Address createAddress(String label) {
183 		return addressManager.createAddress(label);
184 	}
185 
186 	/**
187 	 * <p>
188 	 * address.
189 	 * </p>
190 	 *
191 	 * @param address a {@link io.jawk.intermediate.Address} object
192 	 * @return a {@link io.jawk.intermediate.AwkTuples} object
193 	 */
194 	public AwkTuples address(Address address) {
195 		addressManager.resolveAddress(address, queue.size());
196 		return this;
197 	}
198 
199 	/**
200 	 * <p>
201 	 * nop.
202 	 * </p>
203 	 */
204 	public void nop() {
205 		queue.add(new Tuple.NoOperandTuple(Opcode.NOP));
206 	}
207 
208 	/**
209 	 * <p>
210 	 * print.
211 	 * </p>
212 	 *
213 	 * @param numExprs a int
214 	 */
215 	public void print(int numExprs) {
216 		queue.add(new Tuple.CountTuple(Opcode.PRINT, numExprs));
217 	}
218 
219 	/**
220 	 * <p>
221 	 * printToFile.
222 	 * </p>
223 	 *
224 	 * @param numExprs a int
225 	 * @param append a boolean
226 	 */
227 	public void printToFile(int numExprs, boolean append) {
228 		queue.add(new Tuple.CountAndAppendTuple(Opcode.PRINT_TO_FILE, numExprs, append));
229 	}
230 
231 	/**
232 	 * <p>
233 	 * printToPipe.
234 	 * </p>
235 	 *
236 	 * @param numExprs a int
237 	 */
238 	public void printToPipe(int numExprs) {
239 		queue.add(new Tuple.CountTuple(Opcode.PRINT_TO_PIPE, numExprs));
240 	}
241 
242 	/**
243 	 * <p>
244 	 * printf.
245 	 * </p>
246 	 *
247 	 * @param numExprs a int
248 	 */
249 	public void printf(int numExprs) {
250 		queue.add(new Tuple.CountTuple(Opcode.PRINTF, numExprs));
251 	}
252 
253 	/**
254 	 * <p>
255 	 * printfToFile.
256 	 * </p>
257 	 *
258 	 * @param numExprs a int
259 	 * @param append a boolean
260 	 */
261 	public void printfToFile(int numExprs, boolean append) {
262 		queue.add(new Tuple.CountAndAppendTuple(Opcode.PRINTF_TO_FILE, numExprs, append));
263 	}
264 
265 	/**
266 	 * <p>
267 	 * printfToPipe.
268 	 * </p>
269 	 *
270 	 * @param numExprs a int
271 	 */
272 	public void printfToPipe(int numExprs) {
273 		queue.add(new Tuple.CountTuple(Opcode.PRINTF_TO_PIPE, numExprs));
274 	}
275 
276 	/**
277 	 * <p>
278 	 * sprintf.
279 	 * </p>
280 	 *
281 	 * @param numExprs a int
282 	 */
283 	public void sprintf(int numExprs) {
284 		queue.add(new Tuple.CountTuple(Opcode.SPRINTF, numExprs));
285 	}
286 
287 	/**
288 	 * <p>
289 	 * length.
290 	 * </p>
291 	 *
292 	 * @param numExprs a int
293 	 */
294 	public void length(int numExprs) {
295 		queue.add(new Tuple.CountTuple(Opcode.LENGTH, numExprs));
296 	}
297 
298 	/**
299 	 * <p>
300 	 * concat.
301 	 * </p>
302 	 */
303 	public void concat() {
304 		queue.add(new Tuple.NoOperandTuple(Opcode.CONCAT));
305 	}
306 
307 	/**
308 	 * <p>
309 	 * assign.
310 	 * </p>
311 	 *
312 	 * @param offset a int
313 	 * @param isGlobal a boolean
314 	 */
315 	public void assign(int offset, boolean isGlobal) {
316 		queue.add(new Tuple.VariableTuple(Opcode.ASSIGN, offset, isGlobal));
317 	}
318 
319 	/**
320 	 * <p>
321 	 * assignArray.
322 	 * </p>
323 	 *
324 	 * @param offset a int
325 	 * @param isGlobal a boolean
326 	 */
327 	public void assignArray(int offset, boolean isGlobal) {
328 		queue.add(new Tuple.VariableTuple(Opcode.ASSIGN_ARRAY, offset, isGlobal));
329 	}
330 
331 	/**
332 	 * Assigns a value to a stack-provided associative-array element.
333 	 */
334 	public void assignMapElement() {
335 		queue.add(new Tuple.NoOperandTuple(Opcode.ASSIGN_MAP_ELEMENT));
336 	}
337 
338 	/**
339 	 * <p>
340 	 * assignAsInput.
341 	 * </p>
342 	 */
343 	public void assignAsInput() {
344 		queue.add(new Tuple.NoOperandTuple(Opcode.ASSIGN_AS_INPUT));
345 	}
346 
347 	/**
348 	 * Marks this tuple stream as an expression-eval program rather than a full
349 	 * AWK script. Eval tuple streams can use small tuple-level optimizations that
350 	 * are unsafe for the general case.
351 	 */
352 	public void markEvalTupleStream() {
353 		evalTupleStream = true;
354 	}
355 
356 	/**
357 	 * <p>
358 	 * assignAsInputField.
359 	 * </p>
360 	 */
361 	public void assignAsInputField() {
362 		queue.add(new Tuple.NoOperandTuple(Opcode.ASSIGN_AS_INPUT_FIELD));
363 	}
364 
365 	/**
366 	 * <p>
367 	 * dereference.
368 	 * </p>
369 	 *
370 	 * @param offset a int
371 	 * @param isArray a boolean
372 	 * @param isGlobal a boolean
373 	 */
374 	public void dereference(int offset, boolean isArray, boolean isGlobal) {
375 		queue.add(new Tuple.DereferenceTuple(offset, isArray, isGlobal));
376 	}
377 
378 	/**
379 	 * <p>
380 	 * plusEq.
381 	 * </p>
382 	 *
383 	 * @param offset a int
384 	 * @param isGlobal a boolean
385 	 */
386 	public void plusEq(int offset, boolean isGlobal) {
387 		queue.add(new Tuple.CompoundAssignTuple(Opcode.PLUS_EQ, offset, isGlobal));
388 	}
389 
390 	/**
391 	 * <p>
392 	 * minusEq.
393 	 * </p>
394 	 *
395 	 * @param offset a int
396 	 * @param isGlobal a boolean
397 	 */
398 	public void minusEq(int offset, boolean isGlobal) {
399 		queue.add(new Tuple.CompoundAssignTuple(Opcode.MINUS_EQ, offset, isGlobal));
400 	}
401 
402 	/**
403 	 * <p>
404 	 * multEq.
405 	 * </p>
406 	 *
407 	 * @param offset a int
408 	 * @param isGlobal a boolean
409 	 */
410 	public void multEq(int offset, boolean isGlobal) {
411 		queue.add(new Tuple.CompoundAssignTuple(Opcode.MULT_EQ, offset, isGlobal));
412 	}
413 
414 	/**
415 	 * <p>
416 	 * divEq.
417 	 * </p>
418 	 *
419 	 * @param offset a int
420 	 * @param isGlobal a boolean
421 	 */
422 	public void divEq(int offset, boolean isGlobal) {
423 		queue.add(new Tuple.CompoundAssignTuple(Opcode.DIV_EQ, offset, isGlobal));
424 	}
425 
426 	/**
427 	 * <p>
428 	 * modEq.
429 	 * </p>
430 	 *
431 	 * @param offset a int
432 	 * @param isGlobal a boolean
433 	 */
434 	public void modEq(int offset, boolean isGlobal) {
435 		queue.add(new Tuple.CompoundAssignTuple(Opcode.MOD_EQ, offset, isGlobal));
436 	}
437 
438 	/**
439 	 * <p>
440 	 * powEq.
441 	 * </p>
442 	 *
443 	 * @param offset a int
444 	 * @param isGlobal a boolean
445 	 */
446 	public void powEq(int offset, boolean isGlobal) {
447 		queue.add(new Tuple.CompoundAssignTuple(Opcode.POW_EQ, offset, isGlobal));
448 	}
449 
450 	/**
451 	 * <p>
452 	 * plusEqArray.
453 	 * </p>
454 	 *
455 	 * @param offset a int
456 	 * @param isGlobal a boolean
457 	 */
458 	public void plusEqArray(int offset, boolean isGlobal) {
459 		queue.add(new Tuple.CompoundAssignArrayTuple(Opcode.PLUS_EQ_ARRAY, offset, isGlobal));
460 	}
461 
462 	/**
463 	 * Applies {@code +=} to a stack-provided associative-array element.
464 	 */
465 	public void plusEqMapElement() {
466 		queue.add(new Tuple.CompoundAssignMapElementTuple(Opcode.PLUS_EQ_MAP_ELEMENT));
467 	}
468 
469 	/**
470 	 * <p>
471 	 * minusEqArray.
472 	 * </p>
473 	 *
474 	 * @param offset a int
475 	 * @param isGlobal a boolean
476 	 */
477 	public void minusEqArray(int offset, boolean isGlobal) {
478 		queue.add(new Tuple.CompoundAssignArrayTuple(Opcode.MINUS_EQ_ARRAY, offset, isGlobal));
479 	}
480 
481 	/**
482 	 * Applies {@code -=} to a stack-provided associative-array element.
483 	 */
484 	public void minusEqMapElement() {
485 		queue.add(new Tuple.CompoundAssignMapElementTuple(Opcode.MINUS_EQ_MAP_ELEMENT));
486 	}
487 
488 	/**
489 	 * <p>
490 	 * multEqArray.
491 	 * </p>
492 	 *
493 	 * @param offset a int
494 	 * @param isGlobal a boolean
495 	 */
496 	public void multEqArray(int offset, boolean isGlobal) {
497 		queue.add(new Tuple.CompoundAssignArrayTuple(Opcode.MULT_EQ_ARRAY, offset, isGlobal));
498 	}
499 
500 	/**
501 	 * Applies {@code *=} to a stack-provided associative-array element.
502 	 */
503 	public void multEqMapElement() {
504 		queue.add(new Tuple.CompoundAssignMapElementTuple(Opcode.MULT_EQ_MAP_ELEMENT));
505 	}
506 
507 	/**
508 	 * <p>
509 	 * divEqArray.
510 	 * </p>
511 	 *
512 	 * @param offset a int
513 	 * @param isGlobal a boolean
514 	 */
515 	public void divEqArray(int offset, boolean isGlobal) {
516 		queue.add(new Tuple.CompoundAssignArrayTuple(Opcode.DIV_EQ_ARRAY, offset, isGlobal));
517 	}
518 
519 	/**
520 	 * Applies {@code /=} to a stack-provided associative-array element.
521 	 */
522 	public void divEqMapElement() {
523 		queue.add(new Tuple.CompoundAssignMapElementTuple(Opcode.DIV_EQ_MAP_ELEMENT));
524 	}
525 
526 	/**
527 	 * <p>
528 	 * modEqArray.
529 	 * </p>
530 	 *
531 	 * @param offset a int
532 	 * @param isGlobal a boolean
533 	 */
534 	public void modEqArray(int offset, boolean isGlobal) {
535 		queue.add(new Tuple.CompoundAssignArrayTuple(Opcode.MOD_EQ_ARRAY, offset, isGlobal));
536 	}
537 
538 	/**
539 	 * Applies {@code %=} to a stack-provided associative-array element.
540 	 */
541 	public void modEqMapElement() {
542 		queue.add(new Tuple.CompoundAssignMapElementTuple(Opcode.MOD_EQ_MAP_ELEMENT));
543 	}
544 
545 	/**
546 	 * <p>
547 	 * powEqArray.
548 	 * </p>
549 	 *
550 	 * @param offset a int
551 	 * @param isGlobal a boolean
552 	 */
553 	public void powEqArray(int offset, boolean isGlobal) {
554 		queue.add(new Tuple.CompoundAssignArrayTuple(Opcode.POW_EQ_ARRAY, offset, isGlobal));
555 	}
556 
557 	/**
558 	 * Applies exponentiation assignment to a stack-provided associative-array
559 	 * element.
560 	 */
561 	public void powEqMapElement() {
562 		queue.add(new Tuple.CompoundAssignMapElementTuple(Opcode.POW_EQ_MAP_ELEMENT));
563 	}
564 
565 	/**
566 	 * <p>
567 	 * plusEqInputField.
568 	 * </p>
569 	 */
570 	public void plusEqInputField() {
571 		queue.add(new Tuple.CompoundAssignInputFieldTuple(Opcode.PLUS_EQ_INPUT_FIELD));
572 	}
573 
574 	/**
575 	 * <p>
576 	 * minusEqInputField.
577 	 * </p>
578 	 */
579 	public void minusEqInputField() {
580 		queue.add(new Tuple.CompoundAssignInputFieldTuple(Opcode.MINUS_EQ_INPUT_FIELD));
581 	}
582 
583 	/**
584 	 * <p>
585 	 * multEqInputField.
586 	 * </p>
587 	 */
588 	public void multEqInputField() {
589 		queue.add(new Tuple.CompoundAssignInputFieldTuple(Opcode.MULT_EQ_INPUT_FIELD));
590 	}
591 
592 	/**
593 	 * <p>
594 	 * divEqInputField.
595 	 * </p>
596 	 */
597 	public void divEqInputField() {
598 		queue.add(new Tuple.CompoundAssignInputFieldTuple(Opcode.DIV_EQ_INPUT_FIELD));
599 	}
600 
601 	/**
602 	 * <p>
603 	 * modEqInputField.
604 	 * </p>
605 	 */
606 	public void modEqInputField() {
607 		queue.add(new Tuple.CompoundAssignInputFieldTuple(Opcode.MOD_EQ_INPUT_FIELD));
608 	}
609 
610 	/**
611 	 * <p>
612 	 * powEqInputField.
613 	 * </p>
614 	 */
615 	public void powEqInputField() {
616 		queue.add(new Tuple.CompoundAssignInputFieldTuple(Opcode.POW_EQ_INPUT_FIELD));
617 	}
618 
619 	/**
620 	 * <p>
621 	 * srand.
622 	 * </p>
623 	 *
624 	 * @param num a int
625 	 */
626 	public void srand(int num) {
627 		queue.add(new Tuple.CountTuple(Opcode.SRAND, num));
628 	}
629 
630 	/**
631 	 * <p>
632 	 * rand.
633 	 * </p>
634 	 */
635 	public void rand() {
636 		queue.add(new Tuple.NoOperandTuple(Opcode.RAND));
637 	}
638 
639 	/**
640 	 * <p>
641 	 * intFunc.
642 	 * </p>
643 	 */
644 	public void intFunc() {
645 		queue.add(new Tuple.NoOperandTuple(Opcode.INTFUNC));
646 	}
647 
648 	/**
649 	 * <p>
650 	 * sqrt.
651 	 * </p>
652 	 */
653 	public void sqrt() {
654 		queue.add(new Tuple.NoOperandTuple(Opcode.SQRT));
655 	}
656 
657 	/**
658 	 * <p>
659 	 * log.
660 	 * </p>
661 	 */
662 	public void log() {
663 		queue.add(new Tuple.NoOperandTuple(Opcode.LOG));
664 	}
665 
666 	/**
667 	 * <p>
668 	 * exp.
669 	 * </p>
670 	 */
671 	public void exp() {
672 		queue.add(new Tuple.NoOperandTuple(Opcode.EXP));
673 	}
674 
675 	/**
676 	 * <p>
677 	 * sin.
678 	 * </p>
679 	 */
680 	public void sin() {
681 		queue.add(new Tuple.NoOperandTuple(Opcode.SIN));
682 	}
683 
684 	/**
685 	 * <p>
686 	 * cos.
687 	 * </p>
688 	 */
689 	public void cos() {
690 		queue.add(new Tuple.NoOperandTuple(Opcode.COS));
691 	}
692 
693 	/**
694 	 * <p>
695 	 * atan2.
696 	 * </p>
697 	 */
698 	public void atan2() {
699 		queue.add(new Tuple.NoOperandTuple(Opcode.ATAN2));
700 	}
701 
702 	/**
703 	 * <p>
704 	 * match.
705 	 * </p>
706 	 */
707 	public void match() {
708 		queue.add(new Tuple.NoOperandTuple(Opcode.MATCH));
709 	}
710 
711 	/**
712 	 * <p>
713 	 * index.
714 	 * </p>
715 	 */
716 	public void index() {
717 		queue.add(new Tuple.NoOperandTuple(Opcode.INDEX));
718 	}
719 
720 	/**
721 	 * <p>
722 	 * subForDollar0.
723 	 * </p>
724 	 *
725 	 * @param isGsub a boolean
726 	 */
727 	public void subForDollar0(boolean isGsub) {
728 		queue.add(new Tuple.BooleanTuple(Opcode.SUB_FOR_DOLLAR_0, isGsub));
729 	}
730 
731 	/**
732 	 * <p>
733 	 * subForDollarReference.
734 	 * </p>
735 	 *
736 	 * @param isGsub a boolean
737 	 */
738 	public void subForDollarReference(boolean isGsub) {
739 		queue.add(new Tuple.BooleanTuple(Opcode.SUB_FOR_DOLLAR_REFERENCE, isGsub));
740 	}
741 
742 	/**
743 	 * <p>
744 	 * subForVariable.
745 	 * </p>
746 	 *
747 	 * @param offset a int
748 	 * @param isGlobal a boolean
749 	 * @param isGsub a boolean
750 	 */
751 	public void subForVariable(int offset, boolean isGlobal, boolean isGsub) {
752 		queue.add(new Tuple.SubstitutionVariableTuple(Opcode.SUB_FOR_VARIABLE, offset, isGlobal, isGsub));
753 	}
754 
755 	/**
756 	 * <p>
757 	 * subForArrayReference.
758 	 * </p>
759 	 *
760 	 * @param offset a int
761 	 * @param isGlobal a boolean
762 	 * @param isGsub a boolean
763 	 */
764 	public void subForArrayReference(int offset, boolean isGlobal, boolean isGsub) {
765 		queue.add(new Tuple.SubstitutionVariableTuple(Opcode.SUB_FOR_ARRAY_REFERENCE, offset, isGlobal, isGsub));
766 	}
767 
768 	/**
769 	 * Applies {@code sub}/{@code gsub} to a stack-provided associative-array
770 	 * element.
771 	 *
772 	 * @param isGsub {@code true} for {@code gsub}, {@code false} for {@code sub}
773 	 */
774 	public void subForMapReference(boolean isGsub) {
775 		queue.add(new Tuple.BooleanTuple(Opcode.SUB_FOR_MAP_REFERENCE, isGsub));
776 	}
777 
778 	/**
779 	 * <p>
780 	 * split.
781 	 * </p>
782 	 *
783 	 * @param numargs a int
784 	 */
785 	public void split(int numargs) {
786 		queue.add(new Tuple.CountTuple(Opcode.SPLIT, numargs));
787 	}
788 
789 	/**
790 	 * <p>
791 	 * substr.
792 	 * </p>
793 	 *
794 	 * @param numargs a int
795 	 */
796 	public void substr(int numargs) {
797 		queue.add(new Tuple.CountTuple(Opcode.SUBSTR, numargs));
798 	}
799 
800 	/**
801 	 * <p>
802 	 * tolower.
803 	 * </p>
804 	 */
805 	public void tolower() {
806 		queue.add(new Tuple.NoOperandTuple(Opcode.TOLOWER));
807 	}
808 
809 	/**
810 	 * <p>
811 	 * toupper.
812 	 * </p>
813 	 */
814 	public void toupper() {
815 		queue.add(new Tuple.NoOperandTuple(Opcode.TOUPPER));
816 	}
817 
818 	/**
819 	 * <p>
820 	 * system.
821 	 * </p>
822 	 */
823 	public void system() {
824 		queue.add(new Tuple.NoOperandTuple(Opcode.SYSTEM));
825 	}
826 
827 	/**
828 	 * <p>
829 	 * swap.
830 	 * </p>
831 	 */
832 	public void swap() {
833 		queue.add(new Tuple.NoOperandTuple(Opcode.SWAP));
834 	}
835 
836 	/**
837 	 * <p>
838 	 * add.
839 	 * </p>
840 	 */
841 	public void add() {
842 		queue.add(new Tuple.NoOperandTuple(Opcode.ADD));
843 	}
844 
845 	/**
846 	 * <p>
847 	 * subtract.
848 	 * </p>
849 	 */
850 	public void subtract() {
851 		queue.add(new Tuple.NoOperandTuple(Opcode.SUBTRACT));
852 	}
853 
854 	/**
855 	 * <p>
856 	 * multiply.
857 	 * </p>
858 	 */
859 	public void multiply() {
860 		queue.add(new Tuple.NoOperandTuple(Opcode.MULTIPLY));
861 	}
862 
863 	/**
864 	 * <p>
865 	 * divide.
866 	 * </p>
867 	 */
868 	public void divide() {
869 		queue.add(new Tuple.NoOperandTuple(Opcode.DIVIDE));
870 	}
871 
872 	/**
873 	 * <p>
874 	 * mod.
875 	 * </p>
876 	 */
877 	public void mod() {
878 		queue.add(new Tuple.NoOperandTuple(Opcode.MOD));
879 	}
880 
881 	/**
882 	 * <p>
883 	 * pow.
884 	 * </p>
885 	 */
886 	public void pow() {
887 		queue.add(new Tuple.NoOperandTuple(Opcode.POW));
888 	}
889 
890 	/**
891 	 * <p>
892 	 * inc.
893 	 * </p>
894 	 *
895 	 * @param offset a int
896 	 * @param isGlobal a boolean
897 	 */
898 	public void inc(int offset, boolean isGlobal) {
899 		queue.add(new Tuple.VariableTuple(Opcode.INC, offset, isGlobal));
900 	}
901 
902 	/**
903 	 * <p>
904 	 * dec.
905 	 * </p>
906 	 *
907 	 * @param offset a int
908 	 * @param isGlobal a boolean
909 	 */
910 	public void dec(int offset, boolean isGlobal) {
911 		queue.add(new Tuple.VariableTuple(Opcode.DEC, offset, isGlobal));
912 	}
913 
914 	/**
915 	 * <p>
916 	 * postInc.
917 	 * </p>
918 	 *
919 	 * @param offset a int
920 	 * @param isGlobal a boolean
921 	 */
922 	public void postInc(int offset, boolean isGlobal) {
923 		queue.add(new Tuple.VariableTuple(Opcode.POSTINC, offset, isGlobal));
924 	}
925 
926 	/**
927 	 * <p>
928 	 * postDec.
929 	 * </p>
930 	 *
931 	 * @param offset a int
932 	 * @param isGlobal a boolean
933 	 */
934 	public void postDec(int offset, boolean isGlobal) {
935 		queue.add(new Tuple.VariableTuple(Opcode.POSTDEC, offset, isGlobal));
936 	}
937 
938 	/**
939 	 * <p>
940 	 * incArrayRef.
941 	 * </p>
942 	 *
943 	 * @param offset a int
944 	 * @param isGlobal a boolean
945 	 */
946 	public void incArrayRef(int offset, boolean isGlobal) {
947 		queue.add(new Tuple.VariableTuple(Opcode.INC_ARRAY_REF, offset, isGlobal));
948 	}
949 
950 	/**
951 	 * Increments a stack-provided associative-array element reference.
952 	 */
953 	public void incMapRef() {
954 		queue.add(new Tuple.NoOperandTuple(Opcode.INC_MAP_REF));
955 	}
956 
957 	/**
958 	 * <p>
959 	 * decArrayRef.
960 	 * </p>
961 	 *
962 	 * @param offset a int
963 	 * @param isGlobal a boolean
964 	 */
965 	public void decArrayRef(int offset, boolean isGlobal) {
966 		queue.add(new Tuple.VariableTuple(Opcode.DEC_ARRAY_REF, offset, isGlobal));
967 	}
968 
969 	/**
970 	 * Decrements a stack-provided associative-array element reference.
971 	 */
972 	public void decMapRef() {
973 		queue.add(new Tuple.NoOperandTuple(Opcode.DEC_MAP_REF));
974 	}
975 
976 	/**
977 	 * <p>
978 	 * incDollarRef.
979 	 * </p>
980 	 */
981 	public void incDollarRef() {
982 		queue.add(new Tuple.NoOperandTuple(Opcode.INC_DOLLAR_REF));
983 	}
984 
985 	/**
986 	 * <p>
987 	 * decDollarRef.
988 	 * </p>
989 	 */
990 	public void decDollarRef() {
991 		queue.add(new Tuple.NoOperandTuple(Opcode.DEC_DOLLAR_REF));
992 	}
993 
994 	/**
995 	 * <p>
996 	 * dup.
997 	 * </p>
998 	 */
999 	public void dup() {
1000 		queue.add(new Tuple.NoOperandTuple(Opcode.DUP));
1001 	}
1002 
1003 	/**
1004 	 * <p>
1005 	 * not.
1006 	 * </p>
1007 	 */
1008 	public void not() {
1009 		queue.add(new Tuple.NoOperandTuple(Opcode.NOT));
1010 	}
1011 
1012 	/**
1013 	 * <p>
1014 	 * negate.
1015 	 * </p>
1016 	 */
1017 	public void negate() {
1018 		queue.add(new Tuple.NoOperandTuple(Opcode.NEGATE));
1019 	}
1020 
1021 	/**
1022 	 * <p>
1023 	 * unary plus.
1024 	 * </p>
1025 	 */
1026 	public void unaryPlus() {
1027 		queue.add(new Tuple.NoOperandTuple(Opcode.UNARY_PLUS));
1028 	}
1029 
1030 	/**
1031 	 * <p>
1032 	 * cmpEq.
1033 	 * </p>
1034 	 */
1035 	public void cmpEq() {
1036 		queue.add(new Tuple.NoOperandTuple(Opcode.CMP_EQ));
1037 	}
1038 
1039 	/**
1040 	 * <p>
1041 	 * cmpLt.
1042 	 * </p>
1043 	 */
1044 	public void cmpLt() {
1045 		queue.add(new Tuple.NoOperandTuple(Opcode.CMP_LT));
1046 	}
1047 
1048 	/**
1049 	 * <p>
1050 	 * cmpGt.
1051 	 * </p>
1052 	 */
1053 	public void cmpGt() {
1054 		queue.add(new Tuple.NoOperandTuple(Opcode.CMP_GT));
1055 	}
1056 
1057 	/**
1058 	 * <p>
1059 	 * matches.
1060 	 * </p>
1061 	 */
1062 	public void matches() {
1063 		queue.add(new Tuple.NoOperandTuple(Opcode.MATCHES));
1064 	}
1065 
1066 	/**
1067 	 * <p>
1068 	 * dereferenceArray.
1069 	 * </p>
1070 	 */
1071 	public void dereferenceArray() {
1072 		queue.add(new Tuple.NoOperandTuple(Opcode.DEREF_ARRAY));
1073 	}
1074 
1075 	/**
1076 	 * Looks up an associative-array element without creating a blank entry when
1077 	 * the key is missing.
1078 	 */
1079 	public void peekArrayElement() {
1080 		queue.add(new Tuple.NoOperandTuple(Opcode.PEEK_ARRAY_ELEMENT));
1081 	}
1082 
1083 	/**
1084 	 * Dereferences an associative-array element as a nested array, creating it if
1085 	 * needed.
1086 	 */
1087 	public void ensureArrayElement() {
1088 		queue.add(new Tuple.NoOperandTuple(Opcode.ENSURE_ARRAY_ELEMENT));
1089 	}
1090 
1091 	/**
1092 	 * <p>
1093 	 * key list.
1094 	 * </p>
1095 	 */
1096 	public void keylist() {
1097 		queue.add(new Tuple.NoOperandTuple(Opcode.KEYLIST));
1098 	}
1099 
1100 	/**
1101 	 * <p>
1102 	 * isEmptyList.
1103 	 * </p>
1104 	 *
1105 	 * @param address a {@link io.jawk.intermediate.Address} object
1106 	 */
1107 	public void isEmptyList(Address address) {
1108 		queue.add(new Tuple.AddressTuple(Opcode.IS_EMPTY_KEYLIST, address));
1109 	}
1110 
1111 	/**
1112 	 * <p>
1113 	 * getFirstAndRemoveFromList.
1114 	 * </p>
1115 	 */
1116 	public void getFirstAndRemoveFromList() {
1117 		queue.add(new Tuple.NoOperandTuple(Opcode.GET_FIRST_AND_REMOVE_FROM_KEYLIST));
1118 	}
1119 
1120 	/**
1121 	 * <p>
1122 	 * checkClass.
1123 	 * </p>
1124 	 *
1125 	 * @param cls a {@link java.lang.Class} object
1126 	 * @return a boolean
1127 	 */
1128 	public boolean checkClass(Class<?> cls) {
1129 		queue.add(new Tuple.ClassTuple(cls));
1130 		return true;
1131 	}
1132 
1133 	/**
1134 	 * <p>
1135 	 * getInputField.
1136 	 * </p>
1137 	 */
1138 	public void getInputField() {
1139 		queue.add(new Tuple.NoOperandTuple(Opcode.GET_INPUT_FIELD));
1140 	}
1141 
1142 	/**
1143 	 * <p>
1144 	 * getInputField.
1145 	 * </p>
1146 	 *
1147 	 * @param fieldIndex a long
1148 	 */
1149 	public void getInputField(long fieldIndex) {
1150 		queue.add(new Tuple.InputFieldTuple(fieldIndex));
1151 	}
1152 
1153 	/**
1154 	 * <p>
1155 	 * consumeInput.
1156 	 * </p>
1157 	 *
1158 	 * @param address a {@link io.jawk.intermediate.Address} object
1159 	 */
1160 	public void consumeInput(Address address) {
1161 		queue.add(new Tuple.AddressTuple(Opcode.CONSUME_INPUT, address));
1162 	}
1163 
1164 	/**
1165 	 * <p>
1166 	 * getlineInput.
1167 	 * </p>
1168 	 */
1169 	public void getlineInput() {
1170 		queue.add(new Tuple.NoOperandTuple(Opcode.GETLINE_INPUT));
1171 	}
1172 
1173 	/**
1174 	 * <p>
1175 	 * getlineInputToTarget.
1176 	 * </p>
1177 	 */
1178 	public void getlineInputToTarget() {
1179 		queue.add(new Tuple.NoOperandTuple(Opcode.GETLINE_INPUT_TO_TARGET));
1180 	}
1181 
1182 	/**
1183 	 * <p>
1184 	 * useAsFileInput.
1185 	 * </p>
1186 	 */
1187 	public void useAsFileInput() {
1188 		queue.add(new Tuple.NoOperandTuple(Opcode.USE_AS_FILE_INPUT));
1189 	}
1190 
1191 	/**
1192 	 * <p>
1193 	 * useAsCommandInput.
1194 	 * </p>
1195 	 */
1196 	public void useAsCommandInput() {
1197 		queue.add(new Tuple.NoOperandTuple(Opcode.USE_AS_COMMAND_INPUT));
1198 	}
1199 
1200 	/**
1201 	 * <p>
1202 	 * nfOffset.
1203 	 * </p>
1204 	 *
1205 	 * @param offset a int
1206 	 */
1207 	public void nfOffset(int offset) {
1208 		queue.add(new Tuple.LongTuple(Opcode.NF_OFFSET, offset));
1209 	}
1210 
1211 	/**
1212 	 * <p>
1213 	 * nrOffset.
1214 	 * </p>
1215 	 *
1216 	 * @param offset a int
1217 	 */
1218 	public void nrOffset(int offset) {
1219 		queue.add(new Tuple.LongTuple(Opcode.NR_OFFSET, offset));
1220 	}
1221 
1222 	/**
1223 	 * <p>
1224 	 * fnrOffset.
1225 	 * </p>
1226 	 *
1227 	 * @param offset a int
1228 	 */
1229 	public void fnrOffset(int offset) {
1230 		queue.add(new Tuple.LongTuple(Opcode.FNR_OFFSET, offset));
1231 	}
1232 
1233 	/**
1234 	 * <p>
1235 	 * fsOffset.
1236 	 * </p>
1237 	 *
1238 	 * @param offset a int
1239 	 */
1240 	public void fsOffset(int offset) {
1241 		queue.add(new Tuple.LongTuple(Opcode.FS_OFFSET, offset));
1242 	}
1243 
1244 	/**
1245 	 * <p>
1246 	 * rsOffset.
1247 	 * </p>
1248 	 *
1249 	 * @param offset a int
1250 	 */
1251 	public void rsOffset(int offset) {
1252 		queue.add(new Tuple.LongTuple(Opcode.RS_OFFSET, offset));
1253 	}
1254 
1255 	/**
1256 	 * <p>
1257 	 * ofsOffset.
1258 	 * </p>
1259 	 *
1260 	 * @param offset a int
1261 	 */
1262 	public void ofsOffset(int offset) {
1263 		queue.add(new Tuple.LongTuple(Opcode.OFS_OFFSET, offset));
1264 	}
1265 
1266 	/**
1267 	 * <p>
1268 	 * orsOffset.
1269 	 * </p>
1270 	 *
1271 	 * @param offset a int
1272 	 */
1273 	public void orsOffset(int offset) {
1274 		queue.add(new Tuple.LongTuple(Opcode.ORS_OFFSET, offset));
1275 	}
1276 
1277 	/**
1278 	 * <p>
1279 	 * rstartOffset.
1280 	 * </p>
1281 	 *
1282 	 * @param offset a int
1283 	 */
1284 	public void rstartOffset(int offset) {
1285 		queue.add(new Tuple.LongTuple(Opcode.RSTART_OFFSET, offset));
1286 	}
1287 
1288 	/**
1289 	 * <p>
1290 	 * rlengthOffset.
1291 	 * </p>
1292 	 *
1293 	 * @param offset a int
1294 	 */
1295 	public void rlengthOffset(int offset) {
1296 		queue.add(new Tuple.LongTuple(Opcode.RLENGTH_OFFSET, offset));
1297 	}
1298 
1299 	/**
1300 	 * <p>
1301 	 * filenameOffset.
1302 	 * </p>
1303 	 *
1304 	 * @param offset a int
1305 	 */
1306 	public void filenameOffset(int offset) {
1307 		queue.add(new Tuple.LongTuple(Opcode.FILENAME_OFFSET, offset));
1308 	}
1309 
1310 	/**
1311 	 * <p>
1312 	 * subsepOffset.
1313 	 * </p>
1314 	 *
1315 	 * @param offset a int
1316 	 */
1317 	public void subsepOffset(int offset) {
1318 		queue.add(new Tuple.LongTuple(Opcode.SUBSEP_OFFSET, offset));
1319 	}
1320 
1321 	/**
1322 	 * <p>
1323 	 * convfmtOffset.
1324 	 * </p>
1325 	 *
1326 	 * @param offset a int
1327 	 */
1328 	public void convfmtOffset(int offset) {
1329 		queue.add(new Tuple.LongTuple(Opcode.CONVFMT_OFFSET, offset));
1330 	}
1331 
1332 	/**
1333 	 * <p>
1334 	 * ofmtOffset.
1335 	 * </p>
1336 	 *
1337 	 * @param offset a int
1338 	 */
1339 	public void ofmtOffset(int offset) {
1340 		queue.add(new Tuple.LongTuple(Opcode.OFMT_OFFSET, offset));
1341 	}
1342 
1343 	/**
1344 	 * <p>
1345 	 * environOffset.
1346 	 * </p>
1347 	 *
1348 	 * @param offset a int
1349 	 */
1350 	public void environOffset(int offset) {
1351 		queue.add(new Tuple.LongTuple(Opcode.ENVIRON_OFFSET, offset));
1352 	}
1353 
1354 	/**
1355 	 * <p>
1356 	 * argcOffset.
1357 	 * </p>
1358 	 *
1359 	 * @param offset a int
1360 	 */
1361 	public void argcOffset(int offset) {
1362 		queue.add(new Tuple.LongTuple(Opcode.ARGC_OFFSET, offset));
1363 	}
1364 
1365 	/**
1366 	 * <p>
1367 	 * argvOffset.
1368 	 * </p>
1369 	 *
1370 	 * @param offset a int
1371 	 */
1372 	public void argvOffset(int offset) {
1373 		queue.add(new Tuple.LongTuple(Opcode.ARGV_OFFSET, offset));
1374 	}
1375 
1376 	// JRT-managed special variable helpers
1377 	/** Pushes the current value of {@code NF} onto the operand stack. */
1378 	public void pushNF() {
1379 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_NF));
1380 	}
1381 
1382 	/** Assigns the top-of-stack value to {@code NF}. */
1383 	public void assignNF() {
1384 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_NF));
1385 	}
1386 
1387 	/** Pushes the current value of {@code NR} onto the operand stack. */
1388 	public void pushNR() {
1389 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_NR));
1390 	}
1391 
1392 	/** Assigns the top-of-stack value to {@code NR}. */
1393 	public void assignNR() {
1394 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_NR));
1395 	}
1396 
1397 	/** Pushes the current value of {@code FNR} onto the operand stack. */
1398 	public void pushFNR() {
1399 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_FNR));
1400 	}
1401 
1402 	/** Assigns the top-of-stack value to {@code FNR}. */
1403 	public void assignFNR() {
1404 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_FNR));
1405 	}
1406 
1407 	/** Pushes the current value of {@code FS} onto the operand stack. */
1408 	public void pushFS() {
1409 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_FS));
1410 	}
1411 
1412 	/** Assigns the top-of-stack value to {@code FS}. */
1413 	public void assignFS() {
1414 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_FS));
1415 	}
1416 
1417 	/** Pushes the current value of {@code RS} onto the operand stack. */
1418 	public void pushRS() {
1419 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_RS));
1420 	}
1421 
1422 	/** Assigns the top-of-stack value to {@code RS}. */
1423 	public void assignRS() {
1424 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_RS));
1425 	}
1426 
1427 	/** Pushes the current value of {@code OFS} onto the operand stack. */
1428 	public void pushOFS() {
1429 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_OFS));
1430 	}
1431 
1432 	/** Assigns the top-of-stack value to {@code OFS}. */
1433 	public void assignOFS() {
1434 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_OFS));
1435 	}
1436 
1437 	/** Pushes the current value of {@code ORS} onto the operand stack. */
1438 	public void pushORS() {
1439 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_ORS));
1440 	}
1441 
1442 	/** Assigns the top-of-stack value to {@code ORS}. */
1443 	public void assignORS() {
1444 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_ORS));
1445 	}
1446 
1447 	/** Pushes the current value of {@code RSTART} onto the operand stack. */
1448 	public void pushRSTART() {
1449 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_RSTART));
1450 	}
1451 
1452 	/** Assigns the top-of-stack value to {@code RSTART}. */
1453 	public void assignRSTART() {
1454 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_RSTART));
1455 	}
1456 
1457 	/** Pushes the current value of {@code RLENGTH} onto the operand stack. */
1458 	public void pushRLENGTH() {
1459 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_RLENGTH));
1460 	}
1461 
1462 	/** Assigns the top-of-stack value to {@code RLENGTH}. */
1463 	public void assignRLENGTH() {
1464 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_RLENGTH));
1465 	}
1466 
1467 	/** Pushes the current value of {@code FILENAME} onto the operand stack. */
1468 	public void pushFILENAME() {
1469 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_FILENAME));
1470 	}
1471 
1472 	/** Assigns the top-of-stack value to {@code FILENAME}. */
1473 	public void assignFILENAME() {
1474 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_FILENAME));
1475 	}
1476 
1477 	/** Pushes the current value of {@code SUBSEP} onto the operand stack. */
1478 	public void pushSUBSEP() {
1479 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_SUBSEP));
1480 	}
1481 
1482 	/** Assigns the top-of-stack value to {@code SUBSEP}. */
1483 	public void assignSUBSEP() {
1484 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_SUBSEP));
1485 	}
1486 
1487 	/** Pushes the current value of {@code CONVFMT} onto the operand stack. */
1488 	public void pushCONVFMT() {
1489 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_CONVFMT));
1490 	}
1491 
1492 	/** Assigns the top-of-stack value to {@code CONVFMT}. */
1493 	public void assignCONVFMT() {
1494 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_CONVFMT));
1495 	}
1496 
1497 	/** Pushes the current value of {@code OFMT} onto the operand stack. */
1498 	public void pushOFMT() {
1499 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_OFMT));
1500 	}
1501 
1502 	/** Assigns the top-of-stack value to {@code OFMT}. */
1503 	public void assignOFMT() {
1504 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_OFMT));
1505 	}
1506 
1507 	/** Pushes the current value of {@code ARGC} onto the operand stack. */
1508 	public void pushARGC() {
1509 		queue.add(new Tuple.BuiltinVarTuple(Opcode.PUSH_ARGC));
1510 	}
1511 
1512 	/** Assigns the top-of-stack value to {@code ARGC}. */
1513 	public void assignARGC() {
1514 		queue.add(new Tuple.BuiltinVarTuple(Opcode.ASSIGN_ARGC));
1515 	}
1516 
1517 	/**
1518 	 * <p>
1519 	 * applyRS.
1520 	 * </p>
1521 	 */
1522 	public void applyRS() {
1523 		queue.add(new Tuple.NoOperandTuple(Opcode.APPLY_RS));
1524 	}
1525 
1526 	/**
1527 	 * <p>
1528 	 * function.
1529 	 * </p>
1530 	 *
1531 	 * @param funcName a {@link java.lang.String} object
1532 	 * @param numFormalParams a int
1533 	 */
1534 	public void function(String funcName, int numFormalParams) {
1535 		queue.add(new Tuple.FunctionTuple(funcName, numFormalParams));
1536 	}
1537 
1538 	/**
1539 	 * <p>
1540 	 * callFunction.
1541 	 * </p>
1542 	 *
1543 	 * @param addressSupplier supplier resolving the function's entry point
1544 	 * @param funcName a {@link java.lang.String} object
1545 	 * @param numFormalParams a int
1546 	 * @param numActualParams a int
1547 	 */
1548 	public void callFunction(
1549 			Supplier<Address> addressSupplier,
1550 			String funcName,
1551 			int numFormalParams,
1552 			int numActualParams) {
1553 		queue.add(new Tuple.CallFunctionTuple(addressSupplier, funcName, numFormalParams, numActualParams));
1554 	}
1555 
1556 	/**
1557 	 * <p>
1558 	 * setReturnResult.
1559 	 * </p>
1560 	 */
1561 	public void setReturnResult() {
1562 		queue.add(new Tuple.NoOperandTuple(Opcode.SET_RETURN_RESULT));
1563 	}
1564 
1565 	/**
1566 	 * <p>
1567 	 * returnFromFunction.
1568 	 * </p>
1569 	 */
1570 	public void returnFromFunction() {
1571 		queue.add(new Tuple.NoOperandTuple(Opcode.RETURN_FROM_FUNCTION));
1572 	}
1573 
1574 	/**
1575 	 * <p>
1576 	 * setNumGlobals.
1577 	 * </p>
1578 	 *
1579 	 * @param numGlobals a int
1580 	 */
1581 	public void setNumGlobals(int numGlobals) {
1582 		queue.add(new Tuple.CountTuple(Opcode.SET_NUM_GLOBALS, numGlobals));
1583 	}
1584 
1585 	/**
1586 	 * <p>
1587 	 * close.
1588 	 * </p>
1589 	 */
1590 	public void close() {
1591 		queue.add(new Tuple.NoOperandTuple(Opcode.CLOSE));
1592 	}
1593 
1594 	/**
1595 	 * <p>
1596 	 * applySubsep.
1597 	 * </p>
1598 	 *
1599 	 * @param count a int
1600 	 */
1601 	public void applySubsep(int count) {
1602 		queue.add(new Tuple.CountTuple(Opcode.APPLY_SUBSEP, count));
1603 	}
1604 
1605 	/**
1606 	 * <p>
1607 	 * deleteArrayElement.
1608 	 * </p>
1609 	 *
1610 	 * @param offset a int
1611 	 * @param isGlobal a boolean
1612 	 */
1613 	public void deleteArrayElement(int offset, boolean isGlobal) {
1614 		queue.add(new Tuple.VariableTuple(Opcode.DELETE_ARRAY_ELEMENT, offset, isGlobal));
1615 	}
1616 
1617 	/**
1618 	 * Deletes a stack-provided associative-array element.
1619 	 */
1620 	public void deleteMapElement() {
1621 		queue.add(new Tuple.NoOperandTuple(Opcode.DELETE_MAP_ELEMENT));
1622 	}
1623 
1624 	/**
1625 	 * <p>
1626 	 * deleteArray.
1627 	 * </p>
1628 	 *
1629 	 * @param offset a int
1630 	 * @param isGlobal a boolean
1631 	 */
1632 	public void deleteArray(int offset, boolean isGlobal) {
1633 		queue.add(new Tuple.VariableTuple(Opcode.DELETE_ARRAY, offset, isGlobal));
1634 	}
1635 
1636 	/**
1637 	 * <p>
1638 	 * setExitAddress.
1639 	 * </p>
1640 	 *
1641 	 * @param addr a {@link io.jawk.intermediate.Address} object
1642 	 */
1643 	public void setExitAddress(Address addr) {
1644 		queue.add(new Tuple.AddressTuple(Opcode.SET_EXIT_ADDRESS, addr));
1645 	}
1646 
1647 	/**
1648 	 * <p>
1649 	 * setWithinEndBlocks.
1650 	 * </p>
1651 	 *
1652 	 * @param b a boolean
1653 	 */
1654 	public void setWithinEndBlocks(boolean b) {
1655 		queue.add(new Tuple.BooleanTuple(Opcode.SET_WITHIN_END_BLOCKS, b));
1656 	}
1657 
1658 	/**
1659 	 * <p>
1660 	 * exitWithCode.
1661 	 * </p>
1662 	 */
1663 	public void exitWithCode() {
1664 		queue.add(new Tuple.NoOperandTuple(Opcode.EXIT_WITH_CODE));
1665 	}
1666 
1667 	/**
1668 	 * <p>
1669 	 * exitWithCode.
1670 	 * </p>
1671 	 */
1672 	public void exitWithoutCode() {
1673 		queue.add(new Tuple.NoOperandTuple(Opcode.EXIT_WITHOUT_CODE));
1674 	}
1675 
1676 	/**
1677 	 * <p>
1678 	 * regexp.
1679 	 * </p>
1680 	 *
1681 	 * @param regexpStr a {@link java.lang.String} object
1682 	 */
1683 	public void regexp(String regexpStr) {
1684 		// For literal regexes (created by RegexpAst), precompile the Pattern
1685 		// and store it alongside the original string to skip runtime compilation.
1686 		Pattern precompiled = Pattern.compile(regexpStr);
1687 		queue.add(new Tuple.RegexTuple(regexpStr, precompiled));
1688 	}
1689 
1690 	/**
1691 	 * <p>
1692 	 * regexpPair.
1693 	 * </p>
1694 	 */
1695 	public void conditionPair() {
1696 		queue.add(new Tuple.NoOperandTuple(Opcode.CONDITION_PAIR));
1697 	}
1698 
1699 	/**
1700 	 * <p>
1701 	 * isIn.
1702 	 * </p>
1703 	 */
1704 	public void isIn() {
1705 		queue.add(new Tuple.NoOperandTuple(Opcode.IS_IN));
1706 	}
1707 
1708 	/**
1709 	 * Emits a tuple that pushes the current script context onto the stack.
1710 	 */
1711 	public void scriptThis() {
1712 		queue.add(new Tuple.NoOperandTuple(Opcode.THIS));
1713 	}
1714 
1715 	/**
1716 	 * Emits an extension invocation tuple.
1717 	 *
1718 	 * @param function metadata describing the extension method to invoke
1719 	 * @param paramCount number of arguments supplied for the call
1720 	 * @param isInitial {@code true} when this tuple opens an extension call sequence
1721 	 */
1722 	public void extension(ExtensionFunction function, int paramCount, boolean isInitial) {
1723 		queue.add(new Tuple.ExtensionTuple(function, paramCount, isInitial));
1724 	}
1725 
1726 	/**
1727 	 * Dumps the queued tuples to the provided {@link PrintStream}.
1728 	 *
1729 	 * @param ps destination stream for the tuple listing
1730 	 */
1731 	public void dump(PrintStream ps) {
1732 		ps.println("(intermediate serialVersionUID = " + serialVersionUID + ")");
1733 		ps.println();
1734 		for (int i = 0; i < queue.size(); i++) {
1735 			Address address = addressManager.getAddress(i);
1736 			if (address == null) {
1737 				ps.println(i + " : " + queue.get(i));
1738 			} else {
1739 				ps.println(i + " : [" + address + "] : " + queue.get(i));
1740 			}
1741 		}
1742 	}
1743 
1744 	/**
1745 	 * <p>
1746 	 * top.
1747 	 * </p>
1748 	 *
1749 	 * @return a {@link io.jawk.intermediate.PositionTracker} object
1750 	 */
1751 	public PositionTracker top() {
1752 		return new PositionTracker(queue);
1753 	}
1754 
1755 	/**
1756 	 * Executed after all tuples are entered in the queue.
1757 	 * Its main functions are:
1758 	 * <ul>
1759 	 * <li>Assign queue.next to the next element in the queue.
1760 	 * <li>Calls touch(...) per Tuple so that addresses can be normalized/assigned/allocated
1761 	 * properly.
1762 	 * </ul>
1763 	 */
1764 	public void postProcess() {
1765 		if (postProcessed) {
1766 			return;
1767 		}
1768 		if (!queue.isEmpty() && queue.get(0).hasNext()) {
1769 			postProcessed = true;
1770 			return;
1771 		}
1772 		assignSequentialNextPointers();
1773 		for (Tuple tuple : queue) {
1774 			tuple.touch(queue);
1775 		}
1776 		postProcessed = true;
1777 	}
1778 
1779 	/**
1780 	 * Performs tuple queue optimizations such as reachability pruning, redundant
1781 	 * eval-global setup removal, and NOP collapsing.
1782 	 * <p>
1783 	 * This method is idempotent. Repeated invocations after a successful
1784 	 * optimization run will have no additional effect.
1785 	 * </p>
1786 	 * <p>
1787 	 * Peephole optimization happens at the tuple layer instead of during AST
1788 	 * construction. Folding after parsing guarantees that any tuple-level
1789 	 * transformations (for example, address resolution and extension hooks) have
1790 	 * already run, and it keeps a single optimization toggle ({@code optimize()})
1791 	 * for callers. Performing the work at the tuple layer also lets us recurse
1792 	 * until no more changes occur without complicating the parser.
1793 	 * </p>
1794 	 */
1795 	public void optimize() {
1796 		if (optimized) {
1797 			return;
1798 		}
1799 		if (!postProcessed) {
1800 			postProcess();
1801 		}
1802 		boolean queueModified = removeRedundantEvalSetNumGlobals();
1803 		queueModified |= peepholeOptimize();
1804 		if (queueModified) {
1805 			reprocessQueue();
1806 		}
1807 		simplifyControlFlow();
1808 		optimizeQueue();
1809 		optimized = true;
1810 	}
1811 
1812 	/**
1813 	 * Removes the synthetic {@code SET_NUM_GLOBALS} prelude from eval tuple
1814 	 * streams that never touch runtime-stack-backed variables or global metadata.
1815 	 * <p>
1816 	 * Expression compilation always emits the opcode up front, but field-only or
1817 	 * JRT-special-only expressions can execute without initializing the AVM global
1818 	 * frame. Dropping the tuple here keeps the runtime path lean while preserving
1819 	 * the parser's simpler tuple construction flow.
1820 	 * </p>
1821 	 *
1822 	 * @return {@code true} when a redundant eval {@code SET_NUM_GLOBALS} tuple was
1823 	 *         removed
1824 	 */
1825 	private boolean removeRedundantEvalSetNumGlobals() {
1826 		int setNumGlobalsIndex = -1;
1827 		for (int i = 0; i < queue.size(); i++) {
1828 			Opcode opcode = queue.get(i).getOpcode();
1829 			if (opcode == null) {
1830 				continue;
1831 			}
1832 			switch (opcode) {
1833 			case SET_NUM_GLOBALS:
1834 				if (setNumGlobalsIndex != -1) {
1835 					return false;
1836 				}
1837 				setNumGlobalsIndex = i;
1838 				break;
1839 			default:
1840 				if (requiresEvalGlobalFrame(opcode)) {
1841 					return false;
1842 				}
1843 				break;
1844 			}
1845 		}
1846 		if (!evalTupleStream || setNumGlobalsIndex < 0) {
1847 			return false;
1848 		}
1849 
1850 		int[] indexMapping = new int[queue.size()];
1851 		for (int i = 0, nextIndex = 0; i < queue.size(); i++) {
1852 			if (i == setNumGlobalsIndex) {
1853 				indexMapping[i] = nextIndex;
1854 			} else {
1855 				indexMapping[i] = nextIndex++;
1856 			}
1857 		}
1858 		queue.remove(setNumGlobalsIndex);
1859 		remapAddresses(indexMapping);
1860 		return true;
1861 	}
1862 
1863 	private boolean peepholeOptimize() {
1864 		// Keep running the local rewrite pass because one fold can expose another.
1865 		// Example: PUSH 1, PUSH 2, ADD, NEGATE first becomes PUSH 3, NEGATE and
1866 		// only the next pass can fold it to PUSH -3.
1867 		boolean modified = false;
1868 		boolean passModified;
1869 		do {
1870 			passModified = peepholeOptimizePass();
1871 			modified |= passModified;
1872 		} while (passModified);
1873 		return modified;
1874 	}
1875 
1876 	private boolean peepholeOptimizePass() {
1877 		int originalSize = queue.size();
1878 		if (originalSize < 2) {
1879 			return false;
1880 		}
1881 
1882 		List<Tuple> original = new ArrayList<Tuple>(queue);
1883 		int[] indexMapping = new int[originalSize];
1884 		Arrays.fill(indexMapping, -1);
1885 		List<Tuple> optimizedQueue = new ArrayList<Tuple>(originalSize);
1886 		boolean[] isAddressTarget = addressTargets(original, originalSize);
1887 
1888 		boolean modified = false;
1889 		int oldIndex = 0;
1890 		int newIndex = 0;
1891 		while (oldIndex < originalSize) {
1892 			Tuple tuple = original.get(oldIndex);
1893 			// If an earlier rewrite already happened in this pass, wait for the
1894 			// next pass before collapsing concat runs. That gives literal folding
1895 			// priority so fully constant chains become one PUSH_STRING instead of a
1896 			// partially folded PUSH_STRING plus MULTI_CONCAT.
1897 			ConcatRun concatRun = !modified ? concatRun(original, isAddressTarget, oldIndex) : null;
1898 			if (concatRun != null) {
1899 				// Chained concatenations compile as a run of binary CONCAT tuples
1900 				// after all operands have been pushed. Collapse that postfix run into
1901 				// one counted MULTI_CONCAT, e.g. CONCAT, CONCAT, CONCAT ->
1902 				// MULTI_CONCAT 4.
1903 				Tuple replacement = createMultiConcat(concatRun.itemCount, tuple.getLineNumber());
1904 				optimizedQueue.add(replacement);
1905 				mapFoldedRange(indexMapping, oldIndex, concatRun.tupleCount, newIndex);
1906 				oldIndex += concatRun.tupleCount;
1907 				newIndex++;
1908 				modified = true;
1909 				continue;
1910 			}
1911 
1912 			if (tuple.getOpcode() == Opcode.ASSIGN && (oldIndex + 1) < originalSize) {
1913 				Tuple nextTuple = original.get(oldIndex + 1);
1914 				// Statement assignments compile as ASSIGN followed by POP because
1915 				// ASSIGN normally leaves the assigned value on the stack for
1916 				// expression contexts such as print (a = 1). When the result is
1917 				// discarded immediately, replace both opcodes with ASSIGN_NOPUSH
1918 				// unless the POP itself is a branch target. Branches that land on
1919 				// the POP must continue to skip the assignment and only discard the
1920 				// already-computed expression result.
1921 				if (nextTuple.getOpcode() == Opcode.POP && !isAddressTarget[oldIndex + 1]) {
1922 					Tuple replacement = createAssignNoPush(tuple);
1923 					optimizedQueue.add(replacement);
1924 					mapFoldedRange(indexMapping, oldIndex, 2, newIndex);
1925 					oldIndex += 2;
1926 					newIndex++;
1927 					modified = true;
1928 					continue;
1929 				}
1930 			}
1931 
1932 			Object literal = literalValue(tuple);
1933 			if (literal != null) {
1934 				if ((oldIndex + 1) < originalSize) {
1935 					Tuple nextTuple = original.get(oldIndex + 1);
1936 					if (nextTuple.getOpcode() == Opcode.GET_INPUT_FIELD) {
1937 						// Replace PUSH literal + GET_INPUT_FIELD with the constant-field
1938 						// opcode so $1, $2, etc. do not need a stack round trip for the
1939 						// field index.
1940 						long fieldIndex = JRT.toLong(literal);
1941 						Tuple replacement = createGetInputFieldConst(
1942 								fieldIndex,
1943 								tuple.getLineNumber());
1944 						optimizedQueue.add(replacement);
1945 						mapFoldedRange(indexMapping, oldIndex, 2, newIndex);
1946 						oldIndex += 2;
1947 						newIndex++;
1948 						modified = true;
1949 						continue;
1950 					}
1951 				}
1952 				if ((oldIndex + 2) < originalSize) {
1953 					Tuple nextTuple = original.get(oldIndex + 1);
1954 					Tuple opTuple = original.get(oldIndex + 2);
1955 					Object secondLiteral = literalValue(nextTuple);
1956 					if (secondLiteral != null) {
1957 						Object folded = foldBinary(literal, secondLiteral, opTuple);
1958 						if (folded != null) {
1959 							// Fold two literal pushes followed by a pure binary operator
1960 							// into a single literal push, e.g. PUSH 1, PUSH 2, ADD ->
1961 							// PUSH 3.
1962 							Tuple replacement = createLiteralPush(folded, tuple.getLineNumber());
1963 							optimizedQueue.add(replacement);
1964 							mapFoldedRange(indexMapping, oldIndex, 3, newIndex);
1965 							oldIndex += 3;
1966 							newIndex++;
1967 							modified = true;
1968 							continue;
1969 						}
1970 					}
1971 				}
1972 				if ((oldIndex + 1) < originalSize) {
1973 					Tuple opTuple = original.get(oldIndex + 1);
1974 					Object folded = foldUnary(literal, opTuple);
1975 					if (folded != null) {
1976 						// Fold one literal push followed by a pure unary operator into a
1977 						// single literal push, e.g. PUSH 5, NEGATE -> PUSH -5.
1978 						Tuple replacement = createLiteralPush(folded, tuple.getLineNumber());
1979 						optimizedQueue.add(replacement);
1980 						mapFoldedRange(indexMapping, oldIndex, 2, newIndex);
1981 						oldIndex += 2;
1982 						newIndex++;
1983 						modified = true;
1984 						continue;
1985 					}
1986 				}
1987 			}
1988 
1989 			optimizedQueue.add(tuple);
1990 			indexMapping[oldIndex] = newIndex;
1991 			oldIndex++;
1992 			newIndex++;
1993 		}
1994 
1995 		if (!modified) {
1996 			return false;
1997 		}
1998 
1999 		for (int i = 0; i < optimizedQueue.size(); i++) {
2000 			queue.set(i, optimizedQueue.get(i));
2001 		}
2002 		for (int i = queue.size() - 1; i >= optimizedQueue.size(); i--) {
2003 			queue.remove(i);
2004 		}
2005 
2006 		remapAddresses(indexMapping);
2007 		return true;
2008 	}
2009 
2010 	private boolean[] addressTargets(List<Tuple> tuples, int tupleCount) {
2011 		boolean[] targets = new boolean[tupleCount];
2012 		for (Tuple tuple : tuples) {
2013 			Address address = tuple.getAddress();
2014 			if (address != null) {
2015 				int index = address.index();
2016 				if (index >= 0 && index < tupleCount) {
2017 					targets[index] = true;
2018 				}
2019 			}
2020 		}
2021 		return targets;
2022 	}
2023 
2024 	private void mapFoldedRange(int[] indexMapping, int startIndex, int length, int newIndex) {
2025 		for (int idx = 0; idx < length; idx++) {
2026 			indexMapping[startIndex + idx] = newIndex;
2027 		}
2028 	}
2029 
2030 	private ConcatRun concatRun(List<Tuple> original, boolean[] isAddressTarget, int oldIndex) {
2031 		Tuple tuple = original.get(oldIndex);
2032 		if (tuple.getOpcode() != Opcode.CONCAT || isAddressTarget[oldIndex]) {
2033 			return null;
2034 		}
2035 
2036 		int itemCount = 2;
2037 		int tupleCount = 1;
2038 		int currentIndex = oldIndex + 1;
2039 		while (currentIndex < original.size()
2040 				&& original.get(currentIndex).getOpcode() == Opcode.CONCAT
2041 				&& !isAddressTarget[currentIndex]) {
2042 			itemCount++;
2043 			tupleCount++;
2044 			currentIndex++;
2045 		}
2046 
2047 		if (tupleCount < 2) {
2048 			return null;
2049 		}
2050 		return new ConcatRun(tupleCount, itemCount);
2051 	}
2052 
2053 	private Object literalValue(Tuple tuple) {
2054 		switch (tuple.getOpcode()) {
2055 		case PUSH_LONG:
2056 			return Long.valueOf(((Tuple.PushLongTuple) tuple).getValue());
2057 		case PUSH_DOUBLE:
2058 			return Double.valueOf(((Tuple.PushDoubleTuple) tuple).getValue());
2059 		case PUSH_STRING:
2060 			return ((Tuple.PushStringTuple) tuple).getValue();
2061 		default:
2062 			return null;
2063 		}
2064 	}
2065 
2066 	private Object foldBinary(Object left, Object right, Tuple operation) {
2067 		Opcode opcode = operation.getOpcode();
2068 		if (opcode == null) {
2069 			return null;
2070 		}
2071 		switch (opcode) {
2072 		case ADD: {
2073 			double d1 = JRT.toDouble(left);
2074 			double d2 = JRT.toDouble(right);
2075 			double ans = d1 + d2;
2076 			if (JRT.isActuallyLong(ans)) {
2077 				return Long.valueOf((long) Math.rint(ans));
2078 			}
2079 			return Double.valueOf(ans);
2080 		}
2081 		case SUBTRACT: {
2082 			double d1 = JRT.toDouble(left);
2083 			double d2 = JRT.toDouble(right);
2084 			double ans = d1 - d2;
2085 			if (JRT.isActuallyLong(ans)) {
2086 				return Long.valueOf((long) Math.rint(ans));
2087 			}
2088 			return Double.valueOf(ans);
2089 		}
2090 		case MULTIPLY: {
2091 			double d1 = JRT.toDouble(left);
2092 			double d2 = JRT.toDouble(right);
2093 			double ans = d1 * d2;
2094 			if (JRT.isActuallyLong(ans)) {
2095 				return Long.valueOf((long) Math.rint(ans));
2096 			}
2097 			return Double.valueOf(ans);
2098 		}
2099 		case DIVIDE: {
2100 			double d1 = JRT.toDouble(left);
2101 			double d2 = JRT.toDouble(right);
2102 			double ans = d1 / d2;
2103 			if (JRT.isActuallyLong(ans)) {
2104 				return Long.valueOf((long) Math.rint(ans));
2105 			}
2106 			return Double.valueOf(ans);
2107 		}
2108 		case MOD: {
2109 			double d1 = JRT.toDouble(left);
2110 			double d2 = JRT.toDouble(right);
2111 			double ans = d1 % d2;
2112 			if (JRT.isActuallyLong(ans)) {
2113 				return Long.valueOf((long) Math.rint(ans));
2114 			}
2115 			return Double.valueOf(ans);
2116 		}
2117 		case POW: {
2118 			double d1 = JRT.toDouble(left);
2119 			double d2 = JRT.toDouble(right);
2120 			double ans = Math.pow(d1, d2);
2121 			if (JRT.isActuallyLong(ans)) {
2122 				return Long.valueOf((long) Math.rint(ans));
2123 			}
2124 			return Double.valueOf(ans);
2125 		}
2126 		case CMP_EQ:
2127 			return JRT.compare2(left, right, 0) ? Long.valueOf(1L) : Long.valueOf(0L);
2128 		case CMP_LT:
2129 			return JRT.compare2(left, right, -1) ? Long.valueOf(1L) : Long.valueOf(0L);
2130 		case CMP_GT:
2131 			return JRT.compare2(left, right, 1) ? Long.valueOf(1L) : Long.valueOf(0L);
2132 		case CONCAT:
2133 			if (left instanceof String && right instanceof String) {
2134 				return ((String) left) + ((String) right);
2135 			}
2136 			return null;
2137 		default:
2138 			return null;
2139 		}
2140 	}
2141 
2142 	private Object foldUnary(Object literal, Tuple operation) {
2143 		Opcode opcode = operation.getOpcode();
2144 		if (opcode == null) {
2145 			return null;
2146 		}
2147 		switch (opcode) {
2148 		case NEGATE: {
2149 			double value = JRT.toDouble(literal);
2150 			double ans = -value;
2151 			if (JRT.isActuallyLong(ans)) {
2152 				return Long.valueOf((long) Math.rint(ans));
2153 			}
2154 			return Double.valueOf(ans);
2155 		}
2156 		case UNARY_PLUS: {
2157 			double value = JRT.toDouble(literal);
2158 			if (JRT.isActuallyLong(value)) {
2159 				return Long.valueOf((long) Math.rint(value));
2160 			}
2161 			return Double.valueOf(value);
2162 		}
2163 		default:
2164 			return null;
2165 		}
2166 	}
2167 
2168 	private Tuple createLiteralPush(Object value, int lineNumber) {
2169 		Tuple tuple;
2170 		if (value instanceof Long) {
2171 			tuple = new Tuple.PushLongTuple(((Long) value).longValue());
2172 		} else if (value instanceof Integer) {
2173 			tuple = new Tuple.PushLongTuple(((Integer) value).longValue());
2174 		} else if (value instanceof Double) {
2175 			tuple = new Tuple.PushDoubleTuple(((Double) value).doubleValue());
2176 		} else if (value instanceof Number) {
2177 			double d = ((Number) value).doubleValue();
2178 			if (JRT.isActuallyLong(d)) {
2179 				tuple = new Tuple.PushLongTuple((long) Math.rint(d));
2180 			} else {
2181 				tuple = new Tuple.PushDoubleTuple(d);
2182 			}
2183 		} else if (value instanceof String) {
2184 			tuple = new Tuple.PushStringTuple((String) value);
2185 		} else {
2186 			throw new IllegalArgumentException("Unsupported literal value: " + value);
2187 		}
2188 		tuple.setLineNumber(lineNumber);
2189 		return tuple;
2190 	}
2191 
2192 	private Tuple createAssignNoPush(Tuple tuple) {
2193 		Tuple.VariableTuple variableTuple = (Tuple.VariableTuple) tuple;
2194 		Tuple replacement = new Tuple.VariableTuple(
2195 				Opcode.ASSIGN_NOPUSH,
2196 				variableTuple.getVariableOffset(),
2197 				variableTuple.isGlobal());
2198 		replacement.setLineNumber(tuple.getLineNumber());
2199 		return replacement;
2200 	}
2201 
2202 	private Tuple createGetInputFieldConst(long fieldIndex, int lineNumber) {
2203 		Tuple tuple = new Tuple.InputFieldTuple(fieldIndex);
2204 		tuple.setLineNumber(lineNumber);
2205 		return tuple;
2206 	}
2207 
2208 	private Tuple createMultiConcat(int itemCount, int lineNumber) {
2209 		Tuple tuple = new Tuple.CountTuple(Opcode.MULTI_CONCAT, itemCount);
2210 		tuple.setLineNumber(lineNumber);
2211 		return tuple;
2212 	}
2213 
2214 	private static final class ConcatRun {
2215 		private final int tupleCount;
2216 		private final int itemCount;
2217 
2218 		private ConcatRun(int tupleCount, int itemCount) {
2219 			this.tupleCount = tupleCount;
2220 			this.itemCount = itemCount;
2221 		}
2222 	}
2223 
2224 	private void remapAddresses(int[] indexMapping) {
2225 		if (indexMapping.length == 0) {
2226 			return;
2227 		}
2228 		Set<Address> processedAddresses = Collections.newSetFromMap(new IdentityHashMap<Address, Boolean>());
2229 		for (Tuple tuple : queue) {
2230 			Address address = tuple.getAddress();
2231 			if (address != null && processedAddresses.add(address)) {
2232 				int oldIndex = address.index();
2233 				if (oldIndex >= 0 && oldIndex < indexMapping.length) {
2234 					int mappedIndex = indexMapping[oldIndex];
2235 					if (mappedIndex < 0) {
2236 						throw new Error("Address " + address + " references removed tuple " + oldIndex);
2237 					}
2238 					address.assignIndex(mappedIndex);
2239 				}
2240 			}
2241 		}
2242 		addressManager.remapIndexes(indexMapping);
2243 	}
2244 
2245 	private void reprocessQueue() {
2246 		assignSequentialNextPointers();
2247 		for (Tuple tuple : queue) {
2248 			tuple.touch(queue);
2249 		}
2250 	}
2251 
2252 	private boolean simplifyControlFlow() {
2253 		boolean modified = false;
2254 		boolean passModified;
2255 		do {
2256 			passModified = simplifyControlFlowPass();
2257 			if (passModified) {
2258 				reprocessQueue();
2259 			}
2260 			modified |= passModified;
2261 		} while (passModified);
2262 		return modified;
2263 	}
2264 
2265 	private boolean simplifyControlFlowPass() {
2266 		int size = queue.size();
2267 		if (size < 2) {
2268 			return false;
2269 		}
2270 
2271 		boolean modified = false;
2272 		boolean[] remove = new boolean[size];
2273 		int[] redirectTargets = new int[size];
2274 		int[] visitStamps = new int[size];
2275 		int nextVisitStamp = 1;
2276 		Arrays.fill(redirectTargets, -1);
2277 
2278 		for (int i = 0; i < size; i++) {
2279 			Tuple tuple = queue.get(i);
2280 			Address address = tuple.getAddress();
2281 			if (address != null) {
2282 				int resolvedTarget = resolveJumpEquivalentIndex(
2283 						address.index(),
2284 						size,
2285 						visitStamps,
2286 						nextVisitStamp++);
2287 				if (resolvedTarget >= 0 && resolvedTarget != address.index()) {
2288 					addressManager.reassignAddress(address, resolvedTarget);
2289 					modified = true;
2290 				}
2291 			}
2292 
2293 			switch (tuple.getOpcode()) {
2294 			case NOP: {
2295 				int redirectTarget = resolveJumpEquivalentIndex(
2296 						i + 1,
2297 						size,
2298 						visitStamps,
2299 						nextVisitStamp++);
2300 				if (redirectTarget >= 0) {
2301 					remove[i] = true;
2302 					redirectTargets[i] = redirectTarget;
2303 					modified = true;
2304 				}
2305 				break;
2306 			}
2307 			case GOTO: {
2308 				int target = resolveJumpEquivalentIndex(
2309 						tuple.getAddress().index(),
2310 						size,
2311 						visitStamps,
2312 						nextVisitStamp++);
2313 				int fallthroughTarget = resolveJumpEquivalentIndex(
2314 						i + 1,
2315 						size,
2316 						visitStamps,
2317 						nextVisitStamp++);
2318 				if (target >= 0 && target == fallthroughTarget) {
2319 					remove[i] = true;
2320 					redirectTargets[i] = fallthroughTarget;
2321 					modified = true;
2322 				}
2323 				break;
2324 			}
2325 			default:
2326 				break;
2327 			}
2328 		}
2329 
2330 		if (!modified) {
2331 			return false;
2332 		}
2333 
2334 		boolean anyRemoved = false;
2335 		for (boolean removeTuple : remove) {
2336 			if (removeTuple) {
2337 				anyRemoved = true;
2338 				break;
2339 			}
2340 		}
2341 		if (!anyRemoved) {
2342 			return true;
2343 		}
2344 
2345 		int[] indexMapping = new int[size];
2346 		Arrays.fill(indexMapping, -1);
2347 		int nextIndex = 0;
2348 		for (int i = 0; i < size; i++) {
2349 			if (!remove[i]) {
2350 				indexMapping[i] = nextIndex++;
2351 			}
2352 		}
2353 		for (int i = 0; i < size; i++) {
2354 			if (remove[i] && redirectTargets[i] >= 0) {
2355 				indexMapping[i] = indexMapping[redirectTargets[i]];
2356 			}
2357 		}
2358 
2359 		compactQueue(remove);
2360 
2361 		remapAddresses(indexMapping);
2362 		return true;
2363 	}
2364 
2365 	private int resolveJumpEquivalentIndex(int index, int size, int[] visitStamps, int stamp) {
2366 		if (index < 0 || index >= size) {
2367 			return -1;
2368 		}
2369 		int current = index;
2370 		while (current >= 0 && current < size && visitStamps[current] != stamp) {
2371 			visitStamps[current] = stamp;
2372 			Tuple tuple = queue.get(current);
2373 			switch (tuple.getOpcode()) {
2374 			case NOP:
2375 				current++;
2376 				break;
2377 			case GOTO: {
2378 				Address address = tuple.getAddress();
2379 				if (address == null) {
2380 					return current;
2381 				}
2382 				current = address.index();
2383 				break;
2384 			}
2385 			default:
2386 				return current;
2387 			}
2388 		}
2389 		return -1;
2390 	}
2391 
2392 	private void assignSequentialNextPointers() {
2393 		for (int i = 0; i < queue.size(); i++) {
2394 			Tuple nextTuple = (i + 1) < queue.size() ? queue.get(i + 1) : null;
2395 			queue.get(i).setNext(nextTuple);
2396 		}
2397 	}
2398 
2399 	private void compactQueue(boolean[] remove) {
2400 		ArrayList<Tuple> compactedQueue = new ArrayList<Tuple>(queue.size());
2401 		for (int i = 0; i < remove.length; i++) {
2402 			if (!remove[i]) {
2403 				compactedQueue.add(queue.get(i));
2404 			}
2405 		}
2406 		queue.clear();
2407 		queue.addAll(compactedQueue);
2408 	}
2409 
2410 	private void optimizeQueue() {
2411 		int size = queue.size();
2412 		if (size <= 1) {
2413 			return;
2414 		}
2415 
2416 		boolean[] reachable = new boolean[size];
2417 		int[] referencesFromReachable = new int[size];
2418 
2419 		Deque<Integer> worklist = new ArrayDeque<>();
2420 		if (!queue.isEmpty()) {
2421 			reachable[0] = true;
2422 			worklist.add(0);
2423 		}
2424 
2425 		while (!worklist.isEmpty()) {
2426 			int index = worklist.removeFirst();
2427 			Tuple tuple = queue.get(index);
2428 
2429 			if (fallsThrough(tuple.getOpcode())) {
2430 				Tuple nextTuple = tuple.getNext();
2431 				if (nextTuple != null) {
2432 					int nextIndex = index + 1;
2433 					if (!reachable[nextIndex]) {
2434 						reachable[nextIndex] = true;
2435 						worklist.addLast(nextIndex);
2436 					}
2437 				}
2438 			}
2439 
2440 			Address address = tuple.getAddress();
2441 			if (address != null) {
2442 				int targetIndex = address.index();
2443 				if (targetIndex < 0 || targetIndex >= size) {
2444 					throw new Error("address " + address + " doesn't resolve to an actual list element");
2445 				}
2446 				referencesFromReachable[targetIndex]++;
2447 				if (!reachable[targetIndex]) {
2448 					reachable[targetIndex] = true;
2449 					worklist.addLast(targetIndex);
2450 				}
2451 			}
2452 		}
2453 
2454 		for (int i = 0; i < size; i++) {
2455 			if (!reachable[i] && referencesFromReachable[i] > 0) {
2456 				reachable[i] = true;
2457 				worklist.addLast(i);
2458 			}
2459 		}
2460 
2461 		while (!worklist.isEmpty()) {
2462 			int index = worklist.removeFirst();
2463 			Tuple tuple = queue.get(index);
2464 
2465 			if (fallsThrough(tuple.getOpcode())) {
2466 				Tuple nextTuple = tuple.getNext();
2467 				if (nextTuple != null) {
2468 					int nextIndex = index + 1;
2469 					if (!reachable[nextIndex]) {
2470 						reachable[nextIndex] = true;
2471 						worklist.addLast(nextIndex);
2472 					}
2473 				}
2474 			}
2475 
2476 			Address address = tuple.getAddress();
2477 			if (address != null) {
2478 				int targetIndex = address.index();
2479 				if (targetIndex < 0 || targetIndex >= size) {
2480 					throw new Error("address " + address + " doesn't resolve to an actual list element");
2481 				}
2482 				referencesFromReachable[targetIndex]++;
2483 				if (!reachable[targetIndex]) {
2484 					reachable[targetIndex] = true;
2485 					worklist.addLast(targetIndex);
2486 				}
2487 			}
2488 		}
2489 
2490 		boolean anyRemoved = false;
2491 		boolean[] remove = new boolean[size];
2492 		for (int i = 0; i < size; i++) {
2493 			if (!reachable[i]) {
2494 				remove[i] = true;
2495 				anyRemoved = true;
2496 				continue;
2497 			}
2498 			Tuple tuple = queue.get(i);
2499 			if (tuple.getOpcode() == Opcode.NOP && referencesFromReachable[i] == 0) {
2500 				remove[i] = true;
2501 				anyRemoved = true;
2502 			}
2503 		}
2504 
2505 		if (!anyRemoved) {
2506 			return;
2507 		}
2508 
2509 		int[] indexMapping = new int[size];
2510 		int nextIndex = 0;
2511 		for (int i = 0; i < size; i++) {
2512 			if (remove[i]) {
2513 				indexMapping[i] = -1;
2514 			} else {
2515 				indexMapping[i] = nextIndex++;
2516 			}
2517 		}
2518 
2519 		compactQueue(remove);
2520 
2521 		if (!queue.isEmpty()) {
2522 			assignSequentialNextPointers();
2523 		}
2524 
2525 		remapAddresses(indexMapping);
2526 	}
2527 
2528 	private boolean fallsThrough(Opcode opcode) {
2529 		if (opcode == null) {
2530 			return true;
2531 		}
2532 		switch (opcode) {
2533 		case GOTO:
2534 		case EXIT_WITH_CODE:
2535 		case EXIT_WITHOUT_CODE:
2536 			return false;
2537 		default:
2538 			return true;
2539 		}
2540 	}
2541 
2542 	/** Map of global variables offsets */
2543 	private Map<String, Integer> globalVarOffsetMap = new HashMap<String, Integer>();
2544 
2545 	/** Map of global arrays */
2546 	private Map<String, Boolean> globalVarAarrayMap = new HashMap<String, Boolean>();
2547 
2548 	/** List of user function names */
2549 	private Set<String> functionNames = new HashSet<String>();
2550 
2551 	/** Whether metadata collections are frozen for execution. */
2552 	private boolean metadataFrozen;
2553 
2554 	/**
2555 	 * Accept a {variable_name -&gt; offset} mapping such that global variables can be
2556 	 * assigned while processing name=value and filename command-line arguments.
2557 	 *
2558 	 * @param varname Name of the global variable
2559 	 * @param offset What offset to use for the variable
2560 	 * @param isArray Whether the variable is actually an array
2561 	 */
2562 	public void addGlobalVariableNameToOffsetMapping(String varname, int offset, boolean isArray) {
2563 		ensureMetadataMutable();
2564 		if (globalVarOffsetMap.get(varname) != null) {
2565 			return;
2566 		}
2567 		globalVarOffsetMap.put(varname, offset);
2568 		globalVarAarrayMap.put(varname, isArray);
2569 	}
2570 
2571 	/**
2572 	 * Accept a set of function names from the parser. This is
2573 	 * useful for invalidating name=value assignments from the
2574 	 * command line parameters, either via -v arguments or
2575 	 * passed into ARGV.
2576 	 *
2577 	 * @param names A set of function name strings.
2578 	 */
2579 	public void setFunctionNameSet(Set<String> names) {
2580 		ensureMetadataMutable();
2581 		// setFunctionNameSet is called with a keySet from
2582 		// a HashMap as a parameter, which is Opcode.NOT
2583 		// Serializable. Creating a new HashSet around
2584 		// the parameter resolves the issue.
2585 		// Otherwise, attempting to serialize this
2586 		// object results in a NotSerializableEexception
2587 		// being thrown because of functionNames field
2588 		// being a keyset from a HashMap.
2589 		this.functionNames = new HashSet<String>(names);
2590 	}
2591 
2592 	/**
2593 	 * Freezes the tuple metadata after compilation so execution can reuse the
2594 	 * published maps and sets without creating fresh unmodifiable wrappers.
2595 	 * Repeated calls are ignored.
2596 	 */
2597 	public void freezeMetadata() {
2598 		if (metadataFrozen) {
2599 			return;
2600 		}
2601 		globalVarOffsetMap = freezeMap(globalVarOffsetMap);
2602 		globalVarAarrayMap = freezeMap(globalVarAarrayMap);
2603 		functionNames = freezeSet(functionNames);
2604 		metadataFrozen = true;
2605 	}
2606 
2607 	/**
2608 	 * <p>
2609 	 * getGlobalVariableOffsetMap.
2610 	 * </p>
2611 	 *
2612 	 * @return a {@link java.util.Map} object
2613 	 */
2614 	@SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "freezeMetadata() replaces this field with an unmodifiable snapshot before compiled tuples are exposed")
2615 	public Map<String, Integer> getGlobalVariableOffsetMap() {
2616 		return globalVarOffsetMap;
2617 	}
2618 
2619 	/**
2620 	 * <p>
2621 	 * getGlobalVariableAarrayMap.
2622 	 * </p>
2623 	 *
2624 	 * @return a {@link java.util.Map} object
2625 	 */
2626 	@SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "freezeMetadata() replaces this field with an unmodifiable snapshot before compiled tuples are exposed")
2627 	public Map<String, Boolean> getGlobalVariableAarrayMap() {
2628 		return globalVarAarrayMap;
2629 	}
2630 
2631 	/**
2632 	 * <p>
2633 	 * getFunctionNameSet.
2634 	 * </p>
2635 	 *
2636 	 * @return a {@link java.util.Set} object
2637 	 */
2638 	@SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "freezeMetadata() replaces this field with an unmodifiable snapshot before compiled tuples are exposed")
2639 	public Set<String> getFunctionNameSet() {
2640 		return functionNames;
2641 	}
2642 
2643 	private void ensureMetadataMutable() {
2644 		if (metadataFrozen) {
2645 			throw new IllegalStateException("Tuple metadata is frozen.");
2646 		}
2647 	}
2648 
2649 	private static <K, V> Map<K, V> freezeMap(Map<K, V> map) {
2650 		if (map.isEmpty()) {
2651 			return Collections.emptyMap();
2652 		}
2653 		return Collections.unmodifiableMap(new HashMap<K, V>(map));
2654 	}
2655 
2656 	private static <T> Set<T> freezeSet(Set<T> set) {
2657 		if (set.isEmpty()) {
2658 			return Collections.emptySet();
2659 		}
2660 		return Collections.unmodifiableSet(new HashSet<T>(set));
2661 	}
2662 
2663 	private boolean requiresEvalGlobalFrame(Opcode opcode) {
2664 		switch (opcode) {
2665 		case ASSIGN:
2666 		case ASSIGN_NOPUSH:
2667 		case ASSIGN_ARRAY:
2668 		case DEREFERENCE:
2669 		case PLUS_EQ:
2670 		case MINUS_EQ:
2671 		case MULT_EQ:
2672 		case DIV_EQ:
2673 		case MOD_EQ:
2674 		case POW_EQ:
2675 		case PLUS_EQ_ARRAY:
2676 		case MINUS_EQ_ARRAY:
2677 		case MULT_EQ_ARRAY:
2678 		case DIV_EQ_ARRAY:
2679 		case MOD_EQ_ARRAY:
2680 		case POW_EQ_ARRAY:
2681 		case CALL_FUNCTION:
2682 		case SET_RETURN_RESULT:
2683 		case RETURN_FROM_FUNCTION:
2684 		case MATCH:
2685 		case DELETE_ARRAY_ELEMENT:
2686 		case DELETE_ARRAY:
2687 		case ENVIRON_OFFSET:
2688 		case ARGC_OFFSET:
2689 		case ARGV_OFFSET:
2690 		case ASSIGN_ARGC:
2691 		case PUSH_ARGC:
2692 			return true;
2693 		default:
2694 			return false;
2695 		}
2696 	}
2697 
2698 	/** linenumber stack ... */
2699 	private Deque<Integer> linenoStack = new ArrayDeque<Integer>();
2700 
2701 	/**
2702 	 * Push the current line number onto the line number stack.
2703 	 * This is called by the parser to keep track of the
2704 	 * current source line number. Keeping track of line
2705 	 * numbers this way allows the runtime to report
2706 	 * more meaningful errors by providing source line numbers
2707 	 * within error reports.
2708 	 *
2709 	 * @param lineno The current source line number.
2710 	 */
2711 	public void pushSourceLineNumber(int lineno) {
2712 		linenoStack.push(lineno);
2713 	}
2714 
2715 	/**
2716 	 * <p>
2717 	 * popSourceLineNumber.
2718 	 * </p>
2719 	 *
2720 	 * @param lineno a int
2721 	 */
2722 	public void popSourceLineNumber(int lineno) {
2723 		linenoStack.pop();
2724 	}
2725 
2726 }