dtlc

dependently-typed lambda calculus toy
git clone git://git.rr3.xyz/dtlc
Log | Files | Refs | README | LICENSE

commit 8e0a0d1522a4cca7ee6d239085e9e7f05a48d14b
parent 5bb93471b62efc8ce1f7cc6cd9b9e0c10e34c93a
Author: Robert Russell <robertrussell.72001@gmail.com>
Date:   Wed, 24 Jan 2024 19:45:08 -0800

Dependent pattern matching works! (At least a little.)

Diffstat:
Melab.ml | 32+++++++-------------------------
Merror.ml | 14++++++++++++--
Meval.ml | 88+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mmain.ml | 4++--
Mquote.ml | 26--------------------------
5 files changed, 64 insertions(+), 100 deletions(-)

diff --git a/elab.ml b/elab.ml @@ -39,21 +39,6 @@ let add_name' else (Name.Var.Set.add name name_set, name) -(* -let ensure_distinct' -: Name.Var.t node array -> Name.Var.t array -= fun names -> - let nnames = Array.length names in - let name_set = ref Name.Var.Set.empty in - let names' = Array.make nnames garbage in - for i = 0 to nnames - 1 do - let {loc; data = name} = names.(i) in - name_set := add_name' loc !name_set name; - names'.(i) <- names.(i).data - done; - names' -*) - let rec assign_patt : context -> I.patt -> V.value -> Uniq.uniq -> context = fun ctx {names; disc} vtyp var -> @@ -78,15 +63,15 @@ let rec assign_patt (* Assign each subpattern in type-dependency order, because that's the * order that check_disc' elaborates subpatterns in. The order matters * here, because assign_patt binds variables. *) - let ctx = ref ctx in + let ctx' = ref ctx in for i = 0 to arity - 1 do let j = order.(i) in let subtyp = I.disc_type binders.(j).patt.disc in - let vsubtyp = eval !ctx.env subtyp in - ctx := assign_patt !ctx subpatts.(j) vsubtyp (Uniq.get subvars j) + let vsubtyp = eval ctx.env subtyp in + ctx' := assign_patt !ctx' subpatts.(j) vsubtyp (Uniq.get subvars j) done; - !ctx + !ctx' (* Elaborate explicits to Some and implicits to None. *) let elaborate_args' @@ -429,6 +414,8 @@ and elaborate_match' : context -> Loc.t -> A.term -> A.case array -> V.value -> I.term = fun ctx loc scrutinee cases body_vtyp -> let (scrutinee, scrutinee_vtyp) = infer' ctx scrutinee in + let vars = Uniq.fresh 1 in + let var = Uniq.get vars 0 in let ncases = Array.length cases in let cases' = Array.make ncases garbage in @@ -438,8 +425,6 @@ and elaborate_match' let (_, patt) = check_patt' ctx Name.Var.Set.empty patt scrutinee_vtyp in - let vars = Uniq.fresh 1 in - let var = Uniq.get vars 0 in let ctx = Ctx.bind ctx vars in let ctx = assign_patt ctx patt scrutinee_vtyp var in let body = check' ctx body body_vtyp in @@ -447,9 +432,6 @@ and elaborate_match' cases'.(i) <- {I.patt; I.body} done; - let vars = Uniq.fresh 1 in - let var = Uniq.get vars 0 in - let to_clause : int -> I.case -> clause = fun casei case -> @@ -799,7 +781,7 @@ and check_disc' let vtyp = eval clo typ in let (name_set', subpatt') = match subpatts.(j) with | Some patt -> check_patt' !ctx !name_set patt vtyp - | None -> no_impl "implict args" in + | None -> no_impl "implicit args" in name_set := name_set'; subpatts'.(j) <- subpatt'; (* XXX: I think this is a quadratic time algorithm. We should diff --git a/error.ml b/error.ml @@ -1,6 +1,7 @@ module F = Format open Node module I = Internal +module P = Pretty type error = error' node and error' = @@ -37,7 +38,7 @@ and error' = (*| ElabExpectedFnType of I.term*) let rec pp_print ppf {loc; data = e} = - F.fprintf ppf "[%a] %a" + F.fprintf ppf "@[<v>[%a] %a@]" Loc.pp_print loc pp_print' e @@ -69,9 +70,18 @@ and pp_print' ppf = function (Pretty.pp_print_term 0) typ | ElabNonInferablePatt -> F.pp_print_string ppf "non-inferable pattern" +| ElabUnifyFail (expected, inferred) -> + F.fprintf ppf + "\ + expected type@;<0 4>\ + %a@,\ + does not unify with inferred type@;<0 4>\ + %a@,\ + " + (P.pp_print_term 0) expected + (P.pp_print_term 0) inferred (* TODO *) -| ElabUnifyFail _ -> F.pp_print_string ppf "ErrUnifyFail" | ElabCantInferData -> F.pp_print_string ppf "ErrCantInferData" | ElabNoLetAnnot _ -> F.pp_print_string ppf "ErrNoLetAnnot" | ElabNoBinderAnnot _ -> F.pp_print_string ppf "ErrNoBinderAnnot" diff --git a/eval.ml b/eval.ml @@ -6,33 +6,41 @@ module V = Value * cases? *) (* TODO: Lazily evaluate args? *) -(* let rec define_data_args : V.environment -> V.value -> I.patt -> V.environment = fun env value {disc; _} -> match value, disc with -| value, I.DWild -> env -| V.Data {tagi = data_tagi; args; _}, I.DData {tagi = disc_tagi; subpatts; _} -> - begin if data_tagi <> disc_tagi then - impossible "Eval.define_along_patt: value and disc have different tag" +| value, I.DWild _ -> env +| V.Data v, I.DData d -> + begin if v.tagi <> d.tagi then + impossible "Eval.define_data_args: value and disc have different tag" end; + let tagi = v.tagi in - let arity = Array.length args in - begin if arity <> Array.length subpatts then - impossible "Eval.define_along_patt: value and disc have different arity" + begin if Array.length v.args <> Array.length d.subpatts then + impossible "Eval.define_data_args: value and disc have different arity" end; + let arity = Array.length v.args in let subvars = Uniq.fresh arity in let env = Env.bind env subvars in let env = ref env in for i = 0 to arity - 1 do - env := Env.define !env (Uniq.get subvars i) args.(i); - env := define_data_args !env args.(i) subpatts.(i) + let (_, {I.ctors; _}) = v.datatype_clo in + let vorder = ctors.(tagi).order in + let dorder = d.datatype.ctors.(tagi).order in + begin if vorder.(i) <> dorder.(i) then + impossible "Eval.define_data_args: value and disc have different order" + end; + let j = vorder.(i) in + + let subvar = Uniq.get subvars j in + env := Env.define !env subvar v.args.(j); + env := define_data_args !env v.args.(j) d.subpatts.(j) done; !env | _, I.DData _ -> - impossible "Eval.define_along_patt: value is not data, but disc is" -*) + impossible "Eval.define_data_args: value is not data, but disc is" let rec eval : V.environment -> I.term -> V.value @@ -47,33 +55,11 @@ let rec eval env := Env.define !env (Uniq.get vars i) vrhs done; eval !env body - | I.Match {scrutinee; cases; switch} -> - no_impl "eval match" -(* - let scrutinee = eval env scrutinee in - let vars = Uniq.fresh 1 in - let var = Uniq.get vars 0 in - let switch_env = Env.bind Env.empty vars in - let switch_env = Env.define switch_env var scrutinee in - begin match do_switch switch_env (Index.Of (0, 0)) switch with - | Some casei -> - let {I.patt; I.body} = cases.(casei) in - let vars = Uniq.fresh 1 in - let env = Env.bind env vars in - let env = Env.define env (Uniq.get vars 0) scrutinee in - let env = define_data_args env scrutinee patt in - eval env body - | None -> V.Match {scrutinee; clo = env; cases; switch} - end -*) - + do_match (eval env scrutinee) (env, cases) switch | I.App (fn, args) -> apply (eval env fn) (Array.map (eval env) args) - | I.Fn fn -> V.FnClosure (env, fn) - | I.FnType fntype -> V.FnTypeClosure (env, fntype) - | I.Data {tag; tagi; args; datatype} -> V.Data { tag; @@ -81,23 +67,34 @@ let rec eval args = Array.map (eval env) args; datatype_clo = (env, datatype); } - | I.DataType datatype -> V.DataTypeClosure (env, datatype) - | I.Nat n -> V.Nat n - | I.Var ij -> let var = Env.index env ij in begin match Env.eval env var with | Some v -> v | None -> Arb var end - | I.Type -> V.Type - | I.Builtin b -> V.Builtin b -(* +and do_match +: V.value -> I.case array V.closure -> I.switch -> V.value += fun scrutinee (clo, cases) switch -> + let vars = Uniq.fresh 1 in + let var = Uniq.get vars 0 in + let switch_env = Env.bind Env.empty vars in + let switch_env = Env.define switch_env var scrutinee in + begin match do_switch switch_env (Index.Of (0, 0)) switch with + | Some casei -> + let {I.patt; I.body} = cases.(casei) in + let clo = Env.bind clo vars in + let clo = Env.define clo var scrutinee in + let clo = define_data_args clo scrutinee patt in + eval clo body + | None -> V.NeutralMatch {scrutinee; cases_clo = (clo, cases); switch} + end + and do_switch env ij = function | Switch {index = ij'; subswitches; _} -> let var = Env.index env ij in @@ -116,7 +113,6 @@ and do_switch env ij = function | None -> impossible "Eval.switch: undefined variable" end | Goto casei -> Some casei -*) and apply : V.value -> V.value array -> V.value @@ -142,14 +138,16 @@ and force : V.environment -> V.value -> V.value = fun env -> function | V.RefClo (env', t) -> force env (eval !env' t) -| V.NeutralMatch _ -> no_impl "force neutral match" +| V.NeutralMatch {scrutinee; cases_clo; switch} -> + do_match (force env scrutinee) cases_clo switch (* XXX: There's no reason to force the arguments for "CbNeed redexes" (i.e., * when fn is a lambda), but we do need to force args in a "CbValue redex" * (i.e., when fn is a builtin with builtin reduction rules). *) -| V.NeutralApp (fn, args) -> apply (force env fn) (Array.map (force env) args) +| V.NeutralApp (fn, args) -> + apply (force env fn) (Array.map (force env) args) | V.Arb var -> begin match Env.eval env var with | Some v -> force env v - | None -> Arb var + | None -> V.Arb var end | v -> v diff --git a/main.ml b/main.ml @@ -2,6 +2,7 @@ open Node module I = Internal module V = Value +(* let buf = Lexing.from_string "\ N := [A: Type, A, A -> A] -> A;\n\ add: [N, N] -> N := fn m, n => fn A, z, s => m A (n A z s) s;\n\ @@ -10,8 +11,8 @@ let buf = Lexing.from_string "\ five := add two three;\n\ five Nat 0 (fn n => n + 1)\n\ " +*) -(* let buf = Lexing.from_string "\ Bool := #{@false; @true};\n\ T := #(b: Bool, (if b then Nat else Type));\n\ @@ -20,7 +21,6 @@ let buf = Lexing.from_string "\ (@false, _) => 2,\n\ }): Nat\n\ " -*) (* let buf = Lexing.from_string "\ diff --git a/quote.ml b/quote.ml @@ -3,32 +3,6 @@ open Eval module I = Internal module V = Value -(* -let rec bind_and_define_subpatts -: V.environment -> Uniq.uniq -> I.patt -> V.environment -= fun env var {disc; _} -> match disc with -| I.DWild -> env -| I.DData {tag; tagi; subpatts; datatype} -> - let arity = Array.length subpatts in - let subvars = Uniq.fresh arity in - let data = V.Data { - tag; - tagi; - args = Array.init arity (fun i -> V.Arb (Uniq.get subvars i)); - datatype_clo = (env, datatype); - } in - let env = Env.bind env subvars in - let env = Env.define env var data in - - let env = ref env in - for i = 0 to arity - 1 do - let subvar = Uniq.get subvars i in - env := bind_and_define_subpatts !env subvar subpatts.(i) - done; - - !env -*) - let rec bind_patt_twice : V.environment * V.environment -> I.patt -> V.environment * V.environment = fun (env0, env1) {disc; _} ->