{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Data.GCode.Parse (parseGCode, parseGCodeLine, parseOnlyGCode) where
import Data.GCode.Types
import Control.Applicative
import Prelude hiding (take, takeWhile, mapM)
import Data.Attoparsec.ByteString.Char8
import Data.ByteString (ByteString)
import qualified Data.ByteString
import qualified Data.Char
import qualified Data.Either
import qualified Data.Map
import qualified Data.Maybe
parseGCodeLine :: Parser Code
parseGCodeLine = between lskip lskip parseCodeParts <* endOfLine
parseGCode :: Parser GCode
parseGCode = many1 parseGCodeLine
parseOnlyGCode :: ByteString -> Either String GCode
parseOnlyGCode = parseOnly parseGCode
lskip :: Parser ()
lskip = skipWhile (\x -> x == ' ' || x == '\t')
between :: Monad m => m a1 -> m a2 -> m b -> m b
between open close p = do { _ <- open; x <- p; _ <- close; return x }
isEndOfLineChr :: Char -> Bool
isEndOfLineChr '\n' = True
isEndOfLineChr '\r' = True
isEndOfLineChr _ = False
parseLead :: Parser Class
parseLead = do
a <- satisfy $ inClass $ (asChars allClasses) ++ (map Data.Char.toLower $ asChars allClasses)
return $ Data.Maybe.fromJust $ toCodeClass a
{-# INLINE parseLead #-}
parseAxisDes :: Parser AxisDesignator
parseAxisDes = do
a <- satisfy $ inClass $ asChars allAxisDesignators
return $ Data.Maybe.fromJust $ toAxis a
{-# INLINE parseAxisDes #-}
parseParamDes :: Parser ParamDesignator
parseParamDes = do
a <- satisfy $ inClass $ asChars allParamDesignators
return $ Data.Maybe.fromJust $ toParam a
{-# INLINE parseParamDes #-}
parseParamOrAxis :: Parser (Either (AxisDesignator, Double) (ParamDesignator, Double))
parseParamOrAxis = do
lskip
ax <- option Nothing (Just <$> parseAxisDes)
case ax of
Just val -> do
lskip
f <- double
return $ Left (val, f)
Nothing -> do
paramDes <- parseParamDes
lskip
f <- double
return $ Right (paramDes, f)
parseAxesParams :: Parser (Axes, Params)
parseAxesParams = do
a <- many parseParamOrAxis
return (Data.Map.fromList $ Data.Either.lefts a, Data.Map.fromList $ Data.Either.rights a)
{-# INLINE parseAxesParams #-}
parseCode :: Parser Code
parseCode = do
codeCls <- optional parseLead
codeNum <- optional decimal
codeSub <- optional (char '.' *> decimal)
lskip
(codeAxes, codeParams) <- parseAxesParams
lskip
codeComment <- option "" $ between lskip lskip parseComment'
let c = Code{..}
if c == emptyCode
then return $ Empty
else return c
parseComment' :: Parser ByteString
parseComment' = do
t <- many $ between (lskip *> char '(') (char ')' <* lskip) $ takeWhile1 (/=')')
semisep <- option "" $ char ';' *> takeWhile (not . isEndOfLineChr)
rest <- takeWhile (not . isEndOfLineChr)
return $ Data.ByteString.concat $ t ++ [semisep, rest]
parseComment :: Parser Code
parseComment = Comment <$> parseComment'
parseOther :: Parser Code
parseOther = do
a <- takeWhile (not . isEndOfLineChr)
return $ Other a
parseCodeParts :: Parser Code
parseCodeParts =
parseCode
<|> parseOther
<|> parseComment