Skip to content

Add While and DoWhile extractors Tasty reflect #4630

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 22, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 47 additions & 3 deletions compiler/src/dotty/tools/dotc/tastyreflect/TastyImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -374,9 +374,31 @@ class TastyImpl(val rootContext: Contexts.Context) extends scala.tasty.Tasty { s
}

object Block extends BlockExtractor {
def unapply(x: Term)(implicit ctx: Context): Option[(List[Statement], Term)] = x match {
case x: tpd.Block @unchecked => Some((x.stats, x.expr))
case _ => None
def unapply(x: Term)(implicit ctx: Context): Option[(List[Statement], Term)] = normalizedLoops(x) match {
case Trees.Block(_, expr) if expr.symbol.is(Flags.Label) => None // while or doWhile loops
case Trees.Block(stats, expr) => Some((stats, expr))
case _ => None
}
/** Normilizes non Blocks.
* i) Put `while` and `doWhile` loops in thier own blocks: `{ def while$() = ...; while$() }`
*/
private def normalizedLoops(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
case block: tpd.Block if block.stats.size > 1 =>
def normalizeInnerLoops(stats: List[tpd.Tree]): List[tpd.Tree] = stats match {
case (x: tpd.DefDef) :: y :: xs if y.symbol.is(Flags.Label) =>
tpd.Block(x :: Nil, y) :: normalizeInnerLoops(xs)
case x :: xs => x :: normalizeInnerLoops(xs)
case Nil => Nil
}
if (block.expr.symbol.is(Flags.Label)) {
val stats1 = normalizeInnerLoops(block.stats.init)
val normalLoop = tpd.Block(block.stats.last :: Nil, block.expr)
tpd.Block(stats1, normalLoop)
} else {
val stats1 = normalizeInnerLoops(block.stats)
tpd.cpy.Block(block)(stats1, block.expr)
}
case _ => tree
}
}

Expand Down Expand Up @@ -441,6 +463,28 @@ class TastyImpl(val rootContext: Contexts.Context) extends scala.tasty.Tasty { s
}
}

object While extends WhileExtractor {
def unapply(x: Term)(implicit ctx: Context): Option[(Term, Term)] = x match {
case Trees.Block((ddef: tpd.DefDef) :: Nil, expr) if expr.symbol.is(Flags.Label) && expr.symbol.name == nme.WHILE_PREFIX =>
val Trees.If(cond, Trees.Block(bodyStats, _), _) = ddef.rhs
Some((cond, loopBody(bodyStats)))
case _ => None
}
}

object DoWhile extends DoWhileExtractor {
def unapply(x: Term)(implicit ctx: Context): Option[(Term, Term)] = x match {
case Trees.Block((ddef: tpd.DefDef) :: Nil, expr) if expr.symbol.is(Flags.Label) && expr.symbol.name == nme.DO_WHILE_PREFIX =>
val Trees.Block(bodyStats, Trees.If(cond, _, _)) = ddef.rhs
Some((loopBody(bodyStats), cond))
case _ => None
}
}

private def loopBody(stats: List[tpd.Tree])(implicit ctx: Context): tpd.Tree = stats match {
case body :: Nil => body
case stats => tpd.Block(stats.init, stats.last)
}
}

// ----- CaseDef --------------------------------------------------
Expand Down
11 changes: 11 additions & 0 deletions library/src/scala/tasty/Tasty.scala
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,17 @@ abstract class Tasty { tasty =>
def unapply(x: Term)(implicit ctx: Context): Option[(Term, Int, Type)]
}

val While: WhileExtractor
abstract class WhileExtractor {
/** Extractor for while loops. Matches `while (<cond>) <body>` and returns (<cond>, <body>) */
def unapply(x: Term)(implicit ctx: Context): Option[(Term, Term)]
}

val DoWhile: DoWhileExtractor
abstract class DoWhileExtractor {
/** Extractor for do while loops. Matches `do <body> while (<cond>)` and returns (<body>, <cond>) */
def unapply(x: Term)(implicit ctx: Context): Option[(Term, Term)]
}
}

