@@ -6,13 +6,69 @@ import Bot.Expr
66import Control.Monad
77import Data.Aeson
88import Data.Aeson.Types
9+ import qualified Data.ByteString.Lazy as BS
910import Data.Char
11+ import Data.Foldable
1012import Data.List
1113import qualified Data.Text as T
14+ import HyperNerd.Parser
1215import System.Environment
1316import System.Random
1417import Text.Printf
1518
19+ data FuzzStat = FuzzStat
20+ { fsTextCount :: Int
21+ , fsMaxTextLen :: Int
22+ , fsMinTextLen :: Int
23+ , fsVarCount :: Int
24+ , fsFunCount :: Int
25+ , fsMaxFunArgsCount :: Int
26+ , fsMinFunArgsCount :: Int
27+ } deriving (Show , Eq )
28+
29+ instance Semigroup FuzzStat where
30+ s1 <> s2 =
31+ FuzzStat
32+ { fsTextCount = fsTextCount s1 + fsTextCount s2
33+ , fsMaxTextLen = fsMaxTextLen s1 `max` fsMaxTextLen s2
34+ , fsMinTextLen = fsMinTextLen s1 `min` fsMinTextLen s2
35+ , fsVarCount = fsVarCount s1 + fsVarCount s2
36+ , fsFunCount = fsFunCount s1 + fsFunCount s2
37+ , fsMaxFunArgsCount = fsMaxFunArgsCount s1 `max` fsMaxFunArgsCount s2
38+ , fsMinFunArgsCount = fsMinFunArgsCount s1 `min` fsMinFunArgsCount s2
39+ }
40+
41+ instance Monoid FuzzStat where
42+ mempty =
43+ FuzzStat
44+ { fsTextCount = 0
45+ , fsMaxTextLen = minBound
46+ , fsMinTextLen = maxBound
47+ , fsVarCount = 0
48+ , fsFunCount = 0
49+ , fsMaxFunArgsCount = minBound
50+ , fsMinFunArgsCount = maxBound
51+ }
52+
53+ statOfExprs :: [Expr ] -> FuzzStat
54+ statOfExprs = foldMap statOfExpr
55+
56+ statOfExpr :: Expr -> FuzzStat
57+ statOfExpr (TextExpr text) =
58+ mempty
59+ { fsTextCount = 1
60+ , fsMinTextLen = T. length text
61+ , fsMaxTextLen = T. length text
62+ }
63+ statOfExpr (VarExpr _) = mempty {fsVarCount = 1 }
64+ statOfExpr (FunCallExpr _ args) =
65+ mempty
66+ { fsFunCount = 1
67+ , fsMaxFunArgsCount = length args
68+ , fsMinFunArgsCount = length args
69+ } <>
70+ statOfExprs args
71+
1672data FuzzParams = FuzzParams
1773 { fpFuzzCount :: Int
1874 , fpExprsRange :: (Int , Int )
@@ -42,6 +98,9 @@ instance FromJSON FuzzParams where
4298readFuzzParams :: FilePath -> IO FuzzParams
4399readFuzzParams = fmap (either error id ) . eitherDecodeFileStrict
44100
101+ saveFuzzParams :: FuzzParams -> FilePath -> IO ()
102+ saveFuzzParams params filePath = BS. writeFile filePath $ encode params
103+
45104defaultFuzzParams :: FuzzParams
46105defaultFuzzParams =
47106 FuzzParams
@@ -61,7 +120,7 @@ unparseFunCallArgs = T.concat . intersperse "," . map unparseFunCallArg
61120
62121unparseExpr :: Expr -> T. Text
63122unparseExpr (TextExpr text) = text
64- unparseExpr (VarExpr name) = " %" <> name
123+ unparseExpr (VarExpr name) = " %" <> name <> " % "
65124unparseExpr (FunCallExpr name args) =
66125 " %" <> name <> " (" <> unparseFunCallArgs args <> " )"
67126
@@ -104,36 +163,54 @@ randomExpr params = do
104163 1 -> randomVarExpr params
105164 _ -> randomFunCallExpr params
106165
107- normalizeExprs :: [Expr ] -> [Expr ]
108- normalizeExprs [] = []
109- normalizeExprs (TextExpr t1: TextExpr t2: rest) =
110- normalizeExprs (TextExpr (t1 <> t2) : rest)
111- normalizeExprs (_: rest) = normalizeExprs rest
112-
113166randomExprs :: FuzzParams -> IO [Expr ]
114- randomExprs params = do
115- n <- randomRIO $ fpExprsRange params
116- replicateM n (randomExpr params)
117-
118- fuzzIteration :: FuzzParams -> IO Bool
167+ randomExprs params = randomRIO (fpExprsRange params) >>= f []
168+ where
169+ normalizeExprs :: [Expr ] -> [Expr ]
170+ normalizeExprs [] = []
171+ normalizeExprs (TextExpr t1: TextExpr t2: rest) =
172+ normalizeExprs (TextExpr (t1 <> t2) : rest)
173+ normalizeExprs (x: rest) = x : normalizeExprs rest
174+ f :: [Expr ] -> Int -> IO [Expr ]
175+ f es n
176+ | m >= n = return es
177+ | otherwise = do
178+ es' <- replicateM (n - m) (randomExpr params)
179+ f (normalizeExprs (es ++ es')) n
180+ where
181+ m = length es
182+
183+ fuzzIteration :: FuzzParams -> IO FuzzStat
119184fuzzIteration params = do
120- es <- normalizeExprs <$> randomExprs params
185+ es <- randomExprs params
121186 let es' = runParser exprs $ unparseExprs es
122187 when (Right (" " , es) /= es') $ do
123188 print es
124189 print es'
125- error " test "
126- return ( Right ( " " , es) == es')
190+ error " Failed "
191+ return $ statOfExprs es
127192
128193fuzz :: FuzzParams -> IO ()
129194fuzz params = do
130- report <- replicateM (fpFuzzCount params) (fuzzIteration params)
131- printf " Failures: %d\n " $ length $ filter not report
132- printf " Successes: %d\n " $ length $ filter id report
195+ stats <- replicateM (fpFuzzCount params) (fuzzIteration params)
196+ print $ fold stats
133197
134198mainWithArgs :: [String ] -> IO ()
135- mainWithArgs (fuzzParamsPath: _) = readFuzzParams fuzzParamsPath >>= fuzz
136- mainWithArgs _ = error " Usage: Fuzz <fuzz.json>"
199+ mainWithArgs (" genconf" : configFilePath: _) = do
200+ saveFuzzParams defaultFuzzParams configFilePath
201+ printf " Generated default configuration at %s" configFilePath
202+ mainWithArgs (" runconf" : fuzzParamsPath: _) =
203+ readFuzzParams fuzzParamsPath >>= fuzz
204+ mainWithArgs (" genexpr" : configFilePath: _) = do
205+ putStrLn " Generating expression:"
206+ params <- readFuzzParams configFilePath
207+ randomExprs params >>= print
208+ mainWithArgs _ =
209+ error
210+ " Usage: \n \
211+ \ Fuzz genconf <fuzz.json>\n \
212+ \ Fuzz runconf <fuzz.json>\n \
213+ \ Fuzz genexpr <fuzz.json>"
137214
138215main :: IO ()
139216main = getArgs >>= mainWithArgs
0 commit comments