Theory SmallStep

(*
 * Copyright 2016, Data61, CSIRO
 *
 * This software may be distributed and modified according to the terms of
 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
 * See "LICENSE_BSD2.txt" for details.
 *
 * @TAG(DATA61_BSD)
 *)
section ‹COMPLX small-step semantics›
theory SmallStep
imports Language
begin

text ‹The procedure environment›
type_synonym ('s,'p,'f) body = "'p  ('s,'p,'f) com option"

text ‹State types›
datatype ('s,'f) xstate = Normal 's | Fault 'f | Stuck

lemma rtrancl_mono_proof[mono]:
   "(a b. x a b  y a b)  rtranclp x a b  rtranclp y a b"
   apply (rule impI, rotate_tac, induct rule: rtranclp.induct)
    apply simp_all
   apply (metis rtranclp.intros)
   done


primrec redex:: "('s,'p,'f)com  ('s,'p,'f)com"
where
"redex Skip = Skip" |
"redex (Basic f) = (Basic f)" |
"redex (Spec r) = (Spec r)" |
"redex (Seq c1 c2) = redex c1" |
"redex (Cond b c1 c2) = (Cond b c1 c2)" |
"redex (While b c) = (While b c)" |
"redex (Call p) = (Call p)" |
"redex (DynCom d) = (DynCom d)" |
"redex (Guard f b c) = (Guard f b c)" |
"redex (Throw) = Throw" |
"redex (Catch c1 c2) = redex c1" |
"redex (Await b c) = Await b c" |
"redex (Parallel cs) = Parallel cs"

subsection ‹Small-Step Computation: Γ ⊢(c, s) → (c', s')›

text ‹The final configuration is either of the form (Skip,_)› for normal
termination, or @{term "(Throw,Normal s)"} in case the program was started in 
a @{term "Normal"} state and terminated abruptly. Explicit abrupt states are removed
from the language definition and thus do not need to be propogated.›

type_synonym ('s,'p,'f) config = "('s,'p,'f)com  × ('s,'f) xstate"

definition final:: "('s,'p,'f) config  bool" where
"final cfg = (fst cfg=Skip  (fst cfg=Throw  (s. snd cfg=Normal s)))"

primrec atom_com :: "⌦‹('s,'p,'f) body ⇒› ('s, 'p, 'f) com  bool" where
  "atom_com Skip = True" | 
  "atom_com (Basic f) = True" | 
  "atom_com (Spec r) = True" | 
  "atom_com (Seq  c1 c2) = (atom_com c1  atom_com c2)" | 
  "atom_com (Cond b c1 c2) = (atom_com c1  atom_com c2)" | 
  "atom_com (While b c) = atom_com c" | 
 (* Change to  atom_com (Call p) = atom_com (Θ p)
  Butow do we pass function body from step? *)
  "atom_com (Call p) = False" |
  "atom_com (DynCom f) = (s::'s. atom_com (f s))" | 
  "atom_com (Guard f g c) = atom_com c" | 
  "atom_com Throw = True" | 
  "atom_com (Catch c1 c2) = (atom_com c1  atom_com c2)" | 
  "atom_com (Parallel cs) = False" | 
  "atom_com (Await b c) = False"


inductive
      "step"::"[('s,'p,'f) body, ('s,'p,'f) config,('s,'p,'f) config]  bool"
                                ("_ (_ / _)" [81,81,81] 100)
  and "step_rtrancl" :: "[('s,'p,'f) body, ('s,'p,'f) config,('s,'p,'f) config]  bool"
                                ("_  (_ */ _)" [81,81,81] 100)
  for Γ::"('s,'p,'f) body"
where
  "Γ  a * b  (step Γ)** a b"
| Basic: "Γ (Basic f,Normal s)  (Skip,Normal (f s))"

| Spec: "(s,t)  r  Γ (Spec r,Normal s)  (Skip,Normal t)"
| SpecStuck: "t. (s,t)  r  Γ (Spec r,Normal s)  (Skip,Stuck)"

| Guard: "sg  Γ (Guard f g c,Normal s)  (c,Normal s)"
  
| GuardFault: "sg  Γ (Guard f g c,Normal s)  (Skip,Fault f)"


| Seq: "Γ (c1,s)  (c1',s')
         
        Γ (Seq c1 c2,s)  (Seq c1' c2, s')"
| SeqSkip: "Γ (Seq Skip c2,s)  (c2, s)"
| SeqThrow: "Γ (Seq Throw c2,Normal s)  (Throw, Normal s)"

| CondTrue:  "sb  Γ (Cond b c1 c2,Normal s)  (c1,Normal s)"
| CondFalse: "sb  Γ (Cond b c1 c2,Normal s)  (c2,Normal s)"

| WhileTrue: "sb 
               
              Γ (While b c,Normal s)  (Seq c (While b c),Normal s)"

| WhileFalse: "sb 
                
               Γ (While b c,Normal s)  (Skip,Normal s)"

| Call: "Γ p=Some b 
         Γ (Call p,Normal s)  (b,Normal s)"

| CallUndefined: "Γ p=None 
         Γ (Call p,Normal s)  (Skip,Stuck)"

| DynCom: "Γ (DynCom c,Normal s)  (c s,Normal s)"

| Catch: "Γ (c1,s)  (c1',s')
          
          Γ (Catch c1 c2,s)  (Catch c1' c2,s')"

| CatchSkip: "Γ (Catch Skip c2,s)  (Skip,s)"
| CatchThrow: "Γ (Catch Throw c2,Normal s)  (c2,Normal s)"

| FaultProp:  "cSkip; redex c = c  Γ (c,Fault f)  (Skip,Fault f)"
| StuckProp:  "cSkip; redex c = c  Γ (c,Stuck)  (Skip,Stuck)"


| Parallel: "  i < length cs; Γ  (cs!i, s)  (c', s')  
             Γ  (Parallel cs, s)  (Parallel (cs[i := c']), s')"

| ParSkip: " c  set cs. c = Skip   Γ  (Parallel cs, s)  (Skip, s)"
(* If exception is thrown, the parallel execution may either abort
   immediately (rule ParThrow) or keep executing (rule Parallel) *)
| ParThrow: " Throw  set cs   Γ  (Parallel cs, s)  (Throw, s)"


| Await: " s  b; Γ  (c, Normal s) * (c', Normal s');
           atom_com c; c' = Skip  c' = Throw  
           Γ  (Await b c, Normal s)  (c', Normal s')"
| AwaitStuck: 
         " s  b; Γ  (c, Normal s) * (c', Stuck) ;
           atom_com c
           Γ  (Await b c, Normal s)  (Skip, Stuck)" 
| AwaitFault: 
         " s  b; Γ  (c, Normal s) * (c', Fault f) ;
           atom_com c
           Γ  (Await b c, Normal s)  (Skip, Fault f)" 
| AwaitNonAtom: 
         " ¬ atom_com c
           Γ  (Await b c, Normal s)  (Skip, Stuck)"


lemmas step_induct = step.induct [of _ "(c,s)" "(c',s')", split_format (complete), case_names
Basic Spec SpecStuck Guard GuardFault Seq SeqSkip SeqThrow CondTrue CondFalse
WhileTrue WhileFalse Call CallUndefined DynCom Catch CatchThrow CatchSkip
FaultProp StuckProp Parallel ParSkip Await, induct set]

text ‹The execution of a command is blocked if it cannot make progress, but is not in a final
state. It is the intention that ∃cfg. Γ ⊢ (c, s) → cfg) ∨ final (c, s) ∨ blocked Γ c s›, but
we do not prove this.›

function(sequential) blocked :: "('s,'p,'f) body  ('s,'p,'f) com  ('s,'f)xstate  bool" where
  "blocked Γ (Seq (Await b c1) c2) (Normal s) = (s  b)"
| "blocked Γ (Catch (Await b c1) c2) (Normal s) = (s  b)"
| "blocked Γ (Await b c) (Normal s) = (s  b  (c' s'. Γ  (c, Normal s) * (c', Normal s')  ¬ final (c', Normal s')))"
| "blocked Γ (Parallel cs) (Normal s) = (t  set cs. blocked Γ t (Normal s)  final (t, Normal s))"
| "blocked Γ _ _ = False"
by (pat_completeness, auto)

inductive_cases step_elim_cases [cases set]:
 "Γ (Skip,s)  u"
 "Γ (Guard f g c,s)  u"
 "Γ (Basic f,s)  u"
 "Γ (Spec r,s)  u"
 "Γ (Seq c1 c2,s)  u"
 "Γ (Cond b c1 c2,s)  u"
 "Γ (While b c,s)  u"
 "Γ (Call p,s)  u"
 "Γ (DynCom c,s)  u"
 "Γ (Throw,s)  u"
 "Γ (Catch c1 c2,s)  u"
 "Γ (Parallel cs,s)  u"
 "Γ (Await b e,s)  u"


inductive_cases step_Normal_elim_cases [cases set]:
 "Γ (Skip,Normal s)  u"
 "Γ (Guard f g c,Normal s)  u"
 "Γ (Basic f,Normal s)  u"
 "Γ (Spec r,Normal s)  u"
 "Γ (Seq c1 c2,Normal s)  u"
 "Γ (Cond b c1 c2,Normal s)  u"
 "Γ (While b c,Normal s)  u"
 "Γ (Call p,Normal s)  u"
 "Γ (DynCom c,Normal s)  u"
 "Γ (Throw,Normal s)  u"
 "Γ (Catch c1 c2,Normal s)  u"
 "Γ (Parallel cs,Normal s)  u"
 "Γ (Await b e,Normal s)  u"


abbreviation 
 "step_trancl" :: "[('s,'p,'f) body, ('s,'p,'f) config,('s,'p,'f) config]  bool"
                                ("_ (_ +/ _)" [81,81,81] 100)
 where
  "Γ cf0 + cf1  (CONST step Γ)++ cf0 cf1"

abbreviation 
 "step_n_trancl" :: "[('s,'p,'f) body, ('s,'p,'f) config,nat,('s,'p,'f) config]  bool"
                                ("_ (_ n_/ _)" [81,81,81,81] 100)
 where
  "Γ cf0 nn cf1  (CONST step Γ ^^ n) cf0 cf1"

lemma no_step_final: 
  assumes step: "Γ (c,s)  (c',s')"
  shows "final (c,s)  P"
using step
by induct (auto simp add: final_def)

lemma no_step_final':
  assumes step: "Γ cfg  cfg'"
  shows "final cfg  P"
using step
  by (cases cfg, cases cfg') (auto intro: no_step_final)

lemma no_steps_final:
"Γ  v * w  final v  w = v"
  apply (cases v)
  apply simp
  apply  (erule converse_rtranclpE)
   apply simp
   apply (erule (1) no_step_final')
 done

lemma step_Fault: 
  assumes step: "Γ  (c, s)  (c', s')"
  shows "f. s=Fault f  s'=Fault f"
using step

by (induct) auto

lemma step_Stuck: 
  assumes step: "Γ  (c, s)  (c', s')"
  shows "f. s=Stuck  s'=Stuck"
using step
by (induct) auto

lemma SeqSteps: 
  assumes steps: "Γ cfg1* cfg2"
  shows " c1 s c1' s'. cfg1 = (c1,s);cfg2=(c1',s')
           Γ (Seq c1 c2,s) * (Seq c1' c2, s')"
using steps
proof (induct rule: converse_rtranclp_induct [case_names Refl Trans])
  case Refl
  thus ?case
    by simp
next
  case (Trans cfg1 cfg'')
  have step: "Γ  cfg1  cfg''" by fact
  have steps: "Γ  cfg'' * cfg2" by fact
  have cfg1: "cfg1 = (c1, s)" and cfg2: "cfg2 = (c1', s')"  by fact+
  obtain c1'' s'' where cfg'': "cfg''=(c1'',s'')"
    by (cases cfg'') auto
  from step cfg1 cfg'' 
  have "Γ  (c1,s)  (c1'',s'')"
    by simp
  hence "Γ  (Seq c1 c2,s)  (Seq c1'' c2,s'')"
    by (rule step.Seq)
  also from Trans.hyps (3) [OF cfg'' cfg2]
  have "Γ  (Seq c1'' c2, s'') * (Seq c1' c2, s')" .
  finally show ?case .
qed


lemma CatchSteps: 
  assumes steps: "Γ cfg1* cfg2"
  shows " c1 s c1' s'. cfg1 = (c1,s); cfg2=(c1',s')
           Γ (Catch c1 c2,s) * (Catch c1' c2, s')"
using steps
proof (induct rule: converse_rtranclp_induct [case_names Refl Trans])
  case Refl
  thus ?case
    by simp
next
  case (Trans cfg1 cfg'')
  have step: "Γ  cfg1  cfg''" by fact
  have steps: "Γ  cfg'' * cfg2" by fact
  have cfg1: "cfg1 = (c1, s)" and cfg2: "cfg2 = (c1', s')"  by fact+
  obtain c1'' s'' where cfg'': "cfg''=(c1'',s'')"
    by (cases cfg'') auto
  from step cfg1 cfg'' 
  have s: "Γ  (c1,s)  (c1'',s'')"
    by simp
  hence "Γ  (Catch c1 c2,s)  (Catch c1'' c2,s'')"
    by (rule step.Catch)
  also from Trans.hyps (3) [OF cfg'' cfg2]
  have "Γ  (Catch c1'' c2, s'') * (Catch c1' c2, s')" .
  finally show ?case .      
qed


lemma steps_Fault: "Γ  (c, Fault f) * (Skip, Fault f)"
proof (induct c)
  case (Seq c1 c2)
  have steps_c1: "Γ  (c1, Fault f) * (Skip, Fault f)" by fact
  have steps_c2: "Γ  (c2, Fault f) * (Skip, Fault f)" by fact
  from SeqSteps [OF steps_c1 refl refl]
  have "Γ  (Seq c1 c2, Fault f) * (Seq Skip c2, Fault f)".
  also
  have "Γ  (Seq Skip c2, Fault f)  (c2, Fault f)" by (rule SeqSkip)
  also note steps_c2
  finally show ?case by simp
next
  case (Catch c1 c2)
  have steps_c1: "Γ  (c1, Fault f) * (Skip, Fault f)" by fact
  from CatchSteps [OF steps_c1 refl refl]
  have "Γ  (Catch c1 c2, Fault f) * (Catch Skip c2, Fault f)".
  also
  have "Γ  (Catch Skip c2, Fault f)  (Skip, Fault f)" by (rule CatchSkip) 
  finally show ?case by simp
qed (fastforce intro: step.intros)+

lemma steps_Stuck: "Γ  (c, Stuck) * (Skip, Stuck)"
proof (induct c)
  case (Seq c1 c2)
  have steps_c1: "Γ  (c1, Stuck) * (Skip, Stuck)" by fact
  have steps_c2: "Γ  (c2, Stuck) * (Skip, Stuck)" by fact
  from SeqSteps [OF steps_c1 refl refl]
  have "Γ  (Seq c1 c2, Stuck) * (Seq Skip c2, Stuck)".
  also
  have "Γ  (Seq Skip c2, Stuck)  (c2, Stuck)" by (rule SeqSkip)
  also note steps_c2
  finally show ?case by simp
next
  case (Catch c1 c2)
  have steps_c1: "Γ  (c1, Stuck) * (Skip, Stuck)" by fact
  from CatchSteps [OF steps_c1 refl refl]
  have "Γ  (Catch c1 c2, Stuck) * (Catch Skip c2, Stuck)" .
  also
  have "Γ  (Catch Skip c2, Stuck)  (Skip, Stuck)" by (rule CatchSkip) 
  finally show ?case by simp
qed (fastforce intro: step.intros)+

lemma step_Fault_prop: 
  assumes step: "Γ  (c, s)  (c', s')"
  shows "f. s=Fault f  s'=Fault f"
using step
by (induct) auto

lemma step_Stuck_prop: 
  assumes step: "Γ  (c, s)  (c', s')"
  shows "s=Stuck  s'=Stuck"
using step
by (induct) auto

lemma steps_Fault_prop: 
  assumes step: "Γ  (c, s) * (c', s')"
  shows "s=Fault f  s'=Fault f"
using step
proof (induct rule: converse_rtranclp_induct2 [case_names Refl Trans])
  case Refl thus ?case by simp
next
  case (Trans c s c'' s'')
  thus ?case
    by (auto intro: step_Fault_prop)
qed

lemma steps_Stuck_prop: 
  assumes step: "Γ  (c, s) * (c', s')"
  shows "s=Stuck  s'=Stuck"
using step
proof (induct rule: converse_rtranclp_induct2 [case_names Refl Trans])
  case Refl thus ?case by simp
next
  case (Trans c s c'' s'')
  thus ?case
    by (auto intro: step_Stuck_prop)
qed

end