Scala宏可以打印代码? [英] Scala macro to print code?

查看:87
本文介绍了Scala宏可以打印代码?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想做这样的事情:

def assuming[A](condition: => Boolean)(f: => A): A = {
  require(condition, /* print source-code of condition */)
  f
}

样品用量:

def fib(n: Int) = n match { // yes, yes, I know this is not efficient
  case 0 => 0 
  case 1 => 1
  case i => assuming(i > 0) { fib(i-1) + fib(i-2) }
}

例如,现在,如果您调用fib(-20),我希望它引发类似Assertion failed: -20 > 0Assertation failed: i > 0

Now, for example, if you call fib(-20), I want it to throw an exception with a message like Assertion failed: -20 > 0 or Assertation failed: i > 0

推荐答案

老兄,这不是断言宏,您实现的基本用例之一就是学习如何使用宏?

Dude, isn't an assert macro one of the basic use cases you implement to learn how to use macros?

那也是我的想法.

在我的另一个答案中,通过纯摘录"来表示specs2的作用

By "glean snippets" in my other answer I meant what specs2 does in its s2 macro.

或者,您可以进行任意表示,例如

Or, you can do an arbitrary representation, as in my variant rip-off of expecty.

我想我会在两行中将您的示例输入REPL.毕竟,您只是试图从与代表您的条件的树相对应的源中打印摘录.

I thought I'd type your example into REPL, in a couple of lines. After all, you're just trying to print the snippet from the source that corresponds to the tree representing your conditional.

还有什么更容易的吗?

当然,在-Yrangepos下更容易,但是我们可以假设职位.

Of course, it's easier under -Yrangepos, but we can postulate positions.

我愿意分享失去兴趣之前的成就.

I'm willing to share how far I got before I lost interest.

人们(例如paulp,谁是vox paulpuli)希望树具有表示我在键盘上键入的来源"的附件,因为,您知道,也许我想要它作为消息或弄清楚用户的身份试图实现.

People (e.g., paulp, who is the vox paulpuli) want trees to have attachments representing "the source I typed on my keyboard", because, you know, maybe I want it for a message or to figure out what the user was trying to accomplish.

谓词p似乎没有范围位置.因此,另一个想法是,我们知道宏应用程序的开始,它是第二个参数列表的paren,因此可以通过与第一个参数列表的结束paren匹配的源进行向后工作.

It looks like the predicate p doesn't have a range position. So the other idea is that we know the start of the macro application, which is the paren of the second param list, so working backward through the source, matching the closing paren of the first param list, is doable.

请注意,showCode并没有帮助,因为对于10 < 5这样的条件,它会显示false并整齐地折叠.

Note that showCode isn't helpful because for a conditional like 10 < 5 it shows false, neatly folded.

object X {
  import reflect.macros.blackbox.Context
  def impl[A: c.WeakTypeTag](c: Context)(p: c.Expr[Boolean])(body: c.Expr[A]) = {
    import c.universe._
    def treeLine(t: Tree): String = lineAt(t.pos)
    def lineAt(pos: Position): String = if (pos.isRange) pos.lineContent.drop(pos.column - 1).take(pos.end - pos.start + 1) else "???"
    val msg =
      if (p.tree.pos.isRange) {  // oh, joy
        treeLine(p.tree)
      } else {
        /*
        Console println s"content ${p.tree.pos.lineContent}"
        Console println s"column ${p.tree.pos.column}"  // alas, that's the column of the point of the top of the tree, e.g., < in "a < b".
        val len = body.tree.pos.start - p.tree.pos.start
        p.tree.pos.lineContent drop (p.tree.pos.column - 1) take len
        */
        // OK, I get it: positions are a big mystery. Make woo-woo ghost noises.
        // What we do know is the start of the apply, which must have a close paren or brace in front of it to match:
        // apply(condition)(body)
        showCode(p.tree)
      }
    q"require($p, $msg) ; $body"
  }
  def x[A](p: Boolean)(body: =>A): A = macro X.impl[A]
}

我只是想通过这种方式获得高薪职位:

It just occurred to me to get the rangy position this way:

object X {
  import reflect.macros.blackbox.Context
  def impl(c: Context)(p: c.Expr[Boolean]) = {
    import c.universe._
    def lineAt(pos: Position): String = if (pos.isRange) pos.lineContent.drop(pos.column - 1).take(pos.end - pos.start + 1) else "???" 
    val msg = lineAt(c.macroApplication.pos)  // oh, joy
    q"require($p, $msg) ; new { def apply[A](body: =>A): A = body }"
  }
  def x(p: Boolean): { def apply[A](body: =>A): A } = macro X.impl
}

这在用法x(10 < 5)(println("hi")):requirement failed: (10 < 5)(p上是关闭的.保证金错误.

That's close on usage x(10 < 5)(println("hi")): requirement failed: (10 < 5)(p. Margin for error.

这篇关于Scala宏可以打印代码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