/src/Language/Brainfuck.hs

https://gitlab.com/PoroCYon/robcyon-v3 · Haskell · 86 lines · 79 code · 7 blank · 0 comment · 38 complexity · e21e8e93bb559c4bd76132e705da7013 MD5 · raw file

  1. module Language.Brainfuck (
  2. evaluate
  3. , makeState
  4. , BFState(..)
  5. ) where
  6. import Data.ByteString (ByteString)
  7. import Data.List (unfoldr, foldl')
  8. import Data.Maybe
  9. import Data.Word8 (Word8)
  10. import qualified Data.ByteString as BS
  11. import qualified Data.ByteString.Char8 as C8
  12. import qualified Data.Word8 as W8
  13. data BFState = BFState { program :: ByteString,
  14. input :: ByteString,
  15. memory :: ByteString,
  16. eip :: Int ,
  17. eax :: Int ,
  18. jstack :: [Int] ,
  19. output :: String }
  20. makeState :: String -> String -> Int -> BFState
  21. makeState prg inp memSize = BFState { program = C8.pack prg,
  22. input = C8.pack inp,
  23. memory = BS.replicate memSize (0 :: Word8),
  24. eip = 0,
  25. eax = 0,
  26. jstack = [],
  27. output = "" }
  28. data FNWSState = FNWSState { stackv :: Int ,
  29. lasti :: Int }
  30. evaluate :: BFState -> String
  31. evaluate s =
  32. catMaybes $ unfoldr (\ state ->
  33. let eip' = eip state in
  34. let eax' = eax state in
  35. let prg = program state in
  36. let mem = memory state in
  37. let ns = state { eip = eip' + 1 } in
  38. if eip' >= BS.length prg then Nothing
  39. else
  40. case prg `C8.index` eip' of
  41. '+' -> Just (Nothing, ns { memory = putW8At mem (mem `BS.index` eax' + 1) eax' })
  42. '-' -> Just (Nothing, ns { memory = putW8At mem (mem `BS.index` eax' - 1) eax' })
  43. '>' -> Just (Nothing, ns { eax = eax' + 1 })
  44. '<' -> Just (Nothing, ns { eax = eax' - 1 })
  45. '.' -> Just (Just $ mem `C8.index` eax', ns)
  46. ',' ->
  47. let inp = input state in
  48. let mem' = (if BS.null inp then mem else putChAt mem (C8.head inp) eax') in
  49. let inp' = if BS.null inp then inp else BS.tail inp
  50. in Just (Nothing, ns { memory = mem', input = inp' })
  51. '[' ->
  52. if mem `BS.index` eax' == 0
  53. then case findNextWithStack prg '[' ']' (eip' + 1) of
  54. Just i -> Just (Nothing, state { eip = i + 1 })
  55. Nothing -> Nothing -- RIP
  56. else Just (Nothing, ns { jstack = eip' : jstack state })
  57. ']' ->
  58. let st = jstack state in
  59. if null st || head st == 0 || mem `BS.index` eax' == 0
  60. then Just (Nothing, ns)
  61. else Just (Nothing, state { eip = head st + 1, jstack = tail st })
  62. _ -> Just (Nothing, ns)
  63. ) s
  64. where
  65. findNextWithStack bs inc dec start =
  66. let bs' = BS.drop (start - 1) bs in
  67. let i = lasti $ foldl' (\ s (i, e) ->
  68. case e of
  69. c | c == inc -> s { stackv = stackv s + 1 }
  70. c | c == dec -> if stackv s == 0
  71. then s { stackv = -1, lasti = i }
  72. else s { stackv = stackv s - 1 }
  73. _ -> s
  74. ) (FNWSState 0 (-1)) $ (zip [ 0 .. (BS.length bs' - 1) ] $ C8.unpack bs')
  75. in if i < 0 then Nothing else Just i
  76. map2 f l = map (\ (a, b) -> f a b) l
  77. mapIndex f l = map2 f $ zip [0 .. (length l - 1) ] l
  78. putChAt bs c i = C8.pack $ mapIndex (\ i' e -> if i == i' then c else e) $ C8.unpack bs
  79. putW8At bs b i = BS.pack $ mapIndex (\ i' e -> if i == i' then b else e) $ BS.unpack bs