diff --git a/src/main/scala/splatter/parsing.scala b/src/main/scala/splatter/parsing.scala index e1c20cf..766d9b2 100644 --- a/src/main/scala/splatter/parsing.scala +++ b/src/main/scala/splatter/parsing.scala @@ -105,5 +105,4 @@ object parsing: a def keyword(word: String): P[String] = - token(string(word)) - \ No newline at end of file + token(string(word)) diff --git a/src/main/scala/splatter/stutter.scala b/src/main/scala/splatter/stutter.scala index 4ba0fc8..24a0f44 100644 --- a/src/main/scala/splatter/stutter.scala +++ b/src/main/scala/splatter/stutter.scala @@ -21,12 +21,10 @@ case class Lisp(subs: Seq[Expr]) extends Expr: def isAtom: Boolean = false def isLisp: Boolean = true def isEmpty: Boolean = subs.isEmpty - override def toString: String = subs.mkString("(", " ", ")") sealed trait Extractable[T]: def extract: PartialFunction[Expr,T] - def unapply(e: Expr): Option[T] = extract.lift(e) def is(e: Expr): Boolean = unapply(e).isDefined diff --git a/src/test/scala/splatter/stutter/ExpressionAxioms.scala b/src/test/scala/splatter/stutter/ExpressionAxioms.scala index 1137e37..0ff2865 100644 --- a/src/test/scala/splatter/stutter/ExpressionAxioms.scala +++ b/src/test/scala/splatter/stutter/ExpressionAxioms.scala @@ -5,42 +5,41 @@ import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers._ class ExpressionAxioms extends AnyFunSpec: - describe("Expression axioms") { + describe("Expression axioms"): import Parser._ - it("quote expressions should yield the quoted expression") { + it("quote expressions should yield the quoted expression"): eval("(quote a)").toString should be ("a") eval("'a").toString should be ("a") eval("(quote (a b c))").toString should be ("(a b c)") - } - it("atom expressions should yield the atom `t` if the argument yields an atom or the empty list") { + + it("atom expressions should yield the atom `t` if the argument yields an atom or the empty list"): eval("(atom 'a)").toString should be ("t") eval("(atom '())").toString should be ("t") eval("(atom (atom 'a))").toString should be ("t") - } - it("atom expression should yield the empty list if the argument does not yield an atom or the empty list") { + + it("atom expression should yield the empty list if the argument does not yield an atom or the empty list"): eval("(atom '(atom a))").toString should be ("()") eval("(atom '(a b c))").toString should be ("()") - } - it("eq expression should yield the atom `t` if both arguments yield the same atom or both the empty list") { + + it("eq expression should yield the atom `t` if both arguments yield the same atom or both the empty list"): eval("(eq 'a 'a)").toString should be ("t") eval("(eq '() '())").toString should be ("t") - } - it("eq expression should yield the empty list if both arguments do not yield the same atom or the empty list") { + + it("eq expression should yield the empty list if both arguments do not yield the same atom or the empty list"): eval("(eq 'a 'b)").toString should be ("()") - } - it("car expression should yield the first element of its argument list") { + + it("car expression should yield the first element of its argument list"): eval("(car '(a b c))").toString should be ("a") - } - it("cdr expression should yield everything after the first element of its argument list") { + + it("cdr expression should yield everything after the first element of its argument list"): eval("(cdr '(a b c))").toString should be ("(b c)") - } - it("cons expression should return a list containing the value of its first argument followed by the elements of the value of its second argument") { + + it("cons expression should return a list containing the value of its first argument followed by the elements of the value of its second argument"): eval("(cons 'a '(b c))").toString should be ("(a b c)") eval("(cons 'a (cons 'b (cons 'c '())))").toString should be ("(a b c)") eval("(car (cons 'a '(b c)))").toString should be ("a") eval("(cdr (cons 'a '(b c)))").toString should be ("(b c)") - } - it("cond expression (cond (p1 e1)...(pn en)) is evaluated as follows; the p expressions are evaluated in order until one returns t; when one is found, the value of the corresponding e expression is returned as the value of the whole cond expression.") { + + it("cond expression (cond (p1 e1)...(pn en)) is evaluated as follows; the p expressions are evaluated in order until one returns t; when one is found, the value of the corresponding e expression is returned as the value of the whole cond expression."): eval("(cond ((eq 'a 'b) 'first) ((atom 'a) 'second))").toString should be ("second") - } - } + \ No newline at end of file diff --git a/src/test/scala/splatter/stutter/FunctionCallSpec.scala b/src/test/scala/splatter/stutter/FunctionCallSpec.scala index c0e187f..619e402 100644 --- a/src/test/scala/splatter/stutter/FunctionCallSpec.scala +++ b/src/test/scala/splatter/stutter/FunctionCallSpec.scala @@ -6,8 +6,8 @@ import org.scalatest.matchers.should.Matchers._ class FunctionCallSpec extends AnyFunSpec: import Parser._ - describe("A function call") { - it("works as specified in chapter 2 of roots of lisp...") { + describe("A function call"): + it("works as specified in chapter 2 of roots of lisp..."): eval( """ | ((lambda (x) (cons x '(b))) @@ -22,18 +22,18 @@ class FunctionCallSpec extends AnyFunSpec: | '(a b c)) | """.stripMargin) should be (parseLisp("(z b c)")) - } - it("treats parameters as operators in expressions as well as arguments") { + + it("treats parameters as operators in expressions as well as arguments"): eval( """ | ((lambda (f) (f '(b c))) | ’(lambda (x) (cons 'a x))) | """.stripMargin) should be (parseLisp("(a b c)")) - } - } - describe("structural expression replacement") { - it("replaces expressions recursively, though maintains structural composition") { + + + describe("structural expression replacement"): + it("replaces expressions recursively, though maintains structural composition"): replace(parseLisp("(a b (c d (e) f) (g) h)"), Map( Atom("a") -> Atom("A"), Atom("b") -> Atom("B"), @@ -44,5 +44,3 @@ class FunctionCallSpec extends AnyFunSpec: Atom("g") -> Lisp(Nil), Atom("h") -> Atom("H") )) should be (parseLisp("(A B (C (DA DB) (E) F) (()) H)")) - } - } diff --git a/src/test/scala/splatter/stutter/ParserSpec.scala b/src/test/scala/splatter/stutter/ParserSpec.scala index aecdd1f..89ad3c3 100644 --- a/src/test/scala/splatter/stutter/ParserSpec.scala +++ b/src/test/scala/splatter/stutter/ParserSpec.scala @@ -6,12 +6,12 @@ import org.scalatest.matchers.should.Matchers._ class ParserSpec extends AnyFunSpec: import Parser._ - describe("Parser") { - it("should parse atoms") { + describe("Parser"): + it("should parse atoms"): parseLisp("foo") should be( Atom("foo")) - } - it("should parse lists") { + + it("should parse lists"): parseLisp("()") should be( Lisp(Nil)) parseLisp("(foo)") should be( @@ -20,18 +20,18 @@ class ParserSpec extends AnyFunSpec: Lisp(Seq(Atom("foo"), Atom("bar")))) parseLisp("(a b (c) d)") should be( Lisp(Seq(Atom("a"), Atom("b"), Lisp(Seq(Atom("c"))), Atom("d")))) - } - it("should parse normal single ' quotes on atoms and lists") { + + it("should parse normal single ' quotes on atoms and lists"): parseLisp("'a") should be( Lisp(Seq(Atom("quote"), Atom("a")))) parseLisp("'(a b c)") should be( Lisp(Seq(Atom("quote"), Lisp(Seq(Atom("a"), Atom("b"), Atom("c")))))) - } - it("should parse ugly, copy-paste, english, burn your fingers, sharp ’ quotes") { + + it("should parse ugly, copy-paste, english, burn your fingers, sharp ’ quotes"): parseLisp("’a") should be( Lisp(Seq(Atom("quote"), Atom("a")))) - } - it("should parse complex structures") { + + it("should parse complex structures"): parseLisp("((lambda (x) (cons x '(b))) 'a)") should be( Lisp(Seq( Lisp(Seq( @@ -50,8 +50,8 @@ class ParserSpec extends AnyFunSpec: Lisp(Seq(Atom("quote"), Atom("a"))) )) ) - } - it("should handle complex whitespace") { + + it("should handle complex whitespace"): parseLisp("( a )") should be (parseLisp("(a)")) parseLisp( """'( @@ -59,11 +59,8 @@ class ParserSpec extends AnyFunSpec: | b | c |)""".stripMargin) should be(parseLisp("'(a b c)")) - } - + /* Sanity Checks */ - it("should parse lists recursively") { + it("should parse lists recursively"): parseLisp("(())") should be(Lisp(Seq(Lisp(Nil)))) - } - } diff --git a/src/test/scala/splatter/stutter/PrimitiveExpressions.scala b/src/test/scala/splatter/stutter/PrimitiveExpressions.scala index 7162628..2d8563a 100644 --- a/src/test/scala/splatter/stutter/PrimitiveExpressions.scala +++ b/src/test/scala/splatter/stutter/PrimitiveExpressions.scala @@ -6,8 +6,8 @@ import org.scalatest.matchers.should.Matchers._ class PrimitiveExpressions extends AnyFunSpec: import Parser._ - describe("Primitive expressions") { - it("should be differentiable") { + describe("Primitive expressions"): + it("should be differentiable"): Seq( parseLisp("(atom foo)"), parseLisp("(quote bar)"), @@ -30,54 +30,52 @@ class PrimitiveExpressions extends AnyFunSpec: } ) should be (e.asInstanceOf[Lisp].subs.head.asInstanceOf[Atom].value) ) - } - it("quote expressions should be constructable") { + + it("quote expressions should be constructable"): Lisp(Seq(QuoteLit.Op, Atom("foo"))) should be (parseLisp("(quote foo)")) - } - it("quote expressions should be extractable") { + + it("quote expressions should be extractable"): ((parseLisp("(quote foo)") : @unchecked) match { case Lisp(Seq(QuoteLit.Op, foo)) => foo }) should be (parseLisp("foo")) ((parseLisp("(quote foo)") : @unchecked) match { case QuoteLit(foo) => foo }) should be (parseLisp("foo")) - } - it("atom expressions should be constructable") { + + it("atom expressions should be constructable"): Lisp(Seq(AtomLit.Op, Atom("foo"))) should be (parseLisp("(atom foo)")) - } - it("atom expressions should be extractable") { + + it("atom expressions should be extractable"): ((parseLisp("(atom foo)") : @unchecked) match { case Lisp(Seq(AtomLit.Op, foo)) => foo }) should be (parseLisp("foo")) ((parseLisp("(atom foo)") : @unchecked) match { case AtomLit(foo) => foo }) should be (parseLisp("foo")) - } - it("eq expressions should be constructable") { + + it("eq expressions should be constructable"): Lisp(Seq(EqLit.Op, Atom("foo"), Atom("bar"))) should be (parseLisp("(eq foo bar)")) - } - it("eq expressions should be extractable") { + + it("eq expressions should be extractable"): ((parseLisp("(eq foo bar)") : @unchecked) match { case Lisp(Seq(EqLit.Op, foo, bar)) => (foo,bar) }) should be ((parseLisp("foo"), parseLisp("bar"))) ((parseLisp("(eq foo bar)") : @unchecked) match { case EqLit(foo, bar) => (foo,bar) }) should be ((parseLisp("foo"), parseLisp("bar"))) - } - it("car expressions should be constructable") { + + it("car expressions should be constructable"): Lisp(Seq(CarLit.Op, Lisp(Seq(Atom("foo"), Atom("bar"))))) should be (parseLisp("(car (foo bar))")) - } - it("car expressions should be extractable") { + + it("car expressions should be extractable"): ((parseLisp("(car (foo bar))") : @unchecked) match { case Lisp(Seq(CarLit.Op, arg)) => arg }) should be (parseLisp("(foo bar)")) ((parseLisp("(car (foo bar))") : @unchecked) match { case CarLit(arg) => arg }) should be (parseLisp("(foo bar)")) - } - it("cdr expressions should be constructable") { + + it("cdr expressions should be constructable"): Lisp(Seq(CdrLit.Op, (Lisp(Seq(Atom("foo"), Atom("bar")))))) should be (parseLisp("(cdr (foo bar))")) - } - it("cdr expressions should be extractable") { + + it("cdr expressions should be extractable"): ((parseLisp("(cdr (foo bar))") : @unchecked) match { case Lisp(Seq(CdrLit.Op, arg)) => arg }) should be (parseLisp("(foo bar)")) ((parseLisp("(cdr (foo bar))") : @unchecked) match { case CdrLit(arg) => arg }) should be (parseLisp("(foo bar)")) - } - it("cons expressions should be constructable") { + + it("cons expressions should be constructable"): Lisp(Seq(ConsLit.Op, Lisp(Seq(Atom("foo"))), Lisp(Seq(Atom("bar"))))) should be (parseLisp("(cons (foo) (bar))")) - } - it("cons expressions should be extractable") { + + it("cons expressions should be extractable"): ((parseLisp("(cons (foo) (bar))") : @unchecked) match { case Lisp(Seq(ConsLit.Op, l1, l2)) => (l1, l2) }) should be ((parseLisp("(foo)"), parseLisp("(bar)"))) ((parseLisp("(cons (foo) (bar))") : @unchecked) match { case ConsLit(l1, l2) => (l1, l2) }) should be ((parseLisp("(foo)"), parseLisp("(bar)"))) - } - it("cond expressions should be constructable") { + + it("cond expressions should be constructable"): Lisp(Seq(CondLit.Op, Lisp(Seq(Atom("foo"))), Lisp(Seq(Atom("bar"))))) should be (parseLisp("(cond (foo) (bar))")) - } - it("cond expressions should be extractable") { + + it("cond expressions should be extractable"): ((parseLisp("(cond (foo) (bar))") : @unchecked) match { case Lisp(Seq(CondLit.Op, l1, l2)) => (l1, l2) }) should be ((parseLisp("(foo)"), parseLisp("(bar)"))) ((parseLisp("(cond (foo) (bar))") : @unchecked) match { case CondLit(Seq(l1, l2)) => (l1, l2) }) should be ((parseLisp("(foo)"), parseLisp("(bar)"))) - } - }