Skip to content

Commit bafd97d

Browse files
committed
Ready. Set. Go!
0 parents  commit bafd97d

File tree

1 file changed

+116
-0
lines changed

1 file changed

+116
-0
lines changed

Main.hs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
module Main where
2+
3+
import Data.Char
4+
import Control.Applicative
5+
6+
data JsonValue
7+
= JsonNull
8+
| JsonBool Bool
9+
| JsonNumber Integer -- NOTE: no support for floats
10+
| JsonString String
11+
| JsonArray [JsonValue]
12+
| JsonObject [(String, JsonValue)]
13+
deriving (Show, Eq)
14+
15+
-- NOTE: no proper error reporting
16+
newtype Parser a = Parser
17+
{ runParser :: String -> Maybe (String, a)
18+
}
19+
20+
instance Functor Parser where
21+
fmap f (Parser p) =
22+
Parser $ \input -> do
23+
(input', x) <- p input
24+
Just (input', f x)
25+
26+
instance Applicative Parser where
27+
pure x = Parser $ \input -> Just (input, x)
28+
(Parser p1) <*> (Parser p2) =
29+
Parser $ \input -> do
30+
(input', f) <- p1 input
31+
(input'', a) <- p2 input'
32+
Just (input'', f a)
33+
34+
instance Alternative Parser where
35+
empty = Parser $ \_ -> Nothing
36+
(Parser p1) <|> (Parser p2) =
37+
Parser $ \input -> p1 input <|> p2 input
38+
39+
jsonNull :: Parser JsonValue
40+
jsonNull = (\_ -> JsonNull) <$> stringP "null"
41+
42+
charP :: Char -> Parser Char
43+
charP x = Parser f
44+
where
45+
f (y:ys)
46+
| y == x = Just (ys, x)
47+
| otherwise = Nothing
48+
f [] = Nothing
49+
50+
stringP :: String -> Parser String
51+
stringP = sequenceA . map charP
52+
53+
jsonBool :: Parser JsonValue
54+
jsonBool = f <$> (stringP "true" <|> stringP "false")
55+
where f "true" = JsonBool True
56+
f "false" = JsonBool False
57+
-- This should never happen
58+
f _ = undefined
59+
60+
spanP :: (Char -> Bool) -> Parser String
61+
spanP f =
62+
Parser $ \input ->
63+
let (token, rest) = span f input
64+
in Just (rest, token)
65+
66+
notNull :: Parser [a] -> Parser [a]
67+
notNull (Parser p) =
68+
Parser $ \input -> do
69+
(input', xs) <- p input
70+
if null xs
71+
then Nothing
72+
else Just (input', xs)
73+
74+
jsonNumber :: Parser JsonValue
75+
jsonNumber = f <$> notNull (spanP isDigit)
76+
where f ds = JsonNumber $ read ds
77+
78+
-- NOTE: no escape support
79+
stringLiteral :: Parser String
80+
stringLiteral = charP '"' *> spanP (/= '"') <* charP '"'
81+
82+
jsonString :: Parser JsonValue
83+
jsonString = JsonString <$> stringLiteral
84+
85+
ws :: Parser String
86+
ws = spanP isSpace
87+
88+
sepBy :: Parser a -> Parser b -> Parser [b]
89+
sepBy sep element = (:) <$> element <*> many (sep *> element) <|> pure []
90+
91+
jsonArray :: Parser JsonValue
92+
jsonArray = JsonArray <$> (charP '[' *> ws *>
93+
elements
94+
<* ws <* charP ']')
95+
where
96+
elements = sepBy (ws *> charP ',' <* ws) jsonValue
97+
98+
jsonObject :: Parser JsonValue
99+
jsonObject =
100+
JsonObject <$> (charP '{' *> ws *> sepBy (ws *> charP ',' <* ws) pair <* ws <* charP '}')
101+
where
102+
pair =
103+
(\key _ value -> (key, value)) <$> stringLiteral <*>
104+
(ws *> charP ':' <* ws) <*>
105+
jsonValue
106+
107+
jsonValue :: Parser JsonValue
108+
jsonValue = jsonNull <|> jsonBool <|> jsonNumber <|> jsonString <|> jsonArray <|> jsonObject
109+
110+
parseFile :: FilePath -> Parser a -> IO (Maybe a)
111+
parseFile fileName parser = do
112+
input <- readFile fileName
113+
return (snd <$> runParser parser input)
114+
115+
main :: IO ()
116+
main = undefined

0 commit comments

Comments
 (0)