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