/* NSC -- new Scala compiler * Copyright 2005-2009 LAMP/EPFL * @author Martin Odersky */ // $Id: Parsers.scala 15004 2008-05-13 16:37:33Z odersky $ //todo: allow infix type patterns package scala.tools.nsc package javac import scala.tools.nsc.util.{Position, OffsetPosition, NoPosition, BatchSourceFile} import scala.collection.mutable.ListBuffer import symtab.Flags import JavaTokens._ trait JavaParsers extends JavaScanners { val global : Global import global._ import definitions._ case class JavaOpInfo(operand: Tree, operator: Name, pos: Int) class JavaUnitParser(val unit: global.CompilationUnit) extends JavaParser { val in = new JavaUnitScanner(unit) def freshName(pos : Position, prefix : String) = unit.fresh.newName(pos, prefix) implicit def i2p(offset : Int) : Position = new OffsetPosition(unit.source, offset) def warning(pos : Int, msg : String) : Unit = unit.warning(pos, msg) def syntaxError(pos: Int, msg: String) : Unit = unit.error(pos, msg) } abstract class JavaParser { val in: JavaScanner protected def posToReport: Int = in.currentPos protected def freshName(pos : Position, prefix : String): Name protected implicit def i2p(offset : Int) : Position private implicit def p2i(pos : Position): Int = if (pos.isDefined) pos.point else -1 /** The simple name of the package of the currently parsed file */ private var thisPackageName: Name = nme.EMPTY /** this is the general parse method */ def parse(): Tree = { val t = compilationUnit() accept(EOF) t } // -------- error handling --------------------------------------- private var lastErrorPos : Int = -1 protected def skip() { var nparens = 0 var nbraces = 0 while (true) { in.token match { case EOF => return case SEMI => if (nparens == 0 && nbraces == 0) return case RPAREN => nparens -= 1 case RBRACE => if (nbraces == 0) return nbraces -= 1 case LPAREN => nparens += 1 case LBRACE => nbraces += 1 case _ => } in.nextToken } } def warning(pos : Int, msg : String) : Unit def syntaxError(pos: Int, msg: String) : Unit def syntaxError(msg: String, skipIt: Boolean) { syntaxError(in.currentPos, msg, skipIt) } def syntaxError(pos: Int, msg: String, skipIt: Boolean) { if (pos > lastErrorPos) { syntaxError(pos, msg) // no more errors on this token. lastErrorPos = in.currentPos } if (skipIt) skip() } def warning(msg: String) : Unit = warning(in.currentPos, msg) def errorTypeTree = TypeTree().setType(ErrorType).setPos((in.currentPos)) def errorTermTree = Literal(Constant(null)).setPos((in.currentPos)) def errorPatternTree = blankExpr.setPos((in.currentPos)) // --------- tree building ----------------------------- import gen.{ rootId, scalaDot } def javaDot(name: Name): Tree = Select(rootId(nme.java), name) def javaLangDot(name: Name): Tree = Select(javaDot(nme.lang), name) def javaLangObject(): Tree = javaLangDot(nme.Object.toTypeName) def arrayOf(tpt: Tree) = AppliedTypeTree(scalaDot(nme.Array.toTypeName), List(tpt)) def blankExpr = Ident(nme.WILDCARD) def makePackaging(pkg: RefTree, stats: List[Tree]): PackageDef = atPos(pkg.pos) { PackageDef(pkg, stats) } def makeTemplate(parents: List[Tree], stats: List[Tree]) = Template( parents, emptyValDef, if (treeInfo.firstConstructor(stats) == EmptyTree) makeConstructor(List()) :: stats else stats) def makeParam(name: Name, tpt: Tree) = ValDef(Modifiers(Flags.JAVA | Flags.PARAM), name, tpt, EmptyTree) def makeConstructor(formals: List[Tree]) = { var count = 0 val vparams = for (formal <- formals) yield { count += 1 makeParam(newTermName("x$"+count), formal) } DefDef(Modifiers(Flags.JAVA), nme.CONSTRUCTOR, List(), List(vparams), TypeTree(), blankExpr) } // ------------- general parsing --------------------------- /** skip parent or brace enclosed sequence of things */ def skipAhead() { var nparens = 0 var nbraces = 0 do { in.token match { case LPAREN => nparens += 1 case LBRACE => nbraces += 1 case _ => } in.nextToken in.token match { case RPAREN => nparens -= 1 case RBRACE => nbraces -= 1 case _ => } } while (in.token != EOF && (nparens > 0 || nbraces > 0)) } def skipTo(tokens: Int*) { while (!(tokens contains in.token) && in.token != EOF) { if (in.token == LBRACE) { skipAhead(); accept(RBRACE) } else if (in.token == LPAREN) { skipAhead(); accept(RPAREN) } else in.nextToken } } /** Consume one token of the specified type, or * signal an error if it is not there. */ def accept(token: Int): Int = { val pos = in.currentPos if (in.token != token) { val posToReport = //if (in.currentPos.line(unit.source).get(0) > in.lastPos.line(unit.source).get(0)) // in.lastPos //else in.currentPos val msg = JavaScannerConfiguration.token2string(token) + " expected but " + JavaScannerConfiguration.token2string(in.token) + " found." syntaxError(posToReport, msg, true) } if (in.token == token) in.nextToken pos } def acceptClosingAngle() { if (in.token == GTGTGTEQ) in.token = GTGTEQ else if (in.token == GTGTGT) in.token = GTGT else if (in.token == GTGTEQ) in.token = GTEQ else if (in.token == GTGT) in.token = GT else if (in.token == GTEQ) in.token = ASSIGN else accept(GT) } def ident(): Name = if (in.token == IDENTIFIER) { val name = in.name in.nextToken name } else { accept(IDENTIFIER) nme.ERROR } def repsep[T <: Tree](p: () => T, sep: Int): List[T] = { val buf = ListBuffer[T](p()) while (in.token == sep) { in.nextToken buf += p() } buf.toList } /** Convert (qual)ident to type identifier */ def convertToTypeId(tree: Tree): Tree = tree match { case Ident(name) => Ident(name.toTypeName).setPos(tree.pos) case Select(qual, name) => Select(qual, name.toTypeName).setPos(tree.pos) case AppliedTypeTree(_, _) | ExistentialTypeTree(_, _) => tree case _ => syntaxError(tree.pos, "identifier expected", false) errorTypeTree } // -------------------- specific parsing routines ------------------ def qualId(): RefTree = { var t: RefTree = atPos(in.currentPos) { Ident(ident()) } while (in.token == DOT) { in.nextToken t = atPos(in.currentPos) { Select(t, ident()) } } t } def optArrayBrackets(tpt: Tree): Tree = if (in.token == LBRACKET) { val tpt1 = atPos(in.pos) { arrayOf(tpt) } in.nextToken accept(RBRACKET) optArrayBrackets(tpt1) } else tpt def basicType(): Tree = atPos(in.pos) { in.token match { case BYTE => in.nextToken; TypeTree(ByteClass.tpe) case SHORT => in.nextToken; TypeTree(ShortClass.tpe) case CHAR => in.nextToken; TypeTree(CharClass.tpe) case INT => in.nextToken; TypeTree(IntClass.tpe) case LONG => in.nextToken; TypeTree(LongClass.tpe) case FLOAT => in.nextToken; TypeTree(FloatClass.tpe) case DOUBLE => in.nextToken; TypeTree(DoubleClass.tpe) case BOOLEAN => in.nextToken; TypeTree(BooleanClass.tpe) case _ => syntaxError("illegal start of type", true); errorTypeTree } } def typ(): Tree = optArrayBrackets { if (in.token == FINAL) in.nextToken if (in.token == IDENTIFIER) { var t = typeArgs(atPos(in.currentPos)(Ident(ident()))) while (in.token == DOT) { in.nextToken t = typeArgs(atPos(in.currentPos)(Select(t, ident()))) } convertToTypeId(t) } else { basicType() } } def typeArgs(t: Tree): Tree = { val wildcards = new ListBuffer[TypeDef] def typeArg(): Tree = if (in.token == QMARK) { val pos = in.currentPos in.nextToken var lo: Tree = TypeTree(NothingClass.tpe) var hi: Tree = TypeTree(AnyClass.tpe) if (in.token == EXTENDS) { in.nextToken hi = typ() } else if (in.token == SUPER) { in.nextToken lo = typ() } val tdef = atPos(pos) { TypeDef( Modifiers(Flags.JAVA | Flags.DEFERRED), newTypeName("_$"+ (wildcards.length + 1)), List(), TypeBoundsTree(lo, hi)) } wildcards += tdef atPos(pos) { Ident(tdef.name) } } else { typ() } if (in.token == LT) { in.nextToken val t1 = convertToTypeId(t) val args = repsep(typeArg, COMMA) acceptClosingAngle() atPos(t1.pos) { val t2: Tree = AppliedTypeTree(t1, args) if (wildcards.isEmpty) t2 else ExistentialTypeTree(t2, wildcards.toList) } } else t } def annotations(): List[Tree] = { //var annots = new ListBuffer[Tree] while (in.token == AT) { in.nextToken annotation() } List() // don't pass on annotations for now } /** Annotation ::= TypeName [`(' AnnotationArgument {`,' AnnotationArgument} `)'] */ def annotation() { val pos = in.currentPos var t = qualId() if (in.token == LPAREN) { skipAhead(); accept(RPAREN) } else if (in.token == LBRACE) { skipAhead(); accept(RBRACE) } } /* def annotationArg() = { val pos = in.token if (in.token == IDENTIFIER && in.lookaheadToken == ASSIGN) { val name = ident() accept(ASSIGN) atPos(pos) { ValDef(Modifiers(Flags.JAVA), name, TypeTree(), elementValue()) } } else { elementValue() } } def elementValue(): Tree = if (in.token == AT) annotation() else if (in.token == LBRACE) elementValueArrayInitializer() else expression1() def elementValueArrayInitializer() = { accept(LBRACE) val buf = new ListBuffer[Tree] def loop() = if (in.token != RBRACE) { buf += elementValue() if (in.token == COMMA) { in.nextToken loop() } } loop() accept(RBRACE) buf.toList } */ def modifiers(inInterface: Boolean): Modifiers = { var flags: Long = Flags.JAVA // assumed true unless we see public/private/protected - see bug #1240 var privateWithin: Name = if (inInterface) nme.EMPTY.toTypeName else thisPackageName while (true) { in.token match { case AT if (in.lookaheadToken != INTERFACE) => in.nextToken annotation() case PUBLIC => privateWithin = nme.EMPTY.toTypeName in.nextToken case PROTECTED => flags |= Flags.PROTECTED //privateWithin = thisPackageName in.nextToken case PRIVATE => flags |= Flags.PRIVATE privateWithin = nme.EMPTY.toTypeName in.nextToken case STATIC => flags |= Flags.STATIC in.nextToken case ABSTRACT => flags |= Flags.ABSTRACT in.nextToken case FINAL => flags |= Flags.FINAL in.nextToken case NATIVE | SYNCHRONIZED | TRANSIENT | VOLATILE | STRICTFP => in.nextToken case _ => // XXX both these checks are definitely necessary, which would // seem to indicate the empty package situation needs review // def isEmptyPkg() = // privateWithin == nme.EMPTY.toTypeName || // privateWithin == nme.EMPTY_PACKAGE_NAME_tn // XXX I think this test should just be "if (defaultAccess)" // but then many cases like pos/t1176 fail because scala code // with no package cannot access java code with no package. // if (defaultAccess && !isEmptyPkg) // flags |= Flags.PROTECTED // package private // my every attempt so far has left some combination of // #1240, #1840, #1842, or other java/scala mixes failing. // Reverting to original code, which means #1240 won't // work but other variations should. return Modifiers(flags, privateWithin) } } throw new Error("should not be here") } def typeParams(): List[TypeDef] = if (in.token == LT) { in.nextToken val tparams = repsep(typeParam, COMMA) acceptClosingAngle() tparams } else List() def typeParam(): TypeDef = atPos(in.currentPos) { val name = ident().toTypeName val hi = if (in.token == EXTENDS) { in.nextToken bound() } else { scalaDot(nme.Any.toTypeName) } TypeDef(Modifiers(Flags.JAVA | Flags.DEFERRED | Flags.PARAM), name, List(), TypeBoundsTree(scalaDot(nme.Nothing.toTypeName), hi)) } def bound(): Tree = atPos(in.currentPos) { val buf = ListBuffer[Tree](typ()) while (in.token == AMP) { in.nextToken buf += typ() } val ts = buf.toList if (ts.tail.isEmpty) ts.head else CompoundTypeTree(Template(ts, emptyValDef, List())) } def formalParams(): List[ValDef] = { accept(LPAREN) val vparams = if (in.token == RPAREN) List() else repsep(formalParam, COMMA) accept(RPAREN) vparams } def formalParam(): ValDef = { if (in.token == FINAL) in.nextToken annotations() var t = typ() if (in.token == DOTDOTDOT) { in.nextToken t = atPos(t.pos) { AppliedTypeTree(scalaDot(nme.REPEATED_PARAM_CLASS_NAME.toTypeName), List(t)) } } varDecl(in.currentPos, Modifiers(Flags.JAVA | Flags.PARAM), t, ident()) } def optThrows() { if (in.token == THROWS) { in.nextToken repsep(typ, COMMA) } } def methodBody(): Tree = { skipAhead() accept(RBRACE) // skip block blankExpr } def definesInterface(token: Int) = token == INTERFACE || token == AT def termDecl(mods: Modifiers, parentToken: Int): List[Tree] = { val inInterface = definesInterface(parentToken) val tparams = if (in.token == LT) typeParams() else List() val isVoid = in.token == VOID var rtpt = if (isVoid) { in.nextToken TypeTree(UnitClass.tpe) setPos in.pos } else typ() var pos = in.currentPos val rtptName = rtpt match { case Ident(name) => name case _ => nme.EMPTY } if (in.token == LPAREN && rtptName != nme.EMPTY && !inInterface) { // constructor declaration val vparams = formalParams() optThrows() List { atPos(pos) { DefDef(mods, nme.CONSTRUCTOR, tparams, List(vparams), TypeTree(), methodBody()) } } } else { var mods1 = mods if (mods hasFlag Flags.ABSTRACT) mods1 = mods &~ Flags.ABSTRACT | Flags.DEFERRED pos = in.currentPos val name = ident() if (in.token == LPAREN) { // method declaration val vparams = formalParams() if (!isVoid) rtpt = optArrayBrackets(rtpt) optThrows() val body = if (!inInterface && in.token == LBRACE) { methodBody() } else { if (parentToken == AT && in.token == DEFAULT) { val annot = atPos(pos) { New(rootId(nme.AnnotationDefaultATTR.toTypeName), List(List())) } mods1 = Modifiers(mods1.flags, mods1.privateWithin, annot :: mods1.annotations) skipTo(SEMI) accept(SEMI) blankExpr } else { accept(SEMI) EmptyTree } } if (inInterface) mods1 |= Flags.DEFERRED List { atPos(pos) { DefDef(mods1, name, tparams, List(vparams), rtpt, body) } } } else { if (inInterface) mods1 |= Flags.FINAL | Flags.STATIC val result = fieldDecls(pos, mods1, rtpt, name) accept(SEMI) result } } } /** Parse a sequence of field declarations, separated by commas. * This one is tricky because a comma might also appear in an * initializer. Since we don't parse initializers we don't know * what the comma signifies. * We solve this with a second list buffer `maybe' which contains * potential variable definitions. * Once we have reached the end of the statement, we know whether * these potential definitions are real or not. */ def fieldDecls(pos: Position, mods: Modifiers, tpt: Tree, name: Name): List[Tree] = { val buf = ListBuffer[Tree](varDecl(pos, mods, tpt, name)) val maybe = new ListBuffer[Tree] // potential variable definitions. while (in.token == COMMA) { in.nextToken if (in.token == IDENTIFIER) { // if there's an ident after the comma ... val name = ident() if (in.token == ASSIGN || in.token == SEMI) { // ... followed by a `=' or `;', we know it's a real variable definition buf ++= maybe buf += varDecl(in.currentPos, mods, tpt.duplicate, name) maybe.clear() } else if (in.token == COMMA) { // ... if there's a comma after the ident, it could be a real vardef or not. maybe += varDecl(in.currentPos, mods, tpt.duplicate, name) } else { // ... if there's something else we were still in the initializer of the // previous var def; skip to next comma or semicolon. skipTo(COMMA, SEMI) maybe.clear() } } else { // ... if there's no ident following the comma we were still in the initializer of the // previous var def; skip to next comma or semicolon. skipTo(COMMA, SEMI) maybe.clear() } } if (in.token == SEMI) { buf ++= maybe // every potential vardef that survived until here is real. } buf.toList } def varDecl(pos: Position, mods: Modifiers, tpt: Tree, name: Name): ValDef = { val tpt1 = optArrayBrackets(tpt) if (in.token == ASSIGN && !(mods hasFlag Flags.PARAM)) skipTo(COMMA, SEMI) val mods1 = if (mods hasFlag Flags.FINAL) mods &~ Flags.FINAL else mods | Flags.MUTABLE atPos(pos) { ValDef(mods1, name, tpt1, blankExpr) } } def memberDecl(mods: Modifiers, parentToken: Int): List[Tree] = in.token match { case CLASS | ENUM | INTERFACE | AT => typeDecl(if (definesInterface(parentToken)) mods | Flags.STATIC else mods) case _ => termDecl(mods, parentToken) } def makeCompanionObject(cdef: ClassDef, statics: List[Tree]): Tree = atPos(cdef.pos) { ModuleDef(cdef.mods & (Flags.AccessFlags | Flags.JAVA), cdef.name.toTermName, makeTemplate(List(), statics)) } def importCompanionObject(cdef: ClassDef): Tree = atPos(cdef.pos) { Import(Ident(cdef.name.toTermName), List((nme.WILDCARD, null))) } // create companion object even if it has no statics, so import A._ works; bug #1700 def addCompanionObject(statics: List[Tree], cdef: ClassDef): List[Tree] = List(makeCompanionObject(cdef, statics), importCompanionObject(cdef), cdef) def importDecl(): List[Tree] = { accept(IMPORT) val pos = in.currentPos val buf = new ListBuffer[Name] def collectIdents() { if (in.token == ASTERISK) { in.nextToken buf += nme.WILDCARD } else { buf += ident() if (in.token == DOT) { in.nextToken collectIdents() } } } if (in.token == STATIC) in.nextToken else buf += nme.ROOTPKG collectIdents() accept(SEMI) val names = buf.toList if (names.length < 2) { syntaxError(pos, "illegal import", false) List() } else { val qual = ((Ident(names.head): Tree) /: names.tail.init) (Select(_, _)) val lastname = names.last List { atPos(pos) { if (lastname == nme.WILDCARD) Import(qual, List((lastname, null))) else Import(qual, List((lastname, lastname))) } } } } def interfacesOpt() = if (in.token == IMPLEMENTS) { in.nextToken repsep(typ, COMMA) } else { List() } def classDecl(mods: Modifiers): List[Tree] = { accept(CLASS) val pos = in.currentPos val name = ident().toTypeName val tparams = typeParams() val superclass = if (in.token == EXTENDS) { in.nextToken typ() } else { javaLangObject() } val interfaces = interfacesOpt() val (statics, body) = typeBody(CLASS, name) addCompanionObject(statics, atPos(pos) { ClassDef(mods, name, tparams, makeTemplate(superclass :: interfaces, body)) }) } def interfaceDecl(mods: Modifiers): List[Tree] = { accept(INTERFACE) val pos = in.currentPos val name = ident().toTypeName val tparams = typeParams() val parents = if (in.token == EXTENDS) { in.nextToken repsep(typ, COMMA) } else { List(javaLangObject) } val (statics, body) = typeBody(INTERFACE, name) addCompanionObject(statics, atPos(pos) { ClassDef(mods | Flags.TRAIT | Flags.INTERFACE | Flags.ABSTRACT, name, tparams, makeTemplate(parents, body)) }) } def typeBody(leadingToken: Int, parentName: Name): (List[Tree], List[Tree]) = { accept(LBRACE) val defs = typeBodyDecls(leadingToken, parentName) accept(RBRACE) defs } def typeBodyDecls(parentToken: Int, parentName: Name): (List[Tree], List[Tree]) = { val inInterface = definesInterface(parentToken) val statics = new ListBuffer[Tree] val members = new ListBuffer[Tree] while (in.token != RBRACE && in.token != EOF) { var mods = modifiers(inInterface) if (in.token == LBRACE) { skipAhead() // skip init block, we just assume we have seen only static accept(RBRACE) } else if (in.token == SEMI) { in.nextToken } else { if (in.token == ENUM || definesInterface(in.token)) mods |= Flags.STATIC val decls = memberDecl(mods, parentToken) (if ((mods hasFlag Flags.STATIC) || inInterface && !(decls exists (_.isInstanceOf[DefDef]))) statics else members) ++= decls } } def forwarders(sdef: Tree): List[Tree] = sdef match { case ClassDef(mods, name, tparams, _) if (parentToken == INTERFACE) => val tparams1: List[TypeDef] = tparams map (_.duplicate) var rhs: Tree = Select(Ident(parentName.toTermName), name) if (!tparams1.isEmpty) rhs = AppliedTypeTree(rhs, tparams1 map (tp => Ident(tp.name))) List(TypeDef(Modifiers(Flags.PROTECTED), name, tparams1, rhs)) case _ => List() } val sdefs = statics.toList val idefs = members.toList ::: (sdefs flatMap forwarders) (sdefs, idefs) } def annotationDecl(mods: Modifiers): List[Tree] = { accept(AT) accept(INTERFACE) val pos = in.currentPos val name = ident().toTypeName val parents = List(scalaDot(newTypeName("Annotation")), Select(javaLangDot(newTermName("annotation")), newTypeName("Annotation")), scalaDot(newTypeName("ClassfileAnnotation"))) val (statics, body) = typeBody(AT, name) def getValueMethodType(tree: Tree) = tree match { case DefDef(_, nme.value, _, _, tpt, _) => Some(tpt.duplicate) case _ => None } var templ = makeTemplate(parents, body) for (stat <- templ.body; tpt <- getValueMethodType(stat)) templ = makeTemplate(parents, makeConstructor(List(tpt)) :: templ.body) addCompanionObject(statics, atPos(pos) { ClassDef(mods, name, List(), templ) }) } def enumDecl(mods: Modifiers): List[Tree] = { accept(ENUM) val pos = in.currentPos val name = ident().toTypeName def enumType = Ident(name) val interfaces = interfacesOpt() accept(LBRACE) val buf = new ListBuffer[Tree] def parseEnumConsts() { if (in.token != RBRACE && in.token != SEMI && in.token != EOF) { buf += enumConst(enumType) if (in.token == COMMA) { in.nextToken parseEnumConsts() } } } parseEnumConsts() val consts = buf.toList val (statics, body) = if (in.token == SEMI) { in.nextToken typeBodyDecls(ENUM, name) } else { (List(), List()) } val predefs = List( DefDef( Modifiers(Flags.JAVA | Flags.STATIC), newTermName("values"), List(), List(List()), arrayOf(enumType), blankExpr), DefDef( Modifiers(Flags.JAVA | Flags.STATIC), newTermName("valueOf"), List(), List(List(makeParam(newTermName("x"), TypeTree(StringClass.tpe)))), enumType, blankExpr)) accept(RBRACE) val superclazz = AppliedTypeTree(javaLangDot(newTypeName("Enum")), List(enumType)) addCompanionObject(consts ::: statics ::: predefs, atPos(pos) { ClassDef(mods, name, List(), makeTemplate(superclazz :: interfaces, body)) }) } def enumConst(enumType: Tree) = { annotations() atPos(in.currentPos) { val name = ident() if (in.token == LPAREN) { // skip arguments skipAhead() accept(RPAREN) } if (in.token == LBRACE) { // skip classbody skipAhead() accept(RBRACE) } ValDef(Modifiers(Flags.JAVA | Flags.STATIC), name, enumType, blankExpr) } } def typeDecl(mods: Modifiers): List[Tree] = in.token match { case ENUM => enumDecl(mods) case INTERFACE => interfaceDecl(mods) case AT => annotationDecl(mods) case CLASS => classDecl(mods) case _ => in.nextToken; syntaxError("illegal start of type declaration", true); List(errorTypeTree) } /** CompilationUnit ::= [package QualId semi] TopStatSeq */ def compilationUnit(): Tree = { var pos = in.currentPos; val pkg: RefTree = if (in.token == AT || in.token == PACKAGE) { annotations() pos = in.currentPos accept(PACKAGE) val pkg = qualId() accept(SEMI) pkg } else { Ident(nme.EMPTY_PACKAGE_NAME) } thisPackageName = pkg match { case Ident(name) => name.toTypeName case Select(_, name) => name.toTypeName } val buf = new ListBuffer[Tree] while (in.token == IMPORT) buf ++= importDecl() while (in.token != EOF && in.token != RBRACE) { while (in.token == SEMI) in.nextToken buf ++= typeDecl(modifiers(false)) } accept(EOF) atPos(pos) { makePackaging(pkg, buf.toList) } } } }