lens(http://hackage.haskell.org/package/lens-3.5.1 )を使ってみた。Lensの実用性は高そうだ。
brainfuck.hs
{-# LANGUAGE TemplateHaskell #-}
import qualified Data.IntMap.Strict as M
import Control.Lens
import Control.Applicative
import Control.Monad.State
import Data.Word
import System.Environment
data Op = Inc | Dec | Prev | Next | Get | Put | Loop [Op]
data Machine = Machine {_cursor :: Int, _tape :: M.IntMap Word8}
$(makeLenses ''Machine)
runOp :: Op -> StateT Machine IO ()
runOp op = do
(i, v) <- current
case op of
Inc -> tape %= (at i ?~ succ v)
Dec -> tape %= (at i ?~ pred v)
Prev -> cursor .= pred i
Next -> cursor .= succ i
Get -> do
ch <- lift getChar
tape %= (at i ?~ toEnum (fromEnum ch))
Put -> lift $ putChar $ toEnum $ fromEnum v
Loop code -> fix $ \self -> do
(_, v) <- current
when (v /= 0) $ mapM_ runOp code >> self
where
current = use cursor >>= \i -> (,) i <$> maybe 0 id <$> uses tape (view $ at i)
parse :: String -> (String, [Op])
parse "" = ("", [])
parse ('+':xs) = (Inc:) <$> parse xs
parse ('-':xs) = (Dec:) <$> parse xs
parse ('<':xs) = (Prev:) <$> parse xs
parse ('>':xs) = (Next:) <$> parse xs
parse (',':xs) = (Get:) <$> parse xs
parse ('.':xs) = (Put:) <$> parse xs
parse ('[':xs) = let (xs', op) = Loop <$> parse xs in (op:) <$> parse xs'
parse (']':xs) = (xs, [])
parse (_:xs) = parse xs
main = getArgs >>= \(path:_) -> do
code <- readFile path
flip runStateT (Machine 0 M.empty) $ mapM_ runOp $ snd $ parse code