packages feed

haskelisp-0.1.0.1: src/Emacs/Core.hs

{-# LANGUAGE ForeignFunctionInterface,OverloadedStrings,DataKinds,TypeFamilies,KindSignatures,FlexibleInstances,UndecidableInstances #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DataKinds #-}

module Emacs.Core (
    module Emacs.Internal,
    defmodule,
    -- mk
    mkCons,
    -- funcall
    ToEmacsValue(..),
    funcall1, funcall2, funcall3,
    mkFunctionFromCallable,
    Callable,
    --
    car,
    cdr,
    --
    evalString,
    provide,
    message,
    ) where

import Prelude()
import Protolude hiding (mkInteger)
import Foreign.C.Types
import Foreign.StablePtr
import Emacs.Type
import Emacs.Internal

-- emacsModuleInit に渡す関数
defmodule :: Text -> EmacsM a -> Ptr () -> IO CInt
defmodule name mod ert = do
  env <- getEmacsEnvFromRT ert
  errorHandle env $ do
    ctx <- initCtx env
    runEmacsM ctx $ mod >> funcall1 "provide" (Symbol name)
  return 0

-- | Haskell -> EmacsValue への変換
-- 利便性だけのためかな?
class ToEmacsValue h where
  toEv :: h -> EmacsM EmacsValue

instance ToEmacsValue Int where
  toEv = mkInteger

instance ToEmacsValue Text where
  toEv = mkString

instance ToEmacsValue Symbol where
  toEv (Symbol name) = intern name

instance ToEmacsValue Bool where
  toEv True  = mkT
  toEv False = mkNil

instance ToEmacsValue () where
  toEv _ = mkNil

instance ToEmacsValue EmacsValue where
  toEv = pure

instance ToEmacsValue h => ToEmacsValue [h] where
  toEv xs =
    join $ mkList <$> mapM toEv xs

instance (ToEmacsValue a, ToEmacsValue b) => ToEmacsValue (a,b) where
  toEv (a,b) = do
    av <- toEv a
    bv <- toEv b
    mkCons av bv

-- シンボルを渡す場所は多いので。ただ ToEmacsValue と関係持たせていは
-- いけない。
--
-- 通常は Text の引数でいいと思うんだけ(いちいち)文字列ではなく、シン
-- ボルにするよと明示したいところでは利用するのがいいかな?後は関数か
-- らのシンボルの返値を利用する場合とかかな(Text だと、一旦
-- symbol-name で文字列表現を取り出す必要が出てくる)。
--
-- ああ、やはりどうだろう。そもそもシンボルという概念がhaskellには必要
-- ないか?objectではなく、eq で等価性が判定されるので文字列と同義にな
-- るのかな?(ただ空文字列は駄目かな)。
-- ESymbol でも動作させたい関数は ' バージョンを作るのがいいかな。
-- class ToESymbol h where
--   toESymbol  :: h -> EmacsM ESymbol
--   toESymbol' :: h -> EmacsM EmacsValue
--   toESymbol' = fmap (untag) . toESymbol
-- instance ToESymbol Symbol where
--   toESymbol (Symbol name) = mkESymbol name
-- instance ToESymbol ESymbol where
--   toESymbol = pure
-- instance ToESymbol (EmacsM ESymbol) where
--   toESymbol = id

-- for internal usage.
-- 関数名は ToESymbol ではなく、Text で受け取っているのは利便性のため。
funcall1 :: (ToEmacsValue a)
         => Text -> a -> EmacsM EmacsValue
funcall1 fname ev0 =
  join $ funcall <$> intern fname
                   <*> sequence [toEv ev0]

funcall2 :: (ToEmacsValue a, ToEmacsValue b)
         => Text -> a -> b -> EmacsM EmacsValue
funcall2 fname ev0 ev1 =
  join $ funcall <$> intern fname
                   <*> sequence [toEv ev0, toEv ev1]

funcall3 :: (ToEmacsValue a, ToEmacsValue b, ToEmacsValue c)
         => Text -> a -> b -> c -> EmacsM EmacsValue
funcall3 fname ev0 ev1 ev2 =
  join $ funcall <$> intern fname
                   <*> sequence [toEv ev0, toEv ev1, toEv ev2]


-- | Haskell <- EmacsValue
-- これは mkEFunction のために?
class FromEmacsValue h where
  fromEv :: EmacsValue -> EmacsM (Maybe h)

-- 何故か Num b => .. だと Overlapping で怒られる。分からん。。
instance FromEmacsValue Int where
  fromEv = extractIntegerMaybe

instance FromEmacsValue EmacsValue where
  fromEv = pure . Just

-- hack
-- 多相的な関数は駄目らしい(具体的な関数ならokらしい)
class Callable a where
    call :: a -> [EmacsValue] -> EmacsM (Either Text EmacsValue)
    arity :: a -> Int

instance {-# OVERLAPPING #-} ToEmacsValue a => Callable a where
    call a [] = Right <$> toEv a
    call _ _  = pure $ Left "Too many arguments"
    arity _ = 0

instance {-# OVERLAPPING #-} ToEmacsValue a => Callable (IO a) where
    call a [] = do
      v <- liftIO a
      Right <$> toEv v
    call _ _  = pure $ Left "Too many arguments"
    arity _ = 0

instance {-# OVERLAPPING #-} ToEmacsValue a => Callable (EmacsM a) where
    call am [] = do
      a <- am
      Right <$> toEv a
    call _ _  = pure $ Left "Too many arguments"
    arity _ = 0

instance {-# OVERLAPPING #-} (FromEmacsValue a, Callable b) => Callable (a -> b) where

  call f (e:es) = do
    av' <- fromEv e
    case av' of
      Just av -> call (f av) es
      Nothing -> pure $ Left ""
  call _ [] = pure $ Left "Too less arguments"
  arity f = arity (f undefined) + 1

-- |
-- 多層的な関数は怒られるはず。
--
mkFunctionFromCallable :: Callable f => f -> EmacsM EmacsValue
mkFunctionFromCallable f = do
  let a = arity f
  mkFunction func a a ""
  where
    func :: [EmacsValue] -> EmacsM EmacsValue
    func es = do
      res <- call f es
      case res of
        Right ev -> return ev
        Left _   -> undefined

evalString :: Text -> EmacsM EmacsValue
evalString t =
  funcall1 "eval" =<< (car =<< funcall1 "read-from-string" t)

provide :: Text -> EmacsM ()
provide feature =
  void $ funcall1 "provide" (Symbol feature)

message :: Text -> EmacsM ()
message t =
  void $ funcall1 "message" t

mkCons :: EmacsValue -> EmacsValue -> EmacsM EmacsValue
mkCons = funcall2 "cons"

car :: EmacsValue -> EmacsM EmacsValue
car = funcall1 "car"

cdr :: EmacsValue -> EmacsM EmacsValue
cdr = funcall1 "cdr"