### Storing [ Datatype locations Alias locs Type stores Entity store Funcon initialise-storing Funcon store-clear Datatype variables Alias vars Funcon variable Alias var Funcon allocate-variable Alias alloc Funcon recycle-variables Alias recycle Funcon initialise-variable Alias init Funcon allocate-initialised-variable Alias alloc-init Funcon assign Funcon assigned Funcon current-value Funcon un-assign Funcon structural-assign Funcon structural-assigned ] Meta-variables T, T' <: values #### Stores Type locations ~> atoms Alias locs = locations /* A storage location is represented by an atom. */ Type stores ~> maps(locations, values?) /* The domain of a store is the set of currently allocated locations. Mapping a location to `( )` models the absence of its stored value; removing it from the store allows it to be re-allocated. */ Entity < _ , store(_:stores) > ---> < _ , store(_:stores) > /* The current store is a mutable entity. A transition ``< X , store(Sigma) > ---> < X' , store(Sigma') >`` models a step from `X` to `X'` where the difference between `Sigma` and `Sigma'` (if any) corresponds to storage effects. */ Funcon store-clear : =>null-type Rule < store-clear , store(_) > ---> < null-value , store(map( )) > /* `store-clear` ensures the store is empty. */ Funcon initialise-storing(X:=>T) : =>T ~> sequential(store-clear, initialise-giving(initialise-generating(X))) Alias init-storing = initialise-storing /* `initialise-storing(X)` ensures that the entities used by the funcons for storing are properly initialised. */ #### Simple variables /* Simple variables may store primitive or structured values. The type of values stored by a variable is fixed when it is allocated. For instance, `allocate-variable(integers)` allocates a simple integer variable, and `allocate-variable(vectors(integers))` allocates a structured variable for storing vectors of integers, which can be updated only monolithically. */ Datatype variables ::= variable(_:locations, _:value-types) Alias vars = variables Alias var = variable /* `variables` is the type of simple variables that can store values of a particular type. `variable(L, T)` constructs a simple variable for storing values of type `T` at location `L`. Variables at different locations are independent. Note that `variables` is a subtype of `datatype-values`. */ Funcon allocate-variable(T:types) : =>variables Alias alloc = allocate-variable /* `allocate-variable(T)` gives a simple variable whose location is not in the current store. Subsequent uses of `allocate-variable(T')` give independent variables, except after `recycle-variables(V,...)` or `store-clear`. */ Rule < use-atom-not-in(dom(Sigma)) , store(Sigma) > ---> < L , store(Sigma') > map-override({L |-> ( )}, Sigma') ~> Sigma'' ------------------------------------------------------------------------- < allocate-variable(T:types) , store(Sigma) > ---> < variable(L, T) , store(Sigma'') > Funcon recycle-variables(_:variables+) : =>null-type Alias recycle = recycle-variables /* `recycle-variables(Var,...)` removes the locations of `Var`, ..., from the current store, so that they may subsequently be re-allocated. */ Rule is-in-set(L, dom(Sigma)) == true --------------------------------------------------------------------- < recycle-variables(variable(L:locations, T:types)) , store(Sigma) > ---> < null-value , store(map-delete(Sigma, {L})) > Rule is-in-set(L, dom(Sigma)) == false --------------------------------------------------------------------- < recycle-variables(variable(L:locations, T:types)) , store(Sigma) > ---> < fail , store(Sigma) > Rule recycle-variables(Var:variables, Var+:variables+) ~> sequential(recycle-variables(Var), recycle-variables(Var+)) Funcon initialise-variable(_:variables, _:values) : =>null-type Alias init = initialise-variable /* `initialise-variable(Var, Val)` assigns `Val` as the initial value of `Var`, and gives `null-value`. If `Var` already has an assigned value, it fails. */ Rule and(is-in-set(L, dom(Sigma)), not is-value(map-lookup(Sigma, L)), is-in-type(Val, T)) == true ---------------------------------------------------------------------------- < initialise-variable(variable(L:locations, T:types), Val:values) , store(Sigma) > ---> < null-value , store(map-override({L|->Val}, Sigma)) > Rule and(is-in-set(L, dom(Sigma)), not is-value(map-lookup(Sigma, L)), is-in-type(Val, T)) == false ---------------------------------------------------------------------------- < initialise-variable(variable(L:locations, T:types), Val:values) , store(Sigma) > ---> < fail , store(Sigma) > Funcon allocate-initialised-variable(T, Val:T) : =>variables ~> give(allocate-variable(T), sequential(initialise-variable(given, Val), given)) Alias alloc-init = allocate-initialised-variable /* `allocate-initialised-variable(T, Val)` allocates a simple variable for storing values of type `T`, initialises its value to `Val`, and returns the variable. */ Funcon assign(_:variables, _:values) : =>null-type /* `assign(Var, Val)` assigns the value `Val` to the variable `Var`, provided that `Var` was allocated with a type that contains `Val`. */ Rule and(is-in-set(L, dom(Sigma)), is-in-type(Val, T)) == true ----------------------------------------------------------------------- < assign(variable(L:locations, T:types), Val:values) , store(Sigma) > ---> < null-value , store(map-override({L|->Val}, Sigma)) > Rule and(is-in-set(L, dom(Sigma)), is-in-type(Val, T)) == false -------------------------------------------------------------------------- < assign(variable(L:locations,T:types), Val:values) , store(Sigma) > ---> < fail , store(Sigma) > Funcon assigned(_:variables) : =>values /* `assigned(Var)` gives the value assigned to the variable `Var`, failing if no value is currently assigned. */ Rule map-lookup(Sigma, L) ~> (Val:values) ------------------------------------------------------------------ < assigned(variable(L:locations, T:types)) , store(Sigma) > ---> < Val , store(Sigma) > Rule map-lookup(Sigma, L) == ( ) ------------------------------------------------------------------ < assigned(variable(L:locations, T:types)) , store(Sigma) > ---> < fail , store(Sigma) > Funcon current-value(_:values) : =>values /* `current-value(V)` gives the same result as `assigned(V)` when `V` is a simple variable, and otherwise gives `V`. It represents implicit dereferencing of a value that might be a variable. */ Rule current-value(Var:variables) ~> assigned(Var) Rule current-value(U:~variables) ~> U Funcon un-assign(_:variables) : =>null-type /* `un-assign(Var)` remove the value assigned to the variable `Var`. */ Rule is-in-set(L, dom(Sigma)) == true -------------------------------------------------------------------------- < un-assign(variable(L:locations, T:types)) , store(Sigma) > ---> < null-value , store(map-override({L |-> ( )}, Sigma)) > Rule is-in-set(L, dom(Sigma)) == false -------------------------------------------------------------------------- < un-assign(variable(L:locations, T:types)) , store(Sigma) > ---> < fail , store(Sigma) > #### Structured variables /* Structured variables are structured values where some components are simple variables. Such component variables can be selected using the same funcons as for selecting components of structured values. Structured variables containing both simple variables and values correspond to hybrid structures where particular components are mutable. All datatypes (except for abstractions) can be used to form structured variables. So can maps, but not sets or multisets. Structural generalisations of `assign(Var, Val)` and `assigned(Var)` access all the simple variables contained in a structured variable. Assignment requires each component value of a hybrid structured variable to be equal to the corresponding component of the structured value. */ Funcon structural-assign(_:values, _:values) : =>null-type /* `structural-assign(V1, V2)` takes a (potentially) structured variable `V1`and a (potentially) structured value `V2`. Provided that the structure and all non-variable values in `V1` match the structure and corresponding values of `V2`, all the simple variables in `V1` are assigned the corresponding values of `V2`; otherwise the assignment fails. */ Rule structural-assign(V1:variables, V2:values) ~> assign(V1, V2) Rule V1 : ~(variables) V1 ~> datatype-value(I1:identifiers, V1*:values*) V2 ~> datatype-value(I2:identifiers, V2*:values*) ----------------------------------------------------------------------- structural-assign(V1:datatype-values, V2:datatype-values) ~> sequential( check-true(is-equal(I1, I2)), effect(tuple(interleave-map( structural-assign(tuple-elements(given)), tuple-zip(tuple(V1*), tuple(V2*))))), null-value) /* Note that simple variables are datatype values. */ Rule dom(M1) == {} ------------------------------------------------------ structural-assign(M1:maps(_,_), M2:maps(_,_)) ~> check-true(is-equal(dom(M2), { })) Rule some-element(dom(M1)) ~> K ---------------------------------------------------------------------------- structural-assign(M1:maps(_, _), M2:maps(_, _)) ~> sequential(check-true(is-in-set(K, dom(M2))), structural-assign(map-lookup(M1, K), map-lookup(M2, K)), structural-assign(map-delete(M1, {K}), map-delete(M2, {K}))) Rule V1 : ~(datatype-values|maps(_, _)) --------------------------------------------------------------- structural-assign(V1:values,V2:values) ~> check-true(is-equal(V1, V2)) Funcon structural-assigned(_:values) : =>values /* `structural-assigned(V)` takes a (potentially) structured variable `V`, and computes the value of `V` with all simple variables in `V` replaced by their assigned values, failing if any of them do not have assigned values. When `V` is just a simple variable or a (possibly structured) value with no component variables, `structural-assigned(V)` gives the same result as `current-value(V)`. */ Rule structural-assigned(Var:variables) ~> assigned(Var) Rule V : ~(variables) V ~> datatype-value(I:identifiers, V*:values*) ---------------------------------------------------------------------------- structural-assigned(V:datatype-values) ~> datatype-value(I, interleave-map(structural-assigned(given), V*)) /* Note that simple variables are datatype values. */ Rule structural-assigned(M:maps(_, _)) ~> map(interleave-map(structural-assigned(given), map-elements(M))) Rule U : ~(datatype-values|maps(_, _)) ------------------------------------------ structural-assigned(U:values) ~> U