dtlc

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

commit 9eb795db62dcf1c0a587ae41a7c74abd615cf244
parent 489485290ee773d93718fd0c7e42df66a3fa1ed1
Author: Robert Russell <robertrussell.72001@gmail.com>
Date:   Mon, 18 Dec 2023 20:55:44 -0800

Some refactoring

Diffstat:
Mabstract.ml | 12+++++++-----
Mconcrete.ml | 2++
Mconctoabs.ml | 29+++++++++++++++--------------
Mcore.ml | 2+-
Melab.ml | 158+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Meval.ml | 2+-
Mlexer.mll | 4++++
Mmain.ml | 10+++++++++-
Mparser.mly | 32++++++++++++++++++++++++--------
9 files changed, 157 insertions(+), 94 deletions(-)

diff --git a/abstract.ml b/abstract.ml @@ -1,8 +1,11 @@ open Common -(* TODO: Here (and elsewhere) we should be using arrays instead of lists. *) +(* TODO: Here (and elsewhere) we should be using arrays instead of lists. + * Keeps lists in concrete syntax for convenience when parsing, but change + * to arrays in abstract syntax and make the conversion in conctoabs. *) type term = +| Let of patt * term * term | Match of term * case list | Annot of term * term | App of term * term list @@ -10,19 +13,18 @@ type term = | Index of term * term | Deref of term | Fn of patt list * term -| FnType of annot list * term +| FnType of (patt * term) list * term | Data of ident * (ident * term) list (* TODO: constructor tag should be path eventually *) -| DataType of (ident * annot list) list +| DataType of (ident * (patt * term) list) list | Nat of Z.t | Ident of ident and patt = | PWild | PBind of ident | PAssign of term -| PAnnot of annot +| PAnnot of patt * term | PData of ident * (ident * patt) list | PNat of Z.t | PAnd of patt * patt | POr of patt * patt -and annot = Annot of patt * term and case = Case of {patt: patt; guard: term option; body: term} diff --git a/concrete.ml b/concrete.ml @@ -9,6 +9,8 @@ type expr = | Annot of expr * expr | Add of expr * expr | Mul of expr * expr +| Or of expr * expr +| And of expr * expr | App of expr * expr list | Access of expr * ident | Index of expr * expr diff --git a/conctoabs.ml b/conctoabs.ml @@ -15,12 +15,8 @@ let rec map_named f = function | (x, e) :: xes -> (x, f e) :: map_named f xes let rec term = function -| C.Let (p, a, b) -> A.Match (term a, [ - A.Case {patt = patt p; guard = None; body = term b} - ]) -| C.Seq (a, b) -> A.Match (term a, [ - A.Case {patt = A.PWild; guard = None; body = term b} - ]) +| C.Let (p, a, b) -> A.Let (patt p, term a, term b) +| C.Seq (a, b) -> A.Let (A.PWild, term a, term b) | C.Match (a, cs) -> A.Match (term a, List.map case cs) | C.If (a, b, c) -> A.Match (term a, [ A.Case {patt = A.PData ("true", []); guard = None; body = term b}; @@ -33,6 +29,8 @@ let rec term = function | C.Annot (a, b) -> A.Annot (term a, term b) | C.Add (a, b) -> A.App (A.Ident "`+", [term a; term b]) | C.Mul (a, b) -> A.App (A.Ident "`*", [term a; term b]) +| C.Or (a, b) -> A.App (A.Ident "`|", [term a; term b]) +| C.And (a, b) -> A.App (A.Ident "`&", [term a; term b]) | C.App (a, bs) -> A.App (term a, List.map term bs) | C.Access (a, x) -> A.Access (term a, x) | C.Index (a, b) -> A.Index (term a, term b) @@ -40,13 +38,14 @@ let rec term = function | C.Fn (ps, a) -> A.Fn (List.map patt ps, term a) | C.FnType (anns, cod) -> A.FnType (List.map annot anns, term cod) | C.Variant (x, es) -> A.Data (x, map_ordered term es) -| C.VariantType vs -> A.DataType (map_named (List.map annot) vs) +| C.VariantType variant_defs -> + A.DataType (map_named (List.map annot) variant_defs) | C.Tuple es -> A.Data ("", map_ordered term es) | C.TupleType anns -> A.DataType [("", List.map annot anns)] -| C.Record fs -> A.Data ("", map_named term fs) -| C.RecordType fds -> - let fd_to_ann (x, e) = A.Annot (A.PBind x, term e) in - A.DataType [("", List.map fd_to_ann fds)] +| C.Record fields -> A.Data ("", map_named term fields) +| C.RecordType field_defs -> + let field_def_to_annot (x, e) = (A.PBind x, term e) in + A.DataType [("", List.map field_def_to_annot field_defs)] | C.Unit -> A.Data ("", []) | C.Nat n -> A.Nat n | C.Underscore -> failwith "no impl: holes" @@ -59,9 +58,11 @@ and patt = function | C.Match _ -> raise InvalidPattern | C.If _ -> raise InvalidPattern | C.Iff _ -> raise InvalidPattern -| C.Annot (p, a) -> A.PAnnot (A.Annot (patt p, term a)) +| C.Annot (p, a) -> A.PAnnot (patt p, term a) | C.Add _ -> raise InvalidPattern (* n + k patterns? *) | C.Mul _ -> raise InvalidPattern +| C.Or (p, q) -> A.POr (patt p, patt q) +| C.And (p, q) -> A.PAnd (patt p, patt q) | C.App _ -> raise InvalidPattern | C.Access _ -> raise InvalidPattern | C.Index _ -> raise InvalidPattern @@ -84,5 +85,5 @@ and case (C.Case {patt = p; guard = g; body = b}) = A.Case {patt = patt p; guard = Option.map term g; body = term b} and annot = function -| C.Annot (a, b) -> A.Annot (patt a, term b) -| b -> A.Annot (A.PWild, term b) +| C.Annot (a, b) -> (patt a, term b) +| b -> (A.PWild, term b) diff --git a/core.ml b/core.ml @@ -25,7 +25,7 @@ and patt_discriminant = let rec to_string m e = let s = to_string in let (c, s) = match e with - | Let _ -> failwith "no impl: let" + | Let (scrutinee, body) -> (0, s 1 scrutinee ^ "; " ^ s 0 body) | Case _ -> failwith "no impl: case" | App (f, args) -> (6, String.concat " " (List.map (s 7) (f :: args))) | Fn (arity, body) -> (1, "fn " ^ string_of_int arity ^ " => " ^ s 1 body) diff --git a/elab.ml b/elab.ml @@ -4,6 +4,7 @@ module A = Abstract module C = Core module V = Value +(* TODO: use a hashtable or map for ident -> level lookups *) type context = { len: int; names: ident option list; @@ -52,44 +53,70 @@ let rec bind_arb_telescope ctx xs ts = match xs, ts with | _, _ -> failwith "impossible" let rec infer ctx = function +| A.Let (A.PAnnot (p, a), scrutinee, body) -> + let a = check ctx a (V.Intrin C.Type) in + let va = eval ctx.values a in + let x = check_patt ctx p va in + let scrutinee = check ctx scrutinee va in + let vscrutinee = eval ctx.values scrutinee in + let (body, vb) = infer (bind_def ctx x vscrutinee va) body in + (C.Let (scrutinee, body), vb) +| A.Let (p, scrutinee, body) -> + let (scrutinee, va) = infer ctx scrutinee in + let vscrutinee = eval ctx.values scrutinee in + let x = check_patt ctx p va in + let (body, vb) = infer (bind_def ctx x vscrutinee va) body in + (C.Let (scrutinee, body), vb) | A.Match _ -> failwith "no impl: match" -| A.Annot (a, t) -> - let ct = check ctx t (V.Intrin C.Type) in - let vt = eval ctx.values ct in - let ca = check ctx a vt in - (ca, vt) +| A.Annot (tm, a) -> + let a = check ctx a (V.Intrin C.Type) in + let va = eval ctx.values a in + let tm = check ctx tm va in + (tm, va) | A.App (f, args) -> - let (cf, tf) = infer ctx f in - begin match tf with + let (f, fty) = infer ctx f in + begin match fty with | V.FnType (env, arity, doms, cod) -> - if List.length args <> arity then failwith "arity mismatch" else - let rec go env args doms = match args, doms with - | arg :: args, dom :: doms -> - let vdom = eval env dom in - let carg = check ctx arg vdom in - let varg = eval ctx.values carg in - let (cargs, vcod) = go (varg :: env) args doms in - (carg :: cargs, vcod) - | [], [] -> ([], eval env cod) - | _, _ -> failwith "impossible" in - let (cargs, vcod) = go env args doms in - (C.App (cf, cargs), vcod) - | _ -> failwith "expected function type in application" + if List.length args <> arity then + failwith "type error: arity mismatch in application" + else + let rec go env args doms = match args, doms with + | arg :: args, dom :: doms -> + let vdom = eval env dom in + let arg = check ctx arg vdom in + let varg = eval ctx.values arg in + let (args, vcod) = go (varg :: env) args doms in + (arg :: args, vcod) + | [], [] -> ([], eval env cod) + | _, _ -> failwith "impossible" in + let (args, vcod) = go env args doms in + (C.App (f, args), vcod) + | _ -> failwith "type error: expected function type in application" end | A.Access _ -> failwith "no impl: access" | A.Index _ -> failwith "no impl: index" | A.Deref _ -> failwith "no impl: deref" | A.Fn (ps, body) -> - let (xs, vdoms) = infer_patt_telescope ctx ps in - let arity = List.length xs in - let (cbody, vcod) = infer (bind_arb_telescope ctx xs vdoms) body in - let cdoms = quote_telescope ctx.len vdoms in - let ccod = quote (ctx.len + arity) vcod in - (C.Fn (arity, cbody), V.FnType (ctx.values, arity, cdoms, ccod)) + let rec go ctx = function + | A.PAnnot (p, dom) :: ps -> + let dom = check ctx dom (V.Intrin C.Type) in + let vdom = eval ctx.values dom in + let x = check_patt ctx p vdom in + let (body, doms, vcod) = go (bind_arb ctx x vdom) ps in + (body, dom :: doms, vcod) + | _ :: ps -> + failwith "type error: can not infer type for unannotated function parameter" + | [] -> + let (body, vcod) = infer ctx body in + (body, [], vcod) in + let (body, doms, vcod) = go ctx ps in + let arity = List.length doms in + let cod = quote (ctx.len + arity) vcod in + (C.Fn (arity, body), V.FnType (ctx.values, arity, doms, cod)) | A.FnType (anns, cod) -> let arity = List.length anns in let rec go ctx = function - | A.Annot (p, dom) :: anns -> + | (p, dom) :: anns -> let cdom = check ctx dom (V.Intrin C.Type) in let vdom = eval ctx.values cdom in let x = check_patt ctx p vdom in @@ -109,38 +136,51 @@ let rec infer ctx = function | _, _ -> failwith "impossible" in go 0 ctx.names ctx.types -and check ctx a t = match a, t with -(* TODO: Match, Fn, Data *) -| a, t -> - let (ca, ta) = infer ctx a in - if conv ctx.len t ta then ca else failwith "type error" - -and infer_patt ctx = function -| A.PWild -> failwith "can not infer type for wild pattern" -| A.PBind _ -> failwith "can not infer type for bind pattern" -| A.PAssign _ -> failwith "no impl: assign pattern" -| A.PAnnot (A.Annot (p, t)) -> - let ct = check ctx t (V.Intrin C.Type) in - let vt = eval ctx.values ct in - let x = check_patt ctx p vt in - (x, vt) -| A.PData _ -> failwith "no impl: data pattern" -| A.PNat _ -> failwith "no impl: nat pattern" -| A.PAnd _ -> failwith "no impl: and pattern" -| A.POr _ -> failwith "no impl: or pattern" - -and infer_patt_telescope ctx = function -| p :: ps -> - let (x, t) = infer_patt ctx p in - let (xs, ts) = infer_patt_telescope (bind_arb ctx x t) ps in - (x :: xs, t :: ts) -| [] -> ([], []) +and check ctx tm vty = match tm, vty with +(* TODO: Match, Data *) +| A.Let (A.PAnnot (p, a), scrutinee, body), vb -> + let a = check ctx a (V.Intrin C.Type) in + let va = eval ctx.values a in + let x = check_patt ctx p va in + let scrutinee = check ctx scrutinee va in + let vscrutinee = eval ctx.values scrutinee in + let body = check (bind_def ctx x vscrutinee va) body vb in + C.Let (scrutinee, body) +| A.Let (p, scrutinee, body), vb -> + let (scrutinee, va) = infer ctx scrutinee in + let vscrutinee = eval ctx.values scrutinee in + let x = check_patt ctx p va in + let body = check (bind_def ctx x vscrutinee va) body vb in + C.Let (scrutinee, body) +| A.Fn (ps, body), V.FnType (env, arity, doms, cod) -> + if List.length ps <> arity then + failwith "type error: arity mismatch" + else + let rec go ctx ps env doms = match ps, doms with + | p :: ps, dom :: doms -> + let vdom = eval env dom in + let x = check_patt ctx p vdom in + let ctx = bind_arb ctx x vdom in + let env = List.hd ctx.values :: env in + go ctx ps env doms + | [], [] -> + let vcod = eval env cod in + check ctx body vcod + | _, _ -> failwith "impossible" in + let cbody = go ctx ps env doms in + C.Fn (arity, cbody) +| tm, vexpected -> + let (tm, vinferred) = infer ctx tm in + if conv ctx.len vexpected vinferred then + tm + else + failwith "type error: expected/inferred mismatch" and check_patt ctx p t = match p with | A.PWild -> None | A.PBind x -> Some x | A.PAssign _ -> failwith "no impl: assign pattern" -| A.PAnnot (A.Annot (p, u)) -> +| A.PAnnot (p, u) -> let cu = check ctx u (V.Intrin C.Type) in let vu = eval ctx.values cu in if conv ctx.len vu t then @@ -151,13 +191,3 @@ and check_patt ctx p t = match p with | A.PNat _ -> failwith "no impl: nat pattern" | A.PAnd _ -> failwith "no impl: and pattern" | A.POr _ -> failwith "no impl: or pattern" - -(* -and check_patt_telescope ctx ps ts = match ps, ts with -| p :: ps, t :: ts -> - let x = check_patt ctx p t in - let xs = check_patt_telescope (bind_arb ctx x t) ps ts in - x :: xs -| [], [] -> [] -| _, _ -> failwith "impossible" -*) diff --git a/eval.ml b/eval.ml @@ -9,7 +9,7 @@ let weaken env l n = go env l let rec eval env = function -| C.Let _ -> failwith "no impl: let" +| C.Let (scrutinee, body) -> eval (eval env scrutinee :: env) body | C.Case _ -> failwith "no impl: case" | C.App (f, args) -> begin match eval env f, List.map (eval env) args with diff --git a/lexer.mll b/lexer.mll @@ -63,6 +63,9 @@ rule token = parse | "+" { PLUS } | "*" { ASTERISK } +| "|" { PIPE } +| "&" { AMPERSAND } + | "match" { MATCH } | "if" { IF } @@ -74,6 +77,7 @@ rule token = parse | "fn" { FN } +| "_" { UNDERSCORE } | ident_start ident_cont* { IDENT (Lexing.lexeme lexbuf) } | number { NAT (Z.of_string (Lexing.lexeme lexbuf)) } diff --git a/main.ml b/main.ml @@ -1,5 +1,13 @@ (*let buf = Lexing.from_string "(fn A: Nat -> Type, f: (A 0, A 1) -> A 2, x: A 0, y: A 1 => f x y) (fn _: Nat => Nat) (fn x: Nat, y: Nat => x + y) 3 4 : Nat"*) -let buf = Lexing.from_string "((fn m: (A: Type, A, A -> A) -> A, n: (A: Type, A, A -> A) -> A => fn A: Type, z: A, s: A -> A => m A (n A z s) s) (fn A: Type, z: A, s: A -> A => s (s z)) (fn A: Type, z: A, s: A -> A => s (s (s z)))) Nat 0 (fn n: Nat => n + 1)" +(*let buf = Lexing.from_string "N := (A: Type, A, A -> A) -> A; ((fn m: N, n: N => fn A: Type, z: A, s: A -> A => m A (n A z s) s) (fn A: Type, z: A, s: A -> A => s (s z)) (fn A: Type, z: A, s: A -> A => s (s (s z)))) Nat 0 (fn n: Nat => n + 1)"*) +let buf = Lexing.from_string "\ + N := (A: Type, A, A -> A) -> A;\ + add: (N, N) -> N := fn m, n => fn A, z, s => m A (n A z s) s;\ + two: N := fn A, z, s => s (s z);\ + three: N := fn A, z, s => s (s (s z));\ + five := add two three;\ + five Nat 0 (fn n => n + 1)\ +" let concrete = Parser.start Lexer.token buf let abstract = Conctoabs.term concrete let ctx = Elab.default_ctx diff --git a/parser.mly b/parser.mly @@ -9,17 +9,18 @@ open Concrete %token HASH_LBRACK LBRACK RBRACK %token HASH_LBRACE LBRACE RBRACE %token PLUS ASTERISK +%token PIPE AMPERSAND %token MATCH %token IF THEN ELSE %token IFF DO %token FN +%token UNDERSCORE %token <Common.ident> IDENT %token <Z.t> NAT %start start %type <Concrete.expr> start -%nonassoc PREC_EXPR4 %nonassoc PREC_TUPLE %nonassoc SINGLE_ARROW @@ -51,14 +52,28 @@ expr2: (* Type annotations (right-associative); lowest prec for patterns *) | expr3 COLON expr2 { Annot($1, $3) } | expr3 { $1 } expr3: (* Function arrow (right-associative) *) - | expr4 SINGLE_ARROW expr3 { FnType ([$1], $3) } + | expr6 SINGLE_ARROW expr3 { FnType ([$1], $3) } | tuple SINGLE_ARROW expr3 { FnType ($1, $3) } - | expr4 %prec PREC_EXPR4 { $1 } -expr4: - | expr4 PLUS expr5 { Add ($1, $3) } + | expr4 { $1 } +expr4: (* Additive binary operators (left-associative) *) + | expr4add PLUS expr5 { Add ($1, $3) } + | expr4or PIPE expr5 { Or ($1, $3) } | expr5 { $1 } -expr5: - | expr5 ASTERISK expr6 { Mul ($1, $3) } +expr4add: + | expr4add PLUS expr5 { Add ($1, $3) } + | expr5 { $1 } +expr4or: + | expr4or PIPE expr5 { Or ($1, $3) } + | expr5 { $1 } +expr5: (* Multiplicative binary operators (left-associative) *) + | expr5mul ASTERISK expr6 { Mul ($1, $3) } + | expr5and AMPERSAND expr6 { And ($1, $3) } + | expr6 { $1 } +expr5mul: + | expr5mul ASTERISK expr6 { Mul ($1, $3) } + | expr6 { $1 } +expr5and: + | expr5and AMPERSAND expr6 { And ($1, $3) } | expr6 { $1 } expr6: | expr7 expr7 expr7_apposition { App ($1, $2 :: $3) } @@ -70,7 +85,8 @@ expr7: | expr7 EXCLAM { Deref $1 } | expr8 { $1 } expr8: - | IDENT { if $1 = "_" then Underscore else Ident $1 } + | UNDERSCORE { Underscore } + | IDENT { Ident $1 } | NAT { Nat $1 } | HASH_LBRACK variant_def_list RBRACK { VariantType $2 } | HASH_LPAREN annot_list RPAREN { TupleType $2 }