-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.hs
134 lines (107 loc) · 3.87 KB
/
main.hs
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import Data.Maybe ( fromJust )
import GHC.Base ( Alternative(..), ord )
import Data.Char
import System.Environment ( getArgs )
import Control.Applicative ( Alternative(..) )
-- Data Structure
data Memory = Memory [Int] [Int]
deriving (Show)
data Brainfuck = MoveLeft -- <
| MoveRight -- >
| Increment -- +
| Decrement -- -
| Print -- .
| Input -- ,
| Loop [Brainfuck] -- Commands inside a loop
deriving (Show, Eq)
-- Parser
newtype Parser a = Parser { runParser :: String -> Maybe (a, String) }
instance Functor Parser where
fmap f (Parser p) = Parser $ \input -> do
(x, input') <- p input
Just (f x, input')
instance Applicative Parser where
pure x = Parser $ \input -> Just (x, input)
(Parser p1) <*> (Parser p2) = Parser $ \input -> do
(f, input') <- p1 input
(a, input'') <- p2 input'
Just (f a, input'')
instance Alternative Parser where
empty = Parser $ const Nothing
(Parser p1) <|> (Parser p2) =
Parser $ \input -> p1 input <|> p2 input
charP :: Char -> Parser Char
charP x = Parser go where
go (y:ys)
| x == y = Just (x, ys)
| otherwise = Nothing
go [] = Nothing
spanP :: (Char -> Bool) -> Parser String
spanP f =
Parser $ \input ->
let (token, rest) = span f input
in Just (token, rest)
ignore :: Parser [Char]
ignore = spanP isSpace
bfLeft :: Parser Brainfuck
bfLeft = MoveLeft <$ charP '<'
bfRight :: Parser Brainfuck
bfRight = MoveRight <$ charP '>'
bfIncrement :: Parser Brainfuck
bfIncrement = Increment <$ charP '+'
bfDecrement :: Parser Brainfuck
bfDecrement = Decrement <$ charP '-'
bfLoop :: Parser Brainfuck
bfLoop = Loop <$> (charP '[' *> instructions <* charP ']')
where instructions = some bfCommand
bfPrint :: Parser Brainfuck
bfPrint = Print <$ charP '.'
bfInput :: Parser Brainfuck
bfInput = Input <$ charP ','
bfCommand :: Parser Brainfuck
bfCommand = ignore *> (bfPrint <|> bfInput <|> bfLoop <|> bfDecrement
<|> bfIncrement <|> bfLeft <|> bfRight) <* ignore
bfParser :: Parser [Brainfuck]
bfParser = ignore *> many bfCommand <* ignore
parseFile :: FilePath -> Parser a -> IO (Maybe a)
parseFile filename parser = do
input <- readFile filename
return $ fst <$> runParser parser input
emptyMemory :: Memory
emptyMemory = Memory [] []
moveRight :: Memory -> Memory
moveRight (Memory ls (r:rs)) = Memory (r:ls) rs
moveRight (Memory ls []) = Memory (0:ls) []
moveLeft :: Memory -> Memory
moveLeft (Memory (l:ls) rs) = Memory ls (l:rs)
moveLeft (Memory [] rs) = Memory [] (0:rs)
modifyMemory :: (Int -> Int) -> Memory -> Memory
modifyMemory f (Memory left (x:rs)) = Memory left (f x:rs)
modifyMemory f (Memory left []) = Memory left [f 0]
readCell :: Memory -> Int
readCell (Memory _ (x:rs)) = x
readCell (Memory _ []) = 0
run :: [Brainfuck] -> IO Memory
run = go emptyMemory
where go memory [] = return memory
go memory (x:xs) = case x of
MoveRight -> go (moveRight memory) xs
MoveLeft -> go (moveLeft memory) xs
Increment -> go (modifyMemory (+1) memory) xs
Decrement -> go (modifyMemory (subtract 1) memory) xs
Input -> do
c <- getChar
go (modifyMemory (const $ ord c) memory) xs
Print -> do
putChar (chr (readCell memory))
go memory xs
Loop cmds -> if readCell memory /= 0
then do
memory' <- go memory cmds
go memory' (x:xs)
else go memory xs
main :: IO Memory
main = do
filepath <- getArgs
cmds <- readFile $ head filepath
run $ fromJust $ fst <$> runParser bfParser cmds