(* ========================================================================= *)
(* Simple WHILE-language with relational semantics.                          *)
(* ========================================================================= *)

prioritize_num();;

parse_as_infix("refined",(12,"right"));;

(* ------------------------------------------------------------------------- *)
(* Logical operations "lifted" to predicates, for readability.               *)
(* ------------------------------------------------------------------------- *)

parse_as_infix("AND",(20,"right"));;
parse_as_infix("OR",(16,"right"));;
parse_as_infix("IMP",(13,"right"));;
parse_as_infix("IMPLIES",(12,"right"));;

let FALSE = new_definition
  `FALSE = \x:S. F`;;
let TRUE = new_definition
  `TRUE = \x:S. T`;;
let NOT = new_definition
  `NOT p = \x:S. ~(p x)`;;
let AND = new_definition
  `p AND q = \x:S. p x /\ q x`;;
let OR = new_definition
  `p OR q = \x:S. p x \/ q x`;;
let ANDS = new_definition
  `ANDS P = \x:S. !p. P p ==> p x`;;
let ORS = new_definition
  `ORS P = \x:S. ?p. P p /\ p x`;;
let IMP = new_definition
  `p IMP q = \x:S. p x ==> q x`;;
(* ------------------------------------------------------------------------- *) (* This one is different, corresponding to "subset". *) (* ------------------------------------------------------------------------- *)
let IMPLIES = new_definition
  `p IMPLIES q <=> !x:S. p x ==> q x`;;
(* ------------------------------------------------------------------------- *) (* Simple procedure to prove tautologies at the predicate level. *) (* ------------------------------------------------------------------------- *) let PRED_TAUT = let tac = REWRITE_TAC[FALSE; TRUE; NOT; AND; OR; ANDS; ORS; IMP; IMPLIES; FUN_EQ_THM] THEN MESON_TAC[] in fun tm -> prove(tm,tac);; (* ------------------------------------------------------------------------- *) (* Some applications. *) (* ------------------------------------------------------------------------- *) let IMPLIES_TRANS = PRED_TAUT `!p q r. p IMPLIES q /\ q IMPLIES r ==> p IMPLIES r`;; (* ------------------------------------------------------------------------- *) (* Enumerated type of basic commands, and other derived commands. *) (* ------------------------------------------------------------------------- *) parse_as_infix("Seq",(26,"right"));;
let command_INDUCTION,command_RECURSION = define_type
  "command = Assign (S->S)
           | Seq command command
           | Ite (S->bool) command command
           | While (S->bool) command";
;
let SKIP = new_definition
  `SKIP = Assign I`;;
let ABORT = new_definition
  `ABORT = While TRUE SKIP`;;
let IF = new_definition
  `IF e c = Ite e c SKIP`;;
let DO = new_definition
  `DO c e = c Seq (While e c)`;;
let ASSERT = new_definition
  `ASSERT g = Ite g SKIP ABORT`;;
(* ------------------------------------------------------------------------- *) (* Annotation commands, to allow insertion of loop (in)variants. *) (* ------------------------------------------------------------------------- *)
let AWHILE = new_definition
  `AWHILE (i:S->bool) (v:S->S->bool) (e:S->bool) c = While e c`;;
let ADO = new_definition
  `ADO (i:S->bool) (v:S->S->bool) c (e:S->bool) = DO c e`;;
