Show in Frame No Frame
Up Previous Next Title Page Index Contents Search

5.1.1 Creating a simple generator

Now it is time to write our first generator. Assuming that we are creating a new generator for the WatchApplication Graph type in our demo repository, we can open the editor by opening the ‘Digital Watch’ project, selecting ‘Simple’ (or any other Graph of type WatchApplication) in the Graphs list and choosing Edit Generators from the popup menu. This will open the Generator Editor as shown below in Figure 5–1:

Empty Generator Editor

Figure 5–1. Generator Editor.

(If the contents of the top-left list look significantly different, perhaps you opened it on a different Graph type. You can change Graph types with Generator | Change Graph Type... or the corresponding toolbar button.)

To create a new empty generator definition, press the New button on the toolbar (or Ctrl+N or Generator | New... from the menu bar), and enter Connections() as a name for the generator when prompted. You will now see the new generator ready for editing at the bottom of the Generator Editor window (as in Figure 5–2).

Generator Editor with new generator

Figure 5–2. Generator Editor with new generator definition.

Let us now enter the following MERL code into the editing area (without the line numbers, they are here just to provide reference points) and press Save in the toolbar (or Ctrl+S or Generator | Save from the menu bar):
01  Connections()
02  foreach .State [Watch]
03  {  'State : '
04     :State name;
05     newline
06     'Connects to: '
07     newline
08     do ~From>()~To.State [Watch]
09     {  ' 	'
10        :State name;
11        newline
12     }
13     newline
14  }
This short piece of code illustrates the basic features of a generator definition. The name of the generator is given in the header, either just as the name followed by (), as above, or with the older Report 'name' ... EndReport structure around the generator code (for more information, see Section 6.2.1). Since we edit one generator at a time, rather than having many in the same text, by convention we do not start a new level of indentation after the header.

Generators operating on a graph, like our example here, can access the graph’s member elements like objects, relationships, roles and ports with a top-level foreach loop. In the above example (lines 2 – 14), we loop through the instances of the ‘State [Watch]’ object type and retrieve specific information from them to output. For each state, we want to output its name, followed by a list of the states it is connected to. After the foreach command you will notice the use of the ‘.’ prefix character before the name ‘State [Watch]’. The prefix characters ‘.>~#:’ make the distinction between Objects, Relationships, Roles, Ports and Properties. For example, in line 2 the ‘.’ preceding the type name tells the generator to fetch objects matching that type name.

In line 3 we enter the loop: lines 3 – 14 will be executed once for each ‘State [Watch]’ in the graph. Outside the loop we can say that we are ‘in’ the graph; within the loop we can say that we are ‘in’ a ‘State [Watch]’ object.

In lines 3 – 5 we first output the string ‘State : ’, then the value of the ‘State name’ property of the current ‘State [Watch]’ object and then complete the output with a line break. Again, please note the ‘:’ prefix before ‘State name’ that denotes that a property will follow. Lines 6 – 7 simply output the string ‘Connects to: ’ followed by a line break.

As you can see, none of these output commands need to specify where the output is going: it is going to the current output stream which, by default, just goes to a window, whose result will be shown to the user at the end of the generation. It is also possible to direct the output elsewhere, e.g. to a file or to build up a variable. This will be discussed further in the Primer in Section 5.2.4, and the relevant commands are documented in Section 6.4.1 and 6.5.2.

Lines 8 – 12 contain an important example of a fundamental feature of MERL: navigation through bindings. In this case, we want to find out all other ‘State [Watch]’ objects the current object is connected to, so we use the do command to loop through all of them. After the do command you will see the following token:
~From>()~To.State [Watch]
Though this may look a bit cryptic at first glance, it just says to find all ‘From’ roles for the current object first, then follow them through the relationship in that binding to the respective ‘To’ roles and thence to the ‘State [Watch]’ objects connected to these roles. The ‘~’ prefixes denote the roles and the ‘>’ prefix refers to the relationship. The only extra trick here is that – unlike previously where we used exact type names to access design elements – we now use the wildcard () token after ‘>’ to access any kind of relationship that is bound to the ‘From’ roles.

Within the body of this inner loop we thus iterate over all State objects that are reachable in one From-To ‘hop’ from the outer loop’s State. The content of the inner loop is pretty clear: output some spaces, then the name of the accessed object and a line break. Note how the context has changed within the loop: the current element is now the target State object, whereas in the outer loop it is the source State object. The output of :State Name; in line 10 is thus different from the output of :State Name; in line 4.

After the inner do loop the generator definition is reasonably trivial, just outputting a line break before the main loop is run again for the next State. Note that we are now back in the outer loop, so :State name; here would again refer to the same element as in line 4.

Before we proceed with the execution of our example generator, it is worth noting a few things about the MERL keywords and generator names. As a general rule, generator keywords should always be preceded by white space (space, tab, or carriage return), and for backwards compatibility they can optionally be followed by a semicolon, ‘;’. Type names that contain spaces, or that are followed on the same line by another command, should be followed by a semicolon. This is not needed in the chaining of type names, where the next type prefix ‘.>~#:’ is sufficient to terminate the previous type name, e.g. ‘.State [Watch]~To’.

Generators whose names begin with '_' are hidden from the list shown to the user when choosing Generate... elsewhere than the Generator Editor (e.g. from a Diagram Editor). Names like this should thus be used for generators that are intended only for use as subgenerators, e.g. if they assume that the generation output is already going to a file, or that something other than a graph is already on the generator stack. If necessary, a user can still see and even run these generators elsewhere by holding down Shift while choosing Generate....

Show in Frame No Frame
Up Previous Next Title Page Index Contents Search