packages feed

hoq-0.3: src/Semantics/Pattern.hs

{-# LANGUAGE GADTs, ExistentialQuantification #-}

module Semantics.Pattern
    ( Pattern(..), Patterns(..)
    , Clause(..), clauseToEval
    , ClauseEq(..), clauseToClauseEq
    , ClauseInCtx(..), Some(..)
    , instantiatePats1, instantiatePats
    , instantiatePatsAndLift1, instantiatePatsAndLift
    , instantiateClause
    , patternToTerm, patternsToTerms
    , patternToTermVar, patternsToTermsVar
    , patternToTermSyntax, patternsToTermsSyntax 
    , abstractTermPat, abstractTermPats
    , liftBasePat, liftBasePats
    , patternVars, patternsVars
    , patternsLength, (+++)
    ) where

import Data.Void

import Syntax(Syntax(..),Name)
import Semantics
import Semantics.Value
import qualified TypeChecking.Context as C

data Pattern b a where
    PatDCon :: Syntax -> Int -> Int -> [ClauseInCtx] -> [Term Semantics b] -> Patterns b a -> Pattern b a
    PatPCon :: Pattern b a -> Pattern b a
    PatICon :: ICon -> Pattern b b
    PatVar  :: String -> Pattern b (Scoped b)

data Patterns b a where
    Nil :: Patterns b b
    Cons :: Pattern b c -> Patterns c d -> Patterns b d

data Clause b where
    Clause :: Patterns b a -> Term Semantics a -> Clause b

data ClauseEq b where
    ClauseEq :: Eq a => Patterns b a -> Term Semantics a -> ClauseEq b

data ClauseInCtx = forall b. Eq b => ClauseInCtx (C.Ctx String (Type Semantics) Void b) (Clause b)
data Some f = forall a. Some (f a)

clauseToClauseEq :: Eq b => Clause b -> ClauseEq b
clauseToClauseEq (Clause Nil term) = ClauseEq Nil term
clauseToClauseEq (Clause (Cons (PatDCon v i n cs params ps) pats) term) = case clauseToClauseEq $ Clause (ps +++ pats) term of
    ClauseEq pats' term' -> case patternsSplitAt pats' (patternsLength ps) of
        Split pats1 pats2 -> ClauseEq (Cons (PatDCon v i n cs params pats1) pats2) term'
clauseToClauseEq (Clause (Cons (PatPCon pat) pats) term) = case clauseToClauseEq $ Clause (Cons pat pats) term of
    ClauseEq (Cons pat' pats') term' -> ClauseEq (Cons (PatPCon pat') pats') term'
    _ -> error "clauseToClauseEq"
clauseToClauseEq (Clause (Cons (PatICon con) pats) term) = case clauseToClauseEq (Clause pats term) of
    ClauseEq pats' term' -> ClauseEq (Cons (PatICon con) pats') term'
clauseToClauseEq (Clause (Cons (PatVar var) pats) term) = case clauseToClauseEq (Clause pats term) of
    ClauseEq pats' term' -> ClauseEq (Cons (PatVar var) pats') term'

(>>>=) :: Patterns b a -> (b -> Term Semantics c) -> Some (Patterns c)
Nil >>>= _ = Some Nil
Cons (PatDCon v i n cs params ps) pats >>>= k = case (ps +++ pats) >>>= k of
    Some pats' -> case patternsSplitAt pats' (patternsLength ps) of
        Split pats1 pats2 -> Some $ Cons (PatDCon v i n cs (map (>>= k) params) pats1) pats2
Cons (PatPCon pat) pats >>>= k = case Cons pat pats >>>= k of
    Some (Cons pat' pats') -> Some $ Cons (PatPCon pat') pats'
    _ -> error "(>>>=): Patterns"
Cons (PatICon con) pats >>>= k = case pats >>>= k of
    Some pats' -> Some $ Cons (PatICon con) pats'
Cons (PatVar var) pats >>>= k =
    case (pats >>>= \v -> case v of
                            Bound  -> return Bound
                            Free b -> fmap Free (k b)) of
        Some pats' -> Some $ Cons (PatVar var) pats'

instantiatePats1 :: Term Semantics b -> Patterns (Scoped b) a -> Some (Patterns b)
instantiatePats1 t ps = ps >>>= \v -> case v of
    Bound  -> t
    Free b -> return b

instantiatePatsAndLift1 :: C.Ctx s f b c -> Term Semantics c -> Patterns (Scoped b) a -> Some (Patterns c)
instantiatePatsAndLift1 ctx t ps = ps >>>= \v -> case v of
    Bound  -> t
    Free b -> return (C.liftBase ctx b)

instantiatePatsAndLift :: C.Ctx s f b c -> C.Ctx s f b d -> [Term Semantics d] -> Patterns c a -> Some (Patterns d)
instantiatePatsAndLift ctx1 ctx2 terms pats = pats >>>= go ctx1 ctx2 terms
  where
    go :: C.Ctx s f b c -> C.Ctx s f b d -> [Term Semantics d] -> c -> Term Semantics d
    go C.Nil ctx2 _ b = return (C.liftBase ctx2 b)
    go C.Snoc{} _ (t:_) Bound = t
    go (C.Snoc ctx1 _ _) ctx2 (_:ts) (Free c) = go ctx1 ctx2 ts c
    go _ _ _ _ = error "instantiatePatsAndLift"

instantiatePats :: C.Ctx s f Void b -> [Term Semantics d] -> Patterns b a -> Some (Patterns d)
instantiatePats ctx terms pats = pats >>>= go ctx terms
  where
    go :: C.Ctx s f Void b -> [Term Semantics d] -> b -> Term Semantics d
    go C.Nil _ b = absurd b
    go C.Snoc{} (t:_) Bound = t
    go (C.Snoc ctx _ _) (_:ts) (Free b) = go ctx ts b
    go _ _ _ = error "instantiatePats"

bindClause :: Clause b -> (b -> Term Semantics c) -> Clause c
bindClause (Clause Nil term) k = Clause Nil (term >>= k)
bindClause (Clause (Cons (PatDCon v i n cs params ps) pats) term) k = case bindClause (Clause (ps +++ pats) term) k of
    Clause pats' term' -> case patternsSplitAt pats' (patternsLength ps) of
        Split pats1 pats2 -> Clause (Cons (PatDCon v i n cs (map (>>= k) params) pats1) pats2) term'
bindClause (Clause (Cons (PatPCon pat) pats) term) k = case bindClause (Clause (Cons pat pats) term) k of
    Clause (Cons pat' pats') term' -> Clause (Cons (PatPCon pat') pats') term'
    _ -> error "bindClause"
bindClause (Clause (Cons (PatICon con) pats) term) k = case bindClause (Clause pats term) k of
    Clause pats' term' -> Clause (Cons (PatICon con) pats') term'
bindClause (Clause (Cons (PatVar var) pats) term) k =
    case (Clause pats term `bindClause` \v -> case v of
                                                Bound  -> return Bound
                                                Free b -> fmap Free (k b)) of
        Clause pats' term' -> Clause (Cons (PatVar var) pats') term'

instantiateClause :: C.Ctx s f Void b -> [Term Semantics d] -> Clause b -> Clause d
instantiateClause ctx terms cl = bindClause cl (go ctx terms)
  where
    go :: C.Ctx s f Void b -> [Term Semantics d] -> b -> Term Semantics d
    go C.Nil _ b = absurd b
    go C.Snoc{} (t:_) Bound = t
    go (C.Snoc ctx _ _) (_:ts) (Free b) = go ctx ts b
    go _ _ _ = error "instantiatePats"

patternToTerm :: Pattern b a -> Term Int String
patternToTerm (PatDCon _ i _ _ _ ps) = Apply i (patternsToTerms ps)
patternToTerm (PatPCon p) = Apply 0 [patternToTerm p]
patternToTerm (PatICon ILeft) = capply 0
patternToTerm (PatICon IRight) = capply 1
patternToTerm (PatVar v) = cvar v

patternsToTerms :: Patterns b a -> [Term Int String]
patternsToTerms Nil = []
patternsToTerms (Cons p ps) = patternToTerm p : patternsToTerms ps

patternToTermVar :: Pattern b a -> Term Int a
patternToTermVar (PatDCon _ i _ _ _ ps) = Apply i (patternsToTermsVar ps)
patternToTermVar (PatPCon p) = Apply 0 [patternToTermVar p]
patternToTermVar (PatICon ILeft) = capply 0
patternToTermVar (PatICon IRight) = capply 1
patternToTermVar (PatVar v) = cvar Bound

patternsToTermsVar :: Patterns b a -> [Term Int a]
patternsToTermsVar Nil = []
patternsToTermsVar (Cons p ps) = fmap (liftBasePats ps) (patternToTermVar p) : patternsToTermsVar ps

getName :: Syntax -> Name
getName (Constr _ s) = getName s
getName (Name _ n) = n
getName _ = error "getName"

patternToTermSyntax :: Pattern b a -> Term Name String
patternToTermSyntax (PatDCon syn i _ _ _ ps) = Apply (getName syn) (patternsToTermsSyntax ps)
patternToTermSyntax (PatPCon p) = Apply (getName $ syntax pathSem) [patternToTermSyntax p]
patternToTermSyntax (PatICon c) = capply $ getName $ syntax (iConSem c)
patternToTermSyntax (PatVar v) = cvar v

patternsToTermsSyntax :: Patterns b a -> [Term Name String]
patternsToTermsSyntax Nil = []
patternsToTermsSyntax (Cons p ps) = patternToTermSyntax p : patternsToTermsSyntax ps

clauseToEval :: Clause b -> ([Term Int String], Term Semantics b)
clauseToEval (Clause pats term) = (patternsToTerms pats, abstractTermPats pats term)

abstractTermPats :: Patterns b a -> Term Semantics a -> Term Semantics b
abstractTermPats Nil t = t
abstractTermPats (Cons p ps) t = abstractTermPat p (abstractTermPats ps t)

abstractTermPat :: Pattern b a -> Term Semantics a -> Term Semantics b
abstractTermPat (PatDCon _ _ _ _ _ ps) t = abstractTermPats ps t
abstractTermPat (PatPCon p) t = abstractTermPat p t
abstractTermPat PatICon{} t = t
abstractTermPat PatVar{} t = Lambda t

liftBasePat :: Pattern b a -> b -> a
liftBasePat (PatDCon _ _ _ _ _ pats) = liftBasePats pats
liftBasePat (PatPCon pat) = liftBasePat pat
liftBasePat PatICon{} = id
liftBasePat PatVar{} = Free

liftBasePats :: Patterns b a -> b -> a
liftBasePats Nil = id
liftBasePats (Cons pat pats) = liftBasePats pats . liftBasePat pat

patternVars :: Pattern b a -> [String]
patternVars (PatDCon _ _ _ _ _ pats) = patternsVars pats
patternVars (PatPCon pat) = patternVars pat
patternVars PatICon{} = []
patternVars (PatVar var) = [var]

patternsVars :: Patterns b a -> [String]
patternsVars Nil = []
patternsVars (Cons pat pats) = patternVars pat ++ patternsVars pats

patternsLength :: Patterns a b -> Int
patternsLength Nil = 0
patternsLength (Cons _ ps) = patternsLength ps + 1

(+++) :: Patterns a b -> Patterns b c -> Patterns a c
Nil +++ ps' = ps'
Cons p ps +++ ps' = Cons p (ps +++ ps')

data Split b a where
    Split :: Patterns b c -> Patterns c a -> Split b a

patternsSplitAt :: Patterns b a -> Int -> Split b a
patternsSplitAt pats 0 = Split Nil pats
patternsSplitAt Nil _ = Split Nil Nil
patternsSplitAt (Cons pat pats) k = case patternsSplitAt pats (k - 1) of
    Split pats1 pats2 -> Split (Cons pat pats1) pats2