1
1
mirror of https://github.com/kanaka/mal.git synced 2024-11-10 12:47:45 +03:00
mal/impls/elm/Reader.elm

248 lines
6.0 KiB
Elm
Raw Normal View History

2017-06-05 00:41:21 +03:00
module Reader exposing (..)
import Array
import Dict
import Parser exposing (DeadEnd, Parser, lazy, (|.), (|=))
2017-06-05 00:41:21 +03:00
import Types exposing (MalExpr(..), keywordPrefix)
import Utils exposing (decodeString, makeCall)
comment : Parser ()
2017-06-05 00:41:21 +03:00
comment =
Parser.lineComment ";"
2017-06-05 00:41:21 +03:00
ws : Parser ()
2017-06-05 00:41:21 +03:00
ws =
let
isSpaceChar : Char -> Bool
isSpaceChar c = List.member c [' ', '\n', '\r', ',']
in
Parser.succeed ()
|. Parser.sequence
{ start = ""
, separator = ""
, end = ""
, spaces = Parser.chompWhile isSpaceChar
, item = comment
, trailing = Parser.Optional
}
int : Parser MalExpr
2017-06-05 00:41:21 +03:00
int =
-- Parser.map MalInt Parser.int fails with elm/parser 1.1.0
let
isDigit : Char -> Bool
isDigit c = '0' <= c && c <= '9'
toInt s = case String.toInt s of
Just r -> MalInt r
Nothing -> Debug.todo "should not happen"
in
Parser.map toInt <| Parser.getChompedString <|
Parser.chompIf isDigit
|. Parser.chompWhile isDigit
2017-06-05 00:41:21 +03:00
symbolString : Parser String
2017-06-05 00:41:21 +03:00
symbolString =
let
isSymbolChar : Char -> Bool
isSymbolChar c =
not (List.member c [' ', '\n', '\r', ',', '\\', '[', ']',
'{', '}', '(', '\'', '"', '`', ';', ')'])
in
Parser.getChompedString <|
Parser.chompIf isSymbolChar
|. Parser.chompWhile isSymbolChar
2017-06-05 00:41:21 +03:00
symbolOrConst : Parser MalExpr
2017-06-23 17:56:04 +03:00
symbolOrConst =
let
make sym =
case sym of
"nil" ->
MalNil
"true" ->
MalBool True
"false" ->
MalBool False
_ ->
MalSymbol sym
in
Parser.map make symbolString
2017-06-05 00:41:21 +03:00
keywordString : Parser String
2017-06-05 00:41:21 +03:00
keywordString =
Parser.succeed identity
|. Parser.token ":"
|= symbolString
2017-06-05 00:41:21 +03:00
keyword : Parser MalExpr
2017-06-05 00:41:21 +03:00
keyword =
Parser.map MalKeyword keywordString
2017-06-05 00:41:21 +03:00
list : Parser MalExpr
2017-06-05 00:41:21 +03:00
list =
Parser.map MalList <| Parser.sequence
{ start = "("
, separator = ""
, end = ")"
, spaces = ws
, item = form
, trailing = Parser.Optional
}
2017-06-05 00:41:21 +03:00
vector : Parser MalExpr
2017-06-05 00:41:21 +03:00
vector =
Parser.map (MalVector << Array.fromList) <| Parser.sequence
{ start = "["
, separator = ""
, end = "]"
, spaces = ws
, item = form
, trailing = Parser.Optional
}
2017-06-05 00:41:21 +03:00
mapKey : Parser String
2017-06-05 00:41:21 +03:00
mapKey =
Parser.oneOf
[ Parser.map (String.cons keywordPrefix) keywordString
, Parser.map decodeString strString
2017-06-05 00:41:21 +03:00
]
mapEntry : Parser ( String, MalExpr )
2017-06-05 00:41:21 +03:00
mapEntry =
Parser.succeed Tuple.pair |= mapKey |= form
2017-06-05 00:41:21 +03:00
map : Parser MalExpr
2017-06-05 00:41:21 +03:00
map =
Parser.map (MalMap << Dict.fromList) <| Parser.sequence
{ start = "{"
, separator = ""
, end = "}"
, spaces = ws
, item = mapEntry
, trailing = Parser.Optional
}
2017-06-05 00:41:21 +03:00
atom : Parser MalExpr
2017-06-05 00:41:21 +03:00
atom =
Parser.oneOf
[ Parser.succeed identity
|. Parser.token "-"
|= Parser.oneOf
[ Parser.map (MalInt << negate) Parser.int
, Parser.map (MalSymbol << (++) "-") symbolString
, Parser.succeed (MalSymbol "-")
]
, int
2017-06-05 00:41:21 +03:00
, keyword
2017-06-23 17:56:04 +03:00
, symbolOrConst
, str
2017-06-05 00:41:21 +03:00
]
form : Parser MalExpr
2017-06-05 00:41:21 +03:00
form =
lazy <|
\() ->
let
parsers =
[ list
, vector
, map
, simpleMacro "'" "quote"
, simpleMacro "`" "quasiquote"
, simpleMacro "~@" "splice-unquote"
, simpleMacro "~" "unquote"
, simpleMacro "@" "deref"
, withMeta
, atom
]
in
Parser.succeed identity |. ws |= Parser.oneOf parsers
2017-06-05 00:41:21 +03:00
simpleMacro : String -> String -> Parser MalExpr
2017-06-05 00:41:21 +03:00
simpleMacro token symbol =
Parser.succeed (makeCall symbol << List.singleton)
|. Parser.token token
|= form
2017-06-05 00:41:21 +03:00
withMeta : Parser MalExpr
2017-06-05 00:41:21 +03:00
withMeta =
let
make meta expr =
makeCall "with-meta" [ expr, meta ]
in
Parser.succeed make
|. Parser.token "^"
|= form
|= form
2017-06-05 00:41:21 +03:00
readString : String -> Result String MalExpr
readString str2 =
case Parser.run (form |. ws |. Parser.end) str2 of
Ok ast ->
2017-06-05 00:41:21 +03:00
Ok ast
Err deadEnds ->
-- Should become Err <| Parser.deadEndsToString deadEnds
-- once the function is implemented.
Err <| formatError deadEnds
2017-06-05 00:41:21 +03:00
formatError : List DeadEnd -> String
formatError =
2017-06-05 00:41:21 +03:00
let
format1 deadEnd =
Debug.toString deadEnd.problem
++ " at "
++ String.fromInt deadEnd.row
2017-06-05 00:41:21 +03:00
++ ":"
++ String.fromInt deadEnd.col
in
(++) "end of input\n" << String.join "\n" << List.map format1
2017-06-05 00:41:21 +03:00
str : Parser MalExpr
2017-06-05 00:41:21 +03:00
str =
Parser.map (MalString << decodeString) strString
2017-06-05 00:41:21 +03:00
strString : Parser String
2017-06-05 00:41:21 +03:00
strString =
let
isStringNormalChar : Char -> Bool
isStringNormalChar c = not <| List.member c ['"', '\\']
in
Parser.getChompedString <|
Parser.sequence
{ start = "\""
, separator = ""
, end = "\""
, spaces = Parser.succeed ()
, item = Parser.oneOf
[ Parser.chompIf isStringNormalChar
|. Parser.chompWhile isStringNormalChar
, Parser.token "\\"
|. Parser.chompIf (\_ -> True)
]
, trailing = Parser.Forbidden
}