-
Notifications
You must be signed in to change notification settings - Fork 0
/
Argo.hs
99 lines (71 loc) · 2.42 KB
/
Argo.hs
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
{-# LANGUAGE LambdaCase #-}
module Argo where
import Control.Applicative
import Data.Char (isDigit, isSpace)
data JsonValue
= JsonNull
| JsonBool Bool
| JsonNumber Integer
| JsonString String
| JsonArray [JsonValue]
| JsonObject [(String, JsonValue)]
deriving (Show, Eq)
newtype Parser a
= Parser
{ runParser :: String -> Maybe (String, a)
}
instance Functor Parser where
fmap f (Parser p) = Parser $ \x -> do
(x', y) <- p x
Just (x', f y)
instance Applicative Parser where
pure x = Parser $ \y -> Just (y, x)
Parser p1 <*> Parser p2 = Parser $ \x -> do
(x', f) <- p1 x
(x'', a) <- p2 x'
Just (x'', f a)
instance Alternative Parser where
empty = Parser $ const Nothing
Parser f <|> Parser g = Parser $ \x -> f x <|> g x
-- Parser Combinators
char :: Char -> Parser Char
char c = Parser $ \case
x : xs | x == c -> Just (xs, c)
_ -> Nothing
string :: String -> Parser String
string = traverse char
partition :: (Char -> Bool) -> Parser String
partition f = Parser $ \x ->
let (token, rest) = span f x
in Just (rest, token)
notNull :: Parser [a] -> Parser [a]
notNull (Parser p) = Parser $ \x -> do
(x', xs) <- p x
if null xs then Nothing else Just (x', xs)
-- Parsers
jsonNull :: Parser JsonValue
jsonNull = JsonNull <$ string "null"
jsonBool :: Parser JsonValue
jsonBool = JsonBool . ("true" ==) <$> (string "true" <|> string "false")
jsonNumber :: Parser JsonValue
jsonNumber = JsonNumber . read <$> notNull (partition isDigit)
stringLiteral :: Parser String
stringLiteral = char '"' *> partition (/= '"') <* char '"'
jsonString :: Parser JsonValue
jsonString = JsonString <$> stringLiteral
ws :: Parser String
ws = partition isSpace
sepBy :: Parser a -> Parser b -> Parser [b]
sepBy sep el = (:) <$> el <*> many (sep *> el) <|> pure []
jsonArray :: Parser JsonValue
jsonArray = JsonArray <$> (char '[' *> ws *> sepBy (ws *> char ',' <* ws) jsonValue <* ws <* char ']')
jsonObject :: Parser JsonValue
jsonObject = JsonObject <$> (char '{' *> ws *> sepBy (ws *> char ',' <* ws) keyValue <* ws <* char '}')
where
keyValue = (\key _ value -> (key, value)) <$> stringLiteral <*> (ws *> char ':' <* ws) <*> jsonValue
jsonValue :: Parser JsonValue
jsonValue = jsonNull <|> jsonBool <|> jsonNumber <|> jsonString <|> jsonArray <|> jsonObject
parseFile :: FilePath -> Parser a -> IO (Maybe a)
parseFile file parser = do
input <- readFile file
pure $ snd <$> runParser parser input