/Brainfuck.hs

https://github.com/RaphaelJ/Haskell · Haskell · 77 lines · 63 code · 7 blank · 7 comment · 10 complexity · 6bf924ee1893f550e8741673561d2b61 MD5 · raw file

  1. -- This is a small Brainfuck interpreter.
  2. -- It uses the State monad for parsing and execution.
  3. -- The execution isn't done inside the execution function but inside
  4. -- interact.
  5. import Data.Char
  6. import Control.Monad.State
  7. import System.Environment
  8. data Expr = ExprGT | ExprLT
  9. | ExprPlus | ExprMinus
  10. | ExprDot | ExprComma
  11. | ExprHook [Expr]
  12. deriving (Show)
  13. main = do
  14. [script] <- getArgs
  15. code <- readFile script
  16. interact (execute $ evalState parse code)
  17. parse :: State String [Expr]
  18. parse = do
  19. xs <- get
  20. if xs == []
  21. then return []
  22. else do
  23. let (x:xs') = xs
  24. put xs'
  25. case x of
  26. '[' -> do
  27. nested <- parse
  28. fmap (ExprHook nested :) parse
  29. ']' ->
  30. put xs' >> return []
  31. '>' -> fmap (ExprGT:) parse
  32. '<' -> fmap (ExprLT:) parse
  33. '+' -> fmap (ExprPlus:) parse
  34. '-' -> fmap (ExprMinus:) parse
  35. '.' -> fmap (ExprDot:) parse
  36. ',' -> fmap (ExprComma:) parse
  37. _ -> parse
  38. type Tape = ([Int], [Int])
  39. -- Return a function to use with interact
  40. execute es = evalState execute' (es, ([0,0..], []))
  41. where
  42. execute' :: State ([Expr], Tape) (String -> String)
  43. execute' = do
  44. (es, tape) <- get
  45. case es of
  46. (ExprHook inner : es') ->
  47. if (head $ fst $ tape) == 0
  48. then put (es', tape) >> execute' -- Next
  49. else put (inner, tape) >> execute' >> -- Execute nested
  50. modify (\(_, tape') -> (es, tape')) >> execute' -- Recurse with new tape
  51. (ExprGT : es') ->
  52. let (x:xs, ys) = tape in put (es', (xs, x:ys)) >> execute'
  53. (ExprLT : es') ->
  54. let (xs, y:ys) = tape in put (es', (y:xs, ys)) >> execute'
  55. (ExprPlus : es') ->
  56. let (x:xs, ys) = tape in put (es', (x+1:xs, ys)) >> execute'
  57. (ExprMinus : es') ->
  58. let (x:xs, ys) = tape in put (es', (x-1:xs, ys)) >> execute'
  59. (ExprDot : es') -> do
  60. let (x:_, _) = tape
  61. put (es', tape)
  62. str <- execute'
  63. return $ \cx -> (chr x : str cx)
  64. (ExprComma : es') ->
  65. -- Return a new function which remove a char, put it on
  66. -- the tape and execute the following expressions.
  67. let (_:xs, ys) = tape
  68. in return $ \(c:cx) ->
  69. evalState execute' (es', (ord c : xs, ys)) cx
  70. [] -> return (\_ -> "")