支持Infix
This commit is contained in:
parent
f5e43657b3
commit
48cb1003bb
5 changed files with 65 additions and 2 deletions
12
src/main/scala/minisql/SqlInfix.scala
Normal file
12
src/main/scala/minisql/SqlInfix.scala
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package minisql
|
||||||
|
|
||||||
|
import minisql.ast.Ast
|
||||||
|
import scala.quoted.*
|
||||||
|
|
||||||
|
sealed trait InfixValue {
|
||||||
|
def as[T]: T
|
||||||
|
}
|
||||||
|
|
||||||
|
extension (sc: StringContext) {
|
||||||
|
def infix(args: Any*): InfixValue = throw NonQuotedException()
|
||||||
|
}
|
|
@ -1 +1,29 @@
|
||||||
|
package minisql.parsing
|
||||||
|
|
||||||
|
import minisql.ast
|
||||||
|
import scala.quoted.*
|
||||||
|
|
||||||
|
private[parsing] def infixParsing(
|
||||||
|
astParser: => Parser[ast.Ast]
|
||||||
|
)(using Quotes): Parser[ast.Infix] = {
|
||||||
|
import quotes.reflect.*
|
||||||
|
{
|
||||||
|
case '{ ($x: minisql.InfixValue).as[t] } => infixParsing(astParser)(x)
|
||||||
|
case '{
|
||||||
|
minisql.infix(StringContext(${ Varargs(partsExprs) }*))(${
|
||||||
|
Varargs(argsExprs)
|
||||||
|
}*)
|
||||||
|
} =>
|
||||||
|
val parts = partsExprs.map { p =>
|
||||||
|
p.value.getOrElse(
|
||||||
|
report.errorAndAbort(
|
||||||
|
s"Expected a string literal in StringContext parts, but got: ${p.show}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}.toList
|
||||||
|
|
||||||
|
val params = argsExprs.map(arg => astParser(arg)).toList
|
||||||
|
|
||||||
|
'{ ast.Infix(${ Expr(parts) }, ${ Expr.ofList(params) }, true, false) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -41,8 +41,10 @@ private[minisql] object Parsing {
|
||||||
f: Parser[ast.Ast]
|
f: Parser[ast.Ast]
|
||||||
): Parser[ast.Ast] = {
|
): Parser[ast.Ast] = {
|
||||||
case expr =>
|
case expr =>
|
||||||
val t = expr.asTerm
|
val t = extractTerm(expr.asTerm)
|
||||||
f(extractTerm(t).asExpr)
|
if (t.isExpr)
|
||||||
|
f(t.asExpr)
|
||||||
|
else f(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy val astParser: Parser[ast.Ast] =
|
lazy val astParser: Parser[ast.Ast] =
|
||||||
|
@ -50,6 +52,7 @@ private[minisql] object Parsing {
|
||||||
typedParser
|
typedParser
|
||||||
.orElse(propertyParser)
|
.orElse(propertyParser)
|
||||||
.orElse(liftParser)
|
.orElse(liftParser)
|
||||||
|
.orElse(infixParser)
|
||||||
.orElse(identParser)
|
.orElse(identParser)
|
||||||
.orElse(valueParser)
|
.orElse(valueParser)
|
||||||
.orElse(operationParser)
|
.orElse(operationParser)
|
||||||
|
@ -108,6 +111,10 @@ private[minisql] object Parsing {
|
||||||
lazy val traversableOperationParser: Parser[ast.IterableOperation] =
|
lazy val traversableOperationParser: Parser[ast.IterableOperation] =
|
||||||
traversableOperationParsing(astParser)
|
traversableOperationParsing(astParser)
|
||||||
|
|
||||||
|
lazy val infixParser: Parser[ast.Infix] = infixParsing(
|
||||||
|
astParser
|
||||||
|
)
|
||||||
|
|
||||||
astParser(expr)
|
astParser(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,15 @@ class FromExprsSuite extends FunSuite {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testFor("Infix with different parameters") {
|
||||||
|
Infix(
|
||||||
|
List("?", " + ", "?"),
|
||||||
|
List(Constant(1), Constant(2)),
|
||||||
|
pure = true,
|
||||||
|
noParen = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
testFor("OptionOperation - OptionMap") {
|
testFor("OptionOperation - OptionMap") {
|
||||||
OptionMap(Ident("opt"), Ident("x"), Ident("x"))
|
OptionMap(Ident("opt"), Ident("x"), Ident("x"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,4 +43,11 @@ class QuotedSuite extends munit.FunSuite {
|
||||||
|
|
||||||
println(o)
|
println(o)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("Infix string interpolation") {
|
||||||
|
val o = testContext.io(
|
||||||
|
Foos.map(f => infix"CONCAT(${f.name}, ' ', ${f.id})".as[String])
|
||||||
|
)
|
||||||
|
assertEquals(o.sql, "SELECT CONCAT(f.name, ' ', f.id) FROM foo f")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue