hbf-0.1.0.0: tests/Helper.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
-- needed for smallcheck
module Helper where
import Control.Monad.Trans.State (runStateT)
import Data.Coerce (coerce)
import Data.Int (Int8)
import Data.Semigroup ((<>))
import Data.Text.Lazy (Text, pack)
import qualified Data.Text.Lazy.IO as TIO
import qualified Data.Vector.Unboxed as Vector
import Hedgehog (Gen)
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import Test.SmallCheck.Series
import HBF.Compiler (CompilerOptions (..),
defaultCompilerOptions,
inMemoryCompile)
import HBF.Eval (MachineType, eval)
import HBF.Parser
import HBF.Types
basicOpCharGen :: Gen Char
basicOpCharGen = Gen.element bfSimpleTokens
commentCodeGen :: Gen Char
commentCodeGen = Gen.filter (not . flip elem bfTokens) Gen.unicode
basicCodeGen :: Gen Text
basicCodeGen = pack <$> Gen.filter hasCode strings
where
chars = Gen.frequency [(5, basicOpCharGen), (2, commentCodeGen)]
strings = Gen.list (Range.linear 1 100) chars
hasCode :: String -> Bool
hasCode = any (`elem` bfSimpleTokens)
loopGen :: Gen Text
loopGen = fmap (\code -> "[" <> code <> "]") codeGen
codeGen :: Gen Text
codeGen = (<>) <$> block <*> block
where
block = Gen.frequency [(10, basicCodeGen), (1, loopGen)]
programGen :: Gen (Program Unoptimized)
programGen =
Program . concat <$>
Gen.list
(Range.linear 0 120)
(Gen.recursive weights [basic] [(: []) . Loop . instructions <$> programGen])
where
basic :: Gen [Op]
basic =
Gen.frequency
[ (100, pure [Inc 1 0])
, (50, pure [Inc (-1) 0])
, (80, pure [Move (-1)])
, (70, pure [Move 1])
, (5, pure [Out 1 0])
, (2, pure [In 1 0])
, (10, (\b -> b ++ b) <$> basic) --fusable
, (3, pure [Loop [Move (-1)]]) --scanL
, (2, pure [Loop [Move 1]]) --scanR
, (1, pure [Loop [Inc (-1) 0]]) --clear loop
, ( 1
, pure
[ Inc (-1) 0
, Move 1
, Inc 1 0
, Inc 1 0
, Move 1
, Move 1
, Inc (-1) 0
, Move (-1)
, Move (-1)
]) --mul loop --fixme use makeMul
]
weights [nonrec] = nonrec
weights (nonrec:recursive:_) = Gen.frequency [(10, nonrec), (1, recursive)]
weights [] = error "programGen: unexpected condition"
makeMul :: [(MulFactor, MemOffset)] -> Op
makeMul muls =
Loop $
(Inc (-1) 0 : concatMap mkMul muls) ++
replicate (coerce $ sum $ map snd muls) (Move (-1))
where
mkMul (MulFactor fact, MemOffset off) =
replicate off (Move 1) ++ replicate fact (Inc 1 0)
listMachine :: Machine (Vector.Vector Int8) -> Machine [Int8]
listMachine t = t {memory = Vector.toList (memory t)}
newtype CompFlags =
CompFlags CompilerOptions
deriving (Show)
instance Monad m => Serial m CompFlags where
series =
cons5
(\a b c d e ->
CompFlags
CompilerOptions
{ cOptsOut = Nothing
, cOptsFusionOptimization = a
, cOptsClearLoopOptimization = b
, cOptsMulOptimization = c
, cOptsScanOptimization = d
, cOptsOffsetInstructionsOptimization = e
, cOptsVerbose = False
, cOptsSource = ""
})
cons5 ::
(Serial m a5, Serial m a4, Serial m a3, Serial m a2, Serial m a1)
=> (a1 -> a2 -> a3 -> a4 -> a5 -> a6)
-> Series m a6
cons5 f = decDepth $ f <$> series <~> series <~> series <~> series <~> series
execProgram :: Program Optimized -> MockIO -> IO (MachineType, MockIO)
execProgram p = runStateT (eval p)
execProgramS :: Program Optimized -> String -> IO (MachineType, MockIO)
execProgramS p input = runStateT (eval p) (mkMockIOS input)
execCodeMock :: Text -> String -> IO (MachineType, MockIO)
execCodeMock code input = execProgram p (mkMockIOS input)
where
(Right (p, _)) = inMemoryCompile defaultCompilerOptions code
execCode :: Text -> String -> IO (MachineType, String)
execCode code input = fmap mockOutputS <$> execCodeMock code input
execFile :: FilePath -> String -> IO (MachineType, String)
execFile p input = TIO.readFile p >>= \code -> execCode code input