Browse Source

Clean a few things up, implement reading from STDIN or several files

master
Peter J. Jones 5 years ago
parent
commit
8dfb0e9e5c
3 changed files with 94 additions and 6 deletions
  1. 88
    1
      src/Main.hs
  2. 1
    1
      util
  3. 5
    4
      wc-streams.cabal

+ 88
- 1
src/Main.hs View File

@@ -13,5 +13,92 @@ the LICENSE file.
module Main (main) where

--------------------------------------------------------------------------------
-- Library Imports.
import Control.Monad
import Data.ByteString (ByteString)
import Data.Char (isSpace)
import Data.Monoid
import Data.Text (Text)
import qualified Data.Text as T
import Prelude hiding (lines, words)
import System.Environment
import System.IO.Streams (InputStream)
import qualified System.IO.Streams as Streams
import Text.Printf (printf)

--------------------------------------------------------------------------------
-- | Keep track of a series of counters while processing files.
data Counters = C
{ lines :: Int -- ^ Number of lines.
, words :: Int -- ^ Number of words.
, chars :: Int -- ^ Number of characters.
} deriving Show

instance Monoid Counters where
mempty = C 0 0 0
mappend (C l1 w1 c1) (C l2 w2 c2) = C (l1 + l2) (w1 + w2) (c1 + c2)

--------------------------------------------------------------------------------
-- | State necessary to process a file in chunks.
data State = S
{ inWord :: Bool -- ^ Last character was part of a word.
, counters :: Counters -- ^ Current set of counters.
}

--------------------------------------------------------------------------------
-- | Update the given state based on the given character.
wc :: State -> Char -> State
wc state char = case char of
'\n' -> whitespace (\c -> c {lines = lines c + 1})
_ | isSpace char -> whitespace id
| otherwise -> S True (character id)
where
whitespace :: (Counters -> Counters) -> State
whitespace f = if inWord state
then S False (character (f . \c -> c {words = words c + 1}))
else S False (character f)

character :: (Counters -> Counters) -> Counters
character f = let counters' = f (counters state)
in counters' {chars = chars counters' + 1}

--------------------------------------------------------------------------------
-- | Process all characters in the given stream.
stream :: InputStream ByteString -> IO Counters
stream = Streams.decodeUtf8 >=> go (S False mempty) >=> return . counters
where
go :: State -> InputStream Text -> IO State
go state upstream = do
textM <- Streams.read upstream

case textM of
Nothing -> return (eof state)
Just text -> go (T.foldl' wc state text) upstream

eof :: State -> State
eof state@(S w c) = if w then S False (c {words = words c + 1}) else state

--------------------------------------------------------------------------------
-- | Print a report to STDOUT.
report :: Int -> String -> Counters -> IO ()
report wid label (C l w c) = printf "%*d %*d %*d %s\n" wid l wid w wid c label

--------------------------------------------------------------------------------
-- | Calculate the maximum width of a report column.
width :: [Counters] -> Int
width cs = go 1 $ maximum (0 : map chars cs) where
go :: Int -> Int -> Int
go width' n | n > 10 = go (width' + 1) (n `div` 10)
| otherwise = width'

--------------------------------------------------------------------------------
-- | Do it!
main :: IO ()
main = undefined
main = do
args <- getArgs

case args of
[] -> report (width []) "" =<< stream (Streams.stdin)
xs -> do cs <- mapM (flip Streams.withFileAsInput stream) xs
mapM_ (uncurry $ report (width cs)) (zip xs cs)
report (width cs) "total" (mconcat cs)

+ 1
- 1
util

@@ -1 +1 @@
Subproject commit 12d751d681dc0392b63698541e33af62bb02db6b
Subproject commit 36a98650ac7b3fc615bc30880f1a26e3f2d27ca4

+ 5
- 4
wc-streams.cabal View File

@@ -20,10 +20,8 @@ flag maintainer
--------------------------------------------------------------------------------
executable wc
main-is: Main.hs
-- other-modules:
-- other-extensions:
hs-source-dirs: src
default-language: Haskell2010
default-language: Haskell2010

ghc-options: -Wall -rtsopts
ghc-prof-options: -fprof-auto-top -fprof-cafs
@@ -31,4 +29,7 @@ executable wc
if flag(maintainer)
ghc-options: -Werror

build-depends: base >= 4.7 && < 5.0
build-depends: base >= 4.7 && < 5.0
, bytestring >= 0.10 && < 0.11
, io-streams >= 1.1 && < 1.2
, text >= 1.1 && < 1.2

Loading…
Cancel
Save