{-# 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