All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scala.meta.internal.parsers.ScalametaParser.scala Maven / Gradle / Ivy

The newest version!
package scala.meta
package internal
package parsers

import scala.language.implicitConversions
import scala.compat.Platform.EOL
import scala.reflect.{ClassTag, classTag}
import scala.runtime.ScalaRunTime
import scala.collection.{ mutable, immutable }
import mutable.{ ListBuffer, StringBuilder }
import scala.annotation.tailrec
import scala.{Seq => _}
import scala.collection.immutable._
import scala.meta.dialects.{Quasiquote, QuasiquoteTerm, QuasiquotePat, Metalevel}
import scala.meta.internal.parsers.Location._
import scala.meta.internal.ast._
import scala.meta.internal.ast.Helpers._
import scala.meta.internal.parsers.Absolutize._
import scala.meta.inputs._
import scala.meta.tokens._
import scala.meta.tokens.Token._
import scala.meta.internal.tokens._
import scala.meta.internal.ast.{AstInfo, astInfo}
import scala.meta.parsers._
import scala.meta.tokenizers._
import scala.meta.prettyprinters._
import scala.meta.classifiers._
import scala.meta.internal.classifiers._
import org.scalameta._
import org.scalameta.invariants._

class ScalametaParser(input: Input, dialect: Dialect) { parser =>
  require(Set("@", ":").contains(dialect.bindToSeqWildcardDesignator))
  require(Set("", EOL).contains(dialect.toplevelSeparator))
  implicit val currentDialect: Dialect = dialect
  def inQuasiquote = dialect.metalevel.isQuoted
  def allowInline = dialect.allowInline

/* ------------- PARSER ENTRY POINTS -------------------------------------------- */

  def parseRule[T <: Tree](rule: this.type => T): T = {
    // NOTE: can't require in.tokenPos to be at -1, because TokIterator auto-rewinds when created
    // require(in.tokenPos == -1 && debug(in.tokenPos))
    val start = 0
    accept[BOF]
    val t = rule(this)
    // NOTE: can't have in.prevTokenPos here
    // because we need to subsume all the trailing trivia
    val end = in.tokenPos
    accept[EOF]
    atPos(start, end)(t)
  }

  // Entry points for Parse[T]
  // Used for quasiquotes as well as for ad-hoc parsing
  // parseQuasiquoteStat is used to implement q"..." and surprisingly can't be reduced to anything that's already in the parser,
  // because it needs to support a wide range of constructs, from expressions to top-level definitions.
  // Note the expr(Local) part, which means that we're going to parse lambda expressions in the mode that
  // precludes ambiguities with self-type annotations.
  private val consumeStat: PartialFunction[Token, Stat] = {
    case KwImport() => importStmt()
    case KwPackage() if !dialect.allowToplevelTerms => packageOrPackageObjectDef()
    case DefIntro() => nonLocalDefOrDcl()
    case ExprIntro() => expr(Local) match { case q: Term.Quasi => q.become[Stat.Quasi]; case other => other }
    case Ellipsis(_) => Term.Block(List(ellipsis(1, astInfo[Stat])))
  }
  def parseStat(): Stat = {
    parseRule(parser => {
      def skipStatementSeparators(): Unit = {
        if (token.is[EOF]) return
        if (!token.is[StatSep]) accept[EOF]
        next()
        skipStatementSeparators()
      }
      val maybeStat = consumeStat.lift(token)
      val stat = maybeStat.getOrElse(reporter.syntaxError("unexpected start of statement", at = token))
      skipStatementSeparators()
      stat
    })
  }
  def parseQuasiquoteStat(): Stat = {
    def failEmpty() = {
      reporter.syntaxError("unexpected end of input", at = token)
    }
    def failMix(advice: Option[String]) = {
      val message = "these statements can't be mixed together"
      val addendum = advice.map(", " + _).getOrElse("")
      reporter.syntaxError(message + addendum, at = parserTokens.head)
    }
    parseRule(parser => parser.statSeq(consumeStat) match {
      case Nil => failEmpty()
      case stat :: Nil => stat
      case stats if stats.forall(_.isBlockStat) => Term.Block(stats)
      case stats if stats.forall(_.isTopLevelStat) => failMix(Some("try source\"...\" instead"))
      case other => failMix(None)
    })
  }
  def parseQuasiquoteCtor(): Ctor = parseRule(_.quasiquoteCtor())
  def parseTerm(): Term = parseRule(_.expr())
  def parseUnquoteTerm(): Term = parseRule(_.unquoteExpr())
  def parseTermArg(): Term.Arg = parseRule(_.argumentExpr())
  def parseTermParam(): Term.Param = parseRule(_.param(ownerIsCase = false, ownerIsType = true, isImplicit = false))
  def parseType(): Type = parseRule(_.typ())
  def parseTypeArg(): Type.Arg = parseRule(_.paramType())
  def parseTypeParam(): Type.Param = parseRule(_.typeParam(ownerIsType = true, ctxBoundsAllowed = true))
  def parsePat(): Pat = parseRule(_.pattern()).require[Pat]
  def parseQuasiquotePat(): Pat = parseRule(_.quasiquotePattern()).require[Pat]
  def parseUnquotePat(): Pat = parseRule(_.unquotePattern())
  def parsePatArg(): Pat.Arg = parseRule(_.argumentPattern())
  def parseQuasiquotePatArg(): Pat.Arg = parseRule(_.quasiquotePatternArg())
  def parsePatType(): Pat.Type = parseRule(_.patternTyp())
  def parseQuasiquotePatType(): Pat.Type = parseRule(_.quasiquotePatternTyp())
  def parseCase(): Case = parseRule{parser => parser.accept[KwCase]; parser.caseClause()}
  def parseCtorCall(): Ctor.Call = parseRule(_.constructorCall(typ(), allowArgss = true))
  def parseTemplate(): Template = parseRule(_.template())
  def parseQuasiquoteTemplate(): Template = parseRule(_.quasiquoteTemplate())
  def parseMod(): Mod = {
    implicit class XtensionParser(parser: this.type) {
      def annot() = parser.annots(skipNewLines = false) match {
        case annot :: Nil => annot
        case annot :: other :: _ => parser.reporter.syntaxError(s"end of file expected but ${token.name} found", at = other)
        case Nil => unreachable
      }
    }
    def fail() = parser.reporter.syntaxError(s"modifier expected but ${parser.token.name} found", at = parser.token)
    parseRule(parser => parser.autoPos(parser.token match {
      case Unquote(_)                        => unquote[Mod.Quasi]
      case At()                              => parser.annot()
      case KwPrivate()                       => parser.accessModifier()
      case KwProtected()                     => parser.accessModifier()
      case KwImplicit()                      => parser.next(); Mod.Implicit()
      case KwFinal()                         => parser.next(); Mod.Final()
      case KwSealed()                        => parser.next(); Mod.Sealed()
      case KwOverride()                      => parser.next(); Mod.Override()
      case KwCase()                          => parser.next(); Mod.Case()
      case KwAbstract()                      => parser.next(); Mod.Abstract()
      case Ident("+")                        => parser.next(); Mod.Covariant()
      case Ident("-")                        => parser.next(); Mod.Contravariant()
      case KwLazy()                          => parser.next(); Mod.Lazy()
      case KwVal() if !inQuasiquote          => parser.next(); Mod.ValParam()
      case KwVar() if !inQuasiquote          => parser.next(); Mod.VarParam()
      case Ident("valparam") if inQuasiquote => parser.next(); Mod.ValParam()
      case Ident("varparam") if inQuasiquote => parser.next(); Mod.VarParam()
      case KwInline() if allowInline         => parser.next(); Mod.Inline()
      case _                                 => fail()
    }))
  }
  def parseQuasiquoteMod(): Mod = parseMod() // NOTE: special treatment for mod"valparam" and likes is implemented directly in `parseMod`
  def parseEnumerator(): Enumerator = {
    parseRule(_.enumerator(isFirst = false, allowNestedIf = false) match {
      case enumerator :: Nil => enumerator
      case other => unreachable(debug(other))
    })
  }
  def parseImporter(): Importer = parseRule(_.importer())
  def parseImportee(): Importee = parseRule(_.importee())
  def parseSource(): Source = parseRule(_.source())

/* ------------- TOKEN STREAM HELPERS -------------------------------------------- */

  // NOTE: This is a cache that's necessary for reasonable performance of prev/next for tokens.
  // It maps character positions in input's content into indices in the scannerTokens vector.
  // One complication here is that there can be multiple tokens that sort of occupy a given position,
  // so the clients of this cache need to be wary of that!
  private val scannerTokenCache: Array[Int] = {
    val result = new Array[Int](input.chars.length)
    var i = 0
    while (i < scannerTokens.length) {
      val token = scannerTokens(i)
      var j = token.start
      while (j < token.end) {
        result(j) = i
        j += 1
      }
      i += 1
    }
    result
  }
  implicit class XtensionTokenIndex(token: Token) {
    def index: Int = {
      def lurk(roughIndex: Int): Int = {
        require(roughIndex >= 0 && debug(token))
        if (scannerTokens(roughIndex) eq token) roughIndex
        else lurk(roughIndex - 1)
      }
      lurk(scannerTokenCache(token.start))
    }
    def prev: Token = {
      val prev = scannerTokens.apply(Math.max(token.index - 1, 0))
      if (prev.is[Whitespace] || prev.is[Comment]) prev.prev
      else prev
    }
    def next: Token = {
      val next = scannerTokens.apply(Math.min(token.index + 1, scannerTokens.length - 1))
      if (next.is[Whitespace] || next.is[Comment]) next.next
      else next
    }
  }

/* ------------- PARSER-SPECIFIC TOKENS -------------------------------------------- */

  // TODO: Scala's parser isn't ready to accept whitespace and comment tokens,
  // so we have to filter them out, because otherwise we'll get errors like `expected blah, got whitespace`
  // However, in certain tricky cases some whitespace tokens (namely, newlines) do have to be emitted.
  // This leads to extremely dirty and seriously crazy code, which I'd like to replace in the future.
  private val XtensionParsersDialectApply = "shadow conflicting implicit"
  lazy val scannerTokens = input.tokenize match {
    case Tokenized.Success(tokens) => tokens
    case Tokenized.Error(_, _, details) => throw details
  }
  lazy val (parserTokens, parserTokenPositions) = {
    val parserTokens = mutable.ArrayBuilder.make[Token]()
    val parserTokenPositions = mutable.ArrayBuilder.make[Int]()
    @tailrec def loop(prevPos: Int, currPos: Int, sepRegions: List[Char]): Unit = {
      if (currPos >= scannerTokens.length) return
      val prev = if (prevPos >= 0) scannerTokens(prevPos) else null
      val curr = scannerTokens(currPos)
      val nextPos = {
        var i = currPos + 1
        while (i < scannerTokens.length && scannerTokens(i).is[Trivia]) i += 1
        if (i == scannerTokens.length) i = -1
        i
      }
      val next = if (nextPos != -1) scannerTokens(nextPos) else null
      if (curr.isNot[Trivia]) {
        parserTokens += curr
        parserTokenPositions += currPos
        val sepRegions1 = {
          if (curr.is[LeftParen]) ')' :: sepRegions
          else if (curr.is[LeftBracket]) ']' :: sepRegions
          else if (curr.is[LeftBrace]) '}' :: sepRegions
          else if (curr.is[CaseIntro]) '\u21d2' :: sepRegions
          else if (curr.is[RightBrace]) {
            var sepRegions1 = sepRegions
            while (!sepRegions1.isEmpty && sepRegions1.head != '}') sepRegions1 = sepRegions1.tail
            if (!sepRegions1.isEmpty) sepRegions1 = sepRegions1.tail
            sepRegions1
          } else if (curr.is[RightBracket]) { if (!sepRegions.isEmpty && sepRegions.head == ']') sepRegions.tail else sepRegions }
          else if (curr.is[RightParen]) { if (!sepRegions.isEmpty && sepRegions.head == ')') sepRegions.tail else sepRegions }
          else if (curr.is[RightArrow]) { if (!sepRegions.isEmpty && sepRegions.head == '\u21d2') sepRegions.tail else sepRegions }
          else sepRegions // do nothing for other tokens
        }
        loop(currPos, currPos + 1, sepRegions1)
      } else {
        var i = prevPos + 1
        var lastNewlinePos = -1
        var newlineStreak = false
        var newlines = false
        while (i < nextPos) {
          if (scannerTokens(i).is[LF] || scannerTokens(i).is[FF]) {
            lastNewlinePos = i
            if (newlineStreak) newlines = true
            newlineStreak = true
          }
          newlineStreak &= scannerTokens(i).is[Whitespace]
          i += 1
        }
        if (lastNewlinePos != -1 &&
            prev != null && prev.is[CanEndStat] &&
            next != null && next.isNot[CantStartStat] &&
            (sepRegions.isEmpty || sepRegions.head == '}')) {
          var token = scannerTokens(lastNewlinePos)
          if (newlines) token = LFLF(token.input, token.dialect, token.start, token.end)
          parserTokens += token
          parserTokenPositions += lastNewlinePos
          loop(lastNewlinePos, currPos + 1, sepRegions)
        } else {
          loop(prevPos, nextPos, sepRegions)
        }
      }
    }
    loop(-1, 0, Nil)
    val underlying = parserTokens.result
    (Tokens(underlying, 0, underlying.length), parserTokenPositions.result)
  }

  // NOTE: public methods of TokenIterator return scannerTokens-based positions
  trait TokenIterator extends Iterator[Token] { def prevTokenPos: Int; def tokenPos: Int; def token: Token; def fork: TokenIterator }
  var in: TokenIterator = new SimpleTokenIterator()
  private class SimpleTokenIterator(var i: Int = -1) extends TokenIterator {
    require(parserTokens.nonEmpty)
    if (i == -1) next() // NOTE: only do next() if we've been just created. forks can't go for next()
    def hasNext: Boolean = i < parserTokens.length - 1
    def next(): Token = { if (!hasNext) throw new NoSuchElementException(); i += 1; parserTokens(i) }
    def prevTokenPos: Int = if (i > 0) parserTokenPositions(Math.min(i, parserTokens.length - 1) - 1) else -1
    def tokenPos: Int = if (i > -1) parserTokenPositions(Math.min(i, parserTokens.length - 1)) else -1
    def token: Token = parserTokens(i)
    def fork: TokenIterator = new SimpleTokenIterator(i)
  }
  def token = in.token
  def next() = in.next()
  def nextOnce() = next()
  def nextTwice() = { next(); next() }
  def nextThrice() = { next(); next(); next() }

/* ------------- PARSER COMMON -------------------------------------------- */

  /** Scoping operator used to temporarily look into the future.
   *  Backs up token iterator before evaluating a block and restores it after.
   */
  @inline final def ahead[T](body: => T): T = {
    val forked = in.fork
    next()
    try body finally in = forked
  }

  /** Perform an operation while peeking ahead.
   *  Recover to inputal state in case of exception.
   */
  @inline def peekingAhead[T](tree: => T): T = {
    val forked = in.fork
    next()
    // try it, in case it is recoverable
    try tree catch { case e: Exception => in = forked ; throw e }
  }

  /** Methods inParensOrError and similar take a second argument which, should
   *  the next token not be the expected opener (e.g. token.LeftParen) will be returned
   *  instead of the contents of the groupers.  However in all cases accept[LeftParen]
   *  will be called, so a parse error will still result.  If the grouping is
   *  optional, token should be tested before calling these methods.
   */
  @inline final def inParens[T](body: => T): T = {
    accept[LeftParen]
    val ret = body
    accept[RightParen]
    ret
  }
  @inline final def inParensOrError[T](body: => T, alt: T): T =
    if (token.is[LeftParen]) inParens(body)
    else { accept[LeftParen]; alt }

  @inline final def inParensOrUnit[T, Ret >: Lit](body: => Ret): Ret = inParensOrError(body, Lit(()))
  @inline final def inParensOrNil[T](body: => List[T]): List[T] = inParensOrError(body, Nil)

  @inline final def inBraces[T](body: => T): T = {
    accept[LeftBrace]
    val ret = body
    accept[RightBrace]
    ret
  }
  @inline final def inBracesOrError[T](body: => T, alt: T): T =
    if (token.is[LeftBrace]) inBraces(body)
    else { accept[LeftBrace]; alt }

  @inline final def inBracesOrNil[T](body: => List[T]): List[T] = inBracesOrError(body, Nil)
  @inline final def inBracesOrUnit[T](body: => Term): Term = inBracesOrError(body, Lit(()))
  @inline final def dropAnyBraces[T](body: => T): T =
    if (token.is[LeftBrace]) inBraces(body)
    else body

  @inline final def inBrackets[T](body: => T): T = {
    accept[LeftBracket]
    val ret = body
    accept[RightBracket]
    ret
  }

/* ------------- POSITION HANDLING ------------------------------------------- */

  // TODO: `startTokenPos` and `endTokenPos` are BOTH INCLUSIVE.
  // This is at odds with the rest of scala.meta, where ends are non-inclusive.
  sealed trait Pos {
    def startTokenPos: Int
    def endTokenPos: Int
  }
  case class IndexPos(index: Int) extends Pos {
    def startTokenPos = index
    def endTokenPos = startTokenPos
  }
  case class TokenPos(token: Token) extends Pos {
    def startTokenPos = token.index
    def endTokenPos = startTokenPos
  }
  case class TreePos(tree: Tree) extends Pos {
    val (startTokenPos, endTokenPos) = tree.origin match {
      case Origin.Parsed(_, _, pos) => (pos.start, pos.end - 1)
      case _ => sys.error("internal error: unpositioned prototype ${tree.syntax}: ${tree.structure}")
    }
  }
  case object AutoPos extends Pos {
    def startTokenPos = in.tokenPos
    def endTokenPos = in.prevTokenPos
  }
  implicit def intToIndexPos(index: Int): IndexPos = IndexPos(index)
  implicit def tokenToTokenPos(token: Token): TokenPos = TokenPos(token)
  implicit def treeToTreePos(tree: Tree): TreePos = TreePos(tree)
  implicit def optionTreeToPos(tree: Option[Tree]): Pos = tree.map(TreePos).getOrElse(AutoPos)
  implicit def modsToPos(mods: List[Mod]): Pos = mods.headOption.map(TreePos).getOrElse(AutoPos)
  def auto = AutoPos

