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

--------------------------------------------------------------------------------
-- |
-- Module      :  HarmTrace.Models.Parser
-- Copyright   :  (c) 2010-2012 Universiteit Utrecht, 2012 University of Oxford
-- License     :  GPL3
--
-- Maintainer  :  bash@cs.uu.nl, jpm@cs.ox.ac.uk
-- Stability   :  experimental
-- Portability :  non-portable
--
-- Summary: Semi-generic parser for chords
--------------------------------------------------------------------------------

module HarmTrace.Models.Parser (
                                 ParseG (..)
                               , parseGdefault 
                               , PMusic
                               ) where


-- Parser stuff
import Text.ParserCombinators.UU
import Text.ParserCombinators.UU.BasicInstances

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

-- Music stuff
import HarmTrace.Models.ChordTokens


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

-- | a type synoniome for a harmonic analysis of a piece of music
type PMusic a = P (Str ChordToken [ChordToken] Int) a

class Parse' f where
   parse' :: PMusic f

instance Parse' U where
  parse' = pure U

instance (ParseG a) => Parse' (Rec a) where
  parse' = Rec <$> parseG

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

instance (Constructor c, Parse' f) => Parse' (G.CEq c p p f) where
  parse' = G.C <$> parse' <?> "Constructor " ++ conName (undefined :: C c f)

instance                              Parse' (G.CEq c p q f) where 
  parse' = empty

instance (Parse' f, Parse' g) => Parse' (f :+: g) where
  parse' = L <$> parse' <|> R <$> parse'

instance (Parse' f, Parse' g) => Parse' (f :*: g) where
  parse' = (:*:) <$> parse' <*> parse'


class ParseG a where
  parseG :: PMusic a

instance (ParseG a) => ParseG [a] where
  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
  parseG = pMaybe parseG

-- | default generic parser
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...