packages feed

brainfuck-monad 0.2.0 → 0.4.0

raw patch · 2 files changed

+188/−56 lines, 2 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Control.Monad.BrainFuck: sum :: BrainFuck ()
+ Control.Monad.BrainFuck: alloc' :: (DataPointer -> BrainFuck a) -> BrainFuck a
+ Control.Monad.BrainFuck: copy :: DataPointer -> DataPointer -> BrainFuck ()
+ Control.Monad.BrainFuck: debug :: BrainFuck ()
+ Control.Monad.BrainFuck: if0 :: DataPointer -> (BrainFuck (), BrainFuck ()) -> BrainFuck ()
+ Control.Monad.BrainFuck: sumNext :: BrainFuck ()
+ Control.Monad.BrainFuck: when0 :: DataPointer -> BrainFuck () -> BrainFuck ()
+ Control.Monad.BrainFuck: withAddr :: Integer -> BrainFuck a -> BrainFuck a
+ Control.Monad.BrainFuck: withNot :: DataPointer -> BrainFuck () -> BrainFuck ()
- Control.Monad.BrainFuck: add :: Int -> BrainFuck ()
+ Control.Monad.BrainFuck: add :: Word8 -> BrainFuck ()
- Control.Monad.BrainFuck: loopFrom :: Int -> (DataPointer -> BrainFuck ()) -> BrainFuck ()
+ Control.Monad.BrainFuck: loopFrom :: Word8 -> (DataPointer -> BrainFuck ()) -> BrainFuck ()
- Control.Monad.BrainFuck: mult :: Int -> BrainFuck ()
+ Control.Monad.BrainFuck: mult :: Word8 -> BrainFuck ()
- Control.Monad.BrainFuck: set :: Int -> BrainFuck ()
+ Control.Monad.BrainFuck: set :: Word8 -> BrainFuck ()
- Control.Monad.BrainFuck: sub :: Int -> BrainFuck ()
+ Control.Monad.BrainFuck: sub :: Word8 -> BrainFuck ()
- Control.Monad.BrainFuck: unless0 :: BrainFuck () -> BrainFuck ()
+ Control.Monad.BrainFuck: unless0 :: DataPointer -> BrainFuck () -> BrainFuck ()

Files

