ex4 predef string weird error
This commit is contained in:
parent
cbe868975b
commit
efa648207f
6 changed files with 70 additions and 28 deletions
|
@ -12,7 +12,7 @@ object GenerativeFluentAssertionsDSL:
|
|||
import quotes.reflect.*
|
||||
|
||||
subject match {
|
||||
case '{ $subject: Iterable[Any] } => '{ ContainsVerb($subject) }
|
||||
case '{ $subject: Iterable[elementType] } => '{ ContainsVerb($subject) }
|
||||
case _ => report.errorAndAbort(
|
||||
"assertion subject must be an Iterable[?] in order to use a 'contain' assertion",
|
||||
subject
|
||||
|
@ -45,6 +45,13 @@ object GenerativeFluentAssertionsDSL:
|
|||
.applyDynamic($propertyNameExpr)(satisfying.notEquals[T]($obj, $ord))
|
||||
.$asInstanceOf$[AssertionProperty]
|
||||
}
|
||||
case '{ (${ typeProvider }: ContainsPropertyTypeProvider[subjectType])
|
||||
.applyDynamic($propertyNameExpr: String)(satisfying)
|
||||
.$asInstanceOf$[SatisfyingNotEquals[T]] } => '{
|
||||
$typeProvider
|
||||
.applyDynamic($propertyNameExpr)(satisfying.notEquals[T]($obj, $ord))
|
||||
.$asInstanceOf$[AssertionProperty]
|
||||
}
|
||||
}
|
||||
|
||||
/** Entrypoint. This macro method takes an expression composed of
|
||||
|
@ -124,7 +131,6 @@ object GenerativeFluentAssertionsDSL:
|
|||
.applyDynamic($propertyNameExpr: String)($valueExpr: Condition[valueType])
|
||||
.$asInstanceOf$[AssertionProperty]
|
||||
} =>
|
||||
println(assertionExpr.show)
|
||||
val subjectExpr = '{ $typeProvider.subject }
|
||||
val negatedExpr = '{ $typeProvider.negated }
|
||||
generateShouldHaveAssertion[subjectType, valueType](
|
||||
|
@ -145,7 +151,14 @@ object GenerativeFluentAssertionsDSL:
|
|||
.$asInstanceOf$[AssertionProperty]
|
||||
} =>
|
||||
'{} // placeholder
|
||||
case _ =>
|
||||
case '{
|
||||
(${ typeProvider }: ContainsPropertyTypeProvider[subjectType])
|
||||
.applyDynamic($propertyNameExpr: String)($valueExpr: Condition[valueType])
|
||||
.$asInstanceOf$[AssertionProperty]
|
||||
} =>
|
||||
'{} // placeholder
|
||||
case a =>
|
||||
println(a.show)
|
||||
report.errorAndAbort(
|
||||
"Invalid expression, must be a 'should be' or 'should have' assertion. ",
|
||||
assertionExpr
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ch.usi.si.msde.edsl.lecture10
|
||||
|
||||
import scala.language.dynamics
|
||||
import scala.quoted.Expr
|
||||
|
||||
sealed trait AssertionProperty
|
||||
case class NegatedAssertionProperty(prop: AssertionProperty) extends AssertionProperty
|
||||
|
@ -67,17 +68,19 @@ case object have extends HaveVerb(false)
|
|||
case object contain extends AssertionDSLVerb
|
||||
case object `with` extends AssertionDSLVerb
|
||||
|
||||
case class ContainsVerb[T](subject: Iterable[T]):
|
||||
transparent inline def allElements(verb: `with`.type) = ${
|
||||
ShouldContainTypeProvider.generateShouldContainTypeProvider[T]('subject, 'All)
|
||||
case class ContainsVerb[T](subject: Iterable[T])
|
||||
|
||||
extension [T](inline containsVerb: ContainsVerb[T])
|
||||
transparent inline def allElements(inline verb: `with`.type) = ${
|
||||
ShouldContainTypeProvider.generateShouldContainTypeProvider[T]('containsVerb, 'All)
|
||||
}
|
||||
|
||||
transparent inline def someElements(verb: `with`.type) = ${
|
||||
ShouldContainTypeProvider.generateShouldContainTypeProvider[T]('subject, 'Some)
|
||||
transparent inline def someElements(inline verb: `with`.type) = ${
|
||||
ShouldContainTypeProvider.generateShouldContainTypeProvider[T]('containsVerb, 'Some)
|
||||
}
|
||||
|
||||
transparent inline def noElements(verb: `with`.type) = ${
|
||||
ShouldContainTypeProvider.generateShouldContainTypeProvider[T]('subject, 'None)
|
||||
transparent inline def noElements(inline verb: `with`.type) = ${
|
||||
ShouldContainTypeProvider.generateShouldContainTypeProvider[T]('containsVerb, 'None)
|
||||
}
|
||||
|
||||
/** Implicit class to add 'should' assertions to a generic type T.
|
||||
|
@ -88,6 +91,13 @@ case class ContainsVerb[T](subject: Iterable[T]):
|
|||
extension [T](subject: T)
|
||||
def should(property: AssertionProperty) = ???
|
||||
|
||||
// we use the 'inline' modifier on subject as well to make sure that the expression
|
||||
// generated by the 'shouldContainImpl' macro is a one-liner, so it does not contain
|
||||
// intermediate '$proxy<n>' variable declarations that would make expression pattern
|
||||
// matching impossible. Using the 'inline' modifier requires all extension methods
|
||||
// to be 'inline', so we separate the original 'should be' implementation in the
|
||||
// extension methods block above.
|
||||
extension [T](inline subject: T)
|
||||
transparent inline def should(inline verbWord: be_.type) = ${
|
||||
ShouldBeTypeProvider.generateShouldBeTypeProvider[T]('subject)
|
||||
}
|
||||
|
@ -109,7 +119,13 @@ extension [T](subject: T)
|
|||
transparent inline def should(inline verb: HaveVerb) = ${
|
||||
ShouldHaveTypeProvider.generateShouldHaveTypeProvider[T]('subject, '{verb.negated})
|
||||
}
|
||||
|
||||
transparent inline def should(inline verb: contain.type) = ${
|
||||
GenerativeFluentAssertionsDSL.shouldContainImpl('subject)
|
||||
|
||||
// We define here the 'should contain' extension method. It is not possible to
|
||||
// write an extension method for Iterable[T] as that method would shadow the
|
||||
// 'should be' and 'should have' assertions defined for all objects. Therefore,
|
||||
// we check the subject is an instance of Iterable[T] with a macro, and we use
|
||||
// report.errorAndAbort to provide a clear error message stating that an Iterable[T]
|
||||
// is required for this kind of assertion.
|
||||
transparent inline def should(inline verb: contain.type) = ${
|
||||
GenerativeFluentAssertionsDSL.shouldContainImpl('subject)
|
||||
}
|
|
@ -2,11 +2,11 @@ package ch.usi.si.msde.edsl.lecture10
|
|||
|
||||
// import scala.language.postfixOps
|
||||
|
||||
import squants.mass.MassConversions.MassConversions
|
||||
import squants.market.MoneyConversions.MoneyConversions
|
||||
import GenerativeFluentAssertionsDSL.*
|
||||
import squants.mass.Mass
|
||||
import ch.usi.si.msde.edsl.lecture10.GenerativeFluentAssertionsDSL.*
|
||||
import squants.market.Money
|
||||
import squants.market.MoneyConversions.MoneyConversions
|
||||
import squants.mass.Mass
|
||||
import squants.mass.MassConversions.MassConversions
|
||||
|
||||
extension (value: Double) def i = Complex(0, value)
|
||||
|
||||
|
@ -47,6 +47,11 @@ case class Box(label: String, weight: Mass, price: Money)
|
|||
List(50, 2, 3) should have head satisfying < 100
|
||||
box should !(!have) weight satisfying <= 300.0.kg
|
||||
|
||||
// List("bar","foo","","alice") should contain someElements `with` size satisfying === 0
|
||||
List(Seq(), Seq(), Seq()) should contain allElements `with` size satisfying >= 0
|
||||
// List(Seq(), Seq(), Seq()) should contain allElements `with` size satisfying !== 0
|
||||
List(List(1,2), List(20,1), List(3,4)) should contain noElements `with` head satisfying === 3
|
||||
|
||||
/* should have assertions */
|
||||
// // assert(List(1).head == 1, ...)
|
||||
// List(1) should have head 1
|
||||
|
|
|
@ -53,8 +53,6 @@ object ShouldBeTypeProvider:
|
|||
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,
|
||||
|
@ -66,7 +64,6 @@ object ShouldBeTypeProvider:
|
|||
val p = BePropertyTypeProvider[T]($subject)
|
||||
p.asInstanceOf[tpe]
|
||||
}
|
||||
println(res.show)
|
||||
res
|
||||
|
||||
end ShouldBeTypeProvider
|
||||
|
|
|
@ -7,11 +7,16 @@ object ShouldContainTypeProvider:
|
|||
/** The type provider for the should have assertion, which generates a refined
|
||||
* structural type for type T.
|
||||
*/
|
||||
def generateShouldContainTypeProvider[T](subject: Expr[Iterable[T]], kind: Expr[ContainsAssertionKind])(using Type[T])(using
|
||||
Quotes
|
||||
): Expr[Any] =
|
||||
def generateShouldContainTypeProvider[T](containsExpr: Expr[ContainsVerb[T]], kind: Expr[ContainsAssertionKind])
|
||||
(using Type[T], Quotes): Expr[Any] =
|
||||
import quotes.reflect.*
|
||||
|
||||
println("containsExpr: " + containsExpr.show)
|
||||
println("kind: " + kind.show)
|
||||
|
||||
val subject: Expr[Iterable[T]] = containsExpr match
|
||||
case '{ ContainsVerb.apply[T]($s: Iterable[T]) } => s
|
||||
|
||||
// hack to get the equivalent of `TypeRepr.of[Function1[?, ?]]` (i.e. an arity-1 function type constructor).
|
||||
// Getting it as is returns an applied type with useless bounds (Nothing to Any), and the returned TypeRepr,
|
||||
// if re-applied to some `I` input type and `O` output type would be incompatible with `TypeRepr.of[I => O]` for
|
||||
|
@ -19,7 +24,10 @@ object ShouldContainTypeProvider:
|
|||
def typeConstructor[U](using Type[U]): TypeRepr =
|
||||
AppliedType.unapply(TypeRepr.of[U].asInstanceOf[AppliedType])._1
|
||||
|
||||
val subjectTypeRepr = TypeRepr.of[T]
|
||||
val subjectTypeRepr = TypeRepr.of[T].widen
|
||||
|
||||
println("subjectTypeRepr: " + subjectTypeRepr)
|
||||
println("string: " + TypeRepr.of[String].simplified)
|
||||
|
||||
/** Given a refinable current type, and the symbol of a arity-0 method or a
|
||||
* field of type foo: X, generates a refinement containing a method with
|
||||
|
@ -72,12 +80,15 @@ object ShouldContainTypeProvider:
|
|||
Flags.Synthetic
|
||||
)
|
||||
|
||||
println("typeSymbol: " + subjectTypeRepr.typeSymbol)
|
||||
println("fields: " + fields)
|
||||
println("arityZeroMethods: " + arityZeroMethods)
|
||||
|
||||
val refinedType = refineTypeBySymbols(
|
||||
TypeRepr.of[ContainsPropertyTypeProvider[T]],
|
||||
fields ++ arityZeroMethods
|
||||
)
|
||||
|
||||
println(refinedType.show)
|
||||
|
||||
// This is the way to extract a (reflection) type to a
|
||||
// type that can be used in some quoted code.
|
||||
|
@ -86,6 +97,8 @@ object ShouldContainTypeProvider:
|
|||
// exact type is unknown until compilation time.
|
||||
refinedType.asType match
|
||||
case '[tpe] => // tpe is the exact refined type
|
||||
'{ ContainsPropertyTypeProvider[T]($subject, $kind).asInstanceOf[tpe] }
|
||||
val a = '{ ContainsPropertyTypeProvider[T]($subject, $kind).asInstanceOf[tpe] }
|
||||
println(a.show)
|
||||
a
|
||||
|
||||
end ShouldContainTypeProvider
|
||||
|
|
|
@ -77,8 +77,6 @@ object ShouldHaveTypeProvider:
|
|||
fields ++ arityZeroMethods
|
||||
)
|
||||
|
||||
println(refinedType.show)
|
||||
|
||||
// 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,
|
||||
|
|
Loading…
Reference in a new issue