packages feed

alms-0.4.9: src/Syntax/Ident.hs

{-# LANGUAGE
      DeriveDataTypeable,
      FlexibleInstances,
      FunctionalDependencies,
      GeneralizedNewtypeDeriving,
      MultiParamTypeClasses,
      ScopedTypeVariables,
      TypeFamilies,
      TypeSynonymInstances,
      UndecidableInstances #-}
module Syntax.Ident (
  -- * Identifier classes
  Id(..), Raw(..), Renamed(..), renamed0,
  -- ** Dirty tricks
  trivialRename, trivialRename2,
  -- * Identifiers 
  Path(..),
  Lid(..), Uid(..), BIdent(..),
  Ident, QLid, QUid,
  TyVar(..), tvUn, tvAf, tvalphabet,
  isOperator, lid, uid, qlid, quid,
  -- * Free and defined vars
  FvMap, Fv(..), Dv(..), ADDITIVE(..),
  (|*|), (|+|), (|-|), (|--|)
) where

import Env (Path(..), (:>:)(..))
import Util
import Viewable
import Syntax.Anti
import Syntax.Kind (QLit(..))

import Data.Char (isAlpha, isDigit)
import Data.Generics (Typeable(..), Data(..), everywhere, mkT)
import qualified Data.Map as M
import qualified Data.Set as S
import qualified Unsafe.Coerce

class Data i => Id i where
  -- The trivial identity tag, used when the identity tag is
  -- insufficient to distinguish different thing
  trivialId :: i
  -- Check for triviality
  isTrivial :: i -> Bool
  -- Compare two identifiers, given a secondary criterion to use if
  -- necessary
  compareId :: i -> i -> Ordering -> Ordering

data Raw = Raw_
  deriving (Data, Typeable, Show)

newtype Renamed = Ren_ Int
  deriving (Data, Typeable, Enum, Eq, Ord)

instance Show Renamed where
  showsPrec p (Ren_ z) = showsPrec p z

instance Id Raw where
  trivialId     = Raw_
  isTrivial     = const True
  compareId _ _ = id

instance Id Renamed where
  trivialId          = Ren_ 0
  isTrivial (Ren_ 0) = True
  isTrivial (Ren_ _) = False
  compareId (Ren_ 0) (Ren_ 0) next = next
  compareId (Ren_ 0) _        _    = LT
  compareId _        (Ren_ 0) _    = GT
  compareId (Ren_ a) (Ren_ b) _    = a `compare` b

renamed0 :: Renamed
renamed0  = Ren_ 1

-- | This is super dirty
trivialRename :: forall f i j. (Id i, Id j, Data (f i)) => f i -> f j
trivialRename  = Unsafe.Coerce.unsafeCoerce . everywhere (mkT each) where
  each :: i -> i
  each _ = Unsafe.Coerce.unsafeCoerce (trivialId :: j)

trivialRename2 :: forall f g h i j.
                 (Id i, Id j, Data (f (g i) (h i))) =>
                 f (g i) (h i) -> f (g j) (h j)
trivialRename2  = Unsafe.Coerce.unsafeCoerce . everywhere (mkT each) where
  each :: i -> i
  each _ = Unsafe.Coerce.unsafeCoerce (trivialId :: j)

-- IDENTIFIERS

-- | lowercase identifiers (variables, tycons)
data Lid i
  = Lid {
      lidUnique :: !i,
      unLid     :: !String
    }
  | LidAnti Anti
  deriving (Typeable, Data)

instance Id i => Eq (Lid i) where
  a == b = compare a b == EQ

instance Id i => Ord (Lid i) where
  Lid u1 s1 `compare` Lid u2 s2 = compareId u1 u2 (compare s1 s2)
  LidAnti a `compare` _         = antierror "Lid#compare" a
  _         `compare` LidAnti a = antierror "Lid#compare" a

-- | uppercase identifiers (modules, datacons)
data Uid i
  = Uid {
      uidUnique :: !i,
      unUid     :: !String
    }
  | UidAnti Anti
  deriving (Typeable, Data)

instance Id i => Eq (Uid i) where
  a == b = compare a b == EQ

instance Id i => Ord (Uid i) where
  Uid u1 s1 `compare` Uid u2 s2 = compareId u1 u2 (compare s1 s2)
  UidAnti a `compare` _         = antierror "Uid#compare" a
  _         `compare` UidAnti a = antierror "Uid#compare" a

-- | bare (unqualified) identifers
data BIdent i = Var { unVar :: !(Lid i) }
              | Con { unCon :: !(Uid i) }
  deriving (Eq, Ord, Typeable, Data)

-- | path-qualified uppercase identifiers
type QUid i = Path (Uid i) (Uid i)
-- | path-qualified lowecase identifiers
type QLid i = Path (Uid i) (Lid i)
-- | path-qualified identifiers
type Ident i = Path (Uid i) (BIdent i)

-- | Type variables include qualifiers
data TyVar i
  = TV {
      tvname :: !(Lid i),
      tvqual :: !QLit
    }
  | TVAnti Anti
  deriving (Eq, Ord, Typeable, Data)

lid :: Id i => String -> Lid i
lid = Lid trivialId

uid :: Id i => String -> Uid i
uid = Uid trivialId

tvUn, tvAf :: Id i => String -> TyVar i
tvUn s = TV (lid s) Qu
tvAf s = TV (lid s) Qa

tvalphabet :: Id i => [QLit -> TyVar i]
tvalphabet  = map (TV . lid) alphabet
  where
    alphabet = map return ['a' .. 'z'] ++
               [ x ++ [y] | x <- alphabet, y <- ['a' .. 'z'] ]

-- | Is the lowercase identifier an infix operator?
isOperator :: Lid i -> Bool
isOperator l = case show l of
    '(':_ -> True
    _     -> False

-- | Sugar for generating AST for qualified lowercase identifers
qlid :: Id i => String -> QLid i
qlid s = case reverse (splitBy (=='.') s) of
           []   -> J [] (lid "")
           x:xs -> J (map uid (reverse xs)) (lid x)

-- | Sugar for generating AST for qualified uppercase identifers
quid :: Id i => String -> QUid i
quid s = case reverse (splitBy (=='.') s) of
           []   -> J [] (uid "")
           x:xs -> J (map uid (reverse xs)) (uid x)

instance Show (Lid i) where
  showsPrec _ (Lid _ s) =
    case s of
      '_':_             -> (s++)
      c  :_ | isAlpha c -> (s++)
      c  :_ | isDigit c -> (s++)
      _  :_ | head s == '*' || last s == '*'
                        -> ("( "++) . (s++) . (" )"++)
      _                 -> ('(':) . (s++) . (')':)
    {-
    . let z = Unsafe.Coerce.unsafeCoerce i :: Renamed in
         if z == Unsafe.Coerce.unsafeCoerce Raw_
           then id
           else showChar '[' . shows z . showChar ']'
  -}
  showsPrec p (LidAnti a) = showsPrec p a

instance Show (Uid i) where
  showsPrec _ (Uid _ s)   = (s++)
  showsPrec p (UidAnti a) = showsPrec p a

instance Show (BIdent i) where
  showsPrec p (Var x) = showsPrec p x
  showsPrec p (Con k) = showsPrec p k

instance Show (TyVar i) where
  showsPrec _ (TV x Qu)  = showChar '\''  . shows x
  showsPrec _ (TV x Qa)  = showChar '`' . shows x
  showsPrec _ (TVAnti a) = showChar '\'' . shows a

instance Viewable (Path (Uid i) (BIdent i)) where
  type View (Ident i) = Either (QLid i) (QUid i)
  view (J p (Var n)) = Left (J p n)
  view (J p (Con n)) = Right (J p n)

-- | Simple keys embed into path keyspace
instance (Ord p, (:>:) k k') =>
         (:>:) (Path p k) k'  where liftKey = J [] . liftKey

instance Id i => (:>:) (BIdent i) (Lid i) where liftKey = Var
instance Id i => (:>:) (BIdent i) (Uid i) where liftKey = Con

---
--- Identifier antiquotes
---

---
--- Free variables
---

-- | Our free variables function returns not merely a set,
-- but a map from names to a count of maximum occurrences.
type FvMap i = M.Map (QLid i) Integer

-- | The free variables analysis
class Id i => Fv a i | a -> i where
  fv :: a -> FvMap i

-- | The defined variables analysis
class Id i => Dv a i | a -> i where
  qdv :: a -> S.Set (QLid i)
  dv  :: a -> S.Set (Lid i)

  qdv  = S.mapMonotonic (J []) . dv
  dv a = S.fromDistinctAscList [ v | J [] v <- S.toAscList (qdv a) ]

instance Fv a i => Fv [a] i where
  fv = foldr (|+|) M.empty . map fv

instance Dv a i => Dv [a] i where
  dv = S.unions . map dv

newtype ADDITIVE a = ADDITIVE [a]

instance Fv a i => Fv (ADDITIVE a) i where
  fv (ADDITIVE a) = foldr (|+|) M.empty (map fv a)

-- | Used by the free variables analysis
(|*|), (|+|) :: Id i => FvMap i -> FvMap i -> FvMap i
(|*|) = M.unionWith (+)
(|+|) = M.unionWith max

(|-|) :: Id i => FvMap i -> QLid i -> FvMap i
(|-|)  = flip M.delete

(|--|) :: Id i => FvMap i -> S.Set (QLid i) -> FvMap i
(|--|)  = S.fold M.delete