Files
newt/src/Lib/ProcessDecl.idr

282 lines
10 KiB
Idris

module Lib.ProcessDecl
import Data.IORef
import Data.String
import Data.Vect
import Lib.Elab
import Lib.Parser
import Lib.Syntax
import Lib.TopContext
import Lib.Eval
import Lib.Types
import Lib.Util
-- Check that the arguments are not explicit and the type constructor in codomain matches
-- Later we will build a table of codomain type, and maybe make the user tag the candidates
-- like Idris does with %hint
isCandidate : Val -> Tm -> Bool
isCandidate ty (Pi fc nm Explicit t u) = False
isCandidate ty (Pi fc nm icit t u) = isCandidate ty u
isCandidate (VRef _ nm _ _) (Ref fc nm' def) = nm == nm'
isCandidate ty (App fc t u) = isCandidate ty t
isCandidate _ _ = False
-- This is a crude first pass
-- TODO consider ctx
findMatches : Context -> Val -> List TopEntry -> M (List (Tm, MetaContext))
findMatches ctx ty [] = pure []
findMatches ctx ty ((MkEntry name type def@(Fn t)) :: xs) = do
let True = isCandidate ty type | False => findMatches ctx ty xs
top <- get
-- let ctx = mkCtx top.metas (getFC ty)
-- FIXME we're restoring state, but the INFO logs have already been emitted
-- Also redo this whole thing to run during elab, recheck constraints, etc.
mc <- readIORef top.metas
catchError {e=Error} (do
-- TODO sort out the FC here
let fc = getFC ty
debug "TRY \{name} : \{pprint [] type} for \{show ty}"
-- This check is solving metas, so we save mc below in case we want this solution
-- tm <- check (mkCtx top.metas fc) (RVar fc name) ty
tm <- check ctx (RVar fc name) ty
debug "Found \{pprint [] tm} for \{show ty}"
mc' <- readIORef top.metas
writeIORef top.metas mc
((tm, mc') ::) <$> findMatches ctx ty xs)
(\ err => do
debug "No match \{show ty} \{pprint [] type} \{showError "" err}"
writeIORef top.metas mc
findMatches ctx ty xs)
findMatches ctx ty (y :: xs) = findMatches ctx ty xs
contextMatches : Context -> Val -> M (List (Tm, MetaContext))
contextMatches ctx ty = go (zip ctx.env (toList ctx.types))
where
go : List (Val, String, Val) -> M (List (Tm, MetaContext))
go [] = pure []
go ((tm, nm, vty) :: xs) = do
type <- quote ctx.lvl vty
let True = isCandidate ty type | False => go xs
top <- get
mc <- readIORef top.metas
catchError {e=Error} (do
debug "TRY context \{nm} : \{pprint (names ctx) type} for \{show ty}"
unifyCatch (getFC ty) ctx ty vty
mc' <- readIORef top.metas
writeIORef top.metas mc
tm <- quote ctx.lvl tm
((tm, mc') ::) <$> go xs)
(\ err => do
debug "No match \{show ty} \{pprint (names ctx) type} \{showError "" err}"
writeIORef top.metas mc
go xs)
getArity : Tm -> Nat
getArity (Pi x str icit t u) = S (getArity u)
-- Ref or App (of type constructor) are valid
getArity _ = Z
-- Can metas live in context for now?
-- We'll have to be able to add them, which might put gamma in a ref
||| collectDecl collects multiple Def for one function into one
export
collectDecl : List Decl -> List Decl
collectDecl [] = []
collectDecl ((Def fc nm cl) :: rest@(Def _ nm' cl' :: xs)) =
if nm == nm' then collectDecl (Def fc nm (cl ++ cl') :: xs)
else (Def fc nm cl :: collectDecl rest)
collectDecl (x :: xs) = x :: collectDecl xs
-- Makes the arg for `solve` when we solve an auto
makeSpine : Nat -> Vect k BD -> SnocList Val
makeSpine k [] = [<]
makeSpine (S k) (Defined :: xs) = makeSpine k xs
makeSpine (S k) (Bound :: xs) = makeSpine k xs :< VVar emptyFC k [<]
makeSpine 0 xs = ?fixme
solveAutos : Nat -> List MetaEntry -> M ()
solveAutos mlen [] = pure ()
solveAutos mlen ((Unsolved fc k ctx ty AutoSolve _) :: es) = do
debug "AUTO solving \{show k} : \{show ty}"
-- we want the context here too.
top <- get
[(tm, mc)] <- case !(contextMatches ctx ty) of
[] => findMatches ctx ty top.defs
xs => pure xs
| res => do
debug "FAILED to solve \{show ty}, matches: \{show $ map (pprint [] . fst) res}"
solveAutos mlen es
-- | res => error fc "FAILED to solve \{show ty}, matches: \{show $ map (pprint [] . fst) res}"
writeIORef top.metas mc
val <- eval ctx.env CBN tm
debug "SOLUTION \{pprint [] tm} evaled to \{show val}"
let sp = makeSpine ctx.lvl ctx.bds
solve ctx.env k sp val
mc <- readIORef top.metas
solveAutos mlen (take mlen mc.metas)
solveAutos mlen (_ :: es) = solveAutos mlen es
dumpEnv : Context -> M String
dumpEnv ctx =
unlines . reverse <$> go (names ctx) 0 (reverse $ zip ctx.env (toList ctx.types)) []
where
isVar : Nat -> Val -> Bool
isVar k (VVar _ k' [<]) = k == k'
isVar _ _ = False
go : List String -> Nat -> List (Val, String, Val) -> List String -> M (List String)
go _ _ [] acc = pure acc
go names k ((v, n, ty) :: xs) acc = if isVar k v
-- TODO - use Doc and add <+/> as appropriate to printing
then go names (S k) xs (" \{n} : \{pprint names !(quote ctx.lvl ty)}":: acc)
else go names (S k) xs (" \{n} = \{pprint names !(quote ctx.lvl v)} : \{pprint names !(quote ctx.lvl ty)}":: acc)
logMetas : Nat -> M ()
logMetas mstart = do
-- FIXME, now this isn't logged for Sig / Data.
top <- get
mc <- readIORef top.metas
let mlen = length mc.metas `minus` mstart
for_ (reverse $ take mlen mc.metas) $ \case
(Solved fc k soln) => info fc "solve \{show k} as \{pprint [] !(quote 0 soln)}"
(Unsolved fc k ctx ty User cons) => do
ty' <- quote ctx.lvl ty
let names = (toList $ map fst ctx.types)
env <- dumpEnv ctx
let msg = "\{env} -----------\n \{pprint names ty'}"
info fc "User Hole\n\{msg}"
(Unsolved (l,c) k ctx ty kind cons) => do
tm <- quote ctx.lvl !(forceMeta ty)
-- Now that we're collecting errors, maybe we simply check at the end
-- TODO - log constraints?
-- FIXME in Combinatory, the val doesn't match environment?
let msg = "Unsolved meta \{show k} \{show kind} type \{pprint (names ctx) tm} \{show $ length cons} constraints"
msgs <- for cons $ \ (MkMc fc env sp val) => do
putStrLn " ENV \{show env}"
pure " (m\{show k} (\{unwords $ map show $ sp <>> []}) =?= \{show val}"
addError $ E (l,c) $ unlines ([msg] ++ msgs)
export
processDecl : Decl -> M ()
-- REVIEW I supposed I could have updated top here instead of the dance with the parser...
processDecl (PMixFix{}) = pure ()
processDecl (TypeSig fc names tm) = do
top <- get
mc <- readIORef top.metas
let mstart = length mc.metas
for_ names $ \nm => do
let Nothing := lookup nm top
| _ => error fc "\{show nm} is already defined"
pure ()
putStrLn "-----"
putStrLn "TypeSig \{unwords names} : \{show tm}"
ty <- check (mkCtx top.metas fc) tm (VU fc)
debug "got \{pprint [] ty}"
for_ names $ \nm => setDef nm fc ty Axiom
-- Zoo4eg has metas in TypeSig, need to decide if I want to support that going forward.
-- logMetas mstart
processDecl (PType fc nm ty) = do
top <- get
ty' <- check (mkCtx top.metas fc) (maybe (RU fc) id ty) (VU fc)
setDef nm fc ty' PrimTCon
processDecl (PFunc fc nm ty src) = do
top <- get
ty <- check (mkCtx top.metas fc) ty (VU fc)
ty' <- nf [] ty
putStrLn "pfunc \{nm} : \{pprint [] ty'} := \{show src}"
setDef nm fc ty' (PrimFn src)
processDecl (Def fc nm clauses) = do
putStrLn "-----"
putStrLn "def \{show nm}"
top <- get
mc <- readIORef top.metas
let mstart = length mc.metas
let Just entry = lookup nm top
| Nothing => throwError $ E fc "No declaration for \{nm}"
let (MkEntry name ty Axiom) := entry
| _ => throwError $ E fc "\{nm} already defined"
putStrLn "check \{nm} ... at \{pprint [] ty}"
vty <- eval empty CBN ty
putStrLn "vty is \{show vty}"
-- I can take LHS apart syntactically or elaborate it with an infer
clauses' <- traverse (makeClause top) clauses
tm <- buildTree (mkCtx top.metas fc) (MkProb clauses' vty)
putStrLn "Ok \{pprint [] tm}"
mc <- readIORef top.metas
let mlen = length mc.metas `minus` mstart
solveAutos mlen (take mlen mc.metas)
-- Expand metas
-- tm' <- nf [] tm -- TODO - make nf that expands all metas, Day1.newt is a test case
tm' <- zonk top 0 [] tm
putStrLn "NF \{pprint[] tm'}"
debug "Add def \{nm} \{pprint [] tm'} : \{pprint [] ty}"
updateDef nm fc ty (Fn tm')
logMetas mstart
processDecl (DCheck fc tm ty) = do
putStrLn "----- DCheck"
top <- get
putStrLn "INFO at \{show fc}: check \{show tm} at \{show ty}"
ty' <- check (mkCtx top.metas fc) ty (VU fc)
putStrLn " got type \{pprint [] ty'}"
vty <- eval [] CBN ty'
res <- check (mkCtx top.metas fc) tm vty
putStrLn " got \{pprint [] res}"
norm <- nf [] res
putStrLn " NF \{pprint [] norm}"
norm <- nfv [] res
putStrLn " NFV \{pprint [] norm}"
processDecl (Data fc nm ty cons) = do
putStrLn "-----"
putStrLn "process data \{nm}"
top <- get
mc <- readIORef top.metas
let mstart = length mc.metas
tyty <- check (mkCtx top.metas fc) ty (VU fc)
case lookup nm top of
Just (MkEntry name type Axiom) => do
unifyCatch fc (mkCtx top.metas fc) !(eval [] CBN tyty) !(eval [] CBN type)
Just (MkEntry name type _) => error fc "\{show nm} already declared"
Nothing => setDef nm fc tyty Axiom
cnames <- for cons $ \x => case x of
(TypeSig fc names tm) => do
debug "check dcon \{show names} \{show tm}"
dty <- check (mkCtx top.metas fc) tm (VU fc)
debug "dty \{show names} is \{pprint [] dty}"
-- We only check that codomain uses the right type constructor
-- We know it's in U because it's part of a checked Pi type
let (codomain, tele) = splitTele dty
-- for printing
let tnames = reverse $ map (\(MkBind _ nm _ _) => nm) tele
let (Ref _ hn _, args) := funArgs codomain
| (tm, _) => error (getFC tm) "expected \{nm} got \{pprint tnames tm}"
when (hn /= nm) $
error (getFC codomain) "Constructor codomain is \{pprint tnames codomain} rather than \{nm}"
for_ names $ \ nm' => do
setDef nm' fc dty (DCon (getArity dty) nm)
pure names
_ => throwError $ E (0,0) "expected constructor declaration"
putStrLn "setDef \{nm} TCon \{show $ join cnames}"
updateDef nm fc tyty (TCon (join cnames))
logMetas mstart
where
checkDeclType : Tm -> M ()
checkDeclType (U _) = pure ()
checkDeclType (Pi _ str icit t u) = checkDeclType u
checkDeclType _ = error fc "data type doesn't return U"