Skip to content

Instantly share code, notes, and snippets.

@Jacoby6000
Last active November 13, 2024 18:27
Show Gist options
  • Save Jacoby6000/a60b024dd22ac7d6a2c8ac46ee06cc3d to your computer and use it in GitHub Desktop.
Save Jacoby6000/a60b024dd22ac7d6a2c8ac46ee06cc3d to your computer and use it in GitHub Desktop.
import scala.reflect.ClassTag
import scala.reflect.runtime.universe._
import scala.language.dynamics
import scala.language.implicitConversions
import scala.reflect.api.{Mirror => SRMirror, TypeCreator, Universe}
import scala.Predef.{implicitly, locally, any2stringadd => _}
class ScalaScripter(val a: Any) extends Dynamic {
import ScalaScript.{enable => toScalaScript, disable => unScalaScript}
private val mirror = runtimeMirror(getClass.getClassLoader)
private val runtimeClass = a.getClass
private val classMirror = mirror.reflect(a)
private val symbol = classMirror.symbol
private val typ = symbol.toType
implicit val typeTag: TypeTag[_] = TypeTag(mirror, new TypeCreator { self =>
def apply[U <: Universe with Singleton](m: SRMirror[U]): U#Type =
typ.asInstanceOf[U#Type]
})
implicit val classTag: ClassTag[_] = ClassTag(runtimeClass)
private val typeOfA = typeOf(typeTag)
private def rewriteKey(key: String): String =
key.replaceAll("""\$""", """\$dollar""").replaceAll("""\+""", """\$plus""").replaceAll(""":""", """\$colon""")
val map: scala.collection.mutable.HashMap[String, ScalaScripter] =
scala.collection.mutable.HashMap()
// lol
private def doSafe[B](key: String, args: List[Any], a : => B): B =
try { a } catch {
case ex: ScalaReflectionException =>
val message = s"Failed to invoke function '$key' with args ${args.mkString("'", "' , '", "'")} on instance of type ${typ}"
println(message + ": " + ex.getMessage) // for scastie visibility
throw new RuntimeException(message, ex)
case ex: IllegalArgumentException =>
val message = s"Failed to invoke function '$key' with args ${args.mkString("'", "' , '", "'")} on instance of type ${typ}"
println(message + ": " + ex.getMessage) // for scastie visibility
throw new RuntimeException(message, ex)
}
private def varargsToValueAny(args: Any*): Any =
if(args.isEmpty) null
else if (args.length == 1) args.head
else args
private def findMethod(methodName: String, args: List[Any]): Option[MethodMirror] = {
val methodSymbols = classMirror.symbol.typeSignature.member(TermName(rewriteKey(methodName))) match {
case NoSymbol => Nil
case sym if sym.isMethod && sym.asMethod.isOverloaded => sym.asTerm.alternatives.map(_.asMethod)
case sym if sym.isMethod => List(sym.asMethod)
case _ => Nil
}
val methodsWithCorrectArgCount = methodSymbols.filter { method =>
val params = method.paramLists.headOption.getOrElse(Nil)
params.length == args.length
}
methodsWithCorrectArgCount.find { method =>
val params = method.paramLists.headOption.getOrElse(Nil)
params.zip(args).forall { case (param, arg) =>
val paramType = param.typeSignature
val argType = mirror.reflectClass(mirror.classSymbol(arg.getClass)).symbol.toType
argType <:< paramType
}
}.orElse(methodsWithCorrectArgCount.headOption).map(classMirror.reflectMethod)
}
def invokeMethod(key: String, value: Any*): Any = {
findMethod(key, value.toList)
.map(_.apply(value: _*))
.getOrElse {
val message = s"No such method ${key} matches args ${args.mkString("'", "' , '", "'")} on instance of type ${typ}"
println(message)
throw new NoSuchMethodException(message)
}
}
def selectDynamic(key: String): ScalaScripter = doSafe(key, Nil,
map.get(rewriteKey(key)).map(unScalaScript(_)).getOrElse {
try {
applyDynamic(key)()
} catch {
case _: Throwable => classMirror.reflectField(typeOfA.decl(TermName(rewriteKey(key))).asTerm.accessed.asTerm)
}
})
def updateDynamic(key: String, value: Any*): ScalaScripter = doSafe(key, value.toList, {
try {
invokeMethod(key + "_=", value: _*)
} catch {
case _: Throwable => map.update(key, varargsToValueAny(value: _*))
}
})
def applyDynamic(key: String)(value: Any*): ScalaScripter = doSafe (key, value.toList, {
invokeMethod(key, value: _*)
})
override def toString = if(a != null) a.toString else "null"
}
object ScalaScript {
type In = ScalaScripter
type Out = Any
implicit def enable[A](a: A): ScalaScripter = new ScalaScripter(a)
implicit def disable(s: ScalaScripter): Any = s.a
}
import ScalaScript._
val x: In = List(1,2,3)
val y: In = List("4", "5", "6")
println(y.reverse)
println(y.reverse ::: x) // fails at runtime, but compiles
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment