DEFINITION MODULE SYSTEM; (* Extensions with respect to the standard *) CONST SOURCELINE = (* a constant containing the actual source line number. *) SOURCEFILE = (* a constant containing the name the actual source file. *) TYPE INT8 = [-128 .. 127]; CARD8 = [0 .. 255]; INT16 = [-32768 .. 32767]; CARD16 = [0 .. 65535]; INT32 = [-2147483648 .. 2147483647]; CARD32 = [0 .. 4294967295]; INT64 = [-9223372036854775808 .. 9223372036854775807]; CARD64 = [0 .. 18446744073709551615]; ADRCARD = CARDINAL; WORD8 = LOC; WORD16 = ARRAY [0 .. 1] OF LOC; WORD32 = ARRAY [0 .. 3] OF LOC; WORD64 = ARRAY [0 .. 7] OF LOC; SET8 = PACKEDSET OF [0 .. 7]; SET16 = PACKEDSET OF [0 .. 15]; SET32 = PACKEDSET OF [0 .. 31]; SET64 = PACKEDSET OF [0 .. 63]; BCD = (* 8 bytes: 15 + 1 nibbles for digits + sign *) STR255 = (* 256 bytes: largest possible Pascal string *) PROCEDURE REGISTER (r: CARDINAL): <CARDINAL/LONGREAL> (* Delivers the contents of register r *) PROCEDURE SETREGISTER (r: CARDINAL; x: <WORD64>); (* Sets register r to the given value *) PROCEDURE CODE (x: <CARD32 constant>); (* Places the value x as a 32-bit word directly into the code *) PROCEDURE ASSEMBLER (text: ARRAY OF WORD); (* Places the given text as one line into the emitted assembler source *) PROCEDURE INCADR (VAR addr: ADDRESS; offset: CARDINAL); (* Increments addr by offset (or 1 if offset is omitted. *) PROCEDURE DECADR (VAR addr: ADDRESS; offset: CARDINAL); (* Decrements addr by offset (or 1 if offset is omitted. *) PROCEDURE TOSTR255 (str: ARRAY OF CHAR): STR255; (* Converts a Modula-2 string into a Pascal string *) PROCEDURE FROMSTR255 (str255: STR255; VAR str: ARRAY OF CHAR); (* Converts a Pascal string into a Modula-2 string *) PROCEDURE BREAK; (* Calls the Modula-2 debugger *) PROCEDURE SETEXITCODE (x: INTEGER); (* Sets the return code for the program to x *) PROCEDURE ROUND (x: <real-number expression>): INTEGER; (* rounds x to the nearest integer value *) PROCEDURE CLONE (VAR target: <class type>; source <class type>); (* generates an exact copy of the objects referenced by source *) PROCEDURE OFFS f: <qualified record field>): CARDINAL; (* calculates the offset of f from the beginning of the record in LOCs *) END SYSTEM.
SOURCELINE
This constant always reflects the number of the source line in which this constant is used.
SOURCEFILE
This constant contains the name of the source file currently compiled.
Each data type described here (except "ADRCARD") includes its size in memory in bits at the end of its name.
INT8, CARD8, INT16, CARD16, INT32, CARD32
These types are defined as subranges ([-128 .. 127], [0 .. 255], [-32768 .. 32767], [0 .. 65535], [-2147483648 .. 2147483647], [0 .. 4294967295])
of "INTEGER" or "CARDINAL" and are therefore compatible to them.
INT64, CARD64
These are pseudonyms for "INTEGER" and "CARDINAL" respectively.
WORD8, WORD16, WORD32, WORD64
These are defined as "ARRAY [0 .. n-1] of SYSTEM.LOC" (for n = 1, 2, 4, 8 respectively) and
as a result have all the compatibility properties of type "WORD"
("WORD64" is identical to "WORD").
SET8, SET16, SET32, SET64
These types are defined as "PACKEDSET OF [0 .. n-1]" (for n = 8, 16, 32, 64 respectively).
They are intended for use as set types occupying a predefined amount of space in memory. "SET64" is identical to "BITSET".
ADRCARD
Subrange of "CARDINAL" having the same memory space requirement as "ADDRESS". For all actually
supported architectures ADRCARD is a synonym for CARDINAL.
The standard function "LENGTH" is applicable to variables and constants of type "STR255", its result is the actual length of the string.
For converting between variables of this type and Modula-2 strings "SYSTEM" also furnishes the subroutines "FROMSTR255" and "TOSTR255" (cf. 4.1.4).
The type "STR255" may be indexed. The elements are of type "CHAR". The element with index "0" represents the length of the string (i. e. the following equation holds "ORD (str [0]) = LENGTH (str)").
1.34$ | legal |
10000000$ | illegal (no decimal point) |
1.8E5$ | illegal (exponent not allowed) |
9876543210.987654$ | illegal (too many digits) |
PROCEDURE SETREGISTER (r: CARDINAL; x: WORD);
Sets the register specified by "r" to the value passed in the second parameter.
When setting registers care must be taken that the register
has not already been put to use by the compiler. Besides that, the register might be overwritten
in the next statement (see also
5.3.9 and 5.3.10)!
Only constant values are allowed as first parameter.
PROCEDURE CODE (x: CARD32);
The parameter's value is placed in the code as a 32-bit word at compile time, i.e. this is only a
pseudo procedure, as no procedure call as such is generated. Only constants are allowed as parameters.
This function is very dangerous; the compiler does not undertake checks of any kind. A special
TRAP command could be inserted using this "procedure" for example.
PROCEDURE ASSEMBLER (text: ARRAY OF CHAR);
The parameter's value is placed in the assembler output file at compile time, i.e. this is only a
pseudo procedure, as no procedure call as such is generated. Only constants are allowed as parameters.
Care must be taken when using this procedure as the compiler does not undertake checks of any kind.
PROCEDURE TOSTR255 (str: ARRAY OF CHAR): STR255;
Converts an arbitrary Modula-2 string to type STR255 (largest possible Pascal string type, up
to 255 characters). If the string passed is too long it is simply truncated without error
notification. This function procedure is allowed in constant declarations.
PROCEDURE FROMSTR255 (str255: STR255; VAR str: ARRAY OF CHAR);
Converts a value of type "STR255" (largest possible Pascal string type) into a Modula-2
string. If the array specified is not large enough to accommodate the entire resulting string,
the overflowing characters are simply discarded without error notification.
PROCEDURE BREAK;
"BREAK" calls the error dialog of the p1 Modula-2 run-time system as a subprogram. The
debugger can be intentionally brought into action this way for example. The program can be
continued normally afterwards.
PROCEDURE SETEXITCODE (x: INTEGER);
Sets up the value of the variable "x" as return code for the shell. In this way
the shell can be informed to skip a certain series of commands following the program when an
error occurs.
PROCEDURE ROUND (x: <floating point expression>): INTEGER;
The value of "x" is rounded to the nearest whole number (as specified by the processor, not
necessarily the mathematical definition for rounding of x.5 !). An exception is raised if the
value cannot be represented as number of type "INTEGER".
PROCEDURE CLONE (VAR target: <class type>; source: <class type>);
An exact copy is made of the object referenced by "source" and a reference to this new object
is stored in "target". If "source" contains the value "EMPTY" or if not enough
storage for the copy is available, "EMPTY" is returned in "target". For "source"
the type of "target" is used so that all classes assignment compatible to the type of
"target" are allowed for "source". If "target" specifies an untraced class,
"ALLOCATE" has to be visible as with "CREATE".
PROCEDURE OFFS (f: <qualified record field>): CARDINAL;
The offset of the given record field "f" from the beginning of the
record is calculated. Fields of deeper nested records may be specified (multiple qualification).
pragma | = | "<*" single_pragma { single_pragma } "*>" |
single_pragma | = | [ assignment | definition | environment | save | condition | ";" ] |
assignment | = | known_variable "(" value ")" | "ASSIGN" "(" known_variable "," value ")" |
environment | = | "ENVIRON" "(" unknown_variable "," value ")" |
definition | = | "DEFINE" "(" unknown_variable "," value ")" |
save | = | "PUSH" | "POP" |
condition | = | "IF" boolean_expression "THEN" { "ELSIF" boolean_expression "THEN" } [ "ELSE" ] "END" |
boolean_expression | = | cond_expr [ ( "=" | "<>" | "#" ) cond_expr ] |
cond_expr | = | cond_term { "OR" cond_term } |
cond_term | = | cond_factor { "AND" cond_factor } |
cond_factor | = | [ "NOT" ] cond_factor | value | "(" boolean_expression ")" |
known_variable | = | identifier |
unknown_variable | = | identifier |
identifier | = | any legal Modula-2 identifier |
value | = | any legal Modula-2 string | "TRUE" | "FALSE" | predefined_constant | identifier |
predefined_constant | = | "ALIGN1" | "ALIGN2" | "ALIGN4" | "ALIGN8" | "ALIGN16" | "ISA68k" | "ISAPPC" | "ISAi386" | "ppc" | "i386" | "68k" |
The following pragma variables are already known to the compiler (cf. 4.3.3):
Name | Meaning | Notes |
IndexCheck | Monitor array indices (for under-/overflow) | 1, 4 |
PointerCheck | Test for NIL pointer / EMPTY reference (on deref) | 1, 4 |
RangeCheck | Monitor subrange values (for under-/overflow) | 1, 4 |
ComplexCheck | Separate checks for complex arithmetic (see below) | 1, 4 |
OverflowCheck | Test for overflow in whole-number arithmetic | 1, 4 |
DivZeroCheck | Test for division by 0 | |
StackCheck | Test for stack overflow | 4 |
Warnings | Include warnings in compiler error-output | |
CopyRefparams | Copy reference (i.e. value) parameters | 1, 5 |
PragmaCheck | Issue warnings if unknown pragmas encountered | |
FixedSubrangeSize | Size of subrange types (cf. 5.3.1) | 7 |
Foreign | Generate foreign module | 2 |
UpperCaseNames | Completely capitalize variable names for linker | 3 |
Calling | Use different calling sequence (see below) | |
Reference | Reference parameter (see below) | |
ForeignEntry | External module initialization (cf. 4.4.2.3) | |
EntryName | Linker name (see below) | |
Variadic | Variadic parameter allocation (see below) | |
AlignBorder | Alignment control (see below) | |
ARCH | target architecture | 6 |
OptLevel | Optimization level (see below) | |
p1 | Always TRUE | 6, 7 |
StonyBrook | Always FALSE | 6, 7 |
The values ""CCalling"", ""CByRef"", and ""Default"" can be specified for the pragma variable "Calling" (cf. 4.4.1).
If TRUE is assigned to the pragma variable "Variadic", variadic parameter allocation is used for the next parameter. This prama variable is meaningfull iff the current calling convention is "CCalling" and Arm64 code is produced.
If "TRUE" is assigned to the pragma variable "Reference" the following (value) parameter is passed by reference (cf. 5.3.4).
Any string which is usable as an external linker name can be specified for the pragma variable "EntryName". (see also section 4.4.2.1 / 4.4.2.2).
The values "ALIGN1", "ALIGN2", "ALIGN4", "ALIGN8", and "ALIGN16" can be specified for the pragma variable "AlignBorder" (cf. 5.3.1).
The pragma variable "ARCH" allows to check for the target architecture. Actually, the values
"a64" (Arm 64-bit code generation) and "x86" (Intel 64-bit code generation) may be returned.
These values are meaningful if the C back end is used too.
The pragma variable "OptLevel" allows to check for or set the actual optimization level. In the current implementation the values 0, 1, and 2 are accepted (cf. section 1.2).
All the other pragma variables take logical values.
If the pragma variable "ComplexCheck" is set to "TRUE", additional code is generated to distinguish between exceptions in real arithmetic and exceptions in complex arithmetic. If this variable is set to "FALSE", complex arithmetic exceptions are reported as real arithmetic exceptions. In standard mode "ComplexCheck" always has the value "TRUE".
Notes:
Compilation is controlled by so-called compile-time variables which can be assigned values using special pragmas (and also from the command line). Their values can then be determined using relational expressions. The values "TRUE" and "FALSE" have special meanings. Variables which can take on these and only these values behave like "BOOLEAN" variables. Expressions used in conditions always have to have the type "BOOLEAN".
DEFINE (name, value) | Declare the variable "name" and initialize it with the value "value". A logical variable is defined if "value" is "TRUE", "FALSE" or another logical variable, in all other cases a string variable is defined. |
ASSIGN (name, value) | Assign the value "value" to the variable "name". |
ENVIRON (name, value) | Declare the variable "name" and assign it a value from the command line. If the command line does not contain a value for it, "name" is assigned the value "value". "value" determines the type of the variable. |
IF boolExpression1 THEN | Compile the subsequent text if "boolExpression1" is true. |
ELSIF boolExpression2 THEN | Compile the subsequent parts of the program if previous boolean expression(s) were not true but boolExpression2 is. This pragma is only allowed after a previous "IF booleanExpression THEN" pragma. |
ELSE | Compile the subsequent text if previous boolean expression(s) were not true. This pragma is only allowed after a previous "IF booleanExpression THEN" pragma. |
END | End of the conditionally-compiled program text. Every "IF booleanExpression THEN" pragma must be matched by a concluding "END" pragma. |
value1 = value2 | This boolean expression is true if value1 is equal to value2. |
value1 <> value2 | This boolean expression is true if value1 is not equal to value2. |
name | Simplified form of "name = TRUE"; "name" must be a logical variable. |
NOT value | Negates "value". "value" has to be "TRUE" or "FALSE". |
value1 AND value2 | Logical and, "value1" and "value2" have to be "TRUE" or "FALSE". |
value1 OR value2 | Logical or, "value1" and "value2" have to be "TRUE" or "FALSE". |
(boolExpression1) | Bracketing of boolean expressions. |
FROM <* IF LONG THEN *> LongMath <* ELSE *> RealMath <* END *> IMPORT sqrt;
The function "sqrt" is imported either from "LongMath" or from "RealMath" depending on
the value of compile-time variable "LONG".
<* IF RANGE THEN RangeCheck (TRUE); END *>
If "RANGE" is specified, subrange monitoring is turned on.
<* ENVIRON (TARGET, "MAC") *>
The variable "TARGET" is defined and given the value ""MAC"", unless it was assigned a
value in the command line, in which case it receives that value.
<* IF NOT test THEN *> x := 1; <* END *>
The assignment will only be included in the program if the compile-time variable "test" has
the value "FALSE".
An interface module consists solely of a definition module. It is in fact impossible to compile a corresponding Modula-2 implementation module (the foreign software takes its place after all). Textually an interface module differs from a simple definition module only in having the normal module head preceded by the pragma "<* ASSIGN (Foreign, TRUE) *>". For example:
<* ASSIGN (Foreign, TRUE) *> DEFINITION MODULE Memory;
All programming languages which pass parameters via standard conventions are supported. The programmer is responsible for ensuring that the interface ties in correctly by appropriately mapping parameter types (and possibly also their order) in the foreign software segment. One limitation worth mentioning: no other popular programming language has anything corresponding to Modula-2's concept of open arrays. (see also sections 5.3.2 / 5.3.3 parameter passing.)
In particular, two different kinds of procedure interface are supported by the compiler:
Examples for interfacing to C are given by the Core Definition Files which are derived
from the core definition header files written in C.
These definition files give an example for converting C header files
to Modula-2. They contain the original C code as comments, so that the more complex type
matching can be followed.
<* ASSIGN (Calling, "CCalling") *> PROCEDURE exit (code: INT32); <* ASSIGN (Calling, "CByRef") *> PROCEDURE useThisRect (r: Rect); (* void useThisRect (const Rect *r); *)
PROCEDURE ioctl (fd: INT32; cmd: CARD32<*ASSIGN(Variadic, TRUE)*>; arg: ADDRESS): INT32;
In this case, the main procedure must not be defined in the Modula-2 runtime, so the Modula-2 library has to be replaced by "libm2libforeign.a".
<* ASSIGN (Calling, "CCalling") *> PROCEDURE name (...);
Similarly, procedure types can be declared like this:
<* ASSIGN (Calling, "CCalling") *> TYPE name = PROCEDURE (...);
A procedure type of this kind is not compatible to procedures with default calling conventions. If only the address shall be passed for value parameters of structured types, the calling convention "CByRef" may be specified instead of "CCalling". In this case the parameters have to be declared as "const MyType *", "const MyType&", or something similar in the corresponding C header file.
Modula-2 procedures which conform to C calling conventions (like the examples above) can also be called from Modula-2 without taking any special precautions: the compiler automatically takes these conventions into account when such procedures are called.
The pragma variable "EntryName" allows to specify a linker name in addition to the internal compiler name, e.g. the original procedure name:
<* ASSIGN (Calling, "CCalling") *> <* ASSIGN (EntryName, "MyExternalName") *> PROCEDURE MyName (...);
Defined this way, the procedure "MyName" may be called from a C program also via "MyExternalName".
When required, the initialization of the Modula-2 runtime system (including the execution of the module initialization parts) can be invoked manually. The runtime system provides for the following auxiliary procedures (the headings are written according to Modula-2 conventions):
For multi-dimensional arrays "ARRAY OF" is specified according to the number of dimensions. The declaration of dynamic arrays is a normal type declaration, it may be used in any place a type declaration is possible.
Like with open arrays the index range starts at "0", is a subrange of "CARDINAL", and reaches up to the top index specified on allocation (cf. 4.5.2).
VAR matrix: POINTER TO ARRAY OF ARRAY OF REAL; BEGIN NEW (matrix, 5, 10);allocates storage for a two-dimensional array with the actual type of "ARRAY [0 .. 5] OF ARRAY [0 .. 10] OF REAL".
Using "ALLOCATE"directly to allocate storage for a dynamic array is not allowed and results in an illegal program as the data structure for the index descriptors is not set up, though the compiler cannot check whether "ALLOCATE" is used directly to allocate storage.
Deallocation of storage is done by "DISPOSE" as usual.
Importing all items from a module
"FROM Module_Ident IMPORT *;" has the effect of importing from a module every single
(exported) entity declared in its definition module. The extension "FROM Module_Ident IMPORT * EXCEPT ident1,
ident2, ...;" allows to import all entities but "ident1", "ident2", etc. Too
frequent use of this feature is however discouraged, as it can reduce the readability of a module considerably.
Open arrays
Not only arrays having the same element type are compatible to open-array parameters, but
also variables and expressions of the array-element type.
String constants
String constants may be indexed like a constant of type ARRAY someRange OF CHAR.
They may be used within value constructores to build an array of character.
Constant "NILPROC"
"NILPROC" is compatibel to every procedure type. If a procedure variable containing the
value "NILPROC" is called, the exception "invalidLocation" is raised
(cf. 2.11.3).
Function "HIGH"
The standard function "HIGH" can be used not only with open arrays but also with dynamic
arrays and arrays of known size. In the case of a multidimensional array, the type of the
"constexpr" used to indicate the dimension must be assignment compatible to the
corresponding array index type.
Undefined variables
The compiler checks statically whether a local variable used in an expression has actually
been assigned a value beforehand. If neither a direct assignment nor an indirect one resulting
from use as a "VAR" parameter has taken place a warning is issued. In rare cases ("IF"
statements within loops, assignments resulting from side effects of internal procedures) the
compiler cannot recognize an assignment as having occurred "before" and issues a warning
superfluously. Such warnings can be suppressed locally using "<* Warnings (FALSE) *>".
chapter 3 (compiler) | start page | chapter 5 (compiler) |