|
1 | 1 | open Bwd |
| 2 | +open Bwd.Infix |
2 | 3 | open Dim |
3 | 4 | open Util |
4 | 5 | open List_extra |
@@ -104,7 +105,8 @@ module Command = struct |
104 | 105 | number : int; |
105 | 106 | wsnumber : Whitespace.t list; |
106 | 107 | wscoloneq : Whitespace.t list; |
107 | | - tm : wrapped_parse; |
| 108 | + (* The whitespace is for the commas. The first one is ignored. *) |
| 109 | + tms : (Whitespace.t list * wrapped_parse) list; |
108 | 110 | mutable printed_term : PPrint.document; |
109 | 111 | } |
110 | 112 | (* Show and Undo don't get reformatted (see pp_command, below), so there's no need to store whitespace in them, but we do it anyway for completeness. *) |
@@ -556,7 +558,13 @@ module Parse = struct |
556 | 558 | let* number, wsnumber = integer in |
557 | 559 | let* wscoloneq = token Coloneq in |
558 | 560 | let* tm = C.term [] in |
559 | | - return (Split { wssplit; number; wsnumber; wscoloneq; tm; printed_term = PPrint.empty }) |
| 561 | + let* tms = |
| 562 | + zero_or_more |
| 563 | + (let* wscomma = token (Op ",") in |
| 564 | + let* tm = C.term [] in |
| 565 | + return (wscomma, tm)) in |
| 566 | + let tms = ([], tm) :: tms in |
| 567 | + return (Split { wssplit; number; wsnumber; wscoloneq; tms; printed_term = PPrint.empty }) |
560 | 568 |
|
561 | 569 | let show = |
562 | 570 | let* wsshow = token Show in |
@@ -773,6 +781,59 @@ let condense : Command.t -> [ `Import | `Option | `None | `Bof ] = function |
773 | 781 | | Option _ -> . |
774 | 782 | | _ -> `None |
775 | 783 |
|
| 784 | +(* Subroutine for "split" that generates the cases in a multiple match. *) |
| 785 | +let split_match_cases : type a b. |
| 786 | + (a, b) Ctx.t -> |
| 787 | + (string option, a) Bwv.t -> |
| 788 | + (Whitespace.t list * wrapped_parse) list -> |
| 789 | + observation list list = |
| 790 | + fun ctx vars tms -> |
| 791 | + let open Asai.Range in |
| 792 | + let module S = Monad.State (Bool) in |
| 793 | + let module LS = Monad.ListT (S) in |
| 794 | + let open Monad.Ops (LS) in |
| 795 | + let tok t : observation = Token (t, ([], None)) in |
| 796 | + let rec do_args : type a p ap. |
| 797 | + (a, p, ap) Term.Telescope.t -> |
| 798 | + (No.plus_omega, No.strict, No.plus_omega, No.nonstrict) parse Asai.Range.located list = |
| 799 | + fun args -> |
| 800 | + match args with |
| 801 | + | Emp -> [] |
| 802 | + | Ext (None, _, args) -> locate_opt None (Placeholder []) :: do_args args |
| 803 | + | Ext (Some x, _, args) -> locate_opt None (Ident ([ x ], [])) :: do_args args in |
| 804 | + let rec go = function |
| 805 | + | [] -> |
| 806 | + let* higher = LS.lift S.get in |
| 807 | + let mapsto = if higher then Token.DblMapsto else Mapsto in |
| 808 | + let li, ri = (No.Interval.entire, No.Interval.entire) in |
| 809 | + let h = Hole { li; ri; num = ref 0; ws = []; contents = None } in |
| 810 | + return [ tok mapsto; Term (locate_opt None h) ] |
| 811 | + | (_, Wrap tm) :: tms -> ( |
| 812 | + match process vars tm with |
| 813 | + | { value = Synth rtm; loc } -> ( |
| 814 | + let _, sty = Check.synth (Kinetic `Nolet) ctx (Asai.Range.locate_opt loc rtm) in |
| 815 | + match View.view_type sty "split" with |
| 816 | + | Canonical (_, Data { dim; constrs; _ }, _, _) -> |
| 817 | + let* () = |
| 818 | + match D.compare_zero dim with |
| 819 | + | Zero -> return () |
| 820 | + | Pos _ -> LS.lift (S.put true) in |
| 821 | + let* c, Dataconstr { args; _ } = S.return (Bwd.to_list constrs) in |
| 822 | + let left_ok = No.le_refl No.plus_omega in |
| 823 | + let right_ok = No.le_refl No.plus_omega in |
| 824 | + let first = |
| 825 | + Term |
| 826 | + (List.fold_left |
| 827 | + (fun fn arg -> locate_opt None (App { fn; arg; left_ok; right_ok })) |
| 828 | + (locate_opt None (Constr (Constr.to_string c, []))) |
| 829 | + (do_args args)) in |
| 830 | + let* rest = go tms in |
| 831 | + if List.length rest = 2 then return (first :: rest) |
| 832 | + else return (first :: tok (Op ",") :: rest) |
| 833 | + | _ -> fatal (Invalid_split (`Term, "non-datatype"))) |
| 834 | + | _ -> fatal (Nonsynthesizing "splitting term")) in |
| 835 | + fst (go tms false) |
| 836 | + |
776 | 837 | (* Most execution of commands we can do here, but there are a couple things where we need to call out to the executable: noting when an effectual action like 'echo' is taken (for recording warnings in compiled files), and loading another file. So this function takes a couple of callbacks as arguments. *) |
777 | 838 | let execute ~(action_taken : unit -> unit) ~(get_file : string -> Scope.trie) (cmd : Command.t) : |
778 | 839 | int option * (int * int * int) list = |
@@ -956,11 +1017,10 @@ let execute ~(action_taken : unit -> unit) ~(get_file : string -> Scope.trie) (c |
956 | 1017 | let (Found_hole { instant; termctx; ty; vars; parametric; _ }) = |
957 | 1018 | Global.find_hole data.number in |
958 | 1019 | Global.rewind_command ~parametric ~holes_allowed:(Ok ()) instant @@ fun () -> |
959 | | - let (Wrap tm) = data.tm in |
960 | 1020 | let content = |
961 | 1021 | let ctx = Norm.eval_ctx termctx in |
962 | | - match tm.value with |
963 | | - | Placeholder _ -> ( |
| 1022 | + match data.tms with |
| 1023 | + | [ (_, Wrap { value = Placeholder _; _ }) ] -> ( |
964 | 1024 | let ety = Norm.eval_term (Ctx.env ctx) ty in |
965 | 1025 | match View.view_type ety "split" with |
966 | 1026 | | Canonical (_, Pi (_, doms, _), _, _) -> |
@@ -1020,50 +1080,30 @@ let execute ~(action_taken : unit -> unit) ~(get_file : string -> Scope.trie) (c |
1020 | 1080 | fatal (Invalid_split (`Goal, "datatype with multiple constructors")) |
1021 | 1081 | | Canonical (_, UU _, _, _) -> fatal (Invalid_split (`Goal, "universe")) |
1022 | 1082 | | Neutral _ -> fatal (Invalid_split (`Goal, "neutral"))) |
1023 | | - | _ -> ( |
1024 | | - match process vars tm with |
1025 | | - | { value = Synth rtm; loc } -> ( |
1026 | | - let _, sty = Check.synth (Kinetic `Nolet) ctx (Asai.Range.locate_opt loc rtm) in |
1027 | | - match View.view_type sty "split" with |
1028 | | - | Canonical (_, Data { dim; constrs; _ }, _, _) -> |
1029 | | - let mapsto = |
1030 | | - match D.compare_zero dim with |
1031 | | - | Zero -> Token.to_string Mapsto |
1032 | | - | Pos _ -> Token.to_string DblMapsto in |
1033 | | - let rec do_args : type a p ap. (a, p, ap) Term.Telescope.t -> string -> string = |
1034 | | - fun args acc -> |
1035 | | - match args with |
1036 | | - | Emp -> acc |
1037 | | - | Ext (x, _, args) -> |
1038 | | - Option.value ~default:(Token.to_string Underscore) x |
1039 | | - ^ " " |
1040 | | - ^ do_args args acc in |
1041 | | - let do_constrs : type m ij. |
1042 | | - Constr.t * (m, ij) Value.dataconstr -> string list -> string list = |
1043 | | - fun (c, Dataconstr { args; _ }) acc -> |
1044 | | - (Constr.to_string c |
1045 | | - ^ ". " |
1046 | | - ^ do_args args " " |
1047 | | - ^ mapsto |
1048 | | - ^ " " |
1049 | | - ^ Token.to_string (Hole None)) |
1050 | | - :: acc in |
1051 | | - let constrs = Bwd.fold_right do_constrs constrs [] in |
1052 | | - let buf = Buffer.create 10 in |
1053 | | - PPrint.( |
1054 | | - ToBuffer.pretty 1.0 (Display.columns ()) buf |
1055 | | - (pp_complete_term (Wrap tm) `None)); |
1056 | | - Token.to_string Match |
1057 | | - ^ " " |
1058 | | - ^ Buffer.contents buf |
1059 | | - ^ " " |
1060 | | - ^ Token.to_string LBracket |
1061 | | - ^ " " |
1062 | | - ^ String.concat " | " constrs |
1063 | | - ^ " " |
1064 | | - ^ Token.to_string RBracket |
1065 | | - | _ -> fatal (Invalid_split (`Term, "non-datatype"))) |
1066 | | - | _ -> fatal (Nonsynthesizing "splitting term")) in |
| 1083 | + | _ -> |
| 1084 | + let tok t : observation = Token (t, ([], None)) in |
| 1085 | + let comma_tms = |
| 1086 | + List.tl |
| 1087 | + @@ |
| 1088 | + let open Monad.Ops (Monad.List) in |
| 1089 | + let* wscomma, Wrap tm = data.tms in |
| 1090 | + [ Token (Op ",", (wscomma, None)); Term tm ] in |
| 1091 | + let lines = split_match_cases ctx vars data.tms in |
| 1092 | + let mtch = |
| 1093 | + Asai.Range.locate_opt None |
| 1094 | + @@ outfix ~notn:Builtins.implicit_mtch |
| 1095 | + ~inner: |
| 1096 | + (Multiple |
| 1097 | + ( Left (Match, ([], None)), |
| 1098 | + Emp |
| 1099 | + <@ comma_tms |
| 1100 | + <: tok LBracket |
| 1101 | + <@ List.flatten (List.map (fun line -> tok (Op "|") :: line) lines), |
| 1102 | + Left (RBracket, ([], None)) )) in |
| 1103 | + let buf = Buffer.create 10 in |
| 1104 | + PPrint.( |
| 1105 | + ToBuffer.pretty 1.0 (Display.columns ()) buf (pp_complete_term (Wrap mtch) `None)); |
| 1106 | + Buffer.contents buf in |
1067 | 1107 | let ptm = TermParse.Term.(final (parse (`String { title = None; content }))) in |
1068 | 1108 | let disp = Display.get () in |
1069 | 1109 | Display.run ~init:{ disp with holes = `Without_number } @@ fun () -> |
|
0 commit comments