Skip to content

Instantly share code, notes, and snippets.

@motemen
Created July 7, 2009 02:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save motemen/141851 to your computer and use it in GitHub Desktop.
Save motemen/141851 to your computer and use it in GitHub Desktop.
import scala.io.Source
import scala.collection.immutable._
abstract class Bencoding
case class BString(string : String) extends Bencoding
case class BInt(number : Int) extends Bencoding
case class BList(list : List[Bencoding]) extends Bencoding
case class BDict(dict : Map[String,Bencoding]) extends Bencoding
object Bencoding {
private def startsWith (condition : Char => Boolean) (block : => Bencoding) (implicit source : Source) : Bencoding = {
if (!condition(source.ch)) source.next
if (!condition(source.ch)) error("Unexpected character: " + source.ch)
block
}
private def startsWith (c : Char) (block : => Bencoding) (implicit source : Source) : Bencoding =
startsWith (_ == c) (block)
def parse (implicit source : Source) : Bencoding =
startsWith(c => c.isDigit || c == 'i' || c == 'l' || c == 'd') {
if (source.ch.isDigit) return parseString(source)
if (source.ch == 'i') return parseInt(source)
if (source.ch == 'l') return parseList(source)
if (source.ch == 'd') return parseDict(source)
error("Unexpected character: " + source.ch) // Would not reach
}
def parseString (implicit source : Source) : Bencoding =
startsWith(_.isDigit) {
val length : Int = Integer.parseInt((source.ch :: source.takeWhile(_ != ':').toList).mkString)
BString(source.take(length).mkString)
}
def parseInt (implicit source : Source) : Bencoding =
startsWith('i') {
BInt(Integer.parseInt(source.takeWhile(_ != 'e').mkString))
}
def parseList (implicit source : Source) : Bencoding =
startsWith('l') {
var list : List[Bencoding] = Nil
while (source.hasNext && source.next != 'e') {
list = parse(source) :: list
}
BList(list.reverse)
}
def parseDict (implicit source : Source) : Bencoding =
startsWith('d') {
var dict : Map[String,Bencoding] = new HashMap
while (source.hasNext && source.next != 'e') {
val key : String = parseString(source) match { case BString(s) => s }
val value : Bencoding = parse(source)
dict = dict + (key -> value)
}
BDict(dict)
}
}
println(Bencoding.parse(Source.fromString("4:spam")))
// => BString(spam)
println(Bencoding.parse(Source.fromString("i-3e")))
// => BInt(-3)
println(Bencoding.parse(Source.fromString("l4:spami-3e")))
// => BList(List(BString(spam), BInt(-3)))
println(Bencoding.parse(Source.fromString("d3:cow3:moo4:spam4:eggse")))
// => BDict(Map(spam -> BString(eggs), cow -> BString(moo)))
println(Bencoding.parse(Source.fromString("l1:a1:be")))
// => BList(List(BString(a), BString(b)))
println(Bencoding.parse(Source.fromString("l1:a2:bb3:ccce")))
// => BList(List(BString(a), BString(bb), BString(ccc)))
println(Bencoding.parse(Source.fromString("d4:spaml1:a1:bee")))
// => BDict(Map(spam -> BList(List(BString(a), BString(b)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment