PageRenderTime 1447ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/Brainfuck.hs

http://github.com/niklasb/haskell-brainfuck
Haskell | 71 lines | 55 code | 12 blank | 4 comment | 1 complexity | 3b67a9685c7039adfd2add9d22de2e0e MD5 | raw file
  1. import Control.Monad.State
  2. import Data.Char
  3. import Data.Maybe
  4. import Data.Word
  5. import Text.ParserCombinators.Parsec ( Parser, parse, many, oneOf
  6. , noneOf, between, char, (<|>))
  7. -- zipper
  8. data ListZipper a = ListZipper { getLeft :: [a]
  9. , getValue :: a
  10. , getRight :: [a]
  11. } deriving Show
  12. modifyValue :: (a -> a) -> ListZipper a -> ListZipper a
  13. modifyValue f (ListZipper ls x rs) = ListZipper ls (f x) rs
  14. forward :: ListZipper a -> ListZipper a
  15. forward (ListZipper ls x (r:rs)) = ListZipper (x:ls) r rs
  16. backward :: ListZipper a -> ListZipper a
  17. backward (ListZipper (l:ls) x rs) = ListZipper ls l (x:rs)
  18. -- brainfuck
  19. data BFIns = Next | Prev | Inc | Dec | Read | Write | Loop [BFIns]
  20. deriving Show
  21. type BFCell = Word8
  22. type BFState = ListZipper BFCell
  23. type Brainfuck = StateT BFState IO
  24. emptyState :: BFState
  25. emptyState = ListZipper zeroes 0 zeroes
  26. where zeroes = repeat 0
  27. eval :: BFIns -> Brainfuck ()
  28. eval Next = modify forward
  29. eval Prev = modify backward
  30. eval Inc = modify $ modifyValue (+1)
  31. eval Dec = modify . modifyValue $ subtract 1
  32. eval Write = gets getValue >>= liftIO . putStr . return . chr . fromEnum
  33. eval Read = liftIO getChar >>= modify . modifyValue . const . toEnum . ord
  34. eval loop@(Loop inside) = gets getValue >>= executeLoop
  35. where executeLoop val | val == 0 = return ()
  36. | otherwise = mapM_ eval $ inside ++ [loop]
  37. runBF :: [BFIns] -> IO BFState
  38. runBF = flip execStateT emptyState . mapM_ eval
  39. -- parser
  40. comment :: Parser (Maybe BFIns)
  41. comment = noneOf "]" >> return Nothing
  42. simpleIns :: Parser (Maybe BFIns)
  43. simpleIns = oneOf "<>+-.," >>= \ins -> return . Just $ case ins of
  44. '<' -> Next
  45. '>' -> Prev
  46. '+' -> Inc
  47. '-' -> Dec
  48. '.' -> Write
  49. ',' -> Read
  50. loop :: Parser (Maybe BFIns)
  51. loop = between (char '[') (char ']') (program >>= return . Just . Loop)
  52. program :: Parser [BFIns]
  53. program = liftM catMaybes $ many $ simpleIns <|> loop <|> comment
  54. -- utility
  55. parseAndRunBF :: String -> IO ()
  56. parseAndRunBF str = do let parseRes = parse program "bf" str
  57. case parseRes of
  58. Left err -> putStrLn ("Syntax error: " ++ show err)
  59. Right ins -> runBF ins >> putStrLn ""