From 0b9bff5ba81d3278bfb6e5e93d17dd87be982801 Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Sun, 22 Dec 2024 22:35:13 +0100 Subject: [PATCH] ready for submission --- README.md | 24 +++++++++- .../GenerativeFluentAssertionsDSL.scala | 8 +--- .../GenerativeFluentAssertionsDSLSyntax.scala | 6 +-- .../ch/usi/si/msde/edsl/lecture10/Main.scala | 46 ++++++++----------- .../edsl/lecture10/ShouldBeTypeProvider.scala | 4 +- 5 files changed, 46 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 2143d2b..ef9e61e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,23 @@ -## Lecture 12 +# Assignment 03 -Code for Lecture 12. \ No newline at end of file +Student: Claudio Maggioni + +## Use of AI assistants / LLMs + +I declare that I used the following AI assistants / LLMs (put an X where needed): + +- [X] ChatGPT (GPT-4o mini) + +Briefly, how helpful was the AI assistant for this assignment? + +- assistance on how to pattern match complex quoted expressions for 'should have' and 'should contain' assertions +- assistance on how to build an AST for a lambda expression + +## Exercises Completed + +Please Mark the exercises that you chose/completed: + +- [X] Exercise 1 +- [X] Exercise 2 +- [X] Exercise 3 +- [X] Exercise 4 (with bonus) \ No newline at end of file diff --git a/src/main/scala/ch/usi/si/msde/edsl/lecture10/GenerativeFluentAssertionsDSL.scala b/src/main/scala/ch/usi/si/msde/edsl/lecture10/GenerativeFluentAssertionsDSL.scala index 2c1d9d0..005eeb6 100644 --- a/src/main/scala/ch/usi/si/msde/edsl/lecture10/GenerativeFluentAssertionsDSL.scala +++ b/src/main/scala/ch/usi/si/msde/edsl/lecture10/GenerativeFluentAssertionsDSL.scala @@ -154,16 +154,10 @@ object GenerativeFluentAssertionsDSL: ) case '{ (${ typeProvider }: BePropertyTypeProvider[subjectType]) - .applyDynamic($propertyNameExpr)($unit) + .applyDynamic($propertyNameExpr)($end) .$asInstanceOf$[AssertionProperty] } => '{} // placeholder - case '{ - (${ typeProvider }: BePropertyTypeProvider[subjectType]) - .selectDynamic($propertyNameExpr) - .$asInstanceOf$[AssertionProperty] - } => - '{} // placeholder case a => report.errorAndAbort( "Invalid expression, must be a 'should be' or 'should have' assertion. ", diff --git a/src/main/scala/ch/usi/si/msde/edsl/lecture10/GenerativeFluentAssertionsDSLSyntax.scala b/src/main/scala/ch/usi/si/msde/edsl/lecture10/GenerativeFluentAssertionsDSLSyntax.scala index d3b492c..ba7e421 100644 --- a/src/main/scala/ch/usi/si/msde/edsl/lecture10/GenerativeFluentAssertionsDSLSyntax.scala +++ b/src/main/scala/ch/usi/si/msde/edsl/lecture10/GenerativeFluentAssertionsDSLSyntax.scala @@ -14,11 +14,6 @@ class DynamicShouldBeProperty extends AssertionProperty with Dynamic: def applyDynamic(fieldName: String)(foo: Any*) = ??? end DynamicShouldBeProperty -/** - * Due to operator precedence, '!==' is the only comparison operator that is considered an "assignment operation" - * and thus has lower precedence than alphanumeric operators. Therefore, we must handle "!==" as the final link of - * a method chain instead of a predicate builder on `satisfying`, like the other operators. - */ sealed trait SatisfyingNotEquals[T] case class Condition[-T](predicate: T => Boolean, op: String, expected: Any): @@ -89,6 +84,7 @@ case object have extends AssertionDSLVerb: case object contain extends AssertionDSLVerb case object `with` extends AssertionDSLVerb +case object `!!` extends AssertionDSLVerb case class ContainsVerb[T](subject: Iterable[T]) diff --git a/src/main/scala/ch/usi/si/msde/edsl/lecture10/Main.scala b/src/main/scala/ch/usi/si/msde/edsl/lecture10/Main.scala index 44bd9dc..4de2212 100644 --- a/src/main/scala/ch/usi/si/msde/edsl/lecture10/Main.scala +++ b/src/main/scala/ch/usi/si/msde/edsl/lecture10/Main.scala @@ -17,6 +17,7 @@ case class Complex(re: Double, im: Double): case class Person(firstName: String, lastName: String, age: Int): def adult: Boolean = age >= 18 + def minor: Boolean = !adult case class Box(label: String, weight: Mass, price: Money) @@ -25,28 +26,30 @@ case class Box(label: String, weight: Mass, price: Money) val box = Box("aBox", 30.kg, 10.CHF) assertions: - /* be.property assertions */ - // assert(person.adult, ...) - person should !(!be.adult) - // assert(List().isEmpty, ...) - List() should be.empty - // assert(List(1,2,3).nonEmpty) - List(1, 2, 3) should be.nonEmpty - - person should have age satisfying >= 10 - - // New syntax for `be` with type provider - // either adult is a method with a dummy Unit parameter - person should be_ adult() - // or adult is a property but braces are needed to resolve the type provider - (person should be_).adult + // Exercise 1 solution: + // It is possible to implement a DSL for 'should be' assertions that uses a type provider like the 'should have' + // assertion. However, a slight change to the syntax is needed in order to maintain the 'subject [method value]+' + // pattern that DSL statements should maintain in order to be parsed correctly by the Scala parser. To do this, + // we define an extra token named '!!' as a case object that should be appended at the end of the assertion. This + // object will serve as a dummy parameter to the method called on the type provider to allow the assertion + // statement to be parsed correctly. + // (note that the verb is 'be_' instead of 'be' to avoid a clash with the original implementation) + person should be_ adult !! + // Exercise 2 List(1) should have head satisfying === 1 box should have weight satisfying === 30.kg List() should have size satisfying >= 0 List(3) should have size satisfying !== 0 List(50, 2, 3) should have head satisfying < 100 + person should have age satisfying >= 10 + + // Exercise 3 box should !(!have) weight satisfying <= 300.0.kg + person should !(!be.adult) + person should !be.minor + + // Exercise 4 // we wrap the strings in StringOps instances in order to resolve the scala method "size". This would otherwise // not happen automatically as the string types would resolve to "java.lang.String" thus not allowing the type @@ -58,17 +61,8 @@ case class Box(label: String, weight: Mass, price: Money) new StringOps(""), new StringOps("alice") ) should contain someElements `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 -// // assert(box.weight == 30.kg, ...) -// box should have weight 30.kg -// // assert(person.age == 0x29, ...) -// person should have age 0x29 -// List(2, 3) should have tail List(3) -// 3.i + 1 should have re 1.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 // this assertion fails end assertionsExample diff --git a/src/main/scala/ch/usi/si/msde/edsl/lecture10/ShouldBeTypeProvider.scala b/src/main/scala/ch/usi/si/msde/edsl/lecture10/ShouldBeTypeProvider.scala index 4f2f379..550260c 100644 --- a/src/main/scala/ch/usi/si/msde/edsl/lecture10/ShouldBeTypeProvider.scala +++ b/src/main/scala/ch/usi/si/msde/edsl/lecture10/ShouldBeTypeProvider.scala @@ -16,8 +16,8 @@ object ShouldBeTypeProvider: ): TypeRepr = // refine both as unary method with unit parameter and as property // and let client code choose - val methodType = MethodType(List("unit"))( - _ => List(TypeRepr.of[Unit]), + val methodType = MethodType(List("end"))( + _ => List(TypeRepr.of[`!!`.type]), _ => TypeRepr.of[AssertionProperty] ) Refinement(