open Printer open Error open Exp let extPiG : value -> value * clos = function | VPi (t, g) -> (t, g) | u -> raise (ExpectedPi (rbV u)) let extSigG : value -> value * clos = function | VSig (t, g) -> (t, g) | u -> raise (ExpectedSig (rbV u)) let extSet : value -> Z.t = function | VPre n | VKan n -> n | v -> raise (ExpectedVSet (rbV v)) let extKan : value -> Z.t = function | VKan n -> n | v -> raise (ExpectedFibrant (rbV v)) let extPathP = function | VApp (VApp (VPathP v, u0), u1) -> (v, u0, u1) | v -> raise (ExpectedPath (rbV v)) let extVar ctx x = match Env.find_opt x ctx with | Some (_, _, Value (Var (y, _))) -> y | Some (_, _, Exp (EVar y)) -> y | _ -> x let idv t x y = VApp (VApp (VId t, x), y) let implv a b = VPi (a, (Irrefutable, fun _ -> b)) let hcompval u = EApp (EApp (u, ezero), ERef eone) let imax a b = match a, b with | VKan u, VKan v -> VKan (max u v) | VPre u, VPre v | VPre u, VKan v | VKan u, VPre v -> VPre (max u v) | VKan _, _ | VPre _, _ -> raise (ExpectedVSet (rbV b)) | _, _ -> raise (ExpectedVSet (rbV a)) let orEq f g = Disjunction.equal (Dnf.uniq (Dnf.extOr f)) (Dnf.uniq (Dnf.extOr g)) let andEq f g = Conjunction.equal (Dnf.extAnd f) (Dnf.extAnd g) let compatible xs ys = Env.merge (fun _ x y -> match x, y with | Some d1, Some d2 -> Some (d1 = d2) | _, _ -> Some true) xs ys |> Env.for_all (fun _ b -> b) let leq xs ys = Env.for_all (fun k d1 -> match Env.find_opt k ys with | Some d2 -> d1 = d2 | None -> false) xs let lt xs ys = not (Env.equal (=) xs ys) && leq xs ys let comparable xs ys = leq xs ys || leq ys xs let meet = Env.union (fun _ x y -> if x = y then Some x else raise IncompatibleFaces) let nubRev xs = let ys = ref [] in List.iter (fun x -> if not (List.mem x !ys) then ys := x :: !ys) xs; !ys let meets xs ys = let zs = ref [] in List.iter (fun x -> List.iter (fun y -> try zs := meet x y :: !zs with IncompatibleFaces -> ()) ys) xs; nubRev !zs let eps : face = Env.empty let meetss = List.fold_left meets [eps] let union xs ys = nubRev (List.rev_append xs ys) let mkSystem xs = System.of_seq (List.to_seq xs) let unionSystem xs ys = System.union (fun _ _ _ -> raise (Failure "unionSystem")) xs ys let sign x = function | Zero -> ENeg (EVar x) | One -> EVar x let getFace xs = Env.fold (fun x d y -> EAnd (y, sign x d)) xs (EDir One) let getFormula ts = System.fold (fun x _ e -> EOr (getFace x, e)) ts (EDir Zero) let singleton p x = Env.add p x Env.empty let getFaceV xs = Env.fold (fun x d y -> Dnf.andFormula y (Dnf.contrAtom (x, d))) xs vone let getFormulaV ts = System.fold (fun x _ v -> Dnf.orFormula (Dnf.getFaceV x) v) ts vzero let rec solve k x = match k, x with | VDir y, _ -> if x = y then [eps] else [] | Var (p, _), _ -> [singleton p x] | VNeg n, _ -> solve n (negDir x) | VOr (f, g), One | VAnd (f, g), Zero -> union (solve f x) (solve g x) | VOr (f, g), Zero | VAnd (f, g), One -> meets (solve f x) (solve g x) | _, _ -> failwith (Printf.sprintf "Cannot solve: %s = %s" (showExp (rbV k)) (showDir x)) let freshDim () = let i = freshName "ι" in (i, EVar i, Var (i, VI)) let ieq u v : bool = !girard || u = v let vfst : value -> value = function | VPair (_, u, _) -> u | v -> VFst v let vsnd : value -> value = function | VPair (_, _, u) -> u | v -> VSnd v let rec eval (e0 : exp) (ctx : ctx) = match e0 with | EPre u -> VPre u | EKan u -> VKan u | EVar x -> getRho ctx x | EHole -> VHole | EPi (a, (p, b)) -> let t = eval a ctx in VPi (t, (p, closByVal ctx p t b)) | ESig (a, (p, b)) -> let t = eval a ctx in VSig (t, (p, closByVal ctx p t b)) | ELam (a, (p, b)) -> let t = eval a ctx in VLam (t, (p, closByVal ctx p t b)) | EApp (f, x) -> app (eval f ctx, eval x ctx) | EPair (r, e1, e2) -> VPair (r, eval e1 ctx, eval e2 ctx) | EFst e -> vfst (eval e ctx) | ESnd e -> vsnd (eval e ctx) | EField (e, p) -> evalField p (eval e ctx) | EId e -> VId (eval e ctx) | ERef e -> VRef (eval e ctx) | EJ e -> VJ (eval e ctx) | EPathP e -> VPathP (eval e ctx) | EPLam e -> VPLam (eval e ctx) | EAppFormula (e, x) -> appFormula (eval e ctx) (eval x ctx) | EI -> VI | EDir d -> VDir d | EAnd (e1, e2) -> evalAnd (eval e1 ctx) (eval e2 ctx) | EOr (e1, e2) -> evalOr (eval e1 ctx) (eval e2 ctx) | ENeg e -> Dnf.negFormula (eval e ctx) | ETransp (p, i) -> VTransp (eval p ctx, eval i ctx) | EHComp (t, r, u, u0) -> hcomp (eval t ctx) (eval r ctx) (eval u ctx) (eval u0 ctx) | EPartial e -> let (i, _, _) = freshDim () in VLam (VI, (i, fun r -> let ts = mkSystem (List.map (fun mu -> (mu, eval e (faceEnv mu ctx))) (solve r One)) in VPartialP (VSystem ts, r))) | EPartialP (t, r) -> VPartialP (eval t ctx, eval r ctx) | ESystem xs -> VSystem (evalSystem ctx xs) | ESub (a, i, u) -> VSub (eval a ctx, eval i ctx, eval u ctx) | EInc (t, r) -> VInc (eval t ctx, eval r ctx) | EOuc e -> evalOuc (eval e ctx) | EEmpty -> VEmpty | EIndEmpty e -> VIndEmpty (eval e ctx) | EUnit -> VUnit | EStar -> VStar | EIndUnit e -> VIndUnit (eval e ctx) | EBool -> VBool | EFalse -> VFalse | ETrue -> VTrue | EIndBool e -> VIndBool (eval e ctx) | EW (a, (p, b)) -> let t = eval a ctx in VW (t, (fresh p, closByVal ctx p t b)) | ESup (a, b) -> VSup (eval a ctx, eval b ctx) | EIndW (a, b, c) -> VIndW (eval a ctx, eval b ctx, eval c ctx) and appFormula v x = match v with | VPLam f -> app (f, x) | _ -> let (_, u0, u1) = extPathP (inferV v) in begin match x with | VDir Zero -> u0 | VDir One -> u1 | i -> VAppFormula (v, i) end and evalAnd a b = Dnf.evalAnd a b and evalOr a b = Dnf.evalOr a b and border xs v = mkSystem (List.map (fun alpha -> (alpha, upd alpha v)) xs) and partialv t r = VPartialP (VSystem (border (solve r One) t) , r) and transport p phi u0 = let (_, _, v) = freshDim () in match appFormula p v, phi with (* transp p 1 u₀ ~> u₀ *) | _, VDir One -> u0 (* transp (<_> U) i A ~> A *) | VKan _, _ -> u0 (* transp ( Π (x : A i), B i x) φ u₀ ~> λ (x : A 1), transp ( B i (transFill ( A -j) φ x i)) φ (u₀ (transFill ( A -j) φ x 1)) *) | VPi _, _ -> let x = fresh (ident "x") in let (i, _, _) = freshDim () in let (j, _, _) = freshDim () in let (t, _) = extPiG (appFormula p vone) in VLam (t, (x, fun x -> let v = transFill (VPLam (VLam (VI, (j, fun j -> fst (extPiG (appFormula p (Dnf.negFormula j))))))) phi x in transport (VPLam (VLam (VI, (i, fun i -> let (_, (_, b)) = extPiG (appFormula p i) in b (v (Dnf.negFormula i)))))) phi (app (u0, v vone)))) (* transp ( Σ (x : A i), B i x) φ u₀ ~> (transp ( A j) φ u₀.1, transp ( B i (transFill ( A j) φ u₀.1 i)) φ u₀.2) *) | VSig _, _ -> let (i, _, _) = freshDim () in let (j, _, _) = freshDim () in let a = VPLam (VLam (VI, (j, fun j -> fst (extSigG (appFormula p j))))) in let v1 = transFill a phi (vfst u0) in let v2 = transport (VPLam (VLam (VI, (i, fun i -> let (_, (_, b)) = extSigG (appFormula p i) in b (v1 i))))) phi (vsnd u0) in VPair (ref None, v1 vone, v2) (* transp ( PathP (P i) (v i) (w i)) φ u₀ ~> comp (λ i, P i @ j) (φ ∨ j ∨ -j) (λ (i : I), [(φ = 1) → u₀ @ j, (j = 0) → v i, (j = 1) → w i]) (u₀ @ j) *) | VApp (VApp (VPathP _, _), _), _ -> let i = fresh (ident "ι") in let j = fresh (ident "υ") in VPLam (VLam (VI, (j, fun j -> let uj = appFormula u0 j in let r = Dnf.orFormula phi (Dnf.orFormula j (Dnf.negFormula j)) in comp (fun i -> let (q, _, _) = extPathP (appFormula p i) in appFormula q j) r (VLam (VI, (i, fun i -> let (_, v, w) = extPathP (appFormula p i) in VSystem (unionSystem (border (solve phi One) uj) (unionSystem (border (solve j Zero) v) (border (solve j One) w)))))) uj))) | _, _ -> VApp (VTransp (p, phi), u0) and hcomp t r u u0 = match t, r with (* hcomp A 1 u u₀ ~> u 1 1=1 *) | _, VDir One -> app (app (u, vone), VRef vone) (* hcomp (Π (x : A), B x) φ u u₀ ~> λ (x : A), hcomp (B x) φ (λ (i : I), [φ → u i 1=1 x]) (u₀ x) *) | VPi (t, (_, b)), _ -> let (i, _, _) = freshDim () in let x = fresh (ident "x") in VLam (t, (x, fun x -> hcomp (b x) r (VLam (VI, (i, fun i -> VSystem (border (solve r One) (app (app (app (u, i), VRef vone), x)))))) (app (u0, x)))) (* hcomp (Σ (x : A), B x) φ u u₀ ~> (hfill A φ (λ (k : I), [(r = 1) → (u k 1=1).1]) u₀.1 1, comp (λ i, B (hfill A φ (λ (k : I), [(r = 1) → (u k 1=1).1]) u₀.1 i)) φ (λ (k : I), [(r = 1) → (u k 1=1).2]) u₀.2) *) | VSig (t, (_, b)), _ -> let (k, _, _) = freshDim () in let v1 = hfill t r (VLam (VI, (k, fun k -> VSystem (border (solve r One) (vfst (app (app (u, k), VRef vone))))))) (vfst u0) in let v2 = comp (v1 >> b) r (VLam (VI, (k, fun k -> VSystem (border (solve r One) (vsnd (app (app (u, k), VRef vone))))))) (vsnd u0) in VPair (ref None, v1 vone, v2) (* hcomp (PathP A v w) φ u u₀ ~> hcomp (A @ j) (λ (i : I), [(r = 1) → u i 1=1, (j = 0) → v, (j = 1) → w]) (u₀ @ j) *) | VApp (VApp (VPathP t, v), w), _ -> let (j, _, _) = freshDim () in let (i, _, _) = freshDim () in VPLam (VLam (VI, (j, fun j -> hcomp (appFormula t j) (Dnf.orFormula r (Dnf.orFormula j (Dnf.negFormula j))) (VLam (VI, (i, fun i -> (VSystem (unionSystem (border (solve r One) (appFormula (app (app (u, i), VRef vone)) j)) (unionSystem (border (solve j Zero) v) (border (solve j One) w))))))) (appFormula u0 j)))) | _, _ -> VHComp (t, r, u, u0) and inc t r v = app (VInc (t, r), v) and comp t r u u0 = let (i, _, _) = freshDim () in let (j, _, _) = freshDim () in hcomp (t vone) r (VLam (VI, (i, fun i -> let u1 = transport (VPLam (VLam (VI, (j, fun j -> t (Dnf.orFormula i j))))) i (app (app (u, i), VRef vone)) in VSystem (border (solve r One) u1)))) (transport (VPLam (VLam (VI, (i, t)))) vzero u0) and hfill t r u u0 j = let (i, _, _) = freshDim () in hcomp t (Dnf.orFormula (Dnf.negFormula j) r) (VLam (VI, (i, fun i -> VSystem (unionSystem (border (solve r One) (app (app (u, Dnf.andFormula i j), VRef vone))) (border (solve j Zero) u0))))) u0 and transFill p phi u0 j = let (i, _, _) = freshDim () in transport (VPLam (VLam (VI, (i, fun i -> appFormula p (Dnf.andFormula i j))))) (Dnf.orFormula phi (Dnf.negFormula j)) u0 and closByVal ctx p t e v = (* dirty hack to handle free variables introduced by type checker, for example, while checking terms like p : Path P a b *) let ctx' = match v with | Var (x, t) -> if Env.mem x ctx then ctx else upLocal ctx x t v | _ -> ctx in eval e (upLocal ctx' p t v) and app : value * value -> value = function | VApp (VApp (VApp (VApp (VJ _, _), _), f), _), VRef _ -> f | VTransp (p, i), u0 -> transport p i u0 | VSystem ts, x -> reduceSystem ts x | VLam (_, (_, f)), v -> f v | VInc _, VOuc v -> v | VApp (VIndUnit _, f), VStar -> f | VApp (VApp (VIndBool _, f), _), VFalse -> f | VApp (VApp (VIndBool _, _), g), VTrue -> g | VApp (VIndW (a, b, c), g), VApp (VApp (VSup (_, b1), x), f) -> let rec_call = VLam (app (b1, x), (fresh (ident "b"), fun b' -> app (app (VIndW (a, b, c), g), app (f, b')))) in app (app (app (g, x), f), rec_call) | f, x -> VApp (f, x) and evalSystem ctx ts = let ts' = System.fold (fun alpha talpha -> Env.bindings alpha |> List.rev_map (fun (i, d) -> solve (getRho ctx i) d) |> meetss |> List.rev_map (fun beta -> (beta, eval talpha (faceEnv beta ctx))) |> List.rev_append) ts [] in List.filter (fun (alpha, _) -> not (List.exists (fun (beta, _) -> lt beta alpha) ts')) ts' |> mkSystem and reduceSystem ts x = match System.find_opt eps ts with | Some v -> v | None -> VApp (VSystem ts, x) and evalOuc v = match v, inferV v with | _, VSub (_, VDir One, u) -> app (u, VRef vone) | VApp (VInc _, v), _ -> v | _, _ -> VOuc v and getRho ctx x = match Env.find_opt x ctx with | Some (_, _, Value v) -> v | Some (_, _, Exp e) -> eval e ctx | None -> raise (VariableNotFound x) and act e i ctx = eval (EAppFormula (e, i)) ctx and inferV v = match v with | VPi (t, (x, f)) | VSig (t, (x, f)) -> imax (inferV t) (inferV (f (Var (x, t)))) | VLam (t, (x, f)) -> VPi (t, (x, fun x -> inferV (f x))) | VPLam (VLam (VI, (_, g))) -> let t = VLam (VI, (freshName "ι", g >> inferV)) in VApp (VApp (VPathP (VPLam t), g vzero), g vone) | VW (t, (x, f)) -> inferVTele imax t x f | Var (_, t) -> t | VFst e -> fst (extSigG (inferV e)) | VSnd e -> let (_, (_, g)) = extSigG (inferV e) in g (vfst e) | VApp (VTransp (p, _), _) -> appFormula p vone | VApp (f, x) -> begin match inferV f with | VPartialP (t, _) -> app (t, x) | VPi (_, (_, g)) -> g x | v -> raise (ExpectedPi (rbV v)) end | VAppFormula (f, x) -> let (p, _, _) = extPathP (inferV f) in appFormula p x | VRef v -> VApp (VApp (VId (inferV v), v), v) | VPre n -> VPre (Z.succ n) | VKan n -> VKan (Z.succ n) | VI -> VPre Z.zero | VInc (t, i) -> inferInc t i | VOuc v -> begin match inferV v with | VSub (t, _, _) -> t | _ -> raise (ExpectedSubtype (rbV v)) end | VId v -> let n = extSet (inferV v) in implv v (implv v (VPre n)) | VJ v -> inferJ v (inferV v) | VPathP p -> let (_, _, v) = freshDim () in let t = inferV (appFormula p v) in let v0 = appFormula p vzero in let v1 = appFormula p vone in implv v0 (implv v1 t) | VDir _ | VOr _ | VAnd _ | VNeg _ -> VI | VTransp (p, _) -> implv (appFormula p vzero) (appFormula p vone) | VHComp (t, _, _, _) -> t | VSub (t, _, _) -> VPre (extSet (inferV t)) | VPartialP (VSystem ts, _) -> begin match System.choose_opt ts with | Some (_, t) -> VPre (extSet (inferV t)) | None -> VPre Z.zero end | VPartialP (t, _) -> inferV (inferV t) | VSystem ts -> VPartialP (VSystem (System.map inferV ts), getFormulaV ts) | VEmpty | VUnit | VBool -> VKan Z.zero | VStar -> VUnit | VFalse | VTrue -> VBool | VIndEmpty t -> implv VEmpty t | VIndUnit t -> recUnit t | VIndBool t -> recBool t | VSup (a, b) -> inferSup a b | VIndW (a, b, c) -> inferIndW a b c | VPLam _ | VPair _ | VHole -> raise (InferError (rbV v)) and inferVTele g t x f = g (inferV t) (inferV (f (Var (x, t)))) and extByTag p : value -> value option = function | VPair (t, fst, snd) -> begin match !t with | Some q -> if p = q then Some fst else extByTag p snd | _ -> extByTag p snd end | _ -> None and evalField p v = match extByTag p v with | Some k -> k | None -> begin match inferV v with | VSig (_, (q, _)) -> if matchIdent p q then vfst v else evalField p (vsnd v) | t -> raise (ExpectedSig (rbV t)) end and upd e = function | VLam (t, (x, g)) -> VLam (upd e t, (x, g >> upd e)) | VPair (r, u, v) -> VPair (r, upd e u, upd e v) | VKan u -> VKan u | VPi (t, (x, g)) -> VPi (upd e t, (x, g >> upd e)) | VSig (t, (x, g)) -> VSig (upd e t, (x, g >> upd e)) | VPre u -> VPre u | VPLam f -> VPLam (upd e f) | Var (i, VI) -> begin match Env.find_opt i e with | Some d -> VDir d | None -> Var (i, VI) end | Var (x, t) -> Var (x, upd e t) | VApp (f, x) -> app (upd e f, upd e x) | VFst k -> vfst (upd e k) | VSnd k -> vsnd (upd e k) | VHole -> VHole | VPathP v -> VPathP (upd e v) | VPartialP (t, r) -> VPartialP (upd e t, upd e r) | VSystem ts -> VSystem (System.bindings ts |> List.filter_map (fun (e', v) -> if compatible e e' then Some (Env.filter (fun k _ -> not (Env.mem k e)) e', upd e v) else None) |> mkSystem) | VSub (t, i, u) -> VSub (upd e t, upd e i, upd e u) | VTransp (p, i) -> VTransp (upd e p, upd e i) | VHComp (t, r, u, u0) -> hcomp (upd e t) (upd e r) (upd e u) (upd e u0) | VAppFormula (f, x) -> appFormula (upd e f) (upd e x) | VId v -> VId (upd e v) | VRef v -> VRef (upd e v) | VJ v -> VJ (upd e v) | VI -> VI | VDir d -> VDir d | VAnd (u, v) -> Dnf.andFormula (upd e u) (upd e v) | VOr (u, v) -> Dnf.orFormula (upd e u) (upd e v) | VNeg u -> Dnf.negFormula (upd e u) | VInc (t, r) -> VInc (upd e t, upd e r) | VOuc v -> evalOuc (upd e v) | VEmpty -> VEmpty | VIndEmpty v -> VIndEmpty (upd e v) | VUnit -> VUnit | VStar -> VStar | VIndUnit v -> VIndUnit (upd e v) | VBool -> VBool | VFalse -> VFalse | VTrue -> VTrue | VIndBool v -> VIndBool (upd e v) | VW (t, (x, g)) -> VW (upd e t, (x, g >> upd e)) | VSup (a, b) -> VSup (upd e a, upd e b) | VIndW (a, b, c) -> VIndW (upd e a, upd e b, upd e c) and updTerm alpha = function | Exp e -> Exp e | Value v -> Value (upd alpha v) and faceEnv alpha ctx = Env.map (fun (p, t, v) -> if p = Local then (p, updTerm alpha t, updTerm alpha v) else (p, t, v)) ctx |> Env.fold (fun p dir -> Env.add p (Local, Value VI, Value (VDir dir))) alpha and rbV v : exp = match v with | VLam (t, g) -> rbVTele eLam t g | VPair (r, u, v) -> EPair (r, rbV u, rbV v) | VKan u -> EKan u | VPi (t, g) -> rbVTele ePi t g | VSig (t, g) -> rbVTele eSig t g | VPre u -> EPre u | VPLam f -> EPLam (rbV f) | Var (x, _) -> EVar x | VApp (f, x) -> EApp (rbV f, rbV x) | VFst k -> EFst (rbV k) | VSnd k -> ESnd (rbV k) | VHole -> EHole | VPathP v -> EPathP (rbV v) | VPartialP (t, r) -> EPartialP (rbV t, rbV r) | VSystem ts -> ESystem (System.map rbV ts) | VSub (a, i, u) -> ESub (rbV a, rbV i, rbV u) | VTransp (p, i) -> ETransp (rbV p, rbV i) | VHComp (t, r, u, u0) -> EHComp (rbV t, rbV r, rbV u, rbV u0) | VAppFormula (f, x) -> EAppFormula (rbV f, rbV x) | VId v -> EId (rbV v) | VRef v -> ERef (rbV v) | VJ v -> EJ (rbV v) | VI -> EI | VDir d -> EDir d | VAnd (u, v) -> EAnd (rbV u, rbV v) | VOr (u, v) -> EOr (rbV u, rbV v) | VNeg u -> ENeg (rbV u) | VInc (t, r) -> EInc (rbV t, rbV r) | VOuc v -> EOuc (rbV v) | VEmpty -> EEmpty | VIndEmpty v -> EIndEmpty (rbV v) | VUnit -> EUnit | VStar -> EStar | VIndUnit v -> EIndUnit (rbV v) | VBool -> EBool | VFalse -> EFalse | VTrue -> ETrue | VIndBool v -> EIndBool (rbV v) | VW (t, g) -> rbVTele eW t g | VSup (a, b) -> ESup (rbV a, rbV b) | VIndW (a, b, c) -> EIndW (rbV a, rbV b, rbV c) and rbVTele ctor t (p, g) = let x = Var (p, t) in ctor p (rbV t) (rbV (g x)) and prune ctx x = match Env.find_opt x ctx with | Some (_, _, Exp e) -> e | Some (_, _, Value v) -> rbV v | None -> raise (VariableNotFound x) and conv v1 v2 : bool = v1 == v2 || convInd v1 v2 || begin match v1, v2 with | VKan u, VKan v -> ieq u v | VPair (_, a, b), VPair (_, c, d) -> conv a c && conv b d | VPair (_, a, b), v | v, VPair (_, a, b) -> conv (vfst v) a && conv (vsnd v) b | VPi (a, (p, f)), VPi (b, (_, g)) | VSig (a, (p, f)), VSig (b, (_, g)) | VLam (a, (p, f)), VLam (b, (_, g)) -> let x = Var (p, a) in conv a b && conv (f x) (g x) | VLam (a, (p, f)), b | b, VLam (a, (p, f)) -> let x = Var (p, a) in conv (app (b, x)) (f x) | VW (a, (p, f)), VW (b, (_, g)) -> let x = Var (p, a) in conv a b && conv (f x) (g x) | VPre u, VPre v -> ieq u v | VPLam f, VPLam g -> conv f g | VPLam f, v | v, VPLam f -> let (_, _, i) = freshDim () in conv (appFormula v i) (app (f, i)) | Var (u, _), Var (v, _) -> u = v | VApp (f, a), VApp (g, b) -> conv f g && conv a b | VFst x, VFst y | VSnd x, VSnd y -> conv x y | VPathP a, VPathP b -> conv a b | VPartialP (t1, r1), VPartialP (t2, r2) -> conv t1 t2 && conv r1 r2 | VAppFormula (f, x), VAppFormula (g, y) -> conv f g && conv x y | VSystem xs, VSystem ys -> keys xs = keys ys && System.for_all (fun _ b -> b) (intersectionWith conv xs ys) | VSystem xs, x | x, VSystem xs -> System.for_all (fun alpha y -> conv (app (upd alpha x, VRef vone)) y) xs | VTransp (p, i), VTransp (q, j) -> conv p q && conv i j | VHComp (t1, r1, u1, v1), VHComp (t2, r2, u2, v2) -> conv t1 t2 && conv r1 r2 && conv u1 u2 && conv v1 v2 | VSub (a, i, u), VSub (b, j, v) -> conv a b && conv i j && conv u v | VOr (x, y), VDir Zero | VAnd (x, y), VDir One -> conv x v2 && conv y v2 | VOr (x, y), VDir One | VAnd (x, y), VDir Zero -> conv x v2 || conv y v2 | VOr _, _ | _, VOr _ -> orEq v1 v2 | VAnd _, _ | _, VAnd _ -> andEq v1 v2 | VNeg x, VNeg y -> conv x y | VI, VI -> true | VDir u, VDir v -> u = v | VId u, VId v | VJ u, VJ v -> conv u v | VInc (t1, r1), VInc (t2, r2) -> conv t1 t2 && conv r1 r2 | VOuc u, VOuc v -> conv u v | VEmpty, VEmpty -> true | VIndEmpty u, VIndEmpty v -> conv u v | VUnit, VUnit -> true | VStar, VStar -> true | VIndUnit u, VIndUnit v -> conv u v | VBool, VBool -> true | VFalse, VFalse -> true | VTrue, VTrue -> true | VIndBool u, VIndBool v -> conv u v | VSup (a1, b1), VSup (a2, b2) -> conv a1 a2 && conv b1 b2 | VIndW (a1, b1, c1), VIndW (a2, b2, c2) -> conv a1 a2 && conv b1 b2 && conv c1 c2 | _, _ -> false end || convWithSystem (v1, v2) || convId v1 v2 and convWithSystem = function | v, VApp (VSystem ts, _) | VApp (VSystem ts, _), v -> System.for_all (fun mu -> conv (upd mu v)) ts | _, _ -> false and convId v1 v2 = (* Id A a b is proof-irrelevant *) try match inferV v1, inferV v2 with | VApp (VApp (VId t1, a1), b1), VApp (VApp (VId t2, a2), b2) -> conv t1 t2 && conv a1 a2 && conv b1 b2 | _, _ -> false with ExpectedNeutral _ -> false and convInd v1 v2 = try match inferV v1, inferV v2 with | VUnit, VUnit -> true | VEmpty, VEmpty -> true | _, _ -> false with _ -> false and eqNf v1 v2 : unit = if conv v1 v2 then () else raise (Ineq (rbV v1, rbV v2)) and lookup (x : ident) (ctx : ctx) = match Env.find_opt x ctx with | Some (_, Value v, _) -> v | Some (_, Exp e, _) -> eval e ctx | None -> raise (VariableNotFound x) and check ctx (e0 : exp) (t0 : value) = try match e0, t0 with | ELam (a, (p, b)), VPi (t, (_, g)) -> ignore (extSet (infer ctx a)); eqNf (eval a ctx) t; let x = Var (p, t) in let ctx' = upLocal ctx p t x in check ctx' b (g x) | EPair (r, e1, e2), VSig (t, (p, g)) -> ignore (extSet (inferV t)); check ctx e1 t; check ctx e2 (g (eval e1 ctx)); begin match p with | Ident (v, _) -> r := Some v | Irrefutable -> () end | EHole, v -> traceHole v ctx | EPLam (ELam (EI, (i, e))), VApp (VApp (VPathP p, u0), u1) -> let v = Var (i, VI) in let ctx' = upLocal ctx i VI v in let v0 = eval e (upLocal ctx i VI vzero) in let v1 = eval e (upLocal ctx i VI vone) in check ctx' e (appFormula p v); eqNf v0 u0; eqNf v1 u1 | e, VPre u -> begin match infer ctx e with | VKan v | VPre v -> if ieq u v then () else raise (Ineq (rbV (VPre u), rbV (VPre v))) | t -> raise (Ineq (rbV (VPre u), rbV t)) end | ESystem ts, VPartialP (u, i) -> eqNf (eval (getFormula ts) ctx) i; System.iter (fun alpha e -> check (faceEnv alpha ctx) e (app (upd alpha u, VRef vone))) ts; checkOverlapping ctx ts | e, t -> eqNf (infer ctx e) t with ex -> Printf.printf "When trying to typecheck\n %s\nAgainst type\n %s\n" (showExp e0) (showExp (rbV t0)); raise ex and checkOverlapping ctx ts = System.iter (fun alpha e1 -> System.iter (fun beta e2 -> if comparable alpha beta then let ctx' = faceEnv (meet alpha beta) ctx in eqNf (eval e1 ctx') (eval e2 ctx') else ()) ts) ts and infer ctx e : value = match e with | EVar x -> lookup x ctx | EKan u -> VKan (Z.succ u) | EPi (a, (p, b)) -> inferTele ctx imax p a b | ESig (a, (p, b)) | EW (a, (p, b)) -> inferTele ctx imax p a b | ELam (a, (p, b)) -> inferLam ctx p a b | EApp (f, x) -> begin match infer ctx f with | VPartialP (t, i) -> check ctx x (isOne i); app (t, eval x ctx) | VPi (t, (_, g)) -> check ctx x t; g (eval x ctx) | v -> raise (ExpectedPi (rbV v)) end | EFst e -> fst (extSigG (infer ctx e)) | ESnd e -> let (_, (_, g)) = extSigG (infer ctx e) in g (vfst (eval e ctx)) | EField (e, p) -> inferField ctx p e | EPre u -> VPre (Z.succ u) | EPathP p -> inferPath ctx p | EPLam p -> inferV (eval (EPLam p) ctx) | EPair (_, e1, e2) -> let t1 = infer ctx e1 in let t2 = infer ctx e2 in VSig (t1, (Irrefutable, fun _ -> t2)) | EPartial e -> let n = extSet (infer ctx e) in implv VI (VPre n) | EPartialP (u, r0) -> check ctx r0 VI; let t = infer ctx u in begin match t with | VPartialP (ts, r) -> eqNf r (eval r0 ctx); inferV (inferV ts) | _ -> failwith "Expected partial function into universe" end | EAppFormula (f, x) -> check ctx x VI; let (p, _, _) = extPathP (infer ctx (rbV (eval f ctx))) in appFormula p (eval x ctx) | ETransp (p, i) -> inferTransport ctx p i | EHComp (e, i, u, u0) -> let t = eval e ctx in let r = eval i ctx in ignore (extKan (infer ctx e)); check ctx i VI; check ctx u (implv VI (partialv t r)); check ctx u0 t; List.iter (fun phi -> let ctx' = faceEnv phi ctx in eqNf (eval (hcompval u) ctx') (eval u0 ctx')) (solve r One); t | ESub (a, i, u) -> let n = extSet (infer ctx a) in check ctx i VI; check ctx u (partialv (eval a ctx) (eval i ctx)); VPre n | EI -> VPre Z.zero | EDir _ -> VI | ENeg e -> check ctx e VI; VI | EOr (e1, e2) | EAnd (e1, e2) -> check ctx e1 VI; check ctx e2 VI; VI | EId e -> let v = eval e ctx in let n = extSet (infer ctx e) in implv v (implv v (VPre n)) | ERef e -> let v = eval e ctx in let t = infer ctx e in VApp (VApp (VId t, v), v) | EJ e -> inferJ (eval e ctx) (infer ctx e) | EInc (e, r) -> ignore (extKan (infer ctx e)); check ctx r VI; inferInc (eval e ctx) (eval r ctx) | EOuc e -> begin match infer ctx e with | VSub (t, _, _) -> t | _ -> raise (ExpectedSubtype e) end | ESystem ts -> checkOverlapping ctx ts; VPartialP (VSystem (System.map (infer ctx) ts), eval (getFormula ts) ctx) | EEmpty | EUnit | EBool -> VKan Z.zero | EStar -> VUnit | EFalse | ETrue -> VBool | EIndEmpty e -> ignore (extSet (infer ctx e)); implv VEmpty (eval e ctx) | EIndUnit e -> inferInd false ctx VUnit e recUnit | EIndBool e -> inferInd false ctx VBool e recBool | ESup (a, b) -> let t = eval a ctx in ignore (extSet (infer ctx a)); let (t', (p, g)) = extPiG (infer ctx b) in eqNf t t'; ignore (extSet (g (Var (p, t)))); inferSup t (eval b ctx) | EIndW (a, b, c) -> let t = eval a ctx in ignore (extSet (infer ctx a)); let (t', (p, g)) = extPiG (infer ctx b) in eqNf t t'; ignore (extSet (g (Var (p, t)))); let (w', (q, h)) = extPiG (infer ctx c) in eqNf (wtype t (eval b ctx)) w'; ignore (extSet (h (Var (q, w')))); inferIndW t (eval b ctx) (eval c ctx) | e -> raise (InferError e) and inferInd fibrant ctx t e f = let (t', (p, g)) = extPiG (infer ctx e) in eqNf t t'; let k = g (Var (p, t)) in ignore (if fibrant then extKan k else extSet k); f (eval e ctx) and inferField ctx p e = match infer ctx e with | VSig (t, (q, _)) -> if matchIdent p q then t else inferField ctx p (ESnd e) | t -> raise (ExpectedSig (rbV t)) and inferTele ctx g p a b = ignore (extSet (infer ctx a)); let t = eval a ctx in let x = Var (p, t) in let ctx' = upLocal ctx p t x in let v = infer ctx' b in g (infer ctx a) v and inferLam ctx p a e = ignore (extSet (infer ctx a)); let t = eval a ctx in ignore (infer (upLocal ctx p t (Var (p, t))) e); VPi (t, (p, fun x -> inferV (eval e (upLocal ctx p t x)))) and inferPath (ctx : ctx) (p : exp) = let (i, x, v) = freshDim () in let ctx' = upLocal ctx i VI v in let t = infer ctx' (rbV (act p x ctx')) in ignore (extSet t); let v0 = act p ezero ctx in let v1 = act p eone ctx in implv v0 (implv v1 t) and inferInc t r = let a = freshName "a" in VPi (t, (a, fun v -> VSub (t, r, VSystem (border (solve r One) v)))) and inferTransport (ctx : ctx) (p : exp) (i : exp) = check ctx i VI; let u0 = act p ezero ctx in let u1 = act p eone ctx in let (j, x, v) = freshDim () in let ctx' = upLocal ctx j VI v in ignore (extKan (infer ctx' (rbV (act p x ctx')))); List.iter (fun phi -> let rho = faceEnv phi ctx' in eqNf (act p ezero rho) (act p x rho)) (solve (eval i ctx) One); implv u0 u1 and inferJ v t = let x = freshName "x" in let y = freshName "y" in let pi = freshName "P" in let p = freshName "p" in let k = extSet t in let t = VPi (v, (x, fun x -> VPi (v, (y, fun y -> implv (idv v x y) (VPre k))))) in VPi (t, (pi, fun pi -> VPi (v, (x, fun x -> implv (app (app (app (pi, x), x), VRef x)) (VPi (v, (y, fun y -> VPi (idv v x y, (p, fun p -> app (app (app (pi, x), y), p)))))))))) and recUnit t = let x = freshName "x" in implv (app (t, VStar)) (VPi (VUnit, (x, fun x -> app (t, x)))) and recBool t = let x = freshName "x" in implv (app (t, VFalse)) (implv (app (t, VTrue)) (VPi (VBool, (x, fun x -> app (t, x))))) and wtype a b = VW (a, (freshName "x", fun x -> app (b, x))) and inferSup a b = let t = wtype a b in let x = freshName "x" in VPi (a, (x, fun x -> implv (implv (app (b, x)) t) t)) and inferIndW a b c = let t = wtype a b in implv (VPi (a, (freshName "x", fun x -> VPi (implv (app (b, x)) t, (freshName "f", fun f -> implv (VPi (app (b, x), (freshName "b", fun b -> app (c, (app (f, b)))))) (app (c, VApp (VApp (VSup (a, b), x), f)))))))) (VPi (t, (freshName "w", fun w -> app (c, w)))) let rec salt (ns : ident Env.t) : exp -> exp = function | EKan n -> EKan n | EPre n -> EPre n | EVar x -> EVar (freshVar ns x) | EHole -> EHole | EPi (a, (p, b)) -> saltTele ePi ns p a b | ELam (a, (p, b)) -> saltTele eLam ns p a b | EApp (f, x) -> EApp (salt ns f, salt ns x) | ESig (a, (p, b)) -> saltTele eSig ns p a b | EPair (r, a, b) -> EPair (r, salt ns a, salt ns b) | EFst e -> EFst (salt ns e) | ESnd e -> ESnd (salt ns e) | EField (e, p) -> EField (salt ns e, p) | EId e -> EId (salt ns e) | ERef e -> ERef (salt ns e) | EJ e -> EJ (salt ns e) | EPathP e -> EPathP (salt ns e) | ETransp (p, i) -> ETransp (salt ns p, salt ns i) | EHComp (t, r, u, u0) -> EHComp (salt ns t, salt ns r, salt ns u, salt ns u0) | EPLam e -> EPLam (salt ns e) | EAppFormula (p, i) -> EAppFormula (salt ns p, salt ns i) | EPartial e -> EPartial (salt ns e) | EPartialP (t, r) -> EPartialP (salt ns t, salt ns r) | ESub (a, i, u) -> ESub (salt ns a, salt ns i, salt ns u) | ESystem xs -> ESystem (System.fold (fun k v -> System.add (freshFace ns k) (salt ns v)) xs System.empty) | EInc (t, r) -> EInc (salt ns t, salt ns r) | EOuc e -> EOuc (salt ns e) | EDir d -> EDir d | EI -> EI | EAnd (a, b) -> EAnd (salt ns a, salt ns b) | EOr (a, b) -> EOr (salt ns a, salt ns b) | ENeg e -> ENeg (salt ns e) | EEmpty -> EEmpty | EIndEmpty e -> EIndEmpty (salt ns e) | EUnit -> EUnit | EStar -> EStar | EIndUnit e -> EIndUnit (salt ns e) | EBool -> EBool | EFalse -> EFalse | ETrue -> ETrue | EIndBool e -> EIndBool (salt ns e) | EW (a, (p, b)) -> saltTele eW ns p a b | ESup (a, b) -> ESup (salt ns a, salt ns b) | EIndW (a, b, c) -> EIndW (salt ns a, salt ns b, salt ns c) and freshFace ns phi = Env.fold (fun k v -> Env.add (freshVar ns k) v) phi Env.empty and saltTele ctor ns p a b = let x = fresh p in ctor x (salt ns a) (salt (Env.add p x ns) b) let freshTele ns : tele -> tele = fun (p, e) -> let q = fresh p in let e' = salt !ns e in ns := Env.add p q !ns; (q, e') let freshExp = salt Env.empty let freshDecl : decl -> decl = function | Def (p, exp1, exp2) -> Def (p, (freshExp exp1), freshExp exp2) | Axiom (p, exp) -> Axiom (p, freshExp exp) let ext x = x ^ ".per" let empty : state = (Env.empty, Files.empty) let getTerm e ctx = if !preeval then Value (eval e ctx) else Exp e let checkDecl ctx d : ctx = let x = getDeclName d in if Env.mem (ident x) ctx then raise (AlreadyDeclared x); match d with | Def (p, EHole, e) -> let t = infer ctx e in Env.add (ident p) (Global, Value t, getTerm e ctx) ctx | Def (p, a, e) -> ignore (extSet (infer ctx a)); let t = eval a ctx in let v = ident p in check (upGlobal ctx v t (Var (v, t))) e t; Env.add (ident p) (Global, Value t, getTerm e ctx) ctx | Axiom (p, a) -> ignore (extSet (infer ctx a)); let x = ident p in let t = eval a ctx in Env.add x (Global, Value t, Value (Var (x, t))) ctx let getBoolVal opt = function | "tt" | "true" -> true | "ff" | "false" -> false | value -> raise (UnknownOptionValue (opt, value)) open Exp open Printer let rec orFormula a b = match a, b with | VDir One, _ | _, VDir One -> VDir One | VDir Zero, f | f, VDir Zero -> f | VOr (f, g), h -> orFormula f (orFormula g h) | f, g -> if f = g then f else VOr (f, g) let rec andFormula a b = match a, b with | VDir Zero, _ | _, VDir Zero -> VDir Zero | VDir One, f | f, VDir One -> f | VAnd (f, g), h -> andFormula f (andFormula g h) | f, g -> if f = g then f else VAnd (f, g) let rec extAnd v = match v with | Var (x, _) -> Conjunction.singleton (x, One) | VNeg (Var (x, _)) -> Conjunction.singleton (x, Zero) | VAnd (x, y) -> Conjunction.union (extAnd x) (extAnd y) | VDir One -> Conjunction.empty | _ -> raise (Failure "ExpectedConjunction") let rec extOr v = match v with | VOr (x, y) -> Disjunction.union (extOr x) (extOr y) | VDir Zero -> Disjunction.empty | k -> Disjunction.singleton (extAnd k) let uniq t = let super x y = not (Conjunction.equal x y) && Conjunction.subset y x in Disjunction.filter (fun x -> not (Disjunction.exists (super x) t)) t let unions t1 t2 = Disjunction.fold (fun c1 res -> Disjunction.fold (fun c2 res' -> Disjunction.add (Conjunction.union c1 c2) res' ) t2 res ) t1 Disjunction.empty |> uniq let negConjunction c = Conjunction.fold (fun (x, d) res -> Disjunction.add (Conjunction.singleton (x, negDir d)) res ) c Disjunction.empty let negDisjunction d = Disjunction.fold (fun c res -> unions res (negConjunction c) ) d (Disjunction.singleton Conjunction.empty) let contrAtom : ident * dir -> value = function | (x, Zero) -> VNeg (Var (x, VI)) | (x, One) -> Var (x, VI) let contrAnd (t : conjunction) : value = Conjunction.fold (fun e e' -> andFormula (contrAtom e) e') t (VDir One) let contrOr (t : disjunction) : value = Disjunction.fold (fun e e' -> orFormula (contrAnd e) e') t (VDir Zero) let rec negFormula = function | VDir d -> VDir (negDir d) | VNeg n -> n | v -> contrOr (negDisjunction (extOr v)) let evalAnd a b = contrOr (unions (extOr a) (extOr b)) let evalOr a b = contrOr (uniq (Disjunction.union (extOr a) (extOr b))) let getFaceV xs = Env.fold (fun x d y -> andFormula y (contrAtom (x, d))) xs (VDir One) let getFormulaV ts = System.fold (fun x _ v -> orFormula (getFaceV x) v) ts (VDir Zero) open Printer open Exp exception Restart exception IncompatibleFaces exception InferError of exp exception ExpectedPi of exp exception ExpectedSig of exp exception ExpectedPath of exp exception ExpectedVSet of exp exception Ineq of exp * exp exception ExpectedSubtype of exp exception Parser of int * string * string exception ExpectedSystem of exp exception ExpectedNeutral of exp exception ExpectedFibrant of exp exception UnknownCommand of string exception VariableNotFound of ident exception ExtractionError of string exception AlreadyDeclared of string exception UnknownPrimitive of string exception ExpectedConjunction of exp exception InvalidModuleName of string * string exception UnknownOption of string exception UnknownOptionValue of string * string let prettyPrintError : exn -> unit = function | Ineq (e1, e2) -> Printf.printf "Type mismatch:\n %s\nis not equal to\n %s\n" (showExp e1) (showExp e2) | ExpectedConjunction v -> Printf.printf "“%s” expected to be conjunction\n" (showExp v) | ExtractionError s -> Printf.printf "Error occured during extraction: %s\n" s | ExpectedPath e -> Printf.printf "“%s” expected to be a path.\n" (showExp e) | AlreadyDeclared p -> Printf.printf "“%s” is already declared.\n" p | InferError e -> Printf.printf "Cannot infer type of\n %s\n" (showExp e) | VariableNotFound p -> Printf.printf "Variable %s was not found\n" (showIdent p) | InvalidModuleName (name, filename) -> Printf.printf "Module “%s” does not match name of its file: %s\n" name filename | ExpectedVSet x -> Printf.printf " %s\nexpected to be universe\n" (showExp x) | ExpectedFibrant x -> Printf.printf " %s\nexpected to be fibrant universe\n" (showExp x) | ExpectedPi x -> Printf.printf " %s\nexpected to be Pi-type\n" (showExp x) | ExpectedSig x -> Printf.printf " %s\nexpected to be Sigma-type\n" (showExp x) | ExpectedSystem x -> Printf.printf " %s\nexpected to be a system\n" (showExp x) | ExpectedSubtype x -> Printf.printf " %s\nexpected to be a cubical subtype\n" (showExp x) | UnknownCommand s -> Printf.printf "Unknown command “%s”\n" s | UnknownOption opt -> Printf.printf "Unknown option “%s”\n" opt | UnknownOptionValue (opt, value) -> Printf.printf "Unknown value “%s” of option “%s”\n" value opt | Parser (x, buf, filename) -> Printf.printf "Parsing error at line %d while parsing “%s”: “%s”\n" x filename buf | IncompatibleFaces -> Printf.printf "Incompatible faces\n" | Sys_error s -> print_endline s | Restart -> raise Restart | ex -> Printf.printf "Uncaught exception: %s\n" (Printexc.to_string ex) let handleErrors (f : 'a -> 'b) (x : 'a) (default : 'b) : 'b = try f x with ex -> prettyPrintError ex; defaultlet (<<) f g x = f (g x) let (>>) g f x = f (g x) let initLast xs = let rec func xs = function | [] -> failwith "initLast: expected non-empty list" | [y] -> (xs, y) | y :: ys -> func (y :: xs) ys in let (ys, y) = func [] xs in (List.rev ys, y) let getDigit x = Char.chr (Z.to_int x + 0x80) |> Printf.sprintf "\xE2\x82%c" let ten = Z.of_int 10 let rec showSubscript x = if Z.lt x Z.zero then failwith "showSubscript: expected positive integer" else if Z.equal x Z.zero then "" else let (y, d) = Z.div_rem x ten in showSubscript y ^ getDigit d let moduleSep = '/' let getPath = String.split_on_char moduleSep >> String.concat Filename.dir_sep let trace : bool ref = ref false let indices : bool ref = ref false let preeval : bool ref = ref true let girard : bool ref = ref false let verbose : bool ref = ref true let irrelevance : bool ref = ref false let traceHole _ _ = () type ident = Irrefutable | Ident of string * int64 module Ident = struct type t = ident let compare x y = match (x, y) with | Irrefutable, Irrefutable -> 0 | Irrefutable, Ident _ -> -1 | Ident _, Irrefutable -> 1 | Ident (p, a), Ident (q, b) -> if p = q then compare a b else compare p q end module Env = Map.Make(Ident) type dir = Zero | One module Dir = struct type t = dir let compare a b = match a, b with | One, Zero -> 1 | Zero, One -> -1 | _, _ -> 0 end module Files = Set.Make(String) let gidx : int64 ref = ref 0L let gen () = gidx := Int64.succ !gidx; !gidx let fresh : ident -> ident = function | Irrefutable -> let n = gen () in Ident ("x" ^ showSubscript (Z.of_int64 n), n) | Ident (p, _) -> Ident (p, gen ()) let freshName x = let n = gen () in Ident (x ^ showSubscript (Z.of_int64 n), n) let matchIdent p : ident -> bool = function | Irrefutable -> false | Ident (q, _) -> p = q let getDigit x = Char.chr (x + 0x80) |> Printf.sprintf "\xE2\x82%c" module Atom = struct type t = ident * dir let compare (a, x) (b, y) = if a = b then Dir.compare x y else Ident.compare a b end module Conjunction = Set.Make(Atom) type conjunction = Conjunction.t module Disjunction = Set.Make(Conjunction) type disjunction = Disjunction.t type face = dir Env.t module Face = struct type t = face let compare = Env.compare Dir.compare end type tag = (string option) ref module System = Map.Make(Face) let eps : face = Env.empty let negDir : dir -> dir = function | Zero -> One | One -> Zero let keys ts = List.of_seq (Seq.map fst (System.to_seq ts)) let intersectionWith f = System.merge (fun _ x y -> match x, y with | Some a, Some b -> Some (f a b) | _, _ -> None) type exp = | EPre of Z.t | EKan of Z.t (* cosmos *) | EVar of ident | EHole (* variables *) | EPi of exp * (ident * exp) | ELam of exp * (ident * exp) | EApp of exp * exp (* pi *) | ESig of exp * (ident * exp) | EPair of tag * exp * exp (* sigma *) | EFst of exp | ESnd of exp | EField of exp * string (* sigma *) | EId of exp | ERef of exp | EJ of exp (* strict equality *) | EPathP of exp | EPLam of exp | EAppFormula of exp * exp (* path equality *) | EI | EDir of dir | EAnd of exp * exp | EOr of exp * exp | ENeg of exp (* CCHM interval *) | ETransp of exp * exp | EHComp of exp * exp * exp * exp (* Kan operations *) | EPartial of exp | EPartialP of exp * exp | ESystem of exp System.t (* partial functions *) | ESub of exp * exp * exp | EInc of exp * exp | EOuc of exp (* cubical subtypes *) | EEmpty | EIndEmpty of exp (* 𝟎 *) | EUnit | EStar | EIndUnit of exp (* 𝟏 *) | EBool | EFalse | ETrue | EIndBool of exp (* 𝟐 *) | EW of exp * (ident * exp) | ESup of exp * exp | EIndW of exp * exp * exp (* W *) type tele = ident * exp type scope = Local | Global type value = | VKan of Z.t | VPre of Z.t | Var of ident * value | VHole | VPi of value * clos | VLam of value * clos | VApp of value * value | VSig of value * clos | VPair of tag * value * value | VFst of value | VSnd of value | VId of value | VRef of value | VJ of value | VPathP of value | VPLam of value | VAppFormula of value * value | VI | VDir of dir | VAnd of value * value | VOr of value * value | VNeg of value | VTransp of value * value | VHComp of value * value * value * value | VPartialP of value * value | VSystem of value System.t | VSub of value * value * value | VInc of value * value | VOuc of value | VEmpty | VIndEmpty of value | VUnit | VStar | VIndUnit of value | VBool | VFalse | VTrue | VIndBool of value | VW of value * clos | VSup of value * value | VIndW of value * value * value and clos = ident * (value -> value) and term = Exp of exp | Value of value and record = scope * term * term and ctx = record Env.t type state = ctx * Files.t type command = | Nope | Eval of exp | Action of string | Command of string * exp type decl = | Def of string * exp * exp | Axiom of string * exp type line = | Import of string list | Option of string * string | Decl of decl type content = line list type file = string * content let eLam p a b = ELam (a, (p, b)) let ePi p a b = EPi (a, (p, b)) let eSig p a b = ESig (a, (p, b)) let eW p a b = EW (a, (p, b)) let ezero = EDir Zero let eone = EDir One let vzero = VDir Zero let vone = VDir One let isOne i = VApp (VApp (VId VI, VDir One), i) let extFace x e = e (List.map (fun (p, v) -> Var (p, isOne v)) x) let ident x = Ident (x, 0L) let decl x = EVar (ident x) let upVar p x ctx = match p with Irrefutable -> ctx | _ -> Env.add p x ctx let upLocal ctx p t v = upVar p (Local, Value t, Value v) ctx let upGlobal ctx p t v = upVar p (Global, Value t, Value v) ctx let isGlobal : record -> bool = function Global, _, _ -> false | Local, _, _ -> true let freshVar ns n = match Env.find_opt n ns with Some x -> x | None -> n let mapFace fn phi = Env.fold (fun p d -> Env.add (fn p) d) phi Env.empty let freshFace ns = mapFace (freshVar ns) let rec rbV v = match v with | VLam (t, g) -> rbVTele eLam t g | VPair (r, u, v) -> EPair (r, rbV u, rbV v) | VKan u -> EKan u | VPi (t, g) -> rbVTele ePi t g | VSig (t, g) -> rbVTele eSig t g | VPre u -> EPre u | VPLam f -> EPLam (rbV f) | Var (x, _) -> EVar x | VApp (f, x) -> EApp (rbV f, rbV x) | VFst k -> EFst (rbV k) | VSnd k -> ESnd (rbV k) | VHole -> EHole | VPathP v -> EPathP (rbV v) | VPartialP (t, r) -> EPartialP (rbV t, rbV r) | VSystem ts -> ESystem (System.map rbV ts) | VSub (a, i, u) -> ESub (rbV a, rbV i, rbV u) | VTransp (p, i) -> ETransp (rbV p, rbV i) | VHComp (t, r, u, u0) -> EHComp (rbV t, rbV r, rbV u, rbV u0) | VAppFormula (f, x) -> EAppFormula (rbV f, rbV x) | VId v -> EId (rbV v) | VRef v -> ERef (rbV v) | VJ v -> EJ (rbV v) | VI -> EI | VDir d -> EDir d | VAnd (u, v) -> EAnd (rbV u, rbV v) | VOr (u, v) -> EOr (rbV u, rbV v) | VNeg u -> ENeg (rbV u) | VInc (t, r) -> EInc (rbV t, rbV r) | VOuc v -> EOuc (rbV v) | VEmpty -> EEmpty | VIndEmpty v -> EIndEmpty (rbV v) | VUnit -> EUnit | VStar -> EStar | VIndUnit v -> EIndUnit (rbV v) | VBool -> EBool | VFalse -> EFalse | VTrue -> ETrue | VIndBool v -> EIndBool (rbV v) | VW (t, g) -> rbVTele eW t g | VSup (a, b) -> ESup (rbV a, rbV b) | VIndW (a, b, c) -> EIndW (rbV a, rbV b, rbV c) and rbVTele ctor t (p, g) = let x = Var (p, t) in ctor p (rbV t) (rbV (g x)) let zeroPrim = ref "0" let onePrim = ref "1" let intervalPrim = ref "I" let getVar x = let xs = [ (!intervalPrim, EI); (!zeroPrim, EDir Zero); (!onePrim, EDir One); ("𝟎", EEmpty); ("empty", EEmpty); ("𝟏", EUnit); ("unit", EUnit); ("★", EStar); ("star", EStar); ("𝟐", EBool); ("bool", EBool); ("false", EFalse); ("0₂", EFalse); ("true", ETrue); ("1₂", ETrue) ] in match List.assoc_opt x xs with Some e -> e | None -> decl x type formula = | Falsehood | Equation of ident * dir | Truth exception ExpectedDir of string let getDir x = if x = !zeroPrim then Zero else if x = !onePrim then One else raise (ExpectedDir x) let face p e d : formula = match getVar p, e, getDir d with | EVar x, "=", d -> Equation (x, d) | EDir d1, "=", d2 -> if d1 = d2 then Truth else Falsehood | _, _, _ -> failwith "invalid face" let extEquation : formula -> ident * dir = function | Equation (x, d) -> (x, d) | _ -> raise (Failure "extEquation") let parseFace xs = if List.mem Falsehood xs then None else if List.mem Truth xs then Some eps else Some (Env.of_seq (Seq.map extEquation (List.to_seq xs))) let parsePartial (xs, e) = Option.map (fun ys -> (ys, e)) (parseFace xs) let impl a b = EPi (a, (Irrefutable, b)) let prod a b = ESig (a, (Irrefutable, b)) let rec telescope ctor e : tele list -> exp = function | [] -> e | (p, a) :: xs -> ctor p a (telescope ctor e xs) let rec pLam e : ident list -> exp = function [] -> e | x :: xs -> EPLam (ELam (EI, (x, pLam e xs))) let getDeclName : decl -> string = function Def (p, _, _) | Axiom (p, _) -> p open Printer open Error open Check open Exp let help = "Available commands: infer type and normalize statement :q quit :r restart :h display this message" let init : state = empty let st : state ref = ref init let checkAndEval ctx e : value * value = (Check.infer ctx e, Check.eval e ctx) let main ctx : command -> unit = function | Eval e -> let (t, v) = checkAndEval ctx (freshExp e) in Printf.printf "TYPE: %s\nEVAL: %s\n" (showExp (rbV t)) (showExp (rbV v)) | Action "q" -> exit 0 | Action "r" -> st := init; raise Restart | Action "h" -> print_endline help | Command (s, _) | Action s -> raise (UnknownCommand s) | Nope -> () let check filename = st := handleErrors (Reader.checkFile !st) filename !st let banner = "Per theorem prover [MLTT-80] version 5.3.20" let loop () = print_endline ("\n" ^ banner) ; let (ctx, _) = !st in try while true do print_string "> "; let line = read_line () in handleErrors (fun x -> let cmd = Reader.parseErr Parser.repl (Lexing.from_string x) "" in main ctx cmd) line () done with End_of_file -> () type cmdline = | Check of string | Lex of string | Parse of string | Prim of string * string | Repl | Help | Trace | Indices | Girard | Silent | Irrelevance let help = "\n invocation = per | per list list = [] | command list primitive = zero | one | interval command = check | lex | parse | girard | trace | repl | help " let repl = ref false let cmd : cmdline -> unit = function | Check filename -> check filename | Lex filename -> Reader.lex filename | Parse filename -> Reader.parse filename | Prim (prim, value) -> begin match prim with | "zero" -> zeroPrim := value | "one" -> onePrim := value | "interval" -> intervalPrim := value | _ -> raise (UnknownPrimitive prim) end | Help -> print_endline banner ; print_endline help | Repl -> repl := true | Silent -> verbose := false | Indices -> indices := true | Trace -> indices := true; trace := true | Girard -> girard := true | Irrelevance -> irrelevance := true let rec parseArgs : string list -> cmdline list = function | [] -> [] | "prim" :: prim :: value :: rest -> Prim (prim, value) :: parseArgs rest | "check" :: filename :: rest -> Check filename :: parseArgs rest | "lex" :: filename :: rest -> Lex filename :: parseArgs rest | "parse" :: filename :: rest -> Parse filename :: parseArgs rest | "help" :: rest -> Help :: parseArgs rest | "trace" :: rest -> Trace :: parseArgs rest | "indices" :: rest -> Indices :: parseArgs rest | "irrelevance" :: rest -> Irrelevance :: parseArgs rest | "silent" :: rest -> Silent :: parseArgs rest | "girard" :: rest -> Girard :: parseArgs rest | "repl" :: rest -> Repl :: parseArgs rest | x :: xs -> Printf.printf "Unknown command “%s”\n" x; parseArgs xs let defaults = function | [] -> [Help] | xs -> xs let rec main () = try Array.to_list Sys.argv |> List.tl |> parseArgs |> defaults |> List.iter cmd; if !repl then loop () else () with Restart -> main () let () = main () open Exp let showIdent : ident -> string = function | Irrefutable -> "_" | Ident (p, n) -> if !indices then p ^ "#" ^ Int64.to_string n else p let showDir : dir -> string = function | Zero -> !zeroPrim | One -> !onePrim let showFace phi = if Env.is_empty phi then "(1 = 1)" else Env.bindings phi |> List.map (fun (x, d) -> Printf.sprintf "(%s = %s)" (showIdent x) (showDir d)) |> String.concat " " let showSystem show xs = System.bindings xs |> List.map (fun (x, e) -> Printf.sprintf "%s → %s" (showFace x) (show e)) |> String.concat ", " let rec ppExp paren e = let x = match e with | EKan n -> "U" ^ showSubscript n | EPre n -> "V" ^ showSubscript n | EVar p -> showIdent p | EHole -> "?" | EPi (a, (p, b)) -> showPiExp a p b | ELam (a, (p, b)) -> Printf.sprintf "λ %s, %s" (showTeleExp (p, a)) (showExp b) | EApp (f, x) -> Printf.sprintf "%s %s" (showExp f) (ppExp true x) | ESig (a, (p, b)) -> Printf.sprintf "Σ %s, %s" (showTeleExp (p, a)) (showExp b) | EPair (_, fst, snd) -> Printf.sprintf "(%s, %s)" (showExp fst) (showExp snd) | EFst exp -> ppExp true exp ^ ".1" | ESnd exp -> ppExp true exp ^ ".2" | EField (exp, field) -> ppExp true exp ^ "." ^ field | EPLam (ELam (_, (i, e))) -> Printf.sprintf "<%s> %s" (showIdent i) (showExp e) | EPLam _ -> failwith "showExp: unreachable code was reached" | EAppFormula (f, x) -> Printf.sprintf "%s @ %s" (ppExp true f) (ppExp true x) | ESystem x -> Printf.sprintf "[%s]" (showSystem showExp x) | ESub (a, i, u) -> Printf.sprintf "%s[%s ↦ %s]" (ppExp true a) (showExp i) (showExp u) | EI -> !intervalPrim | EDir d -> showDir d | EAnd (a, b) -> Printf.sprintf "%s ∧ %s" (ppExp true a) (ppExp true b) | EOr (a, b) -> Printf.sprintf "%s ∨ %s" (ppExp true a) (ppExp true b) | ENeg a -> Printf.sprintf "-%s" (ppExp paren a) | ETransp (p, i) -> Printf.sprintf "transp %s %s" (ppExp true p) (ppExp true i) | EPathP e -> "PathP " ^ ppExp true e | EId e -> Printf.sprintf "Id %s" (ppExp true e) | ERef e -> Printf.sprintf "ref %s" (ppExp true e) | EJ e -> Printf.sprintf "idJ %s" (ppExp true e) | EEmpty -> "𝟎" | EUnit -> "𝟏" | EBool -> "𝟐" | EStar -> "★" | EFalse -> "0₂" | ETrue -> "1₂" | EIndEmpty e -> Printf.sprintf "ind₀ %s" (ppExp true e) | EIndUnit e -> Printf.sprintf "ind₁ %s" (ppExp true e) | EIndBool e -> Printf.sprintf "ind₂ %s" (ppExp true e) | ESup (a, b) -> Printf.sprintf "sup %s %s" (ppExp true a) (ppExp true b) | EIndW (a, b, c) -> Printf.sprintf "indᵂ %s %s %s" (ppExp true a) (ppExp true b) (ppExp true c) | EHComp (t, r, u, u0) -> Printf.sprintf "hcomp %s %s %s %s" (ppExp true t) (ppExp true r) (ppExp true u) (ppExp true u0) | EPartial e -> Printf.sprintf "Partial %s" (ppExp true e) | EPartialP (t, r) -> Printf.sprintf "PartialP %s %s" (ppExp true t) (ppExp true r) | EInc (t, r) -> Printf.sprintf "inc %s %s" (ppExp true t) (ppExp true r) | EOuc e -> Printf.sprintf "ouc %s" (ppExp true e) | EW (a, (p, b)) -> Printf.sprintf "W %s, %s" (showTeleExp (p, a)) (showExp b) in match e with | EVar _ | EFst _ | ESnd _ | EI | EPre _ | ESystem _ | EKan _ | EHole | EDir _ | EPair _ | ENeg _ | EW _ -> x | _ -> if paren then "(" ^ x ^ ")" else x and showExp e = ppExp false e and showTeleExp a = match a with | (p, x) -> Printf.sprintf "(%s : %s)" (showIdent p) (showExp x) and showPiExp a p b = match p with | Irrefutable -> Printf.sprintf "%s → %s" (ppExp true a) (showExp b) | _ -> Printf.sprintf "Π %s, %s" (showTeleExp (p, a)) (showExp b) type entry = { mutable total_time : float; mutable call_count : int; } let stats : (string, entry) Hashtbl.t = Hashtbl.create 50 let active_start : (string, float) Hashtbl.t = Hashtbl.create 50 let stack = ref [] let profiling_enabled = ref false let start_prof () = profiling_enabled := true let stop_prof () = profiling_enabled := false let get_time () = Sys.time () let measure name f = if not !profiling_enabled then f () else begin let t0 = get_time () in let parent = match !stack with p::_ -> Some p | [] -> None in stack := name :: !stack; let result = try f () with e -> stack := List.tl !stack; raise e in let t1 = get_time () in let diff = t1 -. t0 in stack := List.tl !stack; let entry = try Hashtbl.find stats name with Not_found -> let e = { total_time = 0.0; call_count = 0 } in Hashtbl.add stats name e; e in entry.total_time <- entry.total_time +. diff; entry.call_count <- entry.call_count + 1; result end let print_prof () = let sorted = Hashtbl.fold (fun k v acc -> (k, v) :: acc) stats [] |> List.sort (fun (_, a) (_, b) -> compare b.total_time a.total_time) in let max_time = match sorted with (_, e)::_ -> e.total_time | [] -> 1.0 in Printf.printf "\nOCaml Profile Results:\n"; List.iter (fun (name, entry) -> let bar_len = int_of_float (entry.total_time /. max_time *. 40.0) in let bar = String.make bar_len '=' ^ String.make (40 - bar_len) ' ' in Printf.printf "%-50s [%s] %.6fs (x%d)\n" name bar entry.total_time entry.call_count ) sorted; Printf.printf "\n" open Printer open Lexing open Exp open Error open Check let parseErr f lexbuf filename = try f Lexer.main lexbuf with Parser.Error -> raise (Error.Parser (lexbuf.lex_curr_p.pos_lnum, lexeme lexbuf, filename)) let lex filename = let chan = open_in filename in let lexbuf = Lexing.from_channel chan in Printf.printf "Lexing “%s”.\n" filename; try while true do let tok = Lexer.main lexbuf in if tok = Parser.EOF then raise End_of_file else print_string (Lexer.tokenToString tok ^ " ") done with End_of_file -> close_in chan; print_newline () let showDecl : decl -> string = function | Def (p, exp1, exp2) -> Printf.sprintf "def %s : %s := %s" p (showExp exp1) (showExp exp2) | Axiom (p, exp) -> Printf.sprintf "axiom %s : %s" p (showExp exp) let showLine : line -> string = function | Import p -> Printf.sprintf "import %s" (String.concat " " p) | Option (opt, value) -> Printf.sprintf "option %s %s" opt value | Decl d -> showDecl d let showContent x = String.concat "\n" (List.map showLine x) let showFile : file -> string = function | (p, x) -> Printf.sprintf "module %s where\n%s" p (showContent x) let rec checkLine st : line -> state = let (ctx, checked) = st in function | Decl d -> if !verbose then begin Printf.printf "Checking: %s\n" (getDeclName d); flush_all () end; (checkDecl ctx (freshDecl d), checked) | Option (opt, value) -> begin match opt with | "irrelevance" -> irrelevance := getBoolVal opt value | "girard" -> girard := getBoolVal opt value | "verbose" -> verbose := getBoolVal opt value | "pre-eval" -> preeval := getBoolVal opt value | _ -> raise (UnknownOption opt) end; st | Import xs -> List.fold_left (fun st x -> let path = ext x in if Files.mem path checked then st else checkFile st path) st xs and checkFile p path = let (ctx, checked) = p in let filename = Filename.basename path in let chan = open_in path in let (name, con) = parseErr Parser.file (Lexing.from_channel chan) path in close_in chan; if !verbose then begin Printf.printf "Parsed “%s” successfully.\n" filename; flush_all () end; if ext name = filename then () else raise (InvalidModuleName (name, filename)); let res = checkContent (ctx, Files.add path checked) con in print_endline ("File “" ^ filename ^ "” checked."); res and checkContent st xs = List.fold_left checkLine st xs let parse filename = let chan = open_in filename in Printf.printf "Parsing “%s”.\n" filename; Error.handleErrors (fun chan -> let lexbuf = Lexing.from_channel chan in let file = parseErr Parser.file lexbuf filename in print_endline (showFile file)) chan ()