2017-06-05 00:41:21 +03:00
|
|
|
module Reader exposing (..)
|
|
|
|
|
|
|
|
import Array
|
|
|
|
import Dict
|
2022-02-07 04:06:39 +03:00
|
|
|
import Parser exposing (DeadEnd, Parser, lazy, (|.), (|=))
|
2017-06-05 00:41:21 +03:00
|
|
|
import Types exposing (MalExpr(..), keywordPrefix)
|
|
|
|
import Utils exposing (decodeString, makeCall)
|
|
|
|
|
|
|
|
|
2022-02-07 04:06:39 +03:00
|
|
|
comment : Parser ()
|
2017-06-05 00:41:21 +03:00
|
|
|
comment =
|
2022-02-07 04:06:39 +03:00
|
|
|
Parser.lineComment ";"
|
2017-06-05 00:41:21 +03:00
|
|
|
|
|
|
|
|
2022-02-07 04:06:39 +03:00
|
|
|
ws : Parser ()
|
2017-06-05 00:41:21 +03:00
|
|
|
ws =
|
2022-02-07 04:06:39 +03:00
|
|
|
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 =
|
2022-02-07 04:06:39 +03:00
|
|
|
-- 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
|
|
|
|
|
|
|
|
2022-02-07 04:06:39 +03:00
|
|
|
symbolString : Parser String
|
2017-06-05 00:41:21 +03:00
|
|
|
symbolString =
|
2022-02-07 04:06:39 +03:00
|
|
|
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
|
|
|
|
|
|
|
|
2022-02-07 04:06:39 +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
|
2022-02-07 04:06:39 +03:00
|
|
|
Parser.map make symbolString
|
2017-06-05 00:41:21 +03:00
|
|
|
|
|
|
|
|
2022-02-07 04:06:39 +03:00
|
|
|
keywordString : Parser String
|
2017-06-05 00:41:21 +03:00
|
|
|
keywordString =
|
2022-02-07 04:06:39 +03:00
|
|
|
Parser.succeed identity
|
|
|
|
|. Parser.token ":"
|
|
|
|
|= symbolString
|
2017-06-05 00:41:21 +03:00
|
|
|
|
|
|
|
|
2022-02-07 04:06:39 +03:00
|
|
|
keyword : Parser MalExpr
|
2017-06-05 00:41:21 +03:00
|
|
|
keyword =
|
2022-02-07 04:06:39 +03:00
|
|
|
Parser.map MalKeyword keywordString
|
2017-06-05 00:41:21 +03:00
|
|
|
|
|
|
|
|
2022-02-07 04:06:39 +03:00
|
|
|
list : Parser MalExpr
|
2017-06-05 00:41:21 +03:00
|
|
|
list =
|
2022-02-07 04:06:39 +03:00
|
|
|
Parser.map MalList <| Parser.sequence
|
|
|
|
{ start = "("
|
|
|
|
, separator = ""
|
|
|
|
, end = ")"
|
|
|
|
, spaces = ws
|
|
|
|
, item = form
|
|
|
|
, trailing = Parser.Optional
|
|
|
|
}
|
2017-06-05 00:41:21 +03:00
|
|
|
|
|
|
|
|
2022-02-07 04:06:39 +03:00
|
|
|
vector : Parser MalExpr
|
2017-06-05 00:41:21 +03:00
|
|
|
vector =
|
2022-02-07 04:06:39 +03:00
|
|
|
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
|
|
|
|
|
|
|
|
2022-02-07 04:06:39 +03:00
|
|
|
mapKey : Parser String
|
2017-06-05 00:41:21 +03:00
|
|
|
mapKey =
|
2022-02-07 04:06:39 +03:00
|
|
|
Parser.oneOf
|
|
|
|
[ Parser.map (String.cons keywordPrefix) keywordString
|
|
|
|
, Parser.map decodeString strString
|
2017-06-05 00:41:21 +03:00
|
|
|
]
|
|
|
|
|
|
|
|
|
2022-02-07 04:06:39 +03:00
|
|
|
mapEntry : Parser ( String, MalExpr )
|
2017-06-05 00:41:21 +03:00
|
|
|
mapEntry =
|
2022-02-07 04:06:39 +03:00
|
|
|
Parser.succeed Tuple.pair |= mapKey |= form
|
2017-06-05 00:41:21 +03:00
|
|
|
|
|
|
|
|
2022-02-07 04:06:39 +03:00
|
|
|
map : Parser MalExpr
|
2017-06-05 00:41:21 +03:00
|
|
|
map =
|
2022-02-07 04:06:39 +03:00
|
|
|
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
|
|
|
|
|
|
|
|
2022-02-07 04:06:39 +03:00
|
|
|
atom : Parser MalExpr
|
2017-06-05 00:41:21 +03:00
|
|
|
atom =
|
2022-02-07 04:06:39 +03:00
|
|
|
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
|
|
|
]
|
|
|
|
|
|
|
|
|
2022-02-07 04:06:39 +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
|
2022-02-07 04:06:39 +03:00
|
|
|
Parser.succeed identity |. ws |= Parser.oneOf parsers
|
2017-06-05 00:41:21 +03:00
|
|
|
|
|
|
|
|
2022-02-07 04:06:39 +03:00
|
|
|
simpleMacro : String -> String -> Parser MalExpr
|
2017-06-05 00:41:21 +03:00
|
|
|
simpleMacro token symbol =
|
2022-02-07 04:06:39 +03:00
|
|
|
Parser.succeed (makeCall symbol << List.singleton)
|
|
|
|
|. Parser.token token
|
|
|
|
|= form
|
2017-06-05 00:41:21 +03:00
|
|
|
|
|
|
|
|
2022-02-07 04:06:39 +03:00
|
|
|
withMeta : Parser MalExpr
|
2017-06-05 00:41:21 +03:00
|
|
|
withMeta =
|
|
|
|
let
|
|
|
|
make meta expr =
|
|
|
|
makeCall "with-meta" [ expr, meta ]
|
|
|
|
in
|
2022-02-07 04:06:39 +03:00
|
|
|
Parser.succeed make
|
|
|
|
|. Parser.token "^"
|
|
|
|
|= form
|
|
|
|
|= form
|
2017-06-05 00:41:21 +03:00
|
|
|
|
|
|
|
|
2022-02-07 04:06:39 +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
|
|
|
|
|
2022-02-07 04:06:39 +03:00
|
|
|
Err deadEnds ->
|
|
|
|
-- Should become Err <| Parser.deadEndsToString deadEnds
|
|
|
|
-- once the function is implemented.
|
|
|
|
Err <| formatError deadEnds
|
2017-06-05 00:41:21 +03:00
|
|
|
|
|
|
|
|
2022-02-07 04:06:39 +03:00
|
|
|
formatError : List DeadEnd -> String
|
|
|
|
formatError =
|
2017-06-05 00:41:21 +03:00
|
|
|
let
|
2022-02-07 04:06:39 +03:00
|
|
|
format1 deadEnd =
|
|
|
|
Debug.toString deadEnd.problem
|
|
|
|
++ " at "
|
|
|
|
++ String.fromInt deadEnd.row
|
2017-06-05 00:41:21 +03:00
|
|
|
++ ":"
|
2022-02-07 04:06:39 +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
|
|
|
|
|
|
|
|
2022-02-07 04:06:39 +03:00
|
|
|
str : Parser MalExpr
|
2017-06-05 00:41:21 +03:00
|
|
|
str =
|
2022-02-07 04:06:39 +03:00
|
|
|
Parser.map (MalString << decodeString) strString
|
2017-06-05 00:41:21 +03:00
|
|
|
|
|
|
|
|
2022-02-07 04:06:39 +03:00
|
|
|
strString : Parser String
|
2017-06-05 00:41:21 +03:00
|
|
|
strString =
|
2022-02-07 04:06:39 +03:00
|
|
|
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
|
|
|
|
}
|