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.*
|
import quotes.reflect.*
|
||||||
|
|
||||||
subject match {
|
subject match {
|
||||||
case '{ $subject: Iterable[Any] } => '{ ContainsVerb($subject) }
|
case '{ $subject: Iterable[elementType] } => '{ ContainsVerb($subject) }
|
||||||
case _ => report.errorAndAbort(
|
case _ => report.errorAndAbort(
|
||||||
"assertion subject must be an Iterable[?] in order to use a 'contain' assertion",
|
"assertion subject must be an Iterable[?] in order to use a 'contain' assertion",
|
||||||
subject
|
subject
|
||||||
|
@ -45,6 +45,13 @@ object GenerativeFluentAssertionsDSL:
|
||||||
.applyDynamic($propertyNameExpr)(satisfying.notEquals[T]($obj, $ord))
|
.applyDynamic($propertyNameExpr)(satisfying.notEquals[T]($obj, $ord))
|
||||||
.$asInstanceOf$[AssertionProperty]
|
.$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
|
/** Entrypoint. This macro method takes an expression composed of
|
||||||
|
@ -124,7 +131,6 @@ object GenerativeFluentAssertionsDSL:
|
||||||
.applyDynamic($propertyNameExpr: String)($valueExpr: Condition[valueType])
|
.applyDynamic($propertyNameExpr: String)($valueExpr: Condition[valueType])
|
||||||
.$asInstanceOf$[AssertionProperty]
|
.$asInstanceOf$[AssertionProperty]
|
||||||
} =>
|
} =>
|
||||||
println(assertionExpr.show)
|
|
||||||
val subjectExpr = '{ $typeProvider.subject }
|
val subjectExpr = '{ $typeProvider.subject }
|
||||||
val negatedExpr = '{ $typeProvider.negated }
|
val negatedExpr = '{ $typeProvider.negated }
|
||||||
generateShouldHaveAssertion[subjectType, valueType](
|
generateShouldHaveAssertion[subjectType, valueType](
|
||||||
|
@ -145,7 +151,14 @@ object GenerativeFluentAssertionsDSL:
|
||||||
.$asInstanceOf$[AssertionProperty]
|
.$asInstanceOf$[AssertionProperty]
|
||||||
} =>
|
} =>
|
||||||
'{} // placeholder
|
'{} // placeholder
|
||||||
case _ =>
|
case '{
|
||||||
|
(${ typeProvider }: ContainsPropertyTypeProvider[subjectType])
|
||||||
|
.applyDynamic($propertyNameExpr: String)($valueExpr: Condition[valueType])
|
||||||
|
.$asInstanceOf$[AssertionProperty]
|
||||||
|
} =>
|
||||||
|
'{} // placeholder
|
||||||
|
case a =>
|
||||||
|
println(a.show)
|
||||||
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. ",
|
||||||
assertionExpr
|
assertionExpr
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package ch.usi.si.msde.edsl.lecture10
|
package ch.usi.si.msde.edsl.lecture10
|
||||||
|
|
||||||
import scala.language.dynamics
|
import scala.language.dynamics
|
||||||
|
import scala.quoted.Expr
|
||||||
|
|
||||||
sealed trait AssertionProperty
|
sealed trait AssertionProperty
|
||||||
case class NegatedAssertionProperty(prop: AssertionProperty) extends 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 contain extends AssertionDSLVerb
|
||||||
case object `with` extends AssertionDSLVerb
|
case object `with` extends AssertionDSLVerb
|
||||||
|
|
||||||
case class ContainsVerb[T](subject: Iterable[T]):
|
case class ContainsVerb[T](subject: Iterable[T])
|
||||||
transparent inline def allElements(verb: `with`.type) = ${
|
|
||||||
ShouldContainTypeProvider.generateShouldContainTypeProvider[T]('subject, 'All)
|
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) = ${
|
transparent inline def someElements(inline verb: `with`.type) = ${
|
||||||
ShouldContainTypeProvider.generateShouldContainTypeProvider[T]('subject, 'Some)
|
ShouldContainTypeProvider.generateShouldContainTypeProvider[T]('containsVerb, 'Some)
|
||||||
}
|
}
|
||||||
|
|
||||||
transparent inline def noElements(verb: `with`.type) = ${
|
transparent inline def noElements(inline verb: `with`.type) = ${
|
||||||
ShouldContainTypeProvider.generateShouldContainTypeProvider[T]('subject, 'None)
|
ShouldContainTypeProvider.generateShouldContainTypeProvider[T]('containsVerb, 'None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Implicit class to add 'should' assertions to a generic type T.
|
/** 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)
|
extension [T](subject: T)
|
||||||
def should(property: AssertionProperty) = ???
|
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) = ${
|
transparent inline def should(inline verbWord: be_.type) = ${
|
||||||
ShouldBeTypeProvider.generateShouldBeTypeProvider[T]('subject)
|
ShouldBeTypeProvider.generateShouldBeTypeProvider[T]('subject)
|
||||||
}
|
}
|
||||||
|
@ -109,7 +119,13 @@ extension [T](subject: T)
|
||||||
transparent inline def should(inline verb: HaveVerb) = ${
|
transparent inline def should(inline verb: HaveVerb) = ${
|
||||||
ShouldHaveTypeProvider.generateShouldHaveTypeProvider[T]('subject, '{verb.negated})
|
ShouldHaveTypeProvider.generateShouldHaveTypeProvider[T]('subject, '{verb.negated})
|
||||||
}
|
}
|
||||||
|
|
||||||
transparent inline def should(inline verb: contain.type) = ${
|
// We define here the 'should contain' extension method. It is not possible to
|
||||||
GenerativeFluentAssertionsDSL.shouldContainImpl('subject)
|
// 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 scala.language.postfixOps
|
||||||
|
|
||||||
import squants.mass.MassConversions.MassConversions
|
import ch.usi.si.msde.edsl.lecture10.GenerativeFluentAssertionsDSL.*
|
||||||
import squants.market.MoneyConversions.MoneyConversions
|
|
||||||
import GenerativeFluentAssertionsDSL.*
|
|
||||||
import squants.mass.Mass
|
|
||||||
import squants.market.Money
|
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)
|
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
|
List(50, 2, 3) should have head satisfying < 100
|
||||||
box should !(!have) weight satisfying <= 300.0.kg
|
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 */
|
/* should have assertions */
|
||||||
// // assert(List(1).head == 1, ...)
|
// // assert(List(1).head == 1, ...)
|
||||||
// List(1) should have head 1
|
// List(1) should have head 1
|
||||||
|
|
|
@ -53,8 +53,6 @@ object ShouldBeTypeProvider:
|
||||||
members
|
members
|
||||||
)
|
)
|
||||||
|
|
||||||
// println(refinedType)
|
|
||||||
|
|
||||||
// This is the way to extract a (reflection) type to a
|
// This is the way to extract a (reflection) type to a
|
||||||
// type that can be used in some quoted code.
|
// type that can be used in some quoted code.
|
||||||
// it's the equivalent of .asExpr for expressions,
|
// it's the equivalent of .asExpr for expressions,
|
||||||
|
@ -66,7 +64,6 @@ object ShouldBeTypeProvider:
|
||||||
val p = BePropertyTypeProvider[T]($subject)
|
val p = BePropertyTypeProvider[T]($subject)
|
||||||
p.asInstanceOf[tpe]
|
p.asInstanceOf[tpe]
|
||||||
}
|
}
|
||||||
println(res.show)
|
|
||||||
res
|
res
|
||||||
|
|
||||||
end ShouldBeTypeProvider
|
end ShouldBeTypeProvider
|
||||||
|
|
|
@ -7,11 +7,16 @@ object ShouldContainTypeProvider:
|
||||||
/** The type provider for the should have assertion, which generates a refined
|
/** The type provider for the should have assertion, which generates a refined
|
||||||
* structural type for type T.
|
* structural type for type T.
|
||||||
*/
|
*/
|
||||||
def generateShouldContainTypeProvider[T](subject: Expr[Iterable[T]], kind: Expr[ContainsAssertionKind])(using Type[T])(using
|
def generateShouldContainTypeProvider[T](containsExpr: Expr[ContainsVerb[T]], kind: Expr[ContainsAssertionKind])
|
||||||
Quotes
|
(using Type[T], Quotes): Expr[Any] =
|
||||||
): Expr[Any] =
|
|
||||||
import quotes.reflect.*
|
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).
|
// 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,
|
// 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
|
// 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 =
|
def typeConstructor[U](using Type[U]): TypeRepr =
|
||||||
AppliedType.unapply(TypeRepr.of[U].asInstanceOf[AppliedType])._1
|
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
|
/** 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
|
* field of type foo: X, generates a refinement containing a method with
|
||||||
|
@ -72,12 +80,15 @@ object ShouldContainTypeProvider:
|
||||||
Flags.Synthetic
|
Flags.Synthetic
|
||||||
)
|
)
|
||||||
|
|
||||||
|
println("typeSymbol: " + subjectTypeRepr.typeSymbol)
|
||||||
|
println("fields: " + fields)
|
||||||
|
println("arityZeroMethods: " + arityZeroMethods)
|
||||||
|
|
||||||
val refinedType = refineTypeBySymbols(
|
val refinedType = refineTypeBySymbols(
|
||||||
TypeRepr.of[ContainsPropertyTypeProvider[T]],
|
TypeRepr.of[ContainsPropertyTypeProvider[T]],
|
||||||
fields ++ arityZeroMethods
|
fields ++ arityZeroMethods
|
||||||
)
|
)
|
||||||
|
|
||||||
println(refinedType.show)
|
|
||||||
|
|
||||||
// This is the way to extract a (reflection) type to a
|
// This is the way to extract a (reflection) type to a
|
||||||
// type that can be used in some quoted code.
|
// type that can be used in some quoted code.
|
||||||
|
@ -86,6 +97,8 @@ object ShouldContainTypeProvider:
|
||||||
// exact type is unknown until compilation time.
|
// exact type is unknown until compilation time.
|
||||||
refinedType.asType match
|
refinedType.asType match
|
||||||
case '[tpe] => // tpe is the exact refined type
|
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
|
end ShouldContainTypeProvider
|
||||||
|
|
|
@ -77,8 +77,6 @@ object ShouldHaveTypeProvider:
|
||||||
fields ++ arityZeroMethods
|
fields ++ arityZeroMethods
|
||||||
)
|
)
|
||||||
|
|
||||||
println(refinedType.show)
|
|
||||||
|
|
||||||
// This is the way to extract a (reflection) type to a
|
// This is the way to extract a (reflection) type to a
|
||||||
// type that can be used in some quoted code.
|
// type that can be used in some quoted code.
|
||||||
// it's the equivalent of .asExpr for expressions,
|
// it's the equivalent of .asExpr for expressions,
|
||||||
|
|
Loading…
Reference in a new issue