Created
May 19, 2017 06:20
-
-
Save phenan/9bda0c19599be203d0d2c46cc2d3b737 to your computer and use it in GitHub Desktop.
Parser combinators for binary files (especially for Java class file)
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 scala.util.Try | |
class ByteReader private (in: DataInputStream) { | |
def u1: Int = read(1, in.readUnsignedByte()) | |
def u2: Int = read(2, in.readUnsignedShort()) | |
def s1: Int = read(1, in.readByte().toInt) | |
def s2: Int = read(2, in.readShort().toInt) | |
def s4: Int = read(4, in.readInt()) | |
def s8: Long = read(8, in.readLong()) | |
def f4: Float = read(4, in.readFloat()) | |
def f8: Double = read(8, in.readDouble()) | |
def bytes(size: Int): Array[Byte] = { | |
val bs = new Array[Byte](size) | |
read(size, in.read(bs)) | |
bs | |
} | |
def utf: String = new String(bytes(u2), "UTF-8") | |
def padding(n: Int): Array[Byte] = { | |
val rem = (pos % n).toInt | |
if (rem == 0) Array.empty | |
else bytes(n - rem) | |
} | |
def position: Long = pos | |
private def close() { in.close() } | |
private def read[T] (size: Int, reader: => T): T = { | |
pos += size | |
reader | |
} | |
private var pos: Long = 0 | |
} | |
object ByteReader { | |
def openFile[T] (file: String)(p: ByteParser[T]): Try[T] = open(new File(file))(p) | |
def openString[T] (string: String)(p: ByteParser[T]): Try[T] = open(string.getBytes("UTF-8"))(p) | |
def open[T] (file: File)(p: ByteParser[T]): Try[T] = { | |
Try(new FileInputStream(file)).flatMap(in => open(in)(p)) | |
} | |
def open[T] (bytes: Array[Byte])(p: ByteParser[T]): Try[T] = { | |
Try(new ByteArrayInputStream(bytes)).flatMap(in => open(in)(p)) | |
} | |
def open[T] (in: InputStream)(p: ByteParser[T]): Try[T] = for { | |
reader <- Try(new ByteReader(new DataInputStream(new BufferedInputStream(in)))) | |
ret <- p.perform(reader) | |
_ <- Try(reader.close()) | |
} yield ret | |
} | |
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.pure(Try(v)) | |
def pure [T] (v: => Try[T]): ByteParser[T] = ByteParser.pure(v) | |
def failure (e: Exception): ByteParser[Nothing] = ByteParser.failure(e) | |
def bytes (n: Int) = ByteParser(_.bytes(n)) | |
def padding (n: Int) = ByteParser(_.padding(n)) | |
def list [T] (p: ByteParser[T]): ByteParser[List[T]] = u2 >>= p.rep | |
def ref [T] (p: ByteParser[T]): ByteParser[T] = ByteParser.ref(p) | |
def parseFile[T] (file: String)(parser: ByteParser[T]): Try[T] = ByteReader.openFile(file)(parser) | |
def parseString[T] (string: String)(parser: ByteParser[T]): Try[T] = ByteReader.openString(string)(parser) | |
def parse[T] (file: File)(parser: ByteParser[T]): Try[T] = ByteReader.open(file)(parser) | |
def parse[T] (bytes: Array[Byte])(parser: ByteParser[T]): Try[T] = ByteReader.open(bytes)(parser) | |
def parse[T] (stream: InputStream)(parser: ByteParser[T]): Try[T] = ByteReader.open(stream)(parser) | |
} | |
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.pure(Try(v)) | |
def pure [T] (v: => Try[T]): ByteParser[T] = ByteParser.pure(v) | |
def failure (e: Exception): ByteParser[Nothing] = ByteParser.failure(e) | |
def bytes (n: Int) = ByteParser(_.bytes(n)) | |
def padding (n: Int) = ByteParser(_.padding(n)) | |
def list [T] (p: ByteParser[T]): ByteParser[List[T]] = u2 >>= p.rep | |
def ref [T] (p: ByteParser[T]): ByteParser[T] = ByteParser.ref(p) | |
def parseFile[T] (file: String)(parser: ByteParser[T]): Try[T] = ByteReader.openFile(file)(parser) | |
def parseString[T] (string: String)(parser: ByteParser[T]): Try[T] = ByteReader.openString(string)(parser) | |
def parse[T] (file: File)(parser: ByteParser[T]): Try[T] = ByteReader.open(file)(parser) | |
def parse[T] (bytes: Array[Byte])(parser: ByteParser[T]): Try[T] = ByteReader.open(bytes)(parser) | |
def parse[T] (stream: InputStream)(parser: ByteParser[T]): Try[T] = ByteReader.open(stream)(parser) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment