DISABLE FDs (REMOVE ME).
[jackhill/mal.git] / haskell / Reader.hs
CommitLineData
b76aa73b
JM
1module Reader
2( read_str )
3where
4
5import Text.ParserCombinators.Parsec (
b091e954 6 Parser, parse, char, digit, letter, try,
526d28fb 7 (<|>), oneOf, noneOf, many, many1, skipMany, skipMany1, sepEndBy, string)
b76aa73b 8import qualified Data.Map as Map
b76aa73b
JM
9
10import Types
11
12spaces :: Parser ()
fa9a9758
JM
13spaces = skipMany1 (oneOf ", \n")
14
15comment :: Parser ()
526d28fb 16comment = char ';' *> skipMany (noneOf "\r\n")
fa9a9758
JM
17
18ignored :: Parser ()
19ignored = skipMany (spaces <|> comment)
b76aa73b
JM
20
21symbol :: Parser Char
22symbol = oneOf "!#$%&|*+-/:<=>?@^_~"
23
24escaped :: Parser Char
526d28fb
NB
25escaped = f <$> (char '\\' *> oneOf "\\\"n")
26 where f 'n' = '\n'
27 f x = x
b76aa73b
JM
28
29read_number :: Parser MalVal
526d28fb 30read_number = MalNumber . read <$> many1 digit
328384f3
DM
31
32read_negative_number :: Parser MalVal
526d28fb
NB
33read_negative_number = f <$> char '-' <*> many1 digit
34 where f sign rest = MalNumber $ read $ sign : rest
b76aa73b
JM
35
36read_string :: Parser MalVal
526d28fb 37read_string = MalString <$> (char '"' *> many (escaped <|> noneOf "\\\"") <* char '"')
b76aa73b
JM
38
39read_symbol :: Parser MalVal
526d28fb
NB
40read_symbol = f <$> (letter <|> symbol) <*> many (letter <|> digit <|> symbol)
41 where f first rest = g (first : rest)
42 g "true" = MalBoolean True
43 g "false" = MalBoolean False
44 g "nil" = Nil
45 g s = MalSymbol s
b76aa73b
JM
46
47read_keyword :: Parser MalVal
526d28fb 48read_keyword = MalString . (:) keywordMagic <$> (char ':' *> many (letter <|> digit <|> symbol))
b76aa73b
JM
49
50read_atom :: Parser MalVal
51read_atom = read_number
328384f3 52 <|> try read_negative_number
b76aa73b
JM
53 <|> read_string
54 <|> read_keyword
55 <|> read_symbol
56
57read_list :: Parser MalVal
526d28fb 58read_list = toList <$> (char '(' *> ignored *> sepEndBy read_form ignored <* char ')')
b76aa73b
JM
59
60read_vector :: Parser MalVal
526d28fb 61read_vector = MalSeq (MetaData Nil) (Vect True) <$> (char '[' *> ignored *> sepEndBy read_form ignored <* char ']')
b76aa73b 62
b76aa73b 63read_hash_map :: Parser MalVal
526d28fb
NB
64read_hash_map = g . keyValuePairs =<< (char '{' *> ignored *> sepEndBy read_form ignored <* char '}')
65 where g (Just pairs) = return $ MalHashMap (MetaData Nil) (Map.fromList pairs)
66 g Nothing = fail "invalid contents inside map braces"
b76aa73b 67
c150ec41 68-- reader macros
526d28fb
NB
69addPrefix :: String -> MalVal -> MalVal
70addPrefix s x = toList [MalSymbol s, x]
71
2988d38e 72read_quote :: Parser MalVal
526d28fb 73read_quote = addPrefix "quote" <$> (char '\'' *> read_form)
2988d38e
JM
74
75read_quasiquote :: Parser MalVal
526d28fb 76read_quasiquote = addPrefix "quasiquote" <$> (char '`' *> read_form)
2988d38e
JM
77
78read_splice_unquote :: Parser MalVal
526d28fb 79read_splice_unquote = addPrefix "splice-unquote" <$> (string "~@" *> read_form)
2988d38e
JM
80
81read_unquote :: Parser MalVal
526d28fb 82read_unquote = addPrefix "unquote" <$> (char '~' *> read_form)
c150ec41
JM
83
84read_deref :: Parser MalVal
526d28fb 85read_deref = addPrefix "deref" <$> (char '@' *> read_form)
2988d38e 86
c150ec41 87read_with_meta :: Parser MalVal
526d28fb
NB
88read_with_meta = f <$> (char '^' *> read_form) <*> read_form
89 where f m x = toList [MalSymbol "with-meta", x, m]
2988d38e
JM
90
91read_macro :: Parser MalVal
92read_macro = read_quote
93 <|> read_quasiquote
94 <|> try read_splice_unquote <|> read_unquote
c150ec41
JM
95 <|> read_deref
96 <|> read_with_meta
97
98--
b76aa73b
JM
99
100read_form :: Parser MalVal
526d28fb
NB
101read_form = ignored *> (
102 read_macro
2988d38e
JM
103 <|> read_list
104 <|> read_vector
105 <|> read_hash_map
526d28fb 106 <|> read_atom)
b76aa73b 107
5400d4bf 108read_str :: String -> IOThrows MalVal
b76aa73b 109read_str str = case parse read_form "Mal" str of
5400d4bf 110 Left err -> throwStr $ show err
b76aa73b 111 Right val -> return val