-
-
Save tarao/e049673e1ed6acdcf2b351a0cbf9b40a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) }) } | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) }) } | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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