-- File created: 2009-07-11 21:47:19 -- ScmValue depends on Haschoo -- Haschoo depends on Context -- Context depends on ScmValue -- -- Thus they all have to be in the same module, to avoid circular dependencies. {-# LANGUAGE BangPatterns, GeneralizedNewtypeDeriving #-} module Haschoo.Types ( Haschoo, runHaschoo, withHaschoo , ScmValue(..), MacroCall(..) , isTrue, isAggregate , toScmString, toScmMString , toScmVector , pairToList, listToPair , Context, mkContext, addToContext, contextLookup, contextSize , scmShow, scmShowWith ) where import Control.Applicative ((<$>)) import Control.Arrow (second) import Control.Monad.Error (ErrorT, MonadError, runErrorT) import Control.Monad.State.Strict (StateT, MonadState, evalStateT) import Control.Monad.Trans (MonadIO, liftIO) import Data.Array.IO (IOArray, IOUArray, newListArray, getElems) import Data.Array.Unboxed (Array, UArray, elems, listArray) import Data.Complex (Complex((:+))) import Data.IORef (IORef, readIORef, newIORef) import Data.List (intercalate) import Data.Ratio (numerator, denominator) import System.IO (Handle) import System.IO.Unsafe (unsafeInterleaveIO) import Text.Show.Functions () import qualified Data.ListTrie.Patricia.Map.Enum as TM import Data.ListTrie.Patricia.Map.Enum (TrieMap) import Haschoo.Utils (ErrOr, lazyMapM) -- Our main monad newtype Haschoo a = Haschoo (StateT HaschState (ErrorT String IO) a) deriving (Functor, Monad, MonadIO, MonadState HaschState, MonadError String) runHaschoo :: HaschState -> Haschoo a -> IO (ErrOr a) runHaschoo state (Haschoo h) = runErrorT $ evalStateT h state -- Like 'local' of the Reader monad withHaschoo :: HaschState -> Haschoo a -> Haschoo a withHaschoo state h = either fail return =<< liftIO (runHaschoo state h) type HaschState = [IORef Context] data ScmValue = ScmPrim String !([ScmValue] -> Haschoo ScmValue) | ScmFunc String !([ScmValue] -> IO (ErrOr ScmValue)) | ScmMacro String !(MacroCall -> Haschoo (ScmValue, TrieMap Char [IORef Context])) | ScmBool !Bool | ScmChar !Char | ScmInt !Integer | ScmRat !Rational | ScmReal !Double | ScmComplex !(Complex Double) | ScmIdentifier !String | ScmPair !(IORef ScmValue) !(IORef ScmValue) | ScmString !( UArray Int Char) | ScmMString !(IOUArray Int Char) | ScmVector !( Array Int ScmValue) | ScmMVector !( IOArray Int ScmValue) | ScmSyntax { patterns :: ![(ScmValue, ScmValue, [String])] , literals :: ![(String, Maybe ScmValue)] } -- These two are only for literal lists, appearing only directly -- from the parser | ScmList ![ScmValue] | ScmDottedList ![ScmValue] !ScmValue -- The list is never empty -- The "unspecified value" returned by IO procedures and such | ScmVoid | ScmInput !Handle | ScmOutput !Handle | ScmEOF -- The "environment-specifier" used by eval-related procedures | ScmContext ![Context] -- The form of the macro usage: -- -- (macro foo bar), (macro foo . bar), or #(macro foo bar) -- -- The macro keyword is not included regardless of constructor data MacroCall = MCList ![ScmValue] | MCDotted ![ScmValue] !ScmValue isTrue, isAggregate :: ScmValue -> Bool isTrue (ScmBool False) = False isTrue _ = True isAggregate (ScmList _) = True isAggregate (ScmDottedList _ _) = True isAggregate (ScmVector _) = True isAggregate (ScmMVector _) = True isAggregate (ScmPair _ _) = True isAggregate _ = False -- From ScmPair to Left . ScmDottedList or Right . ScmList pairToList :: ScmValue -> IO (Either ScmValue ScmValue) pairToList = go [] where go xs (ScmList ys) = return . Right $ ScmList (reverse xs ++ ys) go xs (ScmDottedList ys z) = return . Left $ ScmDottedList (reverse xs ++ ys) z go xs (ScmPair a b) = do a' <- readIORef a b' <- readIORef b go (a':xs) b' go xs v = return . Left $ ScmDottedList (reverse xs) v -- ScmList -> ScmPair, returns the tail pointer if it wasn't empty listToPair :: [ScmValue] -> IO (ScmValue, Maybe (IORef ScmValue)) listToPair [] = return (ScmList [], Nothing) listToPair (x:xs) = second Just <$> go x xs where go :: ScmValue -> [ScmValue] -> IO (ScmValue, IORef ScmValue) go v [] = do a <- newIORef v b <- newIORef (ScmList []) return (ScmPair a b, b) go v (y:ys) = do a <- newIORef v (zs, fin) <- go y ys b <- newIORef zs return (ScmPair a b, fin) toScmString :: String -> ScmValue toScmString s = ScmString $ listArray (0, length s - 1) s toScmMString :: String -> IO ScmValue toScmMString s = ScmMString <$> newListArray (0, length s - 1) s toScmVector :: [ScmValue] -> ScmValue toScmVector v = ScmVector $ listArray (0, length v - 1) v newtype Context = Context (TrieMap Char ScmValue) mkContext :: [(String, ScmValue)] -> Context mkContext = Context . TM.fromList addToContext :: String -> ScmValue -> Context -> Context addToContext name val (Context c) = Context (TM.insert' name val c) contextLookup :: String -> Context -> Maybe ScmValue contextLookup s (Context c) = TM.lookup s c contextSize :: Context -> Int contextSize (Context c) = TM.size' c scmShow :: ScmValue -> IO String scmShow ScmVoid = return "" -- Has no representation scmShow (ScmBool b) = return$ '#' : if b then "t" else "f" scmShow (ScmPrim s _) = return s scmShow (ScmFunc s _) = return s scmShow (ScmMacro s _) = return s scmShow (ScmIdentifier s) = return s scmShow (ScmInt n) = return$ show n scmShow (ScmReal n) | isNaN n = return "+nan.#" | isInfinite n = return$ (if n < 0 then '-' else '+') : "inf.#" | otherwise = return$ show n scmShow (ScmRat n) = if denominator n == 1 then return$ show (numerator n) else return$ concat [show (numerator n), "/", show (denominator n)] scmShow (ScmComplex (a :+ b)) = do bs <- scmShow (ScmReal b) as <- scmShow (ScmReal a) return$ concat [ as , if head bs `elem` "-+" then [] else "+" , bs , "i" ] scmShow (ScmChar c) | c == ' ' = return "#\\space" | c == '\n' = return "#\\newline" | otherwise = return$ "#\\" ++ [c] scmShow (ScmSyntax _ _) = return "" scmShow (ScmContext _) = return "" scmShow ScmEOF = return "" scmShow (ScmInput _) = return "" scmShow (ScmOutput _) = return "" scmShow (ScmString s) = return$ showScmString (elems s) scmShow (ScmMString s) = showScmString <$> getElems s scmShow x | isAggregate x = scmShowWith scmShow x | otherwise = error "scmShow :: the impossible happened" scmShowWith :: (ScmValue -> IO String) -> ScmValue -> IO String scmShowWith f (ScmList xs) = showScmList f xs scmShowWith f (ScmDottedList a b) = do as <- showScmList f a bs <- f b return$ concat [init as, " . ", bs, ")"] scmShowWith f (ScmVector v) = ('#':) <$> showScmList f (elems v) scmShowWith f (ScmMVector v) = ('#':) <$> (showScmList f =<< getElems v) scmShowWith f (ScmPair car cdr) = do a <- readIORef car as <- f a ('(':) . (as ++) <$> unsafeInterleaveIO (readIORef cdr >>= go) where go (ScmList []) = return ")" go x@(ScmList _) = (' ':) . tail <$> f x go x@(ScmDottedList _ _) = (' ':) . tail <$> f x go (ScmPair x y) = do a <- readIORef x as <- f a (' ':) . (as ++) <$> unsafeInterleaveIO (readIORef y >>= go) go x = (" . "++) . (++")") <$> f x scmShowWith _ _ = error "scmShowWith :: the impossible happened" showScmString :: String -> String showScmString s = '"' : foldr ((.) . f) id s "\"" where f c | c == '\\' = showString "\\\\" | c == '\"' = showString "\\\"" | otherwise = showChar c showScmList :: (a -> IO String) -> [a] -> IO String showScmList f xs = ("("++) . (++")") . intercalate " " <$> lazyMapM f xs