  def atPos[T <: Tree](start: Pos, end: Pos)(body: => T): T = {
    val startTokenPos = start.startTokenPos
    val result = body
    var endTokenPos = end.endTokenPos
    if (endTokenPos < startTokenPos) endTokenPos = startTokenPos - 1
    val pos = TokenStreamPosition(startTokenPos, endTokenPos + 1)
    result.withOrigin(Origin.Parsed(input, dialect, pos)).asInstanceOf[T]
  }
  def autoPos[T <: Tree](body: => T): T = atPos(start = auto, end = auto)(body)

/* ------------- ERROR HANDLING ------------------------------------------- */

  lazy val reporter = Reporter()
  import reporter._

  private var inFunReturnType = false
  @inline private def fromWithinReturnType[T](body: => T): T = {
    val saved = inFunReturnType
    inFunReturnType = true
    try body
    finally inFunReturnType = saved
  }

  def syntaxErrorExpected[T <: Token : TokenInfo]: Nothing =
    syntaxError(s"${implicitly[TokenInfo[T]].name} expected but ${token.name} found", at = token)

  /** Consume one token of the specified type, or signal an error if it is not there. */
  def accept[T <: Token : TokenInfo]: Unit =
    if (token.is[T](implicitly[TokenInfo[T]])) {
      if (token.isNot[EOF]) next()
    } else syntaxErrorExpected[T]

  /** If current token is T consume it. */
  def acceptOpt[T <: Token : TokenInfo]: Unit =
    if (token.is[T]) next()

  /** {{{
   *  semi = nl {nl} | `;'
   *  nl  = `\n' // where allowed
   *  }}}
   */
  def acceptStatSep(): Unit = token match {
    case LF() | LFLF() => next()
    case _             => accept[Semicolon]
  }
  def acceptStatSepOpt() =
    if (!token.is[StatSeqEnd])
      acceptStatSep()

/* -------------- TOKEN CLASSES ------------------------------------------- */

  def isIdentAnd(pred: String => Boolean) = token match {
    case Ident(value) if pred(value.stripPrefix("`").stripSuffix("`")) => true
    case _                                                             => false
  }
  def isUnaryOp: Boolean            = isIdentAnd(Helpers.isUnaryOp)
  def isIdentExcept(except: String) = isIdentAnd(_ != except)
  def isIdentOf(name: String)       = isIdentAnd(_ == name)
  def isIdent: Boolean              = isIdentAnd(_ => true)
  def isRawStar: Boolean            = isIdentOf("*")
  def isRawBar: Boolean             = isIdentOf("|")
  def isColonWildcardStar: Boolean  = token.is[Colon] && ahead(token.is[Underscore] && ahead(isIdentOf("*")))
  def isSpliceFollowedBy(check: => Boolean): Boolean
                                    = token.is[Ellipsis] && ahead(token.is[Unquote] && ahead(token.is[Ident] || check))
  def isBackquoted: Boolean         = token.syntax.startsWith("`") && token.syntax.endsWith("`")

  private implicit class XtensionTokenClass(token: Token) {
    def isCaseClassOrObject = token.is[KwCase] && (token.next.is[KwClass] || token.next.is[KwObject])
  }

  @classifier
  trait TypeIntro {
    def unapply(token: Token): Boolean = {
      token.is[Ident] || token.is[KwSuper] || token.is[KwThis] ||
      token.is[LeftParen] || token.is[At] || token.is[Underscore] ||
      token.is[Unquote]
    }
  }

  @classifier
  trait ExprIntro {
    def unapply(token: Token): Boolean = {
      token.is[Ident] || token.is[Literal] ||
      token.is[Interpolation.Id] || token.is[Xml.Start] ||
      token.is[KwDo] || token.is[KwFor] || token.is[KwIf] ||
      token.is[KwNew] || token.is[KwReturn] || token.is[KwSuper] ||
      token.is[KwThis] || token.is[KwThrow] || token.is[KwTry] || token.is[KwWhile] ||
      token.is[LeftParen] || token.is[LeftBrace] || token.is[Underscore] ||
      token.is[Unquote]
    }
  }

  @classifier
  trait CaseIntro {
    def unapply(token: Token): Boolean = {
      token.is[KwCase] && !token.isCaseClassOrObject
    }
  }

  @classifier
  trait DefIntro {
    def unapply(token: Token): Boolean = {
      token.is[Modifier] || token.is[At] ||
      token.is[TemplateIntro] || token.is[DclIntro] ||
      (token.is[Unquote] && token.next.is[DefIntro]) ||
      (token.is[Ellipsis] && token.next.is[DefIntro]) ||
      (token.is[KwCase] && token.isCaseClassOrObject)
    }
  }

  @classifier
  trait TemplateIntro {
    def unapply(token: Token): Boolean = {
      token.is[Modifier] || token.is[At] ||
      token.is[KwClass] || token.is[KwObject] || token.is[KwTrait] ||
      (token.is[Unquote] && token.next.is[TemplateIntro]) ||
      (token.is[KwCase] && token.isCaseClassOrObject)
    }
  }

  @classifier
  trait DclIntro {
    def unapply(token: Token): Boolean = {
      token.is[KwDef] || token.is[KwType] ||
      token.is[KwVal] || token.is[KwVar] ||
      (token.is[Unquote] && token.next.is[DclIntro])
    }
  }

  @classifier
  trait Modifier {
    def unapply(token: Token): Boolean = {
      token.is[KwAbstract] || token.is[KwFinal] ||
      token.is[KwSealed] || token.is[KwImplicit] ||
      token.is[KwLazy] || token.is[KwPrivate] ||
      token.is[KwProtected] || token.is[KwOverride] ||
      token.is[KwInline]
    }
  }

  @classifier
  trait NonlocalModifier {
    def unapply(token: Token): Boolean = {
      token.is[KwPrivate] || token.is[KwProtected] ||
      token.is[KwOverride]
    }
  }

  @classifier
  trait LocalModifier {
    def unapply(token: Token): Boolean = {
      token.is[Modifier] && !token.is[NonlocalModifier]
    }
  }

  @classifier
  trait StatSeqEnd {
    def unapply(token: Token): Boolean = {
      token.is[RightBrace] || token.is[EOF]
    }
  }

  @classifier
  trait CaseDefEnd {
    def unapply(token: Token): Boolean = {
      token.is[RightBrace] || token.is[EOF] ||
      (token.is[KwCase] && !token.isCaseClassOrObject) ||
      (token.is[Ellipsis] && token.next.is[KwCase])
    }
  }

  @classifier
  trait CantStartStat {
    def unapply(token: Token): Boolean = {
      token.is[KwCatch] || token.is[KwElse] || token.is[KwExtends] ||
      token.is[KwFinally] || token.is[KwForsome] || token.is[KwMatch] ||
      token.is[KwWith] || token.is[KwYield] ||
      token.is[RightParen] || token.is[LeftBracket] || token.is[RightBracket] || token.is[RightBrace] ||
      token.is[Comma] || token.is[Colon] || token.is[Dot] || token.is[Equals] ||
      token.is[Semicolon] || token.is[Hash] || token.is[RightArrow] || token.is[LeftArrow] ||
      token.is[Subtype] || token.is[Supertype] || token.is[Viewbound] ||
      token.is[LF] || token.is[LFLF] || token.is[EOF]
    }
  }

  @classifier
  trait CanEndStat {
    def unapply(token: Token): Boolean = {
      token.is[Ident] || token.is[Literal] ||
      token.is[Interpolation.End] || token.is[Xml.End] ||
      token.is[KwReturn] || token.is[KwThis] || token.is[KwType] ||
      token.is[RightParen] || token.is[RightBracket] || token.is[RightBrace] || token.is[Underscore] ||
      token.is[Ellipsis] || token.is[Unquote]
    }
  }

  @classifier
  trait StatSep {
    def unapply(token: Token): Boolean = {
      token.is[Semicolon] || token.is[LF] || token.is[LFLF] || token.is[EOF]
    }
  }

  @classifier
  trait Literal {
    def unapply(token: Token): Boolean = token match {
      case Constant.Int(_) => true
      case Constant.Long(_) => true
      case Constant.Float(_) => true
      case Constant.Double(_) => true
      case Constant.Char(_) => true
      case Constant.Symbol(_) => true
      case Constant.String(_) => true
      case KwTrue() => true
      case KwFalse() => true
      case KwNull() => true
      case _ => false
    }
  }

  @classifier
  trait NumericLiteral {
    def unapply(token: Token): Boolean = token match {
      case Constant.Int(_) => true
      case Constant.Long(_) => true
      case Constant.Float(_) => true
      case Constant.Double(_) => true
      case _ => false
    }
  }

  @classifier
  trait Whitespace {
    def unapply(token: Token): Boolean = {
      token.is[Space] || token.is[Tab] ||
      token.is[CR] || token.is[LF] ||
      token.is[LFLF] || token.is[FF]
    }
  }

  @classifier
  trait Trivia {
    def unapply(token: Token): Boolean = {
      token.is[Whitespace] || token.is[Comment]
    }
  }

/* ---------- TREE CONSTRUCTION ------------------------------------------- */

  def ellipsis[T <: Tree : AstInfo](rank: Int, astInfo: AstInfo[T], extraSkip: => Unit = {}): T = autoPos {
    token match {
      case ellipsis: Ellipsis =>
        if (dialect.allowEllipses) {
          if (ellipsis.rank != rank) {
            syntaxError(Messages.QuasiquoteRankMismatch(ellipsis.rank, rank), at = ellipsis)
          } else {
            next()
            extraSkip
            val tree = {
              val result = token match {
                case LeftParen() => inParens(unquote[T])
                case LeftBrace() => inBraces(unquote[T])
                case Unquote(_) => unquote[T]
                case _ => syntaxError(s"$$, ( or { expected but ${token.name} found", at = token)
              }
              result match {
                case quasi: Quasi =>
                  // NOTE: In the case of an unquote nested directly under ellipsis, we get a bit of a mixup.
                  // Unquote's pt may not be directly equal unwrapped ellipsis's pt, but be its refinement instead.
                  // For example, in `new { ..$stats }`, ellipsis's pt is Seq[Stat], but quasi's pt is Term.
                  // This is an artifact of the current implementation, so we just need to keep it mind and work around it.
                  require(classTag[T].runtimeClass.isAssignableFrom(quasi.pt) && debug(ellipsis, result, result.structure))
                  atPos(quasi, quasi)(astInfo.quasi(quasi.rank, quasi.tree))
                case other =>
                  other
              }
            }
            astInfo.quasi(rank, tree)
          }
        } else {
          syntaxError(s"$dialect doesn't support ellipses", at = ellipsis)
        }
      case _ =>
        unreachable(debug(token))
    }
  }

  def unquote[T <: Tree : AstInfo](advance: Boolean = true): T = autoPos {
    token match {
      case unquote: Unquote =>
        require(unquote.metalevel.nesting == 0)
        require(unquote.input.chars(unquote.start + 1) != '$')
        dialect match {
          case dialect: Quasiquote =>
            // TODO: The necessity to do position fixup for error messages is unsatisfying.
            // NOTE: I considered having Input.Slice produce absolute positions from the get-go,
            // but then such positions wouldn't be usable with Input.Slice.chars.
            val unquotedTree = {
              try {
                val unquoteInput = Input.Slice(input, unquote.start + 1, unquote.end)
                val unquoteDialect = dialect.underlying
                val unquoteParser = new ScalametaParser(unquoteInput, unquoteDialect)
                dialect match {
                  case dialect: QuasiquoteTerm => unquoteParser.parseUnquoteTerm()
                  case dialect: QuasiquotePat => unquoteParser.parseUnquotePat()
                }
              } catch {
                case ex: Exception => throw ex.absolutize
              }
            }
            if (advance) {
              next()
              implicitly[AstInfo[T]].quasi(0, unquotedTree)
            } else ahead {
              implicitly[AstInfo[T]].quasi(0, unquotedTree)
            }
          case _ =>
            syntaxError(s"$dialect doesn't support unquotes", at = unquote)
        }
      case _ =>
        unreachable(debug(token))
    }
  }

  def unquote[T <: Tree : AstInfo]: T = unquote[T](advance = true) // to write `unquote[T]` without round braces

  /** Convert tree to formal parameter list. */
  def convertToParams(tree: Term): List[Term.Param] = tree match {
    case Term.Tuple(ts) => ts.toList flatMap convertToParam
    case _              => List(convertToParam(tree)).flatten
  }

  /** Convert tree to formal parameter. */
  def convertToParam(tree: Term): Option[Term.Param] = tree match {
    case q: Term.Quasi =>
      Some(q.become[Term.Param.Quasi])
    case name: Term.Name =>
      Some(atPos(tree, tree)(Term.Param(Nil, name, None, None)))
    case name: Term.Placeholder =>
      Some(atPos(tree, tree)(Term.Param(Nil, atPos(name, name)(Name.Anonymous()), None, None)))
    case Term.Ascribe(quasiName: Term.Quasi, tpt) =>
      val name = quasiName.become[Term.Name.Quasi]
      Some(atPos(tree, tree)(Term.Param(Nil, name, Some(tpt), None)))
    case Term.Ascribe(name: Term.Name, tpt) =>
      Some(atPos(tree, tree)(Term.Param(Nil, name, Some(tpt), None)))
    case Term.Ascribe(name: Term.Placeholder, tpt) =>
      Some(atPos(tree, tree)(Term.Param(Nil, atPos(name, name)(Name.Anonymous()), Some(tpt), None)))
    case Lit(()) =>
      None
    case other =>
      syntaxError(s"not a legal formal parameter", at = other)
  }

  def convertToTypeId(ref: Term.Ref): Option[Type] = ref match {
    case ref: Term.Ref.Quasi =>
      Some(ref.become[Type.Quasi])
    case Term.Select(qual: Term.Quasi, name: Term.Name.Quasi) =>
      val newQual = qual.become[Term.Ref.Quasi]
      val newName = name.become[Type.Name.Quasi]
      Some(atPos(ref, ref)(Type.Select(newQual, newName)))
    case Term.Select(qual: Term.Ref, name) =>
      val newName = atPos(name, name)(Type.Name(name.value))
      Some(atPos(ref, ref)(Type.Select(qual, newName)))
    case name: Term.Name =>
      Some(atPos(name, name)(Type.Name(name.value)))
    case _ =>
      None
  }

  /** {{{ part { `sep` part } }}}, or if sepFirst is true, {{{ { `sep` part } }}}.
    * if useUnquoteForEllipsis = true, uses direct unquote instead of `part`
    * */
  final def tokenSeparated[Sep <: Token : TokenInfo, T <: Tree : AstInfo](sepFirst: Boolean, part: => T): List[T] = {
    def partOrEllipsis =
      if (token.is[Ellipsis]) ellipsis(1, astInfo[T])
      else part
    val ts = new ListBuffer[T]
    if (!sepFirst)
      ts += partOrEllipsis
    while (token.is[Sep] || token.is[Ellipsis]) {
      if (token.is[Sep]) next()
      ts += partOrEllipsis
    }
    ts.toList
  }

  @inline final def commaSeparated[T <: Tree : AstInfo](part: => T): List[T] =
    tokenSeparated[Comma, T](sepFirst = false, part)

  def makeTuple[T <: Tree](body: List[T], zero: () => T, tuple: List[T] => T): T = body match {
    case Nil => zero()
    case only :: Nil => only match {
      case q: Quasi if q.rank == 1 => tuple(body)
      case _ => only
    }
    case _ => tuple(body)
  }

  def makeTupleTerm(body: List[Term]): Term = {
    // NOTE: we can't make this autoPos, unlike makeTupleTermParens
    // see comments to makeTupleType for discussion
    body match {
      case Seq(q @ Term.Quasi(1, _)) => atPos(q, q)(Term.Tuple(body))
      case _ => makeTuple[Term](body, () => Lit(()), Term.Tuple(_))
    }
  }

  def makeTupleTermParens(bodyf: => List[Term]) = autoPos {
    makeTupleTerm(inParens(if (token.is[RightParen]) Nil else bodyf))
  }

