-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathBrainfuck.fr
More file actions
86 lines (62 loc) · 2.46 KB
/
Brainfuck.fr
File metadata and controls
86 lines (62 loc) · 2.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package examples.Brainfuck where
import frege.Prelude hiding (uncons)
import frege.data.List(lookup)
data Tape = Tape { left :: [Int], cell :: Int, right :: [Int] }
instance Show Tape where
show (Tape ls c rs) = show [reverse ls,[c],rs]
data Op = Plus | Minus | GoLeft | GoRight | Output | Input | Loop [Op]
derive Eq Op
derive Show Op
-- the parser
removeComments :: [Char] -> [Char]
removeComments xs = filter (`elem` (unpacked "+-<>.,[]")) xs
ops = [('+', Plus),('-', Minus),('<',GoLeft),('>',GoRight),('.',Output),(',',Input)]
parseOp :: [Char] -> Maybe (Op, [Char])
parseOp ('[':cs) = case parseOps cs of
(prog, (']':cs')) -> Just (Loop prog, cs')
_ -> Nothing
parseOp (c:cs) = fmap (flip (,) cs) $ lookup c ops
parseOp [] = Nothing
parseOps :: [Char] -> ([Op],[Char])
parseOps cs = go cs [] where
go cs acc = case parseOp cs of
Nothing -> (reverse acc, cs)
Just (op, cs') -> go cs' (op:acc)
parse :: String -> [Op]
parse prog = case parseOps $ removeComments $ unpacked prog of
(ops, []) -> ops
(ops, rest) -> error $ "Parsed: " ++ show ops ++ ", Rest: " ++ packed rest
-- the interpreter
execute :: [Op] -> Tape -> IO Tape
execute prog tape = foldM exec tape prog where
exec :: Tape -> Op -> IO Tape
exec tape Plus = return $ tape.{cell <- succ}
exec tape Minus = return $ tape.{cell <- pred}
exec (Tape ls c rs) GoLeft = let (hd,tl) = uncons ls in return $ Tape tl hd (c:rs)
exec (Tape ls c rs) GoRight = let (hd,tl) = uncons rs in return $ Tape (c:ls) hd tl
exec tape Output = printAsChar tape.cell >> return tape
exec tape Input = do n <- getChar; return tape.{cell = ord n}
exec tape (again @ Loop loop)
| tape.cell == 0 = return tape
| otherwise = execute loop tape >>= flip exec again
-- helper functions
private uncons :: [Int] -> (Int,[Int])
private uncons [] = (0,[])
private uncons (x:xs) = (x,xs)
private printAsChar :: Int -> IO ()
private printAsChar i = print $ packed [Char.from i]
-- execution environment
run :: String -> IO Tape
run prog = execute (parse prog) (Tape [] 0 [])
main _ = do
tape <- run helloWorld
println ""
println tape
-- example programs
helloWorld =
">+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++" ++
"[<++++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------." ++
"[-]>++++++++[<++++>-]<+.[-]++++++++++."
nineToZero =
"++++++++++++++++++++++++++++++++[>+>+<<-]" ++
">>+++++++++++++++++++++++++<<++++++++++[>>.-<.<-]"