diff options
author | Christopher Vogt <oss.nsp@cvogt.org> | 2017-03-01 01:03:04 +0000 |
---|---|---|
committer | Christopher Vogt <oss.nsp@cvogt.org> | 2017-03-01 01:03:04 +0000 |
commit | 1a2da7da5b003b8b473f67706a04256f49c86c6f (patch) | |
tree | eb4f9bcc95b01d4aecbb2c6eb299adbb79f4d540 | |
parent | 02633f3e5c58c93715bc574d30570a2a05b62f3d (diff) | |
download | cbt-1a2da7da5b003b8b473f67706a04256f49c86c6f.tar.gz cbt-1a2da7da5b003b8b473f67706a04256f49c86c6f.tar.bz2 cbt-1a2da7da5b003b8b473f67706a04256f49c86c6f.zip |
add capture_args macro for type-safe embedding of stringly typed apis
-rw-r--r-- | libraries/capture_args/build/build.scala | 11 | ||||
-rw-r--r-- | libraries/capture_args/macros.scala | 50 | ||||
-rw-r--r-- | stage2/BasicBuild.scala | 1 |
3 files changed, 62 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 +} diff --git a/stage2/BasicBuild.scala b/stage2/BasicBuild.scala index b683640..a6e8f3e 100644 --- a/stage2/BasicBuild.scala +++ b/stage2/BasicBuild.scala @@ -14,6 +14,7 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with Trigge object libraries{ def eval = DirectoryDependency( context.cbtHome ++ "/libraries/eval" ) + def captureArgs = DirectoryDependency( context.cbtHome ++ "/libraries/capture_args" ) } // library available to builds |