The ModulaTor logo 7KB

The ModulaTor

Oberon-2 and Modula-2 Technical Publication

Erlangen's First Independent Modula_2 Journal! Nr. 3/Apr-1993


Clarifications of Ambiguities in the Oberon-2 Language Report

with Restrictions and Extensions of the OpenVMS AlphaAXP|VAX Oberon-2 Compiler (A2O|H2O)

from Appendix C of ModulaWare's A2O|H2O User's Guide V1.32

by Guenter Dotzel, ModulaWare

This section contains clarifications concerning the Oberon-2 language semantics, explains implementation details and describes run-time checks and program exceptions. This section also describes the language extensions necessary to support processor specific data types and in order to obey to the calling conventions of OpenVMS.

The implementation of Oberon-2 on the OpenVMS differs in some respect from the language definition; some restrictions have to be considered. The deviations from the Oberon-2 Report are either due to Alpha|VAX architecture or instruction set semantics, due to OpenVMS procedure calling and naming conventions, or due to be compatible with Modula-2 (MaX|MVR) or ISO Modula-2 semantics. Some restrictions will be removed in future releases to be compatible as close as possible to the Oberon-2 Report.

There are some ambiguities contained in the Oberon-2 Report. Below some of these ambiguities are addressed and clarified. These ambiguities could cause portability problems.

A2O|H2O is based on the portable Oberon-2 front-end OP2 designed by Regis Crelier at ETH-Zuerich. Because most other Oberon-2 compiler implementations available from ETH-Zuerich are also based on OP2, A2O|H2O provides best possible source compatibility under OpenVMS. Even inconveniences (you may call them errors) of OP2 are retained (see C.9 below).

Although marked as restrictions below, some implementation restrictions, such as the maximal number of imported modules, maximal identifier length and the maximal number of exported procedures and types could actually turn out as extensions when compared to other Oberon-2 compiler implementations.

___________________________________________________________________ 


C.1 Identifiers 

Identifiers may contain the additional characters "$" and "_": 

ident = ( letter | "_" ) { letter | digit | "$" | "_" }. 

Maximum identifier name length is 31 and the compiler reports an error for 
longer names. All characters are relevant and case-sensitive. 

Extension: "_": Regis Crelier from ETH-Zuerich who designed OP2 agreed 
that Oberon-2 allows underscores anywhere in identfiers. ISO Modula-2 
also allows underscores. 

Extension: "$": To be able to access OpenVMS operating system services 
and layered product libraries, dollar-signs must be allowed in identifiers. 
This possibility is restricted to so called foreign interface modules (FIM). 

Only if the module name contains a "$"-sign (this is the indicator for a FIM), 
then other identifiers to be declared are allowed to also contain "$"-signs. 
Two exceptions of this rule are (1) for the receiver parameter name of 
methods and (2) module aliases which are never allowed to contain a 
"$"-sign. 