(* ------------------------------------------------------------------------- *) (* Useful properties of type constructors for commands. *) (* ------------------------------------------------------------------------- *) let command_DISTINCT = distinctness "command";; let command_INJECTIVE = injectivity "command";; (* ------------------------------------------------------------------------- *) (* Relational semantics of commands. *) (* ------------------------------------------------------------------------- *) let sem_RULES,sem_INDUCT,sem_CASES = new_inductive_definition `(!f s. sem(Assign f) s (f s)) /\ (!c1 c2 s s' s''. sem(c1) s s' /\ sem(c2) s' s'' ==> sem(c1 Seq c2) s s'') /\ (!e c1 c2 s s'. e s /\ sem(c1) s s' ==> sem(Ite e c1 c2) s s') /\ (!e c1 c2 s s'. ~(e s) /\ sem(c2) s s' ==> sem(Ite e c1 c2) s s') /\ (!e c s. ~(e s) ==> sem(While e c) s s) /\ (!e c s s' s''. e s /\ sem(c) s s' /\ sem(While e c) s' s'' ==> sem(While e c) s s'')`;; (* ------------------------------------------------------------------------- *) (* A more "denotational" view of the semantics. *) (* ------------------------------------------------------------------------- *)
let SEM_ASSIGN = 
prove (`sem(Assign f) s s' <=> (s' = f s)`,
GEN_REWRITE_TAC LAND_CONV [sem_CASES] THEN REWRITE_TAC[command_DISTINCT; command_INJECTIVE] THEN MESON_TAC[]);;
let SEM_SEQ = 
prove (`sem(c1 Seq c2) s s' <=> ?s''. sem c1 s s'' /\ sem c2 s'' s'`,
GEN_REWRITE_TAC LAND_CONV [sem_CASES] THEN REWRITE_TAC[command_DISTINCT; command_INJECTIVE] THEN MESON_TAC[]);;
let SEM_ITE = 
prove (`sem(Ite e c1 c2) s s' <=> e s /\ sem c1 s s' \/ ~(e s) /\ sem c2 s s'`,
GEN_REWRITE_TAC LAND_CONV [sem_CASES] THEN REWRITE_TAC[command_DISTINCT; command_INJECTIVE] THEN MESON_TAC[]);;
let SEM_SKIP = 
prove (`sem(SKIP) s s' <=> (s' = s)`,
REWRITE_TAC[SKIP; SEM_ASSIGN; I_THM]);;
let SEM_IF = 
prove (`sem(IF e c) s s' <=> e s /\ sem c s s' \/ ~(e s) /\ (s = s')`,
REWRITE_TAC[IF; SEM_ITE; SEM_SKIP; EQ_SYM_EQ]);;
let SEM_WHILE = 
prove (`sem(While e c) s s' <=> sem(IF e (c Seq While e c)) s s'`,
GEN_REWRITE_TAC LAND_CONV [sem_CASES] THEN REWRITE_TAC[FUN_EQ_THM; SEM_IF; SEM_SEQ] THEN REPEAT GEN_TAC THEN REWRITE_TAC[command_DISTINCT; command_INJECTIVE] THEN MESON_TAC[]);;
let SEM_ABORT = 
prove (`sem(ABORT) s s' <=> F`,
let lemma = prove
   (`!c s s'. sem c s s' ==> ~(c = ABORT)`,
    MATCH_MP_TAC sem_INDUCT THEN
    REWRITE_TAC[command_DISTINCT; command_INJECTIVE; ABORT] THEN
    REWRITE_TAC[FUN_EQ_THM; TRUE] THEN MESON_TAC[]) in
  MESON_TAC[lemma]);;
let SEM_DO = 
prove (`sem(DO c e) s s' <=> sem(c Seq IF e (DO c e)) s s'`,
REWRITE_TAC[DO; SEM_SEQ; GSYM SEM_WHILE]);;
let SEM_ASSERT = 
prove (`sem(ASSERT g) s s' <=> g s /\ (s' = s)`,
REWRITE_TAC[ASSERT; SEM_ITE; SEM_SKIP; SEM_ABORT]);;
(* ------------------------------------------------------------------------- *) (* Proofs that all commands are deterministic. *) (* ------------------------------------------------------------------------- *)
let deterministic = new_definition
  `deterministic r <=> !s s1 s2. r s s1 /\ r s s2 ==> (s1 = s2)`;;
let DETERMINISM = 
prove (`!c:(S)command. deterministic(sem c)`,
REWRITE_TAC[deterministic] THEN SUBGOAL_THEN `!c s s1. sem c s s1 ==> !s2:S. sem c s s2 ==> (s1 = s2)` (fun th -> MESON_TAC[th]) THEN MATCH_MP_TAC sem_INDUCT THEN CONJ_TAC THENL [ALL_TAC; REPEAT CONJ_TAC THEN REPEAT GEN_TAC THEN DISCH_TAC] THEN REPEAT GEN_TAC THEN ONCE_REWRITE_TAC[sem_CASES] THEN REWRITE_TAC[command_DISTINCT; command_INJECTIVE] THEN ASM_MESON_TAC[]);;
(* ------------------------------------------------------------------------- *) (* Termination, weakest liberal precondition and weakest precondition. *) (* ------------------------------------------------------------------------- *)
let terminates = new_definition
  `terminates c s <=> ?s'. sem c s s'`;;
let wlp = new_definition
  `wlp c q s <=> !s'. sem c s s' ==> q s'`;;
let wp = new_definition
  `wp c q s <=> terminates c s /\ wlp c q s`;;
