gcodehs-0.1.2.0: src/Data/GCode/Pipes.hs
{-# LANGUAGE OverloadedStrings #-}
module Data.GCode.Pipes where
import Control.Monad
import Control.Monad.Trans.State.Strict
import Data.ByteString (ByteString)
import System.IO (Handle)
import Data.GCode.Types
import Data.GCode.Canon
import Data.GCode.Eval
import Data.GCode.Line
import Data.GCode.Parse
import Data.GCode.Pretty
import qualified Data.GCode.Canon.Convert
import Pipes
import Pipes.Attoparsec (ParsingError)
import Pipes.Safe (SafeT)
import qualified Data.ByteString.Char8
import qualified Data.Map.Strict
import qualified Pipes.Attoparsec
import qualified Pipes.ByteString
import qualified Pipes.Prelude
import qualified Pipes.Safe
import qualified System.IO
-- something fishy about this type
parseProducer :: Handle -> Producer Code (SafeT IO) (Either (ParsingError, Producer ByteString (SafeT IO) ()) ())
parseProducer = parseProducer' 1024
parseProducer' :: MonadIO m
=> Int
-> Handle
-> Producer Code m (Either (ParsingError, Producer ByteString m ()) ())
parseProducer' bufSize handle = Pipes.Attoparsec.parsed
parseGCodeLine (Pipes.ByteString.hGetSome bufSize handle)
withFile :: FilePath -> (Handle -> (SafeT IO) r) -> IO r
withFile filepath job =
System.IO.withFile filepath System.IO.ReadMode $ \handle ->
Pipes.Safe.runSafeT $ job handle
pipeToList :: FilePath -> Proxy () Code () a (SafeT IO) () -> IO [a]
pipeToList filepath pipeTail = withFile filepath $ \h ->
Pipes.Prelude.toListM
$ (() <$ parseProducer h)
>-> pipeTail
gcodeToCanonList :: FilePath -> IO [Canon]
gcodeToCanonList filepath = pipeToList filepath $ evalP >-> evalCanonP
gcodeToLines :: FilePath -> IO [Line]
gcodeToLines filepath = pipeToList filepath $ evalP >-> evalCanonLinesP
gcodePipe :: FilePath -> (Consumer Code (SafeT IO) ()) -> IO ()
gcodePipe filepath pipeTail =
System.IO.withFile filepath System.IO.ReadMode $ \handle ->
Pipes.Safe.runSafeT . runEffect $
(() <$ parseProducer handle)
>-> pipeTail
-- needs better name
runPipe :: FilePath
-> Maybe FilePath
-> (Pipe Code ByteString (SafeT IO) ())
-> IO ()
runPipe input Nothing pipeMiddle = gcodePipe input (pipeMiddle >-> Pipes.ByteString.stdout)
runPipe input (Just output) pipeMiddle =
System.IO.withFile output System.IO.WriteMode $ \outhandle ->
gcodePipe input (pipeMiddle >-> Pipes.ByteString.toHandle outhandle)
foldedPipe :: FilePath
-> (Producer Code (Pipes.Safe.SafeT IO) () -> Effect (Pipes.Safe.SafeT IO) r)
-> IO r
foldedPipe filepath fold =
System.IO.withFile filepath System.IO.ReadMode $ \handle ->
Pipes.Safe.runSafeT . runEffect $
fold (() <$ parseProducer handle)
-- evaluators
totalizeP :: Pipe Code Code (SafeT IO) ()
totalizeP = flip evalStateT Data.Map.Strict.empty $ forever $ do
x <- lift await
inEffect <- get
let updatedCode = updateFromCurrentModals inEffect x
updatedModals = updateModals inEffect updatedCode
put updatedModals
lift $ yield updatedCode
evalP :: Pipe Code Code (SafeT IO) ()
evalP = flip evalStateT newState $ forever $ do
x <- lift await
st <- get
let (result, steppedState, _rest) = step st [x]
-- XXX: add pretty printer for IPState
--liftIO $ print steppedState
put steppedState
case result of
Just r -> lift $ yield r
Nothing -> return ()
evalCanonP :: Pipe Code Canon (SafeT IO) ()
evalCanonP = flip evalStateT initCanonState $ forever $ do
x <- lift await
st <- get
forM_ (Data.GCode.Canon.Convert.toCanon x) $ \c -> do
let steppedState = stepCanon st c
put steppedState
lift $ yield c
evalCanonLinesP :: Pipe Code Line (SafeT IO) ()
evalCanonLinesP = flip evalStateT initCanonState $ forever $ do
x <- lift await
st <- get
forM_ (Data.GCode.Canon.Convert.toCanon x) $ \c -> do
let steppedState = stepCanon st c
put steppedState
forM_ (toLines st steppedState c) $ lift . yield
-- mmaped experiment, requires pipes-bytestring-mmap
--import qualified Pipes.ByteString.MMap
--main' = do
-- file <- fmap Prelude.head getArgs
-- Pipes.Safe.runSafeT . Pipes.Safe.runEffect $
-- (() <$ Pipes.Attoparsec.parsed parseGCodeLine (Pipes.ByteString.MMap.unsafeMMapFile file) )
-- >-> Pipes.Prelude.map ppGCodeLine
-- >-> Pipes.Prelude.stdoutLn
-- pretty print
prettySinkWith :: (a -> ByteString) -> Pipe a ByteString (SafeT IO) ()
prettySinkWith fn =
Pipes.Prelude.map fn
prettySink :: Pipe Code ByteString (SafeT IO) ()
prettySink =
Pipes.Prelude.map ppGCodeLine
>-> Pipes.Prelude.map (Data.ByteString.Char8.pack . (++"\n"))
compactSink :: Pipe Code ByteString (SafeT IO) ()
compactSink =
Pipes.Prelude.map ppGCodeLineCompact
>-> Pipes.Prelude.map (Data.ByteString.Char8.pack . (++"\n"))
-- Helpers
addNewLine :: ByteString -> ByteString
addNewLine to = Data.ByteString.Char8.append to "\n"
wrapPrinter :: (a -> String) -> a -> ByteString
wrapPrinter p = addNewLine . Data.ByteString.Char8.pack . p