pieuvre/main.ml
Adrien Vannson 2d6028e010
Style
2022-05-19 21:50:57 +02:00

381 lines
13 KiB
OCaml
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

open Structs;;
open Pieuvre;;
open Tactic;;
(* Lorsqu'une tactique ne peut se lancer. Contient la tactique ainsi qu'un message contenant l'erreur.*)
exception TacticException of tactic*string;;
(* Lorsqu'une tactique est mal écrite*)
exception TacticParseException;;
(* Exception renvoyée lorsque la fonction de trou est appelée sur une liste trop grande. *)
(* Ne devrait jamais être lancée *)
exception TrouException;;
(* Parsage des arguments*)
let filename = ref "" in
let reduce_option = ref false in
let alpha_option = ref false in
let typecheck_option = ref false in
let computetype_option = ref false in
Arg.parse
[("-reduce", Arg.Set reduce_option, "Show the step-by-step reduction of a lambda-term");
("-alpha", Arg.Set alpha_option, "Check is two lambda-terms separated by '&' are alpha-convertible");
("-typecheck", Arg.Set typecheck_option, "Check if a lambda term has a given type");
("-computetype", Arg.Set computetype_option, "Computes the type of the input λ-term")]
(fun s -> filename := s)
"The available options are:";
(* Ouverture éventuelle du fichier *)
let file = match !filename with
| "" -> None
| fn -> Some (open_in fn)
in
let is_interactive = match file with
| None -> true
| _ -> false
in
if !reduce_option then (
let lexbuf = Lexing.from_channel (match file with
| None -> stdin
| Some file -> file
)
in
let lambda_term =
try
Parser.main_lambda Lexer.token lexbuf
with e -> (
Printf.printf "Can't read lambda term\n";
raise e
)
in
reduce lambda_term;
exit 0
);
if !alpha_option then (
let lexbuf = Lexing.from_channel (match file with
| None -> stdin
| Some file -> file
)
in
let lam1, lam2 =
try
Parser.main_two_lambda Lexer.token lexbuf
with e -> (
Printf.printf "Can't read lambda terms\n";
raise e
)
in
if alpha lam1 lam2 then (
Printf.printf "%s and %s are α-equivalent\n" (string_of_lam lam1) (string_of_lam lam2)
) else (
Printf.printf "%s and %s are not α-equivalent\n" (string_of_lam lam1) (string_of_lam lam2)
);
exit 0
);
if !typecheck_option then (
let lexbuf = Lexing.from_channel (match file with
| None -> stdin
| Some file -> file
)
in
let lambda_term, ty =
try
Parser.main_typed_lambda Lexer.token lexbuf
with e -> (
Printf.printf "Can't read typed lambda term\n";
raise e
)
in
Printf.printf "The type of %s is " (string_of_lam lambda_term);
if not (typecheck [] lambda_term ty) then (
Printf.printf "not "
);
Printf.printf "%s.\n" (string_of_ty ty);
exit 0
);
if !computetype_option then (
let lexbuf = Lexing.from_channel (match file with
| None -> stdin
| Some file -> file
)
in
let lambda_term =
try
Parser.main_lambda Lexer.token lexbuf
with e -> (
Printf.printf "Can't read lambda term\n";
raise e
)
in
begin match (computeType [] lambda_term) with
| Some t ->
Printf.printf "The type of %s is %s\n" (string_of_lam lambda_term) (string_of_ty t)
| None -> (
Printf.printf
"λ-term %s can't be typed! (it is not closed or not typable)\n"
(string_of_lam lambda_term)
)
end;
exit 0
);
(* Affiche un message si l'entrée est lue sur stdin *)
let show s = match file with
| None -> Printf.printf "%s" s
| _ -> ()
in
(* Ouvre un fichier contenant la preuve écrite *)
let log_file = open_out "log.8pus" in
let readline () =
let line = match file with
| None -> (
Printf.printf ">>> ";
flush stdout;
read_line ()
)
| Some f -> input_line f
in
Printf.fprintf log_file "%s\n" line;
flush log_file;
line
in
show "Please type the formula to prove\n";
let ty =
try
let lexbuf = Lexing.from_string (readline ()) in
Parser.main_type Lexer.token lexbuf
with e -> (
Printf.printf "Can't parse type\n";
raise e
)
in
let subgoals: (ty * (var_lambda * ty) list) list ref = ref [(ty, [])] in
(* On donne en paramètre une liste contenant un lambda-terme par trou (ie subgoal),
* et elle renvoie le lambda-terme complet (en remplaçant les trous) *)
let fill_holes = ref (fun holes -> List.hd holes) in
while !subgoals <> [] do
if is_interactive then (
(* Nettoyage du terminal *)
let _ = Sys.command("clear -x") in
(* Affichage des hypothèses *)
List.iter
(fun (var, h) -> Printf.printf "%s: %s\n" var (string_of_ty h))
(snd (List.hd !subgoals));
(* Affichage des sous-buts *)
Printf.printf "================\n";
List.iter (fun (ty, _) ->
Printf.printf "%s\n" (string_of_ty ty)
) !subgoals;
(* Lecture d'une tactique *)
Printf.printf "What do you want to do?\n"
);
let rec read_tactic () =
try
let lexbuf = Lexing.from_string (readline ()) in
Parser.main_tactic Lexer.token lexbuf
with e -> raise TacticParseException
in
let rec applyTactic tactic = (
let (ty, hyps) = List.hd !subgoals in
let find_hyp (var: var_lambda) : ty option =
let rec explore = function
| [] -> None
| (var_hyp, hyp) :: hyps when var_hyp = var -> Some hyp
| _ :: hyps -> explore hyps
in
explore hyps
in
let f = !fill_holes in
match tactic with
| Intro var -> (
match ty with
| TImpl (ty1, ty2) -> (
subgoals := List.tl !subgoals;
subgoals := (ty2, (var, ty1) :: hyps) :: !subgoals;
fill_holes := fun holes -> match holes with
| h :: hs -> f (LFun (var, ty1, h) :: hs)
| _ -> raise TrouException
)
| _ -> raise (TacticException(tactic, "Cannot intro when the goal is not an implication."))
)
| Intros [] -> ()
| Intros (t :: ts) -> (
applyTactic (Intro t);
applyTactic (Intros ts)
)
| Assumption -> (
let rec explore = function
| (var, hyp) :: _ when hyp = ty -> (
subgoals := List.tl !subgoals;
fill_holes := fun holes -> f ((LVar var) :: holes)
)
| [] -> raise (TacticException (tactic, "Cannot find such an assumption."))
| _ :: hyps -> explore hyps
in
explore hyps
)
| Apply var -> (
match find_hyp var with
| Some (TImpl (t1, t2)) when t2 = ty -> (
subgoals := List.tl !subgoals;
subgoals := (t1, hyps) :: !subgoals;
fill_holes := function
| hole :: holes -> f ((LApp (LVar var, hole)) :: holes)
| [] -> raise TrouException
)
| None -> raise (TacticException (
tactic,
"Cannot apply hypotesis " ^ var ^ " as it does not exist."
))
| _ -> raise (TacticException (
tactic,
"Cannot apply hypotesis " ^ var ^ " as it is not an implication."
))
)
| Elim var -> (
match find_hyp var with
| Some TFalse -> (
subgoals := List.tl !subgoals;
fill_holes := fun holes -> f ((LExf (LVar var, ty)) :: holes)
)
| Some TAnd(tl,tr) -> (
subgoals := List.tl !subgoals;
subgoals := (TImpl (tl, TImpl (tr, ty)), hyps) :: !subgoals;
fill_holes := fun holes -> match holes with
| h::r -> f ((LApp (LApp (h, LFst (LVar var)), LSnd (LVar var))) :: r)
| _ -> raise TrouException
)
| Some TOr(tl,tr) -> (
subgoals := List.tl !subgoals;
subgoals := (TImpl (tl, ty), hyps) :: (TImpl (tr, ty), hyps) :: !subgoals;
fill_holes := fun holes -> match holes with
| hl::hr::r -> f (LCase (LVar var, hl, hr) :: r)
| _ -> raise TrouException
)
| None -> raise (TacticException (
tactic,
"Cannot apply hypotesis " ^ var ^ " as it does not exist."
))
| _ -> raise (TacticException (
tactic,
"Cannot apply hypotesis " ^ var ^ " as it is neither an implication, a /\\ nor a \\/"
))
)
(* Pour montrer A, on montre B -> A et B *)
| Cut tint -> (
subgoals := List.tl !subgoals;
subgoals := (TImpl (tint, ty), hyps) :: (tint, hyps) :: !subgoals;
fill_holes := function
| pf :: px :: s -> f ((LApp (pf, px)) :: s)
| _ -> raise TrouException
)
| Split -> (
match ty with
| TAnd(t1,t2) -> (
subgoals := List.tl !subgoals;
subgoals := (t1, hyps) :: (t2, hyps) :: !subgoals;
fill_holes := fun holes -> match holes with
| h1 :: h2 :: hs -> f (LCouple(h1,h2) :: hs)
| _ -> raise TrouException
)
| _ -> raise (TacticException (tactic, "Cannot split as the goal is not a /\\"))
)
| Left -> (
match ty with
| TOr(tl,tr) -> (
subgoals := List.tl !subgoals;
subgoals := (tl, hyps) :: !subgoals;
fill_holes := fun holes -> match holes with
| hl :: hs -> f (LIg(hl,tr) :: hs)
| _ -> raise TrouException
)
| _ -> raise (TacticException (tactic, "Cannot apply left as the goal is not a \\/"))
)
| Right -> (
match ty with
| TOr(tl,tr) -> (
subgoals := List.tl !subgoals;
subgoals := (tr, hyps) :: !subgoals;
fill_holes := fun holes -> match holes with
| hr :: hs -> f (LId(hr,tl) :: hs)
| _ -> raise TrouException
)
| _ -> raise (TacticException (tactic, "Cannot apply right as the goal is not a \\/"))
)
| Exact l -> (
if not (typecheck hyps l ty) then
let ty_str = match computeType [] l with
| None -> "None"
| Some t -> string_of_ty t
in
raise (TacticException (
tactic,
"λ-term " ^ (string_of_lam l) ^
" can't be typed with " ^ (string_of_ty ty) ^
" as its type is " ^ ty_str
))
else (
subgoals := List.tl !subgoals;
fill_holes := fun holes -> f (l::holes)
)
)
)
in
let rec applyUntilWorking () =
try (
let readTactic = read_tactic () in
applyTactic readTactic
)
with
| TacticException(t,s) ->
Printf.printf "\027[31mCannot apply the tactic: %s\027[0m\n" s;
if is_interactive then
applyUntilWorking ()
else
raise (TacticException (t,s))
| TacticParseException ->
Printf.printf "\027[31mCannot parse the tactic, please refer to pieuvre documentation.\027[0m\n";
if is_interactive then
applyUntilWorking ()
else
raise TacticParseException
| e ->
Printf.printf "\027[31mPieuvre Failed Unexpectedly !\027[0m\n";
raise e
in
applyUntilWorking ()
done;
let finalLam = !fill_holes [] in
if (typecheck [] finalLam ty) then (
Printf.printf "Final proof :\n";
reduce finalLam;
) else (
Printf.printf "Invalid proof constructed!\n";
Printf.printf "%s can't be typed with %s.\n" (string_of_lam finalLam) (string_of_ty ty);
Printf.printf "The whole development team of pieuvre is sorry for the damage eventually done by this error.\n"
);
close_out log_file;;