Skip to content

Instantly share code, notes, and snippets.

@d3dave
Last active August 29, 2015 14:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save d3dave/0d2b59e1fa0260a5f346 to your computer and use it in GitHub Desktop.
Save d3dave/0d2b59e1fa0260a5f346 to your computer and use it in GitHub Desktop.
Brainfuck Interpreter
module Brainfuck where
import Memory
import Control.Monad
import Control.Applicative
import Data.Char
import Data.Functor
type Program = String
type Input = String
type State a = (Memory a, Input)
createState i = (newMem, i)
cmdMap = [ ('+', memInc)
, ('-', memDec)
, ('>', memNext)
, ('<', memPrev)
]
matchBracket :: String -> (String, String)
matchBracket "" = error "Unmatched brackets"
matchBracket (']':cs) = ("", cs)
matchBracket ('[':cs) = let (start , end ) = matchBracket cs
(start', end') = matchBracket end
in ("[" ++ start ++ "]" ++ start', end')
matchBracket (c:cs) = let (start, end) = matchBracket cs
in (c:start, end)
-- | Interprets the Brainfuck source code from the first argument, while
-- supplying it with input from the second. May fail on insufficient input.
interpret :: Program -> Input -> Maybe String
interpret p i = fst . interpret' p $ createState i
where
interpret' :: Program -> State Int -> (Maybe String, State Int)
interpret' [] s = (Just "", s)
interpret' (']': _) s = error "Unmatched brackets" --(Nothing, s)
interpret' (',':cs) s@(_, "") = (Nothing, s)
interpret' ('.':cs) s@(m, i) = (r', s')
where f = chr . memOut
(r, s') = interpret' cs s
r' = ((f m):) <$> r
interpret' (',':cs) s@(m, (ih:it)) = interpret' cs (f ih m, it)
where f = memIn . ord
interpret' ('[':cs) s = while s
where (start, end) = matchBracket cs
while s@(m, i)
| memOut m == 0 = interpret' end s
| otherwise = ((++) <$> r <*> r', s'')
where (r, s') = interpret' start s
(r', s'') = while s'
interpret' ( c :cs) s@(m, i)
| Just f <- lookup c cmdMap = interpret' cs (f m, i)
| otherwise = interpret' cs s
module Memory where
type Memory a = ([a], a, [a])
type Modifier a = Memory a -> Memory a
newMem :: Memory Int
newMem = (repeat 0, 0, repeat 0)
memModifyC :: (a -> a) -> Modifier a
memModifyC f (l, c, r) = (l, f c, r)
memInc :: Enum a => Modifier a
memInc = memModifyC succ
memDec :: Enum a => Modifier a
memDec = memModifyC pred
memNext :: Enum a => Modifier a
memNext (l, c, n:r) = (c:l, n, r)
memPrev :: Enum a => Modifier a
memPrev (p:l, c, r) = (l, p, c:r)
memIn :: a -> Modifier a
memIn = memModifyC . const
memOut :: Memory a -> a
memOut (_, c, _) = c
@muesli4
Copy link

muesli4 commented Mar 13, 2015

Why not use streams for your memory (initialized with repeat 0 in both directions)? That would remove initialisation and provides only one pattern to match, which is much more efficient.

You could also participate in http://www.codewars.com/kata/my-smallest-code-interpreter-aka-brainf-star-star-k.

@d3dave
Copy link
Author

d3dave commented Mar 13, 2015

@muesli4 I hadn't thought of that! I'm new to "thinking lazily" :). Thanks for the Kata link!

@danbst
Copy link

danbst commented Mar 14, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment