[Haskell-cafe] Bencoding in Haskell

Sebastian Sylvan sebastian.sylvan at gmail.com
Wed Apr 20 14:58:41 EDT 2005


I was bored so I ran it through ghci and fixed the small errors I
found, here's the "working" version, I don't really have much of test
data to play with, but it seems to be working with the small examples
I copy-n-pasted from the wiki and the bittorrent website:

import qualified Data.Map as Map
import Data.Map(Map)
import Text.ParserCombinators.Parsec

data Bencode = BEInteger Integer 
             | BEString String 
             | BEList [Bencode] 
             | BEDictionary (Map String Bencode)
             deriving (Show, Eq)

number :: Parser Integer
number = 
    do n_str <- many1 digit 
       let n = read n_str
       return n

beString :: Parser Bencode
beString =
    do n <- number 
       char ':'
       str <- count (fromInteger n) anyChar
       return (BEString str) 


beInt :: Parser Bencode
beInt =
    do char 'i'
       n <- number
       char 'e'
       return (BEInteger n)

-- parse any Bencoded value
beParse :: Parser Bencode
beParse = beInt <|> beString <|> beDictionary <|> beList

beList :: Parser Bencode
beList =
    do char 'l'
       xs <- many beParse -- parse many bencoded values
       char 'e'
       return (BEList xs)

beDictionary :: Parser Bencode
beDictionary =
    do char 'd'
       (BEString key) <- beString
       val <- beParse
       (BEDictionary m) <- beDictionary 
                           <|> do char 'e'
                                  return (BEDictionary Map.empty)

       return (BEDictionary (Map.insert key val m))

-- main parser function
parseBencoded :: String -> Maybe [Bencode]
parseBencoded str = case parse (many beParse) "" str of
                                Left err -> Nothing
                                Right val -> Just val



/S

On 4/20/05, Sebastian Sylvan <sebastian.sylvan at gmail.com> wrote:
> Yeah, you probably want the main parser to be "many beParser" and not
> just beParser:
> 
> -- main parser function
> parseBencoded :: String -> Maybe [Bencode]
> parseBencode str = case parse (many beParse) "" str of
>                                 Left err -> Nothing
>                                 Right val -> Just val
> 
> On 4/20/05, Sebastian Sylvan <sebastian.sylvan at gmail.com> wrote:
> > On 4/20/05, Tommi Airikka <haskell-cafe at airikka.net> wrote:
> > > Hi!
> > >
> > > I was just wondering if there are any good ways to represent a bencoded
> > > (http://en.wikipedia.org/wiki/Bencoding) message in Haskell? Any
> > > suggestions?
> > >
> >
> > Not that I know of, but it should be very easy to write a parser using
> > the parser library Parsec.
> >
> > You'll need a datatype, something like this:
> >
> > data Bencode = BEInteger Integer |
> >                         BEString String |
> >                         BEList [Bencode] |
> >                         BEDictionary (Data.Map String Bencode)
> >                         deriving (Show, Eq)
> >
> > Which should be sufficient to represent any Bencoded message (if I
> > didn't make a misstake).
> > Then you could probably use the standard char-parser in parsec to
> > parse it quite easily. Read the docs, they're quite straightforward.
> >
> > I'm a bit rusty but something like this:
> >
> > -- just parse an integer, parsec might have one of these already
> > number :: Parser Integer
> > number =
> >   do n_str <- many1 digit -- parse a number
> >        let n = read n_str      -- convert to an Int
> >        return n                   -- return the number
> >
> > beString :: Parser Bencode
> > beString =
> >   do n <- number                 -- the length prefix
> >       char ':'                          -- now a ':'
> >       str <- count n anyChar   -- and now n number of letters
> >       return (BEString str)       -- return the string wrapped up as a
> > BEString
> >
> > beInt :: Parser Bencode
> > beInt =
> >   do char 'i'
> >        n <- number
> >        char 'e'
> >        return n
> >
> > -- parse any Bencoded value
> > beParse :: Parser Bencode
> > beParse =
> >   do beInt <|> beString <|> beDictionary <|> beList
> >
> > beList :: Parser Bencode
> > beList =
> >   do char 'l'
> >        xs <- many beParse -- parse many bencoded values
> >        char 'e'
> >        return (BEList xs)
> >
> > beDictionary :: Parser Bencode
> > beDictionary =
> >   do char 'd'
> >        key <- beString
> >        val <- beParse
> >        m <- beDictionary <|> char 'e' >> return Data.Map.empty
> >        return (Data.Map.insert key val m)
> >
> > -- main parser function
> > parseBencoded :: String -> Maybe Bencode
> > parseBencode str = case parse beParse "" str of
> >                                  Left err -> Nothing
> >                                  Right val -> Just val
> >
> > Note: This is all untested code that I just scribbled down real quick.
> > There's probably tons of misstakes, but you should get the picture.
> > Read the Parsec docs and then write your own.
> >
> > /S
> > --
> > Sebastian Sylvan
> > +46(0)736-818655
> > UIN: 44640862
> >
> 
> --
> Sebastian Sylvan
> +46(0)736-818655
> UIN: 44640862
> 


-- 
Sebastian Sylvan
+46(0)736-818655
UIN: 44640862


More information about the Haskell-Cafe mailing list