An optional procedure parameter name suffix "$x" specifies the parameter 
passing mechanism x, whereas x is either I, R, S, N, or O for immediate, 
reference, string descriptor, no-type descriptor, optional parameter (see 
chap. 6.3 in A2O|H2O User's Guide for more details). 

Foreign interface modules must not declare or extend any record types. In 
the case of data structures or parameters being of record type, a separate 
(non-foreign) Oberon-2 module should export the record types that are 
required by the FIM. See Appendix J in A2O|H2O User's Guide for an 
example. 

The rational for the record declaration restriction in FIMs is that a FIM, e.g. 
module fim$, doesn't have an implementation module fim$.obj generated by 
the Oberon-2 compiler and hence doesn't have a type descriptor (objects) 
program section. 

If any procedure with a formal VAR parameter of record type type$ defined 
in FIM fim$ is called with the default parameter passing mechanism (i.e. a 
procedure from a non-foreign module or with a parameter name without 
...$N or ...$O suffix), a reference to the type descriptor (objects) section of 
module fim$ is generated in the callers module. This would then result in an 
unresolved reference to a non-existing global symbol $OBJECTS$ reported 
at link-time. To avoid this error, the compiler's back-end detects such illegal 
references and reports an error. 

In summary, record declarations are allowed in FIMs, but any attempt to 
reference their type descriptor is an error flagged by the compiler. 

___________________________________________________________________ 

 

C.2 Linker-Restrictions 

Module names must not be longer than 24 characters. Due to the naming 
conventions of the linker some additional restrictions must be obeyed for 
module and method names as well as for the names of exported 
procedures. An external name is - at the linker level - composed of the 
module name and the exported name, both converted to upper case and 
separated by a dot "." (H2O) or "_" (A2O) or the character specified in the 
/Name_Separator qualifier. If the composed name is longer than 31 (H2O) 
or 63 (A2O) characters (including the separator and in case of methods 
including another separator and the type name of the receiver) the individual 
names are shortened to 15 (H2O) or 31 (A2O) and in case of methods to 10 
(H2O) or 20 (A2O) characters each. Maximal number of significant 
characters for global symbols is 31 (H2O) or 63 (A2O) characters. A conflict 
will occur if the resulting name is not unique anymore. 

Global symbols are constructed as follows 

ModuleName.ProcedureName 

ModuleName.ReceiverTypeName.MethodProcedureName 

Exported variables from (non-foreign) Oberon-2 modules are addressed by 
module-name's variable section and offset. Imported string constants are 
copied from the symbol file to the constant string program section of the 
importer. 

___________________________________________________________________ 

 

C.3 Module Name and Source File Name Correlation 

Although Oberon-2 module identifiers of any length fit well into OpenVMS 
file names, it is recommended to provide compilation unit identifiers differing 
in the first 8 characters. Note, that default symbol file names are constructed 
from the module names. Other platforms may have more restrictive file 
name length which will cause problems when porting an application. 
However the user may work-around this restriction by applying the 
SYMFILE and QUERY compilation qualifiers. 

There is one obscure feature of the front-end that needs further clarification: 
when the file name and the module name differ, such a module can be 
imported via the file name. Symbol files are looked-up for default by 
constructing the file name from the module name. If the imported module 
name differs, there is an implicit alias IMPORT file_name := module_name; 

  -------source file name: M1.MOD-------
  MODULE M2;
    ...
  END M2.
  -------source file name: M.MOD-------
  MODULE M;
    IMPORT M1;
    ...
  END M.
 

when M1.MOD is compiled without using the /symfile qualifier, the symbol 
file M1.SYN is generated by default. When module M imports M1, M1.SYN 
is read and since the symbol file contains module M2, an automatic alias 
IMPORT M1:=M2 is performed in this case. In the case of explicitly aliasing 
the imported module, even the original module name can be introduced: 

  -------source file name: M.MOD-------
  MODULE M;
    IMPORT M1, M2:=M1;
    ...
  END M.
 

The above example shows another feature of the front-end (OP2): it is 
allowed to import the same module more than once. 

___________________________________________________________________ 

 

C.4 Constant Expressions in Declaration Sections 

Clarification: All standard functions (except those imported from SYSTEM) 
can be used to construct constant expressions in the declaration section of a 
module or procedure. See chapter 10.3 (predeclared function procedures) 
in the Oberon-2 Report. 

___________________________________________________________________ 

 

C.5 Integer Operators DIV and MOD 

Clarification and discussion: Both DIV and MOD for integers are 
implemented according to the semantics defined in the Oberon-2 Report. 
Constant expressions are also evaluated according to this semantics. 

The native VAX instruction DIV and EDIV (and on Alpha, LIB$EDIV 
RTL-routine) have different semantics, i.e. they have the semantics of ISO 
Modula-2's "/" and "REM" with integer operands. This is a problem for 
negative dividends only, since Oberon-2 restricts the divisor to positive 
numbers. For example 

-31 VAX-DIV 10 = -3 (= -4 according to the Oberon-2 Report) 

-31 VAX-MOD 10 = -1 (= 9 according to the Oberon-2 Report) 

(See also DEC VAX architecture handbook 
or documentation of the OpenVMS RTL routines.) 

The goal with the H2O implementation was to be 100% semantically 
compatible with ETH-implementations of Oberon-2. Code is generated to 
check and correct the result for negative dividends at run-time for the DIV 
and MOD operators. 

The semantics of Oberon-2's DIV and MOD is specified by the function 
procedures ODIV and OMOD written in Oberon-2 and listed below. The 
implementation of ODIV and OMOD checks for negative dividends and uses 
DIV and MOD only with positive dividends and hence doesn't depend on the 
semantical differences described above, but doesn't check for overflow: 

  (** 
    y > 0
    x  = ODIV(x,y)*y + OMOD(x,y)
    0 <= OMOD(x,y) < y
  **)

  PROCEDURE OMOD(dividend, divisor: LONGINT): LONGINT;
  VAR rem: LONGINT;
  BEGIN
    IF dividend < 0 THEN
      rem:=(-dividend) MOD divisor;
      IF rem # 0 THEN rem := divisor-rem;
      END;
      RETURN rem;
    ELSE
      RETURN dividend MOD divisor
    END;
  END OMOD;

  PROCEDURE ODIV(dividend, divisor: LONGINT): LONGINT;
  VAR rem,quo: LONGINT;
  BEGIN
    IF dividend < 0 THEN
      rem:=(-dividend) MOD divisor;
      quo:=(-dividend) DIV divisor;
      IF rem # 0 THEN INC(quo);
      END;
      RETURN -quo;
    ELSE
      RETURN dividend DIV divisor
    END;
  END ODIV;

The code actually generated by A2O|H2O is much more efficient and emits 
run-time checks for divisor <= 0. 

___________________________________________________________________ 

 

C.6 Long Sets 

The pervasive type name LONGSET is reserved for a planned extension. 

___________________________________________________________________ 

 

C.7 String Literals 

The length of constant string literals is restricted to a maximum of 508 
characters. Clarification: There is currently no string concatenation or 
constructors as available in ISO Modula-2. 

___________________________________________________________________ 

 

C.8 Monadic Operator "-" for Constants and Operator Precedence 

This is only a clarification for the negation of number literals. Constant 
expressions are evaluated completely by the front-end (OP2). Note, that the 
three expressions -5 MOD 3 and -   5 MOD 3 (* note the space between the 
"-" and "5"*) and -(5 MOD 3) are all evaluated to the value -2 at compile time. 
This is different to the two expressions (-5) MOD 3 and x MOD 3 (with 
CONST x=-5;), which both have the value 1. This is due to the operator 
precedence. Negation is an addition operator which has lower precedence 
than multiplcation operator (e.g. MOD). This could cause problems when 
porting Modula-2 programs to Oberon-2 or Oberon-2 programs to other 
compilers not based on OP2. 

___________________________________________________________________ 

 

C.9 Real Number Literals 

Inconvenience: The front-end's scanner from ETH-Zuerich (OP2) has 
problems scanning and converting number literals which are very close to 
the smallest and largest value of REAL and LONGREAL numbers. Use the 
pervasive functions MIN and MAX to obtain the minimal and maximal 
values. Although the exponent range for REAL and LONGREAL (F_ and 
D_FLOATING) is [-39..38] with VAX's excess 128 binary exponent (see 
DEC VAX architecture handbook)is unsymetric, the Oberon-2 compiler 
evaluates number literals independ from the exponent sign. Then the sign is 
evaluated. Hence, when declaring the smallest possible reals 

  small=2.938736E-39;
  lsmall=2.93873587705571878D-39;

