Skip to content

Commit

Permalink
Minimize the number of parenthesis with Stringification
Browse files Browse the repository at this point in the history
  • Loading branch information
Kevin van der Vlist committed Feb 15, 2017
1 parent 27d6181 commit 3d942d8
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,36 @@ import scala.language.implicitConversions
object Stringify {
implicit def expression2String(e: Expression): String = Stringify(e)

def apply(e: Expression): String = e match {
case v: Variable => v.symbol
case Application(t, s) => s"(${apply(t)} ${apply(s)})"
case LambdaAbstraction(x, a) => s"${apply(x)}.${apply(a)})"
private sealed trait ExpressionType

private case object Function extends ExpressionType

private case object Argument extends ExpressionType

// Mark the parent as nothing special
private case object Nothing extends ExpressionType

def apply(e: Expression): String = display(Nothing, e)("")

private def display(parent: ExpressionType, e: Expression): String => String = (parent, e) match {
case (Function, LambdaAbstraction(_, _)) => parenthesize(display(Nothing, e))
case (_, LambdaAbstraction(x, a)) => lambda compose display(Nothing, x) compose dot compose display(Nothing, a)
case (Function, Application(_: Variable, _: Variable)) => display(Nothing, e)
case (Function, Application(_, _)) => parenthesize(display(Nothing, e))
case (Argument, Application(_, _)) => parenthesize(display(Nothing, e))
case (_, Application(t, s)) => display(Function, t) compose space compose display(Argument, s)
case (_, v: Variable) => show(v.symbol)
}

private def show(s: String): String => String = (suffix: String) =>
s"$s$suffix"

private def space = show(" ")

private def lambda = show("λ")

private def dot = show(".")

private def parenthesize(f: String => String): String => String =
show("(") compose f compose show(")")
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package nl.soqua.lcpi.interpreter.transformation

import nl.soqua.lcpi.ast.lambda.Expression
import nl.soqua.lcpi.ast.lambda.Expression.A
import nl.soqua.lcpi.interpreter.transformation.Stringify._
import nl.soqua.lcpi.parser.lambda.LambdaCalcParser
Expand All @@ -17,14 +18,25 @@ class IsomorphismSpec extends WordSpec with Matchers {
case `deBruijn` => plainIsomorphism
}

private def compareWithClue(expected: Expression, actual: Expression): Assertion = {
withClue(
s"""
|expected: ${Stringify(expected)}
|actual: ${Stringify(actual)}
|
""".stripMargin) {
expected shouldBe actual
}
}

def plainIsomorphism: Assertion = {
val result = for {
p1 <- LambdaCalcParser(expr)
p2 <- LambdaCalcParser(p1)
} yield (p1, p2)
result match {
case Left(ex) => fail(s"Expression $expr failed: $ex")
case Right((e1, e2)) => e1 shouldBe e2
case Right((e1, e2)) => compareWithClue(e1, e2)
}
}

Expand All @@ -35,7 +47,7 @@ class IsomorphismSpec extends WordSpec with Matchers {
} yield (p1, p2)
result match {
case Left(ex) => fail(s"Expression $expr failed: $ex")
case Right((e1, e2)) => e1 shouldBe e2
case Right((e1, e2)) => compareWithClue(e1, e2)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@ class StringifySpec extends WordSpec with Matchers {

val x = V("x")
val y = V("y")
val z = V("z")

"Stringification of λ-expressions" should {
"correctly stringify t s" in {
Stringify(A(x, y)) shouldBe "(x y)"
Stringify(A(x, y)) shouldBe "x y"
}
"correctly stringify t s where t is a λx.x and s is y" in {
Stringify(A(λ(x, x), y)) shouldBe "((λx.x) y)"
Stringify(A(λ(x, x), y)) shouldBe "(λx.x) y"
}
"left-recursive with application" in {
Stringify(A(A(x, y), z)) shouldBe "x y z"
}
"S-expression" in {
Stringify(λ(x, λ(y, λ(z, A(A(x, z), A(y, z)))))) shouldBe "λx.λy.λz.x z (y z)"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ class ReplCompilerSpec extends ReplMonadTester with WordSpecLike with Matchers {
implicit val state = emptyState.copy(traceMode = Enabled)
val e = Application(Variable("I"), Variable("x"))
ReplMonad.expression(e) >> List(
"S => ((λx.x) x)",
"α => ((λx.x) x)",
"S => (λx.x) x",
"α => (λx.x) x",
"β => x",
"η => x",
"x"
Expand Down Expand Up @@ -100,7 +100,7 @@ class ReplCompilerSpec extends ReplMonadTester with WordSpecLike with Matchers {
}
"render an expression in De Bruijn Index notation" in {
val x = V("x")
ReplMonad.deBruijnIndex(λ(x, x)) >> "(λ.1)"
ReplMonad.deBruijnIndex(λ(x, x)) >> "λ.1"
}
"not be able to render a failure as de bruijn index" in {
val x = V("x")
Expand Down

0 comments on commit 3d942d8

Please sign in to comment.