5 import Text
.ParserCombinators
.Parsec
(
6 Parser
, parse
, char
, digit
, letter
, try,
7 (<|
>), oneOf
, noneOf
, many
, many1
, skipMany
, skipMany1
, sepEndBy
, string)
8 import qualified Data
.Map
as Map
13 spaces
= skipMany1
(oneOf
", \n")
16 comment
= char
';' *> skipMany
(noneOf
"\r\n")
19 ignored
= skipMany
(spaces
<|
> comment
)
22 symbol
= oneOf
"!#$%&|*+-/:<=>?@^_~"
24 escaped
:: Parser
Char
25 escaped
= f
<$> (char
'\\' *> oneOf
"\\\"n")
29 read_number
:: Parser MalVal
30 read_number
= MalNumber
. read <$> many1 digit
32 read_negative_number
:: Parser MalVal
33 read_negative_number
= f
<$> char
'-' <*> many1 digit
34 where f sign rest
= MalNumber
$ read $ sign
: rest
36 read_string
:: Parser MalVal
37 read_string
= MalString
<$> (char
'"' *> many (escaped <|> noneOf "\\\"") <* char '"')
39 read_symbol
:: Parser MalVal
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
47 read_keyword
:: Parser MalVal
48 read_keyword
= MalString
. (:) keywordMagic
<$> (char
':' *> many
(letter
<|
> digit
<|
> symbol
))
50 read_atom
:: Parser MalVal
51 read_atom
= read_number
52 <|
> try read_negative_number
57 read_list
:: Parser MalVal
58 read_list
= toList
<$> (char
'(' *> ignored
*> sepEndBy read_form ignored
<* char
')')
60 read_vector
:: Parser MalVal
61 read_vector
= MalSeq
(MetaData Nil
) (Vect
True) <$> (char
'[' *> ignored
*> sepEndBy read_form ignored
<* char
']')
63 read_hash_map
:: Parser MalVal
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"
69 addPrefix
:: String -> MalVal
-> MalVal
70 addPrefix s x
= toList
[MalSymbol s
, x
]
72 read_quote
:: Parser MalVal
73 read_quote
= addPrefix
"quote" <$> (char
'\'' *> read_form
)
75 read_quasiquote
:: Parser MalVal
76 read_quasiquote
= addPrefix
"quasiquote" <$> (char
'`
' *> read_form
)
78 read_splice_unquote
:: Parser MalVal
79 read_splice_unquote
= addPrefix
"splice-unquote" <$> (string "~@" *> read_form
)
81 read_unquote
:: Parser MalVal
82 read_unquote
= addPrefix
"unquote" <$> (char
'~
' *> read_form
)
84 read_deref
:: Parser MalVal
85 read_deref
= addPrefix
"deref" <$> (char
'@' *> read_form
)
87 read_with_meta
:: Parser MalVal
88 read_with_meta
= f
<$> (char
'^
' *> read_form
) <*> read_form
89 where f m x
= toList
[MalSymbol
"with-meta", x
, m
]
91 read_macro
:: Parser MalVal
92 read_macro
= read_quote
94 <|
> try read_splice_unquote
<|
> read_unquote
100 read_form
:: Parser MalVal
101 read_form
= ignored
*> (
108 read_str
:: String -> IOThrows MalVal
109 read_str str
= case parse read_form
"Mal" str
of
110 Left err
-> throwStr
$ show err
111 Right val
-> return val