the Oberon-2 compiler reports an error (number too large). 

Current work around (see ISO Modula-2 interface modules LowReal, 
LowLong): 

  large=MAX(REAL);
  small=2.938736E-37*1.E-2;
  llarge=MAX(LONGREAL); 
  lsmall=2.93873587705571878D-35*1.D-1*1.D-1*1.D-1*1.D-1;

Please do not blame ModulaWare's Oberon-2 implementation for being 
incompatible with ETH-Zuerich's compilers. H2O did not correct this 
inconvenience. The problem was reported to ETH-Zuerich in 1992. Regis 
Crelier didn't care about this problem in OP2. 

___________________________________________________________________ 

 

C.10 Integer Range and Overflow Check 

Code is generated to check for integer overflow for longint, integer and 
shortint when using any integer operator (+, -, *, DIV, MOD or negation). 
Overflow in DIV and MOD occurs when dividing by 0 (and also when dividing 
the largest negative integer (LNI) by -1). A run-time check for divisor>0 is 
generated. Overflow in standard function ABS with LNI argument value and 
in standard procedures INC and DEC is checked. In case of an overflow, an 
integer out-of-range exception is raised, displaying min=MinInt, 
max=MaxInt, actual= offending truncated result or divisor. 

Overflow in control variables of FOR statements is also detected. Note, that 
if the generation of run-time overflow checks is disabled using the /NoCheck 
compilation command qualifier, then for example FOR i:=0 TO 65535 ... 
END with i of INTEGER type (16 bit) is an end-less loop. 

___________________________________________________________________ 

 

C.11 Integer to Integer and Char Conversion 

Code is generated to check for overflow in integer conversions (SHORT), 
when converting from LONGINT to INTEGER or from INTEGER to 
SHORTINT. In case of an overflow, an integer out-of-range exception is 
raised, displaying min=MinInt, max=MaxInt, actual= offending truncated 
result. 

Code is generated to check conversion from any integer type to CHAR. An 
integer out-of-range exception is raised if the argument of standard the 
function CHR is not in the interval [0..255], displaying min=0, max=255, 
actual= offending integer value. Note, that in case of shortint, the result of 
CHR is limited to the interval [CHR(0)..CHR(127)] because 
MAX(SHORTINT) = 127. 

No checks are made for ORD for conversion from CHAR to integer, because 
the result of ORD is always in the interval [0..255] of type INTEGER (16 
bits), so no check is required. 

___________________________________________________________________ 

 

C.12 Real Number Conversion 

Clarification and discussion: Real number conversion with ENTIER is 
implemented according to the semantics described in the Oberon-2 Report. 
ENTIER with a constant expression as argument is also evaluated 
according to this semantics. The result of ENTIER(r) is the largest integer 
not greater than r. Examples: 

ENTIER(5.5)=5; ENTIER(-5.4)=-6; ENTIER(-1.0E-36) = -1. 

Implementation details for H2O only: The VAX-11 conversion instructions 
have different semantics for negative arguments. The result type of 
ENTIER(r) is LONGINT where r can be of any real type (REAL, LONGREAL, 
G_FLOATING or H_FLOATING) with the semantics of the VAX-11 
instruction CVTxL, where x identifies the floating-point type. If an overflow is 
detected, i.e. if result of ENTIER(r) is not in the range [-2^31..2^31-1], the 
program is aborted with an an integer out-of-range exception, displaying 
min=-2^31, max=2^31-1, actual= SYSTEM.VAL (LONGINT, 
offending_real_value). 

For negative numbers the Oberon-2 semantics of ENTIER is not the same 
as the VAX conversion instruction semantics. But the goal with the H2O 
implementation was to be 100% semantically compatible with 
ETH-implementations of Oberon-2. Code is generated to check and correct 
the result for negative arguments at run-time for ENTIER. 

