Skip to content

Commit

Permalink
Fix tpolecat#65: assure incomplete input doesn't pass compilation
Browse files Browse the repository at this point in the history
  • Loading branch information
felixmulder committed Feb 16, 2018
1 parent b05cfca commit 406d888
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 4 deletions.
13 changes: 9 additions & 4 deletions core/src/main/scala/tut/Tut.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ object Tut {

def file(in: File): Tut[Unit] = for {
lines <- FileIO.lines(in).liftIO[Tut]
_ <- lines.zipWithIndex.traverse { case (l, num) => line(l, num + 1) }
_ <- lines.zipWithIndexAndNext(false)(nextCloses)
.traverse { case (l, nextCloses, num) => line(l, num + 1, nextCloses) }
} yield ()

// Private, utility methods

private def line(text: String, lineNumber: Int): Tut[Unit] =
private val nextCloses: String => Boolean =
_.trim.startsWith("```")

private def line(text: String, lineNumber: Int, nextCloses: Boolean): Tut[Unit] =
for {
s <- Tut.state
inv = s.mods.filter(m => m == Invisible || m == Passthrough || m.isInstanceOf[Decorate])
Expand All @@ -35,11 +39,11 @@ object Tut {
s
}
}.liftIO[Tut]
_ <- s.isCode.fold(interp(text, lineNumber), out(fixShed(text, mods ++ inv)))
_ <- s.isCode.fold(interp(text, lineNumber, nextCloses), out(fixShed(text, mods ++ inv)))
_ <- checkBoundary(text, "```tut", true, mods)
} yield ()

private def interp(text: String, lineNum: Int): Tut[Unit] =
private def interp(text: String, lineNum: Int, nextCloses: Boolean): Tut[Unit] =
Tut.state >>= { s =>
(text.trim.nonEmpty || s.partial.nonEmpty || s.mods(Silent)).whenM[Tut,Unit] {
for {
Expand All @@ -48,6 +52,7 @@ object Tut {
_ <- s.spigot.setActive(!(s.mods(Silent) || (s.mods(Invisible)))).liftIO[Tut]
_ <- s.mods(Book).whenM(s.spigot.commentAfter(s.partial + "\n" + text).liftIO[Tut])
r <- IO(s.imain.interpret(s.partial + "\n" + text)).liftIO[Tut] >>= {
case Results.Incomplete if nextCloses => error(lineNum, Some("incomplete input in code block, missing brace or paren?"))
case Results.Incomplete => incomplete(text)
case Results.Success => if (s.mods(Fail)) error(lineNum, Some("failure was asserted but no failure occurred")) else success
case Results.Error => if (s.mods(NoFail) || s.mods(Fail)) success else error(lineNum)
Expand Down
12 changes: 12 additions & 0 deletions core/src/main/scala/tut/felix/Syntax.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package tut.felix

import java.io.OutputStream
import scala.annotation.tailrec

trait Syntax {

Expand Down Expand Up @@ -35,6 +36,17 @@ trait Syntax {
implicit class ListOps[A](as: List[A]) {
def traverse[M[_]: Monad, B](f: A => M[B]): M[List[B]] =
as.foldRight(List.empty[B].point[M])((a, mlb) => f(a).flatMap(b => mlb.map(b :: _)))

def zipWithIndexAndNext[B](z: => B)(f: A => B): List[(A, B, Int)] = {
@tailrec
def fold(xs: List[(A, Int)], acc: List[(A, B, Int)]): List[(A, B, Int)] =
xs match {
case (x, i) :: (xs @ ((y, _) :: _)) => fold(xs, acc :+ ((x, f(y), i)))
case (x, i) :: Nil => acc :+ ((x, z, i))
case Nil => Nil
}
fold(as.zipWithIndex, Nil)
}
}

implicit class BooleanOps(b: Boolean) {
Expand Down

0 comments on commit 406d888

Please sign in to comment.