packages feed

hoq-0.3: src/TypeChecking/Definitions/Termination.hs

{-# LANGUAGE GADTs, ExistentialQuantification #-}

module TypeChecking.Definitions.Termination
    ( checkTermination, CT(..), PatInCtx(..)
    ) where

import qualified Syntax as S
import Semantics.Value
import Semantics
import Syntax.ErrorDoc
import TypeChecking.Context
import TypeChecking.Expressions.Utils

data CT a = Constructor Int | Function ID | Variable a deriving Eq

instance Functor CT where
    fmap _ (Constructor i) = Constructor i
    fmap _ (Function i) = Function i
    fmap f (Variable a) = Variable (f a)

data PatInCtx  = forall a. Eq a => PatInCtx (CT a) [Term Int a] (Term Semantics a)
data PatsInCtx = forall a. Eq a => PatsInCtx       [Term Int a] [Term Semantics a]

checkTermination :: S.Posn -> [PatInCtx] -> [Error]
checkTermination _ [] = []
checkTermination pos clauses = go $ clauses >>= \(PatInCtx name pats term) ->
    map (\(TermsInCtx ctx terms) -> PatsInCtx (map (fmap $ liftBase ctx) pats) terms) (collectFunCalls Nil name term)
  where
    go tc = if any isEmpty tc
            then [Error Other $ emsgLC pos "Termination check failed" enull]
            else if all (\(PatsInCtx pats terms) -> checkTerms pats terms == LT) tc
                 then []
                 else go $ map (\(PatsInCtx pats terms) -> PatsInCtx (tail pats) $ tail terms) tc
    
    isEmpty (PatsInCtx [] _) = True
    isEmpty (PatsInCtx _ []) = True
    isEmpty _ = False

checkTerm :: Eq a => Term Int a -> Term Semantics a -> Ordering
checkTerm (Apply con _) (Apply (Semantics _ (ICon con')) []) | con == iConToInt con' = EQ
  where
    iConToInt ILeft  = 0
    iConToInt IRight = 1
checkTerm (Var a []) (Var a' []) = if a == a' then EQ else GT
checkTerm (Apply i pats) term =
    if minimum (GT : map (\pat -> checkTerm pat term) pats) /= GT
    then LT
    else case term of
        Apply (Semantics _ (DCon i' k _)) terms | i == i' -> checkTerms pats (drop k terms)
        _ -> GT
checkTerm _ _ = GT

checkTerms :: Eq a => [Term Int a] -> [Term Semantics a] -> Ordering
checkTerms [] _ = EQ
checkTerms _ [] = GT
checkTerms (pat:pats) (term:terms) = case checkTerm pat term of
    EQ -> checkTerms pats terms
    r  -> r

data TermsInCtx s f b = forall a. Eq a => TermsInCtx (Ctx s f b a) [Term Semantics a]

collectFunCalls :: Eq a => Ctx String f b a -> CT a -> Term Semantics a -> [TermsInCtx String f b]
collectFunCalls ctx name (Lambda t) = collectFunCalls (Snoc ctx (error "") $ error "") (fmap Free name) t
collectFunCalls ctx name (Var a as) = (if name == Variable a then [TermsInCtx ctx as] else [])
    ++ (as >>= collectFunCalls ctx name)
collectFunCalls ctx name (Apply a as) = (case a of
    Semantics _ (DCon name' k _) | name == Constructor name' -> [TermsInCtx ctx $ drop k as]
    Semantics _ (FunCall name' _) | name == Function name' -> [TermsInCtx ctx as]
    _ -> []) ++ (as >>= collectFunCalls ctx name)