The semantics of Oberon-2's ENTIER is specified by the function procedure 
R_ENTIER and L_ENTIER written in ISO Modula-2 and listed below. The 
implementation checks for negative arguments and corrects the result to the 
floor if reverse conversion is not the same as the argument, but doesn't 
check for overflows. 

  TYPE LONGINT = INTEGER;

  PROCEDURE R_ENTIER(r: REAL):LONGINT;
  VAR i: LONGINT;
  BEGIN 
    i:= INT(r);
    IF (r < FLOAT(0.0)) & (FLOAT(i) # r) THEN
      DEC(i);
    END;
    RETURN i;
  END R_ENTIER;

  PROCEDURE L_ENTIER(r: LONGREAL):LONGINT;
  VAR i: LONGINT;
  BEGIN 
    i:= INT(r);
    IF (r < LFLOAT(0.0)) & (LFLOAT(i) # r) THEN
      DEC(i);
    END;
    RETURN i;
  END L_ENTIER;

Discussion: One possible solution to resolve the efficiency problem of the 
additional run-time check would be to take the ISO Modula-2 solution. which 
has two different conversion functions namely TRUNC and INT (for 
cardinals and integers). In Oberon-2, this could be ENTIER with the 
semantics as defined in the Oberon-2 Report and a new standard function 
INT with different semantics for negative arguments. 

Conversion from H_FLOATING to integer is currently not supported by H2O. 

___________________________________________________________________ 

 

C.13 Complex-Number Type 

To be able to comply with the OpenVMS calling conventions when calling 
procedures written in Modula-2 or a foreign language, the complex type 
names are declared in SYSTEM for use as parameter and function 
procedure type; no operations except assignment are allowed). 

The H2O|A2O distribution includes the ISO Modula-2 standard library 
modules ComplexMath and LongComplexMath. 

___________________________________________________________________ 

 

C.14 ISO Modula-2 Array- and Record-constructors (Aggregates) 

Clarification: Oberon-2 does not have such language constructs. 

___________________________________________________________________ 

 

C.15 [Multi-Dimensional] Open Arrays 

There is no index- or size-limit for value or variable open arrays. The 
parameter passing mechanism is compatible with Modula-2 (MVR|MaX). 

___________________________________________________________________ 

 

C.16 [Multi-Dimensional] Dynamic Arrays 

There is no index- or size-limit for dynamic arrays. The maximal number of 
array dimensions is currently limited to 4. 

___________________________________________________________________ 

 

C.17 Zero- or Negative-Length Dimensions in Dynamic Arrays 

Dynamic arrays are a new and powerful feature. Using the same access 
mechanism as with open arrays, the implementation of this feature added 
almost no complexity to the back-end (code-generator). 

If range check is not disabled with the /NoCheck compilation command 
qualifier, code is generated to check for zero- and negative length 
dimensions and for total size of dynamic arrays at run-time. For any value 
not in the interval (0 .. 2^31-1], an integer out-of-range exception is raised, 
displaying min=1, max=2^31-1, actual= offending dimension value or total 
length value. 

Note, that if range check is disabled, almost nothing can go wrong with 
zero-length dimensions. For example, take the declaration 

VAR p: POINTER TO ARRAY OF anyType; 

the statement 

NEW(p, 0); 

would be an error detected at compile time. But 

n:=0; NEW(p, n); 

must be checked at run-time. In case of any dimension being 0, the program 
is aborted. In the above example, LEN(p^) is equal to 0, which means that 
no array elements are allocated. 

Discussion: In the example below, the outer while-loop is not entered for 
LEN(p^,0)=0: 

  VAR p: ARRAY OF ARRAY OF Type;
  BEGIN
    i:=0; j:=0;
    NEW(i,j);
    WHILE i < LEN(p^,0) DO
      WHILE j < LEN(p^,1) DO
        ... p^[i, j] ... 
       INC(j);
      END;
      ... p^[i] ... 
      INC(i);
    END;
  END ...

So why care about zero-length dimensions? Because everything that can be 
cecked shall be checked. Recommendation: Never disable any run-time 
checks. 

___________________________________________________________________ 

 

C.18 Structured Function Return Values 

Extension: Function procedures may be of any type in A2O|H2O, but 
selection, indexing and dereferencing of the result is not allowed. Note, this 
is a semantical extension and not a syntax change. The Oberon-2 Report 
explicitly defines that function types can neither be record nor array types, 
which means that function types are restricted to basic types only. 
ETH-Zuerich implementations (including OP2) do not allow structured 
function return values. Some other commercial Oberon-2 compilers support 
this feature since 1992. Since it was easily implemented on OpenVMS and 
because it must be possible to call function procedures of structured type 
written in ISO Modula-2 or in a foreign language from Oberon-2, 
ModulaWare decided to make this extension. 

H2O only: For a type size of 5, 6, 7, or more than 8 bytes, the function result 
is treated as a hidden VAR parameter located before the first formal 
parameter. Space for the result is allocated prior to evaluating the 
procedures actual parameter list and reference to bottom of this stack space 
is passed as actual parameter. This is according to the OpenVMS procedure 
calling standard; for other function type sizes (1, 2, 3, 4, and 8 bytes), the 
result is returned in registers R0 and R1 as with the Modula-2 (MVR V4). 
The function result type size must be less than 2^31 -1. 

A2O only: If the result is of any pervasive type, it is returned in register R0 or 
if floating point in F0. Complex data types are returned in F0/F1 
(real/imaginarty part). All other data types are returned on the stack via a 
first hidden variable parameter of the function. 

___________________________________________________________________ 

 

C.19 Standard Procedures 

PROCEDURE COPY (source: ARRAY OF CHAR; VAR target: ARRAY OF 
CHAR) 

Clarification: COPY copies maxChars := MIN(LEN(source), LEN(target)) 
between (open) character arrays with different types of any size from source 
to target. 

Note, that the copy process on Alpha doesn't stop after the first CHR(0) in 
source string was detected. This allows a simple and short string copy loop, 
one quadword at a time. 

If LEN(target) >= LEN(source), the source is assumed to contain a 
terminating CHR(0); the source string is copied completely without looking 
for CHR(0). 

In case of LEN(target) < LEN(source), LEN(target) bytes are copied from 
source to target and the last byte is overwritten with CHR(0); independent 
from whether one or more CHR(0) were already copied to target. 

The target string always contains a terminating CHR(0). This is the 
semantics required by the Oberon-2 Report. 

___________________________________________________________________ 

PROCEDURE SYSTEM.MOVE (source, destination, locsToMove: 
LONGINT) 

H2O only: MOVE and COPY are implemented using the VAX instruction 
MOVC3 (move block of memory). Since the number of bytes to be moved is 
limited to 65535 with a single MOVC3, this limit is tested at run-time and a 
tight loop (MOVC3 and ACBL) is executed, if the number of bytes to be 
moved (locsToMove) exceeds 65535 bytes. 

A2O only: MOVE and COPY call the A2O|MaX run-time library routine 
(MOD$RTSCOPY). No length restrictions apply. 

Clarification: SYSTEM.ADR is parameter compatible with LONGINT. 

locsToMove must be positive. 

___________________________________________________________________ 

PROCEDURE SYSTEM.(GETREG(n,v), PUTREG(n,x), GET(a,v), 
PUT(a,x)) 

H2O only: implemented, but unsupported by ModulaWare. 

A2O: GET and PUT transfer the type size of the v and x from and to memory 
address a. 

GETREG and PUTREG read and write register n, using an integer register if 
the type of v and x is a not a floating point type. 

This avoids the introduction of two new function such as MaX's 
SYSTEM.REGISTER and SYSTEM.FREGISTER without sacrifying 
efficiency because casting between floating and non-floating point type 
would involve a memory operation anyway. 

___________________________________________________________________ 

PROCEDURE HALT(x: integer_constant) x must be greater than 19; the 
program is terminated by signalling a halt exception with one parameter of 
value x. 

___________________________________________________________________ 

 

C.20 Standard Functions 

PROCEDURE ASH(x,n) 

H2O only: Implemented using VAX's ASHL arithmetic shift longword 
instruction. Overflow is detected on a left shift if any bit shifted into sign bit 
position differs from the sign bit of the first operand. In this case, an integer 
out-of-range exception is raised, displaying min=-2^31, max=2^31-1, 
actual= offending argument value. 

A2O only: x is any integer (8 to 64 Bit) 

___________________________________________________________________ 

PROCEDURE SYSTEM.BIT(a,n): BOOLEAN 

H2O only: Not implemented; implementation restriction flagged by the 
compiler. 

A2O only: a of type LONGINT is the memory address; 0 <= n <= 31 

___________________________________________________________________ 

PROCEDURE SYSTEM.CC 

Not supported because not available on VAX and Alpha. 

___________________________________________________________________ 

PROCEDURE SYSTEM.LSH 

H2O only: Currently implemented as pervasive Oberon-2 function ASH 
(arithmetic shift) with VAX instruction ASHL. There is no logical shift 
instruction on the VAX. 

A2O only: Currently implemented as pervasive Oberon-2 function ASH. 

___________________________________________________________________ 

PROCEDURE SYSTEM.ROT 

A2O only: currently not supported. The Alpha processor has no rotate 
instruction. A sequence of instructions is generated, but these operate on a 
64 bit register, which gives a portability problem. 

___________________________________________________________________ 

 

C.21 Additional Standard Function in SYSTEM 

Extension: 

PROCEDURE SYSTEM.LENGTH (semantics as in ISO Modula-2) 

The additional standard function LENGTH returns the number of characters 
in a string. H2O uses the VAX LOCC (locate character) intruction which is 
defined as follows: 

  TYPE card = LONGINT; (* H2O only: restricted to 16 bits i.e. [0...6
5535] *)

  PROCEDURE LENGTH* ( s : ARRAY OF CHAR ) : card;
  VAR i : card;
  BEGIN i := 0;
    WHILE (i < LEN(s)) AND (s[i] # CHR(0)) DO
      INC(i);
    END;
    RETURN i
  END LENGTH;

H2O only: Implementation restriction: For LEN(s) greater than 65535, 
LENGTH(s) must be smaller than 65536. For static types, the compiler 
doesn't allow arrays with larger size argument for LENGTH. For open and 
dynamic arrays, LEN(s) > 65535 is checked at run-time and if true, the 
program is terminated with an LENGTH out-of-range exception, displaying 
min=0, max=65535, actual=LEN of offending argument. 

A2O only: in-line code is generated; there is no restriction for the length of 
the string. 

___________________________________________________________________ 

 

C.22 String Comparison 

Clarification: String compare is only allowed for arrays with element type 
CHAR. The comparison is performed starting at index 0 until either one of 
the string elements is CHR(0) or until the index is equal to maxChars := MIN 
(SYSTEM.LENGTH (string1), SYSTEM.LENGTH (string2)). Because 
SYSTEM.LENGTH (str) returns LEN(str) if str doesn't contain a CHR(0), this 
implies that it is allowed to also compare non-CHR(0)-terminated strings. 
This is important when calling foreign language procedures with string result 
values. Note that ISO Modula-2 doesn't require CHR(0)-termination for 
strings. 

H2O only: Implementation restriction: Using the VAX instruction LOCC and 
CMPC (compare character strings), the size of the arrays to be compared 
must not exceed 65535 bytes. 

For open and dynamic arrays, maxLen := MIN (LEN(string1), LEN(string2)) 
is checked at run-time and if maxLen > 65535, the program is terminated 
with a string compare out-of-range exception, displaying min=0, 
max=65535, actual=maxLen. 

A2O only: in-line code is generated; there is no restriction for the length of 
the strings to compare. 

___________________________________________________________________ 

 

C.23 Array Index Check 

For illegal array index values, a subscript-out-of-range exception is raised. 

___________________________________________________________________ 

 

C.24 CASE Statement Expression Check 

If the value of an expression in a case statements without ELSE-part, does 
not have a corresponding case label an index-out-of-range exception is 
raised. 

___________________________________________________________________ 

 

C.25 CASE Statement Label Range 

Implementation restriction: The labels of a CASE statement must not be 
greater than 7FFFFFFFH. Large case label ranges such as CASE i OF 
0..65535: ... and CASE i OF 0: ... | 65535: ... should be avoided, since the 
size of the branch table produced by the compiler is linearly proportional to 
the range of the lowest and highest case label value used in a case 
statement. 

H2O only: The maximal code table branch distance is limited to 32767 
bytes. 

A2O only: The maximal code table branch distance is limited to one million 
instructions (though actually limited to about 100000 instructions because of 
A2O's procedure code buffer size limitation). 

___________________________________________________________________ 

 

C.26 Pervasive Function LEN 

Clarification only: LEN has an optional second argument used with 
multi-dimensional arrays. Take the declaration 

VAR a: ARRAY 1 OF ARRAY 2 OF ARRAY 3 OF Type; 

Note, that the array dimensions are counted from the left: 

  LEN(a, 0) = LEN(a) = 1;
  LEN(a, 2) =  2;
  LEN(a, 3) =  3;

___________________________________________________________________ 

 

C.27 Pervasive Function SIZE 

Clarification: The argument of SIZE must be a type. Variable designators, 
string constants and expressions are not allowed as arguments. 

___________________________________________________________________ 

 

C.28 FOR Statement 

Implementation restriction: The BY-step must not be greater than 7FH 
(shortint) 7FFFH (integer), and 7FFFFFFFH (longint). A non-constant 
TO-expression of a FOR statement is evaluated before entering the loop 
and stored in a hidden variable called "@for" in the scope of the FOR 
statement. See also section C.10 (range check) above. 

___________________________________________________________________ 

 

C.29 Type Test 

This is a clarification only. If type test is performed with a NIL-pointer value, 
an access violation occurs and the program is terminated with an type guard 
or type test failed exception. This is the semantics of Oberon-2 which was 
confirmed by ETH-Zuerich (Regis Crelier). 

Example 29.1: assume that node is a pointer variable to a base type of 
MyNode. If node = NIL then the type test node(MyNode) will crash, because 
NIL doesn't have a type descriptor. 

Take care and use the following construct as a replacement of x := 
node(MyNode) where node could be NIL: 

IF node = NIL THEN x:= NIL ELSE x := node(MyType) END; 

Attention: Other implementations could be less restrictive in this case and 
generate code for the above construct. The argument could well be: Any 
record type an extension of the NIL-type, because NIL is compatible with 
any pointer type. 

Another problem is with NEW(node(MyType)); Here one could argue that 
the compiler could know that the type test is an argument of the pervasive 
procedure NEW and code should be generated with type tag and storage for 
type MyType. But this is not the case (node = NIL is an error). 

___________________________________________________________________ 

 

C.30 Imports 

Implementation restriction: Each compilation unit may import up to 63 
modules. Other implementation may be restricted to much less than 63. 

___________________________________________________________________ 

 

C.31 Directory Version Limit 

Implementation restriction: The default directory version limit must be set to 
a value greater than 1. This is because the compiler always generates a 
new symbol file with the symbol file name constructed from the compilation 
unit module name. Then new and the old symbol files are compared. If both 
versions are identical, except for the module key (time stamp), the new 
symbol file is deleted and the old is kept. This method doesn't work with a 
directory limit set to 1. 

___________________________________________________________________ 

 

C.32 Symbolic Debugger Data Types 

Implementation restriction: Variables of basic data type SET are displayed 
as unsigned 32 bit integers. Use the debugger command examine/binary 
setVariable to get the bit pattern of the set displayed. 

___________________________________________________________________ 

 

C.33 Standard Procedure ASSERT 

PROCEDURE ASSERT (b: BOOLEAN; n: const_number); 

If b is FALSE then an assertion failed exception with the parameter value n, 
same as HALT(n) is raised. 

Clarification: Even if b is an expression, possibly containing a function call, 
the whole expression is not evaluated if the /NoAssert_eval compilation 
qualifier is used. It's wise not to call a function which has a side-effect in b. 

H2O only: ASSERT is currently not yet implemented. 

This procedure was not defined in the first versions of the Oberon-2 Report, 
but introduced in the front-end OP2 by Regies Crelier in the fall of 1992. So 
it's possible that not all Oberon-2 compilers know this procedure. 

___________________________________________________________________ 

 

C.34 Conversion from Integer to Real 

Clarification: The pervasive function LONG can't be used to convert a 
expression of LONGINT type to REAL type. There is no explicit function for 
that purpose. The assignment real := integer automatically converts from 
any integer type to REAL type. 

___________________________________________________________________ 

 

C.34 Open Array Parameter Compatibility 

Clarification: A single dimensional open array of base type CHAR is 
compatible with a constant of type CHAR. 

 

Example 34.1: 

VAR str=ARRAY len OF CHAR; ch: CHAR; 

PROCEDURE P(a: ARRAY OF CHAR); END P; 

... P("abc..."); P("a"); P(str); 

is allowed, but P(ch) is not allowed. 

 

Example 34.2: 

TYPE T = RECORD ... END; 

VAR vT: ARRAY OF T: v: T; 

PROCEDURE Q(a: ARRAR OF T); END Q; 

... Q(vt); 

Only variable open arrays of SYSTEM.BYTE are compatible with any 
structure. 

 

Example 34.3: 

VAR ab: ARRAY 2 OF SYSTEM.BYTE; VAR at: ARRAY 3 OF REAL; 

PROCEDURE S(a: ARRAY OF SYSTEM.BYTE): END S; 

PROCEDURE T(VAR a: ARRAY OF SYSTEM.BYTE): END T; 

... S(ab); T(ab); T(ab[0]); T(at); T(at[0]); 

are allowed, but S(at) is not allowed. 

___________________________________________________________________ 

 

C.35 SYSTEM.VAL versus ISO Modula-2's SYSTEM.CAST 

Clarification for Oberon-2 and comparison with ISO Modula-2: 

Oberon-2's SYSTEM.VAL is ISO Modula-2's SYSTEM.CAST. Modula-2's 
pervasive function VAL is not available in Oberon-2. Oberon-2's 

SYSTEM.VAL (T, expr) 

SYSTEM.VAL (T, design) 

is also allowed for unequal sizes SIZE(t) and type size of expression expr or 
variable designator design. 

This is also allowed in ISO Modula-2's SYSTEM.CAST. But in ISO 
Modula-2, the smallest of the two type sizes determines how much from the 
source (expr or design) is touched and this forms an expression of type T. In 
Oberon-2, this size is always determined by SIZE(T). 

Also, in case of design being a variable designator, SYSTEM.VAL (T, 
design) simply casts the type of design to T in Oberon-2. In this case, the 
expression SYSTEM.VAL(T, design) is still a variable designator although it 
looks like a function call. What's the effect? SYSTEM.VAL(T, design) can be 
substituted as actual parameter to a variable formal parameter of type T in a 
procedure call. This is not possible in ISO Modula-2 and seems to be a very 
strange feature of Oberon-2, but it allows the elimination of auxiliary 
assignments. 

 

Example: 35.1: 

TYPE T1=...; T2=...; 

PROCEDURE P(VAR v: T1); END P; 

VAR x1: T1; x2: T2; BEGIN 

x1:=SYSTEM.VAL(T1, x2); P(x1); 

could be abbreviated by simply writing 

P(SYSTEM.VAL(T1, x2)); 

___________________________________________________________________ 

 

C.36 Symbol File Change 

A new symbol file is generated when compiling a module where any 
changes were made in the declaration of the exported objects. As shown in 
the following example, also when the type T of a not directly exported type is 
changed, the compiler will generate a new symbol file, even if M1.T has the 
same size. 

  MODULE M;
  TYPE X* = POINTER TO T;
    T=RECORD ... END;
  END M.

  MODULE M;
  IMPORT M1;
  TYPE X* = POINTER TO T;
    T=M1.T;
  END M.

This is because the non-exported type T is exported indirectly via type X 
which means that the type description of M1.T together with the module 
name M1 are also stored in the symbol file of M. 

___________________________________________________________________ 

 

C.37 Variable Record Parameter Compatibility with Modula-2 (MaX|MVR) 

In case of a variable parameter of record type (VarParRec), Oberon-2 
passes an (hidden) additional argument (type tag) as parameter. 

A2O only: Modula-2 is not treated as a foreign language; it's considered to 
be a friend not a foreigner. MaX uses the same calling conventions as A2O 
for VarParRec. When O2 calls M2, the hidden paramter in VarParRec is 
ignored. When M2 calls O2, the tag value 0 is used, because there are no 
type descriptors in M2. An access violation occurs if the O2 procedure tries 
to operate on this tag value. 

A2O and H2O: When calling foreign language procedures, this mechanism 
can be suppressed by using the $N suffix (see C.1). 

H2O only: In case of the foreign language being Modula-2 (MVR), it is 
possible to detect whether the call comes from Oberon-2 or Modula-2 within 
the procedure written in Modula-2. The ISO M2 Std Lib module SysClock 
serves as example how to deal with this situation. 

IMPLEMENTATION MODULE SysClock; (* Written in Modula-2 by Elmar 
Baumgart, ModulaWare, 1992 

GD/10-Dec-1992: GetClock has a VarParRecord and a call from Oberon-2 
would put two parameters on the calling stack (instead of only one from 
Modula-2): 

1. the ADR(callers_userdata_variable) and 

2. a non existing type descriptor tag pointer (references 
SYSCLOCK.$OBJECTS$.) 

So we have the following three choices to get Oberon-2 and Modula-2 
interlanguage compatibility (H2O and MVR): 

A) take care in the Oberon-2 interface module to tell the compiler that no 
descriptor should be passed: when creating an Oberon-2 interface module 
which serves for Modula-2 interface purposes only and there is a procedure 
that has a VAR parameter of a record type then instead use ARRAY OF 
SYSTEM.BYTE as formal parameter type. This would work but the type 
check is lost in Oberon-2. 

B) guarantee full type safety and take care of the caller in Modula-2 library 
modules: we have the argument pointer (ap) = number of arguments, hence 
we could find out whether this routine was called from Oberon-2 [(ap)=2] or 
Modula-2 [(ap)=1 in SysClock.GetClock]. 

C) guarantee full type safety through introduction of an additional keyword 
STRUCT in addition to RECORD as in other commercial Oberon-2 
implementations. No type descriptors are generated for STRUCTs. 

We decided to go for alternative B; see procedure GetClock below. 

*)

