From 113e8ffd912121f85de6b5e10561d2b291258b16 Mon Sep 17 00:00:00 2001 From: nmcb Date: Sat, 28 Mar 2026 09:00:30 +0100 Subject: [PATCH 1/2] scala 3 indentation style --- src/main/scala/splatter/parsing.scala | 190 ++++++++++++++------------ src/main/scala/splatter/stutter.scala | 48 ++++--- 2 files changed, 132 insertions(+), 106 deletions(-) diff --git a/src/main/scala/splatter/parsing.scala b/src/main/scala/splatter/parsing.scala index e393d18..e1c20cf 100644 --- a/src/main/scala/splatter/parsing.scala +++ b/src/main/scala/splatter/parsing.scala @@ -1,93 +1,109 @@ package splatter -package parsing -case class P[+A](parse: String => Option[(A,String)]) { +import scala.annotation.tailrec - import P._ - - def flatMap[B](f: A => P[B]): P[B] = - P(s => parse(s) match { - case Some((a,r)) => f(a).parse(r) - case None => None - }) - - def map[B](f: A => B): P[B] = - P(s => parse(s) match { - case Some((a,r)) => Some(f(a),r) - case None => None - }) - - def |[A1 >: A](that: => P[A1]): P[A1] = - P(s => parse(s) match { - case None => that.parse(s) - case res@Some(_) => res - }) - - def ~[B](that: P[B]): P[B] = - for { _ <- this ; b <- that } yield b - - private def loop[A1 >: A](s: String, acc: List[A1] = List.empty): (List[A1], String) = - parse(s) match { - case None => (acc.reverse, s) - case Some((a,ss)) => loop(ss, a :: acc) - } - - def zeroOrMore: P[List[A]] = - P(s => Some(loop(s))) - - def oneOrMore: P[List[A]] = { - P(s => parse(s).flatMap { case (a,ss) => Some(loop(ss, List(a)))}) - } - - def iff[B](p: A => Boolean)(t: P[B])(f: => P[B]): P[B] = - P(s => parse(s).flatMap { case (a,ss) => if (p(a)) t.parse(ss) else f.parse(ss)}) -} - -object P { - - def run[A](p: P[A])(s: String): A = - p.parse(s) match { - case Some((a, "")) => a - case Some((_, rs)) => sys.error(s"unconsumed: $rs") - case None => sys.error(s"did not produce a value: ${s}") - } - - def unit[A](a: A): P[A] = - P(s => Some(a, s)) +object parsing: - def take: P[Char] = - P(s => if (s.nonEmpty) Some(s.head, s.tail) else None) - - def fail[A]: P[A] = - P(_ => None) - - def satisfy(p: Char => Boolean): P[Char] = - take.flatMap(c => if (p(c)) unit(c) else fail) - - def end: P[Boolean] = - P(s => if (s.isEmpty) Some(true, "") else Some(false, s)) - - def digit: P[Char] = - satisfy(_.isDigit) + case class P[+A](parse: String => Option[(A,String)]): + + def flatMap[B](f: A => P[B]): P[B] = + P(s => + parse(s) match + case Some((a, r)) => f(a).parse(r) + case None => None + ) + + def map[B](f: A => B): P[B] = + P(s => + parse(s) match + case Some((a, r)) => Some(f(a),r) + case None => None + ) + + def |[B >: A](that: => P[B]): P[B] = + P(s => + parse(s) match + case None => that.parse(s) + case res@Some(_) => res + ) + + def ~[B](that: P[B]): P[B] = + for + _ <- this + b <- that + yield + b + + @tailrec + private def loop[B >: A](s: String, acc: List[B] = List.empty): (List[B], String) = + parse(s) match + case None => (acc.reverse, s) + case Some((a, ss)) => loop(ss, a :: acc) - def digits: P[Long] = - satisfy(_.isDigit).oneOrMore.map(_.mkString("").toLong) + def zeroOrMore: P[List[A]] = + P(s => Some(loop(s))) + + def oneOrMore: P[List[A]] = + P(s => parse(s).flatMap((a, ss) => Some(loop(ss, List(a))))) + + def iff[B](p: A => Boolean)(t: P[B])(f: => P[B]): P[B] = + P(s => parse(s).flatMap((a, ss) => if (p(a)) t.parse(ss) else f.parse(ss))) + + object P: + + def run[A](p: P[A])(s: String): A = + p.parse(s) match + case Some((a, "")) => a + case Some((_, rs)) => sys.error(s"unconsumed: $rs") + case None => sys.error(s"did not produce a value: $s") + + private def unit[A](a: A): P[A] = + P(s => Some(a, s)) - def char(c: Char): P[Char] = - satisfy(_ == c) - - def string(s: String): P[String] = - if (s.isEmpty) unit("") else for { _ <- char(s.head) ; _ <- string(s.tail) } yield s - - def oneOf(s: String): P[Char] = - satisfy(s.contains(_)) - - def spaces: P[String] = - oneOf(" \t\n\r").zeroOrMore.map(_.mkString) - - def token[A](p: P[A]): P[A] = - for { a <- p ; _ <- spaces } yield a - - def keyword(word: String): P[String] = - token(string(word)) -} \ No newline at end of file + private def take: P[Char] = + P(s => if (s.nonEmpty) Some(s.head, s.tail) else None) + + private def fail[A]: P[A] = + P(_ => None) + + def satisfy(p: Char => Boolean): P[Char] = + take.flatMap(c => if (p(c)) unit(c) else fail) + + def end: P[Boolean] = + P(s => if (s.isEmpty) Some(true, "") else Some(false, s)) + + def digit: P[Char] = + satisfy(_.isDigit) + + def digits: P[Long] = + satisfy(_.isDigit).oneOrMore.map(_.mkString("").toLong) + + private def char(c: Char): P[Char] = + satisfy(_ == c) + + def string(s: String): P[String] = + if s.isEmpty then + unit("") + else + for + _ <- char(s.head) + _ <- string(s.tail) + yield + s + + private def oneOf(s: String): P[Char] = + satisfy(s.contains(_)) + + def spaces: P[String] = + oneOf(" \t\n\r").zeroOrMore.map(_.mkString) + + private def token[A](p: P[A]): P[A] = + for + a <- p + _ <- spaces + yield + a + + def keyword(word: String): P[String] = + token(string(word)) + \ No newline at end of file diff --git a/src/main/scala/splatter/stutter.scala b/src/main/scala/splatter/stutter.scala index 661f89f..4ba0fc8 100644 --- a/src/main/scala/splatter/stutter.scala +++ b/src/main/scala/splatter/stutter.scala @@ -34,13 +34,19 @@ abstract class ExtractableOp[T](name: String) extends Extractable[T]: val Op: Atom = Atom(name) case class ExtractableOp1(name: String) extends ExtractableOp[Expr](name): - def extract = { case Lisp(Seq(Op, arg)) => arg } + def extract: PartialFunction[Expr, Expr] = { + case Lisp(Seq(Op, arg)) => arg + } case class ExtractableOp2(name: String) extends ExtractableOp[(Expr,Expr)](name): - def extract = { case Lisp(Seq(Op, a, b)) => (a, b) } + def extract: PartialFunction[Expr, (Expr, Expr)] = { + case Lisp(Seq(Op, a, b)) => (a, b) + } case class ExtractableOpN(name: String) extends ExtractableOp[Seq[Expr]](name): - def extract = { case Lisp(Op +: args) => args } + def extract: PartialFunction[Expr, Seq[Expr]] = { + case Lisp(Op +: args) => args + } lazy val QuoteLit = ExtractableOp1("quote") lazy val AtomLit = ExtractableOp1("atom") @@ -55,11 +61,15 @@ val PrimitiveOps: Seq[Atom] = // (lambda (p1 ... pn) e) object Lambda extends ExtractableOp[(Seq[Expr],Expr)]("lambda"): - def extract = { case Lisp(Seq(Op, Lisp(parms), expr)) => (parms, expr) } + def extract: PartialFunction[Expr, (Seq[Expr], Expr)] = { + case Lisp(Seq(Op, Lisp(parms), expr)) => (parms, expr) + } // (quote (lambda (p1 ... pn) e)) object QuotedLambda extends ExtractableOp[(Seq[Expr],Expr)]("quote"): - def extract = { case Lisp(Seq(Op, Lambda(parms, expr))) => (parms, expr) } + def extract: PartialFunction[Expr, (Seq[Expr], Expr)] = { + case Lisp(Seq(Op, Lambda(parms, expr))) => (parms, expr) + } // ((lambda (p1 ... pn) e) a1 ... an) object Function extends Extractable[(Seq[Expr],Expr,Seq[Expr])]: @@ -72,9 +82,8 @@ object Function extends Extractable[(Seq[Expr],Expr,Seq[Expr])]: object QuotedFunction extends Extractable[(Seq[Expr],Atom,Seq[Expr],Seq[Expr])]: def extract: PartialFunction[Expr, (Seq[Expr], Atom, Seq[Expr], Seq[Expr])] = { case Lisp(Lambda(parms, Lisp((op : Atom) +: fargs)) +: args) - if !PrimitiveOps.contains(op) && - args.nonEmpty && - QuotedLambda.is(args.head) => (parms, op, fargs, args) + if !PrimitiveOps.contains(op) && args.nonEmpty && QuotedLambda.is(args.head) => + (parms, op, fargs, args) } def eval(e: Expr): Expr = @@ -91,10 +100,9 @@ def eval(e: Expr): Expr = // - eval all args except for quoted ones // - replace the expression parms with the evaluated args case Function(parms, expr, args) => - val evaluated = args.map { + val evaluated = args.map: case q @ QuoteLit(_) => q case e : Expr => eval(e) - } val replaced = replace(expr, parms.zip(evaluated).toMap) eval(replaced) @@ -135,11 +143,11 @@ def eval(s: String): Expr = eval(Parser.parseLisp(s)) def replace(expr: Expr, parms: Map[Expr, Expr]): Expr = - Lisp(expr.subs.map({ // TODO clearly Lisp needs a `map`. + Lisp(expr.subs.map: case a: Atom if parms.keySet.contains(a) => parms(a) case a: Atom => a case r: Lisp => replace(r, parms) - })) + ) object Parser: @@ -149,22 +157,24 @@ object Parser: def atom: P[Atom] = satisfy(c => c.isLetter).oneOrMore.map(cs => Atom(cs.mkString(""))) - def list: P[Lisp] = - for { + private def list: P[Lisp] = + for _ <- keyword("(") l <- expr.zeroOrMore.map(es => Lisp(es)) _ <- keyword(")") - } yield l + yield + l def quote: P[Lisp] = (keyword("'") | keyword("’")) ~ expr.map(e => Lisp(Seq(QuoteLit.Op, e))) - def expr: P[Expr] = - for { + private def expr: P[Expr] = + for _ <- spaces - e <- (atom | list | quote) + e <- atom | list | quote _ <- spaces - } yield e + yield + e def parseLisp(s: String): Expr = run(expr)(s) From a3640c31e4560db9d05051313446de46cc383b57 Mon Sep 17 00:00:00 2001 From: nmcb Date: Sun, 29 Mar 2026 17:31:28 +0200 Subject: [PATCH 2/2] scala 3 tests indentation style --- src/main/scala/splatter/parsing.scala | 3 +- src/main/scala/splatter/stutter.scala | 2 - .../splatter/stutter/ExpressionAxioms.scala | 39 ++++++------ .../splatter/stutter/FunctionCallSpec.scala | 18 +++--- .../scala/splatter/stutter/ParserSpec.scala | 31 +++++----- .../stutter/PrimitiveExpressions.scala | 62 +++++++++---------- 6 files changed, 72 insertions(+), 83 deletions(-) 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)"))) - } - }