|
Scala example source code file (Parsers.scala)
This example Scala source code file (Parsers.scala) is included in the DevDaily.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.
The Scala Parsers.scala source code
/* NSC -- new Scala compiler
* Copyright 2005-2011 LAMP/EPFL
* @author Martin Odersky
*/
//todo: allow infix type patterns
//todo verify when stableId's should be just plain qualified type ids
package scala.tools.nsc
package ast.parser
import scala.collection.mutable.ListBuffer
import util.{ SourceFile, OffsetPosition, FreshNameCreator }
import scala.reflect.generic.{ ModifierFlags => Flags }
import Tokens._
import util.Chars.{ isScalaLetter }
/** Historical note: JavaParsers started life as a direct copy of Parsers
* but at a time when that Parsers had been replaced by a different one.
* Later it was dropped and the original Parsers reinstated, leaving us with
* massive duplication between Parsers and JavaParsers.
*
* This trait and the similar one for Scanners/JavaScanners represents
* the beginnings of a campaign against this latest incursion by Cutty
* McPastington and his army of very similar soldiers.
*/
trait ParsersCommon extends ScannersCommon {
val global : Global
import global._
trait ParserCommon {
val in: ScannerCommon
def freshName(prefix: String): Name
def freshTermName(prefix: String): TermName
def freshTypeName(prefix: String): TypeName
def deprecationWarning(off: Int, msg: String): Unit
def accept(token: Int): Int
/** Methods inParensOrError and similar take a second argument which, should
* the next token not be the expected opener (e.g. LPAREN) will be returned
* instead of the contents of the groupers. However in all cases accept(LPAREN)
* will be called, so a parse error will still result. If the grouping is
* optional, in.token should be tested before calling these methods.
*/
def inParens[T](body: => T): T = {
accept(LPAREN)
val ret = body
accept(RPAREN)
ret
}
def inParensOrError[T](body: => T, alt: T): T =
if (in.token == LPAREN) inParens(body)
else { accept(LPAREN) ; alt }
def inParensOrUnit[T](body: => Tree): Tree = inParensOrError(body, Literal(()))
def inParensOrNil[T](body: => List[T]): List[T] = inParensOrError(body, Nil)
def inBraces[T](body: => T): T = {
accept(LBRACE)
val ret = body
accept(RBRACE)
ret
}
def inBracesOrError[T](body: => T, alt: T): T =
if (in.token == LBRACE) inBraces(body)
else { accept(LBRACE) ; alt }
def inBracesOrNil[T](body: => List[T]): List[T] = inBracesOrError(body, Nil)
def inBracesOrUnit[T](body: => Tree): Tree = inBracesOrError(body, Literal(()))
def inBrackets[T](body: => T): T = {
accept(LBRACKET)
val ret = body
accept(RBRACKET)
ret
}
/** Creates an actual Parens node (only used during parsing.)
*/
def makeParens(body: => List[Tree]): Parens =
Parens(inParens(if (in.token == RPAREN) Nil else body))
}
}
/** <p>Performs the following context-free rewritings:
* <ol>
* <li>
* Places all pattern variables in Bind nodes. In a pattern, for
* identifiers <code>x: * x => x @ _
* x:T => x @ (_ : T)</pre>
* </li>
* <li>Removes pattern definitions (PatDef's) as follows:
* If pattern is a simple (typed) identifier:<pre>
* <b>val x = e ==> val x = e
* <b>val x: T = e ==> val x: T = e
*
* if there are no variables in pattern<pre>
* <b>val p = e ==> e match (case p => ())
*
* if there is exactly one variable in pattern<pre>
* <b>val x_1 = e match (case p => (x_1))
*
* if there is more than one variable in pattern<pre>
* <b>val p = e ==> private synthetic val t$ = e match (case p => (x_1, ..., x_N))
* <b>val x_1 = t$._1
* ...
* <b>val x_N = t$._N
* </li>
* <li>
* Removes function types as follows:<pre>
* (argtpes) => restpe ==> scala.Function_n[argtpes, restpe]</pre>
* </li>
* <li>
* Wraps naked case definitions in a match as follows:<pre>
* { cases } ==> (x => x.match {cases})<span style="font-family:normal;">, except when already argument to match
* </li>
* </ol>
*/
trait Parsers extends Scanners with MarkupParsers with ParsersCommon {
self =>
val global: Global
import global._
private val glob: global.type = global
case class OpInfo(operand: Tree, operator: Name, offset: Offset)
class SourceFileParser(val source: SourceFile) extends Parser {
/** The parse starting point depends on whether the source file is self-contained:
* if not, the AST will be supplemented.
*/
def parseStartRule =
if (source.isSelfContained) () => compilationUnit()
else () => scriptBody()
def newScanner = new SourceFileScanner(source)
val in = newScanner
in.init()
private val globalFresh = new FreshNameCreator.Default
def freshName(prefix: String): Name = freshTermName(prefix)
def freshTermName(prefix: String): TermName = newTermName(globalFresh.newName(prefix))
def freshTypeName(prefix: String): TypeName = newTypeName(globalFresh.newName(prefix))
def o2p(offset: Int): Position = new OffsetPosition(source, offset)
def r2p(start: Int, mid: Int, end: Int): Position = rangePos(source, start, mid, end)
// suppress warnings; silent abort on errors
def warning(offset: Int, msg: String) {}
def deprecationWarning(offset: Int, msg: String) {}
def syntaxError(offset: Int, msg: String): Unit = throw new MalformedInput(offset, msg)
def incompleteInputError(msg: String): Unit = throw new MalformedInput(source.content.length - 1, msg)
/** the markup parser */
lazy val xmlp = new MarkupParser(this, true)
object symbXMLBuilder extends SymbolicXMLBuilder(this, true) { // DEBUG choices
val global: self.global.type = self.global
def freshName(prefix: String): Name = SourceFileParser.this.freshName(prefix)
}
def xmlLiteral : Tree = xmlp.xLiteral
def xmlLiteralPattern : Tree = xmlp.xLiteralPattern
}
class OutlineParser(source: SourceFile) extends SourceFileParser(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(isPre: Boolean) = skipBraces(emptyValDef, List(EmptyTree))
}
class UnitParser(val unit: global.CompilationUnit, patches: List[BracePatch]) extends SourceFileParser(unit.source) {
def this(unit: global.CompilationUnit) = this(unit, List())
override def newScanner = new UnitScanner(unit, patches)
override def freshTermName(prefix: String): TermName = unit.freshTermName(prefix)
override def freshTypeName(prefix: String): TypeName = unit.freshTypeName(prefix)
override def warning(offset: Int, msg: String) {
unit.warning(o2p(offset), msg)
}
override def deprecationWarning(offset: Int, msg: String) {
unit.deprecationWarning(o2p(offset), msg)
}
private var smartParsing = false
private def withSmartParsing[T](body: => T): T = {
val saved = smartParsing
try {
smartParsing = true
body
}
finally smartParsing = saved // false
}
val syntaxErrors = new ListBuffer[(Int, String)]
def showSyntaxErrors() =
for ((offset, msg) <- syntaxErrors)
unit.error(o2p(offset), msg)
override def syntaxError(offset: Int, msg: String) {
if (smartParsing) syntaxErrors += ((offset, msg))
else unit.error(o2p(offset), msg)
}
override def incompleteInputError(msg: String) {
val offset = source.content.length - 1
if (smartParsing) syntaxErrors += ((offset, msg))
else unit.incompleteInputError(o2p(offset), msg)
}
/** parse unit. If there are inbalanced braces,
* try to correct them and reparse.
*/
def smartParse(): Tree = withSmartParsing {
val firstTry = parse()
if (syntaxErrors.isEmpty) firstTry
else in.healBraces() match {
case Nil => showSyntaxErrors() ; firstTry
case patches => new UnitParser(unit, patches).parse()
}
}
}
final val Local = 0
final val InBlock = 1
final val InTemplate = 2
import nme.raw
abstract class Parser extends ParserCommon {
val in: Scanner
def freshName(prefix: String): Name
def freshTermName(prefix: String): TermName
def freshTypeName(prefix: String): TypeName
def o2p(offset: Int): Position
def r2p(start: Int, mid: Int, end: Int): Position
/** whether a non-continuable syntax error has been seen */
private var lastErrorOffset : Int = -1
object treeBuilder extends TreeBuilder {
val global: self.global.type = self.global
def freshName(prefix: String): Name = freshTermName(prefix)
def freshTermName(prefix: String): TermName = Parser.this.freshTermName(prefix)
def freshTypeName(prefix: String): TypeName = Parser.this.freshTypeName(prefix)
def o2p(offset: Int) = Parser.this.o2p(offset)
def r2p(start: Int, point: Int, end: Int) = Parser.this.r2p(start, point, end)
}
import treeBuilder.{global => _, _}
/** The types of the context bounds of type parameters of the surrounding class
*/
private var classContextBounds: List[Tree] = Nil
private def savingClassContextBounds[T](op: => T): T = {
val saved = classContextBounds
try op
finally classContextBounds = saved
}
/** Are we inside the Scala package? Set for files that start with package scala
*/
private var inScalaPackage = false
private var currentPackage = ""
def resetPackage() {
inScalaPackage = false
currentPackage = ""
}
private lazy val anyValNames: Set[Name] = tpnme.ScalaValueNames.toSet + tpnme.AnyVal
private def inScalaRootPackage = inScalaPackage && currentPackage == "scala"
private def isScalaArray(name: Name) = inScalaRootPackage && name == tpnme.Array
private def isAnyValType(name: Name) = inScalaRootPackage && anyValNames(name)
def parseStartRule: () => Tree
/** This is the general parse entry point.
*/
def parse(): Tree = {
val t = parseStartRule()
accept(EOF)
t
}
/** This is the parse entry point for code which is not self-contained, e.g.
* a script which is a series of template statements. They will be
* swaddled in Trees until the AST is equivalent to the one returned
* by compilationUnit().
*/
def scriptBody(): Tree = {
val stmts = templateStatSeq(false)._2
accept(EOF)
def mainModuleName = settings.script.value
/** If there is only a single object template in the file and it has a
* suitable main method, we will use it rather than building another object
* around it. Since objects are loaded lazily the whole script would have
* been a no-op, so we're not taking much liberty.
*/
def searchForMain(): Option[Tree] = {
/** Have to be fairly liberal about what constitutes a main method since
* nothing has been typed yet - for instance we can't assume the parameter
* type will look exactly like "Array[String]" as it could have been renamed
* via import, etc.
*/
def isMainMethod(t: Tree) = t match {
case DefDef(_, nme.main, Nil, List(_), _, _) => true
case _ => false
}
/** For now we require there only be one top level object. */
var seenModule = false
val newStmts = stmts collect {
case t @ Import(_, _) => t
case md @ ModuleDef(mods, name, template) if !seenModule && (md exists isMainMethod) =>
seenModule = true
/** This slightly hacky situation arises because we have no way to communicate
* back to the scriptrunner what the name of the program is. Even if we were
* willing to take the sketchy route of settings.script.value = progName, that
* does not work when using fsc. And to find out in advance would impose a
* whole additional parse. So instead, if the actual object's name differs from
* what the script is expecting, we transform it to match.
*/
if (name.toString == mainModuleName) md
else treeCopy.ModuleDef(md, mods, mainModuleName, template)
case _ =>
/** If we see anything but the above, fail. */
return None
}
Some(makePackaging(0, emptyPkg, newStmts))
}
if (mainModuleName == ScriptRunner.defaultScriptMain)
searchForMain() foreach { return _ }
/** Here we are building an AST representing the following source fiction,
* where <moduleName> is from -Xscript (defaults to "Main") and are
* the result of parsing the script file.
*
* object <moduleName> {
* def main(argv: Array[String]): Unit = {
* val args = argv
* new AnyRef {
* <stmts>
* }
* }
* }
*/
import definitions._
def emptyPkg = atPos(0, 0, 0) { Ident(nme.EMPTY_PACKAGE_NAME) }
def emptyInit = DefDef(
NoMods,
nme.CONSTRUCTOR,
Nil,
List(Nil),
TypeTree(),
Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), Nil)), Literal(Constant(())))
)
// def main
def mainParamType = AppliedTypeTree(Ident(tpnme.Array), List(Ident(tpnme.String)))
def mainParameter = List(ValDef(Modifiers(Flags.PARAM), "argv", mainParamType, EmptyTree))
def mainSetArgv = List(ValDef(NoMods, "args", TypeTree(), Ident("argv")))
def mainNew = makeNew(Nil, emptyValDef, stmts, List(Nil), NoPosition, NoPosition)
def mainDef = DefDef(NoMods, nme.main, Nil, List(mainParameter), scalaDot(tpnme.Unit), Block(mainSetArgv, mainNew))
// object Main
def moduleName = ScriptRunner scriptMain settings
def moduleBody = Template(List(scalaScalaObjectConstr), emptyValDef, List(emptyInit, mainDef))
def moduleDef = ModuleDef(NoMods, moduleName, moduleBody)
// package <empty> { ... }
makePackaging(0, emptyPkg, List(moduleDef))
}
/* --------------- PLACEHOLDERS ------------------------------------------- */
/** The implicit parameters introduced by `_' in the current expression.
* Parameters appear in reverse order
*/
var placeholderParams: List[ValDef] = Nil
/** The placeholderTypes introduced by `_' in the current type.
* Parameters appear in reverse order
*/
var placeholderTypes: List[TypeDef] = Nil
def checkNoEscapingPlaceholders[T](op: => T): T = {
val savedPlaceholderParams = placeholderParams
val savedPlaceholderTypes = placeholderTypes
placeholderParams = List()
placeholderTypes = List()
val res = op
placeholderParams match {
case vd :: _ =>
syntaxError(vd.pos, "unbound placeholder parameter", false)
placeholderParams = List()
case _ =>
}
placeholderTypes match {
case td :: _ =>
syntaxError(td.pos, "unbound wildcard type", false)
placeholderTypes = List()
case _ =>
}
placeholderParams = savedPlaceholderParams
placeholderTypes = savedPlaceholderTypes
res
}
def placeholderTypeBoundary(op: => Tree): Tree = {
val savedPlaceholderTypes = placeholderTypes
placeholderTypes = List()
var t = op
if (!placeholderTypes.isEmpty && t.isInstanceOf[AppliedTypeTree]) {
val expos = t.pos
ensureNonOverlapping(t, placeholderTypes)
t = atPos(expos) { ExistentialTypeTree(t, placeholderTypes.reverse) }
placeholderTypes = List()
}
placeholderTypes = placeholderTypes ::: savedPlaceholderTypes
t
}
def isWildcard(t: Tree): Boolean = t match {
case Ident(name1) => !placeholderParams.isEmpty && name1 == placeholderParams.head.name
case Typed(t1, _) => isWildcard(t1)
case Annotated(t1, _) => isWildcard(t1)
case _ => false
}
/* ------------- ERROR HANDLING ------------------------------------------- */
var assumedClosingParens = collection.mutable.Map(RPAREN -> 0, RBRACKET -> 0, RBRACE -> 0)
private var inFunReturnType = false
private def fromWithinReturnType[T](body: => T): T = {
val saved = inFunReturnType
try {
inFunReturnType = true
body
}
finally inFunReturnType = saved
}
protected def skip(targetToken: Int) {
var nparens = 0
var nbraces = 0
while (true) {
in.token match {
case EOF =>
return
case SEMI =>
if (nparens == 0 && nbraces == 0) return
case NEWLINE =>
if (nparens == 0 && nbraces == 0) return
case NEWLINES =>
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 _ =>
}
if (targetToken == in.token && nparens == 0 && nbraces == 0) return
in.nextToken()
}
}
def warning(offset: Int, msg: String): Unit
def incompleteInputError(msg: String): Unit
private def syntaxError(pos: Position, msg: String, skipIt: Boolean) {
syntaxError(pos pointOrElse in.offset, msg, skipIt)
}
def syntaxError(offset: Int, msg: String): Unit
def syntaxError(msg: String, skipIt: Boolean) {
syntaxError(in.offset, msg, skipIt)
}
def syntaxError(offset: Int, msg: String, skipIt: Boolean) {
if (offset > lastErrorOffset) {
syntaxError(offset, msg)
// no more errors on this token.
lastErrorOffset = in.offset
}
if (skipIt)
skip(UNDEF)
}
def warning(msg: String) { warning(in.offset, msg) }
def syntaxErrorOrIncomplete(msg: String, skipIt: Boolean) {
if (in.token == EOF)
incompleteInputError(msg)
else
syntaxError(in.offset, msg, skipIt)
}
def expectedMsg(token: Int): String =
token2string(token) + " expected but " +token2string(in.token) + " found."
/** Consume one token of the specified type, or
* signal an error if it is not there.
*/
def accept(token: Int): Int = {
val offset = in.offset
if (in.token != token) {
syntaxErrorOrIncomplete(expectedMsg(token), false)
if ((token == RPAREN || token == RBRACE || token == RBRACKET))
if (in.parenBalance(token) + assumedClosingParens(token) < 0)
assumedClosingParens(token) += 1
else
skip(token)
else
skip(UNDEF)
}
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 acceptStatSepOpt() =
if (!isStatSeqEnd)
acceptStatSep()
def errorTypeTree = TypeTree() setType ErrorType setPos o2p(in.offset)
def errorTermTree = Literal(Constant(null)) setPos o2p(in.offset)
def errorPatternTree = Ident(nme.WILDCARD) setPos o2p(in.offset)
/** Check that type parameter is not by name or repeated */
def checkNotByNameOrVarargs(tpt: Tree) = {
if (treeInfo isByNameParamType tpt)
syntaxError(tpt.pos, "no by-name parameter type allowed here", false)
else if (treeInfo isRepeatedParamType tpt)
syntaxError(tpt.pos, "no * parameter type allowed here", false)
}
/** Check that tree is a legal clause of a forSome */
def checkLegalExistential(t: Tree) = t match {
case TypeDef(_, _, _, TypeBoundsTree(_, _)) |
ValDef(_, _, _, EmptyTree) | EmptyTree =>
;
case _ =>
syntaxError(t.pos, "not a legal existential clause", false)
}
/* -------------- TOKEN CLASSES ------------------------------------------- */
def isModifier: Boolean = in.token match {
case ABSTRACT | FINAL | SEALED | PRIVATE |
PROTECTED | OVERRIDE | IMPLICIT | LAZY => true
case _ => false
}
def isLocalModifier: Boolean = in.token match {
case ABSTRACT | FINAL | SEALED | IMPLICIT | LAZY => true
case _ => false
}
def isTemplateIntro: Boolean = in.token match {
case OBJECT | CASEOBJECT | CLASS | CASECLASS | TRAIT => true
case _ => false
}
def isDclIntro: Boolean = in.token match {
case VAL | VAR | DEF | TYPE => true
case _ => false
}
def isDefIntro = isTemplateIntro || isDclIntro
def isNumericLit: Boolean = in.token match {
case INTLIT | LONGLIT | FLOATLIT | DOUBLELIT => true
case _ => false
}
def isUnaryOp = isIdent && raw.isUnary(in.name)
def isRawStar = isIdent && in.name == raw.STAR
def isRawBar = isIdent && in.name == raw.BAR
def isIdent = in.token == IDENTIFIER || in.token == BACKQUOTED_IDENT
def isLiteralToken(token: Int) = token match {
case CHARLIT | INTLIT | LONGLIT | FLOATLIT | DOUBLELIT |
STRINGLIT | SYMBOLLIT | TRUE | FALSE | NULL => true
case _ => false
}
def isLiteral = isLiteralToken(in.token)
def isExprIntroToken(token: Int): Boolean = isLiteralToken(token) || (token match {
case IDENTIFIER | BACKQUOTED_IDENT |
THIS | SUPER | IF | FOR | NEW | USCORE | TRY | WHILE |
DO | RETURN | THROW | LPAREN | LBRACE | XMLSTART => true
case _ => false
})
def isExprIntro: Boolean = isExprIntroToken(in.token)
def isTypeIntroToken(token: Int): Boolean = token match {
case IDENTIFIER | BACKQUOTED_IDENT | THIS |
SUPER | USCORE | LPAREN | AT => true
case _ => false
}
def isTypeIntro: Boolean = isTypeIntroToken(in.token)
def isStatSeqEnd = in.token == RBRACE || in.token == EOF
def isStatSep(token: Int): Boolean =
token == NEWLINE || token == NEWLINES || token == SEMI
def isStatSep: Boolean = isStatSep(in.token)
/* --------- COMMENT AND ATTRIBUTE COLLECTION ----------------------------- */
/** Join the comment associated with a definition
*/
def joinComment(trees: => List[Tree]): List[Tree] = {
val doc = in.flushDoc
if ((doc ne null) && doc.raw.length > 0) {
val joined = trees map {
t =>
DocDef(doc, t) setPos {
if (t.pos.isDefined) {
val pos = doc.pos.withEnd(t.pos.endOrPoint)
if (t.pos.isOpaqueRange) pos else pos.makeTransparent
} else {
t.pos
}
}
}
joined.find(_.pos.isOpaqueRange) foreach {
main =>
val mains = List(main)
joined foreach { t => if (t ne main) ensureNonOverlapping(t, mains) }
}
joined
}
else trees
}
/* ---------- TREE CONSTRUCTION ------------------------------------------- */
def atPos[T <: Tree](offset: Int)(t: T): T =
global.atPos(r2p(offset, offset, in.lastOffset max offset))(t)
def atPos[T <: Tree](start: Int, point: Int)(t: T): T =
global.atPos(r2p(start, point, in.lastOffset max start))(t)
def atPos[T <: Tree](start: Int, point: Int, end: Int)(t: T): T =
global.atPos(r2p(start, point, end))(t)
def atPos[T <: Tree](pos: Position)(t: T): T =
global.atPos(pos)(t)
/** Convert tree to formal parameter list
*/
def convertToParams(tree: Tree): List[ValDef] = tree match {
case Parens(ts) => ts map convertToParam
case _ => List(convertToParam(tree))
}
/** Convert tree to formal parameter
*/
def convertToParam(tree: Tree): ValDef = atPos(tree.pos) {
def removeAsPlaceholder(name: Name) {
placeholderParams = placeholderParams filter (_.name != name)
}
tree match {
case Ident(name) =>
removeAsPlaceholder(name)
makeParam(name, TypeTree() setPos o2p(tree.pos.endOrPoint))
case Typed(Ident(name), tpe) if tpe.isType => // get the ident!
removeAsPlaceholder(name)
makeParam(name, tpe)
case _ =>
syntaxError(tree.pos, "not a legal formal parameter", false)
makeParam(nme.ERROR, errorTypeTree setPos o2p(tree.pos.endOrPoint))
}
}
/** Convert (qual)ident to type identifier
*/
def convertToTypeId(tree: Tree): Tree = atPos(tree.pos) {
convertToTypeName(tree) getOrElse {
syntaxError(tree.pos, "identifier expected", false)
errorTypeTree
}
}
/** part { `sep` part }
* Or if sepFirst is true, { `sep` part }
*/
def tokenSeparated[T](separator: Int, sepFirst: Boolean, part: => T): List[T] = {
val ts = new ListBuffer[T]
if (!sepFirst)
ts += part
while (in.token == separator) {
in.nextToken()
ts += part
}
ts.toList
}
def commaSeparated[T](part: => T): List[T] = tokenSeparated(COMMA, false, part)
def caseSeparated[T](part: => T): List[T] = tokenSeparated(CASE, true, part)
def readAnnots[T](part: => T): List[T] = tokenSeparated(AT, true, part)
/* --------- OPERAND/OPERATOR STACK --------------------------------------- */
/** modes for infix types */
object InfixMode extends Enumeration {
val FirstOp, LeftOp, RightOp = Value
}
var opstack: List[OpInfo] = Nil
def precedence(operator: Name): Int =
if (operator eq nme.ERROR) -1
else {
val firstCh = operator(0)
if (isScalaLetter(firstCh)) 1
else if (nme.isOpAssignmentName(operator)) 0
else firstCh match {
case '|' => 2
case '^' => 3
case '&' => 4
case '=' | '!' => 5
case '<' | '>' => 6
case ':' => 7
case '+' | '-' => 8
case '*' | '/' | '%' => 9
case _ => 10
}
}
def checkSize(kind: String, size: Int, max: Int) {
if (size > max) syntaxError("too many "+kind+", maximum = "+max, false)
}
def checkAssoc(offset: Int, op: Name, leftAssoc: Boolean) =
if (treeInfo.isLeftAssoc(op) != leftAssoc)
syntaxError(
offset, "left- and right-associative operators with same precedence may not be mixed", false)
def reduceStack(isExpr: Boolean, base: List[OpInfo], top0: Tree,
prec: Int, leftAssoc: Boolean): Tree = {
var top = top0
if (opstack != base && precedence(opstack.head.operator) == prec)
checkAssoc(opstack.head.offset, opstack.head.operator, leftAssoc)
while (opstack != base &&
(prec < precedence(opstack.head.operator) ||
leftAssoc && prec == precedence(opstack.head.operator))) {
val opinfo = opstack.head
opstack = opstack.tail
val opPos = r2p(opinfo.offset, opinfo.offset, opinfo.offset+opinfo.operator.length)
val lPos = opinfo.operand.pos
val start = if (lPos.isDefined) lPos.startOrPoint else opPos.startOrPoint
val rPos = top.pos
val end = if (rPos.isDefined) rPos.endOrPoint else opPos.endOrPoint
top = atPos(start, opinfo.offset, end) {
makeBinop(isExpr, opinfo.operand, opinfo.operator, top, opPos)
}
}
top
}
/* -------- IDENTIFIERS AND LITERALS ------------------------------------------- */
/** Methods which implicitly propagate the context in which they were
* called: either in a pattern context or not. Formerly, this was
* threaded through numerous methods as boolean isPattern.
*/
trait PatternContextSensitive {
/** ArgType ::= Type
*/
def argType(): Tree
def functionArgType(): Tree
private def tupleInfixType(start: Int) = {
in.nextToken()
if (in.token == RPAREN) {
in.nextToken()
atPos(start, accept(ARROW)) { makeFunctionTypeTree(Nil, typ()) }
}
else {
val ts = functionTypes()
accept(RPAREN)
if (in.token == ARROW)
atPos(start, in.skipToken()) { makeFunctionTypeTree(ts, typ()) }
else {
ts foreach checkNotByNameOrVarargs
val tuple = atPos(start) { makeTupleType(ts, true) }
infixTypeRest(
compoundTypeRest(
annotTypeRest(
simpleTypeRest(
tuple))),
InfixMode.FirstOp
)
}
}
}
private def makeExistentialTypeTree(t: Tree) = {
val whereClauses = refinement()
whereClauses foreach checkLegalExistential
ExistentialTypeTree(t, whereClauses)
}
/** Type ::= InfixType `=>' Type
* | `(' [`=>' Type] `)' `=>' Type
* | InfixType [ExistentialClause]
* ExistentialClause ::= forSome `{' ExistentialDcl {semi ExistentialDcl}} `}'
* ExistentialDcl ::= type TypeDcl | val ValDcl
*/
def typ(): Tree = placeholderTypeBoundary {
val start = in.offset
val t =
if (in.token == LPAREN) tupleInfixType(start)
else infixType(InfixMode.FirstOp)
in.token match {
case ARROW => atPos(start, in.skipToken()) { makeFunctionTypeTree(List(t), typ()) }
case FORSOME => atPos(start, in.skipToken()) { makeExistentialTypeTree(t) }
case _ => t
}
}
/** TypeArgs ::= `[' ArgType {`,' ArgType} `]'
*/
def typeArgs(): List[Tree] = inBrackets(types())
/** AnnotType ::= SimpleType {Annotation}
*/
def annotType(): Tree = placeholderTypeBoundary { annotTypeRest(simpleType()) }
/** SimpleType ::= SimpleType TypeArgs
* | SimpleType `#' Id
* | StableId
* | Path `.' type
* | `(' Types `)'
* | WildcardType
*/
def simpleType(): Tree = {
val start = in.offset
simpleTypeRest(in.token match {
case LPAREN => atPos(start)(makeTupleType(inParens(types()), true))
case USCORE => wildcardType(in.skipToken())
case _ =>
path(false, true) match {
case r @ SingletonTypeTree(_) => r
case r => convertToTypeId(r)
}
})
}
private def typeProjection(t: Tree): Tree = {
val hashOffset = in.skipToken()
val nameOffset = in.offset
val name = identForType(false)
val point = if (name == tpnme.ERROR) hashOffset else nameOffset
atPos(t.pos.startOrPoint, point)(SelectFromTypeTree(t, name))
}
def simpleTypeRest(t: Tree): Tree = in.token match {
case HASH => simpleTypeRest(typeProjection(t))
case LBRACKET => simpleTypeRest(atPos(t.pos.startOrPoint)(AppliedTypeTree(t, typeArgs())))
case _ => t
}
/** CompoundType ::= AnnotType {with AnnotType} [Refinement]
* | Refinement
*/
def compoundType(): Tree = compoundTypeRest(
if (in.token == LBRACE) atPos(o2p(in.offset))(scalaAnyRefConstr)
else annotType()
)
def compoundTypeRest(t: Tree): Tree = {
var ts = new ListBuffer[Tree] += t
while (in.token == WITH) {
in.nextToken()
ts += annotType()
}
newLineOptWhenFollowedBy(LBRACE)
atPos(t.pos.startOrPoint) {
if (in.token == LBRACE) {
// Warn if they are attempting to refine Unit; we can't be certain it's
// scala.Unit they're refining because at this point all we have is an
// identifier, but at a later stage we lose the ability to tell an empty
// refinement from no refinement at all. See bug #284.
for (Ident(name) <- ts) name.toString match {
case "Unit" | "scala.Unit" =>
warning("Detected apparent refinement of Unit; are you missing an '=' sign?")
case _ =>
}
CompoundTypeTree(Template(ts.toList, emptyValDef, refinement()))
}
else
makeIntersectionTypeTree(ts.toList)
}
}
def infixTypeRest(t: Tree, mode: InfixMode.Value): Tree = {
if (isIdent && in.name != nme.STAR) {
val opOffset = in.offset
val leftAssoc = treeInfo.isLeftAssoc(in.name)
if (mode != InfixMode.FirstOp) checkAssoc(opOffset, in.name, mode == InfixMode.LeftOp)
val op = identForType()
val tycon = atPos(opOffset) { Ident(op) }
newLineOptWhenFollowing(isTypeIntroToken)
def mkOp(t1: Tree) = atPos(t.pos.startOrPoint, opOffset) { AppliedTypeTree(tycon, List(t, t1)) }
if (leftAssoc)
infixTypeRest(mkOp(compoundType()), InfixMode.LeftOp)
else
mkOp(infixType(InfixMode.RightOp))
} else t
}
/** InfixType ::= CompoundType {id [nl] CompoundType}
*/
def infixType(mode: InfixMode.Value): Tree =
placeholderTypeBoundary { infixTypeRest(compoundType(), mode) }
/** Types ::= Type {`,' Type}
*/
def types(): List[Tree] = commaSeparated(argType())
def functionTypes(): List[Tree] = commaSeparated(functionArgType())
}
/** Assumed (provisionally) to be TermNames. */
def ident(skipIt: Boolean): Name =
if (isIdent) {
val name = in.name.encode
in.nextToken()
name
} else {
syntaxErrorOrIncomplete(expectedMsg(IDENTIFIER), skipIt)
nme.ERROR
}
def ident(): Name = ident(true)
/** For when it's known already to be a type name. */
def identForType(): TypeName = ident().toTypeName
def identForType(skipIt: Boolean): TypeName = ident(skipIt).toTypeName
def selector(t: Tree): Tree = {
val point = in.offset
//assert(t.pos.isDefined, t)
if (t != EmptyTree)
Select(t, ident(false)) setPos r2p(t.pos.startOrPoint, point, in.lastOffset)
else
errorTermTree // has already been reported
}
/** Path ::= StableId
* | [Ident `.'] this
* AnnotType ::= Path [`.' type]
*/
def path(thisOK: Boolean, typeOK: Boolean): Tree = {
val start = in.offset
var t: Tree = null
if (in.token == THIS) {
in.nextToken()
t = atPos(start) { This(tpnme.EMPTY) }
if (!thisOK || in.token == DOT) {
t = selectors(t, typeOK, accept(DOT))
}
} else if (in.token == SUPER) {
in.nextToken()
t = atPos(start) { Super(This(tpnme.EMPTY), mixinQualifierOpt()) }
accept(DOT)
t = selector(t)
if (in.token == DOT) t = selectors(t, typeOK, in.skipToken())
} else {
val tok = in.token
val name = ident()
t = atPos(start) {
if (tok == BACKQUOTED_IDENT) new BackQuotedIdent(name)
else Ident(name)
}
if (in.token == DOT) {
val dotOffset = in.skipToken()
if (in.token == THIS) {
in.nextToken()
t = atPos(start) { This(name.toTypeName) }
if (!thisOK || in.token == DOT)
t = selectors(t, typeOK, accept(DOT))
} else if (in.token == SUPER) {
in.nextToken()
t = atPos(start) { Super(This(name.toTypeName), mixinQualifierOpt()) }
accept(DOT)
t = selector(t)
if (in.token == DOT) t = selectors(t, typeOK, in.skipToken())
} else {
t = selectors(t, typeOK, dotOffset)
}
}
}
t
}
def selectors(t: Tree, typeOK: Boolean, dotOffset: Int): Tree =
if (typeOK && in.token == TYPE) {
in.nextToken()
atPos(t.pos.startOrPoint, dotOffset) { SingletonTypeTree(t) }
} else {
val t1 = selector(t)
if (in.token == DOT) { selectors(t1, typeOK, in.skipToken()) }
else t1
}
/** MixinQualifier ::= `[' Id `]'
*/
def mixinQualifierOpt(): TypeName =
if (in.token == LBRACKET) inBrackets(identForType())
else tpnme.EMPTY
/** StableId ::= Id
* | Path `.' Id
* | [id '.'] super [`[' id `]']`.' id
*/
def stableId(): Tree =
path(false, false)
/** QualId ::= Id {`.' Id}
*/
def qualId(): Tree = {
val start = in.offset
val id = atPos(start) { Ident(ident()) }
if (in.token == DOT) { selectors(id, false, in.skipToken()) }
else id
}
/** Calls qualId() and manages some package state.
*/
private def pkgQualId() = {
if (in.token == IDENTIFIER && in.name.encode == nme.scala_)
inScalaPackage = true
val pkg = qualId()
newLineOptWhenFollowedBy(LBRACE)
if (currentPackage == "") currentPackage = pkg.toString
else currentPackage = currentPackage + "." + pkg
pkg
}
/** SimpleExpr ::= literal
* | symbol
* | null
* @note The returned tree does not yet have a position
*/
def literal(isNegated: Boolean): Tree = {
def finish(value: Any): Tree = {
val t = Literal(Constant(value))
in.nextToken()
t
}
if (in.token == SYMBOLLIT)
Apply(scalaDot(nme.Symbol), List(finish(in.strVal)))
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 => in.strVal
case TRUE => true
case FALSE => false
case NULL => null
case _ =>
syntaxErrorOrIncomplete("illegal literal", true)
null
})
}
/* ------------- NEW LINES ------------------------------------------------- */
def newLineOpt() {
if (in.token == NEWLINE) in.nextToken()
}
def newLinesOpt() {
if (in.token == NEWLINE || in.token == NEWLINES)
in.nextToken()
}
def newLineOptWhenFollowedBy(token: Int) {
// note: next is defined here because current == NEWLINE
if (in.token == NEWLINE && in.next.token == token) newLineOpt()
}
def newLineOptWhenFollowing(p: Int => Boolean) {
// note: next is defined here because current == NEWLINE
if (in.token == NEWLINE && p(in.next.token)) newLineOpt()
}
/* ------------- TYPES ---------------------------------------------------- */
/** TypedOpt ::= [`:' Type]
*/
def typedOpt(): Tree =
if (in.token == COLON) { in.nextToken(); typ() }
else TypeTree()
def typeOrInfixType(location: Int): Tree =
if (location == Local) typ()
else startInfixType()
def annotTypeRest(t: Tree): Tree =
(t /: annotations(false)) (makeAnnotated)
/** WildcardType ::= `_' TypeBounds
*/
def wildcardType(start: Int) = {
val pname = freshTypeName("_$")
val t = atPos(start) { Ident(pname) }
val bounds = typeBounds()
val param = atPos(t.pos union bounds.pos) { makeSyntheticTypeParam(pname, bounds) }
placeholderTypes = param :: placeholderTypes
t
}
/* ----------- EXPRESSIONS ------------------------------------------------ */
/** EqualsExpr ::= `=' Expr
*/
def equalsExpr(): Tree = {
accept(EQUALS)
expr()
}
def condExpr(): Tree = {
if (in.token == LPAREN) {
in.nextToken()
val r = expr()
accept(RPAREN)
r
} else {
accept(LPAREN)
Literal(true)
}
}
/* hook for IDE, unlike expression can be stubbed
* don't use for any tree that can be inspected in the parser!
*/
def statement(location: Int): Tree = expr(location) // !!! still needed?
/** Expr ::= (Bindings | [`implicit'] Id | `_') `=>' Expr
* | Expr1
* ResultExpr ::= (Bindings | Id `:' CompoundType) `=>' Block
* | Expr1
* Expr1 ::= if `(' Expr `)' {nl} Expr [[semi] else Expr]
* | try (`{' Block `}' | Expr) [catch `{' CaseClauses `}'] [finally Expr]
* | while `(' Expr `)' {nl} Expr
* | do Expr [semi] while `(' Expr `)'
* | for (`(' Enumerators `)' | '{' Enumerators '}') {nl} [yield] Expr
* | throw Expr
* | return [Expr]
* | [SimpleExpr `.'] Id `=' Expr
* | SimpleExpr1 ArgumentExprs `=' Expr
* | PostfixExpr Ascription
* | PostfixExpr match `{' CaseClauses `}'
* Bindings ::= `(' [Binding {`,' Binding}] `)'
* Binding ::= (Id | `_') [`:' Type]
* Ascription ::= `:' CompoundType
* | `:' Annotation {Annotation}
* | `:' `_' `*'
*/
def expr(): Tree = expr(Local)
def expr(location: Int): Tree = {
var savedPlaceholderParams = placeholderParams
placeholderParams = List()
var res = expr0(location)
if (!placeholderParams.isEmpty && !isWildcard(res)) {
res = atPos(res.pos){ Function(placeholderParams.reverse, res) }
placeholderParams = List()
}
placeholderParams = placeholderParams ::: savedPlaceholderParams
res
}
def expr0(location: Int): Tree = in.token match {
case IF =>
atPos(in.skipToken()) {
val cond = condExpr()
newLinesOpt()
val thenp = expr()
val elsep = if (in.token == ELSE) { in.nextToken(); expr() }
else Literal(())
If(cond, thenp, elsep)
}
case TRY =>
atPos(in.skipToken()) {
val body = in.token match {
case LBRACE => inBracesOrUnit(block())
case LPAREN => inParensOrUnit(expr())
case _ => expr()
}
def catchFromExpr() = List(makeCatchFromExpr(expr()))
val catches: List[CaseDef] =
if (in.token != CATCH) Nil
else {
in.nextToken()
if (in.token != LBRACE) catchFromExpr()
else inBracesOrNil {
if (in.token == CASE) caseClauses()
else catchFromExpr()
}
}
val finalizer = in.token match {
case FINALLY => in.nextToken() ; expr()
case _ => EmptyTree
}
Try(body, catches, finalizer)
}
case WHILE =>
val start = in.offset
atPos(in.skipToken()) {
val lname: Name = freshTermName(nme.WHILE_PREFIX)
val cond = condExpr()
newLinesOpt()
val body = expr()
makeWhile(lname, cond, body)
}
case DO =>
val start = in.offset
atPos(in.skipToken()) {
val lname: Name = freshTermName(nme.DO_WHILE_PREFIX)
val body = expr()
if (isStatSep) in.nextToken()
accept(WHILE)
val cond = condExpr()
makeDoWhile(lname, body, cond)
}
case FOR =>
atPos(in.skipToken()) {
val enums =
if (in.token == LBRACE) inBracesOrNil(enumerators())
else inParensOrNil(enumerators())
newLinesOpt()
if (in.token == YIELD) {
in.nextToken()
makeForYield(enums, expr())
} else {
makeFor(enums, expr())
}
}
case RETURN =>
atPos(in.skipToken()) {
Return(if (isExprIntro) expr() else Literal(()))
}
case THROW =>
atPos(in.skipToken()) {
Throw(expr())
}
case IMPLICIT =>
implicitClosure(in.skipToken(), location)
case _ =>
var t = postfixExpr()
if (in.token == EQUALS) {
t match {
case Ident(_) | Select(_, _) | Apply(_, _) =>
t = atPos(t.pos.startOrPoint, in.skipToken()) { makeAssign(t, expr()) }
case _ =>
}
} else if (in.token == COLON) {
t = stripParens(t)
val colonPos = in.skipToken()
if (in.token == USCORE) {
//todo: need to handle case where USCORE is a wildcard in a type
val uscorePos = in.skipToken()
if (isIdent && in.name == nme.STAR) {
in.nextToken()
t = atPos(t.pos.startOrPoint, colonPos) {
Typed(t, atPos(uscorePos) { Ident(tpnme.WILDCARD_STAR) })
}
} else {
syntaxErrorOrIncomplete("`*' expected", true)
}
} else if (in.token == AT) {
t = (t /: annotations(false)) (makeAnnotated)
} else {
t = atPos(t.pos.startOrPoint, colonPos) {
val tpt = typeOrInfixType(location)
if (isWildcard(t))
(placeholderParams: @unchecked) match {
case (vd @ ValDef(mods, name, _, _)) :: rest =>
placeholderParams = treeCopy.ValDef(vd, mods, name, tpt.duplicate, EmptyTree) :: rest
}
// this does not correspond to syntax, but is necessary to
// accept closures. We might restrict closures to be between {...} only.
Typed(t, tpt)
}
}
} else if (in.token == MATCH) {
t = atPos(t.pos.startOrPoint, in.skipToken()) {
/** For debugging pattern matcher transition issues */
if (settings.Ypmatnaive.value)
makeSequencedMatch(stripParens(t), inBracesOrNil(caseClauses()))
else
Match(stripParens(t), inBracesOrNil(caseClauses()))
}
}
// in order to allow anonymous functions as statements (as opposed to expressions) inside
// templates, we have to disambiguate them from self type declarations - bug #1565
// The case still missed is unparenthesized single argument, like "x: Int => x + 1", which
// may be impossible to distinguish from a self-type and so remains an error. (See #1564)
def lhsIsTypedParamList() = t match {
case Parens(xs) if xs forall (_.isInstanceOf[Typed]) => true
case _ => false
}
if (in.token == ARROW && (location != InTemplate || lhsIsTypedParamList)) {
t = atPos(t.pos.startOrPoint, in.skipToken()) {
Function(convertToParams(t), if (location != InBlock) expr() else block())
}
}
stripParens(t)
}
/** Expr ::= implicit Id => Expr
*/
def implicitClosure(start: Int, location: Int): Tree = {
val param0 = convertToParam {
atPos(in.offset) {
var paramexpr: Tree = Ident(ident())
if (in.token == COLON) {
in.nextToken()
paramexpr = Typed(paramexpr, typeOrInfixType(location))
}
paramexpr
}
}
val param = treeCopy.ValDef(param0, param0.mods | Flags.IMPLICIT, param0.name, param0.tpt, param0.rhs)
atPos(start, in.offset) {
accept(ARROW)
Function(List(param), if (location != InBlock) expr() else block())
}
}
/** PostfixExpr ::= InfixExpr [Id [nl]]
* InfixExpr ::= PrefixExpr
* | InfixExpr Id [nl] InfixExpr
*/
def postfixExpr(): Tree = {
val base = opstack
var top = prefixExpr()
while (isIdent) {
top = reduceStack(true, base, top, precedence(in.name), treeInfo.isLeftAssoc(in.name))
val op = in.name
opstack = OpInfo(top, op, in.offset) :: opstack
ident()
newLineOptWhenFollowing(isExprIntroToken)
if (isExprIntro) {
val next = prefixExpr()
if (next == EmptyTree)
return reduceStack(true, base, top, 0, true)
top = next
} else {
val topinfo = opstack.head
opstack = opstack.tail
val od = stripParens(reduceStack(true, base, topinfo.operand, 0, true))
return atPos(od.pos.startOrPoint, topinfo.offset) {
Select(od, topinfo.operator.encode)
}
}
}
reduceStack(true, base, top, 0, true)
}
/** PrefixExpr ::= [`-' | `+' | `~' | `!' | `&'] SimpleExpr
*/
def prefixExpr(): Tree = {
if (isUnaryOp) {
atPos(in.offset) {
val name: Name = "unary_" + ident()
if (in.name == raw.MINUS && isNumericLit) simpleExprRest(atPos(in.offset)(literal(true)), true)
else Select(stripParens(simpleExpr()), name)
}
}
else simpleExpr()
}
def xmlLiteral(): Tree
/* SimpleExpr ::= new (ClassTemplate | TemplateBody)
* | BlockExpr
* | SimpleExpr1 [`_']
* SimpleExpr1 ::= literal
* | xLiteral
* | Path
* | `(' [Exprs] `)'
* | SimpleExpr `.' Id
* | SimpleExpr TypeArgs
* | SimpleExpr1 ArgumentExprs
*/
def simpleExpr(): Tree = {
var canApply = true
val t =
if (isLiteral) atPos(in.offset)(literal(false))
else in.token match {
case XMLSTART =>
xmlLiteral()
case IDENTIFIER | BACKQUOTED_IDENT | THIS | SUPER =>
path(true, false)
case USCORE =>
val start = in.offset
val pname = freshName("x$")
in.nextToken()
val id = atPos(start) (Ident(pname))
val param = atPos(id.pos.focus){ makeSyntheticParam(pname) }
placeholderParams = param :: placeholderParams
id
case LPAREN =>
atPos(in.offset)(makeParens(commaSeparated(expr)))
case LBRACE =>
canApply = false
blockExpr()
case NEW =>
canApply = false
val nstart = in.skipToken()
val npos = r2p(nstart, nstart, in.lastOffset)
val tstart = in.offset
val (parents, argss, self, stats) = template(false)
val cpos = r2p(tstart, tstart, in.lastOffset max tstart)
makeNew(parents, self, stats, argss, npos, cpos)
case _ =>
syntaxErrorOrIncomplete("illegal start of simple expression", true)
errorTermTree
}
simpleExprRest(t, canApply)
}
def simpleExprRest(t: Tree, canApply: Boolean): Tree = {
if (canApply) newLineOptWhenFollowedBy(LBRACE)
in.token match {
case DOT =>
in.nextToken()
simpleExprRest(selector(stripParens(t)), true)
case LBRACKET =>
val t1 = stripParens(t)
t1 match {
case Ident(_) | Select(_, _) =>
val tapp = atPos(t1.pos.startOrPoint, in.offset) {
TypeApply(t1, exprTypeArgs())
}
simpleExprRest(tapp, true)
case _ =>
t1
}
case LPAREN | LBRACE if (canApply) =>
val app = atPos(t.pos.startOrPoint, in.offset) {
// look for anonymous function application like (f _)(x) and
// translate to (f _).apply(x), bug #460
val sel = t match {
case Parens(List(Typed(_, _: Function))) =>
Select(stripParens(t), nme.apply)
case _ =>
stripParens(t)
}
Apply(sel, argumentExprs())
}
simpleExprRest(app, true)
case USCORE =>
atPos(t.pos.startOrPoint, in.skipToken()) {
Typed(stripParens(t), Function(Nil, EmptyTree))
}
case _ =>
t
}
}
/** ArgumentExprs ::= `(' [Exprs] `)'
* | [nl] BlockExpr
*/
def argumentExprs(): List[Tree] = {
def args(): List[Tree] = commaSeparated {
val maybeNamed = isIdent
expr() match {
case a @ Assign(id, rhs) if maybeNamed =>
atPos(a.pos) { AssignOrNamedArg(id, rhs) }
case e => e
}
}
in.token match {
case LBRACE => List(blockExpr())
case LPAREN => inParens(if (in.token == RPAREN) Nil else args())
case _ => Nil
}
}
/** A succession of argument lists.
*/
def multipleArgumentExprs(): List[List[Tree]] = {
if (in.token != LPAREN) Nil
else argumentExprs() :: multipleArgumentExprs()
}
/** BlockExpr ::= `{' (CaseClauses | Block) `}'
*/
def blockExpr(): Tree = atPos(in.offset) {
inBraces {
if (in.token == CASE) Match(EmptyTree, caseClauses())
else block()
}
}
/** Block ::= BlockStatSeq
* @note Return tree does not carry position.
*/
def block(): Tree = makeBlock(blockStatSeq())
/** CaseClauses ::= CaseClause {CaseClause}
* CaseClause ::= case Pattern [Guard] `=>' Block
*/
def caseClauses(): List[CaseDef] = {
val cases = caseSeparated { atPos(in.offset)(makeCaseDef(pattern(), guard(), caseBlock())) }
if (cases.isEmpty) // trigger error if there are no cases
accept(CASE)
cases
}
// IDE HOOK (so we can memoize case blocks) // needed?
def caseBlock(): Tree =
atPos(accept(ARROW))(block())
/** Guard ::= if PostfixExpr
*/
def guard(): Tree =
if (in.token == IF) { in.nextToken(); stripParens(postfixExpr()) }
else EmptyTree
/** Enumerators ::= Generator {semi Enumerator}
* Enumerator ::= Generator
* | Guard
* | val Pattern1 `=' Expr
*/
def enumerators(): List[Enumerator] = {
val newStyle = in.token != VAL
if (!newStyle)
deprecationWarning(in.offset, "for (val x <- ... ) has been deprecated; use for (x <- ... ) instead")
val enums = new ListBuffer[Enumerator]
generator(enums, false)
while (isStatSep) {
in.nextToken()
if (newStyle) {
if (in.token == IF) enums += makeFilter(in.offset, guard())
else generator(enums, true)
} else {
if (in.token == VAL) generator(enums, true)
else enums += makeFilter(in.offset, expr())
}
}
enums.toList
}
/** Generator ::= Pattern1 (`<-' | '=') Expr [Guard]
*/
def generator(enums: ListBuffer[Enumerator], eqOK: Boolean) {
val start = in.offset
if (in.token == VAL) in.nextToken()
val pat = noSeq.pattern1()
val point = in.offset
val tok = in.token
if (tok == EQUALS && eqOK) in.nextToken()
else accept(LARROW)
val rhs = expr()
enums += makeGenerator(r2p(start, point, in.lastOffset max start), pat, tok == EQUALS, rhs)
// why max above? IDE stress tests have shown that lastOffset could be less than start,
// I guess this happens if instead if a for-expression we sit on a closing paren.
while (in.token == IF) enums += makeFilter(in.offset, guard())
}
def makeFilter(start: Int, tree: Tree) = Filter(r2p(start, tree.pos.point, tree.pos.endOrPoint), tree)
/* -------- PATTERNS ------------------------------------------- */
/** Methods which implicitly propagate whether the initial call took
* place in a context where sequences are allowed. Formerly, this
* was threaded through methods as boolean seqOK.
*/
trait SeqContextSensitive extends PatternContextSensitive {
/** Returns Some(tree) if it finds a star and prematurely ends parsing.
* This is an artifact of old implementation which has proven difficult
* to cleanly extract.
*/
def interceptStarPattern(top: Tree): Option[Tree]
def functionArgType(): Tree = argType()
def argType(): Tree = {
val start = in.offset
in.token match {
case USCORE =>
in.nextToken()
if (in.token == SUBTYPE || in.token == SUPERTYPE) wildcardType(start)
else atPos(start) { Bind(tpnme.WILDCARD, EmptyTree) }
case IDENTIFIER if treeInfo.isVariableName(in.name) =>
atPos(start) { Bind(identForType(), EmptyTree) }
case _ =>
typ()
}
}
/** Patterns ::= Pattern { `,' Pattern }
* SeqPatterns ::= SeqPattern { `,' SeqPattern }
*/
def patterns(): List[Tree] = commaSeparated(pattern())
/** Pattern ::= Pattern1 { `|' Pattern1 }
* SeqPattern ::= SeqPattern1 { `|' SeqPattern1 }
*/
def pattern(): Tree = {
val start = in.offset
def loop(): List[Tree] = pattern1() :: {
if (isRawBar) { in.nextToken() ; loop() }
else Nil
}
loop() match {
case pat :: Nil => pat
case xs => atPos(start)(makeAlternative(xs))
}
}
/** Pattern1 ::= varid `:' TypePat
* | `_' `:' TypePat
* | Pattern2
* SeqPattern1 ::= varid `:' TypePat
* | `_' `:' TypePat
* | [SeqPattern2]
*/
def pattern1(): Tree = pattern2() match {
case p @ Ident(name) if treeInfo.isVarPattern(p) && in.token == COLON =>
atPos(p.pos.startOrPoint, in.skipToken()) { Typed(p, compoundType()) }
case p =>
p
}
/* Pattern2 ::= varid [ @ Pattern3 ]
* | Pattern3
* SeqPattern2 ::= varid [ @ SeqPattern3 ]
* | SeqPattern3
*/
def pattern2(): Tree = {
val p = pattern3()
if (in.token != AT) p
else p match {
case Ident(nme.WILDCARD) =>
in.nextToken()
pattern3()
case Ident(name) if treeInfo.isVarPattern(p) =>
in.nextToken()
atPos(p.pos.startOrPoint) { Bind(name, pattern3()) }
case _ => p
}
}
/* Pattern3 ::= SimplePattern
* | SimplePattern {Id [nl] SimplePattern}
* SeqPattern3 ::= SeqSimplePattern [ '*' | '?' | '+' ]
* | SeqSimplePattern {Id [nl] SeqSimplePattern}
*/
def pattern3(): Tree = {
val base = opstack
var top = simplePattern()
interceptStarPattern(top) foreach { x => return x }
while (isIdent && in.name != raw.BAR) {
top = reduceStack(
false, base, top, precedence(in.name), treeInfo.isLeftAssoc(in.name))
val op = in.name
opstack = OpInfo(top, op, in.offset) :: opstack
ident()
top = simplePattern()
}
stripParens(reduceStack(false, base, top, 0, true))
}
/** SimplePattern ::= varid
* | `_'
* | literal
* | XmlPattern
* | StableId [TypeArgs] [`(' [SeqPatterns] `)']
* | `(' [Patterns] `)'
* SimpleSeqPattern ::= varid
* | `_'
* | literal
* | XmlPattern
* | `<' xLiteralPattern
* | StableId [TypeArgs] [`(' [SeqPatterns] `)']
* | `(' [SeqPatterns] `)'
*
* XXX: Hook for IDE
*/
def simplePattern(): Tree = {
val start = in.offset
in.token match {
case IDENTIFIER | BACKQUOTED_IDENT | THIS =>
var t = stableId()
in.token match {
case INTLIT | LONGLIT | FLOATLIT | DOUBLELIT =>
t match {
case Ident(nme.MINUS) =>
return atPos(start) { literal(true) }
case _ =>
}
case _ =>
}
val typeAppliedTree = in.token match {
case LBRACKET => atPos(start, in.offset)(TypeApply(convertToTypeId(t), typeArgs()))
case _ => t
}
in.token match {
case LPAREN => atPos(start, in.offset)(Apply(typeAppliedTree, argumentPatterns()))
case _ => typeAppliedTree
}
case USCORE =>
in.nextToken()
atPos(start, start) { Ident(nme.WILDCARD) }
case CHARLIT | INTLIT | LONGLIT | FLOATLIT | DOUBLELIT |
STRINGLIT | SYMBOLLIT | TRUE | FALSE | NULL =>
atPos(start) { literal(false) }
case LPAREN =>
atPos(start)(makeParens(noSeq.patterns()))
case XMLSTART =>
xmlLiteralPattern()
case _ =>
syntaxErrorOrIncomplete("illegal start of simple pattern", true)
errorPatternTree
}
}
}
/** The implementation of the context sensitive methods for parsing
* outside of patterns.
*/
object outPattern extends PatternContextSensitive {
def argType(): Tree = typ()
def functionArgType(): Tree = paramType(useStartAsPosition = true)
}
/** The implementation for parsing inside of patterns at points where
* sequences are allowed.
*/
object seqOK extends SeqContextSensitive {
// See ticket #3189 for the motivation for the null check.
// TODO: dredge out the remnants of regexp patterns.
// ... and now this is back the way it was because it caused #3480.
def interceptStarPattern(top: Tree): Option[Tree] =
if (isRawStar) Some(atPos(top.pos.startOrPoint, in.skipToken())(Star(stripParens(top))))
else None
}
/** The implementation for parsing inside of patterns at points where
* sequences are disallowed.
*/
object noSeq extends SeqContextSensitive {
def interceptStarPattern(top: Tree) = None
}
/** These are default entry points into the pattern context sensitive methods:
* they are all initiated from non-pattern context.
*/
def typ(): Tree = outPattern.typ()
def startInfixType() = outPattern.infixType(InfixMode.FirstOp)
def startAnnotType() = outPattern.annotType()
def exprTypeArgs() = outPattern.typeArgs()
def exprSimpleType() = outPattern.simpleType()
/** Default entry points into some pattern contexts.
*/
def pattern(): Tree = noSeq.pattern()
def patterns(): List[Tree] = noSeq.patterns()
def seqPatterns(): List[Tree] = seqOK.patterns() // Also called from xml parser
def argumentPatterns(): List[Tree] = inParens {
if (in.token == RPAREN) Nil
else seqPatterns()
}
def xmlLiteralPattern(): Tree
/* -------- MODIFIERS and ANNOTATIONS ------------------------------------------- */
/** Drop `private' modifier when followed by a qualifier.
* Contract `abstract' and `override' to ABSOVERRIDE
*/
private def normalize(mods: Modifiers): Modifiers =
if (mods.isPrivate && mods.hasAccessBoundary)
normalize(mods &~ Flags.PRIVATE)
else if (mods hasAllFlags (Flags.ABSTRACT | Flags.OVERRIDE))
normalize(mods &~ (Flags.ABSTRACT | Flags.OVERRIDE) | Flags.ABSOVERRIDE)
else
mods
private def addMod(mods: Modifiers, mod: Long, pos: Position): Modifiers = {
if (mods hasFlag mod) syntaxError(in.offset, "repeated modifier", false)
in.nextToken()
(mods | mod) withPosition (mod, pos)
}
private def tokenRange(token: TokenData) = r2p(token.offset, token.offset, token.offset + token.name.length - 1)
/** AccessQualifier ::= "[" (Id | this) "]"
*/
def accessQualifierOpt(mods: Modifiers): Modifiers = {
var result = mods
if (in.token == LBRACKET) {
in.nextToken()
if (mods.hasAccessBoundary)
syntaxError("duplicate private/protected qualifier", false)
result = if (in.token == THIS) { in.nextToken(); mods | Flags.LOCAL }
else Modifiers(mods.flags, identForType())
accept(RBRACKET)
}
result
}
private val flagTokens: Map[Int, Long] = Map(
ABSTRACT -> Flags.ABSTRACT,
FINAL -> Flags.FINAL,
IMPLICIT -> Flags.IMPLICIT,
LAZY -> Flags.LAZY,
OVERRIDE -> Flags.OVERRIDE,
PRIVATE -> Flags.PRIVATE,
PROTECTED -> Flags.PROTECTED,
SEALED -> Flags.SEALED
)
/** AccessModifier ::= (private | protected) [AccessQualifier]
*/
def accessModifierOpt(): Modifiers = normalize {
in.token match {
case m @ (PRIVATE | PROTECTED) => in.nextToken() ; accessQualifierOpt(Modifiers(flagTokens(m)))
case _ => NoMods
}
}
/** Modifiers ::= {Modifier}
* Modifier ::= LocalModifier
* | AccessModifier
* | override
*/
def modifiers(): Modifiers = normalize {
def loop(mods: Modifiers): Modifiers = in.token match {
case PRIVATE | PROTECTED =>
loop(accessQualifierOpt(addMod(mods, flagTokens(in.token), tokenRange(in))))
case ABSTRACT | FINAL | SEALED | OVERRIDE | IMPLICIT | LAZY =>
loop(addMod(mods, flagTokens(in.token), tokenRange(in)))
case NEWLINE =>
in.nextToken()
loop(mods)
case _ =>
mods
}
loop(NoMods)
}
/** LocalModifiers ::= {LocalModifier}
* LocalModifier ::= abstract | final | sealed | implicit | lazy
*/
def localModifiers(): Modifiers = {
def loop(mods: Modifiers): Modifiers =
if (isLocalModifier) loop(addMod(mods, flagTokens(in.token), tokenRange(in)))
else mods
loop(NoMods)
}
/** Annotations ::= {`@' SimpleType {ArgumentExprs}}
* ConsrAnnotations ::= {`@' SimpleType ArgumentExprs}
*/
def annotations(skipNewLines: Boolean): List[Tree] = readAnnots {
val t = annotationExpr()
if (skipNewLines) newLineOpt()
t
}
def constructorAnnotations(): List[Tree] = readAnnots {
atPos(in.offset)(New(exprSimpleType(), List(argumentExprs())))
}
def annotationExpr(): Tree = atPos(in.offset) {
val t = exprSimpleType()
if (in.token == LPAREN) New(t, multipleArgumentExprs())
else New(t, List(Nil))
}
/* -------- PARAMETERS ------------------------------------------- */
/** ParamClauses ::= {ParamClause} [[nl] `(' implicit Params `)']
* ParamClause ::= [nl] `(' [Params] ')'
* Params ::= Param {`,' Param}
* Param ::= {Annotation} Id [`:' ParamType] [`=' Expr]
* ClassParamClauses ::= {ClassParamClause} [[nl] `(' implicit ClassParams `)']
* ClassParamClause ::= [nl] `(' [ClassParams] ')'
* ClassParams ::= ClassParam {`,' ClassParam}
* ClassParam ::= {Annotation} [{Modifier} (`val' | `var')] Id [`:' ParamType] [`=' Expr]
*/
def paramClauses(owner: Name, contextBounds: List[Tree], ofCaseClass: Boolean): List[List[ValDef]] = {
var implicitmod = 0
var caseParam = ofCaseClass
def param(): ValDef = {
val start = in.offset
val annots = annotations(false)
var mods = Modifiers(Flags.PARAM)
if (owner.isTypeName) {
mods = modifiers() | Flags.PARAMACCESSOR
if (mods.isLazy) syntaxError("lazy modifier not allowed here. Use call-by-name parameters instead", false)
in.token match {
case v @ (VAL | VAR) =>
mods = mods withPosition (in.token, tokenRange(in))
if (v == VAR) mods |= Flags.MUTABLE
in.nextToken()
case _ =>
if (mods.flags != Flags.PARAMACCESSOR) accept(VAL)
if (!caseParam) mods |= Flags.PRIVATE | Flags.LOCAL
}
if (caseParam) mods |= Flags.CASEACCESSOR
}
val nameOffset = in.offset
val name = ident()
var bynamemod = 0
val tpt =
if (settings.YmethodInfer.value && !owner.isTypeName && in.token != COLON) {
TypeTree()
} else { // XX-METHOD-INFER
accept(COLON)
if (in.token == ARROW) {
if (owner.isTypeName && !mods.hasLocalFlag)
syntaxError(
in.offset,
(if (mods.isMutable) "`var'" else "`val'") +
" parameters may not be call-by-name", false)
else if (implicitmod != 0)
syntaxError(
in.offset,
"implicit parameters may not be call-by-name", false)
else bynamemod = Flags.BYNAMEPARAM
}
paramType()
}
val default =
if (in.token == EQUALS) {
in.nextToken()
mods |= Flags.DEFAULTPARAM
expr()
} else EmptyTree
atPos(start, if (name == nme.ERROR) start else nameOffset) {
ValDef((mods | implicitmod | bynamemod) withAnnotations annots, name, tpt, default)
}
}
def paramClause(): List[ValDef] = {
if (in.token == RPAREN)
return Nil
if (in.token == IMPLICIT) {
if (contextBounds.nonEmpty)
syntaxError("cannot have both implicit parameters and context bounds `: ...' or view bounds `<% ...' on type parameters", false)
in.nextToken()
implicitmod = Flags.IMPLICIT
}
commaSeparated(param())
}
val vds = new ListBuffer[List[ValDef]]
val start = in.offset
newLineOptWhenFollowedBy(LPAREN)
if (ofCaseClass && in.token != LPAREN)
deprecationWarning(in.lastOffset, "case classes without a parameter list have been deprecated;\n"+
"use either case objects or case classes with `()' as parameter list.")
while (implicitmod == 0 && in.token == LPAREN) {
in.nextToken()
vds += paramClause()
accept(RPAREN)
caseParam = false
newLineOptWhenFollowedBy(LPAREN)
}
val result = vds.toList
if (owner == nme.CONSTRUCTOR && (result.isEmpty || (result.head take 1 exists (_.mods.isImplicit)))) {
in.token match {
case LBRACKET => syntaxError(in.offset, "no type parameters allowed here", false)
case EOF => incompleteInputError("auxiliary constructor needs non-implicit parameter list")
case _ => syntaxError(start, "auxiliary constructor needs non-implicit parameter list", false)
}
}
addEvidenceParams(owner, result, contextBounds)
}
/** ParamType ::= Type | `=>' Type | Type `*'
*/
def paramType(): Tree = paramType(useStartAsPosition = false)
def paramType(useStartAsPosition: Boolean): Tree = {
val start = in.offset
in.token match {
case ARROW =>
in.nextToken()
atPos(start)(byNameApplication(typ()))
case _ =>
val t = typ()
if (isRawStar) {
in.nextToken()
if (useStartAsPosition) atPos(start)(repeatedApplication(t))
else atPos(t.pos.startOrPoint, t.pos.point)(repeatedApplication(t))
}
else t
}
}
/** TypeParamClauseOpt ::= [TypeParamClause]
* TypeParamClause ::= `[' VariantTypeParam {`,' VariantTypeParam} `]']
* VariantTypeParam ::= {Annotation} [`+' | `-'] TypeParam
* FunTypeParamClauseOpt ::= [FunTypeParamClause]
* FunTypeParamClause ::= `[' TypeParam {`,' TypeParam} `]']
* TypeParam ::= Id TypeParamClauseOpt TypeBounds {<% Type} {":" Type}
*/
def typeParamClauseOpt(owner: Name, contextBoundBuf: ListBuffer[Tree]): List[TypeDef] = {
def typeParam(ms: Modifiers): TypeDef = {
var mods = ms | Flags.PARAM
val start = in.offset
if (owner.isTypeName && isIdent) {
if (in.name == raw.PLUS) {
in.nextToken()
mods |= Flags.COVARIANT
} else if (in.name == raw.MINUS) {
in.nextToken()
mods |= Flags.CONTRAVARIANT
}
}
val nameOffset = in.offset
// TODO AM: freshName(o2p(in.skipToken()), "_$$"), will need to update test suite
val pname: TypeName = wildcardOrIdent().toTypeName
val param = atPos(start, nameOffset) {
val tparams = typeParamClauseOpt(pname, null) // @M TODO null --> no higher-order context bounds for now
TypeDef(mods, pname, tparams, typeBounds())
}
if (contextBoundBuf ne null) {
while (in.token == VIEWBOUND) {
contextBoundBuf += atPos(in.skipToken()) {
makeFunctionTypeTree(List(Ident(pname)), typ())
}
}
while (in.token == COLON) {
contextBoundBuf += atPos(in.skipToken()) {
AppliedTypeTree(typ(), List(Ident(pname)))
}
}
}
param
}
newLineOptWhenFollowedBy(LBRACKET)
if (in.token == LBRACKET) inBrackets(commaSeparated(typeParam(NoMods withAnnotations annotations(true))))
else Nil
}
/** TypeBounds ::= [`>:' Type] [`<:' Type]
*/
def typeBounds(): TypeBoundsTree = {
val t = TypeBoundsTree(
bound(SUPERTYPE, tpnme.Nothing),
bound(SUBTYPE, tpnme.Any)
)
t setPos wrappingPos(List(t.hi, t.lo))
}
def bound(tok: Int, default: TypeName): Tree =
if (in.token == tok) { in.nextToken(); typ() }
else atPos(o2p(in.lastOffset)) { rootScalaDot(default) }
/* -------- DEFS ------------------------------------------- */
/** Import ::= import ImportExpr {`,' ImportExpr}
*/
def importClause(): List[Tree] = {
val offset = accept(IMPORT)
commaSeparated(importExpr()) match {
case Nil => Nil
case t :: rest =>
// The first import should start at the position of the keyword.
t.setPos(t.pos.withStart(offset))
t :: rest
}
}
/** ImportExpr ::= StableId `.' (Id | `_' | ImportSelectors)
*/
def importExpr(): Tree = {
val start = in.offset
def thisDotted(name: TypeName) = {
in.nextToken()
val t = atPos(start)(This(name))
accept(DOT)
val result = selector(t)
accept(DOT)
result
}
/** Walks down import foo.bar.baz.{ ... } until it ends at a
* an underscore, a left brace, or an undotted identifier.
*/
def loop(expr: Tree): Tree = {
expr setPos expr.pos.makeTransparent
val selectors: List[ImportSelector] = in.token match {
case USCORE => List(importSelector()) // import foo.bar._;
case LBRACE => importSelectors() // import foo.bar.{ x, y, z }
case _ =>
val nameOffset = in.offset
val name = ident()
if (in.token == DOT) {
// import foo.bar.ident.<unknown> and so create a select node and recurse.
val t = atPos(start, if (name == nme.ERROR) in.offset else nameOffset)(Select(expr, name))
in.nextToken()
return loop(t)
}
// import foo.bar.Baz;
else List(makeImportSelector(name, nameOffset))
}
// reaching here means we're done walking.
atPos(start)(Import(expr, selectors))
}
loop(in.token match {
case THIS => thisDotted(tpnme.EMPTY)
case _ =>
val id = atPos(start)(Ident(ident()))
accept(DOT)
if (in.token == THIS) thisDotted(id.name.toTypeName)
else id
})
}
/** ImportSelectors ::= `{' {ImportSelector `,'} (ImportSelector | `_') `}'
*/
def importSelectors(): List[ImportSelector] = {
val selectors = inBracesOrNil(commaSeparated(importSelector()))
selectors.init foreach {
case ImportSelector(nme.WILDCARD, pos, _, _) => syntaxError(pos, "Wildcard import must be in last position")
case _ => ()
}
selectors
}
def wildcardOrIdent() = {
if (in.token == USCORE) { in.nextToken() ; nme.WILDCARD }
else ident()
}
/** ImportSelector ::= Id [`=>' Id | `=>' `_']
*/
def importSelector(): ImportSelector = {
val start = in.offset
val name = wildcardOrIdent()
var renameOffset = -1
val rename = in.token match {
case ARROW =>
in.nextToken()
renameOffset = in.offset
wildcardOrIdent()
case _ if name == nme.WILDCARD => null
case _ =>
renameOffset = start
name
}
ImportSelector(name, start, rename, renameOffset)
}
/** Def ::= val PatDef
* | var PatDef
* | def FunDef
* | type [nl] TypeDef
* | TmplDef
* Dcl ::= val PatDcl
* | var PatDcl
* | def FunDcl
* | type [nl] TypeDcl
*/
def defOrDcl(pos: Int, mods: Modifiers): List[Tree] = {
if (mods.isLazy && in.token != VAL)
syntaxError("lazy not allowed here. Only vals can be lazy", false)
in.token match {
case VAL =>
patDefOrDcl(pos, mods withPosition(VAL, tokenRange(in)))
case VAR =>
patDefOrDcl(pos, (mods | Flags.MUTABLE) withPosition (VAR, tokenRange(in)))
case DEF =>
List(funDefOrDcl(pos, mods withPosition(DEF, tokenRange(in))))
case TYPE =>
List(typeDefOrDcl(pos, mods withPosition(TYPE, tokenRange(in))))
case _ =>
List(tmplDef(pos, mods))
}
}
private def caseAwareTokenOffset = if (in.token == CASECLASS || in.token == CASEOBJECT) in.prev.offset else in.offset
def nonLocalDefOrDcl : List[Tree] = {
val annots = annotations(true)
defOrDcl(caseAwareTokenOffset, modifiers() withAnnotations annots)
}
/** PatDef ::= Pattern2 {`,' Pattern2} [`:' Type] `=' Expr
* ValDcl ::= Id {`,' Id} `:' Type
* VarDef ::= PatDef | Id {`,' Id} `:' Type `=' `_'
*/
def patDefOrDcl(pos : Int, mods: Modifiers): List[Tree] = {
var newmods = mods
in.nextToken()
val lhs = commaSeparated(stripParens(noSeq.pattern2()))
val tp = typedOpt()
val rhs =
if (tp.isEmpty || in.token == EQUALS) {
accept(EQUALS)
if (!tp.isEmpty && newmods.isMutable &&
(lhs.toList forall (_.isInstanceOf[Ident])) && in.token == USCORE) {
in.nextToken()
newmods = newmods | Flags.DEFAULTINIT
EmptyTree
} else {
expr()
}
} else {
newmods = newmods | Flags.DEFERRED
EmptyTree
}
def mkDefs(p: Tree, tp: Tree, rhs: Tree): List[Tree] = {
//Console.println("DEBUG: p = "+p.toString()); // DEBUG
val trees =
makePatDef(newmods,
if (tp.isEmpty) p
else Typed(p, tp) setPos (p.pos union tp.pos),
rhs)
if (newmods.isDeferred) {
trees match {
case List(ValDef(_, _, _, EmptyTree)) =>
if (mods.isLazy) syntaxError(p.pos, "lazy values may not be abstract", false)
case _ => syntaxError(p.pos, "pattern definition may not be abstract", false)
}
}
trees
}
val trees = (lhs.toList.init flatMap (mkDefs(_, tp.duplicate, rhs.duplicate))) ::: mkDefs(lhs.last, tp, rhs)
val hd = trees.head
hd setPos hd.pos.withStart(pos)
ensureNonOverlapping(hd, trees.tail)
trees
}
/** VarDef ::= PatDef
* | Id {`,' Id} `:' Type `=' `_'
* VarDcl ::= Id {`,' Id} `:' Type
def varDefOrDcl(mods: Modifiers): List[Tree] = {
var newmods = mods | Flags.MUTABLE
val lhs = new ListBuffer[(Int, Name)]
do {
in.nextToken()
lhs += (in.offset, ident())
} while (in.token == COMMA)
val tp = typedOpt()
val rhs = if (tp.isEmpty || in.token == EQUALS) {
accept(EQUALS)
if (!tp.isEmpty && in.token == USCORE) {
in.nextToken()
EmptyTree
} else {
expr()
}
} else {
newmods = newmods | Flags.DEFERRED
EmptyTree
}
}
*/
/** FunDef ::= FunSig `:' Type `=' Expr
* | FunSig [nl] `{' Block `}'
* | this ParamClause ParamClauses (`=' ConstrExpr | [nl] ConstrBlock)
* FunDcl ::= FunSig [`:' Type]
* FunSig ::= id [FunTypeParamClause] ParamClauses
*/
def funDefOrDcl(start : Int, mods: Modifiers): Tree = {
in.nextToken
if (in.token == THIS) {
atPos(start, in.skipToken()) {
val vparamss = paramClauses(nme.CONSTRUCTOR, classContextBounds map (_.duplicate), false)
newLineOptWhenFollowedBy(LBRACE)
val rhs = in.token match {
case LBRACE => atPos(in.offset) { constrBlock(vparamss) }
case _ => accept(EQUALS) ; atPos(in.offset) { constrExpr(vparamss) }
}
DefDef(mods, nme.CONSTRUCTOR, List(), vparamss, TypeTree(), rhs)
}
}
else {
var newmods = mods
val nameOffset = in.offset
val name = ident()
val result = atPos(start, if (name == nme.ERROR) start else nameOffset) {
// contextBoundBuf is for context bounded type parameters of the form
// [T : B] or [T : => B]; it contains the equivalent implicit parameter type,
// i.e. (B[T] or T => B)
val contextBoundBuf = new ListBuffer[Tree]
val tparams = typeParamClauseOpt(name, contextBoundBuf)
val vparamss = paramClauses(name, contextBoundBuf.toList, false)
newLineOptWhenFollowedBy(LBRACE)
var restype = fromWithinReturnType(typedOpt())
val rhs =
if (isStatSep || in.token == RBRACE) {
if (restype.isEmpty) restype = scalaUnitConstr
newmods |= Flags.DEFERRED
EmptyTree
} else if (restype.isEmpty && in.token == LBRACE) {
restype = scalaUnitConstr
blockExpr()
} else {
equalsExpr()
}
DefDef(newmods, name, tparams, vparamss, restype, rhs)
}
signalParseProgress(result.pos)
result
}
}
/** ConstrExpr ::= SelfInvocation
* | ConstrBlock
*/
def constrExpr(vparamss: List[List[ValDef]]): Tree =
if (in.token == LBRACE) constrBlock(vparamss)
else Block(List(selfInvocation(vparamss)), Literal(()))
/** SelfInvocation ::= this ArgumentExprs {ArgumentExprs}
*/
def selfInvocation(vparamss: List[List[ValDef]]): Tree =
atPos(accept(THIS)) {
newLineOptWhenFollowedBy(LBRACE)
var t = Apply(Ident(nme.CONSTRUCTOR), argumentExprs())
newLineOptWhenFollowedBy(LBRACE)
while (in.token == LPAREN || in.token == LBRACE) {
t = Apply(t, argumentExprs())
newLineOptWhenFollowedBy(LBRACE)
}
if (classContextBounds.isEmpty) t
else Apply(t, vparamss.last.map(vp => Ident(vp.name)))
}
/** ConstrBlock ::= `{' SelfInvocation {semi BlockStat} `}'
*/
def constrBlock(vparamss: List[List[ValDef]]): Tree =
atPos(in.skipToken()) {
val stats = selfInvocation(vparamss) :: {
if (isStatSep) { in.nextToken(); blockStatSeq() }
else Nil
}
accept(RBRACE)
Block(stats, Literal(()))
}
/** TypeDef ::= type Id [TypeParamClause] `=' Type
* TypeDcl ::= type Id [TypeParamClause] TypeBounds
*/
def typeDefOrDcl(start: Int, mods: Modifiers): Tree = {
in.nextToken()
newLinesOpt()
atPos(start, in.offset) {
val name = identForType()
// @M! a type alias as well as an abstract type may declare type parameters
val tparams = typeParamClauseOpt(name, null)
in.token match {
case EQUALS =>
in.nextToken()
TypeDef(mods, name, tparams, typ())
case SUPERTYPE | SUBTYPE | SEMI | NEWLINE | NEWLINES | COMMA | RBRACE =>
TypeDef(mods | Flags.DEFERRED, name, tparams, typeBounds())
case _ =>
syntaxErrorOrIncomplete("`=', `>:', or `<:' expected", true)
EmptyTree
}
}
}
/** Hook for IDE, for top-level classes/objects */
def topLevelTmplDef: Tree = {
val annots = annotations(true)
val pos = caseAwareTokenOffset
val mods = modifiers() withAnnotations annots
tmplDef(pos, mods)
}
/** TmplDef ::= [case] class ClassDef
* | [case] object ObjectDef
* | [override] trait TraitDef
*/
def tmplDef(pos: Int, mods: Modifiers): Tree = {
if (mods.isLazy) syntaxError("classes cannot be lazy", false)
in.token match {
case TRAIT =>
classDef(pos, (mods | Flags.TRAIT | Flags.ABSTRACT) withPosition (Flags.TRAIT, tokenRange(in)))
case CLASS =>
classDef(pos, mods)
case CASECLASS =>
classDef(pos, (mods | Flags.CASE) withPosition (Flags.CASE, tokenRange(in.prev /*scanner skips on 'case' to 'class', thus take prev*/)))
case OBJECT =>
objectDef(pos, mods)
case CASEOBJECT =>
objectDef(pos, (mods | Flags.CASE) withPosition (Flags.CASE, tokenRange(in.prev /*scanner skips on 'case' to 'object', thus take prev*/)))
case _ =>
syntaxErrorOrIncomplete("expected start of definition", true)
EmptyTree
}
}
/** ClassDef ::= Id [TypeParamClause] {Annotation}
[AccessModifier] ClassParamClauses RequiresTypeOpt ClassTemplateOpt
* TraitDef ::= Id [TypeParamClause] RequiresTypeOpt TraitTemplateOpt
*/
def classDef(start: Int, mods: Modifiers): ClassDef = {
in.nextToken
val nameOffset = in.offset
val name = identForType()
def isTrait = mods.hasTraitFlag
atPos(start, if (name == tpnme.ERROR) start else nameOffset) {
savingClassContextBounds {
val contextBoundBuf = new ListBuffer[Tree]
val tparams = typeParamClauseOpt(name, contextBoundBuf)
classContextBounds = contextBoundBuf.toList
val tstart = in.offset :: classContextBounds.map(_.pos.startOrPoint) min;
if (!classContextBounds.isEmpty && isTrait) {
syntaxError("traits cannot have type parameters with context bounds `: ...' nor view bounds `<% ...'", false)
classContextBounds = List()
}
val constrAnnots = constructorAnnotations()
val (constrMods, vparamss) =
if (isTrait) (Modifiers(Flags.TRAIT), List())
else (accessModifierOpt(), paramClauses(name, classContextBounds, mods.isCase))
var mods1 = mods
if (isTrait) {
if (settings.YvirtClasses && in.token == SUBTYPE) mods1 |= Flags.DEFERRED
} else if (in.token == SUBTYPE) {
syntaxError("classes are not allowed to be virtual", false)
}
val template = templateOpt(mods1, name, constrMods withAnnotations constrAnnots, vparamss, tstart)
if (isInterface(mods1, template.body)) mods1 |= Flags.INTERFACE
val result = ClassDef(mods1, name, tparams, template)
// Context bounds generate implicit parameters (part of the template) with types
// from tparams: we need to ensure these don't overlap
if (!classContextBounds.isEmpty)
ensureNonOverlapping(template, tparams)
result
}
}
}
/** ObjectDef ::= Id ClassTemplateOpt
*/
def objectDef(start: Int, mods: Modifiers): ModuleDef = {
in.nextToken
val nameOffset = in.offset
val name = ident()
val tstart = in.offset
atPos(start, if (name == nme.ERROR) start else nameOffset) {
val mods1 = if (in.token == SUBTYPE) mods | Flags.DEFERRED else mods
val template = templateOpt(mods1, name, NoMods, Nil, tstart)
ModuleDef(mods1, name, template)
}
}
/** ClassParents ::= AnnotType {`(' [Exprs] `)'} {with AnnotType}
* TraitParents ::= AnnotType {with AnnotType}
*/
def templateParents(isTrait: Boolean): (List[Tree], List[List[Tree]]) = {
val parents = new ListBuffer[Tree] += startAnnotType()
val argss =
if (in.token == LPAREN && !isTrait) multipleArgumentExprs()
else List(Nil)
while (in.token == WITH) {
in.nextToken()
parents += startAnnotType()
}
(parents.toList, argss)
}
/** ClassTemplate ::= [EarlyDefs with] ClassParents [TemplateBody]
* TraitTemplate ::= [EarlyDefs with] TraitParents [TemplateBody]
* EarlyDefs ::= `{' [EarlyDef {semi EarlyDef}] `}'
* EarlyDef ::= Annotations Modifiers PatDef
*/
def template(isTrait: Boolean): (List[Tree], List[List[Tree]], ValDef, List[Tree]) = {
newLineOptWhenFollowedBy(LBRACE)
if (in.token == LBRACE) {
// @S: pre template body cannot stub like post body can!
val (self, body) = templateBody(true)
if (in.token == WITH && self.isEmpty) {
val earlyDefs: List[Tree] = body flatMap {
case vdef @ ValDef(mods, name, tpt, rhs) if !mods.isDeferred =>
List(treeCopy.ValDef(vdef, mods | Flags.PRESUPER, name, tpt, rhs))
case tdef @ TypeDef(mods, name, tparams, rhs) =>
List(treeCopy.TypeDef(tdef, mods | Flags.PRESUPER, name, tparams, rhs))
case stat if !stat.isEmpty =>
syntaxError(stat.pos, "only type definitions and concrete field definitions allowed in early object initialization section", false)
List()
case _ => List()
}
in.nextToken()
val (parents, argss) = templateParents(isTrait)
val (self1, body1) = templateBodyOpt(isTrait)
(parents, argss, self1, earlyDefs ::: body1)
} else {
(List(), List(List()), self, body)
}
} else {
val (parents, argss) = templateParents(isTrait)
val (self, body) = templateBodyOpt(isTrait)
(parents, argss, self, body)
}
}
def isInterface(mods: Modifiers, body: List[Tree]): Boolean =
mods.hasTraitFlag && (body forall treeInfo.isInterfaceMember)
/** ClassTemplateOpt ::= 'extends' ClassTemplate | [['extends'] TemplateBody]
* TraitTemplateOpt ::= TraitExtends TraitTemplate | [['extends'] TemplateBody] | '<:' TemplateBody
* TraitExtends ::= 'extends' | `<:'
*/
def templateOpt(mods: Modifiers, name: Name, constrMods: Modifiers, vparamss: List[List[ValDef]], tstart: Int): Template = {
val (parents0, argss, self, body) = (
if (in.token == EXTENDS || in.token == SUBTYPE && mods.hasTraitFlag) {
in.nextToken()
template(mods.hasTraitFlag)
}
else {
newLineOptWhenFollowedBy(LBRACE)
val (self, body) = templateBodyOpt(false)
(List(), List(List()), self, body)
}
)
val tstart0 = if (body.isEmpty && in.lastOffset < tstart) in.lastOffset else tstart
atPos(tstart0) {
if (isAnyValType(name)) {
val parent = if (name == tpnme.AnyVal) tpnme.Any else tpnme.AnyVal
Template(List(scalaDot(parent)), self, body)
}
else {
val parents = (
if (!isInterface(mods, body) && !isScalaArray(name)) parents0 :+ scalaScalaObjectConstr
else if (parents0.isEmpty) List(scalaAnyRefConstr)
else parents0
) ++ (
if (mods.isCase) List(productConstr, serializableConstr)
else Nil
)
Template(parents, self, constrMods, vparamss, argss, body, o2p(tstart))
}
}
}
/* -------- TEMPLATES ------------------------------------------- */
/** TemplateBody ::= [nl] `{' TemplateStatSeq `}'
* @param isPre specifies whether in early initializer (true) or not (false)
*/
def templateBody(isPre: Boolean) = inBraces(templateStatSeq(isPre)) match {
case (self, Nil) => (self, List(EmptyTree))
case result => result
}
def templateBodyOpt(traitParentSeen: Boolean): (ValDef, List[Tree]) = {
newLineOptWhenFollowedBy(LBRACE)
if (in.token == LBRACE) {
templateBody(false)
} else {
if (in.token == LPAREN)
syntaxError((if (traitParentSeen) "parents of traits" else "traits or objects")+
" may not have parameters", true)
(emptyValDef, List())
}
}
/** Refinement ::= [nl] `{' RefineStat {semi RefineStat} `}'
*/
def refinement(): List[Tree] = inBraces(refineStatSeq())
/* -------- 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))
}
/*
pkg match {
case id @ Ident(_) =>
PackageDef(id, stats)
case Select(qual, name) => // drop this to flatten packages
makePackaging(start, qual, List(PackageDef(Ident(name), stats)))
}
}
*/
/** Create a tree representing a package object, converting
* package object foo { ... }
* to
* package foo {
* object `package` { ... }
* }
*/
def makePackageObject(start: Int, objDef: ModuleDef): PackageDef = objDef match {
case ModuleDef(mods, name, impl) =>
makePackaging(
start, atPos(o2p(objDef.pos.startOrPoint)){ Ident(name) }, List(ModuleDef(mods, nme.PACKAGEkw, impl)))
}
/** Packaging ::= package QualId [nl] `{' TopStatSeq `}'
*/
def packaging(start: Int): Tree = {
val pkg = pkgQualId()
val stats = inBracesOrNil(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) {
stats ++= (in.token match {
case PACKAGE =>
val start = in.skipToken()
if (in.token == OBJECT)
joinComment(List(makePackageObject(start, objectDef(in.offset, NoMods))))
else {
in.flushDoc
List(packaging(start))
}
case IMPORT =>
in.flushDoc
importClause()
case x if x == AT || isTemplateIntro || isModifier =>
joinComment(List(topLevelTmplDef))
case _ =>
if (!isStatSep)
syntaxErrorOrIncomplete("expected class or object definition", true)
Nil
})
acceptStatSepOpt()
}
stats.toList
}
/** TemplateStatSeq ::= [id [`:' Type] `=>'] TemplateStat {semi TemplateStat}
* TemplateStat ::= Import
* | Annotations Modifiers Def
* | Annotations Modifiers Dcl
* | Expr1
* | super ArgumentExprs {ArgumentExprs}
* |
* @param isPre specifies whether in early initializer (true) or not (false)
*/
def templateStatSeq(isPre : Boolean): (ValDef, List[Tree]) = checkNoEscapingPlaceholders {
var self: ValDef = emptyValDef
val stats = new ListBuffer[Tree]
if (isExprIntro) {
in.flushDoc
val first = expr(InTemplate) // @S: first statement is potentially converted so cannot be stubbed.
if (in.token == ARROW) {
first match {
case Typed(tree @ This(tpnme.EMPTY), tpt) =>
self = atPos(tree.pos union tpt.pos) { makeSelfDef(nme.WILDCARD, tpt) }
case _ =>
convertToParam(first) match {
case tree @ ValDef(_, name, tpt, EmptyTree) if (name != nme.ERROR) =>
self = atPos(tree.pos union tpt.pos) { makeSelfDef(name, tpt) }
case _ =>
}
}
in.nextToken()
} else {
stats += first
acceptStatSepOpt()
}
}
while (!isStatSeqEnd) {
if (in.token == IMPORT) {
in.flushDoc
stats ++= importClause()
} else if (isExprIntro) {
in.flushDoc
stats += statement(InTemplate)
} else if (isDefIntro || isModifier || in.token == AT) {
stats ++= joinComment(nonLocalDefOrDcl)
} else if (!isStatSep) {
syntaxErrorOrIncomplete("illegal start of definition", true)
}
acceptStatSepOpt()
}
(self, stats.toList)
}
/** RefineStatSeq ::= RefineStat {semi RefineStat}
* RefineStat ::= Dcl
* | type TypeDef
* |
*/
def refineStatSeq(): List[Tree] = checkNoEscapingPlaceholders {
val stats = new ListBuffer[Tree]
while (!isStatSeqEnd) {
if (isDclIntro) { // don't IDE hook
stats ++= joinComment(defOrDcl(in.offset, NoMods))
} else if (!isStatSep) {
syntaxErrorOrIncomplete(
"illegal start of declaration"+
(if (inFunReturnType) " (possible cause: missing `=' in front of current method body)"
else ""), true)
}
if (in.token != RBRACE) acceptStatSep()
}
stats.toList
}
/** overridable IDE hook for local definitions of blockStatSeq
* Here's an idea how to fill in start and end positions.
def localDef : List[Tree] = {
atEndPos {
atStartPos(in.offset) {
val annots = annotations(true)
val mods = localModifiers() withAnnotations annots
if (!(mods hasFlag ~(Flags.IMPLICIT | Flags.LAZY))) defOrDcl(mods)
else List(tmplDef(mods))
}
} (in.offset)
}
*/
def localDef(implicitMod: Int): List[Tree] = {
val annots = annotations(true)
val pos = in.offset
val mods = (localModifiers() | implicitMod) withAnnotations annots
val defs =
if (!(mods hasFlag ~(Flags.IMPLICIT | Flags.LAZY))) defOrDcl(pos, mods)
else List(tmplDef(pos, mods))
in.token match {
case RBRACE | CASE => defs :+ (Literal(()) setPos o2p(in.offset))
case _ => defs
}
}
/** BlockStatSeq ::= { BlockStat semi } [ResultExpr]
* BlockStat ::= Import
* | Annotations [implicit] [lazy] Def
* | Annotations LocalModifiers TmplDef
* | Expr1
* |
*/
def blockStatSeq(): List[Tree] = checkNoEscapingPlaceholders {
val stats = new ListBuffer[Tree]
while (!isStatSeqEnd && in.token != CASE) {
if (in.token == IMPORT) {
stats ++= importClause()
acceptStatSep()
}
else if (isExprIntro) {
stats += statement(InBlock)
if (in.token != RBRACE && in.token != CASE) acceptStatSep()
}
else if (isDefIntro || isLocalModifier || in.token == AT) {
if (in.token == IMPLICIT) {
val start = in.skipToken()
if (isIdent) stats += implicitClosure(start, InBlock)
else stats ++= localDef(Flags.IMPLICIT)
} else {
stats ++= localDef(0)
}
acceptStatSepOpt()
}
else if (isStatSep) {
in.nextToken()
}
else {
val addendum = if (isModifier) " (no modifiers allowed here)" else ""
syntaxErrorOrIncomplete("illegal start of statement" + addendum, true)
}
}
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) {
ts ++= joinComment(List(makePackageObject(start, objectDef(in.offset, NoMods))))
if (in.token != EOF) {
acceptStatSep()
ts ++= topStatSeq()
}
} else {
in.flushDoc
val pkg = pkgQualId()
if (in.token == EOF) {
ts += makePackaging(start, pkg, List())
} else if (isStatSep) {
in.nextToken()
ts += makePackaging(start, pkg, topstats())
} else {
ts += inBraces(makePackaging(start, pkg, topStatSeq()))
acceptStatSepOpt()
ts ++= topStatSeq()
}
}
} else {
ts ++= topStatSeq()
}
ts.toList
}
resetPackage()
topstats() match {
case List(stat @ PackageDef(_, _)) => stat
case stats =>
val start =
if (stats forall (_ == EmptyTree)) 0
else {
val wpos = wrappingPos(stats)
if (wpos.isDefined) wpos.startOrPoint
else 0
}
makePackaging(start, atPos(start, start, start) { Ident(nme.EMPTY_PACKAGE_NAME) }, stats)
}
}
}
}
Other Scala examples (source code examples)
Here is a short list of links related to this Scala Parsers.scala source code file:
|