module Language.Scheme.Primitives (
car
, cdr
, cons
, eq
, equal
, makeList
, listCopy
, buildVector
, vectorLength
, vectorRef
, vectorCopy
, vectorToList
, listToVector
, makeVector
, makeByteVector
, byteVector
, byteVectorLength
, byteVectorRef
, byteVectorCopy
, byteVectorAppend
, byteVectorUtf2Str
, byteVectorStr2Utf
, hashTblExists
, hashTblSize
, hashTbl2List
, hashTblKeys
, hashTblValues
, hashTblCopy
, hashTblMake
, wrapHashTbl
, wrapLeadObj
, buildString
, makeString
, doMakeString
, stringLength
, stringRef
, substring
, stringCIEquals
, stringCIBoolBinop
, stringAppend
, stringToNumber
, stringToList
, listToString
, stringToVector
, vectorToString
, stringCopy
, symbol2String
, string2Symbol
, charCIBoolBinop
, charPredicate
, charUpper
, charLower
, charDigitValue
, char2Int
, int2Char
, isHashTbl
, isChar
, isString
, isBoolean
, isBooleanEq
, isSymbolEq
, isDottedList
, isProcedure
, isList
, isVector
, isRecord
, isByteVector
, isNull
, isEOFObject
, isSymbol
, Unpacker ()
, unpackEquals
, boolBinop
, unaryOp
, unaryOp'
, strBoolBinop
, charBoolBinop
, boolBoolBinop
, unpackStr
, unpackBool
, makePort
, makeBufferPort
, openInputString
, openOutputString
, getOutputString
, openInputByteVector
, openOutputByteVector
, getOutputByteVector
, closePort
, flushOutputPort
, currentOutputPort
, currentInputPort
, isTextPort
, isBinaryPort
, isOutputPort
, isInputPort
, isInputPortOpen
, isOutputPortOpen
, isCharReady
, readProc
, readCharProc
, readByteVector
, readString
, writeProc
, writeCharProc
, writeByteVector
, writeString
, readContents
, load
, readAll
, fileExists
, deleteFile
, eofObject
, gensym
, _gensym
, currentTimestamp
, system
, getEnvVars
) where
import Language.Scheme.Numerical
import Language.Scheme.Parser
import Language.Scheme.Types
import Language.Scheme.Variables
import Control.Monad.Error
import qualified Data.ByteString as BS
import qualified Data.ByteString.UTF8 as BSU
import Data.Char hiding (isSymbol)
import Data.Array
import qualified Data.Knob as DK
import qualified Data.Map
import qualified Data.Time.Clock.POSIX
import Data.Unique
import Data.Word
import System.Directory (doesFileExist, removeFile)
import qualified System.Environment as SE
import System.Exit (ExitCode(..))
import System.IO
import System.IO.Error
import qualified System.Process
#if __GLASGOW_HASKELL__ < 702
try' = try
#else
try' :: IO a -> IO (Either IOError a)
try' = tryIOError
#endif
makePort
:: (FilePath -> IOMode -> IO Handle)
-> IOMode
-> [LispVal]
-> IOThrowsError LispVal
makePort openFnc mode [String filename] = do
h <- liftIO $ openFnc filename mode
return $ Port h Nothing
makePort fnc mode [p@(Pointer _ _)] = recDerefPtrs p >>= box >>= makePort fnc mode
makePort _ _ [] = throwError $ NumArgs (Just 1) []
makePort _ _ args@(_ : _) = throwError $ NumArgs (Just 1) args
makeBufferPort :: Maybe LispVal -> IOThrowsError LispVal
makeBufferPort buf = do
let mode = case buf of
Nothing -> WriteMode
_ -> ReadMode
bs <- case buf of
Just (String s)-> return $ BSU.fromString s
Just (ByteVector bv)-> return bv
Just err -> throwError $ TypeMismatch "string or bytevector" err
Nothing -> return $ BS.pack []
k <- DK.newKnob bs
h <- liftIO $ DK.newFileHandle k "temp.buf" mode
return $ Port h (Just k)
getBufferFromPort :: LispVal -> IOThrowsError BSU.ByteString
getBufferFromPort (Port h (Just k)) = do
_ <- liftIO $ hFlush h
DK.getContents k
getBufferFromPort args = do
throwError $ TypeMismatch "output-port" args
openInputString :: [LispVal] -> IOThrowsError LispVal
openInputString [p@(Pointer {})] = recDerefPtrs p >>= box >>= openInputString
openInputString [buf@(String _)] = makeBufferPort (Just buf)
openInputString args = if length args == 1
then throwError $ TypeMismatch "(string)" $ List args
else throwError $ NumArgs (Just 1) args
openOutputString :: [LispVal] -> IOThrowsError LispVal
openOutputString _ = makeBufferPort Nothing
openInputByteVector :: [LispVal] -> IOThrowsError LispVal
openInputByteVector [p@(Pointer {})] = recDerefPtrs p >>= box >>= openInputByteVector
openInputByteVector [buf@(ByteVector _)] = makeBufferPort (Just buf)
openInputByteVector args = if length args == 1
then throwError $ TypeMismatch "(bytevector)" $ List args
else throwError $ NumArgs (Just 1) args
openOutputByteVector :: [LispVal] -> IOThrowsError LispVal
openOutputByteVector _ = makeBufferPort Nothing
getOutputString :: [LispVal] -> IOThrowsError LispVal
getOutputString [p@(Pointer {})] = recDerefPtrs p >>= box >>= getOutputString
getOutputString [p@(Port port _)] = do
o <- liftIO $ hIsOpen port
if o then do
bytes <- getBufferFromPort p
return $ String $ BSU.toString bytes
else return $ String ""
getOutputString args = do
throwError $ TypeMismatch "output-port" $ List args
getOutputByteVector :: [LispVal] -> IOThrowsError LispVal
getOutputByteVector [p@(Pointer {})] = recDerefPtrs p >>= box >>= getOutputByteVector
getOutputByteVector [p@(Port port _)] = do
o <- liftIO $ hIsOpen port
if o then do bytes <- getBufferFromPort p
return $ ByteVector bytes
else return $ ByteVector $ BS.pack []
getOutputByteVector args = do
throwError $ TypeMismatch "output-port" $ List args
closePort :: [LispVal] -> IOThrowsError LispVal
closePort [p@(Pointer {})] = recDerefPtrs p >>= box >>= closePort
closePort [Port port _] = liftIO $ hClose port >> (return $ Bool True)
closePort _ = return $ Bool False
currentInputPort :: [LispVal] -> IOThrowsError LispVal
currentInputPort _ = return $ Port stdin Nothing
currentOutputPort :: [LispVal] -> IOThrowsError LispVal
currentOutputPort _ = return $ Port stdout Nothing
flushOutputPort :: [LispVal] -> IOThrowsError LispVal
flushOutputPort [] = liftIO $ hFlush stdout >> (return $ Bool True)
flushOutputPort [p@(Pointer {})] = recDerefPtrs p >>= box >>= flushOutputPort
flushOutputPort [p@(Port _ _)] =
withOpenPort p $ \port -> liftIO $ hFlush port >> (return $ Bool True)
flushOutputPort _ = return $ Bool False
isTextPort :: [LispVal] -> IOThrowsError LispVal
isTextPort [p@(Pointer {})] = recDerefPtrs p >>= box >>= isTextPort
isTextPort [Port port _] = do
val <- liftIO $ isTextPort' port
return $ Bool val
isTextPort _ = return $ Bool False
isBinaryPort :: [LispVal] -> IOThrowsError LispVal
isBinaryPort [p@(Pointer {})] = recDerefPtrs p >>= box >>= isBinaryPort
isBinaryPort [Port port _] = do
val <- liftIO $ isTextPort' port
return $ Bool $ not val
isBinaryPort _ = return $ Bool False
isTextPort' :: Handle -> IO Bool
isTextPort' port = do
textEncoding <- hGetEncoding port
case textEncoding of
Nothing -> return False
_ -> return True
isInputPortOpen :: [LispVal] -> IOThrowsError LispVal
isInputPortOpen [p@(Pointer {})] = recDerefPtrs p >>= box >>= isInputPortOpen
isInputPortOpen [p@(Port _ _)] = do
withOpenPort p $ \port -> do
r <- liftIO $ hIsReadable port
o <- liftIO $ hIsOpen port
return $ Bool $ r && o
isInputPortOpen _ = return $ Bool False
withOpenPort :: LispVal -> (Handle -> IOThrowsError LispVal) -> IOThrowsError LispVal
withOpenPort p@(Pointer {}) proc = do
obj <- recDerefPtrs p
withOpenPort obj proc
withOpenPort (Port port _) proc = do
o <- liftIO $ hIsOpen port
if o then proc port
else return $ Bool False
withOpenPort _ _ = return $ Bool False
isOutputPortOpen :: [LispVal] -> IOThrowsError LispVal
isOutputPortOpen [p@(Pointer {})] = recDerefPtrs p >>= box >>= isOutputPortOpen
isOutputPortOpen [p@(Port _ _)] = do
withOpenPort p $ \port -> do
w <- liftIO $ hIsWritable port
o <- liftIO $ hIsOpen port
return $ Bool $ w && o
isOutputPortOpen _ = return $ Bool False
isInputPort :: [LispVal] -> IOThrowsError LispVal
isInputPort [p@(Pointer {})] = recDerefPtrs p >>= box >>= isInputPort
isInputPort [p@(Port _ _)] =
withOpenPort p $ \port -> liftM Bool $ liftIO $ hIsReadable port
isInputPort _ = return $ Bool False
isOutputPort :: [LispVal] -> IOThrowsError LispVal
isOutputPort [p@(Pointer {})] = recDerefPtrs p >>= box >>= isOutputPort
isOutputPort [p@(Port _ _)] =
withOpenPort p $ \port -> liftM Bool $ liftIO $ hIsWritable port
isOutputPort _ = return $ Bool False
isCharReady :: [LispVal] -> IOThrowsError LispVal
isCharReady [p@(Pointer {})] = recDerefPtrs p >>= box >>= isCharReady
isCharReady [Port port _] = do
result <- liftIO $ try' (liftIO $ hReady port)
case result of
Left e -> if isEOFError e
then return $ Bool False
else throwError $ Default "I/O error reading from port"
Right _ -> return $ Bool True
isCharReady _ = return $ Bool False
readProc :: Bool -> [LispVal] -> IOThrowsError LispVal
readProc mode [] = readProc mode [Port stdin Nothing]
readProc mode [p@(Pointer {})] = recDerefPtrs p >>= box >>= readProc mode
readProc mode [Port port _] = do
input <- liftIO $ try' (liftIO $ hGetLine port)
case input of
Left e -> if isEOFError e
then return $ EOF
else throwError $ Default "I/O error reading from port"
Right inpStr -> do
liftThrows $
case mode of
True -> readExpr inpStr
_ -> return $ String inpStr
readProc _ args = if length args == 1
then throwError $ TypeMismatch "port" $ List args
else throwError $ NumArgs (Just 1) args
readCharProc :: (Handle -> IO Char) -> [LispVal] -> IOThrowsError LispVal
readCharProc func [p@(Pointer {})] = recDerefPtrs p >>= box >>= readCharProc func
readCharProc func [] = readCharProc func [Port stdin Nothing]
readCharProc func [p@(Port _ _)] = do
withOpenPort p $ \port -> do
liftIO $ hSetBuffering port NoBuffering
input <- liftIO $ try' (liftIO $ func port)
liftIO $ hSetBuffering port LineBuffering
case input of
Left e -> if isEOFError e
then return $ EOF
else throwError $ Default "I/O error reading from port"
Right inpChr -> do
return $ Char inpChr
readCharProc _ args = if length args == 1
then throwError $ TypeMismatch "port" $ List args
else throwError $ NumArgs (Just 1) args
readByteVector :: [LispVal] -> IOThrowsError LispVal
readByteVector args = readBuffer args ByteVector
readString :: [LispVal] -> IOThrowsError LispVal
readString args = readBuffer args (String . BSU.toString)
readBuffer :: [LispVal] -> (BSU.ByteString -> LispVal) -> IOThrowsError LispVal
readBuffer [Number n, Port port _] rvfnc = do
input <- liftIO $ try' (liftIO $ BS.hGet port $ fromInteger n)
case input of
Left e -> if isEOFError e
then return $ EOF
else throwError $ Default "I/O error reading from port"
Right inBytes -> do
if BS.null inBytes
then return $ EOF
else return $ rvfnc inBytes
readBuffer args _ = if length args == 2
then throwError $ TypeMismatch "(k port)" $ List args
else throwError $ NumArgs (Just 2) args
writeProc :: (Handle -> LispVal -> IO a)
-> [LispVal] -> ErrorT LispError IO LispVal
writeProc func [obj] = do
dobj <- recDerefPtrs obj
writeProc func [dobj, Port stdout Nothing]
writeProc func [obj, Port port _] = do
dobj <- recDerefPtrs obj
output <- liftIO $ try' (liftIO $ func port dobj)
case output of
Left _ -> throwError $ Default "I/O error writing to port"
Right _ -> return $ Nil ""
writeProc _ other = if length other == 2
then throwError $ TypeMismatch "(value port)" $ List other
else throwError $ NumArgs (Just 2) other
writeCharProc :: [LispVal] -> IOThrowsError LispVal
writeCharProc [obj] = writeCharProc [obj, Port stdout Nothing]
writeCharProc [obj@(Char _), Port port _] = do
output <- liftIO $ try' (liftIO $ (hPutStr port $ show obj))
case output of
Left _ -> throwError $ Default "I/O error writing to port"
Right _ -> return $ Nil ""
writeCharProc other = if length other == 2
then throwError $ TypeMismatch "(character port)" $ List other
else throwError $ NumArgs (Just 2) other
writeByteVector :: [LispVal] -> IOThrowsError LispVal
writeByteVector args = writeBuffer args bv2b
where
bv2b obj = do
ByteVector bs <- recDerefPtrs obj
return bs
writeString :: [LispVal] -> IOThrowsError LispVal
writeString args = writeBuffer args str2b
where
str2b obj = do
String str <- recDerefPtrs obj
return $ BSU.fromString str
writeBuffer :: [LispVal] -> (LispVal -> IOThrowsError BSU.ByteString) -> IOThrowsError LispVal
writeBuffer [obj, Port port _] getBS = do
bs <- getBS obj
output <- liftIO $ try' (liftIO $ BS.hPut port bs)
case output of
Left _ -> throwError $ Default "I/O error writing to port"
Right _ -> return $ Nil ""
writeBuffer other _ =
if length other == 2
then throwError $ TypeMismatch "(bytevector port)" $ List other
else throwError $ NumArgs (Just 2) other
fileExists :: [LispVal] -> IOThrowsError LispVal
fileExists [p@(Pointer _ _)] = recDerefPtrs p >>= box >>= fileExists
fileExists [String filename] = do
exists <- liftIO $ doesFileExist filename
return $ Bool exists
fileExists [] = throwError $ NumArgs (Just 1) []
fileExists args@(_ : _) = throwError $ NumArgs (Just 1) args
deleteFile :: [LispVal] -> IOThrowsError LispVal
deleteFile [p@(Pointer _ _)] = recDerefPtrs p >>= box >>= deleteFile
deleteFile [String filename] = do
output <- liftIO $ try' (liftIO $ removeFile filename)
case output of
Left _ -> return $ Bool False
Right _ -> return $ Bool True
deleteFile [] = throwError $ NumArgs (Just 1) []
deleteFile args@(_ : _) = throwError $ NumArgs (Just 1) args
readContents :: [LispVal] -> IOThrowsError LispVal
readContents [String filename] = liftM String $ liftIO $ readFile filename
readContents [p@(Pointer _ _)] = recDerefPtrs p >>= box >>= readContents
readContents [] = throwError $ NumArgs (Just 1) []
readContents args@(_ : _) = throwError $ NumArgs (Just 1) args
load :: String -> IOThrowsError [LispVal]
load filename = do
result <- liftIO $ doesFileExist filename
if result
then do
f <- liftIO $ readFile filename
case lines f of
(('#':'!':'/' : _) : ls) -> liftThrows . readExprList $ unlines ls
(('#':'!':' ':'/' : _) : ls) -> liftThrows . readExprList $ unlines ls
_ -> (liftThrows . readExprList) f
else throwError $ Default $ "File does not exist: " ++ filename
readAll :: [LispVal] -> IOThrowsError LispVal
readAll [p@(Pointer _ _)] = recDerefPtrs p >>= box >>= readAll
readAll [String filename] = liftM List $ load filename
readAll [] = do
input <- liftIO $ getContents
lisp <- (liftThrows . readExprList) input
return $ List lisp
readAll args@(_ : _) = throwError $ NumArgs (Just 1) args
_gensym :: String -> IOThrowsError LispVal
_gensym prefix = do
u <- liftIO $ newUnique
return $ Atom $ prefix ++ (show $ Number $ toInteger $ hashUnique u)
gensym :: [LispVal] -> IOThrowsError LispVal
gensym [p@(Pointer _ _)] = recDerefPtrs p >>= box >>= gensym
gensym [String prefix] = _gensym prefix
gensym [] = _gensym " g"
gensym args@(_ : _) = throwError $ NumArgs (Just 1) args
car :: [LispVal] -> IOThrowsError LispVal
car [p@(Pointer _ _)] = derefPtr p >>= box >>= car
car [List (x : _)] = return x
car [DottedList (x : _) _] = return x
car [badArg] = throwError $ TypeMismatch "pair" badArg
car badArgList = throwError $ NumArgs (Just 1) badArgList
cdr :: [LispVal] -> IOThrowsError LispVal
cdr [p@(Pointer _ _)] = derefPtr p >>= box >>= cdr
cdr [List (_ : xs)] = return $ List xs
cdr [DottedList [_] x] = return x
cdr [DottedList (_ : xs) x] = return $ DottedList xs x
cdr [badArg] = throwError $ TypeMismatch "pair" badArg
cdr badArgList = throwError $ NumArgs (Just 1) badArgList
cons :: [LispVal] -> IOThrowsError LispVal
cons [x, p@(Pointer _ _)] = do
y <- derefPtr p
cons [x, y]
cons [x1, List []] = return $ List [x1]
cons [x, List xs] = return $ List $ x : xs
cons [x, DottedList xs xlast] = return $ DottedList (x : xs) xlast
cons [x1, x2] = return $ DottedList [x1] x2
cons badArgList = throwError $ NumArgs (Just 2) badArgList
makeList :: [LispVal] -> ThrowsError LispVal
makeList [(Number n)] = makeList [Number n, List []]
makeList [(Number n), a] = do
let l = replicate (fromInteger n) a
return $ List l
makeList [badType] = throwError $ TypeMismatch "integer" badType
makeList badArgList = throwError $ NumArgs (Just 1) badArgList
listCopy :: [LispVal] -> IOThrowsError LispVal
listCopy [p@(Pointer _ _)] = do
l <- derefPtr p
listCopy [l]
listCopy [(List ls)] = return $ List ls
listCopy [badType] = return badType
listCopy badArgList = throwError $ NumArgs (Just 1) badArgList
vectorCopy :: [LispVal] -> IOThrowsError LispVal
vectorCopy (p@(Pointer _ _) : args) = do
v <- derefPtr p
vectorCopy (v : args)
vectorCopy [Vector vs] = do
let l = elems vs
return $ Vector $ listArray (0, length l 1) l
vectorCopy [Vector vs, Number start] = do
let l = drop (fromInteger start) $
elems vs
return $ Vector $ listArray (0, length l 1) l
vectorCopy [Vector vs, Number start, Number end] = do
let l = take (fromInteger $ end start) $
drop (fromInteger start) $
elems vs
return $ Vector $ listArray (0, length l 1) l
vectorCopy [badType] = return badType
vectorCopy badArgList = throwError $ NumArgs (Just 1) badArgList
eq :: [LispVal] -> IOThrowsError LispVal
eq [(Pointer pA envA), (Pointer pB envB)] = do
return $ Bool $ (pA == pB) && ((bindings envA) == (bindings envB))
eq args = recDerefToFnc eqv args
equal :: [LispVal] -> ThrowsError LispVal
equal [(Vector arg1), (Vector arg2)] = eqvList equal [List $ (elems arg1), List $ (elems arg2)]
equal [l1@(List _), l2@(List _)] = eqvList equal [l1, l2]
equal [(DottedList xs x), (DottedList ys y)] = equal [List $ xs ++ [x], List $ ys ++ [y]]
equal [arg1, arg2] = do
primitiveEquals <- liftM or $ mapM (unpackEquals arg1 arg2)
[AnyUnpacker unpackNum, AnyUnpacker unpackStr, AnyUnpacker unpackBool]
eqvEquals <- eqv [arg1, arg2]
return $ Bool $ (primitiveEquals || let (Bool x) = eqvEquals in x)
equal badArgList = throwError $ NumArgs (Just 2) badArgList
makeVector :: [LispVal] -> ThrowsError LispVal
makeVector [(Number n)] = makeVector [Number n, List []]
makeVector [(Number n), a] = do
let l = replicate (fromInteger n) a
return $ Vector $ (listArray (0, length l 1)) l
makeVector [badType] = throwError $ TypeMismatch "integer" badType
makeVector badArgList = throwError $ NumArgs (Just 1) badArgList
buildVector :: [LispVal] -> ThrowsError LispVal
buildVector lst@(_ : _) = do
return $ Vector $ (listArray (0, length lst 1)) lst
buildVector badArgList = throwError $ NumArgs (Just 1) badArgList
vectorLength :: [LispVal] -> ThrowsError LispVal
vectorLength [(Vector v)] = return $ Number $ toInteger $ length (elems v)
vectorLength [badType] = throwError $ TypeMismatch "vector" badType
vectorLength badArgList = throwError $ NumArgs (Just 1) badArgList
vectorRef :: [LispVal] -> ThrowsError LispVal
vectorRef [(Vector v), (Number n)] = do
let len = toInteger $ (length $ elems v) 1
if n > len || n < 0
then throwError $ Default "Invalid index"
else return $ v ! (fromInteger n)
vectorRef [badType] = throwError $ TypeMismatch "vector integer" badType
vectorRef badArgList = throwError $ NumArgs (Just 2) badArgList
vectorToList :: [LispVal] -> ThrowsError LispVal
vectorToList [(Vector v)] = return $ List $ elems v
vectorToList [badType] = throwError $ TypeMismatch "vector" badType
vectorToList badArgList = throwError $ NumArgs (Just 1) badArgList
listToVector :: [LispVal] -> ThrowsError LispVal
listToVector [(List l)] = return $ Vector $ (listArray (0, length l 1)) l
listToVector [badType] = throwError $ TypeMismatch "list" badType
listToVector badArgList = throwError $ NumArgs (Just 1) badArgList
makeByteVector :: [LispVal] -> ThrowsError LispVal
makeByteVector [(Number n)] = do
let ls = replicate (fromInteger n) (0 :: Word8)
return $ ByteVector $ BS.pack ls
makeByteVector [Number n, Number byte] = do
let ls = replicate (fromInteger n) (fromInteger byte :: Word8)
return $ ByteVector $ BS.pack ls
makeByteVector [badType] = throwError $ TypeMismatch "integer" badType
makeByteVector badArgList = throwError $ NumArgs (Just 2) badArgList
byteVector :: [LispVal] -> ThrowsError LispVal
byteVector bs = do
return $ ByteVector $ BS.pack $ map conv bs
where
conv (Number n) = fromInteger n :: Word8
conv _ = 0 :: Word8
byteVectorCopy :: [LispVal] -> IOThrowsError LispVal
byteVectorCopy (p@(Pointer _ _) : lvs) = do
bv <- derefPtr p
byteVectorCopy (bv : lvs)
byteVectorCopy [ByteVector bv] = do
return $ ByteVector $ BS.copy
bv
byteVectorCopy [ByteVector bv, Number start] = do
return $ ByteVector $ BS.drop
(fromInteger start)
bv
byteVectorCopy [ByteVector bv, Number start, Number end] = do
return $ ByteVector $ BS.take
(fromInteger $ end start)
(BS.drop
(fromInteger start)
bv)
byteVectorCopy [badType] = throwError $ TypeMismatch "bytevector" badType
byteVectorCopy badArgList = throwError $ NumArgs (Just 1) badArgList
byteVectorAppend :: [LispVal] -> IOThrowsError LispVal
byteVectorAppend bs = do
let conv :: LispVal -> IOThrowsError BSU.ByteString
conv p@(Pointer _ _) = derefPtr p >>= conv
conv (ByteVector bvs) = return bvs
conv _ = return BS.empty
bs' <- mapM conv bs
return $ ByteVector $ BS.concat bs'
byteVectorLength :: [LispVal] -> IOThrowsError LispVal
byteVectorLength [p@(Pointer _ _)] = derefPtr p >>= box >>= byteVectorLength
byteVectorLength [(ByteVector bv)] = return $ Number $ toInteger $ BS.length bv
byteVectorLength [badType] = throwError $ TypeMismatch "bytevector" badType
byteVectorLength badArgList = throwError $ NumArgs (Just 1) badArgList
byteVectorRef :: [LispVal] -> IOThrowsError LispVal
byteVectorRef (p@(Pointer _ _) : lvs) = do
bv <- derefPtr p
byteVectorRef (bv : lvs)
byteVectorRef [(ByteVector bv), (Number n)] = do
let len = toInteger $ (BS.length bv) 1
if n > len || n < 0
then throwError $ Default "Invalid index"
else return $ Number $ toInteger $ BS.index bv (fromInteger n)
byteVectorRef [badType] = throwError $ TypeMismatch "bytevector integer" badType
byteVectorRef badArgList = throwError $ NumArgs (Just 2) badArgList
byteVectorUtf2Str :: [LispVal] -> IOThrowsError LispVal
byteVectorUtf2Str [p@(Pointer _ _)] = derefPtr p >>= box >>= byteVectorUtf2Str
byteVectorUtf2Str [(ByteVector bv)] = do
return $ String $ BSU.toString bv
byteVectorUtf2Str [badType] = throwError $ TypeMismatch "bytevector" badType
byteVectorUtf2Str badArgList = throwError $ NumArgs (Just 1) badArgList
byteVectorStr2Utf :: [LispVal] -> IOThrowsError LispVal
byteVectorStr2Utf [p@(Pointer _ _)] = derefPtr p >>= box >>= byteVectorStr2Utf
byteVectorStr2Utf [(String s)] = do
return $ ByteVector $ BSU.fromString s
byteVectorStr2Utf [badType] = throwError $ TypeMismatch "string" badType
byteVectorStr2Utf badArgList = throwError $ NumArgs (Just 1) badArgList
wrapHashTbl :: ([LispVal] -> ThrowsError LispVal) -> [LispVal] -> IOThrowsError LispVal
wrapHashTbl fnc [p@(Pointer _ _)] = do
val <- derefPtr p
liftThrows $ fnc [val]
wrapHashTbl fnc (p@(Pointer _ _) : key : args) = do
ht <- derefPtr p
k <- recDerefPtrs key
liftThrows $ fnc (ht : k : args)
wrapHashTbl fnc args = liftThrows $ fnc args
wrapLeadObj :: ([LispVal] -> ThrowsError LispVal) -> [LispVal] -> IOThrowsError LispVal
wrapLeadObj fnc [p@(Pointer _ _)] = do
val <- derefPtr p
liftThrows $ fnc [val]
wrapLeadObj fnc (p@(Pointer _ _) : args) = do
obj <- derefPtr p
liftThrows $ fnc (obj : args)
wrapLeadObj fnc args = liftThrows $ fnc args
hashTblMake :: [LispVal] -> ThrowsError LispVal
hashTblMake _ = return $ HashTable $ Data.Map.fromList []
isHashTbl :: [LispVal] -> ThrowsError LispVal
isHashTbl [(HashTable _)] = return $ Bool True
isHashTbl _ = return $ Bool False
hashTblExists :: [LispVal] -> ThrowsError LispVal
hashTblExists [(HashTable ht), key] = do
case Data.Map.lookup key ht of
Just _ -> return $ Bool True
Nothing -> return $ Bool False
hashTblExists [] = throwError $ NumArgs (Just 2) []
hashTblExists args@(_ : _) = throwError $ NumArgs (Just 2) args
hashTblSize :: [LispVal] -> ThrowsError LispVal
hashTblSize [(HashTable ht)] = return $ Number $ toInteger $ Data.Map.size ht
hashTblSize [badType] = throwError $ TypeMismatch "hash-table" badType
hashTblSize badArgList = throwError $ NumArgs (Just 1) badArgList
hashTbl2List :: [LispVal] -> ThrowsError LispVal
hashTbl2List [(HashTable ht)] = do
return $ List $ map (\ (k, v) -> List [k, v]) $ Data.Map.toList ht
hashTbl2List [badType] = throwError $ TypeMismatch "hash-table" badType
hashTbl2List badArgList = throwError $ NumArgs (Just 1) badArgList
hashTblKeys :: [LispVal] -> ThrowsError LispVal
hashTblKeys [(HashTable ht)] = do
return $ List $ map fst $ Data.Map.toList ht
hashTblKeys [badType] = throwError $ TypeMismatch "hash-table" badType
hashTblKeys badArgList = throwError $ NumArgs (Just 1) badArgList
hashTblValues :: [LispVal] -> ThrowsError LispVal
hashTblValues [(HashTable ht)] = do
return $ List $ map snd $ Data.Map.toList ht
hashTblValues [badType] = throwError $ TypeMismatch "hash-table" badType
hashTblValues badArgList = throwError $ NumArgs (Just 1) badArgList
hashTblCopy :: [LispVal] -> ThrowsError LispVal
hashTblCopy [(HashTable ht)] = do
return $ HashTable $ Data.Map.fromList $ Data.Map.toList ht
hashTblCopy [badType] = throwError $ TypeMismatch "hash-table" badType
hashTblCopy badArgList = throwError $ NumArgs (Just 1) badArgList
buildString :: [LispVal] -> ThrowsError LispVal
buildString [(Char c)] = return $ String [c]
buildString (Char c : rest) = do
cs <- buildString rest
case cs of
String s -> return $ String $ c:s
badType -> throwError $ TypeMismatch "character" badType
buildString [badType] = throwError $ TypeMismatch "character" badType
buildString badArgList = throwError $ NumArgs (Just 1) badArgList
makeString :: [LispVal] -> ThrowsError LispVal
makeString [(Number n)] = return $ doMakeString n ' ' ""
makeString [(Number n), (Char c)] = return $ doMakeString n c ""
makeString badArgList = throwError $ NumArgs (Just 1) badArgList
doMakeString :: forall a . (Num a, Eq a) => a -> Char -> String -> LispVal
doMakeString n char s =
if n == 0
then String s
else doMakeString (n 1) char (s ++ [char])
stringLength :: [LispVal] -> IOThrowsError LispVal
stringLength [p@(Pointer _ _)] = derefPtr p >>= box >>= stringLength
stringLength [String s] = return $ Number $ foldr (const (+ 1)) 0 s
stringLength [badType] = throwError $ TypeMismatch "string" badType
stringLength badArgList = throwError $ NumArgs (Just 1) badArgList
stringRef :: [LispVal] -> IOThrowsError LispVal
stringRef [p@(Pointer _ _), k@(Number _)] = do
s <- derefPtr p
stringRef [s, k]
stringRef [(String s), (Number k)] = do
let len = toInteger $ (length s) 1
if k > len || k < 0
then throwError $ Default $ "Invalid index " ++ (show k)
else return $ Char $ s !! fromInteger k
stringRef [badType] = throwError $ TypeMismatch "string number" badType
stringRef badArgList = throwError $ NumArgs (Just 2) badArgList
substring :: [LispVal] -> IOThrowsError LispVal
substring (p@(Pointer _ _) : lvs) = do
s <- derefPtr p
substring (s : lvs)
substring [(String s), (Number start), (Number end)] =
do let slength = fromInteger $ end start
let begin = fromInteger start
return $ String $ (take slength . drop begin) s
substring [badType] = throwError $ TypeMismatch "string number number" badType
substring badArgList = throwError $ NumArgs (Just 3) badArgList
stringCIEquals :: [LispVal] -> IOThrowsError LispVal
stringCIEquals args = do
List dargs <- recDerefPtrs $ List args
case dargs of
[(String str1), (String str2)] -> do
if (length str1) /= (length str2)
then return $ Bool False
else return $ Bool $ ciCmp str1 str2 0
[badType] -> throwError $ TypeMismatch "string string" badType
badArgList -> throwError $ NumArgs (Just 2) badArgList
where ciCmp s1 s2 idx =
(idx == (length s1)) ||
(((toLower $ s1 !! idx) == (toLower $ s2 !! idx)) &&
ciCmp s1 s2 (idx + 1))
stringCIBoolBinop :: (String -> String -> Bool) -> [LispVal] -> IOThrowsError LispVal
stringCIBoolBinop op args = do
List dargs <- recDerefPtrs $ List args
case dargs of
[(String s1), (String s2)] ->
liftThrows $ boolBinop unpackStr op [(String $ strToLower s1), (String $ strToLower s2)]
[badType] -> throwError $ TypeMismatch "string string" badType
badArgList -> throwError $ NumArgs (Just 2) badArgList
where strToLower = map toLower
charCIBoolBinop :: (Char -> Char -> Bool) -> [LispVal] -> ThrowsError LispVal
charCIBoolBinop op [(Char s1), (Char s2)] = boolBinop unpackChar op [(Char $ toLower s1), (Char $ toLower s2)]
charCIBoolBinop _ [badType] = throwError $ TypeMismatch "character character" badType
charCIBoolBinop _ badArgList = throwError $ NumArgs (Just 2) badArgList
stringAppend :: [LispVal] -> IOThrowsError LispVal
stringAppend (p@(Pointer _ _) : lvs) = do
s <- derefPtr p
stringAppend (s : lvs)
stringAppend [(String s)] = return $ String s
stringAppend (String st : sts) = do
rest <- stringAppend sts
case rest of
String s -> return $ String $ st ++ s
other -> throwError $ TypeMismatch "string" other
stringAppend [] = return $ String ""
stringAppend [badType] = throwError $ TypeMismatch "string" badType
stringAppend badArgList = throwError $ NumArgs (Just 1) badArgList
stringToNumber :: [LispVal] -> IOThrowsError LispVal
stringToNumber (p@(Pointer _ _) : lvs) = do
s <- derefPtr p
stringToNumber (s : lvs)
stringToNumber [(String s)] = do
result <- liftThrows $ readExpr s
case result of
n@(Number _) -> return n
n@(Rational _) -> return n
n@(Float _) -> return n
n@(Complex _) -> return n
_ -> return $ Bool False
stringToNumber [(String s), Number radix] = do
case radix of
2 -> stringToNumber [String $ "#b" ++ s]
8 -> stringToNumber [String $ "#o" ++ s]
10 -> stringToNumber [String s]
16 -> stringToNumber [String $ "#x" ++ s]
_ -> throwError $ Default $ "Invalid radix: " ++ show radix
stringToNumber [badType] = throwError $ TypeMismatch "string" badType
stringToNumber badArgList = throwError $ NumArgs (Just 1) badArgList
stringToList :: [LispVal] -> IOThrowsError LispVal
stringToList (p@(Pointer _ _) : ps) = do
p' <- derefPtr p
stringToList (p' : ps)
stringToList [(String s)] = return $ List $ map Char s
stringToList [String s, Number start] =
return $ List $ map Char $ trimStart start s
stringToList [String s, Number start, Number end] =
return $ List $ map Char $ trimStartEnd start end s
stringToList [badType] = throwError $ TypeMismatch "string" badType
stringToList badArgList = throwError $ NumArgs (Just 1) badArgList
trimStart :: Integer -> [a] -> [a]
trimStart start = drop (fromInteger start)
trimStartEnd :: Integer -> Integer -> [a] -> [a]
trimStartEnd start end ls =
take (fromInteger $ end start) $ drop (fromInteger start) ls
listToString :: [LispVal] -> IOThrowsError LispVal
listToString [p@(Pointer _ _)] = derefPtr p >>= box >>= listToString
listToString [(List [])] = return $ String ""
listToString [(List l)] = liftThrows $ buildString l
listToString [badType] = throwError $ TypeMismatch "list" badType
listToString [] = throwError $ NumArgs (Just 1) []
listToString args@(_ : _) = throwError $ NumArgs (Just 1) args
stringToVector :: [LispVal] -> IOThrowsError LispVal
stringToVector args = do
List l <- stringToList args
return $ Vector $ listArray (0, length l 1) l
vectorToString :: [LispVal] -> IOThrowsError LispVal
vectorToString (p@(Pointer _ _) : ps) = do
p' <- derefPtr p
vectorToString (p' : ps)
vectorToString [(Vector v)] = do
let l = elems v
case l of
[] -> return $ String ""
_ -> liftThrows $ buildString l
vectorToString [Vector v, Number start] = do
listToString [List $ trimStart start (elems v)]
vectorToString [Vector v, Number start, Number end] = do
listToString [List $ trimStartEnd start end (elems v)]
vectorToString [badType] = throwError $ TypeMismatch "vector" badType
vectorToString [] = throwError $ NumArgs (Just 1) []
vectorToString args@(_ : _) = throwError $ NumArgs (Just 1) args
stringCopy :: [LispVal] -> IOThrowsError LispVal
stringCopy (p@(Pointer _ _) : args) = do
s <- derefPtr p
stringCopy (s : args)
stringCopy [String s] = return $ String s
stringCopy [String s, Number start] = do
return $ String $ trimStart start s
stringCopy [String s, Number start, Number end] = do
return $ String $ trimStartEnd start end s
stringCopy [badType] = throwError $ TypeMismatch "string" badType
stringCopy badArgList = throwError $ NumArgs (Just 2) badArgList
isDottedList :: [LispVal] -> IOThrowsError LispVal
isDottedList ([p@(Pointer _ _)]) = derefPtr p >>= box >>= isDottedList
isDottedList ([DottedList _ _]) = return $ Bool True
isDottedList ([List []]) = return $ Bool False
isDottedList ([List _]) = return $ Bool True
isDottedList _ = return $ Bool False
isProcedure :: [LispVal] -> ThrowsError LispVal
isProcedure ([Continuation {}]) = return $ Bool True
isProcedure ([PrimitiveFunc _]) = return $ Bool True
isProcedure ([Func {}]) = return $ Bool True
isProcedure ([HFunc {}]) = return $ Bool True
isProcedure ([IOFunc _]) = return $ Bool True
isProcedure ([EvalFunc _]) = return $ Bool True
isProcedure ([CustFunc _]) = return $ Bool True
isProcedure _ = return $ Bool False
isVector :: LispVal -> IOThrowsError LispVal
isVector p@(Pointer _ _) = derefPtr p >>= isVector
isVector (Vector vs) = do
case elems vs of
((Atom " record-marker ") : _) -> return $ Bool False
_ -> return $ Bool True
isVector _ = return $ Bool False
isRecord :: LispVal -> IOThrowsError LispVal
isRecord p@(Pointer _ _) = derefPtr p >>= isRecord
isRecord (Vector vs) = do
case (elems vs) of
((Atom " record-marker ") : _) -> return $ Bool True
_ -> return $ Bool False
isRecord _ = return $ Bool False
isByteVector :: LispVal -> IOThrowsError LispVal
isByteVector p@(Pointer _ _) = derefPtr p >>= isVector
isByteVector (ByteVector _) = return $ Bool True
isByteVector _ = return $ Bool False
isList :: LispVal -> IOThrowsError LispVal
isList p@(Pointer _ _) = derefPtr p >>= isList
isList (List _) = return $ Bool True
isList _ = return $ Bool False
isNull :: [LispVal] -> IOThrowsError LispVal
isNull ([p@(Pointer _ _)]) = derefPtr p >>= box >>= isNull
isNull ([List []]) = return $ Bool True
isNull _ = return $ Bool False
isEOFObject :: [LispVal] -> ThrowsError LispVal
isEOFObject ([EOF]) = return $ Bool True
isEOFObject _ = return $ Bool False
eofObject :: [LispVal] -> ThrowsError LispVal
eofObject _ = return $ EOF
isSymbol :: [LispVal] -> ThrowsError LispVal
isSymbol ([Atom _]) = return $ Bool True
isSymbol _ = return $ Bool False
symbol2String :: [LispVal] -> ThrowsError LispVal
symbol2String ([Atom a]) = return $ String a
symbol2String [notAtom] = throwError $ TypeMismatch "symbol" notAtom
symbol2String [] = throwError $ NumArgs (Just 1) []
symbol2String args@(_ : _) = throwError $ NumArgs (Just 1) args
string2Symbol :: [LispVal] -> IOThrowsError LispVal
string2Symbol ([p@(Pointer _ _)]) = derefPtr p >>= box >>= string2Symbol
string2Symbol ([String s]) = return $ Atom s
string2Symbol [] = throwError $ NumArgs (Just 1) []
string2Symbol [notString] = throwError $ TypeMismatch "string" notString
string2Symbol args@(_ : _) = throwError $ NumArgs (Just 1) args
charUpper :: [LispVal] -> ThrowsError LispVal
charUpper [Char c] = return $ Char $ toUpper c
charUpper [notChar] = throwError $ TypeMismatch "char" notChar
charUpper args = throwError $ NumArgs (Just 1) args
charLower :: [LispVal] -> ThrowsError LispVal
charLower [Char c] = return $ Char $ toLower c
charLower [notChar] = throwError $ TypeMismatch "char" notChar
charLower args = throwError $ NumArgs (Just 1) args
charDigitValue :: [LispVal] -> ThrowsError LispVal
charDigitValue [Char c] = do
if isHexDigit c
then return $ Number $ toInteger $ digitToInt c
else return $ Bool False
charDigitValue [notChar] = throwError $ TypeMismatch "char" notChar
charDigitValue args = throwError $ NumArgs (Just 1) args
char2Int :: [LispVal] -> ThrowsError LispVal
char2Int [Char c] = return $ Number $ toInteger $ ord c
char2Int [notChar] = throwError $ TypeMismatch "char" notChar
char2Int args = throwError $ NumArgs (Just 1) args
int2Char :: [LispVal] -> ThrowsError LispVal
int2Char [Number n] = return $ Char $ chr $ fromInteger n
int2Char [notInt] = throwError $ TypeMismatch "integer" notInt
int2Char args = throwError $ NumArgs (Just 1) args
charPredicate :: (Char -> Bool) -> [LispVal] -> ThrowsError LispVal
charPredicate cpred ([Char c]) = return $ Bool $ cpred c
charPredicate _ _ = return $ Bool False
isChar :: [LispVal] -> ThrowsError LispVal
isChar ([Char _]) = return $ Bool True
isChar _ = return $ Bool False
isString :: [LispVal] -> IOThrowsError LispVal
isString [p@(Pointer _ _)] = derefPtr p >>= box >>= isString
isString ([String _]) = return $ Bool True
isString _ = return $ Bool False
isBoolean :: [LispVal] -> ThrowsError LispVal
isBoolean ([Bool _]) = return $ Bool True
isBoolean _ = return $ Bool False
isBooleanEq :: Monad m => [LispVal] -> m LispVal
isBooleanEq (Bool a : Bool b : bs)
| a == b = isBooleanEq (Bool b : bs)
| otherwise = return $ Bool False
isBooleanEq [Bool _] = return $ Bool True
isBooleanEq _ = return $ Bool False
isSymbolEq :: Monad m => [LispVal] -> m LispVal
isSymbolEq (Atom a : Atom b : bs)
| a == b = isSymbolEq (Atom b : bs)
| otherwise = return $ Bool False
isSymbolEq [Atom _] = return $ Bool True
isSymbolEq _ = return $ Bool False
data Unpacker = forall a . Eq a => AnyUnpacker (LispVal -> ThrowsError a)
unpackEquals :: LispVal -> LispVal -> Unpacker -> ThrowsError Bool
unpackEquals arg1 arg2 (AnyUnpacker unpacker) =
do unpacked1 <- unpacker arg1
unpacked2 <- unpacker arg2
return $ unpacked1 == unpacked2
`catchError` (const $ return False)
boolBinop :: (LispVal -> ThrowsError a) -> (a -> a -> Bool) -> [LispVal] -> ThrowsError LispVal
boolBinop unpacker op args = if length args < 2
then throwError $ NumArgs (Just 2) args
else do
result <- cmp (head args) (tail args)
return $ Bool result
where
cmp b1 (b2 : bs) = do
b1' <- unpacker b1
b2' <- unpacker b2
let result = op b1' b2'
if result
then cmp b2 bs
else return False
cmp _ _ = return True
unaryOp :: (LispVal -> ThrowsError LispVal) -> [LispVal] -> ThrowsError LispVal
unaryOp f [v] = f v
unaryOp _ [] = throwError $ NumArgs (Just 1) []
unaryOp _ args@(_ : _) = throwError $ NumArgs (Just 1) args
unaryOp' :: (LispVal -> IOThrowsError LispVal) -> [LispVal] -> IOThrowsError LispVal
unaryOp' f [v] = f v
unaryOp' _ [] = throwError $ NumArgs (Just 1) []
unaryOp' _ args@(_ : _) = throwError $ NumArgs (Just 1) args
strBoolBinop :: (String -> String -> Bool) -> [LispVal] -> IOThrowsError LispVal
strBoolBinop fnc args = do
List dargs <- recDerefPtrs $ List args
liftThrows $ boolBinop unpackStr fnc dargs
charBoolBinop :: (Char -> Char -> Bool)
-> [LispVal] -> ThrowsError LispVal
charBoolBinop = boolBinop unpackChar
boolBoolBinop :: (Bool -> Bool -> Bool) -> [LispVal] -> ThrowsError LispVal
boolBoolBinop = boolBinop unpackBool
unpackChar :: LispVal -> ThrowsError Char
unpackChar (Char c) = return c
unpackChar notChar = throwError $ TypeMismatch "character" notChar
unpackStr :: LispVal -> ThrowsError String
unpackStr (String s) = return s
unpackStr (Number s) = return $ show s
unpackStr (Bool s) = return $ show s
unpackStr notString = throwError $ TypeMismatch "string" notString
unpackBool :: LispVal -> ThrowsError Bool
unpackBool (Bool b) = return b
unpackBool notBool = throwError $ TypeMismatch "boolean" notBool
currentTimestamp :: [LispVal] -> IOThrowsError LispVal
currentTimestamp _ = do
cur <- liftIO $ Data.Time.Clock.POSIX.getPOSIXTime
return $ Float $ realToFrac cur
system :: [LispVal] -> IOThrowsError LispVal
system [String cmd] = do
result <- liftIO $ System.Process.system cmd
case result of
ExitSuccess -> return $ Number 0
ExitFailure code -> return $ Number $ toInteger code
system err = throwError $ TypeMismatch "string" $ List err
getEnvVars :: [LispVal] -> IOThrowsError LispVal
getEnvVars _ = do
vars <- liftIO $ SE.getEnvironment
return $ List $ map (\ (k, v) -> DottedList [String k] (String v)) vars