aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/parsing/Parsers.scala
diff options
context:
space:
mode:
authorFelix Mulder <felix.mulder@gmail.com>2016-11-02 11:08:28 +0100
committerGuillaume Martres <smarter@ubuntu.com>2016-11-22 01:35:07 +0100
commit8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch)
treea8147561d307af862c295cfc8100d271063bb0dd /compiler/src/dotty/tools/dotc/parsing/Parsers.scala
parent6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff)
downloaddotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.gz
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.bz2
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.zip
Move compiler and compiler tests to compiler dir
Diffstat (limited to 'compiler/src/dotty/tools/dotc/parsing/Parsers.scala')
-rw-r--r--compiler/src/dotty/tools/dotc/parsing/Parsers.scala2309
1 files changed, 2309 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala
new file mode 100644
index 000000000..fa0576c7a
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala
@@ -0,0 +1,2309 @@
+package dotty.tools
+package dotc
+package parsing
+
+import scala.collection.mutable.ListBuffer
+import scala.collection.immutable.BitSet
+import util.{ SourceFile, SourcePosition }
+import Tokens._
+import Scanners._
+import MarkupParsers._
+import core._
+import Flags._
+import Contexts._
+import Names._
+import ast.Positioned
+import ast.Trees._
+import Decorators._
+import StdNames._
+import util.Positions._
+import Constants._
+import ScriptParsers._
+import Comments._
+import scala.annotation.{tailrec, switch}
+import util.DotClass
+import rewrite.Rewrites.patch
+
+object Parsers {
+
+ import ast.untpd._
+ import reporting.diagnostic.Message
+ import reporting.diagnostic.messages._
+
+ case class OpInfo(operand: Tree, operator: Name, offset: Offset)
+
+ class ParensCounters {
+ private var parCounts = new Array[Int](lastParen - firstParen)
+
+ def count(tok: Token) = parCounts(tok - firstParen)
+ def change(tok: Token, delta: Int) = parCounts(tok - firstParen) += delta
+ def nonePositive: Boolean = parCounts forall (_ <= 0)
+ }
+
+ @sharable object Location extends Enumeration {
+ val InParens, InBlock, InPattern, ElseWhere = Value
+ }
+
+ @sharable object ParamOwner extends Enumeration {
+ val Class, Type, TypeParam, Def = Value
+ }
+
+ /** The parse starting point depends on whether the source file is self-contained:
+ * if not, the AST will be supplemented.
+ */
+ def parser(source: SourceFile)(implicit ctx: Context) =
+ if (source.isSelfContained) new ScriptParser(source)
+ else new Parser(source)
+
+ abstract class ParserCommon(val source: SourceFile)(implicit ctx: Context) extends DotClass {
+
+ val in: ScannerCommon
+
+ /* ------------- POSITIONS ------------------------------------------- */
+
+ /** Positions tree.
+ * If `t` does not have a position yet, set its position to the given one.
+ */
+ def atPos[T <: Positioned](pos: Position)(t: T): T =
+ if (t.pos.isSourceDerived) t else t.withPos(pos)
+
+ def atPos[T <: Positioned](start: Offset, point: Offset, end: Offset)(t: T): T =
+ atPos(Position(start, end, point))(t)
+
+ /** If the last read offset is strictly greater than `start`, position tree
+ * to position spanning from `start` to last read offset, with given point.
+ * If the last offset is less than or equal to start, the tree `t` did not
+ * consume any source for its construction. In this case, don't position it yet,
+ * but wait for its position to be determined by `setChildPositions` when the
+ * parent node is positioned.
+ */
+ def atPos[T <: Positioned](start: Offset, point: Offset)(t: T): T =
+ if (in.lastOffset > start) atPos(start, point, in.lastOffset)(t) else t
+
+ def atPos[T <: Positioned](start: Offset)(t: T): T =
+ atPos(start, start)(t)
+
+ def nameStart: Offset =
+ if (in.token == BACKQUOTED_IDENT) in.offset + 1 else in.offset
+
+ def sourcePos(off: Int = in.offset): SourcePosition =
+ source atPos Position(off)
+
+
+ /* ------------- ERROR HANDLING ------------------------------------------- */
+ /** The offset where the last syntax error was reported, or if a skip to a
+ * safepoint occurred afterwards, the offset of the safe point.
+ */
+ protected var lastErrorOffset : Int = -1
+
+ /** Issue an error at given offset if beyond last error offset
+ * and update lastErrorOffset.
+ */
+ def syntaxError(msg: => Message, offset: Int = in.offset): Unit =
+ if (offset > lastErrorOffset) {
+ syntaxError(msg, Position(offset))
+ lastErrorOffset = in.offset
+ }
+
+ /** Unconditionally issue an error at given position, without
+ * updating lastErrorOffset.
+ */
+ def syntaxError(msg: => Message, pos: Position): Unit =
+ ctx.error(msg, source atPos pos)
+
+ }
+
+ class Parser(source: SourceFile)(implicit ctx: Context) extends ParserCommon(source) {
+
+ val in: Scanner = new Scanner(source)
+
+ val openParens = new ParensCounters
+
+ /** This is the general parse entry point.
+ * Overridden by ScriptParser
+ */
+ def parse(): Tree = {
+ val t = compilationUnit()
+ accept(EOF)
+ t
+ }
+
+/* -------------- TOKEN CLASSES ------------------------------------------- */
+
+ def isIdent = in.token == IDENTIFIER || in.token == BACKQUOTED_IDENT
+ def isIdent(name: Name) = in.token == IDENTIFIER && in.name == name
+ def isSimpleLiteral = simpleLiteralTokens contains in.token
+ def isLiteral = literalTokens contains in.token
+ def isNumericLit = numericLitTokens contains in.token
+ def isModifier = modifierTokens contains in.token
+ def isExprIntro = canStartExpressionTokens contains in.token
+ def isTemplateIntro = templateIntroTokens contains in.token
+ def isDclIntro = dclIntroTokens contains in.token
+ def isStatSeqEnd = in.token == RBRACE || in.token == EOF
+ def mustStartStat = mustStartStatTokens contains in.token
+
+ def isDefIntro(allowedMods: BitSet) =
+ in.token == AT || (allowedMods contains in.token) || (defIntroTokens contains in.token)
+
+ def isStatSep: Boolean =
+ in.token == NEWLINE || in.token == NEWLINES || in.token == SEMI
+
+/* ------------- ERROR HANDLING ------------------------------------------- */
+
+ /** The offset of the last time when a statement on a new line was definitely
+ * encountered in the current scope or an outer scope.
+ */
+ private var lastStatOffset = -1
+
+ def setLastStatOffset() =
+ if (mustStartStat && in.isAfterLineEnd)
+ lastStatOffset = in.offset
+
+ /** Is offset1 less or equally indented than offset2?
+ * This is the case if the characters between the preceding end-of-line and offset1
+ * are a prefix of the characters between the preceding end-of-line and offset2.
+ */
+ def isLeqIndented(offset1: Int, offset2: Int): Boolean = {
+ def recur(idx1: Int, idx2: Int): Boolean =
+ idx1 == offset1 ||
+ idx2 < offset2 && source(idx1) == source(idx2) && recur(idx1 + 1, idx2 + 1)
+ recur(source.startOfLine(offset1), source.startOfLine(offset2))
+ }
+
+ /** Skip on error to next safe point.
+ * Safe points are:
+ * - Closing braces, provided they match an opening brace before the error point.
+ * - Closing parens and brackets, provided they match an opening parent or bracket
+ * before the error point and there are no intervening other kinds of parens.
+ * - Semicolons and newlines, provided there are no intervening braces.
+ * - Definite statement starts on new lines, provided they are not more indented
+ * than the last known statement start before the error point.
+ */
+ protected def skip(): Unit = {
+ val skippedParens = new ParensCounters
+ while (true) {
+ (in.token: @switch) match {
+ case EOF =>
+ return
+ case SEMI | NEWLINE | NEWLINES =>
+ if (skippedParens.count(LBRACE) == 0) return
+ case RBRACE =>
+ if (openParens.count(LBRACE) > 0 && skippedParens.count(LBRACE) == 0)
+ return
+ skippedParens.change(LBRACE, -1)
+ case RPAREN =>
+ if (openParens.count(LPAREN) > 0 && skippedParens.nonePositive)
+ return
+ skippedParens.change(LPAREN, -1)
+ case RBRACKET =>
+ if (openParens.count(LBRACKET) > 0 && skippedParens.nonePositive)
+ return
+ skippedParens.change(LBRACKET, -1)
+ case LBRACE =>
+ skippedParens.change(LBRACE, + 1)
+ case LPAREN =>
+ skippedParens.change(LPAREN, + 1)
+ case LBRACKET=>
+ skippedParens.change(LBRACKET, + 1)
+ case _ =>
+ if (mustStartStat &&
+ in.isAfterLineEnd() &&
+ isLeqIndented(in.offset, lastStatOffset max 0))
+ return
+ }
+ in.nextToken()
+ }
+ }
+
+ def warning(msg: => Message, sourcePos: SourcePosition) =
+ ctx.warning(msg, sourcePos)
+
+ def warning(msg: => Message, offset: Int = in.offset) =
+ ctx.warning(msg, source atPos Position(offset))
+
+ def deprecationWarning(msg: => Message, offset: Int = in.offset) =
+ ctx.deprecationWarning(msg, source atPos Position(offset))
+
+ /** Issue an error at current offset taht input is incomplete */
+ def incompleteInputError(msg: => Message) =
+ ctx.incompleteInputError(msg, source atPos Position(in.offset))
+
+ /** If at end of file, issue an incompleteInputError.
+ * Otherwise issue a syntax error and skip to next safe point.
+ */
+ def syntaxErrorOrIncomplete(msg: => Message) =
+ if (in.token == EOF) incompleteInputError(msg)
+ else {
+ syntaxError(msg)
+ skip()
+ lastErrorOffset = in.offset
+ } // DEBUG
+
+ private def expectedMsg(token: Int): String =
+ expectedMessage(showToken(token))
+ private def expectedMessage(what: String): String =
+ s"$what expected but ${showToken(in.token)} found"
+
+ /** Consume one token of the specified type, or
+ * signal an error if it is not there.
+ *
+ * @return The offset at the start of the token to accept
+ */
+ def accept(token: Int): Int = {
+ val offset = in.offset
+ if (in.token != token) {
+ syntaxErrorOrIncomplete(expectedMsg(token))
+ }
+ if (in.token == token) in.nextToken()
+ offset
+ }
+
+ /** semi = nl {nl} | `;'
+ * nl = `\n' // where allowed
+ */
+ def acceptStatSep(): Unit = in.token match {
+ case NEWLINE | NEWLINES => in.nextToken()
+ case _ => accept(SEMI)
+ }
+
+ def acceptStatSepUnlessAtEnd(altEnd: Token = EOF) =
+ if (!isStatSeqEnd && in.token != altEnd) acceptStatSep()
+
+ def errorTermTree = atPos(in.offset) { Literal(Constant(null)) }
+
+ private var inFunReturnType = false
+ private def fromWithinReturnType[T](body: => T): T = {
+ val saved = inFunReturnType
+ try {
+ inFunReturnType = true
+ body
+ } finally inFunReturnType = saved
+ }
+
+ def migrationWarningOrError(msg: String, offset: Int = in.offset) =
+ if (in.isScala2Mode)
+ ctx.migrationWarning(msg, source atPos Position(offset))
+ else
+ syntaxError(msg, offset)
+
+/* ---------- TREE CONSTRUCTION ------------------------------------------- */
+
+ /** Convert tree to formal parameter list
+ */
+ def convertToParams(tree: Tree): List[ValDef] = tree match {
+ case Parens(t) => convertToParam(t) :: Nil
+ case Tuple(ts) => ts map (convertToParam(_))
+ case t => convertToParam(t) :: Nil
+ }
+
+ /** Convert tree to formal parameter
+ */
+ def convertToParam(tree: Tree, mods: Modifiers = Modifiers(), expected: String = "formal parameter"): ValDef = tree match {
+ case Ident(name) =>
+ makeParameter(name.asTermName, TypeTree(), mods) withPos tree.pos
+ case Typed(Ident(name), tpt) =>
+ makeParameter(name.asTermName, tpt, mods) withPos tree.pos
+ case _ =>
+ syntaxError(s"not a legal $expected", tree.pos)
+ makeParameter(nme.ERROR, tree, mods)
+ }
+
+ /** Convert (qual)ident to type identifier
+ */
+ def convertToTypeId(tree: Tree): Tree = tree match {
+ case id @ Ident(name) =>
+ cpy.Ident(id)(name.toTypeName)
+ case id @ Select(qual, name) =>
+ cpy.Select(id)(qual, name.toTypeName)
+ case _ =>
+ syntaxError(IdentifierExpected(tree.show), tree.pos)
+ tree
+ }
+
+/* --------------- PLACEHOLDERS ------------------------------------------- */
+
+ /** The implicit parameters introduced by `_` in the current expression.
+ * Parameters appear in reverse order.
+ */
+ var placeholderParams: List[ValDef] = Nil
+
+ def checkNoEscapingPlaceholders[T](op: => T): T = {
+ val savedPlaceholderParams = placeholderParams
+ placeholderParams = Nil
+
+ try op
+ finally {
+ placeholderParams match {
+ case vd :: _ => syntaxError(UnboundPlaceholderParameter(), vd.pos)
+ case _ =>
+ }
+ placeholderParams = savedPlaceholderParams
+ }
+ }
+
+ def isWildcard(t: Tree): Boolean = t match {
+ case Ident(name1) => placeholderParams.nonEmpty && name1 == placeholderParams.head.name
+ case Typed(t1, _) => isWildcard(t1)
+ case Annotated(t1, _) => isWildcard(t1)
+ case Parens(t1) => isWildcard(t1)
+ case _ => false
+ }
+
+/* -------------- XML ---------------------------------------------------- */
+
+ /** the markup parser */
+ lazy val xmlp = new MarkupParser(this, true)
+
+ object symbXMLBuilder extends SymbolicXMLBuilder(this, true) // DEBUG choices
+
+ def xmlLiteral() : Tree = xmlp.xLiteral
+ def xmlLiteralPattern() : Tree = xmlp.xLiteralPattern
+
+/* -------- COMBINATORS -------------------------------------------------------- */
+
+ def enclosed[T](tok: Token, body: => T): T = {
+ accept(tok)
+ openParens.change(tok, 1)
+ try body
+ finally {
+ accept(tok + 1)
+ openParens.change(tok, -1)
+ }
+ }
+
+ def inParens[T](body: => T): T = enclosed(LPAREN, body)
+ def inBraces[T](body: => T): T = enclosed(LBRACE, body)
+ def inBrackets[T](body: => T): T = enclosed(LBRACKET, body)
+
+ def inDefScopeBraces[T](body: => T): T = {
+ val saved = lastStatOffset
+ try inBraces(body)
+ finally lastStatOffset = saved
+ }
+
+ /** part { `separator` part }
+ */
+ def tokenSeparated[T](separator: Int, part: () => T): List[T] = {
+ val ts = new ListBuffer[T] += part()
+ while (in.token == separator) {
+ in.nextToken()
+ ts += part()
+ }
+ ts.toList
+ }
+
+ def commaSeparated[T](part: () => T): List[T] = tokenSeparated(COMMA, part)
+
+/* --------- OPERAND/OPERATOR STACK --------------------------------------- */
+
+ var opStack: List[OpInfo] = Nil
+
+ def checkAssoc(offset: Int, op: Name, leftAssoc: Boolean) =
+ if (isLeftAssoc(op) != leftAssoc)
+ syntaxError(
+ "left- and right-associative operators with same precedence may not be mixed", offset)
+
+ def reduceStack(base: List[OpInfo], top: Tree, prec: Int, leftAssoc: Boolean): Tree = {
+ if (opStack != base && precedence(opStack.head.operator) == prec)
+ checkAssoc(opStack.head.offset, opStack.head.operator, leftAssoc)
+ def recur(top: Tree): Tree = {
+ if (opStack == base) top
+ else {
+ val opInfo = opStack.head
+ val opPrec = precedence(opInfo.operator)
+ if (prec < opPrec || leftAssoc && prec == opPrec) {
+ opStack = opStack.tail
+ recur {
+ val opPos = Position(opInfo.offset, opInfo.offset + opInfo.operator.length, opInfo.offset)
+ atPos(opPos union opInfo.operand.pos union top.pos) {
+ InfixOp(opInfo.operand, opInfo.operator, top)
+ }
+ }
+ }
+ else top
+ }
+ }
+ recur(top)
+ }
+
+ /** operand { infixop operand} [postfixop],
+ * respecting rules of associativity and precedence.
+ * @param notAnOperator a token that does not count as operator.
+ * @param maybePostfix postfix operators are allowed.
+ */
+ def infixOps(
+ first: Tree, canStartOperand: Token => Boolean, operand: () => Tree,
+ isType: Boolean = false,
+ notAnOperator: Name = nme.EMPTY,
+ maybePostfix: Boolean = false): Tree = {
+ val base = opStack
+ var top = first
+ while (isIdent && in.name != notAnOperator) {
+ val op = if (isType) in.name.toTypeName else in.name
+ top = reduceStack(base, top, precedence(op), isLeftAssoc(op))
+ opStack = OpInfo(top, op, in.offset) :: opStack
+ ident()
+ newLineOptWhenFollowing(canStartOperand)
+ if (maybePostfix && !canStartOperand(in.token)) {
+ val topInfo = opStack.head
+ opStack = opStack.tail
+ val od = reduceStack(base, topInfo.operand, 0, true)
+ return atPos(od.pos.start, topInfo.offset) {
+ PostfixOp(od, topInfo.operator)
+ }
+ }
+ top = operand()
+ }
+ reduceStack(base, top, 0, true)
+ }
+
+/* -------- IDENTIFIERS AND LITERALS ------------------------------------------- */
+
+ /** Accept identifier and return its name as a term name. */
+ def ident(): TermName =
+ if (isIdent) {
+ val name = in.name
+ in.nextToken()
+ name
+ } else {
+ syntaxErrorOrIncomplete(expectedMsg(IDENTIFIER))
+ nme.ERROR
+ }
+
+ /** Accept identifier and return Ident with its name as a term name. */
+ def termIdent(): Ident = atPos(in.offset) {
+ makeIdent(in.token, ident())
+ }
+
+ /** Accept identifier and return Ident with its name as a type name. */
+ def typeIdent(): Ident = atPos(in.offset) {
+ makeIdent(in.token, ident().toTypeName)
+ }
+
+ private def makeIdent(tok: Token, name: Name) =
+ if (tok == BACKQUOTED_IDENT) BackquotedIdent(name)
+ else Ident(name)
+
+ def wildcardIdent(): Ident =
+ atPos(accept(USCORE)) { Ident(nme.WILDCARD) }
+
+ def termIdentOrWildcard(): Ident =
+ if (in.token == USCORE) wildcardIdent() else termIdent()
+
+ /** Accept identifier acting as a selector on given tree `t`. */
+ def selector(t: Tree): Tree =
+ atPos(t.pos.start, in.offset) { Select(t, ident()) }
+
+ /** Selectors ::= ident { `.' ident()
+ *
+ * Accept `.' separated identifiers acting as a selectors on given tree `t`.
+ * @param finish An alternative parse in case the next token is not an identifier.
+ * If the alternative does not apply, its tree argument is returned unchanged.
+ */
+ def selectors(t: Tree, finish: Tree => Tree): Tree = {
+ val t1 = finish(t)
+ if (t1 ne t) t1 else dotSelectors(selector(t), finish)
+ }
+
+ /** DotSelectors ::= { `.' ident()
+ *
+ * Accept `.' separated identifiers acting as a selectors on given tree `t`.
+ * @param finish An alternative parse in case the token following a `.' is not an identifier.
+ * If the alternative does not apply, its tree argument is returned unchanged.
+ */
+ def dotSelectors(t: Tree, finish: Tree => Tree = id) =
+ if (in.token == DOT) { in.nextToken(); selectors(t, finish) }
+ else t
+
+ private val id: Tree => Tree = x => x
+
+ /** Path ::= StableId
+ * | [Ident `.'] this
+ *
+ * @param thisOK If true, [Ident `.'] this is acceptable as the path.
+ * If false, another selection is required after the `this`.
+ * @param finish An alternative parse in case the token following a `.' is not an identifier.
+ * If the alternative does not apply, its tree argument is returned unchanged.
+ */
+ def path(thisOK: Boolean, finish: Tree => Tree = id): Tree = {
+ val start = in.offset
+ def handleThis(qual: Ident) = {
+ in.nextToken()
+ val t = atPos(start) { This(qual) }
+ if (!thisOK && in.token != DOT) syntaxError("`.' expected")
+ dotSelectors(t, finish)
+ }
+ def handleSuper(qual: Ident) = {
+ in.nextToken()
+ val mix = mixinQualifierOpt()
+ val t = atPos(start) { Super(This(qual), mix) }
+ accept(DOT)
+ dotSelectors(selector(t), finish)
+ }
+ if (in.token == THIS) handleThis(EmptyTypeIdent)
+ else if (in.token == SUPER) handleSuper(EmptyTypeIdent)
+ else {
+ val t = termIdent()
+ if (in.token == DOT) {
+ def qual = cpy.Ident(t)(t.name.toTypeName)
+ in.nextToken()
+ if (in.token == THIS) handleThis(qual)
+ else if (in.token == SUPER) handleSuper(qual)
+ else selectors(t, finish)
+ }
+ else t
+ }
+ }
+
+ /** MixinQualifier ::= `[' Id `]'
+ */
+ def mixinQualifierOpt(): Ident =
+ if (in.token == LBRACKET) inBrackets(atPos(in.offset) { typeIdent() })
+ else EmptyTypeIdent
+
+ /** StableId ::= Id
+ * | Path `.' Id
+ * | [id '.'] super [`[' id `]']`.' id
+ */
+ def stableId(): Tree =
+ path(thisOK = false)
+
+ /** QualId ::= Id {`.' Id}
+ */
+ def qualId(): Tree =
+ dotSelectors(termIdent())
+
+ /** SimpleExpr ::= literal
+ * | symbol
+ * | null
+ * @param negOffset The offset of a preceding `-' sign, if any.
+ * If the literal is not negated, negOffset = in.offset.
+ */
+ def literal(negOffset: Int = in.offset, inPattern: Boolean = false): Tree = {
+ def finish(value: Any): Tree = {
+ val t = atPos(negOffset) { Literal(Constant(value)) }
+ in.nextToken()
+ t
+ }
+ val isNegated = negOffset < in.offset
+ atPos(negOffset) {
+ if (in.token == SYMBOLLIT) atPos(in.skipToken()) { SymbolLit(in.strVal) }
+ else if (in.token == INTERPOLATIONID) interpolatedString()
+ else finish(in.token match {
+ case CHARLIT => in.charVal
+ case INTLIT => in.intVal(isNegated).toInt
+ case LONGLIT => in.intVal(isNegated)
+ case FLOATLIT => in.floatVal(isNegated).toFloat
+ case DOUBLELIT => in.floatVal(isNegated)
+ case STRINGLIT | STRINGPART => in.strVal
+ case TRUE => true
+ case FALSE => false
+ case NULL => null
+ case _ =>
+ syntaxErrorOrIncomplete(IllegalLiteral())
+ null
+ })
+ }
+ }
+
+ private def interpolatedString(inPattern: Boolean = false): Tree = atPos(in.offset) {
+ val segmentBuf = new ListBuffer[Tree]
+ val interpolator = in.name
+ in.nextToken()
+ while (in.token == STRINGPART) {
+ segmentBuf += Thicket(
+ literal(),
+ atPos(in.offset) {
+ if (in.token == IDENTIFIER)
+ termIdent()
+ else if (in.token == THIS) {
+ in.nextToken()
+ This(EmptyTypeIdent)
+ }
+ else if (in.token == LBRACE)
+ if (inPattern) Block(Nil, inBraces(pattern()))
+ else expr()
+ else {
+ ctx.error(InterpolatedStringError())
+ EmptyTree
+ }
+ })
+ }
+ if (in.token == STRINGLIT) segmentBuf += literal()
+ InterpolatedString(interpolator, segmentBuf.toList)
+ }
+
+/* ------------- NEW LINES ------------------------------------------------- */
+
+ def newLineOpt(): Unit = {
+ if (in.token == NEWLINE) in.nextToken()
+ }
+
+ def newLinesOpt(): Unit = {
+ if (in.token == NEWLINE || in.token == NEWLINES)
+ in.nextToken()
+ }
+
+ def newLineOptWhenFollowedBy(token: Int): Unit = {
+ // note: next is defined here because current == NEWLINE
+ if (in.token == NEWLINE && in.next.token == token) newLineOpt()
+ }
+
+ def newLineOptWhenFollowing(p: Int => Boolean): Unit = {
+ // note: next is defined here because current == NEWLINE
+ if (in.token == NEWLINE && p(in.next.token)) newLineOpt()
+ }
+
+/* ------------- TYPES ------------------------------------------------------ */
+ /** Same as [[typ]], but if this results in a wildcard it emits a syntax error and
+ * returns a tree for type `Any` instead.
+ */
+ def toplevelTyp(): Tree = {
+ val t = typ()
+ findWildcardType(t) match {
+ case Some(wildcardPos) =>
+ syntaxError("unbound wildcard type", wildcardPos)
+ scalaAny
+ case None => t
+ }
+ }
+
+ /** Type ::= FunArgTypes `=>' Type
+ * | HkTypeParamClause `->' Type
+ * | InfixType
+ * FunArgTypes ::= InfixType
+ * | `(' [ FunArgType {`,' FunArgType } ] `)'
+ */
+ def typ(): Tree = {
+ val start = in.offset
+ val t =
+ if (in.token == LPAREN) {
+ in.nextToken()
+ if (in.token == RPAREN) {
+ in.nextToken()
+ atPos(start, accept(ARROW)) { Function(Nil, typ()) }
+ }
+ else {
+ openParens.change(LPAREN, 1)
+ val ts = commaSeparated(funArgType)
+ openParens.change(LPAREN, -1)
+ accept(RPAREN)
+ if (in.token == ARROW)
+ atPos(start, in.skipToken()) { Function(ts, typ()) }
+ else {
+ for (t <- ts)
+ if (t.isInstanceOf[ByNameTypeTree])
+ syntaxError(ByNameParameterNotSupported())
+ val tuple = atPos(start) { makeTupleOrParens(ts) }
+ infixTypeRest(refinedTypeRest(withTypeRest(simpleTypeRest(tuple))))
+ }
+ }
+ }
+ else if (in.token == LBRACKET) {
+ val start = in.offset
+ val tparams = typeParamClause(ParamOwner.TypeParam)
+ if (in.token == ARROW)
+ atPos(start, in.skipToken())(PolyTypeTree(tparams, typ()))
+ else { accept(ARROW); typ() }
+ }
+ else infixType()
+
+ in.token match {
+ case ARROW => atPos(start, in.skipToken()) { Function(List(t), typ()) }
+ case FORSOME => syntaxError("existential types no longer supported; use a wildcard type or dependent type instead"); t
+ case _ => t
+ }
+ }
+
+ /** InfixType ::= RefinedType {id [nl] refinedType}
+ */
+ def infixType(): Tree = infixTypeRest(refinedType())
+
+ def infixTypeRest(t: Tree): Tree =
+ infixOps(t, canStartTypeTokens, refinedType, isType = true, notAnOperator = nme.raw.STAR)
+
+ /** RefinedType ::= WithType {Annotation | [nl] Refinement}
+ */
+ val refinedType: () => Tree = () => refinedTypeRest(withType())
+
+ def refinedTypeRest(t: Tree): Tree = {
+ newLineOptWhenFollowedBy(LBRACE)
+ if (in.token == LBRACE) refinedTypeRest(atPos(t.pos.start) { RefinedTypeTree(t, refinement()) })
+ else t
+ }
+
+ /** WithType ::= AnnotType {`with' AnnotType} (deprecated)
+ */
+ def withType(): Tree = withTypeRest(annotType())
+
+ def withTypeRest(t: Tree): Tree =
+ if (in.token == WITH) {
+ deprecationWarning(DeprecatedWithOperator())
+ in.nextToken()
+ AndTypeTree(t, withType())
+ }
+ else t
+
+ /** AnnotType ::= SimpleType {Annotation}
+ */
+ def annotType(): Tree = annotTypeRest(simpleType())
+
+ def annotTypeRest(t: Tree): Tree =
+ if (in.token == AT) annotTypeRest(atPos(t.pos.start) { Annotated(t, annot()) })
+ else t
+
+ /** SimpleType ::= SimpleType TypeArgs
+ * | SimpleType `#' Id
+ * | StableId
+ * | Path `.' type
+ * | `(' ArgTypes `)'
+ * | `_' TypeBounds
+ * | Refinement
+ * | Literal
+ */
+ def simpleType(): Tree = simpleTypeRest {
+ if (in.token == LPAREN)
+ atPos(in.offset) { makeTupleOrParens(inParens(argTypes())) }
+ else if (in.token == LBRACE)
+ atPos(in.offset) { RefinedTypeTree(EmptyTree, refinement()) }
+ else if (isSimpleLiteral) { SingletonTypeTree(literal()) }
+ else if (in.token == USCORE) {
+ val start = in.skipToken()
+ typeBounds().withPos(Position(start, in.lastOffset, start))
+ }
+ else path(thisOK = false, handleSingletonType) match {
+ case r @ SingletonTypeTree(_) => r
+ case r => convertToTypeId(r)
+ }
+ }
+
+ val handleSingletonType: Tree => Tree = t =>
+ if (in.token == TYPE) {
+ in.nextToken()
+ atPos(t.pos.start) { SingletonTypeTree(t) }
+ } else t
+
+ private def simpleTypeRest(t: Tree): Tree = in.token match {
+ case HASH => simpleTypeRest(typeProjection(t))
+ case LBRACKET => simpleTypeRest(atPos(t.pos.start) { AppliedTypeTree(t, typeArgs(namedOK = true)) })
+ case _ => t
+ }
+
+ private def typeProjection(t: Tree): Tree = {
+ accept(HASH)
+ val id = typeIdent()
+ atPos(t.pos.start, id.pos.start) { Select(t, id.name) }
+ }
+
+ /** NamedTypeArg ::= id `=' Type
+ */
+ val namedTypeArg = () => {
+ val name = ident()
+ accept(EQUALS)
+ NamedArg(name.toTypeName, typ())
+ }
+
+ /** ArgTypes ::= Type {`,' Type}
+ * | NamedTypeArg {`,' NamedTypeArg}
+ */
+ def argTypes(namedOK: Boolean = false) = {
+ def otherArgs(first: Tree, arg: () => Tree): List[Tree] = {
+ val rest =
+ if (in.token == COMMA) {
+ in.nextToken()
+ commaSeparated(arg)
+ }
+ else Nil
+ first :: rest
+ }
+ if (namedOK && in.token == IDENTIFIER)
+ typ() match {
+ case Ident(name) if in.token == EQUALS =>
+ in.nextToken()
+ otherArgs(NamedArg(name, typ()), namedTypeArg)
+ case firstArg =>
+ if (in.token == EQUALS) println(s"??? $firstArg")
+ otherArgs(firstArg, typ)
+ }
+ else commaSeparated(typ)
+ }
+
+ /** FunArgType ::= Type | `=>' Type
+ */
+ val funArgType = () =>
+ if (in.token == ARROW) atPos(in.skipToken()) { ByNameTypeTree(typ()) }
+ else typ()
+
+ /** ParamType ::= [`=>'] ParamValueType
+ */
+ def paramType(): Tree =
+ if (in.token == ARROW) atPos(in.skipToken()) { ByNameTypeTree(paramValueType()) }
+ else paramValueType()
+
+ /** ParamValueType ::= Type [`*']
+ */
+ def paramValueType(): Tree = {
+ val t = toplevelTyp()
+ if (isIdent(nme.raw.STAR)) {
+ in.nextToken()
+ atPos(t.pos.start) { PostfixOp(t, nme.raw.STAR) }
+ } else t
+ }
+
+ /** TypeArgs ::= `[' Type {`,' Type} `]'
+ * NamedTypeArgs ::= `[' NamedTypeArg {`,' NamedTypeArg} `]'
+ */
+ def typeArgs(namedOK: Boolean = false): List[Tree] = inBrackets(argTypes(namedOK))
+
+ /** Refinement ::= `{' RefineStatSeq `}'
+ */
+ def refinement(): List[Tree] = inBraces(refineStatSeq())
+
+ /** TypeBounds ::= [`>:' Type] [`<:' Type]
+ */
+ def typeBounds(): TypeBoundsTree =
+ atPos(in.offset) { TypeBoundsTree(bound(SUPERTYPE), bound(SUBTYPE)) }
+
+ private def bound(tok: Int): Tree =
+ if (in.token == tok) { in.nextToken(); toplevelTyp() }
+ else EmptyTree
+
+ /** TypeParamBounds ::= TypeBounds {`<%' Type} {`:' Type}
+ */
+ def typeParamBounds(pname: TypeName): Tree = {
+ val t = typeBounds()
+ val cbs = contextBounds(pname)
+ if (cbs.isEmpty) t
+ else atPos((t.pos union cbs.head.pos).start) { ContextBounds(t, cbs) }
+ }
+
+ def contextBounds(pname: TypeName): List[Tree] = in.token match {
+ case COLON =>
+ atPos(in.skipToken) {
+ AppliedTypeTree(toplevelTyp(), Ident(pname))
+ } :: contextBounds(pname)
+ case VIEWBOUND =>
+ deprecationWarning("view bounds `<%' are deprecated, use a context bound `:' instead")
+ atPos(in.skipToken) {
+ Function(Ident(pname) :: Nil, toplevelTyp())
+ } :: contextBounds(pname)
+ case _ =>
+ Nil
+ }
+
+ def typedOpt(): Tree =
+ if (in.token == COLON) { in.nextToken(); toplevelTyp() }
+ else TypeTree()
+
+ def typeDependingOn(location: Location.Value): Tree =
+ if (location == Location.InParens) typ()
+ else if (location == Location.InPattern) refinedType()
+ else infixType()
+
+ /** Checks whether `t` is a wildcard type.
+ * If it is, returns the [[Position]] where the wildcard occurs.
+ */
+ @tailrec
+ private final def findWildcardType(t: Tree): Option[Position] = t match {
+ case TypeBoundsTree(_, _) => Some(t.pos)
+ case Parens(t1) => findWildcardType(t1)
+ case Annotated(t1, _) => findWildcardType(t1)
+ case _ => None
+ }
+
+/* ----------- EXPRESSIONS ------------------------------------------------ */
+
+ /** EqualsExpr ::= `=' Expr
+ */
+ def equalsExpr(): Tree = {
+ accept(EQUALS)
+ expr()
+ }
+
+ def condExpr(altToken: Token): Tree = {
+ if (in.token == LPAREN) {
+ val t = atPos(in.offset) { Parens(inParens(exprInParens())) }
+ if (in.token == altToken) in.nextToken()
+ t
+ } else {
+ val t = expr()
+ accept(altToken)
+ t
+ }
+ }
+
+ /** Expr ::= FunParams `=>' Expr
+ * | Expr1
+ * FunParams ::= Bindings
+ * | [`implicit'] Id
+ * | `_'
+ * ExprInParens ::= PostfixExpr `:' Type
+ * | Expr
+ * BlockResult ::= (FunParams | [`implicit'] Id `:' InfixType) => Block
+ * | Expr1
+ * Expr1 ::= `if' `(' Expr `)' {nl} Expr [[semi] else Expr]
+ * | `if' Expr `then' Expr [[semi] else Expr]
+ * | `while' `(' Expr `)' {nl} Expr
+ * | `while' Expr `do' Expr
+ * | `do' Expr [semi] `while' Expr
+ * | `try' Expr Catches [`finally' Expr]
+ * | `try' Expr [`finally' Expr]
+ * | `throw' Expr
+ * | `return' [Expr]
+ * | ForExpr
+ * | [SimpleExpr `.'] Id `=' Expr
+ * | SimpleExpr1 ArgumentExprs `=' Expr
+ * | PostfixExpr [Ascription]
+ * | PostfixExpr `match' `{' CaseClauses `}'
+ * Bindings ::= `(' [Binding {`,' Binding}] `)'
+ * Binding ::= (Id | `_') [`:' Type]
+ * Ascription ::= `:' CompoundType
+ * | `:' Annotation {Annotation}
+ * | `:' `_' `*'
+ */
+ val exprInParens = () => expr(Location.InParens)
+
+ def expr(): Tree = expr(Location.ElseWhere)
+
+ def expr(location: Location.Value): Tree = {
+ val saved = placeholderParams
+ placeholderParams = Nil
+ val t = expr1(location)
+ if (in.token == ARROW) {
+ placeholderParams = saved
+ closureRest(t.pos.start, location, convertToParams(t))
+ }
+ else if (isWildcard(t)) {
+ placeholderParams = placeholderParams ::: saved
+ t
+ }
+ else
+ try
+ if (placeholderParams.isEmpty) t
+ else new WildcardFunction(placeholderParams.reverse, t)
+ finally placeholderParams = saved
+ }
+
+ def expr1(location: Location.Value = Location.ElseWhere): Tree = in.token match {
+ case IF =>
+ atPos(in.skipToken()) {
+ val cond = condExpr(THEN)
+ newLinesOpt()
+ val thenp = expr()
+ val elsep = if (in.token == ELSE) { in.nextToken(); expr() }
+ else EmptyTree
+ If(cond, thenp, elsep)
+ }
+ case WHILE =>
+ atPos(in.skipToken()) {
+ val cond = condExpr(DO)
+ newLinesOpt()
+ val body = expr()
+ WhileDo(cond, body)
+ }
+ case DO =>
+ atPos(in.skipToken()) {
+ val body = expr()
+ if (isStatSep) in.nextToken()
+ accept(WHILE)
+ val cond = expr()
+ DoWhile(body, cond)
+ }
+ case TRY =>
+ val tryOffset = in.offset
+ atPos(in.skipToken()) {
+ val body = expr()
+ val (handler, handlerStart) =
+ if (in.token == CATCH) {
+ val pos = in.offset
+ in.nextToken()
+ (expr(), pos)
+ } else (EmptyTree, -1)
+
+ handler match {
+ case Block(Nil, EmptyTree) =>
+ assert(handlerStart != -1)
+ syntaxError(
+ new EmptyCatchBlock(body),
+ Position(handlerStart, handler.pos.end)
+ )
+ case _ =>
+ }
+
+ val finalizer =
+ if (in.token == FINALLY) { accept(FINALLY); expr() }
+ else {
+ if (handler.isEmpty) warning(
+ EmptyCatchAndFinallyBlock(body),
+ source atPos Position(tryOffset, body.pos.end)
+ )
+ EmptyTree
+ }
+ ParsedTry(body, handler, finalizer)
+ }
+ case THROW =>
+ atPos(in.skipToken()) { Throw(expr()) }
+ case RETURN =>
+ atPos(in.skipToken()) { Return(if (isExprIntro) expr() else EmptyTree, EmptyTree) }
+ case FOR =>
+ forExpr()
+ case IMPLICIT =>
+ implicitClosure(in.skipToken(), location)
+ case _ =>
+ expr1Rest(postfixExpr(), location)
+ }
+
+ def expr1Rest(t: Tree, location: Location.Value) = in.token match {
+ case EQUALS =>
+ t match {
+ case Ident(_) | Select(_, _) | Apply(_, _) =>
+ atPos(t.pos.start, in.skipToken()) { Assign(t, expr()) }
+ case _ =>
+ t
+ }
+ case COLON =>
+ ascription(t, location)
+ case MATCH =>
+ atPos(t.pos.start, in.skipToken()) {
+ inBraces(Match(t, caseClauses()))
+ }
+ case _ =>
+ t
+ }
+
+ def ascription(t: Tree, location: Location.Value) = atPos(t.pos.start, in.skipToken()) {
+ in.token match {
+ case USCORE =>
+ val uscoreStart = in.skipToken()
+ if (isIdent(nme.raw.STAR)) {
+ in.nextToken()
+ if (in.token != RPAREN) syntaxError(SeqWildcardPatternPos(), uscoreStart)
+ Typed(t, atPos(uscoreStart) { Ident(tpnme.WILDCARD_STAR) })
+ } else {
+ syntaxErrorOrIncomplete(IncorrectRepeatedParameterSyntax())
+ t
+ }
+ case AT if location != Location.InPattern =>
+ (t /: annotations())(Annotated)
+ case _ =>
+ val tpt = typeDependingOn(location)
+ if (isWildcard(t) && location != Location.InPattern) {
+ val vd :: rest = placeholderParams
+ placeholderParams =
+ cpy.ValDef(vd)(tpt = tpt).withPos(vd.pos union tpt.pos) :: rest
+ }
+ Typed(t, tpt)
+ }
+ }
+
+ /** Expr ::= implicit Id `=>' Expr
+ * BlockResult ::= implicit Id [`:' InfixType] `=>' Block
+ */
+ def implicitClosure(start: Int, location: Location.Value, implicitMod: Option[Mod] = None): Tree = {
+ var mods = atPos(start) { Modifiers(Implicit) }
+ if (implicitMod.nonEmpty) mods = mods.withAddedMod(implicitMod.get)
+ val id = termIdent()
+ val paramExpr =
+ if (location == Location.InBlock && in.token == COLON)
+ atPos(id.pos.start, in.skipToken()) { Typed(id, infixType()) }
+ else
+ id
+ closureRest(start, location, convertToParam(paramExpr, mods) :: Nil)
+ }
+
+ def closureRest(start: Int, location: Location.Value, params: List[Tree]): Tree =
+ atPos(start, in.offset) {
+ accept(ARROW)
+ Function(params, if (location == Location.InBlock) block() else expr())
+ }
+
+ /** PostfixExpr ::= InfixExpr [Id [nl]]
+ * InfixExpr ::= PrefixExpr
+ * | InfixExpr Id [nl] InfixExpr
+ */
+ def postfixExpr(): Tree =
+ infixOps(prefixExpr(), canStartExpressionTokens, prefixExpr, maybePostfix = true)
+
+ /** PrefixExpr ::= [`-' | `+' | `~' | `!'] SimpleExpr
+ */
+ val prefixExpr = () =>
+ if (isIdent && nme.raw.isUnary(in.name)) {
+ val start = in.offset
+ val name = ident()
+ if (name == nme.raw.MINUS && isNumericLit)
+ simpleExprRest(literal(start), canApply = true)
+ else
+ atPos(start) { PrefixOp(name, simpleExpr()) }
+ }
+ else simpleExpr()
+
+ /** SimpleExpr ::= new Template
+ * | BlockExpr
+ * | SimpleExpr1 [`_']
+ * SimpleExpr1 ::= literal
+ * | xmlLiteral
+ * | Path
+ * | `(' [ExprsInParens] `)'
+ * | SimpleExpr `.' Id
+ * | SimpleExpr (TypeArgs | NamedTypeArgs)
+ * | SimpleExpr1 ArgumentExprs
+ */
+ def simpleExpr(): Tree = {
+ var canApply = true
+ val t = in.token match {
+ case XMLSTART =>
+ xmlLiteral()
+ case IDENTIFIER | BACKQUOTED_IDENT | THIS | SUPER =>
+ path(thisOK = true)
+ case USCORE =>
+ val start = in.skipToken()
+ val pname = ctx.freshName(nme.USCORE_PARAM_PREFIX).toTermName
+ val param = ValDef(pname, TypeTree(), EmptyTree).withFlags(SyntheticTermParam)
+ .withPos(Position(start))
+ placeholderParams = param :: placeholderParams
+ atPos(start) { Ident(pname) }
+ case LPAREN =>
+ atPos(in.offset) { makeTupleOrParens(inParens(exprsInParensOpt())) }
+ case LBRACE =>
+ canApply = false
+ blockExpr()
+ case NEW =>
+ canApply = false
+ val start = in.skipToken()
+ val (impl, missingBody) = template(emptyConstructor)
+ impl.parents match {
+ case parent :: Nil if missingBody =>
+ if (parent.isType) ensureApplied(wrapNew(parent)) else parent
+ case _ =>
+ New(impl.withPos(Position(start, in.lastOffset)))
+ }
+ case _ =>
+ if (isLiteral) literal()
+ else {
+ syntaxErrorOrIncomplete(IllegalStartSimpleExpr(tokenString(in.token)))
+ errorTermTree
+ }
+ }
+ simpleExprRest(t, canApply)
+ }
+
+ def simpleExprRest(t: Tree, canApply: Boolean = true): Tree = {
+ if (canApply) newLineOptWhenFollowedBy(LBRACE)
+ in.token match {
+ case DOT =>
+ in.nextToken()
+ simpleExprRest(selector(t), canApply = true)
+ case LBRACKET =>
+ val tapp = atPos(t.pos.start, in.offset) { TypeApply(t, typeArgs(namedOK = true)) }
+ simpleExprRest(tapp, canApply = true)
+ case LPAREN | LBRACE if canApply =>
+ val app = atPos(t.pos.start, in.offset) { Apply(t, argumentExprs()) }
+ simpleExprRest(app, canApply = true)
+ case USCORE =>
+ atPos(t.pos.start, in.skipToken()) { PostfixOp(t, nme.WILDCARD) }
+ case _ =>
+ t
+ }
+ }
+
+ /** ExprsInParens ::= ExprInParens {`,' ExprInParens}
+ */
+ def exprsInParensOpt(): List[Tree] =
+ if (in.token == RPAREN) Nil else commaSeparated(exprInParens)
+
+ /** ParArgumentExprs ::= `(' [ExprsInParens] `)'
+ * | `(' [ExprsInParens `,'] PostfixExpr `:' `_' `*' ')' \
+ */
+ def parArgumentExprs(): List[Tree] =
+ inParens(if (in.token == RPAREN) Nil else commaSeparated(argumentExpr))
+
+ /** ArgumentExprs ::= ParArgumentExprs
+ * | [nl] BlockExpr
+ */
+ def argumentExprs(): List[Tree] =
+ if (in.token == LBRACE) blockExpr() :: Nil else parArgumentExprs()
+
+ val argumentExpr = () => exprInParens() match {
+ case a @ Assign(Ident(id), rhs) => cpy.NamedArg(a)(id, rhs)
+ case e => e
+ }
+
+ /** ArgumentExprss ::= {ArgumentExprs}
+ */
+ def argumentExprss(fn: Tree): Tree = {
+ newLineOptWhenFollowedBy(LBRACE)
+ if (in.token == LPAREN || in.token == LBRACE) argumentExprss(Apply(fn, argumentExprs()))
+ else fn
+ }
+
+ /** ParArgumentExprss ::= {ParArgumentExprs}
+ */
+ def parArgumentExprss(fn: Tree): Tree =
+ if (in.token == LPAREN) parArgumentExprss(Apply(fn, parArgumentExprs()))
+ else fn
+
+ /** BlockExpr ::= `{' (CaseClauses | Block) `}'
+ */
+ def blockExpr(): Tree = atPos(in.offset) {
+ inDefScopeBraces {
+ if (in.token == CASE) Match(EmptyTree, caseClauses())
+ else block()
+ }
+ }
+
+ /** Block ::= BlockStatSeq
+ * @note Return tree does not carry source position.
+ */
+ def block(): Tree = {
+ val stats = blockStatSeq()
+ def isExpr(stat: Tree) = !(stat.isDef || stat.isInstanceOf[Import])
+ if (stats.nonEmpty && isExpr(stats.last)) Block(stats.init, stats.last)
+ else Block(stats, EmptyTree)
+ }
+
+ /** Guard ::= if PostfixExpr
+ */
+ def guard(): Tree =
+ if (in.token == IF) { in.nextToken(); postfixExpr() }
+ else EmptyTree
+
+ /** Enumerators ::= Generator {semi Enumerator | Guard}
+ */
+ def enumerators(): List[Tree] = generator() :: enumeratorsRest()
+
+ def enumeratorsRest(): List[Tree] =
+ if (isStatSep) { in.nextToken(); enumerator() :: enumeratorsRest() }
+ else if (in.token == IF) guard() :: enumeratorsRest()
+ else Nil
+
+ /** Enumerator ::= Generator
+ * | Guard
+ * | Pattern1 `=' Expr
+ */
+ def enumerator(): Tree =
+ if (in.token == IF) guard()
+ else {
+ val pat = pattern1()
+ if (in.token == EQUALS) atPos(pat.pos.start, in.skipToken()) { GenAlias(pat, expr()) }
+ else generatorRest(pat)
+ }
+
+ /** Generator ::= Pattern `<-' Expr
+ */
+ def generator(): Tree = generatorRest(pattern1())
+
+ def generatorRest(pat: Tree) =
+ atPos(pat.pos.start, accept(LARROW)) { GenFrom(pat, expr()) }
+
+ /** ForExpr ::= `for' (`(' Enumerators `)' | `{' Enumerators `}')
+ * {nl} [`yield'] Expr
+ * | `for' Enumerators (`do' Expr | `yield' Expr)
+ */
+ def forExpr(): Tree = atPos(in.skipToken()) {
+ var wrappedEnums = true
+ val enums =
+ if (in.token == LBRACE) inBraces(enumerators())
+ else if (in.token == LPAREN) {
+ val lparenOffset = in.skipToken()
+ openParens.change(LPAREN, 1)
+ val pats = patternsOpt()
+ val pat =
+ if (in.token == RPAREN || pats.length > 1) {
+ wrappedEnums = false
+ accept(RPAREN)
+ openParens.change(LPAREN, -1)
+ atPos(lparenOffset) { makeTupleOrParens(pats) } // note: alternatives `|' need to be weeded out by typer.
+ }
+ else pats.head
+ val res = generatorRest(pat) :: enumeratorsRest()
+ if (wrappedEnums) {
+ accept(RPAREN)
+ openParens.change(LPAREN, -1)
+ }
+ res
+ } else {
+ wrappedEnums = false
+ enumerators()
+ }
+ newLinesOpt()
+ if (in.token == YIELD) { in.nextToken(); ForYield(enums, expr()) }
+ else if (in.token == DO) { in.nextToken(); ForDo(enums, expr()) }
+ else {
+ if (!wrappedEnums) syntaxErrorOrIncomplete(YieldOrDoExpectedInForComprehension())
+ ForDo(enums, expr())
+ }
+ }
+
+ /** CaseClauses ::= CaseClause {CaseClause}
+ */
+ def caseClauses(): List[CaseDef] = {
+ val buf = new ListBuffer[CaseDef]
+ buf += caseClause()
+ while (in.token == CASE) buf += caseClause()
+ buf.toList
+ }
+
+ /** CaseClause ::= case Pattern [Guard] `=>' Block
+ */
+ def caseClause(): CaseDef = atPos(in.offset) {
+ accept(CASE)
+ CaseDef(pattern(), guard(), atPos(accept(ARROW)) { block() })
+ }
+
+ /* -------- PATTERNS ------------------------------------------- */
+
+ /** Pattern ::= Pattern1 { `|' Pattern1 }
+ */
+ val pattern = () => {
+ val pat = pattern1()
+ if (isIdent(nme.raw.BAR))
+ atPos(pat.pos.start) { Alternative(pat :: patternAlts()) }
+ else pat
+ }
+
+ def patternAlts(): List[Tree] =
+ if (isIdent(nme.raw.BAR)) { in.nextToken(); pattern1() :: patternAlts() }
+ else Nil
+
+ /** Pattern1 ::= PatVar Ascription
+ * | Pattern2
+ */
+ def pattern1(): Tree = {
+ val p = pattern2()
+ if (isVarPattern(p) && in.token == COLON) ascription(p, Location.InPattern)
+ else p
+ }
+
+ /** Pattern2 ::= [varid `@'] InfixPattern
+ */
+ val pattern2 = () => infixPattern() match {
+ case p @ Ident(name) if isVarPattern(p) && in.token == AT =>
+ val offset = in.skipToken()
+
+ // compatibility for Scala2 `x @ _*` syntax
+ infixPattern() match {
+ case pt @ Ident(tpnme.WILDCARD_STAR) =>
+ migrationWarningOrError("The syntax `x @ _*' is no longer supported; use `x : _*' instead", p.pos.start)
+ atPos(p.pos.start, offset) { Typed(p, pt) }
+ case p =>
+ atPos(p.pos.start, offset) { Bind(name, p) }
+ }
+ case p @ Ident(tpnme.WILDCARD_STAR) =>
+ // compatibility for Scala2 `_*` syntax
+ migrationWarningOrError("The syntax `_*' is no longer supported; use `x : _*' instead", p.pos.start)
+ atPos(p.pos.start) { Typed(Ident(nme.WILDCARD), p) }
+ case p =>
+ p
+ }
+
+ /** InfixPattern ::= SimplePattern {Id [nl] SimplePattern}
+ */
+ def infixPattern(): Tree =
+ infixOps(simplePattern(), canStartExpressionTokens, simplePattern, notAnOperator = nme.raw.BAR)
+
+ /** SimplePattern ::= PatVar
+ * | Literal
+ * | XmlPattern
+ * | `(' [Patterns] `)'
+ * | SimplePattern1 [TypeArgs] [ArgumentPatterns]
+ * SimplePattern1 ::= Path
+ * | `{' Block `}'
+ * | SimplePattern1 `.' Id
+ * PatVar ::= Id
+ * | `_'
+ */
+ val simplePattern = () => in.token match {
+ case IDENTIFIER | BACKQUOTED_IDENT | THIS =>
+ path(thisOK = true) match {
+ case id @ Ident(nme.raw.MINUS) if isNumericLit => literal(id.pos.start)
+ case t => simplePatternRest(t)
+ }
+ case USCORE =>
+ val wildIndent = wildcardIdent()
+
+ // compatibility for Scala2 `x @ _*` and `_*` syntax
+ // `x: _*' is parsed in `ascription'
+ if (isIdent(nme.raw.STAR)) {
+ in.nextToken()
+ if (in.token != RPAREN) syntaxError(SeqWildcardPatternPos(), wildIndent.pos)
+ atPos(wildIndent.pos) { Ident(tpnme.WILDCARD_STAR) }
+ } else wildIndent
+ case LPAREN =>
+ atPos(in.offset) { makeTupleOrParens(inParens(patternsOpt())) }
+ case LBRACE =>
+ dotSelectors(blockExpr())
+ case XMLSTART =>
+ xmlLiteralPattern()
+ case _ =>
+ if (isLiteral) literal()
+ else {
+ syntaxErrorOrIncomplete(IllegalStartOfSimplePattern())
+ errorTermTree
+ }
+ }
+
+ def simplePatternRest(t: Tree): Tree = {
+ var p = t
+ if (in.token == LBRACKET)
+ p = atPos(t.pos.start, in.offset) { TypeApply(p, typeArgs()) }
+ if (in.token == LPAREN)
+ p = atPos(t.pos.start, in.offset) { Apply(p, argumentPatterns()) }
+ p
+ }
+
+ /** Patterns ::= Pattern [`,' Pattern]
+ */
+ def patterns() = commaSeparated(pattern)
+
+ def patternsOpt(): List[Tree] =
+ if (in.token == RPAREN) Nil else patterns()
+
+
+ /** ArgumentPatterns ::= `(' [Patterns] `)'
+ * | `(' [Patterns `,'] Pattern2 `:' `_' `*' ')
+ */
+ def argumentPatterns(): List[Tree] =
+ inParens(patternsOpt)
+
+/* -------- MODIFIERS and ANNOTATIONS ------------------------------------------- */
+
+ private def modOfToken(tok: Int): Mod = tok match {
+ case ABSTRACT => Mod.Abstract()
+ case FINAL => Mod.Final()
+ case IMPLICIT => Mod.Implicit(ImplicitCommon)
+ case INLINE => Mod.Inline()
+ case LAZY => Mod.Lazy()
+ case OVERRIDE => Mod.Override()
+ case PRIVATE => Mod.Private()
+ case PROTECTED => Mod.Protected()
+ case SEALED => Mod.Sealed()
+ }
+
+ /** Drop `private' modifier when followed by a qualifier.
+ * Contract `abstract' and `override' to ABSOVERRIDE
+ */
+ private def normalize(mods: Modifiers): Modifiers =
+ if ((mods is Private) && mods.hasPrivateWithin)
+ normalize(mods &~ Private)
+ else if (mods is AbstractAndOverride)
+ normalize(addFlag(mods &~ (Abstract | Override), AbsOverride))
+ else
+ mods
+
+ private def addModifier(mods: Modifiers): Modifiers = {
+ val tok = in.token
+ val mod = atPos(in.skipToken()) { modOfToken(tok) }
+
+ if (mods is mod.flags) syntaxError(RepeatedModifier(mod.flags.toString))
+ addMod(mods, mod)
+ }
+
+ private def compatible(flags1: FlagSet, flags2: FlagSet): Boolean = (
+ flags1.isEmpty
+ || flags2.isEmpty
+ || flags1.isTermFlags && flags2.isTermFlags
+ || flags1.isTypeFlags && flags2.isTypeFlags
+ )
+
+ def addFlag(mods: Modifiers, flag: FlagSet): Modifiers = {
+ def incompatible(kind: String) = {
+ syntaxError(s"modifier(s) `${mods.flags}' not allowed for $kind")
+ Modifiers(flag)
+ }
+ if (compatible(mods.flags, flag)) mods | flag
+ else flag match {
+ case Trait => incompatible("trait")
+ case Method => incompatible("method")
+ case Mutable => incompatible("variable")
+ case _ =>
+ syntaxError(s"illegal modifier combination: ${mods.flags} and $flag")
+ mods
+ }
+ }
+
+ /** Always add the syntactic `mod`, but check and conditionally add semantic `mod.flags`
+ */
+ def addMod(mods: Modifiers, mod: Mod): Modifiers =
+ addFlag(mods, mod.flags).withAddedMod(mod)
+
+ /** AccessQualifier ::= "[" (Id | this) "]"
+ */
+ def accessQualifierOpt(mods: Modifiers): Modifiers =
+ if (in.token == LBRACKET) {
+ if ((mods is Local) || mods.hasPrivateWithin)
+ syntaxError("duplicate private/protected qualifier")
+ inBrackets {
+ if (in.token == THIS) { in.nextToken(); mods | Local }
+ else mods.withPrivateWithin(ident().toTypeName)
+ }
+ } else mods
+
+ /** {Annotation} {Modifier}
+ * Modifiers ::= {Modifier}
+ * LocalModifiers ::= {LocalModifier}
+ * AccessModifier ::= (private | protected) [AccessQualifier]
+ * Modifier ::= LocalModifier
+ * | AccessModifier
+ * | override
+ * LocalModifier ::= abstract | final | sealed | implicit | lazy
+ */
+ def modifiers(allowed: BitSet = modifierTokens, start: Modifiers = Modifiers()): Modifiers = {
+ def loop(mods: Modifiers): Modifiers = {
+ if (allowed contains in.token) {
+ val isAccessMod = accessModifierTokens contains in.token
+ val mods1 = addModifier(mods)
+ loop(if (isAccessMod) accessQualifierOpt(mods1) else mods1)
+ } else if (in.token == NEWLINE && (mods.hasFlags || mods.hasAnnotations)) {
+ in.nextToken()
+ loop(mods)
+ } else {
+ mods
+ }
+ }
+ normalize(loop(start))
+ }
+
+ /** Wrap annotation or constructor in New(...).<init> */
+ def wrapNew(tpt: Tree) = Select(New(tpt), nme.CONSTRUCTOR)
+
+ /** Adjust start of annotation or constructor to position of preceding @ or new */
+ def adjustStart(start: Offset)(tree: Tree): Tree = {
+ val tree1 = tree match {
+ case Apply(fn, args) => cpy.Apply(tree)(adjustStart(start)(fn), args)
+ case Select(qual, name) => cpy.Select(tree)(adjustStart(start)(qual), name)
+ case _ => tree
+ }
+ if (start < tree1.pos.start) tree1.withPos(tree1.pos.withStart(start))
+ else tree1
+ }
+
+ /** Annotation ::= `@' SimpleType {ParArgumentExprs}
+ */
+ def annot() =
+ adjustStart(accept(AT)) {
+ if (in.token == INLINE) in.token = BACKQUOTED_IDENT // allow for now
+ ensureApplied(parArgumentExprss(wrapNew(simpleType())))
+ }
+
+ def annotations(skipNewLines: Boolean = false): List[Tree] = {
+ if (skipNewLines) newLineOptWhenFollowedBy(AT)
+ if (in.token == AT) annot() :: annotations(skipNewLines)
+ else Nil
+ }
+
+ def annotsAsMods(skipNewLines: Boolean = false): Modifiers =
+ Modifiers() withAnnotations annotations(skipNewLines)
+
+ def defAnnotsMods(allowed: BitSet): Modifiers =
+ modifiers(allowed, annotsAsMods(skipNewLines = true))
+
+ /* -------- PARAMETERS ------------------------------------------- */
+
+ /** ClsTypeParamClause::= `[' ClsTypeParam {`,' ClsTypeParam} `]'
+ * ClsTypeParam ::= {Annotation} [{Modifier} type] [`+' | `-']
+ * Id [HkTypeParamClause] TypeParamBounds
+ *
+ * DefTypeParamClause::= `[' DefTypeParam {`,' DefTypeParam} `]'
+ * DefTypeParam ::= {Annotation} Id [HkTypeParamClause] TypeParamBounds
+ *
+ * TypTypeParamCaluse::= `[' TypTypeParam {`,' TypTypeParam} `]'
+ * TypTypeParam ::= {Annotation} Id [HkTypePamClause] TypeBounds
+ *
+ * HkTypeParamClause ::= `[' HkTypeParam {`,' HkTypeParam} `]'
+ * HkTypeParam ::= {Annotation} ['+' | `-'] (Id[HkTypePamClause] | _') TypeBounds
+ */
+ def typeParamClause(ownerKind: ParamOwner.Value): List[TypeDef] = inBrackets {
+ def typeParam(): TypeDef = {
+ val isConcreteOwner = ownerKind == ParamOwner.Class || ownerKind == ParamOwner.Def
+ val start = in.offset
+ var mods = annotsAsMods()
+ if (ownerKind == ParamOwner.Class) {
+ mods = modifiers(start = mods)
+ mods =
+ atPos(start, in.offset) {
+ if (in.token == TYPE) {
+ val mod = atPos(in.skipToken()) { Mod.Type() }
+ (mods | Param | ParamAccessor).withAddedMod(mod)
+ } else {
+ if (mods.hasFlags) syntaxError(TypeParamsTypeExpected(mods, ident()))
+ mods | Param | PrivateLocal
+ }
+ }
+ }
+ else mods = atPos(start) (mods | Param)
+ if (ownerKind != ParamOwner.Def) {
+ if (isIdent(nme.raw.PLUS)) mods |= Covariant
+ else if (isIdent(nme.raw.MINUS)) mods |= Contravariant
+ if (mods is VarianceFlags) in.nextToken()
+ }
+ atPos(start, nameStart) {
+ val name =
+ if (isConcreteOwner || in.token != USCORE) ident().toTypeName
+ else {
+ in.nextToken()
+ ctx.freshName(nme.USCORE_PARAM_PREFIX).toTypeName
+ }
+ val hkparams = typeParamClauseOpt(ParamOwner.TypeParam)
+ val bounds =
+ if (isConcreteOwner) typeParamBounds(name)
+ else typeBounds()
+ TypeDef(name, lambdaAbstract(hkparams, bounds)).withMods(mods)
+ }
+ }
+ commaSeparated(typeParam)
+ }
+
+ def typeParamClauseOpt(ownerKind: ParamOwner.Value): List[TypeDef] =
+ if (in.token == LBRACKET) typeParamClause(ownerKind) else Nil
+
+ /** ClsParamClauses ::= {ClsParamClause} [[nl] `(' `implicit' ClsParams `)']
+ * ClsParamClause ::= [nl] `(' [ClsParams] ')'
+ * ClsParams ::= ClsParam {`' ClsParam}
+ * ClsParam ::= {Annotation} [{Modifier} (`val' | `var') | `inline'] Param
+ * DefParamClauses ::= {DefParamClause} [[nl] `(' `implicit' DefParams `)']
+ * DefParamClause ::= [nl] `(' [DefParams] ')'
+ * DefParams ::= DefParam {`,' DefParam}
+ * DefParam ::= {Annotation} [`inline'] Param
+ * Param ::= id `:' ParamType [`=' Expr]
+ */
+ def paramClauses(owner: Name, ofCaseClass: Boolean = false): List[List[ValDef]] = {
+ var implicitMod: Mod = null
+ var firstClauseOfCaseClass = ofCaseClass
+ var implicitOffset = -1 // use once
+ def param(): ValDef = {
+ val start = in.offset
+ var mods = annotsAsMods()
+ if (owner.isTypeName) {
+ mods = modifiers(start = mods) | ParamAccessor
+ mods =
+ atPos(start, in.offset) {
+ if (in.token == VAL) {
+ val mod = atPos(in.skipToken()) { Mod.Val() }
+ mods.withAddedMod(mod)
+ } else if (in.token == VAR) {
+ val mod = atPos(in.skipToken()) { Mod.Var() }
+ addMod(mods, mod)
+ } else {
+ if (!(mods.flags &~ (ParamAccessor | Inline)).isEmpty)
+ syntaxError("`val' or `var' expected")
+ if (firstClauseOfCaseClass) mods else mods | PrivateLocal
+ }
+ }
+ }
+ else {
+ if (in.token == INLINE) mods = addModifier(mods)
+ mods = atPos(start) { mods | Param }
+ }
+ atPos(start, nameStart) {
+ val name = ident()
+ val tpt =
+ if (ctx.settings.YmethodInfer.value && owner.isTermName && in.token != COLON) {
+ TypeTree() // XX-METHOD-INFER
+ } else {
+ accept(COLON)
+ if (in.token == ARROW) {
+ if (owner.isTypeName && !(mods is Local))
+ syntaxError(s"${if (mods is Mutable) "`var'" else "`val'"} parameters may not be call-by-name")
+ else if (implicitMod != null)
+ syntaxError("implicit parameters may not be call-by-name")
+ }
+ paramType()
+ }
+ val default =
+ if (in.token == EQUALS) { in.nextToken(); expr() }
+ else EmptyTree
+ if (implicitOffset >= 0) {
+ mods = mods.withPos(mods.pos.union(Position(implicitOffset, implicitOffset)))
+ implicitOffset = -1
+ }
+ if (implicitMod != null) mods = addMod(mods, implicitMod)
+ ValDef(name, tpt, default).withMods(mods)
+ }
+ }
+ def paramClause(): List[ValDef] = inParens {
+ if (in.token == RPAREN) Nil
+ else {
+ if (in.token == IMPLICIT) {
+ implicitOffset = in.offset
+ implicitMod = atPos(in.skipToken()) { Mod.Implicit(Implicit) }
+ }
+ commaSeparated(param)
+ }
+ }
+ def clauses(): List[List[ValDef]] = {
+ newLineOptWhenFollowedBy(LPAREN)
+ if (in.token == LPAREN)
+ paramClause() :: {
+ firstClauseOfCaseClass = false
+ if (implicitMod == null) clauses() else Nil
+ }
+ else Nil
+ }
+ val start = in.offset
+ val result = clauses()
+ if (owner == nme.CONSTRUCTOR && (result.isEmpty || (result.head take 1 exists (_.mods is Implicit)))) {
+ in.token match {
+ case LBRACKET => syntaxError("no type parameters allowed here")
+ case EOF => incompleteInputError(AuxConstructorNeedsNonImplicitParameter())
+ case _ => syntaxError(AuxConstructorNeedsNonImplicitParameter(), start)
+ }
+ }
+ result
+ }
+
+/* -------- DEFS ------------------------------------------- */
+
+ /** Import ::= import ImportExpr {`,' ImportExpr}
+ */
+ def importClause(): List[Tree] = {
+ val offset = accept(IMPORT)
+ commaSeparated(importExpr) match {
+ case t :: rest =>
+ // The first import should start at the position of the keyword.
+ t.withPos(t.pos.withStart(offset)) :: rest
+ case nil => nil
+ }
+ }
+
+ /** ImportExpr ::= StableId `.' (Id | `_' | ImportSelectors)
+ */
+ val importExpr = () => path(thisOK = false, handleImport) match {
+ case imp: Import =>
+ imp
+ case sel @ Select(qual, name) =>
+ val selector = atPos(sel.pos.point) { Ident(name) }
+ cpy.Import(sel)(qual, selector :: Nil)
+ case t =>
+ accept(DOT)
+ Import(t, Ident(nme.WILDCARD) :: Nil)
+ }
+
+ val handleImport = { tree: Tree =>
+ if (in.token == USCORE) Import(tree, importSelector() :: Nil)
+ else if (in.token == LBRACE) Import(tree, inBraces(importSelectors()))
+ else tree
+ }
+
+ /** ImportSelectors ::= `{' {ImportSelector `,'} (ImportSelector | `_') `}'
+ */
+ def importSelectors(): List[Tree] =
+ if (in.token == RBRACE) Nil
+ else {
+ val sel = importSelector()
+ sel :: {
+ if (!isWildcardArg(sel) && in.token == COMMA) {
+ in.nextToken()
+ importSelectors()
+ }
+ else Nil
+ }
+ }
+
+ /** ImportSelector ::= Id [`=>' Id | `=>' `_']
+ */
+ def importSelector(): Tree = {
+ val from = termIdentOrWildcard()
+ if (from.name != nme.WILDCARD && in.token == ARROW)
+ atPos(from.pos.start, in.skipToken()) {
+ Thicket(from, termIdentOrWildcard())
+ }
+ else from
+ }
+
+ def posMods(start: Int, mods: Modifiers) = {
+ val mods1 = atPos(start)(mods)
+ in.nextToken()
+ mods1
+ }
+
+ /** Def ::= val PatDef
+ * | var VarDef
+ * | def DefDef
+ * | type {nl} TypeDcl
+ * | TmplDef
+ * Dcl ::= val ValDcl
+ * | var ValDcl
+ * | def DefDcl
+ * | type {nl} TypeDcl
+ */
+ def defOrDcl(start: Int, mods: Modifiers): Tree = in.token match {
+ case VAL =>
+ val mod = atPos(in.skipToken()) { Mod.Val() }
+ val mods1 = mods.withAddedMod(mod)
+ patDefOrDcl(start, mods1, in.getDocComment(start))
+ case VAR =>
+ val mod = atPos(in.skipToken()) { Mod.Var() }
+ val mod1 = addMod(mods, mod)
+ patDefOrDcl(start, mod1, in.getDocComment(start))
+ case DEF =>
+ defDefOrDcl(start, posMods(start, mods), in.getDocComment(start))
+ case TYPE =>
+ typeDefOrDcl(start, posMods(start, mods), in.getDocComment(start))
+ case _ =>
+ tmplDef(start, mods)
+ }
+
+ /** PatDef ::= Pattern2 {`,' Pattern2} [`:' Type] `=' Expr
+ * VarDef ::= PatDef | Id {`,' Id} `:' Type `=' `_'
+ * ValDcl ::= Id {`,' Id} `:' Type
+ * VarDcl ::= Id {`,' Id} `:' Type
+ */
+ def patDefOrDcl(start: Offset, mods: Modifiers, docstring: Option[Comment] = None): Tree = atPos(start, nameStart) {
+ val lhs = commaSeparated(pattern2)
+ val tpt = typedOpt()
+ val rhs =
+ if (tpt.isEmpty || in.token == EQUALS) {
+ accept(EQUALS)
+ if (in.token == USCORE && !tpt.isEmpty && (mods is Mutable) &&
+ (lhs.toList forall (_.isInstanceOf[Ident]))) {
+ wildcardIdent()
+ } else {
+ expr()
+ }
+ } else EmptyTree
+ lhs match {
+ case (id @ Ident(name: TermName)) :: Nil => {
+ ValDef(name, tpt, rhs).withMods(mods).setComment(docstring)
+ } case _ =>
+ PatDef(mods, lhs, tpt, rhs)
+ }
+ }
+
+ /** DefDef ::= DefSig (`:' Type [`=' Expr] | "=" Expr)
+ * | this ParamClause ParamClauses `=' ConstrExpr
+ * DefDcl ::= DefSig `:' Type
+ * DefSig ::= id [DefTypeParamClause] ParamClauses
+ */
+ def defDefOrDcl(start: Offset, mods: Modifiers, docstring: Option[Comment] = None): Tree = atPos(start, nameStart) {
+ def scala2ProcedureSyntax(resultTypeStr: String) = {
+ val toInsert =
+ if (in.token == LBRACE) s"$resultTypeStr ="
+ else ": Unit " // trailing space ensures that `def f()def g()` works.
+ in.testScala2Mode(s"Procedure syntax no longer supported; `$toInsert' should be inserted here") && {
+ patch(source, Position(in.lastOffset), toInsert)
+ true
+ }
+ }
+ if (in.token == THIS) {
+ in.nextToken()
+ val vparamss = paramClauses(nme.CONSTRUCTOR)
+ val rhs = {
+ if (!(in.token == LBRACE && scala2ProcedureSyntax(""))) accept(EQUALS)
+ atPos(in.offset) { constrExpr() }
+ }
+ makeConstructor(Nil, vparamss, rhs).withMods(mods)
+ } else {
+ val mods1 = addFlag(mods, Method)
+ val name = ident()
+ val tparams = typeParamClauseOpt(ParamOwner.Def)
+ val vparamss = paramClauses(name)
+ var tpt = fromWithinReturnType(typedOpt())
+ val rhs =
+ if (in.token == EQUALS) {
+ in.nextToken()
+ expr
+ }
+ else if (!tpt.isEmpty)
+ EmptyTree
+ else if (scala2ProcedureSyntax(": Unit")) {
+ tpt = scalaUnit
+ if (in.token == LBRACE) expr()
+ else EmptyTree
+ }
+ else {
+ if (!isExprIntro) syntaxError(MissingReturnType(), in.lastOffset)
+ accept(EQUALS)
+ expr()
+ }
+ DefDef(name, tparams, vparamss, tpt, rhs).withMods(mods1).setComment(docstring)
+ }
+ }
+
+ /** ConstrExpr ::= SelfInvocation
+ * | ConstrBlock
+ */
+ def constrExpr(): Tree =
+ if (in.token == LBRACE) constrBlock()
+ else Block(selfInvocation() :: Nil, Literal(Constant(())))
+
+ /** SelfInvocation ::= this ArgumentExprs {ArgumentExprs}
+ */
+ def selfInvocation(): Tree =
+ atPos(accept(THIS)) {
+ newLineOptWhenFollowedBy(LBRACE)
+ argumentExprss(Apply(Ident(nme.CONSTRUCTOR), argumentExprs()))
+ }
+
+ /** ConstrBlock ::= `{' SelfInvocation {semi BlockStat} `}'
+ */
+ def constrBlock(): Tree =
+ atPos(in.skipToken()) {
+ val stats = selfInvocation() :: {
+ if (isStatSep) { in.nextToken(); blockStatSeq() }
+ else Nil
+ }
+ accept(RBRACE)
+ Block(stats, Literal(Constant(())))
+ }
+
+ /** TypeDef ::= type Id [TypeParamClause] `=' Type
+ * TypeDcl ::= type Id [TypeParamClause] TypeBounds
+ */
+ def typeDefOrDcl(start: Offset, mods: Modifiers, docstring: Option[Comment] = None): Tree = {
+ newLinesOpt()
+ atPos(start, nameStart) {
+ val name = ident().toTypeName
+ val tparams = typeParamClauseOpt(ParamOwner.Type)
+ in.token match {
+ case EQUALS =>
+ in.nextToken()
+ TypeDef(name, lambdaAbstract(tparams, typ())).withMods(mods).setComment(docstring)
+ case SUPERTYPE | SUBTYPE | SEMI | NEWLINE | NEWLINES | COMMA | RBRACE | EOF =>
+ TypeDef(name, lambdaAbstract(tparams, typeBounds())).withMods(mods).setComment(docstring)
+ case _ =>
+ syntaxErrorOrIncomplete("`=', `>:', or `<:' expected")
+ EmptyTree
+ }
+ }
+ }
+
+ /** TmplDef ::= ([`case'] `class' | `trait') ClassDef
+ * | [`case'] `object' ObjectDef
+ */
+ def tmplDef(start: Int, mods: Modifiers): Tree = {
+ val docstring = in.getDocComment(start)
+ in.token match {
+ case TRAIT =>
+ classDef(start, posMods(start, addFlag(mods, Trait)), docstring)
+ case CLASS =>
+ classDef(start, posMods(start, mods), docstring)
+ case CASECLASS =>
+ classDef(start, posMods(start, mods | Case), docstring)
+ case OBJECT =>
+ objectDef(start, posMods(start, mods | Module), docstring)
+ case CASEOBJECT =>
+ objectDef(start, posMods(start, mods | Case | Module), docstring)
+ case _ =>
+ syntaxErrorOrIncomplete("expected start of definition")
+ EmptyTree
+ }
+ }
+
+ /** ClassDef ::= Id [ClsTypeParamClause]
+ * [ConstrMods] ClsParamClauses TemplateOpt
+ */
+ def classDef(start: Offset, mods: Modifiers, docstring: Option[Comment]): TypeDef = atPos(start, nameStart) {
+ val name = ident().toTypeName
+ val constr = atPos(in.lastOffset) {
+ val tparams = typeParamClauseOpt(ParamOwner.Class)
+ val cmods = constrModsOpt()
+ val vparamss = paramClauses(name, mods is Case)
+
+ makeConstructor(tparams, vparamss).withMods(cmods)
+ }
+ val templ = templateOpt(constr)
+
+ TypeDef(name, templ).withMods(mods).setComment(docstring)
+ }
+
+ /** ConstrMods ::= AccessModifier
+ * | Annotation {Annotation} (AccessModifier | `this')
+ */
+ def constrModsOpt(): Modifiers = {
+ val mods = modifiers(accessModifierTokens, annotsAsMods())
+ if (mods.hasAnnotations && !mods.hasFlags)
+ if (in.token == THIS) in.nextToken()
+ else syntaxError("`private', `protected', or `this' expected")
+ mods
+ }
+
+ /** ObjectDef ::= Id TemplateOpt
+ */
+ def objectDef(start: Offset, mods: Modifiers, docstring: Option[Comment] = None): ModuleDef = atPos(start, nameStart) {
+ val name = ident()
+ val template = templateOpt(emptyConstructor)
+
+ ModuleDef(name, template).withMods(mods).setComment(docstring)
+ }
+
+/* -------- TEMPLATES ------------------------------------------- */
+
+ /** ConstrApp ::= SimpleType {ParArgumentExprs}
+ */
+ val constrApp = () => {
+ val t = annotType()
+ if (in.token == LPAREN) parArgumentExprss(wrapNew(t))
+ else t
+ }
+
+ /** Template ::= ConstrApps [TemplateBody] | TemplateBody
+ * ConstrApps ::= ConstrApp {`with' ConstrApp}
+ *
+ * @return a pair consisting of the template, and a boolean which indicates
+ * whether the template misses a body (i.e. no {...} part).
+ */
+ def template(constr: DefDef): (Template, Boolean) = {
+ newLineOptWhenFollowedBy(LBRACE)
+ if (in.token == LBRACE) (templateBodyOpt(constr, Nil), false)
+ else {
+ val parents = tokenSeparated(WITH, constrApp)
+ newLineOptWhenFollowedBy(LBRACE)
+ val missingBody = in.token != LBRACE
+ (templateBodyOpt(constr, parents), missingBody)
+ }
+ }
+
+ /** TemplateOpt = [`extends' Template | TemplateBody]
+ */
+ def templateOpt(constr: DefDef): Template =
+ if (in.token == EXTENDS) { in.nextToken(); template(constr)._1 }
+ else {
+ newLineOptWhenFollowedBy(LBRACE)
+ if (in.token == LBRACE) template(constr)._1
+ else Template(constr, Nil, EmptyValDef, Nil)
+ }
+
+ /** TemplateBody ::= [nl] `{' TemplateStatSeq `}'
+ */
+ def templateBodyOpt(constr: DefDef, parents: List[Tree]) = {
+ val (self, stats) =
+ if (in.token == LBRACE) templateBody() else (EmptyValDef, Nil)
+ Template(constr, parents, self, stats)
+ }
+
+ def templateBody(): (ValDef, List[Tree]) = {
+ val r = inDefScopeBraces { templateStatSeq() }
+ if (in.token == WITH) {
+ syntaxError(EarlyDefinitionsNotSupported())
+ in.nextToken()
+ template(emptyConstructor)
+ }
+ r
+ }
+
+/* -------- STATSEQS ------------------------------------------- */
+
+ /** Create a tree representing a packaging */
+ def makePackaging(start: Int, pkg: Tree, stats: List[Tree]): PackageDef = pkg match {
+ case x: RefTree => atPos(start, pkg.pos.point)(PackageDef(x, stats))
+ }
+
+ /** Packaging ::= package QualId [nl] `{' TopStatSeq `}'
+ */
+ def packaging(start: Int): Tree = {
+ val pkg = qualId()
+ newLineOptWhenFollowedBy(LBRACE)
+ val stats = inDefScopeBraces(topStatSeq)
+ makePackaging(start, pkg, stats)
+ }
+
+ /** TopStatSeq ::= TopStat {semi TopStat}
+ * TopStat ::= Annotations Modifiers TmplDef
+ * | Packaging
+ * | package object objectDef
+ * | Import
+ * |
+ */
+ def topStatSeq(): List[Tree] = {
+ val stats = new ListBuffer[Tree]
+ while (!isStatSeqEnd) {
+ setLastStatOffset()
+ if (in.token == PACKAGE) {
+ val start = in.skipToken()
+ if (in.token == OBJECT)
+ stats += objectDef(start, atPos(start, in.skipToken()) { Modifiers(Package) })
+ else stats += packaging(start)
+ }
+ else if (in.token == IMPORT)
+ stats ++= importClause()
+ else if (in.token == AT || isTemplateIntro || isModifier)
+ stats += tmplDef(in.offset, defAnnotsMods(modifierTokens))
+ else if (!isStatSep) {
+ if (in.token == CASE)
+ syntaxErrorOrIncomplete("only `case class` or `case object` allowed")
+ else
+ syntaxErrorOrIncomplete("expected class or object definition")
+ if (mustStartStat) // do parse all definitions even if they are probably local (i.e. a "}" has been forgotten)
+ defOrDcl(in.offset, defAnnotsMods(modifierTokens))
+ }
+ acceptStatSepUnlessAtEnd()
+ }
+ stats.toList
+ }
+
+ /** TemplateStatSeq ::= [id [`:' Type] `=>'] TemplateStat {semi TemplateStat}
+ * TemplateStat ::= Import
+ * | Annotations Modifiers Def
+ * | Annotations Modifiers Dcl
+ * | Expr1
+ * | super ArgumentExprs {ArgumentExprs}
+ * |
+ */
+ def templateStatSeq(): (ValDef, List[Tree]) = checkNoEscapingPlaceholders {
+ var self: ValDef = EmptyValDef
+ val stats = new ListBuffer[Tree]
+ if (isExprIntro) {
+ val first = expr1()
+ if (in.token == ARROW) {
+ first match {
+ case Typed(tree @ This(EmptyTypeIdent), tpt) =>
+ self = makeSelfDef(nme.WILDCARD, tpt).withPos(first.pos)
+ case _ =>
+ val ValDef(name, tpt, _) = convertToParam(first, expected = "self type clause")
+ if (name != nme.ERROR)
+ self = makeSelfDef(name, tpt).withPos(first.pos)
+ }
+ in.nextToken()
+ } else {
+ stats += first
+ acceptStatSepUnlessAtEnd()
+ }
+ }
+ var exitOnError = false
+ while (!isStatSeqEnd && !exitOnError) {
+ setLastStatOffset()
+ if (in.token == IMPORT)
+ stats ++= importClause()
+ else if (isExprIntro)
+ stats += expr1()
+ else if (isDefIntro(modifierTokens))
+ stats += defOrDcl(in.offset, defAnnotsMods(modifierTokens))
+ else if (!isStatSep) {
+ exitOnError = mustStartStat
+ syntaxErrorOrIncomplete("illegal start of definition")
+ }
+ acceptStatSepUnlessAtEnd()
+ }
+ (self, if (stats.isEmpty) List(EmptyTree) else stats.toList)
+ }
+
+ /** RefineStatSeq ::= RefineStat {semi RefineStat}
+ * RefineStat ::= Dcl
+ * |
+ * (in reality we admit Defs and filter them out afterwards)
+ */
+ def refineStatSeq(): List[Tree] = {
+ val stats = new ListBuffer[Tree]
+ while (!isStatSeqEnd) {
+ if (isDclIntro) {
+ stats += defOrDcl(in.offset, Modifiers())
+ } else if (!isStatSep) {
+ syntaxErrorOrIncomplete(
+ "illegal start of declaration" +
+ (if (inFunReturnType) " (possible cause: missing `=' in front of current method body)"
+ else ""))
+ }
+ acceptStatSepUnlessAtEnd()
+ }
+ stats.toList
+ }
+
+ def localDef(start: Int, implicitFlag: FlagSet, implicitMod: Option[Mod] = None): Tree = {
+ var mods = addFlag(defAnnotsMods(localModifierTokens), implicitFlag)
+ if (implicitMod.nonEmpty) mods = mods.withAddedMod(implicitMod.get)
+ defOrDcl(start, mods)
+ }
+
+ /** BlockStatSeq ::= { BlockStat semi } [ResultExpr]
+ * BlockStat ::= Import
+ * | Annotations [implicit] [lazy] Def
+ * | Annotations LocalModifiers TmplDef
+ * | Expr1
+ * |
+ */
+ def blockStatSeq(): List[Tree] = checkNoEscapingPlaceholders {
+ val stats = new ListBuffer[Tree]
+ var exitOnError = false
+ while (!isStatSeqEnd && in.token != CASE && !exitOnError) {
+ setLastStatOffset()
+ if (in.token == IMPORT)
+ stats ++= importClause()
+ else if (isExprIntro)
+ stats += expr(Location.InBlock)
+ else if (isDefIntro(localModifierTokens))
+ if (in.token == IMPLICIT) {
+ val start = in.offset
+ val mod = atPos(in.skipToken()) { Mod.Implicit(ImplicitCommon) }
+ if (isIdent) stats += implicitClosure(start, Location.InBlock, Some(mod))
+ else stats += localDef(start, ImplicitCommon, Some(mod))
+ } else {
+ stats += localDef(in.offset, EmptyFlags)
+ }
+ else if (!isStatSep && (in.token != CASE)) {
+ exitOnError = mustStartStat
+ val addendum = if (isModifier) " (no modifiers allowed here)" else ""
+ syntaxErrorOrIncomplete("illegal start of statement" + addendum)
+ }
+ acceptStatSepUnlessAtEnd(CASE)
+ }
+ stats.toList
+ }
+
+ /** CompilationUnit ::= {package QualId semi} TopStatSeq
+ */
+ def compilationUnit(): Tree = checkNoEscapingPlaceholders {
+ def topstats(): List[Tree] = {
+ val ts = new ListBuffer[Tree]
+ while (in.token == SEMI) in.nextToken()
+ val start = in.offset
+ if (in.token == PACKAGE) {
+ in.nextToken()
+ if (in.token == OBJECT) {
+ val docstring = in.getDocComment(start)
+ ts += objectDef(start, atPos(start, in.skipToken()) { Modifiers(Package) }, docstring)
+ if (in.token != EOF) {
+ acceptStatSep()
+ ts ++= topStatSeq()
+ }
+ } else {
+ val pkg = qualId()
+ newLineOptWhenFollowedBy(LBRACE)
+ if (in.token == EOF)
+ ts += makePackaging(start, pkg, List())
+ else if (in.token == LBRACE) {
+ ts += inDefScopeBraces(makePackaging(start, pkg, topStatSeq()))
+ acceptStatSepUnlessAtEnd()
+ ts ++= topStatSeq()
+ }
+ else {
+ acceptStatSep()
+ ts += makePackaging(start, pkg, topstats())
+ }
+ }
+ }
+ else
+ ts ++= topStatSeq()
+
+ ts.toList
+ }
+
+ topstats() match {
+ case List(stat @ PackageDef(_, _)) => stat
+ case Nil => EmptyTree // without this case we'd get package defs without positions
+ case stats => PackageDef(Ident(nme.EMPTY_PACKAGE), stats)
+ }
+ }
+ }
+
+
+ class OutlineParser(source: SourceFile)(implicit ctx: Context) extends Parser(source) {
+
+ def skipBraces[T](body: T): T = {
+ accept(LBRACE)
+ var openBraces = 1
+ while (in.token != EOF && openBraces > 0) {
+ if (in.token == XMLSTART) xmlLiteral()
+ else {
+ if (in.token == LBRACE) openBraces += 1
+ else if (in.token == RBRACE) openBraces -= 1
+ in.nextToken()
+ }
+ }
+ body
+ }
+
+ override def blockExpr(): Tree = skipBraces(EmptyTree)
+
+ override def templateBody() = skipBraces((EmptyValDef, List(EmptyTree)))
+ }
+}