/versions/ioarray/Brainfuck.hs
http://github.com/rickardlindberg/brainfuck · Haskell · 117 lines · 90 code · 22 blank · 5 comment · 14 complexity · 519c22bdb441db148999ec390e96e4f0 MD5 · raw file
- module Brainfuck where
- import Data.Array.IO
- import Data.Char
- import Data.IORef
- import System.IO
- --
- data Command =
- MoveRight
- | MoveLeft
- | Increment
- | Decrement
- | Print
- | Read
- | LoopStart Int
- | LoopEnd Int
- | NOP
- deriving (Show, Eq)
- --
- data IOTape a = IOTape
- { ioTapePos :: IORef Int
- , ioTapeArray :: IOArray Int a
- }
- newIOTapeFromList :: [a] -> IO (IOTape a)
- newIOTapeFromList list = do
- pos <- newIORef 0
- arr <- newListArray (0, length list - 1) list
- return $ IOTape pos arr
- tapeMoveRight :: IOTape a -> IO ()
- tapeMoveRight tape = modifyIORef (ioTapePos tape) inc
- tapeMoveLeft :: IOTape a -> IO ()
- tapeMoveLeft tape = modifyIORef (ioTapePos tape) dec
- tapeMoveTo :: IOTape a -> Int -> IO ()
- tapeMoveTo tape n = writeIORef (ioTapePos tape) n
- tapeModify :: IOTape a -> (a -> a) -> IO ()
- tapeModify tape fn = do
- index <- readIORef (ioTapePos tape)
- value <- readArray (ioTapeArray tape) index
- writeArray (ioTapeArray tape) index (fn value)
- tapeCurrentValue :: IOTape a -> IO a
- tapeCurrentValue tape = do
- index <- readIORef (ioTapePos tape)
- value <- readArray (ioTapeArray tape) index
- return value
- --
- inc :: Int -> Int
- inc x = x + 1
- dec :: Int -> Int
- dec x = x - 1
- --
- type Token = (Int, Char)
- parse :: String -> [Command]
- parse str = parseTokens (tokenize str)
- tokenize :: String -> [Token]
- tokenize str = zip [0..length validCharacters - 1] validCharacters
- where
- validCharacters = filter (`elem` "<>+-.,[]") str
- parseTokens :: [Token] -> [Command]
- parseTokens [] = []
- parseTokens ((_, '<'):xs) = MoveLeft : parseTokens xs
- parseTokens ((_, '>'):xs) = MoveRight : parseTokens xs
- parseTokens ((_, '+'):xs) = Increment : parseTokens xs
- parseTokens ((_, '-'):xs) = Decrement : parseTokens xs
- parseTokens ((_, '.'):xs) = Print : parseTokens xs
- parseTokens ((_, ','):xs) = Read : parseTokens xs
- parseTokens ((n, '['):xs) = let (innerLoop, (n',_):rest) = extractInnerLoop xs
- in ((LoopStart n') : parseTokens innerLoop) ++ [LoopEnd n] ++ parseTokens rest
- extractInnerLoop :: [Token] -> ([Token], [Token])
- extractInnerLoop tokens = extractInnerLoop' 0 [] tokens
- where
- extractInnerLoop' n acc [] = error "unexpected end of input"
- extractInnerLoop' n acc ((j, x):xs)
- | x == '[' = extractInnerLoop' (n+1) (acc ++ [(j, x)]) xs
- | x == ']' && n == 0 = (acc, (j, x):xs)
- | x == ']' = extractInnerLoop' (n-1) (acc ++ [(j, x)]) xs
- | otherwise = extractInnerLoop' n (acc ++ [(j, x)]) xs
- --
- execute :: String -> IO ()
- execute str = do
- prog <- newIOTapeFromList $ parse str ++ [NOP]
- dat <- newIOTapeFromList [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
- let loop = do
- let continue = tapeMoveRight prog >> loop
- let continueAt n = tapeMoveTo prog n >> loop
- command <- tapeCurrentValue prog
- case command of
- MoveRight -> tapeMoveRight dat >> continue
- MoveLeft -> tapeMoveLeft dat >> continue
- Increment -> tapeModify dat inc >> continue
- Decrement -> tapeModify dat dec >> continue
- Print -> tapeCurrentValue dat >>= (putChar . chr) >> hFlush stdout >> continue
- Read -> getChar >>= (\v -> tapeModify dat (const $ ord $ v)) >> continue
- LoopStart n -> tapeCurrentValue dat >>= \v -> if v == 0 then continueAt n else continue
- LoopEnd n -> tapeCurrentValue dat >>= \v -> if v == 0 then continue else continueAt n
- NOP -> putStrLn "done!"
- loop