-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathParser.hs
128 lines (105 loc) · 2.71 KB
/
Parser.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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
module JSON (
JSON(..)
, parseJSON
) where
import Control.Applicative ((<*),(*>),(<$>),(<$))
import Control.Monad (void)
import Data.Char
import Data.List (foldl')
import Text.Parsec
import Text.Parsec.String
parseJSON :: String -> Either ParseError JSON
parseJSON xs = parse json "json" xs
data JSON = JSNull
| JSBool Bool
| JSNumber Int
| JSString String
| JSArray [JSON]
| JSObject [(String,JSON)]
deriving (Show, Eq)
json :: Parser JSON
json = ws *> jsValue
jsValue :: Parser JSON
jsValue = choice [jsNull,jsBool,jsObject,jsArray,jsNumber,jsString]
-- |
--
-- >>> parseJSON " null "
-- Right JSNull
jsNull :: Parser JSON
jsNull = jsAtom "null" JSNull
-- |
--
-- >>> parseJSON " false "
-- Right (JSBool False)
-- >>> parseJSON "true"
-- Right (JSBool True)
jsBool :: Parser JSON
jsBool = jsAtom "false" (JSBool False)
<|> jsAtom "true" (JSBool True)
-- |
--
-- >>> parseJSON " { \"key1\": 2 , \"key2\" : false } "
-- Right (JSObject [("key1",JSNumber 2),("key2",JSBool False)])
jsObject :: Parser JSON
jsObject = JSObject <$> betweenWs '{' kvs '}'
where
kvs = kv `sepBy` charWs ','
kv = do
JSString key <- jsString
charWs ':'
val <- jsValue
return (key, val)
-- |
--
-- >>> parseJSON " [ 1 , \"foo\" , true ] "
-- Right (JSArray [JSNumber 1,JSString "foo",JSBool True])
jsArray :: Parser JSON
jsArray = JSArray <$> betweenWs '[' vals ']'
where
vals = jsValue `sepBy` charWs ','
-- | Integer only.
--
-- >>> parseJSON " 123 "
-- Right (JSNumber 123)
-- >>> parseJSON " -456 "
-- Right (JSNumber (-456))
jsNumber :: Parser JSON
jsNumber = JSNumber <$> do
sign <- option id (negate <$ char '-')
ns <- many1 $ oneOf ['0'..'9']
ws
return $ sign $ fromInts ns
where
fromInts = foldl' (\x y -> x*10 + toInt y) 0
toInt n = ord n - ord '0'
-- | Non Unicode only.
--
-- >>> parseJSON " \"foo bar baz\" "
-- Right (JSString "foo bar baz")
jsString :: Parser JSON
jsString = JSString <$> (between (char '"') (char '"') (many jsChar) <* ws)
where
jsChar = unescaped <|> escaped
unescaped = noneOf "\"\\"
escaped = char '\\' *> escapedChar
escapedChar :: Parser Char
escapedChar = choice $ map ch alist
where
ch (x,y) = y <$ char x
alist = [
('b', '\b')
, ('f', '\f')
, ('n', '\n')
, ('r', '\r')
, ('t', '\t')
, ('\\','\\')
, ('\"','\"')
]
ws :: Parser ()
ws = void $ many $ oneOf " \t\r\n"
jsAtom :: String -> JSON -> Parser JSON
jsAtom str val = val <$ (string str <* ws)
charWs :: Char -> Parser ()
charWs c = char c *> ws
betweenWs :: Char -> Parser a -> Char -> Parser a
betweenWs o vals c = charWs o *> vals <* charWs c