Skip to content

Add ValueOfExpr type class to get an Option[T] from an Expr[T] #6808

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions library/src/scala/quoted/Expr.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ package quoted {
/** Show a source code like representation of this expression */
def show(syntaxHighlight: SyntaxHighlight)(implicit qctx: QuoteContext): String = qctx.show(this, syntaxHighlight)

/** Return the value of this expression.
*
* Returns `None` if the expression does not contain a value or contains side effects.
* Otherwise returns the `Some` of the value.
*/
final def getValue[U >: T] given (qctx: QuoteContext, valueOf: ValueOfExpr[U]): Option[U] = valueOf(this)

}

object Expr {
Expand Down
33 changes: 33 additions & 0 deletions library/src/scala/quoted/ValueOfExpr.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package scala.quoted

/** A typeclass for types that can be turned from a `quoted.Expr[T]` to a `T` */
trait ValueOfExpr[T] {

/** Return the value of the expression.
*
* Returns `None` if the expression does not contain a value or contains side effects.
* Otherwise returns the `Some` of the value.
*/
def apply(x: Expr[T]) given QuoteContext: Option[T]

}

object ValueOfExpr {

implicit val ValueOfExpr_Unit_delegate: ValueOfExpr[Unit] = new PrimitiveValueOfExpr
implicit val ValueOfExpr_Boolean_delegate: ValueOfExpr[Boolean] = new PrimitiveValueOfExpr
implicit val ValueOfExpr_Byte_delegate: ValueOfExpr[Byte] = new PrimitiveValueOfExpr
implicit val ValueOfExpr_Short_delegate: ValueOfExpr[Short] = new PrimitiveValueOfExpr
implicit val ValueOfExpr_Int_delegate: ValueOfExpr[Int] = new PrimitiveValueOfExpr
implicit val ValueOfExpr_Long_delegate: ValueOfExpr[Long] = new PrimitiveValueOfExpr
implicit val ValueOfExpr_Float_delegate: ValueOfExpr[Float] = new PrimitiveValueOfExpr
implicit val ValueOfExpr_Double_delegate: ValueOfExpr[Double] = new PrimitiveValueOfExpr
implicit val ValueOfExpr_Char_delegate: ValueOfExpr[Char] = new PrimitiveValueOfExpr
implicit val ValueOfExpr_String_delegate: ValueOfExpr[String] = new PrimitiveValueOfExpr

private class PrimitiveValueOfExpr[T <: Unit | Null | Int | Boolean | Byte | Short | Int | Long | Float | Double | Char | String] extends ValueOfExpr[T] {
/** Lift a quoted primitive value `'{ n }` into `n` */
def apply(x: Expr[T]) given QuoteContext: Option[T] = matching.Const.unapply(x)
}

}
1 change: 1 addition & 0 deletions library/src/scala/quoted/matching/Const.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ object Const {
def rec(tree: Term): Option[T] = tree match {
case Literal(c) => Some(c.value.asInstanceOf[T])
case Block(Nil, e) => rec(e)
case Typed(e, _) => rec(e)
case Inlined(_, Nil, e) => rec(e)
case _ => None
}
Expand Down
10 changes: 10 additions & 0 deletions tests/run-with-compiler/quote-valueof-list.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Some(0)
Some(1)
None
Some(true)
Some(false)
None
Some(List())
Some(List(a, b, c))
Some(None)
Some(Some(abc))
59 changes: 59 additions & 0 deletions tests/run-with-compiler/quote-valueof-list.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import scala.quoted._

object Test {

implicit val toolbox: scala.quoted.Toolbox = scala.quoted.Toolbox.make(getClass.getClassLoader)

def main(args: Array[String]): Unit = withQuoteContext {

implicit def ValueOfExprInt: ValueOfExpr[Int] = new {
def apply(n: Expr[Int]) given QuoteContext: Option[Int] = n match {
case '{ 0 } => Some(0)
case '{ 1 } => Some(1)
case '{ 2 } => Some(1)
case _ => None
}
}

implicit def ValueOfExprBoolean: ValueOfExpr[Boolean] = new ValueOfExpr[Boolean] {
def apply(b: Expr[Boolean]) given QuoteContext: Option[Boolean] = b match {
case '{ true } => Some(true)
case '{ false } => Some(false)
case _ => None
}
}

implicit def ValueOfExprList[T: ValueOfExpr: Type]: ValueOfExpr[List[T]] = new {
def apply(xs: Expr[List[T]]) given QuoteContext: Option[List[T]] = (xs: Expr[Any]) match {
case '{ ($xs1: List[T]).::($x) } =>
for { head <- x.getValue; tail <- xs1.getValue }
yield head :: tail
case '{ Nil } => Some(Nil)
case _ => None
}
}

implicit def ValueOfExprOption[T: ValueOfExpr: Type]: ValueOfExpr[Option[T]] = new {
def apply(expr: Expr[Option[T]]) given QuoteContext: Option[Option[T]] = expr match {
case '{ Some[T]($x) } => for (v <- x.getValue) yield Some(v)
case '{ None } => Some(None)
case _ => None
}
}

println(('{0}).getValue)
println(('{1}).getValue)
println(('{ println(); 1 }).getValue)

println(('{true}).getValue)
println(('{false}).getValue)
println(('{ println(); false }).getValue)

println(('{ Nil }: Expr[List[String]]).getValue)
println(('{ "a" :: "b" :: "c" :: Nil }: Expr[List[String]]).getValue)

println(('{ None }: Expr[Option[Int]]).getValue)
println(('{ Some("abc") }: Expr[Option[String]]).getValue)

}
}
13 changes: 13 additions & 0 deletions tests/run-with-compiler/quote-valueof.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Some(())
Some(null)
Some(true)
Some(1)
Some(2)
Some(3)
Some(4)
Some(5)
Some(true)
Some(3.56)
Some(34.5)
Some(a)
Some(abc)
22 changes: 22 additions & 0 deletions tests/run-with-compiler/quote-valueof.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import scala.quoted._

object Test {

implicit val toolbox: scala.quoted.Toolbox = scala.quoted.Toolbox.make(getClass.getClassLoader)

def main(args: Array[String]): Unit = withQuoteContext {
println(('{}).getValue)
println(('{null}).getValue)
println(('{true}).getValue)
println(('{1}).getValue)
println(('{2: Byte}).getValue)
println(('{3: Short}).getValue)
println(('{4}).getValue)
println(('{5L}).getValue)
println(('{true}).getValue)
println(('{3.56f}).getValue)
println(('{34.5d}).getValue)
println(('{ 'a' }).getValue)
println(('{"abc"}).getValue)
}
}