module Sound.MIDI.Parser.Stream
(T(..), run, runIncomplete, runPartial,
ByteList(..),
PossiblyIncomplete, UserMessage, processReport, ) where
import Control.Monad.State
(State(runState), evalState,
get, put, liftM, when, )
import qualified Sound.MIDI.Parser.Report as Report
import qualified Sound.MIDI.Parser.Class as Parser
import Sound.MIDI.Parser.Class (UserMessage, PossiblyIncomplete, )
import qualified Sound.MIDI.IO as MIO
import Data.Word (Word8)
import qualified Data.List as List
import qualified Numeric.NonNegative.Wrapper as NonNeg
import Prelude hiding (replicate, until, drop, )
newtype T str a = Cons {decons :: State str (Report.T a)}
runPartial :: T str a -> str -> (Report.T a, str)
runPartial parser input =
flip runState input (decons parser)
run :: ByteStream str => T str a -> str -> Report.T a
run parser input =
flip evalState input $ decons $
(do a <- parser
end <- Parser.isEnd
Parser.force $ when (not end) (warn "unparsed data left over")
return a)
runIncomplete :: ByteStream str =>
T str (PossiblyIncomplete a) -> str -> Report.T a
runIncomplete parser input =
flip run input $
do (me,x) <- parser
Parser.force $ maybe (return ()) warn me
return x
fromState :: State str a -> T str a
fromState p =
Cons $ liftM (\a -> Report.Cons [] (Right a)) p
instance Monad (T str) where
return x = fromState $ return x
x >>= y = Cons $
decons x >>= \ a ->
case Report.result a of
Left err -> return (Report.Cons (Report.warnings a) (Left err))
Right ar ->
liftM (\b ->
Report.Cons
(Report.warnings a ++ Report.warnings b)
(Report.result b)
) $
decons (y ar)
class ByteStream str where
switchL :: a -> (Word8 -> str -> a) -> str -> a
drop :: NonNeg.Integer -> str -> str
newtype ByteList = ByteList MIO.ByteList
instance ByteStream ByteList where
switchL n j (ByteList xss) =
case xss of
(x:xs) -> j x (ByteList xs)
_ -> n
drop n (ByteList xs) = ByteList $ List.genericDrop n xs
instance ByteStream str => Parser.C (T str) where
isEnd = fromState $ liftM (switchL True (\ _ _ -> False)) get
getByte =
switchL
(giveUp "unexpected end of data")
(\s ss -> fromState (put ss) >> return s) =<<
fromState get
skip n = when (n>0) $
do s <- fromState get
switchL
(Parser.giveUp "skip past end of part")
(\ _ rest -> fromState $ put rest)
(drop (n1) s)
warn = warn
giveUp = giveUp
try = try
force = force
warn :: String -> T str ()
warn text =
Cons $ return $ Report.Cons [text] (Right ())
giveUp :: String -> T str a
giveUp text =
Cons $ return $ Report.Cons [] (Left text)
try :: T str a -> T str (Either UserMessage a)
try =
Cons .
liftM (\r -> Report.Cons (Report.warnings r) (Right (Report.result r))) .
decons
force :: T str a -> T str a
force p =
Cons $
do ~(Report.Cons w ~(Right x)) <- decons p
return (Report.Cons w (Right x))
processReport :: Report.T a -> T str a
processReport report =
mapM_ warn (Report.warnings report) >>
either giveUp return (Report.result report)