packages feed

Delta-Lambda-0.3.0.0: source/Check.hs

{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE MultiParamTypeClasses #-}

module Check where

import           AST
import           Printer              ()
import           Reduction

import           Control.Monad.Except
import           Control.Monad.Reader

import           Text.Megaparsec      (SourcePos)

type Context m v i = [Term m v i]

type Judgement m v i a = ReaderT (Context m v i) (Either String) a

typeSynth :: (Show v, Enum i) => Term SourcePos v i -> Judgement SourcePos v i (PseudoTerm SourcePos v i)
typeSynth term' =
  case term' of
    Type metadata -> return $ Kind' (Kind metadata)
    Variable variable index position -> do
      context <- ask
      if Prelude.length context <= fromEnum index - 1
        then throwError $ "invalid variable encountered during type synthesis: "
                          ++ show variable ++ "\nat " ++ show position
        else return $ Term $ context !! (fromEnum index - 1)
    Application position argument function -> do
      function' <- typeSynth function
      case function' of
        Term f -> return $ Term $ Application position argument f
        Kind' _ -> throwError $ "invalid kind constructed during type synthesis"
                                ++ "\nat " ++ show position
    Abstraction variable position parameter body -> do
      body' <- local (parameter :) (typeSynth body)
      case body' of
        Term b  -> return $ Term $ Abstraction variable position parameter b
        Kind' b -> return $ Kind' $ Function variable position parameter b

fTypeSynth :: (Show v, Enum i) => Term SourcePos v i -> Judgement SourcePos v i (PseudoTerm SourcePos v i)
fTypeSynth t = do
  typ <- typeSynth t
  case typ of
    Term t' -> fTypeSynth t'
    Kind' _ -> return typ

termCorrect :: (Enum i, Show v, BetaEq (PseudoTerm SourcePos) v i) => Term SourcePos v i -> Judgement SourcePos v i ()
termCorrect t =
  case t of
    Type _ -> return ()
    Variable{} ->
      void (typeSynth t)
    Abstraction _ _ parameter body -> do
      termCorrect parameter
      local (parameter :) (termCorrect body)
    Application pos argument function -> do
      argType <- typeSynth argument
      termCorrect argument
      Term funType <- fTypeSynth function
      case nfTerm funType of
        Abstraction _ _ parameter body ->
          if Term parameter === argType
            then termCorrect (nfTerm $ substituteTerm (toEnum 1) argument body)
            else let argPosition = show $ getNodeMetadata (Term argument)
                     parPosition = show $ getNodeMetadata (Term parameter)
                 in throwError $ "argument type and function parameter do not match: \n"
                                 ++ "at " ++
                                    argPosition ++
                                    " with argument type of:   " ++
                                    show argType ++
                                    "\n" ++
                                    "at " ++
                                    parPosition ++
                                    " with parameter value of: " ++
                                    show parameter ++
                                    "\n"
        e@_ -> throwError $ "attempted application onto non function type at "
                            ++ show pos ++ "\nwith type: " ++ show e