MAML Technical Manual
[model/observer] [agent]
[var/sub] [plan/schedule]
[extendPlan/extendSchedule] [create/createBegin/createEnd]
[probe/buildProbes] [init] [uses]
Programming in MAML
Regarding many programming techniques, MAML is close to Swarm,
Objective-C, and C. For the moment any MAML code can
contain Objective-C and Swarm sequences. In its current state,
starting with a given MAML code, the (xmc) compiler generates
Swarm code (v1.02 or v1.3) which, in order to build
up an executable file, can be compiled again using appropriate compilers
(e.g. gcc). The code generation process, programming techniques,
and underlying Swarm structures will be presented in the following
text. The next section may require the knowledge of MAML keywords
and syntax, and some Swarm programming skills.
In its code generation process the MAML compiler generates a
couple of Objective-C classes, methods, and variables. Many of these,
which are generated with the `_MAML_' prefix, are for internal use
only. There is no double-checking performed by the compiler, thus the names
used in MAML code may conflict with internal names in Swarm
code. To avoid this please do not use variable names and subroutines in
your MAML code beginning with `_MAML_'. The internal structure
of the generated Swarm code (the names with `_MAML_' prefix)
won't be detailed here, as we will focus on structures generated by the
compiler without the `_MAML_' prefix, which are accessible in MAML
too.
The compiler in its current state supports two type of code generation.
It can generate code for either Swarm v1.02 or v1.3.
The difference between the two code generation alghoritms is not significant.
All the examples without any specification are valid for both fo them.
[Back to the Table of Contents]
Model and Observer
From a MAML model and the appropriate observer, a single Objective-C
class is generated which inherits from `GUISwarm' or `Swarm'
(predefined Swarm classes), as the observer was a `gui' or
a `batch' observer. By default, the `model class' has a `model'
instance variable. This can be used in MAML code to access the model
variables and subroutines. At Swarm level there is the maxTimeSteps
integer variable which is used to specify at what time the model stops
the execution of its `life cycle'. There are also two predefined schedules:
stopSchedule and dispSchedule. The latter won't be generated
in batch mode. The stopSchedule and the dispSchedule
is used by the system to `schedule' both its execution loop and the actions
taken by the graphical user interface. The MAML compiler generates
the probe, stop, and go subroutines. By calling the
probe subroutine, the probe windows will be brought up, whereas
the stop subroutine stops the model's execution, and the go
subroutine starts it again. These subroutines cannot be changed in MAML
code, as they are not inherited subroutines.
The above mentioned variables are defined on Swarm level as:
@public int maxTimeSteps;
@protected id stopSchedule, dispSchedule;
and the subroutines as:
- (id) go ;
- (id) stop ;
- (void) probe ;
The model structures on Swarm level are created in files which take
the name of the MAML model with the `.m' and `.h'
extensions. For example if the model was called `MyModel', a `MyModel.h'
and a `MyModel.m' file will be generated.
[Back to the Table of Contents]
Agents
For MAML agents two Objective-C classes are generated. The
first, taking the agent's name with a `MAML_' prefix (e.g. MAML_Agent),
is a low-level object which incorporates some predefined functionalities.
The second, taking the agents name (e.g. Agent), implements the
MAML agent internal structures and inherits the first one. The `MAML_Agent'
inherits the agent super class specified in the MAML code. If no
agent super class was specified the `MAML_Agent' inherits the `SwarmObject'
predefined Swarm class.
The `MAML_Agent' class adds the following subroutines to its
super class:
+ (id) createBegin: (id) zone;
+ (id) createBegin;
+ (id) create;
This and many other inherited subroutines (see Swarm Reference Manual)
can be accessed or even changed in MAML code, too.
The `Agent' class adds:
This subroutine brings up the probe windows.
The agent structures are created in files taking the agent's name, and
the agent's name with the `MAML_' prefix and appropriate `.m'
and `.h' extensions. For example, if in MAML code an agent
was defined with `MyAgent' name, `MAML_MyAgent.h', `MAML_MyAgent.m',
`MyAgent.h' and `MyAgent.m' files are generated.
[Back to the Table of Contents]
Variables and Subroutines
The inline MAML variables and subroutines are copied in the
Swarm code as they are. The complex variables (i.e., variables defined
with the box operator or agent variables) will be preceded by `*'
signs in their Swarm level declaration which in C means that
they become pointers to structures defined by the corresponding variable
types. Each box operator preceding the MAML type will be
changed to a `*' preceding each variable name in the declaration
list. In the case of agent variables, a supplementary `*' is introduced.
The non-static variables are declared as instance variables in the
corresponding model or agent class, while the static ones are declared
as global variables. The public static variables are also
exported to other files (i.e., a C `extern' directive is introduced
in the appropriate `.h' file). The MAML subroutine headlines
will change as the return and parameter types are changed, as described
above for variable declaration. The subroutine bodies will be changed as
the MAML constructs are `introduced'. How the MAML constructs
are `introduced' will be presented in the next subsections.
[Back to the Table of Contents]
Plans and Schedules
The plans and schedules are generated in the model and agent `buildActions'
methods defined as:
- (void) _MAML_buildActions_
and respectively
+ (void) _MAML_buildActions_: (id) zone
An empty plan from its MAML form:
is generated in Swarm v1.02 as:
{
planA = [ActionGroup createBegin: _MAML_zone_];
[planA setDefaultOrder: Sequential];
planA = [planA createEnd];
}
In Swarm v1.3 code generation the line containing the 'setDefaultOrder'
is not generated. This is true for any of the following examples.
By default any plan is sequential (see: seq MAML keyword)
but it can also be set to randomized (see: rnd) or concurrent (see:
con). In MAML this looks like:
@planDef planA rnd {}
@planDef planB con {}
which is compiled as:
{
planA = [ActionGroup createBegin: _MAML_zone_];
[planA setDefaultOrder: Randomized];
planA = [planA createEnd];
}
{
planB = [ActionGroup createBegin: _MAML_zone_];
[planB setDefaultOrder: Concurrent];
planB = [planB createEnd];
}
The plan in the following simple example:
@planDef planA {
@to agent mehod;
@forEach agentList method;
@plan planB;
}
will be translated as:
{
planA = [ActionGroup createBegin: _MAML_zone_];
[planA setDefaultOrder: Sequential];
planA = [planA createEnd];
[planA createActionTo: agent message: M(mehod)];
[planA createActionForEach: agentList message:
M(method)];
[planA createAction: planB];
}
A subplan:
@planDef planA {
@planDef planB {}
}
has its Swarm equivalent in:
{
planB = [ActionGroup createBegin: _MAML_zone_];
[planB setDefaultOrder: Sequential];
planB = [planB createEnd];
}
{
planA = [ActionGroup createBegin: _MAML_zone_];
[planA setDefaultOrder: Sequential];
planA = [planA createEnd];
[planA createAction: planB];
}
The empty schedule:
in Swarm looks like:
{
scheduleA = [Schedule createBegin: _MAML_zone_];
scheduleA = [scheduleA createEnd];
}
A relative schedule:
@schedule acheduleA relative {}
in Swarm will be:
{
acheduleA = [Schedule createBegin: _MAML_zone_];
[acheduleA setRelativeTime: YES];
acheduleA = [acheduleA createEnd];
}
A cyclic schedule:
@schedule acheduleA cyclic (10) {}
in Swarm:
{
acheduleA = [Schedule createBegin: _MAML_zone_];
[acheduleA setRepeatInterval: (10)];
acheduleA = [acheduleA createEnd];
}
A more complex example:
@schedule scheduleA {
0: @to agent method;
1: @forEach agentList method;
2: @plan planA;
3: @planDef planB {}
}
in Swarm has the form of:
{
planB = [ActionGroup createBegin: _MAML_zone_];
[planB setDefaultOrder: Sequential];
planB = [planB createEnd];
}
{
scheduleA = [Schedule createBegin: _MAML_zone_];
scheduleA = [scheduleA createEnd];
[scheduleA at: (0) createActionTo: agent message:
M(method)];
[scheduleA at: (1) createActionForEach: agentList
message: M(method)];
[scheduleA at: (2) createAction: planA];
[scheduleA at: (3) createAction: planB];
}
The schedules (any model and agent schedule) are activated in the model's
`activateIn' method defined as:
- (id) _MAML_activateIn_: (id) swarmContex
For a schedule with name `scheduleA', the following line will appear
in the `activateIn' method:
[scheduleA activateIn: self];
The extendPlan/addToPlan and extendSchedule/addToSchedule
structures on Swarm level are generated in a way as the above presented
planDef and schedule structures. The only difference is that
the `create part' is not generated.
This example:
@extendPlan planA {
@to agent method;
@forEach agentList method;
@plan planB;
}
is compiled as:
{
[planA createActionTo: agent message: M(method)];
[planA createActionForEach: agentList message:
M(method)];
[planA createAction: planB];
}
For an addToPlan the same code is generated, but is introduced in
the model's buildObjects subroutine defined as:
- (void) _MAML_initModel_
or it is introduced in the corresponding model or agent subroutine in which
it was `called' in the MAML code. If the extendPlan contains a new
plan definition:
@extendPlan planA {
@planDef planC {}
}
the generated code looks like:
In an addToPlan structure a planDef is prohibited. This is
controlled by the compiler, and thus there is no code generation problem
for this.
An extendSchedule structure:
@extendSchedule schA {
1: @to agent method;
3: @forEach agentList method;
2: @plan planB;
}
is compiled as:
{
[schA at: (1) createActionTo: agent message:
M(method)];
[schA at: (3) createActionForEach: agentList
message: M(method)];
[schA at: (2) createAction: planB];
}
An addToSchedule is compiled exactly the same way except that it
will be generated in the internal buildObjects subroutine or in
the appropriate subroutine defined in the MAML code.
If there is a new plan definition inside an extendSchedule:
@schedule schA {}
@extendSchedule schA {
4: @planDef planC {}
}
the compiled code will be:
{
planC = [ActionGroup createBegin: _MAML_zone_];
[planC setDefaultOrder: Sequential];
planC = [planC createEnd];
}
{
schA = [Schedule createBegin: _MAML_zone_];
schA = [schA createEnd];
}
{
[schA at: (4) createAction: planC];
}
As it can be seen from the above examples, the plans are always generated
first, then followed by the schedules. The plans and schedules are generated
in the order in which they were defined.
In the above examples the `_MAML_zone_' variable is set in the
following form:
_MAML_zone_=[self getZone];
This means that any plan or schedule is created in the same zone in which
the model or the agents are created.
[Back to the Table of Contents]
Create Structures
The create structures are generated in subroutines or initialization codes.
A simple MAML `create' directive which creates an array of
characters, such as:
@create [10:i] char n { n[i]=' '; }
is generated in Swarm in the form of:
{
int _MAML_i1_;
{
n=(char*)malloc((10)*sizeof(char));
}
for(_MAML_i1_=0;_MAML_i1_<(10);_MAML_i1_++)
{
int i;
i=_MAML_i1_;
{
n[i]=' ';
}
}
}
The creation of an agent:
@create Guy badguy { [badguy setName : "Joe"]; }
in Swarm looks like:
{
{
badguy=[Guy createBegin: _MAML_zone_];
}
{
{
[badguy setName : "Joe"];
}
}
{
badguy=[badguy createEnd];
}
}
From the MAML line which `creates' an array of arrays of arrays
for n and m CHAR agent variables and call the appropriate
`set' subroutine, such as:
@create [10:i][20:j][30:k] CHAR n, m { [n[i][j][k] set: ' ']; }
the following lines are generated:
{
int _MAML_i1_, _MAML_i2_, _MAML_i3_;
{
n=(CHAR****)malloc((10)*sizeof(CHAR***));
m=(CHAR****)malloc((10)*sizeof(CHAR***));
}
for(_MAML_i1_=0;_MAML_i1_<(10);_MAML_i1_++)
{
n[_MAML_i1_]=(CHAR***)malloc((20)*sizeof(CHAR**));
m[_MAML_i1_]=(CHAR***)malloc((20)*sizeof(CHAR**));
}
for(_MAML_i1_=0;_MAML_i1_<(10);_MAML_i1_++)
for(_MAML_i2_=0;_MAML_i2_<(20);_MAML_i2_++)
{
n[_MAML_i1_][_MAML_i2_]=(CHAR**)malloc((30)*sizeof(CHAR*));
m[_MAML_i1_][_MAML_i2_]=(CHAR**)malloc((30)*sizeof(CHAR*));
}
for(_MAML_i1_=0;_MAML_i1_<(10);_MAML_i1_++)
for(_MAML_i2_=0;_MAML_i2_<(20);_MAML_i2_++)
for(_MAML_i3_=0;_MAML_i3_<(30);_MAML_i3_++)
{
n[_MAML_i1_][_MAML_i2_][_MAML_i3_]=
[CHAR
createBegin: _MAML_zone_];
m[_MAML_i1_][_MAML_i2_][_MAML_i3_]=
[CHAR
createBegin: _MAML_zone_];
}
for(_MAML_i1_=0;_MAML_i1_<(10);_MAML_i1_++)
for(_MAML_i2_=0;_MAML_i2_<(20);_MAML_i2_++)
for(_MAML_i3_=0;_MAML_i3_<(30);_MAML_i3_++)
{
int i, j, k;
i=_MAML_i1_; j=_MAML_i2_; k=_MAML_i3_;
{
[n[i][j][k]
set: ' '];
}
}
for(_MAML_i1_=0;_MAML_i1_<(10);_MAML_i1_++)
for(_MAML_i2_=0;_MAML_i2_<(20);_MAML_i2_++)
for(_MAML_i3_=0;_MAML_i3_<(30);_MAML_i3_++)
{
n[_MAML_i1_][_MAML_i2_][_MAML_i3_]=
[n[_MAML_i1_][_MAML_i2_][_MAML_i3_]
createEnd];
m[_MAML_i1_][_MAML_i2_][_MAML_i3_]=
[m[_MAML_i1_][_MAML_i2_][_MAML_i3_]
createEnd];
}
}
This example shows how much easier it is to implement the creation phase
in MAML.
The create directives can be split in createBegin/createEnd
parts. The following lines:
@createBegin [3] Guy goodguy;
@createBegin Guy myfriend;
@createEnd Guy myfriend;
@createEnd [3] Guy goodguy;
are translated as:
{
int _MAML_i1_;
{
goodguy=(Guy**)malloc((3)*sizeof(Guy*));
}
for(_MAML_i1_=0;_MAML_i1_<(3);_MAML_i1_++)
{
goodguy[_MAML_i1_]=[Guy createBegin:
_MAML_zone_];
}
}
{
{
myfriend=[Guy createBegin: _MAML_zone_];
}
}
{
{
myfriend=[myfriend createEnd];
}
}
{
int _MAML_i1_;
for(_MAML_i1_=0;_MAML_i1_<(3);_MAML_i1_++)
{
goodguy[_MAML_i1_]=[goodguy[_MAML_i1_]
createEnd];
}
}
There is no specific checking performed on the order in which one `calls'
the createBegin/createEnd structures but, there is an `unwritten'
rule of beginning with a createBegin and ending with a createEnd
for any create directive.
The `_MAML_zone_' variable in any subroutine and initialization
routine will take the appropriate value. In `+' Swarm subroutines
it will take the globalZone value (see Swarm Reference Manual)
and in `-' Swarm routines it will take the zone value, in
which the model or agent itself was created, by calling [self getZone].
[Back to the Table of Contents]
Probes
From a line in a MAML observer which extends agent `A' and
defines probes for agent `A' and the model `M':
@extendAgent A { @probe: var "i", sub "look"; }
@probe: var "x", sub "skip";
the following model `M' method is generated in Swarm:
- (void) _MAML_buildProbeMap_ {
id _MAML_zone_; _MAML_zone_=[self getZone];
{
[(MAML_ProbeLibrary *)probeLibrary removeProbeMapFor:
[M class]];
{
ProbeMap * probeMap = [EmptyProbeMap
createBegin: _MAML_zone_];
[probeMap setProbedClass: [M class]];
probeMap = [probeMap createEnd];
[probeMap addProbe: [probeLibrary
getProbeForVariable: "x" inClass: [M class]]];
[probeMap addProbe: [probeLibrary
getProbeForMessage: "skip" inClass: [M class]]];
[probeLibrary setProbeMap: probeMap
For: [M class]];
}
}
}
and in the agent `A':
+ (void) _MAML_buildProbeMap_: (id) zone {
id _MAML_zone_; _MAML_zone_=zone;
{
[(MAML_ProbeLibrary *)probeLibrary removeProbeMapFor:
[A class]];
{
ProbeMap * probeMap = [EmptyProbeMap
createBegin: _MAML_zone_];
[probeMap setProbedClass: [A class]];
probeMap = [probeMap createEnd];
[probeMap addProbe: [probeLibrary
getProbeForVariable: "i" inClass: [A class]]];
[probeMap addProbe: [probeLibrary
getProbeForMessage: "look" inClass: [A class]]];
[probeLibrary setProbeMap: probeMap
For: [A class]];
}
}
}
To build up the probes, you have to `call' the buildProbes construct
in the observer's init subroutine:
which is translated as the buildProbe subroutines are called for
the model and any agent:
[model _MAML_buildProbeMap_];
[A _MAML_buildProbeMap_: _MAML_zone_];
For calling the probe windows, you can use the probe subroutine
defined as:
- (void) probe {
[probeDisplayManager createProbeDisplayFor: self];
}
This can be called in any subroutine or initialization code:
[model probe];
[agent probe];
In the above example the `agent' name denotes an agent variable.
[Back to the Table of Contents]
Init
The model and observer initialization codes are generated in the already
mentioned model buildObjects subroutine. The model initialization
code is generated where the initModel directive is placed:
The buildObjects subroutine also calls the inherited Swam
or GUISwarm buildObject subroutine:
In the model one can also call buildProbes or create directives
as was already mentioned.
[Back to the Table of Contents]
Uses
From the MAML line of the form:
@uses <stdio.h>, <stdlib.h>;
the following Swarm code is generated
#import <stdio.h>
#import <stdlib.h>
These lines are inserted in the corresponding `.h' file. For example,
if the MAML line was used in an agent called `MyAgent', the
Swarm lines are introduced at the beginning of the `MyAgent.h'
file.
[Back to the Table of Contents]
The Default MAML Library
The default MAML library in its current version is generated by
the compiler any time a new application is created. The `MAML.h',
`MAML_ProbeLibrary.h' and `MAML_ProbeLibrary.m' files are
created. In the `MAML.h' file the string and boolean
MAML types are defined:
typedef char (string)[256];
typedef enum {false, true} (boolean);
In the `MAML_ProbeLibrary.h' and `MAML_ProbeLibrary.m' files
the MAML_ProbeLibrary class is created which inherits from
the Swarm ProbeLibrary and adds the removeProbeMapFor
subroutine:
- removeProbeMapFor: (Class) aClass {
if ([classMap at: aClass] != nil) [classMap removeKey:
aClass];
return self;
}
This subroutine was already used in examples presented in Probes
section. A more complex MAML library will be created later. For
the moment the Swarm, Objective-C and C tools in MAML
programming techniques are also regarded as part of the MAML library
later this will be replaced with genuine MAML tools.
The `MAML.h' imports the following C standard library
tools:
#import <stdio.h>
#import <stdlib.h>
#import <signal.h>
#import <string.h>
#import <ctype.h>
The Swarm v1.3 code generation in addition imports in `MAML.h'
the Swarm random library's header file:
The following examples are valid for Swarm v1.02 code generation.
In Swarm v1.3 code generation the only difference is that 'swarmobject'
changed to 'objectbase' and in case of 'gui' observer `simtools'
changed to `simtoolsgui'.
The `MAML_ProbeLibrary.h' imports Swarm library tools
in the following files:
#import <swarmobject/ProbeLibrary.h>
#import <collections.h>
The model header file (e.g., `MyModel.h') imports:
and (for a gui observer) the:
#import <simtools/GUISwarm.h>
or (for a batch observer) the:
#import <swarmobject/Swarm.h>
The agent header file (e.g. `MyAgent.h') imports:
#import <swarmobject/SwarmObject.h>
Any programming tool defined in these files can be used in MAML
programming. For details please contact the appropriate Swarm, Objective-C
and C Reference Manuals.
[Back to the Table of Contents]
The `main.m' and the
`Makefile' files
For any MAML code a `main.m' and a `Makefile' file
is created.
The main program of any generated application looks like:
int main(int argc, char ** argv) {
signal(SIGTERM,&_MAML_sighandler_);
initSwarm(argc, argv);
model = [MyModel createBegin: globalZone];
[model _MAML_initModel_];
model->maxTimeSteps = -1;
model = [model createEnd];
[model _MAML_buildObjects_];
[model _MAML_buildActions_];
[model _MAML_activateIn_: nil];
[model go]; return 0;
}
A special signal handler is installed for the SIGTERM signal which
if is called stops the execution of the model. The Swarm system
is started, the model and the agents are created. The _MAML_initModel
subroutine calls special internal agent subroutines defined for an agent
`A' as:
+ (void) _MAML_initAgent_: (id) zone {
groupOfA = [List create: zone];
}
This subroutine creates the `groupOfA' agent list which will contain
all agents created within the `A' agent class. The maxTimeSteps
is set to -1 which means that by default there is now upper time
limit for the execution, this may be redefined in the MAML model
initialization code to any appropriate value. The buildObjects,
buildActions and activateIn and finally the go subroutine
is called. This later one launches the execution of the application which
will stop either reaching the maxTimeSteps number of life cycles
or forced by the user.
The `Makefile' was created for Linux systems but also
works with the Swarm NT version. It includes the Swarm `Makefile.appl'
and defines the rules for creating the application. The default name for
the application is 'run'. The `Makefile' adds the Swarm
space library and a special `it' rule which after creating the executable
deletes the `.o' object files. Any application can be compiled and
started on Swarm level using the following command:
In older versions of the compiler the default application name (and thus
the name of the generated executable) was the observer's name, but the
`it' make rule renamed the executable file to `run', and
thus the above command had the same effect. This was changed because of
a problem with the Swarm NT version in which the generated executable
got a `.exe' extension (e.g., run.exe).
[Back to the Table of Contents]
[model/observer] [agent]
[var/sub] [plan/schedule]
[extendPlan/extendSchedule] [create/createBegin/createEnd]
[probe/buildProbes] [init] [uses]
|