pieuvre/main.ml

261 lines
7.6 KiB
OCaml
Raw Normal View History

2024-04-30 11:44:28 +02:00
open Parser_entry
2024-04-09 11:30:52 +02:00
open Affichage
2024-04-30 11:44:28 +02:00
open Proof
open Types
2024-05-16 12:38:45 +02:00
open Hlam
open Lam
2024-04-09 11:09:33 +02:00
2024-04-16 11:40:08 +02:00
type entry =
Simple of (unit -> instr)
2024-04-25 04:20:12 +02:00
| Reduce of Lam.lam
2024-04-16 11:40:08 +02:00
| AlphaEquiv of Lam.lam * Lam.lam
2024-05-13 18:05:28 +02:00
type interactive_state = (hlam * ty) option * proof
2024-04-16 11:40:08 +02:00
module StringMap = Map.Make(String)
2024-04-30 11:44:28 +02:00
let parse_lam t =
match Parser.main Lexer.token t with
| Lam l -> l
| Instr _ -> failwith "entry must be a lam"
2024-04-30 11:44:28 +02:00
let parse_cmd t =
match Parser.main Lexer.token t with
| Instr is -> is
2024-04-30 11:44:28 +02:00
| Lam _ -> failwith "entry must be a cmd"
2024-05-13 18:05:28 +02:00
let show_beta_reduction e =
2024-04-25 04:20:12 +02:00
let rec aux = function
Some e ->
print_expr e;
print_newline ();
aux (Lam.betastep e);
| None -> ()
in print_expr e;
print_newline ();
let e = Lam.alpha_convert ~readable:true e in
2024-04-25 04:20:12 +02:00
print_expr e;
print_newline ();
aux (Lam.betastep e)
2024-05-13 18:05:28 +02:00
let rec beta_reduce (l : Lam.lam) =
match Lam.betastep l with
| None -> l
| Some l' -> beta_reduce l'
let clean_state ((s, p) : interactive_state) =
let assoc, new_p = clean_proof p in
match s with
| None -> None, new_p
| Some (hl, ty) -> Some (clean_hlam assoc hl, ty), new_p
2024-04-25 04:20:12 +02:00
let alpha_get_lam where_from =
let input_str = In_channel.input_all where_from in
match Str.split (Str.regexp "&") input_str with
[s1; s2] -> AlphaEquiv (
2024-04-30 11:44:28 +02:00
parse_lam (Lexing.from_string (s1^"\n")),
parse_lam (Lexing.from_string s2)
2024-04-25 04:20:12 +02:00
)
| _ -> failwith "Alpha-equivalence: nombre de delimiteurs incorrect"
let check_via_coq (e : lam) (t : ty) : unit =
(* fill a map to track all types used in the proof *)
let rec fill_ty_map (m : int StringMap.t) = function
TVar x -> StringMap.add x 0 m
| Arr(t1, t2) | Or(t1, t2) | And(t1, t2) ->
let m1 = fill_ty_map m t1 in
fill_ty_map m1 t2
| Bot -> m
(* generate the "forall A B.." *)
in let intro_of_ty (m : int StringMap.t) : string * int =
let tys = StringMap.to_list m in
let n = List.length tys in
let s = List.fold_left (fun acc x -> (fst x ^ " ") ^ acc) "" tys in
if n = 0
then ("", 0)
else ("forall " ^ s ^ ", ", n)
(* generate the right amount of intro. to introduce type variables*)
in let rec repeat_intro n =
if n = 0
then ""
else "intro. " ^ repeat_intro (n-1)
in
(* build checker.v *)
let m = fill_ty_map StringMap.empty t in
let (ty_vars, intro_n) = intro_of_ty m in
let goal_ty = string_of_ty t in
let proof_term = "(" ^ string_of_expr e ^ ")" in
let checker_file = open_out "checker.v" in
let _ =
Printf.fprintf checker_file "Goal %s%s.\n%s\n exact %s.\n"
ty_vars goal_ty (repeat_intro intro_n) proof_term;
close_out checker_file in
(* start Coq in interactive mode and send it the proof term
while blocking coqtop to print on stdout *)
let r = Unix.system "coqtop < checker.v > log 2>&1" in
match r with
Unix.WEXITED x ->
if x = 0
then print_error "Validated by Coq." ""
else print_error "Couldn't not validate proof." ""
| _ -> print_error "Coq failed." ""
2024-05-13 18:05:28 +02:00
(** Interactive loop
- cg : current top goal : type and reference to lambda-term
- g, gs : next goals
- sq : previous states of the interactive loop
*)
let rec interactive (get_instr : unit -> instr) (sl : (interactive_state) list) : proof =
2024-05-14 10:49:24 +02:00
let (cg, (g, gs)), sq = match sl with
[] -> (None, (None, [])), []
| s::sq -> s, sq
in
2024-04-30 11:44:28 +02:00
begin
let _ = match g with
2024-05-01 11:07:40 +02:00
None -> print_string "\n\027[1mNo more goals.\027[0m\n"
2024-05-01 10:44:36 +02:00
| Some g' -> print_newline (); print_goal g'
2024-04-30 11:44:28 +02:00
in
2024-05-01 10:35:12 +02:00
try
match get_instr () with
Cmd c ->
begin match c with
Goal ty ->
let rh = Ref (ref Hole) in
[Some (rh, ty), (Some (rh, ty, []), [])] |> interactive get_instr
| Undo -> interactive get_instr sq
| Qed -> begin match cg with
None ->
print_error "No current goal" "";
(cg, (g, gs))::sq |> interactive get_instr
| Some (h, t) ->
let l = lam_of_hlam h
|> beta_reduce in
(* uncaught Could_not_infer but won't happened
because exact only allow typable terms *)
if Typing.typecheck [] l t then begin
print_string "Ok";
(cg, (g, gs))::sq |> interactive get_instr
end else begin
print_error "Typing failed" "";
print_expr l;
print_newline ();
print_ty t;
(cg, (g, gs))::sq |> interactive get_instr
end
end
| Check -> begin match cg with
(* !! Doesn't work with terms containing exfalso
and Left / Right !! *)
None ->
print_error "No current goal" "";
(cg, (g, gs))::sq |> interactive get_instr
| Some (h, t) ->
begin try
let l = lam_of_hlam h in
check_via_coq l t
with _ -> print_error "Proof is not over" "";
end;
(cg, (g, gs))::sq |> interactive get_instr;
end
2024-05-13 18:05:28 +02:00
end
| Tact t ->
(cg, (apply_tactic (g, gs) t))::(clean_state (cg, (g, gs)))::sq |> interactive get_instr
2024-05-01 10:35:12 +02:00
with
Parser.Error ->
2024-05-01 11:07:40 +02:00
print_error "Invalid input" "";
(cg, (g, gs))::sq |> interactive get_instr
2024-05-11 11:44:43 +02:00
| TacticFailed arg ->
print_error "Tactic failed" arg;
(cg, (g, gs))::sq |> interactive get_instr
2024-05-14 11:43:57 +02:00
| End_of_file | Lexer.Eof ->
2024-05-01 10:35:12 +02:00
print_string "Bye!\n";
(g, gs)
2024-04-30 11:44:28 +02:00
end
2024-04-16 11:40:08 +02:00
let nom_fichier = ref ""
2024-04-25 04:20:12 +02:00
let reduce = ref false
2024-04-16 14:24:06 +02:00
let alpha = ref false
2024-04-25 04:20:12 +02:00
let equiv_fichier = ref ""
2024-04-16 11:40:08 +02:00
let recupere_entree () =
let optlist = [
2024-04-25 04:20:12 +02:00
("-alpha",
Arg.Set alpha,
"Vérifie l'alpha équivalence de deux termes séparés par &");
("-reduce",
Arg.Set reduce,
"Affiche les réductions successives du lambda-terme")
2024-04-16 11:40:08 +02:00
] in
let usage = "Bienvenue à bord." in (* message d'accueil, option -help *)
Arg.parse (* ci-dessous les 3 arguments de Arg.parse : *)
optlist (* la liste des options definie plus haut *)
(fun s -> nom_fichier := s) (* la fonction a declencher lorsqu'on recupere un string qui n'est pas une option : ici c'est le nom du fichier, et on stocke cette information dans la reference nom_fichier *)
usage; (* le message d'accueil *)
2024-04-16 14:24:06 +02:00
try
2024-04-16 11:40:08 +02:00
let where_from = match !nom_fichier with
| "" -> stdin
| s -> open_in s in
2024-04-25 04:20:12 +02:00
if !alpha
then alpha_get_lam where_from
else if !reduce
2024-05-14 11:43:57 +02:00
then Reduce (Lexing.from_channel where_from |> parse_lam)
else Simple begin
let cmd_buff = ref [] in
if !nom_fichier = "" then
(
fun () ->
match !cmd_buff with
| [] ->
begin match (read_line ())^"\n"
|> Lexing.from_string
|> parse_cmd with
[] -> raise Parser.Error
| e::q -> cmd_buff := q; e
end
| e::q -> cmd_buff := q; e
)
else
(
fun () ->
match !cmd_buff with
| [] ->
begin match (input_line where_from)^"\n"
|> Lexing.from_string
|> parse_cmd with
[] -> raise End_of_file
| e::q -> cmd_buff := q; e
end
| e::q -> cmd_buff := q; e
)
end
2024-05-01 11:07:40 +02:00
with e -> (print_error "problème de saisie" ""; raise e)
2024-04-09 11:09:33 +02:00
2024-04-16 11:40:08 +02:00
(* la fonction principale *)
let run () =
try
match recupere_entree () with
Simple get_instr ->
let _ = interactive get_instr [None, (None, [])] in ()
2024-05-14 11:43:57 +02:00
| Reduce l ->
let _ = show_beta_reduction l in ()
2024-04-16 11:40:08 +02:00
| AlphaEquiv (l1, l2) -> begin
if ((Lam.(=~)) l1 l2) then
print_string "true\n"
else
print_string "false\n"
end;
flush stdout
with e -> raise e
2024-04-09 11:09:33 +02:00
2024-04-16 11:40:08 +02:00
let _ = run ()
2024-04-09 11:09:33 +02:00