scala.tools.nsc.transform.patmat.PatternExpander.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scala-compiler Show documentation
Show all versions of scala-compiler Show documentation
Compiler for the SubScript extension of the Scala Programming Language
The newest version!
/* NSC -- new Scala compiler
* Copyright 2005-2013 LAMP/EPFL
* @author Paul Phillips
*/
package scala
package tools
package nsc
package transform
package patmat
/** An extractor returns: F1, F2, ..., Fi, opt[Seq[E] or E*]
* A case matches: P1, P2, ..., Pj, opt[Seq[E]]
* Put together: P1/F1, P2/F2, ... Pi/Fi, Pi+1/E, Pi+2/E, ... Pj/E, opt[Seq[E]]
*
* Here Pm/Fi is the last pattern to match the fixed arity section.
*
* productArity: the value of i, i.e. the number of non-sequence types in the extractor
* nonStarArity: the value of j, i.e. the number of non-star patterns in the case definition
* elementArity: j - i, i.e. the number of non-star patterns which must match sequence elements
* starArity: 1 or 0 based on whether there is a star (sequence-absorbing) pattern
* totalArity: nonStarArity + starArity, i.e. the number of patterns in the case definition
*
* Note that productArity is a function only of the extractor, and
* nonStar/star/totalArity are all functions of the patterns. The key
* value for aligning and typing the patterns is elementArity, as it
* is derived from both sets of information.
*/
trait PatternExpander[Pattern, Type] {
/** You'll note we're not inside the cake. "Pattern" and "Type" are
* arbitrary types here, and NoPattern and NoType arbitrary values.
*/
def NoPattern: Pattern
def NoType: Type
/** It's not optimal that we're carrying both sequence and repeated
* type here, but the implementation requires more unraveling before
* it can be avoided.
*
* sequenceType is Seq[T], elementType is T, repeatedType is T*.
*/
sealed case class Repeated(sequenceType: Type, elementType: Type, repeatedType: Type) {
def exists = elementType != NoType
def elementList = if (exists) elementType :: Nil else Nil
def sequenceList = if (exists) sequenceType :: Nil else Nil
def repeatedList = if (exists) repeatedType :: Nil else Nil
override def toString = s"${elementType}*"
}
object NoRepeated extends Repeated(NoType, NoType, NoType) {
override def toString = ""
}
final case class Patterns(fixed: List[Pattern], star: Pattern) {
def hasStar = star != NoPattern
def starArity = if (hasStar) 1 else 0
def nonStarArity = fixed.length
def totalArity = nonStarArity + starArity
def starPatterns = if (hasStar) star :: Nil else Nil
def all = fixed ::: starPatterns
override def toString = all mkString ", "
}
/** An 'extractor' can be a case class or an unapply or unapplySeq method.
* Decoding what it is that they extract takes place before we arrive here,
* so that this class can concentrate only on the relationship between
* patterns and types.
*
* In a case class, the class is the unextracted type and the fixed and
* repeated types are derived from its constructor parameters.
*
* In an unapply, this is reversed: the parameter to the unapply is the
* unextracted type, and the other types are derived based on the return
* type of the unapply method.
*
* In other words, this case class and unapply are encoded the same:
*
* case class Foo(x: Int, y: Int, zs: Char*)
* def unapplySeq(x: Foo): Option[(Int, Int, Seq[Char])]
*
* Both are Extractor(Foo, Int :: Int :: Nil, Repeated(Seq[Char], Char, Char*))
*
* @param whole The type in its unextracted form
* @param fixed The non-sequence types which are extracted
* @param repeated The sequence type which is extracted
*/
final case class Extractor(whole: Type, fixed: List[Type], repeated: Repeated) {
require(whole != NoType, s"expandTypes($whole, $fixed, $repeated)")
def productArity = fixed.length
def hasSeq = repeated.exists
def elementType = repeated.elementType
def sequenceType = repeated.sequenceType
def allTypes = fixed ::: repeated.sequenceList
def varargsTypes = fixed ::: repeated.repeatedList
def isErroneous = allTypes contains NoType
private def typeStrings = fixed.map("" + _) ::: ( if (hasSeq) List("" + repeated) else Nil )
def offeringString = if (isErroneous) "" else typeStrings match {
case Nil => "Boolean"
case tp :: Nil => tp
case tps => tps.mkString("(", ", ", ")")
}
override def toString = "%s => %s".format(whole, offeringString)
}
final case class TypedPat(pat: Pattern, tpe: Type) {
override def toString = s"$pat: $tpe"
}
/** If elementArity is...
* 0: A perfect match between extractor and the fixed patterns.
* If there is a star pattern it will match any sequence.
* > 0: There are more patterns than products. There will have to be a
* sequence which can populate at least patterns.
* < 0: There are more products than patterns: compile time error.
*/
final case class Aligned(patterns: Patterns, extractor: Extractor) {
def elementArity = patterns.nonStarArity - productArity
def productArity = extractor.productArity
def starArity = patterns.starArity
def totalArity = patterns.totalArity
def wholeType = extractor.whole
def sequenceType = extractor.sequenceType
def productTypes = extractor.fixed
def extractedTypes = extractor.allTypes
def typedNonStarPatterns = products ::: elements
def typedPatterns = typedNonStarPatterns ::: stars
def isBool = !isSeq && productArity == 0
def isSingle = !isSeq && totalArity == 1
def isStar = patterns.hasStar
def isSeq = extractor.hasSeq
private def typedAsElement(pat: Pattern) = TypedPat(pat, extractor.elementType)
private def typedAsSequence(pat: Pattern) = TypedPat(pat, extractor.sequenceType)
private def productPats = patterns.fixed take productArity
private def elementPats = patterns.fixed drop productArity
private def products = (productPats, productTypes).zipped map TypedPat
private def elements = elementPats map typedAsElement
private def stars = patterns.starPatterns map typedAsSequence
override def toString = s"""
|Aligned {
| patterns $patterns
| extractor $extractor
| arities $productArity/$elementArity/$starArity // product/element/star
| typed ${typedPatterns mkString ", "}
|}""".stripMargin.trim
}
}