diff --git a/.gitignore b/.gitignore index 603bb28..816c54d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ target/ .bsp/ .metals/ +.bloop/ +project/metals.sbt \ No newline at end of file diff --git a/.scalafmt.conf b/.scalafmt.conf index 2f87a6f..25f62b6 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,4 +1,4 @@ -version = "3.8.1" +version = "3.8.3" style = defaultWithAlign runner.dialect=scala3 maxColumn = 80 diff --git a/build.sbt b/build.sbt index 9289728..9cd70cf 100644 --- a/build.sbt +++ b/build.sbt @@ -1,9 +1,8 @@ name := "minisql" -scalaVersion := "3.5.0-RC4" +scalaVersion := "3.5.2" libraryDependencies ++= Seq( - ) -scalacOptions ++= Seq("-experimental") +scalacOptions ++= Seq("-experimental", "-language:experimental.namedTuples") diff --git a/project/build.properties b/project/build.properties index 081fdbb..db1723b 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.10.0 +sbt.version=1.10.5 diff --git a/project/project/metals.sbt b/project/project/metals.sbt new file mode 100644 index 0000000..8d78f40 --- /dev/null +++ b/project/project/metals.sbt @@ -0,0 +1,8 @@ +// format: off +// DO NOT EDIT! This file is auto-generated. + +// This file enables sbt-bloop to create bloop config files. + +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.6.0") + +// format: on diff --git a/src/main/scala/miniql/.#QueryDsl.scala b/src/main/scala/miniql/.#QueryDsl.scala deleted file mode 120000 index aa3686d..0000000 --- a/src/main/scala/miniql/.#QueryDsl.scala +++ /dev/null @@ -1 +0,0 @@ -jilen@jilendeiMac.2465 \ No newline at end of file diff --git a/src/main/scala/miniql/QueryDsl.scala b/src/main/scala/miniql/QueryDsl.scala deleted file mode 100644 index 082d98d..0000000 --- a/src/main/scala/miniql/QueryDsl.scala +++ /dev/null @@ -1,28 +0,0 @@ -package minisql - -import scala.collection.immutable.{Map => IMap} -import scala.quoted.* -import minisql.ast.{*, given} -import scala.deriving.* -import scala.compiletime.* - -sealed trait Query[T] { - def ast: Ast -} - -case class Column - -case class EntityQuery[A](ast: Entity) extends Query - -inline def compile(inline e: Ast): Unit = ${ compileImpl('e) } - -private def compileImpl(e: Expr[Ast])(using Quotes) = { - import quotes.reflect.* - e.value match { - case Some(v) => - report.info("Static:" + v.toString()) - case None => - report.info("Dynamic") - } - '{ () } -} diff --git a/src/main/scala/miniql/AstPrinter.scala b/src/main/scala/minisql/AstPrinter.scala similarity index 100% rename from src/main/scala/miniql/AstPrinter.scala rename to src/main/scala/minisql/AstPrinter.scala diff --git a/src/main/scala/miniql/NamingStrategy.scala b/src/main/scala/minisql/NamingStrategy.scala similarity index 100% rename from src/main/scala/miniql/NamingStrategy.scala rename to src/main/scala/minisql/NamingStrategy.scala diff --git a/src/main/scala/miniql/ast/Ast.scala b/src/main/scala/minisql/ast/Ast.scala similarity index 85% rename from src/main/scala/miniql/ast/Ast.scala rename to src/main/scala/minisql/ast/Ast.scala index f6a043d..a1e79f2 100644 --- a/src/main/scala/miniql/ast/Ast.scala +++ b/src/main/scala/minisql/ast/Ast.scala @@ -81,16 +81,16 @@ case class ConcatMap(query: Ast, alias: Ident, body: Ast) extends Query case class SortBy(query: Ast, alias: Ident, criterias: Ast, ordering: Ordering) extends Query -sealed trait Ordering extends Ast +sealed trait Ordering extends Ast case class TupleOrdering(elems: List[Ordering]) extends Ordering sealed trait PropertyOrdering extends Ordering -case object Asc extends PropertyOrdering -case object Desc extends PropertyOrdering -case object AscNullsFirst extends PropertyOrdering -case object DescNullsFirst extends PropertyOrdering -case object AscNullsLast extends PropertyOrdering -case object DescNullsLast extends PropertyOrdering +case object Asc extends PropertyOrdering +case object Desc extends PropertyOrdering +case object AscNullsFirst extends PropertyOrdering +case object DescNullsFirst extends PropertyOrdering +case object AscNullsLast extends PropertyOrdering +case object DescNullsLast extends PropertyOrdering case class GroupBy(query: Ast, alias: Ident, body: Ast) extends Query @@ -154,7 +154,7 @@ case class Ident(name: String, visibility: Visibility) extends Ast { */ object Ident { def apply(name: String): Ident = Ident(name, Visibility.neutral) - def unapply(p: Ident) = Some((p.name)) + def unapply(p: Ident) = Some((p.name)) object Opinionated { def apply(name: String, visibilityNew: Visibility): Ident = @@ -188,7 +188,7 @@ sealed trait OpinionValues[T <: Opinion[T]] { sealed trait Visibility extends Opinion[Visibility] object Visibility extends OpinionValues[Visibility] { case object Visible extends Visibility with Opinion[Visibility] - case object Hidden extends Visibility with Opinion[Visibility] + case object Hidden extends Visibility with Opinion[Visibility] inline override def neutral: Visibility = Visible } @@ -201,7 +201,7 @@ sealed trait Renameable extends Opinion[Renameable] { } } object Renameable extends OpinionValues[Renameable] { - case object Fixed extends Renameable with Opinion[Renameable] + case object Fixed extends Renameable with Opinion[Renameable] case object ByStrategy extends Renameable with Opinion[Renameable] inline override def neutral: Renameable = ByStrategy @@ -248,8 +248,8 @@ object Property { } } -sealed trait OptionOperation extends Ast -case class OptionFlatten(ast: Ast) extends OptionOperation +sealed trait OptionOperation extends Ast +case class OptionFlatten(ast: Ast) extends OptionOperation case class OptionGetOrElse(ast: Ast, body: Ast) extends OptionOperation case class OptionFlatMap(ast: Ast, alias: Ident, body: Ast) extends OptionOperation @@ -259,9 +259,9 @@ case class OptionForall(ast: Ast, alias: Ident, body: Ast) case class OptionExists(ast: Ast, alias: Ident, body: Ast) extends OptionOperation case class OptionContains(ast: Ast, body: Ast) extends OptionOperation -case class OptionIsEmpty(ast: Ast) extends OptionOperation -case class OptionNonEmpty(ast: Ast) extends OptionOperation -case class OptionIsDefined(ast: Ast) extends OptionOperation +case class OptionIsEmpty(ast: Ast) extends OptionOperation +case class OptionNonEmpty(ast: Ast) extends OptionOperation +case class OptionIsDefined(ast: Ast) extends OptionOperation case class OptionTableFlatMap(ast: Ast, alias: Ident, body: Ast) extends OptionOperation case class OptionTableMap(ast: Ast, alias: Ident, body: Ast) @@ -270,15 +270,15 @@ case class OptionTableExists(ast: Ast, alias: Ident, body: Ast) extends OptionOperation case class OptionTableForall(ast: Ast, alias: Ident, body: Ast) extends OptionOperation -object OptionNone extends OptionOperation -case class OptionSome(ast: Ast) extends OptionOperation -case class OptionApply(ast: Ast) extends OptionOperation -case class OptionOrNull(ast: Ast) extends OptionOperation +object OptionNone extends OptionOperation +case class OptionSome(ast: Ast) extends OptionOperation +case class OptionApply(ast: Ast) extends OptionOperation +case class OptionOrNull(ast: Ast) extends OptionOperation case class OptionGetOrNull(ast: Ast) extends OptionOperation -sealed trait IterableOperation extends Ast -case class MapContains(ast: Ast, body: Ast) extends IterableOperation -case class SetContains(ast: Ast, body: Ast) extends IterableOperation +sealed trait IterableOperation extends Ast +case class MapContains(ast: Ast, body: Ast) extends IterableOperation +case class SetContains(ast: Ast, body: Ast) extends IterableOperation case class ListContains(ast: Ast, body: Ast) extends IterableOperation case class If(condition: Ast, `then`: Ast, `else`: Ast) extends Ast @@ -318,7 +318,7 @@ sealed trait Action extends Ast case class Update(query: Ast, assignments: List[Assignment]) extends Action case class Insert(query: Ast, assignments: List[Assignment]) extends Action -case class Delete(query: Ast) extends Action +case class Delete(query: Ast) extends Action sealed trait ReturningAction extends Action { def action: Ast @@ -358,11 +358,11 @@ object OnConflict { } sealed trait Target - case object NoTarget extends Target + case object NoTarget extends Target case class Properties(props: List[Property]) extends Target sealed trait Action - case object Ignore extends Action + case object Ignore extends Action case class Update(assignments: List[Assignment]) extends Action } //************************************************************ @@ -386,34 +386,15 @@ sealed trait ScalarLift extends Lift case class ScalarValueLift( name: String, liftId: String -) extends ScalarLift { - - def expandedLiftId: Option[(String, Int)] = { - val idxStart = liftId.indexOf('[') - val idxEnd = liftId.indexOf(']') - if (idxStart != -1 && idxEnd != -1) { - val idx = liftId.substring(idxStart + 1, idxEnd).toIntOption - idx.map(i => liftId.substring(0, idxStart) -> i) - } else None - } -} - -case class ScalarQueryLift(name: String, liftId: String) extends ScalarLift { - def at(idx: Int): ScalarValueLift = - ScalarValueLift(s"${name}[$idx]", s"${liftId}[${idx}]") -} +) extends ScalarLift object ScalarLift { given ToExpr[ScalarLift] with { def apply(l: ScalarLift)(using Quotes) = l match { case ScalarValueLift(n, id) => '{ ScalarValueLift(${ Expr(n) }, ${ Expr(id) }) } - case ScalarQueryLift(n, id) => - '{ ScalarQueryLift(${ Expr(n) }, ${ Expr(id) }) } } } } sealed trait CaseClassLift extends Lift -case class CaseClassQueryLift(name: String, liftId: String) - extends CaseClassLift diff --git a/src/main/scala/miniql/ast/CollectAst.scala b/src/main/scala/minisql/ast/CollectAst.scala similarity index 100% rename from src/main/scala/miniql/ast/CollectAst.scala rename to src/main/scala/minisql/ast/CollectAst.scala diff --git a/src/main/scala/miniql/ast/FromExprs.scala b/src/main/scala/minisql/ast/FromExprs.scala similarity index 89% rename from src/main/scala/miniql/ast/FromExprs.scala rename to src/main/scala/minisql/ast/FromExprs.scala index 7df747f..1306f6f 100644 --- a/src/main/scala/miniql/ast/FromExprs.scala +++ b/src/main/scala/minisql/ast/FromExprs.scala @@ -68,6 +68,9 @@ private given FromExpr[Property] with { ) } => Some(Property(a, n, r, v)) + case o => + println(s"Canot extrat ${o.show}:${x}") + None } } @@ -103,6 +106,9 @@ private given FromExpr[Query] with { Some(Take(b, n)) case '{ SortBy(${ Expr(b) }, ${ Expr(p) }, ${ Expr(s) }, ${ Expr(o) }) } => Some(SortBy(b, p, s, o)) + case o => + println(s"Cannot extract ${o.show}") + None } } @@ -220,17 +226,37 @@ private given FromExpr[If] with { } } -private[minisql] given astFromExpr: FromExpr[Ast] = new FromExpr[Ast] { - - private def isAstType[t](using Quotes, Type[t]) = { - import quotes.reflect.* - val t1 = TypeRepr.of[t].dealias.simplified - println(s"isAstType ${TypeRepr.of[t]} ===> ${t1}") - t1 <:< TypeRepr.of[Ast] +private def extractTerm(using Quotes)(x: quotes.reflect.Term) = { + import quotes.reflect.* + def unwrapTerm(t: Term): Term = t match { + case Inlined(_, _, o) => unwrapTerm(o) + case Block(Nil, last) => last + case Typed(t, _) => + unwrapTerm(t) + case Select(t, "$asInstanceOf$") => + unwrapTerm(t) + case TypeApply(t, _) => + unwrapTerm(t) + case o => o } + val o = unwrapTerm(x) + println(s"From ========== ${x.show}") + println(s"To ========== ${o.show}") + o +} + +extension (e: Expr[Any]) { + def toTerm(using Quotes) = { + import quotes.reflect.* + e.asTerm + } +} + +given astFromExpr: FromExpr[Ast] = new FromExpr[Ast] { def unapply(e: Expr[Ast])(using Quotes): Option[Ast] = { - e match { + + extractTerm(e.toTerm).asExpr match { case '{ $x: Query } => x.value case '{ $x: ScalarValueLift } => x.value case '{ $x: Property } => x.value @@ -242,26 +268,7 @@ private[minisql] given astFromExpr: FromExpr[Ast] = new FromExpr[Ast] { case '{ $x: Action } => x.value case '{ $x: If } => x.value case '{ $x: Infix } => x.value - case '{ ($x: Ast).asInstanceOf } => - unapply(x.asInstanceOf) - case '{ (${ x }: t1).asInstanceOf[t2] } if isAstType[t2] => - unapply(x.asInstanceOf) - case o => - import quotes.reflect.* - def unwrapInline(x: Term): Unit = x match { - case Inlined(_, bs, t) => - unwrapInline(t) - case Typed(t, _) => - unwrapInline(t) - case TypeApply(t, _) => - unwrapInline(t) - // case Select(x, "$asInstanceOf$") => - // unwrapInline(x) - case o => - println(s"unwrapped term(${o.getClass}): ${o.show}") - } - unwrapInline(o.asTerm) - None + case o => None } } } diff --git a/src/main/scala/miniql/ast/JoinType.scala b/src/main/scala/minisql/ast/JoinType.scala similarity index 100% rename from src/main/scala/miniql/ast/JoinType.scala rename to src/main/scala/minisql/ast/JoinType.scala diff --git a/src/main/scala/miniql/ast/Operator.scala b/src/main/scala/minisql/ast/Operator.scala similarity index 100% rename from src/main/scala/miniql/ast/Operator.scala rename to src/main/scala/minisql/ast/Operator.scala diff --git a/src/main/scala/miniql/ast/StatefulTransformer.scala b/src/main/scala/minisql/ast/StatefulTransformer.scala similarity index 100% rename from src/main/scala/miniql/ast/StatefulTransformer.scala rename to src/main/scala/minisql/ast/StatefulTransformer.scala diff --git a/src/main/scala/miniql/ast/StatelessTransformer.scala b/src/main/scala/minisql/ast/StatelessTransformer.scala similarity index 100% rename from src/main/scala/miniql/ast/StatelessTransformer.scala rename to src/main/scala/minisql/ast/StatelessTransformer.scala diff --git a/src/main/scala/miniql/ast/Transform.scala b/src/main/scala/minisql/ast/Transform.scala similarity index 100% rename from src/main/scala/miniql/ast/Transform.scala rename to src/main/scala/minisql/ast/Transform.scala diff --git a/src/main/scala/minisql/dsl.scala b/src/main/scala/minisql/dsl.scala new file mode 100644 index 0000000..a1e85a2 --- /dev/null +++ b/src/main/scala/minisql/dsl.scala @@ -0,0 +1,46 @@ +package minisql.dsl + +import minisql.ast.{Ast, Entity, given} +import scala.quoted.* +import scala.compiletime.* +import scala.compiletime.ops.string.* + +sealed trait Dsl { + def ast: Ast +} + +trait Query[E] extends Dsl + +class EntityQuery[E](val ast: Ast) extends Query + +given FromExpr[EntityQuery[?]] with { + def unapply(x: Expr[EntityQuery[?]])(using Quotes): Option[EntityQuery[?]] = { + x match { + case '{ EntityQuery(${ Expr(ast) }) } => + Some(EntityQuery(ast)) + case _ => + import quotes.reflect.* + println(s"cannot unlift ${x.asTerm}") + None + } + } +} +given FromExpr[Dsl] with { + def unapply(d: Expr[Dsl])(using Quotes): Option[Dsl] = d match { + case '{ ($x: EntityQuery[?]) } => x.value + } +} + +inline def query[E](inline table: String) = + EntityQuery[E](Entity(table, Nil)) + +inline def compile(inline x: Dsl): Option[String] = ${ compileImpl('x) } + +private def compileImpl(x: Expr[Dsl])(using Quotes): Expr[Option[String]] = { + import quotes.reflect.* + x.value match { + case Some(xv) => '{ Some(${ Expr(xv.ast.toString()) }) } + case None => '{ None } + } + +} diff --git a/src/main/scala/miniql/util/CollectTry.scala b/src/main/scala/minisql/util/CollectTry.scala similarity index 100% rename from src/main/scala/miniql/util/CollectTry.scala rename to src/main/scala/minisql/util/CollectTry.scala diff --git a/src/main/scala/miniql/util/EnableReflectiveCalls.scala b/src/main/scala/minisql/util/EnableReflectiveCalls.scala similarity index 100% rename from src/main/scala/miniql/util/EnableReflectiveCalls.scala rename to src/main/scala/minisql/util/EnableReflectiveCalls.scala diff --git a/src/main/scala/miniql/util/IndentUtil.scala b/src/main/scala/minisql/util/IndentUtil.scala similarity index 100% rename from src/main/scala/miniql/util/IndentUtil.scala rename to src/main/scala/minisql/util/IndentUtil.scala diff --git a/src/main/scala/miniql/util/Interleave.scala b/src/main/scala/minisql/util/Interleave.scala similarity index 100% rename from src/main/scala/miniql/util/Interleave.scala rename to src/main/scala/minisql/util/Interleave.scala diff --git a/src/main/scala/miniql/util/Interpolator.scala b/src/main/scala/minisql/util/Interpolator.scala similarity index 100% rename from src/main/scala/miniql/util/Interpolator.scala rename to src/main/scala/minisql/util/Interpolator.scala diff --git a/src/main/scala/miniql/util/LoadObject.scala b/src/main/scala/minisql/util/LoadObject.scala similarity index 100% rename from src/main/scala/miniql/util/LoadObject.scala rename to src/main/scala/minisql/util/LoadObject.scala diff --git a/src/main/scala/miniql/util/Show.scala b/src/main/scala/minisql/util/Show.scala similarity index 100% rename from src/main/scala/miniql/util/Show.scala rename to src/main/scala/minisql/util/Show.scala