summaryrefslogtreecommitdiff
path: root/test/files/run/macro-range
diff options
context:
space:
mode:
authorEugene Burmako <xeno.by@gmail.com>2012-01-20 01:52:40 +0100
committerEugene Burmako <xeno.by@gmail.com>2012-01-20 06:54:20 +0100
commit35e676ded0f9bfd006a5f090841abdea3ff1759c (patch)
treeade8247e0ebc66964189875194f78b85c6596d27 /test/files/run/macro-range
parent58cb15c40dc431e45eaa0a5278874d9996e42104 (diff)
downloadscala-35e676ded0f9bfd006a5f090841abdea3ff1759c.tar.gz
scala-35e676ded0f9bfd006a5f090841abdea3ff1759c.tar.bz2
scala-35e676ded0f9bfd006a5f090841abdea3ff1759c.zip
Progress with macros
A short recap: * Macro expansion now works finely for instance macro invocations * Macros are now hidden behind -Xmacros * Bodies of macros now have "import _context._" in their preamble * Macros are now loaded from classpath, much like regular libraries * Macros can now override methods (in that case macro expansion does not crash if macro is not found, it just falls back to super) Review by @odersky.
Diffstat (limited to 'test/files/run/macro-range')
-rw-r--r--test/files/run/macro-range/macro_range_1.scala94
-rw-r--r--test/files/run/macro-range/macro_range_2.scala94
2 files changed, 188 insertions, 0 deletions
diff --git a/test/files/run/macro-range/macro_range_1.scala b/test/files/run/macro-range/macro_range_1.scala
new file mode 100644
index 0000000000..15a018fcff
--- /dev/null
+++ b/test/files/run/macro-range/macro_range_1.scala
@@ -0,0 +1,94 @@
+import reflect.api.Modifier
+import reflect.macro.Context
+
+abstract class RangeDefault {
+ val from, to: Int
+ def foreach(f: Int => Unit) = {
+ var i = from
+ while (i < to) { f(i); i += 1 }
+ }
+}
+
+/** This class should go into reflect.macro once it is a bit more stable. */
+abstract class Utils {
+ val context: Context
+ import context._
+
+ class TreeSubstituter(from: List[Symbol], to: List[Tree]) extends Transformer {
+ override def transform(tree: Tree): Tree = tree match {
+ case Ident(_) =>
+ def subst(from: List[Symbol], to: List[Tree]): Tree =
+ if (from.isEmpty) tree
+ else if (tree.symbol == from.head) to.head.duplicate // TODO: does it ever make sense *not* to perform a shallowDuplicate on `to.head`?
+ else subst(from.tail, to.tail);
+ subst(from, to)
+ case _ =>
+ val tree1 = super.transform(tree)
+ if (tree1 ne tree) tree1.tpe = null
+ tree1
+ }
+ }
+ def makeApply(fn: Tree, args: List[Tree]): Tree = fn match {
+ case Function(vparams, body) =>
+ new TreeSubstituter(vparams map (_.symbol), args) transform body
+ case Block(stats, expr) =>
+ Block(stats, makeApply(expr, args))
+ case _ =>
+ println("no beta on "+fn+" "+fn.getClass)
+ Apply(fn, args)
+ }
+ def makeWhile(lname: TermName, cond: Tree, body: Tree): Tree = {
+ val continu = Apply(Ident(lname), Nil)
+ val rhs = If(cond, Block(List(body), continu), Literal(Constant()))
+ LabelDef(lname, Nil, rhs)
+ }
+ def makeBinop(left: Tree, op: String, right: Tree): Tree =
+ Apply(Select(left, newTermName(op)), List(right))
+}
+
+class Range(val from: Int, val to: Int) extends RangeDefault {
+ override def macro foreach(f: Int => Unit): Unit = {
+ println("macro-expand, _this = "+ _this)
+ import _context._
+ object utils extends Utils {
+ val context: _context.type = _context
+ }
+ import utils._
+
+ val initName = newTermName("<init>")
+ // Either:
+ // scala"{ var i = $low; val h = $hi; while (i < h) { $f(i); i = i + 1 } }
+ // or:
+ // scala"($_this: RangeDefault).foreach($f)"
+ _this match {
+ case Apply(Select(New(tpt), initName), List(lo, hi)) if tpt.symbol.fullName == "Range" =>
+ val iname = newTermName("$i")
+ val hname = newTermName("$h")
+ def iref = Ident(iname)
+ def href = Ident(hname)
+ val labelname = newTermName("$while")
+ val cond = makeBinop(iref, "$less", href)
+ val body = Block(
+ List(makeApply(f, List(iref))),
+ Assign(iref, makeBinop(iref, "$plus", Literal(Constant(1)))))
+ tools.nsc.util.trace("generated: ")(
+ Block(
+ List(
+ ValDef(Modifiers(Set(Modifier.mutable)), iname, TypeTree(), lo),
+ ValDef(Modifiers(), hname, TypeTree(), hi)),
+ makeWhile(labelname, cond, body)))
+ case _ =>
+ Apply(
+ Select(
+ Typed(_this, Ident(newTypeName("RangeDefault"))),
+ newTermName("foreach")),
+ List(f))
+ }
+ }
+}
+
+object Test extends App {
+
+ new Range(1, 10) foreach println
+
+}
diff --git a/test/files/run/macro-range/macro_range_2.scala b/test/files/run/macro-range/macro_range_2.scala
new file mode 100644
index 0000000000..15a018fcff
--- /dev/null
+++ b/test/files/run/macro-range/macro_range_2.scala
@@ -0,0 +1,94 @@
+import reflect.api.Modifier
+import reflect.macro.Context
+
+abstract class RangeDefault {
+ val from, to: Int
+ def foreach(f: Int => Unit) = {
+ var i = from
+ while (i < to) { f(i); i += 1 }
+ }
+}
+
+/** This class should go into reflect.macro once it is a bit more stable. */
+abstract class Utils {
+ val context: Context
+ import context._
+
+ class TreeSubstituter(from: List[Symbol], to: List[Tree]) extends Transformer {
+ override def transform(tree: Tree): Tree = tree match {
+ case Ident(_) =>
+ def subst(from: List[Symbol], to: List[Tree]): Tree =
+ if (from.isEmpty) tree
+ else if (tree.symbol == from.head) to.head.duplicate // TODO: does it ever make sense *not* to perform a shallowDuplicate on `to.head`?
+ else subst(from.tail, to.tail);
+ subst(from, to)
+ case _ =>
+ val tree1 = super.transform(tree)
+ if (tree1 ne tree) tree1.tpe = null
+ tree1
+ }
+ }
+ def makeApply(fn: Tree, args: List[Tree]): Tree = fn match {
+ case Function(vparams, body) =>
+ new TreeSubstituter(vparams map (_.symbol), args) transform body
+ case Block(stats, expr) =>
+ Block(stats, makeApply(expr, args))
+ case _ =>
+ println("no beta on "+fn+" "+fn.getClass)
+ Apply(fn, args)
+ }
+ def makeWhile(lname: TermName, cond: Tree, body: Tree): Tree = {
+ val continu = Apply(Ident(lname), Nil)
+ val rhs = If(cond, Block(List(body), continu), Literal(Constant()))
+ LabelDef(lname, Nil, rhs)
+ }
+ def makeBinop(left: Tree, op: String, right: Tree): Tree =
+ Apply(Select(left, newTermName(op)), List(right))
+}
+
+class Range(val from: Int, val to: Int) extends RangeDefault {
+ override def macro foreach(f: Int => Unit): Unit = {
+ println("macro-expand, _this = "+ _this)
+ import _context._
+ object utils extends Utils {
+ val context: _context.type = _context
+ }
+ import utils._
+
+ val initName = newTermName("<init>")
+ // Either:
+ // scala"{ var i = $low; val h = $hi; while (i < h) { $f(i); i = i + 1 } }
+ // or:
+ // scala"($_this: RangeDefault).foreach($f)"
+ _this match {
+ case Apply(Select(New(tpt), initName), List(lo, hi)) if tpt.symbol.fullName == "Range" =>
+ val iname = newTermName("$i")
+ val hname = newTermName("$h")
+ def iref = Ident(iname)
+ def href = Ident(hname)
+ val labelname = newTermName("$while")
+ val cond = makeBinop(iref, "$less", href)
+ val body = Block(
+ List(makeApply(f, List(iref))),
+ Assign(iref, makeBinop(iref, "$plus", Literal(Constant(1)))))
+ tools.nsc.util.trace("generated: ")(
+ Block(
+ List(
+ ValDef(Modifiers(Set(Modifier.mutable)), iname, TypeTree(), lo),
+ ValDef(Modifiers(), hname, TypeTree(), hi)),
+ makeWhile(labelname, cond, body)))
+ case _ =>
+ Apply(
+ Select(
+ Typed(_this, Ident(newTypeName("RangeDefault"))),
+ newTermName("foreach")),
+ List(f))
+ }
+ }
+}
+
+object Test extends App {
+
+ new Range(1, 10) foreach println
+
+}