fmt.. sorry
This commit is contained in:
parent
30eb4b0eaf
commit
ceb2caa21e
1 changed files with 212 additions and 194 deletions
|
@ -3,8 +3,7 @@ open Error
|
||||||
open HopixAST
|
open HopixAST
|
||||||
|
|
||||||
(** [error pos msg] reports execution error messages. *)
|
(** [error pos msg] reports execution error messages. *)
|
||||||
let error positions msg =
|
let error positions msg = errorN "execution" positions msg
|
||||||
errorN "execution" positions msg
|
|
||||||
|
|
||||||
(** Every expression of Hopix evaluates into a [value].
|
(** Every expression of Hopix evaluates into a [value].
|
||||||
|
|
||||||
|
@ -13,9 +12,7 @@ let error positions msg =
|
||||||
Why? The value type and the environment type are mutually recursive
|
Why? The value type and the environment type are mutually recursive
|
||||||
and since we do not want to define them simultaneously, this
|
and since we do not want to define them simultaneously, this
|
||||||
parameterization is a way to describe how the value type will use
|
parameterization is a way to describe how the value type will use
|
||||||
the environment type without an actual definition of this type.
|
the environment type without an actual definition of this type. *)
|
||||||
|
|
||||||
*)
|
|
||||||
type 'e gvalue =
|
type 'e gvalue =
|
||||||
| VInt of Mint.t
|
| VInt of Mint.t
|
||||||
| VChar of char
|
| VChar of char
|
||||||
|
@ -30,93 +27,118 @@ type 'e gvalue =
|
||||||
|
|
||||||
(** Two values for booleans. *)
|
(** Two values for booleans. *)
|
||||||
let ptrue = VTagged (KId "True", [])
|
let ptrue = VTagged (KId "True", [])
|
||||||
|
|
||||||
let pfalse = VTagged (KId "False", [])
|
let pfalse = VTagged (KId "False", [])
|
||||||
|
|
||||||
(**
|
(** We often need to check that a value has a specific shape.
|
||||||
We often need to check that a value has a specific shape.
|
|
||||||
To that end, we introduce the following coercions. A
|
To that end, we introduce the following coercions. A
|
||||||
coercion of type [('a, 'e)] coercion tries to convert an
|
coercion of type [('a, 'e)] coercion tries to convert an
|
||||||
Hopix value into a OCaml value of type ['a]. If this conversion
|
Hopix value into a OCaml value of type ['a]. If this conversion
|
||||||
fails, it returns [None].
|
fails, it returns [None]. *)
|
||||||
*)
|
|
||||||
|
|
||||||
type ('a, 'e) coercion = 'e gvalue -> 'a option
|
type ('a, 'e) coercion = 'e gvalue -> 'a option
|
||||||
|
|
||||||
let fail = None
|
let fail = None
|
||||||
let ret x = Some x
|
let ret x = Some x
|
||||||
let value_as_int = function VInt x -> ret x | _ -> fail
|
|
||||||
let value_as_char = function VChar c -> ret c | _ -> fail
|
let value_as_int = function
|
||||||
let value_as_string = function VString s -> ret s | _ -> fail
|
| VInt x -> ret x
|
||||||
let value_as_tagged = function VTagged (k, vs) -> ret (k, vs) | _ -> fail
|
| _ -> fail
|
||||||
let value_as_record = function VRecord fs -> ret fs | _ -> fail
|
;;
|
||||||
let value_as_location = function VLocation l -> ret l | _ -> fail
|
|
||||||
let value_as_closure = function VClosure (e, p, b) -> ret (e, p, b) | _ -> fail
|
let value_as_char = function
|
||||||
let value_as_primitive = function VPrimitive (p, f) -> ret (p, f) | _ -> fail
|
| VChar c -> ret c
|
||||||
|
| _ -> fail
|
||||||
|
;;
|
||||||
|
|
||||||
|
let value_as_string = function
|
||||||
|
| VString s -> ret s
|
||||||
|
| _ -> fail
|
||||||
|
;;
|
||||||
|
|
||||||
|
let value_as_tagged = function
|
||||||
|
| VTagged (k, vs) -> ret (k, vs)
|
||||||
|
| _ -> fail
|
||||||
|
;;
|
||||||
|
|
||||||
|
let value_as_record = function
|
||||||
|
| VRecord fs -> ret fs
|
||||||
|
| _ -> fail
|
||||||
|
;;
|
||||||
|
|
||||||
|
let value_as_location = function
|
||||||
|
| VLocation l -> ret l
|
||||||
|
| _ -> fail
|
||||||
|
;;
|
||||||
|
|
||||||
|
let value_as_closure = function
|
||||||
|
| VClosure (e, p, b) -> ret (e, p, b)
|
||||||
|
| _ -> fail
|
||||||
|
;;
|
||||||
|
|
||||||
|
let value_as_primitive = function
|
||||||
|
| VPrimitive (p, f) -> ret (p, f)
|
||||||
|
| _ -> fail
|
||||||
|
;;
|
||||||
|
|
||||||
let value_as_bool = function
|
let value_as_bool = function
|
||||||
| VTagged (KId "True", []) -> true
|
| VTagged (KId "True", []) -> true
|
||||||
| VTagged (KId "False", []) -> false
|
| VTagged (KId "False", []) -> false
|
||||||
| _ -> assert false
|
| _ -> assert false
|
||||||
|
;;
|
||||||
|
|
||||||
(**
|
(** It is also very common to have to inject an OCaml value into
|
||||||
It is also very common to have to inject an OCaml value into
|
the types of Hopix values. That is the purpose of a wrapper. *)
|
||||||
the types of Hopix values. That is the purpose of a wrapper.
|
|
||||||
*)
|
|
||||||
type ('a, 'e) wrapper = 'a -> 'e gvalue
|
type ('a, 'e) wrapper = 'a -> 'e gvalue
|
||||||
|
|
||||||
let int_as_value x = VInt x
|
let int_as_value x = VInt x
|
||||||
let bool_as_value b = if b then ptrue else pfalse
|
let bool_as_value b = if b then ptrue else pfalse
|
||||||
|
|
||||||
(**
|
(** The flap toplevel needs to print the result of evaluations. This is
|
||||||
|
|
||||||
The flap toplevel needs to print the result of evaluations. This is
|
|
||||||
especially useful for debugging and testing purpose. Do not modify
|
especially useful for debugging and testing purpose. Do not modify
|
||||||
the code of this function since it is used by the testsuite.
|
the code of this function since it is used by the testsuite. *)
|
||||||
|
|
||||||
*)
|
|
||||||
let print_value m v =
|
let print_value m v =
|
||||||
(** To avoid to print large (or infinite) values, we stop at depth 5. *)
|
(* To avoid to print large (or infinite) values, we stop at depth 5. *)
|
||||||
let max_depth = 5 in
|
let max_depth = 5 in
|
||||||
|
|
||||||
let rec print_value d v =
|
let rec print_value d v =
|
||||||
if d >= max_depth then "..." else
|
if d >= max_depth
|
||||||
|
then "..."
|
||||||
|
else (
|
||||||
match v with
|
match v with
|
||||||
| VInt x ->
|
| VInt x -> Mint.to_string x
|
||||||
Mint.to_string x
|
| VChar c -> "'" ^ Char.escaped c ^ "'"
|
||||||
| VChar c ->
|
| VString s -> "\"" ^ String.escaped s ^ "\""
|
||||||
"'" ^ Char.escaped c ^ "'"
|
| VUnit -> "()"
|
||||||
| VString s ->
|
| VLocation a -> print_array_value d (Memory.dereference m a)
|
||||||
"\"" ^ String.escaped s ^ "\""
|
| VTagged (KId k, []) -> k
|
||||||
| VUnit ->
|
| VTagged (KId k, vs) -> k ^ print_tuple d vs
|
||||||
"()"
|
| VTuple vs -> print_tuple d vs
|
||||||
| VLocation a ->
|
|
||||||
print_array_value d (Memory.dereference m a)
|
|
||||||
| VTagged (KId k, []) ->
|
|
||||||
k
|
|
||||||
| VTagged (KId k, vs) ->
|
|
||||||
k ^ print_tuple d vs
|
|
||||||
| VTuple (vs) ->
|
|
||||||
print_tuple d vs
|
|
||||||
| VRecord fs ->
|
| VRecord fs ->
|
||||||
"{"
|
"{"
|
||||||
^ String.concat ", " (
|
^ String.concat
|
||||||
List.map (fun (LId f, v) -> f ^ " = " ^ print_value (d + 1) v
|
", "
|
||||||
) fs) ^ "}"
|
(List.map (fun (LId f, v) -> f ^ " = " ^ print_value (d + 1) v) fs)
|
||||||
| VClosure _ ->
|
^ "}"
|
||||||
"<fun>"
|
| VClosure _ -> "<fun>"
|
||||||
| VPrimitive (s, _) ->
|
| VPrimitive (s, _) -> Printf.sprintf "<primitive: %s>" s)
|
||||||
Printf.sprintf "<primitive: %s>" s
|
|
||||||
and print_tuple d vs =
|
and print_tuple d vs =
|
||||||
"(" ^ String.concat ", " (List.map (print_value (d + 1)) vs) ^ ")"
|
"(" ^ String.concat ", " (List.map (print_value (d + 1)) vs) ^ ")"
|
||||||
and print_array_value d block =
|
and print_array_value d block =
|
||||||
let r = Memory.read block in
|
let r = Memory.read block in
|
||||||
let n = Mint.to_int (Memory.size block) in
|
let n = Mint.to_int (Memory.size block) in
|
||||||
"[ " ^ String.concat ", " (
|
"[ "
|
||||||
List.(map (fun i -> print_value (d + 1) (r (Mint.of_int i)))
|
^ String.concat
|
||||||
(ExtStd.List.range 0 (n - 1))
|
", "
|
||||||
)) ^ " ]"
|
List.(
|
||||||
|
map
|
||||||
|
(fun i -> print_value (d + 1) (r (Mint.of_int i)))
|
||||||
|
(ExtStd.List.range 0 (n - 1)))
|
||||||
|
^ " ]"
|
||||||
in
|
in
|
||||||
print_value 0 v
|
print_value 0 v
|
||||||
|
;;
|
||||||
|
|
||||||
let print_values m vs =
|
let print_values m vs = String.concat "; " (List.map (print_value m) vs)
|
||||||
String.concat "; " (List.map (print_value m) vs)
|
|
||||||
|
|
||||||
module Environment : sig
|
module Environment : sig
|
||||||
(** Evaluation environments map identifiers to values. *)
|
(** Evaluation environments map identifiers to values. *)
|
||||||
|
@ -146,129 +168,121 @@ module Environment : sig
|
||||||
(** [print env] returns a human readable representation of [env]. *)
|
(** [print env] returns a human readable representation of [env]. *)
|
||||||
val print : t gvalue Memory.t -> t -> string
|
val print : t gvalue Memory.t -> t -> string
|
||||||
end = struct
|
end = struct
|
||||||
|
|
||||||
type t =
|
type t =
|
||||||
| EEmpty
|
| EEmpty
|
||||||
| EBind of identifier * t gvalue ref * t
|
| EBind of identifier * t gvalue ref * t
|
||||||
|
|
||||||
let empty = EEmpty
|
let empty = EEmpty
|
||||||
|
let bind e x v = EBind (x, ref v, e)
|
||||||
let bind e x v =
|
|
||||||
EBind (x, ref v, e)
|
|
||||||
|
|
||||||
exception UnboundIdentifier of identifier * Position.t
|
exception UnboundIdentifier of identifier * Position.t
|
||||||
|
|
||||||
let lookup' pos x =
|
let lookup' pos x =
|
||||||
let rec aux = function
|
let rec aux = function
|
||||||
| EEmpty -> raise (UnboundIdentifier (x, pos))
|
| EEmpty -> raise (UnboundIdentifier (x, pos))
|
||||||
| EBind (y, v, e) ->
|
| EBind (y, v, e) -> if x = y then v else aux e
|
||||||
if x = y then v else aux e
|
|
||||||
in
|
in
|
||||||
aux
|
aux
|
||||||
|
;;
|
||||||
|
|
||||||
let lookup pos x e = !(lookup' pos x e)
|
let lookup pos x e = !(lookup' pos x e)
|
||||||
|
let update pos x e v = lookup' pos x e := v
|
||||||
let update pos x e v =
|
|
||||||
lookup' pos x e := v
|
|
||||||
|
|
||||||
let last = function
|
let last = function
|
||||||
| EBind (x, v, e) -> Some (x, !v, e)
|
| EBind (x, v, e) -> Some (x, !v, e)
|
||||||
| EEmpty -> None
|
| EEmpty -> None
|
||||||
|
;;
|
||||||
|
|
||||||
let print_binding m (Id x, v) =
|
let print_binding m (Id x, v) = x ^ " = " ^ print_value m !v
|
||||||
x ^ " = " ^ print_value m !v
|
|
||||||
|
|
||||||
let print m e =
|
let print m e =
|
||||||
let b = Buffer.create 13 in
|
let b = Buffer.create 13 in
|
||||||
let push x v = Buffer.add_string b (print_binding m (x, v)) in
|
let push x v = Buffer.add_string b (print_binding m (x, v)) in
|
||||||
let rec aux = function
|
let rec aux = function
|
||||||
| EEmpty -> Buffer.contents b
|
| EEmpty -> Buffer.contents b
|
||||||
| EBind (x, v, EEmpty) -> push x v; aux EEmpty
|
| EBind (x, v, EEmpty) ->
|
||||||
| EBind (x, v, e) -> push x v; Buffer.add_string b "\n"; aux e
|
push x v;
|
||||||
|
aux EEmpty
|
||||||
|
| EBind (x, v, e) ->
|
||||||
|
push x v;
|
||||||
|
Buffer.add_string b "\n";
|
||||||
|
aux e
|
||||||
in
|
in
|
||||||
aux e
|
aux e
|
||||||
|
;;
|
||||||
end
|
end
|
||||||
|
|
||||||
(**
|
(** We have everything we need now to define [value] as an instantiation
|
||||||
We have everything we need now to define [value] as an instantiation
|
of ['e gvalue] with ['e = Environment.t], as promised. *)
|
||||||
of ['e gvalue] with ['e = Environment.t], as promised.
|
|
||||||
*)
|
|
||||||
type value = Environment.t gvalue
|
type value = Environment.t gvalue
|
||||||
|
|
||||||
(**
|
(** The following higher-order function lifts a function [f] of type
|
||||||
The following higher-order function lifts a function [f] of type
|
|
||||||
['a -> 'b] as a [name]d Hopix primitive function, that is, an
|
['a -> 'b] as a [name]d Hopix primitive function, that is, an
|
||||||
OCaml function of type [value -> value].
|
OCaml function of type [value -> value]. *)
|
||||||
*)
|
let primitive name ?(error = fun () -> assert false) coercion wrapper f : value =
|
||||||
let primitive name ?(error = fun () -> assert false) coercion wrapper f
|
VPrimitive
|
||||||
: value
|
( name
|
||||||
= VPrimitive (name, fun x ->
|
, fun x ->
|
||||||
match coercion x with
|
match coercion x with
|
||||||
| None -> error ()
|
| None -> error ()
|
||||||
| Some x -> wrapper (f x)
|
| Some x -> wrapper (f x) )
|
||||||
)
|
;;
|
||||||
|
|
||||||
type runtime = {
|
type runtime =
|
||||||
memory : value Memory.t;
|
{ memory : value Memory.t
|
||||||
environment : Environment.t;
|
; environment : Environment.t
|
||||||
}
|
}
|
||||||
|
|
||||||
type observable = {
|
type observable =
|
||||||
new_memory : value Memory.t;
|
{ new_memory : value Memory.t
|
||||||
new_environment : Environment.t;
|
; new_environment : Environment.t
|
||||||
}
|
}
|
||||||
|
|
||||||
(** [primitives] is an environment that contains the implementation
|
(** [primitives] is an environment that contains the implementation
|
||||||
of all primitives (+, <, ...). *)
|
of all primitives (+, <, ...). *)
|
||||||
let primitives =
|
let primitives =
|
||||||
let intbin name out op =
|
let intbin name out op =
|
||||||
let error m v =
|
let error m v =
|
||||||
Printf.eprintf
|
Printf.eprintf "Invalid arguments for `%s': %s\n" name (print_value m v);
|
||||||
"Invalid arguments for `%s': %s\n"
|
|
||||||
name (print_value m v);
|
|
||||||
assert false (* By typing. *)
|
assert false (* By typing. *)
|
||||||
in
|
in
|
||||||
VPrimitive (name, fun m -> function
|
VPrimitive
|
||||||
|
( name
|
||||||
|
, fun m -> function
|
||||||
| VInt x ->
|
| VInt x ->
|
||||||
VPrimitive (name, fun m -> function
|
VPrimitive
|
||||||
|
( name
|
||||||
|
, fun m -> function
|
||||||
| VInt y -> out (op x y)
|
| VInt y -> out (op x y)
|
||||||
| v -> error m v)
|
| v -> error m v )
|
||||||
| v -> error m v)
|
| v -> error m v )
|
||||||
in
|
in
|
||||||
let bind_all what l x =
|
let bind_all what l x =
|
||||||
List.fold_left (fun env (x, v) -> Environment.bind env (Id x) (what x v))
|
List.fold_left (fun env (x, v) -> Environment.bind env (Id x) (what x v)) x l
|
||||||
x l
|
|
||||||
in
|
in
|
||||||
(* Define arithmetic binary operators. *)
|
(* Define arithmetic binary operators. *)
|
||||||
let binarith name =
|
let binarith name = intbin name (fun x -> VInt x) in
|
||||||
intbin name (fun x -> VInt x) in
|
let binarithops = Mint.[ "`+`", add; "`-`", sub; "`*`", mul; "`/`", div ] in
|
||||||
let binarithops = Mint.(
|
|
||||||
[ ("`+`", add); ("`-`", sub); ("`*`", mul); ("`/`", div) ]
|
|
||||||
) in
|
|
||||||
(* Define arithmetic comparison operators. *)
|
(* Define arithmetic comparison operators. *)
|
||||||
let cmparith name = intbin name bool_as_value in
|
let cmparith name = intbin name bool_as_value in
|
||||||
let cmparithops =
|
let cmparithops =
|
||||||
[ ("`=?`", ( = ));
|
[ "`=?`", ( = ); "`<?`", ( < ); "`>?`", ( > ); "`>=?`", ( >= ); "`<=?`", ( <= ) ]
|
||||||
("`<?`", ( < ));
|
|
||||||
("`>?`", ( > ));
|
|
||||||
("`>=?`", ( >= ));
|
|
||||||
("`<=?`", ( <= )) ]
|
|
||||||
in
|
in
|
||||||
let boolbin name out op =
|
let boolbin name out op =
|
||||||
VPrimitive (name, fun _ x -> VPrimitive (name, fun _ y ->
|
VPrimitive
|
||||||
out (op (value_as_bool x) (value_as_bool y))))
|
( name
|
||||||
|
, fun _ x ->
|
||||||
|
VPrimitive (name, fun _ y -> out (op (value_as_bool x) (value_as_bool y))) )
|
||||||
in
|
in
|
||||||
let boolarith name = boolbin name (fun x -> if x then ptrue else pfalse) in
|
let boolarith name = boolbin name (fun x -> if x then ptrue else pfalse) in
|
||||||
let boolarithops =
|
let boolarithops = [ "`||`", ( || ); "`&&`", ( && ) ] in
|
||||||
[ ("`||`", ( || )); ("`&&`", ( && )) ]
|
|
||||||
in
|
|
||||||
let generic_printer =
|
let generic_printer =
|
||||||
VPrimitive ("print", fun m v ->
|
VPrimitive
|
||||||
|
( "print"
|
||||||
|
, fun m v ->
|
||||||
output_string stdout (print_value m v);
|
output_string stdout (print_value m v);
|
||||||
flush stdout;
|
flush stdout;
|
||||||
VUnit
|
VUnit )
|
||||||
)
|
|
||||||
in
|
in
|
||||||
let print s =
|
let print s =
|
||||||
output_string stdout s;
|
output_string stdout s;
|
||||||
|
@ -276,16 +290,18 @@ let primitives =
|
||||||
VUnit
|
VUnit
|
||||||
in
|
in
|
||||||
let print_int =
|
let print_int =
|
||||||
VPrimitive ("print_int", fun _ -> function
|
VPrimitive
|
||||||
|
( "print_int"
|
||||||
|
, fun _ -> function
|
||||||
| VInt x -> print (Mint.to_string x)
|
| VInt x -> print (Mint.to_string x)
|
||||||
| _ -> assert false (* By typing. *)
|
| _ -> assert false (* By typing. *) )
|
||||||
)
|
|
||||||
in
|
in
|
||||||
let print_string =
|
let print_string =
|
||||||
VPrimitive ("print_string", fun _ -> function
|
VPrimitive
|
||||||
|
( "print_string"
|
||||||
|
, fun _ -> function
|
||||||
| VString x -> print x
|
| VString x -> print x
|
||||||
| _ -> assert false (* By typing. *)
|
| _ -> assert false (* By typing. *) )
|
||||||
)
|
|
||||||
in
|
in
|
||||||
let bind' x w env = Environment.bind env (Id x) w in
|
let bind' x w env = Environment.bind env (Id x) w in
|
||||||
Environment.empty
|
Environment.empty
|
||||||
|
@ -298,28 +314,30 @@ let primitives =
|
||||||
|> bind' "true" ptrue
|
|> bind' "true" ptrue
|
||||||
|> bind' "false" pfalse
|
|> bind' "false" pfalse
|
||||||
|> bind' "nothing" VUnit
|
|> bind' "nothing" VUnit
|
||||||
|
;;
|
||||||
|
|
||||||
let initial_runtime () = {
|
let initial_runtime () =
|
||||||
memory = Memory.create (640 * 1024 (* should be enough. -- B.Gates *));
|
{ memory = Memory.create (640 * 1024 (* should be enough. -- B.Gates *))
|
||||||
environment = primitives;
|
; environment = primitives
|
||||||
}
|
}
|
||||||
|
;;
|
||||||
|
|
||||||
let rec evaluate runtime ast =
|
let rec evaluate runtime ast =
|
||||||
try
|
try
|
||||||
let runtime' = List.fold_left definition runtime ast in
|
let runtime' = List.fold_left definition runtime ast in
|
||||||
(runtime', extract_observable runtime runtime')
|
runtime', extract_observable runtime runtime'
|
||||||
with Environment.UnboundIdentifier (Id x, pos) ->
|
with
|
||||||
|
| Environment.UnboundIdentifier (Id x, pos) ->
|
||||||
Error.error "interpretation" pos (Printf.sprintf "`%s' is unbound." x)
|
Error.error "interpretation" pos (Printf.sprintf "`%s' is unbound." x)
|
||||||
|
|
||||||
(** [definition pos runtime d] evaluates the new definition [d]
|
(** [definition pos runtime d] evaluates the new definition [d]
|
||||||
into a new runtime [runtime']. In the specification, this
|
into a new runtime [runtime']. In the specification, this
|
||||||
is the judgment:
|
is the judgment:
|
||||||
|
|
||||||
E, M ⊢ dv ⇒ E', M'
|
E, M ⊢ dv ⇒ E', M' *)
|
||||||
|
|
||||||
*)
|
|
||||||
and definition runtime d =
|
and definition runtime d =
|
||||||
failwith "Students! This is your job!"
|
(* TODO *)
|
||||||
|
failwith "Students! This is your job!"
|
||||||
|
|
||||||
and expression' environment memory e =
|
and expression' environment memory e =
|
||||||
expression (position e) environment memory (value e)
|
expression (position e) environment memory (value e)
|
||||||
|
@ -328,29 +346,29 @@ and expression' environment memory e =
|
||||||
|
|
||||||
E, M ⊢ e ⇓ v, M'
|
E, M ⊢ e ⇓ v, M'
|
||||||
|
|
||||||
and E = [runtime.environment], M = [runtime.memory].
|
and E = [runtime.environment], M = [runtime.memory]. *)
|
||||||
*)
|
|
||||||
and expression _ environment memory =
|
and expression _ environment memory =
|
||||||
failwith "Students! This is your job!"
|
(* TODO *)
|
||||||
|
failwith "Students! This is your job!"
|
||||||
|
|
||||||
(** This function returns the difference between two runtimes. *)
|
(** This function returns the difference between two runtimes. *)
|
||||||
and extract_observable runtime runtime' =
|
and extract_observable runtime runtime' =
|
||||||
let rec substract new_environment env env' =
|
let rec substract new_environment env env' =
|
||||||
if env == env' then new_environment
|
if env == env'
|
||||||
else
|
then new_environment
|
||||||
|
else (
|
||||||
match Environment.last env' with
|
match Environment.last env' with
|
||||||
| None -> assert false (* Absurd. *)
|
| None -> assert false (* Absurd. *)
|
||||||
| Some (x, v, env') ->
|
| Some (x, v, env') ->
|
||||||
let new_environment = Environment.bind new_environment x v in
|
let new_environment = Environment.bind new_environment x v in
|
||||||
substract new_environment env env'
|
substract new_environment env env')
|
||||||
in
|
in
|
||||||
{
|
{ new_environment = substract Environment.empty runtime.environment runtime'.environment
|
||||||
new_environment =
|
; new_memory = runtime'.memory
|
||||||
substract Environment.empty runtime.environment runtime'.environment;
|
|
||||||
new_memory =
|
|
||||||
runtime'.memory
|
|
||||||
}
|
}
|
||||||
|
;;
|
||||||
|
|
||||||
(** This function displays a difference between two runtimes. *)
|
(** This function displays a difference between two runtimes. *)
|
||||||
let print_observable (_ : runtime) observation =
|
let print_observable (_ : runtime) observation =
|
||||||
Environment.print observation.new_memory observation.new_environment
|
Environment.print observation.new_memory observation.new_environment
|
||||||
|
;;
|
||||||
|
|
Reference in a new issue