6.3.5 Iterating over lines of text properties, strings, variables and single
commands
The do and
dowhile loops can also be used for
iterating over the lines of a multi-line string or text: a property of data type
Text, a MERL string, a MERL variable, or the output of a single command like
id or a subgenerator call.
For a Text property, the currently selected element should
be the object, role, relationship of graph which has the text property, and the
operator for the do loop should be the
local name of the text property. The loop is then executed as many times as
there are lines in that text property, including an empty last line if the text
ends in a line break. Within the loop,
id outputs the current line (with no
line break character).
do :property
{ id }
This is particularly useful for translating line
breaks in a property into character sequences which represent a new line or
paragraph in a particular formatting language, such as RTF or HTML. The example
below adds a <BR> tag in HTML; the
newline is simply for clearer
formatting of the HTML source.
do :Documentation
{ id '<BR>' newline }
It can also be used for
programming languages where a comment is preceded by a certain character
sequence, and is not allowed to extend over more than one line. The first
example below shows how to make sure each line of a comment is preceded by the
comment sequence, and the second example strings all the lines together into one
long line, effectively replacing each carriage return with a space.
do :Documentation
{ '// ' id newline }
'// '
dowhile :Documentation
{ id ' ' }
The syntax for iterating over the lines of
variables is similar:
do $myVar
{ id }
This can be useful in many situations, e.g. to
execute a block multiple times with selected values, or to iterate over the
lines of a text file read into a variable.
To iterate over literal strings, you can have the string
spread over multiple lines:
do 'first
second
third'
{ id }
In most cases, you can add a translator as a
suffix to the do loop argument, e.g. to translate some separator character into
a newline:
to '%dotToNL
. \
' endto
do 'first.second.third'%dotToNL
{ id }
When iterating over the output of simple commands
like id, using a translator is almost always necessary, as such outputs will
generally only contain one line. The translator can split them into multiple
lines by translating some separator character to newlines.
Iterating over texts as collections of objects
Output from model elements includes Live Code links to those
elements. Such text output, returned from a sub-generator or stored in a
variable, thus still has links to the original objects (as long as its
formatting has not been lost, e.g. by using a translator). While we are
iterating over the lines of a Text, if we execute a command that would navigate
as if we were in a model element, e.g.
do ~() {...}, MERL would
usually give an error like ‘Do constructs are not allowed for
Texts’. However, if the line contains a Live Code link, MERL will use the
first Live Code link on the line as a model element, and navigate on from there.
We can thus use a variable as a collection of objects (or
other model elements), one per line. When iterating over the variable, we can
navigate on from each object, read its properties etc.
@Buttons = __(dowhile .Button { id newline })
do @Buttons
{ 'Button ' :Button name; ' is used in State(s) '
dowhile ~Event~From.() { id ', ' }
newline
}
Note that, as we are still primarily iterating over
Text lines, considered as objects as a concession, commands like
type will answer ‘Text’
rather than the name of the object type. To move totally to the context of the
object, with no ambiguity with Text, use do
contents within this do
loop:
@Buttons = __(dowhile .Button { id newline })
do @Buttons
{ do contents
{ type ' ' id ' is used in State(s) '
dowhile ~Event~From.() { id ', ' }
newline
}
}