Control/Monad/BrainFuck.hs view
@@ -2,6 +2,7 @@  import qualified Control.Monad as M import qualified Data.Char as Char+import Data.Word (Word8)  newtype BrainFuck a = BrainFuck (DataPointer -> ([Char], DataPointer, a)) @@ -40,6 +41,9 @@ -- | if byte at data pointer is nonzero, jump to optoce after matching open close = opcode ']' +-- | ignored if brainfuck machine does not support debugging+debug = opcode '#'+ -- | Adds an arbitrary character to the program. -- Should not be used directly. opcode :: Char -> BrainFuck ()@@ -53,12 +57,19 @@  -- | The loop is only run if the data pointer doesn't point to 0. ----- On entry, the loop body is run, and then it loops, until the data--- pointer points to 0.+-- On entry, the loop body is run, and then it loops, until the address+-- the data pointer originally pointed at has a value of 0.+--+-- Any change that the loop body makes to the address of the data pointer+-- is reset each time through the loop (and at the end). This is necessary+-- to keep the BrainFuck monad's DataPointer consistent no matter what+-- happens when running the loop. loopUnless0 :: BrainFuck a -> BrainFuck a loopUnless0 a = do+	start <- addr 	open 	r <- a+	setAddr start 	close 	return r @@ -76,17 +87,27 @@ 			then next >> setAddr n 			else return () +-- | Runs an action with the data pointer temporarily set to an address,+-- then restores the data pointer.+withAddr :: Integer -> BrainFuck a -> BrainFuck a+withAddr n a = do+	old <- addr+	setAddr n+	r <- a+	setAddr old+	return r+ -- | Run an action multiple times. multi :: BrainFuck () -> Int -> BrainFuck () multi c n = do 	_ <- sequence (replicate n c) 	return () -add, sub :: Int -> BrainFuck ()--- adds an Int to the byte at the data pointer-add = multi incr--- subtracts an Int from the byte at the data pointer-sub = multi decr+add, sub :: Word8 -> BrainFuck ()+-- adds an byte to the byte at the data pointer (wraps on overflow)+add = multi incr . fromIntegral+-- subtracts an byte from the byte at the data pointer (wraps on underflow)+sub = multi decr . fromIntegral  -- | Zeros the current data cell. zero :: BrainFuck ()@@ -94,10 +115,10 @@  -- | Changes the current data cell to contain a specific value. -- (It can start at any value).-set :: Int -> BrainFuck ()+set :: Word8 -> BrainFuck () set n = do 	zero-	add n	+	add n  -- | For higher-level programming in brainfuck, it's useful to have a way -- to run a function, while allocating a memory cell, which is initialized@@ -118,45 +139,98 @@ 	prev 	return r +alloc' :: (DataPointer -> BrainFuck a) -> BrainFuck a+alloc' a = alloc $ addr >>= a+ -- | Allocates a new memory cell, populates it with a Char, and runs -- the action. withChar :: Char.Char -> BrainFuck a -> BrainFuck a withChar c a = alloc $ do-	set (Char.ord c)+	set $ fromIntegral $ Char.ord c 	a  -- | Allocates a cell and uses it as the loop counter, starting from  -- the provided value. The action will continue running in a loop until -- it decrements the counter to 0.-loopFrom :: Int -> (DataPointer -> BrainFuck ()) -> BrainFuck ()-loopFrom n a = alloc $ do-	i <- addr-	add n-	loopUnless0 $ do-		a i-		setAddr i+loopFrom :: Word8 -> (DataPointer -> BrainFuck ()) -> BrainFuck ()+loopFrom n a =+	alloc' $ \i -> do+		add n+		loopUnless0 $+			a i --- | Runs an action in an infinite loop. The action should avoid--- touching the current memory cell.+-- | Runs an action in an infinite loop. The action can modify its+-- current memory cell, or allocate and use new ones, and will not+-- exit the loop. forever :: BrainFuck a -> BrainFuck ()-forever a = loopFrom 1 $ \_ -> do-	_ <- a-	return ()+forever a = loopFrom 1 $ \i ->+	alloc' $ \scratch -> do+		a+		return () +-- | Copies the value of a cell.+copy :: DataPointer -> DataPointer -> BrainFuck ()+copy src dest = do+	withAddr dest zero+	alloc' $ \tmp -> do+		-- copy src to new and tmp, but this clobbers src+		setAddr src+		loopUnless0 $ do+			decr+			setAddr dest+			incr+			setAddr tmp+			incr+		-- put src back how it was+		setAddr tmp+		loopUnless0 $ do+			decr+			setAddr src+			incr++-- | Runs the action with a new cell that is logically NOT+-- the value of the passed pointer.+withNot :: DataPointer -> BrainFuck () -> BrainFuck ()+withNot p a = alloc' $ \tmp1 -> do+	copy p tmp1+	alloc' $ \tmp2 -> do+		incr+		setAddr tmp1+		loopUnless0 $ do+			zero+			setAddr tmp2+			decr+		setAddr tmp2+		a+ -- | Runs the action unless the data pointer points to 0.-unless0 :: BrainFuck () -> BrainFuck ()-unless0 a = do+unless0 :: DataPointer -> BrainFuck () -> BrainFuck ()+unless0 p a = do 	start <- addr-	loopUnless0 $ do-		a-		next-		zero-	prev+	alloc' $ \tmp -> do+		copy p tmp+		loopUnless0 $ do+			setAddr start+			a+			setAddr tmp+			zero -- don't loop +-- | Runs the action when the data pointer points to 0.+when0 :: DataPointer -> BrainFuck () -> BrainFuck ()+when0 p a = withNot p $+	addr >>= flip unless0 a++-- | Monadic if; the first action is run if the data pointer points to 0,+-- else the second action is run.+if0 :: DataPointer -> (BrainFuck (),  BrainFuck ()) -> BrainFuck ()+if0 p (y, n) = do+	when0 p y+	unless0 p n+ -- | Adds the current and next data cells. The next cell is zeroed -- and the sum is left in the current cell.-sum :: BrainFuck ()-sum = do+sumNext :: BrainFuck ()+sumNext = do 	next 	loopUnless0 $ do 		prev@@ -167,28 +241,24 @@  -- | Multiplies the current data cell by some value. Uses and zeros some -- of the following cells.-mult :: Int -> BrainFuck ()+mult :: Word8 -> BrainFuck () mult y = do 	x <- addr-	alloc $ do-		c1 <- addr-+	alloc' $ \c1 -> do 		-- Copy x to c1, and zero x. 		setAddr x 		loopUnless0 $ do+			decr 			setAddr c1 			incr-			setAddr x-			decr 	 		setAddr c1 		loopUnless0 $ do+			decr 			loopFrom y $ \c2 -> do 				decr 				setAddr x 				incr-			setAddr c1-			decr  -- | Displays a string. Tries to generate a fairly small brainfuck program, -- using a few encoding tricks. The current cell is modified, and not@@ -210,33 +280,55 @@ 		| otherwise = do 			if abs delta < c 				then multi (if delta > 0 then incr else decr) (abs delta)-				else set c+				else set (fromIntegral c) 			output 			go False cs c 	  where 		delta = c - n --- | Prints out the alphabet, repeatedly.+-- | Prints out the alphabet, repeatedly, with some whitespace fun+-- to make it more interesting. demo :: String-demo = brainfuck $ forever $ do-	alloc $ do-		c <- addr-		set start+demo = brainfuck $ alloc' $ \tilt -> do+	settilt+	alloc' $ \tc -> do+		decr -- set to 255+		alloc' $ \wc -> do+			copy tilt wc+			forever $ alloc' $ \c -> do+				set (fromIntegral start)+				loopFrom (fromIntegral numchars) $ \i -> do+					decr 		-		loopFrom numchars $ \i -> do-			decr-			setAddr c-			output-			incr-		withChar ' ' output+					withAddr wc decr+					when0 wc $ do+						loopFrom 3 $ \_ -> do+							decr+							withChar ' ' output+						copy tilt wc+						withAddr tc decr+						when0 tc $ do+							withAddr tc decr+							withAddr tilt decr+							when0 tilt $ do+								withAddr tilt+									settilt+				+					setAddr c+					output+					incr   where 	start = Char.ord 'a' 	end = Char.ord 'z' 	numchars = end - start + 1 +	settilt = do+		set 4+		mult 5+ -- | Copy input to output. cat :: String-cat = brainfuck $ forever $ input >> output+cat = brainfuck $ forever $ alloc $ input >> output  -- | Simple hello world. helloworld :: String@@ -248,9 +340,36 @@ helloworld' :: String helloworld' = optimize $ brainfuck $ display "hello, world!" +{-+euler1 :: BrainFuck+euler1 = alloc' $ \result -> alloc' $ \scratch -> do+	let accum i = do+		setAddr scratch+		dup i+		setAddr result+		sumNext+		+	let checkmod i n = alloc $ do+		dup i+		mod n+		when0 $+			accum i++	loopFrom 1000 $ \i -> do+		decr+		alloc $ do+			checkmod i 5+			print0	+			unless0 $+				checkmod 1 3++	setAddr result+	decimalOutput+-}+ -- | Simple optimiser for brainfuck code. optimize :: String -> String-optimize s = go s [] s+optimize s = trimend $ go s [] s   where 	go orig r [] = 		let new = reverse r in@@ -262,6 +381,19 @@ 	go orig r ('+':'-':l) = go orig r l -- +- is a noop 	go orig r ('-':'+':l) = go orig r l -- -+ is a noop 	go orig r ('[':']':l) = go orig r l -- [] is a noop-	go orig [] ('[':'-':']':l) = go orig [] l -- [-] at start is noop-	go orig [] ('>':'[':'-':']':l) = go orig [] l -- >[-] at start is noop+	-- [-] before , is a noop, because the read overwrites the value+	go orig r ('[':'-':']':',':l) = go orig r (',':l)+	-- so is + or - before ,+	go orig r ('+':',':l) = go orig r (',':l)+	go orig r ('-':',':l) = go orig r (',':l)+	-- [-][-] is the same as [-]+	go orig r ('[':'-':']':'[':'-':']':l) = go orig r ('[':'-':']':l)+	-- [-] at start is noop, because memory starts empty+	go orig [] ('[':'-':']':l) = go orig [] l+	-- >[-] at start is generally a noop (for programs using alloc)+	go orig [] ('>':'[':'-':']':l) = go orig [] l 	go orig r (c:l) = go orig (c:r) l++	-- Any sequence of > and < at the end of a program is a noop.+	trimend = reverse . dropWhile (`elem` "><") . reverse+
brainfuck-monad.cabal view
@@ -1,5 +1,5 @@ Name: brainfuck-monad-Version: 0.2.0+Version: 0.4.0 Cabal-Version: >= 1.8 License: BSD3 Maintainer: Joey Hess <id@joeyh.name>