FROM SYSTEM IMPORT QUADWORD, SHORTWORD, ADR, 
  LONGWORD, CAST, ADDRESS, REGISTER, ADDADR;

CONST AP=12;
TYPE WORD=LONGWORD;

PROCEDURE ArgPtr(w: WORD; offset: INTEGER): WORD;
VAR a: ADDRESS;
    p: POINTER TO WORD;
BEGIN
  a:=CAST(ADDRESS,w);
  a:=ADDADR(a,offset);
  p:=a;
  RETURN p^
END ArgPtr;

...

PROCEDURE GetClock(VAR userData: DateTime);
(* Assigns local date and time of the day to userData *)
VAR
  tim: QUADWORD; status: CARDINAL;
  timbuf: ARRAY[0..6] OF SHORTWORD;
  ud: DateTime;
  udp: POINTER TO DateTime;
BEGIN
  ...
  (* find out whether we are called by M2 or O2: 0(ap)=num_par *)
  IF CAST(INTEGER, ArgPtr(REGISTER(AP),0))=1 THEN (* M2 *)
    udp:=ADR(userData);
  ELSE (* O2: 4(ap)=tap; 8(ap)=ADR(ud) *)
    udp:=CAST(ADDRESS,ArgPtr(REGISTER(AP),8));
  END;

  (* don't touch the parameter userData;
     modify the variable parameters data through upd^: *)
  WITH udp^ DO
    year:= ...
    month:= ...
    ...
  END;
