solution with make-true
This commit is contained in:
parent
7dc37783d8
commit
32f106c61f
4 changed files with 91 additions and 2 deletions
|
@ -81,6 +81,12 @@ object GenerativeFluentAssertionsDSL:
|
||||||
propertyNameExpr,
|
propertyNameExpr,
|
||||||
valueExpr
|
valueExpr
|
||||||
)
|
)
|
||||||
|
case '{
|
||||||
|
(${ typeProvider }: BePropertyTypeProvider[subjectType])
|
||||||
|
.applyDynamic($propertyNameExpr)($value)
|
||||||
|
.$asInstanceOf$[AssertionProperty]
|
||||||
|
} =>
|
||||||
|
'{} // placeholder
|
||||||
case _ =>
|
case _ =>
|
||||||
report.errorAndAbort(
|
report.errorAndAbort(
|
||||||
"Invalid expression, must be a 'should be' or 'should have' assertion. ",
|
"Invalid expression, must be a 'should be' or 'should have' assertion. ",
|
||||||
|
|
|
@ -12,6 +12,10 @@ class DynamicShouldBeProperty extends AssertionProperty with Dynamic:
|
||||||
def applyDynamic(fieldName: String)(foo: Any*) = ???
|
def applyDynamic(fieldName: String)(foo: Any*) = ???
|
||||||
end DynamicShouldBeProperty
|
end DynamicShouldBeProperty
|
||||||
|
|
||||||
|
class BePropertyTypeProvider[T](val subject: T) extends Selectable:
|
||||||
|
def applyDynamic(fieldName: String)(foo: Any*): AssertionProperty = ???
|
||||||
|
end BePropertyTypeProvider
|
||||||
|
|
||||||
/** The class that implements the syntactic aspect of the type provider. It
|
/** The class that implements the syntactic aspect of the type provider. It
|
||||||
* extends Selectable and implements applyDynamic with arity 1, so that any
|
* extends Selectable and implements applyDynamic with arity 1, so that any
|
||||||
* object of this type can be invoked with an arbitrary method with 1 argument,
|
* object of this type can be invoked with an arbitrary method with 1 argument,
|
||||||
|
@ -32,6 +36,7 @@ end HavePropertyTypeProvider
|
||||||
sealed trait AssertionDSLVerb
|
sealed trait AssertionDSLVerb
|
||||||
case object be extends AssertionDSLVerb with Dynamic:
|
case object be extends AssertionDSLVerb with Dynamic:
|
||||||
def selectDynamic(fieldName: String): AssertionProperty = ???
|
def selectDynamic(fieldName: String): AssertionProperty = ???
|
||||||
|
case object make extends AssertionDSLVerb
|
||||||
case object have extends AssertionDSLVerb
|
case object have extends AssertionDSLVerb
|
||||||
|
|
||||||
/** Implicit class to add 'should' assertions to a generic type T.
|
/** Implicit class to add 'should' assertions to a generic type T.
|
||||||
|
@ -51,6 +56,10 @@ extension [T](subject: T)
|
||||||
*/
|
*/
|
||||||
def should(property: AssertionProperty) = ???
|
def should(property: AssertionProperty) = ???
|
||||||
|
|
||||||
|
transparent inline def should(inline verbWord: make.type) = ${
|
||||||
|
ShouldBeTypeProvider.generateShouldBeTypeProvider[T]('subject)
|
||||||
|
}
|
||||||
|
|
||||||
/** Specifies the 'have' assertion.
|
/** Specifies the 'have' assertion.
|
||||||
*
|
*
|
||||||
* The method is a type provider: it generates, through a macro, a refinement
|
* The method is a type provider: it generates, through a macro, a refinement
|
||||||
|
|
|
@ -25,11 +25,18 @@ case class Box(label: String, weight: Mass, price: Money)
|
||||||
val box = Box("aBox", 30.kg, 10.CHF)
|
val box = Box("aBox", 30.kg, 10.CHF)
|
||||||
|
|
||||||
assertions:
|
assertions:
|
||||||
|
// {
|
||||||
|
// val p: BePropertyTypeProvider[Person] = new BePropertyTypeProvider[Person](person)
|
||||||
|
// p.asInstanceOf[BePropertyTypeProvider[Person] {
|
||||||
|
// def adult: AssertionProperty
|
||||||
|
// }].adult
|
||||||
|
// }
|
||||||
|
|
||||||
/* be.property assertions */
|
/* be.property assertions */
|
||||||
// assert(person.adult, ...)
|
// assert(person.adult, ...)
|
||||||
person should be.adult
|
person should make adult true
|
||||||
// assert(List().isEmpty, ...)
|
// assert(List().isEmpty, ...)
|
||||||
List().should(be.empty)
|
List() should be.empty
|
||||||
// assert(List(1,2,3).nonEmpty)
|
// assert(List(1,2,3).nonEmpty)
|
||||||
List(1, 2, 3) should be.nonEmpty
|
List(1, 2, 3) should be.nonEmpty
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
package ch.usi.si.msde.edsl.lecture10
|
||||||
|
|
||||||
|
import scala.annotation.tailrec
|
||||||
|
import scala.quoted.*
|
||||||
|
|
||||||
|
object ShouldBeTypeProvider:
|
||||||
|
|
||||||
|
def generateShouldBeTypeProvider[T](subject: Expr[T])(using Type[T])(using Quotes): Expr[Any] =
|
||||||
|
import quotes.reflect.*
|
||||||
|
|
||||||
|
val subjectTypeRepr = TypeRepr.of[T]
|
||||||
|
|
||||||
|
def refineTypeBySymbol(
|
||||||
|
currentTypeRepr: TypeRepr,
|
||||||
|
symbol: Symbol
|
||||||
|
): TypeRepr =
|
||||||
|
val methodType = MethodType(List("value"))(
|
||||||
|
_ => List(TypeRepr.of[true]),
|
||||||
|
_ => TypeRepr.of[AssertionProperty]
|
||||||
|
)
|
||||||
|
// val methodType = TypeRepr.of[AssertionProperty]
|
||||||
|
Refinement(currentTypeRepr, symbol.name, methodType)
|
||||||
|
|
||||||
|
@tailrec
|
||||||
|
def refineTypeBySymbols(
|
||||||
|
currentTypeRepr: TypeRepr,
|
||||||
|
fields: List[Symbol]
|
||||||
|
): TypeRepr =
|
||||||
|
fields match
|
||||||
|
// this is pattern matching on list - like head :: rest
|
||||||
|
case symbol :: symbols =>
|
||||||
|
refineTypeBySymbols(
|
||||||
|
refineTypeBySymbol(currentTypeRepr, symbol),
|
||||||
|
symbols
|
||||||
|
)
|
||||||
|
// empty list case
|
||||||
|
case Nil =>
|
||||||
|
currentTypeRepr
|
||||||
|
|
||||||
|
val booleanType = TypeRepr.of[Boolean]
|
||||||
|
|
||||||
|
val members = (subjectTypeRepr.typeSymbol.fieldMembers ++ subjectTypeRepr.typeSymbol.methodMembers)
|
||||||
|
.filter: fieldMember =>
|
||||||
|
subjectTypeRepr.memberType(fieldMember).widen <:< booleanType
|
||||||
|
|
||||||
|
val refinedType = refineTypeBySymbols(
|
||||||
|
TypeRepr.of[BePropertyTypeProvider[T]],
|
||||||
|
members
|
||||||
|
)
|
||||||
|
|
||||||
|
println(refinedType)
|
||||||
|
|
||||||
|
// This is the way to extract a (reflection) type to a
|
||||||
|
// type that can be used in some quoted code.
|
||||||
|
// it's the equivalent of .asExpr for expressions,
|
||||||
|
// but it's more complicated because in this case the
|
||||||
|
// exact type is unknown until compilation time.
|
||||||
|
refinedType.asType match
|
||||||
|
case '[tpe] => // tpe is the exact refined type
|
||||||
|
val res = '{
|
||||||
|
val p = BePropertyTypeProvider[T]($subject)
|
||||||
|
p.asInstanceOf[tpe]
|
||||||
|
}
|
||||||
|
println(res.show)
|
||||||
|
res
|
||||||
|
|
||||||
|
end ShouldBeTypeProvider
|
Loading…
Reference in a new issue