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
XDS_family
DOS_compiler
ModulaTor
Bibliography
Oberon[-2]_links
Modula-2_links
Onduleurs