1 /* 2 * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.javatest.regtest.config; 27 28 import static com.sun.javatest.regtest.config.Expr.Token.*; 29 30 /** 31 * Class to support simple expressions for @requires. 32 */ 33 public abstract class Expr { 34 35 public static class Fault extends Exception { 36 private static final long serialVersionUID = 1L; 37 Fault(String msg) { 38 super(msg); 39 } 40 }; 41 42 public interface Context { 43 boolean isValidName(String name); 44 String get(String name) throws Fault; 45 } 46 47 public static Expr parse(String s, Context c) throws Fault { 48 Parser p = new Parser(s, c); 49 return p.parse(); 50 } 51 52 public abstract String eval(Context c) throws Fault; 53 54 public boolean evalBoolean(Context c) throws Fault { 55 String s = eval(c); 56 if (s.equals("true")) { 57 return true; 58 } else if (s.equals("false")) { 59 return false; 60 } else { 61 throw new Fault("invalid boolean value: `" + s + "' for expression `" + this + "'"); 62 } 63 } 64 65 public long evalNumber(Context c) throws Fault { 66 String s = eval(c); 67 try { 68 return Long.parseLong(s); 69 } catch (NumberFormatException ex) { 70 throw new Fault("invalid numeric value: " + s); 71 } 72 } 73 74 abstract int precedence(); 75 76 Expr order() { 77 return this; 78 } 79 80 static class Parser { 81 82 Parser(String text, Context context) throws Fault { 83 this.context = context; 84 this.text = text; 85 nextToken(); 86 } 87 88 Expr parse() throws Fault { 89 Expr e = parseExpr(); 90 expect(END); 91 return e; 92 } 93 94 Expr parseExpr() throws Fault { 95 for (Expr e = parseTerm(); e != null; e = e.order()) { 96 switch (token) { 97 case ADD: 98 nextToken(); 99 e = new AddExpr(e, parseTerm()); 100 break; 101 case AND: 102 nextToken(); 103 e = new AndExpr(e, parseTerm()); 104 break; 105 case DIV: 106 nextToken(); 107 e = new DivideExpr(e, parseTerm()); 108 break; 109 case EQ: 110 nextToken(); 111 e = new EqualExpr(e, parseTerm()); 112 break; 113 case GE: 114 nextToken(); 115 e = new GreaterEqualExpr(e, parseTerm()); 116 break; 117 case GT: 118 nextToken(); 119 e = new GreaterExpr(e, parseTerm()); 120 break; 121 case LE: 122 nextToken(); 123 e = new LessEqualExpr(e, parseTerm()); 124 break; 125 case LT: 126 nextToken(); 127 e = new LessExpr(e, parseTerm()); 128 break; 129 case MATCH: 130 nextToken(); 131 e = new MatchExpr(e, parseTerm()); 132 break; 133 case MUL: 134 nextToken(); 135 e = new MultiplyExpr(e, parseTerm()); 136 break; 137 case NE: 138 nextToken(); 139 e = new NotEqualExpr(e, parseTerm()); 140 break; 141 case OR: 142 nextToken(); 143 e = new OrExpr(e, parseTerm()); 144 break; 145 case REM: 146 nextToken(); 147 e = new RemainderExpr(e, parseTerm()); 148 break; 149 case SUB: 150 nextToken(); 151 e = new SubtractExpr(e, parseTerm()); 152 break; 153 default: 154 return e; 155 } 156 } 157 // bogus return to keep compiler happy 158 return null; 159 } 160 161 Expr parseTerm() throws Fault { 162 switch (token) { 163 case NAME: 164 String id = idValue; 165 nextToken(); 166 if (context.isValidName(id)) 167 return new NameExpr(id); 168 else 169 throw new Fault("invalid name: " + id); 170 171 case NOT: 172 nextToken(); 173 return new NotExpr(parseTerm()); 174 175 case NUMBER: 176 case TRUE: 177 case FALSE: 178 String num = idValue; 179 nextToken(); 180 return new NumberExpr(num); 181 182 case LPAREN: 183 nextToken(); 184 Expr e = parseExpr(); 185 expect(RPAREN); 186 return new ParenExpr(e); 187 188 case STRING: 189 String s = idValue; 190 nextToken(); 191 return new StringExpr(s); 192 193 default: 194 throw new Fault(token.getText() + " not expected"); 195 } 196 } 197 198 private void expect(Token t) throws Fault { 199 if (t == token) { 200 nextToken(); 201 } else { 202 throw new Fault(t.getText() + "expected, but " + token.getText() + " found"); 203 } 204 } 205 206 private void nextToken() throws Fault { 207 while (index < text.length()) { 208 char c = text.charAt(index++); 209 switch (c) { 210 case ' ': 211 case '\t': 212 continue; 213 214 case '&': 215 token = AND; 216 return; 217 218 case '|': 219 token = OR; 220 return; 221 222 case '+': 223 token = ADD; 224 return; 225 226 case '-': 227 token = SUB; 228 return; 229 230 case '*': 231 token = MUL; 232 return; 233 234 case '/': 235 token = DIV; 236 return; 237 238 case '%': 239 token = REM; 240 return; 241 242 case '(': 243 token = LPAREN; 244 return; 245 246 case ')': 247 token = RPAREN; 248 return; 249 250 case '<': 251 if (index < text.length() && text.charAt(index) == '=') { 252 token = LE; 253 index++; 254 } else { 255 token = LT; 256 } 257 return; 258 259 case '>': 260 if (index < text.length() && text.charAt(index) == '=') { 261 token = GE; 262 index++; 263 } else { 264 token = GT; 265 } 266 return; 267 268 case '~': 269 if (index < text.length() && text.charAt(index) == '=') { 270 token = MATCH; 271 index++; 272 } else { 273 throw new Fault("unexpected character after `~'"); 274 } 275 return; 276 277 case '=': 278 if (index < text.length() && text.charAt(index) == '=') { 279 token = EQ; 280 index++; 281 } else { 282 throw new Fault("unexpected character after `='"); 283 } 284 return; 285 286 case '!': 287 if (index < text.length() && text.charAt(index) == '=') { 288 token = NE; 289 index++; 290 } else { 291 token = NOT; 292 } 293 return; 294 295 case '\"': 296 StringBuffer sb = new StringBuffer(); 297 if (index < text.length()) { 298 c = text.charAt(index); 299 index++; 300 } else { 301 throw new Fault("invalid string constant"); 302 } 303 while (c != '\"') { 304 if (c == '\\') { 305 if (index < text.length()) { 306 c = text.charAt(index); 307 index++; 308 } else { 309 throw new Fault("invalid string constant"); 310 } 311 switch (c) { 312 // standard Java escapes; no Unicode (for now) 313 case 'b': c = '\b'; break; 314 case 'f': c = '\f'; break; 315 case 'n': c = '\n'; break; 316 case 'r': c = '\r'; break; 317 case 't': c = '\t'; break; 318 case '\\': c = '\\'; break; 319 case '\'': c = '\''; break; 320 case '\"': c = '\"'; break; 321 default: 322 throw new Fault("invalid string constant"); 323 } 324 } 325 sb.append(c); 326 if (index < text.length()) { 327 c = text.charAt(index); 328 index++; 329 } else { 330 break; 331 } 332 } 333 if (c == '\"') { 334 token = STRING; 335 idValue = String.valueOf(sb); 336 } else { 337 throw new Fault("invalid string constant"); 338 } 339 return; 340 341 default: 342 if (Character.isUnicodeIdentifierStart(c)) { 343 idValue = String.valueOf(c); 344 while (index < text.length()) { 345 c = text.charAt(index); 346 if (Character.isUnicodeIdentifierPart(c) || c == '.') { 347 if (!Character.isIdentifierIgnorable(c)) { 348 idValue += c; 349 } 350 index++; 351 } else { 352 break; 353 } 354 } 355 if (idValue.equalsIgnoreCase("true")) { 356 token = TRUE; 357 } else if (idValue.equalsIgnoreCase("false")) { 358 token = FALSE; 359 } else { 360 token = NAME; 361 } 362 return; 363 } else if (Character.isDigit(c)) { 364 int start = index - 1; 365 while (index < text.length()) { 366 c = text.charAt(index); 367 if (Character.isDigit(c)) { 368 index++; 369 } else { 370 switch (c) { 371 case 'k': case 'K': 372 case 'm': case 'M': 373 case 'g': case 'G': 374 index++; 375 } 376 break; 377 } 378 } 379 token = NUMBER; 380 idValue = text.substring(start, index); 381 return; 382 } else { 383 throw new Fault("unrecognized character: `" + c + "'"); 384 } 385 } 386 } 387 token = END; 388 } 389 390 private final Context context; 391 private final String text; 392 private int index; 393 private Token token; 394 private String idValue; 395 } 396 397 static enum Token { 398 ADD("+"), 399 AND("&"), 400 DIV("/"), 401 END("<end-of-expression>"), 402 ERROR("<error>"), 403 EQ("="), 404 FALSE("false"), 405 GE(">="), 406 GT(">"), 407 LE("<="), 408 LPAREN("("), 409 LT("<"), 410 MATCH("~="), 411 MUL("*"), 412 NAME("<name>"), 413 NE("!="), 414 NOT("!"), 415 NUMBER("<number>"), 416 OR("|"), 417 REM("%"), 418 RPAREN(")"), 419 STRING("<string>"), 420 SUB("-"), 421 TRUE("true"); 422 Token(String text) { 423 this.text = text; 424 } 425 String getText() { 426 return text.startsWith("<") ? text : "'" + text + "'"; 427 } 428 final String text; 429 }; 430 431 protected static final int PREC_LIT = 6; 432 protected static final int PREC_NOT = 6; 433 protected static final int PREC_NUM = 6; 434 protected static final int PREC_PRN = 6; 435 protected static final int PREC_TRM = 6; 436 protected static final int PREC_DIV = 5; 437 protected static final int PREC_MUL = 5; 438 protected static final int PREC_REM = 5; 439 protected static final int PREC_ADD = 4; 440 protected static final int PREC_SUB = 4; 441 protected static final int PREC_GE = 3; 442 protected static final int PREC_GT = 3; 443 protected static final int PREC_LE = 3; 444 protected static final int PREC_LT = 3; 445 protected static final int PREC_EQ = 2; 446 protected static final int PREC_NE = 2; 447 protected static final int PREC_AND = 1; 448 protected static final int PREC_OR = 0; 449 450 //-------------------------------------------------------------------------- 451 452 abstract static class BinaryExpr extends Expr { 453 454 BinaryExpr(Expr left, Expr right) { 455 this.left = left; 456 this.right = right; 457 } 458 459 @Override 460 Expr order() { 461 if (precedence() > left.precedence() && left instanceof BinaryExpr) { 462 BinaryExpr e = (BinaryExpr) left; 463 left = e.right; 464 e.right = order(); 465 return e; 466 } else { 467 return this; 468 } 469 } 470 protected Expr left; 471 protected Expr right; 472 } 473 474 //-------------------------------------------------------------------------- 475 476 static class AddExpr extends BinaryExpr { 477 478 AddExpr(Expr left, Expr right) { 479 super(left, right); 480 } 481 482 public String eval(Context c) throws Fault { 483 return String.valueOf(left.evalNumber(c) + right.evalNumber(c)); 484 } 485 486 int precedence() { 487 return PREC_ADD; 488 } 489 490 @Override 491 public String toString() { 492 return "`" + left + "+" + right + "'"; 493 } 494 } 495 496 //-------------------------------------------------------------------------- 497 498 static class AndExpr extends BinaryExpr { 499 500 AndExpr(Expr left, Expr right) { 501 super(left, right); 502 } 503 504 public String eval(Context c) throws Fault { 505 return String.valueOf(left.evalBoolean(c) & right.evalBoolean(c)); 506 } 507 508 int precedence() { 509 return PREC_AND; 510 } 511 512 @Override 513 public String toString() { 514 return "`" + left + "&" + right + "'"; 515 } 516 } 517 518 //-------------------------------------------------------------------------- 519 520 static class DivideExpr extends BinaryExpr { 521 522 DivideExpr(Expr left, Expr right) { 523 super(left, right); 524 } 525 526 public String eval(Context c) throws Fault { 527 return String.valueOf(left.evalNumber(c) / right.evalNumber(c)); 528 } 529 530 int precedence() { 531 return PREC_DIV; 532 } 533 534 @Override 535 public String toString() { 536 return "`" + left + "/" + right + "'"; 537 } 538 } 539 540 //-------------------------------------------------------------------------- 541 542 static class EqualExpr extends BinaryExpr { 543 544 EqualExpr(Expr left, Expr right) { 545 super(left, right); 546 } 547 548 public String eval(Context c) throws Fault { 549 return String.valueOf(left.eval(c).equalsIgnoreCase(right.eval(c))); 550 } 551 552 int precedence() { 553 return PREC_EQ; 554 } 555 556 @Override 557 public String toString() { 558 return "`" + left + "==" + right + "'"; 559 } 560 } 561 562 //-------------------------------------------------------------------------- 563 564 static class GreaterExpr extends BinaryExpr { 565 566 GreaterExpr(Expr left, Expr right) { 567 super(left, right); 568 } 569 570 public String eval(Context c) throws Fault { 571 return String.valueOf(left.evalNumber(c) > right.evalNumber(c)); 572 } 573 574 int precedence() { 575 return PREC_GT; 576 } 577 578 @Override 579 public String toString() { 580 return "`" + left + ">" + right + "'"; 581 } 582 } 583 584 //-------------------------------------------------------------------------- 585 586 static class GreaterEqualExpr extends BinaryExpr { 587 588 GreaterEqualExpr(Expr left, Expr right) { 589 super(left, right); 590 } 591 592 public String eval(Context c) throws Fault { 593 return String.valueOf(left.evalNumber(c) >= right.evalNumber(c)); 594 } 595 596 int precedence() { 597 return PREC_GE; 598 } 599 600 @Override 601 public String toString() { 602 return "`" + left + ">=" + right + "'"; 603 } 604 } 605 606 //-------------------------------------------------------------------------- 607 608 static class LessExpr extends BinaryExpr { 609 610 LessExpr(Expr left, Expr right) { 611 super(left, right); 612 } 613 614 public String eval(Context c) throws Fault { 615 return String.valueOf(left.evalNumber(c) < right.evalNumber(c)); 616 } 617 618 int precedence() { 619 return PREC_LT; 620 } 621 622 @Override 623 public String toString() { 624 return "`" + left + "<" + right + "'"; 625 } 626 } 627 628 //-------------------------------------------------------------------------- 629 630 static class LessEqualExpr extends BinaryExpr { 631 632 LessEqualExpr(Expr left, Expr right) { 633 super(left, right); 634 } 635 636 public String eval(Context c) throws Fault { 637 return String.valueOf(left.evalNumber(c) <= right.evalNumber(c)); 638 } 639 640 int precedence() { 641 return PREC_LE; 642 } 643 644 @Override 645 public String toString() { 646 return "`" + left + "<=" + right + "'"; 647 } 648 } 649 650 //-------------------------------------------------------------------------- 651 652 static class MatchExpr extends BinaryExpr { 653 654 MatchExpr(Expr left, Expr right) { 655 super(left, right); 656 } 657 658 public String eval(Context c) throws Fault { 659 return String.valueOf(left.eval(c).matches(right.eval(c))); 660 } 661 662 int precedence() { 663 return PREC_EQ; 664 } 665 666 @Override 667 public String toString() { 668 return "`" + left + "~=" + right + "'"; 669 } 670 } 671 672 //-------------------------------------------------------------------------- 673 674 static class MultiplyExpr extends BinaryExpr { 675 676 MultiplyExpr(Expr left, Expr right) { 677 super(left, right); 678 } 679 680 public String eval(Context c) throws Fault { 681 return String.valueOf(left.evalNumber(c) * right.evalNumber(c)); 682 } 683 684 int precedence() { 685 return PREC_MUL; 686 } 687 688 @Override 689 public String toString() { 690 return "`" + left + "*" + right + "'"; 691 } 692 } 693 694 //-------------------------------------------------------------------------- 695 696 static class NameExpr extends Expr { 697 698 NameExpr(String name) { 699 this.name = name; 700 } 701 702 public String eval(Context c) throws Fault { 703 String v = c.get(name); 704 if (v == null) 705 throw new Fault("name not defined: " + name); 706 return v; 707 } 708 709 int precedence() { 710 return PREC_TRM; 711 } 712 713 @Override 714 public String toString() { 715 return name; 716 } 717 718 private final String name; 719 } 720 721 //-------------------------------------------------------------------------- 722 723 static class NotEqualExpr extends BinaryExpr { 724 725 NotEqualExpr(Expr left, Expr right) { 726 super(left, right); 727 } 728 729 public String eval(Context c) throws Fault { 730 return String.valueOf(!left.eval(c).equalsIgnoreCase(right.eval(c))); 731 } 732 733 int precedence() { 734 return PREC_NE; 735 } 736 737 @Override 738 public String toString() { 739 return "`" + left + "!=" + right + "'"; 740 } 741 } 742 743 //-------------------------------------------------------------------------- 744 745 static class NotExpr extends Expr { 746 747 NotExpr(Expr expr) { 748 this.expr = expr; 749 } 750 751 public String eval(Context c) throws Fault { 752 return String.valueOf(!expr.evalBoolean(c)); 753 } 754 755 int precedence() { 756 return PREC_NOT; 757 } 758 759 @Override 760 public String toString() { 761 return "!" + expr; 762 } 763 764 private final Expr expr; 765 } 766 767 //-------------------------------------------------------------------------- 768 769 static class NumberExpr extends Expr { 770 771 NumberExpr(String value) { 772 this.value = value; 773 } 774 775 public String eval(Context c) throws Fault { 776 long scale; 777 char lastCh = value.charAt(value.length() -1); 778 switch (lastCh) { 779 case 'k': case 'K': scale = 1024; break; 780 case 'm': case 'M': scale = 1024 * 1024; break; 781 case 'g': case 'G': scale = 1024 * 1024 * 1024; break; 782 default: 783 return value; 784 } 785 try { 786 String s = value.substring(0, value.length() - 1); 787 return String.valueOf(Long.parseLong(s) * scale); 788 } catch (NumberFormatException ex) { 789 throw new Fault("invalid numeric value: " + value); 790 } 791 } 792 793 int precedence() { 794 return PREC_NUM; 795 } 796 797 @Override 798 public String toString() { 799 return value; 800 } 801 802 private final String value; 803 } 804 805 //-------------------------------------------------------------------------- 806 807 static class OrExpr extends BinaryExpr { 808 809 OrExpr(Expr left, Expr right) { 810 super(left, right); 811 } 812 813 public String eval(Context c) throws Fault { 814 return String.valueOf(left.evalBoolean(c) | right.evalBoolean(c)); 815 } 816 817 int precedence() { 818 return PREC_OR; 819 } 820 821 @Override 822 public String toString() { 823 return "`" + left + "|" + right + "'"; 824 } 825 } 826 827 //-------------------------------------------------------------------------- 828 829 static class ParenExpr extends Expr { 830 831 ParenExpr(Expr expr) { 832 this.expr = expr; 833 } 834 835 public String eval(Context c) throws Fault { 836 return expr.eval(c); 837 } 838 839 int precedence() { 840 return PREC_PRN; 841 } 842 843 @Override 844 public String toString() { 845 return "(" + expr + ")"; 846 } 847 848 private final Expr expr; 849 } 850 851 //-------------------------------------------------------------------------- 852 853 static class RemainderExpr extends BinaryExpr { 854 855 RemainderExpr(Expr left, Expr right) { 856 super(left, right); 857 } 858 859 public String eval(Context c) throws Fault { 860 return String.valueOf(left.evalNumber(c) % right.evalNumber(c)); 861 } 862 863 int precedence() { 864 return PREC_REM; 865 } 866 867 @Override 868 public String toString() { 869 return "`" + left + "%" + right + "'"; 870 } 871 } 872 873 //-------------------------------------------------------------------------- 874 875 static class StringExpr extends Expr { 876 877 StringExpr(String value) { 878 this.value = value; 879 } 880 881 public String eval(Context c) throws Fault { 882 return value; 883 } 884 885 int precedence() { 886 return PREC_LIT; 887 } 888 889 @Override 890 public String toString() { 891 return '"' + value + '"'; 892 } 893 894 private final String value; 895 } 896 897 //-------------------------------------------------------------------------- 898 899 static class SubtractExpr extends BinaryExpr { 900 901 SubtractExpr(Expr left, Expr right) { 902 super(left, right); 903 } 904 905 public String eval(Context c) throws Fault { 906 return String.valueOf(left.evalNumber(c) - right.evalNumber(c)); 907 } 908 909 int precedence() { 910 return PREC_SUB; 911 } 912 913 @Override 914 public String toString() { 915 return "`" + left + "-" + right + "'"; 916 } 917 } 918 919 }