支持Infix

This commit is contained in:
jilen 2025-07-02 12:07:54 +08:00
parent f5e43657b3
commit 48cb1003bb
5 changed files with 65 additions and 2 deletions

View 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()
}

View file

@ -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) }
}
}

View file

@ -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)
} }

View file

@ -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"))
} }

View file

@ -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")
}
} }