Initial commit.
This commit is contained in:
commit
ea936cba8e
33
examples/helloworld.bf
Normal file
33
examples/helloworld.bf
Normal file
@ -0,0 +1,33 @@
|
||||
+++++ +++ Set Cell #0 to 8
|
||||
[
|
||||
>++++ Add 4 to Cell #1; this will always set Cell #1 to 4
|
||||
[ as the cell will be cleared by the loop
|
||||
>++ Add 4*2 to Cell #2
|
||||
>+++ Add 4*3 to Cell #3
|
||||
>+++ Add 4*3 to Cell #4
|
||||
>+ Add 4 to Cell #5
|
||||
<<<<- Decrement the loop counter in Cell #1
|
||||
] Loop till Cell #1 is zero
|
||||
>+ Add 1 to Cell #2
|
||||
>+ Add 1 to Cell #3
|
||||
>- Subtract 1 from Cell #4
|
||||
>>+ Add 1 to Cell #6
|
||||
[<] Move back to the first zero cell you find; this will
|
||||
be Cell #1 which was cleared by the previous loop
|
||||
<- Decrement the loop Counter in Cell #0
|
||||
] Loop till Cell #0 is zero
|
||||
|
||||
The result of this is:
|
||||
Cell No : 0 1 2 3 4 5 6
|
||||
Contents: 0 0 72 104 88 32 8
|
||||
Pointer : ^
|
||||
|
||||
>>. Cell #2 has value 72 which is 'H'
|
||||
>---. Subtract 3 from Cell #3 to get 101 which is 'e'
|
||||
+++++ ++..+++. Likewise for 'llo' from Cell #3
|
||||
>>. Cell #5 is 32 for the space
|
||||
<-. Subtract 1 from Cell #4 for 87 to give a 'W'
|
||||
<. Cell #3 was set to 'o' from the end of 'Hello'
|
||||
+++.----- -.----- ---. Cell #3 for 'rl' and 'd'
|
||||
>>+. Add 1 to Cell #5 gives us an exclamation point
|
||||
>++. And finally a newline from Cell #6
|
||||
179
src/brainfuck.hs
Normal file
179
src/brainfuck.hs
Normal file
@ -0,0 +1,179 @@
|
||||
-- Branfuck interpreter in Haskell: beause why not
|
||||
-- author: ianonavy
|
||||
-- Based on https://github.com/quchen/articles/blob/master/write_yourself_a_brainfuck.md
|
||||
|
||||
import Data.Maybe
|
||||
import Data.List
|
||||
import Data.Char (chr, ord)
|
||||
import System.IO (hFlush, stdout)
|
||||
import System.Environment
|
||||
|
||||
data BrainfuckCommand = GoRight -- >
|
||||
| GoLeft -- <
|
||||
| Increment -- +
|
||||
| Decrement -- -
|
||||
| Print -- .
|
||||
| Read -- ,
|
||||
| LoopL -- [
|
||||
| LoopR -- ]
|
||||
| Comment Char -- anything else
|
||||
|
||||
data BFSource = BFSource [BrainfuckCommand]
|
||||
|
||||
instance Show BFSource where
|
||||
show (BFSource bfs) = map bfToChar bfs where
|
||||
bfToChar GoRight = '>'
|
||||
bfToChar GoLeft = '<'
|
||||
bfToChar Increment = '+'
|
||||
bfToChar Decrement = '-'
|
||||
bfToChar Print = '.'
|
||||
bfToChar Read = ','
|
||||
bfToChar LoopL = '['
|
||||
bfToChar LoopR = ']'
|
||||
bfToChar (Comment c) = c
|
||||
|
||||
data Tape a = Tape [a] -- Left of the pivot element
|
||||
a -- Pivot element
|
||||
[a] -- Right of the pivot element
|
||||
|
||||
instance Functor Tape where
|
||||
fmap f (Tape l p r) = Tape (fmap f l) (f p) (fmap f r)
|
||||
|
||||
emptyTape :: Tape Int
|
||||
emptyTape = Tape zeros 0 zeros
|
||||
where zeros = repeat 0
|
||||
|
||||
moveRight :: Tape a -> Tape a
|
||||
moveRight (Tape ls p (r:rs)) = Tape (p:ls) r rs
|
||||
|
||||
moveLeft :: Tape a -> Tape a
|
||||
moveLeft (Tape (l:ls) p rs) = Tape ls l (p:rs)
|
||||
|
||||
accParens :: (Num a) => a -> BrainfuckCommand -> a
|
||||
accParens acc LoopL = acc + 1
|
||||
accParens acc LoopR = acc - 1
|
||||
accParens acc _ = acc
|
||||
|
||||
hasMissingOpen :: BFSource -> Bool
|
||||
hasMissingOpen bfs = not . null . filter (<0) $ traceParens bfs
|
||||
|
||||
hasMissingClosed :: BFSource -> Bool
|
||||
hasMissingClosed bfs = last (traceParens bfs) /= 0
|
||||
|
||||
traceParens :: BFSource -> [Int]
|
||||
traceParens (BFSource bfs) = scanl accParens 0 bfs
|
||||
|
||||
getMissingOpenError :: BFSource -> String
|
||||
getMissingOpenError (BFSource bfs) =
|
||||
"Syntax Error:\n- Close parens without open paren: character " ++ index where
|
||||
index = (show . fromJust . findIndex (<0) $ scanl accParens 0 bfs)
|
||||
|
||||
getMissingClosedError :: BFSource -> String
|
||||
getMissingClosedError (BFSource bfs) = "Syntax Error:\n- Missing closed paren."
|
||||
|
||||
checkSyntax :: BFSource -> Either String BFSource
|
||||
checkSyntax bfs
|
||||
| hasMissingOpen bfs = Left (getMissingOpenError bfs)
|
||||
| hasMissingClosed bfs = Left (getMissingClosedError bfs)
|
||||
| otherwise = Right bfs
|
||||
|
||||
parseBrainfuck :: String -> Either String BFSource
|
||||
parseBrainfuck = checkSyntax . BFSource . mapMaybe charToBF
|
||||
where charToBF '>' = Just GoRight
|
||||
charToBF '<' = Just GoLeft
|
||||
charToBF '+' = Just Increment
|
||||
charToBF '-' = Just Decrement
|
||||
charToBF '.' = Just Print
|
||||
charToBF ',' = Just Read
|
||||
charToBF '[' = Just LoopL
|
||||
charToBF ']' = Just LoopR
|
||||
charToBF c = Just (Comment c)
|
||||
|
||||
printBrainfuck :: Either String BFSource -> IO ()
|
||||
printBrainfuck (Left bfs) = putStrLn bfs
|
||||
printBrainfuck (Right bfs) = putStrLn . show $ bfs
|
||||
|
||||
runBrainfuck :: Either String BFSource -> IO ()
|
||||
runBrainfuck (Left bfs) = putStrLn bfs -- errors
|
||||
runBrainfuck (Right (BFSource bfs)) = run emptyTape . bfSource2Tape $ bfs
|
||||
where bfSource2Tape (b:bs) = Tape [] b bs
|
||||
|
||||
run :: Tape Int -- Data tape
|
||||
-> Tape BrainfuckCommand -- Instruction tape
|
||||
-> IO ()
|
||||
run dataTape source@(Tape _ GoRight _) =
|
||||
advance (moveRight dataTape) source
|
||||
|
||||
run dataTape source@(Tape _ GoLeft _) =
|
||||
advance (moveLeft dataTape) source
|
||||
|
||||
run (Tape l p r) source@(Tape _ Increment _) =
|
||||
advance (Tape l (p+1) r) source
|
||||
|
||||
run (Tape l p r) source@(Tape _ Decrement _) =
|
||||
advance (Tape l (p-1) r) source
|
||||
|
||||
run dataTape@(Tape _ p _) source@(Tape _ Print _) = do
|
||||
putChar (chr p)
|
||||
hFlush stdout
|
||||
advance dataTape source
|
||||
|
||||
run dataTape@(Tape l _ r) source@(Tape _ Read _) = do
|
||||
p <- getChar
|
||||
advance (Tape l (ord p) r) source
|
||||
|
||||
run dataTape@(Tape _ p _) source@(Tape _ LoopL _)
|
||||
-- If the pivot is zero, jump to the
|
||||
-- corresponding LoopR instruction
|
||||
| p == 0 = seekLoopR 0 dataTape source
|
||||
-- Otherwise just ignore the `[` and continue
|
||||
| otherwise = advance dataTape source
|
||||
|
||||
run dataTape@(Tape _ p _) source@(Tape _ LoopR _)
|
||||
| p /= 0 = seekLoopL 0 dataTape source
|
||||
| otherwise = advance dataTape source
|
||||
|
||||
run dataTape source@(Tape _ (Comment _) _) = advance dataTape source
|
||||
|
||||
-- Move the instruction pointer left until a "[" is found.
|
||||
-- The first parameter ("b" for balance) retains the current
|
||||
-- bracket balance to find the matching partner. When b is 1,
|
||||
-- then the found LoopR would reduce the counter to zero,
|
||||
-- hence we break even and the search is successful.
|
||||
seekLoopR :: Int -- Parenthesis balance
|
||||
-> Tape Int -- Data tape
|
||||
-> Tape BrainfuckCommand -- Instruction tape
|
||||
-> IO ()
|
||||
seekLoopR 1 dataTape source@(Tape _ LoopR _) = advance dataTape source
|
||||
seekLoopR b dataTape source@(Tape _ LoopR _) =
|
||||
seekLoopR (b-1) dataTape (moveRight source)
|
||||
seekLoopR b dataTape source@(Tape _ LoopL _) =
|
||||
seekLoopR (b+1) dataTape (moveRight source)
|
||||
seekLoopR b dataTape source =
|
||||
seekLoopR b dataTape (moveRight source)
|
||||
|
||||
seekLoopL :: Int -- Parenthesis balance
|
||||
-> Tape Int -- Data tape
|
||||
-> Tape BrainfuckCommand -- Instruction tape
|
||||
-> IO ()
|
||||
seekLoopL 1 dataTape source@(Tape _ LoopL _) = advance dataTape source
|
||||
seekLoopL b dataTape source@(Tape _ LoopL _) =
|
||||
seekLoopL (b-1) dataTape (moveLeft source)
|
||||
seekLoopL b dataTape source@(Tape _ LoopR _) =
|
||||
seekLoopL (b+1) dataTape (moveLeft source)
|
||||
seekLoopL b dataTape source =
|
||||
seekLoopL b dataTape (moveLeft source)
|
||||
|
||||
advance :: Tape Int -- Data tape
|
||||
-> Tape BrainfuckCommand -- Instruction tape
|
||||
-> IO ()
|
||||
advance dataTape (Tape _ _ []) = return ()
|
||||
advance dataTape source = run dataTape (moveRight source)
|
||||
|
||||
runFile :: String -> IO ()
|
||||
runFile filename = readFile filename >>= runBrainfuck . parseBrainfuck
|
||||
|
||||
main = do
|
||||
args <- getArgs
|
||||
mapM runFile args
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user