SOIL

From USE: UML-based Specification Environment
Jump to: navigation, search

Status

This page is still under construction. We will provide more documentation about SOIL soon. In the meantime, we also refer to this paper (SBMF'2011).

Summary

SOIL is the imperative programming language of USE. The acronym stands for Simple OCL-based Imperative Language. SOIL embeds OCL in a strictly modular way - unlike in ImperativeOCL (and some other OCL-based languages), the syntax and semantics of OCL remains unchanged by the imperative language.

SOIL is required at two locations in USE:

  • To create and modify systems states of a specification (i.e., a .use file). Here, SOIL extends the former command line language of USE.
  • To imperatively define operations in a .use specification.

Introduction

On the command line, statements can be executed using the "!" operator. For example, the following command line interaction first creates a instance of a class Company, then it creates five Worker objects in a loop, creating a link for the Employs associations for each of them. Finally, the name of the company object is set.

use> !c := new Company
use> !for i in Sequence{1..5} do w := new Worker; insert (c,w) into Employs end
use> !c.name := 'My Company'

The following excerpt of the Project World specificaton (./examples/soil/projectworld/) shows how we can use SOIL do imperatively define operations.

model Projects
-- (excerpt)

class Company
operations
  fire(w : Worker) 
    begin
      delete (self,w) from Employs;
      for p in w.projects do
        delete (p,w) from Member;
        if not p.missingQualifications()->isEmpty then
          p.status := #suspended
        end
      end
    end
    pre  OnlyEmployeesCanBeFired: employees->includes(w)
    post Fired: employees->excludes(w)
    post ProjectsStillHappy: projects->forAll(p| p.status = #active implies p.missingQualifications()->isEmpty)
    post NoEmployeeOverloaded: employees->forAll(e|not e.isOverloaded())
end


You can find more examples for SOIL in your USE installation in ./examples/soil/.

The Language

We use e to denote an OCL expression.

s ::=                                                                  (statement)
   v := new c [(nameExpr)] |                                           (object creation)
   v := new c [(nameExpr)] between (participant1,participant2,...) |   (link object creation)
   destroy e  |                                                        (object destruction)
   insert (e1; ... ; en) into a j |                                    (link insertion)
   delete (e1; ... ; en) from a j |                                    (link deletion)
   e1.a := e2 |                                                        (attribute assignment)
   v := e |                                                            (variable assignment)
   e1.op(e2; ... ; en) |                                               (operation call)
   v := e1.op(e2; ... ; en) |                                          (operation call with result)
   [begin] s1; ... ; sn [end] [declare v1 : t1; ... ; vn : tn] |       (block of statements)
   if e then s1 [else s2] end |                                        (conditional execution)
   for v in e do s end                                                 (iteration)

Variables

(TODO)


Operation Invocations

Both OCL-defined query operations and SOIL-defined imperative operations can be invoked in a SOIL program. But only OCL-defined query operations may be invoked from an OCL expression.

class Demo
attributes
  value : Integer;
operations 
  multipliedValue(factor : Integer) : Integer = value * factor

  multiplyValue1(factor : Integer) begin
    self.value := self.value * factor;
  end

  multiplyValue2(factor : Integer) : Integer begin
    self.value := self.value * factor;
    result := self.value
  end

  test() begin
    declare x : Integer, y1 : Integer, y2 : Integer;
    self.value := 2;
 
    -- this assigns the result of the OCL expression 'self.multipliedValue(3)' to x
    x := self.multipliedValue(3);

    -- this assigns the result of the OCL expression 'self.multipliedValue(3) + self.multipliedValue(2)' to x
    x := self.multipliedValue(3) + self.multipliedValue(2);

    -- imperative operation call without result value
    self.multiplyValue1(4);

    -- imperative operation call with result value, x captures the result
    x := self.multiplyValue2(5);

    -- not possible: use SOIL operations in OCL expression
    -- x := self.multiplyValue2(3) + self.multiplyValue2(2);
    -- rewrite as:
    y1 := self.multiplyValue2(3);
    y2 := self.multiplyValue2(2);
    x := y1 + y2;
  end
end


Operations that are defined by SOIL statements can be guarded by pre- and postconditions, too. See the example above. When a pre- or postcondition is not maintained, the program execution is interrupted and the user can interactively inspect the system state and the variable environment.

Object Destruction

SOIL intentionally has no garbage collection, because garbage collection is not suited for the manipulation of instances where we take a 'global' perspective on objects and cannot assume defined roots that could be used to determine reachability. Therefore, SOIL has an explicit destroy statement. When an object is destroyed, all variables and attributes referring to it are set to Undefined (null). All association links containing this object are removed:

use> !c := new Company
use> !w := new Worker
use> !x := c
use> !insert (c,w) into Employs
use> !destroy x
use> ?c
-> Undefined : OclVoid
use> ?w.employer
-> Undefined : OclVoid

Errors

(TODO)

I/O Operations

Starting with USE 4.0, SOIL provides basic I/O operations. They can be used to write output to and read input from the shell. The output operations are:

Write(s:String)

The argument s is written to the shell without a line break.

WriteLine(s:String)

The argument s is written to the shell and a line break is appended.

The input operations are:

ReadLine():String

Waits for user input which is terminated by an enter and returns the entered String.

ReadInteger():Integer

Waits for user input which is terminated by an enter and returns an Integer or Undefined if the input was not an integer.

Example

 Write('Enter your name: ');
 aName := ReadLine();
 WriteLine('Hello ' + aName + '!');