  // TODO: make zero tuple for types Lit.Unit() too?
  def makeTupleType(body: List[Type]): Type = {
    // NOTE: we can't make this autoPos, unlike makeTuplePatParens
    // because, by the time control reaches this method, we're already past the closing parenthesis
    // therefore, we'll rely on our callers to assign positions to the tuple we return
    // we can't do atPos(body.first, body.last) either, because that wouldn't account for parentheses
    body match {
      case Seq(q @ Type.Quasi(1, _)) => atPos(q, q)(Type.Tuple(body))
      case _ => makeTuple[Type](body, () => unreachable, Type.Tuple(_))
    }
  }

/* -------- 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(): Type
    def functionArgType(): Type.Arg

    private def tupleInfixType(): Type = autoPos {
      require(token.is[LeftParen] && debug(token))
      val openParenPos = in.tokenPos

      // NOTE: This is a really hardcore disambiguation caused by introduction of Type.Method.
      // We need to accept `(T, U) => W`, `(x: T): x.U` and also support unquoting.
      // Maybe it was not the best idea to introduce method types or to have this syntax for them...
      // TODO: Decide whether to keep or remove this after we think about semantic once again.
      var hasParams = false
      var hasImplicits = false
      var hasTypes = false
      var secondOpenParenPos = -1
      var closeParenPos = -1
      val rawtss: List[List[Tree]] = {
        def paramOrType() = {
          def looksLikeParam = token.is[KwImplicit] || (token.is[Ident] && ahead(token.is[Colon]))
          if (token.is[Ellipsis]) {
            ellipsis(1, astInfo[Tree])
          } else if (token.is[Unquote]) {
            unquote[Tree]
          } else if (looksLikeParam) {
            if (hasTypes) syntaxError("can't mix function type and method type syntaxes", at = token)
            if (hasImplicits) accept[Ident]
            hasParams = true
            hasImplicits |= token.is[KwImplicit]
            param(ownerIsCase = false, ownerIsType = false, isImplicit = hasImplicits)
          } else {
            if (hasParams) syntaxError("can't mix function type and method type syntaxes", at = token)
            hasTypes = true
            functionArgType()
          }
        }

        val rawtss = new ListBuffer[List[Tree]]
        while (!hasTypes && !hasImplicits && token.is[LeftParen]) {
          if (openParenPos != in.tokenPos && secondOpenParenPos == 0) secondOpenParenPos = in.tokenPos
          accept[LeftParen]
          val rawts = new ListBuffer[Tree]
          if (!token.is[RightParen]) {
            rawts += paramOrType()
            while (token.is[Comma] || token.is[Ellipsis]) {
              if (token.is[Comma]) next()
              rawts += paramOrType()
            }
          }
          closeParenPos = in.tokenPos
          accept[RightParen]
          // NOTE: can't have this, because otherwise we run into #312
          // newLineOptWhenFollowedBy[LeftParen]
          rawtss += rawts.toList
        }
        rawtss.toList
      }
      def ts: List[Type.Arg] = {
        if (hasParams) require(false && debug(hasParams, hasImplicits, hasTypes))
        if (rawtss.length != 1) {
          val message = "can't have multiple parameter lists in function types"
          syntaxError(message, at = scannerTokens(secondOpenParenPos))
        }
        rawtss.head.map({
          case q: Tree.Quasi => q.become[Type.Arg.Quasi]
          case t: Type.Arg => t
          case other => unreachable(debug(other.syntax, other.structure))
        })
      }
      def pss: List[List[Term.Param]] = {
        if (hasTypes) require(false && debug(hasParams, hasImplicits, hasTypes))
        rawtss.map(_.map({
          case q: Tree.Quasi => q.become[Term.Param.Quasi]
          case t: Term.Param => t
          case other => unreachable(debug(other.syntax, other.structure))
        }))
      }

      if (hasParams && !token.is[Colon]) syntaxError("can't mix function type and method type syntaxes", at = token)
      if (hasTypes && token.is[Colon]) accept[RightArrow]

      if (token.is[RightArrow]) {
        next()
        Type.Function(ts, typ())
      } else {
        val tuple = atPos(openParenPos, closeParenPos)(makeTupleType(ts map {
          case q: Type.Arg.Quasi    => q.become[Type.Quasi]
          case t: Type              => t
          case p: Type.Arg.ByName   => syntaxError("by name type not allowed here", at = p)
          case p: Type.Arg.Repeated => syntaxError("repeated type not allowed here", at = p)
        }))
        infixTypeRest(
          compoundTypeRest(Some(
            annotTypeRest(
              simpleTypeRest(
                tuple)))),
          InfixMode.FirstOp
        )
      }
    }

    /** {{{
     *  Type ::= InfixType `=>' Type
     *         | `(' [`=>' Type] `)' `=>' Type
     *         | `[' [`=>' Type] `]' `=>' Type
     *         | InfixType [ExistentialClause]
     *  ExistentialClause ::= forSome `{' ExistentialDcl {semi ExistentialDcl}} `}'
     *  ExistentialDcl    ::= type TypeDcl | val ValDcl
     *  }}}
     */
    def typ(): Type = autoPos {
      val t: Type =
        if (token.is[LeftParen]) tupleInfixType()
        else infixType(InfixMode.FirstOp)

      token match {
        case RightArrow() => next(); Type.Function(List(t), typ())
        case KwForsome()  => next(); Type.Existential(t, existentialStats())
        case _            => t
      }
    }

    /** {{{
     *  TypeArgs    ::= `[' ArgType {`,' ArgType} `]'
     *  }}}
     */
    def typeArgs(): List[Type] = inBrackets(types())

    /** {{{
     *  ModType        ::=  SimpleType {Annotation}
     *  }}}
     */
    def annotType(): Type = annotTypeRest(simpleType())

    /** {{{
     *  SimpleType       ::=  SimpleType TypeArgs
     *                     |  SimpleType `#' Id
     *                     |  StableId
     *                     |  Path `.' type
     *                     |  `(' Types `)'
     *                     |  WildcardType
     *  }}}
     */
    def simpleType(): Type = {
      simpleTypeRest(autoPos(token match {
        case LeftParen()  => autoPos(makeTupleType(inParens(types())))
        case Underscore() => next(); atPos(in.prevTokenPos, auto)(Type.Placeholder(typeBounds()))
        case _       =>
          val ref: Term.Ref = path()
          if (token.isNot[Dot])
            convertToTypeId(ref) getOrElse { syntaxError("identifier expected", at = ref) }
          else {
            next()
            accept[KwType]
            val refOrQuasi = ref match {
              case quasi: Term.Name.Quasi =>
                quasi.become[Term.Ref.Quasi]
              case ref => ref
            }
            Type.Singleton(refOrQuasi)
          }
      }))
    }

    def simpleTypeRest(t: Type): Type = token match {
      case Hash()      => next(); simpleTypeRest(atPos(t, auto)(Type.Project(t, typeName())))
      case LeftBracket() => simpleTypeRest(atPos(t, auto)(Type.Apply(t, typeArgs())))
      case _           => t
    }


    /** {{{
     *  CompoundType ::= ModType {with ModType} [Refinement]
     *                |  Refinement
     *  }}}
     */
    def compoundType(): Type = compoundTypeRest {
      if (token.is[LeftBrace])
        None
      else if (isSpliceFollowedBy(token.is[KwWith] || token.is[LeftBrace] || (token.is[LF] && ahead (token.is[LeftBrace]))))
        Some(ellipsis(1, astInfo[Type]))
      else
        Some(annotType())
    }

    // TODO: warn about def f: Unit { } case?
    def compoundTypeRest(t: Option[Type]): Type = atPos(t, auto) {
      val ts = new ListBuffer[Type] ++ t
      while (token.is[KwWith]) {
        next()
        if (token.is[Ellipsis]) ts += ellipsis(1, astInfo[Type])
        else ts += annotType()
      }
      newLineOptWhenFollowedBy[LeftBrace]
      val types = ts.toList
      if (token.is[LeftBrace]) {
        val refinements = refinement()
        val hasQuasi = t match {
          case q @ Some(Type.Quasi(1, _)) => true
          case _ => false
        }
        (types, refinements) match {
          case (typ :: Nil, Nil) if !hasQuasi => typ
          case _  => Type.Compound(types, refinements)
        }
      } else {
        if (types.length == 1) types.head
        else Type.Compound(types, Nil)
      }
    }