END GetClock;

...

END SysClock.

___________________________________________________________________ 

 

C.38 Extended Type Hierarchy 

The Oberon-2 report defines the type hierarchy as 

SHORTINT <= INTEGER <= LONGINT <= REAL <= LONGREAL 

A2O introduces SIGNED_64 and IEEE floating point types, which leads to 
the extended type hierarchy 

SHORTINT <= INTEGER <= LONGINT <= SIGNED_64 <= REAL <= 
LONGREAL 

Integer hierarchy (for LONG/SHORT): SHORTINT <= INTEGER <= 
LONGINT <= SIGNED_64 

Floating point hierarchy (for LONG/SHORT): REAL <= LONGREAL 

Internal hierarchy for assignment of values to floating point variables is 

SHORTINT <= INTEGER <= LONGINT <= F <= D <= S <= G <= T <= 
H_FLOATING 

Floating point literals are of type f_/d_floating with /noieee compilation 
option and of type s_/t_floating otherwise. Note that currently there are no 
literals of type SIGNED_64. Values greater than the range of LONGINT can 
be constructed at run-time. For example MIN and MAX(SIGNED_64) can be 
constructed for example with: 

    VAR i, maxint, minint: SYSTEM.SIGNED_64;
    ...
      i := MAX(LONGINT);
      maxint  := i*i*2 + i*4 + 1;
      minint  := - maxint - 1;

_____________________________________________________________

IMPRESSUM: The ModulaTor is an unrefereed journal. Technical papers are to be taken as working papers and personal rather than organizational statements. Items are printed at the discretion of the Editor based upon his judgement on the interest and relevancy to the readership. Letters, announcements, and other items of professional interest are selected on the same basis. Office of publication: The Editor of The ModulaTor is Guenter Dotzel; he can be reached by tel/fax: [removed due to abuse] or by mailto:[email deleted due to spam]


Home Site_index Contact Legal Buy_products OpenVMS_compiler Alpha_Oberon_System DOS_compiler ModulaTor Bibliography Oberon[-2]_links Modula-2_links

Amazon.com [3KB] [Any browser]


Webdesign by www.otolo.com/webworx, 14-Nov-1998