Decoding encoding bencoded data with haskell
Jump to navigation
Jump to search
This is a crude implementation in haskell that uses list comprehension for parsing.
Here we will make a type for the structure of the bencoding. > module BEncode (BValue (..)) where To be able to distinguish digits > import Char Summary: a bvalue is either (vim regular expresions): i\(0\|-\{0,1\}[1-9][0-9]*\)e - an integer in base 10. \([1-9][0-9]*\):[0-\377]*\{\1\} - a string of exactly \1 characters, a bstring. l<bvalue>*e - a heterogeneous list of bvalues. d\(<bstring><bvalue>\)*e - a heterogeneous dictionary with strings as keys. > data BValue = BInteger Int | > BString String | > BList [BValue] | > BDictionary [(String, BValue)] deriving (Eq, Ord) The easy thing, the serialization. > beShow :: BValue -> String > beShow x = beShows x "" We do this to have linear time. > beShows :: BValue -> String -> String > beShows (BInteger x) s = 'i' : shows x ('e':s) > beShows (BString x) s = shows (length x) (':' : x ++ s) > beShows (BList xs) s = 'l' : foldr beShows ('e':s) xs > beShows (BDictionary xs) s = 'd' : foldr mapMap ('e':s) xs > where mapMap (k, v) s = beShows (BString k) (beShows v s) And now we can make BValue an instance of Show. > instance Show(BValue) where > showsPrec _ x = beShows x And now the parser. > beReads :: String -> [(BValue, String)] > beReads ('i':s) = [(BInteger x, rs) | (x, 'e':rs) <- intReads s] > beReads ('l':s) = [(BList xs, rs) | (xs, rs) <- beListReads s] > beReads ('d':s) = [(BDictionary xs, rs) | (xs, rs) <- beDictionaryReads s] > beReads s = [(BString x, rs) | (l, ':':ss) <- intReads s, (x, rs) <- [splitAt l ss]] > beListReads ('e':s) = [([], s)] > beListReads s = [(x:xs, rs) | (x, rs') <- beReads s, (xs, rs) <- beListReads rs'] > beDictionaryReads ('e':s) = [([], s)] > beDictionaryReads s = [((x, y):xys, rs) | (BString x, rs') <- beReads s, > (y, rs'') <- beReads rs', > (xys, rs) <- beDictionaryReads rs''] For now I don't know how to make reading an integer not looking to exponent part so: > intReads (x:s) | isDigit x = [(n, rs) | (n, rs) <- intReads' (digitToInt x) s] > intReads _ = [] > intReads' x (y:s) | isDigit y = [k | k <- intReads' (x * 10 + digitToInt y) s] > intReads' x s = [(x, s)] Let's make BValue an instance of Read also. > instance Read(BValue) where > readsPrec _ x = beReads x