考虑一个简单的 Haskell Brainf*ck解释器。只看interpret
功能。
import Prelude hiding (Either(..))
import Control.Monad
import Data.Char (ord, chr)
-- function in question
interpret :: String -> IO ()
interpret strprog = let (prog, []) = parse strprog
in execBF prog
interpretFile :: FilePath -> IO ()
interpretFile fp = readFile fp >>= interpret
type BF = [BFInstr]
data BFInstr = Left | Right | Inc | Dec | Input | Output | Loop BF
type Tape = ([Integer], [Integer])
emptyTape = (repeat 0, repeat 0)
execBFTape :: Tape -> BF -> IO Tape
execBFTape = foldM doBF
execBF :: BF -> IO ()
execBF prog = do
execBFTape emptyTape prog
return ()
doBF :: Tape -> BFInstr -> IO Tape
doBF ((x:lefts), rights) Left = return (lefts, x:rights)
doBF (lefts, (x:rights)) Right = return (x:lefts, rights)
doBF (left, (x:rights)) Inc = return (left, (x+1):rights)
doBF (left, (x:rights)) Dec = return (left, (x-1):rights)
doBF (left, (_:rights)) Input = getChar >>= \c -> return (left, fromIntegral (ord c):rights)
doBF t@(_, (x: _)) Output = putChar (chr (fromIntegral x)) >> return t
doBF t@(left, (x: _)) (Loop bf) = if x == 0
then return t
else do t' <- execBFTape t bf
doBF t' (Loop bf)
simpleCommands = [('<', Left),
('>', Right),
(',', Input),
('.', Output),
('+', Inc),
('-', Dec)]
parse :: String -> (BF, String)
parse [] = ([], [])
parse (char:prog) = case lookup char simpleCommands of
Just command -> let (rest, prog') = parse prog
in (command : rest, prog')
Nothing ->
case char of
']' -> ([], prog)
'[' -> let (loop, prog') = parse prog
(rest, prog'') = parse prog'
in (Loop loop:rest, prog'')
_ -> parse prog
所以我有一个像interpret "[->+<]"
. 这给了我一个IO ()
执行给定程序的单子动作。它具有成为main
某些程序的正确类型。
假设我想将此操作编译为可执行文件,也就是说,我想生成一个可执行文件,其结果为interpret ...
main 函数。当然,这个可执行文件必须包含 GHC 运行时系统(用于无限列表、整数运算等)。
问题:
- 我认为根本不可能只采取单子动作并将其保存为新文件。这是真的?
- 一个人怎么能找到一个类似的解决方案呢?GHC Api 和提示有帮助吗?
编辑
抱歉,我在原始问题中过于简单化了。当然,我可以像这样写一个文件:
main = interpret "..."
但这不是我们在尝试编译某些东西时通常会做的事情,所以请考虑一下interpretFile :: FilePath -> IO ()
。让BF程序保存在一个文件中(helloworld.bf
)。
我将如何创建一个可执行文件,该可执行文件在helloworld.bf
实际上不需要该文件的情况下执行该文件的内容?
$ ./MyBfCompiler helloworld.bf -o helloworld