packages feed

kangaroo (empty) → 0.1.0

raw patch · 18 files changed

+2206/−0 lines, 18 filesdep +arraydep +basesetup-changed

Dependencies added: array, base

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2008 Stephen Peter Tetley++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:++1. Redistributions of source code must retain the above copyright+   notice, this list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright+   notice, this list of conditions and the following disclaimer in the+   documentation and/or other materials provided with the distribution.++3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,4 @@+#!/usr/bin/env runhaskell++import Distribution.Simple+main = defaultMain
+ demo/MidiDatatypes.hs view
@@ -0,0 +1,252 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module      :  MidiDatatypes+-- Copyright   :  (c) Stephen Tetley 2009+-- License     :  BSD3+--+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>+-- Stability   :  volatile+-- Portability :  to be determined.+--+-- Syntax tree for MIDI files.+--+--------------------------------------------------------------------------------+++module MidiDatatypes (+    -- * MidiFile representation+    MidiFile(..),+    -- * File header with basic information+    Header(..),+    -- * Track - a series of events+    Track(..),+    -- * Header format+    HFormat(..),+    +    DeltaTime,++    -- * A midi event paired with the delta time of its onset+    Message, +    -- * A control or meta event+    Event(..),+    +    VoiceEvent(..),+    SystemEvent(..),+    MetaEvent(..),+    +    -- * Representation of delta times as a division of quarter notes+    TimeDivision(..),+    -- * Type of a text meta-event+    TextType(..),+    +    -- * Scale type - used for setting key signature+    ScaleType(..),+    +    -- * Two helpers+    varlenSplit, hexStr+    +  ) where++import Data.Bits+import Data.Int+import Data.Sequence+import Data.Word+import Numeric (showHex)++++data MidiFile = MidiFile Header (Seq Track)+  deriving (Eq,Show)++-- | @'Header' fmt nt td@ +--+-- @fmt@ - file format - see @'HFormat'@, +-- @nt@  - number of tracks,+-- @td@  - @'TimeDivision'@, often 384 or 480 ticks per beat.+-- The header is the start of a MIDI file, it is indicated by the +-- marker \'@MThd@\'.   +data Header = Header HFormat Word16 TimeDivision+  deriving (Eq,Show)++-- | @'Track' xs@ +--+-- @xs@ - list of @'Message'@. In MIDI files, the start of a track is+-- indicated by the marker \'@MTrk@\'.  +newtype Track = Track { getTrack :: Seq Message }+  deriving (Eq,Show)++-- | @'HFormat'@ +--+-- The file format - in a MIDI file this is a big-endian word16 with 0,1 or 2+-- being the only valid values. +data HFormat +    -- | Format 0 file - single multi-channel track.+    = MF0 +    -- | Format 1 file - 1 or more tracks, played simultaneously.+    | MF1+    -- | Format 2 file - 1 or more independent tracks.+    | MF2+  deriving (Eq, Enum, Show) ++-- | @'TimeDivision'@ +--+-- Defines the default unit of time in the MIDI file.+data TimeDivision +    -- | frames per second.+    = FPS Word16+    -- | ticks per beat, i.e. the number of units for a quater note.+    | TPB Word16    +  deriving (Eq,Show)+                                             +-- | @'TextType'@ +--+-- Signal the type of a text meta event.+data TextType +    = GENERIC_TEXT +    | COPYRIGHT_NOTICE +    | SEQUENCE_NAME +    | INSTRUMENT_NAME+    | LYRICS +    | MARKER +    | CUE_POINT +  deriving (Eq,Enum,Ord,Show) ++-- | @'DeltaTime'@ +--+-- All time values in a MIDI track are represented as a \delta\ from the +-- previous event rather than an absolute time. +-- Although DeltaTime is a type synonym for Word32, in MIDI files it is +-- represented as a @varlen@ to save space. +type DeltaTime = Word32++-- | @'Message'@ +--+-- MIDI messages are pairs of @'DeltaTime'@ and @'Event'@ wrapped in a newtype. +-- Sequential messages with delta time 0 will be played simultaneously.  +type Message = (DeltaTime, Event)+++-- Note, the Ord instance for pairs is very useful for rendering.+-- When we have have a NoteOn and a NoteOff on the same channel at the +-- same time we want the NoteOff played first. An ordinary sort will +-- give us this.++     +++-- | @'Event'@ +--+-- MIDI has three types of event.+data Event +    -- | Meta events - usually interpretable (e.g. @end-of-track@, @set-tempo@).+    = MetaEvent         MetaEvent+    -- | SysEx - system exclusive - events. Usually synthesizer specific.+    | SystemEvent       SystemEvent+    -- | Voice events (e.g @note-on@, @note-off@) are relayed to specific+    -- channels.+    | VoiceEvent        VoiceEvent    +  deriving (Eq,Show,Ord)++-- | @'VoiceEvent'@ +--+-- Voice events control the output of the synthesizer.+-- Note - these are not in the the same order as there byte values+-- Controller and ProgramChange are higher than they /naturally/+-- occur so the will come first after a comparison / sort.+data VoiceEvent +    -- | @Controller chan type value@ - controller change to the channel,+    -- e.g. by a footswitch.+    = Controller          Word8 Word8 Word8+    -- | @ProgramChange chan num@ - change the instrument playing on the +    -- specified channel. See 'GMInst' for the instruments available with+    -- General MIDI.+    | ProgramChange       Word8 Word8+    -- | @NoteOff chan note velocity@ - turn off a sounding note.+    | NoteOff             Word8 Word8 Word8   +    -- | @NoteOn chan note velocity@ - start playing a note.+    | NoteOn              Word8 Word8 Word8+    -- | @NoteAftertouch chan note value@ - change in pressure applied to +    -- the synthesizer key. +    | NoteAftertouch      Word8 Word8 Word8     +    -- | @ChanAftertouch chan value@ - +    | ChanAftertouch      Word8 Word8+    -- | @PitchBend chan value@ - change the pitch of a sounding note. +    -- Often used to approxiamate microtonal tunings.+    | PitchBend           Word8 Word16+  deriving (Eq,Show,Ord)++-- | @'SystemEvent'@ +--+-- \SysEx\ events - these are generally uninterpreted. +data SystemEvent +    -- | @SysEx length data@ - an uninterpreted sysex event.          +    = SysEx               Word32 [Word8]+    -- | @DataEvent value@ - value should be in the range 0..127.+    | DataEvent           Word8 +  deriving (Eq,Show,Ord)++-- | @'MetaEvent'@ +--+-- Meta events - in Format 1 files \general\ events (e.g. text events) should+-- only appear in track 1. \Specific\ events (e.g. end-of-track) should appear+-- in any track where necessary. +data MetaEvent+    -- | @TextEvent text_type contents@ - a free text field (e.g. copyright).+    -- The contents can notionally be any length.+    = TextEvent           TextType String+    -- | @SequenceNumber value@ - Format 1 files - only track 1 should have a +    -- sequence number. Format 2 files - a sequence number should identify +    -- each track. +    -- This should occur at the start of a track, before any non-zero time +    -- events.+    | SequenceNumber      Word16+    -- | @ChannelPrefix chan@ - relay all meta and sysex events to the +    -- given channel.+    | ChannelPrefix       Word8+    -- | @EndOfTrack@ - indicated end of track. +    | EndOfTrack+    -- | @SetTempo mspqn@ - microseconds per quarter-note.+    | SetTempo            Word32+    -- | @SMPTEOffset hour  minute second frac subfrac@ - the SMPTE time when +    -- a track should start. This should occur at the start of a track,+    -- before any non-zero time events.+    | SMPTEOffset         Word8 Word8 Word8 Word8 Word8+    +    -- | @TimeSignature numerator denominator metro num-32nd-notes@ - time +    -- signature.+    | TimeSignature       Word8 Word8 Word8 Word8+    +    -- | @KeySignature key_type scale_type@ - key_type is the number of +    -- sharps (postive numbers) or flats (negative numbers), +    -- e.g. (-1) is 1 flat.+    -- 'ScaleType' indicates major or minor.  +    | KeySignature        Int8 ScaleType+    +    -- | @SSME length data@ - sequencer specific meta-event.+    | SSME                Word32 [Word8]+  deriving (Eq,Show,Ord)++-- | @'ScaleType'@ +--+-- Scale type - @major@ or @minor@.  +data ScaleType = MAJOR | MINOR+  deriving (Eq,Enum,Ord,Show)++--------------------------------------------------------------------------------+-- helper for varlen+--------------------------------------------------------------------------------+-- | @varlenSplit@ - reduce a varlen into a list of Word8. +varlenSplit :: Word32 -> [Word8]+varlenSplit i | i < 0x80        = [fromIntegral i]+              | i < 0x4000      = [wise i 7, wise i 0]+              | i < 0x200000    = [wise i 14, wise i 7, wise i 0] +              | otherwise       = [wise i 21, wise i 14, wise i 7, wise i 0] +  where         +    wise m 0 = fromIntegral $ m .&. 0x7F+    wise m n = fromIntegral $ m `shiftR` n   .&.  0x7F  .|.  0x80;+    ++hexStr :: (Integral a) => a -> String+hexStr i = (showString "0x" . showHex i) "" 
+ demo/MidiRead.hs view
@@ -0,0 +1,233 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module      :  MidiRead+-- Copyright   :  (c) Stephen Tetley 2009+-- License     :  BSD3+--+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>+-- Stability   :  volatile+-- Portability :  to be determined.+--+-- A MIDI file parser. +--+--------------------------------------------------------------------------------++module MidiRead (+    -- * Read a MIDI file+    -- $readmididoc+    readMidi+  ) where++import MidiDatatypes++import Data.ParserCombinators.KangarooWriter++import Control.Applicative+import Control.Monad+import Data.Bits+import Data.Sequence ( Seq , (<|) )+import qualified Data.Sequence as S+import Data.Word++type MidiParser a = Kangaroo String a+++readMidi :: FilePath -> IO MidiFile  +readMidi filename = do +   (a,w) <- runKangaroo midiFile filename+   putStrLn w+   either error return a+    +{-++logMsg :: String -> MidiParser ()+logMsg = tell . ("\n" ++)++logPos :: String -> MidiParser a -> MidiParser a+logPos s p = do +  p1 <- position+  ans <- p+  p2 <- position+  logMsg $ unwords [show p1, s, show p2]+  return ans++logBound :: String -> MidiParser a -> MidiParser a+logBound s p = do +  b <- upperBound+  logMsg $ unwords [ s, "region boundary", show b ]+  p++-}++--------------------------------------------------------------------------------+-- +++    +midiFile :: MidiParser MidiFile  +midiFile = mprogress MidiFile trackCount header (countS `flip` track)+  where+    trackCount :: Header -> Int +    trackCount (Header _ n _) = fromIntegral n++header :: MidiParser Header  +header = Header <$> (assertString "MThd"  *> assertWord32 (6::Int) *> format)+                <*> word16be             <*> timeDivision ++countS :: Int -> MidiParser a -> MidiParser (Seq a)+countS = genericCount (<|) S.empty +++track :: MidiParser Track+track = liftM Track (trackHeader >>= getMessages)++trackHeader :: MidiParser Word32+trackHeader = assertString "MTrk" >> word32be++getMessages :: Word32 -> MidiParser (Seq Message)++getMessages i = alfineRelative 0 (fromIntegral i) messages+  where    +    messages = genericRunOn (<|) S.empty message++message :: MidiParser Message+message = pairA deltaTime (uncurry next =<< word8split)+  where  +    next code chan  +        | code == 0xF && chan == 0xF  = MetaEvent   <$> (word8 >>= metaEvent)         +        | code == 0xF                 = SystemEvent <$> systemEvent+        | code >= 0x8 && code <  0xF  = VoiceEvent  <$> voiceEvent code chan+        | otherwise                   = reportError $ unwords +                                          [ "unrecognized message "+                                          , hexStr ((code `shiftL` 4) + chan)+                                          , "code:"+                                          , hex2 code []+                                          , "chan:"+                                          , hex2 chan []+                                          ]++++deltaTime :: MidiParser Word32+deltaTime = getVarlen+   +voiceEvent :: Word8 -> Word8 -> MidiParser VoiceEvent+voiceEvent 0x8 ch = (NoteOff ch)        <$> word8 <*> word8+voiceEvent 0x9 ch = (NoteOn ch)         <$> word8 <*> word8+voiceEvent 0xA ch = (NoteAftertouch ch) <$> word8 <*> word8+voiceEvent 0xB ch = (Controller ch)     <$> word8 <*> word8+voiceEvent 0xC ch = (ProgramChange ch)  <$> word8 +voiceEvent 0xD ch = (ChanAftertouch ch) <$> word8 +voiceEvent z   _  = reportError $ "voiceEvent " ++ hexStr z +++++metaEvent :: Word8 -> MidiParser MetaEvent+metaEvent 0x00 = SequenceNumber   <$> (assertWord8 2     *> word16be)+metaEvent 0x01 = textEvent GENERIC_TEXT +metaEvent 0x02 = textEvent COPYRIGHT_NOTICE+metaEvent 0x03 = textEvent SEQUENCE_NAME+metaEvent 0x04 = textEvent INSTRUMENT_NAME+metaEvent 0x05 = textEvent LYRICS+metaEvent 0x06 = textEvent MARKER+metaEvent 0x07 = textEvent CUE_POINT+metaEvent 0x2F = EndOfTrack       <$   assertWord8 0  +metaEvent 0x51 = SetTempo         <$> (assertWord8 3     *> word24be)+metaEvent 0x54 = SMPTEOffset      <$> (assertWord8 5     *> word8) +                                  <*> word8             <*> word8+                                  <*> word8             <*> word8+metaEvent 0x58 = TimeSignature    <$> (assertWord8 4     *> word8)+                                  <*> word8             <*> word8 +                                  <*> word8 +metaEvent 0x59 = KeySignature     <$> (assertWord8 2     *> int8) +                                  <*> scale+metaEvent 0x7F = (uncurry SSME)   <$> getVarlenBytes     +metaEvent z    = reportError $ "unreconized meta-event " ++ hexStr z+++systemEvent :: MidiParser SystemEvent+systemEvent = (uncurry SysEx) <$> getVarlenBytes+                      +                          +format :: MidiParser HFormat+format = word16be >>= fn +  where +    fn 0 = return MF0+    fn 1 = return MF1+    fn 2 = return MF2+    fn z = reportError $ +              "getFormat - unrecognized file format " ++ hexStr z+        +timeDivision :: MidiParser TimeDivision+timeDivision = division <$> word16be+  where division i | i `testBit` 15 = FPS (i `clearBit` 15)+                   | otherwise      = TPB i+++scale :: MidiParser ScaleType+scale = word8 >>= fn +  where+    fn 0 = return MAJOR+    fn 1 = return MINOR+    fn z = reportError $ "scale expecting 0 or 1, got " ++ hexStr z+    +    +textEvent :: TextType -> MidiParser MetaEvent+textEvent ty = (TextEvent ty . snd) <$> getVarlenText++--------------------------------------------------------------------------------+-- helpers++{-+-- enumerate doesn't really seem worth it for scale etc... +enumerate :: Integral a => [ans] -> a -> String -> MidiParser ans+enumerate []     _ msg = reportError msg+enumerate (x:xs) i msg | i == 0    = return x+                       | otherwise = enumerate xs (i-1) msg++-}+++word24be   :: MidiParser Word32+word24be   = w32be 0 <$> word8 <*> word8 <*> word8+++word8split :: MidiParser (Word8,Word8) +word8split = split <$> word8 +  where+    split i = ((i .&. 0xF0) `shiftR` 4, i .&. 0x0F)++ +assertWord8 :: Word8 -> MidiParser Word8+assertWord8 i = postCheck word8 (==i) msg+  where +    msg = "assertWord8 - input did not match " ++ show i+             +assertWord32 :: Integral a => a -> MidiParser Word32+assertWord32 i = postCheck word32be ((==i) . fromIntegral) msg+  where+    msg = "assertWord32 - input did not match " ++ show i++assertString :: String -> MidiParser String+assertString s = postCheck (text $ length s) (==s) msg+  where+    msg = "assertString - input did not match " ++ s+++getVarlenText :: MidiParser (Word32,String)  +getVarlenText = countPrefixed getVarlen char++getVarlenBytes :: MidiParser (Word32,[Word8]) +getVarlenBytes = countPrefixed getVarlen word8+++getVarlen :: MidiParser Word32+getVarlen = buildWhile (`testBit` 7) merge merge 0 word8+  where+    merge i acc = (acc `shiftL` 7) + ((fromIntegral i) .&. 0x7F)+   ++
+ demo/MidiText.hs view
@@ -0,0 +1,160 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module      :  MidiText+-- Copyright   :  (c) Stephen Tetley 2009+-- License     :  BSD3+--+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>+-- Stability   :  volatile+-- Portability :  to be determined.+--+-- Pretty print a textual representaton of MIDI files. +--+--------------------------------------------------------------------------------+++module MidiText (+    -- * Print a readable text representation+    printMidi, ppMidiFile,+    +    ppHeader, ppMessage,+  +  ) where+++import MidiDatatypes++import qualified Data.Foldable as F+import Numeric+import Text.PrettyPrint.HughesPJ+++printMidi :: MidiFile -> IO ()+printMidi = putStrLn . render . ppMidiFile +++++integral :: Integral a => a -> Doc+integral = integer . fromIntegral++infixl 6 `hyph`, `dblhyph`++hyph :: Doc -> Doc -> Doc+x `hyph` y         = x <> text " - " <> y++dblhyph :: Doc -> Doc -> Doc+x `dblhyph` y         = x <> text " -- " <> y++ppHex :: Integral a => a -> Doc+ppHex i = text $ showHex i []++++ppMidiFile :: MidiFile -> Doc+ppMidiFile (MidiFile header tracks)  = ppHeader header $$ tdoc $$ empty+  where+    tdoc = fst $ F.foldl fn (empty,0) tracks+    fn (d,i) t = (d $$ prettyTrack t i, i+1)+      +ppHeader :: Header -> Doc+ppHeader (Header hformat ntrks td) = +    text "[Header]" <+> ppHFormat hformat +                    `hyph` (integral ntrks <+> text "tracks") +                    `hyph` ppTimeDivision td+  +ppHFormat :: HFormat -> Doc+ppHFormat MF0  = text "Type 0"+ppHFormat MF1  = text "Type 1"+ppHFormat MF2  = text "Type 2"+ +ppTimeDivision :: TimeDivision -> Doc+ppTimeDivision (FPS i)   = text "fps"   <+> integral i+ppTimeDivision (TPB i)   = text "ticks" <+> integral i++prettyTrack :: Track -> Int -> Doc+prettyTrack (Track se) i = +    brackets (text "Track" <+> int i) $$ (snd $ F.foldl fn (0,empty) se)+  where+    fn (gt,doc) msg@(dt,_)  = +      (gt+dt, doc $$ (padshow 8 (gt+dt) `hyph` ppMessage msg))    +++ppMessage :: Message -> Doc+ppMessage (dt,evt) = padshow 5 dt `hyph` ppEvent evt      +++ppEvent :: Event -> Doc+ppEvent (VoiceEvent e)   = ppVoiceEvent e+ppEvent (SystemEvent e)  = text "sys"  `dblhyph` ppSystemEvent e+ppEvent (MetaEvent e)    = text "meta" `dblhyph` ppMetaEvent e++padshow :: Show a => Int -> a -> Doc+padshow pad a = let s = show a ; dif = pad - length s in+    text $ replicate dif ' ' ++ s +                  +++fli :: (Integral a, Show a) => a -> Doc+fli = padshow 3++ppVoiceEvent :: VoiceEvent -> Doc+ppVoiceEvent (NoteOff ch nt vel)        = +    text "note-off" `hyph` fli ch `hyph` fli nt `hyph` fli vel+ +ppVoiceEvent (NoteOn ch nt vel)         =+    text "note-on " `hyph` fli ch `hyph` fli nt `hyph` fli vel+    +ppVoiceEvent (NoteAftertouch ch nt val) = +    text "note-after-touch" `hyph` fli ch `hyph` fli nt `hyph` fli val+    +ppVoiceEvent (Controller ch ty val)     = +    text "ctlr" `hyph` fli ch `hyph` fli ty `hyph` fli val+    +ppVoiceEvent (ProgramChange ch num)     = +    text "pc" `hyph` fli ch `hyph` fli num+    +ppVoiceEvent (ChanAftertouch ch val)    = +    text "chan-after-touch" `hyph` fli ch `hyph` fli val+      +ppVoiceEvent (PitchBend ch val)         = +    text "pitch-bend" `hyph` fli ch `hyph` fli val++  ++ppSystemEvent :: SystemEvent -> Doc+ppSystemEvent (SysEx _ _)    = text "sysex"   -- system exclusive event - length x data+ppSystemEvent (DataEvent i)  = text "data" <+> ppHex i++ppMetaEvent :: MetaEvent -> Doc+ppMetaEvent (TextEvent ty s)     = ppTextType ty `hyph` doubleQuotes (text s)+ppMetaEvent (SequenceNumber i)   = text "sequence-number" `hyph` integral i+ppMetaEvent (ChannelPrefix ch)   = text "channel-prefix"  `hyph` integral ch+ppMetaEvent (EndOfTrack)         = text "end-of-track"+ppMetaEvent (SetTempo mspqn)     = text "set-tempo" `hyph` integral mspqn  -- microseconds per quarter-note+ppMetaEvent (SMPTEOffset h m s f sf) = +    text "smpte" `hyph` fli h `hyph` fli m `hyph` fli s `hyph` fli f `hyph` fli sf+    +ppMetaEvent (TimeSignature n d m ns) = +    text "time-sig" `hyph` fli n `hyph` fli d `hyph` fli m `hyph` fli ns+    +ppMetaEvent (KeySignature i sc)  = +    text "key-sig" `hyph` integral i `hyph` ppScaleType sc+       +ppMetaEvent (SSME i _)           = text "ssme" `hyph` ppHex i <+> text "..."++ppScaleType :: ScaleType -> Doc+ppScaleType MAJOR  = text "major"+ppScaleType MINOR  = text "minor"+  +ppTextType :: TextType -> Doc+ppTextType GENERIC_TEXT         = text "generic-text" +ppTextType COPYRIGHT_NOTICE     = text "copyright-notice"  +ppTextType SEQUENCE_NAME        = text "sequence-name"+ppTextType INSTRUMENT_NAME      = text "instrument-name"+ppTextType LYRICS               = text "lyrics"+ppTextType MARKER               = text "marker"+ppTextType CUE_POINT            = text "cue-point"+  
+ demo/PrintMidi.hs view
@@ -0,0 +1,38 @@+{-# OPTIONS -Wall #-}+++-- shell> ghc --make PrintMIDI.hs +--+-- shell> runhaskell.exe PrintMIDI.hs bulgarian6.mid++++module Main where++import MidiRead+import MidiText+++import Control.Exception+import Prelude hiding (catch)++import System.Environment+import System.Exit+++main :: IO ()+main = do +  args <- getArgs+  case args of+    [path] -> process path+    _ ->  putStrLn "Usage: MidiPrint <filename>"++process :: FilePath -> IO ()+process filename = do+    ans <- catch (readMidi filename) exitHandle+    printMidi ans+  where+    exitHandle :: IOException -> IO a +    exitHandle e = putStrLn (show e) >> exitFailure++ 
+ kangaroo.cabal view
@@ -0,0 +1,72 @@+name:             kangaroo+version:          0.1.0+license:          BSD3+license-file:     LICENSE+copyright:        Stephen Tetley <stephen.tetley@gmail.com>+maintainer:       Stephen Tetley <stephen.tetley@gmail.com>+homepage:         http://code.google.com/p/copperbox/+category:         Parsing+synopsis:         Random access binary combinator parser.+description:+  Binary parsing with random access. The target file to be +  parsed is loaded into memory at the start (represented as+  an IOUArray Int Word8). Parsing proceeds by moving a cursor+  around, the array is left intact. This allows _jumping_+  inside the file and contrasts with other parser +  combinators that progress via consuming input.+  .+  \* Caveat \* - the above of course means that the target file +  is obliged to be small enough to fit into memory. +  .+  \*\* MAJOR CAVEAT \*\* - kangaroo is somewhat half-baked at the+  moment - the parsing machinery seems good but the combinators+  and primitive parsers need more work. I\'ve only released it on +  Hackage because I'm now using it with Hurdle which was already +  on Hackage.+  .+  Changelog:+  . +  0.1.0 First version+  +build-type:         Simple+stability:          half baked+cabal-version:      >= 1.2++extra-source-files:+  demo/MidiDatatypes.hs+  demo/MidiRead.hs+  demo/MidiText.hs+  demo/PrintMidi.hs+  test/Picklers.hs+++library+  hs-source-dirs:     src+  build-depends:      base < 5,+                      array >= 0.3.0.0 && < 0.4++  +  exposed-modules:+    Data.ParserCombinators.Kangaroo,+    Data.ParserCombinators.KangarooReader+    Data.ParserCombinators.KangarooRWS,+    Data.ParserCombinators.KangarooState,+    Data.ParserCombinators.KangarooWriter,+    Data.ParserCombinators.Kangaroo.Combinators,+    Data.ParserCombinators.Kangaroo.IEEEFloat,+    Data.ParserCombinators.Kangaroo.ParseMonad,+    Data.ParserCombinators.Kangaroo.Prim,+    Data.ParserCombinators.Kangaroo.Utils++  other-modules:+      +  extensions:+    ++  ghc-options:+  +  includes: +  ++  +  
+ src/Data/ParserCombinators/Kangaroo.hs view
@@ -0,0 +1,43 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module      :  Data.ParserCombinators.Kangaroo+-- Copyright   :  (c) Stephen Tetley 2009+-- License     :  BSD3+--+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>+-- Stability   :  highly unstable+-- Portability :  to be determined.+--+-- Binary parser combinators with random access+--+--------------------------------------------------------------------------------++module Data.ParserCombinators.Kangaroo +  (+    module Data.ParserCombinators.Kangaroo.Combinators+  , module Data.ParserCombinators.Kangaroo.ParseMonad+  , module Data.ParserCombinators.Kangaroo.Prim+  , module Data.ParserCombinators.Kangaroo.Utils+  , Kangaroo+  , runKangaroo+  , parse+  ) where++import Data.ParserCombinators.Kangaroo.Combinators+import Data.ParserCombinators.Kangaroo.ParseMonad hiding (+    getSt, putSt, modifySt ) -- TO SORT...+import Data.ParserCombinators.Kangaroo.Prim+import Data.ParserCombinators.Kangaroo.Utils hiding ( (<:>), oo, ooo, oooo )+++type Kangaroo a = GenKangaroo () a++runKangaroo :: Kangaroo a -> FilePath -> IO (Either ParseErr a)+runKangaroo p filename = runGenKangaroo p () filename >>= \(a,_) -> return a++-- jsut runKangaroo here+parse :: Kangaroo a -> FilePath -> IO (Either ParseErr a)+parse = runKangaroo +
+ src/Data/ParserCombinators/Kangaroo/Combinators.hs view
@@ -0,0 +1,192 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module      :  Data.ParserCombinators.Kangaroo.Combinators+-- Copyright   :  (c) Stephen Tetley 2009+-- License     :  BSD3+--+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>+-- Stability   :  highly unstable+-- Portability :  to be determined.+--+-- Combinators+--+--------------------------------------------------------------------------------++module Data.ParserCombinators.Kangaroo.Combinators +  ( +    satisfy+  , manyTill+  , genericManyTill+  , manyTillPC+  , genericManyTillPC+  , count+  , countPrefixed+  , genericCount+  , runOn+  , genericRunOn+  , postCheck+  , buildWhile+  , buildPrimitive++  ) where++import Data.ParserCombinators.Kangaroo.ParseMonad+import Data.ParserCombinators.Kangaroo.Utils++import Control.Applicative+import Data.Word++satisfy :: (Word8 -> Bool) -> GenKangaroo ust Word8+satisfy p = word8 >>= \x -> +    if p x then return x else reportError $ "satisfy"+++manyTill :: GenKangaroo ust a -> GenKangaroo ust b -> GenKangaroo ust [a]+manyTill = genericManyTill (:) [] ++++genericManyTill :: (a -> c -> c) -> c +                -> GenKangaroo ust a +                -> GenKangaroo ust b+                -> GenKangaroo ust c+genericManyTill op initial p end = opt end >>= \ans ->+    case ans of +      Just _  -> return initial+      Nothing -> op <$> p <*> genericManyTill op initial p end+++manyTillPC :: GenKangaroo ust a -> (a -> Bool) -> GenKangaroo ust ([a],a)+manyTillPC = genericManyTillPC (:) []++genericManyTillPC :: (a -> b -> b) -> b +                  -> GenKangaroo ust a +                  -> (a -> Bool) +                  -> GenKangaroo ust (b,a)+genericManyTillPC op initial p check =  p >>= \ans ->+  if check ans then do { (acc,end) <- genericManyTillPC op initial p check+                       ; return (ans `op` acc,end)+                       }+               else return (initial,ans) +   +count :: Int -> GenKangaroo ust a -> GenKangaroo ust [a]+count = genericCount (:) []+++-- Generalized version of count (like a fold)+genericCount :: (a -> b -> b) -> b -> Int +             -> GenKangaroo ust a +             -> GenKangaroo ust b+genericCount op initial i p +    | i <= 0    = pure initial+    | otherwise = op <$> p <*> genericCount op initial (i-1) p++countPrefixed :: Integral i +              => GenKangaroo ust i -> GenKangaroo ust a -> GenKangaroo ust (i,[a]) +countPrefixed plen p = plen >>= \i -> +    count (fromIntegral i) p >>= \ans -> return (i,ans)+++runOn :: GenKangaroo ust a -> GenKangaroo ust [a]+runOn p = atEnd >>= \end -> if end then return [] else p <:> runOn p+++genericRunOn :: (a -> b -> b) -> b -> GenKangaroo ust a -> GenKangaroo ust b+genericRunOn op initial p = atEnd >>= \end -> +   if end then return initial else op <$> p <*> genericRunOn op initial p++++-- | Apply parse then apply the check, if the check fails report+-- the error message. +postCheck :: GenKangaroo ust a -> (a -> Bool) -> String -> GenKangaroo ust a+postCheck p check msg = p >>= \ans -> +    if check ans then return ans else reportError msg+++-- | Build a value by while the test holds. When the test fails +-- the position is not backtracked, instead we use the \"failing\"+-- element with @lastOp@ potentially still building the value +-- with it.+-- +buildWhile :: (a -> Bool) +           -> (a -> b -> b) +           -> (a -> b -> b) +           -> b +           -> GenKangaroo ust a +           -> GenKangaroo ust b+buildWhile test op lastOp initial p = step where+    step = p >>= \ans -> +      if test ans then (step >>= \acc -> return $ ans `op` acc)+                  else (return $ ans `lastOp` initial)++-- building on Word8\'s, progress is checked - only elements that +-- match the check are consumed up the the max count.+buildPrimitive :: Int +               -> (Word8 -> Bool) +               -> (Word8 -> b -> b) +               -> b +               -> GenKangaroo ust b+buildPrimitive maxc check op initial | maxc <= 0 = return initial+                                     | otherwise = step 0 initial+  where+    step i a | i >= maxc = return a+             | otherwise = checkWord8 check >>= \ans -> case ans of +                             Nothing -> return a +                             Just x  -> step (i+1) (x `op` a)++ ++-- Can't rely on Alternative as Kangaroo doesn't support it.+{-++choice :: Alternative f => [f a] -> f a+choice = foldr (<|>) empty +          +between :: Applicative f => f open -> f close -> f a -> f a+between o c a = o *> a <* c++          +option :: Alternative f => a -> f a -> f a+option x p          = p <|> pure x++optionMaybe :: Alternative f => f a -> f (Maybe a)+optionMaybe = optional++-- aka Parsecs /optional/+optionUnit :: Alternative f => f a -> f ()+optionUnit p = () <$ p <|> pure ()++skipMany1 :: Alternative f => f a -> f ()+skipMany1 p = p *> skipMany p++skipMany :: Alternative f => f a -> f ()+skipMany p = many_p+  where many_p = some_p <|> pure ()+        some_p = p       *> many_p++-- | @many1@ an alias for @some@. +many1 :: Alternative f => f a -> f [a]+many1 = some++sepBy :: Alternative f => f a -> f b -> f [a]+sepBy p sep = sepBy1 p sep <|> pure []++sepBy1 :: Alternative f => f a -> f b -> f [a]+sepBy1 p sep = p <:> step where+    step = (sep *> p) <:> step <|> pure []++sepEndBy :: Alternative f => f a -> f b -> f [a]+sepEndBy p sep = sepEndBy1 p sep <|> pure []++sepEndBy1 :: Alternative f => f a -> f b -> f [a]+sepEndBy1 p sep = (p <* sep) <:> step where+    step = (p <* sep) <:> step <|> pure []+    +manyTill :: Alternative f => f a -> f b -> f [a]+manyTill p end = step <|> pure [] where+    step = p <:> (step <|> (pure [] <$> end))++-}
+ src/Data/ParserCombinators/Kangaroo/IEEEFloat.hs view
@@ -0,0 +1,118 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module      :  Data.ParserCombinators.Kangaroo.IEEEFloat+-- Copyright   :  (c) Stephen Tetley 2009+-- License     :  BSD3+--+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>+-- Stability   :  highly unstable+-- Portability :  to be determined.+--+-- IEEE floats (single precision so far...)+--+--------------------------------------------------------------------------------++module Data.ParserCombinators.Kangaroo.IEEEFloat where++import Data.ParserCombinators.Kangaroo.Utils+++import Data.Bits+import Data.Char+import Data.Word+import Numeric+++const_B :: Int+const_B = 127+++printBin :: (Fractional a,Ord a) => a -> ShowS+printBin a = +    f s . showChar ' ' . f t . showChar ' ' . f u . showChar ' ' . f v+  where+    f = showIntAtBase 2 (chr . (48+))+    (s,t,u,v) = packIEEESingle a +  +toAndFro :: (Fractional a, Ord a) => a -> a+toAndFro a = let (s,t,u,v) = packIEEESingle a in unpackIEEESingle s t u v++unpackIEEESingle :: Fractional a => Word8 -> Word8 -> Word8 -> Word8 -> a+unpackIEEESingle b24_31 b16_23 b8_15 b0_7 =  sign $ fract * (2 ^^ expo)+  where+    sign  = if b24_31 `testBit` 7 then negate else id+  +    expo  = exponent' b24_31 b16_23+  +    fract = fraction b16_23 b8_15 b0_7+++exponent' :: Word8  -> Word8 -> Int+exponent' a b = (a' `shiftL` 1) + (b' `shiftR` 7) - 127+  where+    a' = fromIntegral $ (a .&. 0x7f) +    b' = fromIntegral $ (b .&. 0x80)+++iPow :: Fractional a => a -> Integer -> a+iPow = (^^)++fraction :: Fractional a => Word8 -> Word8 -> Word8 -> a+fraction b16_24 b8_15 b0_7 = 1.0 + ((fromIntegral frac) / (2 `iPow` 23))+  where+   frac :: Int +   frac = (shiftL16 (b16_24 .&. 0x7f)) + (shiftL8 b8_15) + fromIntegral b0_7+    +++packIEEESingle :: (Fractional a,Ord a) => a -> (Word8,Word8,Word8,Word8)+packIEEESingle a = (flipSign b24_31, exp_part+mant_part, b8_15, b0_7)+  where+    k     = findPosExpo $ abs a+    e     = k + const_B+    halfa = (abs a) / (2 `iPow` fromIntegral k)+    f     = expand $ halfa - 1+    +    (b24_31, exp_part)          = expoWords e+    (mant_part,b8_15, b0_7)     = mantWords f+    +    flipSign = if a > 0 then id else (`setBit` 7)+++findPosExpo :: (Fractional a, Ord a) => a -> Int+findPosExpo r | r <= 0    = 0 +              | otherwise = step r 1+  where+    step r' k | r <= fromIntegral (2::Int) ^^ k = k-1+              | otherwise                       = step r' (k+1)+++expand :: (Fractional a, Ord a) => a -> Word32+expand n = (`shiftR` 9) $ step n 0 id+  where+    step x ix f | x <= 0    = f (0::Word32) +                | otherwise = let y = 1 / (2 ^^ (ix+1))+                              in if x >= y +                                 then step (x-y) (ix+1) (f . (`setBit` (31-ix)))+                                 else step x (ix+1) f++++-- 7 bits left, 1 bit right+expoWords :: Int -> (Word8,Word8)+expoWords n = (left, right)+  where+    right = if n `testBit` 0 then 128 else 0+    left  = fromIntegral $ n `shiftR` 1+++mantWords :: Word32 -> (Word8,Word8,Word8)+mantWords x = (a,b,c)+  where+    c = fromIntegral $ x .&. 0xff+    b = fromIntegral $ (`shiftR` 8)  $ x .&. 0xff00+    a = fromIntegral $ (`shiftR` 16) $ x .&. 0xff0000+    +
+ src/Data/ParserCombinators/Kangaroo/ParseMonad.hs view
@@ -0,0 +1,418 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module      :  Data.ParserCombinators.Kangaroo.ParseMonad+-- Copyright   :  (c) Stephen Tetley 2009+-- License     :  BSD3+--+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>+-- Stability   :  highly unstable+-- Portability :  to be determined.+--+-- Random access parse monad +--+--------------------------------------------------------------------------------++module Data.ParserCombinators.Kangaroo.ParseMonad +  (+    GenKangaroo+  , ParseErr+  +  , getSt+  , putSt+  , modifySt+  +  , getUserSt+  , putUserSt+  , modifyUserSt++  , askEnv+  , throwErr+  , runGenKangaroo++  , reportError+  , substError+  , word8+  , checkWord8+  , opt +  , position+  , regionEnd+  , atEnd+  , lengthRemaining++  -- * Parse within a /region/.+  , dalpunto+  , dalpuntoRelative+  , advanceDalpunto+  , advanceDalpuntoAbsolute++  , alfine+  , alfineRelative+  , restrictAlfine++  , alfermata+  , alfermataRelative+  , advanceAlfermata+  , advanceAlfermataAbsolute+  , restrictAlfermata +   +  ) where++import Control.Applicative+import Control.Monad+import Data.Array.IO+import Data.Word+import Numeric+import System.IO++type ParseErr = String++type ImageData = IOUArray Int Word8   -- Is Int big enough for index?++data ArrIx = ArrIx { arr_ix_ptr :: !Int, arr_ix_end :: !Int }+  deriving (Eq,Show) ++type St    = ArrIx+type Env   = ImageData+  ++-- Kangaroo is not a transformer as IO is always at the +-- \'bottom\' of the effect stack. Like the original Parsec it is+-- parametric on user state (refered to as ust).+--+newtype GenKangaroo ust a = GenKangaroo { +          getGenKangaroo :: Env -> St -> ust -> IO (Either ParseErr a, St, ust) }+          ++fmapKang :: (a -> b) -> GenKangaroo ust a -> GenKangaroo ust b+fmapKang f (GenKangaroo x) = GenKangaroo $ \env st ust -> +    x env st ust `bindIO` \(a,st',ust') -> return (fmap f a, st', ust')+++instance Functor (GenKangaroo ust) where+    fmap = fmapKang++++returnIO :: a -> IO a+returnIO = return++infixl 1 `bindIO`++bindIO :: IO a -> (a -> IO b) -> IO b+bindIO = (>>=)+++returnKang :: a -> GenKangaroo st a+returnKang a = GenKangaroo $ \_ st ust -> returnIO (Right a, st, ust)++infixl 1 `bindKang`++bindKang :: GenKangaroo ust a -> (a -> GenKangaroo ust b) -> GenKangaroo ust b+(GenKangaroo x) `bindKang` f = GenKangaroo $ \env st ust -> +    x env st ust `bindIO` \(ans, st', ust') ->+        case ans of Left err -> returnIO (Left err,st',ust')+                    Right a  -> getGenKangaroo (f a) env st' ust'+ ++++instance Monad (GenKangaroo ust) where+  return = returnKang+  (>>=)  = bindKang+++instance Applicative (GenKangaroo ust) where+  pure = return+  (<*>) = ap++-- I don't think Kangaroo has natural implementations of +-- Alternative or MonadPlus.+-- My 'proposition' is that the sort of parsing that Kangaroo +-- intends to provide you always now want you want hence there +-- is no inbuilt backtracking or support for list-of-successes.+++getSt :: GenKangaroo ust St+getSt = GenKangaroo $ \_ st ust -> return (Right st, st, ust)++putSt :: St -> GenKangaroo ust ()+putSt st = GenKangaroo $ \_ _ ust -> return (Right (), st, ust)++modifySt :: (St -> St) -> GenKangaroo ust ()+modifySt f = GenKangaroo $ \_ st ust -> return (Right (), f st, ust)+++getUserSt :: GenKangaroo ust ust+getUserSt = GenKangaroo $ \_ st ust -> return (Right ust, st, ust)++putUserSt :: ust -> GenKangaroo ust ()+putUserSt ust = GenKangaroo $ \_ st _ -> return (Right (), st, ust)++modifyUserSt :: (ust -> ust) -> GenKangaroo ust ()+modifyUserSt f = GenKangaroo $ \_ st ust -> return (Right (), st, f ust)++++askEnv :: GenKangaroo ust Env+askEnv = GenKangaroo $ \env st ust -> return (Right env, st, ust)+++throwErr :: String -> GenKangaroo ust a+throwErr msg = GenKangaroo $ \_ st ust -> return (Left msg, st, ust)++liftIOAction :: IO a -> GenKangaroo ust a+liftIOAction ma = GenKangaroo $ \_ st ust -> +    ma >>= \a -> return (Right a, st, ust) +++++runGenKangaroo :: GenKangaroo ust a -> ust -> FilePath -> IO (Either ParseErr a,ust)+runGenKangaroo p user_state filename = +    withBinaryFile filename ReadMode $ \ handle -> +      do { sz          <- hFileSize handle+         ; arr         <- newArray_ (0,fromIntegral $ sz-1)+         ; rsz         <- hGetArray handle arr  (fromIntegral sz)+         ; (ans,_,ust) <- runP p rsz arr+         ; return (ans,ust)   +         }+  where +    runP (GenKangaroo x) upper arr = x arr (ArrIx 0 (upper-1)) user_state++++--------------------------------------------------------------------------------+-- Helpers++modifyIx :: (Int -> Int) -> GenKangaroo ust ()+modifyIx f = modifySt $ \(ArrIx ix end) -> ArrIx (f ix) end++--------------------------------------------------------------------------------+-- +++reportError :: ParseErr -> GenKangaroo ust a+reportError s = do +    posn <- getSt+    throwErr $ s ++ posStr posn+  where+    posStr (ArrIx pos end) = concat [ " absolute position "+                                    , show pos+                                    , " (0x" +                                    , showHex pos []+                                    , "), region length "+                                    , show end+                                    ]+++substError :: GenKangaroo ust a -> ParseErr -> GenKangaroo ust a+substError p msg = GenKangaroo $ \env st ust -> +    (getGenKangaroo p) env st ust >>= \ ans -> +      case ans of+        (Left _, st', ust')  -> return (Left msg, st', ust')+        okay                 -> return okay++   +word8 :: GenKangaroo ust Word8+word8 = do+    (ArrIx ix end)   <- getSt+    when (ix>end)    (reportError "word8")   -- test emphatically is (>) !+    arr              <- askEnv+    a                <- liftIOAction $ readArray arr ix+    putSt $ ArrIx (ix+1) end+    return a+++checkWord8 :: (Word8 -> Bool) -> GenKangaroo ust (Maybe Word8)+checkWord8 check = word8 >>= \ans ->+    if check ans then return $ Just ans+                 else modifyIx (subtract 1) >> return Nothing++++-- no 'try' in Kangaroo... +-- opt is the nearest to it, opt backtracks the cursor onm failure.+opt :: GenKangaroo ust a -> GenKangaroo ust (Maybe a)+opt p = GenKangaroo $ \env st ust -> (getGenKangaroo p) env st ust >>= \ ans -> +    case ans of+      (Left _, _, ust')    -> return (Right Nothing, st, ust')+      (Right a, st', ust') -> return (Right $ Just a, st', ust')++position :: GenKangaroo ust Int+position = liftM arr_ix_ptr getSt++regionEnd :: GenKangaroo ust Int+regionEnd = liftM arr_ix_end getSt++++atEnd :: GenKangaroo ust Bool+atEnd = getSt >>= \(ArrIx ix end) -> return $ ix >= end++lengthRemaining :: GenKangaroo ust Int+lengthRemaining = getSt >>= \(ArrIx ix end) -> +   let rest = end - ix in if rest < 0 then return 0 else return rest++--------------------------------------------------------------------------------+-- The important ones parsing within a /region/ ...++-- Three useful final positions +--+-- 1. dalpunto  - 'from the point'      +-- - Run the parser within a region and return to where you came+--   from.+--+-- 2. alfine    - 'to the end'     +-- - Run the parser within a region and jump to the right-end of +--   the region after the parse.+--+-- 3. alfermata - 'to the stop'    +-- - Run the parser within a region, the cursor remains wherever +--   the parse finished.+--+++++assertSubsetRegion :: String -> Int -> Int -> GenKangaroo ust ()+assertSubsetRegion fun_name start end = +    getSt >>= \(ArrIx pos endpos) -> step pos endpos +  where+    step pos endpos | start < pos   = reportError $ +                                        backtrackErr start pos fun_name+                    | end > endpos  = reportError $ +                                        tooFarErr end endpos fun_name+                    | otherwise     = return ()+++backtrackErr :: Int -> Int -> String -> String  +backtrackErr new_pos old_pos fun_name = concat+    [ "Kangaroo.ParseMonad."+    , fun_name+    , " - cannot backtrack, " +    , show new_pos +    , " is before current position " +    , show old_pos+    ]++tooFarErr :: Int -> Int -> String -> String+tooFarErr new_end old_end fun_name = concat+    [ "Kangaroo.ParseMonad."+    , fun_name +    , " - new end point " +    , show new_end +    , " extends beyond the end of the current region "+    , show old_end+    ]+++-- Parser inside the supplied region, afterwards restore the+-- end of the /outer/ region and return to the initial position.+--+dalpuntoP :: String -> Int -> Int +                 -> GenKangaroo ust a +                 -> GenKangaroo ust a+dalpuntoP fun_name start end p = do+    st <- getSt +    assertSubsetRegion fun_name start end+    putSt $ ArrIx start end+    ans <- p+    putSt st+    return ans+    +++-- | return to the start position - start x length+dalpunto :: Int -> Int -> GenKangaroo ust a -> GenKangaroo ust a+dalpunto = dalpuntoP "dalpunto"+++-- | return to the start position - displacement x length+dalpuntoRelative :: Int -> Int -> GenKangaroo ust a -> GenKangaroo ust a+dalpuntoRelative disp len p = getSt >>= \(ArrIx pos _) ->+    dalpuntoP "dalpuntoRelative" (pos+disp) (pos+disp+len-1) p++-- | Advance the current position by the supplied distance.+advanceDalpunto :: Int -> GenKangaroo ust p -> GenKangaroo ust p+advanceDalpunto i p = getSt >>= \(ArrIx pos end) -> +    dalpuntoP "advanceDalpunto" (pos+i) end p++-- | Advance the current position to the supplied (absolute) +-- position.+advanceDalpuntoAbsolute :: Int -> GenKangaroo ust p -> GenKangaroo ust p+advanceDalpuntoAbsolute i p = getSt >>= \(ArrIx _ end) -> +    dalpuntoP "advanceDalpuntoAbsolute" i end p+++-- Parse inside the supplied region, afterwards go to the end of+-- the supplied region and restore the end of the /outer/ region.+--+alfineP :: String -> Int -> Int -> GenKangaroo ust a -> GenKangaroo ust a+alfineP fun_name start end p = do+    ArrIx _ prev_end <- getSt +    assertSubsetRegion fun_name start end+    putSt $ ArrIx start end+    ans <- p+    putSt $ ArrIx (end+1) prev_end+    return ans+++-- | alfine - parse to the end+alfine :: Int -> Int -> GenKangaroo ust a -> GenKangaroo ust a+alfine = alfineP "alfine"++-- | finish at the right of the region - displacement x length+alfineRelative :: Int -> Int -> GenKangaroo ust a -> GenKangaroo ust a+alfineRelative disp len p = getSt >>= \(ArrIx pos _) ->+    alfineP "alfineRelative" (pos+disp) (pos+disp+len-1) p+++restrictAlfine :: Int -> GenKangaroo ust p -> GenKangaroo ust p+restrictAlfine dist_to_end p = getSt >>= \(ArrIx pos _) -> +    alfineP "restrictAlfine" pos (pos + dist_to_end) p+    +++-- Parser inside the supplied region, afterwards restore the end +-- of the /outer/region but keep the cursor in at the current +-- position.+--+alfermataP :: String -> Int -> Int -> GenKangaroo ust a -> GenKangaroo ust a+alfermataP fun_name start end p = do+    ArrIx _ prev_end    <- getSt +    assertSubsetRegion fun_name start end+    putSt $ ArrIx start end+    ans <- p+    ArrIx curr_pos _    <- getSt+    putSt $ ArrIx curr_pos prev_end +    return ans+ +-- | alfermata - parse to the /sign/ i.e. wherever the supplied +-- parser stops within the supplied region. At the end of the+-- parse restore the outer region.+--+alfermata :: Int -> Int -> GenKangaroo ust a -> GenKangaroo ust a+alfermata = alfermataP "alfermata"+++alfermataRelative :: Int -> Int -> GenKangaroo ust a -> GenKangaroo ust a+alfermataRelative disp len p = getSt >>= \(ArrIx pos _) ->+    alfermataP "alfermataRel" (pos+disp) (pos+disp+len-1) p+++-- | Advance the current position by the supplied distance.+advanceAlfermata :: Int -> GenKangaroo ust p -> GenKangaroo ust p+advanceAlfermata i p = getSt >>= \(ArrIx pos end) -> +    alfermataP "advanceAlfermata" (pos+i) end p++-- | Advance the current position to the supplied (absolute) +-- position.+advanceAlfermataAbsolute :: Int -> GenKangaroo ust p -> GenKangaroo ust p+advanceAlfermataAbsolute i p = getSt >>= \(ArrIx _ end) -> +    alfermataP "advanceAlfermataAbsolute" i end p++restrictAlfermata :: Int -> GenKangaroo ust p -> GenKangaroo ust p+restrictAlfermata dist_to_end p = getSt >>= \(ArrIx pos _) ->+    alfermataP "restrictAlfermata" pos (pos + dist_to_end) p
+ src/Data/ParserCombinators/Kangaroo/Prim.hs view
@@ -0,0 +1,113 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module      :  Data.ParserCombinators.Kangaroo.Prim+-- Copyright   :  (c) Stephen Tetley 2009+-- License     :  BSD3+--+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>+-- Stability   :  highly unstable+-- Portability :  to be determined.+--+-- Primitive parsers - charactors, numbers...+--+--------------------------------------------------------------------------------++module Data.ParserCombinators.Kangaroo.Prim +  (+    cstring+  , w8Zero+  , getBytes+  , char+  , text+  , int8+  , word16be+  , word32be+  , word64be  +  , word16le+  , word32le+  , int16be+  , int32be+  , int16le+  , int32le++  , ieeeFloatSP++  ) where++import Data.ParserCombinators.Kangaroo.Combinators+import Data.ParserCombinators.Kangaroo.IEEEFloat+import Data.ParserCombinators.Kangaroo.ParseMonad+import Data.ParserCombinators.Kangaroo.Utils++import Control.Applicative+import Data.Char+import Data.Int+import Data.Word+++-- | Read a null-terminated string+cstring :: GenKangaroo ust String+cstring = manyTill char w8Zero+++w8Zero :: GenKangaroo ust Word8+w8Zero = satisfy (==0)++getBytes :: Integral a => a -> GenKangaroo ust [Word8]+getBytes i = count (fromIntegral i) word8++char :: GenKangaroo ust Char+char = (chr . fromIntegral) <$> word8 ++++text :: Int -> GenKangaroo ust String+text = count `flip` char++++int8 :: GenKangaroo ust Int8+int8 = (fromIntegral . unwrap) <$> word8+  where+    unwrap :: Word8 -> Int+    unwrap i | i > 128   = (fromIntegral i) - 256+             | otherwise = fromIntegral i++word16be   :: GenKangaroo ust Word16+word16be   = w16be     <$> word8 <*> word8  ++word32be   :: GenKangaroo ust Word32+word32be   = w32be     <$> word8 <*> word8 <*> word8 <*> word8++word64be   :: GenKangaroo ust Word64+word64be   = w64be <$> word8 <*> word8 <*> word8 <*> word8+                   <*> word8 <*> word8 <*> word8 <*> word8++word16le   :: GenKangaroo ust Word16+word16le   = w16le     <$> word8 <*> word8  ++word32le   :: GenKangaroo ust Word32+word32le   = w32le     <$> word8 <*> word8 <*> word8 <*> word8+++int16be   :: GenKangaroo ust Int16+int16be   = i16be <$> word8 <*> word8+  +int32be   :: GenKangaroo ust Int32+int32be   = i32be <$> word8 <*> word8 <*> word8 <*> word8++                         +int16le   :: GenKangaroo ust Int16+int16le   = i16le <$> word8 <*> word8+                         +int32le   :: GenKangaroo ust Int32+int32le   = i32le <$> word8 <*> word8 <*> word8 <*> word8+++ieeeFloatSP :: Fractional a => GenKangaroo ust a+ieeeFloatSP = unpackIEEESingle <$> word8 <*> word8 <*> word8 <*> word8+++
+ src/Data/ParserCombinators/Kangaroo/Utils.hs view
@@ -0,0 +1,195 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module      :  Data.ParserCombinators.Kangaroo.Utils+-- Copyright   :  (c) Stephen Tetley 2009+-- License     :  BSD3+--+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>+-- Stability   :  highly unstable+-- Portability :  to be determined.+--+-- Utils...+--+--------------------------------------------------------------------------------++module Data.ParserCombinators.Kangaroo.Utils +  ( +    (<:>)+  , pairA+  , mprogress++  -- * Specs+  , oo+  , ooo+  , oooo++  -- * numbers from Word8 +  , w16be+  , w32be++  , w16le+  , w32le+  , w64be++  , i16be+  , i32be++  , i16le+  , i32le+  +  , shiftL8+  , shiftL16+  , shiftL24+  , shiftL32+  , shiftL40+  , shiftL48+  , shiftL56+  +  -- * Hex printing+  , hex2+  , hex4+  , hex8+  ++  ) where+++import Control.Applicative++import Data.Bits+import Data.Int+import Data.Word+import Numeric++infixr 5 <:>++-- | applicative cons+(<:>) :: Applicative f => f a -> f [a] -> f [a]+(<:>) p1 p2 = (:) <$> p1 <*> p2+++    ++pairA :: Applicative f => f a -> f b -> f (a,b)+pairA fa fb = (,) <$> fa <*> fb++-- needs renaming...+mprogress :: Monad m => (a -> c -> d) -> (a -> b) -> m a -> (b -> m c) -> m d+mprogress comb f ma mb = ma >>= \a -> mb (f a) >>= \b -> return $ comb a b++-- specs - defined in my package data-aviary but defined here to +-- avoid a dependency++-- | Compose an arity 1 function with an arity 2 function.+-- B1 - blackbird+oo :: (c -> d) -> (a -> b -> c) -> a -> b -> d+oo f g = (f .) . g++-- | Compose an arity 1 function with an arity 3 function.+-- B2 - bunting+ooo :: (d -> e) -> (a -> b -> c -> d) -> a -> b -> c -> e+ooo f g = ((f .) .) . g++-- | Compose an arity 1 function with an arity 4 function.+oooo :: (e -> f) -> (a -> b -> c -> d -> e) -> a -> b -> c -> d -> f+oooo f g = (((f .) .) .) . g  ++++--------------------------------------------------------------------------------++++w16le :: Word8 -> Word8 -> Word16+w16le a b = fromIntegral a + (shiftL8 b)++w32le :: Word8 -> Word8 -> Word8 -> Word8 -> Word32+w32le a b c d = fromIntegral a + (shiftL8 b) + (shiftL16 c) + (shiftL24 d)      +++-- Woah! The integer ones don't look right - what about the sign?++i16le :: Word8 -> Word8 -> Int16+i16le a b = fromIntegral $ w16le a b+              +i32le :: Word8 -> Word8 -> Word8 -> Word8 -> Int32+i32le a b c d  = fromIntegral $ w32le a b c d                +++i16be :: Word8 -> Word8 -> Int16+i16be a b = fromIntegral $ w16be a b+                            +i32be :: Word8 -> Word8 -> Word8 -> Word8 -> Int32+i32be a b c d = fromIntegral $ w32be a b c d+++w16be :: Word8 -> Word8 -> Word16+w16be a b = (shiftL8 a) + fromIntegral b+     +            +w32be :: Word8 -> Word8 -> Word8 -> Word8 -> Word32+w32be a b c d = (shiftL24 a) + (shiftL16 b) + (shiftL8 c) + fromIntegral d++-- To do...+w64be :: Word8 -> Word8 -> Word8 -> Word8 -> +           Word8 -> Word8 -> Word8 -> Word8 -> Word64+w64be a b c d e f g h = a' + b' + c' + d' + e' + f' + g' + h' where+    a' = (fromIntegral a) `shiftL` 56+    b' = (fromIntegral b) `shiftL` 48+    c' = (fromIntegral c) `shiftL` 40+    d' = (fromIntegral d) `shiftL` 32+    e' = (fromIntegral e) `shiftL` 24+    f' = (fromIntegral f) `shiftL` 16+    g' = (fromIntegral g) `shiftL` 8+    h' = (fromIntegral h) +++shiftL8 :: (Bits b, Integral b) => Word8 -> b+shiftL8 = (`shiftL` 8) . fromIntegral++shiftL16 :: (Bits b, Integral b) => Word8 -> b+shiftL16 = (`shiftL` 16) . fromIntegral++shiftL24 :: (Bits b, Integral b) => Word8 -> b+shiftL24 = (`shiftL` 24) . fromIntegral++shiftL32 :: (Bits b, Integral b) => Word8 -> b+shiftL32 = (`shiftL` 32) . fromIntegral++shiftL40 :: (Bits b, Integral b) => Word8 -> b+shiftL40 = (`shiftL` 40) . fromIntegral++shiftL48 :: (Bits b, Integral b) => Word8 -> b+shiftL48 = (`shiftL` 48) . fromIntegral++shiftL56 :: (Bits b, Integral b) => Word8 -> b+shiftL56 = (`shiftL` 56) . fromIntegral+++++hex2 :: Integral a => a -> ShowS+hex2 a | a < 0      = showString "-ve"+       | a < 0x10   = showString "0x0" . showHex a+       | otherwise  = showString "0x"  . showHex a +++hex4 :: Integral a => a -> ShowS+hex4 a | a < 0      = showString "-ve"+       | a < 0x10   = showString "0x000" . showHex a+       | a < 0x100  = showString "0x00"  . showHex a +       | a < 0x1000 = showString "0x0"   . showHex a +       | otherwise  = showString "0x"    . showHex a ++hex8 :: Integral a => a -> ShowS+hex8 a | a < 0          = showString "-ve"+       | a < 0x10       = showString "0x0000000" . showHex a+       | a < 0x100      = showString "0x000000"  . showHex a +       | a < 0x1000     = showString "0x00000"   . showHex a +       | a < 0x10000    = showString "0x0000"    . showHex a +       | a < 0x100000   = showString "0x000"     . showHex a +       | a < 0x1000000  = showString "0x00"      . showHex a +       | a < 0x10000000 = showString "0x0"       . showHex a +       | otherwise      = showString "0x"        . showHex a 
+ src/Data/ParserCombinators/KangarooRWS.hs view
@@ -0,0 +1,112 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module      :  Data.ParserCombinators.KangarooRWS+-- Copyright   :  (c) Stephen Tetley 2009+-- License     :  BSD3+--+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>+-- Stability   :  highly unstable+-- Portability :  to be determined.+--+-- Kangaroo parse monad with user env, logging and state.+--+--------------------------------------------------------------------------------++module Data.ParserCombinators.KangarooRWS+  (+    module Data.ParserCombinators.Kangaroo.Combinators+  , module Data.ParserCombinators.Kangaroo.ParseMonad+  , module Data.ParserCombinators.Kangaroo.Prim+  , module Data.ParserCombinators.Kangaroo.Utils+  , Kangaroo+  , parse+  , runKangaroo+  , evalKangaroo+  , execKangaroo+  , put+  , get+  , modify+  , gets+  , tell+  , ask++  ) where++import Data.ParserCombinators.Kangaroo.Combinators+import Data.ParserCombinators.Kangaroo.ParseMonad+import Data.ParserCombinators.Kangaroo.Prim+import Data.ParserCombinators.Kangaroo.Utils hiding ( (<:>), oo, ooo, oooo )+import qualified Data.ParserCombinators.Kangaroo.Utils as Specs++import Control.Monad+import Data.Monoid++type Kangaroo r w st a = GenKangaroo (r,w,st) a+++values :: (a,(r,w,st)) -> (a,w,st)+values (ans,(_,w,ust)) = (ans,w,ust)+++state3 :: (r,w,st) -> st+state3 (_,_,st) = st++env3 :: (r,w,st) -> r+env3 (r,_,_) = r++parse :: Monoid w +      => Kangaroo r w st a +      -> r +      -> st +      -> FilePath +      -> IO (Either ParseErr a)+parse = liftM fst `Specs.oooo` evalKangaroo ++runKangaroo :: Monoid w +            => Kangaroo r w st a +            -> r +            -> st +            -> FilePath +            -> IO (Either ParseErr a,w,st)+runKangaroo p env st filename = +    liftM values $ runGenKangaroo p (env,mempty,st) filename++-- answer, no state+evalKangaroo :: Monoid w +             => Kangaroo r w st a +             -> r +             -> st +             -> FilePath +             -> IO (Either ParseErr a,w)+evalKangaroo = liftM fn `Specs.oooo` runKangaroo where+    fn (a,w,_) = (a,w)++-- state, no answer+execKangaroo :: Monoid w +             => Kangaroo r w st a +             -> r +             -> st +             -> FilePath +             -> IO st+execKangaroo = liftM state3 `Specs.oooo` runKangaroo + ++put :: st -> Kangaroo r w st ()+put st = getUserSt >>= \(r,w,_) -> putUserSt (r,w,st)++get :: Kangaroo r w st st+get = liftM state3 getUserSt ++modify :: (st -> st) -> Kangaroo r w st ()+modify f = getUserSt >>= \(r,w,st) -> putUserSt (r,w,f st)++gets :: (st -> a) -> Kangaroo r w st a+gets f = liftM f get++tell :: Monoid w => w -> Kangaroo r w st ()+tell s = getUserSt >>= \(r,w,st) -> putUserSt (r,w `mappend` s,st)++ask :: Kangaroo r w st r+ask = liftM env3 getUserSt
+ src/Data/ParserCombinators/KangarooReader.hs view
@@ -0,0 +1,53 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module      :  Data.ParserCombinators.KangarooReader+-- Copyright   :  (c) Stephen Tetley 2009+-- License     :  BSD3+--+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>+-- Stability   :  highly unstable+-- Portability :  to be determined.+--+-- Kangaroo parse monad with env.+--+--------------------------------------------------------------------------------++module Data.ParserCombinators.KangarooReader+  (+    module Data.ParserCombinators.Kangaroo.Combinators+  , module Data.ParserCombinators.Kangaroo.ParseMonad+  , module Data.ParserCombinators.Kangaroo.Prim+  , module Data.ParserCombinators.Kangaroo.Utils+  , Kangaroo+  , parse+  , runKangaroo+  , ask++  ) where++import Data.ParserCombinators.Kangaroo.Combinators+import Data.ParserCombinators.Kangaroo.ParseMonad+import Data.ParserCombinators.Kangaroo.Prim+import Data.ParserCombinators.Kangaroo.Utils hiding ( (<:>), oo, ooo, oooo )++import Control.Monad ( liftM )++type Kangaroo e a = GenKangaroo e a+++parse :: Kangaroo e a  +      -> e+      -> FilePath +      -> IO (Either ParseErr a)+parse = runKangaroo ++runKangaroo :: Kangaroo e a+            -> e+            -> FilePath +            -> IO (Either ParseErr a)+runKangaroo p env filename = liftM fst (runGenKangaroo p env filename)++ask :: Kangaroo e e+ask = getUserSt
+ src/Data/ParserCombinators/KangarooState.hs view
@@ -0,0 +1,70 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module      :  Data.ParserCombinators.KangarooState+-- Copyright   :  (c) Stephen Tetley 2009+-- License     :  BSD3+--+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>+-- Stability   :  highly unstable+-- Portability :  to be determined.+--+-- Kangaroo parse monad with user state.+--+--------------------------------------------------------------------------------++module Data.ParserCombinators.KangarooState+  (+    module Data.ParserCombinators.Kangaroo.Combinators+  , module Data.ParserCombinators.Kangaroo.ParseMonad+  , module Data.ParserCombinators.Kangaroo.Prim+  , module Data.ParserCombinators.Kangaroo.Utils+  , Kangaroo+  , parse+  , runKangaroo+  , evalKangaroo+  , execKangaroo+  , put+  , get+  , modify+  , gets++  ) where++import Data.ParserCombinators.Kangaroo.Combinators+import Data.ParserCombinators.Kangaroo.ParseMonad+import Data.ParserCombinators.Kangaroo.Prim+import Data.ParserCombinators.Kangaroo.Utils hiding ( (<:>), oo, ooo, oooo )+import qualified Data.ParserCombinators.Kangaroo.Utils as Specs++import Control.Monad++type Kangaroo st a = GenKangaroo st a++parse :: Kangaroo st a -> st -> FilePath -> IO (Either ParseErr a)+parse = evalKangaroo++runKangaroo :: Kangaroo st a -> st -> FilePath -> IO (Either ParseErr a,st)+runKangaroo p st filename = runGenKangaroo p st filename++-- answer, no state+evalKangaroo :: Kangaroo st a -> st -> FilePath -> IO (Either ParseErr a)+evalKangaroo = liftM fst `Specs.ooo` runKangaroo++-- state, no answer+execKangaroo :: Kangaroo st a -> st -> FilePath -> IO st+execKangaroo = liftM snd `Specs.ooo` runKangaroo+++put :: st -> Kangaroo st ()+put = putUserSt++get :: Kangaroo st st+get = getUserSt++modify :: (st -> st) -> Kangaroo st ()+modify = modifyUserSt++gets :: (st -> a) -> Kangaroo st a+gets f = liftM f get
+ src/Data/ParserCombinators/KangarooWriter.hs view
@@ -0,0 +1,53 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module      :  Data.ParserCombinators.KangarooWriter+-- Copyright   :  (c) Stephen Tetley 2009+-- License     :  BSD3+--+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>+-- Stability   :  highly unstable+-- Portability :  to be determined.+--+-- Kangaroo parse monad with logging.+--+--------------------------------------------------------------------------------++module Data.ParserCombinators.KangarooWriter+  (+    module Data.ParserCombinators.Kangaroo.Combinators+  , module Data.ParserCombinators.Kangaroo.ParseMonad+  , module Data.ParserCombinators.Kangaroo.Prim+  , module Data.ParserCombinators.Kangaroo.Utils+  , Kangaroo+  , parse+  , runKangaroo+  , tell++  ) where++import Data.ParserCombinators.Kangaroo.Combinators+import Data.ParserCombinators.Kangaroo.ParseMonad+import Data.ParserCombinators.Kangaroo.Prim+import Data.ParserCombinators.Kangaroo.Utils hiding ( (<:>), oo, ooo, oooo )++import Data.Monoid++type Kangaroo r a = GenKangaroo r a+++parse :: Monoid w +      => Kangaroo w a  +      -> FilePath +      -> IO (Either ParseErr a,w)+parse = runKangaroo ++runKangaroo :: Monoid w +            => Kangaroo w a+            -> FilePath +            -> IO (Either ParseErr a,w)+runKangaroo p filename = runGenKangaroo p mempty filename++tell :: Monoid w => w -> Kangaroo w ()+tell s = getUserSt >>= \w -> putUserSt $ w `mappend` s
+ test/Picklers.hs view
@@ -0,0 +1,50 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module      :  Picklers+-- Copyright   :  (c) Stephen Tetley 2009+-- License     :  BSD3+--+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>+-- Stability   :  highly unstable+-- Portability :  to be determined.+--+-- Pickling+--+--------------------------------------------------------------------------------++module Picklers where+++import qualified Data.ByteString.Char8 as BS+import Data.Char+import Data.List ( foldl' )+import Data.Word+import System.IO++newtype RevBS = RevBS { getRevBS :: BS.ByteString }++type Pickle = RevBS++empty :: RevBS +empty = RevBS BS.empty+++writePickle :: FilePath -> Pickle -> IO ()+writePickle file pkl = withBinaryFile file WriteMode (BS.hPut `flip` rpkl)+  where rpkl = BS.reverse $ getRevBS pkl +++cons :: Char -> Pickle -> Pickle+cons c = RevBS . BS.cons c . getRevBS ++putCString :: String -> Pickle -> Pickle+putCString s pkl = putWord8 0 $ foldl' (flip cons) pkl s++putWord8 :: Word8 -> Pickle -> Pickle+putWord8 w = cons (chr $ fromIntegral w)++-- TODO +cstring :: String -> Pickle+cstring = putCString `flip` empty