Permalink
Browse files

Cleaner means to inline functions.

Rather than the fragile approach taken before,
which relied on unhygienic bindings from the
inlined function body to identifiers in the tree
built with reify, we now defer perform the inlining
in a post-processing step.

Less code, more safety.
  • Loading branch information...
1 parent 7cc27ad commit bacf5afce61795623e57f920aab53ce9b8394ddd @retronym committed Mar 11, 2012
View
@@ -17,7 +17,7 @@ mkir ~/usr
cp build/pack ~/usr/scala-kepler
```
-If you choose a different path, edit `build.sbt` in this project to point `scalaHome` to the right place.
+If you choose a different path, edit `build.scala` in this project to point `scalaHome` to the right place.
### SBT 0.12.0-SNAPSHOT
View
@@ -1,13 +0,0 @@
-organization := "com.github.retronym"
-
-name := "macrocosm"
-
-version := "0.1-SNAPSHOT"
-
-scalaHome := Some(file(System.getProperty("user.home")) / "usr" / "scala-kepler")
-
-scalacOptions ++= Seq("-Xmacros", "-unchecked", "-Yvirtpatmat", "-Xexperimental")
-
-resolvers += "sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapshots"
-
-libraryDependencies <+= (scalaVersion)(sv => "org.scala-lang" % "scala-compiler" % sv)
@@ -0,0 +1,15 @@
+package com.github.retronym.macrocosm
+
+import reflect.makro.Context
+
+/**
+ * Macros useful in the implementation of other macros.
+ *
+ * Just a placeholder to see how this works out, nothing
+ * very useful is currently provided.
+ */
+object MacrocosmGround {
+ def id[A <: AnyRef](a: A): a.type = macro idImpl[a.type]
+
+ def idImpl[A: c.TypeTag](c: Context)(a: c.Expr[A]): c.Expr[A] = a
+}
View
@@ -0,0 +1,26 @@
+import sbt._
+import Keys._
+
+object build extends Build {
+ val sharedSettings = Defaults.defaultSettings ++ Seq(
+ organization := "com.github.retronym",
+ version := "0.1-SNAPSHOT",
+ scalaHome := Some(file(System.getProperty("user.home")) / "usr" / "scala-kepler"),
+ scalacOptions ++= Seq("-Xmacros", "-unchecked", "-Yvirtpatmat", "-Xexperimental" /*, "-Ymacro-debug"*/),
+ resolvers += "sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapshots",
+ libraryDependencies <+= (scalaVersion)(sv => "org.scala-lang" % "scala-compiler" % sv)
+ )
+
+ lazy val root = Project(
+ id = "macrocosm",
+ base = file("."),
+ settings = sharedSettings,
+ dependencies = Seq(ground)
+ )
+
+ lazy val ground = Project(
+ id = "macrocosm-ground",
+ base = file("ground"),
+ settings = sharedSettings
+ )
+}
@@ -275,17 +275,15 @@ object Macrocosm {
(act: c.Expr[A => Unit]): c.Expr[Unit] = {
import c.mirror._
val util = Util(c)
- val elementVarName = newTermName("$elem")
- val actExpr = Expr[Unit](util.inlineApply(act, List(Ident(elementVarName))))
val e = reify {
val i = iterator.eval
while(i.hasNext) {
- val $elem = i.next()
- actExpr.eval
+ val elem = i.next()
+ act.eval(elem)
}
}
- c.Expr[Unit](c.resetAllAttrs(e.tree))
+ c.Expr[Unit](c.resetAllAttrs(util.inlineApplyRecursive(e.tree)))
}
/**
@@ -319,22 +317,19 @@ object Macrocosm {
(f: c.Expr[(A, Int) => Unit]): c.Expr[Unit] = {
import c.mirror._
val util = Util(c); import util._
- val indexVarName = newTermName("$i")
- val elementVarName = newTermName("$elem")
- val fExpr = Expr[Unit](util.inlineApply(f, List(Ident(elementVarName), Ident(indexVarName))))
val expr = reify {
val a = array.eval
- var $i = 0
+ var i = 0
val len = a.length
- while ($i < len) {
- val $elem = a($i)
- fExpr.eval
- $i += 1
+ while (i < len) {
+ val elem = a(i)
+ f.eval(elem, i)
+ i += 1
}
}
- Expr(c.resetAllAttrs(expr.tree))
+ Expr(c.resetAllAttrs(inlineApplyRecursive(expr.tree)))
}
/**
@@ -365,55 +360,15 @@ object Macrocosm {
import c.mirror._
val util = Util(c)
- val elementVarName = newTermName("$elem")
- def inlineApplyExpr[B](f: c.Expr[A => B]) =
- Expr[B](util.inlineApply(f, List(Ident(elementVarName))))
-
- val okayInline = inlineApplyExpr[Boolean](okay)
- val nextInline = inlineApplyExpr[A](next)
- val actInline = inlineApplyExpr[Unit](act)
-
val t = reify {
- var $elem: A = zero.eval
- while(okayInline.eval) {
- actInline.eval
- $elem = nextInline.eval
+ var elem: A = zero.eval
+ while(okay.eval(elem)) {
+ act.eval(elem)
+ elem = next.eval(elem)
}
}
- c.Expr[Unit](c.resetAllAttrs(t))
- }
-
- /**
- * Convert a tree:
- * `((p1, ..., pn) => <body>)(a1, ..., an)`
- * to:
- * `val p1 = a1; ... val pn = a1; <body>`
- *
- * Intended for use in another macro.
- */
- def inlineFunctionApply[A](expr: A) = macro inlineFunctionApplyImpl[A]
-
- def inlineFunctionApplyImpl[A: c.TypeTag]
- (c: Context)
- (expr: c.Expr[A]): c.Expr[A] = {
- import c.mirror._
- val ApplyName = newTermName("apply")
-
- expr.tree match {
- case Apply(Select(prefix, ApplyName), args) =>
- prefix match {
- case Function(params, body) =>
- if (params.length != args.length) sys.error("incorrect arity")
- // val a = args(0); val b = args(1); ...
- val paramVals = params.zip(args).map {
- case (ValDef(_, pName, _, _), a) =>
- ValDef(Modifiers(), pName, TypeTree(), a)
- }
- c.Expr[A](c.resetAllAttrs(Block(paramVals, body)))
- case _ => sys.error("parameter `f` must be a function literal. Found: " + showRaw(prefix))
- }
- case _ => sys.error("unexpected tree: " + showRaw(expr.tree))
- }
+ val t1 = util.inlineApplyRecursive(t)
+ c.Expr[Unit](c.resetAllAttrs(t1))
}
// //case class Lens[A, B](getter: A => B, setter: (A, B) => A)
@@ -479,6 +434,9 @@ object Macrocosm {
import context.mirror._
/**
+ * Reursively transforms `tree`, inlining direct function
+ * application.
+ *
* In:
* `((p1, p2, ... pN) => <body>).apply(a1, a2, ..., aN)`
*
@@ -488,18 +446,37 @@ object Macrocosm {
* <body>
* ````
*/
- def inlineApply(f: Tree, args: List[Tree]): Tree = {
- f match {
- case Function(params, body) =>
- if (params.length != args.length) sys.error("incorrect arity")
- // val a = args(0); val b = args(1); ...
- val paramVals = params.zip(args).map {
- case (ValDef(_, pName, _, _), a) =>
- ValDef(Modifiers(), pName, TypeTree(), a)
+ def inlineApplyRecursive(tree: Tree): Tree = {
+ import context.mirror._
+ val ApplyName = newTermName("apply")
+
+ object inliner extends Transformer {
+ override def transform(tree: Tree): Tree = {
+ tree match {
+ case ap @ Apply(Select(prefix, ApplyName), args) =>
+ prefix match {
+ case Function(params, body) =>
+ if (params.length != args.length) sys.error("incorrect arity")
+ // val a$0 = args(0); val b$0 = args(1); ...
+ val paramVals = params.zip(args).map {
+ case (ValDef(_, pName, _, _), a) =>
+ ValDef(Modifiers(), newTermName(pName.toString + "$0"), TypeTree(), a)
+ }
+ // val a = a$0; val b = b$0
+ val paramVals2 = params.zip(args).map {
+ case (ValDef(_, pName, _, _), a) =>
+ ValDef(Modifiers(), pName, TypeTree(), Ident(newTermName(pName.toString + "$0")))
+ }
+ // The nested blocks avoid name clashes.
+ Block(paramVals, Block(paramVals2, body))
+ case x => ap
+ }
+ case _ => super.transform(tree)
}
- Block(paramVals, body)
- case _ => sys.error("parameter `f` must be a function literal.")
+ }
}
+ val t = inliner.transform(tree)
+ t
}
}
-}
+}

0 comments on commit bacf5af

Please sign in to comment.