単純な 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 ...
ます。つまり、メイン関数になる結果を含む実行可能ファイルを生成したいとします。もちろん、この実行可能ファイルには GHC ランタイム システム (無限リスト、整数演算など) が含まれている必要があります。
質問:
- 私の意見では、モナド アクションを実行して新しいファイルとして保存することはまったく不可能です。これは本当ですか?
- 同等の解決策に到達するにはどうすればよいでしょうか? GHC API とヒントは役に立ちますか?
編集
申し訳ありませんが、元の質問を単純化しすぎました。もちろん、次のようなファイルを書くこともできます。
main = interpret "..."
しかし、これは何かをコンパイルしようとするときに通常行うことではないため、interpretFile :: FilePath -> IO ()
代わりに検討してください。BF プログラムをファイル ( helloworld.bf
) に保存するとします。
helloworld.bf
実際にファイルを必要とせずにコンテンツを実行する実行可能ファイルを作成するにはどうすればよいですか?
$ ./MyBfCompiler helloworld.bf -o helloworld