PageRenderTime 26ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/versions/bytetail/Brainfuck.hs

http://github.com/rickardlindberg/brainfuck
Haskell | 158 lines | 123 code | 32 blank | 3 comment | 1 complexity | 3a830b41cff46c0698c1ea69931c4363 MD5 | raw file
  1. module Brainfuck where
  2. import qualified Data.Char as C
  3. import qualified Data.Map as M
  4. import Text.ParserCombinators.Parsec
  5. -- Data typeclass
  6. class Data d where
  7. emptyData :: d
  8. dataGet :: d -> Int
  9. dataModifyValue :: (Int -> Int) -> d -> d
  10. dataModifyPos :: (Int -> Int) -> d -> d
  11. dataMoveRight :: Data d => d -> d
  12. dataMoveRight = dataModifyPos inc
  13. dataMoveLeft :: Data d => d -> d
  14. dataMoveLeft = dataModifyPos dec
  15. dataGetAscii :: Data d => d -> Char
  16. dataGetAscii dat = C.chr $ dataGet dat
  17. dataWriteAscii :: Data d => Char -> d -> d
  18. dataWriteAscii i = dataModifyValue (const $ C.ord i)
  19. dataIncValue :: Data d => d -> d
  20. dataIncValue = dataModifyValue inc
  21. dataDecValue :: Data d => d -> d
  22. dataDecValue = dataModifyValue dec
  23. inc :: Int -> Int
  24. inc = (+1)
  25. dec :: Int -> Int
  26. dec x = x - 1
  27. -- Different instances of the data typeclass
  28. data DataMap = DataMap
  29. { currentPos :: Int
  30. , values :: M.Map Int Int
  31. }
  32. emptyDataMap :: DataMap
  33. emptyDataMap = DataMap 0 M.empty
  34. instance Data DataMap where
  35. emptyData = emptyDataMap
  36. dataGet dat = M.findWithDefault 0 (currentPos dat) (values dat)
  37. dataModifyValue fn dat = dat { values = newValues }
  38. where
  39. oldValue = dataGet dat
  40. newValues = M.insert (currentPos dat) (fn oldValue) (values dat)
  41. dataModifyPos fn dat = dat { currentPos = fn (currentPos dat) }
  42. data CachingData = CachingData
  43. { currentValue :: Maybe Int
  44. , dataMap :: DataMap
  45. }
  46. emptyCachingDataMap :: CachingData
  47. emptyCachingDataMap = CachingData (Just 0) emptyDataMap
  48. instance Data CachingData where
  49. emptyData = emptyCachingDataMap
  50. dataGet (CachingData Nothing dataMap) = dataGet dataMap
  51. dataGet (CachingData (Just x) _) = x
  52. dataModifyValue fn (CachingData Nothing dataMap) = CachingData (Just $ fn $ dataGet dataMap) dataMap
  53. dataModifyValue fn (CachingData (Just x) dataMap) = CachingData (Just $ fn x) dataMap
  54. dataModifyPos fn (CachingData Nothing dataMap) = CachingData Nothing $ dataModifyPos fn dataMap
  55. dataModifyPos fn (CachingData (Just x) dataMap) = CachingData Nothing $ dataModifyPos fn $ dataModifyValue (const x) dataMap
  56. -- The brainfuck code
  57. data Token
  58. = TInc
  59. | TDec
  60. | TLeft
  61. | TRight
  62. | TPrint
  63. | TRead
  64. | TLoop [Token]
  65. deriving (Show, Eq)
  66. data ByteCode
  67. = BInc ByteCode
  68. | BDec ByteCode
  69. | BLeft ByteCode
  70. | BRight ByteCode
  71. | BPrint ByteCode
  72. | BRead ByteCode
  73. | BLoop ByteCode ByteCode
  74. | BEND
  75. deriving (Show, Eq)
  76. toByteCode :: [Token] -> ByteCode
  77. toByteCode tokens = toByteCode' tokens BEND
  78. where
  79. toByteCode' :: [Token] -> ByteCode -> ByteCode
  80. toByteCode' [] end = end
  81. toByteCode' (TInc :xs) end = BInc (toByteCode' xs end)
  82. toByteCode' (TDec :xs) end = BDec (toByteCode' xs end)
  83. toByteCode' (TLeft :xs) end = BLeft (toByteCode' xs end)
  84. toByteCode' (TRight :xs) end = BRight (toByteCode' xs end)
  85. toByteCode' (TPrint :xs) end = BPrint (toByteCode' xs end)
  86. toByteCode' (TRead :xs) end = BRead (toByteCode' xs end)
  87. toByteCode' (TLoop ls:xs) end = let inner = toByteCode' ls loop
  88. rest = toByteCode' xs end
  89. loop = BLoop inner rest
  90. in loop
  91. parseTokens :: String -> [Token]
  92. parseTokens input =
  93. case parse bfTokens fileName (removeComments input) of
  94. Left err -> error (show err)
  95. Right x -> x
  96. where
  97. fileName :: String
  98. fileName = ""
  99. removeComments :: String -> String
  100. removeComments = filter (`elem` "+-<>.,[]")
  101. bfTokens :: Parser [Token]
  102. bfTokens = many bfToken
  103. bfToken :: Parser Token
  104. bfToken = fmap (const TInc) (char '+')
  105. <|> fmap (const TDec) (char '-')
  106. <|> fmap (const TLeft) (char '<')
  107. <|> fmap (const TRight) (char '>')
  108. <|> fmap (const TPrint) (char '.')
  109. <|> fmap (const TRead) (char ',')
  110. <|> fmap TLoop (between (char '[') (char ']')
  111. bfTokens)
  112. compile :: String -> ByteCode
  113. compile = toByteCode . parseTokens
  114. run :: Data d => ByteCode -> d -> String -> String
  115. run BEND dat input = "done!\n"
  116. run (BInc next) dat input = run next (dataIncValue dat) input
  117. run (BDec next) dat input = run next (dataDecValue dat) input
  118. run (BLeft next) dat input = run next (dataMoveLeft dat) input
  119. run (BRight next) dat input = run next (dataMoveRight dat) input
  120. run (BPrint next) dat input = (dataGetAscii dat) : run next dat input
  121. run (BRead next) dat (i:is) = run next (dataWriteAscii i dat) is
  122. run (BLoop loop next) dat input
  123. | dataGet dat == 0 = run next dat input
  124. | otherwise = run loop dat input
  125. execute :: String -> IO ()
  126. execute program = interact (run (compile program) emptyDataMap)