• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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