Skip to content

Commit

Permalink
Merge pull request #14 from benhag/args
Browse files Browse the repository at this point in the history
Added macro for passing all arguments of a method as implicit parameter
  • Loading branch information
lihaoyi committed May 24, 2016
2 parents 8e83e38 + ebc1b11 commit 017d3fc
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 0 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ The kinds of compilation-time data that `sourcecode` provides are:
you have multiple statements in a `{}` block, `sourcecode.Text` will only
capture the source code for the last expression that gets returned. This
implicit is slightly experimental; be sure to report any bugs you find!
- `sourcecode.Args`: the arguments that where provided to the nearest enclosing
method
- `sourcecode.Name.Machine`, `sourcecode.FullName.Machine` and
`sourcecode.Enclosing.Machine` which are similar to `sourcecode.Name`,
`sourcecode.FullName` and `sourcecode.Enclosing` except they do not filter
Expand Down Expand Up @@ -475,6 +477,21 @@ be printed. You can, or course, define your own `log` method in the same way,
customizing it to print or not-print exactly what you want to see via the
implicits that `sourcecode` provides!

`sourcecode.Args` can be used to access all parameters that where provided
to a method:

```scala
def debug(implicit name: sourcecode.Name, args: sourcecode.Args): Unit = {
println(name.value + args.value.map(_.map(a => a.source + "=" + a.value).mkString("(", ", ", ")")).mkString(""))
}

def foo(bar: String, baz: Int)(p: Boolean): Unit = {
debug
}

foo("baz", 42)(true) // foo(bar=baz, baz=42)(p=true)
```

Embedding Domain-Specific Languages
-----------------------------------

Expand Down
13 changes: 13 additions & 0 deletions sourcecode/shared/src/main/scala-2.10/sourcecode/Compat.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,17 @@ object Compat{
.owner
.asInstanceOf[c.Symbol]
}

def enclosingParamList(c: Context): List[List[c.Symbol]] = {
def nearestClassOrMethod(owner: c.Symbol): c.Symbol =
if (owner.isMethod || owner.isClass) owner else nearestClassOrMethod(owner.owner)

val com = nearestClassOrMethod(enclosingOwner(c))
if (com.isClass) {
val pc = com.typeSignature.members.filter(m => m.isMethod && m.asMethod.isPrimaryConstructor)
pc.head.asMethod.paramss
} else {
com.asMethod.paramss
}
}
}
9 changes: 9 additions & 0 deletions sourcecode/shared/src/main/scala-2.11/sourcecode/Compat.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,13 @@ package sourcecode
object Compat{
type Context = scala.reflect.macros.blackbox.Context
def enclosingOwner(c: Context) = c.internal.enclosingOwner

def enclosingParamList(c: Context): List[List[c.Symbol]] = {
def nearestEnclosingMethod(owner: c.Symbol): c.Symbol =
if (owner.isMethod) owner
else if (owner.isClass) owner.asClass.primaryConstructor
else nearestEnclosingMethod(owner.owner)

nearestEnclosingMethod(enclosingOwner(c)).asMethod.paramLists
}
}
13 changes: 13 additions & 0 deletions sourcecode/shared/src/main/scala/sourcecode/SourceContext.scala
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,19 @@ object Text{
def apply[T](v: T): Text[T] = macro Impls.text[T]

}

case class Args(value: Seq[Seq[Text[_]]]) extends SourceValue[Seq[Seq[Text[_]]]]
object Args extends SourceCompanion[Seq[Seq[Text[_]]], Args](new Args(_)) {
implicit def generate: Args = macro impl
def impl(c: Compat.Context): c.Expr[Args] = {
import c.universe._
val param = Compat.enclosingParamList(c)
val texts = param.map(_.map(p => c.Expr[Text[_]](q"""sourcecode.Text($p, ${p.name.toString})""")))
val textSeqs = texts.map(s => c.Expr(q"""Seq(..$s)"""))
c.Expr[Args](q"""Seq(..$textSeqs)""")
}
}

object Impls{
def text[T: c.WeakTypeTag](c: Compat.Context)(v: c.Expr[T]): c.Expr[sourcecode.Text[T]] = {
import c.universe._
Expand Down
60 changes: 60 additions & 0 deletions sourcecode/shared/src/test/scala/sourcecode/ArgsTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package sourcecode

object ArgsTests {
def apply() = {

var args: Seq[Seq[(String, Any)]] = Seq()

def debug(implicit arguments: sourcecode.Args): Unit = args = arguments.value.map(_.map(t => t.source -> t.value))

def foo(p1: String, p2: Long, p3: Boolean)(foo: String, bar: String): Unit = {
debug
}

def bar(p1: String, p2: Long, p3: Boolean)(foo: String, bar: String): Unit = {
val bar = {
debug
"bar"
}
}

def baz: Unit = {
debug
}

def withImplicit(p1: String, p2: Long, p3: Boolean)(implicit foo: String): Unit = {
debug
}

class Foo(p1: String, p2: Long, p3: Boolean)(foo: String, bar: String) {
debug

def this(p1: String, p2: Long) = {
this(p1, p2, false)("foo", "bar")
debug
}
}

new Foo("text", 42, false)("foo", "bar")
assert(args == Seq(Seq("p1" -> "text", "p2" -> 42, "p3" -> false), Seq("foo" -> "foo", "bar" -> "bar")))

new Foo("text", 42)
assert(args == Seq(Seq("p1" -> "text", "p2" -> 42)))

foo("text", 42, false)("foo", "bar")
assert(args == Seq(Seq("p1" -> "text", "p2" -> 42, "p3" -> false), Seq("foo" -> "foo", "bar" -> "bar")))

bar("text", 42, false)("foo", "bar")
assert(args == Seq(Seq("p1" -> "text", "p2" -> 42, "p3" -> false), Seq("foo" -> "foo", "bar" -> "bar")))

baz
assert(args == Seq())

withImplicit("text", 42, false)("foo")
assert(args == Seq(Seq("p1" -> "text", "p2" -> 42, "p3" -> false), Seq("foo" -> "foo")))

implicit val implicitFoo = "bar"
withImplicit("text", 42, false)
assert(args == Seq(Seq("p1" -> "text", "p2" -> 42, "p3" -> false), Seq("foo" -> "bar")))
}
}
1 change: 1 addition & 0 deletions sourcecode/shared/src/test/scala/sourcecode/Tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ object Tests{
Synthetic.run()
ManualImplicit()
TextTests()
ArgsTests()

println("================LogExample================")
logExample()
Expand Down

0 comments on commit 017d3fc

Please sign in to comment.