Module ImpIO

Extension of Coq language with some IO and exception-handling operators. TODO: integration with http://coq.io/ ?

Require Export ImpPrelude.

Import Notations.
Local Open Scope impure.

Printing functions

Axiom print: pstring -> ?? unit.
Extract Constant print => "ImpIOOracles.print".

Axiom println: pstring -> ?? unit.
Extract Constant println => "ImpIOOracles.println".

Axiom read_line: unit -> ?? pstring.
Extract Constant read_line => "ImpIOOracles.read_line".

Require Import ZArith.
Axiom string_of_Z: Z -> ?? pstring.
Extract Constant string_of_Z => "ImpIOOracles.string_of_Z".

timer

Axiom timer: forall {A B}, (A -> ?? B)*A -> ?? B.
Extract Constant timer => "ImpIOOracles.timer".

Exception Handling

Axiom exit_observer: Type.
Extract Constant exit_observer => "((unit -> unit) ref)".

Axiom new_exit_observer: (unit -> ??unit) -> ??exit_observer.
Extract Constant new_exit_observer => "ImpIOOracles.new_exit_observer".

Axiom set_exit_observer: exit_observer * (unit -> ??unit) -> ??unit.
Extract Constant set_exit_observer => "ImpIOOracles.set_exit_observer".

Axiom exn: Type.
Extract Inlined Constant exn => "exn".

Axiom raise: forall {A}, exn -> ?? A.
Extract Constant raise => "raise".

Axiom exn2string: exn -> ?? pstring.
Extract Constant exn2string => "ImpIOOracles.exn2string".

Axiom fail: forall {A}, pstring -> ?? A.
Extract Constant fail => "ImpIOOracles.fail".

Axiom try_with_fail: forall {A}, (unit -> ?? A) * (pstring -> exn -> ??A) -> ??A.
Extract Constant try_with_fail => "ImpIOOracles.try_with_fail".

Axiom try_with_any: forall {A}, (unit -> ?? A) * (exn -> ??A) -> ??A.
Extract Constant try_with_any => "ImpIOOracles.try_with_any".

Notation "'RAISE' e" := (DO r <~ raise (A:=False) e ;; RET (match r with end)) (at level 0): impure_scope.
Notation "'FAILWITH' msg" := (DO r <~ fail (A:=False) msg ;; RET (match r with end)) (at level 0): impure_scope.

Definition _FAILWITH {A:Type} msg: ?? A := FAILWITH msg.

Example _FAILWITH_correct A msg (P: A -> Prop):
  WHEN _FAILWITH msg ~> r THEN P r.
Proof.
  wlp_simplify.
Qed.

Notation "'TRY' k1 'WITH_FAIL' s ',' e '=>' k2" := (try_with_fail (fun _ => k1, fun s e => k2))
    (at level 55, k1 at level 53, right associativity): impure_scope.

Notation "'TRY' k1 'WITH_ANY' e '=>' k2" := (try_with_any (fun _ => k1, fun e => k2))
    (at level 55, k1 at level 53, right associativity): impure_scope.


Program Definition assert_b (b: bool) (msg: pstring): ?? b=true :=
  match b with
  | true => RET _
  | false => FAILWITH msg
  end.

Lemma assert_wlp_true msg b: WHEN assert_b b msg ~> _ THEN b=true.
Proof.
  wlp_simplify.
Qed.

Lemma assert_false_wlp msg (P: Prop): WHEN assert_b false msg ~> _ THEN P.
Proof.
  simpl; wlp_simplify.
Qed.

Program Definition try_catch_fail_ensure {A} (k1: unit -> ?? A) (k2: pstring -> exn -> ??A) (P: A -> Prop | wlp (k1 tt) P /\ (forall s e, wlp (k2 s e) P)): ?? { r | P r }
  := TRY
        DO r <~ mk_annot (k1 tt);;
        RET (exist P r _)
     WITH_FAIL s, e =>
        DO r <~ mk_annot (k2 s e);;
        RET (exist P r _).
Obligation 2.
  unfold wlp in * |- *; eauto.
Qed.

Notation "'TRY' k1 'CATCH_FAIL' s ',' e '=>' k2 'ENSURE' P" := (try_catch_fail_ensure (fun _ => k1) (fun s e => k2) (exist _ P _))
    (at level 55, k1 at level 53, right associativity): impure_scope.

Definition is_try_post {A} (P: A -> Prop) k1 k2 : Prop :=
  wlp (k1 ()) P /\ forall (e:exn), wlp (k2 e) P.

Program Definition try_catch_ensure {A} k1 k2 (P:A->Prop|is_try_post P k1 k2): ?? { r | P r }
  := TRY
        DO r <~ mk_annot (k1 ());;
        RET (exist P r _)
     WITH_ANY e =>
        DO r <~ mk_annot (k2 e);;
        RET (exist P r _).
Obligation 1.
  unfold is_try_post, wlp in * |- *; intuition eauto.
Qed. Obligation 2.
  unfold is_try_post, wlp in * |- *; intuition eauto.
Qed.
Notation "'TRY' k1 'CATCH' e '=>' k2 'ENSURE' P" := (try_catch_ensure (fun _ => k1) (fun e => k2) (exist _ P _))
    (at level 55, k1 at level 53, right associativity): impure_scope.


Program Example tryex {A} (x y:A) :=
  TRY (RET x)
  CATCH _ => (RET y)
  ENSURE (fun r => r = x \/ r = y).
Obligation 1.
  split; wlp_simplify.
Qed.
Program Example tryex_test {A} (x y:A):
  WHEN tryex x y ~> r THEN `r <> x -> `r = y.
Proof.
  wlp_simplify. destruct exta as [r [X|X]]; intuition.
Qed.


Program Example try_branch1 {A} (x:A): ?? { r | r = x} :=
  TRY (RET x)
  CATCH e => (FAILWITH "!")
  ENSURE _.
Obligation 1.
  split; wlp_simplify.
Qed.
Program Example try_branch2 {A} (x:A): ?? { r | r = x} :=
  TRY (FAILWITH "!")
  CATCH e => (RET x)
  ENSURE _.
Obligation 1.
  split; wlp_simplify.
Qed.