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