(* ------------------------------------------------------------------------- *) (* Dijkstra's healthiness conditions (the last because of determinism). *) (* ------------------------------------------------------------------------- *)
let WP_TOTAL = 
prove (`!c. (wp c FALSE = FALSE)`,
REWRITE_TAC[FUN_EQ_THM; wp; wlp; terminates; FALSE] THEN MESON_TAC[]);;
let WP_MONOTONIC = 
prove (`q IMPLIES r ==> wp c q IMPLIES wp c r`,
REWRITE_TAC[IMPLIES; wp; wlp; terminates] THEN MESON_TAC[]);;
let WP_CONJUNCTIVE = 
prove (`(wp c q) AND (wp c r) = wp c (q AND r)`,
REWRITE_TAC[FUN_EQ_THM; wp; wlp; terminates; AND] THEN MESON_TAC[]);;
let WP_DISJUNCTIVE = 
prove (`(wp c p) OR (wp c q) = wp c (p OR q)`,
REWRITE_TAC[FUN_EQ_THM; wp; wlp; OR; terminates] THEN MESON_TAC[REWRITE_RULE[deterministic] DETERMINISM]);;
(* ------------------------------------------------------------------------- *) (* Weakest preconditions for the primitive and derived commands. *) (* ------------------------------------------------------------------------- *)
let WP_ASSIGN = 
prove (`!f q. wp (Assign f) q = q o f`,
REWRITE_TAC[wp; wlp; terminates; o_THM; FUN_EQ_THM; SEM_ASSIGN] THEN MESON_TAC[]);;
let WP_SEQ = 
prove (`!c1 c2 q. wp (c1 Seq c2) q = wp c1 (wp c2 q)`,
REWRITE_TAC[wp; wlp; terminates; SEM_SEQ; FUN_EQ_THM] THEN MESON_TAC[REWRITE_RULE[deterministic] DETERMINISM]);;
let WP_ITE = 
prove (`!e c1 c2 q. wp (Ite e c1 c2) q = (e AND wp c1 q) OR (NOT e AND wp c2 q)`,
REWRITE_TAC[wp; wlp; terminates; SEM_ITE; FUN_EQ_THM; AND; OR; NOT] THEN MESON_TAC[]);;
let WP_WHILE = 
prove (`!e c. wp (IF e (c Seq While e c)) q = wp (While e c) q`,
REWRITE_TAC[FUN_EQ_THM; wp; wlp; terminates; GSYM SEM_WHILE]);;
let WP_SKIP = 
prove (`!q. wp SKIP q = q`,
REWRITE_TAC[FUN_EQ_THM; SKIP; WP_ASSIGN; I_THM; o_THM]);;
let WP_ABORT = 
prove (`!q. wp ABORT q = FALSE`,
REWRITE_TAC[FUN_EQ_THM; wp; wlp; terminates; SEM_ABORT; FALSE]);;
let WP_IF = 
prove (`!e c q. wp (IF e c) q = (e AND wp c q) OR (NOT e AND q)`,
REWRITE_TAC[IF; WP_ITE; WP_SKIP]);;
let WP_DO = 
prove (`!e c. wp (c Seq IF e (DO c e)) q = wp (DO c e) q`,
REWRITE_TAC[FUN_EQ_THM; wp; wlp; terminates; GSYM SEM_DO]);;
let WP_ASSERT = 
prove (`!g q. wp (ASSERT g) q = g AND q`,
REWRITE_TAC[wp; wlp; terminates; SEM_ASSERT; FUN_EQ_THM; AND] THEN MESON_TAC[]);;
(* ------------------------------------------------------------------------- *) (* Rules for total correctness. *) (* ------------------------------------------------------------------------- *)
let correct = new_definition
  `correct p c q <=> p IMPLIES (wp c q)`;;
let CORRECT_PRESTRENGTH = 
prove (`!p p' c q. p IMPLIES p' /\ correct p' c q ==> correct p c q`,
REWRITE_TAC[correct; IMPLIES_TRANS]);;
let CORRECT_POSTWEAK = 
prove (`!p c q q'. correct p c q' /\ q' IMPLIES q ==> correct p c q`,
REWRITE_TAC[correct] THEN MESON_TAC[WP_MONOTONIC; IMPLIES_TRANS]);;
let CORRECT_ASSIGN = 
prove (`!p f q. (p IMPLIES (\s. q(f s))) ==> correct p (Assign f) q`,
REWRITE_TAC[correct; WP_ASSIGN; IMPLIES; o_THM]);;
let CORRECT_SEQ = 
prove (`!p q r c1 c2. correct p c1 r /\ correct r c2 q ==> correct p (c1 Seq c2) q`,
REWRITE_TAC[correct; WP_SEQ; o_THM] THEN MESON_TAC[WP_MONOTONIC; IMPLIES_TRANS]);;
let CORRECT_ITE = 
prove (`!p e c1 c2 q. correct (p AND e) c1 q /\ correct (p AND (NOT e)) c2 q ==> correct p (Ite e c1 c2) q`,
REWRITE_TAC[correct; WP_ITE; AND; NOT; IMPLIES; OR] THEN MESON_TAC[]);;
let CORRECT_WHILE = 
prove (`! (<<) p c q e invariant. WF(<<) /\ p IMPLIES invariant /\ (NOT e) AND invariant IMPLIES q /\ (!X:S. correct (invariant AND e AND (\s. X = s)) c (invariant AND (\s. s << X))) ==> correct p (While e c) q`,
REWRITE_TAC[correct; IMPLIES; IN; AND; NOT] THEN REPEAT STRIP_TAC THEN SUBGOAL_THEN `!s:S. invariant s ==> wp (While e c) q s` MP_TAC THENL [ALL_TAC; ASM_MESON_TAC[]] THEN FIRST_ASSUM(MATCH_MP_TAC o REWRITE_RULE[WF_IND]) THEN X_GEN_TAC `s:S` THEN REPEAT DISCH_TAC THEN ONCE_REWRITE_TAC[GSYM WP_WHILE] THEN REWRITE_TAC[WP_IF; WP_SEQ; AND; OR; NOT; o_THM] THEN ASM_CASES_TAC `(e:S->bool) s` THEN ASM_REWRITE_TAC[] THENL [ALL_TAC; ASM_MESON_TAC[]] THEN SUBGOAL_THEN `wp c (\x:S. invariant x /\ x << s) (s:S) :bool` MP_TAC THENL [FIRST_ASSUM MATCH_MP_TAC THEN ASM_REWRITE_TAC[]; ALL_TAC] THEN SUBGOAL_THEN `(\x:S. invariant x /\ x << (s:S)) IMPLIES wp (While e c) q` MP_TAC THENL [REWRITE_TAC[IMPLIES] THEN ASM_MESON_TAC[]; ALL_TAC] THEN MESON_TAC[WP_MONOTONIC; IMPLIES]);;
let CORRECT_SKIP = 
prove (`!p q. (p IMPLIES q) ==> correct p SKIP q`,
REWRITE_TAC[correct; WP_SKIP]);;
let CORRECT_ABORT = 
prove (`!p q. F ==> correct p ABORT q`,
REWRITE_TAC[]);;
let CORRECT_IF = 
prove (`!p e c q. correct (p AND e) c q /\ (p AND (NOT e)) IMPLIES q ==> correct p (IF e c) q`,
REWRITE_TAC[correct; WP_IF; AND; NOT; IMPLIES; OR] THEN MESON_TAC[]);;
let CORRECT_DO = 
prove (`! (<<) p q c invariant. WF(<<) /\ (e AND invariant) IMPLIES p /\ ((NOT e) AND invariant) IMPLIES q /\ (!X:S. correct (p AND (\s. X = s)) c (invariant AND (\s. s << X))) ==> correct p (DO c e) q`,
REPEAT STRIP_TAC THEN REWRITE_TAC[DO] THEN MATCH_MP_TAC CORRECT_SEQ THEN EXISTS_TAC `invariant:S->bool` THEN ASM_REWRITE_TAC[] THEN CONJ_TAC THENL [POP_ASSUM_LIST(MP_TAC o end_itlist CONJ) THEN REWRITE_TAC[correct; GSYM WP_CONJUNCTIVE] THEN REWRITE_TAC[AND; IMPLIES] THEN MESON_TAC[]; MATCH_MP_TAC CORRECT_WHILE THEN MAP_EVERY EXISTS_TAC [`(<<) :S->S->bool`; `invariant:S->bool`] THEN ASM_REWRITE_TAC[IMPLIES] THEN X_GEN_TAC `X:S` THEN MATCH_MP_TAC CORRECT_PRESTRENGTH THEN EXISTS_TAC `p AND (\s:S. X = s)` THEN ASM_REWRITE_TAC[] THEN UNDISCH_TAC `(e:S->bool) AND invariant IMPLIES p` THEN REWRITE_TAC[AND; IMPLIES] THEN MESON_TAC[]]);;
let CORRECT_ASSERT = 
prove (`!p g q. p IMPLIES (g AND q) ==> correct p (ASSERT g) q`,
REWRITE_TAC[correct; WP_ASSERT]);;
(* ------------------------------------------------------------------------- *) (* VCs for the basic commands (in fact only assign should be needed). *) (* ------------------------------------------------------------------------- *)
let VC_ASSIGN = 
prove (`p IMPLIES (q o f) ==> correct p (Assign f) q`,
REWRITE_TAC[o_DEF; CORRECT_ASSIGN]);;
let VC_SKIP = 
prove (`p IMPLIES q ==> correct p SKIP q`,
REWRITE_TAC[CORRECT_SKIP]);;
let VC_ABORT = 
prove (`F ==> correct p ABORT q`,
MATCH_ACCEPT_TAC CORRECT_ABORT);;
let VC_ASSERT = 
prove (`p IMPLIES (b AND q) ==> correct p (ASSERT b) q`,
REWRITE_TAC[CORRECT_ASSERT]);;
(* ------------------------------------------------------------------------- *) (* VCs for composite commands other than sequences. *) (* ------------------------------------------------------------------------- *)
let VC_ITE = 
prove (`correct (p AND e) c1 q /\ correct (p AND NOT e) c2 q ==> correct p (Ite e c1 c2) q`,
REWRITE_TAC[CORRECT_ITE]);;
let VC_IF = 
prove (`correct (p AND e) c q /\ p AND NOT e IMPLIES q ==> correct p (IF e c) q`,
REWRITE_TAC[CORRECT_IF]);;
let VC_AWHILE_VARIANT = 
prove (`WF(<<) /\ p IMPLIES invariant /\ (NOT e) AND invariant IMPLIES q /\ (!X. correct (invariant AND e AND (\s. X = s)) c (invariant AND (\s. s << X))) ==> correct p (AWHILE invariant (<<) e c) q`,
REWRITE_TAC[AWHILE; CORRECT_WHILE]);;
let VC_AWHILE_MEASURE = 
prove (`p IMPLIES invariant /\ (NOT e) AND invariant IMPLIES q /\ (!X. correct (invariant AND e AND (\s:S. X = m(s))) c (invariant AND (\s. m(s) < X))) ==> correct p (AWHILE invariant (MEASURE m) e c) q`,
STRIP_TAC THEN MATCH_MP_TAC VC_AWHILE_VARIANT THEN ASM_REWRITE_TAC[WF_MEASURE] THEN X_GEN_TAC `X:S` THEN FIRST_ASSUM(MP_TAC o SPEC `(m:S->num) X`) THEN REWRITE_TAC[correct; AND; IMPLIES; MEASURE] THEN MESON_TAC[]);;
let VC_ADO_VARIANT = 
prove (`WF(<<) /\ (e AND invariant) IMPLIES p /\ ((NOT e) AND invariant) IMPLIES q /\ (!X. correct (p AND (\s. X = s)) c (invariant AND (\s. s << X))) ==> correct p (ADO invariant (<<) c e) q`,
REWRITE_TAC[ADO; CORRECT_DO]);;
let VC_ADO_MEASURE = 
prove (`(e AND invariant) IMPLIES p /\ ((NOT e) AND invariant) IMPLIES q /\ (!X. correct (p AND (\s:S. X = m(s))) c (invariant AND (\s. m(s) < X))) ==> correct p (ADO invariant (MEASURE m) c e) q`,
STRIP_TAC THEN MATCH_MP_TAC VC_ADO_VARIANT THEN ASM_REWRITE_TAC[WF_MEASURE] THEN X_GEN_TAC `X:S` THEN FIRST_ASSUM(MP_TAC o SPEC `(m:S->num) X`) THEN REWRITE_TAC[correct; AND; IMPLIES; MEASURE] THEN MESON_TAC[]);;
(* ------------------------------------------------------------------------- *) (* VCs for sequences of commands, using intelligence where possible. *) (* ------------------------------------------------------------------------- *)
let VC_SEQ_ASSERT_LEFT = 
prove (`p IMPLIES b /\ correct b c q ==> correct p (ASSERT b Seq c) q`,
MESON_TAC[CORRECT_SEQ; CORRECT_ASSERT; CORRECT_PRESTRENGTH; PRED_TAUT `(p IMPLIES b) ==> (p IMPLIES b AND p)`]);;
let VC_SEQ_ASSERT_RIGHT = 
prove (`correct p c b /\ b IMPLIES q ==> correct p (c Seq (ASSERT b)) q`,
MESON_TAC[CORRECT_SEQ; CORRECT_ASSERT; PRED_TAUT `(p IMPLIES b) ==> (p IMPLIES p AND b)`]);;
let VC_SEQ_ASSERT_MIDDLE = 
prove (`correct p c b /\ correct b c' q ==> correct p (c Seq (ASSERT b) Seq c') q`,
MESON_TAC[CORRECT_SEQ; CORRECT_ASSERT; PRED_TAUT `b IMPLIES b AND b`]);;
let VC_SEQ_ASSIGN_LEFT = 
prove (`(p o f = p) /\ (f o f = f) /\ correct (p AND (\s:S. s = f s)) c q ==> correct p ((Assign f) Seq c) q`,
REWRITE_TAC[FUN_EQ_THM; o_THM] THEN STRIP_TAC THEN MATCH_MP_TAC CORRECT_SEQ THEN EXISTS_TAC `p AND (\s:S. s = f s)` THEN ASM_REWRITE_TAC[] THEN MATCH_MP_TAC VC_ASSIGN THEN ASM_REWRITE_TAC[IMPLIES; AND; o_THM]);;
let VC_SEQ_ASSIGN_RIGHT = 
prove (`correct p c (q o f) ==> correct p (c Seq (Assign f)) q`,
MESON_TAC[CORRECT_SEQ; VC_ASSIGN; PRED_TAUT `(p:S->bool) IMPLIES p`]);;
(* ------------------------------------------------------------------------- *) (* Parser for correctness assertions. *) (* ------------------------------------------------------------------------- *) let rec dive_to_var ptm = match ptm with Varp(_,_) as vp -> vp | Typing(t,_) -> dive_to_var t | _ -> fail();; let reserve_program_words,unreserve_program_words = let words = ["var"; "end"; "skip"; "abort"; ":="; "if"; "then"; "else"; "while"; "do"] in (fun () -> reserve_words words), (fun () -> unreserve_words words);; reserve_program_words();; let parse_program,parse_program_assertion = let assign_ptm = Varp("Assign",dpty) and seq_ptm = Varp("Seq",dpty) and ite_ptm = Varp("Ite",dpty) and while_ptm = Varp("While",dpty) and skip_ptm = Varp("SKIP",dpty) and abort_ptm = Varp("ABORT",dpty) and if_ptm = Varp("IF",dpty) and do_ptm = Varp("DO",dpty) and assert_ptm = Varp("ASSERT",dpty) and awhile_ptm = Varp("AWHILE",dpty) and ado_ptm = Varp("ADO",dpty) in let pmk_pair(ptm1,ptm2) = Combp(Combp(Varp(",",dpty),ptm1),ptm2) in let varname ptm = match dive_to_var ptm with Varp(n,_) -> n | _ -> fail() in let rec assign s v e = match s with Combp(Combp(pop,lptm),rptm) -> if varname pop = "," then Combp(Combp(pop,assign lptm v e),assign rptm v e) else fail() | _ -> if varname s = v then e else s in let lmk_assign s v e = Combp(assign_ptm,Absp(s,assign s v e)) and lmk_seq c cs = if cs = [] then c else Combp(Combp(seq_ptm,c),hd cs) and lmk_ite e c1 c2 = Combp(Combp(Combp(ite_ptm,e),c1),c2) and lmk_while e c = Combp(Combp(while_ptm,e),c) and lmk_skip _ = skip_ptm and lmk_abort _ = abort_ptm and lmk_if e c = Combp(Combp(if_ptm,e),c) and lmk_do c e = Combp(Combp(do_ptm,c),e) and lmk_assert e = Combp(assert_ptm,e) and lmk_awhile i v e c = Combp(Combp(Combp(Combp(awhile_ptm,i),v),e),c) and lmk_ado i v c e = Combp(Combp(Combp(Combp(ado_ptm,i),v),c),e) in let lmk_gwhile al e c = if al = [] then lmk_while e c else lmk_awhile (fst(hd al)) (snd(hd al)) e c and lmk_gdo al c e = if al = [] then lmk_do c e else lmk_ado (fst(hd al)) (snd(hd al)) c e in let expression s = parse_preterm >> (fun p -> Absp(s,p)) in let identifier = function ((Ident n)::rest) -> n,rest | _ -> raise Noparse in let variant s = a (Ident "variant") ++ parse_preterm >> snd || a (Ident "measure") ++ expression s >> fun (_,m) -> Combp(Varp("MEASURE",dpty),m) in let annotation s = a (Resword "[") ++ a (Ident "invariant") ++ expression s ++ a (Resword ";") ++ variant s ++ a (Resword "]") >> fun (((((_,_),i),_),v),_) -> (i,v) in let rec command s i = (a (Resword "(") ++ commands s ++ a (Resword ")") >> (fun ((_,c),_) -> c) || a (Resword "skip") >> lmk_skip || a (Resword "abort") >> lmk_abort || a (Resword "if") ++ expression s ++ a (Resword "then") ++ command s ++ possibly (a (Resword "else") ++ command s >> snd) >> (fun ((((_,e),_),c),cs) -> if cs = [] then lmk_if e c else lmk_ite e c (hd cs)) || a (Resword "while") ++ expression s ++ a (Resword "do") ++ possibly (annotation s) ++ command s >> (fun ((((_,e),_),al),c) -> lmk_gwhile al e c) || a (Resword "do") ++ possibly (annotation s) ++ command s ++ a (Resword "while") ++ expression s >> (fun ((((_,al),c),_),e) -> lmk_gdo al c e) || a (Resword "{") ++ expression s ++ a (Resword "}") >> (fun ((_,e),_) -> lmk_assert e) || identifier ++ a (Resword ":=") ++ parse_preterm >> (fun ((v,_),e) -> lmk_assign s v e)) i and commands s i = (command s ++ possibly (a (Resword ";") ++ commands s >> snd) >> (fun (c,cs) -> lmk_seq c cs)) i in let program i = let ((_,s),_),r = (a (Resword "var") ++ parse_preterm ++ a (Resword ";")) i in let c,r' = (commands s ++ a (Resword "end") >> fst) r in (s,c),r' in let assertion = a (Ident "correct") ++ parse_preterm ++ program ++ parse_preterm >> fun (((_,p),(s,c)),q) -> Combp(Combp(Combp(Varp("correct",dpty),Absp(s,p)),c),Absp(s,q)) in (program >> snd),assertion;; (* ------------------------------------------------------------------------- *) (* Introduce the variables in the VCs. *) (* ------------------------------------------------------------------------- *) let STATE_GEN_TAC = let PAIR_CONV = REWR_CONV(GSYM PAIR) in let rec repair vs v acc = try let l,r = dest_pair vs in let th = PAIR_CONV v in let tm = rand(concl th) in let rtm = rator tm in let lth,acc1 = repair l (rand rtm) acc in let rth,acc2 = repair r (rand tm) acc1 in TRANS th (MK_COMB(AP_TERM (rator rtm) lth,rth)),acc2 with Failure _ -> REFL v,((v,vs)::acc) in fun (asl,w) -> let abstm = find_term (fun t -> not (is_abs t) & is_gabs t) w in let vs = fst(dest_gabs abstm) in let v = genvar(type_of(fst(dest_forall w))) in let th,gens = repair vs v [] in (X_GEN_TAC v THEN SUBST1_TAC th THEN MAP_EVERY SPEC_TAC gens THEN REPEAT GEN_TAC) (asl,w);; let STATE_GEN_TAC' = let PAIR_CONV = REWR_CONV(GSYM PAIR) in let rec repair vs v acc = try let l,r = dest_pair vs in let th = PAIR_CONV v in let tm = rand(concl th) in let rtm = rator tm in let lth,acc1 = repair l (rand rtm) acc in let rth,acc2 = repair r (rand tm) acc1 in TRANS th (MK_COMB(AP_TERM (rator rtm) lth,rth)),acc2 with Failure _ -> REFL v,((v,vs)::acc) in fun (asl,w) -> let abstm = find_term (fun t -> not (is_abs t) & is_gabs t) w in let vs0 = fst(dest_gabs abstm) in let vl0 = striplist dest_pair vs0 in let vl = map (variant (variables (list_mk_conj(w::map (concl o snd) asl)))) vl0 in let vs = end_itlist (curry mk_pair) vl in let v = genvar(type_of(fst(dest_forall w))) in let th,gens = repair vs v [] in (X_GEN_TAC v THEN SUBST1_TAC th THEN MAP_EVERY SPEC_TAC gens THEN REPEAT GEN_TAC) (asl,w);; (* ------------------------------------------------------------------------- *) (* Tidy up a verification condition. *) (* ------------------------------------------------------------------------- *) let VC_UNPACK_TAC = REWRITE_TAC[IMPLIES; o_THM; FALSE; TRUE; AND; OR; NOT; IMP] THEN STATE_GEN_TAC THEN CONV_TAC(REDEPTH_CONV GEN_BETA_CONV) THEN REWRITE_TAC[PAIR_EQ; GSYM CONJ_ASSOC];; (* ------------------------------------------------------------------------- *) (* Calculate a (pseudo-) weakest precondition for command. *) (* ------------------------------------------------------------------------- *) let find_pwp = let wptms = (map (snd o strip_forall o concl) [WP_ASSIGN; WP_ITE; WP_SKIP; WP_ABORT; WP_IF; WP_ASSERT]) @ [`wp (AWHILE i v e c) q = i`; `wp (ADO i v c e) q = i`] in let conv tm = tryfind (fun t -> rand (instantiate (term_match [] (lhand t) tm) t)) wptms in fun tm q -> conv(mk_comb(list_mk_icomb "wp" [tm],q));; (* ------------------------------------------------------------------------- *) (* Tools for automatic VC generation from annotated program. *) (* ------------------------------------------------------------------------- *) let VC_SEQ_TAC = let is_seq = is_binary "Seq" and strip_seq = striplist (dest_binary "Seq") and is_assert tm = try fst(dest_const(rator tm)) = "ASSERT" with Failure _ -> false and is_assign tm = try fst(dest_const(rator tm)) = "Assign" with Failure _ -> false and SIDE_TAC = GEN_REWRITE_TAC I [FUN_EQ_THM] THEN STATE_GEN_TAC THEN PURE_REWRITE_TAC[IMPLIES; o_THM; FALSE; TRUE; AND; OR; NOT; IMP] THEN CONV_TAC(REDEPTH_CONV GEN_BETA_CONV) THEN REWRITE_TAC[PAIR_EQ] THEN NO_TAC in let ADJUST_TAC ptm ptm' ((_,w) as gl) = let w' = subst [ptm',ptm] w in let th = EQT_ELIM(REWRITE_CONV[correct; WP_SEQ] (mk_eq(w,w'))) in GEN_REWRITE_TAC I [th] gl in fun (asl,w) -> let cptm,q = dest_comb w in let cpt,ptm = dest_comb cptm in let ctm,p = dest_comb cpt in let ptms = strip_seq ptm in let seq = rator(rator ptm) in try let atm = find is_assert ptms in let i = index atm ptms in if i = 0 then let ptm' = mk_binop seq (hd ptms) (list_mk_binop seq (tl ptms)) in (ADJUST_TAC ptm ptm' THEN MATCH_MP_TAC VC_SEQ_ASSERT_LEFT THEN CONJ_TAC THENL [VC_UNPACK_TAC; ALL_TAC]) (asl,w) else if i = length ptms - 1 then let ptm' = mk_binop seq (list_mk_binop seq (butlast ptms)) (last ptms) in (ADJUST_TAC ptm ptm' THEN MATCH_MP_TAC VC_SEQ_ASSERT_RIGHT THEN CONJ_TAC THENL [ALL_TAC; VC_UNPACK_TAC]) (asl,w) else let l,mr = chop_list (index atm ptms) ptms in let ptm' = mk_binop seq (list_mk_binop seq l) (mk_binop seq (hd mr) (list_mk_binop seq (tl mr))) in (ADJUST_TAC ptm ptm' THEN MATCH_MP_TAC VC_SEQ_ASSERT_MIDDLE THEN CONJ_TAC) (asl,w) with Failure "find" -> try if is_assign (hd ptms) then let ptm' = mk_binop seq (hd ptms) (list_mk_binop seq (tl ptms)) in (ADJUST_TAC ptm ptm' THEN MATCH_MP_TAC VC_SEQ_ASSIGN_LEFT THEN REPEAT CONJ_TAC THENL [SIDE_TAC; SIDE_TAC; ALL_TAC]) (asl,w) else fail() with Failure _ -> let ptm' = mk_binop seq (list_mk_binop seq (butlast ptms)) (last ptms) in let pwp = find_pwp (rand ptm') q in (ADJUST_TAC ptm ptm' THEN MATCH_MP_TAC CORRECT_SEQ THEN EXISTS_TAC pwp THEN CONJ_TAC) (asl,w);; (* ------------------------------------------------------------------------- *) (* Tactic to apply a 1-step VC generation. *) (* ------------------------------------------------------------------------- *) let VC_STEP_TAC = let tacnet = itlist (enter []) [`correct p SKIP q`, MATCH_MP_TAC VC_SKIP THEN VC_UNPACK_TAC; `correct p (ASSERT b) q`, MATCH_MP_TAC VC_ASSERT THEN VC_UNPACK_TAC; `correct p (Assign f) q`, MATCH_MP_TAC VC_ASSIGN THEN VC_UNPACK_TAC; `correct p (Ite e c1 c2) q`, MATCH_MP_TAC VC_ITE THEN CONJ_TAC; `correct p (IF e c) q`, MATCH_MP_TAC VC_IF THEN CONJ_TAC THENL [ALL_TAC; VC_UNPACK_TAC]; `correct p (AWHILE i (MEASURE m) e c) q`, MATCH_MP_TAC VC_AWHILE_MEASURE THEN REPEAT CONJ_TAC THENL [VC_UNPACK_TAC; VC_UNPACK_TAC; GEN_TAC]; `correct p (AWHILE i v e c) q`, MATCH_MP_TAC VC_AWHILE_VARIANT THEN REPEAT CONJ_TAC THENL [ALL_TAC; VC_UNPACK_TAC; VC_UNPACK_TAC; STATE_GEN_TAC']; `correct p (ADO i (MEASURE m) c e) q`, MATCH_MP_TAC VC_ADO_MEASURE THEN REPEAT CONJ_TAC THENL [VC_UNPACK_TAC; VC_UNPACK_TAC; STATE_GEN_TAC']; `correct p (ADO i v c e) q`, MATCH_MP_TAC VC_ADO_VARIANT THEN REPEAT CONJ_TAC THENL [ALL_TAC; VC_UNPACK_TAC; VC_UNPACK_TAC; STATE_GEN_TAC']; `correct p (c1 Seq c2) q`, VC_SEQ_TAC] empty_net in fun (asl,w) -> FIRST(lookup w tacnet) (asl,w);; (* ------------------------------------------------------------------------- *) (* Final packaging to strip away the program completely. *) (* ------------------------------------------------------------------------- *) let VC_TAC = REPEAT VC_STEP_TAC;; (* ------------------------------------------------------------------------- *) (* Some examples. *) (* ------------------------------------------------------------------------- *) install_parser ("correct",parse_program_assertion);;
let EXAMPLE_FACTORIAL = 
prove (`correct T var x,y,n; x := 0; y := 1; while x < n do [invariant x <= n /\ (y = FACT x); measure n - x] (x := x + 1; y := y * x) end y = FACT n`,
VC_TAC THENL [STRIP_TAC THEN ASM_REWRITE_TAC[FACT; LE_0]; REWRITE_TAC[CONJ_ASSOC; NOT_LT; LE_ANTISYM] THEN MESON_TAC[]; REWRITE_TAC[GSYM ADD1; FACT] THEN STRIP_TAC THEN ASM_REWRITE_TAC[MULT_AC] THEN UNDISCH_TAC `x < n` THEN ARITH_TAC]);;
delete_parser "correct";;