Search code examples
scalaread-eval-print-loopscala-macrosscala-3

Expand Code to Raw AST Scala 3 in the REPL


I am currently doing a lot of compiler research. This not only entails to writing compiler plugins but also modifying the dotty compiler, from the parser to the typer. Therefore I need to constantly look at the raw ASTs to sketch the necessary transformations.

In Scala 2, the reflection library provided the following functionality:

   val expression = ....
   val tree = reify{expression}.tree
   showRaw(tree)

Now from what I understand from the docs, the final step has been replaced by Printer.TreeStructure.show(tree)

However, I can not find anything in the meta programming docs for an alternative to reify. Now I can obviously use various meta programming techniques inside a Scala program and print the tree to stdout but this is a very time consuming process compared to expanding in the REPL for a quick manual verification.

Is there a way to do this in the Scala 3 REPL?


Solution

  • You can define your own reify in your project

    import scala.quoted.*
    
    object App {
      inline def reify(inline a: Any) = ${reifyImpl('a)}
    
      def reifyImpl(a: Expr[Any])(using Quotes): Expr[String] = {
        import quotes.reflect.*
        Literal(StringConstant(Printer.TreeStructure.show(a.asTerm))).asExprOf[String]
      }
    }
    

    and use it in REPL

    sbt console
    
    scala> import App.reify
    
    scala> reify{ class A }                                                                                                                                                             
    val res0: String = Inlined(None, Nil, Block(List(ClassDef("A", DefDef("<init>", List(TermParamClause(Nil)), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil)), None, Nil)), Literal(UnitConstant())))
    

    Unfortunately defining reify directly in REPL doesn't seem to work

    scala> import scala.quoted.*; inline def reify(inline a: Any) = ${reifyImpl('a)}; def reifyImpl(a: Expr[Any])(using Quotes): Expr[String] = {import quotes.reflect.*; Literal(StringConstant(Printer.TreeStructure.show(a.asTerm))).asExprOf[String]}
    def reify(a: Any): String
    def reifyImpl
      (a: quoted.Expr[Any])(using x$2: quoted.Quotes): quoted.Expr[String]
    
    scala> reify{class A}
    -- Error:
    1 |reify{class A}
      |^^^^^^^^^^^^^^
      |Failed to evaluate macro.
      |  Caused by class java.lang.ClassNotFoundException: rs$line$6
      |    java.lang.ClassLoader.loadClass(ClassLoader.java:418)
      |    java.lang.ClassLoader.loadClass(ClassLoader.java:351)
      |    dotty.tools.repl.AbstractFileClassLoader.loadClass(AbstractFileClassLoader.scala:55)
      |    dotty.tools.dotc.transform.Splicer$Interpreter.loadReplLineClass(Splicer.scala:402)
      |    dotty.tools.dotc.transform.Splicer$Interpreter.interpretedStaticMethodCall(Splicer.scala:354)
      |    dotty.tools.dotc.transform.Splicer$Interpreter.interpretTree(Splicer.scala:260)
      |    dotty.tools.dotc.transform.Splicer$Interpreter.interpretTree$$anonfun$2(Splicer.scala:281)
      |             
      | This location contains code that was inlined from rs$line$7:1
    

    In 3.2.0 reify can be defined directly in REPL (scala-cli)

    $ scala-cli
    
    Welcome to Scala 3.2.0 (1.8.0_231, Java Java HotSpot(TM) 64-Bit GraalVM EE 19.3.0).
    Type in expressions for evaluation. Or try :help.
                                                                                    
    scala> import scala.quoted.*; inline def reify(inline a: Any) = ${reifyImpl('a)}; def reifyImpl(a: Expr[Any])(using Quotes): Expr[String] = {import quotes.reflect.*; Literal(StringConstant(Printer.TreeStructure.show(a.asTerm))).asExprOf[String]}
    def reify(a: Any): String
    def reifyImpl
      (a: quoted.Expr[Any])(using x$2: quoted.Quotes): quoted.Expr[String]
                                                                                    
    scala> reify{class A}
    val res0: String = Inlined(None, Nil, Block(List(ClassDef("A", DefDef("<init>", List(TermParamClause(Nil)), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil)), None, Nil)), Literal(UnitConstant())))