haskeem-0.7.4: Library.hs
{- Copyright 2008 Uwe Hollerbach <uh@alumni.caltech.edu>
Portions of this were derived from Jonathan Tang's haskell
tutorial "Write yourself a scheme in 48 hours" and are thus
Copyright Jonathan Tang
(but I can't easily tell anymore who originally wrote what)
This file is part of haskeem.
haskeem is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
haskeem is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with haskeem; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
$Id: library.hs,v 1.30 2009-06-27 21:51:29 uwe Exp $ -}
module Library (primitiveBindings, delayCounter, symbolCounter, loadFile, eqv)
where
import Prelude
import IO
import Data.Bits
import Data.Char
import Data.Ratio
import Control.Monad.Error as CME
import Data.IORef
import System.Directory
import System.Posix.Env
import System.Posix.Files
import System.Posix.Types()
import System.Exit
import System.Time
import System.CPUTime
import System.Random
import qualified Data.IntMap as DIM
import Network
import LispData
import Parser
import Environment
import WriteNumber
errNumArgs name want got = throwError (NumArgs name want got)
errTypeMismatch name want got = throwError (TypeMismatch name want got)
genericBadArg :: [LispVal] -> String -> String -> Int
-> ThrowsError LispVal
genericBadArg badArgList func want num =
if num < 0 || length badArgList == num
then errTypeMismatch func want (List badArgList)
else errNumArgs func (toInteger num) badArgList
genericIOBadArg :: [LispVal] -> String -> String -> Int
-> IOThrowsError LispVal
genericIOBadArg badArgList func want num =
if length badArgList == num
then errTypeMismatch func want (badArgList !! 0)
else errNumArgs func (toInteger num) badArgList
-- A bunch of library functions that don't do IO:
-- these get put into the primitives table below
isChar :: [LispVal] -> ThrowsError LispVal
isChar [Char _] = return lispTrue
isChar _ = return lispFalse
isBool :: [LispVal] -> ThrowsError LispVal
isBool [Boolean _] = return lispTrue
isBool _ = return lispFalse
isNumber :: [LispVal] -> ThrowsError LispVal
isNumber [IntNumber _] = return lispTrue
isNumber [RatNumber _] = return lispTrue
isNumber [FltNumber _] = return lispTrue
isNumber _ = return lispFalse
isInteger :: [LispVal] -> ThrowsError LispVal
isInteger [IntNumber _] = return lispTrue
isInteger [RatNumber n] = return (Boolean ((denominator n) == 1))
isInteger _ = return lispFalse
isRational :: [LispVal] -> ThrowsError LispVal
isRational [IntNumber _] = return lispTrue
isRational [RatNumber _] = return lispTrue
isRational _ = return lispFalse
isReal :: [LispVal] -> ThrowsError LispVal
isReal [IntNumber _] = return lispTrue
isReal [RatNumber _] = return lispTrue
isReal [FltNumber _] = return lispTrue
isReal _ = return lispFalse
isString :: [LispVal] -> ThrowsError LispVal
isString [String _] = return lispTrue
isString _ = return lispFalse
isSymbol :: [LispVal] -> ThrowsError LispVal
isSymbol [Symbol _] = return lispTrue
isSymbol _ = return lispFalse
isList :: [LispVal] -> ThrowsError LispVal
isList [List _] = return lispTrue
isList _ = return lispFalse
isPair :: [LispVal] -> ThrowsError LispVal
isPair [List []] = return lispFalse
isPair [List _] = return lispTrue
isPair [DottedList _ _] = return lispTrue
isPair _ = return lispFalse
isPort :: [LispVal] -> ThrowsError LispVal
isPort [Port _] = return lispTrue
isPort [Socket _] = return lispTrue
isPort _ = return lispFalse
isProcedure :: [LispVal] -> ThrowsError LispVal
isProcedure [Prim _] = return lispTrue
isProcedure [IOPrim _] = return lispTrue
isProcedure [Func _ _ _ _ _ _] = return lispTrue
isProcedure _ = return lispFalse
isVector :: [LispVal] -> ThrowsError LispVal
isVector [Vector _ _] = return lispTrue
isVector _ = return lispFalse
isNull :: [LispVal] -> ThrowsError LispVal
isNull [List []] = return lispTrue
isNull _ = return lispFalse
isZero :: [LispVal] -> ThrowsError LispVal
isZero [IntNumber n] =
if n == 0 then return lispTrue else return lispFalse
isZero [RatNumber n] =
if (n == 0) then return lispTrue else return lispFalse
isZero [FltNumber n] =
if n == 0 then return lispTrue else return lispFalse
isZero _ = return lispFalse
isPositive :: [LispVal] -> ThrowsError LispVal
isPositive [IntNumber n] =
if n > 0 then return lispTrue else return lispFalse
isPositive [RatNumber n] =
if n > 0 then return lispTrue else return lispFalse
isPositive [FltNumber n] =
if n > 0 then return lispTrue else return lispFalse
isPositive _ = return lispFalse
isNegative :: [LispVal] -> ThrowsError LispVal
isNegative [IntNumber n] =
if n < 0 then return lispTrue else return lispFalse
isNegative [RatNumber n] =
if n < 0 then return lispTrue else return lispFalse
isNegative [FltNumber n] =
if n < 0 then return lispTrue else return lispFalse
isNegative _ = return lispFalse
-- The treatment of Inf and NaN is not quite according to R6RS here. they say
-- (/ 1 0) causes an exception, but I can't find what happens if the parser
-- sees the number 1/0. I think it ought to be Inf, or perhaps RatInf. So I'm
-- going to return true for isInf for the rational numbers 1/0 and -1/0, and
-- true for isNaN for 0/0, allow entering of both (/ 1 0) and 1/0 plus ninf
-- and nan, and deal with these when they arise in computations
lispIsNaN :: [LispVal] -> ThrowsError LispVal
lispIsNaN [RatNumber n] =
return (Boolean (((numerator n) == 0) && ((denominator n) == 0)))
lispIsNaN [FltNumber n] = return (Boolean (isNaN n))
lispIsNaN _ = return lispFalse
lispIsInf :: [LispVal] -> ThrowsError LispVal
lispIsInf [RatNumber n] =
return (Boolean (((numerator n) /= 0) && ((denominator n) == 0)))
lispIsInf [FltNumber n] = return (Boolean (isInfinite n))
lispIsInf _ = return lispFalse
lispIsFinite :: [LispVal] -> ThrowsError LispVal
lispIsFinite [IntNumber _] = return lispTrue
lispIsFinite [RatNumber n] = return (Boolean ((denominator n) /= 0))
lispIsFinite [FltNumber n] =
return (Boolean (not ((isInfinite n) && (isNaN n))))
lispIsFinite _ = return lispFalse
lispIsEven :: [LispVal] -> ThrowsError LispVal
lispIsEven [IntNumber n] | even n = return lispTrue
| otherwise = return lispFalse
lispIsEven _ = return lispFalse
lispIsOdd :: [LispVal] -> ThrowsError LispVal
lispIsOdd [IntNumber n] | even n = return lispFalse
| otherwise = return lispTrue
lispIsOdd _ = return lispFalse
lispId :: [LispVal] -> ThrowsError LispVal
lispId [val@(_)] = return val
lispNot :: [LispVal] -> ThrowsError LispVal
lispNot [Boolean False] = return lispTrue
lispNot _ = return lispFalse
unpackChar :: LispVal -> ThrowsError Char
unpackChar (Char c) = return c
unpackChar notChar = errTypeMismatch "<unpackChar>" "character" notChar
unpackIntNum :: LispVal -> ThrowsError Integer
unpackIntNum (IntNumber n) = return n
unpackIntNum (RatNumber n) =
if (denominator n) /= 0
then return (truncate n)
else errTypeMismatch "<unpackIntNum>" "number" (RatNumber n)
unpackIntNum (FltNumber n) = return (truncate n)
unpackIntNum (List [n]) = unpackIntNum n
unpackIntNum notNum = errTypeMismatch "<unpackIntNum>" "number" notNum
unpackRatNum :: LispVal -> ThrowsError Rational
unpackRatNum (IntNumber n) = return (fromInteger n)
unpackRatNum (RatNumber n) = return n
unpackRatNum (FltNumber n) = return (toRational n)
unpackRatNum (List [n]) = unpackRatNum n
unpackRatNum notNum = errTypeMismatch "<unpackRatNum>" "number" notNum
unpackFltNum :: LispVal -> ThrowsError Double
unpackFltNum (IntNumber n) = return (fromInteger n)
unpackFltNum (RatNumber n) = return (fromRational n)
unpackFltNum (FltNumber n) = return n
unpackFltNum (List [n]) = unpackFltNum n
unpackFltNum notNum = errTypeMismatch "<unpackFltNum>" "number" notNum
unpackStr :: LispVal -> ThrowsError String
unpackStr (String s) = return s
unpackStr (IntNumber n) = return (show n)
unpackStr (RatNumber n) = return (show n)
unpackStr (FltNumber n) = return (show n)
unpackStr (Boolean b) = return (show b)
unpackStr notString = errTypeMismatch "<unpackStr>" "string" notString
-- first cut at "number tower": if we have a set of args that are all
-- integers, we want to keep everything as an integer, including the result;
-- if there's a rational in there somewhere, promote them all to rationals;
-- ditto for doubles.
isIntType, isRatType, isFltType, isOther :: Int
isIntType = 0
isRatType = 1
isFltType = 2
isOther = 3
libRatNaN, libFltNaN, libRatPInf :: LispVal
libRatNaN = RatNumber myRatNaN
libFltNaN = FltNumber myFltNaN
libRatPInf = RatNumber myRatPInf
scanRatNaN :: [LispVal] -> Bool
scanRatNaN [] = False
scanRatNaN (IntNumber _:vs) = scanRatNaN vs
scanRatNaN (RatNumber v:vs) = if v == myRatNaN then True else scanRatNaN vs
scanFltNaN :: [LispVal] -> Bool
scanFltNaN [] = False
scanFltNaN (IntNumber _:vs) = scanFltNaN vs
scanFltNaN (RatNumber v:vs) = if v == myRatNaN then True else scanFltNaN vs
scanFltNaN (FltNumber v:vs) = if isNaN v then True else scanFltNaN vs
getType :: [LispVal] -> Int
getType p = foldl1 max (map g p)
where g (IntNumber _) = isIntType
g (RatNumber _) = isRatType
g (FltNumber _) = isFltType
g _ = isOther
isNumType :: Int -> Bool
isNumType n = (n == isIntType || n == isRatType || n == isFltType)
intOrRat :: Rational -> LispVal
intOrRat n | (denominator n) == 1 = (IntNumber (numerator n))
| otherwise = (RatNumber n)
numericBinop :: String -> (Integer -> Integer -> Integer) ->
(Rational -> Rational -> Rational) ->
(Double -> Double -> Double) ->
[LispVal] -> ThrowsError LispVal
numericBinop name intOp ratOp dblOp av =
do let mytype = getType av
if length av < 2
then errNumArgs name 2 ((String (show (length av))) : av)
else if mytype == isIntType
then mapM unpackIntNum av >>=
return . IntNumber . foldl1 intOp
else if mytype == isRatType
then if scanRatNaN av
then return libRatNaN
else mapM unpackRatNum av >>=
return . intOrRat . foldl1 ratOp
else if mytype == isFltType
then if scanFltNaN av
then return libFltNaN
else mapM unpackFltNum av >>=
return . FltNumber . foldl1 dblOp
else errTypeMismatch name "number" (List av)
integerBinop :: String -> (Integer -> Integer -> Integer) ->
[LispVal] -> ThrowsError LispVal
integerBinop name intOp av =
do let mytype = getType av
if length av < 2
then errNumArgs name 2 av
else if mytype == isIntType
then mapM unpackIntNum av >>=
return . IntNumber . foldl1 intOp
else errTypeMismatch name "number" (List av)
numBoolBinop :: String -> (Integer -> Integer -> Bool) ->
(Rational -> Rational -> Bool) ->
(Double -> Double -> Bool) ->
Bool -> [LispVal] -> ThrowsError LispVal
numBoolBinop name intOp ratOp dblOp nanval av =
do let mytype = getType av
if length av < 2
then errNumArgs name 2 av
else if mytype == isIntType
then do ll <- unpackIntNum (av !! 0)
rr <- unpackIntNum (av !! 1)
return (Boolean (intOp ll rr))
else if mytype == isRatType
then if scanRatNaN av
then return (Boolean nanval)
else do ll <- unpackRatNum (av !! 0)
rr <- unpackRatNum (av !! 1)
return (Boolean (ratOp ll rr))
else if mytype == isFltType
then if scanFltNaN av
then return (Boolean nanval)
else do ll <- unpackFltNum (av !! 0)
rr <- unpackFltNum (av !! 1)
return (Boolean (dblOp ll rr))
else errTypeMismatch name "number" (List av)
numericFunc :: String -> (Double -> Double) -> [LispVal] -> ThrowsError LispVal
numericFunc name fun av =
do let mytype = getType av
if length av /= 1
then errNumArgs name 1 av
else if isNumType mytype
then if scanFltNaN av
then return libFltNaN
else unpackFltNum (av !! 0) >>=
return . FltNumber . fun
else errTypeMismatch name "number" (List av)
lispNumerator :: [LispVal] -> ThrowsError LispVal
lispNumerator [IntNumber n] = return (IntNumber n)
lispNumerator [RatNumber n] = return (IntNumber (numerator n))
lispNumerator (val:[]) = errTypeMismatch "-" "number" (String (show val))
lispNumerator badArgList =
genericBadArg badArgList "numerator" "integer or rational" 1
lispDenominator :: [LispVal] -> ThrowsError LispVal
lispDenominator [IntNumber _] = return (IntNumber 1)
lispDenominator [RatNumber n] = return (IntNumber (denominator n))
lispDenominator (val:[]) = errTypeMismatch "-" "number" (String (show val))
lispDenominator badArgList =
genericBadArg badArgList "denominator" "integer or rational" 1
-- I want haskeem to be able to deal with rational-number infinities and
-- nans, so this stuff works around haskell's normalization of rational
-- numbers, which breaks for non-finite numbers like 1/0 or 0/0.
-- There are some asymmetries in these case tables, arising from the fact
-- that there are positive and negative infinities, but not also positive
-- and negative zero; so for example the reciprocal of -inf is 0, and thus
-- (reciprocal (reciprocal -inf)) is +inf rather than -inf. In that respect,
-- rational infs and nan aren't numbers; but then, they are also not
-- number-like in that they are their own successors: inf + 1 = inf. One of
-- the SRFIs points this out as a reason to only have inexact, ie
-- floating-point, infinities and NaNs; but I still prefer to have
-- rational-format infinities and NaNs when all calculations are only
-- integers and rationals: if the final answer in such a calculation comes
-- out finite, ie by dividing by an infinity, it will remain an exact
-- quantity; that is mathematically correct, but would not be the case if
-- infinities were necessarily inexact.
myrecip :: Rational -> Rational
myrecip n | n == myRatNaN = myRatNaN
| (n == myRatPInf || n == myRatNInf) = 0
| n == 0 = myRatPInf
| otherwise = (recip n)
mymul :: Rational -> Rational -> Rational
mymul n1 n2 | ((denominator n1 /= 0) &&
(denominator n2 /= 0)) = n1 * n2
| (n1 == myRatNaN || n2 == myRatNaN) ||
(n1 == 0 && (n2 == myRatPInf ||
n2 == myRatNInf)) ||
(n2 == 0 && (n1 == myRatPInf ||
n1 == myRatNInf)) = myRatNaN
| (sg n1) == (sg n2) = myRatPInf
| otherwise = myRatNInf
where sg n = if n > 0 then 1 else -1
myadd :: Rational -> Rational -> Rational
myadd n1 n2 | ((denominator n1 /= 0) &&
(denominator n2 /= 0)) = n1 + n2
| (n1 == myRatNaN || n2 == myRatNaN) ||
(n1 == myRatPInf && n2 == myRatNInf) ||
(n1 == myRatNInf && n2 == myRatPInf) = myRatNaN
| (denominator n1 /= 0) = n2
| otherwise = n1
mypow :: Rational -> Integer -> Rational
mypow b e | (b == myRatNaN) = myRatNaN
| (b == myRatPInf && e > 0) = myRatPInf
| (b == myRatPInf && e == 0) = myRatNaN
| (b == myRatNInf && e > 0 && even e) = myRatPInf
| (b == myRatNInf && e > 0 && odd e) = myRatNInf
| (b == myRatNInf && e == 0) = myRatNaN
| (b == 0 && e > 0) = 0
| (e == 0) = 1 -- including 0**0,
-- mandated by R6RS
| (e < 0) = mypow (myrecip b) (negate e)
| otherwise = b ^ e
addOp = numericBinop "+" (+) myadd (+)
mulOp = numericBinop "*" (*) mymul (*)
lispPlus :: [LispVal] -> ThrowsError LispVal
lispPlus [] = return (IntNumber 0)
lispPlus [IntNumber n] = return (IntNumber n)
lispPlus [RatNumber n] = return (RatNumber n)
lispPlus [FltNumber n] = return (FltNumber n)
lispPlus (val:[]) = errTypeMismatch "+" "number" (String (show val))
lispPlus (v:vs) = addOp (v:vs)
lispMinus :: [LispVal] -> ThrowsError LispVal
lispMinus [IntNumber n] = return (IntNumber (negate n))
lispMinus [RatNumber n] = return (RatNumber (negate n))
lispMinus [FltNumber n] = return (FltNumber (negate n))
lispMinus (val:[]) = errTypeMismatch "-" "number" (String (show val))
lispMinus (a:as) =
do aux <- addOp ((IntNumber 0):as)
rec <- lispMinus [aux]
res <- addOp (a:[rec])
return res
lispMul :: [LispVal] -> ThrowsError LispVal
lispMul [] = return (IntNumber 1)
lispMul [IntNumber n] = return (IntNumber n)
lispMul [RatNumber n] = return (RatNumber n)
lispMul [FltNumber n] = return (FltNumber n)
lispMul (val:[]) = errTypeMismatch "*" "number" (String (show val))
lispMul (v:vs) = mulOp (v:vs)
lispDiv :: [LispVal] -> ThrowsError LispVal
lispDiv [IntNumber n] = return (RatNumber (myrecip (fromInteger n)))
lispDiv [RatNumber n] =
let nr = myrecip n
in if (denominator nr) == 1
then return (IntNumber (numerator nr))
else return (RatNumber nr)
lispDiv [FltNumber n] = return (FltNumber (1.0 / n))
lispDiv (val:[]) = errTypeMismatch "/" "number" (String (show val))
lispDiv (a:as) =
do aux <- mulOp ((IntNumber 1):as)
rec <- lispDiv [aux]
res <- mulOp (a:[rec])
return res
lispMin :: [LispVal] -> ThrowsError LispVal
lispMin [] = errNumArgs "min" 1 []
lispMin [IntNumber n] = return (IntNumber n)
lispMin [RatNumber n] = return (RatNumber n)
lispMin [FltNumber n] = return (FltNumber n)
lispMin (val:[]) = errTypeMismatch "min" "number" (String (show val))
lispMin (a:as) = numericBinop "min" min min min (a:as)
lispMax :: [LispVal] -> ThrowsError LispVal
lispMax [] = errNumArgs "max" 1 []
lispMax [IntNumber n] = return (IntNumber n)
lispMax [RatNumber n] = return (RatNumber n)
lispMax [FltNumber n] = return (FltNumber n)
lispMax (val:[]) = errTypeMismatch "max" "number" (String (show val))
lispMax (a:as) = numericBinop "max" max max max (a:as)
fltpow :: Double -> Double -> LispVal
fltpow x y | y == 0 = IntNumber 1
| (x == 0 && y > 0) = IntNumber 0
| (x == 0 && y < 0) = libRatPInf
| ((abs x) < 1 && y > 0 && isInfinite y) = IntNumber 0
| ((abs x) > 1 && y < 0 && isInfinite y) = IntNumber 0
| otherwise = FltNumber (x ** y)
spow :: Rational -> Rational -> LispVal
spow x y | x < -1 && y == myRatPInf = libRatNaN
| x < 0 && x > -1 && y == myRatNInf = libRatNaN
| x == -1 && (abs y) == myRatPInf = libRatNaN
| x == myRatNInf && y > 0 && (denominator y) > 1 = libRatNaN
| (abs x) == myRatPInf && y > 0 = libRatPInf
| x > 1 && y == myRatPInf = libRatPInf
| x > 0 && x < 1 && y == myRatNInf = libRatPInf
| (abs x) == myRatPInf && y < 0 = IntNumber 0
| x == 1 = IntNumber 1
| otherwise = fltpow (fromRational x) (fromRational y)
ratpow :: Rational -> Rational -> LispVal
ratpow x y =
if (denominator y) == 1
then let pow = mypow x (numerator y)
in if (denominator pow) == 1
then (IntNumber (numerator pow))
else (RatNumber pow)
else (spow x y)
lispPow :: [LispVal] -> ThrowsError LispVal
lispPow av =
if length av /= 2
then do errNumArgs "**" 2 av
else do let mytype = getType av
if isNumType mytype
then if (mytype == isIntType || mytype == isRatType)
then if scanRatNaN av
then return libRatNaN
else do b <- unpackRatNum (av !! 0)
e <- unpackRatNum (av !! 1)
return (ratpow b e)
else if scanFltNaN av
then return libFltNaN
else do b <- unpackFltNum (av !! 0)
e <- unpackFltNum (av !! 1)
return (fltpow b e)
else errTypeMismatch "expt" "number" (List av)
boolBinop :: (LispVal -> ThrowsError a) -> String -> (a -> a -> Bool) ->
[LispVal] -> ThrowsError LispVal
boolBinop unpacker name op args =
if length args /= 2
then errNumArgs name 2 args
else do ll <- unpacker (args !! 0)
rr <- unpacker (args !! 1)
return (Boolean (ll `op` rr))
strBoolBinop = boolBinop unpackStr
charBoolBinop = boolBinop unpackChar
car :: [LispVal] -> ThrowsError LispVal
car [List (x:_)] = return x
car [DottedList (x:_) _] = return x
car badArgList = genericBadArg badArgList "car" "pair or list" 1
cdr :: [LispVal] -> ThrowsError LispVal
cdr [List (_:xs)] = return (List xs)
cdr [DottedList (_:[]) x] = return x
cdr [DottedList (_:xs) x] = return (DottedList xs x)
cdr badArgList = genericBadArg badArgList "cdr" "pair or list" 1
cons :: [LispVal] -> ThrowsError LispVal
cons [xl, List []] = return (List [xl])
cons [x, List xs] = return (List ([x] ++ xs))
cons [x, DottedList xs xl] = return (DottedList ([x] ++ xs) xl)
cons [x1, x2] = return (DottedList [x1] x2)
cons badArgList = errNumArgs "cons" 2 badArgList
eqv :: [LispVal] -> Bool
eqv [(Boolean v1), (Boolean v2)] = (v1 == v2)
eqv [(Char c1), (Char c2)] = (c1 == c2)
eqv [(IntNumber v1), (IntNumber v2)] = (v1 == v2)
eqv [(RatNumber v1), (RatNumber v2)] = (v1 == v2)
eqv [(FltNumber v1), (FltNumber v2)] = (v1 == v2) || ((isNaN v1) && (isNaN v2))
eqv [(String v1), (String v2)] = (v1 == v2)
eqv [(Symbol v1), (Symbol v2)] = (v1 == v2)
eqv [(DottedList l1 t1), (DottedList l2 t2)] =
eqv [(List (l1 ++ [t1])), (List (l2 ++ [t2]))]
eqv [(List l1), (List l2)] =
((length l1 == length l2) && (and (map eqvPair (zip l1 l2))))
where eqvPair (x1,x2) = eqv [x1,x2]
eqv [(Vector l1 v1), (Vector l2 v2)] =
((l1 == l2) && (and (map eqvPair (zip (getval (DIM.toAscList v1))
(getval (DIM.toAscList v2))))))
where eqvPair (x1,x2) = eqv [x1,x2]
getval [] = []
getval ((_,v):vs) = v:(getval vs)
eqv _ = False
eqvFunc :: [LispVal] -> ThrowsError LispVal
eqvFunc (v1:v2:[]) = return (Boolean (eqv [v1,v2]))
eqvFunc badArgList = genericBadArg badArgList "eqv?" "matched types" 2
char2int :: [LispVal] -> ThrowsError LispVal
char2int [Char c] = return (IntNumber (toInteger (ord c)))
char2int badArgList = genericBadArg badArgList "char->integer" "character" 1
int2char :: [LispVal] -> ThrowsError LispVal
int2char [IntNumber c] = return (Char (chr (fromInteger c)))
int2char badArgList = genericBadArg badArgList "integer->char" "integer" 1
char2str :: [LispVal] -> ThrowsError LispVal
char2str [] = return (String "")
char2str [List chars] = char2str chars
char2str chars = mapM unpackChar chars >>= return . String . (foldr (:) [])
str2char :: [LispVal] -> ThrowsError LispVal
str2char [] = return (List [])
str2char [String s] = return (List (map Char s))
str2char badArgList = genericBadArg badArgList "string->char" "string" 1
symb2str :: [LispVal] -> ThrowsError LispVal
symb2str [] = return (String "")
symb2str [Symbol s] = return (String s)
symb2str badArgList = genericBadArg badArgList "symbol->string" "string" 1
readNum :: [LispVal] -> ThrowsError LispVal
readNum [String s] = readNumber s
readNum badArgList = genericBadArg badArgList "string->number" "string" 1
charIs :: (Char -> Bool) -> [LispVal] -> ThrowsError LispVal
charIs op [Char c] = return (Boolean (op c))
charIs _ badArgList = genericBadArg badArgList "char-istype?" "character" 1
charTo :: (Char -> Char) -> [LispVal] -> ThrowsError LispVal
charTo op [Char char] = return (Char (op char))
charTo op [List chars] = charTo op chars
charTo op [String str] = return (String (map op str))
charTo op chars =
mapM oneTo chars >>= return . List
where oneTo (Char c) = return (Char (op c))
oneTo notChar = errTypeMismatch "charTo" "character" notChar
lispFloor :: [LispVal] -> ThrowsError LispVal
lispFloor [IntNumber c] = return (IntNumber c)
lispFloor [RatNumber c] =
if (denominator c) /= 0
then return (IntNumber (floor c))
else return (RatNumber c)
lispFloor [FltNumber c] = return (IntNumber (floor c))
lispFloor badArgList = genericBadArg badArgList "floor" "number" 1
lispTruncate :: [LispVal] -> ThrowsError LispVal
lispTruncate [IntNumber c] = return (IntNumber c)
lispTruncate [RatNumber c] =
if (denominator c) /= 0
then return (IntNumber (truncate c))
else return (RatNumber c)
lispTruncate [FltNumber c] = return (IntNumber (truncate c))
lispTruncate badArgList = genericBadArg badArgList "truncate" "number" 1
-- TODO: implement relative-tolerance rounding, so that specifying
-- 3 digits relative gives X.XXXeXX, for any exponent
-- round to a {rational,floating-point} number with a specified non-negative
-- number of digits to the right of the decimal point; this is a number of
-- the same type as the input
roundToAP :: RealFrac a => a -> Integer -> Integer -> a
roundToAP n b d =
let bd = (fromInteger (b^d))
in (fromInteger (round (n * bd)))/bd
-- equivalent of the above, except with the truncation point to the left
-- of the decimal point; that means the result is always an integer
roundToAN :: RealFrac a => a -> Integer -> Integer -> Integer
roundToAN n b d =
let bd = b^d
in bd*(round (n/(fromInteger bd)))
lispRound :: [LispVal] -> ThrowsError LispVal
lispRound [IntNumber c] = return (IntNumber c)
lispRound [RatNumber c] =
if (denominator c) /= 0
then return (IntNumber (round c))
else return (RatNumber c)
lispRound [FltNumber c] = return (IntNumber (round c))
lispRound [IntNumber c, IntNumber b, IntNumber d] =
if b > 1
then if d >= 0
then return (IntNumber c)
else return (IntNumber (roundToAN (toRational c) b (- d)))
else throwError (Default ("bad base arg to round: " ++ (show b)))
lispRound [RatNumber c, IntNumber b, IntNumber d] =
if (denominator c) /= 0
then if b > 1
then if d > 0
then return (RatNumber (roundToAP c b d))
else return (IntNumber (roundToAN c b (- d)))
else throwError (Default ("bad base arg to round: " ++ (show b)))
else return (RatNumber c)
lispRound [FltNumber c, IntNumber b, IntNumber d] =
if b > 1
then if d > 0
then return (FltNumber (roundToAP c b d))
else return (IntNumber (roundToAN c b (- d)))
else throwError (Default ("bad base arg to round: " ++ (show b)))
lispRound badArgList = genericBadArg badArgList "round" "number" 1
lispCeiling :: [LispVal] -> ThrowsError LispVal
lispCeiling [IntNumber c] = return (IntNumber c)
lispCeiling [RatNumber c] =
if (denominator c) /= 0
then return (IntNumber (ceiling c))
else return (RatNumber c)
lispCeiling [FltNumber c] = return (IntNumber (ceiling c))
lispCeiling badArgList = genericBadArg badArgList "ceiling" "number" 1
lispAbs :: [LispVal] -> ThrowsError LispVal
lispAbs [IntNumber c] = return (IntNumber (abs c))
lispAbs [RatNumber n] = return (RatNumber (abs n))
lispAbs [FltNumber c] = return (FltNumber (abs c))
lispAbs badArgList = genericBadArg badArgList "abs" "number" 1
lispATan2 :: [LispVal] -> ThrowsError LispVal
lispATan2 av =
do let mytype = getType av
if isNumType mytype
then if scanFltNaN av
then return libFltNaN
else case length av of
1 -> mapM unpackFltNum av >>=
return . FltNumber . atan . head
2 -> mapM unpackFltNum av >>=
return . FltNumber . at2
_ -> errNumArgs "atan" 2 av
else errTypeMismatch "atan" "number" (List av)
where at2 (y:x:[]) = atan2 y x
lispListFromArgs :: [LispVal] -> ThrowsError LispVal
lispListFromArgs vals = return (List vals)
lispReverse :: [LispVal] -> ThrowsError LispVal
lispReverse [List lst] = return (List (reverse lst))
lispReverse badArgList = genericBadArg badArgList "reverse" "list" 1
lispLast :: [LispVal] -> ThrowsError LispVal
lispLast [List []] = return (List [])
lispLast [List lst] = return (last lst)
lispLast badArgList = genericBadArg badArgList "last" "list" 1
lispLength :: [LispVal] -> ThrowsError LispVal
lispLength [List lst] = return (IntNumber (toInteger (length lst)))
lispLength badArgList = genericBadArg badArgList "length" "list" 1
lispListHead :: [LispVal] -> ThrowsError LispVal
lispListHead [List lst, IntNumber n] = return (List (take (fromInteger n) lst))
lispListHead badArgList =
genericBadArg badArgList "list-head" "list + number" 2
lispListTail :: [LispVal] -> ThrowsError LispVal
lispListTail [List lst, IntNumber n] = return (List (drop (fromInteger n) lst))
lispListTail badArgList =
genericBadArg badArgList "list-tail" "list + number" 2
lispListRef :: [LispVal] -> ThrowsError LispVal
lispListRef [List lst, IntNumber n] =
return (hON (drop (fromInteger n) lst))
where hON [] = List []
hON (x:_) = x
lispListRef badArgList = genericBadArg badArgList "list-ref" "list + number" 2
lispILog :: [LispVal] -> ThrowsError LispVal
lispILog [IntNumber n] = return (IntNumber (ilogb 2 n))
lispILog [IntNumber b, IntNumber n] =
if b < 2
then errTypeMismatch "ilog" "b > 2" (IntNumber b)
else return (IntNumber (ilogb b n))
lispILog badArgList = genericBadArg badArgList "ilog" "integer" 1
lispFactorial :: [LispVal] -> ThrowsError LispVal
lispFactorial [IntNumber n] =
if n > 0
then return (IntNumber (product [1 .. n]))
else if n == 0
then return (IntNumber 1)
else genericBadArg [IntNumber n] "factorial"
"non-negative integer" 1
lispFactorial badArgList = genericBadArg badArgList "factorial" "integer" 1
-- Vector primitives
lispMakeVector :: [LispVal] -> ThrowsError LispVal
lispMakeVector [IntNumber n] = lispMakeVector [IntNumber n, Boolean False]
lispMakeVector [IntNumber n, val] =
if n > 0
then return (Vector n (DIM.fromAscList (addkey val (fromInteger n))))
else errTypeMismatch "make-vector" "n > 0" (IntNumber n)
where addkey _ 0 = []
addkey v k = ((k-1), v):(addkey v (k-1))
lispMakeVector badArgList = genericBadArg badArgList "make-vector" "integer" 1
lispVecFromArgs :: [LispVal] -> ThrowsError LispVal
lispVecFromArgs vals =
return (Vector (toInteger (length vals)) (DIM.fromAscList (addkey 0 vals)))
where addkey _ [] = []
addkey n (v:vs) = (n, v):(addkey (n+1) vs)
lispListToVec :: [LispVal] -> ThrowsError LispVal
lispListToVec [List vals] =
return (Vector (toInteger (length vals)) (DIM.fromAscList (addkey 0 vals)))
where addkey _ [] = []
addkey n (v:vs) = (n, v):(addkey (n+1) vs)
lispListToVec badArgList =
genericBadArg badArgList "list->vector" "list" 1
lispVecToList :: [LispVal] -> ThrowsError LispVal
lispVecToList [Vector _ vec] =
return (List (getval (DIM.toAscList vec)))
where getval [] = []
getval ((_,v):vs) = v:(getval vs)
lispVecToList badArgList =
genericBadArg badArgList "vector->list" "vector" 1
lispVecSize :: [LispVal] -> ThrowsError LispVal
lispVecSize [Vector len _] = return (IntNumber len)
lispVecSize badArgList = genericBadArg badArgList "vector-length" "vector" 1
lispVecRef :: [LispVal] -> ThrowsError LispVal
lispVecRef [Vector len vec, IntNumber n] =
if (n >= 0 && n < len)
then return (DIM.findWithDefault lispFalse (fromInteger n) vec)
else throwError (VectorBounds len (IntNumber n))
lispVecRef badArgList =
genericBadArg badArgList "vector-ref" "vector + integer" 2
getfn1 :: LispVal -> [LispVal] -> LispVal
getfn1 a b = List (Symbol "lambda" : a : b)
getfn :: [String] -> Maybe String -> [LispVal] -> LispVal
getfn ps Nothing body = getfn1 (List (map Symbol ps)) body
getfn [] (Just v) body = getfn1 (Symbol v) body
getfn ps (Just v) body = getfn1 (DottedList (map Symbol ps) (Symbol v)) body
proc2data :: [LispVal] -> ThrowsError LispVal
proc2data [Func pars var body _ _ _] = return (getfn pars var body)
proc2data [Delay obj _ _] = return obj
proc2data [Prim _] =
throwError (Default "procedure->data can't handle builtin functions")
proc2data [IOPrim _] =
throwError (Default "procedure->data can't handle builtin functions")
proc2data badArgList =
genericBadArg badArgList "procedure->data" "lisp function" 1
bitsAnd :: [LispVal] -> ThrowsError LispVal
bitsAnd [IntNumber n1, IntNumber n2] = return (IntNumber (n1 .&. n2))
bitsAnd badArgList = genericBadArg badArgList "bits-and" "integer" 2
bitsOr :: [LispVal] -> ThrowsError LispVal
bitsOr [IntNumber n1, IntNumber n2] = return (IntNumber (n1 .|. n2))
bitsOr badArgList = genericBadArg badArgList "bits-or" "integer" 2
bitsXOr :: [LispVal] -> ThrowsError LispVal
bitsXOr [IntNumber n1, IntNumber n2] = return (IntNumber (xor n1 n2))
bitsXOr badArgList = genericBadArg badArgList "bits-xor" "integer" 2
-- It's not quite clear to me that this one is useful...
-- it seems to implement the function
-- bn :: Integer -> Integer
-- bn n = -(n + 1)
-- which is correct enough in infinite-bits 2-adic numbers,
-- but the actual bit patterns returned don't look like complements.
-- Use bitsFlip instead...
-- bitsNot :: [LispVal] -> ThrowsError LispVal
-- bitsNot [IntNumber n] = return (IntNumber (complement n))
-- bitsNot badArgList = genericBadArg badArgList "bits-not" "integer" 1
bitsShift :: [LispVal] -> ThrowsError LispVal
bitsShift [IntNumber n1, IntNumber n2] =
return (IntNumber (shift n1 (fromInteger n2)))
bitsShift badArgList = genericBadArg badArgList "bits-shift" "integer" 2
bitsSet :: [LispVal] -> ThrowsError LispVal
bitsSet [IntNumber n1, IntNumber n2] =
return (IntNumber (setBit n1 (fromInteger n2)))
bitsSet badArgList = genericBadArg badArgList "bits-set" "integer" 2
bitsClear :: [LispVal] -> ThrowsError LispVal
bitsClear [IntNumber n1, IntNumber n2] =
return (IntNumber (clearBit n1 (fromInteger n2)))
bitsClear badArgList = genericBadArg badArgList "bits-clear" "integer" 2
bitsFlip :: [LispVal] -> ThrowsError LispVal
bitsFlip [IntNumber n1, IntNumber n2] =
return (IntNumber (complementBit n1 (fromInteger n2)))
bitsFlip badArgList = genericBadArg badArgList "bits-flip" "integer" 2
bitsGet :: [LispVal] -> ThrowsError LispVal
bitsGet [IntNumber n1, IntNumber n2] =
return (IntNumber (n1 .&. (bit (fromInteger n2))))
bitsGet badArgList = genericBadArg badArgList "bits-get" "integer" 2
bitsIsSet :: [LispVal] -> ThrowsError LispVal
bitsIsSet [IntNumber n1, IntNumber n2] =
return (Boolean (testBit n1 (fromInteger n2)))
bitsIsSet badArgList = genericBadArg badArgList "bits-set?" "integer" 2
primitives :: [(String, [LispVal] -> ThrowsError LispVal)]
primitives = [("+", lispPlus),
("-", lispMinus),
("*", lispMul),
("/", lispDiv),
("expt", lispPow),
("min", lispMin),
("max", lispMax),
("modulo", integerBinop "modulo" mod),
("quotient", integerBinop "quotient" quot),
("remainder", integerBinop "remainder" rem),
("gcd", integerBinop "gcd" gcd),
("lcm", integerBinop "lcm" lcm),
("=", numBoolBinop "=" (==) (==) (==) False),
("<", numBoolBinop "<" (<) (<) (<) False),
(">", numBoolBinop ">" (>) (>) (>) False),
("/=", numBoolBinop "/=" (/=) (/=) (/=) True),
(">=", numBoolBinop ">=" (>=) (>=) (>=) False),
("<=", numBoolBinop "<=" (<=) (<=) (<=) False),
("boolean?", isBool),
("symbol?", Library.isSymbol),
("char?", isChar),
("number?", Library.isNumber),
("integer?", isInteger),
("rational?", isRational),
("real?", isReal),
("string?", isString),
("pair?", isPair),
("list?", isList),
("null?", isNull),
("port?", isPort),
("procedure?", isProcedure),
("vector?", isVector),
("even?", lispIsEven),
("odd?", lispIsOdd),
("zero?", isZero),
("positive?", isPositive),
("negative?", isNegative),
("nan?", lispIsNaN),
("infinite?", lispIsInf),
("finite?", lispIsFinite),
("not", lispNot),
("id", lispId),
("string=?", strBoolBinop "string=?" (==)),
("string<?", strBoolBinop "string<?" (<)),
("string>?", strBoolBinop "string>?" (>)),
("string>=?", strBoolBinop "string>=?" (>=)),
("string<=?", strBoolBinop "string<=?" (<=)),
("char=?", charBoolBinop "char=?" (==)),
("char<?", charBoolBinop "char<?" (<)),
("char>?", charBoolBinop "char>?" (>)),
("char>=?", charBoolBinop "char>=?" (>=)),
("char<=?", charBoolBinop "char<=?" (<=)),
("char->string", char2str),
("string->char", str2char),
("string->number", readNum),
("number->string", writeNum),
("symbol->string", symb2str),
("char-alphabetic?", charIs isAlpha),
("char-numeric?", charIs isDigit),
("char-oct-digit?", charIs isOctDigit),
("char-hex-digit?", charIs isHexDigit),
("char-whitespace?", charIs isSpace),
("char-upper-case?", charIs isUpper),
("char-lower-case?", charIs isLower),
("char-alphanumeric?", charIs isAlphaNum),
("char-control?", charIs isControl),
("char-printable?", charIs isPrint),
("char-upcase", charTo toUpper),
("char-downcase", charTo toLower),
("string-upcase", charTo toUpper),
("string-downcase", charTo toLower),
("car", car),
("cdr", cdr),
("cons", cons),
("eqv?", eqvFunc),
("char->integer", char2int),
("integer->char", int2char),
("floor", lispFloor),
("truncate", lispTruncate),
("round", lispRound),
("ceiling", lispCeiling),
("numerator", lispNumerator),
("denominator", lispDenominator),
("abs", lispAbs),
("sqrt", numericFunc "sqrt" sqrt),
("exp", numericFunc "exp" exp),
("log", numericFunc "log" log),
("sin", numericFunc "sin" sin),
("cos", numericFunc "cos" cos),
("tan", numericFunc "tan" tan),
("sinh", numericFunc "sinh" sinh),
("cosh", numericFunc "cosh" cosh),
("tanh", numericFunc "tanh" tanh),
("asin", numericFunc "asin" asin),
("acos", numericFunc "acos" acos),
("atan", lispATan2),
("asinh", numericFunc "asinh" asinh),
("acosh", numericFunc "acosh" acosh),
("atanh", numericFunc "atanh" atanh),
("list", lispListFromArgs),
("reverse", lispReverse),
("last", lispLast),
("length", lispLength),
("list-head", lispListHead),
("list-tail", lispListTail),
("list-ref", lispListRef),
("ilog", lispILog),
("factorial", lispFactorial),
("make-vector", lispMakeVector),
("vector", lispVecFromArgs),
("vector-length", lispVecSize),
("list->vector", lispListToVec),
("vector->list", lispVecToList),
("vector-ref", lispVecRef),
("procedure->data", proc2data),
("bits-and", bitsAnd),
("bits-or", bitsOr),
("bits-xor", bitsXOr),
-- ("bits-not", bitsNot),
("bits-shift", bitsShift),
("bits-set", bitsSet),
("bits-clear", bitsClear),
("bits-flip", bitsFlip),
("bits-get", bitsGet),
("bits-set?", bitsIsSet)]
-- A bunch of library functions that do IO:
-- these get put into the ioPrimitives table below
-- This is the wrapper which catches IO errors... I dunno what type it is
-- This converts a system-level error into a lisp-level error by the
-- kinda-funky (Default (show err)), assuming we don't swallow the error
doIOAction action ctor epred =
do ret <- liftIO (try action)
case ret of
Left err -> if epred err
then throwError (Default (show err))
else return lispFalse
Right val -> return (ctor val)
-- A couple of utility functions for using doIOAction: dropToBool is a
-- quasi-constructor which drops whatever it was handed, and instead only
-- returns lispTrue; allErrs and noEOF are selectors for various errors:
-- generally we want to hear about errors, but EOF when reading a line or
-- character isn't really an error, so we silence that one.
dropToBool _ = lispTrue
allErrs _ = True
noEOF err = not (isEOFError err)
makePort :: IOMode -> [LispVal] -> IOThrowsError LispVal
makePort mode [String filename] =
doIOAction (openFile filename mode) Port allErrs
makePort _ badArgList = genericIOBadArg badArgList "open-IO-file" "string" 1
closePort :: [LispVal] -> IOThrowsError LispVal
closePort [Port port] =
doIOAction (hClose port) dropToBool allErrs
closePort [Socket sock] =
doIOAction (sClose sock) dropToBool allErrs
closePort _ = return lispFalse
readLine :: [LispVal] -> IOThrowsError LispVal
readLine [] = readLine [Port stdin]
readLine [Port port] = doIOAction (hGetLine port) String noEOF
readLine badArgList = genericIOBadArg badArgList "read-line" "read port" 1
readChar :: [LispVal] -> IOThrowsError LispVal
readChar [] = readChar [Port stdin]
readChar [Port port] = doIOAction (hGetChar port) Char noEOF
readChar badArgList = genericIOBadArg badArgList "read-char" "read port" 1
displayProc :: [LispVal] -> IOThrowsError LispVal
displayProc [obj] = displayProc [obj, Port stdout]
displayProc [obj, Port port] =
doIOAction (hPutStr port (show obj)) dropToBool allErrs
displayProc badArgList = genericIOBadArg badArgList "display" "write port" 2
readContents :: [LispVal] -> IOThrowsError LispVal
readContents [String filename] = doIOAction (readFile filename) String allErrs
readContents badArgList = genericIOBadArg badArgList "read-contents" "string" 1
loadFile :: String -> IOThrowsError [LispVal]
loadFile filename =
do str <- doIOAction (readFile filename) String allErrs
case str of
Boolean False -> throwError (Default "operation failed")
String val -> liftThrows (readExprList val)
readAll :: [LispVal] -> IOThrowsError LispVal
readAll [String filename] = liftM List (loadFile filename)
readAll badArgList = genericIOBadArg badArgList "read-all" "string" 1
lispPutStr :: [LispVal] -> IOThrowsError LispVal
lispPutStr [] = return lispFalse
lispPutStr ((Port port):rest) =
mapM outStr rest >> return lispTrue
where outStr (String s) = doIOAction (hPutStr port s) dropToBool allErrs
outStr (Char c) = doIOAction (hPutChar port c) dropToBool allErrs
outStr notS = genericIOBadArg [notS] "write-string" "string" 1
lispPutStr (s:ss) = lispPutStr ((Port stdout):s:ss)
flushPort :: [LispVal] -> IOThrowsError LispVal
flushPort [] = flushPort [Port stdout]
flushPort [Port p] = doIOAction (hFlush p) dropToBool allErrs
flushPort badArgList = genericIOBadArg badArgList "flush-port" "port" 1
lispError :: [LispVal] -> IOThrowsError LispVal
lispError [] = throwError (UserException (List []))
lispError [val] = throwError (UserException val)
lispError info = throwError (UserException (List info))
lispExit :: [LispVal] -> IOThrowsError LispVal
lispExit [Boolean False] = liftIO (exitWith (ExitFailure 1))
lispExit [Boolean True] = liftIO (exitWith ExitSuccess)
lispExit [IntNumber n] | n == 0 = liftIO (exitWith ExitSuccess)
| otherwise = liftIO(exitWith (ExitFailure
(fromInteger n)))
lispExit [String s] = liftIO (hPutStrLn stderr s >> exitWith (ExitFailure 1))
lispExit _ = liftIO (hPutStrLn stderr "goodbye!" >> exitWith (ExitFailure 1))
lispFileExists :: [LispVal] -> IOThrowsError LispVal
lispFileExists [String filename] =
doIOAction (doesFileExist filename) Boolean allErrs
lispFileExists badArgList =
genericIOBadArg badArgList "file-exists?" "string" 1
lispDirExists :: [LispVal] -> IOThrowsError LispVal
lispDirExists [String dirname] =
doIOAction (doesDirectoryExist dirname) Boolean allErrs
lispDirExists badArgList =
genericIOBadArg badArgList "directory-exists?" "string" 1
lispRenameFile :: [LispVal] -> IOThrowsError LispVal
lispRenameFile [String oldname, String newname] =
doIOAction (renameFile oldname newname) dropToBool allErrs
lispRenameFile badArgList = genericIOBadArg badArgList "rename-file" "string" 2
lispCreateLink :: [LispVal] -> IOThrowsError LispVal
lispCreateLink [String oldname, String newname] =
doIOAction (createLink oldname newname) dropToBool allErrs
lispCreateLink badArgList = genericIOBadArg badArgList "create-link" "string" 2
lispCreateSymbolicLink :: [LispVal] -> IOThrowsError LispVal
lispCreateSymbolicLink [String oldname, String newname] =
doIOAction (createSymbolicLink oldname newname) dropToBool allErrs
lispCreateSymbolicLink badArgList =
genericIOBadArg badArgList "create-symbolic-link" "string" 2
lispRemoveFile :: [LispVal] -> IOThrowsError LispVal
lispRemoveFile [String filename] =
doIOAction (removeFile filename) dropToBool allErrs
lispRemoveFile badArgList = genericIOBadArg badArgList "remove-file" "string" 2
lispCreateDir :: [LispVal] -> IOThrowsError LispVal
lispCreateDir [String dirname] =
doIOAction (createDirectory dirname) dropToBool allErrs
lispCreateDir badArgList =
genericIOBadArg badArgList "create-directory" "string" 1
lispRemoveDir :: [LispVal] -> IOThrowsError LispVal
lispRemoveDir [String dirname] =
doIOAction (removeDirectory dirname) dropToBool allErrs
lispRemoveDir badArgList =
genericIOBadArg badArgList "remove-directory" "string" 1
lispRenameDir :: [LispVal] -> IOThrowsError LispVal
lispRenameDir [String oldname, String newname] =
doIOAction (renameDirectory oldname newname) dropToBool allErrs
lispRenameDir badArgList =
genericIOBadArg badArgList "rename-directory" "string" 2
lispSetCurrentDir :: [LispVal] -> IOThrowsError LispVal
lispSetCurrentDir [String dirname] =
doIOAction (setCurrentDirectory dirname) dropToBool allErrs
lispSetCurrentDir badArgList =
genericIOBadArg badArgList "set-current-directory" "nothing" 0
lispGetCurrentDir :: [LispVal] -> IOThrowsError LispVal
lispGetCurrentDir [] =
doIOAction (getCurrentDirectory) String allErrs
lispGetCurrentDir badArgList =
genericIOBadArg badArgList "get-current-directory" "nothing" 0
lispGetDirContents :: [LispVal] -> IOThrowsError LispVal
lispGetDirContents [String dirname] =
doIOAction (getDirectoryContents dirname) loS allErrs
where loS arr = List (toS arr)
toS [] = []
toS (str:strs) = (String str):(toS strs)
lispGetDirContents badArgList =
genericIOBadArg badArgList "read-directory" "string" 1
lispGetEnv :: [LispVal] -> IOThrowsError LispVal
lispGetEnv [] =
doIOAction (getEnvironment) loSS allErrs
where loSS arr = List (toSS arr)
toSS [] = []
toSS ((key,val):strs) =
(DottedList [String key] (String val)):(toSS strs)
lispGetEnv [String key] =
doIOAction (getEnvDefault key "") String allErrs
lispGetEnv badArgList =
genericIOBadArg badArgList "get-environment" "string" 1
lispSetEnv :: [LispVal] -> IOThrowsError LispVal
lispSetEnv [String key, String val] =
doIOAction (putEnv (key ++ "=" ++ val)) dropToBool allErrs
lispSetEnv badArgList =
genericIOBadArg badArgList "set-environment" "string" 2
lispUnSetEnv :: [LispVal] -> IOThrowsError LispVal
lispUnSetEnv [String key] = doIOAction (unsetEnv key) dropToBool allErrs
lispUnSetEnv badArgList =
genericIOBadArg badArgList "unset-environment" "string" 1
lispEpochTime :: [LispVal] -> IOThrowsError LispVal
lispEpochTime [] = doIOAction (getClockTime) getET allErrs
where getET (TOD sec psec) =
FltNumber ((fromInteger sec) + 1.0e-12*(fromInteger psec))
lispEpochTime badArgList = genericIOBadArg badArgList "epochtime" "nothing" 0
lispLocalTime :: [LispVal] -> IOThrowsError LispVal
lispLocalTime [] = doIOAction (getClockTime) toS allErrs
where toS val = String (show val)
lispLocalTime [IntNumber n] =
doIOAction (toCalendarTime (TOD n 0)) toS allErrs
where toS val = String (calendarTimeToString val)
lispLocalTime [RatNumber n] =
doIOAction (toCalendarTime (TOD (round n) 0)) toS allErrs
where toS val = String (calendarTimeToString val)
lispLocalTime [FltNumber n] =
doIOAction (toCalendarTime (TOD (round n) 0)) toS allErrs
where toS val = String (calendarTimeToString val)
lispLocalTime badArgList = genericIOBadArg badArgList "localtime" "nothing" 0
lispUTCTime :: [LispVal] -> IOThrowsError LispVal
lispUTCTime [] = doIOAction (getClockTime) toS allErrs
where toS val = String (calendarTimeToString (toUTCTime val))
lispUTCTime [IntNumber n] =
return (String (calendarTimeToString (toUTCTime (TOD n 0))))
lispUTCTime [RatNumber n] =
return (String (calendarTimeToString (toUTCTime (TOD (round n) 0))))
lispUTCTime [FltNumber n] =
return (String (calendarTimeToString (toUTCTime (TOD (round n) 0))))
lispUTCTime badArgList = genericIOBadArg badArgList "UTCtime" "nothing" 0
lispGetCPUTime :: [LispVal] -> IOThrowsError LispVal
lispGetCPUTime [] =
doIOAction (getCPUTime) toS allErrs
where toS val = FltNumber ((fromInteger val)/1.0e12)
lispGetCPUTime badArgList =
genericIOBadArg badArgList "cputime" "nothing" 0
statData :: FileStatus -> LispVal
statData stat =
List [IntNumber (read (show (deviceID stat))),
IntNumber (toInteger (fileID stat)),
IntNumber (toInteger (fileMode stat)),
IntNumber (toInteger (linkCount stat)),
IntNumber (toInteger (fileOwner stat)),
IntNumber (toInteger (fileGroup stat)),
IntNumber (read (show (specialDeviceID stat))),
IntNumber (toInteger (fileSize stat)),
getET (accessTime stat),
getET (modificationTime stat),
getET (statusChangeTime stat)]
where getET t = FltNumber (realToFrac t)
lispGetFileStatus :: [LispVal] -> IOThrowsError LispVal
lispGetFileStatus [String filename] =
doIOAction (getFileStatus filename) statData allErrs
lispGetFileStatus badArgList =
genericIOBadArg badArgList "get-file-status" "string" 1
lispGetLinkStatus :: [LispVal] -> IOThrowsError LispVal
lispGetLinkStatus [String filename] =
doIOAction (getSymbolicLinkStatus filename) statData allErrs
lispGetLinkStatus badArgList =
genericIOBadArg badArgList "get-link-status" "string" 1
lispIsBlockDevice :: [LispVal] -> IOThrowsError LispVal
lispIsBlockDevice [String filename] =
doIOAction (getFileStatus filename) (Boolean . isBlockDevice) allErrs
lispIsBlockDevice badArgList =
genericIOBadArg badArgList "is-block-device?" "string" 1
lispIsCharacterDevice :: [LispVal] -> IOThrowsError LispVal
lispIsCharacterDevice [String filename] =
doIOAction (getFileStatus filename) (Boolean . isCharacterDevice) allErrs
lispIsCharacterDevice badArgList =
genericIOBadArg badArgList "is-char-device?" "string" 1
lispIsNamedPipe :: [LispVal] -> IOThrowsError LispVal
lispIsNamedPipe [String filename] =
doIOAction (getFileStatus filename) (Boolean . isNamedPipe) allErrs
lispIsNamedPipe badArgList =
genericIOBadArg badArgList "is-named-pipe?" "string" 1
lispIsRegularFile :: [LispVal] -> IOThrowsError LispVal
lispIsRegularFile [String filename] =
doIOAction (getFileStatus filename) (Boolean . isRegularFile) allErrs
lispIsRegularFile badArgList =
genericIOBadArg badArgList "is-regular-file?" "string" 1
lispIsDirectory :: [LispVal] -> IOThrowsError LispVal
lispIsDirectory [String filename] =
doIOAction (getFileStatus filename) (Boolean . isDirectory) allErrs
lispIsDirectory badArgList =
genericIOBadArg badArgList "is-directory?" "string" 1
lispIsSymbolicLink :: [LispVal] -> IOThrowsError LispVal
lispIsSymbolicLink [String filename] =
doIOAction (getFileStatus filename) (Boolean . isSymbolicLink) allErrs
lispIsSymbolicLink badArgList =
genericIOBadArg badArgList "is-symbolic-link?" "string" 1
lispIsSocket :: [LispVal] -> IOThrowsError LispVal
lispIsSocket [String filename] =
doIOAction (getFileStatus filename) (Boolean . isSocket) allErrs
lispIsSocket badArgList =
genericIOBadArg badArgList "is-socket?" "string" 1
lispRandUni :: [LispVal] -> IOThrowsError LispVal
lispRandUni [] =
doIOAction (getStdRandom (randomR (0 :: Double, 1))) FltNumber allErrs
lispRandUni [IntNumber lo, IntNumber hi] =
if lo < hi
then doIOAction (getStdRandom (randomR (lo, hi))) IntNumber allErrs
else doIOAction (getStdRandom (randomR (hi, lo))) IntNumber allErrs
lispRandUni [FltNumber lo, FltNumber hi] =
if lo < hi
then doIOAction (getStdRandom (randomR (lo, hi))) FltNumber allErrs
else doIOAction (getStdRandom (randomR (hi, lo))) FltNumber allErrs
lispRandUni badArgList =
genericIOBadArg badArgList "random-uniform" "two numbers" 2
lispRandExp :: [LispVal] -> IOThrowsError LispVal
lispRandExp [FltNumber m] =
if m > 0
then getrand >>= return . FltNumber . (sc m) . negate . log
else genericIOBadArg [FltNumber m] "random-exponential" "positive rate" 1
where getrand =
do val <- lispRandUni []
if getnum val == 0
then getrand
else return (getnum val)
getnum (FltNumber n) = n
sc s v = v/s
lispRandExp [] = lispRandExp [FltNumber 1.0]
lispRandExp [IntNumber m] = lispRandExp [FltNumber (fromInteger m)]
lispRandExp [RatNumber m] = lispRandExp [FltNumber (fromRational m)]
lispRandExp badArgList =
genericIOBadArg badArgList "random-exponential" "number" 1
lispRandNorm :: [LispVal] -> IOThrowsError LispVal
lispRandNorm [FltNumber m, FltNumber s] =
if s > 0
then do x1 <- getrand
x2 <- getrand
let a = s*sqrt (-2.0*(log x1))
b = 2.0*pi*x2
y1 = m + a*(cos b)
y2 = m + a*(sin b)
return (List [FltNumber y1, FltNumber y2])
else throwError (Default ("random-normal-pair needs a positive stddev,"
++ " got " ++ (show s)))
where getrand =
do val <- lispRandUni []
if getnum val == 0
then getrand
else return (getnum val)
getnum (FltNumber n) = n
lispRandNorm [] = lispRandNorm [FltNumber 0.0, FltNumber 1.0]
lispRandNorm [IntNumber m, IntNumber s] =
lispRandNorm [FltNumber (fromInteger m), FltNumber (fromInteger s)]
lispRandNorm [IntNumber m, RatNumber s] =
lispRandNorm [FltNumber (fromInteger m), FltNumber (fromRational s)]
lispRandNorm [IntNumber m, FltNumber s] =
lispRandNorm [FltNumber (fromInteger m), FltNumber s]
lispRandNorm [RatNumber m, IntNumber s] =
lispRandNorm [FltNumber (fromRational m), FltNumber (fromInteger s)]
lispRandNorm [RatNumber m, RatNumber s] =
lispRandNorm [FltNumber (fromRational m), FltNumber (fromRational s)]
lispRandNorm [RatNumber m, FltNumber s] =
lispRandNorm [FltNumber (fromRational m), FltNumber s]
lispRandNorm [FltNumber m, IntNumber s] =
lispRandNorm [FltNumber m, FltNumber (fromInteger s)]
lispRandNorm [FltNumber m, RatNumber s] =
lispRandNorm [FltNumber m, FltNumber (fromRational s)]
lispRandNorm badArgList =
genericIOBadArg badArgList "random-normal-pair" "number" 2
-- TODO: for large lambda, this will be slow! Fix!
lispRandPoisson :: [LispVal] -> IOThrowsError LispVal
lispRandPoisson [FltNumber lambda] =
if lambda > 0
then do val <- doit (exp (-lambda)) (-1) (1 :: Double)
return (IntNumber val)
else throwError (Default ("random-poisson needs a positive lambda,"
++ " got " ++ (show lambda)))
where doit l k p =
do r <- lispRandUni []
let pp = p*(getnum r)
kp = k + 1
if pp < l
then return kp
else doit l kp pp
getnum (FltNumber n) = n
lispRandPoisson [IntNumber m] = lispRandPoisson [FltNumber (fromInteger m)]
lispRandPoisson [RatNumber m] = lispRandPoisson [FltNumber (fromRational m)]
lispSeedRandom :: [LispVal] -> IOThrowsError LispVal
lispSeedRandom [IntNumber n] =
doIOAction (setStdGen (mkStdGen (fromInteger n))) dropToBool allErrs
lispSeedRandom [String s] =
doIOAction (setStdGen (read s)) dropToBool allErrs
lispSeedRandom badArgList =
genericIOBadArg badArgList "random-seed!" "integer or string" 1
lispConnectTo :: [LispVal] -> IOThrowsError LispVal
lispConnectTo [String hostname, IntNumber port] =
doIOAction (connectTo hostname (PortNumber (fromInteger port))) Port allErrs
lispConnectTo [String hostname, String usock] =
doIOAction (connectTo hostname (UnixSocket usock)) Port allErrs
lispConnectTo badArgList =
genericIOBadArg badArgList "connect-to" "host port" 2
lispListenOn :: [LispVal] -> IOThrowsError LispVal
lispListenOn [IntNumber port] =
doIOAction (listenOn (PortNumber (fromInteger port))) Socket allErrs
lispListenOn [String usock] =
doIOAction (listenOn (UnixSocket usock)) Socket allErrs
lispListenOn badArgList =
genericIOBadArg badArgList "listen-on" "port" 1
lispAccept :: [LispVal] -> IOThrowsError LispVal
lispAccept [Socket s] =
do ret <- liftIO (try (accept s))
case ret of
Left err -> throwError (Default (show err))
Right val -> return (List [Port (val1 val),
String (val2 val),
IntNumber (toInteger (val3 val))])
where val1 (a,_,_) = a
val2 (_,b,_) = b
val3 (_,_,c) = c
lispAccept badArgList =
genericIOBadArg badArgList "accept" "socket" 1
lispSetLineBuf :: [LispVal] -> IOThrowsError LispVal
lispSetLineBuf [Port h] =
doIOAction (hSetBuffering h LineBuffering) dropToBool allErrs
lispSetLineBuf badArgList =
genericIOBadArg badArgList "set-line-buffering!" "port" 1
lispSetNoBuf :: [LispVal] -> IOThrowsError LispVal
lispSetNoBuf [Port h] =
doIOAction (hSetBuffering h NoBuffering) dropToBool allErrs
lispSetNoBuf badArgList =
genericIOBadArg badArgList "set-no-buffering!" "port" 1
ioPrimitives :: [(String, [LispVal] -> IOThrowsError LispVal)]
ioPrimitives = [("open-input-file", makePort ReadMode),
("open-output-file", makePort WriteMode),
("open-append-file", makePort AppendMode),
("rename-file", lispRenameFile),
("remove-file", lispRemoveFile),
("create-link", lispCreateLink),
("create-symbolic-link", lispCreateSymbolicLink),
("close-port", closePort),
("raise", lispError),
("display", displayProc),
("read-line", readLine),
("read-char", readChar),
("read-contents", readContents),
("read-all", readAll),
("write-string", lispPutStr),
("flush-port", flushPort),
("exit", lispExit),
("get-current-directory", lispGetCurrentDir),
("set-current-directory", lispSetCurrentDir),
("create-directory", lispCreateDir),
("remove-directory", lispRemoveDir),
("rename-directory", lispRenameDir),
("read-directory", lispGetDirContents),
("file-exists?", lispFileExists),
("directory-exists?", lispDirExists),
("get-environment", lispGetEnv),
("set-environment", lispSetEnv),
("unset-environment", lispUnSetEnv),
("epochtime", lispEpochTime),
("localtime", lispLocalTime),
("UTCtime", lispUTCTime),
("cputime", lispGetCPUTime),
("get-file-status", lispGetFileStatus),
("get-link-status", lispGetLinkStatus),
("is-block-device?", lispIsBlockDevice),
("is-char-device?", lispIsCharacterDevice),
("is-named-pipe?", lispIsNamedPipe),
("is-regular-file?", lispIsRegularFile),
("is-directory?", lispIsDirectory),
("is-symbolic-link?", lispIsSymbolicLink),
("is-socket?", lispIsSocket),
("random-uniform", lispRandUni),
("random-exponential", lispRandExp),
("random-normal-pair", lispRandNorm),
("random-poisson", lispRandPoisson),
("random-seed!", lispSeedRandom),
("set-line-buffering!", lispSetLineBuf),
("set-no-buffering!", lispSetNoBuf),
("connect-to", lispConnectTo),
("listen-on", lispListenOn),
("accept", lispAccept)]
-- A couple of predefined data values
ioPorts :: [(String, LispVal)]
ioPorts = [("stdin", (Port stdin)),
("stdout", (Port stdout)),
("stderr", (Port stderr)),
("pi", (FltNumber pi))]
-- And finally some stuff for internal work
-- delayCounter is the name under which a counter for delay objects
-- is stored in the environment. It contains spaces, so that it is
-- impossible for the user to enter this as a valid symbol. Ditto
-- for symbolCounter: this is for generating new internal symbols.
delayCounter :: String
delayCounter = " delay "
symbolCounter :: String
symbolCounter = " symbol "
internals :: [(String, LispVal)]
internals = [(delayCounter, (IntNumber 0)),
(symbolCounter, (IntNumber 0))]
primitiveBindings :: IO Env
primitiveBindings = newIORef [] >>=
(flip bindVars (internals ++ map (mkf IOPrim) ioPrimitives ++
map (mkf Prim) primitives ++ ioPorts))
where mkf constructor (var, func) = (var, constructor func)