Last active
December 20, 2015 11:29
-
-
Save oxlade39/6123535 to your computer and use it in GitHub Desktop.
bencoding using scala parser
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
object BencodeParser { | |
def parse(s: String) = parser.parseAll(parser.bvalue, s) match { | |
case parser.Success(bencoded, _) => Some(bencoded) | |
case _ => None | |
} | |
private[this] object parser extends scala.util.parsing.combinator.RegexParsers { | |
override type Elem = Char | |
def positiveInt = """([1-9]\d*)""".r ^^ { | |
i => Integer.parseInt(i) | |
} | |
def integer = """(0|-*[1-9]\d*)""".r ^^ { | |
i => Integer.parseInt(i) | |
} | |
def bint: Parser[BInt] = 'i' ~ integer ~ 'e' ^^ { | |
case start ~ i ~ end => BInt(i) | |
} | |
def length = positiveInt ~ ":" | |
def bstring: Parser[BString] = length >> { | |
len => """\w{%d}""".format(len._1).r | |
} ^^ { | |
case s => BString(s) | |
} | |
def blist: Parser[BList] = 'l' ~ rep(bvalue) ~ 'e' ^^ { | |
case l ~ b ~ e => BList(b: _*) | |
} | |
def bmap: Parser[BMap] = 'd' ~ rep(bstring ~ bvalue) ~ 'e' ^^ { | |
case d ~ items ~ e => BMap(items.foldLeft(Map[String, BValue]()) { | |
case (accum, item) => | |
val key: BString = item._1 | |
val value: BValue = item._2 | |
accum + (key.value -> value) | |
}) | |
} | |
def bvalue: Parser[BValue] = bint | bstring | blist | bmap | |
} | |
} |
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 org.specs2.mutable.Specification | |
class BencodeParserSpec extends Specification { | |
"BencodeParser" should { | |
"parse bencoded integers" in { | |
val output = BencodeParser.parse("i42e") | |
output mustEqual Some(BInt(42)) | |
} | |
"parse bencoded negative integers" in { | |
val output = BencodeParser.parse("i-42e") | |
output mustEqual Some(BInt(-42)) | |
} | |
"not parse bencoded negative zero" in { | |
val output = BencodeParser.parse("i-e") | |
output mustEqual None | |
} | |
"parse bencoded strings" in { | |
val output = BencodeParser.parse("4:spam") | |
output mustEqual Some(BString("spam")) | |
} | |
"parse bencoded lists" in { | |
val output = BencodeParser.parse("l4:spami42ee") | |
output mustEqual Some(BList(BString("spam"), BInt(42))) | |
} | |
"parse bencoded maps" in { | |
val output = BencodeParser.parse("d3:bar4:spam3:fooi42ee") | |
output mustEqual Some(BMap(Map("foo" -> BInt(42), "bar" -> BString("spam")))) | |
} | |
} | |
"BValue" should { | |
"encode" in { | |
BMap(Map("foo" -> BInt(42), "bar" -> BString("spam"))).encode mustEqual "d3:bar4:spam3:fooi42ee" | |
} | |
} | |
} |
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
sealed trait BValue { | |
def encode: String | |
} | |
case class BInt(value: Int) extends BValue{ | |
lazy val encode = "i" + value + "e" | |
} | |
case class BString(value: String) extends BValue{ | |
lazy val encode = value.size + ":" + value | |
} | |
case class BList(values: BValue*) extends BValue{ | |
def encode = values.foldLeft("l")((accum, value) => accum + value.encode) + "e" | |
} | |
case class BMap(values: Map[String, BValue]) extends BValue{ | |
def encode = values.foldLeft("d")((accum, kv) => accum + kv._1 + kv._2.encode) + "e" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment