|
Scala example source code file (Patterns.scala)
The Scala Patterns.scala source code/* NSC -- new Scala compiler * Copyright 2005-2011 LAMP/EPFL * Author: Paul Phillips */ package scala.tools.nsc package matching import symtab.Flags import scala.reflect.NameTransformer.decode import PartialFunction._ /** Patterns are wrappers for Trees with enhanced semantics. * * @author Paul Phillips */ trait Patterns extends ast.TreeDSL { self: transform.ExplicitOuter => import global.{ typer => _, _ } import definitions._ import CODE._ import Debug._ import treeInfo.{ unbind, isStar, isVarPattern, isVariableName } type PatternMatch = MatchMatrix#PatternMatch private type PatternVar = MatrixContext#PatternVar // private def unapplyArgs(x: Any) = x match { // case UnApply(Apply(TypeApply(_, targs), args), _) => (targs map (_.symbol), args map (_.symbol)) // case _ => (Nil, Nil) // } // // private def unapplyCall(x: Any) = x match { // case UnApply(t, _) => treeInfo.methPart(t).symbol // case _ => NoSymbol // } // Fresh patterns def emptyPatterns(i: Int): List[Pattern] = List.fill(i)(NoPattern) def emptyTrees(i: Int): List[Tree] = List.fill(i)(EmptyTree) // An empty pattern def NoPattern = WildcardPattern() // The constant null pattern def NullPattern = LiteralPattern(NULL) // The Nil pattern def NilPattern = Pattern(gen.mkNil) // 8.1.1 case class VariablePattern(tree: Ident) extends NamePattern { lazy val Ident(name) = tree require(isVarPattern(tree) && name != nme.WILDCARD) override def description = "%s".format(name) } // 8.1.1 (b) case class WildcardPattern() extends Pattern { def tree = EmptyTree override def isDefault = true override def description = "_" } // 8.1.2 case class TypedPattern(tree: Typed) extends Pattern { lazy val Typed(expr, tpt) = tree override def subpatternsForVars: List[Pattern] = List(Pattern(expr)) override def simplify(pv: PatternVar) = Pattern(expr) match { case ExtractorPattern(ua) if pv.sym.tpe <:< tpt.tpe => this rebindTo expr case _ => this } override def description = "%s: %s".format(Pattern(expr), tpt) } // 8.1.3 case class LiteralPattern(tree: Literal) extends Pattern { lazy val Literal(const @ Constant(value)) = tree def isSwitchable = cond(const.tag) { case ByteTag | ShortTag | IntTag | CharTag => true } def intValue = const.intValue override def description = { val s = if (value == null) "null" else value.toString() "Lit(%s)".format(s) } } // 8.1.4 (a) case class ApplyIdentPattern(tree: Apply) extends ApplyPattern with NamePattern { // XXX - see bug 3411 for code which violates this assumption // require (!isVarPattern(fn) && args.isEmpty) lazy val ident @ Ident(name) = fn override def sufficientType = Pattern(ident).equalsCheck override def simplify(pv: PatternVar) = this.rebindToObjectCheck() override def description = "Id(%s)".format(name) } // 8.1.4 (b) case class ApplySelectPattern(tree: Apply) extends ApplyPattern with SelectPattern { require (args.isEmpty) lazy val Apply(select: Select, _) = tree override lazy val sufficientType = qualifier.tpe match { case t: ThisType => singleType(t, sym) // this.X case _ => qualifier match { case _: Apply => PseudoType(tree) case _ => singleType(Pattern(qualifier).necessaryType, sym) } } override def simplify(pv: PatternVar) = this.rebindToObjectCheck() override def description = backticked match { case Some(s) => "this." + s case _ => "Sel(%s.%s)".format(Pattern(qualifier), name) } } // 8.1.4 (c) case class StableIdPattern(tree: Select) extends SelectPattern { def select = tree override def description = "St(%s)".format(printableSegments.mkString(" . ")) private def printableSegments = pathSegments filter (x => !x.isEmpty && (x.toString != "$iw")) } // 8.1.4 (d) case class ObjectPattern(tree: Apply) extends ApplyPattern { // NamePattern? require(!fn.isType && isModule) override def sufficientType = tpe.narrow override def simplify(pv: PatternVar) = this.rebindToObjectCheck() override def description = "Obj(%s)".format(fn) } // 8.1.4 (e) case class SimpleIdPattern(tree: Ident) extends NamePattern { lazy val Ident(name) = tree override def description = "Id(%s)".format(name) } // 8.1.5 case class ConstructorPattern(tree: Apply) extends ApplyPattern with NamePattern { require(fn.isType && this.isCaseClass, "tree: " + tree + " fn: " + fn) def name = tpe.typeSymbol.name def cleanName = tpe.typeSymbol.decodedName def hasPrefix = tpe.prefix.prefixString != "" def prefixedName = if (hasPrefix) "%s.%s".format(tpe.prefix.prefixString, cleanName) else cleanName private def isColonColon = cleanName == "::" override def subpatterns(pm: MatchMatrix#PatternMatch) = if (pm.head.isCaseClass) toPats(args) else super.subpatterns(pm) override def simplify(pv: PatternVar) = if (args.isEmpty) this rebindToEmpty tree.tpe else this override def description = { if (isColonColon) "%s :: %s".format(Pattern(args(0)), Pattern(args(1))) else "%s(%s)".format(name, toPats(args).mkString(", ")) } } // 8.1.6 case class TuplePattern(tree: Apply) extends ApplyPattern { override def description = "((%s))".format(args.size, toPats(args).mkString(", ")) } // 8.1.7 / 8.1.8 (unapply and unapplySeq calls) case class ExtractorPattern(tree: UnApply) extends UnapplyPattern { override def simplify(pv: PatternVar) = { if (pv.sym hasFlag NO_EXHAUSTIVE) () else { TRACE("Setting NO_EXHAUSTIVE on " + pv.sym + " due to extractor " + tree) pv.sym setFlag NO_EXHAUSTIVE } if (pv.tpe <:< arg.tpe) this else this rebindTo uaTyped } override def description = "Unapply(%s => %s)".format(necessaryType, resTypesString) } // Special List handling. It was like that when I got here. case class ListExtractorPattern(tree: UnApply, tpt: Tree, elems: List[Tree]) extends UnapplyPattern with SequenceLikePattern { // As yet I can't testify this is doing any good relative to using // tpt.tpe, but it doesn't seem to hurt either. private lazy val packedType = global.typer.computeType(tpt, tpt.tpe) private lazy val consRef = typeRef(NoPrefix, ConsClass, List(packedType)) private lazy val listRef = typeRef(NoPrefix, ListClass, List(packedType)) private lazy val seqRef = typeRef(NoPrefix, SeqClass, List(packedType)) private def thisSeqRef = { val tc = (tree.tpe baseType SeqClass).typeConstructor if (tc.typeParams.size == 1) appliedType(tc, List(packedType)) else seqRef } // Fold a list into a well-typed x :: y :: etc :: tree. private def listFolder(hd: Tree, tl: Tree): Tree = unbind(hd) match { case t @ Star(_) => moveBindings(hd, WILD(t.tpe)) case _ => val dummyMethod = new TermSymbol(NoSymbol, NoPosition, "matching$dummy") val consType = MethodType(dummyMethod newSyntheticValueParams List(packedType, listRef), consRef) Apply(TypeTree(consType), List(hd, tl)) setType consRef } private def foldedPatterns = elems.foldRight(gen.mkNil)((x, y) => listFolder(x, y)) override def necessaryType = if (nonStarPatterns.nonEmpty) consRef else listRef override def simplify(pv: PatternVar) = { if (pv.tpe <:< necessaryType) Pattern(foldedPatterns) else this rebindTo (Typed(tree, TypeTree(necessaryType)) setType necessaryType) } override def description = "List(%s => %s)".format(packedType, resTypesString) } trait SequenceLikePattern extends Pattern { def elems: List[Tree] override def hasStar = elems.nonEmpty && isStar(elems.last) def elemPatterns = toPats(elems) def nonStarElems = if (hasStar) elems.init else elems def nonStarPatterns = toPats(nonStarElems) def nonStarLength = nonStarElems.length } // 8.1.8 (b) (literal ArrayValues) case class SequencePattern(tree: ArrayValue) extends Pattern with SequenceLikePattern { lazy val ArrayValue(elemtpt, elems) = tree override def subpatternsForVars: List[Pattern] = elemPatterns override def description = "Seq(%s)".format(elemPatterns mkString ", ") } // 8.1.8 (c) case class StarPattern(tree: Star) extends Pattern { lazy val Star(elem) = tree override def description = "_*" } // XXX temporary? case class ThisPattern(tree: This) extends NamePattern { lazy val This(name) = tree override def description = "this" } // 8.1.9 // InfixPattern ... subsumed by Constructor/Extractor Patterns // 8.1.10 case class AlternativePattern(tree: Alternative) extends Pattern { private lazy val Alternative(subtrees) = tree private def alts = toPats(subtrees) override def description = "Alt(%s)".format(alts mkString " | ") } // 8.1.11 // XMLPattern ... for now, subsumed by SequencePattern, but if we want // to make it work right, it probably needs special handling. private def abortUnknownTree(tree: Tree) = abort("Unknown Tree reached pattern matcher: %s/%s".format(tree, tree.getClass)) object Pattern { // a small tree -> pattern cache private val cache = new collection.mutable.HashMap[Tree, Pattern] def clear() = cache.clear() def apply(tree: Tree): Pattern = { if (cache contains tree) return cache(tree) val p = tree match { case x: Bind => apply(unbind(tree)) setBound x case EmptyTree => WildcardPattern() case Ident(nme.WILDCARD) => WildcardPattern() case x @ Alternative(ps) => AlternativePattern(x) case x: Apply => ApplyPattern(x) case x: Typed => TypedPattern(x) case x: Literal => LiteralPattern(x) case x: UnApply => UnapplyPattern(x) case x: Ident => if (isVarPattern(x)) VariablePattern(x) else SimpleIdPattern(x) case x: ArrayValue => SequencePattern(x) case x: Select => StableIdPattern(x) case x: Star => StarPattern(x) case x: This => ThisPattern(x) // XXX ? case _ => abortUnknownTree(tree) } cache(tree) = p // limiting the trace output p match { case WildcardPattern() => p case _: LiteralPattern => p case _ => tracing("Pattern")(p) } } // matching on Pattern(...) always skips the bindings. def unapply(other: Any): Option[Tree] = other match { case x: Tree => unapply(Pattern(x)) case x: Pattern => Some(x.tree) case _ => None } } object UnapplyPattern { private object UnapplySeq { def unapply(x: UnApply) = x match { case UnApply( Apply(TypeApply(Select(qual, nme.unapplySeq), List(tpt)), _), List(ArrayValue(_, elems))) => Some(qual.symbol, tpt, elems) case _ => None } } def apply(x: UnApply): Pattern = x match { case UnapplySeq(ListModule, tpt, elems) => ListExtractorPattern(x, tpt, elems) case _ => ExtractorPattern(x) } } // right now a tree like x @ Apply(fn, Nil) where !fn.isType // is handled by creating a singleton type: // // val stype = Types.singleType(x.tpe.prefix, x.symbol) // // and then passing that as a type argument to EqualsPatternClass: // // val tpe = typeRef(NoPrefix, EqualsPatternClass, List(stype)) // // then creating a Typed pattern and rebinding. // // val newpat = Typed(EmptyTree, TypeTree(tpe)) setType tpe) // // This is also how Select(qual, name) is handled. object ApplyPattern { def apply(x: Apply): Pattern = { val Apply(fn, args) = x def isModule = x.symbol.isModule || x.tpe.termSymbol.isModule def isTuple = isTupleTypeOrSubtype(fn.tpe) if (fn.isType) { if (isTuple) TuplePattern(x) else ConstructorPattern(x) } else if (args.isEmpty) { if (isModule) ObjectPattern(x) else fn match { case _: Ident => ApplyIdentPattern(x) case _: Select => ApplySelectPattern(x) } } else abortUnknownTree(x) } } /** Some intermediate pattern classes with shared structure **/ sealed trait SelectPattern extends NamePattern { def select: Select lazy val Select(qualifier, name) = select def pathSegments = getPathSegments(tree) def backticked: Option[String] = qualifier match { case _: This if isVariableName(name) => Some("`%s`".format(name)) case _ => None } protected def getPathSegments(t: Tree): List[Name] = t match { case Select(q, name) => name :: getPathSegments(q) case Apply(f, Nil) => getPathSegments(f) case _ => Nil } } sealed trait NamePattern extends Pattern { def name: Name override def sufficientType = tpe.narrow override def simplify(pv: PatternVar) = this.rebindToEqualsCheck() override def description = name.toString() } sealed trait UnapplyPattern extends Pattern { lazy val UnApply(unfn, args) = tree lazy val Apply(fn, _) = unfn lazy val MethodType(List(arg, _*), _) = fn.tpe protected def uaTyped = Typed(tree, TypeTree(arg.tpe)) setType arg.tpe override def necessaryType = arg.tpe override def subpatternsForVars = args match { case List(ArrayValue(elemtpe, elems)) => toPats(elems) case _ => toPats(args) } def resTypes = analyzer.unapplyTypeList(unfn.symbol, unfn.tpe) def resTypesString = resTypes match { case Nil => "Boolean" case xs => xs.mkString(", ") } } sealed trait ApplyPattern extends Pattern { lazy val Apply(fn, args) = tree override def subpatternsForVars: List[Pattern] = toPats(args) override def dummies = if (!this.isCaseClass) Nil else emptyPatterns(sufficientType.typeSymbol.caseFieldAccessors.size) def isConstructorPattern = fn.isType } sealed abstract class Pattern extends PatternBindingLogic { def tree: Tree // returns either a simplification of this pattern or identity. def simplify(pv: PatternVar): Pattern = this // the right number of dummies for this pattern def dummies: List[Pattern] = Nil // Is this a default pattern (untyped "_" or an EmptyTree inserted by the matcher) def isDefault = false // what type must a scrutinee have to have any chance of matching this pattern? def necessaryType = tpe // what type could a scrutinee have which would automatically indicate a match? // (nullness and guards will still be checked.) def sufficientType = tpe // the subpatterns for this pattern (at the moment, that means constructor arguments) def subpatterns(pm: MatchMatrix#PatternMatch): List[Pattern] = pm.dummies def sym = tree.symbol def tpe = tree.tpe def isEmpty = tree.isEmpty def isModule = sym.isModule || tpe.termSymbol.isModule def isCaseClass = tpe.typeSymbol.isCase def isObject = (sym != null) && (sym != NoSymbol) && tpe.prefix.isStable // XXX not entire logic def hasStar = false def setType(tpe: Type): this.type = { tree setType tpe this } def equalsCheck = tracing("equalsCheck")( if (sym.isValue) singleType(NoPrefix, sym) else tpe.narrow ) /** Standard methods **/ override def equals(other: Any) = other match { case x: Pattern => this.boundTree == x.boundTree case _ => super.equals(other) } override def hashCode() = boundTree.hashCode() def description = super.toString() final override def toString() = description def toTypeString() = "%s <: x <: %s".format(necessaryType, sufficientType) } /*** Extractors ***/ object UnapplyParamType { def unapply(x: Tree): Option[Type] = condOpt(unbind(x)) { case UnApply(Apply(fn, _), _) => fn.tpe match { case m: MethodType => m.paramTypes.head } } } } Other Scala examples (source code examples)Here is a short list of links related to this Scala Patterns.scala source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.