parallelmatching.scala
来自「JAVA 语言的函数式编程扩展」· SCALA 代码 · 共 1,405 行 · 第 1/4 页
SCALA
1,405 行
/* NSC -- new Scala compiler * Copyright 2005-2007 LAMP/EPFL * Copyright 2007 Google Inc. All Rights Reserved. * Author: bqe@google.com (Burak Emir) */// $Id: ParallelMatching.scala 14626 2008-04-10 23:11:00Z emir $package scala.tools.nsc.matchingimport scala.tools.nsc.util.Positionimport collection.mutable.ListBuffer/** Translation of match expressions. * * `p': pattern * `g': guard * `bx': body index * * internal representation is (temp:List[Symbol], row:List[Row]) * * tmp1 tmp_n * Row( p_11 ... p_1n g_1 b_1 ) + subst * * Row( p_m1 ... p_mn g_m b_m ) + subst * */trait ParallelMatching { self: transform.ExplicitOuter with PatternNodes with CodeFactory => import global._ import typer.typed import symtab.Flags // used as argument to `EqualsPatternClass' case class PseudoType(o: Tree) extends SimpleTypeProxy { override def underlying: Type = o.tpe } /** picks which rewrite rule to apply * @precondition: column does not contain alternatives (ensured by initRep) */ def MixtureRule(scrutinee: Symbol, column: List[Tree], rest: Rep)(implicit rep: RepFactory): RuleApplication = { def isSimpleSwitch: Boolean = { (isSameType(scrutinee.tpe.widen, definitions.IntClass.tpe) || isSameType(scrutinee.tpe.widen, definitions.CharClass.tpe)) && { var xs = column; while(!xs.isEmpty) { // forall val h = xs.head if (strip2(h).isInstanceOf[Literal] || isDefaultPattern(h)) xs = xs.tail else return false } return true }} // an unapply for which we don't need a type test def isUnapplyHead(): Boolean = column.head match { case __UnApply(_,argtpe,_) => scrutinee.tpe <:< argtpe case _ => false } // true if pattern type is direct subtype of scrutinee (can't use just <:< cause have to take variance into account) def directSubtype(ptpe: Type) = (ptpe.parents.exists { x => ((x.typeSymbol eq scrutinee.tpe.typeSymbol) && (x <:< scrutinee.tpe))}); // true if each pattern type is case and direct subtype of scrutinee def isFlatCases(col:List[Tree]): Boolean = (col eq Nil) || { strip2(col.head) match { case a @ Apply(fn,_) => isCaseClass(a.tpe) && directSubtype( a.tpe ) && isFlatCases(col.tail) case t @ Typed(_,tpt) => isCaseClass(tpt.tpe) && directSubtype( t.tpe ) && isFlatCases(col.tail) case Ident(nme.WILDCARD) => isFlatCases(col.tail) // treat col.tail specially? case p => false } } if (isEqualsPattern(column.head.tpe)) { return new MixEquals(scrutinee, column, rest) } if (column.head.isInstanceOf[ArrayValue]) { val av = column.head.asInstanceOf[ArrayValue] return if (!isRightIgnoring(av)) new MixSequence(scrutinee, column, rest) else new MixSequenceStar(scrutinee, column, rest) } if (isSimpleSwitch) { return new MixLiterals(scrutinee, column, rest) } if (settings_casetags && (column.length > 1) && isFlatCases(column)) { return new MixCases(scrutinee, column, rest) } if (isUnapplyHead()) { return new MixUnapply(scrutinee, column, rest) } return new MixTypes(scrutinee, column, rest) } sealed abstract class RuleApplication(rep: RepFactory) { def scrutinee:Symbol // used in MixEquals and MixSequence final protected def repWithoutHead(col: List[Tree],rest: Rep)(implicit theOwner: Symbol): Rep = { var fcol = col.tail var frow = rest.row.tail val nfailrow = new ListBuffer[Row] while(fcol ne Nil) { val p = fcol.head frow.head match { case Row(pats, binds, g, bx) => nfailrow += Row(p::pats, binds, g, bx) } fcol = fcol.tail frow = frow.tail } rep.make(scrutinee::rest.temp, nfailrow.toList) } /** translate outcome of the rule application into code (possible involving recursive application of rewriting) */ def tree(implicit theOwner: Symbol, failTree: Tree): Tree } case class ErrorRule(implicit rep:RepFactory) extends RuleApplication(rep) { def scrutinee:Symbol = throw new RuntimeException("this never happens") final def tree(implicit theOwner: Symbol, failTree: Tree) = failTree } /** {case ... if guard => bx} else {guardedRest} */ case class VariableRule(subst:Binding, guard: Tree, guardedRest:Rep, bx: Int)(implicit rep:RepFactory) extends RuleApplication(rep) { def scrutinee:Symbol = throw new RuntimeException("this never happens") final def tree(implicit theOwner: Symbol, failTree: Tree): Tree = { val body = typed { rep.requestBody(bx, subst) } if (guard eq EmptyTree) return body val vdefs = targetParams(subst) val typedElse = repToTree(guardedRest) val typedIf = typed { If(guard.duplicate, body, typedElse) } typer.typed { squeezedBlock(vdefs, typedIf) } } } /** superclass of mixture rules for case classes and literals (both translated to switch on an integer) */ abstract class CaseRuleApplication(rep:RepFactory) extends RuleApplication(rep) { def column: List[Tree] def rest:Rep // e.g. (1,1) (1,3) (42,2) for column {case ..1.. => ;; case ..42..=> ;; case ..1.. => } protected var defaults: List[Int] = Nil var defaultV: collection.immutable.Set[Symbol] = emptySymbolSet var theDefaultRows: List[Row] = null def getDefaultRows: List[Row] = { if (theDefaultRows ne null) return theDefaultRows var res:List[Row] = Nil var ds = defaults; while(ds ne Nil) { res = grabRow(ds.head) :: res ds = ds.tail } theDefaultRows = res res } // sorted e.g. case _ => 7,5,1 protected def insertDefault(tag: Int,vs:Set[Symbol]) { defaultV = defaultV ++ vs defaults = insertSorted(tag, defaults) } protected def haveDefault: Boolean = !defaults.isEmpty var tagIndexPairs: TagIndexPair = null protected def grabTemps: List[Symbol] = rest.temp protected def grabRow(index:Int): Row = rest.row(index) match { case r @ Row(pats, s, g, bx) => if (defaultV.isEmpty) r else { val vs = strip1(column(index)) // get vars val nbindings = s.add(vs.elements,scrutinee) Row(pats, nbindings, g, bx) } } /** inserts row indices using in to list of tagindexpairs*/ protected def tagIndicesToReps(implicit theOwner: Symbol) = { val defaultRows = getDefaultRows var trs: List[(Int,Rep)] = Nil var old = tagIndexPairs while (tagIndexPairs ne null) { // collect all with same tag val tag = tagIndexPairs.tag var tagRows = this.grabRow(tagIndexPairs.index)::Nil tagIndexPairs = tagIndexPairs.next while ((tagIndexPairs ne null) && tagIndexPairs.tag == tag) { tagRows = this.grabRow(tagIndexPairs.index)::tagRows tagIndexPairs = tagIndexPairs.next } trs = (tag, rep.make(this.grabTemps, tagRows ::: defaultRows)) :: trs } tagIndexPairs = old trs } protected def defaultsToRep(implicit theOwner: Symbol) = rep.make(rest.temp, getDefaultRows) protected def insertTagIndexPair(tag: Int, index: Int) { tagIndexPairs = TagIndexPair.insert(tagIndexPairs, tag, index) } /** returns * @return list of continuations, * @return variables bound to default continuation, * @return optionally, a default continuation, **/ def getTransition(implicit theOwner: Symbol): (List[(Int,Rep)],Set[Symbol],Option[Rep]) = (tagIndicesToReps, defaultV, {if (haveDefault) Some(defaultsToRep) else None}) } /** mixture rule for flat case class (using tags) * this rule gets translated to a switch of _.$tag() **/ class MixCases(val scrutinee:Symbol, val column:List[Tree], val rest:Rep)(implicit rep:RepFactory) extends CaseRuleApplication(rep) { /** insert row indices into list of tagindexpairs */ { var xs = column; var i = 0; while(xs ne Nil) { // forall val p = strip2(xs.head) if (isDefaultPattern(p)) insertDefault(i, strip1(xs.head)) else insertTagIndexPair(getCaseTag(p.tpe), i) i += 1 xs = xs.tail } } override def grabTemps = scrutinee::rest.temp override def grabRow(index:Int) = rest.row(tagIndexPairs.index) match { case Row(pats, s, g, bx) => val nbindings = s.add(strip1(column(index)).elements, scrutinee) Row(column(tagIndexPairs.index)::pats, nbindings, g, bx) } final def tree(implicit theOwner: Symbol, failTree: Tree): Tree = { val (branches, defaultV, default) = getTransition // tag body pairs var ndefault = if (default.isEmpty) failTree else repToTree(default.get) var cases = branches map { case (tag, r) => CaseDef(Literal(tag), EmptyTree, { val pat = this.column(this.tagIndexPairs.find(tag)); val ptpe = pat.tpe if (this.scrutinee.tpe.typeSymbol.hasFlag(symtab.Flags.SEALED) && strip2(pat).isInstanceOf[Apply]) { //cast val vtmp = newVar(pat.pos, ptpe) squeezedBlock( List(typedValDef(vtmp, gen.mkAsInstanceOf(mkIdent(this.scrutinee), ptpe))), repToTree(rep.make(vtmp :: r.temp.tail, r.row)) ) } else repToTree(r) } )} // make first case a default case. if (this.scrutinee.tpe.typeSymbol.hasFlag(symtab.Flags.SEALED) && defaultV.isEmpty) { ndefault = cases.head.body cases = cases.tail } cases.length match { case 0 => ndefault case 1 => val CaseDef(lit,_,body) = cases.head If(Equals(Select(mkIdent(this.scrutinee), nme.tag), lit), body, ndefault) case _ => val defCase = CaseDef(mk_(definitions.IntClass.tpe), EmptyTree, ndefault) Match(Select(mkIdent(this.scrutinee),nme.tag), cases ::: defCase :: Nil) } } } /** mixture rule for literals */ class MixLiterals(val scrutinee:Symbol, val column:List[Tree], val rest:Rep)(implicit rep:RepFactory) extends CaseRuleApplication(rep) { private var defaultIndexSet: Set64 = if (column.length < 64) new Set64 else null override def insertDefault(tag: Int, vs: Set[Symbol]): Unit = if (defaultIndexSet eq null) super.insertDefault(tag,vs) else { defaultIndexSet |= tag defaultV = defaultV ++ vs } protected override def haveDefault: Boolean = if (defaultIndexSet eq null) super.haveDefault else (defaultIndexSet.underlying != 0) override def getDefaultRows: List[Row] = { if (theDefaultRows ne null) return theDefaultRows if (defaultIndexSet eq null) super.getDefaultRows else { var ix = 63 var res:List[Row] = Nil while((ix >= 0) && !defaultIndexSet.contains(ix)) { ix = ix - 1 } while(ix >= 0) { res = grabRow(ix) :: res ix = ix - 1 while((ix >= 0) && !defaultIndexSet.contains(ix)) { ix = ix - 1 } } theDefaultRows = res res } } var varMap: List[(Int,List[Symbol])] = Nil private def sanity(pos:Position, tag: Int, pvars:List[Symbol]) { varMap = (tag,pvars)::varMap } //lazy private def bindVars(Tag:Int, orig: Binding): Binding = { def myBindVars(rest:List[(Int,List[Symbol])], bnd: Binding): Binding = rest match { case Nil => bnd case (Tag,vs)::xs => myBindVars(xs, bnd.add(vs.elements, scrutinee)) case (_, vs)::xs => myBindVars(xs, bnd) } myBindVars(varMap, orig) } /*block*/{ var xs = column var i = 0; var last = -1; while(xs ne Nil) { // forall if (last != -1) { cunit.error(xs.head.pos, "unreachable code") } strip(xs.head) match { case (pvars, p @ Literal(Constant(c:Int))) => sanity(p.pos, c , definedVars(xs.head)); insertTagIndexPair(c,i) case (pvars, p @ Literal(Constant(c:Char))) => sanity(p.pos, c.toInt, definedVars(xs.head)); insertTagIndexPair(c.toInt,i) case (pvars, p ) if isDefaultPattern(p) => insertDefault(i,pvars) case (pvars, p ) if isDefaultPattern(p) => if (rest.row(i).guard == EmptyTree) { last = i; } insertDefault(i,pvars) } i += 1 xs = xs.tail
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?