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