Below, I have presented an overview and a short explanation of the basics of Modula-2, especially in combination with the Mocka compiler. This document contains several tables for Keywords, Standard functions, numerical operators and data types and their physical properties. Please read the message at the bottom of this webpage.
|;||This operator tells the compiler that one more statement is coming up. The semicolon is not a terminator like in many lesser languages. At the end of a block, no semicolon is needed.|
This is the assignment operator in Modula-2. It has this strange form to make clear that it is NOT a
comparison operator ('=').
The ':=' should be pronounced as 'becomes'. Some examples you can find below.
Part of a conditional expression in an IF-THEN-ELSE clause like in:
IF suspect = DeckOfCards [n] AND SuspectState = ALIVE THEN Arrest (suspect) END;
ARRAY is used to indicate that this is a variable with more than one dimension. ARRAY definitions in
Modula-2 can be very 'strange' at first glance. Some examples:
string : ARRAY [0..9] OF CHAR (* a character string of 10 tokens *) quarks : ARRAY [3..7] OF CARDINAL (* array of cardinals *) days : ARRAY [Mon..Fri] OF Days (* the workingdays array.. *)
BEGIN marks the start of the instructions for a PROCEDURE or for the main body. Unlike in Pascal, BEGIN may
not be used as the start of a compound statement.
In Modula-2, every statement that CAN be compound already IS a compound statement.
Defines the stepsize in a FOR loop, like in:
FOR n := 1 TO 99 BY 11 DO .... END;
CASE is used for a multiconditional handler. The 'labels' in a CASE statement must be constants of some
kind. An example:
CASE char OF '1' : Write ('1') | '2' : Write ('9') | '3', '4', '5', '6' : Write ('Z') | ['a'..'z'] : Write ('-') ELSE Write ('Q') END;In this example, the value of 'char' is tested against some conditions. If char = '1' then an action is taken. When char = '2' then another action is taken.
When char is between '3' and '6' (inclusive) then a third action is taken. If char is a lowercase letter, a hyphen is printed. In all other cases the ELSE clause kicks in and executes it's part.
CONST declares that the following definitions are to be treated as constant values. An example:
CONST LF = 12C; CR = 15C; MaxPos = 120; VAR Xpos, Ypos : CARDINAL; str : ARRAY [0..63] OF CHAR;When the compiler finds the 'VAR' keyword, it stops compiling symbols as constants.
|DEFINITION||The DEFINITION keywords signals that the current file is a DEFINITION MODULE. Such a file shows how the syntax of the enclosed functions is but does not reveal the actual source.|
DIV is the DIVIDE instruction for non-floating point numbers, such as in:
yield := cherries DIV pickers; DogsPart := cherries MOD pickers;In this example, the cherrypickers get a positive integer number of cherries each. The dog gets the remainder.
The DO word is used as part of some control loops (FOR, WITH and WHILE) as an indicator that the actual
instructions start there. Some examples:
FOR n := 1 TO HIGH (string) DO string [n] := CAP (string [n]) END; WHILE n < HIGH (string) DO string [n] := CAP (string [n]) INC (n) END; WITH House [n] DO LivingRoom := 12; Toilet := 11; Kitchen := 9 END;The WITH statement is very special. It allows operations on RECORD types where (in this case) the RECORD has at least these three fields and each of them is assigned a value.
|ELSE||ELSE is used in either and IF-THEN-ELSE-END clause, or in a CASE-OF-ELSE-END construction to specify a default action in case the specified condition is not met. You can find examples under the CASE and the IF keywords.|
If you want to build a multicondition brancher, and you're not dealing with constant expressions, you
cannot use a CASE statement.
For such events, there is the IF-THEN-ELSIF-THEN construction. An example:
IF option = 'Home' THEN Signal (Red) ELSIF option = 'Work' THEN Signal (Green) ELSIF option = 'Out' THEN Signal (Yellow) ELSE Signal (Flash) END;The ELSIF is a very neat construction to prevent nesting IF-THEN loops too deep.
END signals the end of an entity.
The statement before an END statement does not need a semicolon as a line terminator. You MAY put it there, but it has no function:
IF cherries > 0 THEN EachPart := cherries DIV pickers; DogsPart := cherries MOD pickers END;The 'DogsPart' line does not need a semicolon as a line terminator.
|EXIT||This is the only way to get out of a LOOP-END construction.|
This is the first keyword for the well known FOR/NEXT instruction in BASIC. The Modula-2 syntax is
different of course. Look at the example below:
FOR n := 1 TO HIGH (string) DO string [n] := CAP (string [n]) END;
|FROM||FROM is used in IMPORT lists. Look there for more information.|
The IF is used to set up a conditional branch. IF constructions can be very long and complex. Therefore
indentation is highly recommended in order to keep an eye on which IF belongs to which ELSE en which END.
IF KeyPressed = TRUE THEN GetKey (token); IF token > 'a' AND token < 'z' token := CHR (ORD (token) - 32) ELSE token := '*' END ELSE Write (ASCII.BEL) END;
A file which is a DEFINITION MODULE mentions the names and parameters or types of variables but does not
disclose the actual way the functions in that library were constructed. This is 'code hiding' and Modula-2
is very good at it.
But it's obvious that the functions defined in the DEFINITION MODULE need to be coded somewhere. And that's where the IMPLEMENTATION MODULE comes in.
The IMPLEMENTATION MODULE contains the source code of the functions described in the DEFINITION MODULE.
On commercial systems, when a company sells libraries, it can suffice to sell the DEFINITION MODULE and the
object file which was made by compiling the IMPLEMENTATION MODULE. Doing so, the company can sell their
products without having nosy geeks trying to steal their code.
The IMPORT statement allows us to extract functions from libraries, like described above, under
IMPLEMENTATION. First some examples:
FROM InOut IMPORT Write, WriteLn, WriteCard; IMPORT ASCII; IMPORT Terminal;As you can see, there are two ways to IMPORT functions. In the first example three functions are extracted from the library called InOut. In the remainder of the program, these functions can be called as such.
The second way of importing functions (or properties) can be handy if you need to use two entities of the same name, but from different DEFINITION MODULES. Here's an example, relying on the above IMPORT's:
WriteCard (number, 6); Write (ASCII.LF); IF number < 230 THEN Terminal.Write ('#') END;This construct can be handy if (for example) the Write from library Terminal does not advance the cursor or something similar.
IN is used in functions with SETs. It checks if a certain element is in a certain set. An example is in the
IF 23 IN BITSET (flagword) THEN Signal (SigSegv) END;This is a rather complex subject. Please study a decent tutorial (like the one from Coronado) to get a good explanation.
In the example, bit 23 is checked in a flag word and if the bit was set, a SigSegv violation is reported.
This is my favorite looping construction: the endless loop. A typical LOOP-END construct is as follows:
LOOP GetKey (token); IF token = ASCII.LF THEN EXIT END; IF n <= HIGH (string) THEN string [n] := token; INC (n) END END;This loop reads tokens from the keyboard and puts them in a string. It will loop forever, unless a LineFeed token is entered.
LOOP-END loops are very powerful since you can specify multiple exit points by having several IF or CASE statements which issue the EXIT command. Remember, however, that EXIT jumps out of the (at that moment) most inner loop. LOOP-END is th GOTO equivalent of Modula-2....
|MOD||This is the MODULO operator which calculates the remainder after an integer division. Please take a look at 'END' and 'DIV'.|
This tells the compiler that a certain entity is coming up.
A MODULE as such is a standalone program.
The DEFINITION MODULE and the IMPLEMENTATION MODULE are described above.
This is the logical NOT operator. It makes TRUE what FALSE is and the other way round.
IF NOT (Sky = Blue) THEN Get (umbrella) END;
|OF||Used in conjunction with a CASE statement.|
The logical OR, as used in conditional statements like in:
IF I = mad OR U = mad THEN WriteString ('At least one of us is mad.') ELSE WriteString ('We're both sane.') END;
|POINTER||A POINTER is a type declaration. A pointer contains an address to an entity. Pointers are rather difficult to explain in such a short space, so please study the Coronado tutorial.|
This word tells the compiler that a subroutine definition is coming up. See the example:
PROCEDURE WriteString (string : ARRAY OF CHAR); VAR i : CARDINAL; BEGIN i := 0; LOOP IF i > HIGH (string) THEN EXIT END; IF string [i] = 0C THEN EXIT END; Write (string [i]); INC (i) END END WriteString;
A record is a data collection that can consist of many different types, but that can be accessed under a
common denominator. Look at the example:
TYPE DoorStatus = (Open, Close, Out); Temperatures = RECORD RoomNr : CARDINAL; RoomTemp : INTEGER; Doors : DoorStatus END; VAR TempTrack : ARRAY [0..31] OF Temperatures WITH TempTrack [n] DO RoomNr := InPort (LabelScanner); RoomTemp := ReadADC (RoomNr); Doors := CheckDoor (RoomNr) END;The RECORD concept, combined with the WITH-TO functionality is a very powerful and clear way to use complex datastructures.
The REPEAT is one of the standard Modula-2 looping controls. It has the following syntax:
REPEAT WriteCard (temps [i], 5); INC (i); UNTIL i > HIGH (temps);The lines between REPEAT and UNTIL are executed at least one time. After that, the check is made which results in either jumping back for a new loop or an abort of this loop.
If a function or procedure is meant to return a value, it must be declared with this keyword:
PROCEDURE GetTemperature (nr : CARDINAL) : REAL; VAR temp : REAL; BEGIN temp := ReadAdc (nr); RETURN temp END GetTemperature;Multiple RETURNS may be in one PROCEDURE. If a PROCEDURE does not return a value to the caller, the RETURN can be used to jump out of the PROCEDURE as a kind of emergency exit.
|SET||Modula-2 supports the use of SET's of datatypes. SET's are powerful means to make better to understand sources. Please study the Coronado tutorial for more details.|
|THEN||Integral part of the IF-THEN clause.|
|TO||Integral part of the FOR loop|
The keyword TYPE tells the compiler that new TYPE definitions are coming up. TYPEs are very powerful and
Modula-2 expects you to be very keen on the use of TYPE's. Take your time to study the concept and use
TYPE's wherever you feel fit.
Please study some sample sources in the Modula-2 section of Fruttenboel. Many contain TYPE definitions that make the source better to read and understand.
|UNTIL||Integral part of the REPEAT statement.|
By means of the VAR keyword, we tell the compiler that variable definitions are coming up.
VAR definitions can be global (if defined outside a PROCEDURE) or local (if declared inside a PROCEDURE). Take care that the scope of variables is very important in Modula-2. Here's an example:
MODULE TryMe; VAR Me : CARDINAL; PROCEDURE Try (VAR x : CARDINAL); VAR local : REAL; BEGIN local := <something>; END Try; BEGIN Try (Me) END TryMe.The first VAR declares a global variable called 'Me' and of type CARDINAL.
The second VAR tells the compiler that the variable 'x' will be passed by reference, i.e. it's address will be passed to the function. This allows the procedure to alter the contents of the passed variable.
The third VAR declares a local variable which ONLY lives inside the PROCEDURE Try.
WHILE is the last looping construct which is part of Modula-2. It is similar to the REPEAT/UNTIL loop, with
the one exception that the looping condition is checked before the first execution of the body of the
WHILE-DO loop. An example:
WHILE n # 12 DO something (); INC (n) END;
|WITH||Look under 'RECORD' and under 'DO'.|
|Standard functions and description|
Returns the absolute value of the integer number x. In a formula:
PROCEDURE ABS (VAR x : INTEGER); BEGIN IF x < 0 THEN x := -x END END ABS;
If 'c' is a lowercase character, make it uppercase.
PROCEDURE CAP (VAR ch : CHAR); BEGIN IF ch > 'a' AND ch < 'z' THEN ch := CHR (ORD (ch) - 32) END END CAP;
|CHR (n)||This function returns the n-th element of the ASCII character set. CHR is comparable to CHR$ in BASIC.|
If you need to convert an INTEGER or CARDINAL of any length) to a REAL number, you need the FLOAT function:
VAR Celsius : REAL; Fahrenheit : INTEGER; Celsius := (FLOAT (Fahrenheit) - 32.0) / 1.8; Fahrenheit := TRUNC (Celsius * 1.8) + 32;
|HIGH (s)||Returns the highest index of array 's'.|
|MAX (t)||Returns the most positive value of variable type t.|
|MIN (t)||Returns the lowest or most negative value of type t.|
Returns TRUE if the number x cannot be divided by 2.
PROCEDURE ODD (x : CARDINAL) : BOOLEAN; BEGIN IF x MOD 2 = 0 THEN RETURN FALSE ELSE RETURN TRUE END END ODD;
Returns the sequential order of elememt 'x' in the corresponding SET. Some examples:
TYPE DaySet = (Mon, Tue, Wed, Thu, Fri, Sat, Sun); ChrSet = [A..Z]; VAR chrvar : ChrSet; day : DaySet; char : CHAR; chrvar := 'C'; (* ORD (chrvar) now is 2 *) day := Sun; (* ORD (day) is 6 *) char := 'P'; (* ORD (char) = 80 *)The ORD and CHR functions are needed because in Modula-2, the CHAR type of variables is a separate entity, not a subrange of INT, like it is in C. Therefore you need conversions between these TYPEs.
|SIZE (t)||SIZE returns the amount of space the described dataTYPE t takes up in memory. The unit of SIZE is BYTE. In Mocka, SIZE (CARDINAL) = 4|
|TRUNC (f)||TRUNC is used to TRUNCate a floating point number to form an INTEGER, a CARDINAL or subrange of these types.|
|VAL (T, x)||
If we can extract the sequential number of an element 'x' in a TYPEd SET 'T', we can use VAL to do the
opposite. If we take the example described above under 'ORD':
TYPE DaySet = (Mon, Tue, Wed, Thu, Fri, Sat, Sun); ChrSet = [A..Z]; VAR chrvar : ChrSet; day : DaySet; char : CHAR; chrvar := VAL (ChrSet, 2); (* chrvar now is C *) day := VAL (DaySet, 5); (* day now is Sat *)Please correct me if I'm wrong.
Decrement the specified parameter. If no second parameter is given, decrement by 1 else decrement by the
specified amount. Some examples:
DEC (n) equals n := n - 1; DEC (n, 4) equals n := n - 4; DEC (n, Counts [i]) equals n := n - Counts [i];As you can see in the last example, the second parameter of a DEC does not need to be a constant expression.
DEC is a very powerful operator when combined with POINTER datatypes. A DEC (PointerType) automatically decrements the pointed to address by the value of SIZE (PointerType).
INC is the same as DEC, with the exception that INC does an Increase:
INC (n) equals n := n + 1; INC (n, 4) equals n := n + 4; INC (n, Counts [i]) equals n := n + Counts [i];
|EXCL ()||Remove (exclude) an element from a set.|
|HALT||Stops execution of this program. May not work under modern operating systems.|
|INCL ()||Include an element into a compatible SET.|
The BITSET operator in effect says: treat the following number as a collection of bits, wherein each bit
can be checked independantly.
It will be clear that BITSET is typical low level function.
IF n IN BITSET (statusword) THEN ... some action ... END;
A variable that can be either TRUE or FALSE. It is not important which numerical value either of these two
states has, since you wouldn't be able to do calculations with them anyway.
This is a serious point of attention for C programmers who got used to working with the 'FALSE = 0' assumption.
A variable that can be only positive and without a fractional part.
With Mocka a CARDINAL is 32 bits long. A LONGCARD is the same size. A SHORTCARD is 16 bits long.
|CHAR||A variable that can hold one character from the ASCII character set. The numerical range of a CHAR is one byte or [0..255].|
|INTEGER||A variable that can hold positive and negative numbers. With Mocka, an INTEGER is 32 bits long, just like a LONGINT. A SHORTINT is only 16 bits long.|
|LONGINT||Under Mocka: the same as INTEGER.|
|LONGREAL||A double precision floating point number.|
|PROC||This is a PROCEDURE type variable. I never used it, but the essence is that it is some kind of POINTER TO FUNCTION to enable dynamic subroutine calls.|
|REAL||A single precision floating point variable.|
|NOT||Invert the result of the following condition.|
|~||Same as NOT|
|*||Times. Perform a multiplication.|
|/||Divide. Perform a division operation upon two floating point numbers. This operator is NOT compatible with natural numbers.|
Divide. Perform a division operation upon two natural numbers.
The natural numbers are all (sub)types of CARDINAL and INTEGER.
Determine the reminder after an integer division. An example:
quotient := 16 DIV 3; (* quotient = 3 *) remainder := 16 MOD 3; (* remainder = 1 *)
|AND||Perform a logical AND upon two BOOLEAN results.|
|&||Same as AND.|
|+||Perform an addition between numbers.|
|-||Perform a subtraction between numbers.|
|OR||Perform an OR operation between two BOOLEAN values.|
Equality operator, used in conditional statements:
IF a = 12 THEN ... END; (* if a is 12 then ... *)
|#||Not equal. The opposite of the '=' operator.|
|<>||Same as '#'.|
1 < 2 is TRUE 2 < 1 is FALSE
Less than or equal. This is the same as not greater than.
1 <= 2 is TRUE 1 <= 1 is TRUE 2 <= 1 is FALSE
1 > 2 is FALSE 2 > 1 is TRUE
Greater than or equal to. This is the same as 'not less than'.
1 >= 2 is FALSE 2 >= 2 is TRUE 2 >= 1 is TRUE
This operator checks whether a certain element is in a certain SET.
IF 1 IN BITSET (2) THEN WriteString ('Bit 1 was set.') ELSE WriteString ('Bit 1 was cleared.') ENDThis code sequence will print the message 'Bit 1 was cleared.'.
The operators in the above list are in order of precedence. I.e. the NOT operator has more 'priority' than the 'IN' operator. You can change the precedence of operators by applying brackets around operands.
|REAL||4||-3,4028 E+38||3,4028 E+38|
|LONGREAL||8||-1,7977 E+308||1,7977 E+308|
Although I have given this webpage a lot of attention, I cannot guarantee that all information contained in it is true. This page is published under the same clauses as other GPL products.
Page created 2002,