Skip to content
Merged

style #105

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/main/scala/splatter/parsing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -105,5 +105,4 @@ object parsing:
a

def keyword(word: String): P[String] =
token(string(word))

token(string(word))
2 changes: 0 additions & 2 deletions src/main/scala/splatter/stutter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
39 changes: 19 additions & 20 deletions src/test/scala/splatter/stutter/ExpressionAxioms.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}

18 changes: 8 additions & 10 deletions src/test/scala/splatter/stutter/FunctionCallSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
Expand All @@ -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"),
Expand All @@ -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)"))
}
}
31 changes: 14 additions & 17 deletions src/test/scala/splatter/stutter/ParserSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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(
Expand All @@ -50,20 +50,17 @@ 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(
"""'(
| a
| 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))))
}
}
62 changes: 30 additions & 32 deletions src/test/scala/splatter/stutter/PrimitiveExpressions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)"),
Expand All @@ -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)")))
}
}