1
1
mirror of https://github.com/kanaka/mal.git synced 2024-09-20 01:57:09 +03:00
mal/elm/Reader.elm
2017-06-05 12:23:33 +02:00

204 lines
3.9 KiB
Elm

module Reader exposing (..)
import Array
import Dict
import Combine exposing (..)
import Combine.Num
import Types exposing (MalExpr(..), keywordPrefix)
import Utils exposing (decodeString, makeCall)
comment : Parser s String
comment =
regex ";.*"
ws : Parser s (List String)
ws =
many (comment <|> string "," <|> whitespace)
nil : Parser s MalExpr
nil =
MalNil <$ string "nil" <?> "nil"
int : Parser s MalExpr
int =
MalInt <$> Combine.Num.int <?> "int"
bool : Parser s MalExpr
bool =
MalBool
<$> choice
[ True <$ string "true"
, False <$ string "false"
]
<?> "bool"
symbolString : Parser s String
symbolString =
regex "[^\\s\\[\\]{}('\"`,;)]+"
symbol : Parser s MalExpr
symbol =
MalSymbol
<$> symbolString
<?> "symbol"
keywordString : Parser s String
keywordString =
(++)
<$> string ":"
<*> symbolString
keyword : Parser s MalExpr
keyword =
MalKeyword <$> keywordString
list : Parser s MalExpr
list =
MalList
<$> parens (many form <* ws)
<?> "list"
vector : Parser s MalExpr
vector =
MalVector
<< Array.fromList
<$> (string "["
*> many form
<* ws
<* string "]"
)
<?> "vector"
mapKey : Parser s String
mapKey =
choice
[ String.cons keywordPrefix <$> keywordString
, decodeString <$> strString
]
mapEntry : Parser s ( String, MalExpr )
mapEntry =
(,) <$> mapKey <*> form <?> "map entry"
map : Parser s MalExpr
map =
lazy <|
\() ->
MalMap
<< Dict.fromList
<$> (string "{"
*> many (ws *> mapEntry)
<* ws
<* string "}"
)
<?> "map"
atom : Parser s MalExpr
atom =
choice
[ int
, bool
, str
, nil
, keyword
, symbol
]
<?> "atom"
form : Parser s MalExpr
form =
lazy <|
\() ->
let
parsers =
[ list
, vector
, map
, simpleMacro "'" "quote"
, simpleMacro "`" "quasiquote"
, simpleMacro "~@" "splice-unquote"
, simpleMacro "~" "unquote"
, simpleMacro "@" "deref"
, withMeta
, atom
]
in
ws *> choice parsers <?> "form"
simpleMacro : String -> String -> Parser s MalExpr
simpleMacro token symbol =
makeCall symbol
<< List.singleton
<$> (string token *> form)
<?> symbol
withMeta : Parser s MalExpr
withMeta =
lazy <|
\() ->
let
make meta expr =
makeCall "with-meta" [ expr, meta ]
in
make
<$> (string "^" *> map)
<*> form
<?> "with-meta"
readString : String -> Result String (Maybe MalExpr)
readString str =
case parse ((maybe form) <* ws <* end) str of
Ok ( _, _, ast ) ->
Ok ast
Err ( _, stream, ms ) ->
Err <| formatError ms stream
formatError : List String -> InputStream -> String
formatError ms stream =
let
location =
currentLocation stream
in
"Parse error: "
++ String.join ", " ms
++ " "
++ "(at "
++ toString location.line
++ ":"
++ toString location.column
++ ")"
str : Parser s MalExpr
str =
MalString << decodeString <$> strString
{-| Syntax highlighter in VS code is messed up by this regex,
that's why it's down below. :)
-}
strString : Parser s String
strString =
regex "\"(\\\\\"|[^\"])*\"" <?> "string"