    def infixTypeRest(t: Type, mode: InfixMode.Value): Type = atPos(t, auto) {
      if (isIdent || token.is[Unquote]) {
        if (isRawStar && ahead(token.is[RightParen] || token.is[Comma] || token.is[Equals] || token.is[RightBrace] || token.is[EOF])) {
          // we assume that this is a type specification for a vararg parameter
          t
        } else {
          val name = termName(advance = false)
          val leftAssoc = name.isLeftAssoc
          if (mode != InfixMode.FirstOp) checkAssoc(name, leftAssoc = mode == InfixMode.LeftOp)
          val op = typeName()
          newLineOptWhenFollowing(_.is[TypeIntro])
          def mkOp(t1: Type) = atPos(t, t1)(Type.ApplyInfix(t, op, 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): Type =
      infixTypeRest(compoundType(), mode)

    /** {{{
     *  Types ::= Type {`,' Type}
     *  }}}
     */
    def types(): List[Type] = commaSeparated(argType())

    // TODO: I have a feeling that we no longer need PatternContextSensitive
    // now that we have Pat.Type separate from Type
    def patternTyp(allowInfix: Boolean, allowImmediateTypevars: Boolean): Pat.Type = {
      def loop(tpe: Type, convertTypevars: Boolean): Pat.Type = tpe match {
        case q: Type.Quasi =>
          q.become[Pat.Type.Quasi]
        case tpe @ Type.Name(value) if convertTypevars && value(0).isLower =>
          atPos(tpe, tpe)(Pat.Var.Type(tpe))
        case tpe: Type.Name =>
          tpe
        case tpe: Type.Select =>
          tpe
        case Type.Project(qual, name) =>
          val qual1 = loop(qual, convertTypevars = false)
          val name1 = name
          atPos(tpe, tpe)(Pat.Type.Project(qual1, name1))
        case tpe: Type.Singleton =>
          tpe
        case Type.Apply(underlying, args) =>
          val underlying1 = loop(underlying, convertTypevars = false)
          val args1 = args.map(arg => loop(arg, convertTypevars = true))
          atPos(tpe, tpe)(Pat.Type.Apply(underlying1, args1))
        case Type.ApplyInfix(lhs, op, rhs) =>
          val lhs1 = loop(lhs, convertTypevars = false)
          val op1 = op
          val rhs1 = loop(rhs, convertTypevars = false)
          atPos(tpe, tpe)(Pat.Type.ApplyInfix(lhs1, op1, rhs1))
        case Type.Function(params, res) =>
          val params1 = params.map {
            case q @ Type.Arg.Quasi(1, Type.Arg.Quasi(0, _)) => q.become[Pat.Type.Quasi]
            case p => loop(p.require[Type], convertTypevars = true)
          }
          val res1 = loop(res, convertTypevars = false)
          atPos(tpe, tpe)(Pat.Type.Function(params1, res1))
        case Type.Tuple(elements) =>
          val elements1 = elements.map(el => loop(el, convertTypevars = true))
          atPos(tpe, tpe)(Pat.Type.Tuple(elements1))
        case Type.Compound(tpes, refinement) =>
          val tpes1 = tpes.map(tpe => loop(tpe, convertTypevars = false))
          val refinement1 = refinement
          atPos(tpe, tpe)(Pat.Type.Compound(tpes1, refinement1))
        case Type.Existential(underlying, quants) =>
          val underlying1 = loop(underlying, convertTypevars = false)
          val quants1 = quants
          atPos(tpe, tpe)(Pat.Type.Existential(underlying1, quants1))
        case Type.Annotate(underlying, annots) =>
          val underlying1 = loop(underlying, convertTypevars = false)
          val annots1 = annots
          atPos(tpe, tpe)(Pat.Type.Annotate(underlying1, annots1))
        case Type.Placeholder(Type.Bounds(None, None)) if convertTypevars =>
          atPos(tpe, tpe)(Pat.Type.Wildcard())
        case Type.Placeholder(bounds) =>
          val bounds1 = bounds
          atPos(tpe, tpe)(Pat.Type.Placeholder(bounds1))
        case tpe: Lit =>
          tpe
      }
      val t: Type = {
        if (allowInfix) {
          val t = if (token.is[LeftParen]) tupleInfixType() else compoundType()
          def mkOp(t1: Type) = atPos(t, t1)(Type.ApplyInfix(t, typeName(), t1))
          token match {
            case KwForsome() => next(); atPos(t, t)(Type.Existential(t, existentialStats()))
            case Unquote(_) | Ident(_) if !isRawBar => infixTypeRest(mkOp(compoundType()), InfixMode.LeftOp)
            case _ => t
          }
        } else {
          compoundType()
        }
      }
      loop(t, convertTypevars = allowImmediateTypevars)
    }

    def quasiquotePatternTyp(allowInfix: Boolean, allowImmediateTypevars: Boolean): Pat.Type = autoPos {
      // NOTE: As per quasiquotes.md
      // * pt"_" => Pat.Type.Wildcard (doesn't parse)
      // * pt"x" => Pat.Var.Type (needs postprocessing, parsed as Type.Name)
      // * pt"X" => error
      // * pt"`x`" => Type.Name (ok)
      // * pt"`X`" => Type.Name (ok)
      token match {
        case Underscore() if ahead(token.is[EOF]) =>
          next()
          Pat.Type.Wildcard()
        case _ =>
          val bqIdent = isIdent && isBackquoted
          val nonbqIdent = isIdent && !isBackquoted
          val ptpe = patternTyp(allowInfix = true, allowImmediateTypevars = false)
          ptpe match {
            case ptpe: Type.Name if nonbqIdent =>
              if (ptpe.value.head.isLower) atPos(ptpe, ptpe)(Pat.Var.Type(ptpe))
              else syntaxError("Pattern type variables must start with a lower-case letter", at = ptpe)
            case ptpe: Type.Name if bqIdent =>
              syntaxError("Pattern type variables must not be enclosed in backquotes", at = ptpe)
            case _ =>
              ptpe
          }
      }
    }

    def patternTypeArgs() = inBrackets(commaSeparated(patternTyp(allowInfix = true, allowImmediateTypevars = true)))
  }

  private trait AllowedName[T]
  private object AllowedName {
    implicit object AllowedTermName extends AllowedName[Term.Name]
    implicit object AllowedTypeName extends AllowedName[Type.Name]
  }
  private def name[T <: Tree : AllowedName : AstInfo](ctor: String => T, advance: Boolean): T = token match {
    case Ident(value) =>
      val name = value.stripPrefix("`").stripSuffix("`")
      val res = atPos(in.tokenPos, in.tokenPos)(ctor(name))
      if (advance) next()
      res
    case Unquote(_) =>
      unquote[T](advance)
    case _ =>
      syntaxErrorExpected[Ident]
  }
  def termName(advance: Boolean = true): Term.Name = name(Term.Name(_), advance)
  def typeName(advance: Boolean = true): Type.Name = name(Type.Name(_), advance)

  /** {{{
   *  Path       ::= StableId
   *              |  [Ident `.'] this
   *  ModType ::= Path [`.' type]
   *  }}}
   */
  // TODO: this has to be rewritten
  def path(thisOK: Boolean = true): Term.Ref = {
    val startsAtBof = token.prev.is[BOF]
    def endsAtEof = token.is[EOF]
    def stop = token.isNot[Dot] || ahead { token.isNot[KwThis] && token.isNot[KwSuper] && token.isNot[Ident] && token.isNot[Unquote] }
    if (token.is[KwThis]) {
      val anonqual = atPos(in.tokenPos, in.prevTokenPos)(Name.Anonymous())
      next()
      val thisp = atPos(in.prevTokenPos, auto)(Term.This(anonqual))
      if (stop && thisOK) thisp
      else {
        accept[Dot]
        selectors(thisp)
      }
    } else if (token.is[KwSuper]) {
      val anonqual = atPos(in.tokenPos, in.prevTokenPos)(Name.Anonymous())
      next()
      val superp = atPos(in.prevTokenPos, auto)(Term.Super(anonqual, mixinQualifier()))
      if (startsAtBof && endsAtEof && inQuasiquote) return superp
      accept[Dot]
      val supersel = atPos(superp, auto)(Term.Select(superp, termName()))
      if (stop) supersel
      else {
        next()
        selectors(supersel)
      }
    } else {
      val name = termName()
      if (stop) name
      else {
        next()
        if (token.is[KwThis]) {
          next()
          val qual = name match {
            case q: Term.Name.Quasi => q.become[Name.Qualifier.Quasi]
            case name => atPos(name, name)(Name.Indeterminate(name.value))
          }
          val thisp = atPos(name, auto)(Term.This(qual))
          if (stop && thisOK) thisp
          else {
            accept[Dot]
            selectors(thisp)
          }
        } else if (token.is[KwSuper]) {
          next()
          val qual = name match {
            case q: Term.Name.Quasi => q.become[Name.Qualifier.Quasi]
            case name => atPos(name, name)(Name.Indeterminate(name.value))
          }
          val superp = atPos(name, auto)(Term.Super(qual, mixinQualifier()))
          if (startsAtBof && endsAtEof && inQuasiquote) return superp
          accept[Dot]
          val supersel = atPos(superp, auto)(Term.Select(superp, termName()))
          if (stop) supersel
          else {
            next()
            selectors(supersel)
          }
        } else {
          selectors(name match {
            case q: Term.Name.Quasi => q.become[Term.Quasi]
            case name => name
          })
        }
      }
    }
  }

  def selector(t: Term): Term.Select = atPos(t, auto)(Term.Select(t, termName()))
  def selectors(t: Term): Term.Ref = {
    val t1 = selector(t)
    if (token.is[Dot] && ahead { token.is[Ident] }) {
      next()
      selectors(t1)
    }
    else t1
  }

  /** {{{
  *   MixinQualifier ::= `[' Id `]'
  *   }}}
  */
  def mixinQualifier(): Name.Qualifier = {
    if (token.is[LeftBracket]) {
      inBrackets {
        typeName() match {
          case q: Type.Name.Quasi => q.become[Name.Qualifier.Quasi]
          case name => atPos(name, name)(Name.Indeterminate(name.value))
        }

      }
    } else {
      atPos(in.tokenPos, in.prevTokenPos)(Name.Anonymous())
    }
  }

  /** {{{
   *  StableId ::= Id
   *            |  Path `.' Id
   *            |  [id `.'] super [`[' id `]']`.' id
   *  }}}
   */
  def stableId(): Term.Ref =
    path(thisOK = false)

  /** {{{
  *   QualId ::= Id {`.' Id}
  *   }}}
  */
  def qualId(): Term.Ref = {
    val name = termName() match {
      case quasi: Term.Name.Quasi =>
        quasi.become[Term.Ref.Quasi]
      case ref => ref
    }
    if (token.isNot[Dot]) name
    else {
      next()
      selectors(name)
    }
  }

  /** {{{
   *  SimpleExpr    ::= literal
   *                  | symbol
   *                  | null
   *  }}}
   */
  def literal(isNegated: Boolean = false): Lit = autoPos {
    def isHex = token.syntax.startsWith("0x") || token.syntax.startsWith("0X")
    val res = token match {
      case Constant.Int(rawValue) =>
        val value = if (isNegated) -rawValue else rawValue
        val min = if (isHex) BigInt(Int.MinValue) * 2 + 1 else BigInt(Int.MinValue)
        val max = if (isHex) BigInt(Int.MaxValue) * 2 + 1 else BigInt(Int.MaxValue)
        if (value > max) syntaxError("integer number too large", at = token)
        else if (value < min) syntaxError("integer number too small", at = token)
        Lit(value.toInt)
      case Constant.Long(rawValue) =>
        val value = if (isNegated) -rawValue else rawValue
        val min = if (isHex) BigInt(Long.MinValue) * 2 + 1 else BigInt(Long.MinValue)
        val max = if (isHex) BigInt(Long.MaxValue) * 2 + 1 else BigInt(Long.MaxValue)
        if (value > max) syntaxError("integer number too large", at = token)
        else if (value < min) syntaxError("integer number too small", at = token)
        Lit(value.toLong)
      case Constant.Float(rawValue) =>
        val value = if (isNegated) -rawValue else rawValue
        if (value > Float.MaxValue) syntaxError("floating point number too large", at = token)
        else if (value < Float.MinValue) syntaxError("floating point number too small", at = token)
        Lit(value.toFloat)
      case Constant.Double(rawValue) =>
        val value = if (isNegated) -rawValue else rawValue
        if (value > Double.MaxValue) syntaxError("floating point number too large", at = token)
        else if (value < Double.MinValue) syntaxError("floating point number too small", at = token)
        Lit(value.toDouble)
      case Constant.Char(value) =>
        Lit(value)
      case Constant.String(value) =>
        Lit(value)
      case Constant.Symbol(value) =>
        Lit(value)
      case KwTrue() =>
        Lit(true)
      case KwFalse() =>
        Lit(false)
      case KwNull() =>
        Lit(null)
      case _ =>
        unreachable(debug(token))
    }
    next()
    res
  }

  def interpolate[Ctx <: Tree, Ret <: Tree](arg: () => Ctx, result: (Term.Name, List[Lit], List[Ctx]) => Ret): Ret = autoPos {
    val interpolator = {
      val name = token match {
        case Xml.Start() => Term.Name("xml")
        case Interpolation.Id(value) => Term.Name(value)
        case _ => unreachable(debug(token))
      }
      atPos(in.tokenPos, in.tokenPos)(name)
    }
    if (token.is[Interpolation.Id]) next()
    val partsBuf = new ListBuffer[Lit]
    val argsBuf = new ListBuffer[Ctx]
    def loop(): Unit = token match {
      case Interpolation.Start() | Xml.Start() =>
        next()
        loop()
      case Interpolation.Part(value) =>
        partsBuf += atPos(in.tokenPos, in.tokenPos)(Lit(value))
        next()
        loop()
      case Xml.Part(value) =>
        partsBuf += atPos(in.tokenPos, in.tokenPos)(Lit(value))
        next()
        loop()
      case Interpolation.SpliceStart() | Xml.SpliceStart() =>
        next()
        argsBuf += arg()
        loop()
      case Interpolation.SpliceEnd() | Xml.SpliceEnd() =>
        next()
        loop()
      case Interpolation.End() | Xml.End() =>
        next(); // simply return
      case _ =>
        unreachable(debug(token, token.structure))
    }
    loop()
    result(interpolator, partsBuf.toList, argsBuf.toList)
  }

  def interpolateTerm(): Term.Interpolate = {
    interpolate[Term, Term.Interpolate](unquoteExpr _, Term.Interpolate.apply _)
  }

  def xmlTerm(): Term.Xml =
    interpolate[Term, Term.Xml](unquoteXmlExpr _, (_, parts, args) => Term.Xml.apply(parts, args))

  def interpolatePat(): Pat.Interpolate =
    interpolate[Pat, Pat.Interpolate](unquotePattern _, Pat.Interpolate.apply _)

  def xmlPat(): Pat.Xml =
    interpolate[Pat, Pat.Xml](unquoteXmlPattern _, (_, parts, args) => Pat.Xml.apply(parts, args))

/* ------------- NEW LINES ------------------------------------------------- */

  def newLineOpt(): Unit = {
    if (token.is[LF]) next()
  }

  def newLinesOpt(): Unit = {
    if (token.is[LF] || token.is[LFLF])
      next()
  }

  def newLineOptWhenFollowedBy[T <: Token : TokenInfo]: Unit = {
    // note: next is defined here because current is token.LF
    if (token.is[LF] && ahead { token.is[T] }) newLineOpt()
  }

  def newLineOptWhenFollowing(p: Token => Boolean): Unit = {
    // note: next is defined here because current is token.LF
    if (token.is[LF] && ahead { p(token) }) newLineOpt()
  }

/* ------------- TYPES ---------------------------------------------------- */

  /** {{{
   *  TypedOpt ::= [`:' Type]
   *  }}}
   */
  def typedOpt(): Option[Type] =
    if (token.is[Colon]) { next(); Some(typ()) }
    else None

  def typeOrInfixType(location: Location): Type =
    if (location == Local) typ()
    else startInfixType()

  def annotTypeRest(t: Type): Type = atPos(t, auto) {
    val annots = this.annots(skipNewLines = false)
    if (annots.isEmpty) t
    else Type.Annotate(t, annots)
  }

/* ----------- EXPRESSIONS ------------------------------------------------ */

  def condExpr(): Term = {
    accept[LeftParen]
    val r = expr()
    accept[RightParen]
    r
  }

  /** {{{
   *  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(): Term = expr(Local)

  def unquoteExpr(): Term = {
    def dropTrivialBlock(term: Term): Term = term match {
      case Term.Block((stat: Term) :: Nil) => stat
      case _ => term
    }
    token match {
      case Ident(_)    => termName()
      case LeftBrace() => dropTrivialBlock(expr())
      case KwThis()    => val qual = atPos(in.tokenPos, in.prevTokenPos)(Name.Anonymous()); next(); atPos(in.prevTokenPos, auto)(Term.This(qual))
      case _           => syntaxError("error in interpolated string: identifier, `this' or block expected", at = token)
    }
  }

  def unquoteXmlExpr(): Term = {
    // TODO: verify this
    dropAnyBraces(expr())
  }

  // TODO: when parsing `(2 + 3)`, do we want the ApplyInfix's position to include parentheses?
  // if yes, then nothing has to change here
  // if no, we need eschew autoPos here, because it forces those parentheses on the result of calling prefixExpr
  def expr(location: Location): Term = autoPos(token match {
    case KwIf() =>
      next()
      val cond = condExpr()
      newLinesOpt()
      val thenp = expr()
      if (token.is[KwElse]) { next(); Term.If(cond, thenp, expr()) }
      else if (token.is[Semicolon] && ahead { token.is[KwElse] }) { next(); next(); Term.If(cond, thenp, expr()) }
      else { Term.If(cond, thenp, atPos(in.tokenPos, in.prevTokenPos)(Lit(()))) }
    case KwTry() =>
      next()
      val body: Term = token match {
        case LeftBrace() => autoPos(inBracesOrUnit(block()))
        case LeftParen() => inParensOrUnit(expr())
        case _      => expr()
      }
      val catchopt =
        if (token.isNot[KwCatch]) None
        else {
          next()
          if (token.isNot[LeftBrace]) Some(expr())
          else inBraces {
            if (token.is[CaseIntro] || token.is[Ellipsis]) Some(caseClauses())
            else Some(expr())
          }
        }
      val finallyopt = token match {
        case KwFinally() => next(); Some(expr())
        case _           => None
      }
      catchopt match {
        case None => Term.TryWithCases(body, Nil, finallyopt)
        case Some(cases: List[_]) => Term.TryWithCases(body, cases.require[List[Case]], finallyopt)
        case Some(term: Term) => Term.TryWithTerm(body, term, finallyopt)
        case _ => unreachable(debug(catchopt))
      }
    case KwWhile() =>
      next()
      val cond = condExpr()
      newLinesOpt()
      val body = expr()
      Term.While(cond, body)
    case KwDo() =>
      next()
      val body = expr()
      while (token.is[StatSep]) next()
      accept[KwWhile]
      val cond = condExpr()
      Term.Do(body, cond)
    case KwFor() =>
      next()
      val enums =
        if (token.is[LeftBrace]) inBracesOrNil(enumerators())
        else inParensOrNil(enumerators())
      newLinesOpt()
      if (token.is[KwYield]) {
        next()
        Term.ForYield(enums, expr())
      } else {
        Term.For(enums, expr())
      }
    case KwReturn() =>
      next()
      if (token.is[ExprIntro]) Term.Return(expr())
      else Term.Return(atPos(in.tokenPos, auto)(Lit(())))
    case KwThrow() =>
      next()
      Term.Throw(expr())
    case KwImplicit() =>
      next()
      implicitClosure(location)
    case _ =>
      var t: Term = autoPos(postfixExpr())
      if (token.is[Equals]) {
        t match {
          case ref: Term.Ref =>
            next()
            t = atPos(ref, auto)(Term.Assign(ref, expr()))
          case app: Term.Apply =>
            def decompose(term: Term): (Term, List[List[Term.Arg]]) = term match {
              case Term.Apply(fun, args) =>
                val (core, otherArgss) = decompose(fun)
                (core, args.toList +: otherArgss)
              case term =>
                (term, Nil)
            }
            val (core, argss) = decompose(app)
            next()
            t = atPos(core, auto)(Term.Update(core, argss, expr()))
          case q: Term.Quasi =>
            next()
            t = atPos(q, auto)(Term.Assign(q.become[Term.Ref.Quasi], expr()))
          case _ =>
        }
      } else if (token.is[Colon]) {
        next()
        if (token.is[At] || (token.is[Ellipsis] && ahead(token.is[At]))) {
          t = atPos(t, auto)(Term.Annotate(t, annots(skipNewLines = false)))
        } else {
          t = {
            val tpt = typeOrInfixType(location)
            // this does not correspond to syntax, but is necessary to
            // accept closures. We might restrict closures to be between {...} only.
            atPos(t, tpt)(Term.Ascribe(t, tpt))
          }
        }
      } else if (token.is[KwMatch]) {
        next()
        t = atPos(t, auto)(Term.Match(t, inBracesOrNil(caseClauses())))
      }

      // Note the absense of `else if` here!!
      if (token.is[RightArrow]) {
        // This is a tricky one. In order to parse lambdas, we need to recognize token sequences
        // like `(...) => ...`, `id | _ => ...` and `implicit id | _ => ...`.
        //
        // If we exclude Implicit (which is parsed elsewhere anyway), then we can see that
        // these sequences are non-trivially ambiguous with tuples and self-type annotations
        // (i.e. are not resolvable with static lookahead).
        //
        // Therefore, when we encounter RightArrow, the part in parentheses is already parsed into a Term,
        // and we need to figure out whether that term represents what we expect from a lambda's param list
        // in order to disambiguate. The term that we have at hand might wildly differ from the param list that one would expect.
        // For example, when parsing `() => x`, we arrive at RightArrow having `Lit.Unit` as the parsed term.
        // That's why we later need `convertToParams` to make sense of what the parser has produced.
        //
        // Rules:
        // 1) `() => ...` means lambda
        // 2) `x => ...` means self-type annotation, but only in template position
        // 3) `(x) => ...` means self-type annotation, but only in template position
        // 4a) `x: Int => ...` means self-type annotation in template position
        // 4b) `x: Int => ...` means lambda in block position
        // 4c) `x: Int => ...` means ascription, i.e. `x: (Int => ...)`, in expression position
        // 5) `(x: Int) => ...` means lambda
        // 6) `(x, y) => ...` or `(x: Int, y: Int) => ...` or with more entries means lambda
        //
        // A funny thing is that scalac's parser tries to disambiguate between self-type annotations and lambdas
        // even if it's not parsing the first statement in the template. E.g. `class C { foo; x => x }` will be
        // a parse error, because `x => x` will be deemed a self-type annotation, which ends up being inapplicable there.
        val looksLikeLambda = {
          val inParens = t.tokens.nonEmpty && t.tokens.head.is[LeftParen] && t.tokens.last.is[RightParen]
          object NameLike { def unapply(tree: Tree): Boolean = tree.is[Term.Name] || tree.is[Term.Placeholder] }
          object ParamLike {
            def unapply(tree: Tree): Boolean = tree match {
              case Term.Quasi(0, _) => true
              case Term.Quasi(1, ParamLike()) => true
              case NameLike() => true
              case Term.Ascribe(Term.Quasi(0, _), _) => true
              case Term.Ascribe(NameLike(), _) => true
              case _ => false
            }
          }
          t match {
            case Lit(()) => true // 1
            case NameLike() => location != InTemplate // 2-3
            case ParamLike() => inParens || location == InBlock // 4-5
            case Term.Tuple(xs) => xs.forall(ParamLike.unapply) // 6
            case _ => false
          }
        }
        if (looksLikeLambda) {
          next()
          t = atPos(t, auto)(Term.Function(convertToParams(t), if (location != InBlock) expr() else block()))
        } else {
          // do nothing, which will either allow self-type annotation parsing to kick in
          // or will trigger an unexpected token error down the line
        }
      }
      t
  })

  /** {{{
   *  Expr ::= implicit Id => Expr
   *  }}}
   */

  def implicitClosure(location: Location): Term.Function = {
    require(token.isNot[KwImplicit] && debug(token))
    val implicitPos = in.prevTokenPos
    val paramName = termName()
    val paramTpt = if (token.is[Colon]) { next(); Some(typeOrInfixType(location)) } else None
    val param = atPos(implicitPos, auto)(Term.Param(List(atPos(implicitPos, implicitPos)(Mod.Implicit())), paramName, paramTpt, None))
    accept[RightArrow]
    atPos(implicitPos, auto)(Term.Function(List(param), if (location != InBlock) expr() else block()))
  }

  // Encapsulates state and behavior of parsing infix syntax.
  // See `postfixExpr` for an involved usage example.
  // Another, much less involved usage, lives in `pattern3`.
  sealed abstract class InfixContext {
    // Lhs is the type of the left-hand side of an infix expression.
    // (Lhs, op and targs form UnfinishedInfix).
    // Rhs if the type of the right-hand side.
    // FinishedInfix is the type of an infix expression.
    // The conversions are necessary to push the output of finishInfixExpr on stack.
    type Lhs
    type Rhs
    // type UnfinishedInfix (see below)
    type FinishedInfix
    def toLhs(rhs: Rhs): Lhs
    def toRhs(fin: FinishedInfix): Rhs

    // Represents an unfinished infix expression, e.g. [a * b +] in `a * b + c`.
    // 1) T is either Term for infix syntax in expressions or Pat for infix syntax in patterns.
    // 2) We need to carry lhsStart/lhsEnd separately from lhs.pos
    //    because their extent may be bigger than lhs because of parentheses or whatnot.
    case class UnfinishedInfix(lhsStart: Pos, lhs: Lhs, lhsEnd: Pos, op: Term.Name, targs: List[Type]) {
      def precedence = op.precedence
      override def toString = {
        val s_lhs = lhs match {
          case tree: Tree => tree.toString
          case List(tree) => tree.toString
          case List(trees @ _*) => "(" + trees.mkString(", ") + ")"
        }
        val s_targs = if (targs.nonEmpty) "[" + targs.mkString(", ") + "]" else ""
        s"[$s_lhs $op$s_targs]"
      }
    }

    // The stack of unfinished infix expressions, e.g. Stack([a + ]) in `a + b [*] c`.
    // `push` takes `b`, reads `*`, checks for type arguments and adds [b *] on the top of the stack.
    // Other methods working on the stack are self-explanatory.
    var stack: List[UnfinishedInfix] = Nil
    def head = stack.head
    def push(lhsStart: Pos, lhs: Lhs, lhsEnd: Pos, op: Term.Name, targs: List[Type]): Unit = stack ::= UnfinishedInfix(lhsStart, lhs, lhsEnd, op, targs)
    def pop(): UnfinishedInfix = try head finally stack = stack.tail

    def reduceStack(stack: List[UnfinishedInfix], curr: Rhs, currEnd: Pos, op: Option[Term.Name]): Lhs = {
      val opPrecedence = op.map(_.precedence).getOrElse(0)
      val leftAssoc    = op.map(_.isLeftAssoc).getOrElse(true)

      def isDone          = this.stack == stack
      def lowerPrecedence = !isDone && (opPrecedence < this.head.precedence)
      def samePrecedence  = !isDone && (opPrecedence == this.head.precedence)
      def canReduce       = lowerPrecedence || leftAssoc && samePrecedence

      if (samePrecedence) {
        checkAssoc(this.head.op, leftAssoc)
      }

      // Pop off an unfinished infix expression off the stack and finish it with the rhs.
      // Then convert the result, so that it can become someone else's rhs.
      // Repeat while precedence and associativity allow.
      def loop(rhs: Rhs): Lhs = {
        if (!canReduce) {
          toLhs(rhs)
        } else {
          val lhs = pop()
          val fin = finishInfixExpr(lhs, rhs, currEnd)
          val rhs1 = toRhs(fin)
          loop(rhs1)
        }
      }

      loop(curr)
    }

    // Takes the unfinished infix expression, e.g. `[x +]`,
    // then takes the right-hand side (which can have multiple args), e.g. ` (y, z)`,
    // and creates `x + (y, z)`.
    // We need to carry endPos explicitly because its extent may be bigger than rhs because of parent of whatnot.
    protected def finishInfixExpr(unf: UnfinishedInfix, rhs: Rhs, rhsEnd: Pos): FinishedInfix
  }

  // Infix syntax in terms is borderline crazy.
  //
  // For example, did you know that `a * b + (c, d) * (f, g: _*)` means:
  // a.$times(b).$plus(scala.Tuple2(c, d).$times(f, g: _*))?!
  //
  // Therefore, Lhs = List[Term], Rhs = List[Term.Arg], FinishedInfix = Term.
  //
  // Actually there's even crazier stuff in scala-compiler.jar.
  // Apparently you can parse and typecheck `a + (bs: _*) * c`,
  // however I'm going to error out on this.
  object termInfixContext extends InfixContext {
    type Lhs = List[Term]
    type Rhs = List[Term.Arg]
    type FinishedInfix = Term

    def toRhs(fin: FinishedInfix): Rhs = List(fin)
    def toLhs(rhs: Rhs): Lhs = rhs.map({
      case term: Term => term
      case arg => syntaxError("`: _*' annotations are only allowed in arguments to *-parameters", at = arg)
    })

    protected def finishInfixExpr(unf: UnfinishedInfix, rhs: Rhs, rhsEnd: Pos): FinishedInfix = {
      val UnfinishedInfix(lhsStart, lhses, lhsEnd, op, targs) = unf
      val lhs = atPos(lhsStart, lhsEnd)(makeTupleTerm(lhses)) // `a + (b, c) * d` leads to creation of a tuple!
      atPos(lhsStart, rhsEnd)(Term.ApplyInfix(lhs, op, targs, checkNoTripleDots(rhs)))
    }
  }

  // In comparison with terms, patterns are trivial.
  implicit object patInfixContext extends InfixContext {
    type Lhs = Pat
    type Rhs = Pat
    type FinishedInfix = Pat

    def toRhs(fin: FinishedInfix): Rhs = fin
    def toLhs(rhs: Rhs): Lhs = rhs

    protected def finishInfixExpr(unf: UnfinishedInfix, rhs: Rhs, rhsEnd: Pos): FinishedInfix = {
      val UnfinishedInfix(lhsStart, lhs, _, op, _) = unf
      val args = rhs match { case Pat.Tuple(args) => args.toList; case _ => List(rhs) }
      atPos(lhsStart, rhsEnd)(Pat.ExtractInfix(lhs, op, checkNoTripleDots(args)))
    }
  }

  def checkAssoc(op: Term.Name, leftAssoc: Boolean): Unit = (
    if (op.isLeftAssoc != leftAssoc)
      syntaxError("left- and right-associative operators with same precedence may not be mixed", at = op)
  )

  /** {{{
   *  PostfixExpr   ::= InfixExpr [Id [nl]]
   *  InfixExpr     ::= PrefixExpr
   *                  | InfixExpr Id [nl] InfixExpr
   *  }}}
   */
  def postfixExpr(): Term = {
    val ctx  = termInfixContext
    val base = ctx.stack

    // Skip to later in the `postfixExpr` method to start mental debugging.
    // rhsStartK/rhsEndK may be bigger than then extent of rhsK,
    // so we really have to track them separately.
    def loop(rhsStartK: Pos, rhsK: ctx.Rhs, rhsEndK: Pos): ctx.Rhs = {
      if (!token.is[Ident] && !token.is[Unquote]) {
        // Infix chain has ended.
        // In the running example, we're at `a + b[]`
        // with base = List([a +]), rhsK = List([b]).
        rhsK
      } else {
        // Infix chain continues.
        // In the running example, we're at `a [+] b`.
        val op = termName() // op = [+]
        val targs = if (token.is[LeftBracket]) exprTypeArgs() else Nil // targs = Nil

        // Check whether we're still infix or already postfix by testing the current token.
        // In the running example, we're at `a + [b]` (infix).
        // If we were parsing `val c = a b`, then we'd be at `val c = a b[]` (postfix).
        newLineOptWhenFollowing(_.is[ExprIntro])
        if (token.is[ExprIntro]) {
          // Infix chain continues, so we need to reduce the stack.
          // In the running example, base = List(), rhsK = [a].
          val lhsK = ctx.reduceStack(base, rhsK, rhsEndK, Some(op)) // lhsK = [a]
          val lhsStartK = Math.min(rhsStartK.startTokenPos, lhsK.head.startTokenPos)
          ctx.push(lhsStartK, lhsK, rhsEndK, op, targs) // afterwards, ctx.stack = List([a +])

          val preRhsKplus1 = in.token
          var rhsStartKplus1: Pos = IndexPos(in.tokenPos)
          val rhsKplus1 = argumentExprsOrPrefixExpr()
          var rhsEndKplus1: Pos = IndexPos(in.prevTokenPos)
          if (preRhsKplus1.isNot[LeftBrace] && preRhsKplus1.isNot[LeftParen]) {
            rhsStartKplus1 = TreePos(rhsKplus1.head)
            rhsEndKplus1 = TreePos(rhsKplus1.head)
          }

          // Try to continue the infix chain.
          loop(rhsStartKplus1, rhsKplus1, rhsEndKplus1) // base = List([a +]), rhsKplus1 = List([b])
        } else {
          // Infix chain has ended with a postfix expression.
          // This never happens in the running example.
          // TODO: The two type conversions that I have to do here are unfortunate,
          // but I don't have time to figure our an elegant way of approaching this
          val lhsQual = ctx.reduceStack(base, rhsK, rhsEndK, Some(op))
          val finQual = lhsQual match { case List(finQual) => finQual; case _ => unreachable(debug(lhsQual)) }
          if (targs.nonEmpty) syntaxError("type application is not allowed for postfix operators", at = token)
          ctx.toRhs(atPos(finQual, op)(Term.Select(finQual, op)))
        }
      }
    }

    // Start the infix chain.
    // We'll use `a + b` as our running example.
    val rhs0 = ctx.toRhs(prefixExpr())

    // Iteratively read the infix chain via `loop`.
    // rhs0 is now [a]
    // If the next token is not an ident or an unquote, the infix chain ends immediately,
    // and `postfixExpr` becomes a fallthrough.
    val rhsN = loop(rhs0.head, rhs0, rhs0.head)

    // Infix chain has ended.
    // base contains pending UnfinishedInfix parts and rhsN is the final rhs.
    // For our running example, this'll be List([a +]) and [b].
    // Afterwards, `lhsResult` will be List([a + b]).
    val lhsResult = ctx.reduceStack(base, rhsN, in.prevTokenPos, None)

    // This is something not captured by the type system.
    // When applied to a result of `loop`, `reduceStack` will produce a singleton list.
    lhsResult match { case List(finResult) => finResult; case _ => unreachable(debug(lhsResult)) }
  }

  /** {{{
   *  PrefixExpr   ::= [`-' | `+' | `~' | `!' | `&'] SimpleExpr
   *  }}}
   */
  def prefixExpr(): Term =
    if (!isUnaryOp) simpleExpr()
    else {
      val op = termName()
      if (op.value == "-" && token.is[NumericLiteral])
        simpleExprRest(atPos(op, auto)(literal(isNegated = true)), canApply = true)
      else
        atPos(op, auto)(Term.ApplyUnary(op, simpleExpr()))
    }

  /** {{{
   *  SimpleExpr    ::= new (ClassTemplate | TemplateBody)
   *                  |  BlockExpr
   *                  |  SimpleExpr1 [`_']
   *  SimpleExpr1   ::= literal
   *                  |  xLiteral
   *                  |  Path
   *                  |  `(' [Exprs] `)'
   *                  |  SimpleExpr `.' Id
   *                  |  SimpleExpr TypeArgs
   *                  |  SimpleExpr1 ArgumentExprs
   *  }}}
   */
  def simpleExpr(): Term = autoPos {
    var canApply = true
    val t: Term = {
      token match {
        case Literal() =>
          literal()
        case Interpolation.Id(_) =>
          interpolateTerm()
        case Xml.Start() =>
          xmlTerm()
        case Ident(_) | KwThis() | KwSuper() | Unquote(_) =>
          path() match {
            case q: Term.Ref.Quasi => q.become[Term.Quasi]
            case path => path
          }
        case Underscore() =>
          next()
          atPos(in.prevTokenPos, in.prevTokenPos)(Term.Placeholder())
        case LeftParen() =>
          makeTupleTermParens(commaSeparated(expr()))
        case LeftBrace() =>
          canApply = false
          blockExpr()
        case KwNew() =>
          canApply = false
          next()
          atPos(in.prevTokenPos, auto)(Term.New(template()))
        case _ =>
          syntaxError(s"illegal start of simple expression", at = token)
      }
    }
    simpleExprRest(t, canApply = canApply)
  }

  def simpleExprRest(t: Term, canApply: Boolean): Term = atPos(t, auto) {
    if (canApply) newLineOptWhenFollowedBy[LeftBrace]
    token match {
      case Dot() =>
        next()
        simpleExprRest(selector(t), canApply = true)
      case LeftBracket() =>
        t match {
          case _: Term.Quasi | _: Term.Name | _: Term.Select | _: Term.Apply =>
            var app: Term = t
            while (token.is[LeftBracket])
              app = atPos(t, auto)(Term.ApplyType(app, exprTypeArgs()))

            simpleExprRest(app, canApply = true)
          case _ =>
            t
        }
      case LeftParen() | LeftBrace() if (canApply) =>
        simpleExprRest(atPos(t, auto)(Term.Apply(t, argumentExprs())), canApply = true)
      case Underscore() =>
        next()
        Term.Eta(t)
      case _ =>
        t
    }
  }

  def argumentExprsOrPrefixExpr(): List[Term.Arg] = {
    if (token.isNot[LeftBrace] && token.isNot[LeftParen]) prefixExpr() :: Nil
    else {
      def argsToTerm(args: List[Term.Arg], openParenPos: Int, closeParenPos: Int): Term = {
        def badRep(rep: Term.Arg.Repeated) = syntaxError("repeated argument not allowed here", at = rep)
        def loop(args: List[Term.Arg]): List[Term] = args match {
          case Nil                                                 => Nil
          case (t: Term) :: rest                                   => t :: loop(rest)
          case (nmd @ Term.Arg.Named(name, rhs: Term)) :: rest     => atPos(nmd, nmd)(Term.Assign(name, rhs)) :: loop(rest)
          case (Term.Arg.Named(_, rep: Term.Arg.Repeated)) :: rest => badRep(rep)
          case (rep: Term.Arg.Repeated) :: rest                    => badRep(rep)
          case _                                                   => unreachable(debug(args))
        }
        atPos(openParenPos, closeParenPos)(makeTupleTerm(loop(args)))
      }
      val openParenPos = in.tokenPos
      val args = argumentExprs()
      val closeParenPos = in.prevTokenPos
      token match {
        case Dot() | LeftBracket() | LeftParen() | LeftBrace() | Underscore() =>
          simpleExprRest(argsToTerm(args, openParenPos, closeParenPos), canApply = true) :: Nil
        case _ =>
          args match {
            case arg :: Nil => atPos(openParenPos, closeParenPos)(arg) :: Nil
            case other => other
          }
      }
    }
  }

  def argumentExpr(): Term.Arg = {
    expr() match {
      case q: Quasi =>
        q.become[Term.Arg.Quasi]
      case Term.Ascribe(t1, Type.Placeholder(Type.Bounds(None, None))) if isIdentOf("*") =>
        next()
        atPos(t1, auto)(Term.Arg.Repeated(t1))
      case Term.Assign(t1: Term.Name, Term.Ascribe(t2, Type.Placeholder(Type.Bounds(None, None)))) if isIdentOf("*") =>
        next()
        atPos(t1, auto)(Term.Arg.Named(t1, atPos(t2, auto)(Term.Arg.Repeated(t2))))
      case Term.Assign(t2: Term.Name, rhs) =>
        atPos(t2, auto)(Term.Arg.Named(t2, rhs))
      case other =>
        other
    }
  }

  /** {{{
   *  ArgumentExprs ::= `(' [Exprs] `)'
   *                  | [nl] BlockExpr
   *  }}}
   */
  def argumentExprs(): List[Term.Arg] = token match {
    case LeftBrace() =>
      List(blockExpr())
    case LeftParen() =>
      inParens(token match {
        case RightParen() =>
          Nil
        case tok: Ellipsis if tok.rank == 2 =>
          List(ellipsis(2, astInfo[Term.Arg]))
        case _ =>
          commaSeparated(argumentExpr)
      })
    case _ =>
      Nil
  }

  private def checkNoTripleDots[T <: Tree](trees: Seq[T]): Seq[T] = {
    val illegalQuasis = trees.collect{ case q: Quasi if q.rank == 2 => q }
    illegalQuasis.foreach(q => syntaxError(Messages.QuasiquoteRankMismatch(q.rank, 1), at = q))
    trees
  }

  /** {{{
   *  BlockExpr ::= `{' (CaseClauses | Block) `}'
   *  }}}
   */
  def blockExpr(): Term = autoPos {
    inBraces {
      if (token.is[CaseIntro] || (token.is[Ellipsis] && ahead(token.is[KwCase]))) Term.PartialFunction(caseClauses())
      else block()
    }
  }

  /** {{{
   *  Block ::= BlockStatSeq
   *  }}}
   *  @note  Return tree does not carry position.
   */
  def block(): Term = autoPos {
    Term.Block(blockStatSeq())
  }

  def caseClause(): Case = atPos(in.prevTokenPos, auto) {
    require(token.isNot[KwCase] && debug(token))
    Case(pattern().require[Pat], guard(), {
      accept[RightArrow]
      val start = in.tokenPos
      def end = in.prevTokenPos
      blockStatSeq() match {
        case List(q: Quasi) => q.become[Term.Quasi]
        case List(term: Term) => term
        case other => atPos(start, end)(Term.Block(other))
      }
    })
  }

  /** {{{
   *  CaseClauses ::= CaseClause {CaseClause}
   *  CaseClause  ::= case Pattern [Guard] `=>' Block
   *  }}}
   */
  def caseClauses(): List[Case] = {
    val cases = new ListBuffer[Case]
    while (token.is[CaseIntro] || token.is[Ellipsis]) {
      if (token.is[Ellipsis]) {
        cases += ellipsis(1, astInfo[Case], accept[KwCase])
        while (token.is[StatSep]) next()
      } else if (token.is[KwCase] && ahead(token.is[Unquote])) {
        next()
        cases += unquote[Case]
        while (token.is[StatSep]) next()
      } else {
        next()
        cases += caseClause()
      }
    }
    if (cases.isEmpty)  // trigger error if there are no cases
      accept[KwCase]
    cases.toList
  }

  /** {{{
   *  Guard ::= if PostfixExpr
   *  }}}
   */
  def guard(): Option[Term] =
    if (token.is[KwIf]) { next(); Some(autoPos(postfixExpr())) }
    else None

  /** {{{
   *  Enumerators ::= Generator {semi Enumerator}
   *  Enumerator  ::=  Generator
   *                |  Guard
   *                |  val Pattern1 `=' Expr
   *  }}}
   */
  def enumerators(): List[Enumerator] = {
    val enums = new ListBuffer[Enumerator]
    enums ++= enumerator(isFirst = true)
    while (token.is[StatSep]) {
      next()
      enums ++= enumerator(isFirst = false)
    }
    enums.toList
  }

  def enumerator(isFirst: Boolean, allowNestedIf: Boolean = true): List[Enumerator] =
    if (token.is[KwIf] && !isFirst) autoPos(Enumerator.Guard(guard().get)) :: Nil
    else if (token.is[Ellipsis]) {
      ellipsis(1, astInfo[Enumerator.Generator]) :: Nil
    } else if (token.is[Unquote] && ahead(!token.is[Equals] && !token.is[LeftArrow])) { // support for q"for ($enum1; ..$enums; $enum2)"
      unquote[Enumerator.Generator] :: Nil
    }
    else generator(!isFirst, allowNestedIf)

  /** {{{
   *  Generator ::= Pattern1 (`<-' | `=') Expr [Guard]
   *  }}}
   */
  def generator(eqOK: Boolean, allowNestedIf: Boolean = true): List[Enumerator] = {
    val startPos = in.tokenPos
    val hasVal = token.is[KwVal]
    if (hasVal)
      next()

    val pat   = noSeq.pattern1()
    val point = token.start
    val hasEq = token.is[Equals]

    if (hasVal) {
      if (hasEq) deprecationWarning("val keyword in for comprehension is deprecated", at = token)
      else syntaxError("val in for comprehension must be followed by assignment", at = token)
    }

    if (hasEq && eqOK) next()
    else accept[LeftArrow]
    val rhs = expr()

    val head = {
      if (hasEq) atPos(startPos, auto)(Enumerator.Val(pat.require[Pat], rhs))
      else atPos(startPos, auto)(Enumerator.Generator(pat.require[Pat], rhs))
    }
    val tail = {
      def loop(): List[Enumerator] = {
        if (token.isNot[KwIf]) Nil
        else autoPos(Enumerator.Guard(guard().get)) :: loop()
      }
      if (allowNestedIf) loop()
      else Nil
    }
    head :: tail
  }

/* -------- 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 {
    // is a sequence pattern _* allowed?
    def isSequenceOK: Boolean

    // are we in an XML pattern?
    def isXML: Boolean = false

    def functionArgType(): Type.Arg = paramType()
    def argType(): Type = typ()

    /** {{{
     *  Patterns ::= Pattern { `,' Pattern }
     *  SeqPatterns ::= SeqPattern { `,' SeqPattern }
     *  }}}
     */
    def patterns(): List[Pat.Arg] = commaSeparated(pattern())

    /** {{{
     *  Pattern  ::=  Pattern1 { `|' Pattern1 }
     *  SeqPattern ::= SeqPattern1 { `|' SeqPattern1 }
     *  }}}
     */
    def pattern(): Pat.Arg = {
      def loop(pat: Pat.Arg): Pat.Arg =
        if (!isRawBar) pat
        else { next(); atPos(pat, auto)(Pat.Alternative(pat.require[Pat], pattern().require[Pat])) }
      loop(pattern1())
    }

    private def topLevelNamesToPats[T >: Pat](op: => T): T = {
      // NOTE: As per quasiquotes.md
      // * p"x" => Pat.Var.Term (ok)
      // * p"X" => Pat.Var.Term (needs postprocessing, parsed as Term.Name)
      // * p"`x`" => Term.Name (ok)
      // * p"`X`" => Term.Name (ok)
      val nonbqIdent = isIdent && !isBackquoted
      val pat = op
      pat match {
        case pat: Term.Name if nonbqIdent => atPos(pat, pat)(Pat.Var.Term(pat))
        case _ => pat
      }
    }

    def quasiquotePattern(): Pat.Arg = {
      topLevelNamesToPats(pattern())
    }

    def unquotePattern(): Pat = {
      dropAnyBraces(pattern().require[Pat])
    }

    def unquoteXmlPattern(): Pat = {
      // TODO: verify this
      dropAnyBraces(pattern().require[Pat])
    }

    def quasiquotePatternArg(): Pat.Arg = {
      topLevelNamesToPats(argumentPattern())
    }

    /** {{{
     *  Pattern1    ::= varid `:' TypePat
     *                |  `_' `:' TypePat
     *                |  Pattern2
     *  SeqPattern1 ::= varid `:' TypePat
     *                |  `_' `:' TypePat
     *                |  [SeqPattern2]
     *  }}}
     */
    def pattern1(): Pat.Arg = autoPos {
      val p = pattern2()
      if (token.isNot[Colon]) p
      else {
        p match {
          case p: Pat.Quasi =>
            nextOnce()
            Pat.Typed(p, patternTyp(allowInfix = false, allowImmediateTypevars = false))
          case p: Pat.Var.Term if dialect.bindToSeqWildcardDesignator == ":" && isColonWildcardStar =>
            nextOnce()
            val wildcard = autoPos({ nextTwice(); Pat.Arg.SeqWildcard() })
            Pat.Bind(p, wildcard)
          case p: Pat.Var.Term =>
            nextOnce()
            Pat.Typed(p, patternTyp(allowInfix = false, allowImmediateTypevars = false))
          case p: Pat.Wildcard if dialect.bindToSeqWildcardDesignator == ":" && isColonWildcardStar =>
            nextThrice()
            Pat.Arg.SeqWildcard()
          case p: Pat.Wildcard =>
            nextOnce()
            Pat.Typed(p, patternTyp(allowInfix = false, allowImmediateTypevars = false))
          case p =>
            p
        }
      }
    }

    /** {{{
     *  Pattern2    ::=  varid [ @ Pattern3 ]
     *                |   Pattern3
     *  SeqPattern2 ::=  varid [ @ SeqPattern3 ]
     *                |   SeqPattern3
     *  }}}
     */
    def pattern2(): Pat.Arg = autoPos {
      val p = pattern3()
      if (token.isNot[At]) p
      else p match {
        case p: Pat.Quasi =>
          next()
          val lhs = p.become[Pat.Var.Term.Quasi]
          val rhs = pattern3() match {
            case q @ Pat.Quasi(0, _) => q.become[Pat.Arg.Quasi]
            case p => p
          }
          Pat.Bind(lhs, rhs)
        case p: Term.Name =>
          syntaxError("Pattern variables must start with a lower-case letter. (SLS 8.1.1.)", at = p)
        case p: Pat.Var.Term =>
          next()
          Pat.Bind(p, pattern3())
        case p: Pat.Wildcard =>
          next()
          pattern3()
        case p =>
          p
      }
    }

    /** {{{
     *  Pattern3    ::= SimplePattern
     *                |  SimplePattern {Id [nl] SimplePattern}
     *  }}}
     */
    def pattern3(): Pat.Arg = {
      val ctx = patInfixContext
      val lhs = simplePattern(badPattern3)
      val base = ctx.stack
      // See SI-3189, SI-4832 for motivation. Cf SI-3480 for counter-motivation.
      def isCloseDelim = token match {
        case RightBrace() => isXML
        case RightParen() => !isXML
        case _      => false
      }
      def checkWildStar: Option[Pat.Arg.SeqWildcard] = lhs match {
        case Pat.Wildcard() if isSequenceOK && isRawStar && dialect.bindToSeqWildcardDesignator == "@" => peekingAhead (
          // TODO: used to be Star(lhs) | EmptyTree, why start had param?
          if (isCloseDelim) Some(atPos(lhs, auto)(Pat.Arg.SeqWildcard()))
          else None
        )
        case _ => None
      }
      def loop(rhs: ctx.Rhs): ctx.Rhs = {
        val op = if (isIdentExcept("|") || token.is[Unquote]) Some(termName()) else None
        val lhs1 = ctx.reduceStack(base, rhs, rhs, op)
        op match {
          case Some(op) =>
            if (token.is[LeftBracket]) syntaxError("infix patterns cannot have type arguments", at = token)
            ctx.push(lhs1, lhs1, lhs1, op, Nil)
            val rhs1 = simplePattern(badPattern3)
            loop(rhs1)
          case None =>
            lhs1 // TODO: more rigorous type discipline
        }
      }
      checkWildStar getOrElse loop(lhs) // TODO: more rigorous type discipline
    }

    def badPattern3(token: Token): Nothing = {
      import patInfixContext._
      def isComma                = token.is[Comma]
      def isDelimiter            = token.is[RightParen] || token.is[RightBrace]
      def isCommaOrDelimiter     = isComma || isDelimiter
      val (isUnderscore, isStar) = stack match {
        case UnfinishedInfix(_, Pat.Wildcard(), _, Term.Name("*"), _) :: _ => (true,   true)
        case UnfinishedInfix(_, _, _, Term.Name("*"), _) :: _              => (false,  true)
        case _                                                    => (false, false)
      }
      def isSeqPatternClose = isUnderscore && isStar && isSequenceOK && isDelimiter
      val preamble = "bad simple pattern:"
      val subtext = (isUnderscore, isStar, isSequenceOK) match {
        case (true,  true, true)  if isComma            => "bad use of _* (a sequence pattern must be the last pattern)"
        case (true,  true, true)  if isDelimiter        => "bad brace or paren after _*"
        case (true,  true, false) if isDelimiter        => "bad use of _* (sequence pattern not allowed)"
        case (false, true, true)  if isDelimiter        => "use _* to match a sequence"
        case (false, true, _)     if isCommaOrDelimiter => "trailing * is not a valid pattern"
        case _                                          => null
      }
      val msg = if (subtext != null) s"$preamble $subtext" else "illegal start of simple pattern"
      // better recovery if don't skip delims of patterns
      val skip = !isCommaOrDelimiter || isSeqPatternClose
      syntaxError(msg, at = token)
    }

    /** {{{
     *  SimplePattern    ::= varid
     *                    |  `_'
     *                    |  literal
     *                    |  XmlPattern
     *                    |  StableId  /[TypeArgs]/ [`(' [Patterns] `)']
     *                    |  StableId  [`(' [Patterns] `)']
     *                    |  StableId  [`(' [Patterns] `,' [varid `@'] `_' `*' `)']
     *                    |  `(' [Patterns] `)'
     *  }}}
     *
     * XXX: Hook for IDE
     */
    def simplePattern(): Pat =
      // simple diagnostics for this entry point
      simplePattern(token => syntaxError("illegal start of simple pattern", at = token))
    def simplePattern(onError: Token => Nothing): Pat = autoPos(token match {
      case Ident(_) | KwThis() | Unquote(_) =>
        val isBackquoted = parser.isBackquoted
        val sid = stableId()
        val isVarPattern = sid match {
          case _: Term.Name.Quasi => false
          case Term.Name(value) => !isBackquoted && ((value.head.isLower && value.head.isLetter) || value.head == '_')
          case _ => false
        }
        if (token.is[NumericLiteral]) {
          sid match {
            case Term.Name("-") =>
              return atPos(sid, auto)(literal(isNegated = true))
            case _ =>
          }
        }
        val targs = token match {
          case LeftBracket() => patternTypeArgs()
          case _        => Nil
        }
        (token, sid) match {
          case (LeftParen(), _)                     =>
            val quasiOrSid = sid match {
              case quasi: Term.Name.Quasi => quasi.become[Term.Ref.Quasi]
              case path => path
            }
            Pat.Extract(quasiOrSid, targs, checkNoTripleDots(argumentPatterns()))
          case (_, _) if targs.nonEmpty             => syntaxError("pattern must be a value", at = token)
          case (_, name: Term.Name.Quasi)           => name.become[Pat.Quasi]
          case (_, name: Term.Name) if isVarPattern => Pat.Var.Term(name)
          case (_, name: Term.Name)                 => name
          case (_, select: Term.Select)             => select
          case _                                    => unreachable(debug(token, token.structure, sid, sid.structure))
        }
      case Underscore() =>
        next()
        Pat.Wildcard()
      case Literal() =>
        literal()
      case Interpolation.Id(_) =>
        interpolatePat()
      case Xml.Start() =>
        xmlPat()
      case LeftParen() =>
        val patterns = inParens(if (token.is[RightParen]) Nil else noSeq.patterns()).map {
          case q: Pat.Arg.Quasi => q.become[Pat.Quasi]
          case p => p.require[Pat]
        }
        makeTuple[Pat](patterns, () => Lit(()), Pat.Tuple(_))
      case _ =>
        onError(token)
    })
  }
  /** The implementation of the context sensitive methods for parsing outside of patterns. */
  object outPattern extends PatternContextSensitive {
    def argType(): Type = typ()
    def functionArgType(): Type.Arg = paramType()
  }
  /** The implementation for parsing inside of patterns at points where sequences are allowed. */
  object seqOK extends SeqContextSensitive {
    val isSequenceOK = true
  }
  /** The implementation for parsing inside of patterns at points where sequences are disallowed. */
  object noSeq extends SeqContextSensitive {
    val isSequenceOK = false
  }
  /** For use from xml pattern, where sequence is allowed and encouraged. */
  object xmlSeqOK extends SeqContextSensitive {
    val isSequenceOK = true
    override val isXML = true
  }
  /** These are default entry points into the pattern context sensitive methods:
   *  they are all initiated from non-pattern context.
   */
  def typ() = outPattern.typ()
  def startInfixType() = outPattern.infixType(InfixMode.FirstOp)
  def startModType() = outPattern.annotType()
  def exprTypeArgs() = outPattern.typeArgs()
  def exprSimpleType() = outPattern.simpleType()

  /** Default entry points into some pattern contexts. */
  def pattern(): Pat.Arg = noSeq.pattern()
  def quasiquotePattern(): Pat.Arg = noSeq.quasiquotePattern()
  def unquotePattern(): Pat = noSeq.unquotePattern()
  def unquoteXmlPattern(): Pat = xmlSeqOK.unquoteXmlPattern()
  def quasiquotePatternArg(): Pat.Arg = seqOK.quasiquotePatternArg()
  def seqPatterns(): List[Pat.Arg] = seqOK.patterns()
  def xmlSeqPatterns(): List[Pat.Arg] = xmlSeqOK.patterns() // Called from xml parser
  def argumentPattern(): Pat.Arg = seqOK.pattern()
  def argumentPatterns(): List[Pat.Arg] = inParens {
    if (token.is[RightParen]) Nil
    else seqPatterns()
  }
  def xmlLiteralPattern(): Pat = syntaxError("XML literals are not supported", at = in.token)
  def patternTyp() = noSeq.patternTyp(allowInfix = true, allowImmediateTypevars = false)
  def quasiquotePatternTyp() = noSeq.quasiquotePatternTyp(allowInfix = true, allowImmediateTypevars = false)
  def patternTypeArgs() = noSeq.patternTypeArgs()

/* -------- MODIFIERS and ANNOTATIONS ------------------------------------------- */

  def accessModifier(): Mod = autoPos {
    val mod = in.token match {
      case KwPrivate() => (name: Name.Qualifier) => Mod.Private(name)
      case KwProtected() => (name: Name.Qualifier) => Mod.Protected(name)
      case other => unreachable(debug(other, other.structure))
    }
    next()
    if (in.token.isNot[LeftBracket]) mod(autoPos(Name.Anonymous()))
    else {
      next()
      val result = {
        if (in.token.is[KwThis]) {
          val qual = atPos(in.tokenPos, in.prevTokenPos)(Name.Anonymous())
          next()
          mod(atPos(in.prevTokenPos, auto)(Term.This(qual)))
        } else if (in.token.is[Unquote]) {
          mod(unquote[Name.Qualifier.Quasi])
        } else {
          val name = termName()
          mod(atPos(name, name)(Name.Indeterminate(name.value)))
        }
      }
      accept[RightBracket]
      result
    }
  }

  /** {{{
   *  AccessModifier ::= (private | protected) [AccessQualifier]
   *  AccessQualifier ::= `[' (Id | this) `]'
   *  }}}
   */
  def accessModifierOpt(): Option[Mod] = token match {
    case Unquote(_)    => Some(unquote[Mod])
    case Ellipsis(_)   => Some(ellipsis(1, astInfo[Mod]))
    case KwPrivate()   => Some(accessModifier())
    case KwProtected() => Some(accessModifier())
    case _             => None
  }

  def modifier(): Mod = autoPos(token match {
    case Unquote(_)                  => unquote[Mod]
    case Ellipsis(_)                 => ellipsis(1, astInfo[Mod])
    case KwAbstract()                => next(); Mod.Abstract()
    case KwFinal()                   => next(); Mod.Final()
    case KwSealed()                  => next(); Mod.Sealed()
    case KwImplicit()                => next(); Mod.Implicit()
    case KwLazy()                    => next(); Mod.Lazy()
    case KwOverride()                => next(); Mod.Override()
    case KwPrivate()                 => accessModifier()
    case KwProtected()               => accessModifier()
    case KwInline() if allowInline   => next(); Mod.Inline()
    case _                           => syntaxError(s"modifier expected but ${token.name} found", at = token)
  })

  /** {{{
   *  Modifiers ::= {Modifier}
   *  Modifier  ::= LocalModifier
   *              |  AccessModifier
   *              |  override
   *  }}}
   */
  def modifiers(isLocal: Boolean = false): List[Mod] = {
    def appendMod(mods: List[Mod], mod: Mod): List[Mod] = {
      def validate() = {
        if (isLocal && !mod.tokens.head.is[LocalModifier]) syntaxError("illegal modifier for a local definition", at = mod)
        if (!mod.is[Mod.Quasi]) mods.foreach(m => if (m.productPrefix == mod.productPrefix) syntaxError("repeated modifier", at = mod))
        if (mod.hasAccessBoundary) mods.filter(_.hasAccessBoundary).foreach(mod => syntaxError("duplicate access qualifier", at = mod))
      }
      validate()
      mods :+ mod
    }
    // the only things that can come after $mod or $mods are either keywords or $pname/$tpname; the former is easy,
    // but in the case of the latter, we need to take care to not hastily parse those names as modifiers
    def continueLoop = ahead(token.is[Colon] || token.is[Equals] || token.is[EOF] || token.is[LeftBracket] || token.is[Subtype] || token.is[Supertype] || token.is[Viewbound])
    def loop(mods: List[Mod]): List[Mod] = token match {
      case Unquote(_)       => if (continueLoop) mods else loop(appendMod(mods, modifier()))
      case Ellipsis(_)      => loop(appendMod(mods, modifier()))
      case Modifier()       => loop(appendMod(mods, modifier()))
      case LF() if !isLocal => next(); loop(mods)
      case _                => mods
    }
    loop(Nil)
  }

  /** {{{
   *  LocalModifiers ::= {LocalModifier}
   *  LocalModifier  ::= abstract | final | sealed | implicit | lazy
   *  }}}
   */
  def localModifiers(): List[Mod] = modifiers(isLocal = true)

  /** {{{
   *  Annotations      ::= {`@' SimpleType {ArgumentExprs}}
   *  ConsrAnnotations ::= {`@' SimpleType ArgumentExprs}
   *  }}}
   */
  def annots(skipNewLines: Boolean, allowArgss: Boolean = true): List[Mod.Annot] = {
    val annots = new ListBuffer[Mod.Annot]
    while (token.is[At] || (token.is[Ellipsis] && ahead(token.is[At]))) {
      if (token.is[Ellipsis]) {
        annots += ellipsis(1, astInfo[Mod.Annot], accept[At])
      } else {
        next()
        if (token.is[Unquote]) annots += unquote[Mod.Annot]
        else annots += atPos(in.prevTokenPos, auto)(Mod.Annot(constructorCall(exprSimpleType(), allowArgss)))
      }
      if (skipNewLines) newLineOpt()
    }
    annots.toList
  }

  def constructorAnnots(): List[Mod.Annot] = annots(skipNewLines = false, allowArgss = false)

/* -------- 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(ownerIsType: Boolean, ownerIsCase: Boolean = false): List[List[Term.Param]] = {
    var parsedImplicits = false
    def paramClause(): List[Term.Param] = token match {
      case RightParen() =>
        Nil
      case tok: Ellipsis if tok.rank == 2 =>
        List(ellipsis(2, astInfo[Term.Param]))
      case _ =>
        if (token.is[KwImplicit]) {
          next()
          parsedImplicits = true
        }
        commaSeparated(param(ownerIsCase, ownerIsType, isImplicit = parsedImplicits))
    }
    val paramss = new ListBuffer[List[Term.Param]]
    newLineOptWhenFollowedBy[LeftParen]
    while (!parsedImplicits && token.is[LeftParen]) {
      next()
      paramss += paramClause()
      accept[RightParen]
      newLineOptWhenFollowedBy[LeftParen]
    }
    paramss.toList
  }

  /** {{{
   *  ParamType ::= Type | `=>' Type | Type `*'
   *  }}}
   */
  def paramType(): Type.Arg = autoPos(token match {
    case RightArrow() =>
      next()
      Type.Arg.ByName(typ())
    case _ =>
      val t = typ()
      if (!isRawStar) t
      else {
        next()
        Type.Arg.Repeated(t)
      }
  })

  def param(ownerIsCase: Boolean, ownerIsType: Boolean, isImplicit: Boolean): Term.Param = autoPos {
    var mods: List[Mod] = annots(skipNewLines = false)
    if (isImplicit) mods ++= List(atPos(in.tokenPos, in.prevTokenPos)(Mod.Implicit()))
    if (ownerIsType) {
      mods ++= modifiers()
      mods.getAll[Mod.Lazy].foreach { m =>
        syntaxError("lazy modifier not allowed here. Use call-by-name parameters instead", at = m)
      }
    }
    val (isValParam, isVarParam) = (ownerIsType && token.is[KwVal], ownerIsType && token.is[KwVar])
    if (isValParam) { mods :+= atPos(in.tokenPos, in.tokenPos)(Mod.ValParam()); next() }
    if (isVarParam) { mods :+= atPos(in.tokenPos, in.tokenPos)(Mod.VarParam()); next() }
    val name = termName() match {
      case q: Term.Name.Quasi => q.become[Term.Param.Name.Quasi]
      case x => x
    }
    val tpt =
      if (token.isNot[Colon] && name.is[Term.Param.Name.Quasi])
        None
      else {
        accept[Colon]
        val tpt = paramType() match {
          case q: Type.Quasi => q.become[Type.Arg.Quasi]
          case x => x
        }
        if (tpt.is[Type.Arg.ByName]) {
          def mayNotBeByName(subj: String) =
            syntaxError(s"$subj parameters may not be call-by-name", at = name)
          val isLocalToThis: Boolean = {
            val isExplicitlyLocal = mods.accessBoundary.exists(_.is[Term.This])
            if (ownerIsCase) isExplicitlyLocal
            else isExplicitlyLocal || (!isValParam && !isVarParam)
          }
          if (ownerIsType && !isLocalToThis) {
            if (isVarParam)
              mayNotBeByName("`var'")
            else
              mayNotBeByName("`val'")
          } else if (isImplicit)
            mayNotBeByName("implicit")
        }
        Some(tpt)
      }
    val default =
      if (token.isNot[Equals]) None
      else {
        next()
        Some(expr())
      }
    Term.Param(mods, name, tpt, default)
  }

  /** {{{
   *  TypeParamClauseOpt    ::= [TypeParamClause]
   *  TypeParamClause       ::= `[' VariantTypeParam {`,' VariantTypeParam} `]']
   *  VariantTypeParam      ::= {Annotation} [`+' | `-'] TypeParam
   *  FunTypeParamClauseOpt ::= [FunTypeParamClause]
   *  FunTypeParamClause    ::= `[' TypeParam {`,' TypeParam} `]']
   *  TypeParam             ::= Id TypeParamClauseOpt TypeBounds {<% Type} {":" Type}
   *  }}}
   */
  def typeParamClauseOpt(ownerIsType: Boolean, ctxBoundsAllowed: Boolean): List[Type.Param] = {
    newLineOptWhenFollowedBy[LeftBracket]
    if (token.isNot[LeftBracket]) Nil
    else inBrackets(commaSeparated(typeParam(ownerIsType, ctxBoundsAllowed)))
  }

  def typeParam(ownerIsType: Boolean, ctxBoundsAllowed: Boolean): Type.Param = autoPos {
    if (token.is[Unquote]) return unquote[Type.Param]
    var mods: List[Mod] = if (token.is[Ellipsis]) List(ellipsis(1, astInfo[Mod])) else annots(skipNewLines = true)
    if (ownerIsType && token.is[Ident]) {
      if (isIdentOf("+")) {
        next()
        mods ++= List(atPos(in.prevTokenPos, in.prevTokenPos)(Mod.Covariant()))
      } else if (isIdentOf("-")) {
        next()
        mods ++= List(atPos(in.prevTokenPos, in.prevTokenPos)(Mod.Contravariant()))
      }
    }
    val nameopt =
      if (token.is[Ident]) typeName()
      else if (token.is[Unquote]) unquote[Type.Param.Name]
      else if (token.is[Underscore]) { next(); atPos(in.prevTokenPos, in.prevTokenPos)(Name.Anonymous()) }
      else syntaxError("identifier or `_' expected", at = token)
    val tparams = typeParamClauseOpt(ownerIsType = true, ctxBoundsAllowed = false)
    val tbounds = this.typeBounds()
    val vbounds = new ListBuffer[Type]
    val cbounds = new ListBuffer[Type]
    if (ctxBoundsAllowed) {
      while (token.is[Viewbound]) {
        // TODO: dialect?
        // if (settings.future) {
        //   val msg = ("Use an implicit parameter instead.\n" +
        //              "Example: Instead of `def f[A <% Int](a: A)` " +
        //              "use `def f[A](a: A)(implicit ev: A => Int)`.")
        //   deprecationWarning(s"View bounds are deprecated. $msg")
        // }
        next()
        if (token.is[Ellipsis]) vbounds += ellipsis(1, astInfo[Type])
        else vbounds += typ()
      }
      while (token.is[Colon]) {
        next()
        if (token.is[Ellipsis]) cbounds += ellipsis(1, astInfo[Type])
        else cbounds += typ()
      }
    }
    Type.Param(mods, nameopt, tparams, tbounds, vbounds.toList, cbounds.toList)
  }

  /** {{{
   *  TypeBounds ::= [`>:' Type] [`<:' Type]
   *  }}}
   */
  def typeBounds() =
    autoPos(Type.Bounds(bound[Supertype], bound[Subtype]))

  def bound[T <: Token : TokenInfo]: Option[Type] =
    if (token.is[T]) { next(); Some(typ()) } else None

/* -------- DEFS ------------------------------------------- */


  /** {{{
   *  Import  ::= import ImportExpr {`,' ImportExpr}
   *  }}}
   */
  def importStmt(): Import = autoPos {
    accept[KwImport]
    Import(commaSeparated(importer()))
  }

  /** {{{
   *  ImportExpr ::= StableId `.' (Id | `_' | ImportSelectors)
   *  }}}
   */
  def importer(): Importer = autoPos {
    val sid = stableId() match {
      case quasi: Term.Name.Quasi => quasi.become[Term.Ref.Quasi]
      case path => path
    }
    def dotselectors = { accept[Dot]; Importer(sid, importees()) }
    sid match {
      case Term.Select(sid: Term.Ref, name: Term.Name) if sid.isStableId =>
        if (token.is[Dot]) dotselectors
        else Importer(sid, atPos(name, name)(Importee.Name(atPos(name, name)(Name.Indeterminate(name.value)))) :: Nil)
      case _ =>
        dotselectors
    }
  }

  /** {{{
   *  ImportSelectors ::= `{' {ImportSelector `,'} (ImportSelector | `_') `}'
   *  }}}
   */
  def importees(): List[Importee] =
    if (token.isNot[LeftBrace]) List(importWildcardOrName())
    else inBraces(commaSeparated(importee()))

  def importWildcardOrName(): Importee = autoPos {
    if (token.is[Underscore]) { next(); Importee.Wildcard() }
    else if (token.is[Unquote]) Importee.Name(unquote[Name.Indeterminate.Quasi])
    else { val name = termName(); Importee.Name(atPos(name, name)(Name.Indeterminate(name.value))) }
  }

  /** {{{
   *  ImportSelector ::= Id [`=>' Id | `=>' `_']
   *  }}}
   */
  def importee(): Importee = autoPos {
    importWildcardOrName() match {
      case from: Importee.Name if token.is[RightArrow] =>
        next()
        importWildcardOrName() match {
          case to: Importee.Name     => Importee.Rename(from.value, to.value)
          case to: Importee.Wildcard => Importee.Unimport(from.value)
          case other                 => unreachable(debug(other, other.structure))
        }
      // NOTE: this is completely nuts
      case from: Importee.Wildcard if token.is[RightArrow] && ahead(token.is[Underscore]) =>
        nextTwice()
        from
      case other =>
        other
    }
  }

  def nonLocalDefOrDcl(): Stat = {
    val anns = annots(skipNewLines = true)
    val mods = anns ++ modifiers()
    defOrDclOrSecondaryCtor(mods) match {
      case s if s.isTemplateStat => s
      case other                 => syntaxError("is not a valid template statement", at = other)
    }
  }

  /** {{{
   *  Def    ::= val PatDef
   *           | var PatDef
   *           | def FunDef
   *           | type [nl] TypeDef
   *           | TmplDef
   *  Dcl    ::= val PatDcl
   *           | var PatDcl
   *           | def FunDcl
   *           | type [nl] TypeDcl
   *  }}}
   */
  def defOrDclOrSecondaryCtor(mods: List[Mod]): Stat = {
    mods.getAll[Mod.Lazy].foreach { m =>
      if (token.isNot[KwVal]) syntaxError("lazy not allowed here. Only vals can be lazy", at = m)
    }
    token match {
      case KwVal() | KwVar() =>
        patDefOrDcl(mods)
      case KwDef() =>
        funDefOrDclOrSecondaryCtor(mods)
      case KwType() =>
        typeDefOrDcl(mods)
      case _ =>
        tmplDef(mods)
    }
  }

  /** {{{
   *  PatDef ::= Pattern2 {`,' Pattern2} [`:' Type] `=' Expr
   *  ValDcl ::= Id {`,' Id} `:' Type
   *  VarDef ::= PatDef | Id {`,' Id} `:' Type `=' `_'
   *  }}}
   */
  def patDefOrDcl(mods: List[Mod]): Stat = atPos(mods, auto) {
    val isMutable = token.is[KwVar]
    next()
    val lhs: List[Pat] = commaSeparated(noSeq.pattern2().require[Pat]).map {
      case q: Pat.Quasi => q.become[Pat.Var.Term.Quasi]
      case name: Term.Name => atPos(name, name)(Pat.Var.Term(name))
      case pat => pat
    }
    val tp: Option[Type] = typedOpt()

    if (tp.isEmpty || token.is[Equals]) {
      accept[Equals]
      val rhs =
        if (token.is[Underscore] && tp.nonEmpty && isMutable && lhs.forall(_.is[Pat.Var.Term])) {
          next()
          None
        } else Some(expr())

      if (isMutable) Defn.Var(mods, lhs, tp, rhs)
      else Defn.Val(mods, lhs, tp, rhs.get)
    } else {
      mods.getAll[Mod.Lazy].foreach { m => syntaxError("lazy values may not be abstract", at = m) }
      val ids = lhs.map {
        case name: Pat.Var.Term => name
        case other              => syntaxError("pattern definition may not be abstract", at = other)
      }

      if (isMutable) Decl.Var(mods, ids, tp.get)
      else Decl.Val(mods, ids, tp.get)
    }
  }

  /** {{{
   *  FunDef ::= FunSig [`:' Type] `=' [`macro'] Expr
   *          |  FunSig [nl] `{' Block `}'
   *          |  `this' ParamClause ParamClauses
   *                 (`=' ConstrExpr | [nl] ConstrBlock)
   *  FunDcl ::= FunSig [`:' Type]
   *  FunSig ::= id [FunTypeParamClause] ParamClauses
   *  }}}
   */
  def funDefOrDclOrSecondaryCtor(mods: List[Mod]): Stat = {
    if (ahead(token.isNot[KwThis])) funDefRest(mods)
    else secondaryCtor(mods)
  }

  def funDefRest(mods: List[Mod]): Stat = atPos(mods, auto) {
    accept[KwDef]
    val name = termName()
    def warnProcedureDeprecation =
      deprecationWarning(s"Procedure syntax is deprecated. Convert procedure `$name` to method by adding `: Unit`.", at = name)
    val tparams = typeParamClauseOpt(ownerIsType = false, ctxBoundsAllowed = true)
    val paramss = paramClauses(ownerIsType = false).require[Seq[Seq[Term.Param]]]
    newLineOptWhenFollowedBy[LeftBrace]
    val restype = fromWithinReturnType(typedOpt())
    if (token.is[StatSep] || token.is[RightBrace]) {
      if (restype.isEmpty) {
        warnProcedureDeprecation
        Decl.Def(mods, name, tparams, paramss, atPos(in.tokenPos, in.prevTokenPos)(Type.Name("Unit"))) // TODO: hygiene!
      } else
        Decl.Def(mods, name, tparams, paramss, restype.get)
    } else if (restype.isEmpty && token.is[LeftBrace]) {
      warnProcedureDeprecation
      Defn.Def(mods, name, tparams, paramss, Some(atPos(in.tokenPos, in.prevTokenPos)(Type.Name("Unit"))), expr()) // TODO: hygiene!
    } else {
      var isMacro = false
      val rhs = {
        if (token.is[Equals]) {
          next()
          isMacro = token.is[KwMacro]
          if (isMacro) next()
        } else {
          accept[Equals]
        }
        expr()
      }
      if (isMacro) Defn.Macro(mods, name, tparams, paramss, restype, rhs)
      else Defn.Def(mods, name, tparams, paramss, restype, rhs)
    }
  }

  /** {{{
   *  TypeDef ::= type Id [TypeParamClause] `=' Type
   *            | FunSig `=' Expr
   *  TypeDcl ::= type Id [TypeParamClause] TypeBounds
   *  }}}
   */
  def typeDefOrDcl(mods: List[Mod]): Member.Type with Stat = atPos(mods, auto) {
    accept[KwType]
    newLinesOpt()
    val name = typeName()
    val tparams = typeParamClauseOpt(ownerIsType = true, ctxBoundsAllowed = false)
    def aliasType() = Defn.Type(mods, name, tparams, typ())
    def abstractType() = Decl.Type(mods, name, tparams, typeBounds())
    token match {
      case Equals() => next(); aliasType()
      case Supertype() | Subtype() | Comma() | RightBrace() => abstractType()
      case StatSep() => abstractType()
      case _ => syntaxError("`=', `>:', or `<:' expected", at = token)
    }
  }

  /** Hook for IDE, for top-level classes/objects. */
  def topLevelTmplDef: Member with Stat =
    tmplDef(annots(skipNewLines = true) ++ modifiers())

  /** {{{
   *  TmplDef ::= [case] class ClassDef
   *            |  [case] object ObjectDef
   *            |  [override] trait TraitDef
   *  }}}
   */
  def tmplDef(mods: List[Mod]): Member with Stat = {
    mods.getAll[Mod.Lazy].foreach { m => syntaxError("classes cannot be lazy", at = m) }
    token match {
      case KwTrait() =>
        traitDef(mods)
      case KwClass() =>
        classDef(mods)
      case KwCase() if ahead(token.is[KwClass]) =>
        val casePos = in.tokenPos
        next()
        classDef(mods :+ atPos(casePos, casePos)(Mod.Case()))
      case KwObject() =>
        objectDef(mods)
      case KwCase() if ahead(token.is[KwObject]) =>
        val casePos = in.tokenPos
        next()
        objectDef(mods :+ atPos(casePos, casePos)(Mod.Case()))
      case _ =>
        syntaxError(s"expected start of definition", at = token)
    }
  }

  /** {{{
   *  TraitDef ::= Id [TypeParamClause] RequiresTypeOpt TraitTemplateOpt
   *  }}}
   */
  def traitDef(mods: List[Mod]): Defn.Trait = atPos(mods, auto) {
    accept[KwTrait]
    Defn.Trait(mods, typeName(),
               typeParamClauseOpt(ownerIsType = true, ctxBoundsAllowed = false),
               primaryCtor(OwnedByTrait),
               templateOpt(OwnedByTrait))
  }

  /** {{{
   *  ClassDef ::= Id [TypeParamClause] {Annotation}
   *               [AccessModifier] ClassParamClauses RequiresTypeOpt ClassTemplateOpt
   *  }}}
   */
  def classDef(mods: List[Mod]): Defn.Class = atPos(mods, auto) {
    accept[KwClass]
    // TODO:
    // if (ofCaseClass && token.isNot[LeftParen])
    //  syntaxError(token.offset, "case classes without a parameter list are not allowed;\n"+
    //                             "use either case objects or case classes with an explicit `()' as a parameter list.")
    // TODO:
    // if (owner == nme.CONSTRUCTOR && (result.isEmpty || (result.head take 1 exists (_.mods.isImplicit)))) {
    //  token match {
    //    case LeftBracket() => syntaxError("no type parameters allowed here")
    //    case EOF()       => incompleteInputError("auxiliary constructor needs non-implicit parameter list")
    //    case _           => syntaxError(start, "auxiliary constructor needs non-implicit parameter list")
    //  }
    // }
    Defn.Class(mods, typeName(),
               typeParamClauseOpt(ownerIsType = true, ctxBoundsAllowed = true),
               primaryCtor(if (mods.has[Mod.Case]) OwnedByCaseClass else OwnedByClass), templateOpt(OwnedByClass))
  }

  /** {{{
   *  ObjectDef       ::= Id ClassTemplateOpt
   *  }}}
   */
  def objectDef(mods: List[Mod]): Defn.Object = atPos(mods, auto) {
    accept[KwObject]
    Defn.Object(mods, termName(), templateOpt(OwnedByObject))
  }

/* -------- CONSTRUCTORS ------------------------------------------- */

  // TODO: we need to store some string in Ctor.Name in order to represent constructor calls (which also use Ctor.Name)
  // however, when representing constructor defns, we can't easily figure out that name
  // a natural desire would be to have this name equal to the name of the enclosing class/trait/object
  // but unfortunately we can't do that, because we can create ctors in isolation from their enclosures
  // therefore, I'm going to use `Term.Name("this")` here for the time being

  def primaryCtor(owner: TemplateOwner): Ctor.Primary = autoPos {
    if (owner.isClass) {
      val mods = constructorAnnots() ++ accessModifierOpt()
      val name = atPos(in.tokenPos, in.prevTokenPos)(Ctor.Name("this"))
      val paramss = paramClauses(ownerIsType = true, owner == OwnedByCaseClass)
      Ctor.Primary(mods, name, paramss)
    } else if (owner.isTrait) {
      Ctor.Primary(Nil, atPos(in.tokenPos, in.prevTokenPos)(Ctor.Name("this")), Nil)
    } else {
      unreachable(debug(owner))
    }
  }

  def secondaryCtor(mods: List[Mod]): Ctor.Secondary = atPos(mods, auto) {
    accept[KwDef]
    val thisPos = in.tokenPos
    accept[KwThis]
    // TODO: ownerIsType = true is most likely a bug here
    // secondary constructors can't have val/var parameters
    val paramss = paramClauses(ownerIsType = true)
    newLineOptWhenFollowedBy[LeftBrace]
    val body = token match {
      case LeftBrace() => constrBlock()
      case _      => accept[Equals]; constrExpr()
    }
    Ctor.Secondary(mods, atPos(thisPos, thisPos)(Ctor.Name("this")), paramss, body)
  }

  def quasiquoteCtor(): Ctor = autoPos {
    val anns = annots(skipNewLines = true)
    val mods = anns ++ modifiers()
    accept[KwDef]
    val name = atPos(in.tokenPos, in.tokenPos)(Ctor.Name("this"))
    accept[KwThis]
    val paramss = paramClauses(ownerIsType = true)
    newLineOptWhenFollowedBy[LeftBrace]
    if (token.is[EOF]) {
      Ctor.Primary(mods, name, paramss)
    } else {
      val body = token match {
        case LeftBrace() => constrBlock()
        case _      => accept[Equals]; constrExpr()
      }
      Ctor.Secondary(mods, name, paramss, body)
    }
  }

  /** {{{
   *  ConstrBlock    ::=  `{' SelfInvocation {semi BlockStat} `}'
   *  }}}
   */
  def constrBlock(): Term.Block = autoPos {
    accept[LeftBrace]
    val supercall = selfInvocation()
    val stats =
      if (!token.is[StatSep]) Nil
      else { next(); blockStatSeq() }
    accept[RightBrace]
    Term.Block(supercall +: stats)
  }

  /** {{{
   *  ConstrExpr      ::=  SelfInvocation
   *                    |  ConstrBlock
   *  }}}
   */
  def constrExpr(): Term =
    if (token.is[LeftBrace]) constrBlock()
    else selfInvocation()

  /** {{{
   *  SelfInvocation  ::= this ArgumentExprs {ArgumentExprs}
   *  }}}
   */
  def selfInvocation(): Term =
    if (token.is[Unquote])
      unquote[Term]
    else {
      var result: Term = autoPos(Ctor.Name("this"))
      accept[KwThis]
      newLineOptWhenFollowedBy[LeftBrace]
      while (token.is[LeftParen] || token.is[LeftBrace]) {
        result = atPos(result, auto)(Term.Apply(result, argumentExprs()))
        newLineOptWhenFollowedBy[LeftBrace]
      }
      result
    }

  def constructorCall(tpe: Type, allowArgss: Boolean = true): Ctor.Call = {
    object Types {
      def unapply(tpes: Seq[Type.Arg]): Option[Seq[Type]] = {
        if (tpes.forall(t => t.is[Type] || t.is[Type.Arg.Quasi])) Some(tpes.map {
          case q: Type.Arg.Quasi => q.become[Type.Quasi]
          case t: Type => t.require[Type]
        })
        else None
      }
    }
    def convert(tpe: Type): Ctor.Call = {
      // TODO: we should really think of a different representation for constructor invocations...
      // if anything, this mkCtorRefFunction is a testament to how problematic our current encoding is
      // the Type.ApplyInfix => Term.ApplyType conversion is weird as well
      def mkCtorRefFunction(tpe: Type) = {
        val arrow = scannerTokens.slice(tpe.tokens.head.index, tpe.tokens.last.index + 1).find(_.is[RightArrow]).get
        atPos(arrow, arrow)(Ctor.Ref.Function(atPos(arrow, arrow)(Ctor.Name("=>"))))
      }
      atPos(tpe, tpe)(tpe match {
        case q: Type.Quasi => q.become[Ctor.Call.Quasi]
        case Type.Name(value) => Ctor.Name(value)
        case Type.Select(qual, name: Type.Name.Quasi) => Ctor.Ref.Select(qual, atPos(name, name)(name.become[Ctor.Name.Quasi]))
        case Type.Select(qual, name) => Ctor.Ref.Select(qual, atPos(name, name)(Ctor.Name(name.value)))
        case Type.Project(qual, name: Type.Name.Quasi) => Ctor.Ref.Project(qual, atPos(name, name)(name.become[Ctor.Name.Quasi]))
        case Type.Project(qual, name) => Ctor.Ref.Project(qual, atPos(name, name)(Ctor.Name(name.value)))
        case Type.Function(Types(params), ret) => Term.ApplyType(mkCtorRefFunction(tpe), params :+ ret)
        case Type.Annotate(tpe, annots) => Term.Annotate(convert(tpe), annots)
        case Type.Apply(tpe, args) => Term.ApplyType(convert(tpe), args)
        case Type.ApplyInfix(lhs, op, rhs) => Term.ApplyType(convert(op), List(lhs, rhs))
        case _ => syntaxError("this type can't be used in a constructor call", at = tpe)
      })
    }
    var result = convert(tpe)
    var done = false
    while (token.is[LeftParen] && !done) {
      result = atPos(result, auto)(Term.Apply(result, argumentExprs()))
      if (!allowArgss) done = true
    }
    result
  }

/* -------- TEMPLATES ------------------------------------------- */

  sealed trait TemplateOwner {
    def isTerm = this eq OwnedByObject
    def isClass = (this eq OwnedByCaseClass) || (this eq OwnedByClass)
    def isTrait = this eq OwnedByTrait
  }
  object OwnedByTrait extends TemplateOwner
  object OwnedByCaseClass extends TemplateOwner
  object OwnedByClass extends TemplateOwner
  object OwnedByObject extends TemplateOwner

  /** {{{
   *  ClassParents       ::= ModType {`(' [Exprs] `)'} {with ModType}
   *  TraitParents       ::= ModType {with ModType}
   *  }}}
   */
  def templateParents(): List[Ctor.Call] = {
    val parents = new ListBuffer[Ctor.Call]
    def readAppliedParent() = {
      val parent = token match {
        case Unquote(_) =>
          val parent = constructorCall(startModType())
          parent match {
            case parent: Ctor.Ref.Name.Quasi => parent.become[Ctor.Call.Quasi]
            case other => other
          }
        case Ellipsis(_) =>
          ellipsis(1, astInfo[Ctor.Call])
        case _ =>
          constructorCall(startModType())
      }
      parents += parent
    }
    readAppliedParent()
    while (token.is[KwWith]) { next(); readAppliedParent() }
    parents.toList
  }

  /** {{{
   *  ClassTemplate ::= [EarlyDefs with] ClassParents [TemplateBody]
   *  TraitTemplate ::= [EarlyDefs with] TraitParents [TemplateBody]
   *  EarlyDefs     ::= `{' [EarlyDef {semi EarlyDef}] `}'
   *  EarlyDef      ::= Annotations Modifiers PatDef
   *  }}}
   */
  def template(): Template = autoPos {
    newLineOptWhenFollowedBy[LeftBrace]
    if (token.is[LeftBrace]) {
      // @S: pre template body cannot stub like post body can!
      val (self, body) = templateBody(isPre = true)
      if (token.is[KwWith] && self.name.is[Name.Anonymous] && self.decltpe.isEmpty) {
        val edefs = body.map(ensureEarlyDef)
        next()
        val parents = templateParents()
        val (self1, body1) = templateBodyOpt(parenMeansSyntaxError = false)
        Template(edefs, parents, self1, body1)
      } else {
        Template(Nil, Nil, self, Some(body))
      }
    } else if (token.is[Unquote] && ahead(
      !token.is[Dot] && !token.is[Hash] && !token.is[At] && !token.is[Ellipsis] &&
      !token.is[LeftParen] && !token.is[LeftBracket] && !token.is[LeftBrace] && !token.is[KwWith]
    )) {
      unquote[Template]
    } else {
      val parents = templateParents()
      val (self, body) = templateBodyOpt(parenMeansSyntaxError = false)
      Template(Nil, parents, self, body)
    }
  }

  def quasiquoteTemplate(): Template = autoPos {
    token match {
      case Unquote(_) if ahead(token.is[EOF]) =>
        val parents = List(unquote[Ctor.Call])
        val self = autoPos(Term.Param(Nil, autoPos(Name.Anonymous()), None, None))
        Template(Nil, parents, self, None)
      case _ =>
        template()
    }
  }

  def ensureEarlyDef(tree: Stat): Stat = tree match {
    case v: Stat.Quasi => v
    case v: Defn.Val => v
    case v: Defn.Var => v
    case t: Defn.Type => t
    case other => syntaxError("not a valid early definition", at = other)
  }

  /** {{{
   *  ClassTemplateOpt ::= `extends' ClassTemplate | [[`extends'] TemplateBody]
   *  TraitTemplateOpt ::= TraitExtends TraitTemplate | [[`extends'] TemplateBody] | `<:' TemplateBody
   *  TraitExtends     ::= `extends' | `<:'
   *  }}}
   */
  def templateOpt(owner: TemplateOwner): Template = {
    if (token.is[KwExtends] || (token.is[Subtype] && owner.isTrait)) {
      next()
      template()
    } else {
      val startPos = in.tokenPos
      val (self, body) = templateBodyOpt(parenMeansSyntaxError = !owner.isClass)
      atPos(startPos, auto)(Template(Nil, Nil, self, body))
    }
  }

  /** {{{
   *  TemplateBody ::= [nl] `{' TemplateStatSeq `}'
   *  }}}
   * @param isPre specifies whether in early initializer (true) or not (false)
   */
  def templateBody(isPre: Boolean): (Term.Param, List[Stat]) =
    inBraces(templateStatSeq(isPre = isPre))

  def templateBodyOpt(parenMeansSyntaxError: Boolean): (Term.Param, Option[List[Stat]]) = {
    newLineOptWhenFollowedBy[LeftBrace]
    if (token.is[LeftBrace]) {
      val (self, body) = templateBody(isPre = false)
      (self, Some(body))
    } else {
      if (token.is[LeftParen]) {
        if (parenMeansSyntaxError) syntaxError("traits or objects may not have parameters", at = token)
        else syntaxError("unexpected opening parenthesis", at = token)
      }
      (autoPos(Term.Param(Nil, autoPos(Name.Anonymous()), None, None)), None)
    }
  }

  /** {{{
   *  Refinement ::= [nl] `{' RefineStat {semi RefineStat} `}'
   *  }}}
   */
  def refinement(): List[Stat] = inBraces(refineStatSeq())

  def existentialStats(): List[Stat] = refinement() map {
    case stat if stat.isExistentialStat => stat
    case other                          => syntaxError("not a legal existential clause", at = other)
  }

/* -------- STATSEQS ------------------------------------------- */

  def statSeq[T <: Tree: AstInfo](statpf: PartialFunction[Token, T],
                                      errorMsg: String = "illegal start of definition"): List[T] = {
    val stats = new ListBuffer[T]
    while (!token.is[StatSeqEnd]) {
      def isDefinedInEllipsis = { if (token.is[LeftParen] || token.is[LeftBrace]) next(); statpf.isDefinedAt(token) }
      if (statpf.isDefinedAt(token) || (token.is[Ellipsis] && ahead(isDefinedInEllipsis)))
        stats += statpf(token)
      else if (!token.is[StatSep])
        syntaxError(errorMsg, at = token)
      acceptStatSepOpt()
    }
    stats.toList
  }

  /** {{{
   *  TopStatSeq ::= TopStat {semi TopStat}
   *  TopStat ::= Annotations Modifiers TmplDef
   *            | Packaging
   *            | package object objectDef
   *            | Import
   *            |
   *  }}}
   */
  def topStatSeq(): List[Stat] = statSeq(topStat, errorMsg = "expected class or object definition")
  def topStat: PartialFunction[Token, Stat] = {
    case Ellipsis(_) =>
      ellipsis(1, astInfo[Stat])
    case Unquote(_) =>
      unquote[Stat]
    case KwPackage() =>
      packageOrPackageObjectDef()
    case KwImport() =>
      importStmt()
    case TemplateIntro() =>
      topLevelTmplDef
  }

  /** {{{
   *  TemplateStatSeq  ::= [id [`:' Type] `=>'] TemplateStats
   *  }}}
   * @param isPre specifies whether in early initializer (true) or not (false)
   */
  def templateStatSeq(isPre : Boolean): (Term.Param, List[Stat]) = {
    var self: Term.Param = autoPos(Term.Param(Nil, autoPos(Name.Anonymous()), None, None))
    var firstOpt: Option[Stat] = None
    if (token.is[ExprIntro]) {
      val first = expr(InTemplate) // @S: first statement is potentially converted so cannot be stubbed.
      if (token.is[RightArrow]) {
        first match {
          case q: Term.Quasi =>
            self = q.become[Term.Param.Quasi]
          case name: Term.Placeholder =>
            self = atPos(first, first)(Term.Param(Nil, atPos(name, name)(Name.Anonymous()), None, None))
          case name @ Term.This(Name.Anonymous()) =>
            self = atPos(first, first)(Term.Param(Nil, atPos(name, name)(Name.Anonymous()), None, None))
          case name: Term.Name =>
            self = atPos(first, first)(Term.Param(Nil, name, None, None))
          case Term.Ascribe(name: Term.Placeholder, tpt) =>
            self = atPos(first, first)(Term.Param(Nil, atPos(name, name)(Name.Anonymous()), Some(tpt), None))
          case Term.Ascribe(name @ Term.This(Name.Anonymous()), tpt) =>
            self = atPos(first, first)(Term.Param(Nil, atPos(name, name)(Name.Anonymous()), Some(tpt), None))
          case Term.Ascribe(name: Term.Name, tpt) =>
            self = atPos(first, first)(Term.Param(Nil, name, Some(tpt), None))
          case _ =>
        }
        next()
      } else {
        firstOpt = Some(first match {
          case q: Term.Quasi => q.become[Stat.Quasi]
          case other => other
        })
        acceptStatSepOpt()
      }
    }
    (self, firstOpt ++: templateStats())
  }

  /** {{{
   *  TemplateStats    ::= TemplateStat {semi TemplateStat}
   *  TemplateStat     ::= Import
   *                     | Annotations Modifiers Def
   *                     | Annotations Modifiers Dcl
   *                     | Expr1
   *                     | super ArgumentExprs {ArgumentExprs}
   *                     |
   *  }}}
   */
  def templateStats(): List[Stat] = statSeq(templateStat)
  def templateStat: PartialFunction[Token, Stat] = {
    case KwImport() =>
      importStmt()
    case DefIntro() =>
      nonLocalDefOrDcl()
    case Unquote(_) =>
      unquote[Stat]
    case Ellipsis(_) =>
      ellipsis(1, astInfo[Stat])
    case ExprIntro() =>
      expr(InTemplate)
  }

  /** {{{
   *  RefineStatSeq    ::= RefineStat {semi RefineStat}
   *  RefineStat       ::= Dcl
   *                     | type TypeDef
   *                     |
   *  }}}
   */
  def refineStatSeq(): List[Stat] = {
    val stats = new ListBuffer[Stat]
    while (!token.is[StatSeqEnd]) {
      stats ++= refineStat()
      if (token.isNot[RightBrace]) acceptStatSep()
    }
    stats.toList
  }

  def refineStat(): Option[Stat] =
    if (token.is[Ellipsis]) {
      Some(ellipsis(1, astInfo[Stat]))
    } else if (token.is[DclIntro]) {
      defOrDclOrSecondaryCtor(Nil) match {
        case stat if stat.isRefineStat => Some(stat)
        case other                     => syntaxError("is not a valid refinement declaration", at = other)
      }
    } else if (!token.is[StatSep]) {
      syntaxError(
        "illegal start of declaration"+
        (if (inFunReturnType) " (possible cause: missing `=' in front of current method body)"
         else ""), at = token)
    } else None

  def localDef(implicitMod: Option[Mod.Implicit]): Stat = {
    val mods = (implicitMod ++: annots(skipNewLines = true)) ++ localModifiers()
    if (mods forall { case _: Mod.Implicit | _: Mod.Lazy | _: Mod.Inline | _: Mod.Annot => true; case _ => false })
      (defOrDclOrSecondaryCtor(mods) match {
        case stat if stat.isBlockStat => stat
        case other                    => syntaxError("is not a valid block statement", at = other)
      })
    else
      (tmplDef(mods) match {
        case stat if stat.isBlockStat => stat
        case other                    => syntaxError("is not a valid block statement", at = other)
      })
  }

  /** {{{
   *  BlockStatSeq ::= { BlockStat semi } [ResultExpr]
   *  BlockStat    ::= Import
   *                 | Annotations [implicit] [lazy] Def
   *                 | Annotations LocalModifiers TmplDef
   *                 | Expr1
   *                 |
   *  }}}
   */
  def blockStatSeq(): List[Stat] = {
    val stats = new ListBuffer[Stat]
    while (!token.is[StatSeqEnd] && !token.is[CaseDefEnd]) {
      if (token.is[KwImport]) {
        stats += importStmt()
        acceptStatSepOpt()
      }
      else if (token.is[DefIntro] && !token.is[NonlocalModifier]) {
        if (token.is[KwImplicit]) {
          val implicitPos = in.tokenPos
          next()
          if (token.is[Ident]) stats += implicitClosure(InBlock)
          else stats += localDef(Some(atPos(implicitPos, implicitPos)(Mod.Implicit())))
        } else {
          stats += localDef(None)
        }
        acceptStatSepOpt()
      }
      else if (token.is[ExprIntro]) {
        stats += (expr(InBlock) match { case q: Term.Quasi => q.become[Stat.Quasi]; case other => other })
        if (!token.is[CaseDefEnd]) acceptStatSep()
      }
      else if (token.is[StatSep]) {
        next()
      }
      else if (token.is[Ellipsis]) {
        stats += ellipsis(1, astInfo[Stat])
      }
      else {
        syntaxError("illegal start of statement", at = token)
      }
    }
    stats.toList
  }


  def packageOrPackageObjectDef(): Stat = autoPos {
    require(token.is[KwPackage] && debug(token))
    if (ahead(token.is[KwObject])) packageObjectDef()
    else packageDef()
  }

  def packageDef(): Pkg = autoPos {
    accept[KwPackage]
    Pkg(qualId(), inBracesOrNil(topStatSeq()))
  }

  def packageObjectDef(): Pkg.Object = autoPos {
    accept[KwPackage]
    accept[KwObject]
    Pkg.Object(Nil, termName(), templateOpt(OwnedByObject))
  }

  /** {{{
   *  CompilationUnit ::= {package QualId semi} TopStatSeq
   *  }}}
   */
  def source(): Source = autoPos {
    if (dialect.allowToplevelTerms) scriptSource()
    else batchSource()
  }

  def scriptSource(): Source = autoPos {
    // TODO: Faithfully reimplement the logic in SBT (see #368 in details).
    // So far, our use case is to reformat already valid programs,
    // so we can afford accepting more than necessary.
    if (dialect.toplevelSeparator == "") {
      Source(parser.statSeq(consumeStat))
    } else {
      require(dialect.toplevelSeparator == EOL)
      Source(parser.statSeq(consumeStat))
    }
  }

  def batchSource(): Source = autoPos {
    def inBracelessPackage() = token.is[KwPackage] && !ahead(token.is[KwObject]) && ahead{ qualId(); token.isNot[LeftBrace] }
    def bracelessPackageStats(): Seq[Stat] = {
      if (token.is[EOF]) {
        Nil
      } else if (token.is[StatSep]) {
        next()
        bracelessPackageStats()
      } else if (token.is[KwPackage] && !ahead(token.is[KwObject])) {
        val startPos = in.tokenPos
        accept[KwPackage]
        val qid = qualId()
        if (token.is[LeftBrace]) {
          val pkg = atPos(startPos, auto)(Pkg(qid, inBraces(topStatSeq())))
          acceptStatSepOpt()
          pkg +: bracelessPackageStats()
        } else {
          List(atPos(startPos, auto)(Pkg(qid, bracelessPackageStats())))
        }
      } else if (token.is[LeftBrace]) {
        inBraces(topStatSeq())
      } else {
        topStatSeq()
      }
    }
    if (inBracelessPackage) {
      val startPos = in.tokenPos
      accept[KwPackage]
      Source(List(atPos(startPos, auto)(Pkg(qualId(), bracelessPackageStats()))))
    } else {
      Source(topStatSeq())
    }
  }
}

object ScalametaParser {
  def toParse[T](fn: ScalametaParser => T): Parse[T] = new Parse[T] {
    def apply(input: Input, dialect: Dialect): Parsed[T] = {
      try {
        val parser = new ScalametaParser(input, dialect)
        Parsed.Success(fn(parser))
      } catch {
        case details @ TokenizeException(pos, message) =>
          Parsed.Error(pos, message, details)
        case details @ ParseException(pos, message) =>
          Parsed.Error(pos, message, details)
      }
    }
  }
}

class Location private(val value: Int) extends AnyVal
object Location {
  val Local      = new Location(0)
  val InBlock    = new Location(1)
  val InTemplate = new Location(2)
}

object InfixMode extends Enumeration {
  val FirstOp, LeftOp, RightOp = Value
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy