packages feed

alms-0.6.0: src/AST/TypeAnnotation.hs

-- | For treating syntactic types as type annotations.
module AST.TypeAnnotation (
  Annot, HasAnnotations(..),
) where

import Util
import AST
import Meta.Quasi

import Prelude ()
import qualified Data.Map as M
import qualified Data.Set as S

-- | A type annotation is merely a syntactic type
type Annot i = Type i

-- | Find out the free variables of a type annotation.  Minimal
--   definition: @annotFtvMap@
class Tag i ⇒ HasAnnotations a i | a → i where
  -- | Accumulate information about type variables.
  annotFtvMap   ∷ (TyVar i → r) →
                  (QTypId i → Int → r → r) →
                  (r → r → r) →
                  a →
                  M.Map (TyVar i) r
  -- | Just the set of type variables, please.
  annotFtvSet   ∷ a → S.Set (TyVar i)
  annotFtvSet   = M.keysSet . annotFtvMap (\_ → ()) (\_ _ () → ()) (\_ _ → ())

-- | Shorter-named alias
afm ∷ HasAnnotations a i ⇒
      (TyVar i → r) →
      (QTypId i → Int → r → r) →
      (r → r → r) →
      a →
      M.Map (TyVar i) r
afm = annotFtvMap

--
-- Generic instances
--

instance (HasAnnotations a i, HasAnnotations b i) ⇒
         HasAnnotations (a, b) i where
  annotFtvMap var con cmb (a, b) =
    M.unionWith cmb (afm var con cmb a) (afm var con cmb b)

instance (HasAnnotations a i, HasAnnotations b i, HasAnnotations c i) ⇒
         HasAnnotations (a, b, c) i where
  annotFtvMap var con cmb (a, b, c) = afm var con cmb (a, (b, c))

instance HasAnnotations a i ⇒ HasAnnotations [a] i where
  annotFtvMap var con cmb = M.unionsWith cmb . map (afm var con cmb)

instance HasAnnotations a i ⇒ HasAnnotations (Maybe a) i where
  annotFtvMap var con cmb = maybe mempty (afm var con cmb)

instance HasAnnotations a i ⇒ HasAnnotations (N note a) i where
  annotFtvMap = afm <$$$.> dataOf

--
-- Specific instances for syntax.
--

instance Tag i ⇒ HasAnnotations (TyVar i) i where
  annotFtvMap _   _ _ (TVAnti a) = $antierror
  annotFtvMap var _ _ tv         = M.singleton tv (var tv)

instance Tag i ⇒ HasAnnotations (QExp' i) i where
  annotFtvMap var con cmb qe0 = case qe0 of
    [qeQ|' $qlit:_   |]           → mempty
    [qeQ|'  '$tv |]               → afm var con cmb tv
    [qeQ|' $qe1 ⋁ $qe2 |]         → afm var con cmb (qe1, qe2)
    [qeQ|' $anti:a |]             → $antierror

instance Tag i ⇒ HasAnnotations (Type' i) i where
  annotFtvMap var con cmb t0 = case t0 of
    [ty|' ($list:ts) $qtid:ql |]  →
      M.unionsWith cmb
        [ con ql ix <$> afm var con cmb t
        | t  ← ts
        | ix ← [ 0 .. ] ]
    [ty|'  '$tv |]                → afm var con cmb tv
    [ty|' $t1 -[$opt:qe]> $t2 |]  →
      let t1m = con (qident "->") 0 <$> afm var con cmb t1
          qem = con (qident "->") 1 <$> afm var con cmb qe
          t2m = con (qident "->") 2 <$> afm var con cmb t2
       in M.unionsWith cmb [t1m, qem, t2m]
    [ty|' $quant:_ `$tv. $t |]    → M.delete tv $ afm var con cmb t
    [ty|' μ `$tv. $t |]           → M.delete tv $ afm var con cmb t
    [ty|' `$uid:_ of $t1 | $t2 |] → afm var con cmb (t1, t2)
    [ty|' $anti:a |]              → $antierror

instance Tag i => HasAnnotations (Patt' i) i where
  annotFtvMap var con cmb x0 = case x0 of
    [pa|' _ |]                  → mempty
    [pa|' $lid:_ |]             → mempty
    [pa|' $qcid:_ $opt:mx |]    → afm var con cmb mx
    [pa|' ($x, $y) |]           → afm var con cmb (x, y)
    [pa|' $lit:_ |]             → mempty
    [pa|' $x as $vid:_ |]       → afm var con cmb x
    [pa|' `$uid:_ $opt:mx |]    → afm var con cmb mx
    [pa|' $x : $t |]            → afm var con cmb (x, t)
    [pa|' {$uid:_ = $x | $y} |] → afm var con cmb (x, y)
    [pa|' ! $x |]               → afm var con cmb x
    [pa|' $anti:a |]            → $antierror

instance Tag i ⇒ HasAnnotations (Expr' i) i where
  annotFtvMap var con cmb e0 = case e0 of
    [ex|' $qvid:_ |]            → mempty
    [ex|' $lit:_ |]             → mempty
    [ex|' $qcid:_ $opt:me |]    → afm var con cmb me
    [ex|' let $x = $e in $e' |] → afm var con cmb (x, e, e')
    [ex|' match $e with $list:cas |]
                                → afm var con cmb (e, cas)
    [ex|' let rec $list:bns in $e |]
                                → afm var con cmb (bns, e)
    [ex|' let $decl:_ in $e |]  → afm var con cmb e
    [ex|' ($e1, $e2) |]         → afm var con cmb (e1, e2)
    [ex|' λ $x → $e |]          → afm var con cmb (x, e)
    [ex|' $e1 $e2 |]            → afm var con cmb (e1, e2)
    [ex|' `$uid:_ $opt:me |]    → afm var con cmb me
    [ex|' #$uid:_ $e |]         → afm var con cmb e
    [ex|' { $list:flds | $e2 } |]
                                → afm var con cmb (flds, e2)
    [ex|' {+ $list:flds | $e2 +} |]
                                → afm var con cmb (flds, e2)
    [ex|' $e1.$uid:_ |]         → afm var con cmb e1
    [ex|' $e : $t |]            → afm var con cmb (e, t)
    [ex|' $e :> $t |]           → afm var con cmb (e, t)
    [ex|' $anti:a |]            → $antierror

instance Tag i ⇒ HasAnnotations (CaseAlt' i) i where
  annotFtvMap var con cmb ca0 = case ca0 of
    [caQ|' $x → $e |]           → afm var con cmb (x, e)
    [caQ|' #$uid:_ $opt:mx → $e |]
                                → afm var con cmb (mx, e)
    [caQ|' $antiC:a |]          → $antierror

instance Tag i ⇒ HasAnnotations (Binding' i) i where
  annotFtvMap var con cmb bn0 = case bn0 of
    [bnQ|' $lid:_ = $e |]       → afm var con cmb  e
    [bnQ|' $antiB:a |]          → $antierror

instance Tag i ⇒ HasAnnotations (Field' i) i where
  annotFtvMap var con cmb bn0 = case bn0 of
    [fdQ|' $uid:_ = $e |]       → afm var con cmb  e
    [fdQ|' $antiF:a |]          → $antierror