motemen (owner)

Revisions

gist: 141851 Download_button fork
public
Public Clone URL: git://gist.github.com/141851.git
Embed All Files: show embed
bencoding.scala #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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)))))