next up previous contents
Next: Basic Input and Output Up: The MPD Programming Language Previous: Operations, Procedures and Processes   Contents

Subsections


Resources and Globals

Programs so far have consisted of a single resource. Here we show how to write programs containing more than one resource. Resources and Globals both have modular structures, but are created and used differently. A resource defines a template - a bit like defining a class in Java - from which resource instance can be created dynamically; resource instances can also be destroyed. A global is a single, unparameterized, automatically created instance of a resource.

Resources

Each resource, in the general case, is composed of two parts, a spec and a body. The general form is

  resource resource_name
     imports
     constants, types, operation defs
  body resource_name (parameter_list)
     imports
     declarations, statements, procs
     final_code
  end resource_name

The spec part

The body contains the code to implement the resource, and in particular implement any procs that correspond to operations declared in the spec. Local imports, variables, types, operations, etc. which are defined in the body are hidden from the outside world.

Example

resource Stack
  type result = enum(OK, OVERFLOW, UNDERFLOW)
  op push(int item) returns result  r
  op pop(res int item) returns result  r
body Stack(int size)
  int store[1:size]
  int top = 0
  proc push(item) returns r  {
    if (top < size) { store[++top] = item; r = OK
    } else if (top == size ) { r = OVERFLOW
    }
  }
  proc pop(item) returns r  {
    if (top > 0) { item = store[top--]; r = OK
    } else if (top == 0 ) { r = UNDERFLOW
    }
  }
end Stack

Simple resources

If a resource does not export anything (i.e. there are no declarations in its spec) then there is an abbreviated form. The following example illustrates this. The resource

resource foo                        
body foo (int x, y)
 # some code
end foo

can be written equivalently as

resource foo (int x, y)
 # some code
end foo

Separate Spec and Body

The spec and the body can be separated. These parts can appear in the same file or in different files. The body must always be compiled after the spec, and should be recompiled whenever the spec changes. A tool mpdm can be used to generate UNIX Makefiles that will ensure the correct compilation order. mpdm is a tool that scans the resources and constructs a Makefile. The command make reads the Makefile and decides which resources need to be recompiled and in what order.

Example

resource heap
   import graph

   op new_node (N_CLASS) returns ptr NODE
   op new_array (int) returns ptr [*] ptr NODE

body heap () separate

and elsewhere we may define:

body heap
   proc new_node (class) returns n {
      n = new(NODE)
      n^.n_class = class
      n^.visited = false
   }

   proc new_array (size) returns ap {
      ap = new ([size] ptr NODE)
   }
end heap

Imports

When one resource wants to use another, it must import the other resource. An import statement has the form:
import id , id , ...
It may appear anywhere that a declaration may appear. For example,

resource Stack_User()
    import Stack
    Stack.result x
    ...
end

declares a variable x whose type is declared in the spec of Stack. If the name of the type result is unique in this context then we could also write the declaration as

    result x

Creating Resource Instances

There is one resource instance that is always created: the main resource of the program. This is the last resource which is compiled. It must not be imported by other resources, it cannot have parameters, and does not have a spec. It imports all the required resources and starts the ball rolling.

All other instances of resources that are required must created during program execution. Several instances of the same resource can be active in the same program.

A resource is created using a create expression, e.g.

 create heap ()

This is an expression. It returns a resource capability, which acts as a reference to the resource that is created. A typical use would be

myheap = create heap ()

In this case myheap must have the type cap heap. To define and create a heap in a single declaration, we could write

cap heap myheap = create heap ()

Here is an example using the stack resource:

resource Stack_User()
  import Stack
  result x
  cap Stack s1, s2
  s1 = create Stack(10)
  s2 = create Stack(20)
  int  y

  s1.push(4); s1.push(37); s2.push(98)
  if ((x = s1.pop(y)) != OK) { write("error")
  } else { write(y)
  }
  if ((x = s2.pop(y)) != OK) { write("error")
  } else { write(y)
  }
end


Using resource capability variables


Initial and final code

Whenever a resource is created, the statements appearing in it at the top level are executed. These statements are called the initial code. Final code appears as a single block of code, and is introduced by the keyword final. The final block has the form

  final {
    final_code
  }

and is executed right before the resource instance disappears - for example a resource instance that has dynamically created a linked list should, as part of its final code, free the list.

  global global_name
     imports
     declarations
  end

Globals

In may cases globals could be equally well represented by resource. Key differences are that globals allow sharing of definitions between other resources and globals. A global can be thought of as a constant resource. There is only one instance.

The simplest form of global is used to declare constants, types and variables:

  global global_name
      imports
      declarations
  end

The general form permits a body. This enables initialization of variables as in, for example:

global Diagonal
    const N = 20
    real a[N,N] = ([N] ([N] 0.0))
body Diagonal
  for [i = 1 to N]{ a[i,i] = i }
end Diagonal

It also enables the implementation of shared procedures. For example consider the following outline:

global Screen

    op move_to (int x; int y)
    op up (), down (), left (), right ()
    op refresh ()

body Screen
    proc move_to(x,y){
    ...
    }
    ...
    # declaration of procs for up, down, left, right, refresh
end Screen

Suppose that this global provides a library for screen management. Although this could be written as a resource, in order to share this resource between other resources we would then need to create an instance of Screen and pass a capability for that instance to the resources who wish to use it. In contrast, since Screen is declared as a global, then it can be used simply by importing it.


next up previous contents
Next: Basic Input and Output Up: The MPD Programming Language Previous: Operations, Procedures and Processes   Contents
David Sands 2003-09-05