X-Git-Url: http://git.cielonegro.org/gitweb.cgi?a=blobdiff_plain;f=src%2Fmain%2Fscala%2Fjp%2Fymir%2FtaskReporter%2Fcore%2FTSV.scala;h=817d950376ab7a3779ff2aa75e7881a373c9dc3b;hb=c3c60f005928ab2660f79643af25f8380d5ad4db;hp=fc4947d766cecc33a2601d4cebd6a2b6cb97056c;hpb=1b5b7ced1ab2585580ab8f1b65dcf4f4fe21bff2;p=task-reporter.git diff --git a/src/main/scala/jp/ymir/taskReporter/core/TSV.scala b/src/main/scala/jp/ymir/taskReporter/core/TSV.scala index fc4947d..817d950 100644 --- a/src/main/scala/jp/ymir/taskReporter/core/TSV.scala +++ b/src/main/scala/jp/ymir/taskReporter/core/TSV.scala @@ -2,22 +2,14 @@ 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[_], 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] - } - implicit def ParserFunctor = new Functor[Parser] { + 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)) @@ -26,11 +18,11 @@ object TSV { } } - implicit def ParserApplicative = new Applicative[Parser] { + 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 runParser[F[_], R](kf: Failure[F, R], ks: Success[A, F, R]): F[R] + = ks(a) } - 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] = { @@ -42,8 +34,8 @@ object TSV { } } - implicit def ParserMonad = new Monad[Parser] { - def point[A](a: => A) = implicitly[Applicative[Parser]].point(a) + implicit val ParserMonad = new Monad[Parser] { + def point[A](a: => A) = a.point[Parser] 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] = { @@ -53,30 +45,69 @@ object TSV { } } - def runParser[A](p: Parser[A]): Either[String, A] = { - type F[A] = Either[String, A] - def left (errMsg: String): F[A] = Left(errMsg) - def right(x : A ): F[A] = Right(x) - return p.runParser(left, right) + 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[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: String) = new Parser[A] { + def runParser[F[_], R](kf: Failure[F, R], ks: Success[A, F, R]): F[R] + = kf(e) + } } - // --- - 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 ToMonadErrorOps[A](m: Parser[A]) = new { + def handleError(f: String => Parser[A]): Parser[A] + = implicitly[MonadError[({type λ[String, α] = Parser[α]})#λ, String]].handleError(m)(f) + } + + 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 @@ -86,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] + } + } } }