git.iko.soy/production-haskell
on master help sign in
.
commitc46af96f2cc264dbc1c25a83cd8fe9770b618b2e authoriko <mail@iko.soy> dateWed 15 Jan 2025, 09:33 (2 years ago) parent184c660
14 files changed+618−15
seminars/2/package.yaml+1 seminars/2/src/Task.hs+47−13 seminars/2/task.cabal+2−1 seminars/2/test/Tests.hs+9−1 seminars/3/package.yaml+15 seminars/3/server.cabal+28 seminars/3/src/Main.hs+138 seminars/3/src/Task.hs+367 seminars/3/stack.yaml+9 seminars/3/state/BFState/checkpoints-0000000000.log seminars/3/state/BFState/checkpoints.version+1 seminars/3/state/BFState/events-0000000000.log seminars/3/state/BFState/events-0000000001.log seminars/3/state/BFState/events.version+1
seminars/2/package.yaml+1
@@ -18,3 +18,4 @@ tests:
1818 - tasty
1919 - task
2020 - tasty-hunit
21+ - tasty-quickcheck
seminars/2/src/Task.hs+47−13
@@ -5,6 +5,8 @@
55module Task
66 ( calculate,
77 Expr(..),
8+ parseFull,
9+ runParser,
810 )
911where
1012
@@ -67,6 +69,11 @@ anyToken = Parser $ \s -> case s of
6769 "" -> Left "Empty String"
6870 (hd: tl) -> Right (hd, tl)
6971
72+peekToken :: Parser Char
73+peekToken = Parser $ \s -> case s of
74+ "" -> Left "Empty String"
75+ (hd: tl) -> Right (hd, hd : tl)
76+
7077eos :: Parser ()
7178eos = Parser $ \s -> case s of
7279 "" -> Right ((), "")
@@ -119,33 +126,58 @@ number = do
119126 Just res -> return res
120127 Nothing -> fail $ "unexpected invalid number: '" ++ chars ++ "'"
121128
129+anyNumber :: Parser Int
130+anyNumber = do
131+ nextChar <- peekToken
132+ case nextChar of
133+ '-' -> do
134+ anyToken
135+ number_abs <- number
136+ pure $ negate number_abs
137+ _ -> number
138+
122139data Expr
123140 = Num Int
124141 | AddExpr Expr Expr
125142 | MulExpr Expr Expr
126143 | SubExpr Expr Expr
127144 | DivExpr Expr Expr
128− deriving Show
145+ deriving (Eq)
146+
147+instance Show Expr where
148+ show arg = case arg of
149+ (Num i) -> show i
150+ (AddExpr e1 e2) -> template "+" e1 e2
151+ (MulExpr e1 e2) -> template "*" e1 e2
152+ (SubExpr e1 e2) -> template "-" e1 e2
153+ (DivExpr e1 e2) -> template "/" e1 e2
154+ where template op e1 e2 = "(" <> show e1 <> " " <> op <> " " <> show e2 <> ")"
129155
130156half :: Gen a -> Gen a
131157half = scale (`div` 2)
132158
133−instance Arbitrary Expr where
134− arbitrary = do
135− size <- getSize
136− case size of
137− 0 -> Num <$> arbitrary
138− otherwise ->
139− oneof [
140− Num <$> arbitrary,
141− AddExpgr <$> half arbitrary <*> half arbitrary,
159+genPrimitive :: Gen Expr
160+genPrimitive = Num <$> scale ((* 5) . (+ 1)) arbitrary
161+
162+genRecursive :: Gen Expr
163+genRecursive = oneof [
164+ AddExpr <$> half arbitrary <*> half arbitrary,
142165 SubExpr <$> half arbitrary <*> half arbitrary,
143166 MulExpr <$> half arbitrary <*> half arbitrary,
144167 DivExpr <$> half arbitrary <*> half arbitrary
145− ]
168+ ]
169+
170+recursiveProb :: Int -> (Int, Int)
171+recursiveProb sz = (maximum [1, sz `div` 5], sz)
172+
173+instance Arbitrary Expr where
174+ arbitrary = do
175+ sz <- getSize
176+ let (wPrim, wRec) = recursiveProb sz
177+ frequency [(wPrim, genPrimitive), (wRec, genRecursive)]
146178
147179parse :: Parser Expr
148−parse = Num <$> number <|> (do
180+parse = Num <$> anyNumber <|> (do
149181 token '('
150182 tokensWhile isSpace
151183 expr1 <- parse
@@ -181,9 +213,11 @@ evaluate (DivExpr left right) = div <$> (evaluate left) <*> (evaluate right)
181213runParser :: Parser x -> String -> Maybe x
182214runParser (Parser parser_fun) context =
183215 case parser_fun context of
184− Left msg -> trace msg Nothing
216+ Left msg -> Nothing
185217 Right (x, _) -> Just x
186218
219+
220+
187221calculate :: String -> Maybe Int
188222calculate s = do
189223 expression <- runParser parseFull s
seminars/2/task.cabal+2−1
@@ -4,7 +4,7 @@ cabal-version: 2.2
44--
55-- see: https://github.com/sol/hpack
66--
7−-- hash: c2b1bcd2599c61a3fda41614499046ca5654131a2e5ac9976e0c07ec2b8887d8
7+-- hash: 570d0dca563c281c3678c542d3e97ca36237c1a0d42875afdb6b78c85c35b732
88
99name: task
1010version: 0.1.0.0
@@ -43,4 +43,5 @@ test-suite task-tests
4343 , task
4444 , tasty
4545 , tasty-hunit
46+ , tasty-quickcheck
4647 default-language: Haskell2010
seminars/2/test/Tests.hs+9−1
@@ -3,9 +3,12 @@ module Tests
33 )
44where
55
6−import Task
6+import Task
77import Test.Tasty
88import Test.Tasty.HUnit
9+import Test.Tasty.QuickCheck
10+
11+
912
1013tests :: TestTree
1114tests = testGroup "Tests"
@@ -24,4 +27,9 @@ tests = testGroup "Tests"
2427 calculate "(((1 + 2) *3) +( if ( 1 = 2 ) then (2 + 4) else 2 + 1)" @=? Nothing
2528 calculate "(((1 + 2) *3) +( f ( 1 = 2 ) then (2 + 4) else 2 + 1) )" @=? Nothing
2629 calculate "(((1 + 2) *3) +( f ( 1 = 2 ) then (2 + 4) + 1) )" @=? Nothing
30+ , testProperty "parse & convert"
31+ (\expr -> let expr_str = show expr
32+ expr_from_str = runParser parseFull expr_str in
33+ Just expr == expr_from_str
34+ )
2735 ]
seminars/3/package.yamlnew file+15
@@ -0,0 +1,15 @@
1+name: server
2+version: 0.1.0.0
3+license: Unlicense
4+
5+dependencies:
6+ - base
7+ - scotty
8+ - text
9+ - mtl
10+ - acid-state
11+ - safecopy
12+
13+executable:
14+ source-dirs: src
15+ main: Main.hs
seminars/3/server.cabalnew file+28
@@ -0,0 +1,28 @@
1+cabal-version: 2.2
2+
3+-- This file has been generated from package.yaml by hpack version 0.36.0.
4+--
5+-- see: https://github.com/sol/hpack
6+
7+name: server
8+version: 0.1.0.0
9+license: Unlicense
10+build-type: Simple
11+
12+executable server
13+ main-is: Main.hs
14+ other-modules:
15+ Task
16+ Paths_server
17+ autogen-modules:
18+ Paths_server
19+ hs-source-dirs:
20+ src
21+ build-depends:
22+ acid-state
23+ , base
24+ , mtl
25+ , safecopy
26+ , scotty
27+ , text
28+ default-language: Haskell2010
seminars/3/src/Main.hsnew file+138
@@ -0,0 +1,138 @@
1+{-# LANGUAGE ScopedTypeVariables #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE TypeFamilies #-}
4+{-# LANGUAGE TemplateHaskell #-}
5+
6+module Main (main) where
7+import Data.Text.Lazy
8+import Data.Maybe
9+import Web.Scotty
10+import Task (Tape(..), shiftTapeR, shiftTapeL, emptyTape)
11+import qualified Control.Monad.State.Class as ST
12+import Control.Monad.Reader.Class
13+
14+import Data.Acid
15+import Data.SafeCopy
16+import Data.Typeable
17+import Control.Monad.IO.Class
18+
19+
20+-- TASK:
21+-- 1 endpoint per BF command
22+-- read tape
23+-- read ouput
24+-- clear state
25+
26+
27+
28+data BFState
29+ = BFState
30+ { -- | The data tape.
31+ bfDataTape :: Tape Int,
32+ -- | The output stream of the BF interpreter.
33+ bfOutput :: [Int]
34+ }
35+ deriving (Eq, Show)
36+
37+data BFCommand
38+ = -- | The `>` command.
39+ ShiftRight
40+ | -- | The `<` command.
41+ ShiftLeft
42+ | -- | The `+` command.
43+ Increment
44+ | -- | The `-` command.
45+ Decrement
46+ | -- | The `,` command.
47+ ReadInput Int
48+ | -- | The `.` command.
49+ WriteOutput
50+ deriving (Eq, Show)
51+
52+executeCommandOnTape :: BFState -> (Tape Int -> Maybe (Tape Int)) -> BFState
53+executeCommandOnTape state f =
54+ let tape = bfDataTape state
55+ new_tape = fromJust (f tape)
56+ in state {bfDataTape = new_tape}
57+
58+executeCommandAddToCell :: Int -> BFState -> BFState
59+executeCommandAddToCell addValue state =
60+ let tape = bfDataTape state
61+ old_value = tapeValue tape
62+ new_tape = tape {tapeValue = old_value + addValue} in
63+ state {bfDataTape = new_tape}
64+
65+executeCommand :: BFCommand -> BFState -> BFState
66+executeCommand ShiftLeft state =
67+ executeCommandOnTape state shiftTapeL
68+executeCommand ShiftRight state =
69+ executeCommandOnTape state shiftTapeR
70+
71+executeCommand Increment state = executeCommandAddToCell 1 state
72+executeCommand Decrement state = executeCommandAddToCell (-1) state
73+
74+executeCommand (ReadInput new_value) state =
75+ let tape = bfDataTape state
76+ new_tape = tape {tapeValue = new_value} in
77+ state {bfDataTape = new_tape}
78+
79+executeCommand WriteOutput state =
80+ let tape = bfDataTape state
81+ old_output = bfOutput state
82+ value = tapeValue tape
83+ in state {bfOutput = value : old_output}
84+
85+-------------- acid state boilerplateexecuteCommand
86+
87+$(deriveSafeCopy 0 'base ''Tape)
88+$(deriveSafeCopy 0 'base ''BFState)
89+$(deriveSafeCopy 0 'base ''BFCommand)
90+
91+------------- add transactions
92+updateStateByExecutingCommand :: BFCommand -> Update BFState ()
93+updateStateByExecutingCommand command = do
94+ currentState <- ST.get
95+ -- let newState =
96+ ST.put $ executeCommand command currentState
97+
98+resetState :: Update BFState ()
99+resetState = do
100+ ST.put BFState {
101+ bfDataTape = emptyTape,
102+ bfOutput = []
103+ }
104+
105+queryTape :: Query BFState (Tape Int)
106+queryTape = bfDataTape <$> ask
107+
108+queryOutput :: Query BFState [Int]
109+queryOutput = bfOutput <$> ask
110+
111+--queryState :: Query HelloWorldState String
112+--queryState = do HelloWorldState string <- ask
113+-- return string
114+
115+$(makeAcidic ''BFState ['updateStateByExecutingCommand, 'resetState, 'queryTape, 'queryOutput])
116+
117+
118+------------------------------
119+
120+main :: IO ()
121+main = do
122+ acidstate <- openLocalState (BFState {bfDataTape = emptyTape, bfOutput = []})
123+ scotty 3000 $ do
124+ -- /add/8/12 -> 20
125+ get "/tape" $ do
126+ tape <- liftIO $ query acidstate QueryTape
127+ text $ pack . show $ tape
128+ post "/increment" $ do
129+ liftIO $ update acidstate (UpdateStateByExecutingCommand Increment)
130+ get "/add/:first/:second" $ do
131+ first <- captureParam "first"
132+ second <- captureParam "second"
133+ let result:: Integer = first + second
134+ text $ pack . show $ result
135+ get "/:word" $ do
136+ beam <- captureParam "word"
137+ text beam
138+
seminars/3/src/Task.hsnew file+367
@@ -0,0 +1,367 @@
1+module Task
2+ ( Tape (..),
3+ touchTape,
4+ initializeTape,
5+ emptyTape,
6+ shiftTapeL,
7+ shiftTapeR,
8+ ErrorState (..),
9+ executeErrorState,
10+ runErrorState,
11+ get,
12+ put,
13+ throwError,
14+ modify,
15+ BFState (..),
16+ BFError (..),
17+ BFMonad,
18+ readInput,
19+ writeOutput,
20+ shiftDataR,
21+ shiftDataL,
22+ readData,
23+ writeData,
24+ BFCommand (..),
25+ executeCommand,
26+ evaluateProgram,
27+ executeProgram,
28+ )
29+where
30+
31+import Data.Char
32+import Data.List
33+import Control.Monad.Except
34+import Control.Monad.State
35+
36+-- ____ _____ _ _ _
37+-- | __ )| ___| (_)_ __ | |_ ___ _ __ _ __ _ __ ___| |_ ___ _ __
38+-- | _ \| |_ | | '_ \| __/ _ \ '__| '_ \| '__/ _ \ __/ _ \ '__|
39+-- | |_) | _| | | | | | || __/ | | |_) | | | __/ || __/ |
40+-- |____/|_| |_|_| |_|\__\___|_| | .__/|_| \___|\__\___|_|
41+-- |_|
42+
43+-- In this section you will implements an interpreter for the BF language.
44+-- If you are not familiar with it, please familiarize yourself:
45+-- https://en.wikipedia.org/wiki/Brainfuck
46+
47+-- _____
48+-- |_ _|_ _ _ __ ___
49+-- | |/ _` | '_ \/ -_)
50+-- |_|\__,_| .__/\___|
51+-- |_|
52+
53+-- | Represents a possibly infinite tape with a tape head pointing to one of the
54+-- tape value.
55+--
56+-- So, this object:
57+--
58+-- Tape
59+-- { leftTape :: [2, 6, ...],
60+-- tapeValue :: 1,
61+-- rightTape :: [5, 8, ...]
62+-- }
63+--
64+-- Would represent this tape:
65+--
66+-- ◀──┬─────┬─────┬─────┬─────┬─────┬──▶
67+-- ... │ 6 │ 2 │ 1 │ 5 │ 8 │ ...
68+-- ◀──┴─────┴─────┴─────┴─────┴─────┴──▶
69+-- ▲
70+-- │
71+data Tape a
72+ = Tape
73+ { leftTape :: [a],
74+ tapeValue :: a,
75+ rightTape :: [a]
76+ }
77+ deriving (Eq, Ord)
78+
79+-- We need a special instance to handle infinite tapes. The derived
80+-- implementation would go into an infinite loop.
81+instance Show a => Show (Tape a) where
82+ show (Tape l c right) =
83+ "["
84+ <> tapeify (reverse (take 5 l))
85+ <> " { "
86+ <> show c
87+ <> " } "
88+ <> tapeify (take 5 right)
89+ <> "]"
90+ where
91+ tapeify = mconcat . intersperse " " . fmap show
92+
93+-- | Creates a tape from a list.
94+--
95+-- Note that the tape will be empty to the left.
96+--
97+-- >>> initializeTape [1, 5, 8, ...]
98+--
99+-- ┌─────┬─────┬─────┬──▶
100+-- │ 1 │ 5 │ 8 │ ...
101+-- └─────┴─────┴─────┴──▶
102+-- ▲
103+-- │
104+initializeTape :: [a] -> Maybe (Tape a)
105+initializeTape [] = Nothing
106+initializeTape (a : aa) = Just (Tape [] a aa)
107+
108+-- | This is represents the operation of moving the tape pointer to the left.
109+shiftTapeL :: Tape a -> Maybe (Tape a)
110+shiftTapeL (Tape (l : ll) c rr) = Just (Tape ll l (c : rr))
111+shiftTapeL _ = Nothing
112+
113+-- | This is represents the operation of moving the tape pointer to the right.
114+shiftTapeR :: Tape a -> Maybe (Tape a)
115+shiftTapeR (Tape ll c (r : rr)) = Just (Tape (c : ll) r rr)
116+shiftTapeR _ = Nothing
117+
118+-- | This operation allows you to modify the cell at the tape pointer.
119+touchTape :: (a -> a) -> Tape a -> Tape a
120+touchTape f (Tape l c r) = Tape l (f c) r
121+
122+-- ___ _ _ _
123+-- / __| |_ __ _| |_ ___ _ __ ___ _ _ __ _ __| |
124+-- \__ \ _/ _` | _/ -_) | ' \/ _ \ ' \/ _` / _` |
125+-- |___/\__\__,_|\__\___| |_|_|_\___/_||_\__,_\__,_|
126+
127+-- In this section we will implement the state monad with some basic operations
128+-- it supports.
129+
130+-- | Since the standard `State` monad doesn't have error handling capabilities.
131+-- we will implement a version of `State`, which can also handle errors.
132+--
133+-- `e` is the type of the possible error
134+-- `s` is the type of the state that is carried through the monad
135+-- `a` is the value that is returned.
136+-- data ErrorState e s a
137+-- = ErrorState
138+-- {runErrorState :: s -> Either e (a, s)}
139+
140+type ErrorState e s a = ExceptT e (State s) a
141+
142+-- | Executes the state monad and returns the result without returning the
143+-- final state.
144+executeErrorState :: s -> ErrorState e s a -> Either e a
145+executeErrorState s = flip evalState s . runExceptT
146+
147+-- runErrorState :: s -> ErrorState e s a -> Either e s
148+runErrorState s = flip runState s . runExceptT
149+
150+-- ___ ___ _
151+-- | _ ) __| _ __ ___ _ _ __ _ __| |
152+-- | _ \ _| | ' \/ _ \ ' \/ _` / _` |
153+-- |___/_| |_|_|_\___/_||_\__,_\__,_|
154+
155+-- In this section we will implement the specific state we will be using and
156+-- some operations that work within it.
157+
158+-- | This represents the state of our BF interpreter.
159+data BFState
160+ = BFState
161+ { -- | The data tape.
162+ bfDataTape :: Tape Int,
163+ -- | The input stream of the BF interpreter.
164+ bfInput :: [Int],
165+ -- | The output stream of the BF interpreter.
166+ bfOutput :: [Int]
167+ }
168+ deriving (Eq, Show)
169+
170+-- | This represents the errors that can occur while executing a BF program.
171+data BFError
172+ = -- | Tried reading input, but the input stream was empty.
173+ NotEnoughInput
174+ | -- | Tried going past the end of the data tape (if it was finite).
175+ DataTapeExhausted
176+ deriving (Show, Eq)
177+
178+-- | The monad in which we will interpreting the BF commands.
179+type BFMonad a = ErrorState BFError BFState a
180+
181+-- | This operation should consume one element from the input stream
182+-- and return it.
183+--
184+-- If there is not enough input, it should throw 'NotEnoughInput' error.
185+readInput :: BFMonad Int
186+readInput = do
187+ bfst <- get
188+ case bfInput bfst of
189+ [] -> throwError NotEnoughInput
190+ (x:xs)-> do
191+ put bfst { bfInput = xs }
192+ return x
193+
194+-- | This operation should write one element to the output stream.
195+-- This should just prepend the character to the start of the string using the
196+-- `:` (cons) operator.
197+--
198+-- This will make writing O(1).
199+writeOutput :: Int -> BFMonad ()
200+writeOutput value = do
201+ bfst <- get
202+ let old_output = bfOutput bfst
203+ put bfst {bfOutput = value : old_output }
204+ return ()
205+
206+-- | This operation shifts the data pointer to the right.
207+--
208+-- NOTE: if the tape has ended, you should throw the 'DataTapeExhausted' error.
209+shiftDataR :: BFMonad ()
210+shiftDataR = shiftData shiftTapeR
211+
212+
213+shiftData :: (Tape Int -> Maybe (Tape Int)) -> BFMonad ()
214+shiftData f = do
215+ bfst <- get
216+ case f $ bfDataTape bfst of
217+ Nothing -> throwError DataTapeExhausted
218+ Just x -> put bfst {bfDataTape = x }
219+
220+
221+-- | This operation shifts the data pointer to the left.
222+--
223+-- NOTE: if the tape has ended, you should throw the 'DataTapeExhausted' error.
224+shiftDataL :: BFMonad ()
225+shiftDataL = shiftData shiftTapeL
226+
227+-- | This operation reads the element at the data pointer.
228+readData :: BFMonad Int
229+readData = do
230+ bfst <- get
231+ return (tapeValue $ bfDataTape bfst)
232+
233+-- | This operation writes the element to the current data pointer.
234+writeData :: Int -> BFMonad ()
235+writeData val = do
236+ bfst <- get
237+ put bfst {bfDataTape = touchTape (const val) $ bfDataTape bfst }
238+-- _____ _ _ _ _
239+-- |_ _| |_ ___ (_)_ _| |_ ___ _ _ _ __ _ _ ___| |_ ___ _ _
240+-- | | | ' \/ -_) | | ' \ _/ -_) '_| '_ \ '_/ -_) _/ -_) '_|
241+-- |_| |_||_\___| |_|_||_\__\___|_| | .__/_| \___|\__\___|_|
242+-- |_|
243+
244+-- | The parsed commands from the BF language.
245+--
246+-- Please have a look at this commands table:
247+-- https://en.wikipedia.org/wiki/Brainfuck#Commands
248+data BFCommand
249+ = -- | 'Loop x' is equivalent to `[x]` in BF. It is essentially a while loop.
250+ Loop [BFCommand]
251+ | -- | The `>` command.
252+ ShiftRight
253+ | -- | The `<` command.
254+ ShiftLeft
255+ | -- | The `+` command.
256+ Increment
257+ | -- | The `-` command.
258+ Decrement
259+ | -- | The `,` command.
260+ ReadInput
261+ | -- | The `.` command.
262+ WriteOutput
263+ deriving (Eq, Show)
264+
265+type BFProgram = [BFCommand]
266+
267+prependMaybe :: Maybe a -> [a] -> [a]
268+prependMaybe Nothing aa = aa
269+prependMaybe (Just x) xx = x : xx
270+
271+-- | This is just a helper function.
272+parseProgram' :: String -> (BFProgram, String)
273+parseProgram' "" = ([], "")
274+parseProgram' ('[' : rest) = (Loop innerProgram : outerProgram, s')
275+ where
276+ (innerProgram, s) = parseProgram' rest
277+ (outerProgram, s') = parseProgram' s
278+parseProgram' (']' : rest) = ([], rest)
279+parseProgram' (x : rest) = (command `prependMaybe` restProgram, s)
280+ where
281+ (restProgram, s) = parseProgram' rest
282+ command = case x of
283+ '>' -> pure ShiftRight
284+ '<' -> pure ShiftLeft
285+ '+' -> pure Increment
286+ '-' -> pure Decrement
287+ '.' -> pure WriteOutput
288+ ',' -> pure ReadInput
289+ _ -> Nothing
290+
291+-- | This function parses the given string and returns a sequence of BF
292+-- commands.
293+parseProgram :: String -> BFProgram
294+parseProgram = fst . parseProgram'
295+
296+-- | Executes one BF command.
297+--
298+-- NOTE: You will probably need to call 'evaluateProgram' somewhere in
299+-- this function.
300+executeCommand :: BFCommand -> BFMonad ()
301+executeCommand ShiftLeft = shiftDataL
302+executeCommand ShiftRight = shiftDataR
303+executeCommand Increment = do
304+ cur <- readData
305+ writeData $ cur + 1
306+executeCommand Decrement = do
307+ cur <- readData
308+ writeData $ cur - 1
309+executeCommand ReadInput = readInput >>= writeData
310+executeCommand WriteOutput = readData >>= writeOutput
311+executeCommand (Loop commands) = do
312+ cur <- readData
313+ if cur == 0 then
314+ pure ()
315+ else do
316+ evaluateProgram commands
317+ executeCommand (Loop commands)
318+
319+-- | This function should evaluate the whole program.
320+evaluateProgram :: BFProgram -> BFMonad ()
321+evaluateProgram commands = sequence_ $ executeCommand <$> commands
322+
323+-- | This constant just contains an infinite empty tape. You can use this as
324+-- the initial data tape.
325+--
326+-- NOTE: It is infinite.
327+emptyTape :: Tape Int
328+emptyTape = Tape (repeat 0) 0 (repeat 0)
329+
330+-- | In this function you should bring everything together and execute the given
331+-- list of commands.
332+--
333+-- The input stream is the second argument to the function.
334+--
335+-- The returned string should contain the output stream that the evaluation
336+-- produces.
337+--
338+-- NOTE: since the output stream of the program is written backwards, you
339+-- will need to reverse the output stream. (You can use the 'reverse' function.)
340+--
341+-- You will need to construct the initial state for the monad, evaluate the
342+-- program from the initial state, and convert the resulting value to the
343+-- appropriate type.
344+executeProgram ::
345+ -- | The text of the BF program.
346+ String ->
347+ -- | The input to pass into the program.
348+ String ->
349+ Maybe String
350+executeProgram pr_str input = let program = parseProgram pr_str
351+ mon = evaluateProgram program
352+ init_bfstate = BFState {bfDataTape=emptyTape, bfInput = ord <$> input, bfOutput = []}
353+ output_or_err = executeErrorState init_bfstate (mon *> get)
354+ in case output_or_err of
355+ Left _ -> Nothing
356+ Right output -> Just $ reverse $ chr <$> (bfOutput output)
357+
358+
359+-- data BFState
360+-- = BFState
361+-- { -- | The data tape.
362+-- bfDataTape :: Tape Int,
363+-- -- | The input stream of the BF interpreter.
364+-- bfInput :: [Int],
365+-- -- | The output stream of the BF interpreter.
366+-- bfOutput :: [Int]
367+-- }
seminars/3/stack.yamlnew file+9
@@ -0,0 +1,9 @@
1+resolver: lts-22.6
2+packages:
3+- .
4+
5+nix:
6+ enable: true
7+ packages:
8+ - libffi
9+ - zlib
seminars/3/state/BFState/checkpoints-0000000000.lognew file

No changes to the content.

seminars/3/state/BFState/checkpoints.versionnew file+1
@@ -0,0 +1 @@
1+0.16.1.3
seminars/3/state/BFState/events-0000000000.lognew file

Binary file not shown.

seminars/3/state/BFState/events-0000000001.lognew file

Binary file not shown.

seminars/3/state/BFState/events.versionnew file+1
@@ -0,0 +1 @@
1+0.16.1.3