1 /* 2 * Copyright 2016 Google Inc. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.turbine.parse; 18 19 import static com.google.common.collect.Iterables.getOnlyElement; 20 21 import com.google.common.collect.ImmutableList; 22 import com.google.errorprone.annotations.CheckReturnValue; 23 import com.google.turbine.diag.TurbineError; 24 import com.google.turbine.diag.TurbineError.ErrorKind; 25 import com.google.turbine.model.Const; 26 import com.google.turbine.model.TurbineConstantTypeKind; 27 import com.google.turbine.tree.Tree; 28 import com.google.turbine.tree.Tree.AnnoExpr; 29 import com.google.turbine.tree.Tree.ClassLiteral; 30 import com.google.turbine.tree.Tree.ClassTy; 31 import com.google.turbine.tree.Tree.Expression; 32 import com.google.turbine.tree.Tree.Ident; 33 import com.google.turbine.tree.TurbineOperatorKind; 34 import java.util.Optional; 35 import org.jspecify.nullness.Nullable; 36 37 /** A parser for compile-time constant expressions. */ 38 public class ConstExpressionParser { 39 40 Token token; 41 private int position; 42 private final Lexer lexer; 43 ConstExpressionParser(Lexer lexer, Token token, int position)44 public ConstExpressionParser(Lexer lexer, Token token, int position) { 45 this.lexer = lexer; 46 this.token = token; 47 this.position = position; 48 } 49 operator(Token token)50 private static @Nullable TurbineOperatorKind operator(Token token) { 51 switch (token) { 52 case ASSIGN: 53 // TODO(cushon): only allow in annotations? 54 return TurbineOperatorKind.ASSIGN; 55 case MULT: 56 return TurbineOperatorKind.MULT; 57 case DIV: 58 return TurbineOperatorKind.DIVIDE; 59 case MOD: 60 return TurbineOperatorKind.MODULO; 61 case PLUS: 62 return TurbineOperatorKind.PLUS; 63 case MINUS: 64 return TurbineOperatorKind.MINUS; 65 case LTLT: 66 return TurbineOperatorKind.SHIFT_LEFT; 67 case GTGT: 68 return TurbineOperatorKind.SHIFT_RIGHT; 69 case GTGTGT: 70 return TurbineOperatorKind.UNSIGNED_SHIFT_RIGHT; 71 case LT: 72 return TurbineOperatorKind.LESS_THAN; 73 case GT: 74 return TurbineOperatorKind.GREATER_THAN; 75 case LTE: 76 return TurbineOperatorKind.LESS_THAN_EQ; 77 case GTE: 78 return TurbineOperatorKind.GREATER_THAN_EQ; 79 case EQ: 80 return TurbineOperatorKind.EQUAL; 81 case NOTEQ: 82 return TurbineOperatorKind.NOT_EQUAL; 83 case AND: 84 return TurbineOperatorKind.BITWISE_AND; 85 case OR: 86 return TurbineOperatorKind.BITWISE_OR; 87 case XOR: 88 return TurbineOperatorKind.BITWISE_XOR; 89 case ANDAND: 90 return TurbineOperatorKind.AND; 91 case OROR: 92 return TurbineOperatorKind.OR; 93 case COND: 94 return TurbineOperatorKind.TERNARY; 95 default: 96 return null; 97 } 98 } 99 primary(boolean negate)100 private @Nullable Expression primary(boolean negate) { 101 switch (token) { 102 case INT_LITERAL: 103 return finishLiteral(TurbineConstantTypeKind.INT, negate); 104 case DOUBLE_LITERAL: 105 return finishLiteral(TurbineConstantTypeKind.DOUBLE, negate); 106 case LONG_LITERAL: 107 return finishLiteral(TurbineConstantTypeKind.LONG, negate); 108 case FLOAT_LITERAL: 109 return finishLiteral(TurbineConstantTypeKind.FLOAT, negate); 110 case TRUE: 111 { 112 int pos = position; 113 eat(); 114 return new Tree.Literal( 115 pos, TurbineConstantTypeKind.BOOLEAN, new Const.BooleanValue(true)); 116 } 117 case FALSE: 118 { 119 int pos = position; 120 eat(); 121 return new Tree.Literal( 122 pos, TurbineConstantTypeKind.BOOLEAN, new Const.BooleanValue(false)); 123 } 124 case CHAR_LITERAL: 125 return finishLiteral(TurbineConstantTypeKind.CHAR, negate); 126 case STRING_LITERAL: 127 return finishLiteral(TurbineConstantTypeKind.STRING, false); 128 case PLUS: 129 eat(); 130 return unaryRest(TurbineOperatorKind.UNARY_PLUS); 131 case MINUS: 132 eat(); 133 return unaryRest(TurbineOperatorKind.NEG); 134 case NOT: 135 eat(); 136 return unaryRest(TurbineOperatorKind.NOT); 137 case TILDE: 138 eat(); 139 return unaryRest(TurbineOperatorKind.BITWISE_COMP); 140 case LPAREN: 141 return maybeCast(); 142 case LBRACE: 143 int pos = position; 144 eat(); 145 return arrayInitializer(pos); 146 case IDENT: 147 return qualIdent(); 148 case BYTE: 149 return primitiveClassLiteral(TurbineConstantTypeKind.BYTE); 150 case CHAR: 151 return primitiveClassLiteral(TurbineConstantTypeKind.CHAR); 152 case DOUBLE: 153 return primitiveClassLiteral(TurbineConstantTypeKind.DOUBLE); 154 case FLOAT: 155 return primitiveClassLiteral(TurbineConstantTypeKind.FLOAT); 156 case INT: 157 return primitiveClassLiteral(TurbineConstantTypeKind.INT); 158 case LONG: 159 return primitiveClassLiteral(TurbineConstantTypeKind.LONG); 160 case SHORT: 161 return primitiveClassLiteral(TurbineConstantTypeKind.SHORT); 162 case BOOLEAN: 163 return primitiveClassLiteral(TurbineConstantTypeKind.BOOLEAN); 164 case VOID: 165 eat(); 166 return finishClassLiteral(position, new Tree.VoidTy(position)); 167 case AT: 168 return annotation(); 169 default: 170 return null; 171 } 172 } 173 primitiveClassLiteral(TurbineConstantTypeKind type)174 private Expression primitiveClassLiteral(TurbineConstantTypeKind type) { 175 eat(); 176 return finishClassLiteral(position, new Tree.PrimTy(position, ImmutableList.of(), type)); 177 } 178 maybeCast()179 private Expression maybeCast() { 180 eat(); 181 switch (token) { 182 case BOOLEAN: 183 eat(); 184 return castTail(TurbineConstantTypeKind.BOOLEAN); 185 case BYTE: 186 eat(); 187 return castTail(TurbineConstantTypeKind.BYTE); 188 case SHORT: 189 eat(); 190 return castTail(TurbineConstantTypeKind.SHORT); 191 case INT: 192 eat(); 193 return castTail(TurbineConstantTypeKind.INT); 194 case LONG: 195 eat(); 196 return castTail(TurbineConstantTypeKind.LONG); 197 case CHAR: 198 eat(); 199 return castTail(TurbineConstantTypeKind.CHAR); 200 case DOUBLE: 201 eat(); 202 return castTail(TurbineConstantTypeKind.DOUBLE); 203 case FLOAT: 204 eat(); 205 return castTail(TurbineConstantTypeKind.FLOAT); 206 default: 207 return notCast(); 208 } 209 } 210 notCast()211 private @Nullable Expression notCast() { 212 Expression expr = expression(null); 213 if (expr == null) { 214 return null; 215 } 216 if (token != Token.RPAREN) { 217 return null; 218 } 219 eat(); 220 if (expr.kind() == Tree.Kind.CONST_VAR_NAME) { 221 Tree.ConstVarName cvar = (Tree.ConstVarName) expr; 222 switch (token) { 223 case INT_LITERAL: 224 case FLOAT_LITERAL: 225 case TRUE: 226 case FALSE: 227 case CHAR_LITERAL: 228 case STRING_LITERAL: 229 case NOT: 230 case TILDE: 231 case IDENT: 232 Expression expression = primary(false); 233 if (expression == null) { 234 throw error(ErrorKind.EXPRESSION_ERROR); 235 } 236 return new Tree.TypeCast(position, asClassTy(cvar.position(), cvar.name()), expression); 237 default: 238 return new Tree.Paren(position, expr); 239 } 240 } else { 241 return new Tree.Paren(position, expr); 242 } 243 } 244 asClassTy(int pos, ImmutableList<Tree.Ident> names)245 private static ClassTy asClassTy(int pos, ImmutableList<Tree.Ident> names) { 246 ClassTy cty = null; 247 for (Tree.Ident bit : names) { 248 cty = new ClassTy(pos, Optional.ofNullable(cty), bit, ImmutableList.of(), ImmutableList.of()); 249 } 250 return cty; 251 } 252 eat()253 private void eat() { 254 token = lexer.next(); 255 position = lexer.position(); 256 } 257 arrayInitializer(int pos)258 private @Nullable Expression arrayInitializer(int pos) { 259 if (token == Token.RBRACE) { 260 eat(); 261 return new Tree.ArrayInit(pos, ImmutableList.<Tree.Expression>of()); 262 } 263 264 ImmutableList.Builder<Tree.Expression> exprs = ImmutableList.builder(); 265 OUTER: 266 while (true) { 267 if (token == Token.RBRACE) { 268 eat(); 269 break OUTER; 270 } 271 Expression item = expression(null); 272 if (item == null) { 273 return null; 274 } 275 exprs.add(item); 276 switch (token) { 277 case COMMA: 278 eat(); 279 break; 280 case RBRACE: 281 eat(); 282 break OUTER; 283 default: 284 return null; 285 } 286 } 287 return new Tree.ArrayInit(pos, exprs.build()); 288 } 289 290 /** Finish hex, decimal, octal, and binary integer literals (see JLS 3.10.1). */ finishLiteral(TurbineConstantTypeKind kind, boolean negate)291 private Expression finishLiteral(TurbineConstantTypeKind kind, boolean negate) { 292 int pos = position; 293 String text = ident().value(); 294 Const.Value value; 295 switch (kind) { 296 case INT: 297 { 298 int radix = 10; 299 if (text.startsWith("0x") || text.startsWith("0X")) { 300 text = text.substring(2); 301 radix = 0x10; 302 } else if (isOctal(text)) { 303 radix = 010; 304 } else if (text.startsWith("0b") || text.startsWith("0B")) { 305 text = text.substring(2); 306 radix = 0b10; 307 } 308 if (negate) { 309 text = "-" + text; 310 } 311 long longValue = parseLong(text, radix); 312 if (radix == 10) { 313 if (longValue != (int) longValue) { 314 throw error(ErrorKind.INVALID_LITERAL, text); 315 } 316 } else { 317 if (Math.abs(longValue) >> 32 != 0) { 318 throw error(ErrorKind.INVALID_LITERAL, text); 319 } 320 } 321 value = new Const.IntValue((int) longValue); 322 break; 323 } 324 case LONG: 325 { 326 int radix = 10; 327 if (text.startsWith("0x") || text.startsWith("0X")) { 328 text = text.substring(2); 329 radix = 0x10; 330 } else if (isOctal(text)) { 331 radix = 010; 332 } else if (text.startsWith("0b") || text.startsWith("0B")) { 333 text = text.substring(2); 334 radix = 0b10; 335 } 336 if (negate) { 337 text = "-" + text; 338 } 339 if (text.endsWith("L") || text.endsWith("l")) { 340 text = text.substring(0, text.length() - 1); 341 } 342 value = new Const.LongValue(parseLong(text, radix)); 343 break; 344 } 345 case CHAR: 346 value = new Const.CharValue(text.charAt(0)); 347 break; 348 case FLOAT: 349 try { 350 value = new Const.FloatValue(Float.parseFloat(text.replace("_", ""))); 351 } catch (NumberFormatException e) { 352 throw error(ErrorKind.INVALID_LITERAL, text); 353 } 354 break; 355 case DOUBLE: 356 try { 357 value = new Const.DoubleValue(Double.parseDouble(text.replace("_", ""))); 358 } catch (NumberFormatException e) { 359 throw error(ErrorKind.INVALID_LITERAL, text); 360 } 361 break; 362 case STRING: 363 value = new Const.StringValue(text); 364 break; 365 default: 366 throw new AssertionError(kind); 367 } 368 eat(); 369 return new Tree.Literal(pos, kind, value); 370 } 371 isOctal(String text)372 static boolean isOctal(String text) { 373 if (!text.startsWith("0")) { 374 return false; 375 } 376 if (text.length() <= 1) { 377 return false; 378 } 379 char next = text.charAt(1); 380 return Character.isDigit(next) || next == '_'; 381 } 382 383 /** 384 * Parse the string as a signed long. 385 * 386 * <p>{@link Long#parseLong} doesn't accept {@link Long#MIN_VALUE}. 387 */ parseLong(String text, int radix)388 private long parseLong(String text, int radix) { 389 long r = 0; 390 boolean neg = text.startsWith("-"); 391 if (neg) { 392 text = text.substring(1); 393 } 394 for (int i = 0; i < text.length(); i++) { 395 char c = text.charAt(i); 396 int digit; 397 if ('0' <= c && c <= '9') { 398 digit = c - '0'; 399 } else if ('a' <= c && c <= 'f') { 400 digit = 10 + (c - 'a'); 401 } else if ('A' <= c && c <= 'F') { 402 digit = 10 + (c - 'A'); 403 } else if (c == '_') { 404 continue; 405 } else { 406 throw error(ErrorKind.INVALID_LITERAL, text); 407 } 408 r = (r * radix) + digit; 409 } 410 if (neg) { 411 r = -r; 412 } 413 return r; 414 } 415 unaryRest(TurbineOperatorKind op)416 private @Nullable Expression unaryRest(TurbineOperatorKind op) { 417 boolean negate = op == TurbineOperatorKind.NEG; 418 Expression expr = primary(negate); 419 if (expr == null) { 420 return null; 421 } 422 if (negate && expr.kind() == Tree.Kind.LITERAL) { 423 Tree.Literal lit = (Tree.Literal) expr; 424 switch (lit.tykind()) { 425 case INT: 426 case LONG: 427 return expr; 428 default: 429 break; 430 } 431 } 432 return new Tree.Unary(position, expr, op); 433 } 434 qualIdent()435 private @Nullable Expression qualIdent() { 436 int pos = position; 437 ImmutableList.Builder<Ident> bits = ImmutableList.builder(); 438 bits.add(ident()); 439 eat(); 440 while (token == Token.DOT) { 441 eat(); 442 switch (token) { 443 case IDENT: 444 bits.add(ident()); 445 break; 446 case CLASS: 447 // TODO(cushon): only allow in annotations? 448 eat(); 449 return new Tree.ClassLiteral(pos, asClassTy(pos, bits.build())); 450 default: 451 return null; 452 } 453 eat(); 454 } 455 if (token == Token.LBRACK) { 456 return finishClassLiteral(pos, asClassTy(pos, bits.build())); 457 } 458 return new Tree.ConstVarName(pos, bits.build()); 459 } 460 ident()461 private Ident ident() { 462 return new Ident(lexer.position(), lexer.stringValue()); 463 } 464 finishClassLiteral(int pos, Tree.Type type)465 private @Nullable Expression finishClassLiteral(int pos, Tree.Type type) { 466 while (token == Token.LBRACK) { 467 eat(); 468 if (token != Token.RBRACK) { 469 return null; 470 } 471 eat(); 472 type = new Tree.ArrTy(position, ImmutableList.of(), type); 473 } 474 if (token != Token.DOT) { 475 return null; 476 } 477 eat(); 478 if (token != Token.CLASS) { 479 return null; 480 } 481 eat(); 482 return new ClassLiteral(pos, type); 483 } 484 expression()485 public @Nullable Expression expression() { 486 Expression result = expression(null); 487 switch (token) { 488 case EOF: 489 case SEMI: 490 // TODO(cushon): only allow in annotations? 491 case COMMA: 492 case RPAREN: 493 return result; 494 default: 495 return null; 496 } 497 } 498 expression(TurbineOperatorKind.Precedence prec)499 private @Nullable Expression expression(TurbineOperatorKind.Precedence prec) { 500 Expression term1 = primary(false); 501 if (term1 == null) { 502 return null; 503 } 504 return expression(term1, prec); 505 } 506 expression(Expression term1, TurbineOperatorKind.Precedence prec)507 private @Nullable Expression expression(Expression term1, TurbineOperatorKind.Precedence prec) { 508 while (true) { 509 if (token == Token.EOF) { 510 return term1; 511 } 512 TurbineOperatorKind op = operator(token); 513 if (op == null) { 514 return term1; 515 } 516 if (prec != null && op.prec().rank() <= prec.rank()) { 517 return term1; 518 } 519 eat(); 520 switch (op) { 521 case TERNARY: 522 term1 = ternary(term1); 523 break; 524 case ASSIGN: 525 term1 = assign(term1, op); 526 break; 527 default: 528 int pos = position; 529 Expression term2 = expression(op.prec()); 530 if (term2 == null) { 531 return null; 532 } 533 term1 = new Tree.Binary(pos, term1, term2, op); 534 } 535 if (term1 == null) { 536 return null; 537 } 538 } 539 } 540 assign(Expression term1, TurbineOperatorKind op)541 private @Nullable Expression assign(Expression term1, TurbineOperatorKind op) { 542 if (!(term1 instanceof Tree.ConstVarName)) { 543 return null; 544 } 545 ImmutableList<Ident> names = ((Tree.ConstVarName) term1).name(); 546 if (names.size() > 1) { 547 return null; 548 } 549 Ident name = getOnlyElement(names); 550 Expression rhs = expression(op.prec()); 551 if (rhs == null) { 552 return null; 553 } 554 return new Tree.Assign(term1.position(), name, rhs); 555 } 556 ternary(Expression term1)557 private @Nullable Expression ternary(Expression term1) { 558 Expression thenExpr = expression(TurbineOperatorKind.Precedence.TERNARY); 559 if (thenExpr == null) { 560 return null; 561 } 562 if (token != Token.COLON) { 563 return null; 564 } 565 eat(); 566 Expression elseExpr = expression(); 567 if (elseExpr == null) { 568 return null; 569 } 570 return new Tree.Conditional(position, term1, thenExpr, elseExpr); 571 } 572 castTail(TurbineConstantTypeKind ty)573 private @Nullable Expression castTail(TurbineConstantTypeKind ty) { 574 if (token != Token.RPAREN) { 575 return null; 576 } 577 eat(); 578 Expression rhs = primary(false); 579 if (rhs == null) { 580 return null; 581 } 582 return new Tree.TypeCast(position, new Tree.PrimTy(position, ImmutableList.of(), ty), rhs); 583 } 584 annotation()585 private @Nullable AnnoExpr annotation() { 586 if (token != Token.AT) { 587 throw new AssertionError(); 588 } 589 eat(); 590 int pos = position; 591 Expression constVarName = qualIdent(); 592 if (!(constVarName instanceof Tree.ConstVarName)) { 593 return null; 594 } 595 ImmutableList<Ident> name = ((Tree.ConstVarName) constVarName).name(); 596 ImmutableList.Builder<Tree.Expression> args = ImmutableList.builder(); 597 if (token == Token.LPAREN) { 598 eat(); 599 while (token != Token.RPAREN) { 600 int argPos = position; 601 Expression expression = expression(); 602 if (expression == null) { 603 throw TurbineError.format(lexer.source(), argPos, ErrorKind.INVALID_ANNOTATION_ARGUMENT); 604 } 605 args.add(expression); 606 if (token != Token.COMMA) { 607 break; 608 } 609 eat(); 610 } 611 if (token == Token.RPAREN) { 612 eat(); 613 } 614 } 615 return new Tree.AnnoExpr(pos, new Tree.Anno(pos, name, args.build())); 616 } 617 618 @CheckReturnValue error(ErrorKind kind, Object... args)619 private TurbineError error(ErrorKind kind, Object... args) { 620 return TurbineError.format(lexer.source(), lexer.position(), kind, args); 621 } 622 f()623 public int f() { 624 return helper(1, 2); 625 } 626 helper(int x, int y)627 private int helper(int x, int y) { 628 return x + y; 629 } 630 } 631