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.

Keyword Description
; 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.
AND Part of a conditional expression in an IF-THEN-ELSE clause like in:
IF suspect = DeckOfCards [n] AND SuspectState = ALIVE THEN
      Arrest (suspect)
ARRAY 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 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.
BY Defines the stepsize in a FOR loop, like in:
FOR  n := 1  TO  99  BY  11  DO .... END;
CASE 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 ('-')
   Write ('Q')
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 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 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.
DO 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])
WHILE n < HIGH (string) DO
   string [n] := CAP (string [n])
   INC (n)
WITH House [n] DO
   LivingRoom := 12;
   Toilet := 11;
   Kitchen := 9
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.
ELSIF 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)
    Signal (Flash)
The ELSIF is a very neat construction to prevent nesting IF-THEN loops too deep.
END 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
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.
FOR 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])
FROM FROM is used in IMPORT lists. Look there for more information.
IF 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. An example:
IF KeyPressed = TRUE THEN
   GetKey (token);
   IF token > 'a' AND token < 'z'
       token := CHR (ORD (token) - 32)
       token := '*'
   Write (ASCII.BEL)
IMPLEMENTATION 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.
In the GPL time, this commercial part is less important. But the code hiding features remain important when groups of programmers work on a combined project and none may rely on the others code. This last, to prevent the whole program from collapsing as a result of one altered line in someone else's source...

IMPORT 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 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 IN is used in functions with SETs. It checks if a certain element is in a certain set. An example is in the BITSET type:
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.
LOOP This is my favorite looping construction: the endless loop. A typical LOOP-END construct is as follows:
    GetKey (token);
    IF token = ASCII.LF THEN  EXIT  END;
    IF n <= HIGH (string) THEN
	string [n] := token;
	INC (n)
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'.
MODULE This tells the compiler that a certain entity is coming up.
A MODULE as such is a standalone program.
NOT 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.
OR 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.')
    WriteString ('We're both sane.')
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.
PROCEDURE This word tells the compiler that a subroutine definition is coming up. See the example:
PROCEDURE  WriteString (string  : ARRAY OF CHAR);
VAR   i      : CARDINAL;
    i := 0;
        IF  i > HIGH (string)  THEN  EXIT  END;
	IF string [i] = 0C THEN
	Write (string [i]);
	INC (i)
END WriteString;
RECORD 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

VAR    TempTrack    : ARRAY [0..31] OF Temperatures

WITH TempTrack [n] DO
     RoomNr   := InPort (LabelScanner);
     RoomTemp := ReadADC (RoomNr);
     Doors    := CheckDoor (RoomNr)
The RECORD concept, combined with the WITH-TO functionality is a very powerful and clear way to use complex datastructures.
REPEAT The REPEAT is one of the standard Modula-2 looping controls. It has the following syntax:
    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.
RETURN 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;
   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
TYPE 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.
VAR 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:

VAR    Me       : CARDINAL;


VAR  local           : REAL;

    local := <something>;
END Try;

    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 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)
WITH Look under 'RECORD' and under 'DO'.

  Standard functions and description
ABS (x) Returns the absolute value of the integer number x. In a formula:

    IF x < 0 THEN 
        x := -x
END ABS;      
CAP (c) If 'c' is a lowercase character, make it uppercase.
    IF ch > 'a' AND ch < 'z' THEN
	ch := CHR (ORD (ch) - 32)
CHR (n) This function returns the n-th element of the ASCII character set. CHR is comparable to CHR$ in BASIC.
FLOAT (x) 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.
ODD (x) Returns TRUE if the number x cannot be divided by 2.

    IF x MOD 2 = 0 THEN
ORD (x) 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.
DEC () 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 () 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.
BITSET 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;
BOOLEAN 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.
CARDINAL 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.

Operator Description
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.
DIV Divide. Perform a division operation upon two natural numbers.
The natural numbers are all (sub)types of CARDINAL and INTEGER.
MOD 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 '#'.
< Less than.
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
> Greater than.
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
IN This operator checks whether a certain element is in a certain SET.
   WriteString ('Bit 1 was set.')
   WriteString ('Bit 1 was cleared.')
This 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.

TYPE Size (bytes) MIN MAX
CHAR 1 0 255
SHORTCARD 2 0 65.535
SHORTINT 2 -32.768 32.767
INTEGER 4 -2.147.483.648 2.147.483.647
LONGINT 4 -2.147.483.648 2.147.483.647
CARDINAL 4 0 4.294.967.295
LONGCARD 4 0 4.294.967.295
BYTE 1 0 255
WORD 1 0 255
REAL 4 -3,4028 E+38 3,4028 E+38
LONGREAL 8 -1,7977 E+308 1,7977 E+308
BITSET 4 0 4.294.967.295
ADDRESS 4 0 4.294.967.295

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,