summaryrefslogtreecommitdiff
path: root/test/files/run/macro-range/macro_range_2.scala
blob: fdfe7169adb5994ece7de4be39cd4f7587d4f289 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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 _ =>
      // todo. read the compiler config and print if -Ydebug is set
      //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 = {
    // todo. read the compiler config and print if -Ydebug is set
    //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)))))
        val generated =
        Block(
          List(
            ValDef(Modifiers(Set(Modifier.mutable)), iname, TypeTree(), lo),
            ValDef(Modifiers(), hname, TypeTree(), hi)),
          makeWhile(labelname, cond, body))
        // todo. read the compiler config and print if -Ydebug is set
        //tools.nsc.util.trace("generated: ")(generated)
        generated
      case _ =>
        Apply(
          Select(
            Typed(_this, Ident(newTypeName("RangeDefault"))),
            newTermName("foreach")),
          List(f))
    }
  }
}

object Test extends App {

  new Range(1, 10) foreach println

}