Advertisement
Guest User

Untitled

a guest
Mar 29th, 2018
22
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. module Lib
  2.     ( interpret
  3.     ) where
  4.  
  5. import Data.Char
  6. import Data.Maybe
  7. import System.IO
  8.  
  9. data BFInstruction = MemoryRight | MemoryLeft | Increment | Decrement | Output | Input | LoopBegin | LoopEnd deriving (Enum)
  10. data BFProgram = BFProgram [BFInstruction] BFInstruction [BFInstruction]
  11.  
  12. newtype BFMemoryCell = BFMemoryCell Int
  13. data BFMemory = BFMemory [BFMemoryCell] BFMemoryCell [BFMemoryCell]
  14.  
  15. startProgram :: [BFInstruction] -> BFProgram
  16. startProgram instructions = BFProgram [] (head instructions) (tail instructions)
  17.  
  18. advance :: BFProgram -> BFProgram
  19. advance (BFProgram past current next) = BFProgram (past ++ [current]) (head next) (tail next)
  20.  
  21. advanceBy :: Int -> BFProgram -> BFProgram
  22. advanceBy advancement = case advancement of
  23.     x | x < 0   -> decreaseBy' (-advancement)
  24.    0           -> id
  25.    x | x > 0   -> advanceBy' advancement
  26.  
  27. advanceBy' :: Int -> BFProgram -> BFProgram
  28. advanceBy' advancement (BFProgram past current next) = do
  29.     let next' = current:next
  30.    let next'' = drop advancement next'
  31.     BFProgram (past ++ take advancement next') (head next'') (tail next'')
  32.  
  33. decreaseBy' :: Int -> BFProgram -> BFProgram
  34. decreaseBy' decrement (BFProgram past current next) = do
  35.    let past' = past ++ [current]
  36.     let length' = length past' - decrement
  37.     let past'' = take length' past'
  38.     BFProgram (init past'') (last past'') (drop length' past' ++ next)
  39.  
  40. makeCell :: Int -> BFMemoryCell
  41. makeCell = BFMemoryCell . wrap
  42.  
  43. incrementCell :: BFMemoryCell -> BFMemoryCell
  44. incrementCell = makeCell . (+1) . getCell
  45.  
  46. decrementCell :: BFMemoryCell -> BFMemoryCell
  47. decrementCell = makeCell . subtract 1 . getCell
  48.  
  49. getCell :: BFMemoryCell -> Int
  50. getCell (BFMemoryCell value) = value
  51.  
  52. wrap :: Int -> Int
  53. wrap input = mod input 256
  54.  
  55. moveMemoryRight :: BFMemory -> BFMemory
  56. moveMemoryRight (BFMemory previous current []) = BFMemory (previous ++ [current]) (makeCell 0) []
  57. moveMemoryRight (BFMemory previous current next) = BFMemory (previous ++ [current]) (head next) (tail next)
  58.  
  59. moveMemoryLeft :: BFMemory -> BFMemory
  60. moveMemoryLeft (BFMemory [] current next) = BFMemory [] (makeCell 0) (current:next)
  61. moveMemoryLeft (BFMemory previous current next) = BFMemory (init previous) (last previous) (current:next)
  62.  
  63. onCurrentCell :: (BFMemoryCell -> BFMemoryCell) -> BFMemory -> BFMemory
  64. onCurrentCell func (BFMemory previous current next) = BFMemory previous (func current) next
  65.  
  66. toInstructions :: String -> [BFInstruction]
  67. toInstructions = mapMaybe toInstruction
  68.  
  69. toInstruction :: Char -> Maybe BFInstruction
  70. toInstruction instruction = case instruction of
  71.     '>' -> Just MemoryRight
  72.     '<' -> Just MemoryLeft
  73.     '+' -> Just Increment
  74.     '-' -> Just Decrement
  75.     '.' -> Just Output
  76.     ',' -> Just Input
  77.     '[' -> Just LoopBegin
  78.     ']' -> Just LoopEnd
  79.     _   -> Nothing
  80.  
  81. interpret :: String -> IO (Either String BFMemory)
  82. interpret program = step (startProgram $ toInstructions program) (BFMemory [] (makeCell 0) [])
  83.  
  84. step :: BFProgram -> BFMemory -> IO (Either String BFMemory)
  85. step (BFProgram _ _ []) memory = return . Right $ memory
  86. step program@(BFProgram previousProgram instruction nextProgram) memory@(BFMemory previousMemory currentMemory nextMemory) = case instruction of
  87.     MemoryRight -> step (advance program) (moveMemoryRight memory)
  88.     MemoryLeft  -> step (advance program) (moveMemoryLeft memory)
  89.     Increment   -> step (advance program) (onCurrentCell incrementCell memory)
  90.     Decrement   -> step (advance program) (onCurrentCell decrementCell memory)
  91.     Output      -> do
  92.         putChar . chr . getCell $ currentMemory
  93.         hFlush stdout
  94.         step (advance program) memory
  95.     Input       -> do
  96.         newCurrentChar <- getChar
  97.         let newCurrent = makeCell . ord $ newCurrentChar
  98.         step (advance program) (BFMemory previousMemory newCurrent nextMemory)
  99.     LoopBegin   -> case getCell currentMemory of
  100.         0   -> case findMatchingLoopClose nextProgram 0 0 of
  101.             Left err            -> return . Left $ err
  102.             Right advancement   -> step (advanceBy (advancement + 1) program) memory
  103.         _   -> step (advance program) memory
  104.     LoopEnd     -> case getCell currentMemory of
  105.         0   -> step (advance program) memory
  106.         _   -> case findMatchingLoopOpen previousProgram 0 0 of
  107.             Left err        -> return . Left $ err
  108.             Right decrease  -> step (advanceBy (-(decrease + 1)) program) memory
  109.  
  110. findMatchingLoopClose :: [BFInstruction] -> Int -> Int -> Either String Int
  111. findMatchingLoopClose [] _ _ = Left "findMatchingLoopClose: No matching ] found"
  112. findMatchingLoopClose (instruction:remainingProgram) index nestingCounter = case instruction of
  113.     LoopBegin   -> findMatchingLoopClose remainingProgram (index + 1) (nestingCounter + 1)
  114.     LoopEnd     -> if nestingCounter == 0
  115.         then Right index
  116.         else findMatchingLoopClose remainingProgram (index + 1) (nestingCounter - 1)
  117.     _           -> findMatchingLoopClose remainingProgram (index + 1) nestingCounter
  118.  
  119. findMatchingLoopOpen :: [BFInstruction] -> Int -> Int -> Either String Int
  120. findMatchingLoopOpen = findMatchingLoopOpen' . reverse
  121.  
  122. findMatchingLoopOpen' :: [BFInstruction] -> Int -> Int -> Either String Int
  123. findMatchingLoopOpen' [] _ _ = Left "findMatchingLoopOpen: No matching [ found"
  124. findMatchingLoopOpen' (instruction:remainingProgram) index nestingCounter = case instruction of
  125.     LoopEnd     -> findMatchingLoopOpen' remainingProgram (index + 1) (nestingCounter + 1)
  126.    LoopBegin   -> if nestingCounter == 0
  127.        then Right index
  128.        else findMatchingLoopOpen' remainingProgram (index + 1) (nestingCounter - 1)
  129.     _           -> findMatchingLoopOpen' remainingProgram (index + 1) nestingCounter
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement