alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Scala example source code file (Patterns.scala)

This example Scala source code file (Patterns.scala) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Scala tags/keywords

apply, apply, applypattern, ident, list, list, namepattern, pattern, pattern, patternvar, select, tree, tree, unapply

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

 

new blog posts

 

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.