Astyanax task
https://repl.it/@astynax/ParserCombinators
-------------
import Data.Char (isDigit, isSpace)
-- No more imports should be here!
-- ******** Types ***********
newtype Parser a = Parser
{ runParser
:: String
-- ^ parser's input
-> Maybe -- parsing can fail
( a
-- ^ parsed value
, String
-- ^ rest of input
)
}
instance Functor Parser where
fmap = error "fmap: implement me!"
instance Applicative Parser where
pure = error "pure: implement me!"
{-
> runParser (pure 42) "asd"
Just (42, "asd")
> runParser (pure 42) ""
Just (42, "")
-}
-- Hint: (<*>) :: Parser (a -> b) -> Parser a -> Parser b
(<*>) = error "(<*>): implement me!"
-- ********* Elementary parsers ************
{-
> runParser anyChar "ab"
Just ('a', "b")
> runParser anyChar "a"
Just ('a', "")
> runParser anyChar ""
Nothing
-}
anyChar :: Parser Char
anyChar = Parser $ \s ->
case s of
(x:xs) -> Just (x, xs)
_ -> Nothing
{-
> runParser (satisfy (== 'a') anyChar) "abc"
Just ('a', "bc")
> runParser (satisfy isSpace anyChar) " a "
Just (' ', "a ")
> runParser (satisfy isSpace anyChar) "xyz"
Nothing
> runParser (satisfy isSpace anyChar) ""
Nothing
-}
satisfy :: (a -> Bool) -> Parser a -> Parser a
satisfy pred p = Parser $ \s ->
case runParser p s of
Just (x, xs) | pred x -> Just (x, xs)
_ -> Nothing
{-
> runParser (char '1') "123"
Just ('1', "23")
-}
char :: Char -> Parser Char
char c = satisfy (== c) anyChar
{-
> runParser eof "asd"
Nothing
> runParser eof ""
Just ((), "")
-}
eof :: Parser ()
eof = Parser $ \s ->
case s of
[] -> Just ((), "")
_ -> Nothing
-- ********* Combinators ***********
{-
> runParser (char 'a' <|> char 'b') "a"
Just ('a', "")
> runParser (char 'a' <|> char 'b') "b"
Just ('b', "")
> runParser (char 'a' <|> char 'b') "c"
Nothing
-}
(<|>) :: Parser a -> Parser a -> Parser a
p1 <|> p2 = error "(<|>): implement me!"
{-
> runParser (many $ char '.') ".abc"
Just (".", "abc")
> runParser (many $ char '.') ".....abc"
Just (".....", "abc")
> runParser (many $ char '.') "abc"
Just ("", "abc")
-}
many :: Parser a -> Parser [a]
many p = many1 p <|> pure []
{-
> runParser (many1 $ char '.') ".abc"
Just (".", "abc")
> runParser (many1 $ char '.') ".....abc"
Just (".....", "abc")
> runParser (many1 $ char '.') "abc"
Nothing
-}
many1 :: Parser a -> Parser [a]
many1 p = (:) <$> p <*> many p
{-
> runParser (between (char '{') (char '}') (many anyChar)) "{}asd"
Just ("", "asd")
> runParser (between (char '{') (char '}') (many anyChar)) "{as}d"
Just ("as", "d")
> runParser (between (char '{') (char '}') (many anyChar)) "{asd"
Nothing
-}
between :: Parser a -> Parser b -> Parser c -> Parser c
between p1 p2 p = p1 *> p <* p2
{-
> runParser (sepBy (char ',') (satisfy isDigit anyChar)) "1,2,3,a"
Just ("123", ",a")
> runParser (sepBy (char ',') (satisfy isDigit anyChar)) "1"
Just ("1", "")
> runParser (sepBy (char ',') (satisfy isDigit anyChar)) ""
Just ("", "")
-}
sepBy :: Parser a -> Parser b -> Parser [b]
sepBy s p = error "sepBy: implement me!"
-- ********* Combined parsers **********
{-
> runParser (string "foo") "fo"
Nothing
> runParser (string "foo") "foobar"
Just ("foo", "bar")
-}
string :: String -> Parser String
string = error "string: implement me!"
-- *********** Simple JSON parser *****************
-- * incomplete(!), i.e. w/o any advanced stuff like special char escaping, spaces, etc
data JSON
= JNull
| JBool Bool
| JNum Int
| JString String
| JArray [JSON]
| JObject [(String, JSON)]
deriving (Show)
quotedString :: Parser String
quotedString = between (char '"') (char '"') $ many (satisfy (/= '"') anyChar)
jnull, jbool, jnum, jstring, jarray, jobject, json :: Parser JSON
json = jnull <|> jbool <|> jnum <|> jstring <|> jarray <|> jobject
jnull = JNull <$ string "null"
jbool = JBool <$> ((True <$ string "true") <|> (False <$ string "false"))
jnum = error "jnum: implement me!"
jstring = JString <$> quotedString
jarray = JArray <$> between (char '[') (char ']') (sepBy (char ',') json)
jobject = JObject <$> between (char '{') (char '}')
(sepBy (char ',') $ (,) <$> quotedString <* char ':' <*> json)
-- ************** Some tests
main :: IO ()
main = do
putStrLn "These ones should parse something"
print $ runParser jnull "null"
print $ runParser jbool "true"
print $ runParser jnum "123"
print $ runParser jstring "\"abc\""
print $ runParser jarray "[]"
print $ runParser jarray "[null]"
print $ runParser jarray "[true,false]"
print $ runParser jobject "{}"
print $ runParser jobject "{\"a\":null}"
print $ runParser (json <* eof) "{\"a\":[1,null,false,\"foo\",{}],\"b\":true}"
putStrLn "This one should fail:"
print $ runParser (json <* eof) "{\"a\":[1,null,false,\"foo\",{}],\"b\":true}blabla"
Comments
0 B
|👍
/👎
0 B
|👍
/👎