module Idris.Elab.Record(elabRecord) where
import Idris.AbsSyntax
import Idris.Docstrings
import Idris.Error
import Idris.Delaborate
import Idris.Imports
import Idris.Elab.Term
import Idris.Coverage
import Idris.DataOpts
import Idris.Providers
import Idris.Primitives
import Idris.Inliner
import Idris.PartialEval
import Idris.DeepSeq
import Idris.Output (iputStrLn, pshow, iWarn, sendHighlighting)
import IRTS.Lang
import Idris.ParseExpr (tryFullExpr)
import Idris.Elab.Type
import Idris.Elab.Data
import Idris.Elab.Utils
import Idris.Core.TT
import Idris.Core.Evaluate
import Idris.Elab.Data
import Data.Maybe
import Data.List
import Control.Monad
elabRecord :: ElabInfo
-> ElabWhat
-> (Docstring (Either Err PTerm))
-> SyntaxInfo -> FC -> DataOpts
-> Name
-> FC
-> [(Name, FC, Plicity, PTerm)]
-> [(Name, Docstring (Either Err PTerm))]
-> [(Maybe (Name, FC), Plicity, PTerm, Maybe (Docstring (Either Err PTerm)))]
-> Maybe (Name, FC)
-> (Docstring (Either Err PTerm))
-> SyntaxInfo
-> Idris ()
elabRecord info what doc rsyn fc opts tyn nfc params paramDocs fields cname cdoc csyn
= do logLvl 1 $ "Building data declaration for " ++ show tyn
let tycon = generateTyConType params
logLvl 1 $ "Type constructor " ++ showTmImpls tycon
dconName <- generateDConName (fmap fst cname)
let dconTy = generateDConType params fieldsWithNameAndDoc
logLvl 1 $ "Data constructor: " ++ showTmImpls dconTy
logLvl 1 $ foldr (++) "" $ intersperse "\n" (map show dconsArgDocs)
let datadecl = case what of
ETypes -> PLaterdecl tyn NoFC tycon
_ -> PDatadecl tyn NoFC tycon [(cdoc, dconsArgDocs, dconName, NoFC, dconTy, fc, [])]
elabData info rsyn doc paramDocs fc opts datadecl
let parameters = [(n,pt) | (n, _, _, pt) <- params]
let projections = [n | (n, _, _, _, _) <- fieldsWithName]
addRecord tyn (RI parameters dconName projections)
addIBC (IBCRecord tyn)
when (what /= ETypes) $ do
logLvl 1 $ "fieldsWithName " ++ show fieldsWithName
logLvl 1 $ "fieldsWIthNameAndDoc " ++ show fieldsWithNameAndDoc
elabRecordFunctions info rsyn fc tyn paramsAndDoc fieldsWithNameAndDoc dconName target
sendHighlighting $
[(nfc, AnnName tyn Nothing Nothing Nothing)] ++
maybe [] (\(_, cnfc) -> [(cnfc, AnnName dconName Nothing Nothing Nothing)]) cname ++
[(ffc, AnnBoundName fn False) | (fn, ffc, _, _, _) <- fieldsWithName]
where
generateTyConType :: [(Name, FC, Plicity, PTerm)] -> PTerm
generateTyConType ((n, nfc, p, t) : rest) = PPi p (nsroot n) nfc t (generateTyConType rest)
generateTyConType [] = (PType fc)
generateDConName :: Maybe Name -> Idris Name
generateDConName (Just n) = return $ expandNS csyn n
generateDConName Nothing = uniqueName (expandNS csyn $ sMN 0 ("Mk" ++ (show (nsroot tyn))))
where
uniqueName :: Name -> Idris Name
uniqueName n = do i <- getIState
case lookupTyNameExact n (tt_ctxt i) of
Just _ -> uniqueName (nextName n)
Nothing -> return n
generateDConType :: [(Name, FC, Plicity, PTerm)] -> [(Name, FC, Plicity, PTerm, a)] -> PTerm
generateDConType ((n, nfc, _, t) : ps) as = PPi impl (nsroot n) NoFC t (generateDConType ps as)
generateDConType [] ((n, _, p, t, _) : as) = PPi p (nsroot n) NoFC t (generateDConType [] as)
generateDConType [] [] = target
target :: PTerm
target = PApp fc (PRef fc [] tyn) $ map (uncurry asPRefArg) [(p, n) | (n, _, p, _) <- params]
paramsAndDoc :: [(Name, FC, Plicity, PTerm, Docstring (Either Err PTerm))]
paramsAndDoc = pad params paramDocs
where
pad :: [(Name, FC, Plicity, PTerm)] -> [(Name, Docstring (Either Err PTerm))] -> [(Name, FC, Plicity, PTerm, Docstring (Either Err PTerm))]
pad ((n, fc, p, t) : rest) docs
= let d = case lookup n docs of
Just d' -> d
Nothing -> emptyDocstring
in (n, fc, p, t, d) : (pad rest docs)
pad _ _ = []
dconsArgDocs :: [(Name, Docstring (Either Err PTerm))]
dconsArgDocs = paramDocs ++ (dcad fieldsWithName)
where
dcad :: [(Name, FC, Plicity, PTerm, Maybe (Docstring (Either Err PTerm)))] -> [(Name, Docstring (Either Err PTerm))]
dcad ((n, _, _, _, (Just d)) : rest) = ((nsroot n), d) : (dcad rest)
dcad (_ : rest) = dcad rest
dcad [] = []
fieldsWithName :: [(Name, FC, Plicity, PTerm, Maybe (Docstring (Either Err PTerm)))]
fieldsWithName = fwn [] fields
where
fwn :: [Name] -> [(Maybe (Name, FC), Plicity, PTerm, Maybe (Docstring (Either Err PTerm)))] -> [(Name, FC, Plicity, PTerm, Maybe (Docstring (Either Err PTerm)))]
fwn ns ((n, p, t, d) : rest)
= let nn = case n of
Just n' -> n'
Nothing -> newName ns baseName
withNS = expandNS rsyn (fst nn)
in (withNS, snd nn, p, t, d) : (fwn (fst nn : ns) rest)
fwn _ _ = []
baseName = (sUN "__pi_arg", NoFC)
newName :: [Name] -> (Name, FC) -> (Name, FC)
newName ns (n, nfc)
| n `elem` ns = newName ns (nextName n, nfc)
| otherwise = (n, nfc)
fieldsWithNameAndDoc :: [(Name, FC, Plicity, PTerm, Docstring (Either Err PTerm))]
fieldsWithNameAndDoc = fwnad fieldsWithName
where
fwnad :: [(Name, FC, Plicity, PTerm, Maybe (Docstring (Either Err PTerm)))] -> [(Name, FC, Plicity, PTerm, Docstring (Either Err PTerm))]
fwnad ((n, nfc, p, t, d) : rest)
= let doc = fromMaybe emptyDocstring d
in (n, nfc, p, t, doc) : (fwnad rest)
fwnad [] = []
elabRecordFunctions :: ElabInfo -> SyntaxInfo -> FC
-> Name
-> [(Name, FC, Plicity, PTerm, Docstring (Either Err PTerm))]
-> [(Name, FC, Plicity, PTerm, Docstring (Either Err PTerm))]
-> Name
-> PTerm
-> Idris ()
elabRecordFunctions info rsyn fc tyn params fields dconName target
= do logLvl 1 $ "Elaborating helper functions for record " ++ show tyn
logLvl 1 $ "Fields: " ++ show fieldNames
logLvl 1 $ "Params: " ++ show paramNames
i <- getIState
ttConsTy <-
case lookupTyExact dconName (tt_ctxt i) of
Just as -> return as
Nothing -> tclift $ tfail $ At fc (Elaborating "record " tyn (InternalMsg "It seems like the constructor for this record has disappeared. :( \n This is a bug. Please report."))
let constructorArgs = getArgTys ttConsTy
logLvl 1 $ "Cons args: " ++ show constructorArgs
logLvl 1 $ "Free fields: " ++ show (filter (not . isFieldOrParam') constructorArgs)
let freeFieldsForElab = map (freeField i) (filter (not . isFieldOrParam') constructorArgs)
let paramsForElab = [((nsroot n), (paramName n), impl, t, d) | (n, _, _, t, d) <- params]
let userFieldsForElab = [((nsroot n), n, p, t, d) | (n, nfc, p, t, d) <- fields]
let projectors = [(n, n', p, t, d, i) | ((n, n', p, t, d), i) <- zip (freeFieldsForElab ++ paramsForElab ++ userFieldsForElab) [0..]]
elabProj dconName projectors
logLvl 1 $ "Dependencies: " ++ show fieldDependencies
logLvl 1 $ "Depended on: " ++ show dependedOn
let updaters = [(n, n', p, t, d, i) | ((n, n', p, t, d), i) <- zip (paramsForElab ++ userFieldsForElab) [0..]]
elabUp dconName updaters
where
placeholderArg :: Plicity -> Name -> PArg
placeholderArg p n = asArg p (nsroot n) Placeholder
fieldNames :: [Name]
fieldNames = [nsroot n | (n, _, _, _, _) <- fields]
paramNames :: [Name]
paramNames = [nsroot n | (n, _, _, _, _) <- params]
isFieldOrParam :: Name -> Bool
isFieldOrParam n = n `elem` (fieldNames ++ paramNames)
isFieldOrParam' :: (Name, a) -> Bool
isFieldOrParam' = isFieldOrParam . fst
isField :: Name -> Bool
isField = flip elem fieldNames
isField' :: (Name, a, b, c, d, f) -> Bool
isField' (n, _, _, _, _, _) = isField n
fieldTerms :: [PTerm]
fieldTerms = [t | (_, _, _, t, _) <- fields]
freeField :: IState -> (Name, TT Name) -> (Name, Name, Plicity, PTerm, Docstring (Either Err PTerm))
freeField i arg = let nameInCons = fst arg
nameFree = expandNS rsyn (freeName $ fst arg)
plicity = impl
fieldType = delab i (snd arg)
doc = emptyDocstring
in (nameInCons, nameFree, plicity, fieldType, doc)
freeName :: Name -> Name
freeName (UN n) = sUN ("free_" ++ str n)
freeName (MN i n) = sMN i ("free_" ++ str n)
freeName (NS n s) = NS (freeName n) s
freeName n = n
zipParams :: IState -> [(Name, Plicity, PTerm)] -> [(Name, Docstring (Either Err PTerm))] -> [(Name, PTerm, Docstring (Either Err PTerm))]
zipParams i ((n, _, t) : rest) ((_, d) : rest') = (n, t, d) : (zipParams i rest rest')
zipParams i ((n, _, t) : rest) [] = (n, t, emptyDoc) : (zipParams i rest [])
where emptyDoc = annotCode (tryFullExpr rsyn i) emptyDocstring
zipParams _ [] [] = []
paramName :: Name -> Name
paramName (UN n) = sUN ("param_" ++ str n)
paramName (MN i n) = sMN i ("param_" ++ str n)
paramName (NS n s) = NS (paramName n) s
paramName n = n
elabProj :: Name -> [(Name, Name, Plicity, PTerm, Docstring (Either Err PTerm), Int)] -> Idris ()
elabProj cn fs = let phArgs = map (uncurry placeholderArg) [(p, n) | (n, _, p, _, _, _) <- fs]
elab = \(n, n', p, t, doc, i) ->
do let t' = projectInType [(m, m') | (m, m', _, _, _, _) <- fs
, not (m `elem` paramNames)] t
elabProjection info n n' p t' doc rsyn fc target cn phArgs fieldNames i
in mapM_ elab fs
elabUp :: Name -> [(Name, Name, Plicity, PTerm, Docstring (Either Err PTerm), Int)] -> Idris ()
elabUp cn fs = let args = map (uncurry asPRefArg) [(p, n) | (n, _, p, _, _, _) <- fs]
elab = \(n, n', p, t, doc, i) -> elabUpdate info n n' p t doc rsyn fc target cn args fieldNames i (optionalSetter n)
in mapM_ elab fs
optionalSetter :: Name -> Bool
optionalSetter n = n `elem` dependedOn
fieldDependencies :: [(Name, [Name])]
fieldDependencies = map (uncurry fieldDep) [(n, t) | (n, _, _, t, _) <- fields ++ params]
where
fieldDep :: Name -> PTerm -> (Name, [Name])
fieldDep n t = ((nsroot n), paramNames ++ fieldNames `intersect` allNamesIn t)
dependentFields :: [Name]
dependentFields = filter depends fieldNames
where
depends :: Name -> Bool
depends n = case lookup n fieldDependencies of
Just xs -> not $ null xs
Nothing -> False
dependedOn :: [Name]
dependedOn = concat ((catMaybes (map (\x -> lookup x fieldDependencies) fieldNames)))
elabProjection :: ElabInfo
-> Name
-> Name
-> Plicity
-> PTerm
-> (Docstring (Either Err PTerm))
-> SyntaxInfo
-> FC -> PTerm
-> Name
-> [PArg]
-> [Name]
-> Int
-> Idris ()
elabProjection info cname pname plicity projTy pdoc psyn fc targetTy cn phArgs fnames index
= do logLvl 1 $ "Generating Projection for " ++ show pname
let ty = generateTy
logLvl 1 $ "Type of " ++ show pname ++ ": " ++ show ty
let lhs = generateLhs
logLvl 1 $ "LHS of " ++ show pname ++ ": " ++ showTmImpls lhs
let rhs = generateRhs
logLvl 1 $ "RHS of " ++ show pname ++ ": " ++ showTmImpls rhs
rec_elabDecl info EAll info ty
let clause = PClause fc pname lhs [] rhs []
rec_elabDecl info EAll info $ PClauses fc [] pname [clause]
where
generateTy :: PDecl
generateTy = PTy pdoc [] psyn fc [] pname NoFC $
PPi expl recName NoFC targetTy projTy
generateLhs :: PTerm
generateLhs = let args = lhsArgs index phArgs
in PApp fc (PRef fc [] pname) [pexp (PApp fc (PRef fc [] cn) args)]
where
lhsArgs :: Int -> [PArg] -> [PArg]
lhsArgs 0 (_ : rest) = (asArg plicity (nsroot cname) (PRef fc [] pname_in)) : rest
lhsArgs i (x : rest) = x : (lhsArgs (i1) rest)
lhsArgs _ [] = []
pname_in :: Name
pname_in = rootname
rootname :: Name
rootname = nsroot cname
generateRhs :: PTerm
generateRhs = PRef fc [] pname_in
elabUpdate :: ElabInfo
-> Name
-> Name
-> Plicity
-> PTerm
-> (Docstring (Either Err PTerm))
-> SyntaxInfo
-> FC -> PTerm
-> Name
-> [PArg]
-> [Name]
-> Int
-> Bool
-> Idris ()
elabUpdate info cname pname plicity pty pdoc psyn fc sty cn args fnames i optional
= do logLvl 1 $ "Generating Update for " ++ show pname
let ty = generateTy
logLvl 1 $ "Type of " ++ show set_pname ++ ": " ++ show ty
let lhs = generateLhs
logLvl 1 $ "LHS of " ++ show set_pname ++ ": " ++ showTmImpls lhs
let rhs = generateRhs
logLvl 1 $ "RHS of " ++ show set_pname ++ ": " ++ showTmImpls rhs
let clause = PClause fc set_pname lhs [] rhs []
idrisCatch (do rec_elabDecl info EAll info ty
rec_elabDecl info EAll info $ PClauses fc [] set_pname [clause])
(\err -> logLvl 1 $ "Could not generate update function for " ++ show pname)
where
generateTy :: PDecl
generateTy = PTy pdoc [] psyn fc [] set_pname NoFC $
PPi expl (nsroot pname) NoFC pty $
PPi expl recName NoFC sty (substInput sty)
where substInput = substMatches [(cname, PRef fc [] (nsroot pname))]
set_pname :: Name
set_pname = set_name pname
set_name :: Name -> Name
set_name (UN n) = sUN ("set_" ++ str n)
set_name (MN i n) = sMN i ("set_" ++ str n)
set_name (NS n s) = NS (set_name n) s
set_name n = n
generateLhs :: PTerm
generateLhs = PApp fc (PRef fc [] set_pname) [pexp $ PRef fc [] pname_in, pexp constructorPattern]
where
constructorPattern :: PTerm
constructorPattern = PApp fc (PRef fc [] cn) args
pname_in :: Name
pname_in = in_name rootname
rootname :: Name
rootname = nsroot pname
generateRhs :: PTerm
generateRhs = PApp fc (PRef fc [] cn) (newArgs i args)
where
newArgs :: Int -> [PArg] -> [PArg]
newArgs 0 (_ : rest) = (asArg plicity (nsroot cname) (PRef fc [] pname_in)) : rest
newArgs i (x : rest) = x : (newArgs (i1) rest)
newArgs _ [] = []
in_name :: Name -> Name
in_name (UN n) = sMN 0 (str n ++ "_in")
in_name (MN i n) = sMN i (str n ++ "_in")
in_name (NS n s) = NS (in_name n) s
in_name n = n
asArg :: Plicity -> Name -> PTerm -> PArg
asArg (Imp os _ _ _) n t = PImp 0 False os n t
asArg (Exp os _ _) n t = PExp 0 os n t
asArg (Constraint os _) n t = PConstraint 0 os n t
asArg (TacImp os _ s) n t = PTacImplicit 0 os n s t
recName :: Name
recName = sMN 0 "rec"
recRef = PRef emptyFC [] recName
projectInType :: [(Name, Name)] -> PTerm -> PTerm
projectInType xs = mapPT st
where
st :: PTerm -> PTerm
st (PRef fc hls n)
| Just pn <- lookup n xs = PApp fc (PRef fc hls pn) [pexp recRef]
st t = t
asPRefArg :: Plicity -> Name -> PArg
asPRefArg p n = asArg p (nsroot n) $ PRef emptyFC [] (nsroot n)