Skip to content

Commit 0c1f78d

Browse files
Merge pull request #6808 from dotty-staging/add-ValueOfExpr
Add ValueOfExpr type class to get an Option[T] from an Expr[T]
2 parents eb12c21 + 7d07ac2 commit 0c1f78d

File tree

7 files changed

+145
-0
lines changed

7 files changed

+145
-0
lines changed

library/src/scala/quoted/Expr.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ package quoted {
1919
/** Show a source code like representation of this expression */
2020
def show(syntaxHighlight: SyntaxHighlight)(implicit qctx: QuoteContext): String = qctx.show(this, syntaxHighlight)
2121

22+
/** Return the value of this expression.
23+
*
24+
* Returns `None` if the expression does not contain a value or contains side effects.
25+
* Otherwise returns the `Some` of the value.
26+
*/
27+
final def getValue[U >: T] given (qctx: QuoteContext, valueOf: ValueOfExpr[U]): Option[U] = valueOf(this)
28+
2229
}
2330

2431
object Expr {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package scala.quoted
2+
3+
/** A typeclass for types that can be turned from a `quoted.Expr[T]` to a `T` */
4+
trait ValueOfExpr[T] {
5+
6+
/** Return the value of the expression.
7+
*
8+
* Returns `None` if the expression does not contain a value or contains side effects.
9+
* Otherwise returns the `Some` of the value.
10+
*/
11+
def apply(x: Expr[T]) given QuoteContext: Option[T]
12+
13+
}
14+
15+
object ValueOfExpr {
16+
17+
implicit val ValueOfExpr_Unit_delegate: ValueOfExpr[Unit] = new PrimitiveValueOfExpr
18+
implicit val ValueOfExpr_Boolean_delegate: ValueOfExpr[Boolean] = new PrimitiveValueOfExpr
19+
implicit val ValueOfExpr_Byte_delegate: ValueOfExpr[Byte] = new PrimitiveValueOfExpr
20+
implicit val ValueOfExpr_Short_delegate: ValueOfExpr[Short] = new PrimitiveValueOfExpr
21+
implicit val ValueOfExpr_Int_delegate: ValueOfExpr[Int] = new PrimitiveValueOfExpr
22+
implicit val ValueOfExpr_Long_delegate: ValueOfExpr[Long] = new PrimitiveValueOfExpr
23+
implicit val ValueOfExpr_Float_delegate: ValueOfExpr[Float] = new PrimitiveValueOfExpr
24+
implicit val ValueOfExpr_Double_delegate: ValueOfExpr[Double] = new PrimitiveValueOfExpr
25+
implicit val ValueOfExpr_Char_delegate: ValueOfExpr[Char] = new PrimitiveValueOfExpr
26+
implicit val ValueOfExpr_String_delegate: ValueOfExpr[String] = new PrimitiveValueOfExpr
27+
28+
private class PrimitiveValueOfExpr[T <: Unit | Null | Int | Boolean | Byte | Short | Int | Long | Float | Double | Char | String] extends ValueOfExpr[T] {
29+
/** Lift a quoted primitive value `'{ n }` into `n` */
30+
def apply(x: Expr[T]) given QuoteContext: Option[T] = matching.Const.unapply(x)
31+
}
32+
33+
}

library/src/scala/quoted/matching/Const.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ object Const {
1919
def rec(tree: Term): Option[T] = tree match {
2020
case Literal(c) => Some(c.value.asInstanceOf[T])
2121
case Block(Nil, e) => rec(e)
22+
case Typed(e, _) => rec(e)
2223
case Inlined(_, Nil, e) => rec(e)
2324
case _ => None
2425
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Some(0)
2+
Some(1)
3+
None
4+
Some(true)
5+
Some(false)
6+
None
7+
Some(List())
8+
Some(List(a, b, c))
9+
Some(None)
10+
Some(Some(abc))
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import scala.quoted._
2+
3+
object Test {
4+
5+
implicit val toolbox: scala.quoted.Toolbox = scala.quoted.Toolbox.make(getClass.getClassLoader)
6+
7+
def main(args: Array[String]): Unit = withQuoteContext {
8+
9+
implicit def ValueOfExprInt: ValueOfExpr[Int] = new {
10+
def apply(n: Expr[Int]) given QuoteContext: Option[Int] = n match {
11+
case '{ 0 } => Some(0)
12+
case '{ 1 } => Some(1)
13+
case '{ 2 } => Some(1)
14+
case _ => None
15+
}
16+
}
17+
18+
implicit def ValueOfExprBoolean: ValueOfExpr[Boolean] = new ValueOfExpr[Boolean] {
19+
def apply(b: Expr[Boolean]) given QuoteContext: Option[Boolean] = b match {
20+
case '{ true } => Some(true)
21+
case '{ false } => Some(false)
22+
case _ => None
23+
}
24+
}
25+
26+
implicit def ValueOfExprList[T: ValueOfExpr: Type]: ValueOfExpr[List[T]] = new {
27+
def apply(xs: Expr[List[T]]) given QuoteContext: Option[List[T]] = (xs: Expr[Any]) match {
28+
case '{ ($xs1: List[T]).::($x) } =>
29+
for { head <- x.getValue; tail <- xs1.getValue }
30+
yield head :: tail
31+
case '{ Nil } => Some(Nil)
32+
case _ => None
33+
}
34+
}
35+
36+
implicit def ValueOfExprOption[T: ValueOfExpr: Type]: ValueOfExpr[Option[T]] = new {
37+
def apply(expr: Expr[Option[T]]) given QuoteContext: Option[Option[T]] = expr match {
38+
case '{ Some[T]($x) } => for (v <- x.getValue) yield Some(v)
39+
case '{ None } => Some(None)
40+
case _ => None
41+
}
42+
}
43+
44+
println(('{0}).getValue)
45+
println(('{1}).getValue)
46+
println(('{ println(); 1 }).getValue)
47+
48+
println(('{true}).getValue)
49+
println(('{false}).getValue)
50+
println(('{ println(); false }).getValue)
51+
52+
println(('{ Nil }: Expr[List[String]]).getValue)
53+
println(('{ "a" :: "b" :: "c" :: Nil }: Expr[List[String]]).getValue)
54+
55+
println(('{ None }: Expr[Option[Int]]).getValue)
56+
println(('{ Some("abc") }: Expr[Option[String]]).getValue)
57+
58+
}
59+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Some(())
2+
Some(null)
3+
Some(true)
4+
Some(1)
5+
Some(2)
6+
Some(3)
7+
Some(4)
8+
Some(5)
9+
Some(true)
10+
Some(3.56)
11+
Some(34.5)
12+
Some(a)
13+
Some(abc)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import scala.quoted._
2+
3+
object Test {
4+
5+
implicit val toolbox: scala.quoted.Toolbox = scala.quoted.Toolbox.make(getClass.getClassLoader)
6+
7+
def main(args: Array[String]): Unit = withQuoteContext {
8+
println(('{}).getValue)
9+
println(('{null}).getValue)
10+
println(('{true}).getValue)
11+
println(('{1}).getValue)
12+
println(('{2: Byte}).getValue)
13+
println(('{3: Short}).getValue)
14+
println(('{4}).getValue)
15+
println(('{5L}).getValue)
16+
println(('{true}).getValue)
17+
println(('{3.56f}).getValue)
18+
println(('{34.5d}).getValue)
19+
println(('{ 'a' }).getValue)
20+
println(('{"abc"}).getValue)
21+
}
22+
}

0 commit comments

Comments
 (0)