// ----- CaseDef --------------------------------------------------
Expand Down
58 changes: 8 additions & 50 deletions library/src/scala/tasty/util/ShowSourceCode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -204,33 +204,15 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
this
}

case While(cond, stats) =>
case Term.While(cond, body) =>
this += "while ("
printTree(cond)
this += ") "
stats match {
case stat :: Nil =>
printTree(stat)
case stats =>
this += "{"
indented {
printStats(stats.init, stats.last)
}
this += lineBreak() += "}"
}
printTree(body)

case DoWhile(stats, cond) =>
case Term.DoWhile(body, cond) =>
this += "do "
stats match {
case stat :: Nil =>
printTree(stat)
case stats =>
this += "{"
indented {
printStats(stats.init, stats.last)
}
this += lineBreak() += "}"
}
printTree(body)
this += " while ("
printTree(cond)
this += ")"
Expand Down Expand Up @@ -349,14 +331,11 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
printTree(rhs)

case Term.Block(stats0, expr) =>
def shouldNotBePrinted(tree: Tree): Boolean = tree match {
case Term.Apply(Term.Ident("while$" | "doWhile$"), _) => true
case tree @ ValDef(_, _, _) => tree.flags.isObject
case _ => false
val stats = stats0.filter {
case tree @ ValDef(_, _, _) => !tree.flags.isObject
case _ => true
}

val stats = stats0.filterNot(shouldNotBePrinted)

expr match {
case Term.Lambda(_, _) =>
// Decompile lambda from { def annon$(...) = ...; closure(annon$, ...)}
Expand All @@ -368,11 +347,8 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
this += ")"
case _ =>
this += "{"
val (stats1, expr1) =
if (shouldNotBePrinted(expr)) (stats.init, stats.last)
else (stats, expr)
indented {
printStats(stats1, expr1)
printStats(stats, expr)
}
this += lineBreak() += "}"
}
Expand Down Expand Up @@ -441,7 +417,6 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
// Avoid accidental application of opening `{` on next line with a double break
val next = if (nextStats.isEmpty) expr else nextStats.head
next match {
case Term.Block(DefDef("while$" | "doWhile$", _, _, _, _) :: Nil, _) => this += lineBreak()
case Term.Block(_, _) => this += doubleLineBreak()
case Term.Inlined(_, _, _) => this += doubleLineBreak()
case _ => this += lineBreak()
Expand Down Expand Up @@ -1073,7 +1048,6 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
private def escapedString(str: String): String = str flatMap escapedChar
}


private object SpecialOp {
def unapply(arg: Term)(implicit ctx: Context): Option[(String, List[Term])] = arg match {
case arg@Term.Apply(fn, args) =>
Expand All @@ -1086,22 +1060,6 @@ class ShowSourceCode[T <: Tasty with Singleton](tasty0: T) extends Show[T](tasty
}
}

private object While {
def unapply(arg: Tree)(implicit ctx: Context): Option[(Term, List[Statement])] = arg match {
case DefDef("while$", _, _, _, Some(Term.If(cond, Term.Block(bodyStats, _), _))) => Some((cond, bodyStats))
case Term.Block(List(tree), _) => unapply(tree)
case _ => None
}
}

private object DoWhile {
def unapply(arg: Tree)(implicit ctx: Context): Option[(List[Statement], Term)] = arg match {
case DefDef("doWhile$", _, _, _, Some(Term.Block(body, Term.If(cond, _, _)))) => Some((body, cond))
case Term.Block(List(tree), _) => unapply(tree)
case _ => None
}
}

private object Annotation {
def unapply(arg: Tree)(implicit ctx: Context): Option[(TypeTree, List[Term])] = arg match {
case Term.New(annot) => Some((annot, Nil))
Expand Down
2 changes: 2 additions & 0 deletions tests/pos/tasty/definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ object definitions {
case Return(expr: Term)
case Repeated(args: List[Term])
case SelectOuter(from: Term, levels: Int, target: Type) // can be generated by inlining
case While(cond: Term, body: Term)
case DoWhile(body: Term, cond: Term)
}

/** Trees denoting types */
Expand Down