packages feed

HarmTrace-0.4: MIR/HarmGram/ParserChord.hs

{-# LANGUAGE TypeOperators          #-}
{-# LANGUAGE TypeSynonymInstances   #-}
{-# LANGUAGE FlexibleInstances      #-}
{-# LANGUAGE FlexibleContexts       #-}
{-# LANGUAGE TypeFamilies           #-}
{-# LANGUAGE MultiParamTypeClasses  #-}
{-# LANGUAGE OverlappingInstances   #-}
{-# LANGUAGE ScopedTypeVariables    #-}

-- Semi-generic parser for chords
module MIR.HarmGram.ParserChord where


-- Parser stuff
import Text.ParserCombinators.UU hiding (T)
import Text.ParserCombinators.UU.BasicInstances hiding (head)
import Text.ParserCombinators.UU.BasicInstances.List

-- Generics stuff
import Generics.Instant.Base as G

-- Music stuff
import MIR.HarmGram.Tokenizer


--------------------------------------------------------------------------------
-- The generic part of the parser
--------------------------------------------------------------------------------

--type PMusic a = Parser [ChordDegree] a (Int, Int)
-- type Parser a b c = Stream a d => P (Str d a c) b
type PMusic a = P (Str ChordDegree [ChordDegree] (Int, Int)) a

class Parse' f where
  parse' :: PMusic f

instance Parse' U where
  {- INLINE parse' #-}
  parse' = pure U

instance (ParseG a) => Parse' (Rec a) where
  {- INLINE parse' #-}
  parse' = Rec <$> parseG

-- Not really necessary because TH is not generating any Var, but anyway
instance (ParseG a) => Parse' (Var a) where
  {- INLINE parse' #-}
  parse' = Var <$> parseG

instance (Constructor c, Parse' f) => Parse' (G.C c f) where
  {- INLINE parse' #-}
  parse' = G.C <$> parse' <?> "Constructor " ++ conName (undefined :: C c f)

instance (Parse' f, Parse' g) => Parse' (f :+: g) where
  {- INLINE parse' #-}
  parse' = L <$> parse' <|> R <$> parse'

instance (Parse' f, Parse' g) => Parse' (f :*: g) where
  {- INLINE parse' #-}
  parse' = (:*:) <$> parse' <*> parse'


class ParseG a where
  {- INLINE parseG #-}
  parseG :: PMusic a

instance (ParseG a) => ParseG [a] where
  {- INLINE parseG #-}
  parseG = pList1 parseG
  -- We should use non-greedy parsing here, else the final Dom is never parsed
  -- as such.
  -- parseG = pList1_ng parseG

instance (ParseG a) => ParseG (Maybe a) where
  {- INLINE parseG #-}
  parseG = pMaybe parseG

{- INLINE parseGdefault #-}
parseGdefault :: (Representable a, Parse' (Rep a)) => PMusic a
-- parseGdefault = fmap (to . head) (amb parse')
-- Previously we used:
parseGdefault = fmap to parse'
-- This gave rise to many ambiguities. Now we allow parse' to be ambiguous
-- (note that the sum case uses <|>) but then pick only the very first tree
-- from all the possible results. It remains to be seen if the first tree is
-- the best...