Home | History | Annotate | Download | only in parse
      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.INTERFACE;
     21 import static com.google.turbine.parse.Token.LPAREN;
     22 import static com.google.turbine.parse.Token.RPAREN;
     23 import static com.google.turbine.tree.TurbineModifier.PROTECTED;
     24 import static com.google.turbine.tree.TurbineModifier.PUBLIC;
     25 
     26 import com.google.common.base.Optional;
     27 import com.google.common.collect.ImmutableList;
     28 import com.google.common.collect.ImmutableSet;
     29 import com.google.turbine.diag.SourceFile;
     30 import com.google.turbine.diag.TurbineError;
     31 import com.google.turbine.diag.TurbineError.ErrorKind;
     32 import com.google.turbine.model.TurbineConstantTypeKind;
     33 import com.google.turbine.model.TurbineTyKind;
     34 import com.google.turbine.tree.Tree;
     35 import com.google.turbine.tree.Tree.Anno;
     36 import com.google.turbine.tree.Tree.ArrTy;
     37 import com.google.turbine.tree.Tree.ClassTy;
     38 import com.google.turbine.tree.Tree.CompUnit;
     39 import com.google.turbine.tree.Tree.Expression;
     40 import com.google.turbine.tree.Tree.ImportDecl;
     41 import com.google.turbine.tree.Tree.Kind;
     42 import com.google.turbine.tree.Tree.MethDecl;
     43 import com.google.turbine.tree.Tree.PkgDecl;
     44 import com.google.turbine.tree.Tree.PrimTy;
     45 import com.google.turbine.tree.Tree.TyDecl;
     46 import com.google.turbine.tree.Tree.TyParam;
     47 import com.google.turbine.tree.Tree.Type;
     48 import com.google.turbine.tree.Tree.VarDecl;
     49 import com.google.turbine.tree.Tree.WildTy;
     50 import com.google.turbine.tree.TurbineModifier;
     51 import java.util.ArrayDeque;
     52 import java.util.Deque;
     53 import java.util.EnumSet;
     54 import java.util.List;
     55 import javax.annotation.Nullable;
     56 
     57 /**
     58  * A parser for the subset of Java required for header compilation.
     59  *
     60  * <p>See JLS 19: https://docs.oracle.com/javase/specs/jls/se8/html/jls-19.html
     61  */
     62 public class Parser {
     63 
     64   private static final String CTOR_NAME = "<init>";
     65   private final Lexer lexer;
     66 
     67   private Token token;
     68   private int position;
     69 
     70   public static CompUnit parse(String source) {
     71     return parse(new SourceFile(null, source));
     72   }
     73 
     74   public static CompUnit parse(SourceFile source) {
     75     return new Parser(new StreamLexer(new UnicodeEscapePreprocessor(source))).compilationUnit();
     76   }
     77 
     78   private Parser(Lexer lexer) {
     79     this.lexer = lexer;
     80     this.token = lexer.next();
     81   }
     82 
     83   public CompUnit compilationUnit() {
     84     // TODO(cushon): consider enforcing package, import, and declaration order
     85     // and make it bug-compatible with javac:
     86     // http://mail.openjdk.java.net/pipermail/compiler-dev/2013-August/006968.html
     87     Optional<PkgDecl> pkg = Optional.absent();
     88     EnumSet<TurbineModifier> access = EnumSet.noneOf(TurbineModifier.class);
     89     ImmutableList.Builder<ImportDecl> imports = ImmutableList.builder();
     90     ImmutableList.Builder<TyDecl> decls = ImmutableList.builder();
     91     ImmutableList.Builder<Anno> annos = ImmutableList.builder();
     92     while (true) {
     93       switch (token) {
     94         case PACKAGE:
     95           {
     96             next();
     97             pkg = Optional.of(packageDeclaration(annos.build()));
     98             annos = ImmutableList.builder();
     99             break;
    100           }
    101         case IMPORT:
    102           {
    103             next();
    104             ImportDecl i = importDeclaration();
    105             if (i == null) {
    106               continue;
    107             }
    108             imports.add(i);
    109             break;
    110           }
    111         case PUBLIC:
    112           next();
    113           access.add(PUBLIC);
    114           break;
    115         case PROTECTED:
    116           next();
    117           access.add(PROTECTED);
    118           break;
    119         case PRIVATE:
    120           next();
    121           access.add(TurbineModifier.PRIVATE);
    122           break;
    123         case STATIC:
    124           next();
    125           access.add(TurbineModifier.STATIC);
    126           break;
    127         case ABSTRACT:
    128           next();
    129           access.add(TurbineModifier.ABSTRACT);
    130           break;
    131         case FINAL:
    132           next();
    133           access.add(TurbineModifier.FINAL);
    134           break;
    135         case STRICTFP:
    136           next();
    137           access.add(TurbineModifier.STRICTFP);
    138           break;
    139         case AT:
    140           {
    141             next();
    142             if (token == INTERFACE) {
    143               decls.add(annotationDeclaration(access, annos.build()));
    144               access = EnumSet.noneOf(TurbineModifier.class);
    145               annos = ImmutableList.builder();
    146             } else {
    147               annos.add(annotation());
    148             }
    149             break;
    150           }
    151         case CLASS:
    152           decls.add(classDeclaration(access, annos.build()));
    153           access = EnumSet.noneOf(TurbineModifier.class);
    154           annos = ImmutableList.builder();
    155           break;
    156         case INTERFACE:
    157           decls.add(interfaceDeclaration(access, annos.build()));
    158           access = EnumSet.noneOf(TurbineModifier.class);
    159           annos = ImmutableList.builder();
    160           break;
    161         case ENUM:
    162           decls.add(enumDeclaration(access, annos.build()));
    163           access = EnumSet.noneOf(TurbineModifier.class);
    164           annos = ImmutableList.builder();
    165           break;
    166         case EOF:
    167           // TODO(cushon): check for dangling modifiers?
    168           return new CompUnit(position, pkg, imports.build(), decls.build(), lexer.source());
    169         case SEMI:
    170           // TODO(cushon): check for dangling modifiers?
    171           next();
    172           continue;
    173         default:
    174           throw error(token);
    175       }
    176     }
    177   }
    178 
    179   private void next() {
    180     token = lexer.next();
    181     position = lexer.position();
    182   }
    183 
    184   private TyDecl interfaceDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) {
    185     eat(Token.INTERFACE);
    186     String name = eatIdent();
    187     ImmutableList<TyParam> typarams;
    188     if (token == Token.LT) {
    189       typarams = typarams();
    190     } else {
    191       typarams = ImmutableList.of();
    192     }
    193     ImmutableList.Builder<ClassTy> interfaces = ImmutableList.builder();
    194     if (token == Token.EXTENDS) {
    195       next();
    196       do {
    197         interfaces.add(classty());
    198       } while (maybe(Token.COMMA));
    199     }
    200     eat(Token.LBRACE);
    201     ImmutableList<Tree> members = classMembers();
    202     eat(Token.RBRACE);
    203     return new TyDecl(
    204         position,
    205         access,
    206         annos,
    207         name,
    208         typarams,
    209         Optional.<ClassTy>absent(),
    210         interfaces.build(),
    211         members,
    212         TurbineTyKind.INTERFACE);
    213   }
    214 
    215   private TyDecl annotationDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) {
    216     eat(Token.INTERFACE);
    217     String name = eatIdent();
    218     eat(Token.LBRACE);
    219     ImmutableList<Tree> members = classMembers();
    220     eat(Token.RBRACE);
    221     return new TyDecl(
    222         position,
    223         access,
    224         annos,
    225         name,
    226         ImmutableList.<TyParam>of(),
    227         Optional.<ClassTy>absent(),
    228         ImmutableList.<ClassTy>of(),
    229         members,
    230         TurbineTyKind.ANNOTATION);
    231   }
    232 
    233   private TyDecl enumDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) {
    234     eat(Token.ENUM);
    235     String name = eatIdent();
    236     ImmutableList.Builder<ClassTy> interfaces = ImmutableList.builder();
    237     if (token == Token.IMPLEMENTS) {
    238       next();
    239       do {
    240         interfaces.add(classty());
    241       } while (maybe(Token.COMMA));
    242     }
    243     eat(Token.LBRACE);
    244     ImmutableList<Tree> members =
    245         ImmutableList.<Tree>builder().addAll(enumMembers(name)).addAll(classMembers()).build();
    246     eat(Token.RBRACE);
    247     return new TyDecl(
    248         position,
    249         access,
    250         annos,
    251         name,
    252         ImmutableList.<TyParam>of(),
    253         Optional.<ClassTy>absent(),
    254         interfaces.build(),
    255         members,
    256         TurbineTyKind.ENUM);
    257   }
    258 
    259   private static final ImmutableSet<TurbineModifier> ENUM_CONSTANT_MODIFIERS =
    260       ImmutableSet.of(
    261           TurbineModifier.PUBLIC,
    262           TurbineModifier.STATIC,
    263           TurbineModifier.ACC_ENUM,
    264           TurbineModifier.FINAL);
    265 
    266   private ImmutableList<Tree> enumMembers(String enumName) {
    267     ImmutableList.Builder<Tree> result = ImmutableList.builder();
    268     ImmutableList.Builder<Anno> annos = ImmutableList.builder();
    269     OUTER:
    270     while (true) {
    271       switch (token) {
    272         case IDENT:
    273           {
    274             String name = eatIdent();
    275             if (token == Token.LPAREN) {
    276               dropParens();
    277             }
    278             // TODO(cushon): consider desugaring enum constants later
    279             if (token == Token.LBRACE) {
    280               dropBlocks();
    281             }
    282             maybe(Token.COMMA);
    283             result.add(
    284                 new VarDecl(
    285                     position,
    286                     ENUM_CONSTANT_MODIFIERS,
    287                     annos.build(),
    288                     new ClassTy(
    289                         position,
    290                         Optional.<ClassTy>absent(),
    291                         enumName,
    292                         ImmutableList.<Type>of(),
    293                         ImmutableList.of()),
    294                     name,
    295                     Optional.<Expression>absent()));
    296             annos = ImmutableList.builder();
    297             break;
    298           }
    299         case SEMI:
    300           next();
    301           annos = ImmutableList.builder();
    302           break OUTER;
    303         case RBRACE:
    304           annos = ImmutableList.builder();
    305           break OUTER;
    306         case AT:
    307           next();
    308           annos.add(annotation());
    309           break;
    310         default:
    311           throw error(token);
    312       }
    313     }
    314     return result.build();
    315   }
    316 
    317   private TyDecl classDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) {
    318     eat(Token.CLASS);
    319     String name = eatIdent();
    320     ImmutableList<TyParam> tyParams = ImmutableList.of();
    321     if (token == Token.LT) {
    322       tyParams = typarams();
    323     }
    324     ClassTy xtnds = null;
    325     if (token == Token.EXTENDS) {
    326       next();
    327       xtnds = classty();
    328     }
    329     ImmutableList.Builder<ClassTy> interfaces = ImmutableList.builder();
    330     if (token == Token.IMPLEMENTS) {
    331       next();
    332       do {
    333         interfaces.add(classty());
    334       } while (maybe(Token.COMMA));
    335     }
    336     eat(Token.LBRACE);
    337     ImmutableList<Tree> members = classMembers();
    338     eat(Token.RBRACE);
    339     return new TyDecl(
    340         position,
    341         access,
    342         annos,
    343         name,
    344         tyParams,
    345         Optional.fromNullable(xtnds),
    346         interfaces.build(),
    347         members,
    348         TurbineTyKind.CLASS);
    349   }
    350 
    351   private ImmutableList<Tree> classMembers() {
    352     ImmutableList.Builder<Tree> acc = ImmutableList.builder();
    353     EnumSet<TurbineModifier> access = EnumSet.noneOf(TurbineModifier.class);
    354     ImmutableList.Builder<Anno> annos = ImmutableList.builder();
    355     while (true) {
    356       switch (token) {
    357         case PUBLIC:
    358           next();
    359           access.add(TurbineModifier.PUBLIC);
    360           break;
    361         case PROTECTED:
    362           next();
    363           access.add(TurbineModifier.PROTECTED);
    364           break;
    365         case PRIVATE:
    366           next();
    367           access.add(TurbineModifier.PRIVATE);
    368           break;
    369         case STATIC:
    370           next();
    371           access.add(TurbineModifier.STATIC);
    372           break;
    373         case ABSTRACT:
    374           next();
    375           access.add(TurbineModifier.ABSTRACT);
    376           break;
    377         case FINAL:
    378           next();
    379           access.add(TurbineModifier.FINAL);
    380           break;
    381         case NATIVE:
    382           next();
    383           access.add(TurbineModifier.NATIVE);
    384           break;
    385         case SYNCHRONIZED:
    386           next();
    387           access.add(TurbineModifier.SYNCHRONIZED);
    388           break;
    389         case TRANSIENT:
    390           next();
    391           access.add(TurbineModifier.TRANSIENT);
    392           break;
    393         case VOLATILE:
    394           next();
    395           access.add(TurbineModifier.VOLATILE);
    396           break;
    397         case STRICTFP:
    398           next();
    399           access.add(TurbineModifier.STRICTFP);
    400           break;
    401         case DEFAULT:
    402           next();
    403           access.add(TurbineModifier.DEFAULT);
    404           break;
    405         case AT:
    406           {
    407             // TODO(cushon): de-dup with top-level parsing
    408             next();
    409             if (token == INTERFACE) {
    410               acc.add(annotationDeclaration(access, annos.build()));
    411               access = EnumSet.noneOf(TurbineModifier.class);
    412               annos = ImmutableList.builder();
    413             } else {
    414               annos.add(annotation());
    415             }
    416             break;
    417           }
    418 
    419         case IDENT:
    420         case BOOLEAN:
    421         case BYTE:
    422         case SHORT:
    423         case INT:
    424         case LONG:
    425         case CHAR:
    426         case DOUBLE:
    427         case FLOAT:
    428         case VOID:
    429         case LT:
    430           acc.addAll(classMember(access, annos.build()));
    431           access = EnumSet.noneOf(TurbineModifier.class);
    432           annos = ImmutableList.builder();
    433           break;
    434         case LBRACE:
    435           dropBlocks();
    436           access = EnumSet.noneOf(TurbineModifier.class);
    437           annos = ImmutableList.builder();
    438           break;
    439         case CLASS:
    440           acc.add(classDeclaration(access, annos.build()));
    441           access = EnumSet.noneOf(TurbineModifier.class);
    442           annos = ImmutableList.builder();
    443           break;
    444         case INTERFACE:
    445           acc.add(interfaceDeclaration(access, annos.build()));
    446           access = EnumSet.noneOf(TurbineModifier.class);
    447           annos = ImmutableList.builder();
    448           break;
    449         case ENUM:
    450           acc.add(enumDeclaration(access, annos.build()));
    451           access = EnumSet.noneOf(TurbineModifier.class);
    452           annos = ImmutableList.builder();
    453           break;
    454         case RBRACE:
    455           return acc.build();
    456         case SEMI:
    457           next();
    458           continue;
    459         default:
    460           throw error(token);
    461       }
    462     }
    463   }
    464 
    465   private ImmutableList<Tree> classMember(
    466       EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) {
    467     ImmutableList<TyParam> typaram = ImmutableList.of();
    468     Type result;
    469     String name;
    470 
    471     if (token == Token.LT) {
    472       typaram = typarams();
    473     }
    474 
    475     if (token == Token.AT) {
    476       annos = ImmutableList.<Anno>builder().addAll(annos).addAll(maybeAnnos()).build();
    477     }
    478 
    479     switch (token) {
    480       case VOID:
    481         {
    482           result = new Tree.VoidTy(position);
    483           next();
    484           int pos = position;
    485           name = eatIdent();
    486           return memberRest(pos, access, annos, typaram, result, name);
    487         }
    488       case BOOLEAN:
    489       case BYTE:
    490       case SHORT:
    491       case INT:
    492       case LONG:
    493       case CHAR:
    494       case DOUBLE:
    495       case FLOAT:
    496         {
    497           result = referenceType(ImmutableList.of());
    498           int pos = position;
    499           name = eatIdent();
    500           return memberRest(pos, access, annos, typaram, result, name);
    501         }
    502       case IDENT:
    503         {
    504           int pos = position;
    505           String ident = eatIdent();
    506           switch (token) {
    507             case LPAREN:
    508               {
    509                 name = ident;
    510                 return ImmutableList.of(methodRest(pos, access, annos, typaram, null, name));
    511               }
    512             case IDENT:
    513               {
    514                 result =
    515                     new ClassTy(
    516                         position,
    517                         Optional.<ClassTy>absent(),
    518                         ident,
    519                         ImmutableList.<Type>of(),
    520                         ImmutableList.of());
    521                 pos = position;
    522                 name = eatIdent();
    523                 return memberRest(pos, access, annos, typaram, result, name);
    524               }
    525             case AT:
    526             case LBRACK:
    527               {
    528                 result =
    529                     new ClassTy(
    530                         position,
    531                         Optional.<ClassTy>absent(),
    532                         ident,
    533                         ImmutableList.<Type>of(),
    534                         ImmutableList.of());
    535                 result = maybeDims(maybeAnnos(), result);
    536                 break;
    537               }
    538             case LT:
    539               {
    540                 result =
    541                     new ClassTy(
    542                         position, Optional.<ClassTy>absent(), ident, tyargs(), ImmutableList.of());
    543                 result = maybeDims(maybeAnnos(), result);
    544                 break;
    545               }
    546             case DOT:
    547               result =
    548                   new ClassTy(
    549                       position,
    550                       Optional.<ClassTy>absent(),
    551                       ident,
    552                       ImmutableList.<Type>of(),
    553                       ImmutableList.of());
    554               break;
    555             default:
    556               throw error(token);
    557           }
    558           if (result == null) {
    559             throw error(token);
    560           }
    561           if (token == Token.DOT) {
    562             next();
    563             // TODO(cushon): is this cast OK?
    564             result = classty((ClassTy) result);
    565           }
    566           result = maybeDims(maybeAnnos(), result);
    567           pos = position;
    568           name = eatIdent();
    569           switch (token) {
    570             case LPAREN:
    571               return ImmutableList.of(methodRest(pos, access, annos, typaram, result, name));
    572             case LBRACK:
    573             case SEMI:
    574             case ASSIGN:
    575             case COMMA:
    576               {
    577                 if (!typaram.isEmpty()) {
    578                   throw error(ErrorKind.UNEXPECTED_TYPE_PARAMETER, typaram);
    579                 }
    580                 return fieldRest(pos, access, annos, result, name);
    581               }
    582             default:
    583               throw error(token);
    584           }
    585         }
    586       default:
    587         throw error(token);
    588     }
    589   }
    590 
    591   private ImmutableList<Anno> maybeAnnos() {
    592     if (token != Token.AT) {
    593       return ImmutableList.of();
    594     }
    595     ImmutableList.Builder<Anno> builder = ImmutableList.builder();
    596     while (token == Token.AT) {
    597       next();
    598       builder.add(annotation());
    599     }
    600     return builder.build();
    601   }
    602 
    603   private ImmutableList<Tree> memberRest(
    604       int pos,
    605       EnumSet<TurbineModifier> access,
    606       ImmutableList<Anno> annos,
    607       ImmutableList<TyParam> typaram,
    608       Type result,
    609       String name) {
    610     switch (token) {
    611       case ASSIGN:
    612       case SEMI:
    613       case LBRACK:
    614       case COMMA:
    615         {
    616           if (!typaram.isEmpty()) {
    617             throw error(ErrorKind.UNEXPECTED_TYPE_PARAMETER, typaram);
    618           }
    619           return fieldRest(pos, access, annos, result, name);
    620         }
    621       case LPAREN:
    622         return ImmutableList.of(methodRest(pos, access, annos, typaram, result, name));
    623       default:
    624         throw error(token);
    625     }
    626   }
    627 
    628   private ImmutableList<Tree> fieldRest(
    629       int pos,
    630       EnumSet<TurbineModifier> access,
    631       ImmutableList<Anno> annos,
    632       Type baseTy,
    633       String name) {
    634     ImmutableList.Builder<Tree> result = ImmutableList.builder();
    635     VariableInitializerParser initializerParser = new VariableInitializerParser(token, lexer);
    636     List<List<SavedToken>> bits = initializerParser.parseInitializers();
    637     token = initializerParser.token;
    638 
    639     boolean first = true;
    640     for (List<SavedToken> bit : bits) {
    641       IteratorLexer lexer = new IteratorLexer(this.lexer.source(), bit.iterator());
    642       Parser parser = new Parser(lexer);
    643       if (first) {
    644         first = false;
    645       } else {
    646         name = parser.eatIdent();
    647       }
    648       Type ty = baseTy;
    649       ty = parser.extraDims(ty);
    650       // TODO(cushon): skip more fields that are definitely non-const
    651       Expression init = new ConstExpressionParser(lexer, lexer.next()).expression();
    652       if (init != null && init.kind() == Tree.Kind.ARRAY_INIT) {
    653         init = null;
    654       }
    655       result.add(new VarDecl(pos, access, annos, ty, name, Optional.fromNullable(init)));
    656     }
    657     eat(Token.SEMI);
    658     return result.build();
    659   }
    660 
    661   private Tree methodRest(
    662       int pos,
    663       EnumSet<TurbineModifier> access,
    664       ImmutableList<Anno> annos,
    665       ImmutableList<TyParam> typaram,
    666       Type result,
    667       String name) {
    668     eat(Token.LPAREN);
    669     ImmutableList.Builder<VarDecl> formals = ImmutableList.builder();
    670     formalParams(formals, access);
    671     eat(Token.RPAREN);
    672 
    673     result = extraDims(result);
    674 
    675     ImmutableList.Builder<ClassTy> exceptions = ImmutableList.builder();
    676     if (token == Token.THROWS) {
    677       next();
    678       exceptions.addAll(exceptions());
    679     }
    680     Tree defaultValue = null;
    681     switch (token) {
    682       case SEMI:
    683         next();
    684         break;
    685       case LBRACE:
    686         dropBlocks();
    687         break;
    688       case DEFAULT:
    689         {
    690           ConstExpressionParser cparser = new ConstExpressionParser(lexer, lexer.next());
    691           Tree expr = cparser.expression();
    692           token = cparser.token;
    693           if (expr == null && token == Token.AT) {
    694             next();
    695             expr = annotation();
    696           }
    697           if (expr == null) {
    698             throw error(token);
    699           }
    700           defaultValue = expr;
    701           eat(Token.SEMI);
    702           break;
    703         }
    704       default:
    705         throw error(token);
    706     }
    707     if (result == null) {
    708       name = CTOR_NAME;
    709     }
    710     return new MethDecl(
    711         pos,
    712         access,
    713         annos,
    714         typaram,
    715         Optional.<Tree>fromNullable(result),
    716         name,
    717         formals.build(),
    718         exceptions.build(),
    719         Optional.fromNullable(defaultValue));
    720   }
    721 
    722   /**
    723    * Given a base {@code type} and some number of {@code extra} c-style array dimension specifiers,
    724    * construct a new array type.
    725    *
    726    * <p>For reasons that are unclear from the spec, {@code int @A [] x []} is equivalent to {@code
    727    * int [] @A [] x}, not {@code int @A [] [] x}.
    728    */
    729   private Type extraDims(Type ty) {
    730     ImmutableList<Anno> annos = maybeAnnos();
    731     if (!annos.isEmpty() && token != Token.LBRACK) {
    732       // orphaned type annotations
    733       throw error(token);
    734     }
    735     Deque<ImmutableList<Anno>> extra = new ArrayDeque<>();
    736     while (maybe(Token.LBRACK)) {
    737       eat(Token.RBRACK);
    738       extra.push(annos);
    739       annos = maybeAnnos();
    740     }
    741     ty = extraDims(ty, extra);
    742     return ty;
    743   }
    744 
    745   private Type extraDims(Type type, Deque<ImmutableList<Anno>> extra) {
    746     if (extra.isEmpty()) {
    747       return type;
    748     }
    749     if (type.kind() == Kind.ARR_TY) {
    750       ArrTy arrTy = (ArrTy) type;
    751       return new ArrTy(arrTy.position(), arrTy.annos(), extraDims(arrTy.elem(), extra));
    752     }
    753     return new ArrTy(type.position(), extra.pop(), extraDims(type, extra));
    754   }
    755 
    756   private ImmutableList<ClassTy> exceptions() {
    757     ImmutableList.Builder<ClassTy> result = ImmutableList.builder();
    758     result.add(classty());
    759     while (maybe(Token.COMMA)) {
    760       result.add(classty());
    761     }
    762     return result.build();
    763   }
    764 
    765   private void formalParams(
    766       ImmutableList.Builder<VarDecl> builder, EnumSet<TurbineModifier> access) {
    767     while (token != Token.RPAREN) {
    768       VarDecl formal = formalParam();
    769       builder.add(formal);
    770       if (formal.mods().contains(TurbineModifier.VARARGS)) {
    771         access.add(TurbineModifier.VARARGS);
    772       }
    773       if (token != Token.COMMA) {
    774         break;
    775       }
    776       next();
    777     }
    778   }
    779 
    780   private VarDecl formalParam() {
    781     ImmutableList.Builder<Anno> annos = ImmutableList.builder();
    782     EnumSet<TurbineModifier> access = modifiersAndAnnotations(annos);
    783     Type ty = referenceType(ImmutableList.of());
    784     ImmutableList<Anno> typeAnnos = maybeAnnos();
    785     if (maybe(Token.ELLIPSIS)) {
    786       access.add(TurbineModifier.VARARGS);
    787       ty = new ArrTy(position, typeAnnos, ty);
    788     } else {
    789       ty = maybeDims(typeAnnos, ty);
    790     }
    791     // the parameter name is `this` for receiver parameters, and a qualified this expression
    792     // for inner classes
    793     String name = identOrThis();
    794     while (token == Token.DOT) {
    795       eat(Token.DOT);
    796       // Overwrite everything up to the terminal 'this' for inner classes; we don't need it
    797       name = identOrThis();
    798     }
    799     ty = extraDims(ty);
    800     return new VarDecl(position, access, annos.build(), ty, name, Optional.<Expression>absent());
    801   }
    802 
    803   private String identOrThis() {
    804     switch (token) {
    805       case IDENT:
    806         return eatIdent();
    807       case THIS:
    808         eat(Token.THIS);
    809         return "this";
    810       default:
    811         throw error(token);
    812     }
    813   }
    814 
    815   private void dropParens() {
    816     eat(Token.LPAREN);
    817     int depth = 1;
    818     while (depth > 0) {
    819       switch (token) {
    820         case RPAREN:
    821           depth--;
    822           break;
    823         case LPAREN:
    824           depth++;
    825           break;
    826         default:
    827           break;
    828       }
    829       next();
    830     }
    831   }
    832 
    833   private void dropBlocks() {
    834     eat(Token.LBRACE);
    835     int depth = 1;
    836     while (depth > 0) {
    837       switch (token) {
    838         case RBRACE:
    839           depth--;
    840           break;
    841         case LBRACE:
    842           depth++;
    843           break;
    844         default:
    845           break;
    846       }
    847       next();
    848     }
    849   }
    850 
    851   private ImmutableList<TyParam> typarams() {
    852     ImmutableList.Builder<TyParam> acc = ImmutableList.builder();
    853     eat(Token.LT);
    854     OUTER:
    855     while (true) {
    856       ImmutableList<Anno> annotations = maybeAnnos();
    857       String name = eatIdent();
    858       ImmutableList<Tree> bounds = ImmutableList.of();
    859       if (token == Token.EXTENDS) {
    860         next();
    861         bounds = tybounds();
    862       }
    863       acc.add(new TyParam(position, name, bounds, annotations));
    864       switch (token) {
    865         case COMMA:
    866           eat(Token.COMMA);
    867           continue;
    868         case GT:
    869           next();
    870           break OUTER;
    871         default:
    872           throw error(token);
    873       }
    874     }
    875     return acc.build();
    876   }
    877 
    878   private ImmutableList<Tree> tybounds() {
    879     ImmutableList.Builder<Tree> acc = ImmutableList.builder();
    880     do {
    881       acc.add(classty());
    882     } while (maybe(Token.AND));
    883     return acc.build();
    884   }
    885 
    886   private ClassTy classty() {
    887     return classty(null);
    888   }
    889 
    890   private ClassTy classty(ClassTy ty) {
    891     return classty(ty, null);
    892   }
    893 
    894   private ClassTy classty(ClassTy ty, @Nullable ImmutableList<Anno> typeAnnos) {
    895     int pos = position;
    896     do {
    897       if (typeAnnos == null) {
    898         typeAnnos = maybeAnnos();
    899       }
    900       String name = eatIdent();
    901       ImmutableList<Type> tyargs = ImmutableList.of();
    902       if (token == Token.LT) {
    903         tyargs = tyargs();
    904       }
    905       ty = new ClassTy(pos, Optional.fromNullable(ty), name, tyargs, typeAnnos);
    906       typeAnnos = null;
    907     } while (maybe(Token.DOT));
    908     return ty;
    909   }
    910 
    911   private ImmutableList<Type> tyargs() {
    912     ImmutableList.Builder<Type> acc = ImmutableList.builder();
    913     eat(Token.LT);
    914     OUTER:
    915     do {
    916       ImmutableList<Anno> typeAnnos = maybeAnnos();
    917       switch (token) {
    918         case COND:
    919           {
    920             next();
    921             switch (token) {
    922               case EXTENDS:
    923                 next();
    924                 Type upper = referenceType(maybeAnnos());
    925                 acc.add(
    926                     new WildTy(position, typeAnnos, Optional.of(upper), Optional.<Type>absent()));
    927                 break;
    928               case SUPER:
    929                 next();
    930                 Type lower = referenceType(maybeAnnos());
    931                 acc.add(
    932                     new WildTy(position, typeAnnos, Optional.<Type>absent(), Optional.of(lower)));
    933                 break;
    934               case COMMA:
    935                 acc.add(
    936                     new WildTy(
    937                         position, typeAnnos, Optional.<Type>absent(), Optional.<Type>absent()));
    938                 continue OUTER;
    939               case GT:
    940               case GTGT:
    941               case GTGTGT:
    942                 acc.add(
    943                     new WildTy(
    944                         position, typeAnnos, Optional.<Type>absent(), Optional.<Type>absent()));
    945                 break OUTER;
    946               default:
    947                 throw error(token);
    948             }
    949             break;
    950           }
    951         case IDENT:
    952         case BOOLEAN:
    953         case BYTE:
    954         case SHORT:
    955         case INT:
    956         case LONG:
    957         case CHAR:
    958         case DOUBLE:
    959         case FLOAT:
    960           acc.add(referenceType(typeAnnos));
    961           break;
    962         default:
    963           throw error(token);
    964       }
    965     } while (maybe(Token.COMMA));
    966     switch (token) {
    967       case GT:
    968         next();
    969         break;
    970       case GTGT:
    971         token = Token.GT;
    972         break;
    973       case GTGTGT:
    974         token = Token.GTGT;
    975         break;
    976       default:
    977         throw error(token);
    978     }
    979     return acc.build();
    980   }
    981 
    982   private Type referenceType(ImmutableList<Anno> typeAnnos) {
    983     Type ty;
    984     switch (token) {
    985       case IDENT:
    986         ty = classty(null, typeAnnos);
    987         break;
    988       case BOOLEAN:
    989         next();
    990         ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.BOOLEAN);
    991         break;
    992       case BYTE:
    993         next();
    994         ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.BYTE);
    995         break;
    996       case SHORT:
    997         next();
    998         ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.SHORT);
    999         break;
   1000       case INT:
   1001         next();
   1002         ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.INT);
   1003         break;
   1004       case LONG:
   1005         next();
   1006         ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.LONG);
   1007         break;
   1008       case CHAR:
   1009         next();
   1010         ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.CHAR);
   1011         break;
   1012       case DOUBLE:
   1013         next();
   1014         ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.DOUBLE);
   1015         break;
   1016       case FLOAT:
   1017         next();
   1018         ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.FLOAT);
   1019         break;
   1020       default:
   1021         throw error(token);
   1022     }
   1023     ty = maybeDims(maybeAnnos(), ty);
   1024     return ty;
   1025   }
   1026 
   1027   private Type maybeDims(ImmutableList<Anno> typeAnnos, Type ty) {
   1028     while (maybe(Token.LBRACK)) {
   1029       eat(Token.RBRACK);
   1030       ty = new ArrTy(position, typeAnnos, ty);
   1031       typeAnnos = maybeAnnos();
   1032     }
   1033     return ty;
   1034   }
   1035 
   1036   private EnumSet<TurbineModifier> modifiersAndAnnotations(ImmutableList.Builder<Anno> annos) {
   1037     EnumSet<TurbineModifier> access = EnumSet.noneOf(TurbineModifier.class);
   1038     while (true) {
   1039       switch (token) {
   1040         case PUBLIC:
   1041           next();
   1042           access.add(TurbineModifier.PUBLIC);
   1043           break;
   1044         case PROTECTED:
   1045           next();
   1046           access.add(TurbineModifier.PROTECTED);
   1047           break;
   1048         case PRIVATE:
   1049           next();
   1050           access.add(TurbineModifier.PRIVATE);
   1051           break;
   1052         case STATIC:
   1053           next();
   1054           access.add(TurbineModifier.STATIC);
   1055           break;
   1056         case ABSTRACT:
   1057           next();
   1058           access.add(TurbineModifier.ABSTRACT);
   1059           break;
   1060         case FINAL:
   1061           next();
   1062           access.add(TurbineModifier.FINAL);
   1063           break;
   1064         case NATIVE:
   1065           next();
   1066           access.add(TurbineModifier.NATIVE);
   1067           break;
   1068         case SYNCHRONIZED:
   1069           next();
   1070           access.add(TurbineModifier.SYNCHRONIZED);
   1071           break;
   1072         case TRANSIENT:
   1073           next();
   1074           access.add(TurbineModifier.TRANSIENT);
   1075           break;
   1076         case VOLATILE:
   1077           next();
   1078           access.add(TurbineModifier.VOLATILE);
   1079           break;
   1080         case STRICTFP:
   1081           next();
   1082           access.add(TurbineModifier.STRICTFP);
   1083           break;
   1084         case AT:
   1085           next();
   1086           annos.add(annotation());
   1087           break;
   1088         default:
   1089           return access;
   1090       }
   1091     }
   1092   }
   1093 
   1094   private ImportDecl importDeclaration() {
   1095     boolean stat = maybe(Token.STATIC);
   1096 
   1097     int pos = position;
   1098     ImmutableList.Builder<String> type = ImmutableList.builder();
   1099     type.add(eatIdent());
   1100     boolean wild = false;
   1101     OUTER:
   1102     while (maybe(Token.DOT)) {
   1103       switch (token) {
   1104         case IDENT:
   1105           type.add(eatIdent());
   1106           break;
   1107         case MULT:
   1108           eat(Token.MULT);
   1109           wild = true;
   1110           break OUTER;
   1111         default:
   1112           break;
   1113       }
   1114     }
   1115     eat(Token.SEMI);
   1116     return new ImportDecl(pos, type.build(), stat, wild);
   1117   }
   1118 
   1119   private PkgDecl packageDeclaration(ImmutableList<Anno> annos) {
   1120     PkgDecl result = new PkgDecl(position, qualIdent(), annos);
   1121     eat(Token.SEMI);
   1122     return result;
   1123   }
   1124 
   1125   private ImmutableList<String> qualIdent() {
   1126     ImmutableList.Builder<String> name = ImmutableList.builder();
   1127     name.add(eatIdent());
   1128     while (maybe(Token.DOT)) {
   1129       name.add(eatIdent());
   1130     }
   1131     return name.build();
   1132   }
   1133 
   1134   private Anno annotation() {
   1135     int pos = position;
   1136     ImmutableList<String> name = qualIdent();
   1137 
   1138     ImmutableList.Builder<Expression> args = ImmutableList.builder();
   1139     if (token == Token.LPAREN) {
   1140       eat(LPAREN);
   1141       while (token != RPAREN) {
   1142         ConstExpressionParser cparser = new ConstExpressionParser(lexer, token);
   1143         Expression arg = cparser.expression();
   1144         if (arg == null) {
   1145           throw error(ErrorKind.INVALID_ANNOTATION_ARGUMENT);
   1146         }
   1147         args.add(arg);
   1148         token = cparser.token;
   1149         if (!maybe(COMMA)) {
   1150           break;
   1151         }
   1152       }
   1153       eat(Token.RPAREN);
   1154     }
   1155 
   1156     return new Anno(pos, name, args.build());
   1157   }
   1158 
   1159   private String eatIdent() {
   1160     String value = lexer.stringValue();
   1161     eat(Token.IDENT);
   1162     return value;
   1163   }
   1164 
   1165   private void eat(Token kind) {
   1166     if (token != kind) {
   1167       throw error(ErrorKind.EXPECTED_TOKEN, kind);
   1168     }
   1169     next();
   1170   }
   1171 
   1172   private boolean maybe(Token kind) {
   1173     if (token == kind) {
   1174       next();
   1175       return true;
   1176     }
   1177     return false;
   1178   }
   1179 
   1180   TurbineError error(Token token) {
   1181     switch (token) {
   1182       case IDENT:
   1183         return error(ErrorKind.UNEXPECTED_IDENTIFIER, lexer.stringValue());
   1184       case EOF:
   1185         return error(ErrorKind.UNEXPECTED_EOF);
   1186       default:
   1187         return error(ErrorKind.UNEXPECTED_TOKEN, token);
   1188     }
   1189   }
   1190 
   1191   private TurbineError error(ErrorKind kind, Object... args) {
   1192     return TurbineError.format(
   1193         lexer.source(),
   1194         Math.min(lexer.position(), lexer.source().source().length() - 1),
   1195         kind,
   1196         args);
   1197   }
   1198 }
   1199