]> gitweb @ CieloNegro.org - task-reporter.git/blobdiff - src/main/scala/jp/ymir/taskReporter/core/TSV.scala
The TSV parser is now monadic.
[task-reporter.git] / src / main / scala / jp / ymir / taskReporter / core / TSV.scala
index 541104f8ae3a037bd53501d85c52a4efe664756c..817d950376ab7a3779ff2aa75e7881a373c9dc3b 100644 (file)
@@ -2,38 +2,30 @@ package jp.ymir.taskReporter.core
 import scala.collection._
 import scala.collection.immutable.StringLike
 import scala.language.higherKinds
+import scala.language.implicitConversions
+import scala.language.reflectiveCalls
 import scalaz._
+import Scalaz._
 
 object TSV {
-  type Header = Seq[Symbol]
-  type Field  = String
-  type Record = Map[Symbol, Field]
-
-  // ---
-  type Failure[   F[_, _], E, R] = E => F[E, R]
-  type Success[A, F[_, _], E, R] = A => F[E, R]
 
-  trait Parser[E, A] {
-    def runParser[F[_, _], R](kf: Failure[F, E, R], ks: Success[A, F, E, R]): F[E, R]
-  }
-
-  implicit def ParserFunctor[E] = new Functor[({type λ[α] = Parser[E, α]})#λ] {
-    def map[A, B](m: Parser[E, A])(f: A => B) = new Parser[E, B] {
-      def runParser[F[_, _], R](kf: Failure[F, E, R], ks: Success[B, F, E, R]): F[E, R] = {
+  implicit val ParserFunctor = new Functor[Parser] {
+    def map[A, B](m: Parser[A])(f: A => B) = new Parser[B] {
+      def runParser[F[_], R](kf: Failure[F, R], ks: Success[B, F, R]): F[R] = {
         def ks_(a: A) = ks(f(a))
         return m.runParser(kf, ks_)
       }
     }
   }
 
-  implicit def ParserApplicative[E] = new Applicative[({type λ[α] = Parser[E, α]})#λ] {
-    def point[A](a: => A) = new Parser[E, A] {
-      def runParser[F[_, _], R](kf: Failure[F, E, R], ks: Success[A, F, E, R]): F[E, R]
+  implicit val ParserApplicative = new Applicative[Parser] {
+    def point[A](a: => A) = new Parser[A] {
+      def runParser[F[_], R](kf: Failure[F, R], ks: Success[A, F, R]): F[R]
         = ks(a)
     }
-    def ap[A, B](m: => Parser[E, A])(fm: => Parser[E, A => B]) = new Parser[E, B] {
-      def runParser[F[_, _], R](kf: Failure[F, E, R], ks: Success[B, F, E, R]): F[E, R] = {
-        def ks_(f: A => B): F[E, R] = {
+    def ap[A, B](m: => Parser[A])(fm: => Parser[A => B]) = new Parser[B] {
+      def runParser[F[_], R](kf: Failure[F, R], ks: Success[B, F, R]): F[R] = {
+        def ks_(f: A => B): F[R] = {
           def ks__(a: A) = ks(f(a))
           return m.runParser(kf, ks__)
         }
@@ -42,61 +34,80 @@ object TSV {
     }
   }
 
-  implicit def ParserMonad[E] = new Monad[({type λ[α] = Parser[E, α]})#λ] {
-    def point[A](a: => A)
-      = implicitly[Applicative[({type λ[α] = Parser[E, α]})#λ]].point(a)
+  implicit val ParserMonad = new Monad[Parser] {
+    def point[A](a: => A) = a.point[Parser]
 
-    def bind[A, B](m: Parser[E, A])(f: A => Parser[E, B]) = new Parser[E, B] {
-      def runParser[F[_, _], R](kf: Failure[F, E, R], ks: Success[B, F, E, R]): F[E, R] = {
+    def bind[A, B](m: Parser[A])(f: A => Parser[B]) = new Parser[B] {
+      def runParser[F[_], R](kf: Failure[F, R], ks: Success[B, F, R]): F[R] = {
         def ks_(a: A) = f(a).runParser(kf, ks)
         return m.runParser(kf, ks_)
       }
     }
   }
 
-  implicit def ParserMonadError[E] = new MonadError[Parser, E] {
-    def point[A](a: => A)
-      = implicitly[Applicative[({type λ[α] = Parser[E, α]})#λ]].point(a)
-
-    def bind[A, B](m: Parser[E, A])(f: A => Parser[E, B])
-      = implicitly[Monad[({type λ[α] = Parser[E, α]})#λ]].bind(m)(f)
+  implicit val ParserMonadError = new MonadError[({type λ[String, α] = Parser[α]})#λ, String] {
+    def point[A](a: => A) = a.point[Parser]
+    def bind[A, B](m: Parser[A])(f: A => Parser[B]) = m.flatMap(f)
 
-    def handleError[A](m: Parser[E, A])(f: E => Parser[E, A]) = new Parser[E, A] {
-      def runParser[F[_, _], R](kf: Failure[F, E, R], ks: Success[A, F, E, R]): F[E, R] = {
-        def kf_(e: E) = f(e).runParser(kf, ks)
+    def handleError[A](m: Parser[A])(f: String => Parser[A]) = new Parser[A] {
+      def runParser[F[_], R](kf: Failure[F, R], ks: Success[A, F, R]): F[R] = {
+        def kf_(e: String) = f(e).runParser(kf, ks)
         return m.runParser(kf_, ks)
       }
     }
 
-    def raiseError[A](e: E) = new Parser[E, A] {
-      def runParser[F[_, _], R](kf: Failure[F, E, R], ks: Success[A, F, E, R]): F[E, R]
+    def raiseError[A](e: String) = new Parser[A] {
+      def runParser[F[_], R](kf: Failure[F, R], ks: Success[A, F, R]): F[R]
         = kf(e)
     }
   }
 
-  def runParser[E, A](p: Parser[E, A]): Either[E, A] = {
-    def left (e: E): Either[E, A] = Left(e)
-    def right(x: A): Either[E, A] = Right(x)
-    return p.runParser(left, right)
+  implicit def ToMonadErrorOps[A](m: Parser[A]) = new {
+    def handleError(f: String => Parser[A]): Parser[A]
+      = implicitly[MonadError[({type λ[String, α] = Parser[α]})#λ, String]].handleError(m)(f)
   }
-  // ---
 
-  def decode[A: FromRecord](tsv: StringLike[_]): Seq[A] = {
-    val lines  = tsv.split('\n').filter(!_.isEmpty)
-    val header = lines.head.split('\t').map(Symbol(_))
-    val body   = lines.tail.map(line => (header zip line.split('\t')).toMap)
-    return body.map(implicitly[FromRecord[A]].parseRecord)
+  implicit def ToMonadErrorIdOps(e: String) = new {
+    def raiseError[A]: Parser[A]
+      = implicitly[MonadError[({type λ[String, α] = Parser[α]})#λ, String]].raiseError(e)
+  }
+
+  type Header = Seq[Symbol]
+
+  case class Field(value: String) {
+    def parseField[A: FromField]
+      = implicitly[FromField[A]].parseField(Field(value))
+  }
+
+  case class Record(value: Map[Symbol, Field]) {
+    def parseRecord[A: FromRecord]
+      = implicitly[FromRecord[A]].parseRecord(Record(value))
+
+    def lookup[A: FromField](name: Symbol): Parser[A]
+      = value.get(name).fold(("no field named " + name.name).raiseError[A])(_.parseField[A])
+  }
+
+  type Failure[   F[_], R] = String => F[R]
+  type Success[A, F[_], R] = A      => F[R]
+
+  trait Parser[+A] {
+    def runParser[F[_], R](kf: Failure[F, R], ks: Success[A, F, R]): F[R]
+  }
+
+  def runParser[A](p: Parser[A]): Either[String, A] = {
+    type F[R] = Either[String, R]
+    def left (e: String): F[A] = Left(e)
+    def right(x: A     ): F[A] = Right(x)
+    return p.runParser(left, right)
   }
 
   trait FromRecord[A] {
-    def parseRecord(r: Record): A
+    def parseRecord(r: Record): Parser[A]
   }
 
   trait FromField[A] {
-    def parseField(f: Field): A
+    def parseField(f: Field): Parser[A]
   }
-  def parseField[A: FromField](f: Field): A
-    = implicitly[FromField[A]].parseField(f)
 
   trait ToRecord[A] {
     def toRecord(a: A): Record
@@ -106,25 +117,50 @@ object TSV {
     def toField(a: A): Field
   }
 
+  class DecodeFailedException private(e: RuntimeException) extends RuntimeException(e) {
+    def this(msg: String) = this(new RuntimeException(msg))
+    def this(msg: String, cause: Throwable) = this(new RuntimeException(msg, cause))
+  }
+
+  def decode[A: FromRecord](tsv: StringLike[_]): Seq[A] = {
+    val lines  = tsv.split('\n').filter(!_.isEmpty)
+    val header = lines.head.split('\t').map(Symbol(_))
+    val body   = lines.tail.map(line => Record((header zip line.split('\t').map { Field(_) }).toMap))
+    return body.map { r =>
+      runParser(r.parseRecord[A]) match {
+        case Right(a) => a
+        case Left(e)  => throw new DecodeFailedException(e)
+      }
+    }
+  }
+
   // Option[A]
   implicit def OptionFromField[A: FromField] = new FromField[Option[A]] {
-    def parseField(f: Field): Option[A] = {
-      if (f.isEmpty) {
-        return None
+    def parseField(f: Field): Parser[Option[A]] = {
+      if (f.value.isEmpty) {
+        return None.point[Parser]
       }
       else {
-        return Some(implicitly[FromField[A]].parseField(f))
+        f.parseField[A].map { Some(_) }
       }
     }
   }
 
   // String
-  implicit def StringFromField = new FromField[String] {
-    def parseField(f: Field): String = f
+  implicit val StringFromField = new FromField[String] {
+    def parseField(f: Field): Parser[String] = f.value.point[Parser]
   }
 
   // Int
-  implicit def IntFromField = new FromField[Int] {
-    def parseField(f: Field): Int = f.toInt
+  implicit val IntFromField = new FromField[Int] {
+    def parseField(f: Field): Parser[Int] = {
+      try {
+        f.value.toInt.point[Parser]
+      }
+      catch {
+        case e: NumberFormatException =>
+          e.getMessage().raiseError[Int]
+      }
+    }
   }
 }