Up Previous Next Title Page Index Contents

5.3.8 Hints and tips

In most cases writing a generator definition is a reasonably straightforward task, but it is still sometimes good to be aware of certain existing solutions or techniques for advanced use of MERL. In this section we give some hints and tips related to MERL.

Context

While writing generator definitions, it is important to keep in mind the context you are operating on. For example, when diving through an explosion link, the context changes from the original graph to that of the explosion graph, which may be of a different graph type. This means that the parts of the generator definition that handle the explosion graph must be written according to that graph’s metamodel. Similarly, when accessing properties, you may encounter a situation where a non-property is used as a property, and therefore you have to be aware of what kind of information you can retrieve from it.

Loop-backs

Navigating via a relationship while navigating through bindings may sometimes produce unexpected results. Consider the following expression when navigating from an object:
do >Transition.()
What we probably wanted to do with this was to access all objects that the current object is connected to via a ‘Transition’ relationship. However, navigating to the relationship leaves us ‘in’ that relationship, and when we try to navigate from there to objects, we can get to not only the object we wanted, but also back to the object we started from.

The simplest solution is to specify the role types, effectively making the relationship directed:
do ~From>Transition~To.()
If those role types are not used with other relationship types, it is also be possible to omit the relationship entirely, and navigate from the From role to all To roles contained in the same binding:
do ~From~To.()
Even if the role types at either end of this binding are of the same type, this is still possible. Navigating in one step from a role to roles, there is no way to get from the role back to itself. For instance, if there is an Association relationship with just Involved roles:
do ~Involved~Involved.()
If there is a need to check the type of the relationship in this kind of construction, remember that we cannot insert >Association between the roles. That would bring us back to the same problem we started with, i.e. from the Association we could get back to the same Involved role we came from. Instead, we can check the relationship type with an if statement:
do ~Involved~Involved
{  if >Association then 
      do .()
      {  ... }
   endif
}

Subgenerators and modularization

Subgenerators provide a useful mechanism for modularization of generator definitions. However, organizing and calling subgenerators can get fairly complex without a good generator hierarchy. A good technique to support generator hierarchies is to use the name of a type as the name for the generator definition related to that type. This enables you to call subgenerators using the type keyword to define the name of the executed subgenerator:
subreport type run
The obvious benefit of this approach is that it saves the metamodeler from writing multiple if clauses when deciding which subgenerator to run when there are several available. This solution is also extendable: if a new type becomes available as one choice among others, it can be handled just by adding a generator of the same name for it.

For practical examples of using subgenerators, please see the code generator for the Watch example. In general, it is good to use a prefix before the type name, e.g. to distinguish between different programming languages. This also allows the Generator Editor Hierarchy view to tell that the subreport call is to one of a limited number of generators, rather than apparently to any generator.
subreport '_Java_' type run

Checking instance uniqueness

To check that a given object type only has one instance per graph, you would normally use an Occurrence Constraint in the graph type. You could also use the following generator definition:
Report 'Check XYZ instances'
subreport '_translators' run
$occurrences='0'
foreach .XYZ { $occurrences++%null }
if $occurrences > '1' num then
   'Object type XYZ found '
   $occurrences
   ' times in this graph:' 
   foreach .XYZ 
   {  id newline 
   } 
endif
endreport
When the generator is run on a graph containing more than one occurrence of XYZ, an output window will report the number and names of XYZ objects. Each object name can be double-clicked to take the user to the object. If no duplicates are found, no output window is opened.

Autobuild and external processing

If you need to perform actions after generation, you can use the generator language to export the data you need to a file, and then have it call another program to operate on that data. For instance, the Autobuild generator in the Watch example generates the code and then calls a compiler, and the Export to Word generator starts Word and calls a macro in the Word template supplied.

Parameterizing generators

If you have state that should persist between generator runs, e.g. to parameterize generators, a good solution is to have that information as properties in the model, e.g. of the top level graph from which the generator is run. To separate these parameters from the top level graph, you can store them in a separate graph type which just holds the parameters and a link to the top level graph.

If the parameters are temporary, you could use prompt...ask. If the parameters are per user, and should apply to all generation for that user, you could use text files in the working directory, read with filename...read and assigned to variables as necessary (a regex translator could easily separate the left and right parts of a line in .ini file format).

Up Previous Next Title Page Index Contents