Theory Ffi

theory Ffi
imports Lem_pervasives_extra Lib
chapter ‹Generated by Lem from ‹semantics/ffi/ffi.lem›.›

theory "Ffi" 

imports
  Main
  "HOL-Library.Datatype_Records"
  "LEM.Lem_pervasives"
  "LEM.Lem_pervasives_extra"
  "Lib"

begin 

― ‹‹open import Pervasives››
― ‹‹open import Pervasives_extra››
― ‹‹open import Lib››

― ‹‹ An oracle says how to perform an ffi call based on its internal state,
* represented by the type variable 'ffi. ››

datatype 'ffi oracle_result = Oracle_return " 'ffi " " 8 word list " | Oracle_diverge | Oracle_fail
type_synonym 'ffi oracle_function =" 'ffi ⇒ 8 word list ⇒ 8 word list ⇒ 'ffi oracle_result "
type_synonym 'ffi oracle0 =" string ⇒ 'ffi oracle_function "

― ‹‹ An I/O event, IO_event s bytes bytes2, represents the call of FFI function s with
* immutable input bytes and mutable input map fst bytes2,
* returning map snd bytes2 in the mutable array. ››

datatype io_event = IO_event " string " " 8 word list " " ( (8 word * 8 word)list)"

datatype ffi_outcome = FFI_diverged | FFI_failed
datatype final_event = Final_event " string " " 8 word list " " 8 word list " " ffi_outcome "

datatype_record 'ffi ffi_state =

 oracle0      ::" 'ffi oracle0 "
 
 ffi_state   ::" 'ffi "
 
 final_event ::"  final_event option "
 
 io_events   ::" io_event list "
 


― ‹‹val initial_ffi_state : forall 'ffi. oracle 'ffi -> 'ffi -> ffi_state 'ffi››
definition initial_ffi_state  :: "(string ⇒ 'ffi oracle_function)⇒ 'ffi ⇒ 'ffi ffi_state "  where 
     " initial_ffi_state oc ffi1 = (
(| oracle0      = oc
 , ffi_state   = ffi1
 , final_event = None
 , io_events   = ([])
 |) )"


― ‹‹val call_FFI : forall 'ffi. ffi_state 'ffi -> string -> list word8 -> list word8 -> ffi_state 'ffi * list word8››
definition call_FFI  :: " 'ffi ffi_state ⇒ string ⇒(8 word)list ⇒(8 word)list ⇒ 'ffi ffi_state*(8 word)list "  where 
     " call_FFI st s conf bytes = (
  if ((final_event   st) = None) ∧ ¬ (s = ('''')) then
    (case (oracle0   st) s(ffi_state   st) conf bytes of
      Oracle_return ffi' bytes' =>
        if List.length bytes' = List.length bytes then
          (( st (| ffi_state := ffi'
                    , io_events :=
                        ((io_events   st) @
                          [IO_event s conf (zipSameLength bytes bytes')])
            |)), bytes')
        else (( st (| final_event := (Some (Final_event s conf bytes FFI_failed)) |)), bytes)
    | Oracle_diverge =>
          (( st (| final_event := (Some (Final_event s conf bytes FFI_diverged)) |)), bytes)
    | Oracle_fail =>
        (( st (| final_event := (Some (Final_event s conf bytes FFI_failed)) |)), bytes)
    )
  else (st, bytes))"


datatype outcome = Success | Resource_limit_hit | FFI_outcome " final_event "

― ‹‹ A program can Diverge, Terminate, or Fail. We prove that Fail is
   avoided. For Diverge and Terminate, we keep track of what I/O
   events are valid I/O events for this behaviour. ››
datatype  behaviour =
    ― ‹‹ There cannot be any non-returning FFI calls in a diverging
       exeuction. The list of I/O events can be finite or infinite,
       hence the llist (lazy list) type. ››
    Diverge "  io_event llist "
    ― ‹‹ Terminating executions can only perform a finite number of
       FFI calls. The execution can be terminated by a non-returning
       FFI call. ››
  | Terminate " outcome " " io_event list "
    ― ‹‹ Failure is a behaviour which we prove cannot occur for any
       well-typed program. ››
  | Fail

― ‹‹ trace-based semantics can be recovered as an instance of oracle-based
 * semantics as follows. ››

― ‹‹val trace_oracle : oracle (llist io_event)››
definition trace_oracle  :: " string ⇒(io_event)llist ⇒(8 word)list ⇒(8 word)list ⇒((io_event)llist)oracle_result "  where 
     " trace_oracle s io_trace conf input1 = (
  (case  lhd' io_trace of
    Some (IO_event s' conf' bytes2) =>
      if (s = s') ∧ ((List.map fst bytes2 = input1) ∧ (conf = conf')) then
        Oracle_return (Option.the (ltl' io_trace)) (List.map snd bytes2)
      else Oracle_fail
  | _ => Oracle_fail
  ))"

end