feat[compressed]: ability to uncompress

This commit is contained in:
vegowotenks 2025-06-23 14:00:21 +02:00
parent f05da30c8a
commit 40ce94ab7b
3 changed files with 41 additions and 2 deletions

View file

@ -1,13 +1,19 @@
{-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE ImportQualifiedPost #-}
module Language.Brainfuck.Instruction.Compressed ( CompressedInstruction(..), compress ) where {-# LANGUAGE InstanceSigs #-}
module Language.Brainfuck.Instruction.Compressed ( CompressedInstruction(..), compress, uncompress ) where
import Data.Bifunctor ( Bifunctor(first) ) import Data.Bifunctor ( Bifunctor(first) )
import Data.Vector ( Vector ) import Data.Vector ( Vector )
import Data.Word ( Word8 ) import Data.Word ( Word8 )
import Numeric.Natural ( Natural ) import Numeric.Natural ( Natural )
import Test.QuickCheck.Gen (Gen)
import Test.QuickCheck.Arbitrary (Arbitrary(arbitrary))
import Test.QuickCheck.Instances.Natural ()
import Data.Vector qualified as Vector import Data.Vector qualified as Vector
import Test.QuickCheck.Gen qualified as Gen
import Language.Brainfuck.Instruction ( Instruction ) import Language.Brainfuck.Instruction ( Instruction )
import Language.Brainfuck.Instruction qualified as Instruction import Language.Brainfuck.Instruction qualified as Instruction
@ -21,6 +27,20 @@ data CompressedInstruction
| Loop (Vector CompressedInstruction) | Loop (Vector CompressedInstruction)
deriving (Show, Eq) deriving (Show, Eq)
instance Arbitrary CompressedInstruction where
arbitrary :: Gen CompressedInstruction
arbitrary = Gen.oneof
[ Add <$> arbitrary
, Subtract <$> arbitrary
, MoveRight <$> arbitrary
, MoveLeft <$> arbitrary
, pure ReadByte
, pure PutByte
, Loop <$> resize (`div` 8) arbitrary
]
where
resize f x = Gen.sized $ \ s -> Gen.resize (f s) x
compress :: Vector Instruction -> Vector CompressedInstruction compress :: Vector Instruction -> Vector CompressedInstruction
compress instructions = Vector.fromList (go instructions) compress instructions = Vector.fromList (go instructions)
where where
@ -51,3 +71,18 @@ compress instructions = Vector.fromList (go instructions)
-- >>> compress . Vector.fromList $ replicate 300 Instruction.MoveRight -- >>> compress . Vector.fromList $ replicate 300 Instruction.MoveRight
-- [MoveRight 300] -- [MoveRight 300]
uncompress :: Vector CompressedInstruction -> Vector Instruction
uncompress = Vector.concatMap uncompressSingle
where
uncompressSingle instruction = let
repeated x times = Vector.replicate (fromIntegral times) x
in case instruction of
Add times -> repeated Instruction.Increment times
Subtract times -> repeated Instruction.Decrement times
MoveRight times -> repeated Instruction.MoveRight times
MoveLeft times -> repeated Instruction.MoveLeft times
PutByte -> Vector.singleton Instruction.PutByte
ReadByte -> Vector.singleton Instruction.ReadByte
Loop body -> Vector.singleton $ Instruction.Loop (uncompress body)

View file

@ -96,4 +96,3 @@ translationSize = Vector.sum . Vector.map instructionTranslationSize
-- --
-- >>> parse [CompressedInstruction.Add 5, CompressedInstruction.Loop [CompressedInstruction.Loop [CompressedInstruction.ReadByte], CompressedInstruction.PutByte]] -- >>> parse [CompressedInstruction.Add 5, CompressedInstruction.Loop [CompressedInstruction.Loop [CompressedInstruction.ReadByte], CompressedInstruction.PutByte]]
-- [Modify Add 5,Jump 4,Jump 1,Interact Read,WhenNonZero (Jump (-2)),Interact Write,WhenNonZero (Jump (-5))] -- [Modify Add 5,Jump 4,Jump 1,Interact Read,WhenNonZero (Jump (-2)),Interact Write,WhenNonZero (Jump (-5))]

View file

@ -11,6 +11,7 @@ import qualified Data.Either as Either
import qualified Language.Brainfuck as Brainfuck import qualified Language.Brainfuck as Brainfuck
import qualified Arbitrary.ValidBrainfuckText as ValidBrainfuckText import qualified Arbitrary.ValidBrainfuckText as ValidBrainfuckText
import qualified Arbitrary.InvalidBrainfuckText as InValidBrainfuckText import qualified Arbitrary.InvalidBrainfuckText as InValidBrainfuckText
import qualified Language.Brainfuck.Instruction.Compressed as CompressedInstruction
-- | ValidBrainfuckText will produce a text with comments, it must be parsed correctly -- | ValidBrainfuckText will produce a text with comments, it must be parsed correctly
prop_acceptValidTexts :: ValidBrainfuckText.ValidBrainfuckText -> Bool prop_acceptValidTexts :: ValidBrainfuckText.ValidBrainfuckText -> Bool
@ -24,6 +25,10 @@ prop_rejectInvalidTexts = Either.isLeft . Brainfuck.parse . InValidBrainfuckText
prop_renderParseInverse :: Vector Instruction -> Bool prop_renderParseInverse :: Vector Instruction -> Bool
prop_renderParseInverse source = Right source == (Brainfuck.parse . Brainfuck.render $ source) prop_renderParseInverse source = Right source == (Brainfuck.parse . Brainfuck.render $ source)
-- | Compressing and Uncompressing should be inverse
prop_CompressUncompressInverse :: Vector Instruction -> Bool
prop_CompressUncompressInverse source = source == CompressedInstruction.uncompress . CompressedInstruction.compress
return [] -- template haskell guard return [] -- template haskell guard
runQuickCheck :: IO Bool runQuickCheck :: IO Bool
runQuickCheck = $quickCheckAll runQuickCheck = $quickCheckAll