packages feed

haskell-awk-1.0: src/System/Console/Hawk/Runtime.hs

--   Copyright 2013 Mario Pastorelli (pastorelli.mario@gmail.com) Samuel Gélineau (gelisam@gmail.com)
--
--   Licensed under the Apache License, Version 2.0 (the "License");
--   you may not use this file except in compliance with the License.
--   You may obtain a copy of the License at
--
--       http://www.apache.org/licenses/LICENSE-2.0
--
--   Unless required by applicable law or agreed to in writing, software
--   distributed under the License is distributed on an "AS IS" BASIS,
--   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--   See the License for the specific language governing permissions and
--   limitations under the License.

{-# LANGUAGE ExistentialQuantification
           , ExtendedDefaultRules
           , OverloadedStrings
           , ScopedTypeVariables #-}

-- | This is Hawk's runtime, it needs to be installed in order to evaluate Hawk
--   expressions. The API may change at any time.
module System.Console.Hawk.Runtime (
    c8pack
  , sc8pack
  , listMap
  , listMapWords
  , printRows
  , printRow
  , parseRows
  , parseWords
  , showRows
  , runExpr
--  , runExprs
) where

import Prelude
import Control.Exception (SomeException,handle)
import qualified Data.ByteString.Char8 as SC8
import Data.ByteString.Lazy.Char8 (ByteString)
import qualified Data.ByteString.Lazy.Char8 as C8 hiding (hPutStrLn)
import qualified Data.List as L
import qualified Data.ByteString.Lazy.Search as BS
import qualified System.IO as IO

import System.Console.Hawk.Representable
import qualified System.Console.Hawk.IO as HawkIO

handleErrors :: IO () -> IO ()
handleErrors = handle (\(e :: SomeException) -> IO.hPrint IO.stderr e)

dropLastIfEmpty :: [C8.ByteString] -> [C8.ByteString]
dropLastIfEmpty [] = []
dropLastIfEmpty (x:[]) = if C8.null x then [] else [x]
dropLastIfEmpty (x:xs) = x:dropLastIfEmpty xs

listMap :: (a -> b) -> [a] -> [b]
listMap = L.map

listMapWords :: ([a] -> b) -> [[a]] -> [b]
listMapWords = L.map

c8pack :: String
       -> C8.ByteString
c8pack = C8.pack

sc8pack :: String
        -> SC8.ByteString
sc8pack = SC8.pack

dropTrailingNewline :: C8.ByteString -> C8.ByteString
dropTrailingNewline "" = ""
dropTrailingNewline s = if last_char == '\r' then s' else s
    where last_char = C8.last s
          n = C8.length s
          s' = C8.take (n - 1) s

-- if delim is "\n", Windows-style "\r\n" delimiters are also accepted.
parseRows :: SC8.ByteString -> C8.ByteString -> [C8.ByteString]
parseRows delim str = dropLastIfEmpty
                    $ maybeDropTrailingNewline
                    $ BS.split delim str
    where maybeDropTrailingNewline = if delim == "\n"
                                       then map dropTrailingNewline
                                       else id

---- special case for space
parseWords :: SC8.ByteString -> SC8.ByteString -> C8.ByteString -> [[C8.ByteString]]
parseWords rowsDelim columnsDelim str = let rows = parseRows rowsDelim str
                                        in L.map f rows
    where f = if columnsDelim == SC8.singleton ' '
                then L.filter  (not . C8.null) . BS.split columnsDelim
                else BS.split columnsDelim
         
--parseWords :: SC8.ByteString -> [C8.ByteString] -> [[C8.ByteString]]
--parseWords delim strs = L.map f strs
--    where f = if delim == SC8.singleton ' '
--                then L.filter  (not . C8.null) . BS.split delim
--                else BS.split delim

--runOnInput :: Maybe FilePath -- ^ the input file or stdout when Nothing
--            -> (C8.ByteString -> IO ()) -- ^ the action to run on the input
--            -> IO ()
--runOnInput fp f = do
--    input <- maybe C8.getContents C8.readFile fp
--    f input
--    IO.hFlush IO.stdout
    -- TODO: we need also hFlush stderr?

runExpr :: Maybe FilePath -- ^ if the input is a file
        -> (Maybe FilePath -> IO C8.ByteString) -- ^ input reader
        -> (C8.ByteString -> C8.ByteString)
        -> (C8.ByteString -> IO ())
        -> IO ()
runExpr m i f o = i m >>= o . f
--runExpr = runOnInput
--
--runExprs :: Maybe FilePath -> (C8.ByteString -> [IO ()]) -> IO ()
--runExprs fp f = runOnInput fp (sequence_ . f)
--
showRows :: forall a . (Rows a)
         => C8.ByteString -- ^ rows delimiter
         -> C8.ByteString -- ^ columns delimiter
         -> a -- ^ value to print
         -> C8.ByteString
showRows rd cd = C8.intercalate rd . repr cd

printRows :: forall a . (Rows a) 
          => Bool -- ^ if printRows will continue after errors
          -> C8.ByteString -- ^ rows delimiter
          -> C8.ByteString -- ^ columns delimiter
          -> a -- ^ the value to print as rows
          -> IO ()
printRows _ rd cd = HawkIO.printOutput . showRows rd cd
--printRows _ rd cd v = handle handler printRows_ 
--  where handler e = case ioe_type e of
--                      ResourceVanished -> return ()
--                      _ -> IO.hPrint IO.stderr e
--        printRows_ = C8.putStrLn (showRows rd cd v) >> IO.hFlush IO.stdout
--printRows b rowDelimiter columnDelimiter = printFirstRow_ . repr columnDelimiter
--    where printRows_ [] = return ()
--          printRows_ (x:xs) = do
--            putStrAndDelim x
--            handle ioExceptionsHandler (continue xs)
--          printFirstRow_ [] = return ()
--          printFirstRow_ (x:xs) = do
--            putStrOnly x
--            handle ioExceptionsHandler (continue xs)
--          putStrAndDelim = if b then handleErrors . putDelimAndStr_
--                                else putDelimAndStr_
--          putStrOnly = if b then handleErrors . C8.putStr else C8.putStr
--          putDelimAndStr_ :: C8.ByteString -> IO ()
--          putDelimAndStr_ c = C8.putStr rowDelimiter >> C8.putStr c
--          continue xs = IO.hFlush IO.stdout >> printRows_ xs
--          ioExceptionsHandler e = case ioe_type e of
--                                    ResourceVanished -> return ()
--                                    _ -> IO.hPrint IO.stderr e

printRow :: forall a . (Row a)
         => Bool -- ^ if printRow should continue after errors
         -> ByteString -- ^ the column delimiter
         -> a -- ^ the value to print
         -> IO ()
printRow b d = if b then handleErrors . f else f
  where f = C8.putStrLn . repr' d