{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.SCargot.Scheme.R7RS where
import Data.Char (chr, isAlphaNum)
import Data.Text (Text)
import qualified Data.Text as T
import Data.String (IsString(..))
import Data.SCargot.Common
import Data.SCargot.General
import Data.SCargot.Repr.Basic
import Data.Word (Word8)
import Text.Parsec
import Text.Parsec.Text (Parser)
instance IsString (SchemeAtom c) where
fromString = ScmIdent . fromString
-- | A Scheme value type. This is strictly larger than just
-- 'atoms', as they may include things like vectors or
-- labeled data, which must be able to refer to yet other
-- s-expressions. Thus, the SchemeAtom type must be able
-- to itself refer to the carrier type in which it is
-- contained.
data SchemeAtom carrier
= ScmIdent Text
| ScmBool Bool
| ScmString Text
| ScmNum Integer
| ScmChar Char
| ScmVec [carrier (SchemeAtom carrier)]
| ScmByteVec [Word8]
| ScmLabeledDatum Int (carrier (SchemeAtom carrier))
| ScmLabelReference Int
-- | Scheme has a lot of numbers.
data SchemeNumber
= ScmNumber
| ScmComplexNumber Double Double
| ScmRealNumber Double
| ScmRationalNumber Rational
| ScmInteger Integer
deriving (Eq, Show)
deriving instance Show (c (SchemeAtom c)) => Show (SchemeAtom c)
deriving instance Eq (c (SchemeAtom c)) => Eq (SchemeAtom c)
badSpec :: SExprSpec (SchemeAtom SExpr) (SExpr (SchemeAtom SExpr))
badSpec = mkSpec (ScmIdent . T.pack <$> many1 (satisfy isAlphaNum)) undefined
withQuasiQuote :: SExprSpec (SchemeAtom SExpr) (SExpr (SchemeAtom SExpr))
-> SExprSpec (SchemeAtom SExpr) (SExpr (SchemeAtom SExpr))
withQuasiQuote spec = addReader '`' (fmap (go "quasiquote"))
$ addReader ',' unquote
$ addReader '\'' (fmap (go "quote"))
$ spec
where go name s = name ::: s ::: Nil
unquote p = char '@' *> fmap (go "unquote-splicing") p
<|> fmap (go "unquote") p
octoReader :: Reader (SchemeAtom SExpr)
octoReader pSexpr =
string "true" *> pure (A (ScmBool True))
<|> string "false" *> pure (A (ScmBool False))
<|> char 't' *> pure (A (ScmBool True))
<|> char 'f' *> pure (A (ScmBool False))
<|> char '\\' *> fmap (A . ScmChar) characterConstant
<|> char '(' *> fmap (A . ScmVec) (vector pSexpr)
<|> string "u8(" *> fmap A bytevec
<|> do n <- read <$> many1 digit
(char '#' *> pure (A (ScmLabelReference n)) <|>
char '=' *> fmap (A . ScmLabeledDatum n) pSexpr)
vector :: Parser (SExpr (SchemeAtom SExpr)) -> Parser [SExpr (SchemeAtom SExpr)]
vector pSExpr =
(char ')' *> pure []) <|> ((:) <$> pSExpr <*> vector pSExpr)
bytevec :: Parser (SchemeAtom SExpr)
bytevec = undefined
characterConstant :: Parser Char
characterConstant = namedCharacter
<|> (chr . fromInteger <$> (char 'x' *> hexNumber))
<|> anyCharacter
where namedCharacter = string "alarm" *> pure '\x07'
<|> string "backspace" *> pure '\x08'
<|> string "delete" *> pure '\x7f'
<|> string "escape" *> pure '\x1b'
<|> string "newline" *> pure '\x0a'
<|> string "null" *> pure '\x00'
<|> string "return" *> pure '\x0d'
<|> string "space" *> pure ' '
<|> string "tab" *> pure '\x09'
anyCharacter = anyToken
r7rsNum :: Int -> Parser Int
r7rsNum radix = prefix <*> complex
where prefix = radix <*> exactness <|> exactness <*> radix
complex = real
<|> real <* char '@' <*> real
<|> real <* char '+' <*> ureal <* char 'i'
<|> real <* char '-' <*> ureal <* char 'i'
<|> real <* char '+' <* char 'i'
<|> real <* char '-' <* char 'i'
<|> real <*> infnan <* char 'i'
<|> char '+' *> ureal <* char 'i'
<|> char '-' *> ureal <* char 'i'
<|> infnan <* char 'i'
<|> string "+i"
<|> string "-i"
real = ($) <$> sign <*> ureal
<|> infnan