c++ - 用 C++ 编写 Haskell 解释器(使用 ghc 或 hugs 作为库)

标签 c++ haskell ghc ghci hugs

我正在编写一个需要解释和评估 haskell 代码的 C++ 应用程序。此代码在编译时是未知的,而是由用户提供的。 有没有办法将 haskell 编译器/解释器(如 GHCi 或 hugs)用作库?

  • 我找到了 FFI,但这似乎只适用于编译时已知的 haskell 代码。
  • 我找到了 GHC API 和提示,但它们似乎只在我想从 haskell 中解释 haskell 代码时才起作用。

最佳答案

我建议不要使用 GHC api,而是绑定(bind)到 Hint对于这种特殊方法,它只是 GHC api 的简化包装器。我推荐这个的原因是因为 GHC api 的学习曲线有点陡峭。

但无论如何,就像我在评论中所说的那样,根据您希望它进行的深度,它需要的 FFI 调用数量惊人地少。下面我给出了一个示例,说明如何从加载的文件运行表达式并返回结果(仅当有 show 实例时)。这只是基础知识,应该也可以将结果作为结构返回。

module FFIInterpreter where

import Language.Haskell.Interpreter

import Data.IORef
import Foreign.StablePtr

type Session = Interpreter ()
type Context = StablePtr (IORef Session)

-- @@ Export
-- | Create a new empty Context to be used when calling any functions inside
--   this class.
--   .
--   String: The path to the module to load or the module name
createContext :: ModuleName -> IO Context
createContext name 
  = do let session = newModule name 
       _ <- runInterpreter session
       liftIO $ newStablePtr =<< newIORef session

newModule :: ModuleName -> Session
newModule name = loadModules [name] >> setTopLevelModules [name]

-- @@ Export
-- | free a context up
freeContext :: Context -> IO ()
freeContext = freeStablePtr

-- @@ Export = evalExpression
runExpr :: Context -> String -> IO String
runExpr env input
  = do env_value <- deRefStablePtr env
       tcs_value <- readIORef env_value
       result    <- runInterpreter (tcs_value >> eval input) 
       return $ either show id result

因为我们必须退出 haskell 领域,所以我们必须有一些方法来引用上下文,我们可以使用 StablePtr 来做到这一点,我只是将它包装在 IORef 让它可变,以防你将来想改变东西。请注意,GHC API 不支持对内存缓冲区进行类型检查,因此您必须在加载之前将要解释的代码保存到临时文件中。

--@@注解是我的工具Hs2lib的注解,不用的不用管。

我的测试文件是

module Test where

import Control.Monad
import Control.Monad.Instances

-- | This function calculates the value \x->x*x
bar :: Int -> Int
bar = join (*)

我们可以用一个简单的测试来测试它

*FFIInterpreter> session <- createContext "Test"
*FFIInterpreter> runExpr session "bar 5"
"25"

是的,它在 Haskell 中工作,现在让它在 haskell 之外工作。

只需在文件顶部为 Hs2lib 添加一些关于如何编码 ModuleName 的说明,因为该类型是在没有源代码的文件中定义的。

{- @@ INSTANCE ModuleName 0                 @@ -}
{- @@ HS2HS ModuleName CWString             @@ -}
{- @@ IMPORT "Data.IORef"                   @@ -}
{- @@ IMPORT "Language.Haskell.Interpreter" @@ -}
{- @@ HS2C  ModuleName "wchar_t*@4"         @@ -}

{- @@ HS2C  ModuleName "wchar_t*@8"         @@ -}

如果在 64 位架构上,

只需调用Hs2lib

PS Haskell\FFIInterpreter> hs2lib .\FFIInterpreter.hs -n "HsInterpreter"
Linking main.exe ...
Done.

你最终会得到一个包含文件

#ifdef __cplusplus
extern "C" {
#endif
// Runtime control methods
// HsStart :: IO ()
extern CALLTYPE(void) HsStart ( void );

// HsEnd :: IO ()
extern CALLTYPE(void) HsEnd ( void );

// createContext :: ModuleName -> IO (StablePtr (IORef (Interpreter ())))
//
// Create a new empty Context to be used when calling any functionsinside this class.
// String: The path to the module to load or themodule name
//
extern CALLTYPE(void*) createContext (wchar_t* arg1);

// freeContext :: StablePtr (IORef (Interpreter ())) -> IO ()
//
// free a context up
//
extern CALLTYPE(void) freeContext (void* arg1);

// evalExpression :: StablePtr (IORef (Interpreter ())) -> String -> IO String
extern CALLTYPE(wchar_t*) evalExpression (void* arg1, wchar_t* arg2);

#ifdef __cplusplus
}
#endif

我没有测试过 C++ 端,但没有理由它不工作。 这是一个非常简单的示例,如果将其编译为动态库,您可能需要重定向标准输出、标准错误和标准输入。

关于c++ - 用 C++ 编写 Haskell 解释器(使用 ghc 或 hugs 作为库),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8646247/

相关文章:

haskell - 如何让 GHC 将编译错误输出到文件而不是标准输出?

c++ - 采用 lambda 的模板类的类型推导

c++ - 程序已停止工作?如何使 int 数组大小超过 1,000,000?

haskell - 在 Haskell 中, `this@(Sentence string _) = do` 是什么意思?

haskell - 是否可以在haskell : (r -> [a]) -> [r -> a])?中编写函数

haskell - 将 Haskell Word32/64 中的 IEEE 754 浮点与 Haskell Float/Double 相互转换

haskell - 尾递归识别

c++ - 将多个参数传递给线程函数 c++11

c++ - 为什么转换不起作用?

haskell - 添加 ghci 的推断类型签名会导致错误