diff --git a/library/src/scala/quoted/Expr.scala b/library/src/scala/quoted/Expr.scala index cc6d116f2dfb..bf4794357231 100644 --- a/library/src/scala/quoted/Expr.scala +++ b/library/src/scala/quoted/Expr.scala @@ -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 { diff --git a/library/src/scala/quoted/ValueOfExpr.scala b/library/src/scala/quoted/ValueOfExpr.scala new file mode 100644 index 000000000000..32d27feb5246 --- /dev/null +++ b/library/src/scala/quoted/ValueOfExpr.scala @@ -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) + } + +} diff --git a/library/src/scala/quoted/matching/Const.scala b/library/src/scala/quoted/matching/Const.scala index 1680ce396d0f..7ac829eee4cd 100644 --- a/library/src/scala/quoted/matching/Const.scala +++ b/library/src/scala/quoted/matching/Const.scala @@ -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 } diff --git a/tests/run-with-compiler/quote-valueof-list.check b/tests/run-with-compiler/quote-valueof-list.check new file mode 100644 index 000000000000..0835d959985d --- /dev/null +++ b/tests/run-with-compiler/quote-valueof-list.check @@ -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)) diff --git a/tests/run-with-compiler/quote-valueof-list.scala b/tests/run-with-compiler/quote-valueof-list.scala new file mode 100644 index 000000000000..956c4054523f --- /dev/null +++ b/tests/run-with-compiler/quote-valueof-list.scala @@ -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) + + } +} diff --git a/tests/run-with-compiler/quote-valueof.check b/tests/run-with-compiler/quote-valueof.check new file mode 100644 index 000000000000..5c264c48b40a --- /dev/null +++ b/tests/run-with-compiler/quote-valueof.check @@ -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) diff --git a/tests/run-with-compiler/quote-valueof.scala b/tests/run-with-compiler/quote-valueof.scala new file mode 100644 index 000000000000..a1e8a0fda07c --- /dev/null +++ b/tests/run-with-compiler/quote-valueof.scala @@ -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) + } +}