haskelisp 0.1.0.1 → 0.1.0.2
raw patch · 7 files changed
+55/−139 lines, 7 filesdep −logging-effect
Dependencies removed: logging-effect
Files
- haskelisp.cabal +25/−5
- src/Emacs.hs +0/−2
- src/Emacs/Core.hs +5/−40
- src/Emacs/Internal.hs +6/−42
- src/Emacs/Symbol.hs +15/−0
- src/Emacs/Type.hs +4/−26
- src/Emacs/Variable.hs +0/−24
haskelisp.cabal view
@@ -1,5 +1,5 @@ name: haskelisp-version: 0.1.0.1+version: 0.1.0.2 cabal-version: >=1.10 build-type: Simple license: GPL-3@@ -9,7 +9,29 @@ homepage: http://github.com/githubuser/haskelisp#readme synopsis: Write Emacs module in Haskell, using Emacs 25's Dynamic Module feature description:- Please see README.md+ EXPERIMENTAL+ Write Emacs module in Haskell, using Emacs 25's Dynamic Module feature.+ .+ * Only tested with linux.+ * You need to build emacs with --with-modules configuration options+ * You need to specify some ghc-options+ .+ >+ -# LANGUAGE ForeignFunctionInterface,OverloadedStrings #-+ > module Main where+ >+ > import Emacs+ >+ > foreign export ccall "emacs_module_init" emacsModuleInit :: EmacsModule+ >+ > emacsModuleInit :: EmacsModule+ > emacsModuleInit = defmodule "sample-module" $ do+ >+ > setVal "foo" (Symbol "bar")+ >+ > defun "square" $ \i -> do+ > message "haskell squre function called"+ > return $ (i*i :: Int) category: Editor, Emacs author: Takenari Shinohara @@ -24,7 +46,6 @@ Emacs.Internal Emacs.Symbol Emacs.Core- Emacs.Variable Emacs.Function Emacs.Command build-depends:@@ -32,8 +53,7 @@ protolude <0.2, containers <0.6, mtl <2.3,- text <1.3,- logging-effect <1.1+ text <1.3 c-sources: cbits/stab.c default-language: Haskell2010
src/Emacs.hs view
@@ -1,13 +1,11 @@ module Emacs ( module Emacs.Core , module Emacs.Symbol- , module Emacs.Variable , module Emacs.Function , module Emacs.Command ) where import Emacs.Core import Emacs.Symbol-import Emacs.Variable import Emacs.Function import Emacs.Command
src/Emacs/Core.hs view
@@ -1,10 +1,7 @@ {-# 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,@@ -33,7 +30,7 @@ import Emacs.Internal -- emacsModuleInit に渡す関数-defmodule :: Text -> EmacsM a -> Ptr () -> IO CInt+defmodule :: Text -> EmacsM a -> EmacsModule defmodule name mod ert = do env <- getEmacsEnvFromRT ert errorHandle env $ do@@ -41,8 +38,6 @@ runEmacsM ctx $ mod >> funcall1 "provide" (Symbol name) return 0 --- | Haskell -> EmacsValue への変換--- 利便性だけのためかな? class ToEmacsValue h where toEv :: h -> EmacsM EmacsValue @@ -75,52 +70,25 @@ 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]+ <*> 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]+ <*> 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]+ <*> sequence [toEv ev0, toEv ev1, toEv ev2] --- | Haskell <- EmacsValue--- これは mkEFunction のために? class FromEmacsValue h where fromEv :: EmacsValue -> EmacsM (Maybe h) @@ -131,7 +99,6 @@ instance FromEmacsValue EmacsValue where fromEv = pure . Just --- hack -- 多相的な関数は駄目らしい(具体的な関数ならokらしい) class Callable a where call :: a -> [EmacsValue] -> EmacsM (Either Text EmacsValue)@@ -166,9 +133,7 @@ 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
src/Emacs/Internal.hs view
@@ -1,9 +1,4 @@-{-# LANGUAGE KindSignatures #-}-{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE TemplateHaskell #-} module Emacs.Internal ( module Emacs.Type, initState,@@ -35,11 +30,6 @@ errorHandle ) where -{--emacs-modue.h の関数。-型は EmacsValue しか扱わない。基本、このレベルの関数は使うべきではない。--}- import Prelude() import Protolude hiding (mkInteger) import Control.Exception (displayException)@@ -56,7 +46,6 @@ import GHC.Ptr import qualified GHC.Foreign as GHC import GHC.IO.Encoding.UTF8 (utf8)-import Control.Monad.Log initState :: MonadIO m => m PState initState = do@@ -84,9 +73,8 @@ -- Logging here is not a good idea. When passing high order function, -- which could be invoked manytimes, its get quite slow. runEmacsM :: MonadIO m => Ctx -> EmacsM a -> m a-runEmacsM ctx action = do- let logHandler t = pure ()- liftIO $ runReaderT (runLoggingT action logHandler) ctx+runEmacsM ctx action =+ liftIO $ runReaderT action ctx foreign import ccall _get_emacs_env_from_rt :: Ptr ()@@ -96,8 +84,6 @@ getEmacsEnvFromRT = _get_emacs_env_from_rt --- TODO: シンボル のキャッシュしないと毎回シンボルを取得するのは遅い気--- がする。 foreign import ccall _type_of :: EmacsEnv -> EmacsValue@@ -120,12 +106,6 @@ t <- typeOf ev return $ t == ty --- | EmacsVAlue -> Haskell の変換------ ただし可能なのは限られている。例えば バッファとかは Haskell 側で対--- 応する型を持たないので EBuffer(もしくは EmacsValue)のままで扱う必要--- がある。- -- 引数が integer じゃない場合多分 signal が投げられる foreign import ccall _extract_integer :: EmacsEnv@@ -145,7 +125,7 @@ then (Just <$> extractInteger v) else return Nothing --- 使い方が今イチ曖昧だが、コード見たら分かりやすい。+-- emacs-module.c 参照 -- -- * Can throw signals(その場合 false が返る) -- * もし Buffer が null の場合、Length に文字列のutf8で格納する際の@@ -179,9 +159,9 @@ else pure "" else pure "" --- | どうなんだ?--- eq は bool 返すのだが、haskell では CBool は用意していないので int にして返している。--- module_eq は珍しく MODULE_FUNCTION_BEGIN を使っていない。+-- eq は bool 返すのだが、haskell では CBool は用意していないので int+-- にして返している。module_eq は珍しく MODULE_FUNCTION_BEGIN を使って+-- いない。 foreign import ccall _eq :: EmacsEnv -> EmacsValue@@ -208,10 +188,7 @@ isNil :: EmacsValue -> EmacsM Bool isNil = (fmap . fmap) not isNotNil --- TODO: castFunPtrToPtr はまずいかな?inline-c 自体にFunPtrを作る機能--- が備わっている?$fun:(...) 何故か使えない。。 -- TODO: arity と doc は Arity と Doc 型にするべきかな。- foreign import ccall _make_function :: EmacsEnv -> CPtrdiff@@ -405,14 +382,6 @@ -- そもそも list という型は emacs側には存在しない。 -- listp という関数があるが、これは cons もしくは nil かどうかを判定している。------ 以下のナイーブな実装だと大量に emacs 側の関数呼び出しが必要になり、遅い。------ mkList :: [EmacsValue] -> EmacsM EmacsValue--- mkList evs = do--- nil <- mkNil--- foldrM mkCons nil evs- mkList :: [EmacsValue] -> EmacsM EmacsValue mkList evs = do listQ <- intern "list"@@ -439,8 +408,6 @@ env <- getEnv toEnum . fromIntegral <$> liftIO (_non_local_exit_check env) ------ Emacs に戻ったときに signal を投げる。 foreign import ccall _non_local_exit_signal :: EmacsEnv -> EmacsValue@@ -491,9 +458,6 @@ free a0' free a1' return (toEnum (fromIntegral fe), a0, a1)---- | Emacs の--- 呼び出しは symbol に限らないのか。関数の直接起動とか。 foreign import ccall _funcall :: EmacsEnv
src/Emacs/Symbol.hs view
@@ -21,6 +21,21 @@ -- | Symbol has four slots? +setVal :: ToEmacsValue a => Text -> a -> EmacsM EmacsValue+setVal name val =+ funcall2 "set" (Symbol name) val++isBounded :: Text -> EmacsM Bool+isBounded name =+ isNotNil =<< funcall1 "boundp" (Symbol name)++getVal :: Text -> EmacsM (Maybe EmacsValue)+getVal name = do+ bounded <- isNotNil =<< funcall1 "boundp" (Symbol name)+ if bounded+ then Just <$> funcall1 "symbol-value" (Symbol name)+ else pure Nothing+ -- | Keyword Symbol -- | シンボルは任意の属性を持つことができる。
src/Emacs/Type.hs view
@@ -1,8 +1,5 @@-{-# LANGUAGE KindSignatures #-}-{-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE GADTs #-} {-# LANGUAGE OverloadedStrings #-} module Emacs.Type where @@ -12,7 +9,6 @@ import GHC.Ptr import Foreign.C.Types import Foreign.StablePtr-import Control.Monad.Log import Control.Monad.Reader import Data.Data hiding(typeOf) @@ -27,8 +23,7 @@ } type EmacsM =- LoggingT Text (ReaderT Ctx IO)-+ ReaderT Ctx IO -- | nil について --@@ -62,38 +57,21 @@ emacsTypes :: [EmacsType] emacsTypes = fromConstr <$> dataTypeConstrs (dataTypeOf ESymbol) --- Singlton Types テクニック--- 値と型を結び付けている--- 現在は tagAs のところでしか使っていないのでオーバーキルかな。。--- export しないほうがいいかも。internal だけに留めるかな?--- data SEmacsType (et :: EmacsType) where--- SESymbol :: SEmacsType 'ESymbol--- SEInteger :: SEmacsType 'EInteger--- SECons :: SEmacsType 'ECons--- SENil :: SEmacsType 'ENil --- emacsTypeValue :: SEmacsType b -> EmacsType--- emacsTypeValue SESymbol = ESymbol--- emacsTypeValue SEInteger = EInteger-+type EmacsModule = Ptr () -> IO CInt newtype EmacsEnv = EmacsEnv (Ptr ()) deriving (Storable)+ newtype EmacsValue = EmacsValue (Ptr ()) deriving (Storable)+ newtype GlobalEmacsValue = GlobalEmacsValue (Ptr ()) deriving (Storable) castGlobalToEmacsValue :: GlobalEmacsValue -> EmacsValue castGlobalToEmacsValue (GlobalEmacsValue p) = EmacsValue p---- type ESymbol = Tagged 'ESymbol EmacsValue--- type EInteger = Tagged 'EInteger EmacsValue--- type EFunction = Tagged 'EFunction EmacsValue--- type EString = Tagged 'EString EmacsValue--- type ECons = Tagged 'ECons EmacsValue--- type ENil = Tagged 'ENil EmacsValue -- | Emacs の値に対する Haskell の型 -- 数値や文字列は素直なんだけど、他
− src/Emacs/Variable.hs
@@ -1,24 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Emacs.Variable- ( setVariable- , variableValue- , isVariableBounded- ) where--import Emacs.Core-import Data.Text--setVariable :: ToEmacsValue a => Text -> a -> EmacsM EmacsValue-setVariable name val =- funcall2 "set" (Symbol name) val--isVariableBounded :: Text -> EmacsM Bool-isVariableBounded name =- isNotNil =<< funcall1 "boundp" (Symbol name)--variableValue :: Text -> EmacsM (Maybe EmacsValue)-variableValue name = do- bounded <- isNotNil =<< funcall1 "boundp" (Symbol name)- if bounded- then Just <$> funcall1 "symbol-value" (Symbol name)- else pure Nothing