5.3.6 String and number commands
The commands in this category are used to store temporary
information into variables
, translate strings and
evaluate mathematical expressions. Whilst these thus provide functions familiar
from other programming languages, it should be stressed that they should be used
sparingly. The navigation and stream functions in MERL provide powerful and
concise ways to generate the necessary output in almost all cases. You should
first become expert in using those fundamentals of MERL before starting to use
the commands in this section. Otherwise, you run the risk of doing something
similar to programming with an ANSI C style in an object-oriented programming
language.
Simple variable use
As elsewhere in MERL, all variables are simply strings; some
operations will allow them to be treated like numbers or collections of lines.
To assign a simple variable with a fixed string, simple
command, chain clause (e.g. :Name,
explosions) or other variable’s
value:
$variableName1 = 'variableValue'
$variableName2 = id
$variableName3 = $variableName2
$variableName4 = $variableName5 = $variableName6 = ''
To
output the value of a variable, or use it in an
if condition etc., use:
$variableName
Variable write and append
To set the value of a variable, you can also use the
template:
variable 'variableName' write
'variableValue'
close
This sets the value of the variable 'variableName'
to ‘variableValue’. Similarly to
filename clause, the variable value can
be overwritten or appended by using the
write or
append after variable name. For
example,
variable 'variableName' append
'appendedValue'
close
appends the ‘appendedValue’ to the
‘variableValue’, thus the new value of variable
‘variableName’ is
‘variableValueappendedValue’.
Similarly to
filename..write..close between
filename and
write there can be any generator
commands, all of whose output goes to building up the variable name to be used.
The same applies to forming the variable value.
By forming the variable name at run-time, rather than
having it as a fixed element in the generator definition, it is possible to
create associative variables. For instance, you can create a variable for each
type, and append to it the names of each object of that type. You can also add
numbers to the end of the variable name, effectively giving you indexed
collections: a variable called “Name3” is equivalent to the C syntax
“name[3]”.
Variable read
To output the value of a variable, you can also use the
template:
variable 'variableName' read
This outputs the value
of the variable ‘variableName’ to the current output. The variable
name can again be formed by other generator commands, allowing associative
variables. Note that if the variable name is fixed, you can use the more concise
variable referencing syntax above.
Variable incrementing and decrementing
To increment and output the value of a variable as a number
use the syntax:
$++variableName
To first output and then increment
the value of a variable, use the syntax:
$variableName++
The value of a variable may also be
decremented by using “--“
in place of “++” in the
above examples. Note that if the variable value is not a number it will be
interpreted as 0 for the increment or decrement operation.
If you do not want the result output, you can add a string
translation to it to map all characters (or just all digits) to nothing. The
translator would normally be defined just once at the start of the outermost
generator, but we include it inline here for completeness:
to '%null' newline '* $' endto
$variableName++%null
More complex calculations with
variables can be done by using the
math..evaluate
clause.
Translating strings: to translate endto
To replace characters or substrings in a string use the
template:
to
'A-Z a-z'
translate
'Foo'
endto
which outputs ‘foo’. The clauses
between to and
translate form the translator, and
those between translate and endto form the output to which that translator is
applied. For example, the rule ‘A-Z
a-z’ above means that each uppercase letter is converted into
the corresponding lowercase letter.
Translators can be named and used many times. For example
to
'%lower' newline 'A-Z a-z'
endto
defines a translator named ‘lower’
whose rule is ‘A-Z a-z’. The clauses between
to and
endto form the translator definition:
the first line sets the translator name and the next lines contain the rules.
This translator can be used later many times:
to '%lower' translate 'Foo' endto
There is also a
shortcut syntax for using named translators with simple commands, design element
output commands, variables and literal strings:
id;2;%lower
:myName%lower
$variable%lower
A translator definition can contain
multiple rules, each one line of text that maps a left-hand side to a right-hand
side. There are several different kinds of rule, such as character to character,
or string to string. The different kinds of rules available are explained in the
table below. Special characters (newline space \ / $ % - *) must be escaped with
backslash, e.g. to map spaces to underscores use:
'\ _' (backslash, space, space,
underscore). Remember too that if the translator definition is expressed in a
literal string, a single quote ' must
of course be escaped by doubling it.
Name or comment
|
'%myName' as the
first line in a translator definition gives the translator a name
“myName”. Lines starting with % later in the translator are ignored
as comments.
|
Character
|
'a b' maps each
occurrence of character a to character b.
|
Range |
'1-9 a-i' maps each
character in the range on the left to the corresponding character in the range
on the right. In this example numbers are mapped to letters: 1 becomes a, 2
becomes b and so on. Note that ranges must be of equal size, thus ‘a-c
1-4’ is not legal.
Note: range can be reversed, e.g. "a-z z-a".
|
Multiple character
|
'123 abc' maps each
number to a letter: 1 to a, 2 to b, 3 to c. The difference from range is that
each character is specified explicitly.
|
String
|
'$dog $cat' means
replace each occurrence of the string ‘dog’ with ‘cat’.
|
Mixed
|
'aeiou $VOWEL' means
replace each vowel with the string "VOWEL". This is applied with the character
translations.
|
Asterisk
|
An asterisk on the left is the default mapping – what to
map all unspecified characters to (the default is to leave them unchanged):
'* $abc' means replace each character
with the string "abc"
An asterisk on the right means leave the characters on the
left unchanged: ‘abc *’ do not change a, b and c.
|
|
'/[A-Z][a-z]*/ $NAME'
means replace each occurrence of a capital letter followed by lowercase letters
with NAME. The left-hand side need not escape special translation characters,
but can use the normal regular expression escapes; / must be escaped by doubling
it.
The right-hand side (after the initial $) can use $0 to
refer to the whole matched string, $1 for the substring matching the first
parenthesized subexpression etc. E.g. the following rule (which should be on one
line) would turn “Fred Bloggs and John Doe” into “Bloggs, Fred
and Doe, John”
/([A-Z][a-z]*) ([A-Z][a-z]*)/
$$2,\
$1
|
All rules that apply to single characters are collected
together first to build one large character mapping, which is applied to the
input text in one operation. After that all rules that apply to strings,
including regular expression rules, are applied in order, one at a time, to the
whole text. If you need to change this order, e.g. to translate strings first
then characters, you can use two translators. The first will translate just
strings and the second just characters, and you can apply the first and then the
second to achieve the desired result.
Some useful translators such as %lower can be found from
the _translators generator in the Graph metatype. To be able to use these
translators in your own generators, use “subreport
‘_translators’ run” somewhere near the start of your outermost
generator.
Math evaluate
To evaluate a mathematical expression use the
template:
math '1+3*2' evaluate
which outputs 7. The
expression between math and
evaluate keywords forms the evaluated
expression. The rules for forming the expression are:
Numbers
|
The number format in the input stream is integer, float, or
scientific format 3e10. Leading zeroes are allowed (e.g. 001), and float must
have an integer part (e.g. 0.3 not .3).
|
Operations |
The operations are + - * / and ^ for power, all binary, and
unary plus and minus. Also // (division without remainder) and \\
(modulo).
|
Precedence
|
Precedence is as normal: parentheses, ^ / * - +.
math '5+4*3^2'
evaluate
outputs
41, i.e. 5 + (4 * (3 ^
2))
math '3^2*4+5'
evaluate
outputs 41, i.e.
((3 ^ 2) * 4) + 5).
Precedence of unary minus and plus is higher than
exponentiation:
-2^-2
= (-2)^(-2), not
-(2^-2)
|
Associativity
|
Binary operators are left associative, except ^ which is right
associative:
100/10/5 outputs
2, i.e. (100 / 10) / 5
3-4-5 outputs
-6, i.e. (3 - 4) - 5
4^3^2 outputs
262144, i.e. 4 ^ (3 ^ 2)
|
Output formats
|
Integers (3,
unbounded range)
Fractions
(3/4,
unbounded range and precision. If you want to turn this into a float,
0.75, use a float as part of the
calculation, e.g. 3/4.0)
Floats (3.0 or,
if the absolute value is larger than 1,000,000 or smaller than 0.001, in
scientific format 1.0e-4. Precision is
8 or 9 digits. Range is plus and minus 1.0e38 (an error will be raised if the
range is exceeded).
|