Jump to: navigation, search

ACG

ACG (ATL VM Code Generator) is a domain specific language designed to express the compilation of a model transformation into ASM code executable by the ATL Virtual Machine (ATL VM).

Background

Use Case

The ATL language semantics defines operations on models (such as testing condition on an element, creating an element or setting an element feature to a value). These operations are implemented by the ATL VM which executes ASM program on source model(s) to produce the output model(s).

Getting the compiled ASM version of an ATL transformation is done in three stages:

  1. injecting ATL source code to get it into a model based representation (XMI whose structure conforms to the ATL model)
  2. transform this ATL model into an ASM model
  3. extracting the ASM model to get it into a textual representation conforming to its syntax (ASM code).

The second stage is actually done by a transformation in ACG defined in ATL.acg.

This process has been bootstrapped. This means that ACG semantic is defined in ACG. Thus an ACG script is now compiled using ACG to get the corresponding ASM version. This means that the compilation from a transformation model to ATL VM code is done by the ATL VM itself using ACG.acg.

Resulting requirements

The reader is assumed to have full understanding of the ATL VM specification

Exemplification base

As, historically, the ATL VM (and thus ACG) has been created for the ATL language, the example given are excerpts from ATL.acg which can be found on ATL Eclipse CVS

Language

Overall structure

ACG Header

An ACG transformation begins by its own declaration and a section which embodies the transformation description.

Definition:

--ACG Header
acg <Transformation Language Name> startsWith <Start Element Template> {
	<Transformation description items>
}

Example:

acg ATL startsWith Unit {
...
}
Every transformation defines an entry point (with the keyword startsWith). It is a production rule called at the beginning of the transformation. It defines thereby the top level element of the transformation.

Templates

The elements used to express the transformation are the templates. They are the code production rules defined in the context of an element of the transformation language model. They define what code should be generated for this kind of item.

Defintion:

Template
<Context ModelElement> {
	<Produced code definition>
}

or

<Context ModelElement> | <call condition> {
	<Produced code definition>
}

Example:

Helper {
	analyze self.definition.feature 
}
This is a production rule defined from the object Helper which carries out only one operation (analyze). Accordingly, the production rule will be called corresponding to the property feature of the property definition of the current Helper model element

Functions

Functions are used as a programming facility. Like in an object oriented language, functions are assigned to model elements and have access to all properties of that model element. They provide the means to easily request derived information from a model element, like the following example shows.

Definition:

function <Context>::<Function Name>(<Parameters>) =
	<Function Definition>;

Example:

function OclModelElement::encode() = 
'M' + self.model.name + '!' + self.name+';';
This function without parameter is defined on OclModelElement to build a string.

Code production helpers

Code Production Management Statements

If Statement

Clearly, the code generated depends on a given condition.

Defintion:

if(<condition>) {
	<Produced code definition>
}

or

If Then Else Statement
if(<condition>) {
	<Produced code definition>
} else {
	<Produced code definition>
}

Example:

asm Module name self.name {
	...
      if(self.isRefining) {
      	report error 'refining mode not supported by ATL 2006 yet'
      }
	...
}
In this example, an error is raised if the isRefining property of the object Module is true.
For Each statement

For each element (accessible through a roaming variable) of the collection constructed by a given OCL definition, the corresponding code is generated (potentially using the roaming variable).

Defintion:

foreach(<roaming variable name> in <OCL construct of collection>) {
	<Produced code definition>
}

Example:

foreach(p in self.parameters->select(p | p.kind='in')) {
	param p.varName : 'J'
}
This statement is defined in the context of an object having a multi-valued feature named parameters. For each one of these parameters a param instruction is called with arguments:
  • the varName feature of the parameters component in question
  • the string “J”
In a few words, for each object in the parameters feature, it creates a parameter for the current operation with the same name and whose type is “oclAny”.
Only Once Evaluation Statement

The corresponding rules will be evaluated only once. If it is defined inside a Foreach statement, the evaluation will be done in the middle of the loop. This is useful for blocks interweaving.

Defintion:

[ <Produced code definition> ]

Example:

foreach(i in self.iterators) { 
	...
	iterate
	...
	[
		analyze self.body
		store self.result
	]
	}
	enditerate
	...
}
If there is three objects iterator, the result will be as below:
<iterate/>
	<iterate/>
		<iterate/>
			... result of production rule defined for the body object...
			<store arg = “23”/>
		<enditerate/>
	<enditerate/>
<enditerate/>
Analyze statement

Call the matching template for the model element resulting from the OCL evaluation in the current context.

Definition:

analyze <OCL navigation to model element>

Example:

analyze self.inPattern.filter
From within the current object, this will fetch the object pointed by the inPattern reference, and then will follow its filter reference to get the object which will lead in the launch of the appropriate production rule.

Offsets management

