use opaque type to encoding dsl

This commit is contained in:
jilen 2024-12-13 14:05:21 +08:00
parent 87e0213b06
commit b0ec98b6a2
2 changed files with 27 additions and 44 deletions

View file

@ -300,7 +300,8 @@ private def fromBlock(using
given astFromExpr: FromExpr[Ast] = new FromExpr[Ast] { given astFromExpr: FromExpr[Ast] = new FromExpr[Ast] {
def unapply(e: Expr[Ast])(using Quotes): Option[Ast] = { def unapply(e: Expr[Ast])(using Quotes): Option[Ast] = {
e match { val t = extractTerm(e.toTerm)
t.asExpr match {
case '{ $x: Query } => x.value case '{ $x: Query } => x.value
case '{ $x: ScalarValueLift } => x.value case '{ $x: ScalarValueLift } => x.value
case '{ $x: Property } => x.value case '{ $x: Property } => x.value
@ -312,7 +313,10 @@ given astFromExpr: FromExpr[Ast] = new FromExpr[Ast] {
case '{ $x: Action } => x.value case '{ $x: Action } => x.value
case '{ $x: If } => x.value case '{ $x: If } => x.value
case '{ $x: Infix } => x.value case '{ $x: Infix } => x.value
case o => None case o =>
import quotes.reflect.*
report.warning(s"Cannot get value from ${o.show}", o.asTerm.pos)
None
} }
} }
} }

View file

@ -1,69 +1,44 @@
package minisql.dsl package minisql.dsl
import minisql.* import minisql.*
import minisql.parsing import minisql.parsing.*
import minisql.ast.{Ast, Entity, Map, Property, Ident, given} import minisql.ast.{Ast, Entity, Map, Property, Ident, given}
import scala.quoted.* import scala.quoted.*
import scala.compiletime.* import scala.compiletime.*
import scala.compiletime.ops.string.* import scala.compiletime.ops.string.*
import scala.collection.immutable.{Map => IMap} import scala.collection.immutable.{Map => IMap}
sealed trait Quoted { opaque type Quoted <: Ast = Ast
def ast: Ast
def lifts: IMap[String, (Any, ParamEncoder[?])] = IMap.empty
}
trait Query[E] extends Quoted opaque type Query[E] <: Quoted = Quoted
case class EntityQuery[E](ast: Ast) extends Query[E] opaque type EntityQuery[E] <: Query[E] = Query[E]
extension [E](inline e: EntityQuery[E]) { extension [E](inline e: EntityQuery[E]) {
inline def map[E1](inline f: E => E1): EntityQuery[E1] = { inline def map[E1](inline f: E => E1): EntityQuery[E1] = {
transform(e.ast)(f)(Map.apply)(EntityQuery.apply[E1]) transform(e)(f)(Map.apply)
} }
} }
private inline def transform[D1 <: Quoted, D2 <: Quoted, A, B](inline ast: Ast)( private inline def transform[A, B](inline q1: Quoted)(
inline f: A => B inline f: A => B
)(inline fast: (Ast, Ident, Ast) => Ast)(inline f2: Ast => D2): D2 = { )(inline fast: (Ast, Ident, Ast) => Ast): Quoted = {
f2(fast(ast, f.param0, f.body)) fast(q1, f.param0, f.body)
} }
private given FromExpr[EntityQuery[?]] with { inline def query[E](inline table: String): EntityQuery[E] =
def unapply(x: Expr[EntityQuery[?]])(using Quotes): Option[EntityQuery[?]] = { Entity(table, Nil)
x match {
case '{ inline def compile(inline x: Ast): Option[String] = ${
val x: Ast = ${ Expr(ast) } compileImpl('{ x })
EntityQuery(x)
} =>
Some(EntityQuery(ast))
case '{ EntityQuery(${ Expr(ast) }) } =>
Some(EntityQuery(ast))
case _ =>
import quotes.reflect.*
println(s"cannot unlift ${x.show}: ${x.asTerm.getClass}")
None
}
}
}
private given FromExpr[Quoted] with {
def unapply(x: Expr[Quoted])(using Quotes): Option[Quoted] = {
import quotes.reflect.*
x match {
case '{ ($x: EntityQuery[?]) } => x.value
}
}
} }
inline def query[E](inline table: String) = private def compileImpl(
EntityQuery[E](Entity(table, Nil)) x: Expr[Ast]
)(using Quotes): Expr[Option[String]] = {
inline def compile(inline x: Quoted): Option[String] = ${ compileImpl('x) }
private def compileImpl(x: Expr[Quoted])(using Quotes): Expr[Option[String]] = {
import quotes.reflect.* import quotes.reflect.*
x.value match { x.value match {
case Some(xv) => '{ Some(${ Expr(xv.ast.toString()) }) } case Some(xv) => '{ Some(${ Expr(xv.toString()) }) }
case None => '{ None } case None => '{ None }
} }
} }
@ -78,3 +53,7 @@ extension [A1, A2, B](inline f1: (A1, A2) => B) {
private inline def param1 = parsing.parseParamAt(f1, 1) private inline def param1 = parsing.parseParamAt(f1, 1)
private inline def body = parsing.parseBody(f1) private inline def body = parsing.parseBody(f1)
} }
case class Foo(id: Int)
inline def queryFooId = query[Foo]("foo").map(_.id)