1 package io.jawk.frontend;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 import java.io.File;
26 import java.io.IOException;
27 import java.io.LineNumberReader;
28 import java.io.PrintStream;
29 import java.nio.charset.StandardCharsets;
30 import java.nio.file.Files;
31 import java.nio.file.InvalidPathException;
32 import java.nio.file.Path;
33 import java.nio.file.Paths;
34 import java.util.ArrayDeque;
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.Deque;
38 import java.util.EnumSet;
39 import java.util.HashMap;
40 import java.util.HashSet;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.Set;
44 import java.util.function.Supplier;
45 import io.jawk.AwkSandboxException;
46 import io.jawk.NotImplementedError;
47 import io.jawk.backend.AVM;
48 import io.jawk.ext.ExtensionFunction;
49 import io.jawk.intermediate.Address;
50 import io.jawk.intermediate.AwkTuples;
51 import io.jawk.intermediate.BuiltinFunction;
52 import io.jawk.jrt.JRT;
53 import io.jawk.intermediate.Tuple;
54 import io.jawk.util.ScriptFileSource;
55 import io.jawk.util.ScriptSource;
56 import io.jawk.frontend.ast.LexerException;
57 import io.jawk.frontend.ast.ParserException;
58
59
60
61
62
63
64
65
66
67 public class AwkParser {
68
69
70
71
72
73
74 private enum AstFlag {
75 BREAKABLE,
76 NEXTABLE,
77 CONTINUEABLE,
78 RETURNABLE,
79 NON_STATEMENT
80 }
81
82
83 enum Token {
84 EOF,
85 NEWLINE,
86 SEMICOLON,
87 ID,
88 FUNC_ID,
89 INTEGER,
90 DOUBLE,
91 STRING,
92
93 EQUALS,
94
95 AND,
96 OR,
97
98 EQ,
99 GT,
100 GE,
101 LT,
102 LE,
103 NE,
104 NOT,
105 PIPE,
106 QUESTION_MARK,
107 COLON,
108 APPEND,
109
110 PLUS,
111 MINUS,
112 MULT,
113 DIVIDE,
114 MOD,
115 POW,
116 COMMA,
117 MATCHES,
118 NOT_MATCHES,
119 DOLLAR,
120
121 INC,
122 DEC,
123
124 PLUS_EQ,
125 MINUS_EQ,
126 MULT_EQ,
127 DIV_EQ,
128 MOD_EQ,
129 POW_EQ,
130
131 OPEN_PAREN,
132 CLOSE_PAREN,
133 OPEN_BRACE,
134 CLOSE_BRACE,
135 OPEN_BRACKET,
136 CLOSE_BRACKET,
137
138 BUILTIN_FUNC_NAME,
139
140 EXTENSION,
141 TYPED_REGEXP,
142 INDIRECT,
143 DIRECTIVE_INCLUDE,
144 DIRECTIVE_NAMESPACE,
145 DIRECTIVE_UNSUPPORTED,
146
147 KW_FUNCTION,
148 KW_BEGIN,
149 KW_END,
150 KW_BEGINFILE,
151 KW_ENDFILE,
152 KW_IN,
153 KW_IF,
154 KW_ELSE,
155 KW_WHILE,
156 KW_FOR,
157 KW_DO,
158 KW_RETURN,
159 KW_EXIT,
160 KW_NEXT,
161 KW_NEXTFILE,
162 KW_CONTINUE,
163 KW_DELETE,
164 KW_BREAK,
165 KW_PRINT,
166 KW_PRINTF,
167 KW_GETLINE
168 }
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184 private static final Map<String, Token> KEYWORDS = new HashMap<String, Token>();
185
186 static {
187
188 KEYWORDS.put("function", Token.KW_FUNCTION);
189 KEYWORDS.put("BEGIN", Token.KW_BEGIN);
190 KEYWORDS.put("END", Token.KW_END);
191 KEYWORDS.put("BEGINFILE", Token.KW_BEGINFILE);
192 KEYWORDS.put("ENDFILE", Token.KW_ENDFILE);
193 KEYWORDS.put("in", Token.KW_IN);
194
195
196 KEYWORDS.put("if", Token.KW_IF);
197 KEYWORDS.put("else", Token.KW_ELSE);
198 KEYWORDS.put("while", Token.KW_WHILE);
199 KEYWORDS.put("for", Token.KW_FOR);
200 KEYWORDS.put("do", Token.KW_DO);
201 KEYWORDS.put("return", Token.KW_RETURN);
202 KEYWORDS.put("exit", Token.KW_EXIT);
203 KEYWORDS.put("next", Token.KW_NEXT);
204 KEYWORDS.put("nextfile", Token.KW_NEXTFILE);
205 KEYWORDS.put("continue", Token.KW_CONTINUE);
206 KEYWORDS.put("delete", Token.KW_DELETE);
207 KEYWORDS.put("break", Token.KW_BREAK);
208
209
210 KEYWORDS.put("print", Token.KW_PRINT);
211 KEYWORDS.put("printf", Token.KW_PRINTF);
212 KEYWORDS.put("getline", Token.KW_GETLINE);
213 }
214
215 private static final int SP_IDX = 257;
216
217
218
219
220
221
222
223
224
225 private static final Map<String, Integer> SPECIAL_VAR_NAMES = new HashMap<String, Integer>();
226
227 static {
228 SPECIAL_VAR_NAMES.put("NR", SP_IDX);
229 SPECIAL_VAR_NAMES.put("FNR", SP_IDX);
230 SPECIAL_VAR_NAMES.put("NF", SP_IDX);
231 SPECIAL_VAR_NAMES.put("FS", SP_IDX);
232 SPECIAL_VAR_NAMES.put("RS", SP_IDX);
233 SPECIAL_VAR_NAMES.put("OFS", SP_IDX);
234 SPECIAL_VAR_NAMES.put("ORS", SP_IDX);
235 SPECIAL_VAR_NAMES.put("RSTART", SP_IDX);
236 SPECIAL_VAR_NAMES.put("RLENGTH", SP_IDX);
237 SPECIAL_VAR_NAMES.put("FILENAME", SP_IDX);
238 SPECIAL_VAR_NAMES.put("SUBSEP", SP_IDX);
239 SPECIAL_VAR_NAMES.put("CONVFMT", SP_IDX);
240 SPECIAL_VAR_NAMES.put("OFMT", SP_IDX);
241 SPECIAL_VAR_NAMES.put("ENVIRON", SP_IDX);
242 SPECIAL_VAR_NAMES.put("ARGC", SP_IDX);
243 SPECIAL_VAR_NAMES.put("ARGV", SP_IDX);
244 SPECIAL_VAR_NAMES.put("IGNORECASE", SP_IDX);
245 SPECIAL_VAR_NAMES.put("ERRNO", SP_IDX);
246 SPECIAL_VAR_NAMES.put("ARGIND", SP_IDX);
247 }
248
249
250
251
252
253
254 private final AwkSymbolTableImpl symbolTable = new AwkSymbolTableImpl();
255
256 private final Map<String, ExtensionFunction> extensions;
257
258
259 private final boolean posix;
260
261
262 private final boolean sourceIncludeAllowed;
263
264
265
266
267
268
269
270
271
272 public AwkParser(Map<String, ExtensionFunction> extensions, boolean posix) {
273 this(extensions, posix, true);
274 }
275
276
277
278
279
280
281
282
283 public AwkParser(
284 Map<String, ExtensionFunction> extensions,
285 boolean posix,
286 boolean sourceIncludeAllowed) {
287 this.extensions = extensions == null ?
288 Collections.emptyMap() : Collections.unmodifiableMap(new HashMap<>(extensions));
289 this.posix = posix;
290 this.sourceIncludeAllowed = sourceIncludeAllowed;
291 }
292
293
294
295
296
297
298
299
300
301
302 private boolean isDisabledKeyword(Token keywordToken) {
303 return posix && (keywordToken == Token.KW_BEGINFILE || keywordToken == Token.KW_ENDFILE);
304 }
305
306 private boolean isAwkNamespaceIdentifier(String identifier) {
307 int separator = identifier.indexOf("::");
308 if (separator >= 0) {
309 return "awk".equals(identifier.substring(0, separator));
310 }
311 return "awk".equals(currentNamespace);
312 }
313
314 private String awkNamespaceComponent(String identifier) {
315 return identifier.startsWith("awk::") ? identifier.substring("awk::".length()) : identifier;
316 }
317
318 private String qualifyGlobalIdentifier(String identifier) {
319 int separator = identifier.indexOf("::");
320 if (separator >= 0) {
321 return "awk".equals(identifier.substring(0, separator)) ? identifier.substring(separator + 2) : identifier;
322 }
323 if ("awk".equals(currentNamespace) || isAllUppercaseIdentifier(identifier)) {
324 return identifier;
325 }
326 return currentNamespace + "::" + identifier;
327 }
328
329 private boolean isAllUppercaseIdentifier(String identifier) {
330 if (identifier.isEmpty()) {
331 return false;
332 }
333 for (int i = 0; i < identifier.length(); i++) {
334 char ch = identifier.charAt(i);
335 if (ch < 'A' || ch > 'Z') {
336 return false;
337 }
338 }
339 return true;
340 }
341
342 private List<ScriptSource> scriptSources;
343 private int scriptSourcesCurrentIndex;
344 private ScriptSource currentScriptSource;
345 private LineNumberReader reader;
346 private int c;
347 private Token token;
348 private String pendingIndirectIdentifier;
349 private boolean pendingColon;
350 private String currentNamespace = "awk";
351 private long conditionPairCount;
352 private final Deque<SourceState> includedSourceStack = new ArrayDeque<SourceState>();
353 private final Set<Path> includedSourcePaths = new HashSet<Path>();
354 private final Set<Path> topLevelSourcePaths = new HashSet<Path>();
355
356 private StringBuffer text = new StringBuffer();
357 private StringBuffer string = new StringBuffer();
358 private StringBuffer regexp = new StringBuffer();
359
360 private static final class SourceState {
361 private final ScriptSource scriptSource;
362 private final LineNumberReader reader;
363 private final int currentCharacter;
364 private final String namespace;
365
366 private SourceState(
367 ScriptSource scriptSourceParam,
368 LineNumberReader readerParam,
369 int currentCharacterParam,
370 String namespaceParam) {
371 scriptSource = scriptSourceParam;
372 reader = readerParam;
373 currentCharacter = currentCharacterParam;
374 namespace = namespaceParam;
375 }
376 }
377
378 private void read() throws IOException {
379 text.append((char) c);
380 c = reader.read();
381
382 while (c == '\r') {
383 c = reader.read();
384 }
385 }
386
387
388
389
390
391
392
393
394
395
396
397
398 private boolean readExponent() throws IOException {
399 if (c != 'e' && c != 'E') {
400 return false;
401 }
402
403
404
405 reader.mark(3);
406 int first = reader.read();
407 boolean validExponent;
408 if (first == '+' || first == '-') {
409 validExponent = Character.isDigit(reader.read());
410 } else {
411 validExponent = Character.isDigit(first);
412 }
413 reader.reset();
414 if (!validExponent) {
415 return false;
416 }
417 read();
418 if (c == '+' || c == '-') {
419 read();
420 }
421 while (c > 0 && Character.isDigit(c)) {
422 read();
423 }
424 return true;
425 }
426
427
428
429
430
431
432
433
434
435
436
437
438 private void advancePastEndOfSource() throws IOException {
439 while (c < 0) {
440 if (!includedSourceStack.isEmpty()) {
441 reader.close();
442 SourceState previous = includedSourceStack.pop();
443 currentScriptSource = previous.scriptSource;
444 reader = previous.reader;
445 c = previous.currentCharacter;
446 currentNamespace = previous.namespace;
447 } else if ((scriptSourcesCurrentIndex + 1) < scriptSources.size()) {
448 scriptSourcesCurrentIndex++;
449 currentScriptSource = scriptSources.get(scriptSourcesCurrentIndex);
450 reader = new LineNumberReader(currentScriptSource.getReader());
451 currentNamespace = "awk";
452 c = reader.read();
453 while (c == '\r') {
454 c = reader.read();
455 }
456 } else {
457 return;
458 }
459 }
460 }
461
462
463
464
465
466
467 private void skipWhitespaces() throws IOException {
468 while (c == ' ' || c == '\t' || c == '#' || c == '\n') {
469 if (c == '#') {
470 while (c >= 0 && c != '\n') {
471 read();
472 }
473 }
474 read();
475 }
476 }
477
478
479
480
481
482
483
484
485
486 public AstNode parse(List<ScriptSource> localScriptSources) throws IOException {
487 if (localScriptSources == null || localScriptSources.isEmpty()) {
488 throw new IOException("No script sources supplied");
489 }
490 this.scriptSources = Collections.unmodifiableList(new ArrayList<>(localScriptSources));
491 scriptSourcesCurrentIndex = 0;
492 currentScriptSource = this.scriptSources.get(scriptSourcesCurrentIndex);
493 reader = new LineNumberReader(currentScriptSource.getReader());
494 currentNamespace = "awk";
495 includedSourceStack.clear();
496 resetIncludedSourcePaths();
497 pendingIndirectIdentifier = null;
498 pendingColon = false;
499 read();
500 lexer();
501 return SCRIPT();
502 }
503
504
505
506
507
508
509
510
511 public AstNode parseExpression(ScriptSource expressionSource) throws IOException {
512
513
514 if (expressionSource == null) {
515 throw new IOException("No source supplied");
516 }
517
518
519 this.scriptSources = Collections.singletonList(expressionSource);
520 scriptSourcesCurrentIndex = 0;
521 currentScriptSource = expressionSource;
522 reader = new LineNumberReader(currentScriptSource.getReader());
523 currentNamespace = "awk";
524 includedSourceStack.clear();
525 resetIncludedSourcePaths();
526 pendingIndirectIdentifier = null;
527 pendingColon = false;
528
529
530 read();
531 lexer();
532
533
534 return EXPRESSION_TO_EVALUATE();
535 }
536
537 private void resetIncludedSourcePaths() throws IOException {
538 includedSourcePaths.clear();
539 topLevelSourcePaths.clear();
540 for (ScriptSource source : scriptSources) {
541 if (source instanceof ScriptFileSource) {
542 String filePath = ((ScriptFileSource) source).getFilePath();
543 Path sourcePath = Paths.get(filePath).toRealPath();
544 includedSourcePaths.add(sourcePath);
545 topLevelSourcePaths.add(sourcePath);
546 }
547 }
548 }
549
550 private LexerException lexerException(String msg) {
551 return new LexerException(
552 msg,
553 currentScriptSource.getDescription(),
554 reader.getLineNumber());
555 }
556
557
558
559
560
561
562
563 private int currentSourceLineNumber() {
564 return reader.getLineNumber() + 1;
565 }
566
567
568
569
570
571
572 private void readString() throws IOException {
573 string.setLength(0);
574
575 while (token != Token.EOF && c > 0 && c != '"' && c != '\n') {
576 if (c == '\\') {
577 read();
578 switch (c) {
579 case 'n':
580 string.append('\n');
581 break;
582 case 't':
583 string.append('\t');
584 break;
585 case 'r':
586 string.append('\r');
587 break;
588 case 'a':
589 string.append('\007');
590 break;
591 case 'b':
592 string.append('\010');
593 break;
594 case 'f':
595 string.append('\014');
596 break;
597 case 'v':
598 string.append('\013');
599 break;
600
601 case '0':
602 case '1':
603 case '2':
604 case '3':
605 case '4':
606 case '5':
607 case '6':
608 case '7': {
609 int octalChar = c - '0';
610 read();
611 if (c >= '0' && c <= '7') {
612 octalChar = (octalChar << 3) + c - '0';
613 read();
614 if (c >= '0' && c <= '7') {
615 octalChar = (octalChar << 3) + c - '0';
616 read();
617 }
618 }
619 string.append((char) octalChar);
620 continue;
621 }
622
623 case 'x': {
624 int hexChar = 0;
625 read();
626 if (c >= '0' && c <= '9') {
627 hexChar = c - '0';
628 } else if (c >= 'A' && c <= 'F') {
629 hexChar = c - 'A' + 10;
630 } else if (c >= 'a' && c <= 'f') {
631 hexChar = c - 'a' + 10;
632 } else {
633 string.append('x');
634 continue;
635 }
636 read();
637 if (c >= '0' && c <= '9') {
638 hexChar = (hexChar << 4) + c - '0';
639 } else if (c >= 'A' && c <= 'F') {
640 hexChar = (hexChar << 4) + c - 'A' + 10;
641 } else if (c >= 'a' && c <= 'f') {
642 hexChar = (hexChar << 4) + c - 'a' + 10;
643 } else {
644
645 string.append((char) hexChar);
646 continue;
647 }
648 string.append((char) hexChar);
649 break;
650 }
651 default:
652 string.append((char) c);
653 break;
654 }
655 } else {
656 string.append((char) c);
657 }
658 read();
659 }
660 if (token == Token.EOF || c == '\n' || c <= 0) {
661 throw lexerException("Unterminated string: " + text);
662 }
663 read();
664 }
665
666
667
668
669
670
671
672
673 private void readRegexp() throws IOException {
674 regexp.setLength(0);
675
676 boolean inBracket = false;
677 while (token != Token.EOF && c > 0 && (c != '/' || inBracket) && c != '\n') {
678 if (c == '\\') {
679 read();
680 if (c != '/') {
681 regexp.append('\\');
682 }
683 regexp.append((char) c);
684 read();
685 continue;
686 }
687 if (!inBracket && c == '[') {
688 inBracket = true;
689 regexp.append((char) c);
690 read();
691
692 if (c == '^') {
693 regexp.append((char) c);
694 read();
695 }
696 if (c == ']') {
697 regexp.append((char) c);
698 read();
699 }
700 continue;
701 }
702 if (inBracket && c == '[') {
703 regexp.append((char) c);
704 read();
705
706
707
708 if (c == ':' || c == '.' || c == '=') {
709 int delimiter = c;
710 boolean closed = false;
711 while (token != Token.EOF && c > 0 && c != '\n' && !closed) {
712 int previous = c;
713 regexp.append((char) c);
714 read();
715 if (previous == delimiter && c == ']') {
716 regexp.append((char) c);
717 read();
718 closed = true;
719 }
720 }
721 }
722 continue;
723 }
724 if (inBracket && c == ']') {
725 inBracket = false;
726 }
727 regexp.append((char) c);
728 read();
729 }
730 if (token == Token.EOF || c == '\n' || c <= 0) {
731 throw lexerException("Unterminated string: " + text);
732 }
733 read();
734 }
735
736 private Token lexer(Token expectedToken) throws IOException {
737 if (token != expectedToken) {
738 throw parserException(
739 "Expecting " + expectedToken.name() + ". Found: " + token.name() + " (" + text + ")");
740 }
741 return lexer();
742 }
743
744 private Token lexer() throws IOException {
745
746 while (true) {
747 advancePastEndOfSource();
748 if (c < 0 || c != ' ' && c != '\t' && c != '#' && c != '\\') {
749 break;
750 }
751 if (c == '\\') {
752 read();
753 if (c == '\n') {
754 read();
755 }
756 continue;
757 }
758 if (c == '#') {
759
760 while (c >= 0 && c != '\n') {
761 read();
762 }
763 } else {
764 read();
765 }
766 }
767 text.setLength(0);
768 if (pendingColon) {
769 pendingColon = false;
770 token = Token.COLON;
771 return token;
772 }
773 if (pendingIndirectIdentifier != null) {
774 text.append(pendingIndirectIdentifier);
775 pendingIndirectIdentifier = null;
776 token = Token.ID;
777 return token;
778 }
779 if (c < 0) {
780 token = Token.EOF;
781 return token;
782 }
783 if (c == ',') {
784 read();
785 skipWhitespaces();
786 token = Token.COMMA;
787 return token;
788 }
789 if (c == '(') {
790 read();
791 token = Token.OPEN_PAREN;
792 return token;
793 }
794 if (c == ')') {
795 read();
796 token = Token.CLOSE_PAREN;
797 return token;
798 }
799 if (c == '{') {
800 read();
801 skipWhitespaces();
802 token = Token.OPEN_BRACE;
803 return token;
804 }
805 if (c == '}') {
806 read();
807 token = Token.CLOSE_BRACE;
808 return token;
809 }
810 if (c == '[') {
811 read();
812 token = Token.OPEN_BRACKET;
813 return token;
814 }
815 if (c == ']') {
816 read();
817 token = Token.CLOSE_BRACKET;
818 return token;
819 }
820 if (c == '$') {
821 read();
822 token = Token.DOLLAR;
823 return token;
824 }
825 if (c == '@') {
826 if (posix) {
827 throw lexerException("gawk @ syntax is not supported in POSIX mode.");
828 }
829 read();
830 if (c == '/') {
831 read();
832 readRegexp();
833 token = Token.TYPED_REGEXP;
834 return token;
835 }
836 if (Character.isJavaIdentifierStart(c)) {
837 while (Character.isJavaIdentifierPart(c)) {
838 read();
839 }
840 if (c == ':') {
841 read();
842 if (c != ':') {
843 throw lexerException("Namespace separator must be two colons (::).");
844 }
845 read();
846 if (!Character.isJavaIdentifierStart(c)) {
847 throw lexerException("A namespace-qualified name requires an identifier after ::.");
848 }
849 read();
850 while (Character.isJavaIdentifierPart(c)) {
851 read();
852 }
853 }
854 String atWord = text.toString();
855 if ("@include".equals(atWord)) {
856 token = Token.DIRECTIVE_INCLUDE;
857 return token;
858 }
859 if ("@namespace".equals(atWord)) {
860 token = Token.DIRECTIVE_NAMESPACE;
861 return token;
862 }
863 if ("@load".equals(atWord)) {
864 token = Token.DIRECTIVE_UNSUPPORTED;
865 return token;
866 }
867 pendingIndirectIdentifier = atWord.substring(1);
868 validateIndirectIdentifier(pendingIndirectIdentifier);
869 token = Token.INDIRECT;
870 return token;
871 }
872 token = Token.INDIRECT;
873 return token;
874 }
875 if (c == '~') {
876 read();
877 token = Token.MATCHES;
878 return token;
879 }
880 if (c == '?') {
881 read();
882 skipWhitespaces();
883 token = Token.QUESTION_MARK;
884 return token;
885 }
886 if (c == ':') {
887 read();
888 skipWhitespaces();
889 token = Token.COLON;
890 return token;
891 }
892 if (c == '&') {
893 read();
894 if (c == '&') {
895 read();
896 skipWhitespaces();
897 token = Token.AND;
898 return token;
899 }
900 throw lexerException("use && for logical and");
901 }
902 if (c == '|') {
903 read();
904 if (c == '|') {
905 read();
906 skipWhitespaces();
907 token = Token.OR;
908 return token;
909 }
910 token = Token.PIPE;
911 return token;
912 }
913 if (c == '=') {
914 read();
915 if (c == '=') {
916 read();
917 token = Token.EQ;
918 return token;
919 }
920 token = Token.EQUALS;
921 return token;
922 }
923 if (c == '+') {
924 read();
925 if (c == '=') {
926 read();
927 token = Token.PLUS_EQ;
928 return token;
929 } else if (c == '+') {
930 read();
931 token = Token.INC;
932 return token;
933 }
934 token = Token.PLUS;
935 return token;
936 }
937 if (c == '-') {
938 read();
939 if (c == '=') {
940 read();
941 token = Token.MINUS_EQ;
942 return token;
943 } else if (c == '-') {
944 read();
945 token = Token.DEC;
946 return token;
947 }
948 token = Token.MINUS;
949 return token;
950 }
951 if (c == '*') {
952 read();
953 if (c == '=') {
954 read();
955 token = Token.MULT_EQ;
956 return token;
957 } else if (c == '*') {
958 read();
959 if (c == '=') {
960 read();
961 token = Token.POW_EQ;
962 return token;
963 }
964 token = Token.POW;
965 return token;
966 }
967 token = Token.MULT;
968 return token;
969 }
970 if (c == '/') {
971 read();
972 if (c == '=') {
973 read();
974 token = Token.DIV_EQ;
975 return token;
976 }
977 token = Token.DIVIDE;
978 return token;
979 }
980 if (c == '%') {
981 read();
982 if (c == '=') {
983 read();
984 token = Token.MOD_EQ;
985 return token;
986 }
987 token = Token.MOD;
988 return token;
989 }
990 if (c == '^') {
991 read();
992 if (c == '=') {
993 read();
994 token = Token.POW_EQ;
995 return token;
996 }
997 token = Token.POW;
998 return token;
999 }
1000 if (c == '>') {
1001 read();
1002 if (c == '=') {
1003 read();
1004 token = Token.GE;
1005 return token;
1006 } else if (c == '>') {
1007 read();
1008 token = Token.APPEND;
1009 return token;
1010 }
1011 token = Token.GT;
1012 return token;
1013 }
1014 if (c == '<') {
1015 read();
1016 if (c == '=') {
1017 read();
1018 token = Token.LE;
1019 return token;
1020 }
1021 token = Token.LT;
1022 return token;
1023 }
1024 if (c == '!') {
1025 read();
1026 if (c == '=') {
1027 read();
1028 token = Token.NE;
1029 return token;
1030 } else if (c == '~') {
1031 read();
1032 token = Token.NOT_MATCHES;
1033 return token;
1034 }
1035 token = Token.NOT;
1036 return token;
1037 }
1038
1039 if (c == '.') {
1040
1041 read();
1042 boolean hit = false;
1043 while (c > 0 && Character.isDigit(c)) {
1044 hit = true;
1045 read();
1046 }
1047 if (!hit) {
1048 throw lexerException("Decimal point encountered with no values on either side.");
1049 }
1050 readExponent();
1051 token = Token.DOUBLE;
1052 return token;
1053 }
1054
1055 if (Character.isDigit(c)) {
1056
1057 read();
1058 while (c > 0) {
1059 if (c == '.') {
1060
1061 read();
1062 while (c > 0 && Character.isDigit(c)) {
1063 read();
1064 }
1065 readExponent();
1066 token = Token.DOUBLE;
1067 return token;
1068 } else if (Character.isDigit(c)) {
1069
1070 read();
1071 } else {
1072 break;
1073 }
1074 }
1075 if (readExponent()) {
1076 token = Token.DOUBLE;
1077 return token;
1078 }
1079
1080 token = Token.INTEGER;
1081 return token;
1082 }
1083
1084 if (Character.isJavaIdentifierStart(c)) {
1085 read();
1086 while (Character.isJavaIdentifierPart(c)) {
1087 read();
1088 }
1089 if (c == ':') {
1090 read();
1091 if (c != ':') {
1092 text.setLength(text.length() - 1);
1093 pendingColon = true;
1094 } else {
1095 if (posix) {
1096 throw lexerException("gawk namespace syntax is not supported in POSIX mode.");
1097 }
1098 read();
1099 if (!Character.isJavaIdentifierStart(c)) {
1100 throw lexerException("A namespace-qualified name requires an identifier after ::.");
1101 }
1102 read();
1103 while (Character.isJavaIdentifierPart(c)) {
1104 read();
1105 }
1106 if (c == ':') {
1107 read();
1108 if (c == ':') {
1109 throw lexerException("A namespace-qualified name may contain only one :: separator.");
1110 }
1111 text.setLength(text.length() - 1);
1112 pendingColon = true;
1113 }
1114 }
1115 }
1116
1117
1118 String sourceIdentifier = text.toString();
1119 int namespaceSeparator = sourceIdentifier.indexOf("::");
1120 if (namespaceSeparator >= 0) {
1121 String namespaceComponent = sourceIdentifier.substring(namespaceSeparator + 2);
1122 if (KEYWORDS.containsKey(namespaceComponent)
1123 || BuiltinFunction.of(namespaceComponent) != null) {
1124 throw lexerException(
1125 "Reserved word cannot be used after a namespace separator: "
1126 + sourceIdentifier);
1127 }
1128 }
1129 String lookupIdentifier = awkNamespaceComponent(sourceIdentifier);
1130 boolean awkNamespaceIdentifier = isAwkNamespaceIdentifier(sourceIdentifier);
1131 boolean unqualifiedIdentifier = namespaceSeparator < 0;
1132 if (awkNamespaceIdentifier && extensions.get(lookupIdentifier) != null) {
1133 text.setLength(0);
1134 text.append(lookupIdentifier);
1135 token = Token.EXTENSION;
1136 return token;
1137 }
1138 Token kwToken = KEYWORDS.get(sourceIdentifier);
1139 if (kwToken != null && !isDisabledKeyword(kwToken)) {
1140 token = kwToken;
1141 return token;
1142 }
1143 if ((unqualifiedIdentifier || awkNamespaceIdentifier)
1144 && BuiltinFunction.of(lookupIdentifier) != null) {
1145 text.setLength(0);
1146 text.append(lookupIdentifier);
1147 token = Token.BUILTIN_FUNC_NAME;
1148 return token;
1149 }
1150 if (c == '(' && !pendingColon) {
1151 token = Token.FUNC_ID;
1152 return token;
1153 } else {
1154 token = Token.ID;
1155 return token;
1156 }
1157 }
1158
1159 if (c == ';') {
1160 read();
1161 while (c == ' ' || c == '\t' || c == '\n' || c == '#') {
1162 if (c == '\n') {
1163 break;
1164 }
1165 if (c == '#') {
1166 while (c >= 0 && c != '\n') {
1167 read();
1168 }
1169 if (c == '\n') {
1170 read();
1171 }
1172 } else {
1173 read();
1174 }
1175 }
1176 token = Token.SEMICOLON;
1177 return token;
1178 }
1179
1180 if (c == '\n') {
1181 read();
1182 while (c == ' ' || c == '\t' || c == '#' || c == '\n') {
1183 if (c == '#') {
1184 while (c >= 0 && c != '\n') {
1185 read();
1186 }
1187 }
1188 read();
1189 }
1190 token = Token.NEWLINE;
1191 return token;
1192 }
1193
1194 if (c == '"') {
1195
1196 read();
1197 readString();
1198 token = Token.STRING;
1199 return token;
1200 }
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214 throw lexerException("Invalid character (" + c + "): " + ((char) c));
1215 }
1216
1217
1218 private void terminator() throws IOException {
1219
1220 if (!optTerminator()) {
1221 throw parserException("Expecting statement terminator. Got " + token.name() + ": " + text);
1222 }
1223 }
1224
1225 private boolean optTerminator() throws IOException {
1226 if (optNewline()) {
1227 return true;
1228 } else if (token == Token.EOF || token == Token.CLOSE_BRACE) {
1229 return true;
1230 } else if (token == Token.SEMICOLON) {
1231 lexer();
1232 return true;
1233 } else {
1234
1235 return false;
1236 }
1237 }
1238
1239 private boolean optNewline() throws IOException {
1240 if (token == Token.NEWLINE) {
1241 lexer();
1242 return true;
1243 } else {
1244 return false;
1245 }
1246 }
1247
1248
1249
1250
1251 AST SCRIPT() throws IOException {
1252 AST rl;
1253 if (token != Token.EOF) {
1254 rl = RULE_LIST();
1255 } else {
1256 rl = null;
1257 }
1258 lexer(Token.EOF);
1259 return rl;
1260 }
1261
1262
1263
1264 AST EXPRESSION_TO_EVALUATE() throws IOException {
1265 AST exprAst = token != Token.EOF ? TERNARY_EXPRESSION(null, true, false, true) : null;
1266 lexer(Token.EOF);
1267 return new ExpressionToEvaluateAst(exprAst);
1268 }
1269
1270
1271 AST RULE_LIST() throws IOException {
1272 optNewline();
1273 AST ruleOrFunction = null;
1274 if (token == Token.DIRECTIVE_INCLUDE) {
1275 INCLUDE_DIRECTIVE();
1276 return RULE_LIST();
1277 } else if (token == Token.DIRECTIVE_NAMESPACE) {
1278 NAMESPACE_DIRECTIVE();
1279 return RULE_LIST();
1280 } else if (token == Token.DIRECTIVE_UNSUPPORTED) {
1281 throw parserException("Unsupported gawk directive: " + text);
1282 } else if (token == Token.KW_FUNCTION) {
1283 ruleOrFunction = FUNCTION();
1284 } else if (token != Token.EOF) {
1285 ruleOrFunction = RULE();
1286 } else {
1287 return null;
1288 }
1289 optTerminator();
1290 return new RuleListAst(ruleOrFunction, RULE_LIST());
1291 }
1292
1293 private void NAMESPACE_DIRECTIVE() throws IOException {
1294 lexer();
1295 if (token != Token.STRING) {
1296 throw parserException("@namespace requires a quoted namespace name.");
1297 }
1298 String namespace = string.toString();
1299 validateNamespace(namespace);
1300 currentNamespace = namespace;
1301 lexer();
1302 terminator();
1303 }
1304
1305 private void INCLUDE_DIRECTIVE() throws IOException {
1306 lexer();
1307 if (token != Token.STRING) {
1308 throw parserException("@include requires a quoted file name.");
1309 }
1310 if (!sourceIncludeAllowed) {
1311 throw new AwkSandboxException("@include is disabled in sandbox mode");
1312 }
1313 String includeName = string.toString();
1314 boolean includeTerminatedByEndOfSource = validateIncludeTerminator();
1315 Path includePath = resolveIncludePath(includeName);
1316 if (topLevelSourcePaths.contains(includePath)) {
1317 throw parserException(
1318 "Cannot include a top-level program source: " + includeName);
1319 }
1320 if (!includedSourcePaths.add(includePath)) {
1321 lexer();
1322 if (!includeTerminatedByEndOfSource) {
1323 terminator();
1324 }
1325 return;
1326 }
1327 includedSourceStack.push(new SourceState(currentScriptSource, reader, c, currentNamespace));
1328 currentScriptSource = new ScriptSource(
1329 includePath.toString(),
1330 Files.newBufferedReader(includePath, StandardCharsets.UTF_8));
1331 reader = new LineNumberReader(currentScriptSource.getReader());
1332 currentNamespace = "awk";
1333 c = reader.read();
1334 while (c == '\r') {
1335 c = reader.read();
1336 }
1337 advancePastEndOfSource();
1338 lexer();
1339 }
1340
1341 private boolean validateIncludeTerminator() throws IOException {
1342 while (c == ' ' || c == '\t') {
1343 read();
1344 }
1345 if (c == '#') {
1346 while (c >= 0 && c != '\n') {
1347 read();
1348 }
1349 }
1350 if (c >= 0 && c != '\n' && c != ';') {
1351 throw parserException("@include must be followed by a newline, semicolon, or end of file.");
1352 }
1353 return c < 0;
1354 }
1355
1356 private void validateNamespace(String namespace) {
1357 if (namespace == null
1358 || namespace.isEmpty()
1359 || !Character.isJavaIdentifierStart(namespace.charAt(0))) {
1360 throw parserException("Invalid gawk namespace name: " + namespace);
1361 }
1362 for (int i = 1; i < namespace.length(); i++) {
1363 if (!Character.isJavaIdentifierPart(namespace.charAt(i))) {
1364 throw parserException("Invalid gawk namespace name: " + namespace);
1365 }
1366 }
1367 if (KEYWORDS.containsKey(namespace)
1368 || BuiltinFunction.of(namespace) != null
1369 || extensions.containsKey(namespace)) {
1370 throw parserException("Reserved identifier cannot be used as a gawk namespace: " + namespace);
1371 }
1372 }
1373
1374 private void validateIndirectIdentifier(String identifier) throws LexerException {
1375 int separator = identifier.indexOf("::");
1376 String namespace = separator < 0 ? "awk" : identifier.substring(0, separator);
1377 String component = separator < 0 ? identifier : identifier.substring(separator + 2);
1378 if (KEYWORDS.containsKey(component)
1379 || BuiltinFunction.of(component) != null
1380 || ("awk".equals(namespace) && extensions.containsKey(component))) {
1381 throw lexerException("Reserved identifier cannot be used as an indirect-call selector: " + identifier);
1382 }
1383 }
1384
1385 private Path resolveIncludePath(String includeName) {
1386 Path requested = Paths.get(includeName);
1387 List<Path> candidates = new ArrayList<Path>();
1388 if (requested.isAbsolute()) {
1389 candidates.add(requested);
1390 } else {
1391 if (!ScriptSource.DESCRIPTION_COMMAND_LINE_SCRIPT.equals(currentScriptSource.getDescription())) {
1392 try {
1393 Path sourcePath = Paths.get(currentScriptSource.getDescription());
1394 Path parent = sourcePath.toAbsolutePath().normalize().getParent();
1395 if (parent != null) {
1396 candidates.add(parent.resolve(requested));
1397 }
1398 } catch (InvalidPathException ignored) {
1399
1400
1401 }
1402 }
1403 String awkPath = System.getenv("AWKPATH");
1404 if (awkPath != null) {
1405 for (String entry : awkPath.split(java.util.regex.Pattern.quote(File.pathSeparator), -1)) {
1406 candidates.add(Paths.get(entry.isEmpty() ? "." : entry).resolve(requested));
1407 }
1408 }
1409 candidates.add(requested);
1410 }
1411 for (Path candidate : candidates) {
1412 Path normalized = candidate.toAbsolutePath().normalize();
1413 if (Files.isRegularFile(normalized)) {
1414 try {
1415 return normalized.toRealPath();
1416 } catch (IOException ignored) {
1417
1418
1419 }
1420 }
1421 }
1422 throw parserException("Cannot find @include file: " + includeName);
1423 }
1424
1425
1426 AST FUNCTION() throws IOException {
1427 expectKeyword("function");
1428 String functionName;
1429 if (token == Token.FUNC_ID || token == Token.ID) {
1430 functionName = qualifyGlobalIdentifier(text.toString());
1431 lexer();
1432 } else {
1433 throw parserException("Expecting function name. Got " + token.name() + ": " + text);
1434 }
1435 symbolTable.setFunctionName(functionName);
1436 lexer(Token.OPEN_PAREN);
1437 AST formalParamList;
1438 if (token == Token.CLOSE_PAREN) {
1439 formalParamList = null;
1440 } else {
1441 formalParamList = FORMAL_PARAM_LIST(functionName);
1442 }
1443 lexer(Token.CLOSE_PAREN);
1444 optNewline();
1445
1446 lexer(Token.OPEN_BRACE);
1447 AST functionBlock = STATEMENT_LIST();
1448 lexer(Token.CLOSE_BRACE);
1449 symbolTable.clearFunctionName(functionName);
1450 return symbolTable.addFunctionDef(functionName, formalParamList, functionBlock);
1451 }
1452
1453
1454 AST FORMAL_PARAM_LIST(String functionName) throws IOException {
1455 if (token == Token.ID) {
1456 String id = text.toString();
1457 symbolTable.addFunctionParameter(functionName, id);
1458 lexer();
1459 if (token == Token.COMMA) {
1460 lexer();
1461 optNewline();
1462 AST rest = FORMAL_PARAM_LIST(functionName);
1463 if (rest == null) {
1464 throw parserException("Cannot terminate a formal parameter list with a comma.");
1465 } else {
1466 return new FunctionDefParamListAst(id, rest);
1467 }
1468 } else {
1469 return new FunctionDefParamListAst(id, null);
1470 }
1471 } else {
1472 return null;
1473 }
1474 }
1475
1476
1477 AST RULE() throws IOException {
1478 AST optExpr;
1479 AST optStmts;
1480 if (token == Token.KW_BEGIN) {
1481 lexer();
1482 optExpr = symbolTable.addBEGIN();
1483 } else if (token == Token.KW_END) {
1484 lexer();
1485 optExpr = symbolTable.addEND();
1486 } else if (token == Token.KW_BEGINFILE) {
1487 lexer();
1488 optExpr = symbolTable.addBEGINFILE();
1489 } else if (token == Token.KW_ENDFILE) {
1490 lexer();
1491 optExpr = symbolTable.addENDFILE();
1492 } else if (token != Token.OPEN_BRACE && token != Token.SEMICOLON && token != Token.NEWLINE && token != Token.EOF) {
1493
1494 optExpr = ASSIGNMENT_EXPRESSION(null, true, true, false);
1495
1496 if (token == Token.COMMA) {
1497 lexer();
1498 optNewline();
1499
1500 optExpr = new ConditionPairAst(
1501 optExpr,
1502 ASSIGNMENT_EXPRESSION(null, true, true, false));
1503 }
1504 } else {
1505 optExpr = null;
1506 }
1507 if (token == Token.OPEN_BRACE) {
1508 lexer();
1509 optStmts = STATEMENT_LIST();
1510 lexer(Token.CLOSE_BRACE);
1511 } else {
1512 optStmts = null;
1513 }
1514 return new RuleAst(optExpr, optStmts);
1515 }
1516
1517
1518 private AST STATEMENT_LIST() throws IOException {
1519
1520 optNewline();
1521 if (token == Token.CLOSE_BRACE || token == Token.EOF) {
1522 return null;
1523 }
1524 AST stmt;
1525 if (token == Token.OPEN_BRACE) {
1526 lexer();
1527 stmt = STATEMENT_LIST();
1528 lexer(Token.CLOSE_BRACE);
1529 } else {
1530 if (token == Token.SEMICOLON) {
1531
1532
1533
1534 lexer();
1535 return STATEMENT_LIST();
1536 } else {
1537 stmt = STATEMENT();
1538 }
1539 }
1540
1541 AST rest = STATEMENT_LIST();
1542 if (rest == null) {
1543 return stmt;
1544 } else if (stmt == null) {
1545 return rest;
1546 } else {
1547 return new StatementListAst(stmt, rest);
1548 }
1549 }
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561 AST EXPRESSION_LIST(boolean allowComparisons, boolean allowInKeyword) throws IOException {
1562
1563
1564
1565 int lineNo = currentSourceLineNumber();
1566
1567
1568
1569 AST expr = ASSIGNMENT_EXPRESSION(null, allowComparisons, allowInKeyword, false);
1570
1571
1572
1573 if (token == Token.COMMA) {
1574 lexer();
1575 optNewline();
1576
1577 AST rest = EXPRESSION_LIST(allowComparisons, allowInKeyword);
1578 return new FunctionCallParamListAst(lineNo, expr, rest);
1579 }
1580
1581
1582 return new FunctionCallParamListAst(lineNo, expr, null);
1583 }
1584
1585 private AST ASSIGNMENT_EXPRESSION(
1586 AST left,
1587 boolean allowComparison,
1588 boolean allowInKeyword,
1589 boolean allowMultidimIndices)
1590 throws IOException {
1591
1592
1593
1594 int startLineNo = currentSourceLineNumber();
1595 AST ternaryExpression = TERNARY_EXPRESSION(left, allowComparison, allowInKeyword, allowMultidimIndices);
1596 AST result = ternaryExpression;
1597 if (token == Token.EQUALS
1598 || token == Token.PLUS_EQ
1599 || token == Token.MINUS_EQ
1600 || token == Token.MULT_EQ
1601 || token == Token.DIV_EQ
1602 || token == Token.MOD_EQ
1603 || token == Token.POW_EQ) {
1604 Token op = token;
1605 String txt = text.toString();
1606 lexer();
1607
1608
1609
1610 AST assignmentExpression = ASSIGNMENT_EXPRESSION(
1611 null,
1612 allowComparison,
1613 allowInKeyword,
1614 false);
1615 result = new AssignmentExpressionAst(ternaryExpression, op, txt, assignmentExpression);
1616 }
1617
1618
1619
1620
1621 if (allowMultidimIndices && token == Token.COMMA) {
1622 lexer();
1623 optNewline();
1624 int restLineNo = currentSourceLineNumber();
1625 AST rest = ASSIGNMENT_EXPRESSION(null, allowComparison, allowInKeyword, true);
1626 if (rest instanceof ArrayIndexAst) {
1627 return new ArrayIndexAst(startLineNo, result, rest);
1628 }
1629 return new ArrayIndexAst(startLineNo, result, new ArrayIndexAst(restLineNo, rest, null));
1630 }
1631 return result;
1632 }
1633
1634
1635 private AST TERNARY_EXPRESSION(
1636 AST left,
1637 boolean allowComparison,
1638 boolean allowInKeyword,
1639 boolean allowMultidimIndices)
1640 throws IOException {
1641 AST condition = LOGICAL_OR_EXPRESSION(left, allowComparison, allowInKeyword, allowMultidimIndices);
1642 if (token == Token.QUESTION_MARK) {
1643 lexer();
1644 AST trueBlock = TERNARY_EXPRESSION(null, allowComparison, allowInKeyword, allowMultidimIndices);
1645 lexer(Token.COLON);
1646 AST falseBlock = TERNARY_EXPRESSION(null, allowComparison, allowInKeyword, allowMultidimIndices);
1647 return new TernaryExpressionAst(condition, trueBlock, falseBlock);
1648 }
1649 return condition;
1650 }
1651
1652
1653 private AST LOGICAL_OR_EXPRESSION(
1654 AST left,
1655 boolean allowComparison,
1656 boolean allowInKeyword,
1657 boolean allowMultidimIndices)
1658 throws IOException {
1659 AST result = LOGICAL_AND_EXPRESSION(left, allowComparison, allowInKeyword, allowMultidimIndices);
1660 while (token == Token.OR) {
1661 Token op = token;
1662 String txt = text.toString();
1663 lexer();
1664 AST rhs = LOGICAL_OR_EXPRESSION(null, allowComparison, allowInKeyword, allowMultidimIndices);
1665 result = new LogicalExpressionAst(result, op, txt, rhs);
1666 }
1667 return result;
1668 }
1669
1670
1671 private AST LOGICAL_AND_EXPRESSION(
1672 AST left,
1673 boolean allowComparison,
1674 boolean allowInKeyword,
1675 boolean allowMultidimIndices)
1676 throws IOException {
1677 AST result = IN_EXPRESSION(left, allowComparison, allowInKeyword, allowMultidimIndices);
1678 while (token == Token.AND) {
1679 Token op = token;
1680 String txt = text.toString();
1681 lexer();
1682 AST rhs = LOGICAL_AND_EXPRESSION(null, allowComparison, allowInKeyword, allowMultidimIndices);
1683 result = new LogicalExpressionAst(result, op, txt, rhs);
1684 }
1685 return result;
1686 }
1687
1688
1689
1690
1691
1692
1693
1694 private AST IN_EXPRESSION(
1695 AST left,
1696 boolean allowComparison,
1697 boolean allowInKeyword,
1698 boolean allowMultidimIndices)
1699 throws IOException {
1700 AST result = MATCHING_EXPRESSION(left, allowComparison, allowInKeyword, allowMultidimIndices);
1701 if (allowInKeyword && token == Token.KW_IN) {
1702 lexer();
1703 result = new InExpressionAst(
1704 result,
1705 IN_EXPRESSION(null, allowComparison, allowInKeyword, allowMultidimIndices));
1706 }
1707 return result;
1708 }
1709
1710
1711 private AST MATCHING_EXPRESSION(
1712 AST left,
1713 boolean allowComparison,
1714 boolean allowInKeyword,
1715 boolean allowMultidimIndices)
1716 throws IOException {
1717 AST result = COMPARISON_EXPRESSION(left, allowComparison, allowInKeyword, allowMultidimIndices);
1718 while (token == Token.MATCHES || token == Token.NOT_MATCHES) {
1719 Token op = token;
1720 String txt = text.toString();
1721 lexer();
1722 AST rhs = MATCHING_EXPRESSION(null, allowComparison, allowInKeyword, allowMultidimIndices);
1723 result = new ComparisonExpressionAst(result, op, txt, rhs);
1724 }
1725 return result;
1726 }
1727
1728
1729
1730
1731 private AST COMPARISON_EXPRESSION(
1732 AST left,
1733 boolean allowComparison,
1734 boolean allowInKeyword,
1735 boolean allowMultidimIndices)
1736 throws IOException {
1737 AST result = CONCAT_EXPRESSION(left, allowComparison, allowInKeyword, allowMultidimIndices);
1738 if (token == Token.EQ
1739 || token == Token.GE
1740 || token == Token.LT
1741 || token == Token.LE
1742 || token == Token.NE
1743 || (token == Token.GT && allowComparison)) {
1744 Token op = token;
1745 String txt = text.toString();
1746 lexer();
1747 AST rhs = COMPARISON_EXPRESSION(null, allowComparison, allowInKeyword, allowMultidimIndices);
1748 return new ComparisonExpressionAst(result, op, txt, rhs);
1749 } else if (allowComparison && token == Token.PIPE) {
1750 lexer();
1751 return GETLINE_EXPRESSION(result, allowComparison, allowInKeyword);
1752 }
1753
1754 return result;
1755 }
1756
1757
1758 private AST CONCAT_EXPRESSION(
1759 AST left,
1760 boolean allowComparison,
1761 boolean allowInKeyword,
1762 boolean allowMultidimIndices)
1763 throws IOException {
1764 AST result = EXPRESSION(left, allowComparison, allowInKeyword, allowMultidimIndices);
1765 if (token == Token.INTEGER
1766 || token == Token.DOUBLE
1767 || token == Token.OPEN_PAREN
1768 || token == Token.FUNC_ID
1769 || token == Token.INC
1770 || token == Token.DEC
1771 || token == Token.ID
1772 || token == Token.STRING
1773 || token == Token.DOLLAR
1774 || token == Token.BUILTIN_FUNC_NAME
1775 || token == Token.EXTENSION) {
1776 return new ConcatExpressionAst(
1777 result,
1778 CONCAT_EXPRESSION(null, allowComparison, allowInKeyword, allowMultidimIndices));
1779 }
1780 return result;
1781 }
1782
1783
1784 private AST EXPRESSION(
1785 AST left,
1786 boolean allowComparison,
1787 boolean allowInKeyword,
1788 boolean allowMultidimIndices)
1789 throws IOException {
1790 AST result = TERM(left, allowComparison, allowInKeyword, allowMultidimIndices);
1791 while (token == Token.PLUS || token == Token.MINUS) {
1792 Token op = token;
1793 String txt = text.toString();
1794 lexer();
1795 AST nextTerm = TERM(null, allowComparison, allowInKeyword, allowMultidimIndices);
1796 result = new BinaryExpressionAst(result, op, txt, nextTerm);
1797 }
1798 return result;
1799 }
1800
1801
1802 private AST TERM(
1803 AST left,
1804 boolean allowComparison,
1805 boolean allowInKeyword,
1806 boolean allowMultidimIndices)
1807 throws IOException {
1808 AST result = (left == null) ? UNARY_FACTOR(allowComparison, allowInKeyword, allowMultidimIndices) : left;
1809 while (token == Token.MULT || token == Token.DIVIDE || token == Token.MOD) {
1810 Token op = token;
1811 String txt = text.toString();
1812 lexer();
1813 AST nextUnaryFactor = UNARY_FACTOR(allowComparison, allowInKeyword, allowMultidimIndices);
1814 result = new BinaryExpressionAst(result, op, txt, nextUnaryFactor);
1815 }
1816 return result;
1817 }
1818
1819
1820 AST UNARY_FACTOR(boolean allowComparison, boolean allowInKeyword, boolean allowMultidimIndices)
1821 throws IOException {
1822 if (token == Token.NOT) {
1823 lexer();
1824 return new NotExpressionAst(POWER_FACTOR(null, allowComparison, allowInKeyword, allowMultidimIndices));
1825 } else if (token == Token.MINUS) {
1826 lexer();
1827 return new NegativeExpressionAst(
1828 POWER_FACTOR(null, allowComparison, allowInKeyword, allowMultidimIndices));
1829 } else if (token == Token.PLUS) {
1830 lexer();
1831 return new UnaryPlusExpressionAst(
1832 POWER_FACTOR(null, allowComparison, allowInKeyword, allowMultidimIndices));
1833 } else {
1834 return POWER_FACTOR(null, allowComparison, allowInKeyword, allowMultidimIndices);
1835 }
1836 }
1837
1838
1839 private AST POWER_FACTOR(
1840 AST left,
1841 boolean allowComparison,
1842 boolean allowInKeyword,
1843 boolean allowMultidimIndices)
1844 throws IOException {
1845 AST result = (left == null) ? FACTOR_FOR_INCDEC(allowComparison, allowInKeyword, allowMultidimIndices) : left;
1846 if (token == Token.POW) {
1847 Token op = token;
1848 String txt = text.toString();
1849 lexer();
1850 AST rhs = POWER_FACTOR(null, allowComparison, allowInKeyword, allowMultidimIndices);
1851 return new BinaryExpressionAst(result, op, txt, rhs);
1852 }
1853 return result;
1854 }
1855
1856
1857
1858
1859 private boolean isLvalue(AST ast) {
1860 return (ast instanceof IDAst) || (ast instanceof ArrayReferenceAst) || (ast instanceof DollarExpressionAst);
1861 }
1862
1863 AST FACTOR_FOR_INCDEC(boolean allowComparison, boolean allowInKeyword, boolean allowMultidimIndices)
1864 throws IOException {
1865 boolean preInc = false;
1866 boolean preDec = false;
1867 boolean postInc = false;
1868 boolean postDec = false;
1869 if (token == Token.INC) {
1870 preInc = true;
1871 lexer();
1872 } else if (token == Token.DEC) {
1873 preDec = true;
1874 lexer();
1875 }
1876
1877 AST factorAst = FACTOR(allowComparison, allowInKeyword, allowMultidimIndices);
1878
1879 if ((preInc || preDec) && !isLvalue(factorAst)) {
1880 throw parserException("Cannot pre inc/dec a non-lvalue");
1881 }
1882
1883
1884
1885
1886 if (isLvalue(factorAst) && !preInc && !preDec) {
1887 if (token == Token.INC) {
1888 postInc = true;
1889 lexer();
1890 } else if (token == Token.DEC) {
1891 postDec = true;
1892 lexer();
1893 }
1894 }
1895
1896 if ((preInc || preDec) && (postInc || postDec)) {
1897 throw parserException("Cannot do pre inc/dec Token.AND post inc/dec.");
1898 }
1899
1900 if (preInc) {
1901 return new PreIncAst(factorAst);
1902 } else if (preDec) {
1903 return new PreDecAst(factorAst);
1904 } else if (postInc) {
1905 return new PostIncAst(factorAst);
1906 } else if (postDec) {
1907 return new PostDecAst(factorAst);
1908 } else {
1909 return factorAst;
1910 }
1911 }
1912
1913
1914
1915
1916
1917 AST FACTOR(boolean allowComparison, boolean allowInKeyword, boolean allowMultidimIndices) throws IOException {
1918 if (token == Token.OPEN_PAREN) {
1919 lexer();
1920
1921 AST assignmentExpression = ASSIGNMENT_EXPRESSION(null, true, allowInKeyword, true);
1922 lexer(Token.CLOSE_PAREN);
1923 if (assignmentExpression instanceof ArrayIndexAst && !(allowInKeyword && token == Token.KW_IN)) {
1924
1925
1926
1927
1928 throw parserException("A parenthesized expression list is only valid before 'in'.");
1929 }
1930 return assignmentExpression;
1931 } else if (token == Token.INTEGER) {
1932 AST integer = symbolTable.addINTEGER(text.toString());
1933 lexer();
1934 return integer;
1935 } else if (token == Token.DOUBLE) {
1936 AST dbl = symbolTable.addDOUBLE(text.toString());
1937 lexer();
1938 return dbl;
1939 } else if (token == Token.STRING) {
1940 AST str = symbolTable.addSTRING(string.toString());
1941 lexer();
1942 return str;
1943 } else if (token == Token.INDIRECT) {
1944 return INDIRECT_FUNCTION_CALL(allowInKeyword);
1945 } else if (token == Token.TYPED_REGEXP) {
1946 AST regexpAst = symbolTable.addTYPED_REGEXP(regexp.toString());
1947 lexer();
1948 return regexpAst;
1949 } else if (token == Token.KW_GETLINE) {
1950 return GETLINE_EXPRESSION(null, allowComparison, allowInKeyword);
1951 } else if (token == Token.DIVIDE || token == Token.DIV_EQ) {
1952 readRegexp();
1953 if (token == Token.DIV_EQ) {
1954 regexp.insert(0, '=');
1955 }
1956 AST regexpAst = symbolTable.addREGEXP(regexp.toString());
1957 lexer();
1958 return regexpAst;
1959 } else {
1960 if (token == Token.DOLLAR) {
1961 lexer();
1962 if (token == Token.INC || token == Token.DEC) {
1963 return new DollarExpressionAst(
1964 FACTOR_FOR_INCDEC(allowComparison, allowInKeyword, allowMultidimIndices));
1965 }
1966 if (token == Token.NOT || token == Token.MINUS || token == Token.PLUS) {
1967 return new DollarExpressionAst(UNARY_FACTOR(allowComparison, allowInKeyword, allowMultidimIndices));
1968 }
1969 return new DollarExpressionAst(FACTOR(allowComparison, allowInKeyword, allowMultidimIndices));
1970 }
1971 return SYMBOL(allowComparison, allowInKeyword);
1972 }
1973 }
1974
1975 private AST INDIRECT_FUNCTION_CALL(boolean allowInKeyword) throws IOException {
1976 lexer();
1977 if (token != Token.ID) {
1978 throw parserException("An indirect function call requires a variable name after @.");
1979 }
1980 AST functionNameAst = symbolTable.getID(text.toString());
1981 lexer();
1982 lexer(Token.OPEN_PAREN);
1983 AST params = token == Token.CLOSE_PAREN ? null : EXPRESSION_LIST(true, allowInKeyword);
1984 lexer(Token.CLOSE_PAREN);
1985 return new IndirectFunctionCallAst(functionNameAst, params);
1986 }
1987
1988
1989 AST SYMBOL(boolean allowComparison, boolean allowInKeyword) throws IOException {
1990 if (token != Token.ID && token != Token.FUNC_ID && token != Token.BUILTIN_FUNC_NAME && token != Token.EXTENSION) {
1991 throw parserException("Expecting an Token.ID. Got " + token.name() + ": " + text);
1992 }
1993 Token idToken = token;
1994 String id = text.toString();
1995 lexer();
1996
1997 if (idToken == Token.EXTENSION) {
1998 String extensionKeyword = id;
1999 ExtensionFunction function = extensions.get(extensionKeyword);
2000 if (function == null) {
2001 throw parserException("Unknown extension keyword: " + extensionKeyword);
2002 }
2003 AST params;
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030 if (token == Token.OPEN_PAREN) {
2031 lexer();
2032 if (token == Token.CLOSE_PAREN) {
2033 params = null;
2034 } else {
2035 params = EXPRESSION_LIST(true, allowInKeyword);
2036 }
2037 lexer(Token.CLOSE_PAREN);
2038 } else {
2039
2040
2041
2042
2043
2044
2045
2046
2047 params = null;
2048 }
2049
2050 return new ExtensionAst(function, params, extensionCallLineNumber(params));
2051 } else if (idToken == Token.FUNC_ID || idToken == Token.BUILTIN_FUNC_NAME) {
2052 AST params;
2053
2054 if (id.equals("length")) {
2055 if (token == Token.OPEN_PAREN) {
2056 lexer();
2057 if (token == Token.CLOSE_PAREN) {
2058 params = null;
2059 } else {
2060 params = EXPRESSION_LIST(true, allowInKeyword);
2061 }
2062 lexer(Token.CLOSE_PAREN);
2063 } else {
2064 params = null;
2065 }
2066 } else {
2067 lexer(Token.OPEN_PAREN);
2068 if (token == Token.CLOSE_PAREN) {
2069 params = null;
2070 } else {
2071 params = EXPRESSION_LIST(true, allowInKeyword);
2072 }
2073 lexer(Token.CLOSE_PAREN);
2074 }
2075 if (idToken == Token.BUILTIN_FUNC_NAME) {
2076 return new BuiltinFunctionCallAst(id, params);
2077 } else {
2078 return symbolTable.addFunctionCall(id, params);
2079 }
2080 }
2081 if (token == Token.OPEN_BRACKET) {
2082 int arrayReferenceLineNo = currentSourceLineNumber();
2083 lexer();
2084 AST idxAst = ARRAY_INDEX(true, allowInKeyword);
2085 lexer(Token.CLOSE_BRACKET);
2086 AST arrayReference = symbolTable.addArrayReference(id, idxAst, arrayReferenceLineNo);
2087 if (posix && token == Token.OPEN_BRACKET) {
2088 throw parserException("Use [a,b,c,...] instead of [a][b][c]... for multi-dimensional arrays.");
2089 }
2090 while (!posix && token == Token.OPEN_BRACKET) {
2091 int nestedArrayReferenceLineNo = currentSourceLineNumber();
2092 lexer();
2093 idxAst = ARRAY_INDEX(true, allowInKeyword);
2094 lexer(Token.CLOSE_BRACKET);
2095 arrayReference = new ArrayReferenceAst(nestedArrayReferenceLineNo, arrayReference, idxAst);
2096 }
2097 return arrayReference;
2098 }
2099 return symbolTable.addID(id);
2100 }
2101
2102
2103 AST ARRAY_INDEX(boolean allowComparison, boolean allowInKeyword) throws IOException {
2104
2105
2106
2107 int lineNo = currentSourceLineNumber();
2108 AST exprAst = ASSIGNMENT_EXPRESSION(null, allowComparison, allowInKeyword, false);
2109 if (token == Token.COMMA) {
2110 optNewline();
2111 lexer();
2112 return new ArrayIndexAst(lineNo, exprAst, ARRAY_INDEX(allowComparison, allowInKeyword));
2113 } else {
2114 return new ArrayIndexAst(lineNo, exprAst, null);
2115 }
2116 }
2117
2118
2119
2120
2121
2122
2123
2124
2125 AST STATEMENT() throws IOException {
2126 if (token == Token.OPEN_BRACE) {
2127 lexer();
2128 AST lst = STATEMENT_LIST();
2129 lexer(Token.CLOSE_BRACE);
2130 return lst;
2131 }
2132 AST stmt;
2133 if (token == Token.KW_IF) {
2134 stmt = IF_STATEMENT();
2135 } else if (token == Token.KW_WHILE) {
2136 stmt = WHILE_STATEMENT();
2137 } else if (token == Token.KW_FOR) {
2138 stmt = FOR_STATEMENT();
2139 } else {
2140 if (token == Token.KW_DO) {
2141 stmt = DO_STATEMENT();
2142 } else if (token == Token.KW_RETURN) {
2143 stmt = RETURN_STATEMENT();
2144 } else if (token == Token.KW_EXIT) {
2145 stmt = EXIT_STATEMENT();
2146 } else if (token == Token.KW_DELETE) {
2147 stmt = DELETE_STATEMENT();
2148 } else if (token == Token.KW_PRINT) {
2149 stmt = PRINT_STATEMENT();
2150 } else if (token == Token.KW_PRINTF) {
2151 stmt = PRINTF_STATEMENT();
2152 } else if (token == Token.KW_NEXT) {
2153 stmt = NEXT_STATEMENT();
2154 } else if (token == Token.KW_NEXTFILE) {
2155 stmt = NEXTFILE_STATEMENT();
2156 } else if (token == Token.KW_CONTINUE) {
2157 stmt = CONTINUE_STATEMENT();
2158 } else if (token == Token.KW_BREAK) {
2159 stmt = BREAK_STATEMENT();
2160 } else {
2161 stmt = EXPRESSION_STATEMENT(true);
2162 }
2163 terminator();
2164 return stmt;
2165 }
2166
2167
2168 return stmt;
2169 }
2170
2171 AST EXPRESSION_STATEMENT(boolean allowInKeyword) throws IOException {
2172
2173
2174
2175
2176 AST exprAst = ASSIGNMENT_EXPRESSION(null, true, allowInKeyword, false);
2177 return new ExpressionStatementAst(exprAst);
2178 }
2179
2180 AST IF_STATEMENT() throws IOException {
2181 expectKeyword("if");
2182 lexer(Token.OPEN_PAREN);
2183 AST expr = ASSIGNMENT_EXPRESSION(null, true, true, false);
2184
2185
2186
2187 lexer(Token.CLOSE_PAREN);
2188
2189
2190
2191
2192
2193
2194 optNewline();
2195 AST b1;
2196 if (token == Token.SEMICOLON) {
2197 lexer();
2198
2199 optNewline();
2200 b1 = null;
2201 } else {
2202 b1 = BLOCK_OR_STMT();
2203 }
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215 optNewline();
2216 if (token == Token.KW_ELSE) {
2217 lexer();
2218 optNewline();
2219 AST b2 = BLOCK_OR_STMT();
2220 return new IfStatementAst(expr, b1, b2);
2221 } else {
2222 AST ifAst = new IfStatementAst(expr, b1, null);
2223 return ifAst;
2224 }
2225 }
2226
2227 AST BREAK_STATEMENT() throws IOException {
2228 expectKeyword("break");
2229 return new BreakStatementAst();
2230 }
2231
2232 AST BLOCK_OR_STMT() throws IOException {
2233
2234 return BLOCK_OR_STMT(false);
2235 }
2236
2237 AST BLOCK_OR_STMT(boolean requireTerminator) throws IOException {
2238 optNewline();
2239 AST block;
2240
2241 if (token == Token.OPEN_BRACE) {
2242 lexer();
2243 block = STATEMENT_LIST();
2244 lexer(Token.CLOSE_BRACE);
2245 return block;
2246 } else if (token == Token.SEMICOLON) {
2247 block = null;
2248 } else {
2249 block = STATEMENT();
2250
2251 }
2252 if (requireTerminator) {
2253 terminator();
2254 }
2255 return block;
2256 }
2257
2258 AST WHILE_STATEMENT() throws IOException {
2259 expectKeyword("while");
2260 lexer(Token.OPEN_PAREN);
2261 AST expr = ASSIGNMENT_EXPRESSION(null, true, true, false);
2262
2263
2264
2265 lexer(Token.CLOSE_PAREN);
2266 AST block = BLOCK_OR_STMT();
2267 return new WhileStatementAst(expr, block);
2268 }
2269
2270 AST FOR_STATEMENT() throws IOException {
2271 expectKeyword("for");
2272 AST expr1 = null;
2273 AST expr2 = null;
2274 AST expr3 = null;
2275 lexer(Token.OPEN_PAREN);
2276 expr1 = OPT_SIMPLE_STATEMENT(false);
2277
2278
2279 if (token == Token.KW_IN) {
2280 if (expr1.ast1 == null || expr1.ast2 != null) {
2281 throw parserException("Invalid expression prior to 'in' statement. Got : " + expr1);
2282 }
2283 expr1 = expr1.ast1;
2284
2285 if (!(expr1 instanceof IDAst)) {
2286 throw parserException("Expecting an Token.ID for 'in' statement. Got : " + expr1);
2287 }
2288
2289 lexer();
2290 if (token != Token.ID) {
2291 throw parserException(
2292 "Expecting an array or subarray for 'in' statement. Got " + token.name() + ": " + text);
2293 }
2294 AST arrayAst = SYMBOL(true, true);
2295
2296 lexer(Token.CLOSE_PAREN);
2297 AST block = BLOCK_OR_STMT();
2298 return new ForInStatementAst(expr1, arrayAst, block);
2299 }
2300
2301 if (token == Token.SEMICOLON) {
2302 lexer();
2303 optNewline();
2304 } else {
2305 throw parserException("Expecting ;. Got " + token.name() + ": " + text);
2306 }
2307 if (token != Token.SEMICOLON) {
2308 expr2 = ASSIGNMENT_EXPRESSION(null, true, true, false);
2309
2310
2311 }
2312 if (token == Token.SEMICOLON) {
2313 lexer();
2314 optNewline();
2315 } else {
2316 throw parserException("Expecting ;. Got " + token.name() + ": " + text);
2317 }
2318 if (token != Token.CLOSE_PAREN) {
2319 expr3 = OPT_SIMPLE_STATEMENT(true);
2320 }
2321 lexer(Token.CLOSE_PAREN);
2322 AST block = BLOCK_OR_STMT();
2323 return new ForStatementAst(expr1, expr2, expr3, block);
2324 }
2325
2326 AST OPT_SIMPLE_STATEMENT(boolean allowInKeyword) throws IOException {
2327 if (token == Token.SEMICOLON) {
2328 return null;
2329 } else if (token == Token.KW_DELETE) {
2330 return DELETE_STATEMENT();
2331 } else if (token == Token.KW_PRINT) {
2332 return PRINT_STATEMENT();
2333 } else if (token == Token.KW_PRINTF) {
2334 return PRINTF_STATEMENT();
2335 } else {
2336
2337 return EXPRESSION_STATEMENT(allowInKeyword);
2338 }
2339 }
2340
2341 AST DELETE_STATEMENT() throws IOException {
2342 boolean parens = c == '(';
2343 expectKeyword("delete");
2344 if (parens) {
2345 lexer();
2346 }
2347 AST symbolAst = SYMBOL(true, true);
2348 if (parens) {
2349 lexer(Token.CLOSE_PAREN);
2350 }
2351
2352 return new DeleteStatementAst(symbolAst);
2353 }
2354
2355 private static final class ParsedPrintStatement {
2356
2357 private final AST funcParams;
2358 private final Token outputToken;
2359 private final AST outputExpr;
2360 private final boolean parenthesized;
2361
2362 ParsedPrintStatement(AST funcParams, Token outputToken, AST outputExpr, boolean parenthesized) {
2363 this.funcParams = funcParams;
2364 this.outputToken = outputToken;
2365 this.outputExpr = outputExpr;
2366 this.parenthesized = parenthesized;
2367 }
2368
2369 public AST getFuncParams() {
2370 return funcParams;
2371 }
2372
2373 public Token getOutputToken() {
2374 return outputToken;
2375 }
2376
2377 public AST getOutputExpr() {
2378 return outputExpr;
2379 }
2380
2381 public boolean isParenthesized() {
2382 return parenthesized;
2383 }
2384 }
2385
2386 private ParsedPrintStatement parsePrintStatement() throws IOException {
2387 AST funcParams;
2388 Token outputToken;
2389 AST outputExpr;
2390 boolean parenthesized = false;
2391
2392 if (token == Token.OPEN_PAREN) {
2393 parenthesized = true;
2394 funcParams = parseParenthesizedPrintArguments();
2395 } else if (endsPrintArgumentList(token)) {
2396 funcParams = null;
2397 } else {
2398 funcParams = EXPRESSION_LIST(false, true);
2399 }
2400
2401 if (token == Token.GT || token == Token.APPEND || token == Token.PIPE) {
2402 outputToken = token;
2403 lexer();
2404 outputExpr = ASSIGNMENT_EXPRESSION(null, true, true, false);
2405 } else {
2406 outputToken = null;
2407 outputExpr = null;
2408 }
2409
2410 return new ParsedPrintStatement(funcParams, outputToken, outputExpr, parenthesized);
2411 }
2412
2413 private boolean endsPrintArgumentList(Token candidate) {
2414 return candidate == Token.NEWLINE
2415 || candidate == Token.SEMICOLON
2416 || candidate == Token.CLOSE_BRACE
2417 || candidate == Token.CLOSE_PAREN
2418 || candidate == Token.GT
2419 || candidate == Token.APPEND
2420 || candidate == Token.PIPE
2421 || candidate == Token.EOF;
2422 }
2423
2424 private AST parseParenthesizedPrintArguments() throws IOException {
2425 lexer();
2426 if (token == Token.CLOSE_PAREN) {
2427 lexer();
2428 return null;
2429 }
2430
2431 AST params = EXPRESSION_LIST(true, true);
2432 lexer(Token.CLOSE_PAREN);
2433
2434 if (params instanceof FunctionCallParamListAst) {
2435 FunctionCallParamListAst paramList = (FunctionCallParamListAst) params;
2436 boolean singleExpression = paramList.getAst2() == null;
2437
2438
2439 boolean membershipKey = !singleExpression && token == Token.KW_IN;
2440 if ((singleExpression || membershipKey) && !endsPrintArgumentList(token)) {
2441 AST continuedExpression = ASSIGNMENT_EXPRESSION(
2442 singleExpression ? paramList.getAst1() : toMultidimIndex(paramList),
2443 false,
2444 true,
2445 false);
2446 if (token == Token.COMMA) {
2447
2448
2449 lexer();
2450 optNewline();
2451 return new FunctionCallParamListAst(continuedExpression, EXPRESSION_LIST(false, true));
2452 }
2453 return new FunctionCallParamListAst(continuedExpression, null);
2454 }
2455 }
2456
2457 return params;
2458 }
2459
2460
2461
2462 private AST toMultidimIndex(FunctionCallParamListAst list) {
2463 AST rest = list.getAst2() == null ? null : toMultidimIndex((FunctionCallParamListAst) list.getAst2());
2464 return new ArrayIndexAst(list.getLineNo(), list.getAst1(), rest);
2465 }
2466
2467 AST PRINT_STATEMENT() throws IOException {
2468 expectKeyword("print");
2469 ParsedPrintStatement parsedPrintStatement = parsePrintStatement();
2470
2471 AST params = parsedPrintStatement.getFuncParams();
2472 if (parsedPrintStatement.isParenthesized()
2473 && token == Token.QUESTION_MARK
2474 && params instanceof FunctionCallParamListAst
2475 && ((FunctionCallParamListAst) params).getAst2() == null) {
2476 AST condExpr = ((FunctionCallParamListAst) params).getAst1();
2477 lexer();
2478 AST trueBlock = TERNARY_EXPRESSION(null, true, true, true);
2479 lexer(Token.COLON);
2480 AST falseBlock = TERNARY_EXPRESSION(null, true, true, true);
2481 params = new FunctionCallParamListAst(
2482 new TernaryExpressionAst(condExpr, trueBlock, falseBlock),
2483 null);
2484 }
2485
2486 return new PrintAst(
2487 params,
2488 parsedPrintStatement.getOutputToken(),
2489 parsedPrintStatement.getOutputExpr(),
2490 parsedPrintStatement.isParenthesized());
2491 }
2492
2493 AST PRINTF_STATEMENT() throws IOException {
2494 expectKeyword("printf");
2495 ParsedPrintStatement parsedPrintStatement = parsePrintStatement();
2496
2497 AST params = parsedPrintStatement.getFuncParams();
2498 if (parsedPrintStatement.isParenthesized()
2499 && token == Token.QUESTION_MARK
2500 && params instanceof FunctionCallParamListAst
2501 && ((FunctionCallParamListAst) params).getAst2() == null) {
2502 AST condExpr = ((FunctionCallParamListAst) params).getAst1();
2503 lexer();
2504 AST trueBlock = TERNARY_EXPRESSION(null, true, true, true);
2505 lexer(Token.COLON);
2506 AST falseBlock = TERNARY_EXPRESSION(null, true, true, true);
2507 params = new FunctionCallParamListAst(
2508 new TernaryExpressionAst(condExpr, trueBlock, falseBlock),
2509 null);
2510 }
2511
2512 return new PrintfAst(
2513 params,
2514 parsedPrintStatement.getOutputToken(),
2515 parsedPrintStatement.getOutputExpr());
2516 }
2517
2518 AST GETLINE_EXPRESSION(AST pipeExpr, boolean allowComparison, boolean allowInKeyword) throws IOException {
2519 expectKeyword("getline");
2520 AST lvalue = LVALUE(allowComparison, allowInKeyword);
2521 if (token == Token.LT) {
2522 lexer();
2523 AST assignmentExpr = ASSIGNMENT_EXPRESSION(null, allowComparison, allowInKeyword, false);
2524
2525
2526 return pipeExpr == null ?
2527 new GetlineAst(null, lvalue, assignmentExpr) : new GetlineAst(pipeExpr, lvalue, assignmentExpr);
2528 } else {
2529 return pipeExpr == null ? new GetlineAst(null, lvalue, null) : new GetlineAst(pipeExpr, lvalue, null);
2530 }
2531 }
2532
2533 AST LVALUE(boolean allowComparison, boolean allowInKeyword) throws IOException {
2534
2535 if (token == Token.DOLLAR) {
2536 return FACTOR(allowComparison, allowInKeyword, false);
2537 }
2538 if (token == Token.ID) {
2539 return FACTOR(allowComparison, allowInKeyword, false);
2540 }
2541 return null;
2542 }
2543
2544 AST DO_STATEMENT() throws IOException {
2545 expectKeyword("do");
2546 optNewline();
2547 AST block = BLOCK_OR_STMT();
2548 if (token == Token.SEMICOLON) {
2549 lexer();
2550 }
2551 optNewline();
2552 expectKeyword("while");
2553 lexer(Token.OPEN_PAREN);
2554 AST expr = ASSIGNMENT_EXPRESSION(null, true, true, false);
2555
2556
2557
2558 lexer(Token.CLOSE_PAREN);
2559 return new DoStatementAst(block, expr);
2560 }
2561
2562 AST RETURN_STATEMENT() throws IOException {
2563 expectKeyword("return");
2564 if (token == Token.SEMICOLON || token == Token.NEWLINE || token == Token.CLOSE_BRACE) {
2565 return new ReturnStatementAst(null);
2566 } else {
2567 return new ReturnStatementAst(ASSIGNMENT_EXPRESSION(null, true, true, false));
2568
2569
2570
2571 }
2572 }
2573
2574 AST EXIT_STATEMENT() throws IOException {
2575 expectKeyword("exit");
2576 if (token == Token.SEMICOLON || token == Token.NEWLINE || token == Token.CLOSE_BRACE) {
2577 return new ExitStatementAst(null);
2578 } else {
2579 return new ExitStatementAst(ASSIGNMENT_EXPRESSION(null, true, true, false));
2580
2581
2582
2583 }
2584 }
2585
2586 AST NEXT_STATEMENT() throws IOException {
2587 expectKeyword("next");
2588 return new NextStatementAst();
2589 }
2590
2591 AST NEXTFILE_STATEMENT() throws IOException {
2592 expectKeyword("nextfile");
2593 nextfileEncountered = true;
2594 return new NextfileStatementAst();
2595 }
2596
2597 AST CONTINUE_STATEMENT() throws IOException {
2598 expectKeyword("continue");
2599 return new ContinueStatementAst();
2600 }
2601
2602
2603
2604 private void expectKeyword(String keyword) throws IOException {
2605 if (token == KEYWORDS.get(keyword)) {
2606 lexer();
2607 } else {
2608 throw parserException("Expecting " + keyword + ". Got " + token.name() + ": " + text);
2609 }
2610 }
2611
2612 private void populateArrayOperandTuples(
2613 AST arrayAst,
2614 AwkTuples tuples,
2615 boolean createIfMissing,
2616 String errorMessage) {
2617 if (arrayAst instanceof IDAst) {
2618 IDAst idAst = (IDAst) arrayAst;
2619 idAst.setArray(true);
2620 if (isJrtManagedSpecialName(idAst.id)) {
2621 idAst.populateTuples(tuples);
2622 } else {
2623 tuples.dereference(idAst.offset, true, idAst.isGlobal);
2624 }
2625 return;
2626 }
2627 if (arrayAst instanceof ArrayReferenceAst) {
2628 if (posix) {
2629 arrayAst.throwSemanticException(errorMessage);
2630 }
2631 ((ArrayReferenceAst) arrayAst).populateArrayValueTuples(tuples, createIfMissing);
2632 return;
2633 }
2634 arrayAst.throwSemanticException(errorMessage);
2635 }
2636
2637 private int populateActualParameters(
2638 AwkTuples tuples,
2639 FunctionCallParamListAst params,
2640 Set<Integer> arrayParameterIndexes,
2641 Set<Integer> rawValueParameterIndexes,
2642 Set<Integer> literalRegexpIndexes,
2643 int parameterIndex) {
2644 if (params == null) {
2645 return 0;
2646 }
2647 if (arrayParameterIndexes.contains(Integer.valueOf(parameterIndex))) {
2648 populateArrayOperandTuples(
2649 params.getAst1(),
2650 tuples,
2651 true,
2652 "Parameter position " + (parameterIndex + 1) + " must be an array or subarray.");
2653 } else if (literalRegexpIndexes.contains(Integer.valueOf(parameterIndex))) {
2654 populateRawRegexpParameterTuples(params.getAst1(), tuples);
2655 } else if (rawValueParameterIndexes.contains(Integer.valueOf(parameterIndex))) {
2656 populateRawValueTuples(params.getAst1(), tuples);
2657 } else {
2658 params.getAst1().populateTuples(tuples);
2659 }
2660 if (params.getAst2() == null) {
2661 return 1;
2662 }
2663 return 1 + populateActualParameters(
2664 tuples,
2665 (FunctionCallParamListAst) params.getAst2(),
2666 arrayParameterIndexes,
2667 rawValueParameterIndexes,
2668 literalRegexpIndexes,
2669 parameterIndex + 1);
2670 }
2671
2672 private int populateActualParameters(
2673 AwkTuples tuples,
2674 FunctionCallParamListAst params,
2675 int... literalRegexpIndexesParam) {
2676 Set<Integer> literalRegexpIndexes = new HashSet<Integer>();
2677 for (int idx : literalRegexpIndexesParam) {
2678 literalRegexpIndexes.add(Integer.valueOf(idx));
2679 }
2680 return populateActualParameters(
2681 tuples,
2682 params,
2683 Collections.<Integer>emptySet(),
2684 Collections.<Integer>emptySet(),
2685 literalRegexpIndexes,
2686 0);
2687 }
2688
2689 private int populateActualParametersUpTo(
2690 AwkTuples tuples,
2691 FunctionCallParamListAst params,
2692 int parameterIndex,
2693 int maxParameterCount) {
2694
2695
2696
2697
2698
2699
2700 if (params == null) {
2701 return 0;
2702 }
2703 if (parameterIndex >= maxParameterCount) {
2704
2705
2706
2707 populateRawValueTuples(params.getAst1(), tuples);
2708 tuples.pop();
2709 populateActualParametersUpTo(
2710 tuples,
2711 (FunctionCallParamListAst) params.getAst2(),
2712 parameterIndex + 1,
2713 maxParameterCount);
2714 return 0;
2715 }
2716 AST argument = params.getAst1();
2717 if (argument instanceof IDAst
2718 && !isJrtManagedSpecialName(((IDAst) argument).id)) {
2719 IDAst idAst = (IDAst) argument;
2720 tuples.pushIndirectArgument(idAst.offset, idAst.isGlobal);
2721 } else if (argument instanceof ArrayReferenceAst) {
2722 ((ArrayReferenceAst) argument).populateTargetReferenceTuples(tuples);
2723 tuples.pushIndirectArrayArgument();
2724 } else {
2725 argument.populateTuples(tuples);
2726 }
2727 if (params.getAst2() == null) {
2728 return 1;
2729 }
2730 return 1 + populateActualParametersUpTo(
2731 tuples,
2732 (FunctionCallParamListAst) params.getAst2(),
2733 parameterIndex + 1,
2734 maxParameterCount);
2735 }
2736
2737 private void populateRawValueTuples(AST valueAst, AwkTuples tuples) {
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748 if (valueAst instanceof IDAst) {
2749 IDAst idAst = (IDAst) valueAst;
2750 if (isJrtManagedSpecialName(idAst.id)) {
2751 idAst.populateTuples(tuples);
2752 } else {
2753 tuples.peekDereference(idAst.offset, idAst.isGlobal);
2754 }
2755 return;
2756 }
2757 valueAst.populateTuples(tuples);
2758 }
2759
2760 private void populateRawRegexpParameterTuples(AST valueAst, AwkTuples tuples) {
2761 if (valueAst instanceof RegexpAst) {
2762 ((RegexpAst) valueAst).populateRawRegexpTuples(tuples);
2763 return;
2764 }
2765 valueAst.populateTuples(tuples);
2766 }
2767
2768 private int populateIndirectActualParameters(
2769 AwkTuples tuples,
2770 FunctionCallParamListAst params) {
2771 if (params == null) {
2772 return 0;
2773 }
2774 AST argument = params.getAst1();
2775 if (argument instanceof IDAst
2776 && !isJrtManagedSpecialName(((IDAst) argument).id)) {
2777 IDAst idAst = (IDAst) argument;
2778 tuples.pushIndirectArgument(idAst.offset, idAst.isGlobal);
2779 } else if (argument instanceof ArrayReferenceAst) {
2780 ((ArrayReferenceAst) argument).populateTargetReferenceTuples(tuples);
2781 tuples.pushIndirectArrayArgument();
2782 } else {
2783 argument.populateTuples(tuples);
2784 }
2785 return 1 + populateIndirectActualParameters(
2786 tuples,
2787 (FunctionCallParamListAst) params.getAst2());
2788 }
2789
2790 private Set<Integer> collectArrayParameterIndexes(FunctionDefAst functionDefAst) {
2791 Set<Integer> arrayIndexes = new HashSet<Integer>();
2792 FunctionDefParamListAst fPtr = (FunctionDefParamListAst) functionDefAst.getAst1();
2793 int index = 0;
2794 while (fPtr != null) {
2795 IDAst fparam = symbolTable.getFunctionParameterIDAST(functionDefAst.id, fPtr.id);
2796 if (fparam.isArray()) {
2797 arrayIndexes.add(Integer.valueOf(index));
2798 }
2799 fPtr = (FunctionDefParamListAst) fPtr.getAst1();
2800 index++;
2801 }
2802 return arrayIndexes;
2803 }
2804
2805
2806
2807
2808 private abstract class AST extends AstNode {
2809
2810 private final String sourceDescription = currentScriptSource.getDescription();
2811
2812
2813 private final int lineNo;
2814 private AST parent;
2815 private AST ast1, ast2, ast3, ast4;
2816 private final EnumSet<AstFlag> flags = EnumSet.noneOf(AstFlag.class);
2817
2818 protected final void addFlag(AstFlag flag) {
2819 flags.add(flag);
2820 }
2821
2822 protected final boolean hasFlag(AstFlag flag) {
2823 return flags.contains(flag);
2824 }
2825
2826 protected Address breakAddress() {
2827 return null;
2828 }
2829
2830 protected Address continueAddress() {
2831 return null;
2832 }
2833
2834 protected Address nextAddress() {
2835 return null;
2836 }
2837
2838 protected Address returnAddress() {
2839 return null;
2840 }
2841
2842 protected final AST getParent() {
2843 return parent;
2844 }
2845
2846 @SuppressWarnings("unused")
2847 protected final void setParent(AST p) {
2848 parent = p;
2849 }
2850
2851 protected final AST getAst1() {
2852 return ast1;
2853 }
2854
2855 @SuppressWarnings("unused")
2856 protected final void setAst1(AST a1) {
2857 ast1 = a1;
2858 }
2859
2860 protected final AST getAst2() {
2861 return ast2;
2862 }
2863
2864 @SuppressWarnings("unused")
2865 protected final void setAst2(AST a2) {
2866 ast2 = a2;
2867 }
2868
2869 protected final AST getAst3() {
2870 return ast3;
2871 }
2872
2873 @SuppressWarnings("unused")
2874 protected final void setAst3(AST a3) {
2875 ast3 = a3;
2876 }
2877
2878 protected final AST getAst4() {
2879 return ast4;
2880 }
2881
2882 @SuppressWarnings("unused")
2883 protected final void setAst4(AST a4) {
2884 ast4 = a4;
2885 }
2886
2887 protected final AST searchFor(AstFlag flag) {
2888 AST ptr = this;
2889 while (ptr != null) {
2890 if (ptr.hasFlag(flag)) {
2891 return ptr;
2892 }
2893 ptr = ptr.parent;
2894 }
2895 return null;
2896 }
2897
2898 protected AST() {
2899 this(currentSourceLineNumber());
2900 }
2901
2902 protected AST(int lineNo) {
2903 this.lineNo = lineNo;
2904 }
2905
2906 protected int getLineNo() {
2907 return lineNo;
2908 }
2909
2910 protected String getSourceDescription() {
2911 return sourceDescription;
2912 }
2913
2914 protected String sourceBasename() {
2915
2916
2917 return new File(getSourceDescription()).getName();
2918 }
2919
2920 protected AST(AST ast1) {
2921 this(currentSourceLineNumber(), ast1);
2922 }
2923
2924 protected AST(int lineNo, AST ast1) {
2925 this(lineNo);
2926 this.ast1 = ast1;
2927
2928 if (ast1 != null) {
2929 ast1.parent = this;
2930 }
2931 }
2932
2933 protected AST(AST ast1, AST ast2) {
2934 this(currentSourceLineNumber(), ast1, ast2);
2935 }
2936
2937 protected AST(int lineNo, AST ast1, AST ast2) {
2938 this(lineNo);
2939 this.ast1 = ast1;
2940 this.ast2 = ast2;
2941
2942 if (ast1 != null) {
2943 ast1.parent = this;
2944 }
2945 if (ast2 != null) {
2946 ast2.parent = this;
2947 }
2948 }
2949
2950 protected AST(AST ast1, AST ast2, AST ast3) {
2951 this(currentSourceLineNumber(), ast1, ast2, ast3);
2952 }
2953
2954 protected AST(int lineNo, AST ast1, AST ast2, AST ast3) {
2955 this(lineNo);
2956 this.ast1 = ast1;
2957 this.ast2 = ast2;
2958 this.ast3 = ast3;
2959
2960 if (ast1 != null) {
2961 ast1.parent = this;
2962 }
2963 if (ast2 != null) {
2964 ast2.parent = this;
2965 }
2966 if (ast3 != null) {
2967 ast3.parent = this;
2968 }
2969 }
2970
2971 protected AST(AST ast1, AST ast2, AST ast3, AST ast4) {
2972 this(currentSourceLineNumber(), ast1, ast2, ast3, ast4);
2973 }
2974
2975 protected AST(int lineNo, AST ast1, AST ast2, AST ast3, AST ast4) {
2976 this(lineNo);
2977 this.ast1 = ast1;
2978 this.ast2 = ast2;
2979 this.ast3 = ast3;
2980 this.ast4 = ast4;
2981
2982 if (ast1 != null) {
2983 ast1.parent = this;
2984 }
2985 if (ast2 != null) {
2986 ast2.parent = this;
2987 }
2988 if (ast3 != null) {
2989 ast3.parent = this;
2990 }
2991 if (ast4 != null) {
2992 ast4.parent = this;
2993 }
2994 }
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006 @Override
3007 public void dump(PrintStream ps) {
3008 dump(ps, 0);
3009 }
3010
3011 private void dump(PrintStream ps, int lvl) {
3012 StringBuffer spaces = new StringBuffer();
3013 for (int i = 0; i < lvl; i++) {
3014 spaces.append(' ');
3015 }
3016 ps.println(spaces + toString());
3017 if (ast1 != null) {
3018 ast1.dump(ps, lvl + 1);
3019 }
3020 if (ast2 != null) {
3021 ast2.dump(ps, lvl + 1);
3022 }
3023 if (ast3 != null) {
3024 ast3.dump(ps, lvl + 1);
3025 }
3026 if (ast4 != null) {
3027 ast4.dump(ps, lvl + 1);
3028 }
3029 }
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040 @Override
3041 public void semanticAnalysis() {
3042 if (ast1 != null) {
3043 ast1.semanticAnalysis();
3044 }
3045 if (ast2 != null) {
3046 ast2.semanticAnalysis();
3047 }
3048 if (ast3 != null) {
3049 ast3.semanticAnalysis();
3050 }
3051 if (ast4 != null) {
3052 ast4.semanticAnalysis();
3053 }
3054 }
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070 @Override
3071 public abstract int populateTuples(AwkTuples tuples);
3072
3073 protected final void pushSourceLineNumber(AwkTuples tuples) {
3074 tuples.pushSourceLineNumber(lineNo);
3075 }
3076
3077 protected final void popSourceLineNumber(AwkTuples tuples) {
3078 tuples.popSourceLineNumber(lineNo);
3079 }
3080
3081 private boolean isBegin = isBegin();
3082
3083 @SuppressWarnings("unused")
3084 protected final boolean isBeginFlag() {
3085 return isBegin;
3086 }
3087
3088 protected final void setBeginFlag(boolean flag) {
3089 isBegin = flag;
3090 }
3091
3092 private boolean isBegin() {
3093 boolean result = isBegin;
3094 if (!result && ast1 != null) {
3095 result = ast1.isBegin();
3096 }
3097 if (!result && ast2 != null) {
3098 result = ast2.isBegin();
3099 }
3100 if (!result && ast3 != null) {
3101 result = ast3.isBegin();
3102 }
3103 if (!result && ast4 != null) {
3104 result = ast4.isBegin();
3105 }
3106 return result;
3107 }
3108
3109 private boolean isEnd = isEnd();
3110
3111 @SuppressWarnings("unused")
3112 protected final boolean isEndFlag() {
3113 return isEnd;
3114 }
3115
3116 protected final void setEndFlag(boolean flag) {
3117 isEnd = flag;
3118 }
3119
3120 private boolean isEnd() {
3121 boolean result = isEnd;
3122 if (!result && ast1 != null) {
3123 result = ast1.isEnd();
3124 }
3125 if (!result && ast2 != null) {
3126 result = ast2.isEnd();
3127 }
3128 if (!result && ast3 != null) {
3129 result = ast3.isEnd();
3130 }
3131 if (!result && getAst4() != null) {
3132 result = getAst4().isEnd();
3133 }
3134 return result;
3135 }
3136
3137 private boolean isBeginFile = isBeginFile();
3138
3139 protected final void setBeginFileFlag(boolean flag) {
3140 isBeginFile = flag;
3141 }
3142
3143 private boolean isBeginFile() {
3144 boolean result = isBeginFile;
3145 if (!result && ast1 != null) {
3146 result = ast1.isBeginFile();
3147 }
3148 if (!result && ast2 != null) {
3149 result = ast2.isBeginFile();
3150 }
3151 if (!result && ast3 != null) {
3152 result = ast3.isBeginFile();
3153 }
3154 if (!result && ast4 != null) {
3155 result = ast4.isBeginFile();
3156 }
3157 return result;
3158 }
3159
3160 private boolean isEndFile = isEndFile();
3161
3162 protected final void setEndFileFlag(boolean flag) {
3163 isEndFile = flag;
3164 }
3165
3166 private boolean isEndFile() {
3167 boolean result = isEndFile;
3168 if (!result && ast1 != null) {
3169 result = ast1.isEndFile();
3170 }
3171 if (!result && ast2 != null) {
3172 result = ast2.isEndFile();
3173 }
3174 if (!result && ast3 != null) {
3175 result = ast3.isEndFile();
3176 }
3177 if (!result && ast4 != null) {
3178 result = ast4.isEndFile();
3179 }
3180 return result;
3181 }
3182
3183 private boolean isFunction = isFunction();
3184
3185 @SuppressWarnings("unused")
3186 protected final boolean isFunctionFlag() {
3187 return isFunction;
3188 }
3189
3190 protected final void setFunctionFlag(boolean flag) {
3191 isFunction = flag;
3192 }
3193
3194 private boolean isFunction() {
3195 boolean result = isFunction;
3196 if (!result && getAst1() != null) {
3197 result = getAst1().isFunction();
3198 }
3199 if (!result && getAst2() != null) {
3200 result = getAst2().isFunction();
3201 }
3202 if (!result && getAst3() != null) {
3203 result = getAst3().isFunction();
3204 }
3205 if (!result && getAst4() != null) {
3206 result = getAst4().isFunction();
3207 }
3208 return result;
3209 }
3210
3211 public boolean isArray() {
3212 return false;
3213 }
3214
3215 public boolean isScalar() {
3216 return false;
3217 }
3218
3219
3220
3221
3222
3223 protected class SemanticException extends RuntimeException {
3224
3225 private static final long serialVersionUID = 1L;
3226
3227 SemanticException(String msg) {
3228 super(msg + " (" + sourceDescription + ":" + lineNo + ")");
3229 }
3230 }
3231
3232 protected final void throwSemanticException(String msg) {
3233 throw new SemanticException(msg);
3234 }
3235
3236 @Override
3237 public String toString() {
3238 return getClass().getName().replaceFirst(".*[$.]", "");
3239 }
3240 }
3241
3242 private abstract class ScalarExpressionAst extends AST {
3243
3244 protected ScalarExpressionAst() {
3245 super();
3246 }
3247
3248 protected ScalarExpressionAst(int lineNo) {
3249 super(lineNo);
3250 }
3251
3252 protected ScalarExpressionAst(AST a1) {
3253 super(a1);
3254 }
3255
3256 protected ScalarExpressionAst(int lineNo, AST a1) {
3257 super(lineNo, a1);
3258 }
3259
3260 protected ScalarExpressionAst(AST a1, AST a2) {
3261 super(a1, a2);
3262 }
3263
3264 protected ScalarExpressionAst(int lineNo, AST a1, AST a2) {
3265 super(lineNo, a1, a2);
3266 }
3267
3268 protected ScalarExpressionAst(AST a1, AST a2, AST a3) {
3269 super(a1, a2, a3);
3270 }
3271
3272 protected ScalarExpressionAst(int lineNo, AST a1, AST a2, AST a3) {
3273 super(lineNo, a1, a2, a3);
3274 }
3275
3276 @Override
3277 public boolean isArray() {
3278 return false;
3279 }
3280
3281 @Override
3282 public boolean isScalar() {
3283 return true;
3284 }
3285 }
3286
3287 private static boolean isRule(AST ast) {
3288 return ast != null
3289 && !ast.isBegin()
3290 && !ast.isEnd()
3291 && !ast.isBeginFile()
3292 && !ast.isEndFile()
3293 && !ast.isFunction();
3294 }
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307 @SuppressWarnings("unused")
3308 private static boolean isExtensionConditionRule(AST ast) {
3309 if (!isRule(ast)) {
3310 return false;
3311 }
3312 if (ast.getAst1() == null) {
3313 return false;
3314 }
3315
3316 if (!containsASTType(ast.getAst1(), ExtensionAst.class)) {
3317 return false;
3318 }
3319
3320 if (containsASTType(ast.getAst1(), new Class[] { FunctionCallAst.class, DollarExpressionAst.class })) {
3321 return false;
3322 }
3323
3324 return true;
3325 }
3326
3327 private static boolean containsASTType(AST ast, Class<?> cls) {
3328 return containsASTType(ast, new Class[] { cls });
3329 }
3330
3331 private static boolean containsASTType(AST ast, Class<?>[] clsArray) {
3332 if (ast == null) {
3333 return false;
3334 }
3335 for (Class<?> cls : clsArray) {
3336 if (cls.isInstance(ast)) {
3337 return true;
3338 }
3339 }
3340
3341 return containsASTType(ast.getAst1(), clsArray)
3342 || containsASTType(ast.getAst2(), clsArray)
3343 || containsASTType(ast.getAst3(), clsArray)
3344 || containsASTType(ast.getAst4(), clsArray);
3345 }
3346
3347 private Address nextAddress;
3348
3349
3350
3351
3352
3353
3354
3355 private boolean nextfileEncountered;
3356
3357 private final class RuleListAst extends AST {
3358
3359 private RuleListAst(AST rule, AST rest) {
3360 super(rule, rest);
3361 }
3362
3363 @Override
3364 public int populateTuples(AwkTuples tuples) {
3365
3366 pushSourceLineNumber(tuples);
3367
3368 nextAddress = tuples.createAddress("nextAddress");
3369
3370
3371 Address startAddress = tuples.createAddress("start address");
3372 tuples.gotoAddress(startAddress);
3373
3374 AST ptr;
3375
3376
3377 ptr = this;
3378 while (ptr != null) {
3379 if (ptr.getAst1() != null && ptr.getAst1().isFunction()) {
3380 ptr.getAst1().populateTuples(tuples);
3381 }
3382
3383 ptr = ptr.getAst2();
3384 }
3385
3386
3387 tuples.address(startAddress);
3388
3389
3390 symbolTable.getID("NR");
3391 symbolTable.getID("FNR");
3392 symbolTable.getID("NF");
3393 symbolTable.getID("FS");
3394 symbolTable.getID("RS");
3395 symbolTable.getID("OFS");
3396 symbolTable.getID("ORS");
3397 symbolTable.getID("RSTART");
3398 symbolTable.getID("RLENGTH");
3399 symbolTable.getID("FILENAME");
3400 symbolTable.getID("SUBSEP");
3401 symbolTable.getID("CONVFMT");
3402 symbolTable.getID("OFMT");
3403 IDAst environAst = symbolTable.getID("ENVIRON");
3404 IDAst argcAst = symbolTable.getID("ARGC");
3405 IDAst argvAst = symbolTable.getID("ARGV");
3406
3407
3408
3409
3410 tuples.setNumGlobals(symbolTable.numGlobals());
3411
3412
3413
3414
3415
3416
3417
3418
3419 boolean symtabActive = !posix && symbolTable.isGlobalReferenced("SYMTAB");
3420 if (environAst.isReferenced() || symtabActive) {
3421 tuples.environOffset(environAst.offset);
3422 }
3423 tuples.argcOffset(argcAst.offset);
3424 if (argvAst.isReferenced() || symtabActive) {
3425 tuples.argvOffset(argvAst.offset);
3426 }
3427
3428 if (symtabActive) {
3429 tuples.updateSymtab(symbolTable.getID("SYMTAB").offset);
3430 }
3431 if (!posix && symbolTable.isGlobalReferenced("FUNCTAB")) {
3432 tuples.updateFunctab(symbolTable.getID("FUNCTAB").offset);
3433 }
3434 tuples.beforeStartHooks();
3435
3436 Address exitAddr = tuples.createAddress("end blocks start address");
3437 tuples.setExitAddress(exitAddr);
3438
3439
3440
3441
3442 boolean hasBeginFileRules = false;
3443 boolean hasEndFileRules = false;
3444 ptr = this;
3445 while (ptr != null) {
3446 if (ptr.getAst1() != null && ptr.getAst1().isBeginFile()) {
3447 hasBeginFileRules = true;
3448 }
3449 if (ptr.getAst1() != null && ptr.getAst1().isEndFile()) {
3450 hasEndFileRules = true;
3451 }
3452 ptr = ptr.getAst2();
3453 }
3454
3455
3456
3457
3458 boolean reqInput = hasBeginFileRules || hasEndFileRules;
3459
3460
3461 ptr = this;
3462 while (!reqInput && (ptr != null)) {
3463 if (isRule(ptr.getAst1())) {
3464 reqInput = true;
3465 }
3466 ptr = ptr.getAst2();
3467 }
3468
3469
3470 ptr = this;
3471 while (!reqInput && (ptr != null)) {
3472 if (ptr.getAst1() != null && ptr.getAst1().isEnd()) {
3473 reqInput = true;
3474 }
3475 ptr = ptr.getAst2();
3476 }
3477
3478 boolean perFileScaffolding = reqInput
3479 && (hasBeginFileRules || hasEndFileRules || nextfileEncountered);
3480
3481
3482
3483
3484
3485 Address beginFileAddress = null;
3486 Address endFileAddress = null;
3487 if (perFileScaffolding) {
3488 beginFileAddress = tuples.createAddress("begin_file");
3489 endFileAddress = tuples.createAddress("end_file");
3490
3491
3492
3493
3494 if (hasBeginFileRules || hasEndFileRules) {
3495 tuples.setEndFileAddress(endFileAddress);
3496 }
3497 tuples.setNextFileAddress(beginFileAddress);
3498 }
3499
3500
3501 ptr = this;
3502
3503 while (ptr != null) {
3504 if (ptr.getAst1() != null && ptr.getAst1().isBegin()) {
3505 ptr.getAst1().populateTuples(tuples);
3506 }
3507
3508 ptr = ptr.getAst2();
3509 }
3510
3511 if (reqInput) {
3512 Address inputLoopAddress = tuples.createAddress("input_loop_address");
3513 Address noMoreInput = tuples.createAddress("no_more_input");
3514
3515 if (perFileScaffolding) {
3516
3517
3518
3519 tuples.address(beginFileAddress);
3520 tuples.nextFile(noMoreInput);
3521
3522
3523 ptr = this;
3524 while (ptr != null) {
3525 if (ptr.getAst1() != null && ptr.getAst1().isBeginFile()) {
3526 ptr.getAst1().populateTuples(tuples);
3527 }
3528 ptr = ptr.getAst2();
3529 }
3530
3531
3532
3533 tuples.address(inputLoopAddress);
3534 tuples.consumeFileInput(endFileAddress);
3535 } else {
3536 tuples.address(inputLoopAddress);
3537 tuples.consumeInput(noMoreInput);
3538 }
3539
3540
3541 ptr = this;
3542 while (ptr != null) {
3543
3544 if (isRule(ptr.getAst1())) {
3545 ptr.getAst1().populateTuples(tuples);
3546 }
3547 ptr = ptr.getAst2();
3548 }
3549
3550
3551
3552 tuples.setNextAddress(nextAddress);
3553 tuples.address(nextAddress);
3554
3555 tuples.gotoAddress(inputLoopAddress);
3556
3557 if (perFileScaffolding) {
3558
3559 tuples.address(endFileAddress);
3560 ptr = this;
3561 while (ptr != null) {
3562 if (ptr.getAst1() != null && ptr.getAst1().isEndFile()) {
3563 ptr.getAst1().populateTuples(tuples);
3564 }
3565 ptr = ptr.getAst2();
3566 }
3567
3568
3569
3570
3571 tuples.gotoAddress(beginFileAddress);
3572 }
3573
3574 tuples.address(noMoreInput);
3575
3576 tuples.nop();
3577 }
3578
3579
3580
3581 tuples.address(exitAddr);
3582 tuples.setWithinEndBlocks(true);
3583
3584
3585 ptr = this;
3586 while (ptr != null) {
3587 if (ptr.getAst1() != null && ptr.getAst1().isEnd()) {
3588 ptr.getAst1().populateTuples(tuples);
3589 }
3590 ptr = ptr.getAst2();
3591 }
3592
3593
3594
3595 tuples.nop();
3596
3597 popSourceLineNumber(tuples);
3598 return 0;
3599 }
3600 }
3601
3602 private final class ExpressionToEvaluateAst extends AST {
3603
3604 private ExpressionToEvaluateAst(AST expr) {
3605 super(expr);
3606 }
3607
3608 @Override
3609 public int populateTuples(AwkTuples tuples) {
3610
3611 pushSourceLineNumber(tuples);
3612
3613
3614 symbolTable.getID("NR");
3615 symbolTable.getID("FNR");
3616 symbolTable.getID("NF");
3617 symbolTable.getID("FS");
3618 symbolTable.getID("RS");
3619 symbolTable.getID("SUBSEP");
3620 symbolTable.getID("CONVFMT");
3621 IDAst environAst = symbolTable.getID("ENVIRON");
3622
3623
3624
3625
3626 tuples.markEvalTupleStream();
3627 tuples.setNumGlobals(symbolTable.numGlobals());
3628
3629 boolean evalSymtabActive = !posix && symbolTable.isGlobalReferenced("SYMTAB");
3630 if (environAst.isReferenced() || evalSymtabActive) {
3631 tuples.environOffset(environAst.offset);
3632 }
3633 if (evalSymtabActive) {
3634 tuples.updateSymtab(symbolTable.getID("SYMTAB").offset);
3635 }
3636 if (!posix && symbolTable.isGlobalReferenced("FUNCTAB")) {
3637 tuples.updateFunctab(symbolTable.getID("FUNCTAB").offset);
3638 }
3639 tuples.beforeStartHooks();
3640
3641 if (getAst1() != null) {
3642 getAst1().populateTuples(tuples);
3643 }
3644
3645
3646 tuples.nop();
3647
3648 popSourceLineNumber(tuples);
3649 return 0;
3650 }
3651 }
3652
3653
3654 private final class RuleAst extends AST {
3655
3656 private RuleAst(AST optExpression, AST optRule) {
3657 super(optExpression, optRule);
3658 addFlag(AstFlag.NEXTABLE);
3659 }
3660
3661 @Override
3662 public int populateTuples(AwkTuples tuples) {
3663 pushSourceLineNumber(tuples);
3664 boolean unconditionalRule = getAst1() == null
3665 || getAst1().isBegin()
3666 || getAst1().isEnd()
3667 || getAst1().isBeginFile()
3668 || getAst1().isEndFile();
3669 if (!unconditionalRule) {
3670 getAst1().populateTuples(tuples);
3671
3672 Address bypassRule = tuples.createAddress("bypassRule");
3673 tuples.ifFalse(bypassRule);
3674 populateRuleBody(tuples);
3675 tuples.address(bypassRule).nop();
3676 } else {
3677 populateRuleBody(tuples);
3678 }
3679 popSourceLineNumber(tuples);
3680 return 0;
3681 }
3682
3683 private void populateRuleBody(AwkTuples tuples) {
3684
3685 if (getAst2() == null) {
3686 if (isRule(this)) {
3687
3688 tuples.print(0);
3689 }
3690
3691
3692 } else {
3693
3694 getAst2().populateTuples(tuples);
3695 }
3696 }
3697
3698 @Override
3699 public Address nextAddress() {
3700 if (!isRule(this)) {
3701 throw new SemanticException(
3702 "`next' cannot be called from a `" + specialRuleName() + "' rule.");
3703 }
3704 if (nextAddress == null) {
3705 throw new SemanticException("Cannot call next here.");
3706 }
3707 return nextAddress;
3708 }
3709
3710
3711
3712
3713
3714 private String specialRuleName() {
3715 AST pattern = getAst1();
3716 if (pattern != null && pattern.isBegin()) {
3717 return "BEGIN";
3718 }
3719 if (pattern != null && pattern.isEnd()) {
3720 return "END";
3721 }
3722 if (pattern != null && pattern.isBeginFile()) {
3723 return "BEGINFILE";
3724 }
3725 if (pattern != null && pattern.isEndFile()) {
3726 return "ENDFILE";
3727 }
3728 return "special";
3729 }
3730 }
3731
3732 private final class IfStatementAst extends AST {
3733
3734 private IfStatementAst(AST expr, AST b1, AST b2) {
3735 super(expr, b1, b2);
3736 }
3737
3738 @Override
3739 public int populateTuples(AwkTuples tuples) {
3740 pushSourceLineNumber(tuples);
3741
3742 Address elseblock = tuples.createAddress("elseblock");
3743
3744 getAst1().populateTuples(tuples);
3745 tuples.ifFalse(elseblock);
3746 if (getAst2() != null) {
3747 getAst2().populateTuples(tuples);
3748 }
3749 if (getAst3() == null) {
3750 tuples.address(elseblock);
3751 } else {
3752 Address end = tuples.createAddress("end");
3753 tuples.gotoAddress(end);
3754 tuples.address(elseblock);
3755 getAst3().populateTuples(tuples);
3756 tuples.address(end);
3757 }
3758 popSourceLineNumber(tuples);
3759 return 0;
3760 }
3761 }
3762
3763 private final class TernaryExpressionAst extends ScalarExpressionAst {
3764
3765 private TernaryExpressionAst(AST a1, AST a2, AST a3) {
3766 super(a1, a2, a3);
3767 }
3768
3769 @Override
3770 public int populateTuples(AwkTuples tuples) {
3771 pushSourceLineNumber(tuples);
3772
3773 Address elseexpr = tuples.createAddress("elseexpr");
3774 Address endTertiary = tuples.createAddress("endTertiary");
3775
3776 getAst1().populateTuples(tuples);
3777 tuples.ifFalse(elseexpr);
3778 getAst2().populateTuples(tuples);
3779 tuples.gotoAddress(endTertiary);
3780
3781 tuples.address(elseexpr);
3782 getAst3().populateTuples(tuples);
3783 tuples.address(endTertiary);
3784
3785 popSourceLineNumber(tuples);
3786 return 1;
3787 }
3788 }
3789
3790 private final class WhileStatementAst extends AST {
3791
3792 private Address breakAddress;
3793 private Address continueAddress;
3794
3795 private WhileStatementAst(AST expr, AST block) {
3796 super(expr, block);
3797 addFlag(AstFlag.BREAKABLE);
3798 addFlag(AstFlag.CONTINUEABLE);
3799 }
3800
3801 @Override
3802 public Address breakAddress() {
3803 return breakAddress;
3804 }
3805
3806 @Override
3807 public Address continueAddress() {
3808 return continueAddress;
3809 }
3810
3811 @Override
3812 public int populateTuples(AwkTuples tuples) {
3813 pushSourceLineNumber(tuples);
3814
3815 breakAddress = tuples.createAddress("breakAddress");
3816
3817
3818 Address loop = tuples.createAddress("loop");
3819 tuples.address(loop);
3820
3821
3822 continueAddress = loop;
3823
3824
3825 getAst1().populateTuples(tuples);
3826 tuples.ifFalse(breakAddress);
3827
3828 if (getAst2() != null) {
3829 getAst2().populateTuples(tuples);
3830 }
3831
3832 tuples.gotoAddress(loop);
3833
3834 tuples.address(breakAddress);
3835
3836 popSourceLineNumber(tuples);
3837 return 0;
3838 }
3839 }
3840
3841 private final class DoStatementAst extends AST {
3842
3843 private Address breakAddress;
3844 private Address continueAddress;
3845
3846 private DoStatementAst(AST block, AST expr) {
3847 super(block, expr);
3848 addFlag(AstFlag.BREAKABLE);
3849 addFlag(AstFlag.CONTINUEABLE);
3850 }
3851
3852 @Override
3853 public Address breakAddress() {
3854 return breakAddress;
3855 }
3856
3857 @Override
3858 public Address continueAddress() {
3859 return continueAddress;
3860 }
3861
3862 @Override
3863 public int populateTuples(AwkTuples tuples) {
3864 pushSourceLineNumber(tuples);
3865
3866 breakAddress = tuples.createAddress("breakAddress");
3867 continueAddress = tuples.createAddress("continueAddress");
3868
3869
3870 Address loop = tuples.createAddress("loop");
3871 tuples.address(loop);
3872
3873 if (getAst1() != null) {
3874 getAst1().populateTuples(tuples);
3875 }
3876
3877
3878 tuples.address(continueAddress);
3879
3880
3881 getAst2().populateTuples(tuples);
3882 tuples.ifTrue(loop);
3883
3884
3885
3886 tuples.address(breakAddress);
3887
3888 popSourceLineNumber(tuples);
3889 return 0;
3890 }
3891 }
3892
3893 private final class ForStatementAst extends AST {
3894
3895 private Address breakAddress;
3896 private Address continueAddress;
3897
3898 private ForStatementAst(AST expr1, AST expr2, AST expr3, AST block) {
3899 super(expr1, expr2, expr3, block);
3900 addFlag(AstFlag.BREAKABLE);
3901 addFlag(AstFlag.CONTINUEABLE);
3902 }
3903
3904 @Override
3905 public Address breakAddress() {
3906 return breakAddress;
3907 }
3908
3909 @Override
3910 public Address continueAddress() {
3911 return continueAddress;
3912 }
3913
3914 @Override
3915 public int populateTuples(AwkTuples tuples) {
3916 pushSourceLineNumber(tuples);
3917
3918 breakAddress = tuples.createAddress("breakAddress");
3919 continueAddress = tuples.createAddress("continueAddress");
3920
3921
3922 if (getAst1() != null) {
3923 int ast1Result = getAst1().populateTuples(tuples);
3924 for (int i = 0; i < ast1Result; i++) {
3925 tuples.pop();
3926 }
3927 }
3928
3929 Address loop = tuples.createAddress("loop");
3930 tuples.address(loop);
3931
3932 if (getAst2() != null) {
3933
3934
3935 getAst2().populateTuples(tuples);
3936 tuples.ifFalse(breakAddress);
3937 }
3938
3939 if (getAst4() != null) {
3940
3941 getAst4().populateTuples(tuples);
3942 }
3943
3944
3945 tuples.address(continueAddress);
3946
3947
3948 if (getAst3() != null) {
3949 int ast3Result = getAst3().populateTuples(tuples);
3950 for (int i = 0; i < ast3Result; i++) {
3951 tuples.pop();
3952 }
3953 }
3954
3955 tuples.gotoAddress(loop);
3956
3957 tuples.address(breakAddress);
3958
3959 popSourceLineNumber(tuples);
3960 return 0;
3961 }
3962 }
3963
3964 private final class ForInStatementAst extends AST {
3965
3966 private Address breakAddress;
3967 private Address continueAddress;
3968
3969 private ForInStatementAst(AST keyIdAst, AST arrayIdAst, AST block) {
3970 super(keyIdAst, arrayIdAst, block);
3971 addFlag(AstFlag.BREAKABLE);
3972 addFlag(AstFlag.CONTINUEABLE);
3973 }
3974
3975 @Override
3976 public Address breakAddress() {
3977 return breakAddress;
3978 }
3979
3980 @Override
3981 public Address continueAddress() {
3982 return continueAddress;
3983 }
3984
3985 @Override
3986 public int populateTuples(AwkTuples tuples) {
3987 pushSourceLineNumber(tuples);
3988
3989 breakAddress = tuples.createAddress("breakAddress");
3990
3991 populateArrayOperandTuples(getAst2(), tuples, false, getAst2() + " is not an array");
3992
3993 tuples.keylist();
3994
3995
3996
3997
3998
3999 Address loop = tuples.createAddress("loop");
4000 tuples.address(loop);
4001
4002
4003 continueAddress = loop;
4004
4005
4006 tuples.dup();
4007 tuples.isEmptyList(breakAddress);
4008
4009
4010 tuples.dup();
4011 tuples.getFirstAndRemoveFromList();
4012
4013 tuples.assign(((IDAst) getAst1()).offset, ((IDAst) getAst1()).isGlobal);
4014 tuples.pop();
4015
4016 if (getAst3() != null) {
4017
4018 getAst3().populateTuples(tuples);
4019 }
4020
4021
4022 tuples.gotoAddress(loop);
4023
4024 tuples.address(breakAddress);
4025 tuples.pop();
4026
4027 popSourceLineNumber(tuples);
4028 return 0;
4029 }
4030 }
4031
4032 @SuppressWarnings("unused")
4033 private final class EmptyStatementAst extends AST {
4034
4035 private EmptyStatementAst() {
4036 super();
4037 }
4038
4039 @Override
4040 public int populateTuples(AwkTuples tuples) {
4041 pushSourceLineNumber(tuples);
4042
4043 popSourceLineNumber(tuples);
4044 return 0;
4045 }
4046 }
4047
4048
4049
4050
4051
4052
4053 private final class ExpressionStatementAst extends AST {
4054
4055 private ExpressionStatementAst(AST expr) {
4056 super(expr);
4057 }
4058
4059 @Override
4060 public int populateTuples(AwkTuples tuples) {
4061 pushSourceLineNumber(tuples);
4062 int exprCount = getAst1().populateTuples(tuples);
4063 if (exprCount == 1) {
4064 tuples.popScalar();
4065 }
4066 popSourceLineNumber(tuples);
4067 return 0;
4068 }
4069 }
4070
4071 private final class AssignmentExpressionAst extends ScalarExpressionAst {
4072
4073
4074 private Token op;
4075 private String text;
4076
4077 private AssignmentExpressionAst(AST lhs, Token op, String text, AST rhs) {
4078 super(lhs, rhs);
4079 this.op = op;
4080 this.text = text;
4081 }
4082
4083 @Override
4084 public String toString() {
4085 return super.toString() + " (" + op + "/" + text + ")";
4086 }
4087
4088 @Override
4089 public int populateTuples(AwkTuples tuples) {
4090 pushSourceLineNumber(tuples);
4091 getAst2().populateTuples(tuples);
4092 if (getAst1() instanceof IDAst) {
4093 IDAst idAst = (IDAst) getAst1();
4094 idAst.setScalar(true);
4095 boolean isSpecial = isJrtManagedSpecialName(idAst.id);
4096 if (isSpecial) {
4097
4098 switch (op) {
4099 case EQUALS:
4100 assignSpecial(tuples, idAst.id);
4101 break;
4102 case PLUS_EQ:
4103 pushSpecialThenSwap(tuples, idAst.id);
4104 tuples.add();
4105 assignSpecial(tuples, idAst.id);
4106 break;
4107 case MINUS_EQ:
4108 pushSpecialThenSwap(tuples, idAst.id);
4109 tuples.subtract();
4110 assignSpecial(tuples, idAst.id);
4111 break;
4112 case MULT_EQ:
4113 pushSpecialThenSwap(tuples, idAst.id);
4114 tuples.multiply();
4115 assignSpecial(tuples, idAst.id);
4116 break;
4117 case DIV_EQ:
4118 pushSpecialThenSwap(tuples, idAst.id);
4119 tuples.divide();
4120 assignSpecial(tuples, idAst.id);
4121 break;
4122 case MOD_EQ:
4123 pushSpecialThenSwap(tuples, idAst.id);
4124 tuples.mod();
4125 assignSpecial(tuples, idAst.id);
4126 break;
4127 case POW_EQ:
4128 pushSpecialThenSwap(tuples, idAst.id);
4129 tuples.pow();
4130 assignSpecial(tuples, idAst.id);
4131 break;
4132 default:
4133 throw new Error("Unhandled op: " + op + " / " + text);
4134 }
4135 if ("RS".equals(idAst.id)) {
4136 tuples.applyRS();
4137 }
4138 } else {
4139 if (op == Token.EQUALS) {
4140
4141
4142 tuples.assign(idAst.offset, idAst.isGlobal);
4143 } else if (op == Token.PLUS_EQ) {
4144 tuples.plusEq(idAst.offset, idAst.isGlobal);
4145 } else if (op == Token.MINUS_EQ) {
4146 tuples.minusEq(idAst.offset, idAst.isGlobal);
4147 } else if (op == Token.MULT_EQ) {
4148 tuples.multEq(idAst.offset, idAst.isGlobal);
4149 } else if (op == Token.DIV_EQ) {
4150 tuples.divEq(idAst.offset, idAst.isGlobal);
4151 } else if (op == Token.MOD_EQ) {
4152 tuples.modEq(idAst.offset, idAst.isGlobal);
4153 } else if (op == Token.POW_EQ) {
4154 tuples.powEq(idAst.offset, idAst.isGlobal);
4155 } else {
4156 throw new Error("Unhandled op: " + op + " / " + text);
4157 }
4158 if (idAst.id.equals("RS")) {
4159 tuples.applyRS();
4160 }
4161 }
4162 } else if (getAst1() instanceof ArrayReferenceAst) {
4163 ArrayReferenceAst arr = (ArrayReferenceAst) getAst1();
4164 if (arr.getAst1() instanceof IDAst) {
4165 IDAst idAst = (IDAst) arr.getAst1();
4166 idAst.setArray(true);
4167 }
4168 arr.populateTargetReferenceTuples(tuples);
4169 if (op == Token.EQUALS) {
4170 tuples.assignMapElement();
4171 } else if (op == Token.PLUS_EQ) {
4172 tuples.plusEqMapElement();
4173 } else if (op == Token.MINUS_EQ) {
4174 tuples.minusEqMapElement();
4175 } else if (op == Token.MULT_EQ) {
4176 tuples.multEqMapElement();
4177 } else if (op == Token.DIV_EQ) {
4178 tuples.divEqMapElement();
4179 } else if (op == Token.MOD_EQ) {
4180 tuples.modEqMapElement();
4181 } else if (op == Token.POW_EQ) {
4182 tuples.powEqMapElement();
4183 } else {
4184 throw new NotImplementedError("Unhandled op: " + op + " / " + text + " for arrays.");
4185 }
4186 } else if (getAst1() instanceof DollarExpressionAst) {
4187 DollarExpressionAst dollarExpr = (DollarExpressionAst) getAst1();
4188 dollarExpr.getAst1().populateTuples(tuples);
4189
4190 if (op == Token.EQUALS) {
4191 tuples.assignAsInputField();
4192 } else if (op == Token.PLUS_EQ) {
4193 tuples.plusEqInputField();
4194 } else if (op == Token.MINUS_EQ) {
4195 tuples.minusEqInputField();
4196 } else if (op == Token.MULT_EQ) {
4197 tuples.multEqInputField();
4198 } else if (op == Token.DIV_EQ) {
4199 tuples.divEqInputField();
4200 } else if (op == Token.MOD_EQ) {
4201 tuples.modEqInputField();
4202 } else if (op == Token.POW_EQ) {
4203 tuples.powEqInputField();
4204 } else {
4205 throw new NotImplementedError("Unhandled op: " + op + " / " + text + " for dollar expressions.");
4206 }
4207 } else {
4208 throw new SemanticException("Cannot perform an assignment on: " + getAst1());
4209 }
4210 popSourceLineNumber(tuples);
4211 return 1;
4212 }
4213
4214 private void pushSpecialThenSwap(AwkTuples tuples, String id) {
4215 pushSpecialVariable(tuples, id);
4216 tuples.swap();
4217 }
4218
4219 private void assignSpecial(AwkTuples tuples, String id) {
4220 assignSpecialVariable(tuples, id);
4221 }
4222 }
4223
4224 private final class InExpressionAst extends ScalarExpressionAst {
4225
4226 private InExpressionAst(AST arg, AST arr) {
4227 super(arg, arr);
4228 }
4229
4230 @Override
4231 public int populateTuples(AwkTuples tuples) {
4232 pushSourceLineNumber(tuples);
4233 if (!(getAst2() instanceof IDAst) && !(getAst2() instanceof ArrayReferenceAst)) {
4234 throw new SemanticException("Expecting an array for rhs of IN. Got an expression.");
4235 }
4236
4237
4238
4239
4240 AST keyAst = getAst1();
4241 if (keyAst instanceof ArrayIndexAst) {
4242 ArrayIndexAst indexAst = (ArrayIndexAst) keyAst;
4243 int count = indexAst.populateComponentTuples(tuples);
4244 populateArrayOperandTuples(getAst2(), tuples, false, "Expecting an array for rhs of IN. Got a scalar.");
4245
4246
4247 indexAst.pushSourceLineNumber(tuples);
4248 tuples.applySubsepUnderTop(count);
4249 indexAst.popSourceLineNumber(tuples);
4250 } else {
4251 keyAst.populateTuples(tuples);
4252 populateArrayOperandTuples(getAst2(), tuples, false, "Expecting an array for rhs of IN. Got a scalar.");
4253 tuples.applySubsepUnderTop(1);
4254 }
4255 tuples.isIn();
4256
4257 popSourceLineNumber(tuples);
4258 return 1;
4259 }
4260 }
4261
4262 private final class ComparisonExpressionAst extends ScalarExpressionAst {
4263
4264
4265
4266
4267 private Token op;
4268 private String text;
4269
4270 private ComparisonExpressionAst(AST lhs, Token op, String text, AST rhs) {
4271 super(lhs, rhs);
4272 this.op = op;
4273 this.text = text;
4274 }
4275
4276 @Override
4277 public String toString() {
4278 return super.toString() + " (" + op + "/" + text + ")";
4279 }
4280
4281 @Override
4282 public int populateTuples(AwkTuples tuples) {
4283 pushSourceLineNumber(tuples);
4284
4285 getAst1().populateTuples(tuples);
4286 if (op == Token.MATCHES || op == Token.NOT_MATCHES) {
4287 populateRawRegexpParameterTuples(getAst2(), tuples);
4288 } else {
4289 getAst2().populateTuples(tuples);
4290 }
4291
4292
4293 if (op == Token.EQ) {
4294 tuples.cmpEq();
4295 } else if (op == Token.NE) {
4296 tuples.cmpEq();
4297 tuples.not();
4298 } else if (op == Token.LT) {
4299 tuples.cmpLt();
4300 } else if (op == Token.GT) {
4301 tuples.cmpGt();
4302 } else if (op == Token.LE) {
4303 tuples.cmpGt();
4304 tuples.not();
4305 } else if (op == Token.GE) {
4306 tuples.cmpLt();
4307 tuples.not();
4308 } else if (op == Token.MATCHES) {
4309 tuples.matches();
4310 } else if (op == Token.NOT_MATCHES) {
4311 tuples.matches();
4312 tuples.not();
4313 } else {
4314 throw new Error("Unhandled op: " + op + " / " + text);
4315 }
4316
4317 popSourceLineNumber(tuples);
4318 return 1;
4319 }
4320 }
4321
4322 private final class LogicalExpressionAst extends ScalarExpressionAst {
4323
4324
4325
4326
4327 private Token op;
4328 private String text;
4329
4330 private LogicalExpressionAst(AST lhs, Token op, String text, AST rhs) {
4331 super(lhs, rhs);
4332 this.op = op;
4333 this.text = text;
4334 }
4335
4336 @Override
4337 public String toString() {
4338 return super.toString() + " (" + op + "/" + text + ")";
4339 }
4340
4341 @Override
4342 public int populateTuples(AwkTuples tuples) {
4343 pushSourceLineNumber(tuples);
4344
4345 Address end = tuples.createAddress("end");
4346 getAst1().populateTuples(tuples);
4347 tuples.dup();
4348 if (op == Token.OR) {
4349
4350 tuples.ifTrue(end);
4351 } else if (op == Token.AND) {
4352 tuples.ifFalse(end);
4353 }
4354 tuples.pop();
4355 getAst2().populateTuples(tuples);
4356 tuples.address(end);
4357
4358
4359 tuples.toNumber();
4360 popSourceLineNumber(tuples);
4361 return 1;
4362 }
4363 }
4364
4365 private final class BinaryExpressionAst extends ScalarExpressionAst {
4366
4367
4368
4369
4370 private Token op;
4371 private String text;
4372
4373 private BinaryExpressionAst(AST lhs, Token op, String text, AST rhs) {
4374 super(lhs, rhs);
4375 this.op = op;
4376 this.text = text;
4377 }
4378
4379 @Override
4380 public String toString() {
4381 return super.toString() + " (" + op + "/" + text + ")";
4382 }
4383
4384 @Override
4385 public int populateTuples(AwkTuples tuples) {
4386 pushSourceLineNumber(tuples);
4387 getAst1().populateTuples(tuples);
4388 getAst2().populateTuples(tuples);
4389 if (op == Token.PLUS) {
4390 tuples.add();
4391 } else if (op == Token.MINUS) {
4392 tuples.subtract();
4393 } else if (op == Token.MULT) {
4394 tuples.multiply();
4395 } else if (op == Token.DIVIDE) {
4396 tuples.divide();
4397 } else if (op == Token.MOD) {
4398 tuples.mod();
4399 } else if (op == Token.POW) {
4400 tuples.pow();
4401 } else {
4402 throw new Error("Unhandled op: " + op + " / " + this);
4403 }
4404 popSourceLineNumber(tuples);
4405 return 1;
4406 }
4407 }
4408
4409 private final class ConcatExpressionAst extends ScalarExpressionAst {
4410
4411 private ConcatExpressionAst(AST lhs, AST rhs) {
4412 super(lhs, rhs);
4413 }
4414
4415 @Override
4416 public int populateTuples(AwkTuples tuples) {
4417 pushSourceLineNumber(tuples);
4418 getAst1().populateTuples(tuples);
4419 getAst2().populateTuples(tuples);
4420 tuples.concat();
4421 popSourceLineNumber(tuples);
4422 return 1;
4423 }
4424 }
4425
4426 private final class NegativeExpressionAst extends ScalarExpressionAst {
4427
4428 private NegativeExpressionAst(AST expr) {
4429 super(expr);
4430 }
4431
4432 @Override
4433 public int populateTuples(AwkTuples tuples) {
4434 pushSourceLineNumber(tuples);
4435 getAst1().populateTuples(tuples);
4436 tuples.negate();
4437 popSourceLineNumber(tuples);
4438 return 1;
4439 }
4440 }
4441
4442 private final class UnaryPlusExpressionAst extends ScalarExpressionAst {
4443
4444 private UnaryPlusExpressionAst(AST expr) {
4445 super(expr);
4446 }
4447
4448 @Override
4449 public int populateTuples(AwkTuples tuples) {
4450 pushSourceLineNumber(tuples);
4451 getAst1().populateTuples(tuples);
4452 tuples.unaryPlus();
4453 popSourceLineNumber(tuples);
4454 return 1;
4455 }
4456 }
4457
4458 private final class NotExpressionAst extends ScalarExpressionAst {
4459
4460 private NotExpressionAst(AST expr) {
4461 super(expr);
4462 }
4463
4464 @Override
4465 public int populateTuples(AwkTuples tuples) {
4466 pushSourceLineNumber(tuples);
4467 getAst1().populateTuples(tuples);
4468 tuples.not();
4469 popSourceLineNumber(tuples);
4470 return 1;
4471 }
4472 }
4473
4474 private final class DollarExpressionAst extends ScalarExpressionAst {
4475
4476 private DollarExpressionAst(AST expr) {
4477 super(expr);
4478 }
4479
4480 @Override
4481 public int populateTuples(AwkTuples tuples) {
4482 pushSourceLineNumber(tuples);
4483 getAst1().populateTuples(tuples);
4484 tuples.getInputField();
4485 popSourceLineNumber(tuples);
4486 return 1;
4487 }
4488 }
4489
4490 private final class ArrayIndexAst extends ScalarExpressionAst {
4491
4492 private ArrayIndexAst(int lineNo, AST exprAst, AST next) {
4493
4494
4495 super(lineNo, exprAst, next);
4496 }
4497
4498 @Override
4499 public int populateTuples(AwkTuples tuples) {
4500 pushSourceLineNumber(tuples);
4501 int cnt = populateComponentTuples(tuples);
4502
4503
4504
4505 tuples.applySubsep(cnt);
4506 popSourceLineNumber(tuples);
4507 return 1;
4508 }
4509
4510
4511
4512
4513
4514
4515 private int populateComponentTuples(AwkTuples tuples) {
4516 AST ptr = this;
4517 int cnt = 0;
4518 while (ptr != null) {
4519 ptr.getAst1().populateTuples(tuples);
4520 ++cnt;
4521 ptr = ptr.getAst2();
4522 }
4523 return cnt;
4524 }
4525 }
4526
4527
4528 private final class StatementListAst extends AST {
4529
4530 private StatementListAst(AST statementAst, AST rest) {
4531 super(statementAst, rest);
4532 }
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543 @Override
4544 public int populateTuples(AwkTuples tuples) {
4545 pushSourceLineNumber(tuples);
4546
4547 getAst1().populateTuples(tuples);
4548 if (getAst2() != null) {
4549 getAst2().populateTuples(tuples);
4550 }
4551 popSourceLineNumber(tuples);
4552 return 0;
4553 }
4554
4555 @Override
4556 public String toString() {
4557 return super.toString() + " <" + getAst1() + ">";
4558 }
4559 }
4560
4561
4562 private final class FunctionDefAst extends AST {
4563
4564 private String id;
4565 private Address functionAddress;
4566 private Address returnAddress;
4567
4568 @Override
4569 public Address returnAddress() {
4570 return returnAddress;
4571 }
4572
4573 private FunctionDefAst(String id, AST params, AST funcBody) {
4574 super(params, funcBody);
4575 this.id = id;
4576 setFunctionFlag(true);
4577 addFlag(AstFlag.RETURNABLE);
4578 }
4579
4580 public Address getAddress() {
4581 return functionAddress;
4582 }
4583
4584 @Override
4585 public int populateTuples(AwkTuples tuples) {
4586 pushSourceLineNumber(tuples);
4587
4588 functionAddress = tuples.createAddress("function: " + id);
4589 returnAddress = tuples.createAddress("returnAddress for " + id);
4590
4591
4592
4593
4594 tuples.function(id, paramCount());
4595
4596
4597
4598
4599
4600
4601 tuples.address(functionAddress);
4602
4603
4604
4605
4606
4607 if (getAst2() != null) {
4608 getAst2().populateTuples(tuples);
4609 }
4610
4611 tuples.address(returnAddress);
4612
4613 tuples.returnFromFunction();
4614
4615
4616
4617 popSourceLineNumber(tuples);
4618 return 0;
4619 }
4620
4621 int paramCount() {
4622 AST ptr = getAst1();
4623 int count = 0;
4624 while (ptr != null) {
4625 ++count;
4626 ptr = ptr.getAst1();
4627 }
4628 return count;
4629 }
4630
4631 }
4632
4633 private final class FunctionCallAst extends ScalarExpressionAst {
4634
4635 private FunctionProxy functionProxy;
4636
4637 private FunctionCallAst(FunctionProxy functionProxy, AST params) {
4638 super(params);
4639 this.functionProxy = functionProxy;
4640 }
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656 @Override
4657 public void semanticAnalysis() throws SemanticException {
4658 if (!functionProxy.isDefined()) {
4659 throw new SemanticException("function " + functionProxy + " not defined");
4660 }
4661 }
4662
4663 @Override
4664 public int populateTuples(AwkTuples tuples) {
4665 pushSourceLineNumber(tuples);
4666 if (!functionProxy.isDefined()) {
4667 throw new SemanticException("function " + functionProxy + " not defined");
4668 }
4669 tuples.scriptThis();
4670 int actualParamCountLocal;
4671 if (getAst1() == null) {
4672 actualParamCountLocal = 0;
4673 } else {
4674 actualParamCountLocal = populateActualParametersUpTo(
4675 tuples,
4676 (FunctionCallParamListAst) getAst1(),
4677 0,
4678 functionProxy.getFunctionParamCount());
4679 }
4680 int formalParamCount = functionProxy.getFunctionParamCount();
4681
4682 if (actualParamCount() > formalParamCount) {
4683
4684 tuples.warning(extraArgumentWarning());
4685 }
4686 tuples
4687 .callFunction(
4688 functionProxy,
4689 functionProxy.getFunctionName(),
4690 formalParamCount,
4691 actualParamCountLocal);
4692 popSourceLineNumber(tuples);
4693 return 1;
4694 }
4695
4696 private int actualParamCount() {
4697 int cnt = 0;
4698 AST ptr = getAst1();
4699 while (ptr != null) {
4700 ++cnt;
4701 ptr = ptr.getAst2();
4702 }
4703 return cnt;
4704 }
4705
4706 private String extraArgumentWarning() {
4707 return String
4708 .format(
4709 "gawk: %s:%d: warning: function `%s' called with more arguments than declared",
4710 sourceBasename(),
4711 warningLineNo(),
4712 functionProxy.getFunctionName());
4713 }
4714
4715 private int warningLineNo() {
4716 if (getAst1() instanceof FunctionCallParamListAst) {
4717 AST firstParam = getAst1().getAst1();
4718 if (firstParam != null) {
4719 return firstParam.getLineNo();
4720 }
4721 }
4722 return getLineNo();
4723 }
4724
4725 }
4726
4727 private final class IndirectFunctionCallAst extends ScalarExpressionAst {
4728
4729 private IndirectFunctionCallAst(AST functionNameAst, AST params) {
4730 super(functionNameAst, params);
4731 }
4732
4733 @Override
4734 public int populateTuples(AwkTuples tuples) {
4735 pushSourceLineNumber(tuples);
4736 getAst1().populateTuples(tuples);
4737 int actualParamCount = populateIndirectActualParameters(
4738 tuples,
4739 (FunctionCallParamListAst) getAst2());
4740 tuples
4741 .indirectCall(
4742 symbolTable.indirectFunctionTargets(),
4743 extensions,
4744 actualParamCount,
4745 sourceBasename(),
4746 getLineNo());
4747 popSourceLineNumber(tuples);
4748 return 1;
4749 }
4750 }
4751
4752 private final class BuiltinFunctionCallAst extends ScalarExpressionAst {
4753
4754 private final String id;
4755 private final BuiltinFunction builtin;
4756
4757 private BuiltinFunctionCallAst(String id, AST params) {
4758 super(params);
4759 this.id = id;
4760 this.builtin = BuiltinFunction.of(id);
4761 }
4762
4763 @Override
4764 public int populateTuples(AwkTuples tuples) {
4765 pushSourceLineNumber(tuples);
4766 switch (builtin) {
4767 case SPRINTF:
4768 populateSprintfTuples(tuples);
4769 break;
4770 case CLOSE:
4771 populateOneArgumentTuples(tuples, "close");
4772 tuples.close();
4773 break;
4774 case LENGTH:
4775 populateLengthTuples(tuples);
4776 break;
4777 case SRAND:
4778 populateSrandTuples(tuples);
4779 break;
4780 case RAND:
4781 if (getAst1() != null) {
4782 throw new SemanticException("rand does not take arguments");
4783 }
4784 tuples.rand();
4785 break;
4786 case SQRT:
4787 populateArgumentsTuples(tuples, 1, "sqrt requires only 1 argument");
4788 tuples.sqrt();
4789 break;
4790 case INT:
4791 populateArgumentsTuples(tuples, 1, "int requires only 1 argument");
4792 tuples.intFunc();
4793 break;
4794 case LOG:
4795 populateArgumentsTuples(tuples, 1, "log requires only 1 argument");
4796 tuples.log();
4797 break;
4798 case EXP:
4799 populateArgumentsTuples(tuples, 1, "exp requires only 1 argument");
4800 tuples.exp();
4801 break;
4802 case SIN:
4803 populateArgumentsTuples(tuples, 1, "sin requires only 1 argument");
4804 tuples.sin();
4805 break;
4806 case COS:
4807 populateArgumentsTuples(tuples, 1, "cos requires only 1 argument");
4808 tuples.cos();
4809 break;
4810 case ATAN2:
4811 populateArgumentsTuples(tuples, 2, "atan2 requires 2 arguments");
4812 tuples.atan2();
4813 break;
4814 case MATCH:
4815 populateMatchArgumentsTuples(tuples);
4816 tuples.match();
4817 break;
4818 case INDEX:
4819 populateArgumentsTuples(tuples, 2, "index requires 2 arguments");
4820 tuples.index();
4821 break;
4822 case SUB:
4823 case GSUB:
4824 populateSubTuples(tuples, builtin == BuiltinFunction.GSUB);
4825 break;
4826 case SPLIT:
4827 populateSplitTuples(tuples);
4828 break;
4829 case SUBSTR:
4830 populateSubstrTuples(tuples);
4831 break;
4832 case TOLOWER:
4833 populateOneArgumentTuples(tuples, "tolower");
4834 tuples.tolower();
4835 break;
4836 case TOUPPER:
4837 populateOneArgumentTuples(tuples, "toupper");
4838 tuples.toupper();
4839 break;
4840 case SYSTEM:
4841 populateOneArgumentTuples(tuples, "system");
4842 tuples.system();
4843 break;
4844 default:
4845 throw new NotImplementedError("builtin: " + id);
4846 }
4847 popSourceLineNumber(tuples);
4848 return 1;
4849 }
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860 private void populateArgumentsTuples(AwkTuples tuples, int expectedCount, String errorMessage) {
4861 int ast1Result = getAst1().populateTuples(tuples);
4862 if (ast1Result != expectedCount) {
4863 throw new SemanticException(errorMessage);
4864 }
4865 }
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875 private void populateOneArgumentTuples(AwkTuples tuples, String functionName) {
4876 if (getAst1() == null) {
4877 throw new SemanticException(functionName + " requires 1 argument");
4878 }
4879 int ast1Result = getAst1().populateTuples(tuples);
4880 if (ast1Result != 1) {
4881 throw new SemanticException(functionName + " requires only 1 argument");
4882 }
4883 }
4884
4885
4886
4887
4888
4889
4890
4891
4892 private void populateMatchArgumentsTuples(AwkTuples tuples) {
4893 int ast1Result = populateActualParameters(tuples, (FunctionCallParamListAst) getAst1(), 1);
4894 if (ast1Result != 2) {
4895 throw new SemanticException("match requires 2 arguments");
4896 }
4897 }
4898
4899
4900
4901
4902
4903
4904 private void populateSprintfTuples(AwkTuples tuples) {
4905 if (getAst1() == null) {
4906 throw new SemanticException("sprintf requires at least 1 argument");
4907 }
4908 int ast1Result = getAst1().populateTuples(tuples);
4909 if (ast1Result == 0) {
4910 throw new SemanticException("sprintf requires at minimum 1 argument");
4911 }
4912 tuples.sprintf(ast1Result);
4913 }
4914
4915
4916
4917
4918
4919
4920
4921 private void populateLengthTuples(AwkTuples tuples) {
4922 if (getAst1() == null) {
4923 tuples.length(0);
4924 } else {
4925 AST params = getAst1();
4926 AST argument = params instanceof FunctionCallParamListAst
4927 && params.getAst2() == null ?
4928 params.getAst1() : params;
4929 int ast1Result;
4930 if (argument instanceof IDAst
4931 && !isJrtManagedSpecialName(((IDAst) argument).id)) {
4932 IDAst idAst = (IDAst) argument;
4933 tuples.pushIndirectArgument(idAst.offset, idAst.isGlobal);
4934 ast1Result = 1;
4935 } else {
4936 ast1Result = argument.populateTuples(tuples);
4937 }
4938 if (ast1Result != 1) {
4939 throw new SemanticException("length requires at least one argument");
4940 }
4941 tuples.length(1);
4942 }
4943 }
4944
4945
4946
4947
4948
4949
4950
4951 private void populateSrandTuples(AwkTuples tuples) {
4952 if (getAst1() == null) {
4953 tuples.srand(0);
4954 } else {
4955 int ast1Result = getAst1().populateTuples(tuples);
4956 if (ast1Result != 1) {
4957 throw new SemanticException("srand takes either 0 or one argument, not " + ast1Result);
4958 }
4959 tuples.srand(1);
4960 }
4961 }
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973 private void populateSubTuples(AwkTuples tuples, boolean isGsub) {
4974 if (getAst1() == null || getAst1().getAst2() == null || getAst1().getAst2().getAst1() == null) {
4975 throw new SemanticException("sub needs at least 2 arguments");
4976 }
4977 int numargs = 0;
4978 for (AST paramPtr = getAst1(); paramPtr != null; paramPtr = paramPtr.getAst2()) {
4979 numargs++;
4980 }
4981 if (numargs != 2 && numargs != 3) {
4982 throw new SemanticException("sub requires 2 or 3 arguments, not " + numargs);
4983 }
4984
4985 populateRawRegexpParameterTuples(getAst1().getAst1(), tuples);
4986 getAst1().getAst2().getAst1().populateTuples(tuples);
4987 if (numargs == 3) {
4988 AST targetAst = getAst1().getAst2().getAst2().getAst1();
4989 if (targetAst instanceof ArrayReferenceAst) {
4990 ((ArrayReferenceAst) targetAst).populateTargetValueTuples(tuples);
4991 } else {
4992 targetAst.populateTuples(tuples);
4993 }
4994 }
4995
4996
4997
4998 if (numargs == 2) {
4999 tuples.subForDollar0(isGsub);
5000 } else if (numargs == 3) {
5001 AST ptr = getAst1().getAst2().getAst2().getAst1();
5002 if (ptr instanceof IDAst) {
5003 IDAst idAst = (IDAst) ptr;
5004 idAst.setScalar(true);
5005 tuples.subForVariable(idAst.offset, idAst.isGlobal, isGsub);
5006 } else if (ptr instanceof ArrayReferenceAst) {
5007 ArrayReferenceAst arrAst = (ArrayReferenceAst) ptr;
5008 if (arrAst.getAst1() instanceof IDAst) {
5009 IDAst idAst = (IDAst) arrAst.getAst1();
5010 idAst.setArray(true);
5011 }
5012 arrAst.populateTargetReferenceTuples(tuples);
5013 tuples.subForMapReference(isGsub);
5014 } else if (ptr instanceof DollarExpressionAst) {
5015
5016 DollarExpressionAst dollarExpr = (DollarExpressionAst) ptr;
5017 dollarExpr.getAst1().populateTuples(tuples);
5018 tuples.subForDollarReference(isGsub);
5019 } else {
5020 throw new SemanticException(
5021 "sub's 3rd argument must be either an id, an array reference, or an input field reference");
5022 }
5023 }
5024 }
5025
5026
5027
5028
5029
5030
5031 private void populateSplitTuples(AwkTuples tuples) {
5032
5033
5034
5035
5036
5037 if (getAst1() == null || getAst1().getAst2() == null || getAst1().getAst2().getAst1() == null) {
5038 throw new SemanticException("split needs at least 2 arguments");
5039 }
5040 AST ptr = getAst1().getAst2().getAst1();
5041 if (!(ptr instanceof IDAst) && !(ptr instanceof ArrayReferenceAst)) {
5042 throw new SemanticException("split needs an array or subarray reference as its 2nd argument");
5043 }
5044 if (ptr instanceof IDAst) {
5045 IDAst arrAst = (IDAst) ptr;
5046 if (arrAst.isScalar()) {
5047 throw new SemanticException("split's 2nd arg cannot be a scalar");
5048 }
5049 arrAst.setArray(true);
5050 }
5051
5052 int ast1Result = 0;
5053 for (AST paramPtr = getAst1(); paramPtr != null; paramPtr = paramPtr.getAst2()) {
5054 ast1Result++;
5055 }
5056 if (ast1Result != 2 && ast1Result != 3) {
5057 throw new SemanticException("split requires 2 or 3 arguments, not " + ast1Result);
5058 }
5059
5060 getAst1().getAst1().populateTuples(tuples);
5061 populateArrayOperandTuples(
5062 ptr,
5063 tuples,
5064 true,
5065 "split's 2nd arg must be an array or subarray reference");
5066 if (ast1Result == 3) {
5067 populateRawRegexpParameterTuples(getAst1().getAst2().getAst2().getAst1(), tuples);
5068 }
5069 tuples.split(ast1Result);
5070 }
5071
5072
5073
5074
5075
5076
5077
5078 private void populateSubstrTuples(AwkTuples tuples) {
5079 if (getAst1() == null) {
5080 throw new SemanticException("substr requires at least 2 arguments");
5081 }
5082 int ast1Result = getAst1().populateTuples(tuples);
5083 if (ast1Result != 2 && ast1Result != 3) {
5084 throw new SemanticException("substr requires 2 or 3 arguments, not " + ast1Result);
5085 }
5086 tuples.substr(ast1Result);
5087 }
5088 }
5089
5090 private final class FunctionCallParamListAst extends AST {
5091
5092 private FunctionCallParamListAst(AST expr, AST rest) {
5093 super(expr, rest);
5094 }
5095
5096 private FunctionCallParamListAst(int lineNo, AST expr, AST rest) {
5097 super(lineNo, expr, rest);
5098 }
5099
5100 @Override
5101 public int populateTuples(AwkTuples tuples) {
5102 pushSourceLineNumber(tuples);
5103 int retval;
5104 if (getAst2() == null) {
5105 retval = getAst1().populateTuples(tuples);
5106 } else {
5107 retval = getAst1().populateTuples(tuples) + getAst2().populateTuples(tuples);
5108 }
5109 popSourceLineNumber(tuples);
5110 return retval;
5111 }
5112 }
5113
5114 private final class FunctionDefParamListAst extends AST {
5115
5116 private String id;
5117
5118 private FunctionDefParamListAst(String id, AST rest) {
5119 super(rest);
5120 this.id = id;
5121 }
5122
5123 public int populateTuples(AwkTuples tuples) {
5124 throw new Error("Cannot 'execute' function definition parameter list (formal parameters) in this manner.");
5125 }
5126
5127
5128
5129
5130
5131
5132
5133
5134
5135 @Override
5136 public void semanticAnalysis() throws SemanticException {
5137
5138
5139
5140
5141
5142 FunctionDefParamListAst ptr = this;
5143 while (ptr != null) {
5144 if (isSpecialVariableName(ptr.id)) {
5145 throw new SemanticException("Special variable " + ptr.id + " cannot be used as a formal parameter");
5146 }
5147 ptr = (FunctionDefParamListAst) ptr.getAst1();
5148 }
5149 }
5150 }
5151
5152
5153
5154
5155
5156
5157
5158
5159
5160
5161
5162
5163 private final class IDAst extends AST {
5164
5165 private String id;
5166 private int offset = AVM.NULL_OFFSET;
5167 private boolean isGlobal;
5168 private boolean referenced;
5169
5170 private IDAst(String id, boolean isGlobal) {
5171 this.id = id;
5172 this.isGlobal = isGlobal;
5173 addFlag(AstFlag.NON_STATEMENT);
5174 }
5175
5176 private boolean isArray = false;
5177 private boolean isScalar = false;
5178
5179 @Override
5180 public String toString() {
5181 return super.toString() + " (" + id + ")";
5182 }
5183
5184 @Override
5185 public int populateTuples(AwkTuples tuples) {
5186 pushSourceLineNumber(tuples);
5187 if (isJrtManagedSpecialName(id)) {
5188
5189 pushSpecialVariable(tuples, id);
5190 } else {
5191
5192
5193 tuples.dereference(offset, false, isGlobal);
5194 }
5195 popSourceLineNumber(tuples);
5196 return 1;
5197 }
5198
5199 @Override
5200 public boolean isArray() {
5201 return isArray;
5202 }
5203
5204 @Override
5205 public boolean isScalar() {
5206 return isScalar;
5207 }
5208
5209 private boolean isReferenced() {
5210 return referenced;
5211 }
5212
5213 private void markReferenced() {
5214 referenced = true;
5215 }
5216
5217 private void setArray(boolean b) {
5218 isArray = b;
5219 }
5220
5221 private void setScalar(boolean b) {
5222 isScalar = b;
5223 }
5224 }
5225
5226 private final class ArrayReferenceAst extends ScalarExpressionAst {
5227
5228 private ArrayReferenceAst(AST idAst, AST idxAst) {
5229 super(idAst, idxAst);
5230 }
5231
5232 private ArrayReferenceAst(int lineNo, AST idAst, AST idxAst) {
5233 super(lineNo, idAst, idxAst);
5234 }
5235
5236 @Override
5237 public String toString() {
5238 return super.toString() + " (" + getAst1() + " [...])";
5239 }
5240
5241 @Override
5242 public int populateTuples(AwkTuples tuples) {
5243 pushSourceLineNumber(tuples);
5244
5245 populateContainerTuples(tuples);
5246
5247 getAst2().populateTuples(tuples);
5248 tuples.dereferenceArray();
5249 popSourceLineNumber(tuples);
5250 return 1;
5251 }
5252
5253 private void populateTargetReferenceTuples(AwkTuples tuples) {
5254 pushSourceLineNumber(tuples);
5255 populateContainerTuples(tuples);
5256 getAst2().populateTuples(tuples);
5257 popSourceLineNumber(tuples);
5258 }
5259
5260 private void populateArrayValueTuples(AwkTuples tuples, boolean createIfMissing) {
5261 pushSourceLineNumber(tuples);
5262 populateContainerTuples(tuples);
5263 getAst2().populateTuples(tuples);
5264 if (createIfMissing) {
5265 tuples.ensureArrayElement();
5266 } else {
5267 tuples.peekArrayElement();
5268 }
5269 popSourceLineNumber(tuples);
5270 }
5271
5272 private void populateTargetValueTuples(AwkTuples tuples) {
5273 pushSourceLineNumber(tuples);
5274 populateContainerTuples(tuples);
5275 getAst2().populateTuples(tuples);
5276 tuples.dereferenceArray();
5277 popSourceLineNumber(tuples);
5278 }
5279
5280 private void populateContainerTuples(AwkTuples tuples) {
5281 if (getAst1() instanceof ArrayReferenceAst) {
5282 ((ArrayReferenceAst) getAst1()).populateArrayValueTuples(tuples, true);
5283 } else if (getAst1() instanceof IDAst) {
5284 IDAst idAst = (IDAst) getAst1();
5285 if (isJrtManagedSpecialName(idAst.id)) {
5286 idAst.populateTuples(tuples);
5287 } else {
5288 tuples.dereference(idAst.offset, true, idAst.isGlobal);
5289 }
5290 } else {
5291 getAst1().populateTuples(tuples);
5292 }
5293 }
5294 }
5295
5296 private final class IntegerAst extends ScalarExpressionAst {
5297
5298 private Long value;
5299
5300 private IntegerAst(Long value) {
5301 this.value = value;
5302 addFlag(AstFlag.NON_STATEMENT);
5303 }
5304
5305 @Override
5306 public String toString() {
5307 return super.toString() + " (" + value + ")";
5308 }
5309
5310 @Override
5311 public int populateTuples(AwkTuples tuples) {
5312 pushSourceLineNumber(tuples);
5313 tuples.push(value);
5314 popSourceLineNumber(tuples);
5315 return 1;
5316 }
5317 }
5318
5319
5320
5321
5322
5323 private final class DoubleAst extends ScalarExpressionAst {
5324
5325 private Object value;
5326
5327 private DoubleAst(Double val) {
5328 double d = val.doubleValue();
5329 if (d == (int) d) {
5330 this.value = (int) d;
5331 } else {
5332 this.value = d;
5333 }
5334 addFlag(AstFlag.NON_STATEMENT);
5335 }
5336
5337 @Override
5338 public String toString() {
5339 return super.toString() + " (" + value + ")";
5340 }
5341
5342 @Override
5343 public int populateTuples(AwkTuples tuples) {
5344 pushSourceLineNumber(tuples);
5345 tuples.push(value);
5346 popSourceLineNumber(tuples);
5347 return 1;
5348 }
5349 }
5350
5351
5352
5353
5354
5355 private final class StringAst extends ScalarExpressionAst {
5356
5357 private String value;
5358
5359 private StringAst(String str) {
5360 this.value = str;
5361 addFlag(AstFlag.NON_STATEMENT);
5362 }
5363
5364 @Override
5365 public String toString() {
5366 return super.toString() + " (" + value + ")";
5367 }
5368
5369 @Override
5370 public int populateTuples(AwkTuples tuples) {
5371 pushSourceLineNumber(tuples);
5372 tuples.push(value);
5373 popSourceLineNumber(tuples);
5374 return 1;
5375 }
5376 }
5377
5378 private final class RegexpAst extends ScalarExpressionAst {
5379
5380 private String regexpStr;
5381 private boolean typed;
5382
5383 private RegexpAst(String regexpStr, boolean typedParam) {
5384 this.regexpStr = regexpStr;
5385 this.typed = typedParam;
5386 }
5387
5388 @Override
5389 public String toString() {
5390 return super.toString() + " (" + regexpStr + ")";
5391 }
5392
5393 @Override
5394 public int populateTuples(AwkTuples tuples) {
5395 pushSourceLineNumber(tuples);
5396 if (typed) {
5397 tuples.regexp(regexpStr);
5398 } else {
5399 tuples.getInputField(0);
5400 tuples.regexp(regexpStr);
5401 tuples.matches();
5402 }
5403 popSourceLineNumber(tuples);
5404 return 1;
5405 }
5406
5407 private int populateRawRegexpTuples(AwkTuples tuples) {
5408 tuples.regexp(regexpStr);
5409 return 1;
5410 }
5411 }
5412
5413 private final class ConditionPairAst extends ScalarExpressionAst {
5414
5415 private final long conditionPairId;
5416
5417 private ConditionPairAst(AST booleanAst1, AST booleanAst2) {
5418 super(booleanAst1, booleanAst2);
5419 conditionPairId = ++conditionPairCount;
5420 }
5421
5422 @Override
5423 public int populateTuples(AwkTuples tuples) {
5424
5425
5426
5427 pushSourceLineNumber(tuples);
5428
5429 Address enterRange = tuples.createAddress("conditionPairEnterRange");
5430 Address testEnd = tuples.createAddress("conditionPairTestEnd");
5431 Address withinRange = tuples.createAddress("conditionPairWithinRange");
5432 Address end = tuples.createAddress("conditionPairEnd");
5433
5434 tuples.conditionPairInRange(conditionPairId);
5435 tuples.ifTrue(testEnd);
5436 getAst1().populateTuples(tuples);
5437 tuples.ifTrue(enterRange);
5438 tuples.push(0);
5439 tuples.gotoAddress(end);
5440
5441 tuples.address(enterRange);
5442 tuples.conditionPairEnter(conditionPairId);
5443
5444 tuples.address(testEnd);
5445 getAst2().populateTuples(tuples);
5446 tuples.ifFalse(withinRange);
5447 tuples.conditionPairLeave(conditionPairId);
5448
5449 tuples.address(withinRange);
5450 tuples.push(1);
5451 tuples.address(end);
5452
5453 popSourceLineNumber(tuples);
5454 return 1;
5455 }
5456 }
5457
5458 private final class BeginAst extends AST {
5459
5460 private BeginAst() {
5461 super();
5462 setBeginFlag(true);
5463 }
5464
5465 @Override
5466 public int populateTuples(AwkTuples tuples) {
5467 pushSourceLineNumber(tuples);
5468 tuples.push(1);
5469 popSourceLineNumber(tuples);
5470 return 1;
5471 }
5472 }
5473
5474 private final class EndAst extends AST {
5475
5476 private EndAst() {
5477 super();
5478 setEndFlag(true);
5479 }
5480
5481 @Override
5482 public int populateTuples(AwkTuples tuples) {
5483 pushSourceLineNumber(tuples);
5484 tuples.push(1);
5485 popSourceLineNumber(tuples);
5486 return 1;
5487 }
5488 }
5489
5490 private final class BeginFileAst extends AST {
5491
5492 private BeginFileAst() {
5493 super();
5494 setBeginFileFlag(true);
5495 }
5496
5497 @Override
5498 public int populateTuples(AwkTuples tuples) {
5499 pushSourceLineNumber(tuples);
5500 tuples.push(1);
5501 popSourceLineNumber(tuples);
5502 return 1;
5503 }
5504 }
5505
5506 private final class EndFileAst extends AST {
5507
5508 private EndFileAst() {
5509 super();
5510 setEndFileFlag(true);
5511 }
5512
5513 @Override
5514 public int populateTuples(AwkTuples tuples) {
5515 pushSourceLineNumber(tuples);
5516 tuples.push(1);
5517 popSourceLineNumber(tuples);
5518 return 1;
5519 }
5520 }
5521
5522 private final class PreIncAst extends ScalarExpressionAst {
5523
5524 private PreIncAst(AST symbolAst) {
5525 super(symbolAst);
5526 }
5527
5528 @Override
5529 public int populateTuples(AwkTuples tuples) {
5530 pushSourceLineNumber(tuples);
5531 if (getAst1() instanceof IDAst && isJrtManagedSpecialName(((IDAst) getAst1()).id)) {
5532
5533 populateSpecialIncDec(tuples, ((IDAst) getAst1()).id, true, false);
5534 popSourceLineNumber(tuples);
5535 return 1;
5536 } else if (getAst1() instanceof IDAst) {
5537 IDAst idAst = (IDAst) getAst1();
5538 tuples.inc(idAst.offset, idAst.isGlobal);
5539 } else if (getAst1() instanceof ArrayReferenceAst) {
5540 ArrayReferenceAst arrAst = (ArrayReferenceAst) getAst1();
5541 if (arrAst.getAst1() instanceof IDAst) {
5542 IDAst idAst = (IDAst) arrAst.getAst1();
5543 if (idAst.isScalar()) {
5544 throw new SemanticException("Cannot use " + idAst + " as an array.");
5545 }
5546 idAst.setArray(true);
5547 }
5548 arrAst.populateTargetReferenceTuples(tuples);
5549 tuples.incMapRef();
5550 } else if (getAst1() instanceof DollarExpressionAst) {
5551 DollarExpressionAst dollarExpr = (DollarExpressionAst) getAst1();
5552 dollarExpr.getAst1().populateTuples(tuples);
5553
5554 tuples.dup();
5555
5556
5557 tuples.incDollarRef();
5558
5559
5560
5561 tuples.getInputField();
5562 popSourceLineNumber(tuples);
5563 return 1;
5564 } else {
5565 throw new NotImplementedError("unhandled preinc for " + getAst1());
5566 }
5567
5568
5569 getAst1().populateTuples(tuples);
5570 popSourceLineNumber(tuples);
5571 return 1;
5572 }
5573 }
5574
5575 private final class PreDecAst extends ScalarExpressionAst {
5576
5577 private PreDecAst(AST symbolAst) {
5578 super(symbolAst);
5579 }
5580
5581 @Override
5582 public int populateTuples(AwkTuples tuples) {
5583 pushSourceLineNumber(tuples);
5584 if (getAst1() instanceof IDAst && isJrtManagedSpecialName(((IDAst) getAst1()).id)) {
5585
5586 populateSpecialIncDec(tuples, ((IDAst) getAst1()).id, false, false);
5587 popSourceLineNumber(tuples);
5588 return 1;
5589 } else if (getAst1() instanceof IDAst) {
5590 IDAst idAst = (IDAst) getAst1();
5591 tuples.dec(idAst.offset, idAst.isGlobal);
5592 } else if (getAst1() instanceof ArrayReferenceAst) {
5593 ArrayReferenceAst arrAst = (ArrayReferenceAst) getAst1();
5594 if (arrAst.getAst1() instanceof IDAst) {
5595 IDAst idAst = (IDAst) arrAst.getAst1();
5596 if (idAst.isScalar()) {
5597 throw new SemanticException("Cannot use " + idAst + " as an array.");
5598 }
5599 idAst.setArray(true);
5600 }
5601 arrAst.populateTargetReferenceTuples(tuples);
5602 tuples.decMapRef();
5603 } else if (getAst1() instanceof DollarExpressionAst) {
5604 DollarExpressionAst dollarExpr = (DollarExpressionAst) getAst1();
5605 dollarExpr.getAst1().populateTuples(tuples);
5606
5607 tuples.dup();
5608
5609
5610 tuples.decDollarRef();
5611
5612
5613
5614 tuples.getInputField();
5615 popSourceLineNumber(tuples);
5616 return 1;
5617 } else {
5618 throw new NotImplementedError("unhandled predec for " + getAst1());
5619 }
5620 getAst1().populateTuples(tuples);
5621 popSourceLineNumber(tuples);
5622 return 1;
5623 }
5624 }
5625
5626 private final class PostIncAst extends ScalarExpressionAst {
5627
5628 private PostIncAst(AST symbolAst) {
5629 super(symbolAst);
5630 }
5631
5632 @Override
5633 public int populateTuples(AwkTuples tuples) {
5634 pushSourceLineNumber(tuples);
5635 if (getAst1() instanceof DollarExpressionAst) {
5636 DollarExpressionAst dollarExpr = (DollarExpressionAst) getAst1();
5637 dollarExpr.getAst1().populateTuples(tuples);
5638 tuples.incDollarRef();
5639 } else if (getAst1() instanceof IDAst && isJrtManagedSpecialName(((IDAst) getAst1()).id)) {
5640 populateSpecialIncDec(tuples, ((IDAst) getAst1()).id, true, true);
5641 } else {
5642 if (getAst1() instanceof ArrayReferenceAst) {
5643 ((ArrayReferenceAst) getAst1()).populateTargetValueTuples(tuples);
5644 tuples.unaryPlus();
5645 } else {
5646 getAst1().populateTuples(tuples);
5647 }
5648 if (getAst1() instanceof IDAst) {
5649 IDAst idAst = (IDAst) getAst1();
5650 tuples.postInc(idAst.offset, idAst.isGlobal);
5651 } else if (getAst1() instanceof ArrayReferenceAst) {
5652 ArrayReferenceAst arrAst = (ArrayReferenceAst) getAst1();
5653 if (arrAst.getAst1() instanceof IDAst) {
5654 IDAst idAst = (IDAst) arrAst.getAst1();
5655 if (idAst.isScalar()) {
5656 throw new SemanticException("Cannot use " + idAst + " as an array.");
5657 }
5658 idAst.setArray(true);
5659 }
5660 arrAst.populateTargetReferenceTuples(tuples);
5661 tuples.incMapRef();
5662 } else {
5663 throw new NotImplementedError("unhandled postinc for " + getAst1());
5664 }
5665 }
5666 popSourceLineNumber(tuples);
5667 return 1;
5668 }
5669 }
5670
5671 private final class PostDecAst extends ScalarExpressionAst {
5672
5673 private PostDecAst(AST symbolAst) {
5674 super(symbolAst);
5675 }
5676
5677 @Override
5678 public int populateTuples(AwkTuples tuples) {
5679 pushSourceLineNumber(tuples);
5680 if (getAst1() instanceof IDAst && isJrtManagedSpecialName(((IDAst) getAst1()).id)) {
5681 populateSpecialIncDec(tuples, ((IDAst) getAst1()).id, false, true);
5682 popSourceLineNumber(tuples);
5683 return 1;
5684 }
5685 if (getAst1() instanceof ArrayReferenceAst) {
5686 ((ArrayReferenceAst) getAst1()).populateTargetValueTuples(tuples);
5687 tuples.unaryPlus();
5688 } else {
5689 getAst1().populateTuples(tuples);
5690 }
5691 if (getAst1() instanceof IDAst) {
5692 IDAst idAst = (IDAst) getAst1();
5693 tuples.postDec(idAst.offset, idAst.isGlobal);
5694 } else if (getAst1() instanceof ArrayReferenceAst) {
5695 ArrayReferenceAst arrAst = (ArrayReferenceAst) getAst1();
5696 if (arrAst.getAst1() instanceof IDAst) {
5697 IDAst idAst = (IDAst) arrAst.getAst1();
5698 if (idAst.isScalar()) {
5699 throw new SemanticException("Cannot use " + idAst + " as an array.");
5700 }
5701 idAst.setArray(true);
5702 }
5703 arrAst.populateTargetReferenceTuples(tuples);
5704 tuples.decMapRef();
5705 } else if (getAst1() instanceof DollarExpressionAst) {
5706 DollarExpressionAst dollarExpr = (DollarExpressionAst) getAst1();
5707 dollarExpr.getAst1().populateTuples(tuples);
5708 tuples.decDollarRef();
5709 } else {
5710 throw new NotImplementedError("unhandled postinc for " + getAst1());
5711 }
5712 popSourceLineNumber(tuples);
5713 return 1;
5714 }
5715 }
5716
5717 private final class PrintAst extends ScalarExpressionAst {
5718
5719 private Token outputToken;
5720 private boolean parenthesized;
5721
5722 private PrintAst(AST exprList, Token outToken, AST outputExpr, boolean parenthesized) {
5723 super(exprList, outputExpr);
5724 this.outputToken = outToken;
5725 this.parenthesized = parenthesized;
5726 }
5727
5728 @Override
5729 public int populateTuples(AwkTuples tuples) {
5730 pushSourceLineNumber(tuples);
5731
5732 int paramCount;
5733 if (getAst1() == null) {
5734 if (parenthesized) {
5735 throw new SemanticException("print() requires at least 1 argument");
5736 }
5737 paramCount = 0;
5738 } else {
5739 paramCount = getAst1().populateTuples(tuples);
5740 if (paramCount == 0) {
5741 throw new SemanticException("Cannot print the result. The expression doesn't return anything.");
5742 }
5743 }
5744
5745 if (getAst2() != null) {
5746 getAst2().populateTuples(tuples);
5747 }
5748
5749 if (outputToken == Token.GT) {
5750 tuples.printToFile(paramCount, false);
5751 } else if (outputToken == Token.APPEND) {
5752 tuples.printToFile(paramCount, true);
5753 } else if (outputToken == Token.PIPE) {
5754 tuples.printToPipe(paramCount);
5755 } else {
5756 tuples.print(paramCount);
5757 }
5758
5759 popSourceLineNumber(tuples);
5760 return 0;
5761 }
5762 }
5763
5764
5765
5766
5767
5768
5769
5770
5771
5772
5773
5774 private boolean isJrtManagedSpecialName(String id) {
5775 return isSpecialVariableName(id) && !"ENVIRON".equals(id) && !"ARGV".equals(id);
5776 }
5777
5778
5779
5780
5781
5782
5783
5784
5785
5786
5787
5788
5789 private boolean isSpecialVariableName(String id) {
5790 if (!SPECIAL_VAR_NAMES.containsKey(id)) {
5791 return false;
5792 }
5793 if (JRT.isGawkOnlySpecialVariable(id)) {
5794 return !posix;
5795 }
5796 return true;
5797 }
5798
5799
5800 private void pushSpecialVariable(AwkTuples tuples, String id) {
5801 switch (id) {
5802 case "NF":
5803 tuples.pushNF();
5804 break;
5805 case "NR":
5806 tuples.pushNR();
5807 break;
5808 case "FNR":
5809 tuples.pushFNR();
5810 break;
5811 case "FS":
5812 tuples.pushFS();
5813 break;
5814 case "RS":
5815 tuples.pushRS();
5816 break;
5817 case "OFS":
5818 tuples.pushOFS();
5819 break;
5820 case "ORS":
5821 tuples.pushORS();
5822 break;
5823 case "RSTART":
5824 tuples.pushRSTART();
5825 break;
5826 case "RLENGTH":
5827 tuples.pushRLENGTH();
5828 break;
5829 case "IGNORECASE":
5830 tuples.pushIGNORECASE();
5831 break;
5832 case "FILENAME":
5833 tuples.pushFILENAME();
5834 break;
5835 case "SUBSEP":
5836 tuples.pushSUBSEP();
5837 break;
5838 case "CONVFMT":
5839 tuples.pushCONVFMT();
5840 break;
5841 case "OFMT":
5842 tuples.pushOFMT();
5843 break;
5844 case "ARGC":
5845 tuples.pushARGC();
5846 break;
5847 case "ERRNO":
5848 tuples.pushERRNO();
5849 break;
5850 case "ARGIND":
5851 tuples.pushARGIND();
5852 break;
5853 default:
5854 throw new Error("Unhandled special var: " + id);
5855 }
5856 }
5857
5858
5859 private void assignSpecialVariable(AwkTuples tuples, String id) {
5860 switch (id) {
5861 case "NF":
5862 tuples.assignNF();
5863 break;
5864 case "NR":
5865 tuples.assignNR();
5866 break;
5867 case "FNR":
5868 tuples.assignFNR();
5869 break;
5870 case "FS":
5871 tuples.assignFS();
5872 break;
5873 case "RS":
5874 tuples.assignRS();
5875 break;
5876 case "OFS":
5877 tuples.assignOFS();
5878 break;
5879 case "ORS":
5880 tuples.assignORS();
5881 break;
5882 case "RSTART":
5883 tuples.assignRSTART();
5884 break;
5885 case "RLENGTH":
5886 tuples.assignRLENGTH();
5887 break;
5888 case "IGNORECASE":
5889 tuples.assignIGNORECASE();
5890 break;
5891 case "FILENAME":
5892 tuples.assignFILENAME();
5893 break;
5894 case "SUBSEP":
5895 tuples.assignSUBSEP();
5896 break;
5897 case "CONVFMT":
5898 tuples.assignCONVFMT();
5899 break;
5900 case "OFMT":
5901 tuples.assignOFMT();
5902 break;
5903 case "ARGC":
5904 tuples.assignARGC();
5905 break;
5906 case "ERRNO":
5907 tuples.assignERRNO();
5908 break;
5909 case "ARGIND":
5910 tuples.assignARGIND();
5911 break;
5912 default:
5913 throw new Error("Unhandled special var: " + id);
5914 }
5915 }
5916
5917
5918
5919
5920
5921
5922
5923
5924 private void populateSpecialIncDec(AwkTuples tuples, String id, boolean increment, boolean postfix) {
5925 pushSpecialVariable(tuples, id);
5926 if (postfix) {
5927 tuples.dup();
5928 }
5929 tuples.push(Long.valueOf(1L));
5930 if (increment) {
5931 tuples.add();
5932 } else {
5933 tuples.subtract();
5934 }
5935 assignSpecialVariable(tuples, id);
5936 if (postfix) {
5937 tuples.pop();
5938 }
5939 }
5940
5941
5942
5943
5944
5945
5946
5947 private int extensionCallLineNumber(AST params) {
5948 if (params != null && params.getAst1() != null) {
5949 return params.getAst1().getLineNo();
5950 }
5951 return currentSourceLineNumber();
5952 }
5953
5954 private final class ExtensionAst extends AST {
5955
5956 private final ExtensionFunction function;
5957
5958 private ExtensionAst(ExtensionFunction functionParam, AST paramAst, int lineNoParam) {
5959 super(lineNoParam, paramAst);
5960 this.function = functionParam;
5961 }
5962
5963 @Override
5964 public int populateTuples(AwkTuples tuples) {
5965 pushSourceLineNumber(tuples);
5966 int argCount;
5967 if (getAst1() == null) {
5968 argCount = 0;
5969 } else {
5970 argCount = countParams((FunctionCallParamListAst) getAst1());
5971 }
5972
5973 int[] reqArrayIdxs = function.collectAssocArrayIndexes(argCount);
5974 int[] rawValueIdxs = function.collectRawValueIndexes(argCount);
5975
5976 int paramCount;
5977 if (getAst1() == null) {
5978 paramCount = 0;
5979 } else {
5980 Set<Integer> arrayIndexes = new HashSet<Integer>();
5981 Set<Integer> rawValueIndexes = new HashSet<Integer>();
5982 Set<Integer> literalRegexpIndexes = new HashSet<Integer>();
5983 for (int idx : rawValueIdxs) {
5984 rawValueIndexes.add(Integer.valueOf(idx));
5985 }
5986 for (int idx : reqArrayIdxs) {
5987 AST paramAst = getParamAst((FunctionCallParamListAst) getAst1(), idx).getAst1();
5988 if (paramAst instanceof IDAst) {
5989 IDAst idAst = (IDAst) paramAst;
5990 if (idAst.isScalar()) {
5991 throw new SemanticException(
5992 "Extension '"
5993 + function.getKeyword()
5994 + "' requires parameter position "
5995 + idx
5996 + " be an associative array, not a scalar.");
5997 }
5998 idAst.setArray(true);
5999 arrayIndexes.add(Integer.valueOf(idx));
6000 } else if (paramAst instanceof ArrayReferenceAst) {
6001 arrayIndexes.add(Integer.valueOf(idx));
6002 }
6003 }
6004 for (int idx : function.collectRegexpIndexes(argCount)) {
6005 literalRegexpIndexes.add(Integer.valueOf(idx));
6006 }
6007
6008 paramCount = populateActualParameters(
6009 tuples,
6010 (FunctionCallParamListAst) getAst1(),
6011 arrayIndexes,
6012 rawValueIndexes,
6013 literalRegexpIndexes,
6014 0);
6015 }
6016 tuples.extension(function, paramCount);
6017 popSourceLineNumber(tuples);
6018
6019 return 1;
6020 }
6021
6022 private AST getParamAst(FunctionCallParamListAst pAst, int pos) {
6023 for (int i = 0; i < pos; ++i) {
6024 pAst = (FunctionCallParamListAst) pAst.getAst2();
6025 if (pAst == null) {
6026 throw new SemanticException("More arguments required for assoc array parameter position specification.");
6027 }
6028 }
6029 return pAst;
6030 }
6031
6032 private int countParams(FunctionCallParamListAst pAst) {
6033 int cnt = 0;
6034 while (pAst != null) {
6035 pAst = (FunctionCallParamListAst) pAst.getAst2();
6036 ++cnt;
6037 }
6038 return cnt;
6039 }
6040
6041 @Override
6042 public String toString() {
6043 return super.toString() + " (" + function.getKeyword() + ")";
6044 }
6045 }
6046
6047 private final class PrintfAst extends ScalarExpressionAst {
6048
6049 private Token outputToken;
6050
6051 private PrintfAst(AST exprList, Token outToken, AST outputExpr) {
6052 super(exprList, outputExpr);
6053 this.outputToken = outToken;
6054 }
6055
6056 @Override
6057 public int populateTuples(AwkTuples tuples) {
6058 pushSourceLineNumber(tuples);
6059
6060 int paramCount;
6061 if (getAst1() == null) {
6062 throw new SemanticException("printf requires at least 1 argument");
6063 } else {
6064 paramCount = getAst1().populateTuples(tuples);
6065 if (paramCount == 0) {
6066 throw new SemanticException("Cannot printf the result. The expression doesn't return anything.");
6067 }
6068 }
6069
6070 if (getAst2() != null) {
6071 getAst2().populateTuples(tuples);
6072 }
6073
6074 if (outputToken == Token.GT) {
6075 tuples.printfToFile(paramCount, false);
6076 } else if (outputToken == Token.APPEND) {
6077 tuples.printfToFile(paramCount, true);
6078 } else if (outputToken == Token.PIPE) {
6079 tuples.printfToPipe(paramCount);
6080 } else {
6081 tuples.printf(paramCount);
6082 }
6083
6084 popSourceLineNumber(tuples);
6085 return 0;
6086 }
6087 }
6088
6089 private final class GetlineAst extends ScalarExpressionAst {
6090
6091 private GetlineAst(AST pipeExpr, AST lvalueAst, AST inRedirect) {
6092 super(pipeExpr, lvalueAst, inRedirect);
6093 }
6094
6095 @Override
6096 public int populateTuples(AwkTuples tuples) {
6097 pushSourceLineNumber(tuples);
6098
6099
6100
6101
6102
6103 if (getAst1() == null && getAst3() == null) {
6104 AST enclosingRule = searchFor(AstFlag.NEXTABLE);
6105 AST pattern = enclosingRule == null ? null : enclosingRule.getAst1();
6106 if (pattern != null && (pattern.isBeginFile() || pattern.isEndFile())) {
6107 throw new SemanticException(
6108 "non-redirected `getline' invalid inside `"
6109 + (pattern.isBeginFile() ? "BEGINFILE" : "ENDFILE")
6110 + "' rule");
6111 }
6112 }
6113 if (getAst1() == null && getAst3() == null && getAst2() == null) {
6114 tuples.getlineInput();
6115 popSourceLineNumber(tuples);
6116 return 1;
6117 }
6118
6119
6120
6121
6122 Address noRecord = tuples.createAddress("getline_no_record");
6123 if (getAst1() != null) {
6124 getAst1().populateTuples(tuples);
6125 tuples.useAsCommandInput(noRecord);
6126 } else if (getAst3() != null) {
6127
6128 getAst3().populateTuples(tuples);
6129 tuples.useAsFileInput(noRecord);
6130 } else {
6131 tuples.getlineInputToTarget(noRecord);
6132 }
6133
6134
6135
6136 if (getAst2() == null) {
6137 tuples.assignAsInput();
6138
6139
6140 } else if (getAst2() instanceof IDAst) {
6141 IDAst idAst = (IDAst) getAst2();
6142 tuples.assign(idAst.offset, idAst.isGlobal);
6143 if (idAst.id.equals("RS")) {
6144 tuples.applyRS();
6145 }
6146 } else if (getAst2() instanceof ArrayReferenceAst) {
6147 ArrayReferenceAst arr = (ArrayReferenceAst) getAst2();
6148 if (arr.getAst1() instanceof IDAst) {
6149 IDAst idAst = (IDAst) arr.getAst1();
6150 if (idAst.isScalar()) {
6151 throw new SemanticException("Cannot use " + idAst + " as an array.");
6152 }
6153 idAst.setArray(true);
6154 }
6155 arr.populateTargetReferenceTuples(tuples);
6156 tuples.assignMapElement();
6157 } else if (getAst2() instanceof DollarExpressionAst) {
6158 DollarExpressionAst dollarExpr = (DollarExpressionAst) getAst2();
6159
6160 dollarExpr.getAst1().populateTuples(tuples);
6161 tuples.assignAsInputField();
6162 } else {
6163 throw new SemanticException("Cannot getline into a " + getAst2());
6164 }
6165
6166 tuples.pop();
6167 if (getAst2() instanceof ArrayReferenceAst || getAst2() instanceof DollarExpressionAst) {
6168
6169
6170
6171
6172 Address end = tuples.createAddress("getline_end");
6173 tuples.gotoAddress(end);
6174 tuples.address(noRecord);
6175
6176
6177
6178 getAst2().populateTuples(tuples);
6179 tuples.pop();
6180 tuples.address(end);
6181 } else {
6182 tuples.address(noRecord);
6183 }
6184
6185 popSourceLineNumber(tuples);
6186 return 1;
6187 }
6188 }
6189
6190 private final class ReturnStatementAst extends AST {
6191
6192 private ReturnStatementAst(AST expr) {
6193 super(expr);
6194 }
6195
6196 @Override
6197 public int populateTuples(AwkTuples tuples) {
6198 pushSourceLineNumber(tuples);
6199 AST returnable = searchFor(AstFlag.RETURNABLE);
6200 if (returnable == null) {
6201 throw new SemanticException("Cannot use return here.");
6202 }
6203 if (getAst1() != null) {
6204 getAst1().populateTuples(tuples);
6205 tuples.setReturnResult();
6206 }
6207 tuples.gotoAddress(returnable.returnAddress());
6208 popSourceLineNumber(tuples);
6209 return 0;
6210 }
6211 }
6212
6213 private final class ExitStatementAst extends AST {
6214
6215 private ExitStatementAst(AST expr) {
6216 super(expr);
6217 }
6218
6219 @Override
6220 public int populateTuples(AwkTuples tuples) {
6221 pushSourceLineNumber(tuples);
6222 if (getAst1() != null) {
6223 getAst1().populateTuples(tuples);
6224 tuples.exitWithCode();
6225 } else {
6226 tuples.exitWithoutCode();
6227 }
6228 popSourceLineNumber(tuples);
6229 return 0;
6230 }
6231 }
6232
6233 private final class DeleteStatementAst extends AST {
6234
6235 private DeleteStatementAst(AST symbolAst) {
6236 super(symbolAst);
6237 }
6238
6239 @Override
6240 public int populateTuples(AwkTuples tuples) {
6241 pushSourceLineNumber(tuples);
6242
6243 if (getAst1() instanceof ArrayReferenceAst) {
6244 ArrayReferenceAst arrAst = (ArrayReferenceAst) getAst1();
6245 if (arrAst.getAst1() instanceof IDAst) {
6246 IDAst idAst = (IDAst) arrAst.getAst1();
6247 idAst.setArray(true);
6248 }
6249 arrAst.populateTargetReferenceTuples(tuples);
6250 tuples.deleteMapElement();
6251 } else if (getAst1() instanceof IDAst) {
6252 IDAst idAst = (IDAst) getAst1();
6253 idAst.setArray(true);
6254 tuples.deleteArray(idAst.offset, idAst.isGlobal);
6255 } else {
6256 throw new Error("Should never reach here : delete for " + getAst1());
6257 }
6258
6259 popSourceLineNumber(tuples);
6260 return 0;
6261 }
6262 }
6263
6264 private class BreakStatementAst extends AST {
6265
6266 @Override
6267 public int populateTuples(AwkTuples tuples) {
6268 pushSourceLineNumber(tuples);
6269 AST breakable = searchFor(AstFlag.BREAKABLE);
6270 if (breakable == null) {
6271 throw new SemanticException("cannot break; not within a loop");
6272 }
6273 tuples.gotoAddress(breakable.breakAddress());
6274 popSourceLineNumber(tuples);
6275 return 0;
6276 }
6277 }
6278
6279 private class NextStatementAst extends AST {
6280
6281 @Override
6282 public int populateTuples(AwkTuples tuples) {
6283 pushSourceLineNumber(tuples);
6284 AST nextable = searchFor(AstFlag.NEXTABLE);
6285 if (nextable == null) {
6286
6287
6288
6289
6290
6291
6292 tuples.execNext();
6293 } else {
6294 tuples.gotoAddress(nextable.nextAddress());
6295 }
6296 popSourceLineNumber(tuples);
6297 return 0;
6298 }
6299 }
6300
6301 private class NextfileStatementAst extends AST {
6302
6303 @Override
6304 public int populateTuples(AwkTuples tuples) {
6305 pushSourceLineNumber(tuples);
6306 AST nextable = searchFor(AstFlag.NEXTABLE);
6307 if (nextable != null) {
6308
6309
6310
6311 AST pattern = nextable.getAst1();
6312 if (pattern != null && (pattern.isBegin() || pattern.isEnd() || pattern.isEndFile())) {
6313 String ruleName = pattern.isBegin() ? "BEGIN" : pattern.isEnd() ? "END" : "ENDFILE";
6314 throw new SemanticException(
6315 "`nextfile' cannot be called from a `" + ruleName + "' rule.");
6316 }
6317 }
6318 tuples.execNextfile();
6319 popSourceLineNumber(tuples);
6320 return 0;
6321 }
6322 }
6323
6324 private final class ContinueStatementAst extends AST {
6325
6326 private ContinueStatementAst() {
6327 super();
6328 }
6329
6330 @Override
6331 public int populateTuples(AwkTuples tuples) {
6332 pushSourceLineNumber(tuples);
6333 AST continueable = searchFor(AstFlag.CONTINUEABLE);
6334 if (continueable == null) {
6335 throw new SemanticException("cannot issue a continue; not within any loops");
6336 }
6337 tuples.gotoAddress(continueable.continueAddress());
6338 popSourceLineNumber(tuples);
6339 return 0;
6340 }
6341 }
6342
6343
6344
6345 private final class FunctionProxy implements Supplier<Address> {
6346
6347 private FunctionDefAst functionDefAst;
6348 private String id;
6349
6350 private FunctionProxy(String id) {
6351 this.id = id;
6352 }
6353
6354 private void setFunctionDefinition(FunctionDefAst functionDef) {
6355 if (functionDefAst != null) {
6356 throw parserException("function " + functionDef + " already defined");
6357 } else {
6358 functionDefAst = functionDef;
6359 }
6360 }
6361
6362 private boolean isDefined() {
6363 return functionDefAst != null;
6364 }
6365
6366 @Override
6367 public Address get() {
6368 return functionDefAst.getAddress();
6369 }
6370
6371 private String getFunctionName() {
6372 return id;
6373 }
6374
6375 private int getFunctionParamCount() {
6376 return functionDefAst.paramCount();
6377 }
6378
6379 @Override
6380 public String toString() {
6381 return super.toString() + " (" + id + ")";
6382 }
6383
6384 }
6385
6386
6387
6388
6389
6390
6391
6392
6393
6394
6395
6396 public void populateGlobalVariableNameToOffsetMappings(AwkTuples tuples) {
6397 for (String varname : symbolTable.globalIds.keySet()) {
6398 IDAst idAst = symbolTable.globalIds.get(varname);
6399
6400
6401
6402 tuples.addGlobalVariableNameToOffsetMapping(varname, idAst.offset, idAst.isArray);
6403 }
6404 tuples.setFunctionNameSet(symbolTable.functionProxies.keySet());
6405 }
6406
6407 private class AwkSymbolTableImpl {
6408
6409 int numGlobals() {
6410 return globalIds.size();
6411 }
6412
6413
6414 private BeginAst beginAst = null;
6415 private EndAst endAst = null;
6416 private BeginFileAst beginFileAst = null;
6417 private EndFileAst endFileAst = null;
6418
6419
6420 private Map<String, FunctionProxy> functionProxies = new HashMap<String, FunctionProxy>();
6421 private Map<String, Tuple.IndirectFunctionTarget> cachedIndirectFunctionTargets;
6422
6423
6424 private Map<String, IDAst> globalIds = new HashMap<String, IDAst>();
6425 private Map<String, Map<String, IDAst>> localIds = new HashMap<String, Map<String, IDAst>>();
6426 private Map<String, Set<String>> functionParameters = new HashMap<String, Set<String>>();
6427 private Set<String> ids = new HashSet<String>();
6428
6429
6430 private String currentFunctionName = null;
6431
6432
6433 void setFunctionName(String functionName) {
6434 this.currentFunctionName = functionName;
6435 }
6436
6437 void clearFunctionName(String functionName) {
6438 this.currentFunctionName = null;
6439 }
6440
6441 AST addBEGIN() {
6442 if (beginAst == null) {
6443 beginAst = new BeginAst();
6444 }
6445 return beginAst;
6446 }
6447
6448 AST addEND() {
6449 if (endAst == null) {
6450 endAst = new EndAst();
6451 }
6452 return endAst;
6453 }
6454
6455 AST addBEGINFILE() {
6456 if (beginFileAst == null) {
6457 beginFileAst = new BeginFileAst();
6458 }
6459 return beginFileAst;
6460 }
6461
6462 AST addENDFILE() {
6463 if (endFileAst == null) {
6464 endFileAst = new EndFileAst();
6465 }
6466 return endFileAst;
6467 }
6468
6469
6470
6471
6472
6473 private boolean isGlobalReferenced(String id) {
6474 IDAst idAst = globalIds.get(id);
6475 return idAst != null && idAst.isReferenced();
6476 }
6477
6478 private IDAst getID(String id) {
6479 id = resolveVariableIdentifier(id);
6480
6481 Map<String, IDAst> map;
6482 if (currentFunctionName == null) {
6483 map = globalIds;
6484 } else {
6485 Set<String> set = functionParameters.get(currentFunctionName);
6486
6487
6488
6489 if (set != null && set.contains(id)) {
6490 map = localIds.get(currentFunctionName);
6491 if (map == null) {
6492 map = new HashMap<String, IDAst>();
6493 localIds.put(currentFunctionName, map);
6494 }
6495 } else {
6496 map = globalIds;
6497 }
6498 }
6499 if (map == globalIds) {
6500
6501
6502
6503 if (functionProxies.get(id) != null) {
6504 throw parserException("cannot use " + id + " as a variable; it is a function");
6505 }
6506 ids.add(id);
6507 }
6508 IDAst idAst = map.get(id);
6509 if (idAst == null) {
6510 idAst = new IDAst(id, map == globalIds);
6511 idAst.offset = map.size();
6512 if (map == globalIds && !posix && ("SYMTAB".equals(id) || "FUNCTAB".equals(id))) {
6513
6514
6515 idAst.setArray(true);
6516 }
6517 map.put(id, idAst);
6518 }
6519 return idAst;
6520 }
6521
6522 private String resolveVariableIdentifier(String id) {
6523 if (currentFunctionName != null) {
6524 Set<String> parameters = functionParameters.get(currentFunctionName);
6525 if (parameters != null && parameters.contains(id)) {
6526 return id;
6527 }
6528 }
6529 return qualifyGlobalIdentifier(id);
6530 }
6531
6532 AST addID(String id) throws ParserException {
6533 IDAst retVal = getID(id);
6534 retVal.markReferenced();
6535
6536
6537
6538
6539
6540
6541
6542 return retVal;
6543 }
6544
6545 int addFunctionParameter(String functionName, String id) {
6546 int namespaceSeparator = functionName.indexOf("::");
6547 String unqualifiedFunctionName = namespaceSeparator < 0 ?
6548 functionName : functionName.substring(namespaceSeparator + 2);
6549 if (unqualifiedFunctionName.equals(id)) {
6550 throw parserException("cannot use " + id + " as a parameter; it is the function name");
6551 }
6552 Set<String> set = functionParameters.get(functionName);
6553 if (set == null) {
6554 set = new HashSet<String>();
6555 functionParameters.put(functionName, set);
6556 }
6557 if (set.contains(id)) {
6558 throw parserException("multiply defined parameter " + id + " in function " + functionName);
6559 }
6560 int retval = set.size();
6561 set.add(id);
6562 Map<String, IDAst> map = localIds.get(functionName);
6563 if (map == null) {
6564 map = new HashMap<String, IDAst>();
6565 localIds.put(functionName, map);
6566 }
6567 IDAst idAst = map.get(id);
6568 if (idAst == null) {
6569 idAst = new IDAst(id, map == globalIds);
6570 idAst.offset = map.size();
6571 map.put(id, idAst);
6572 }
6573
6574 return retval;
6575 }
6576
6577 IDAst getFunctionParameterIDAST(String functionName, String fIdString) {
6578 return localIds.get(functionName).get(fIdString);
6579 }
6580
6581 AST addArrayID(String id) throws ParserException {
6582 IDAst retVal = getID(id);
6583 retVal.markReferenced();
6584 retVal.setArray(true);
6585 return retVal;
6586 }
6587
6588 AST addFunctionDef(String functionName, AST paramList, AST block) {
6589 if (ids.contains(functionName)) {
6590 throw parserException("cannot use " + functionName + " as a function; it is a variable");
6591 }
6592 FunctionProxy functionProxy = functionProxies.get(functionName);
6593 if (functionProxy == null) {
6594 functionProxy = new FunctionProxy(functionName);
6595 functionProxies.put(functionName, functionProxy);
6596 }
6597 FunctionDefAst functionDef = new FunctionDefAst(functionName, paramList, block);
6598 functionProxy.setFunctionDefinition(functionDef);
6599 return functionDef;
6600 }
6601
6602 AST addFunctionCall(String id, AST paramList) {
6603 id = qualifyGlobalIdentifier(id);
6604 FunctionProxy functionProxy = functionProxies.get(id);
6605 if (functionProxy == null) {
6606 functionProxy = new FunctionProxy(id);
6607 functionProxies.put(id, functionProxy);
6608 }
6609 return new FunctionCallAst(functionProxy, paramList);
6610 }
6611
6612 Map<String, Tuple.IndirectFunctionTarget> indirectFunctionTargets() {
6613 if (cachedIndirectFunctionTargets != null) {
6614 return cachedIndirectFunctionTargets;
6615 }
6616 Map<String, Tuple.IndirectFunctionTarget> targets = new HashMap<String, Tuple.IndirectFunctionTarget>();
6617 for (Map.Entry<String, FunctionProxy> entry : functionProxies.entrySet()) {
6618 FunctionProxy proxy = entry.getValue();
6619 if (proxy.isDefined()) {
6620 targets
6621 .put(
6622 entry.getKey(),
6623 new Tuple.IndirectFunctionTarget(
6624 proxy,
6625 proxy.getFunctionParamCount(),
6626 collectArrayParameterIndexes(proxy.functionDefAst)));
6627 }
6628 }
6629 cachedIndirectFunctionTargets = Collections.unmodifiableMap(targets);
6630 return cachedIndirectFunctionTargets;
6631 }
6632
6633 AST addArrayReference(String id, AST idxAst, int lineNo) throws ParserException {
6634 return new ArrayReferenceAst(lineNo, addArrayID(id), idxAst);
6635 }
6636
6637
6638
6639
6640 AST addINTEGER(String integer) {
6641 try {
6642 return new IntegerAst(Long.parseLong(integer));
6643 } catch (NumberFormatException beyondLongRange) {
6644
6645
6646 return new DoubleAst(Double.parseDouble(integer));
6647 }
6648 }
6649
6650 AST addDOUBLE(String dbl) {
6651 return new DoubleAst(Double.valueOf(dbl));
6652 }
6653
6654 AST addSTRING(String str) {
6655 return new StringAst(str);
6656 }
6657
6658 AST addREGEXP(String localRegexp) {
6659 return new RegexpAst(localRegexp, false);
6660 }
6661
6662 AST addTYPED_REGEXP(String localRegexp) {
6663 return new RegexpAst(localRegexp, true);
6664 }
6665 }
6666
6667 private ParserException parserException(String msg) {
6668 return new ParserException(
6669 msg,
6670 currentScriptSource.getDescription(),
6671 reader.getLineNumber());
6672 }
6673 }