{-# Language TypeSynonymInstances, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses, UndecidableInstances, DeriveDataTypeable, TypeFamilies #-} {- | Module : Language.Egison.Types Copyright : Satoshi Egi Licence : MIT This module contains type definitions of Egison Data. -} module Language.Egison.Types ( -- * Egison expressions EgisonTopExpr (..) , EgisonExpr (..) , EgisonPattern (..) , InnerExpr (..) , BindingExpr (..) , MatchClause (..) , MatcherInfo (..) , LoopRange (..) , PrimitivePatPattern (..) , PrimitiveDataPattern (..) -- * Egison values , EgisonValue (..) , Matcher (..) , PrimitiveFunc (..) , Egison (..) -- * Internal data , Object (..) , ObjectRef (..) , WHNFData (..) , Intermediate (..) , Inner (..) , EgisonWHNF (..) -- * Environment , Env (..) , Var (..) , Binding (..) , nullEnv , extendEnv , refVar -- * Pattern matching , Match , PMMode (..) , pmMode , MatchingState (..) , MatchingTree (..) , PatternBinding (..) , LoopContext (..) -- * Errors , EgisonError (..) , liftError -- * Monads , EgisonM (..) , runEgisonM , liftEgisonM , fromEgisonM , FreshT (..) , Fresh (..) , MonadFresh (..) , runFreshT , MatchM (..) , matchFail , MList (..) , fromList , fromSeq , fromMList , msingleton , mfoldr , mappend , mconcat , mmap , mfor ) where import Prelude hiding (foldr) import Control.Exception import Data.Typeable import Control.Applicative import Control.Monad.Error import Control.Monad.State import Control.Monad.Reader (ReaderT) import Control.Monad.Writer (WriterT) import Control.Monad.Identity import Control.Monad.Trans.Maybe import Data.Monoid (Monoid) import qualified Data.Array as Array import qualified Data.Sequence as Sq import Data.Sequence (Seq) import Data.Foldable (foldr, toList) import Data.IORef import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HashMap import Data.ByteString.Lazy (ByteString) import Data.ByteString.Lazy.Char8 () import qualified Data.ByteString.Lazy.Char8 as B import System.IO import Data.Ratio import System.IO.Unsafe (unsafePerformIO) -- -- Expressions -- data EgisonTopExpr = Define String EgisonExpr | Test EgisonExpr | Execute EgisonExpr -- temporary : we will replace load to import and export | LoadFile String | Load String deriving (Show) data EgisonExpr = CharExpr Char | StringExpr String | BoolExpr Bool | RationalExpr Rational | IntegerExpr Integer | FloatExpr Double | VarExpr String | IndexedExpr EgisonExpr [EgisonExpr] | InductiveDataExpr String [EgisonExpr] | TupleExpr [EgisonExpr] | CollectionExpr [InnerExpr] | ArrayExpr [EgisonExpr] | HashExpr [(EgisonExpr, EgisonExpr)] | LambdaExpr [String] EgisonExpr | MemoizedLambdaExpr [String] EgisonExpr | MemoizeExpr EgisonExpr | PatternFunctionExpr [String] EgisonPattern | IfExpr EgisonExpr EgisonExpr EgisonExpr | LetExpr [BindingExpr] EgisonExpr | LetRecExpr [BindingExpr] EgisonExpr | MatchExpr EgisonExpr EgisonExpr [MatchClause] | MatchAllExpr EgisonExpr EgisonExpr MatchClause | MatchLambdaExpr EgisonExpr [MatchClause] | MatchAllLambdaExpr EgisonExpr MatchClause | NextMatchExpr EgisonExpr EgisonExpr [MatchClause] | NextMatchAllExpr EgisonExpr EgisonExpr MatchClause | NextMatchLambdaExpr EgisonExpr [MatchClause] | NextMatchAllLambdaExpr EgisonExpr MatchClause | MatcherBFSExpr MatcherInfo | MatcherDFSExpr MatcherInfo | DoExpr [BindingExpr] EgisonExpr | IoExpr EgisonExpr | SeqExpr EgisonExpr EgisonExpr | ApplyExpr EgisonExpr EgisonExpr | AlgebraicDataMatcherExpr [(String, [EgisonExpr])] | GenerateArrayExpr [String] EgisonExpr EgisonExpr | ArrayBoundsExpr EgisonExpr | ArrayRefExpr EgisonExpr EgisonExpr | SomethingExpr | UndefinedExpr deriving (Show) data InnerExpr = ElementExpr EgisonExpr | SubCollectionExpr EgisonExpr deriving (Show) type BindingExpr = ([String], EgisonExpr) type MatchClause = (EgisonPattern, EgisonExpr) type MatcherInfo = [(PrimitivePatPattern, EgisonExpr, [(PrimitiveDataPattern, EgisonExpr)])] data EgisonPattern = WildCard | PatVar String | ValuePat EgisonExpr | PredPat EgisonExpr | IndexedPat EgisonPattern [EgisonExpr] | LetPat [BindingExpr] EgisonPattern | NotPat EgisonPattern | AndPat [EgisonPattern] | OrPat [EgisonPattern] | TuplePat [EgisonPattern] | InductivePat String [EgisonPattern] | LoopPat String LoopRange EgisonPattern EgisonPattern | ContPat | ApplyPat EgisonExpr [EgisonPattern] | VarPat String deriving (Show) data LoopRange = LoopRange EgisonExpr EgisonExpr EgisonPattern deriving (Show) data PrimitivePatPattern = PPWildCard | PPPatVar | PPValuePat String | PPInductivePat String [PrimitivePatPattern] deriving (Show) data PrimitiveDataPattern = PDWildCard | PDPatVar String | PDInductivePat String [PrimitiveDataPattern] | PDEmptyPat | PDConsPat PrimitiveDataPattern PrimitiveDataPattern | PDSnocPat PrimitiveDataPattern PrimitiveDataPattern | PDConstantPat EgisonExpr deriving (Show) -- -- Values -- data EgisonValue = World | Char Char | Bool Bool | Rational Rational | Integer Integer | Float Double | InductiveData String [EgisonValue] | Tuple [EgisonValue] | Collection (Seq EgisonValue) | Array (Array.Array Integer EgisonValue) | IntHash (HashMap Integer EgisonValue) | StrHash (HashMap ByteString EgisonValue) | UserMatcher Env PMMode MatcherInfo | Func Env [String] EgisonExpr | MemoizedFunc ObjectRef (IORef (HashMap [Integer] ObjectRef)) Env [String] EgisonExpr | PatternFunc Env [String] EgisonPattern | PrimitiveFunc PrimitiveFunc | IOFunc (EgisonM WHNFData) | Port Handle | Something | Undefined | EOF type Matcher = EgisonValue type PrimitiveFunc = WHNFData -> EgisonM WHNFData instance Show EgisonValue where show (Char c) = "'" ++ [c] ++ "'" show (Bool True) = "#t" show (Bool False) = "#f" show (Rational x) = show (numerator x) ++ "/" ++ show (denominator x) show (Integer i) = show i show (Float f) = show f show (InductiveData name []) = "<" ++ name ++ ">" show (InductiveData name vals) = "<" ++ name ++ " " ++ unwords (map show vals) ++ ">" show (Tuple vals) = "[" ++ unwords (map show vals) ++ "]" show (Collection vals) = if Sq.null vals then "{}" else if all isChar (toList vals) then "\"" ++ map (\(Char c) -> c) (toList vals) ++ "\"" else "{" ++ unwords (map show (toList vals)) ++ "}" where isChar :: EgisonValue -> Bool isChar (Char _) = True isChar _ = False show (Array vals) = "[|" ++ unwords (map show $ Array.elems vals) ++ "|]" show (IntHash hash) = "{|" ++ unwords (map (\(key, val) -> "[" ++ show key ++ " " ++ show val ++ "]") $ HashMap.toList hash) ++ "|}" show (StrHash hash) = "{|" ++ unwords (map (\(key, val) -> "[\"" ++ B.unpack key ++ "\" " ++ show val ++ "]") $ HashMap.toList hash) ++ "|}" show (UserMatcher _ BFSMode _) = "#" show (UserMatcher _ DFSMode _) = "#" show (Func _ names _) = "(lambda [" ++ unwords names ++ "] ...)" show (PatternFunc _ _ _) = "#" show (PrimitiveFunc _) = "#" show (IOFunc _) = "#" show (Port _) = "#" show Something = "something" show Undefined = "undefined" show World = "#" show EOF = "#" instance Eq EgisonValue where (Char c) == (Char c') = c == c' (Bool b) == (Bool b') = b == b' (Integer i) == (Integer i') = i == i' (Float f) == (Float f') = f == f' (InductiveData name vals) == (InductiveData name' vals') = name == name' && vals == vals' (Tuple vals) == (Tuple vals') = vals == vals' (Collection vals) == (Collection vals') = vals == vals' (Array vals) == (Array vals') = vals == vals' (IntHash vals) == (IntHash vals') = vals == vals' (StrHash vals) == (StrHash vals') = vals == vals' _ == _ = False -- -- Egison data and Haskell data -- class Egison a where toEgison :: a -> EgisonValue fromEgison :: EgisonValue -> EgisonM a instance Egison Char where toEgison c = Char c fromEgison = liftError . fromCharValue instance Egison Bool where toEgison b = Bool b fromEgison = liftError . fromBoolValue instance Egison Integer where toEgison i = Integer i fromEgison = liftError . fromIntegerValue instance Egison Rational where toEgison r = Rational r fromEgison = liftError . fromRationalValue instance Egison Double where toEgison f = Float f fromEgison = liftError . fromFloatValue instance Egison Handle where toEgison h = Port h fromEgison = liftError . fromPortValue instance (Egison a) => Egison [a] where toEgison xs = Collection $ Sq.fromList (map toEgison xs) fromEgison (Collection seq) = mapM fromEgison (toList seq) fromEgison val = liftError $ throwError $ TypeMismatch "collection" (Value val) instance Egison () where toEgison () = Tuple [] fromEgison (Tuple []) = return () fromEgison val = liftError $ throwError $ TypeMismatch "zero element tuple" (Value val) instance (Egison a, Egison b) => Egison (a, b) where toEgison (x, y) = Tuple [toEgison x, toEgison y] fromEgison (Tuple (x:y:[])) = (liftM2 (,)) (fromEgison x) (fromEgison y) fromEgison val = liftError $ throwError $ TypeMismatch "two elements tuple" (Value val) instance (Egison a, Egison b, Egison c) => Egison (a, b, c) where toEgison (x, y, z) = Tuple [toEgison x, toEgison y, toEgison z] fromEgison (Tuple (x:y:z:[])) = do x' <- fromEgison x y' <- fromEgison y z' <- fromEgison z return (x', y', z') fromEgison val = liftError $ throwError $ TypeMismatch "two elements tuple" (Value val) instance (Egison a, Egison b, Egison c, Egison d) => Egison (a, b, c, d) where toEgison (x, y, z, w) = Tuple [toEgison x, toEgison y, toEgison z, toEgison w] fromEgison (Tuple (x:y:z:w:[])) = do x' <- fromEgison x y' <- fromEgison y z' <- fromEgison z w' <- fromEgison w return (x', y', z', w') fromEgison val = liftError $ throwError $ TypeMismatch "two elements tuple" (Value val) fromCharValue :: EgisonValue -> Either EgisonError Char fromCharValue (Char c) = return c fromCharValue val = throwError $ TypeMismatch "char" (Value val) fromBoolValue :: EgisonValue -> Either EgisonError Bool fromBoolValue (Bool b) = return b fromBoolValue val = throwError $ TypeMismatch "bool" (Value val) fromIntegerValue :: EgisonValue -> Either EgisonError Integer fromIntegerValue (Integer i) = return i fromIntegerValue val = throwError $ TypeMismatch "integer" (Value val) fromRationalValue :: EgisonValue -> Either EgisonError Rational fromRationalValue (Rational x) = return x fromRationalValue val = throwError $ TypeMismatch "rational" (Value val) fromFloatValue :: EgisonValue -> Either EgisonError Double fromFloatValue (Float f) = return f fromFloatValue val = throwError $ TypeMismatch "float" (Value val) fromPortValue :: EgisonValue -> Either EgisonError Handle fromPortValue (Port h) = return h fromPortValue val = throwError $ TypeMismatch "port" (Value val) -- -- Internal Data -- -- |For memoization type ObjectRef = IORef Object data Object = Thunk (EgisonM WHNFData) | WHNF WHNFData data WHNFData = Intermediate Intermediate | Value EgisonValue data Intermediate = IInductiveData String [ObjectRef] | ITuple [ObjectRef] | ICollection (IORef (Seq Inner)) | IArray (Array.Array Integer ObjectRef) | IIntHash (HashMap Integer ObjectRef) | IStrHash (HashMap ByteString ObjectRef) data Inner = IElement ObjectRef | ISubCollection ObjectRef instance Show WHNFData where show (Value val) = show val show (Intermediate (IInductiveData name _)) = "<" ++ name ++ " ...>" show (Intermediate (ITuple _)) = "[...]" show (Intermediate (ICollection _)) = "{...}" show (Intermediate (IArray _)) = "[|...|]" show (Intermediate (IIntHash _)) = "{|...|}" show (Intermediate (IStrHash _)) = "{|...|}" instance Show Object where show (Thunk _) = "#" show (WHNF whnf) = show whnf instance Show ObjectRef where show _ = "#" -- -- Extract data from WHNF -- class (Egison a) => EgisonWHNF a where toWHNF :: a -> WHNFData fromWHNF :: WHNFData -> EgisonM a toWHNF = Value . toEgison instance EgisonWHNF Char where fromWHNF = liftError . fromCharWHNF instance EgisonWHNF Bool where fromWHNF = liftError . fromBoolWHNF instance EgisonWHNF Integer where fromWHNF = liftError . fromIntegerWHNF instance EgisonWHNF Rational where fromWHNF = liftError . fromRationalWHNF instance EgisonWHNF Double where fromWHNF = liftError . fromFloatWHNF instance EgisonWHNF Handle where fromWHNF = liftError . fromPortWHNF fromCharWHNF :: WHNFData -> Either EgisonError Char fromCharWHNF (Value (Char c)) = return c fromCharWHNF whnf = throwError $ TypeMismatch "char" whnf fromBoolWHNF :: WHNFData -> Either EgisonError Bool fromBoolWHNF (Value (Bool b)) = return b fromBoolWHNF whnf = throwError $ TypeMismatch "bool" whnf fromIntegerWHNF :: WHNFData -> Either EgisonError Integer fromIntegerWHNF (Value (Integer i)) = return i fromIntegerWHNF whnf = throwError $ TypeMismatch "integer" whnf fromRationalWHNF :: WHNFData -> Either EgisonError Rational fromRationalWHNF (Value (Rational x)) = return x fromRationalWHNF whnf = throwError $ TypeMismatch "rational" whnf fromFloatWHNF :: WHNFData -> Either EgisonError Double fromFloatWHNF (Value (Float f)) = return f fromFloatWHNF whnf = throwError $ TypeMismatch "float" whnf fromPortWHNF :: WHNFData -> Either EgisonError Handle fromPortWHNF (Value (Port h)) = return h fromPortWHNF whnf = throwError $ TypeMismatch "port" whnf class (EgisonWHNF a) => EgisonObject a where toObject :: a -> Object toObject = WHNF . toWHNF -- -- Environment -- type Env = [HashMap Var ObjectRef] type Var = String type Binding = (Var, ObjectRef) nullEnv :: Env nullEnv = [] extendEnv :: Env -> [Binding] -> Env extendEnv env = (: env) . HashMap.fromList refVar :: Env -> Var -> EgisonM ObjectRef refVar env var = maybe (throwError $ UnboundVariable var) return (msum $ map (HashMap.lookup var) env) -- -- Pattern Match -- type Match = [Binding] data PMMode = BFSMode | DFSMode deriving (Show) pmMode :: Matcher -> PMMode pmMode (UserMatcher _ mode _) = mode pmMode (Tuple _) = DFSMode pmMode Something = DFSMode data MatchingState = MState Env [LoopContext] [Binding] [MatchingTree] deriving (Show) data MatchingTree = MAtom EgisonPattern ObjectRef Matcher | MNode [PatternBinding] MatchingState deriving (Show) type PatternBinding = (Var, EgisonPattern) data LoopContext = LoopContext Binding ObjectRef EgisonPattern EgisonPattern EgisonPattern deriving (Show) -- -- Errors -- data EgisonError = UnboundVariable Var | TypeMismatch String WHNFData | ArgumentsNumWithNames [String] Int Int | ArgumentsNumPrimitive Int Int | ArgumentsNum Int Int | NotImplemented String | Assertion String | Match String | Parser String | Desugar String | EgisonBug String | Default String deriving Typeable instance Show EgisonError where show (Parser err) = "Parse error at: " ++ err show (UnboundVariable var) = "Unbound variable: " ++ var show (TypeMismatch expected found) = "Expected " ++ expected ++ ", but found: " ++ show found show (ArgumentsNumWithNames names expected got) = "Wrong number of arguments: " ++ show names ++ ": expected " ++ show expected ++ ", but got " ++ show got show (ArgumentsNumPrimitive expected got) = "Wrong number of arguments for a primitive function: expected " ++ show expected ++ ", but got " ++ show got show (ArgumentsNum expected got) = "Wrong number of arguments: expected " ++ show expected ++ ", but got " ++ show got show (NotImplemented message) = "Not implemented: " ++ message show (Assertion message) = "Assertion failed: " ++ message show (Desugar message) = "Error: " ++ message show (EgisonBug message) = "Egison Error: " ++ message show (Default message) = "Error: " ++ message instance Exception EgisonError instance Error EgisonError where noMsg = Default "An error has occurred" strMsg = Default liftError :: (MonadError e m) => Either e a -> m a liftError = either throwError return -- -- Monads -- newtype EgisonM a = EgisonM { unEgisonM :: ErrorT EgisonError (FreshT IO) a } deriving (Functor, Applicative, Monad, MonadIO, MonadError EgisonError, MonadFresh) runEgisonM :: EgisonM a -> FreshT IO (Either EgisonError a) runEgisonM = runErrorT . unEgisonM liftEgisonM :: Fresh (Either EgisonError a) -> EgisonM a liftEgisonM m = EgisonM $ ErrorT $ FreshT $ do s <- get (a, s') <- return $ runFresh s m put s' return $ either throwError return $ a fromEgisonM :: EgisonM a -> IO (Either EgisonError a) fromEgisonM = modifyCounter . runEgisonM counter :: IORef Int counter = unsafePerformIO (newIORef 0) readCounter :: IO Int readCounter = readIORef counter updateCounter :: Int -> IO () updateCounter = writeIORef counter modifyCounter :: FreshT IO a -> IO a modifyCounter m = do seed <- readCounter (result, seed) <- runFreshT seed m updateCounter seed return result newtype FreshT m a = FreshT { unFreshT :: StateT Int m a } deriving (Functor, Applicative, Monad, MonadState Int, MonadTrans) type Fresh = FreshT Identity class (Applicative m, Monad m) => MonadFresh m where fresh :: m String instance (Applicative m, Monad m) => MonadFresh (FreshT m) where fresh = FreshT $ do counter <- get; modify (+ 1) return $ "$_" ++ show counter instance (MonadError e m) => MonadError e (FreshT m) where throwError = lift . throwError catchError m h = FreshT $ catchError (unFreshT m) (unFreshT . h) instance (MonadState s m) => MonadState s (FreshT m) where get = lift $ get put s = lift $ put s instance (MonadFresh m) => MonadFresh (StateT s m) where fresh = lift $ fresh instance (MonadFresh m, Error e) => MonadFresh (ErrorT e m) where fresh = lift $ fresh instance (MonadFresh m, Monoid e) => MonadFresh (ReaderT e m) where fresh = lift $ fresh instance (MonadFresh m, Monoid e) => MonadFresh (WriterT e m) where fresh = lift $ fresh instance MonadIO (FreshT IO) where liftIO = lift runFreshT :: Monad m => Int -> FreshT m a -> m (a, Int) runFreshT seed = flip (runStateT . unFreshT) seed runFresh :: Int -> Fresh a -> (a, Int) runFresh seed m = runIdentity $ flip runStateT seed $ unFreshT m type MatchM = MaybeT EgisonM matchFail :: MatchM a matchFail = MaybeT $ return Nothing data MList m a = MNil | MCons a (m (MList m a)) instance Show (MList m a) where show MNil = "MNil" show (MCons _ _) = "(MCons ... ...)" fromList :: Monad m => [a] -> MList m a fromList = foldr f MNil where f x xs = MCons x $ return xs fromSeq :: Monad m => Seq a -> MList m a fromSeq = foldr f MNil where f x xs = MCons x $ return xs fromMList :: Monad m => MList m a -> m [a] fromMList = mfoldr f $ return [] where f x xs = xs >>= return . (x:) msingleton :: Monad m => a -> MList m a msingleton = flip MCons $ return MNil mfoldr :: Monad m => (a -> m b -> m b) -> m b -> MList m a -> m b mfoldr f init MNil = init mfoldr f init (MCons x xs) = f x (xs >>= mfoldr f init) mappend :: Monad m => MList m a -> m (MList m a) -> m (MList m a) mappend xs ys = mfoldr ((return .) . MCons) ys xs mconcat :: Monad m => MList m (MList m a) -> m (MList m a) mconcat = mfoldr mappend $ return MNil mmap :: Monad m => (a -> m b) -> MList m a -> m (MList m b) mmap f = mfoldr g $ return MNil where g x xs = f x >>= return . flip MCons xs mfor :: Monad m => MList m a -> (a -> m b) -> m (MList m b) mfor = flip mmap