Transformation patterns: Difference between revisions

From Event-B
Jump to navigationJump to search
imported>Ilya.lopatkin
mNo edit summary
imported>Ilya.lopatkin
 
(12 intermediate revisions by 2 users not shown)
Line 1: Line 1:
The ''Transformation patterns plugin'' allows users to write transformation scripts in [http://www.eclipse.org/gmt/epsilon/doc/eol/ EOL] (simple object-based imperative language) and easily run them over models within the same project.
The ''Transformation patterns plugin'' allows users to write transformation scripts in [http://www.eclipse.org/gmt/epsilon/doc/eol/ EOL] (simple object-based imperative language) and easily run them over models within the same project. Editing is done in a simple text editor with syntax coloring. No compilation is needed to run transformations.


Editing is done in a simple text editor with syntax coloring. No compilation is needed to run transformations.
The plugin is essentially a wrapper around the [http://www.eclipse.org/gmt/epsilon/ Epsilon framework] of interoperatable programming languages which are used to operate over the given EMF models (Event-B in our case). The plugin performs the necessary setup for running the transformations, and provides a simple library handling user input of Event-B elements such as machines, events, variables etc.


== What it is ==
== Syntax example ==


The plugin is essentially a wrapper around the [http://www.eclipse.org/gmt/epsilon/ Epsilon framework] of interoperatable programming languages which are used to operate over the given EMF models (Event-B in our case). The plugin performs the necessary setup for running the transformations, provides simple user interface for choosing Event-B elements such as machines, events, variables etc, and caches the user input (see later).
The following is a simple example showing a syntax of patterns when using the provided library. The pattern implements the atomicity refinement of an abstract event:


== User input cache ==
import "system.eol";
import "util.eol";
var machine: Machine = inputMachine("target");
var abstractMachine: Machine = machine.getOrInputRefines();
var absEvent: Event = abstractMachine.inputEvent("av", "Abstract event");
var counter: Variable = machine.inputOrNewVariable("cnt", "Counter variable");
var num: Integer = enterInt("Number of events");
for (i:Integer in Sequence{1..num})
{
    machine.newEvent(absEvent.name + "_ref_" + i)
              .addGuard("grd_cnt", counter.name + " = " + i)
              .addAction("act_cnt", counter.name + " := " + counter.name + "+1");
}
machine.newEvent(absEvent.name + "_ref_" + num).addRefines(absEvent);


The plugin provides a cache of objects (Event-B EMF elements and strings) entered by a user. If during the execution a pattern requires to enter an object more than once, then at the first time the user will be asked for input, and the result will be cached. All subsequent requests will return the cached object unless the record about the object is erased.
The plugin is currently tested using EOL only, however it is theoretically possible to import and use files written in other Epsilon languages. The description of syntax and facilities provided by Epsilon can be found in the first few sections of [http://dev.eclipse.org/svnroot/modeling/org.eclipse.gmt.epsilon/trunk/doc/org.eclipse.epsilon.book/EpsilonBook.pdf The Epsilon Book]. The full list of operations for handling user input and modifying Event-B elements can be found in [[Transformation_patterns/Reference]]


The cache can be accessed directly from within the scripts, and used as a means to pass the objects between different transformations without them knowing the actual source of input. Thus, the input can be extended to other domain-specific EMF models.
== How to run a pattern ==


A cache is a map from ''keys'' to ''values'' where a key consists of a type of object and its identifier, and the value is the actual cached object. The types of objects are handled by the utility library, while the identifiers must be supplied by script developer.
The files in the project folder with the extension .ptrn are considered to be runnable patterns. They can ''import'' .eol files (libraries) and use the operations declared there. Libraries can import other libraries etc. The tool does not give user the ability to run .eol libraries directly (although this can possibly be done through standard Epsilon tools). The suggested scenario of usage is to write the actual transformation code in .eol files as EOL operations, and call them from short .ptrn files which would look like the following:


The operations which request input from the user without caching it all start with '''enter''', such as in enterString(..), enterEvent(..) etc. The operations starting with '''input''' do the following:
import "system.eol";
# check the cache for existing objects, and return if the object is in the cache
import "my_library.eol";
# if there is no object in the cache with the given id, then ask user to enter the object
# put the entered object into the cache and return to caller
var m: Machine = inputMachine("target");
The parameter '''id''' is optional. If '''id'''=null then an '''input''' operation degenerates into '''enter'''.
m.myTransformationPattern();


== Full list of provided operations ==
All the .ptrn files would appear under the context menu (right click) of the target machine -> Run ->


{|border="1" cellpadding="10" cellspacing="0" align="center" width = "70%"
On the code above, inputMachine("target") returns an EMF model of the machine upon which the pattern is run.
|-
| enterString(userMessage:String):String


enterString(userMessage, _default:String):String
Pattern files are text files. To create/rename/delete them use standard Eclipse wizards and explorer.


enterInt(userMessage:String):Integer
== User input cache ==


enterInt(userMessage:String, _default:Integer):String
The plugin contains a cache of objects (EMF elements and strings) entered by a user (or external program). If during the execution a pattern requires to enter an object more than once, then at the first time the user will be asked for input, and the result will be cached. All subsequent requests will return the cached object unless the record about the object is erased.


inputString(id:String):String
The cache can be accessed directly from within the scripts, and used as a means to pass the objects between different transformations without them knowing the actual source of input. Thus, the input can be extended to other domain-specific EMF models.


inputString(id, userMessage:String):String
A cache is a map from ''keys'' to ''values'' where a key consists of a type of object and its identifier, and the value is the actual cached object. The types of objects are handled by the utility library, while the identifiers must be supplied by script developer.


inputString(id,userMessage,_default:String):String
The operations which request input from the user without caching it all start with '''enter''', such as enterString(..), enterEvent(..) etc. The operations starting with '''input''' do the following:
 
# they check the cache for existing objects, and return if the object is in the cache
inputInt(id:String):Integer
# if there is no object in the cache with the given id, then ask user to enter the object
 
# put the entered object into the cache and return to caller
inputInt(id,userMessage:String):Integer
The parameter '''id''' is optional. If '''id'''=null then an '''input*''' operation degenerates into '''enter*'''.
 
inputInt(id,userMessage:String, _default:Integer):Integer
 
| Operations that provide input of basic strings and integers from user. Present a simple dialog using '''userMessage''' as a description of what the user is supposed to enter. If specified, '''_default''' gives a default value for the dialog input field.
'''inputX''' operations additionally check the input cache. If the cache contains an object with the key '''id''' then the object is returned and no dialog is shown. Otherwise the user enters the requested object, and the operation puts the latter into the cache under the key '''id'''.
|-
|inputMachine(id:String):Machine
 
inputMachine(id,userMessage:String):Machine
 
inputMachine(id,userMessage,fileName:String):Machine
 
inputContext(id:String):Context
 
inputContext(id,userMessage:String):Context
 
inputContext(id,userMessage,fileName:String):Context
 
Machine enterEvent(userMessage:String):Event
 
Machine enterVariable(userMessage:String):Variable
 
Machine inputEvent(id:String):Event
 
Machine inputEvent(id,userMessage:String):Event
 
Machine inputVariable(id:String):Variable
 
Machine inputVariable(id,userMessage:String):Variable
 
|Ask user to choose an Event-B machine/context/event/variable.
The dialogs present a list of appropriate elements: machines and contexts are from the current project, events and variables are from the specified machine. The '''inputX''' operations check the input cache against a given '''id''' key, and put the chosen elements into the cache.
If '''fileName''' is specified, then the machine/context with the given name is loaded and the dialog is not shown.
|-
|Machine findEvent(name: String): Event
 
Event findWitness(name: String): Witness
 
Event findParameter(name: String): Parameter
 
Event findGuard(name: String): Guard
 
Event findAction(name:String):Action
 
Machine findVariable(name: String): Variable
 
Machine findInvariant(name: String): Invariant
 
Context findConstant(name: String): Constant
 
Context findSet(name: String): CarrierSet
 
Context findAxiom(name: String): Axiom
|Return the child element with the given '''name''' if it exists. Return null otherwise.
Example:
m.findEvent("send")
will return the event named "send" of the machine m if it exists, or null.
|-
|Machine newEvent(name: String) : Event
 
Machine newEvent(name,id: String) : Event
 
Event newWitness(name, predicate: String): Witness
 
Event newParameter(name: String): Parameter
 
Event newGuard(name, predicate: String):Guard
 
Event newAction(name, predicate: String): Action
 
Machine newVariable(name: String) : Variable
 
Machine newVariable(name,id:String) : Variable
 
Machine newInvariant(name, predicate: String): Invariant
 
Context newConstant(name: String) : Constant
 
Context newSet(name: String): CarrierSet
 
Context newAxiom(name, predicate: String): Axiom
|Create and return new child element within a given element. If the element with '''name''' already exists, then return the existing one. For witnesses, guards, actions, invariants and axioms, a '''predicate''' must be specified. If '''id''' is specified, then add to the cache.
|-
|style=white-space:nowrap|Machine inputOrNewEvent(id,userMessage:String):Event
 
Machine inputOrNewVariable(id,userMessage:String):Variable
|Ask user to enter event/variable, or creates a new one based on user input. If there are no events/variables in the machine or a user clicks Cancel during the first dialog, operations ask user to enter a name for the new element and create it.
Uses the input cache as in other operations.
|-
|Machine addEvent(name:String):Machine
 
Machine addEvent(name,id:String):Machine
 
Event addWitness(name, predicate: String): Event
 
Event addParameter(name: String): Event
 
Event addGuard(name, predicate: String) : Event
 
Event addAction(name, predicate: String) : Event


Machine addVariable(name: String):Machine
Example. If two operations request objects with the same '''id''' then only the first operation would initiate a dialog with user:


Machine addVariable(name,id: String):Machine
//the first operation will cache the event with id="myid"
machine.first();
//the second operation will read the event from cache if called after the first
machine.second();
operation Machine first()
{
    self.inputEvent("myid", "Choose the first event").addGuard("grd1", "stop=false");
}
operation Machine second()
{
    self.inputEvent("myid", "Choose the second event").addGuard("grd2", "q>3");
}


Machine addInvariant(name, predicate: String): Machine
The second operation is also safe to call from elsewhere. In that case the cache would not contain the requested event, and the user would be asked for input.


Context addConstant(name: String): Context
== How to install ==
To use the transformation pattern plugin the following are required:
# Install the Epsilon framework from [http://download.eclipse.org/modeling/gmt/epsilon/updates/ http://download.eclipse.org/modeling/gmt/epsilon/updates/]
# Install the pattern plugin from [http://rodinmodeftview.sourceforge.net/ http://rodinmodeftview.sourceforge.net/]
# Copy file [http://rodinmodeftview.sourceforge.net/patterns/system.eol http://rodinmodeftview.sourceforge.net/patterns/system.eol] to your project folder


Context addSet(name: String): Context
== Samples ==
[http://rodinmodeftview.sourceforge.net/patterns/samples/atomic.ptrn atomic.ptrn]


Context addAxiom(name, predicate: String): Context
[http://rodinmodeftview.sourceforge.net/patterns/samples/extendAll.ptrn extendAll.ptrn]
|Add a child element with the given '''name''' (and '''predicate''' where appropriate). If '''id''' is specified, then add to the cache. Equivalent to '''new*''' operations, the only difference is that '''add*''' operations return the parent object as opposed to the newly created one. This allows structuring multiple creations in a better way:
var m:Machine = ...;
m.addVariable("stop")
  .addInvariant("stop_inv", "fail=TRUE => stop=TRUE")
  .newEvent("stop")
    .addGuard("stop_grd", "error=TRUE")
    .addAction("fail_act", "fail:=TRUE")
    .addAction("stop_act", "stop:=TRUE");
Note how different elements can be added into the model without introducing unnecessary variables.
|-
|Machine addRefines(name:String):Machine


Machine addRefines(machine:Machine):Machine
[http://rodinmodeftview.sourceforge.net/patterns/samples/fmea_generate_abstract.ptrn fmea_generate_abstract.ptrn]


Machine addSees(name:String):Machine
== Good things missing ==
Will be added in next versions:
* Public entry point to run transformation scripts from other plugins programmatically
* Support for predicates as syntax trees. For now they're strings only


Machine addSees(context:Context):Machine
== References ==
|Add '''machine'''/'''context''' to the appropriate list of refined machines / seen contexts. Can be also given by their '''name'''s. Return the container machine.
* [http://www.eclipse.org/gmt/epsilon/ Epsilon site]
* [http://dev.eclipse.org/svnroot/modeling/org.eclipse.gmt.epsilon/trunk/doc/org.eclipse.epsilon.book/EpsilonBook.pdf The Epsilon Book]
* Transformation patterns plugin update site: [http://rodinmodeftview.sourceforge.net/ http://rodinmodeftview.sourceforge.net/]
* Reference for provided operations: [[Transformation_patterns/Reference]]
* Developer's email: [mailto:ilya.lopatkin@ncl.ac.uk ilya.lopatkin@ncl.ac.uk]


|-
|Machine getRefines(): Machine


Machine getSees(): Context
[[Category:Plugin]]
|Return the first machine in the list of refined machines, and context in the list of seen contexts. If the according list is empty, then ask user for a machine/context and add the entered object in the appropriate list. Does not cache.
[[Category:User documentation]]
|}

Latest revision as of 13:08, 1 July 2013

The Transformation patterns plugin allows users to write transformation scripts in EOL (simple object-based imperative language) and easily run them over models within the same project. Editing is done in a simple text editor with syntax coloring. No compilation is needed to run transformations.

The plugin is essentially a wrapper around the Epsilon framework of interoperatable programming languages which are used to operate over the given EMF models (Event-B in our case). The plugin performs the necessary setup for running the transformations, and provides a simple library handling user input of Event-B elements such as machines, events, variables etc.

Syntax example

The following is a simple example showing a syntax of patterns when using the provided library. The pattern implements the atomicity refinement of an abstract event:

import "system.eol";
import "util.eol";

var machine: Machine = inputMachine("target");
var abstractMachine: Machine = machine.getOrInputRefines();
var absEvent: Event = abstractMachine.inputEvent("av", "Abstract event");
var counter: Variable = machine.inputOrNewVariable("cnt", "Counter variable");
var num: Integer = enterInt("Number of events");

for (i:Integer in Sequence{1..num})
{
    machine.newEvent(absEvent.name + "_ref_" + i)
             .addGuard("grd_cnt", counter.name + " = " + i)
             .addAction("act_cnt", counter.name + " := " + counter.name + "+1");
}

machine.newEvent(absEvent.name + "_ref_" + num).addRefines(absEvent);

The plugin is currently tested using EOL only, however it is theoretically possible to import and use files written in other Epsilon languages. The description of syntax and facilities provided by Epsilon can be found in the first few sections of The Epsilon Book. The full list of operations for handling user input and modifying Event-B elements can be found in Transformation_patterns/Reference

How to run a pattern

The files in the project folder with the extension .ptrn are considered to be runnable patterns. They can import .eol files (libraries) and use the operations declared there. Libraries can import other libraries etc. The tool does not give user the ability to run .eol libraries directly (although this can possibly be done through standard Epsilon tools). The suggested scenario of usage is to write the actual transformation code in .eol files as EOL operations, and call them from short .ptrn files which would look like the following:

import "system.eol";
import "my_library.eol";

var m: Machine = inputMachine("target");
m.myTransformationPattern();

All the .ptrn files would appear under the context menu (right click) of the target machine -> Run ->

On the code above, inputMachine("target") returns an EMF model of the machine upon which the pattern is run.

Pattern files are text files. To create/rename/delete them use standard Eclipse wizards and explorer.

User input cache

The plugin contains a cache of objects (EMF elements and strings) entered by a user (or external program). If during the execution a pattern requires to enter an object more than once, then at the first time the user will be asked for input, and the result will be cached. All subsequent requests will return the cached object unless the record about the object is erased.

The cache can be accessed directly from within the scripts, and used as a means to pass the objects between different transformations without them knowing the actual source of input. Thus, the input can be extended to other domain-specific EMF models.

A cache is a map from keys to values where a key consists of a type of object and its identifier, and the value is the actual cached object. The types of objects are handled by the utility library, while the identifiers must be supplied by script developer.

The operations which request input from the user without caching it all start with enter, such as enterString(..), enterEvent(..) etc. The operations starting with input do the following:

  1. they check the cache for existing objects, and return if the object is in the cache
  2. if there is no object in the cache with the given id, then ask user to enter the object
  3. put the entered object into the cache and return to caller

The parameter id is optional. If id=null then an input* operation degenerates into enter*.

Example. If two operations request objects with the same id then only the first operation would initiate a dialog with user:

//the first operation will cache the event with id="myid"
machine.first();
//the second operation will read the event from cache if called after the first
machine.second();

operation Machine first()
{
   self.inputEvent("myid", "Choose the first event").addGuard("grd1", "stop=false");
}

operation Machine second()
{
   self.inputEvent("myid", "Choose the second event").addGuard("grd2", "q>3");
}

The second operation is also safe to call from elsewhere. In that case the cache would not contain the requested event, and the user would be asked for input.

How to install

To use the transformation pattern plugin the following are required:

  1. Install the Epsilon framework from http://download.eclipse.org/modeling/gmt/epsilon/updates/
  2. Install the pattern plugin from http://rodinmodeftview.sourceforge.net/
  3. Copy file http://rodinmodeftview.sourceforge.net/patterns/system.eol to your project folder

Samples

atomic.ptrn

extendAll.ptrn

fmea_generate_abstract.ptrn

Good things missing

Will be added in next versions:

  • Public entry point to run transformation scripts from other plugins programmatically
  • Support for predicates as syntax trees. For now they're strings only

References