-- Annotation framework for Yhc Core module Yhc.Core.Annotation ( CoreAnnotations, CoreAnnotable (..), CoreProperty (..), addAnnotation, getAnnotation, combineAnnotations) where import Data.Maybe import qualified Data.Map as M import qualified Data.List as L import Yhc.Core -- |Annotations database: a two-level map to hold property mappings -- for each of annotated objects. type CoreAnnotations = M.Map String (M.Map String String) -- |For each annotable object, unique key should be generated, to be used -- with the top level map in the annotations database. class CoreAnnotable a where toAnnotationKey :: a -> String -- |For each property, an encoding (to String) and decoding (from String) -- functions should be defined. class CoreProperty p where toAnnString :: p -> String -- ^arbitrary property to a string to store fromAnnString :: (Monad m) => String -> m p -- ^from stored string to property value or fail -- |Given an annotable object, append a property with given name and value -- to the existing annotations database. addAnnotation :: (CoreAnnotable a, CoreProperty p) => a -- ^annotable object -> (String, p) -- ^pair of name and value -> CoreAnnotations -- ^existing annotations database -> CoreAnnotations -- ^updated annotations database addAnnotation a (n, p) b = let ak = toAnnotationKey a -- annotation key to lookup at top level aa = M.findWithDefault M.empty ak b -- second level map or empty map if new ps = toAnnString p -- stringify the property value aa' = M.insert n ps aa -- insert/replace property at the second level in M.insert ak aa' b -- insert/replace properties at the top level getAnnotation :: (CoreAnnotable a, CoreProperty p) => a -- ^annotable object -> String -- ^property name -> CoreAnnotations -- ^annotations database -> Maybe p -- ^returned value or nothing getAnnotation a n b = do -- as we are in a monad... let ak = toAnnotationKey a -- annotation key to lookup at top level aa <- M.lookup ak b -- second level map or fail right here ps <- M.lookup n aa -- property value or fail right here p <- fromAnnString ps -- decode property value or fail rught here return p -- |Given the two annotation sets, combine them into one. If the same object -- is annotated in both sets, annotations are combines for such object, -- and left annotations take precedence. combineAnnotations :: CoreAnnotations -> CoreAnnotations -> CoreAnnotations combineAnnotations l r = let flkup = flip M.lookup lkeys = M.keys l rkeys = M.keys r ikeys = L.intersect lkeys rkeys lnodup = lkeys L.\\ ikeys rnodup = rkeys L.\\ ikeys lanno = concat $ map (maybeToList . flkup l) lnodup ranno = concat $ map (maybeToList . flkup r) rnodup ilanno = concat $ map (maybeToList . flkup l) ikeys iranno = concat $ map (maybeToList . flkup r) ikeys imps = zipWith M.union ilanno iranno in M.fromList (zip lnodup lanno ++ zip rnodup ranno ++ zip ikeys imps)