The procedural style of ACG (defining templates as units of processing) leaves the replacing of a template call by corresponding code to the compiler. This results in a lack of a priori knowledge of the amount of code generated by an analyze statement and thereby in the difficulty to handle code offsets (needed by some instructions of the ATL VM). To fix this difficulty, ACG provides a mechanism of labels.

Definition:

<label name>:

This way, the ACG compilation replaces any argument of jump instructions referencing a label by the correct offset. Of course, no trace of label is left in the ASM code.

Example:

code IfStat {
	analyze self.condition
	if thn
		analyze self.elseStatements
		goto eoi
	thn:
		analyze self.thenStatements
	eoi:
}
This code starts with an analyze statement followed by a if statement. We can assume that the result of the evaluation of self.condition will lead to instructions pushing their result (a boolean) on the stack. The if statement will ask an evaluation of this variable an will make the execution continue at a point depending on the result. If there is true on the top of the stack, the execution pointer will jump over the instructions produced by the production rules before thn:. Else it will continue on the next line. The same applies for the goto statement with the eoi: label.

Simple code factoring

The let statement allow a programmer not to write again a particular OCL expression when he needs it several times. Instead, he can bind it to a literal. This way, anywhere inside the let statement scope, the compiler will replace this literal by the corresponding OCL expression.

Definition:

Let <literal binding> = <OCL expression> in
<expression using literal binding>

or

Let <literal binding> = <OCL expression> {
	<code using literal binding>
}

Example:

let ep = self.elements
		->select(e | e isa CalledRule)
		->select(e | e.isEntrypoint).first() {
	if(not ep.oclIsUndefined()) {
		getasm
		call 'A.' + ep.name + '():V'
	}
}
The wider brackets define the let statement scope (the code in which ep will be recognized). The code inside uses this binding resulting in a clearer and smarter code.

Code generation units

Templates

Templates can be assigned call condition which allows to define several behaviours for a single type depending on a condition.

When an analyze statement is found, a template defined for the element true type is called. If there exists conditional templates, the conditions are evaluated within the context of the element analysed. As conditions must define a partition of candidate model elements, at most one conditional template can be called. In case that no conditional template is defined or no condition is matched, the default template (the one with no condition call on it) is called.

Functions

Functions can be called on model elements if they are of the type (or a subtype of) of the function context. A function defined on type foo, named bar and with no parameter would be called this way:

Definition:

<template type> {
	<instruction> <OCL navigation leading to a foo element>.bar()
}

Example:

MyObject {
	push self.foo.bar()
}

Operation based instructions

To define an operation in the compilation outcome, ACG provides the following instructions:

operation

Declares a context for the other operation based instructions.

Definition:

operation

context

Sets the type on which the current operation can be applied.

Definition:

context <encoded type>

name

Sets the name of the current operation.

Definition:

name <operation name>

param

Adds a parameter at the current operation.

Definition:

param <parameter name> : <parameter type>

Example:

operation
	context 'A'
	name 'resolveTemp' {
		param 'value' : 'J'
		param 'name' : 'S'

            ...
	}
This is the definition of an operation called resolveTemp on the ATL context module with two parameters (an Object value and a String name).

variable

Allocates a slot in the operation. The scope of the the variable is defined by the brackets. It also calls a store instruction at its place (the result is an immediate initialization of the variable with the value on the top of the stack (popped).

Definition:

variable <identifier> name <variable name> {
	<Produced code definition for which the variable is defined>
}

The variable instruction needs an identifier as the name is not restrictive enough. Indeed, several variable with the same name can coexists in the same code scope.

Example:

variable self named 'e' {
	getasm
	load self
	call 'A.__resolve__(J):J'
	call 'QJ.including(J):QJ'
	}
The variable definition (line 1) allows the load call (line 3).

VM Instructions

There exists one statement per ATL VM instruction. Each of these statements have the same name than the instruction and, of course, leads to the production of this instruction in the target model.

An instruction is followed by its operand as defined in the VM specification. For some arguments defined is ACG, the compiler performs a rewriting (or more exactly an interpretation -read transformation) given that that ASM code is not convenient to write by hand and that ACG could help by making things more "human compliant". It is the case for :

  • the load instruction: Its argument is a variable reference. In ACG, this reference is:
    • their identifier for common variables
    • their name for parameters.
→ It is rewritten in corresponding slot number.
  • the if instruction: Its argument is a code offset.
→ It is replaced by the absolute position of where the referred label should have been if it had not been removed.
  • the goto instruction
→ Same rewritting

Use

Once the ACG file is defined for the transformation language model, it has to be compiled so that every transformations written is this language could be compiled into ASM.

This is done thanks to an AMMA tool set including several Ant scripts. Those scripts are intended to provide all the facilities to build a Domain Specific Language (editor, megamodel, concrete and abstract syntax and a compiler if the semantics relies on the ATL VM).