Skip to content

Instantly share code, notes, and snippets.

@tarao
Created December 5, 2023 09:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tarao/e049673e1ed6acdcf2b351a0cbf9b40a to your computer and use it in GitHub Desktop.
Save tarao/e049673e1ed6acdcf2b351a0cbf9b40a to your computer and use it in GitHub Desktop.
object MacroExample1 {
import scala.annotation.tailrec
import scala.language.dynamics
import scala.quoted.*
class NamedArray[T, Names <: Tuple](array: IndexedSeq[T]) extends Dynamic {
def names[NN <: Tuple]: NamedArray[T, NN] =
this.asInstanceOf[NamedArray[T, NN]]
inline def selectDynamic[S <: String](name: S): T =
${ NamedArray.selectImpl[T, Names, S]('array, 'name) }
}
object NamedArray {
def apply[T](args: T*): NamedArray[T, EmptyTuple] =
new NamedArray(Vector(args: _*))
def selectImpl[T: Type, Names <: Tuple: Type, S <: String](
array: Expr[IndexedSeq[T]],
name: Expr[S],
)(using Quotes): Expr[T] = {
import quotes.reflect.*
val constName: String = name.valueOrAbort
val index = ConstantType(StringConstant(constName)).asType match {
case '[name] =>
@tailrec def findIndexOrAbort(tpe: Type[?], index: Int = 0): Int =
tpe match {
case '[`name` *: tail] =>
index
case '[_ *: tail] =>
findIndexOrAbort(Type.of[tail], index + 1)
case _ =>
report.errorAndAbort(
s"'${constName}' is not found in ${Type.show[Names]}",
name,
)
}
findIndexOrAbort(Type.of[Names])
}
'{ ${ array }.apply(${ Expr(index) }) }
}
}
}
object MacroExample2 {
import scala.annotation.tailrec
import scala.language.dynamics
import scala.quoted.*
class NamedArray[T, N <: Int, Names <: Tuple](array: IndexedSeq[T])
extends Dynamic {
def names[NN <: Tuple]: NamedArray[T, N, NN] =
this.asInstanceOf[NamedArray[T, N, NN]]
inline def selectDynamic[S <: String](name: S): T =
${ NamedArray.selectImpl[T, N, Names, S]('array, 'name) }
}
object NamedArray {
transparent inline def apply[T](inline args: T*) =
${ applyImpl('args) }
def applyImpl[T: Type](
varargs: Expr[Seq[T]],
)(using Quotes): Expr[Any] = {
import quotes.reflect.*
val args = varargs match {
case Varargs(args) => args
case _ =>
report.errorAndAbort("Expected explicit varargs sequence", varargs)
}
val size = args.size
Expr(size) match {
case '{ type n <: Int; ${ _ }: `n` } =>
'{
new NamedArray[T, n, EmptyTuple](Vector(${ Expr.ofSeq(args) }: _*))
}
}
}
def selectImpl[T: Type, N <: Int: Type, Names <: Tuple: Type, S <: String](
array: Expr[IndexedSeq[T]],
name: Expr[S],
)(using Quotes): Expr[T] = {
import quotes.reflect.*
val constName: String = name.valueOrAbort
val index = ConstantType(StringConstant(constName)).asType match {
case '[name] =>
@tailrec def findIndexOrAbort(tpe: Type[?], index: Int = 0): Int =
tpe match {
case '[`name` *: tail] =>
index
case '[_ *: tail] =>
findIndexOrAbort(Type.of[tail], index + 1)
case _ =>
report.errorAndAbort(
s"'${constName}' is not found in ${Type.show[Names]}",
name,
)
}
findIndexOrAbort(Type.of[Names])
}
TypeRepr.of[N] match {
case ConstantType(IntConstant(n)) =>
if (index >= n) {
report.errorAndAbort(s"Index out of bound: ${index} >= ${n}", name)
}
case _ =>
report.errorAndAbort("Unknown length of the array", array)
}
'{ ${ array }.apply(${ Expr(index) }) }
}
}
}
object MacroUtil {
import scala.quoted.*
def inspectImpl[T: Type](x: Expr[T])(using Quotes): Expr[T] = {
println(x.show)
x
}
inline def inspect[T](inline x: T): T = ${ inspectImpl('x) }
val logging = true
inline def log(msg: String): Unit =
${ logImpl('msg) }
def logImpl(msg: Expr[String])(using Quotes): Expr[Unit] = {
if (logging)
'{ println(${ msg }) }
else
'{ () }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment