1module Parser23import Gopher4import Data.String.Parser56many1 : Monad m => ParseT m a -> ParseT m (List a)7many1 p = do8 x <- p9 xs <- (many p)10 pure $ (x :: xs)1112parseUnAscii : ParseT IO Char13parseUnAscii = satisfy isUnAscii <?> "any character except: Tab, CR-LF, NUL"1415parseType : ParseT IO ItemType16parseType = do17 res <- parseUnAscii18 case (unmarshalType res) of19 Just a => pure $ a20 Nothing => fail $ "unknown item type: " ++ show res2122parseDesc : ParseT IO String23parseDesc = do24 res <- many parseUnAscii25 pure $ pack res2627parseSelector : ParseT IO Selector28parseSelector = parseDesc2930parseHost' : ParseT IO String31parseHost' = do32 r1 <- many $ satisfy isHostPart33 r2 <- (string ".")34 pure $ ((pack r1) ++ r2)35parseHost : ParseT IO String36parseHost = do37 name <- many parseHost'38 tld <- many $ satisfy isHostPart39 pure $ (concat name) ++ (pack tld)4041parsePort : ParseT IO Nat42parsePort = natural4344parseTab : ParseT IO Char45parseTab = satisfy (\x => x == '\t') <?> "tab character"4647parseDelim : ParseT IO String48parseDelim = string "\r\n"4950parseItem : ParseT IO Item51parseItem = do52 type <- parseType53 desc <- parseDesc54 ignore $ parseTab55 select <- parseSelector56 ignore $ parseTab57 host <- parseHost58 ignore $ parseTab59 port <- parsePort60 ignore $ parseDelim61 pure $ MkItem type desc select (MkPair host port)6263parseItems : ParseT IO (List Item)64parseItems = many1 parseItem6566public export67parseAll : String -> IO (Either String (List Item))68parseAll input = do69 r <- parseT parseItems input70 pure $ case r of71 Left err => Left err72 Right (items, n) => if n /= (cast $ length input)73 then Left $ "not all data consumed, remaining: "74 ++ (show $ (cast (length input)) - n)75 else Right items