-- | Parsers for pitch class sets and sequences, and for 'SRO's. module Music.Theory.Parse (rnrtnmi,pco) where import Control.Monad import Data.Char import Music.Theory.PitchClass import Text.ParserCombinators.Parsec -- | A 'Char' parser. type P a = GenParser Char () a -- | Boolean 'P' for given 'Char'. is_char :: Char -> P Bool is_char c = let f '_' = False f _ = True in liftM f (option '_' (char c)) -- | Parse 'Int'. get_int :: P Int get_int = liftM read (many1 digit) -- | Parse a Morris format serial operator descriptor. -- -- > rnrtnmi "r2RT3MI" == SRO 2 True 3 True True rnrtnmi :: String -> SRO Int rnrtnmi s = let p = do { r <- rot ; r' <- is_char 'R' ; _ <- char 'T' ; t <- get_int ; m <- is_char 'M' ; i <- is_char 'I' ; eof ; return (SRO r r' t m i) } rot = option 0 (char 'r' >> get_int) in either (\e -> error ("rnRTnMI parse failed\n" ++ show e)) id (parse p "" s) -- | Parse a /pitch class object/ string. Each 'Char' is either a -- number, a space which is ignored, or a letter name for the numbers -- 10 ('t' or 'a' or 'A') or 11 ('e' or 'B' or 'b'). -- -- > pco "13te" == [1,3,10,11] -- > pco "13te" == pco "13ab" pco :: String -> [Int] pco s = let s' = dropWhile isSpace s s'' = takeWhile (`elem` "0123456789taAebB") s' f c | c `elem` "taA" = 10 | c `elem` "ebB" = 11 | otherwise = read [c] in map f s''