@@ -6,31 +6,35 @@ module Fake (
66 Env (.. ),
77 newEnv ,
88 eval ,
9+ runExpr
910) where
1011
1112import qualified Aeson as A
12- import Control.Monad (forM )
13+ import Control.Monad (forM , replicateM )
1314import Control.Monad.IO.Class (liftIO )
1415import Control.Monad.Trans.State.Strict (StateT )
1516import qualified Control.Monad.Trans.State.Strict as State
1617import Data.Aeson (Value (.. ), object )
1718import qualified Data.ByteString.Char8 as BS
1819import qualified Data.HashMap.Strict as M
20+ import Data.Maybe (fromMaybe )
1921import qualified Data.Scientific as S
22+ import qualified Data.Set as Set
2023import qualified Data.Text as T
2124import qualified Data.Text.Encoding as T
2225import qualified Data.UUID as UUID
2326import qualified Data.UUID.V1 as UUID1
2427import qualified Data.Vector as V
2528import Expr (Expr (.. ))
2629import Prelude hiding (lines , replicate )
27- import System.Random (StdGen , newStdGen , random ,
28- randomR )
30+ import System.Random (StdGen , mkStdGen , newStdGen ,
31+ random , randomR )
32+ import qualified Text.Regex.TDFA.Pattern as R
33+ import qualified Text.Regex.TDFA.ReadRegex as R
2934
3035
3136-- $setup
3237-- >>> :set -XOverloadedStrings
33- -- >>> import System.Random (mkStdGen)
3438-- >>> let g = Env (mkStdGen 1) M.empty
3539-- >>> let exec expr = State.evalStateT (eval expr) g
3640
@@ -42,6 +46,12 @@ data Env = Env
4246 , envFileCache :: M. HashMap T. Text (V. Vector Value ) }
4347
4448
49+ runExpr :: Int -> Expr -> IO Value
50+ runExpr seed expr = State. evalStateT (eval expr) env
51+ where
52+ env = Env (mkStdGen seed) M. empty
53+
54+
4555newEnv :: IO Env
4656newEnv = do
4757 stdGen <- newStdGen
@@ -111,9 +121,7 @@ oneOfArray arr = do
111121-- >>> exec "oneOf(37, 42, 21)"
112122-- Number 21.0
113123oneOfArgs :: [Expr ] -> State Value
114- oneOfArgs args = do
115- idx <- withStdGen $ randomR (0 , length args - 1 )
116- eval (args !! idx)
124+ oneOfArgs args = rndListItem args >>= eval
117125
118126
119127-- | Create an array with `num` items
@@ -146,6 +154,66 @@ objectFromArgs args = do
146154 pure $ object pairs
147155
148156
157+ rndListItem :: [a ] -> State a
158+ rndListItem xs = do
159+ idx <- withStdGen $ randomR (0 , length xs - 1 )
160+ pure $ xs !! idx
161+
162+
163+ rndSetItem :: Set. Set a -> State a
164+ rndSetItem xs = do
165+ idx <- withStdGen $ randomR (0 , Set. size xs - 1 )
166+ pure $ Set. elemAt idx xs
167+
168+
169+ allPossibleChars :: Set. Set Char
170+ allPossibleChars = Set. fromList [minBound .. maxBound ]
171+
172+
173+ -- | Create random data that would be matched by the given regex
174+ --
175+ -- >>> exec "fromRegex('\\d-\\d{1,3}-FOO')"
176+ -- String "6-78-FOO"
177+ --
178+ -- >>> exec "fromRegex('[a-z]{3}')"
179+ -- String "vjy"
180+ --
181+ -- >>> exec "fromRegex('[^0-9][0-9]B')"
182+ -- String "\27960\&5B"
183+ fromRegex :: T. Text -> State Value
184+ fromRegex input =
185+ case R. parseRegex input' of
186+ (Left err) -> error $ show err
187+ (Right (pattern , _)) -> String <$> generateText pattern
188+ where
189+ input' = T. unpack input
190+ defaultUpper = 10
191+ replicatePattern lower upper pattern = do
192+ numChars <- withStdGen $ randomR (lower, upper)
193+ T. concat <$> replicateM numChars (generateText pattern )
194+ generateText p = case p of
195+ (R. POr patterns) -> rndListItem patterns >>= generateText
196+ (R. PConcat patterns) -> T. concat <$> mapM generateText patterns
197+ (R. PPlus pattern ) -> replicatePattern 1 defaultUpper pattern
198+ (R. PStar _ pattern ) -> replicatePattern 0 defaultUpper pattern
199+ (R. PBound lower mUpper pattern ) -> do
200+ replicatePattern lower (fromMaybe defaultUpper mUpper) pattern
201+ (R. PAny _ patternSet) -> fromPatternSet patternSet
202+ (R. PAnyNot _ ps@ (R. PatternSet mChars _ _ _)) -> case mChars of
203+ (Just notAllowedChars) ->
204+ charToText <$> rndSetItem (Set. difference allPossibleChars notAllowedChars)
205+ Nothing -> error $ " Can't generate data from regex pattern" <> show ps
206+ (R. PEscape _ ' d' ) -> do
207+ T. pack . show <$> (withStdGen $ randomR (0 , 9 :: Int ))
208+ (R. PChar _ char) -> pure $ charToText char
209+ _ -> error $ " Can't generate data from regex pattern" <> show p
210+ fromPatternSet ps@ (R. PatternSet mCharSet _ _ _) =
211+ case mCharSet of
212+ (Just charSet) -> charToText <$> rndSetItem charSet
213+ Nothing -> error $ " Can't generate data from regex pattern" <> show ps
214+ charToText c = T. pack [c]
215+
216+
149217fromFile :: Expr -> State Value
150218fromFile fileName = do
151219 fileName' <- A. asText <$> eval fileName
@@ -183,4 +251,5 @@ eval (FunctionCall "oneOf" args) = oneOfArgs args
183251eval (FunctionCall " replicate" [num, expr]) = replicate num expr
184252eval (FunctionCall " object" args) = objectFromArgs args
185253eval (FunctionCall " fromFile" [fileName]) = fromFile fileName
254+ eval (FunctionCall " fromRegex" [pattern ]) = eval pattern >>= fromRegex . A. asText
186255eval (FunctionCall name _) = error $ " No random generator for " <> T. unpack name
0 commit comments