aboutsummaryrefslogtreecommitdiff
path: root/libraries
diff options
context:
space:
mode:
authorChristopher Vogt <oss.nsp@cvogt.org>2017-03-01 01:03:04 +0000
committerChristopher Vogt <oss.nsp@cvogt.org>2017-03-01 01:03:04 +0000
commit1a2da7da5b003b8b473f67706a04256f49c86c6f (patch)
treeeb4f9bcc95b01d4aecbb2c6eb299adbb79f4d540 /libraries
parent02633f3e5c58c93715bc574d30570a2a05b62f3d (diff)
downloadcbt-1a2da7da5b003b8b473f67706a04256f49c86c6f.tar.gz
cbt-1a2da7da5b003b8b473f67706a04256f49c86c6f.tar.bz2
cbt-1a2da7da5b003b8b473f67706a04256f49c86c6f.zip
add capture_args macro for type-safe embedding of stringly typed apis
Diffstat (limited to 'libraries')
-rw-r--r--libraries/capture_args/build/build.scala11
-rw-r--r--libraries/capture_args/macros.scala50
2 files changed, 61 insertions, 0 deletions
diff --git a/libraries/capture_args/build/build.scala b/libraries/capture_args/build/build.scala
new file mode 100644
index 0000000..24c1faa
--- /dev/null
+++ b/libraries/capture_args/build/build.scala
@@ -0,0 +1,11 @@
+package cbt_build.cbt.capture_args
+import cbt._
+class Build(val context: Context) extends BaseBuild{
+ override def dependencies = (
+ super.dependencies ++ // don't forget super.dependencies here for scala-library, etc.
+ Resolver( mavenCentral ).bind(
+ MavenDependency( "org.scala-lang", "scala-reflect", scalaVersion )
+ )
+ )
+ override def scalacOptions = super.scalacOptions :+ "-language:experimental.macros"
+}
diff --git a/libraries/capture_args/macros.scala b/libraries/capture_args/macros.scala
new file mode 100644
index 0000000..8f2d747
--- /dev/null
+++ b/libraries/capture_args/macros.scala
@@ -0,0 +1,50 @@
+package cbt.capture_args
+import scala.reflect._
+import scala.reflect.macros.blackbox.Context
+
+case class Argument( annotations: Seq[annotation.Annotation], name: String, values: Option[Seq[String]] ){
+ def toSeqOption = values.map( name +: _ )
+}
+case class Signature( name: String, args: Seq[Argument] )
+object `package`{
+ def captureArgsImplementation(c: Context): c.Tree = {
+ import c.universe._
+
+ def literal( a: Any ) = Literal(Constant(a))
+ def ident( name: String ) = Ident(TermName(name))
+
+ def findOwnerRecursive(symbol: Symbol, predicate: Symbol => Boolean): Option[Symbol] = {
+ Option(symbol).flatMap{
+ s =>
+ if(s == NoSymbol) None else if(predicate(s)) Some(s) else findOwnerRecursive(s.owner, predicate)
+ }
+ }
+
+ val method: MethodSymbol = (
+ findOwnerRecursive(c.internal.enclosingOwner, _.isMethod).map(_.asMethod)
+ orElse
+ findOwnerRecursive(c.internal.enclosingOwner, _.isClass).map(_.asClass.primaryConstructor.asMethod)
+ getOrElse {
+ c.error(
+ c.enclosingPosition,
+ "embed needs to be called in the body of a qualified method"
+ )
+ ???
+ }
+ )
+ val name = literal(method.name.decodedName.toString)
+ // Note: method.paramLists requires explicitly annotated result type
+ val params = method.paramLists.flatten.map(_.asTerm)
+
+ val args = params.map{ s =>
+ val name = literal( s.name.decodedName.toString )
+ val i = ident( s.name.toString )
+ q"_root_.cbt.capture_args.Argument( _root_.scala.Seq( ..${s.annotations.map(_.tree)} ), $name, valueToStrings($i) )"
+ }
+ val tree = q"""
+ _root_.cbt.capture_args.Signature( name = $name, args = Seq( ..$args ) )
+ """
+ tree
+ }
+ def captureArgs: Signature = macro captureArgsImplementation
+}