es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Cómo obtener el cuerpo de la inicialización de variable del ámbito externo en macros de Scala 3?

Suponga que tengo este código para extraer el código que inicializa una variable:

“`def extractBodyImpl[T: Type](expr: Expr[T])(using Quotes) =
import quotes.reflect._
expr.asTerm.underlyingArgument match
case ident @ Ident() =>
ident.symbol.tree match
case ValDef(
,,rhs) => println(rhs)
case DefDef(
,,,rhs) => println(rhs)
‘{ () }

inline def extractBody[T](inline expr: T) = ${ extractBodyImpl(‘expr) }


Cuando se llama en una variable declarada en el mismo ámbito, funciona como se desea:

@main def hello() =
val x = 1
extractBody(x)


imprime `Some(Literal(Constant(1)))`. Sin embargo, en una variable desde el ámbito externo, imprime `None`:

val x = 1
@main def hello() =
extractBody(x)
“`

¿Cómo puedo hacer que funcione en el segundo caso?

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    Para hacer que la función extractBody funcione en una variable fuera de su alcance, es necesario utilizar quotes.reflect, que es una biblioteca de reflexión en tiempo de ejecución para Scala 3.

    Aquí está el código modificado que utiliza quotes.reflect para extraer el código de inicialización de una variable desde el alcance externo:

    import scala.quoted.*
    
    inline def extractBody[T](inline expr: T): Option[Any] =
      ${ extractBodyImpl('expr) }
    
    def extractBodyImpl[T](expr: Expr[T])(using Quotes): Expr[Option[Any]] =
      import quotes.reflect.*
    
      // Obtener el valor de la variable si está definida en el mismo alcance
      def getValue(ident: Ident): Option[Any] =
        ident.symbol.tree match
          case ValDef(_, _, Some(rhs)) => Some(rhs.asExpr.asInstanceOf[Expr[Any]].value)
          case _ => None
    
      // Traversar la estructura para encontrar la variable si está definida en un alcance externo
      def findValue(tree: Tree): Option[Any] =
        tree match
          case Block(stats, expr) =>
            stats.collectFirst {
              case ValDef(_, _, Some(rhs)) if rhs.matches(expr) => rhs.asExpr.asInstanceOf[Expr[Any]].value
              case DefDef(_, _, _, Some(rhs)) if rhs.matches(expr) => rhs.asExpr.asInstanceOf[Expr[Any]].value
            }
          case _ => None
    
      // Obtener la estructura de la variable
      val tree = expr.asTerm.underlyingArgument
      tree match
        case ident: Ident =>
          getValue(ident).map(value => Expr(Some(value)))
            .getOrElse(findValue(ident.symbol.tree).map(value => Expr(Some(value)))
              .getOrElse(Expr(None)))
        case _ => Expr(None)
    

    La función extractBodyImpl ahora tiene dos funciones auxiliares: getValue y findValue. getValue se utiliza para extraer el código de inicialización de una variable desde el mismo alcance. Toma un árbol Ident y comprueba si es una definición de variable con un valor de inicialización. Si lo es, devuelve el valor de la inicialización. Si no lo es, devuelve None.

    findValue se utiliza para extraer el código de inicialización de una variable desde un alcance externo. Toma un Tree y lo atraviesa para encontrar la definición de variable que contiene el código de inicialización. Lo hace llamando recursivamente a sí mismo en el árbol padre hasta que encuentre un bloque que contenga la definición de variable. Si encuentra una definición coincidente, devuelve su valor de inicialización. Si no encuentra una definición coincidente, devuelve None.

    El código modificado debería funcionar para variables definidas tanto en el mismo como en un alcance externo.

Comments are closed.