Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using LabelDef in scala macros (2.10)

I'm experimenting with the scala 2.10 macro features. I have trouble using LabelDef in some cases, though. To some extent I peeked in the compiler's code, read excerpts of Miguel Garcia's papers but I'm still stuck.

If my understanding is correct, a pseudo-definition would be:
LabelDef(labelName, listOfParameters, stmsAndApply) where the 3 arguments are Trees and:
- labelNameis the identifier of the label $L being defined
- listOfParameters correspond to the arguments passed when label-apply occurs, as in $L(a1,...,an), and can be empty
- stmsAndApplycorresponds to the block of statements (possibly none) and final apply-expression
label-apply meaning more-or-less a GOTO to a label

For instance, in the case of a simple loop, a LabelDef can eventually apply itself:
LabelDef($L, (), {...; $L()})

Now, if I want to define 2 LabelDef that jump to each other:

...
LabelDef($L1, (), $L2())
...
LabelDef($L2, (), $L1())
...

The 2nd LabelDef is fine, but the compiler outputs an error on the 1st, "not found: value $L2". I guess that is because $L2 isn't yet defined while there is an attempt to apply it. This is a tree being constructed so that would make sense to me. Is my understanding correct so far? Because if no error is expected, that means my macro implementation is probably buggy.

Anyway, I believe there must be a way to apply $L2 (i.e. Jumping to $L2) from $L1, somehow, but I just have no clue how to do it. Does someone have an example of doing that, or any pointer?


Other unclear points (but less of a concern right now) about using LabelDef in macros are:
-what the 2nd argument is, concretely, how is it used when non-empty? In other words, what are the mechanisms of a label-apply with parameters?
-is it valid to put in the 3rd argument's final expression anything else than a label-apply? (not that I can't try, but macros are still experimental)
-is it possible to perform a forwarding label-apply outside a LabelDef? (maybe this is a redundant question)

Any macro implementation example in the answer is, of course, very welcome!
Cheers,

like image 283
eruve Avatar asked Nov 14 '22 01:11

eruve


1 Answers

Because if no error is expected, that means my macro implementation is probably buggy.
Yes, it seems that was a bug (^^; Although I'm not sure whether or not the limitation with the Block/LabelDef combination exists on purpose.

def EVIL_LABELS_MACRO = macro EVIL_LABELS_MACRO_impl
def EVIL_LABELS_MACRO_impl(c:Context):c.Expr[Unit] = { // fails to expand
  import c.universe._
  val lt1 = newTermName("$L1"); val lt2 = newTermName("$L2")
  val ld1 = LabelDef(lt1, Nil, Block(c.reify{println("$L1")}.tree, Apply(Ident(lt2), Nil)))
  val ld2 = LabelDef(lt2, Nil, Block(c.reify{println("$L2")}.tree, Apply(Ident(lt1), Nil)))
  c.Expr( Block(ld1, c.reify{println("ignored")}.tree, ld2) )
}

def FINE_LABELS_MACRO = macro FINE_LABELS_MACRO_impl
def FINE_LABELS_MACRO_impl(c:Context):c.Expr[Unit] = { // The End isn't near
  import c.universe._
  val lt1 = newTermName("$L1"); val lt2 = newTermName("$L2")
  val ld1 = LabelDef(lt1, Nil, Block(c.reify{println("$L1")}.tree, Apply(Ident(lt2), Nil)))
  val ld2 = LabelDef(lt2, Nil, Block(c.reify{println("$L2")}.tree, Apply(Ident(lt1), Nil)))
  c.Expr( Block(ld1, c.reify{println("ignored")}.tree, ld2, c.reify{println("The End")}.tree) )
}

I think a Block is parsed into { statements; expression } thus the last argument is the expression. If a LabelDef "falls in" expression, e.g. the EVIL_LABELS_MACRO pattern, its expansion won't be visible in statements; hence error "not found: value $L2".

So it's better to make sure all LabelDef "fall in" statements. FINE_LABELS_MACRO does that and expands to:

{
  $L1(){
    scala.this.Predef.println("$L1");
    $L2()
  };
  scala.this.Predef.println("ignored");
  $L2(){
    scala.this.Predef.println("$L2");
    $L1()
  };
  scala.this.Predef.println("The End")
}
like image 77
eruve Avatar answered Dec 21 '22 10:12

eruve