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