Loading...
-- ghc -O2  -fspec-constr-recursive=10 -fmax-worker-args=16
-- Convert the input file to camel case and write to stdout

import Data.Function ((&))
import Data.Word (Word8)
import System.Environment (getArgs)
import System.IO (Handle, IOMode(..), openFile, stdout)

import qualified Streamly.Data.Fold as Fold
import qualified Streamly.Data.Stream as Stream
import qualified Streamly.FileSystem.Handle as Handle

-- | @camelCase source-file dest-file@
camelCase :: Handle -> Handle -> IO ()
camelCase src dst =
      Stream.unfold Handle.reader src    -- Stream IO Word8
    & Stream.postscan fold               -- Stream IO (Bool, Maybe Word8)
    & Stream.mapMaybe snd                -- Stream IO Word8
    & Stream.fold (Handle.write dst)     -- IO ()

    where

    isNewline x = x == 0x0a
    isUpper x = x >= 0x41 && x <= 0x5a
    isLower x = x >= 0x61 && x <= 0x7a

    -- Scan accumulator @(wasSpace, output)@ contains whether the previous
    -- character was white space, and if the current char should be emitted in
    -- the output.
    step :: (Bool, Maybe Word8) -> Word8 -> (Bool, Maybe Word8)
    step (wasSpace, _) x
        -- Newline or upper case chars go unmodified
        | isNewline x || isUpper x = (False, Just x)
        -- Convert lower to upper if preceded by whitespace
        | isLower x = (False, Just $ if wasSpace then x - 32 else x)
        | otherwise = (True, Nothing)

    fold = Fold.foldl' step (True, Nothing)

main :: IO ()
main = do
    name <- fmap head getArgs
    src <- openFile name ReadMode
    camelCase src stdout