Чувак, разве макрос assert не является одним из основных вариантов использования, который ты реализуешь, чтобы научиться использовать макросы?
Ну, я тоже так подумал.
Под «подбором фрагментов» в моем другом ответе я имел в виду, что делает spec2 в своем макросе s2.
Или вы можете сделать произвольное представление, как в моем варианте обдирал ожидания.
Я подумал, что наберу ваш пример в REPL в пару строк. В конце концов, вы просто пытаетесь напечатать фрагмент из источника, который соответствует дереву, представляющему ваше условное выражение.
Что может быть проще?
Конечно, под -Yrangepos
проще, но мы можем постулировать позиции.
Я готов поделиться тем, как далеко я продвинулся, прежде чем потерял интерес.
Люди (например, paulp, который является vox paulpuli) хотят, чтобы деревья имели вложения, представляющие «источник, который я набрал на своей клавиатуре», потому что, вы знаете, может быть, я хочу, чтобы это было для сообщения или чтобы выяснить, что пользователь пытался сделать. выполнить.
Похоже, предикат p
не имеет позиции диапазона. Итак, другая идея состоит в том, что мы знаем начало приложения макроса, которое является скобкой второго списка параметров, поэтому можно работать в обратном направлении через источник, совпадая с закрывающей скобкой первого списка параметров.
Обратите внимание, что showCode
бесполезен, потому что для условного предложения, такого как 10 < 5
, оно показывает false
, аккуратно сложенное.
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]
}
Мне просто пришло в голову получить ранжированную позицию таким образом:
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
. Запас на ошибку.
person
som-snytt
schedule
01.05.2014