我想在THEN部分打印IF条件的Scala源代码。
示例:IF{ 2 + 2 < 5 } THEN { println("I am in THEN because: " + sourceCodeOfCondition) }
我们现在跳过THEN部分,问题是:如何在IF之后获取块的源代码?
我认为IF应该是一个宏......
注意:此问题是Macro to access source code of function at runtime的重新定义版本,其中我描述{ val i = 5; List(1, 2, 3); true }.logValueImpl
适用于我(根据其他问题Macro to access source code text at runtime)。
答案 0 :(得分:3)
袖手旁观的实施,因为我只有一分钟:
import scala.reflect.macros.Context
import scala.language.experimental.macros
case class Conditional(conditionCode: String, value: Boolean) {
def THEN(doIt: Unit) = macro Conditional.THEN_impl
}
object Conditional {
def sourceCodeOfCondition: String = ???
def IF(condition: Boolean) = macro IF_impl
def IF_impl(c: Context)(condition: c.Expr[Boolean]): c.Expr[Conditional] = {
import c.universe._
c.Expr(q"Conditional(${ show(condition.tree) }, $condition)")
}
def THEN_impl(c: Context)(doIt: c.Expr[Unit]): c.Expr[Unit] = {
import c.universe._
val rewriter = new Transformer {
override def transform(tree: Tree) = tree match {
case Select(_, TermName("sourceCodeOfCondition")) =>
c.typeCheck(q"${ c.prefix.tree }.conditionCode")
case other => super.transform(other)
}
}
c.Expr(q"if (${ c.prefix.tree }.value) ${ rewriter.transform(doIt.tree) }")
}
}
然后:
object Demo {
import Conditional._
val x = 1
def demo = IF { x + 5 < 10 } THEN { println(sourceCodeOfCondition) }
}
最后:
scala> Demo.demo
Demo.this.x.+(5).<(10)
这是对来源的一种贬低的表现,但是我认为这是你最好的。
有关该技术的一些讨论,请参阅我的博文here。
答案 1 :(得分:0)
从 2.13 开始,您还可以通过包装表达式来执行此操作,这意味着您不必定义自定义 if
函数:
implicit def debugIf[A]: DebugIf => Unit = { cond: DebugIf =>
logger.info(s"condition = {}, result = ${cond.result}", cond.code)
}
decorateIfs {
if (System.currentTimeMillis() % 2 == 0) {
println("decorateIfs: if block")
} else {
println("decorateIfs: else block")
}
}
使用宏实现:
def decorateIfs[A: c.WeakTypeTag](a: c.Expr[A])(output: c.Expr[DebugIf => Unit]): c.Expr[A] = {
def isEmpty(tree: Trees#Tree): Boolean = {
tree match {
case Literal(Constant(())) =>
true
case other =>
false
}
}
c.Expr[A] {
a.tree match {
// https://docs.scala-lang.org/overviews/quasiquotes/expression-details.html#if
case q"if ($cond) $thenp else $elsep" =>
val condSource = extractRange(cond) getOrElse ""
val printThen = q"$output(DebugIf($condSource, true))"
val elseThen = q"$output(DebugIf($condSource, false))"
val thenTree = q"""{ $printThen; $thenp }"""
val elseTree = if (isEmpty(elsep)) elsep else q"""{ $elseThen; $elsep }"""
q"if ($cond) $thenTree else $elseTree"
case other =>
other
}
}
}
private def extractRange(t: Trees#Tree): Option[String] = {
val pos = t.pos
val source = pos.source.content
if (pos.isRange) Option(new String(source.drop(pos.start).take(pos.end - pos.start))) else None
}
case class DebugIf(code: String, result: Boolean)