-
Notifications
You must be signed in to change notification settings - Fork 258
/
Copy pathbf-marray.hs
151 lines (135 loc) · 4.39 KB
/
bf-marray.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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Main where
import qualified Data.Array.Base as ArrayBase
import qualified Data.Array.IO as IOUArray
import qualified Data.Array.MArray as MArray
import qualified Data.ByteString.Char8 as C
import Control.Exception
import Control.Monad
import Data.Bits
import Data.Char
import Data.Maybe
import Network.Socket
import Network.Socket.ByteString
import System.Environment
import System.Exit
import System.IO (hFlush, stdout)
import System.Posix (getProcessID)
import Text.RawString.QQ
data Op = Inc !Int | Move !Int | Print | Loop ![Op] deriving Show
data Tape = Tape { tapeData :: IOUArray.IOUArray Int Int
, tapePos :: !Int
}
data Printer = Printer { sum1 :: Int
, sum2 :: Int
, quiet :: Bool
}
write :: Printer -> Int -> IO Printer
write p n = if quiet p
then do let s1 = mod (sum1 p + n) 255
let s2 = mod (s1 + sum2 p) 255
return Printer {
sum1=s1,
sum2=s2,
quiet=True
}
else do
putStr [chr n]
hFlush stdout
return p
getChecksum :: Printer -> Int
getChecksum p = (sum2 p `shiftL` 8) .|. sum1 p
current :: Tape -> IO Int
current tape = ArrayBase.unsafeRead (tapeData tape) (tapePos tape)
inc :: Int -> Tape -> IO ()
inc delta tape = do
prev <- current tape
ArrayBase.unsafeWrite (tapeData tape) (tapePos tape) (prev + delta)
move :: Int -> Tape -> IO Tape
move m tape = do
len <- ArrayBase.getNumElements curData
newData <- if newPos < len
then return curData
else do
el <- MArray.getElems curData
MArray.newListArray (0, newPos)
(el ++ replicate (newPos - len + 1) 0)
return $ Tape newData newPos
where
curData = tapeData tape
newPos = tapePos tape + m
parse :: ([Char], [Op]) -> ([Char], [Op])
parse ([], acc) = ([], reverse acc)
parse (c:cs, acc) =
case c of
'+' -> parse (cs, Inc 1:acc)
'-' -> parse (cs, Inc (-1):acc)
'>' -> parse (cs, Move 1:acc)
'<' -> parse (cs, Move (-1):acc)
'.' -> parse (cs, Print:acc)
'[' -> parse (newCs, Loop loop:acc)
where (newCs, loop) = parse (cs, [])
']' -> (cs, reverse acc)
_ -> parse (cs, acc)
run :: [Op] -> Tape -> Printer -> IO (Tape, Printer)
run [] tape p = return (tape, p)
run (op:ops) tape p = do
case op of
Inc d -> do
inc d tape
run ops tape p
Move m -> do
newTape <- move m tape
run ops newTape p
Print -> do
x <- current tape
newP <- write p x
run ops tape newP
Loop loop -> do
x <- current tape
if x == 0
then run ops tape p
else do
(newTape, newP) <- run loop tape p
run (op:ops) newTape newP
notify :: String -> IO ()
notify msg = withSocketsDo $ do
addr:_ <- getAddrInfo (Just defaultHints) (Just "localhost") (Just "9001")
catch (_notify addr) (\(_ :: IOException) -> return ())
where
writeMsg s = sendAll s $ C.pack msg
_notify addr = bracket (openSocket addr) close $ \sock -> do
connect sock $ addrAddress addr
writeMsg sock
verify :: IO ()
verify = do
let source = [r|++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>\
---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.|]
let (_, ops) = parse (source, [])
empty <- MArray.newListArray (0, 0) [0]
(_, pLeft) <- run ops
(Tape empty 0)
Printer {sum1=0, sum2=0, quiet=True}
let left = getChecksum pLeft
pRight <- foldM (\p c -> write p $ ord c)
(Printer {sum1=0, sum2=0, quiet=True})
"Hello World!\n"
let right = getChecksum pRight
when (left /= right)
$ die $ show left ++ " != " ++ show right
main :: IO ()
main = do
verify
[filename] <- getArgs
source <- readFile filename
quiet_env <- lookupEnv "QUIET"
let p = Printer {sum1=0, sum2=0, quiet=isJust quiet_env}
pid <- getProcessID
notify $ "Haskell (MArray)\t" ++ show pid
let (_, ops) = parse (source, [])
empty <- MArray.newListArray (0, 0) [0]
(_, newP) <- run ops (Tape empty 0) p
notify "stop"
when (quiet newP)
$ do putStrLn $ "Output checksum: " ++ show (getChecksum newP)