Created
October 20, 2017 15:05
-
-
Save phenan/02e3c4890ce89acb6c667630831f9e98 to your computer and use it in GitHub Desktop.
バイナリ用パーザコンビネータ
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io._ | |
import scalaz.std.list._ | |
import scalaz.syntax.traverse._ | |
// Tries.scala (https://gist.github.com/phenan/63833c2cc715c96c02a4c33e17d9e2ac) | |
import tries._ | |
/** | |
* Created by phenan on 2017/10/18. | |
*/ | |
object ParserExamples extends ByteParsers { | |
def u2_s4_data: ByteParser[(Int, Int)] = for { | |
a <- u2 | |
b <- s4 | |
} yield (a, b) | |
def length_bytes: ByteParser[Array[Byte]] = for { | |
n <- u2 | |
bs <- bytes(n) | |
} yield bs | |
} | |
trait ByteParsers { | |
val u1 = ByteParser(_.u1) | |
val u2 = ByteParser(_.u2) | |
val s1 = ByteParser(_.s1) | |
val s2 = ByteParser(_.s2) | |
val s4 = ByteParser(_.s4) | |
val s8 = ByteParser(_.s8) | |
val f4 = ByteParser(_.f4) | |
val f8 = ByteParser(_.f8) | |
val utf = ByteParser(_.utf) | |
def ret [T] (v: => T): ByteParser[T] = ByteParser(_ => Tries(v)) | |
def lift [T] (v: => Tries[T]): ByteParser[T] = ByteParser(_ => v) | |
def failure (e: => Exception): ByteParser[Nothing] = ByteParser(_ => Fails(e)) | |
def bytes (n: Int) = ByteParser(_.bytes(n)) | |
def list [T] (p: ByteParser[T]): ByteParser[List[T]] = u2 >>= p.rep | |
} | |
case class ByteParser[+T] private (val perform: ByteReader => Tries[T]) { | |
def parse (file: String): Tries[T] = ByteReader.read(file)(perform) | |
def parse (file: File): Tries[T] = ByteReader.read(file)(perform) | |
def parse (bytes: Array[Byte]): Tries[T] = ByteReader.read(bytes)(perform) | |
def parse (in: => InputStream): Tries[T] = ByteReader.read(in)(perform) | |
} | |
object ByteParser { | |
implicit class ByteParserUtils [T] (parser: => ByteParser[T]) { | |
def map [U] (f: T => U): ByteParser[U] = ByteParser { r => parser.perform(r).map(f) } | |
def flatMap [U] (f: T => ByteParser[U]): ByteParser[U] = ByteParser { r => parser.perform(r).flatMap(t => f(t).perform(r)) } | |
def filter (f: T => Boolean): ByteParser[T] = ByteParser { r => parser.perform(r).filters(f) } | |
def withFilter (f: T => Boolean): WithFilter[T] = new WithFilter(parser.perform, f) | |
def >>= [U] (f: T => ByteParser[U]): ByteParser[U] = flatMap(f) | |
def rep (n: Int): ByteParser[List[T]] = ByteParser { r => | |
(0 until n).toList.traverse[Tries, T](_ => parser.perform(r)) | |
} | |
} | |
class WithFilter[T] (run: ByteReader => Tries[T], filter: T => Boolean) { | |
final def map [U] (f: T => U): ByteParser[U] = ByteParser { r => run(r).filters(filter).map(f) } | |
final def flatMap [U] (f: T => ByteParser[U]): ByteParser[U] = ByteParser { r => run(r).filters(filter).flatMap(t => f(t).perform(r)) } | |
final def withFilter (f: T => Boolean): WithFilter[T] = new WithFilter(run, { t => filter(t) && f(t) }) | |
} | |
} | |
class ByteReader private (in: DataInputStream) { | |
def u1: Tries[Int] = Tries { in.readUnsignedByte() } | |
def u2: Tries[Int] = Tries { in.readUnsignedShort() } | |
def s1: Tries[Byte] = Tries { in.readByte() } | |
def s2: Tries[Short] = Tries { in.readShort() } | |
def s4: Tries[Int] = Tries { in.readInt() } | |
def s8: Tries[Long] = Tries { in.readLong() } | |
def f4: Tries[Float] = Tries { in.readFloat() } | |
def f8: Tries[Double] = Tries { in.readDouble() } | |
def bytes(size: Int): Tries[Array[Byte]] = Tries { | |
val bs = new Array[Byte](size) | |
in.read(bs) | |
bs | |
} | |
def utf: Tries[String] = for { | |
size <- u2 | |
bs <- bytes(size) | |
} yield new String(bs, "UTF-8") | |
private def close() = Tries { in.close() } | |
} | |
object ByteReader { | |
def read [T] (file: String)(f: ByteReader => Tries[T]): Tries[T] = read(new FileInputStream(file))(f) | |
def read [T] (file: File)(f: ByteReader => Tries[T]): Tries[T] = read(new FileInputStream(file))(f) | |
def read [T] (bytes: Array[Byte])(f: ByteReader => Tries[T]): Tries[T] = read(new ByteArrayInputStream(bytes))(f) | |
def read [T] (in: => InputStream)(f: ByteReader => Tries[T]): Tries[T] = for { | |
r <- Tries { new ByteReader(new DataInputStream(new BufferedInputStream(in))) } | |
v <- f(r) | |
_ <- r.close() | |
} yield v | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment