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.turbine.parse.Token.COMMA; 20 import static com.google.turbine.parse.Token.IDENT; 21 import static com.google.turbine.parse.Token.INTERFACE; 22 import static com.google.turbine.parse.Token.LPAREN; 23 import static com.google.turbine.parse.Token.MINUS; 24 import static com.google.turbine.parse.Token.RPAREN; 25 import static com.google.turbine.parse.Token.SEMI; 26 import static com.google.turbine.tree.TurbineModifier.PROTECTED; 27 import static com.google.turbine.tree.TurbineModifier.PUBLIC; 28 import static com.google.turbine.tree.TurbineModifier.VARARGS; 29 30 import com.google.common.collect.ImmutableList; 31 import com.google.common.collect.ImmutableSet; 32 import com.google.errorprone.annotations.CanIgnoreReturnValue; 33 import com.google.turbine.diag.SourceFile; 34 import com.google.turbine.diag.TurbineError; 35 import com.google.turbine.diag.TurbineError.ErrorKind; 36 import com.google.turbine.model.TurbineConstantTypeKind; 37 import com.google.turbine.model.TurbineTyKind; 38 import com.google.turbine.tree.Tree; 39 import com.google.turbine.tree.Tree.Anno; 40 import com.google.turbine.tree.Tree.ArrTy; 41 import com.google.turbine.tree.Tree.ClassTy; 42 import com.google.turbine.tree.Tree.CompUnit; 43 import com.google.turbine.tree.Tree.Expression; 44 import com.google.turbine.tree.Tree.Ident; 45 import com.google.turbine.tree.Tree.ImportDecl; 46 import com.google.turbine.tree.Tree.Kind; 47 import com.google.turbine.tree.Tree.MethDecl; 48 import com.google.turbine.tree.Tree.ModDecl; 49 import com.google.turbine.tree.Tree.ModDirective; 50 import com.google.turbine.tree.Tree.ModExports; 51 import com.google.turbine.tree.Tree.ModOpens; 52 import com.google.turbine.tree.Tree.ModProvides; 53 import com.google.turbine.tree.Tree.ModRequires; 54 import com.google.turbine.tree.Tree.ModUses; 55 import com.google.turbine.tree.Tree.PkgDecl; 56 import com.google.turbine.tree.Tree.PrimTy; 57 import com.google.turbine.tree.Tree.TyDecl; 58 import com.google.turbine.tree.Tree.TyParam; 59 import com.google.turbine.tree.Tree.Type; 60 import com.google.turbine.tree.Tree.VarDecl; 61 import com.google.turbine.tree.Tree.WildTy; 62 import com.google.turbine.tree.TurbineModifier; 63 import java.util.ArrayDeque; 64 import java.util.Deque; 65 import java.util.EnumSet; 66 import java.util.List; 67 import java.util.Optional; 68 import org.jspecify.nullness.Nullable; 69 70 /** 71 * A parser for the subset of Java required for header compilation. 72 * 73 * <p>See JLS 19: https://docs.oracle.com/javase/specs/jls/se8/html/jls-19.html 74 */ 75 public class Parser { 76 77 private static final String CTOR_NAME = "<init>"; 78 private final Lexer lexer; 79 80 private Token token; 81 private int position; 82 parse(String source)83 public static CompUnit parse(String source) { 84 return parse(new SourceFile(null, source)); 85 } 86 parse(SourceFile source)87 public static CompUnit parse(SourceFile source) { 88 return new Parser(new StreamLexer(new UnicodeEscapePreprocessor(source))).compilationUnit(); 89 } 90 Parser(Lexer lexer)91 private Parser(Lexer lexer) { 92 this.lexer = lexer; 93 this.token = lexer.next(); 94 } 95 compilationUnit()96 public CompUnit compilationUnit() { 97 // TODO(cushon): consider enforcing package, import, and declaration order 98 // and make it bug-compatible with javac: 99 // http://mail.openjdk.java.net/pipermail/compiler-dev/2013-August/006968.html 100 Optional<PkgDecl> pkg = Optional.empty(); 101 Optional<ModDecl> mod = Optional.empty(); 102 EnumSet<TurbineModifier> access = EnumSet.noneOf(TurbineModifier.class); 103 ImmutableList.Builder<ImportDecl> imports = ImmutableList.builder(); 104 ImmutableList.Builder<TyDecl> decls = ImmutableList.builder(); 105 ImmutableList.Builder<Anno> annos = ImmutableList.builder(); 106 while (true) { 107 switch (token) { 108 case PACKAGE: 109 { 110 next(); 111 pkg = Optional.of(packageDeclaration(annos.build())); 112 annos = ImmutableList.builder(); 113 break; 114 } 115 case IMPORT: 116 { 117 next(); 118 ImportDecl i = importDeclaration(); 119 if (i == null) { 120 continue; 121 } 122 imports.add(i); 123 break; 124 } 125 case PUBLIC: 126 next(); 127 access.add(PUBLIC); 128 break; 129 case PROTECTED: 130 next(); 131 access.add(PROTECTED); 132 break; 133 case PRIVATE: 134 next(); 135 access.add(TurbineModifier.PRIVATE); 136 break; 137 case STATIC: 138 next(); 139 access.add(TurbineModifier.STATIC); 140 break; 141 case ABSTRACT: 142 next(); 143 access.add(TurbineModifier.ABSTRACT); 144 break; 145 case FINAL: 146 next(); 147 access.add(TurbineModifier.FINAL); 148 break; 149 case STRICTFP: 150 next(); 151 access.add(TurbineModifier.STRICTFP); 152 break; 153 case AT: 154 { 155 int pos = position; 156 next(); 157 if (token == INTERFACE) { 158 decls.add(annotationDeclaration(access, annos.build())); 159 access = EnumSet.noneOf(TurbineModifier.class); 160 annos = ImmutableList.builder(); 161 } else { 162 annos.add(annotation(pos)); 163 } 164 break; 165 } 166 case CLASS: 167 decls.add(classDeclaration(access, annos.build())); 168 access = EnumSet.noneOf(TurbineModifier.class); 169 annos = ImmutableList.builder(); 170 break; 171 case INTERFACE: 172 decls.add(interfaceDeclaration(access, annos.build())); 173 access = EnumSet.noneOf(TurbineModifier.class); 174 annos = ImmutableList.builder(); 175 break; 176 case ENUM: 177 decls.add(enumDeclaration(access, annos.build())); 178 access = EnumSet.noneOf(TurbineModifier.class); 179 annos = ImmutableList.builder(); 180 break; 181 case EOF: 182 // TODO(cushon): check for dangling modifiers? 183 return new CompUnit(position, pkg, mod, imports.build(), decls.build(), lexer.source()); 184 case SEMI: 185 // TODO(cushon): check for dangling modifiers? 186 next(); 187 continue; 188 case IDENT: 189 { 190 Ident ident = ident(); 191 if (ident.value().equals("record")) { 192 next(); 193 decls.add(recordDeclaration(access, annos.build())); 194 access = EnumSet.noneOf(TurbineModifier.class); 195 annos = ImmutableList.builder(); 196 break; 197 } 198 if (ident.value().equals("sealed")) { 199 next(); 200 access.add(TurbineModifier.SEALED); 201 break; 202 } 203 if (ident.value().equals("non")) { 204 int start = position; 205 next(); 206 eatNonSealed(start); 207 next(); 208 access.add(TurbineModifier.NON_SEALED); 209 break; 210 } 211 if (access.isEmpty() 212 && (ident.value().equals("module") || ident.value().equals("open"))) { 213 boolean open = false; 214 if (ident.value().equals("open")) { 215 ident = eatIdent(); 216 open = true; 217 } 218 if (!ident.value().equals("module")) { 219 throw error(token); 220 } 221 next(); 222 mod = Optional.of(moduleDeclaration(open, annos.build())); 223 annos = ImmutableList.builder(); 224 break; 225 } 226 } 227 // fall through 228 default: 229 throw error(token); 230 } 231 } 232 } 233 234 // Handle the hypenated pseudo-keyword 'non-sealed'. 235 // 236 // This will need to be updated to handle other hyphenated keywords if when/they are introduced. eatNonSealed(int start)237 private void eatNonSealed(int start) { 238 eat(Token.MINUS); 239 if (token != IDENT) { 240 throw error(token); 241 } 242 if (!ident().value().equals("sealed")) { 243 throw error(token); 244 } 245 if (position != start + "non-".length()) { 246 throw error(token); 247 } 248 } 249 next()250 private void next() { 251 token = lexer.next(); 252 position = lexer.position(); 253 } 254 recordDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos)255 private TyDecl recordDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) { 256 String javadoc = lexer.javadoc(); 257 int pos = position; 258 Ident name = eatIdent(); 259 ImmutableList<TyParam> typarams; 260 if (token == Token.LT) { 261 typarams = typarams(); 262 } else { 263 typarams = ImmutableList.of(); 264 } 265 ImmutableList.Builder<VarDecl> formals = ImmutableList.builder(); 266 if (token == Token.LPAREN) { 267 next(); 268 formalParams(formals, EnumSet.noneOf(TurbineModifier.class)); 269 eat(Token.RPAREN); 270 } 271 ImmutableList.Builder<ClassTy> interfaces = ImmutableList.builder(); 272 if (token == Token.IMPLEMENTS) { 273 next(); 274 do { 275 interfaces.add(classty()); 276 } while (maybe(Token.COMMA)); 277 } 278 eat(Token.LBRACE); 279 ImmutableList<Tree> members = classMembers(); 280 eat(Token.RBRACE); 281 return new TyDecl( 282 pos, 283 access, 284 annos, 285 name, 286 typarams, 287 Optional.<ClassTy>empty(), 288 interfaces.build(), 289 /* permits= */ ImmutableList.of(), 290 members, 291 formals.build(), 292 TurbineTyKind.RECORD, 293 javadoc); 294 } 295 interfaceDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos)296 private TyDecl interfaceDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) { 297 String javadoc = lexer.javadoc(); 298 eat(Token.INTERFACE); 299 int pos = position; 300 Ident name = eatIdent(); 301 ImmutableList<TyParam> typarams; 302 if (token == Token.LT) { 303 typarams = typarams(); 304 } else { 305 typarams = ImmutableList.of(); 306 } 307 ImmutableList.Builder<ClassTy> interfaces = ImmutableList.builder(); 308 if (token == Token.EXTENDS) { 309 next(); 310 do { 311 interfaces.add(classty()); 312 } while (maybe(Token.COMMA)); 313 } 314 ImmutableList.Builder<ClassTy> permits = ImmutableList.builder(); 315 if (token == Token.IDENT) { 316 if (ident().value().equals("permits")) { 317 eat(Token.IDENT); 318 do { 319 permits.add(classty()); 320 } while (maybe(Token.COMMA)); 321 } 322 } 323 eat(Token.LBRACE); 324 ImmutableList<Tree> members = classMembers(); 325 eat(Token.RBRACE); 326 return new TyDecl( 327 pos, 328 access, 329 annos, 330 name, 331 typarams, 332 Optional.<ClassTy>empty(), 333 interfaces.build(), 334 permits.build(), 335 members, 336 ImmutableList.of(), 337 TurbineTyKind.INTERFACE, 338 javadoc); 339 } 340 annotationDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos)341 private TyDecl annotationDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) { 342 String javadoc = lexer.javadoc(); 343 eat(Token.INTERFACE); 344 int pos = position; 345 Ident name = eatIdent(); 346 eat(Token.LBRACE); 347 ImmutableList<Tree> members = classMembers(); 348 eat(Token.RBRACE); 349 return new TyDecl( 350 pos, 351 access, 352 annos, 353 name, 354 ImmutableList.<TyParam>of(), 355 Optional.<ClassTy>empty(), 356 ImmutableList.<ClassTy>of(), 357 ImmutableList.of(), 358 members, 359 ImmutableList.of(), 360 TurbineTyKind.ANNOTATION, 361 javadoc); 362 } 363 enumDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos)364 private TyDecl enumDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) { 365 String javadoc = lexer.javadoc(); 366 eat(Token.ENUM); 367 int pos = position; 368 Ident name = eatIdent(); 369 ImmutableList.Builder<ClassTy> interfaces = ImmutableList.builder(); 370 if (token == Token.IMPLEMENTS) { 371 next(); 372 do { 373 interfaces.add(classty()); 374 } while (maybe(Token.COMMA)); 375 } 376 eat(Token.LBRACE); 377 ImmutableList<Tree> members = 378 ImmutableList.<Tree>builder().addAll(enumMembers(name)).addAll(classMembers()).build(); 379 eat(Token.RBRACE); 380 return new TyDecl( 381 pos, 382 access, 383 annos, 384 name, 385 ImmutableList.<TyParam>of(), 386 Optional.<ClassTy>empty(), 387 interfaces.build(), 388 ImmutableList.of(), 389 members, 390 ImmutableList.of(), 391 TurbineTyKind.ENUM, 392 javadoc); 393 } 394 moduleName()395 private String moduleName() { 396 return flatname('.', qualIdent()); 397 } 398 packageName()399 private String packageName() { 400 return flatname('/', qualIdent()); 401 } 402 moduleDeclaration(boolean open, ImmutableList<Anno> annos)403 private ModDecl moduleDeclaration(boolean open, ImmutableList<Anno> annos) { 404 int pos = position; 405 String moduleName = moduleName(); 406 eat(Token.LBRACE); 407 ImmutableList.Builder<ModDirective> directives = ImmutableList.builder(); 408 OUTER: 409 while (true) { 410 switch (token) { 411 case IDENT: 412 { 413 String ident = lexer.stringValue(); 414 next(); 415 switch (ident) { 416 case "requires": 417 directives.add(moduleRequires()); 418 break; 419 case "exports": 420 directives.add(moduleExports()); 421 break; 422 case "opens": 423 directives.add(moduleOpens()); 424 break; 425 case "uses": 426 directives.add(moduleUses()); 427 break; 428 case "provides": 429 directives.add(moduleProvides()); 430 break; 431 default: // fall out 432 } 433 break; 434 } 435 case RBRACE: 436 break OUTER; 437 default: 438 throw error(token); 439 } 440 } 441 eat(Token.RBRACE); 442 return new ModDecl(pos, annos, open, moduleName, directives.build()); 443 } 444 flatname(char join, ImmutableList<Ident> idents)445 private static String flatname(char join, ImmutableList<Ident> idents) { 446 StringBuilder sb = new StringBuilder(); 447 boolean first = true; 448 for (Ident ident : idents) { 449 if (!first) { 450 sb.append(join); 451 } 452 sb.append(ident.value()); 453 first = false; 454 } 455 return sb.toString(); 456 } 457 moduleRequires()458 private ModRequires moduleRequires() { 459 int pos = position; 460 EnumSet<TurbineModifier> access = EnumSet.noneOf(TurbineModifier.class); 461 while (true) { 462 if (token == Token.IDENT && lexer.stringValue().equals("transitive")) { 463 next(); 464 access.add(TurbineModifier.TRANSITIVE); 465 break; 466 } 467 if (token == Token.STATIC) { 468 next(); 469 access.add(TurbineModifier.STATIC); 470 break; 471 } 472 break; 473 } 474 475 String moduleName = moduleName(); 476 eat(Token.SEMI); 477 return new ModRequires(pos, ImmutableSet.copyOf(access), moduleName); 478 } 479 moduleExports()480 private ModExports moduleExports() { 481 int pos = position; 482 483 String packageName = packageName(); 484 ImmutableList.Builder<String> moduleNames = ImmutableList.builder(); 485 if (lexer.stringValue().equals("to")) { 486 next(); 487 do { 488 489 moduleNames.add(moduleName()); 490 } while (maybe(Token.COMMA)); 491 } 492 eat(Token.SEMI); 493 return new ModExports(pos, packageName, moduleNames.build()); 494 } 495 moduleOpens()496 private ModOpens moduleOpens() { 497 int pos = position; 498 499 String packageName = packageName(); 500 ImmutableList.Builder<String> moduleNames = ImmutableList.builder(); 501 if (lexer.stringValue().equals("to")) { 502 next(); 503 do { 504 505 String moduleName = moduleName(); 506 moduleNames.add(moduleName); 507 } while (maybe(Token.COMMA)); 508 } 509 eat(Token.SEMI); 510 return new ModOpens(pos, packageName, moduleNames.build()); 511 } 512 moduleUses()513 private ModUses moduleUses() { 514 int pos = position; 515 ImmutableList<Ident> uses = qualIdent(); 516 eat(Token.SEMI); 517 return new ModUses(pos, uses); 518 } 519 moduleProvides()520 private ModProvides moduleProvides() { 521 int pos = position; 522 ImmutableList<Ident> typeName = qualIdent(); 523 if (!eatIdent().value().equals("with")) { 524 throw error(token); 525 } 526 ImmutableList.Builder<ImmutableList<Ident>> implNames = ImmutableList.builder(); 527 do { 528 ImmutableList<Ident> implName = qualIdent(); 529 implNames.add(implName); 530 } while (maybe(Token.COMMA)); 531 eat(Token.SEMI); 532 return new ModProvides(pos, typeName, implNames.build()); 533 } 534 535 private static final ImmutableSet<TurbineModifier> ENUM_CONSTANT_MODIFIERS = 536 ImmutableSet.of( 537 TurbineModifier.PUBLIC, 538 TurbineModifier.STATIC, 539 TurbineModifier.ACC_ENUM, 540 TurbineModifier.FINAL); 541 enumMembers(Ident enumName)542 private ImmutableList<Tree> enumMembers(Ident enumName) { 543 ImmutableList.Builder<Tree> result = ImmutableList.builder(); 544 ImmutableList.Builder<Anno> annos = ImmutableList.builder(); 545 OUTER: 546 while (true) { 547 switch (token) { 548 case IDENT: 549 { 550 String javadoc = lexer.javadoc(); 551 Ident name = eatIdent(); 552 if (token == Token.LPAREN) { 553 dropParens(); 554 } 555 // TODO(cushon): consider desugaring enum constants later 556 if (token == Token.LBRACE) { 557 dropBlocks(); 558 } 559 maybe(Token.COMMA); 560 result.add( 561 new VarDecl( 562 position, 563 ENUM_CONSTANT_MODIFIERS, 564 annos.build(), 565 new ClassTy( 566 position, 567 Optional.<ClassTy>empty(), 568 enumName, 569 ImmutableList.<Type>of(), 570 ImmutableList.of()), 571 name, 572 Optional.<Expression>empty(), 573 javadoc)); 574 annos = ImmutableList.builder(); 575 break; 576 } 577 case SEMI: 578 next(); 579 annos = ImmutableList.builder(); 580 break OUTER; 581 case RBRACE: 582 annos = ImmutableList.builder(); 583 break OUTER; 584 case AT: 585 int pos = position; 586 next(); 587 annos.add(annotation(pos)); 588 break; 589 default: 590 throw error(token); 591 } 592 } 593 return result.build(); 594 } 595 classDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos)596 private TyDecl classDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) { 597 String javadoc = lexer.javadoc(); 598 eat(Token.CLASS); 599 int pos = position; 600 Ident name = eatIdent(); 601 ImmutableList<TyParam> tyParams = ImmutableList.of(); 602 if (token == Token.LT) { 603 tyParams = typarams(); 604 } 605 ClassTy xtnds = null; 606 if (token == Token.EXTENDS) { 607 next(); 608 xtnds = classty(); 609 } 610 ImmutableList.Builder<ClassTy> interfaces = ImmutableList.builder(); 611 if (token == Token.IMPLEMENTS) { 612 next(); 613 do { 614 interfaces.add(classty()); 615 } while (maybe(Token.COMMA)); 616 } 617 ImmutableList.Builder<ClassTy> permits = ImmutableList.builder(); 618 if (token == Token.IDENT) { 619 if (ident().value().equals("permits")) { 620 eat(Token.IDENT); 621 do { 622 permits.add(classty()); 623 } while (maybe(Token.COMMA)); 624 } 625 } 626 switch (token) { 627 case LBRACE: 628 next(); 629 break; 630 case EXTENDS: 631 throw error(ErrorKind.EXTENDS_AFTER_IMPLEMENTS); 632 default: 633 throw error(ErrorKind.EXPECTED_TOKEN, Token.LBRACE); 634 } 635 ImmutableList<Tree> members = classMembers(); 636 eat(Token.RBRACE); 637 return new TyDecl( 638 pos, 639 access, 640 annos, 641 name, 642 tyParams, 643 Optional.ofNullable(xtnds), 644 interfaces.build(), 645 permits.build(), 646 members, 647 ImmutableList.of(), 648 TurbineTyKind.CLASS, 649 javadoc); 650 } 651 classMembers()652 private ImmutableList<Tree> classMembers() { 653 ImmutableList.Builder<Tree> acc = ImmutableList.builder(); 654 EnumSet<TurbineModifier> access = EnumSet.noneOf(TurbineModifier.class); 655 ImmutableList.Builder<Anno> annos = ImmutableList.builder(); 656 while (true) { 657 switch (token) { 658 case PUBLIC: 659 next(); 660 access.add(TurbineModifier.PUBLIC); 661 break; 662 case PROTECTED: 663 next(); 664 access.add(TurbineModifier.PROTECTED); 665 break; 666 case PRIVATE: 667 next(); 668 access.add(TurbineModifier.PRIVATE); 669 break; 670 case STATIC: 671 next(); 672 access.add(TurbineModifier.STATIC); 673 break; 674 case ABSTRACT: 675 next(); 676 access.add(TurbineModifier.ABSTRACT); 677 break; 678 case FINAL: 679 next(); 680 access.add(TurbineModifier.FINAL); 681 break; 682 case NATIVE: 683 next(); 684 access.add(TurbineModifier.NATIVE); 685 break; 686 case SYNCHRONIZED: 687 next(); 688 access.add(TurbineModifier.SYNCHRONIZED); 689 break; 690 case TRANSIENT: 691 next(); 692 access.add(TurbineModifier.TRANSIENT); 693 break; 694 case VOLATILE: 695 next(); 696 access.add(TurbineModifier.VOLATILE); 697 break; 698 case STRICTFP: 699 next(); 700 access.add(TurbineModifier.STRICTFP); 701 break; 702 case DEFAULT: 703 next(); 704 access.add(TurbineModifier.DEFAULT); 705 break; 706 case AT: 707 { 708 // TODO(cushon): de-dup with top-level parsing 709 int pos = position; 710 next(); 711 if (token == INTERFACE) { 712 acc.add(annotationDeclaration(access, annos.build())); 713 access = EnumSet.noneOf(TurbineModifier.class); 714 annos = ImmutableList.builder(); 715 } else { 716 annos.add(annotation(pos)); 717 } 718 break; 719 } 720 721 case IDENT: 722 Ident ident = ident(); 723 if (ident.value().equals("sealed")) { 724 next(); 725 access.add(TurbineModifier.SEALED); 726 break; 727 } 728 if (ident.value().equals("non")) { 729 int pos = position; 730 next(); 731 if (token != MINUS) { 732 acc.addAll(member(access, annos.build(), ImmutableList.of(), pos, ident)); 733 access = EnumSet.noneOf(TurbineModifier.class); 734 annos = ImmutableList.builder(); 735 } else { 736 eatNonSealed(pos); 737 next(); 738 access.add(TurbineModifier.NON_SEALED); 739 } 740 break; 741 } 742 if (ident.value().equals("record")) { 743 eat(IDENT); 744 acc.add(recordDeclaration(access, annos.build())); 745 access = EnumSet.noneOf(TurbineModifier.class); 746 annos = ImmutableList.builder(); 747 break; 748 } 749 // fall through 750 case BOOLEAN: 751 case BYTE: 752 case SHORT: 753 case INT: 754 case LONG: 755 case CHAR: 756 case DOUBLE: 757 case FLOAT: 758 case VOID: 759 case LT: 760 acc.addAll(classMember(access, annos.build())); 761 access = EnumSet.noneOf(TurbineModifier.class); 762 annos = ImmutableList.builder(); 763 break; 764 case LBRACE: 765 dropBlocks(); 766 access = EnumSet.noneOf(TurbineModifier.class); 767 annos = ImmutableList.builder(); 768 break; 769 case CLASS: 770 acc.add(classDeclaration(access, annos.build())); 771 access = EnumSet.noneOf(TurbineModifier.class); 772 annos = ImmutableList.builder(); 773 break; 774 case INTERFACE: 775 acc.add(interfaceDeclaration(access, annos.build())); 776 access = EnumSet.noneOf(TurbineModifier.class); 777 annos = ImmutableList.builder(); 778 break; 779 case ENUM: 780 acc.add(enumDeclaration(access, annos.build())); 781 access = EnumSet.noneOf(TurbineModifier.class); 782 annos = ImmutableList.builder(); 783 break; 784 case RBRACE: 785 return acc.build(); 786 case SEMI: 787 next(); 788 continue; 789 default: 790 throw error(token); 791 } 792 } 793 } 794 classMember( EnumSet<TurbineModifier> access, ImmutableList<Anno> annos)795 private ImmutableList<Tree> classMember( 796 EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) { 797 ImmutableList<TyParam> typaram = ImmutableList.of(); 798 Type result; 799 Ident name; 800 801 if (token == Token.LT) { 802 typaram = typarams(); 803 } 804 805 if (token == Token.AT) { 806 annos = ImmutableList.<Anno>builder().addAll(annos).addAll(maybeAnnos()).build(); 807 } 808 809 switch (token) { 810 case VOID: 811 { 812 result = new Tree.VoidTy(position); 813 next(); 814 int pos = position; 815 name = eatIdent(); 816 return memberRest(pos, access, annos, typaram, result, name); 817 } 818 case BOOLEAN: 819 case BYTE: 820 case SHORT: 821 case INT: 822 case LONG: 823 case CHAR: 824 case DOUBLE: 825 case FLOAT: 826 { 827 result = referenceType(ImmutableList.of()); 828 int pos = position; 829 name = eatIdent(); 830 return memberRest(pos, access, annos, typaram, result, name); 831 } 832 case IDENT: 833 int pos = position; 834 Ident ident = eatIdent(); 835 return member(access, annos, typaram, pos, ident); 836 default: 837 throw error(token); 838 } 839 } 840 member( EnumSet<TurbineModifier> access, ImmutableList<Anno> annos, ImmutableList<TyParam> typaram, int pos, Ident ident)841 private ImmutableList<Tree> member( 842 EnumSet<TurbineModifier> access, 843 ImmutableList<Anno> annos, 844 ImmutableList<TyParam> typaram, 845 int pos, 846 Ident ident) { 847 Type result; 848 Ident name; 849 switch (token) { 850 case LPAREN: 851 { 852 name = ident; 853 return ImmutableList.of(methodRest(pos, access, annos, typaram, null, name)); 854 } 855 case LBRACE: 856 { 857 dropBlocks(); 858 name = new Ident(position, CTOR_NAME); 859 String javadoc = lexer.javadoc(); 860 access.add(TurbineModifier.COMPACT_CTOR); 861 return ImmutableList.<Tree>of( 862 new MethDecl( 863 pos, 864 access, 865 annos, 866 typaram, 867 /* ret= */ Optional.empty(), 868 name, 869 /* params= */ ImmutableList.of(), 870 /* exntys= */ ImmutableList.of(), 871 /* defaultValue= */ Optional.empty(), 872 javadoc)); 873 } 874 case IDENT: 875 { 876 result = 877 new ClassTy( 878 position, 879 Optional.<ClassTy>empty(), 880 ident, 881 ImmutableList.<Type>of(), 882 ImmutableList.of()); 883 pos = position; 884 name = eatIdent(); 885 return memberRest(pos, access, annos, typaram, result, name); 886 } 887 case AT: 888 case LBRACK: 889 { 890 result = 891 new ClassTy( 892 position, 893 Optional.<ClassTy>empty(), 894 ident, 895 ImmutableList.<Type>of(), 896 ImmutableList.of()); 897 result = maybeDims(maybeAnnos(), result); 898 break; 899 } 900 case LT: 901 { 902 result = 903 new ClassTy(position, Optional.<ClassTy>empty(), ident, tyargs(), ImmutableList.of()); 904 result = maybeDims(maybeAnnos(), result); 905 break; 906 } 907 case DOT: 908 result = 909 new ClassTy( 910 position, 911 Optional.<ClassTy>empty(), 912 ident, 913 ImmutableList.<Type>of(), 914 ImmutableList.of()); 915 break; 916 917 default: 918 throw error(token); 919 } 920 if (result == null) { 921 throw error(token); 922 } 923 if (token == Token.DOT) { 924 next(); 925 if (!result.kind().equals(Kind.CLASS_TY)) { 926 throw error(token); 927 } 928 result = classty((ClassTy) result); 929 } 930 result = maybeDims(maybeAnnos(), result); 931 pos = position; 932 name = eatIdent(); 933 switch (token) { 934 case LPAREN: 935 return ImmutableList.of(methodRest(pos, access, annos, typaram, result, name)); 936 case LBRACK: 937 case SEMI: 938 case ASSIGN: 939 case COMMA: 940 { 941 if (!typaram.isEmpty()) { 942 throw error(ErrorKind.UNEXPECTED_TYPE_PARAMETER, typaram); 943 } 944 return fieldRest(pos, access, annos, result, name); 945 } 946 default: 947 throw error(token); 948 } 949 } 950 maybeAnnos()951 private ImmutableList<Anno> maybeAnnos() { 952 if (token != Token.AT) { 953 return ImmutableList.of(); 954 } 955 ImmutableList.Builder<Anno> builder = ImmutableList.builder(); 956 while (token == Token.AT) { 957 int pos = position; 958 next(); 959 builder.add(annotation(pos)); 960 } 961 return builder.build(); 962 } 963 memberRest( int pos, EnumSet<TurbineModifier> access, ImmutableList<Anno> annos, ImmutableList<TyParam> typaram, Type result, Ident name)964 private ImmutableList<Tree> memberRest( 965 int pos, 966 EnumSet<TurbineModifier> access, 967 ImmutableList<Anno> annos, 968 ImmutableList<TyParam> typaram, 969 Type result, 970 Ident name) { 971 switch (token) { 972 case ASSIGN: 973 case AT: 974 case COMMA: 975 case LBRACK: 976 case SEMI: 977 { 978 if (!typaram.isEmpty()) { 979 throw error(ErrorKind.UNEXPECTED_TYPE_PARAMETER, typaram); 980 } 981 return fieldRest(pos, access, annos, result, name); 982 } 983 case LPAREN: 984 return ImmutableList.of(methodRest(pos, access, annos, typaram, result, name)); 985 default: 986 throw error(token); 987 } 988 } 989 fieldRest( int pos, EnumSet<TurbineModifier> access, ImmutableList<Anno> annos, Type baseTy, Ident name)990 private ImmutableList<Tree> fieldRest( 991 int pos, 992 EnumSet<TurbineModifier> access, 993 ImmutableList<Anno> annos, 994 Type baseTy, 995 Ident name) { 996 String javadoc = lexer.javadoc(); 997 ImmutableList.Builder<Tree> result = ImmutableList.builder(); 998 VariableInitializerParser initializerParser = new VariableInitializerParser(token, lexer); 999 List<List<SavedToken>> bits = initializerParser.parseInitializers(); 1000 token = initializerParser.token; 1001 1002 boolean first = true; 1003 int expressionStart = pos; 1004 for (List<SavedToken> bit : bits) { 1005 IteratorLexer lexer = new IteratorLexer(this.lexer.source(), bit.iterator()); 1006 Parser parser = new Parser(lexer); 1007 if (first) { 1008 first = false; 1009 } else { 1010 name = parser.eatIdent(); 1011 } 1012 Type ty = baseTy; 1013 ty = parser.extraDims(ty); 1014 // TODO(cushon): skip more fields that are definitely non-const 1015 ConstExpressionParser constExpressionParser = 1016 new ConstExpressionParser(lexer, lexer.next(), lexer.position()); 1017 expressionStart = lexer.position(); 1018 Expression init = constExpressionParser.expression(); 1019 if (init != null && init.kind() == Tree.Kind.ARRAY_INIT) { 1020 init = null; 1021 } 1022 result.add(new VarDecl(pos, access, annos, ty, name, Optional.ofNullable(init), javadoc)); 1023 } 1024 if (token != SEMI) { 1025 throw TurbineError.format(lexer.source(), expressionStart, ErrorKind.UNTERMINATED_EXPRESSION); 1026 } 1027 eat(Token.SEMI); 1028 return result.build(); 1029 } 1030 methodRest( int pos, EnumSet<TurbineModifier> access, ImmutableList<Anno> annos, ImmutableList<TyParam> typaram, Type result, Ident name)1031 private Tree methodRest( 1032 int pos, 1033 EnumSet<TurbineModifier> access, 1034 ImmutableList<Anno> annos, 1035 ImmutableList<TyParam> typaram, 1036 Type result, 1037 Ident name) { 1038 String javadoc = lexer.javadoc(); 1039 eat(Token.LPAREN); 1040 ImmutableList.Builder<VarDecl> formals = ImmutableList.builder(); 1041 formalParams(formals, access); 1042 eat(Token.RPAREN); 1043 1044 result = extraDims(result); 1045 1046 ImmutableList.Builder<ClassTy> exceptions = ImmutableList.builder(); 1047 if (token == Token.THROWS) { 1048 next(); 1049 exceptions.addAll(exceptions()); 1050 } 1051 Tree defaultValue = null; 1052 switch (token) { 1053 case SEMI: 1054 next(); 1055 break; 1056 case LBRACE: 1057 dropBlocks(); 1058 break; 1059 case DEFAULT: 1060 { 1061 ConstExpressionParser cparser = 1062 new ConstExpressionParser(lexer, lexer.next(), lexer.position()); 1063 Tree expr = cparser.expression(); 1064 token = cparser.token; 1065 if (expr == null && token == Token.AT) { 1066 int annoPos = position; 1067 next(); 1068 expr = annotation(annoPos); 1069 } 1070 if (expr == null) { 1071 throw error(token); 1072 } 1073 defaultValue = expr; 1074 eat(Token.SEMI); 1075 break; 1076 } 1077 default: 1078 throw error(token); 1079 } 1080 if (result == null) { 1081 name = new Ident(position, CTOR_NAME); 1082 } 1083 return new MethDecl( 1084 pos, 1085 access, 1086 annos, 1087 typaram, 1088 Optional.<Tree>ofNullable(result), 1089 name, 1090 formals.build(), 1091 exceptions.build(), 1092 Optional.ofNullable(defaultValue), 1093 javadoc); 1094 } 1095 1096 /** 1097 * Given a base {@code type} and some number of {@code extra} c-style array dimension specifiers, 1098 * construct a new array type. 1099 * 1100 * <p>For reasons that are unclear from the spec, {@code int @A [] x []} is equivalent to {@code 1101 * int [] @A [] x}, not {@code int @A [] [] x}. 1102 */ extraDims(Type ty)1103 private Type extraDims(Type ty) { 1104 ImmutableList<Anno> annos = maybeAnnos(); 1105 if (!annos.isEmpty() && token != Token.LBRACK) { 1106 // orphaned type annotations 1107 throw error(token); 1108 } 1109 Deque<ImmutableList<Anno>> extra = new ArrayDeque<>(); 1110 while (maybe(Token.LBRACK)) { 1111 eat(Token.RBRACK); 1112 extra.push(annos); 1113 annos = maybeAnnos(); 1114 } 1115 ty = extraDims(ty, extra); 1116 return ty; 1117 } 1118 extraDims(Type type, Deque<ImmutableList<Anno>> extra)1119 private Type extraDims(Type type, Deque<ImmutableList<Anno>> extra) { 1120 if (extra.isEmpty()) { 1121 return type; 1122 } 1123 if (type == null) { 1124 // trailing dims without a type, e.g. for a constructor declaration 1125 throw error(token); 1126 } 1127 if (type.kind() == Kind.ARR_TY) { 1128 ArrTy arrTy = (ArrTy) type; 1129 return new ArrTy(arrTy.position(), arrTy.annos(), extraDims(arrTy.elem(), extra)); 1130 } 1131 return new ArrTy(type.position(), extra.pop(), extraDims(type, extra)); 1132 } 1133 exceptions()1134 private ImmutableList<ClassTy> exceptions() { 1135 ImmutableList.Builder<ClassTy> result = ImmutableList.builder(); 1136 result.add(classty()); 1137 while (maybe(Token.COMMA)) { 1138 result.add(classty()); 1139 } 1140 return result.build(); 1141 } 1142 formalParams( ImmutableList.Builder<VarDecl> builder, EnumSet<TurbineModifier> access)1143 private void formalParams( 1144 ImmutableList.Builder<VarDecl> builder, EnumSet<TurbineModifier> access) { 1145 while (token != Token.RPAREN) { 1146 VarDecl formal = formalParam(); 1147 builder.add(formal); 1148 if (formal.mods().contains(TurbineModifier.VARARGS)) { 1149 access.add(TurbineModifier.VARARGS); 1150 } 1151 if (token != Token.COMMA) { 1152 break; 1153 } 1154 next(); 1155 } 1156 } 1157 formalParam()1158 private VarDecl formalParam() { 1159 ImmutableList.Builder<Anno> annos = ImmutableList.builder(); 1160 EnumSet<TurbineModifier> access = modifiersAndAnnotations(annos); 1161 Type ty = referenceTypeWithoutDims(ImmutableList.of()); 1162 ImmutableList<Anno> typeAnnos = maybeAnnos(); 1163 OUTER: 1164 while (true) { 1165 switch (token) { 1166 case LBRACK: 1167 next(); 1168 eat(Token.RBRACK); 1169 ty = new ArrTy(position, typeAnnos, ty); 1170 typeAnnos = maybeAnnos(); 1171 break; 1172 case ELLIPSIS: 1173 next(); 1174 access.add(VARARGS); 1175 ty = new ArrTy(position, typeAnnos, ty); 1176 typeAnnos = ImmutableList.of(); 1177 break OUTER; 1178 default: 1179 break OUTER; 1180 } 1181 } 1182 if (!typeAnnos.isEmpty()) { 1183 throw error(token); 1184 } 1185 // the parameter name is `this` for receiver parameters, and a qualified this expression 1186 // for inner classes 1187 Ident name = identOrThis(); 1188 while (token == Token.DOT) { 1189 eat(Token.DOT); 1190 // Overwrite everything up to the terminal 'this' for inner classes; we don't need it 1191 name = identOrThis(); 1192 } 1193 ty = extraDims(ty); 1194 return new VarDecl( 1195 position, access, annos.build(), ty, name, Optional.<Expression>empty(), null); 1196 } 1197 identOrThis()1198 private Ident identOrThis() { 1199 switch (token) { 1200 case IDENT: 1201 return eatIdent(); 1202 case THIS: 1203 int position = lexer.position(); 1204 eat(Token.THIS); 1205 return new Ident(position, "this"); 1206 default: 1207 throw error(token); 1208 } 1209 } 1210 dropParens()1211 private void dropParens() { 1212 eat(Token.LPAREN); 1213 int depth = 1; 1214 while (depth > 0) { 1215 switch (token) { 1216 case RPAREN: 1217 depth--; 1218 break; 1219 case LPAREN: 1220 depth++; 1221 break; 1222 case EOF: 1223 throw error(ErrorKind.UNEXPECTED_EOF); 1224 default: 1225 break; 1226 } 1227 next(); 1228 } 1229 } 1230 dropBlocks()1231 private void dropBlocks() { 1232 eat(Token.LBRACE); 1233 int depth = 1; 1234 while (depth > 0) { 1235 switch (token) { 1236 case RBRACE: 1237 depth--; 1238 break; 1239 case LBRACE: 1240 depth++; 1241 break; 1242 case EOF: 1243 throw error(ErrorKind.UNEXPECTED_EOF); 1244 default: 1245 break; 1246 } 1247 next(); 1248 } 1249 } 1250 typarams()1251 private ImmutableList<TyParam> typarams() { 1252 ImmutableList.Builder<TyParam> acc = ImmutableList.builder(); 1253 eat(Token.LT); 1254 OUTER: 1255 while (true) { 1256 ImmutableList<Anno> annotations = maybeAnnos(); 1257 int pos = position; 1258 Ident name = eatIdent(); 1259 ImmutableList<Tree> bounds = ImmutableList.of(); 1260 if (token == Token.EXTENDS) { 1261 next(); 1262 bounds = tybounds(); 1263 } 1264 acc.add(new TyParam(pos, name, bounds, annotations)); 1265 switch (token) { 1266 case COMMA: 1267 eat(Token.COMMA); 1268 continue; 1269 case GT: 1270 next(); 1271 break OUTER; 1272 default: 1273 throw error(token); 1274 } 1275 } 1276 return acc.build(); 1277 } 1278 tybounds()1279 private ImmutableList<Tree> tybounds() { 1280 ImmutableList.Builder<Tree> acc = ImmutableList.builder(); 1281 do { 1282 acc.add(classty()); 1283 } while (maybe(Token.AND)); 1284 return acc.build(); 1285 } 1286 classty()1287 private ClassTy classty() { 1288 return classty(null); 1289 } 1290 classty(ClassTy ty)1291 private ClassTy classty(ClassTy ty) { 1292 return classty(ty, null); 1293 } 1294 classty(ClassTy ty, @Nullable ImmutableList<Anno> typeAnnos)1295 private ClassTy classty(ClassTy ty, @Nullable ImmutableList<Anno> typeAnnos) { 1296 int pos = position; 1297 do { 1298 if (typeAnnos == null) { 1299 typeAnnos = maybeAnnos(); 1300 } 1301 Ident name = eatIdent(); 1302 ImmutableList<Type> tyargs = ImmutableList.of(); 1303 if (token == Token.LT) { 1304 tyargs = tyargs(); 1305 } 1306 ty = new ClassTy(pos, Optional.ofNullable(ty), name, tyargs, typeAnnos); 1307 typeAnnos = null; 1308 } while (maybe(Token.DOT)); 1309 return ty; 1310 } 1311 tyargs()1312 private ImmutableList<Type> tyargs() { 1313 ImmutableList.Builder<Type> acc = ImmutableList.builder(); 1314 eat(Token.LT); 1315 OUTER: 1316 do { 1317 ImmutableList<Anno> typeAnnos = maybeAnnos(); 1318 switch (token) { 1319 case COND: 1320 { 1321 next(); 1322 switch (token) { 1323 case EXTENDS: 1324 next(); 1325 Type upper = referenceType(maybeAnnos()); 1326 acc.add( 1327 new WildTy(position, typeAnnos, Optional.of(upper), Optional.<Type>empty())); 1328 break; 1329 case SUPER: 1330 next(); 1331 Type lower = referenceType(maybeAnnos()); 1332 acc.add( 1333 new WildTy(position, typeAnnos, Optional.<Type>empty(), Optional.of(lower))); 1334 break; 1335 case COMMA: 1336 acc.add( 1337 new WildTy( 1338 position, typeAnnos, Optional.<Type>empty(), Optional.<Type>empty())); 1339 continue OUTER; 1340 case GT: 1341 case GTGT: 1342 case GTGTGT: 1343 acc.add( 1344 new WildTy( 1345 position, typeAnnos, Optional.<Type>empty(), Optional.<Type>empty())); 1346 break OUTER; 1347 default: 1348 throw error(token); 1349 } 1350 break; 1351 } 1352 case IDENT: 1353 case BOOLEAN: 1354 case BYTE: 1355 case SHORT: 1356 case INT: 1357 case LONG: 1358 case CHAR: 1359 case DOUBLE: 1360 case FLOAT: 1361 acc.add(referenceType(typeAnnos)); 1362 break; 1363 default: 1364 throw error(token); 1365 } 1366 } while (maybe(Token.COMMA)); 1367 switch (token) { 1368 case GT: 1369 next(); 1370 break; 1371 case GTGT: 1372 token = Token.GT; 1373 break; 1374 case GTGTGT: 1375 token = Token.GTGT; 1376 break; 1377 default: 1378 throw error(token); 1379 } 1380 return acc.build(); 1381 } 1382 referenceTypeWithoutDims(ImmutableList<Anno> typeAnnos)1383 private Type referenceTypeWithoutDims(ImmutableList<Anno> typeAnnos) { 1384 switch (token) { 1385 case IDENT: 1386 return classty(null, typeAnnos); 1387 case BOOLEAN: 1388 next(); 1389 return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.BOOLEAN); 1390 case BYTE: 1391 next(); 1392 return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.BYTE); 1393 case SHORT: 1394 next(); 1395 return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.SHORT); 1396 case INT: 1397 next(); 1398 return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.INT); 1399 case LONG: 1400 next(); 1401 return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.LONG); 1402 case CHAR: 1403 next(); 1404 return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.CHAR); 1405 case DOUBLE: 1406 next(); 1407 return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.DOUBLE); 1408 case FLOAT: 1409 next(); 1410 return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.FLOAT); 1411 default: 1412 throw error(token); 1413 } 1414 } 1415 referenceType(ImmutableList<Anno> typeAnnos)1416 private Type referenceType(ImmutableList<Anno> typeAnnos) { 1417 Type ty = referenceTypeWithoutDims(typeAnnos); 1418 return maybeDims(maybeAnnos(), ty); 1419 } 1420 maybeDims(ImmutableList<Anno> typeAnnos, Type ty)1421 private Type maybeDims(ImmutableList<Anno> typeAnnos, Type ty) { 1422 while (maybe(Token.LBRACK)) { 1423 eat(Token.RBRACK); 1424 ty = new ArrTy(position, typeAnnos, ty); 1425 typeAnnos = maybeAnnos(); 1426 } 1427 return ty; 1428 } 1429 modifiersAndAnnotations(ImmutableList.Builder<Anno> annos)1430 private EnumSet<TurbineModifier> modifiersAndAnnotations(ImmutableList.Builder<Anno> annos) { 1431 EnumSet<TurbineModifier> access = EnumSet.noneOf(TurbineModifier.class); 1432 while (true) { 1433 switch (token) { 1434 case PUBLIC: 1435 next(); 1436 access.add(TurbineModifier.PUBLIC); 1437 break; 1438 case PROTECTED: 1439 next(); 1440 access.add(TurbineModifier.PROTECTED); 1441 break; 1442 case PRIVATE: 1443 next(); 1444 access.add(TurbineModifier.PRIVATE); 1445 break; 1446 case STATIC: 1447 next(); 1448 access.add(TurbineModifier.STATIC); 1449 break; 1450 case ABSTRACT: 1451 next(); 1452 access.add(TurbineModifier.ABSTRACT); 1453 break; 1454 case FINAL: 1455 next(); 1456 access.add(TurbineModifier.FINAL); 1457 break; 1458 case NATIVE: 1459 next(); 1460 access.add(TurbineModifier.NATIVE); 1461 break; 1462 case SYNCHRONIZED: 1463 next(); 1464 access.add(TurbineModifier.SYNCHRONIZED); 1465 break; 1466 case TRANSIENT: 1467 next(); 1468 access.add(TurbineModifier.TRANSIENT); 1469 break; 1470 case VOLATILE: 1471 next(); 1472 access.add(TurbineModifier.VOLATILE); 1473 break; 1474 case STRICTFP: 1475 next(); 1476 access.add(TurbineModifier.STRICTFP); 1477 break; 1478 case AT: 1479 int pos = position; 1480 next(); 1481 annos.add(annotation(pos)); 1482 break; 1483 default: 1484 return access; 1485 } 1486 } 1487 } 1488 importDeclaration()1489 private ImportDecl importDeclaration() { 1490 boolean stat = maybe(Token.STATIC); 1491 1492 int pos = position; 1493 ImmutableList.Builder<Ident> type = ImmutableList.builder(); 1494 type.add(eatIdent()); 1495 boolean wild = false; 1496 OUTER: 1497 while (maybe(Token.DOT)) { 1498 switch (token) { 1499 case IDENT: 1500 type.add(eatIdent()); 1501 break; 1502 case MULT: 1503 eat(Token.MULT); 1504 wild = true; 1505 break OUTER; 1506 default: 1507 break; 1508 } 1509 } 1510 eat(Token.SEMI); 1511 return new ImportDecl(pos, type.build(), stat, wild); 1512 } 1513 packageDeclaration(ImmutableList<Anno> annos)1514 private PkgDecl packageDeclaration(ImmutableList<Anno> annos) { 1515 PkgDecl result = new PkgDecl(position, qualIdent(), annos); 1516 eat(Token.SEMI); 1517 return result; 1518 } 1519 qualIdent()1520 private ImmutableList<Ident> qualIdent() { 1521 ImmutableList.Builder<Ident> name = ImmutableList.builder(); 1522 name.add(eatIdent()); 1523 while (maybe(Token.DOT)) { 1524 name.add(eatIdent()); 1525 } 1526 return name.build(); 1527 } 1528 annotation(int pos)1529 private Anno annotation(int pos) { 1530 ImmutableList<Ident> name = qualIdent(); 1531 1532 ImmutableList.Builder<Expression> args = ImmutableList.builder(); 1533 if (token == Token.LPAREN) { 1534 eat(LPAREN); 1535 while (token != RPAREN) { 1536 ConstExpressionParser cparser = new ConstExpressionParser(lexer, token, position); 1537 Expression arg = cparser.expression(); 1538 if (arg == null) { 1539 throw error(ErrorKind.INVALID_ANNOTATION_ARGUMENT); 1540 } 1541 args.add(arg); 1542 token = cparser.token; 1543 if (!maybe(COMMA)) { 1544 break; 1545 } 1546 } 1547 eat(Token.RPAREN); 1548 } 1549 1550 return new Anno(pos, name, args.build()); 1551 } 1552 ident()1553 private Ident ident() { 1554 int position = lexer.position(); 1555 String value = lexer.stringValue(); 1556 return new Ident(position, value); 1557 } 1558 eatIdent()1559 private Ident eatIdent() { 1560 Ident ident = ident(); 1561 eat(Token.IDENT); 1562 return ident; 1563 } 1564 eat(Token kind)1565 private void eat(Token kind) { 1566 if (token != kind) { 1567 throw error(ErrorKind.EXPECTED_TOKEN, kind); 1568 } 1569 next(); 1570 } 1571 1572 @CanIgnoreReturnValue maybe(Token kind)1573 private boolean maybe(Token kind) { 1574 if (token == kind) { 1575 next(); 1576 return true; 1577 } 1578 return false; 1579 } 1580 error(Token token)1581 TurbineError error(Token token) { 1582 switch (token) { 1583 case IDENT: 1584 return error(ErrorKind.UNEXPECTED_IDENTIFIER, lexer.stringValue()); 1585 case EOF: 1586 return error(ErrorKind.UNEXPECTED_EOF); 1587 default: 1588 return error(ErrorKind.UNEXPECTED_TOKEN, token); 1589 } 1590 } 1591 error(ErrorKind kind, Object... args)1592 private TurbineError error(ErrorKind kind, Object... args) { 1593 return TurbineError.format( 1594 lexer.source(), 1595 Math.min(lexer.position(), lexer.source().source().length() - 1), 1596 kind, 1597 args); 1598 } 1599 } 1600