Scala eval function that gets a String with variables

143 views Asked by At

I want to be able to evaluate the expression that returns from func. The problem is that the expression includes the variable a, which is not familiar in the scope of func but is familiar in the scope of playground.

I want to be able to send the String: s"$a + 1" when $ is not an operator and s is a part of the String. I saw that $$ should solve the problem with the $, but then the char s is not a part of the String, and the eval function needs an expression with the pattern of s"".

object playground extends App{
  val a = 5.5
  val expression = func()
  val str: String = expression
  val tb = currentMirror.mkToolBox()
  val x = tb.eval(tb.parse(str)).toString
  print(x)
}

object second {
  def func(): String = {
    s"$a + 1"
  }
}

Thanks for any help :)

1

There are 1 answers

0
Dmytro Mitin On

expression includes the variable a, which is not familiar in the scope of func but is familiar in the scope of playground.

Try to fully qualify the variable

def func(): String =
  s"${playground.a} + 1"

or use import

def func(): String =
  s"import playground._; $a + 1"
def func(): String =
  s"""
    import playground._
    $a + 1
  """

or add a parameter to the function

def func(a: Double): String =
  s"$a + 1"
val a = 5.5
val expression = func(a)

s"$a + 1" when $ is not an operator and s is a part of the String. I saw that $$ should solve the problem with the $, but then the char s is not a part of the String

I'm not sure what you're after. Try to provide some inputs and desired outputs to make it clear.

"$a + 1" is just a String (with a dollar sign), s"$a + 1" is a String where a variable a from current scope is substituted (after .toString).

Yes, you can escape $ inside s"..." as $$. So s"$$a + 1" is the same as just "$a + 1" (without prefix s).

I don't understand what "s is a part of the String", "but then the char s is not a part of the String" mean.

and the eval function needs an expression with the pattern of s"".

Why? tb.parse (or q"...") accepts arbitrary String. tb.eval accepts arbitrary Tree.


I guess I got it. It seems you are expecting func() to expand into s"$a + 1" at a call site. Then s"$a + 1" should be not a String but a Tree itself (s"$a + 1" should be not a String s""" s"$$a + 1" """ but a piece of code) and func() should be a macro.

import scala.language.experimental.macros
import scala.reflect.macros.blackbox

object second {
  def func(): String = macro funcImpl

  def funcImpl(c: blackbox.Context)(): c.Tree = {
    import c.universe._
    q""" s"$$a + 1" """
  }
}
// in a different subproject

import second.func
import scala.reflect.runtime.currentMirror
import scala.tools.reflect.ToolBox

object playground extends App {
  val a = 5.5
  val expression = func()
  val str: String = expression
  val tb = currentMirror.mkToolBox()
  val x = tb.eval(tb.parse(str)).toString
  print(x) // 6.5
}

But using macros (and mixing them with runtime compilation via Toolbox) in your case seems an overkill, you should prefer easier options from the above.