packages feed

scat-1.1.0.0: src/Scat/Builder.hs

{- | This modules defines `Builder`s,
     which are simple parsers on `Integer`. -}
module Scat.Builder
    (
    -- * Type
      Builder

    -- * Execution
    , runBuilder
    , evalBuilder
    , execBuilder

    -- * Primitives

    -- ** Numbers
    , lessThan
    , inRange

    -- ** Char
    , digit
    , letter
    , lower
    , upper
    , ascii
    , special

    -- * Combinators
    , useup
    , shuffle
    , oneOf
    , oneOfV
    ) where

import Data.Char (ord, chr)
import Data.Monoid
import Control.Applicative
import Control.Monad
import Control.Arrow (second)
import Data.Vector (Vector)
import qualified Data.Vector as V

import Scat.Utils.Permutation

-- | Parser acting on an `Integer`.
newtype Builder a = Builder
    { runBuilder :: Integer -> (Integer, a)
      -- ^ Runs the builder.
    }

-- | Evaluates the builder.
evalBuilder :: Builder a -> Integer -> a
evalBuilder b n = snd $ runBuilder b n

-- | Executes the builder.
execBuilder :: Builder a -> Integer -> Integer
execBuilder b n = fst $ runBuilder b n

instance Functor Builder where
    fmap f (Builder g) = Builder $ second f . g

instance Applicative Builder where
    pure x = Builder (\ n -> (n, x))
    f <*> x = Builder $ \ n ->
        let (n', g) = runBuilder f n
        in g <$> runBuilder x n'

instance Monad Builder where
    return = pure
    x >>= f = Builder $ \ n ->
        let (n', v) = runBuilder x n
        in runBuilder (f v) n'

instance Monoid a => Monoid (Builder a) where
    mempty = return mempty
    mappend a b = mappend <$> a <*> b

-- | Returns a positive integer less than `i`.
lessThan :: Integral a => a -> Builder a
lessThan i = Builder $ \ n -> second fromIntegral $ quotRem n $ fromIntegral i

-- | Returns an integer between `a` and `b`, both inclusive.
inRange :: Integral a => (a, a) -> Builder a
inRange (a, b) = fmap (+ a) $ lessThan $ b + 1 - a

-- | Returns a lower case letter.
lower :: Builder Char
lower = (chr . (+ ord 'a')) <$> lessThan 26

-- | Returns an upper case letter.
upper :: Builder Char
upper = (chr . (+ ord 'A')) <$> lessThan 26

-- | Returns an printable ascii char.
ascii :: Builder Char
ascii = chr <$> inRange (32, 126)

-- | Returns a digit.
digit :: Builder Char
digit = chr <$> inRange (48, 57)

-- | Returns a letter.
letter :: Builder Char
letter = join $ oneOf [upper, lower]

-- | Returns a special character.
special :: Builder Char
special = oneOf "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"

-- | Returns one element of the list.
oneOf :: [a] -> Builder a
oneOf [] = error "oneOf on empty list"
oneOf xs = fmap (xs !!) $ lessThan $ length xs

-- | Returns on element of the vector.
oneOfV :: Vector a -> Builder a
oneOfV vect = fmap (vect V.!) $ lessThan $ V.length vect

{- | Returns the results of the input builder
     until the consummed integer is 0. -}
useup :: Builder a -> Builder [a]
useup b = Builder $ \ n ->
    if n == 0 then (0, []) else runBuilder
        ((:) <$> b <*> useup b) n

-- | Shuffles the input list.
shuffle :: [a] -> Builder [a]
shuffle xs = fmap (perm xs) $ lessThan $ fact $ fromIntegral $ length xs
  where
    fact :: Integer -> Integer
    fact n = product [1 .. n]