Go to the first, previous, next, last section, table of contents.


Pragmas

A pragma is a directive embedded in the source code of an Oberon-2 module. Although a pragma is not part of a module's declarations or executable code, it influences how the module is translated by the compiler. Pragmas provide mechanisms for conditional compilation of pieces of source code, for embedding code generation directives into a module, and for selection of language features.

Pragmas are not defined as part of the programming language Oberon-2; they are an extension added to OOC. Pragmas are embedded in the source code by special delimiters <* and *>. Like comments, they are ignored by the parser, but they can change variables that control the compiler's behavior and they can "remove" text from a module's source code by excluding a piece of source text from the translation process. Superficially, pragmas resemble preprocessor directives, as known from the programming language C, but they are much more restricted, and they are tightly integrated into the OOC compiler.

Pragma Syntax

A pragma statement is either a variable definition, an assignment, a conditional statement, or a save/restore command.

A pragma sequence consists of zero or more pragma statements, which are separated by semicolons.

A pragma starts with a `<*', followed by a pragma sequence, and ends with a `*>'.

The full syntax for pragmas in EBNF is as follows:

Pragma        = "<*" PragmaSeq "*>".
PragmaSeq     = PragStatement {";" PragStatement}.
PragStatement = [Assignment | Definition | SaveRestore | Condition].

Assignment    = ident ":=" Expr.
Definition    = "DEFINE" ident ":=" Expr.
SaveRestore   = "PUSH" | "POP".

Condition     = IfPart {ElsifPart} [ElsePart] EndIfPart.
IfPart        = "IF" Expr "THEN" PragmaSeq.
ElsifPart     = "ELSIF" Expr "THEN" PragmaSeq.
ElsePart      = "ELSE" PragmaSeq.
EndIfPart     = "END".

Expr          = SimpleExpr [Relation SimpleExpr].
Relation      = "=" | "#" | "<" | "<=" | ">" | ">=".
SimpleExpr    = Term {"OR" term}.
Term          = Factor {"&" Factor}.
Factor        = "TRUE" | "FALSE" | "(" Expr ")" | "~" Factor |
                string | integer | ident.

The symbols `ident', `string', and `integer' are defined like their Oberon-2 counterparts. An underscore is permitted as part of an `ident' in place of a character. No Oberon-2 keyword, nor the pragma keywords TRUE, FALSE, PUSH, POP, or DEFINE can be used as the name of a variable.

Any Oberon-2 string (including the empty string) and all integer numbers (including hexadecimal numbers) are valid values. Character constants (like `20X') are equivalent to string constants of length 1 (or length 0 in the case of `0X'). Hexadecimal constants are interpreted just like Oberon-2 constant literals.

Example:

<* DEFINE CpuType := "AMD" *>

<* IF CpuType="AMD" THEN *>
IMPORT AMDPrimitives;
<* ELSIF CpuType="Motorola" THEN *>
IMPORT MotorolaPrimitives;
<* END *>

Here a variable `CpuType' is introduced and set to the value `"AMD"'. The variable can then be used to switch between two code variants: The first variant is used if `CpuType' is set to `"AMD"', and the other is used if it is set to `"Motorola"'. Neither of the variants is used if `CpuType' has any other value.

Pragma Semantics

A pragma (the entire sequence of characters starting with `<*' and ending with `*>') can end with a pragma statement, or between the parts of a Condition. The parts of a condition, and all other pragma statements, must be textually complete within a single pragma:

<* DEFINE CpuType := "AMD" *>        (* Legal *)
<* DEFINE CpuType := *> <* "AMD" *>  (* Illegal! *)

<* IF b THEN *>                      (* Legal *)
<* IF b *> <* THEN *>                (* Illegal! *)

Also, note that

Conditional Compilation

Conditions are a special type of pragma statement; the parts of a condition can extend over several pragmas. That is, conditions can be, and usually are, interspersed with plain Oberon-2 source text, and potentially additional pragmas as well. This provides for conditional compilation, which allows lines of source text to be skipped or eliminated by the compiler based on the evaluation of a boolean condition.

Pragma conditions assume the various forms of IF statements: they consist of an opening `IfPart', any number of `ElsifParts', an optional `ElsePart', and a terminating `EndIfPart'. Nested condition statements are allowed.

If the `Expr' of the `IfPart' is true, the text that follows is fully interpreted until reaching the next corresponding `ElsifPart', `ElsePart', or `EndIfPart'; in this case, any remaining text (i.e., any ElsifPart and ElsePart clauses) is skipped until encountering the corresponding `EndIfPart'. If the `Expr' of the `IfPart' is false, the text immediately following the `IfPart' is skipped. If the next condition is an `ElsifPart', its `Expr' is tested, and so on. If no condition holds, the text following the `ElsePart' is interpreted.

"Skipping" of text means that no interpretation is done except recognition of comments and condition statements. That is, although the pragma syntax in the skipped text is checked, the meaning of the pragmas is not recognized. This implies that type errors, or references to undefined variables, are ignored in skipped pragmas.

Note that a pragma sequence can appear as part of a condition:

<* IF Cpu = "Intel" THEN
  DEFINE HaveManyRegisters := FALSE;
  DEFINE InsertFunnyRandomBehaviour := TRUE
END *>

The parts of a condition may exist within a single pragma, or may extend across several pragmas. Both of the following are legal:

<* IF b THEN END *>
<* IF b THEN *><* END *>

Boolean Operators and Relations

The Oberon-2 type rules apply to boolean operators and relations. The expressions in an `IfPart' and `ElsifPart' have to be of boolean type.

The boolean operators & and OR are evaluated like their Oberon-2 counterparts: if evaluation of the left side is sufficient to know the result, the right side is not evaluated. Expressions are always checked syntactically, even if they are not evaluated; this works exactly like pragmas that are skipped due to conditional compilation.

Pragma Variables

New pragma variables are defined by the definition statement `DEFINE var := value'. The identifier `var' must not be already known, or an error message is produced. That is, one cannot override existing variables that are predefined by the compiler, or were defined earlier. But once it is defined, the value can be changed with an assignment. The scope of a variable defined in a module extends from the point of its definition to the end of the module.

PUSH and POP

The pragma statements PUSH and POP operate on a stack. PUSH saves the current state of all pragma variables, both predefined and user-defined. POP restores the values of saved variables as set by the corresponding PUSH, and removes the associated states from the stack. Variables introduced after the PUSH operation are not affected by this; a POP does not return variables to an undefined state.

Predefined Pragma Variables

Every implementation of OOC predefines a number of pragma variables. These

The compiler provides safe defaults for all predefined variables. That is, all useful run-time checks are enabled and all compiler specific options are disabled. These values can be redefined in the initialization file, and by command line options. Predefined variables can also be changed through pragma assignments.

Example:

<* IndexCheck := TRUE *>     
  generate code for index check
<* RangeCheck := FALSE *>
  switch off detection of invalid set elements

All run-time checks supported by the particular compiler are enabled by default. The compiler issues a warning when an attempt is made to

The following tables lists the pragma variables that control the generation of run-time checks by the compiler. All variables are of type boolean. Setting such a variable to `TRUE' enables the appropriate run-time check; this means that code is inserted to raise an exception if the tested condition fails. Setting the variable to `FALSE' disables the checks.

`CaseSelectCheck'
Raise an exception if the value of the selection expression of a CASE statement does not match any of the labels and no ELSE part is specified.
`IndexCheck'
Raise an exception if the value of an array index is not in the range `0 <= index < LEN(array)'.
`DerefCheck'
Raise an exception if a pointer of value NIL is dereferenced. Note that applying a type test or type guard to NIL, or an attempt to activate a procedure value of NIL, also triggers this exception.
`FunctResult'
Raise an exception if the end of a function procedure is reached without executing a RETURN statement.
`RealOverflowCheck'
Raise an exception if a real number operation overflows.
`RealDivCheck'
Raise an exception when attempting to divide a real number by zero.
`RangeCheck'
Raise an exception if a set element is outside the range of possible values for the applicable set type. This applies to INCL(), EXCL(), IN, and the set constructor `{a..b}'.
`OverflowCheck'
Raise an exception if the result of an integer operation overflows.
`IntDivCheck'
Raise an exception when attempting to divide an integer number by zero. Note that this applies to both DIV and MOD.
`TypeGuard'
Raise an exception if a type guard fails.
`StackCheck'
Raise an exception on stack overflow. More precisely, if StackCheck = TRUE, stack overflows are detected when entering a procedure body `B'. Note that, even if `B' is compiled with StackCheck = TRUE, procedures called from `B' might still overflow the stack undetected, unless they have also been compiled with this run-time check enabled. On most systems, stack overflows are detected by the operating system without any need for special software stack checks by the program.

The following pragma variables adjust semantical checks and code generation of the compiler:

`ConformantMode'
Selects one of two slightly different language variants. Setting this to `TRUE' enables conformant mode, which tells the compiler to behave like an ETH compiler; modules compiled with conformant mode enabled should generally work with any compiler. Changing the variable to `FALSE' (the default) produces results that more closely match the language report. See section Non-conformant Mode, for reasons why non-conformant mode is considered preferable.
`IdentLength'
An integer value that determines the maximum number of characters allowed in an identifier. Negative values produce warnings (whereas positive values generate errors) when `Length(ident) > ABS(IdentLength)'. The default value is `MIN(LONGINT)' (i.e., no length restriction at all). The Oakwood Guidelines suggest that compilers should support a minimum of 23 significant characters.
`StringLength'
An integer value that sets the maximum number of characters allowed in a literal string. This works like `IdentLength'.
`Assertions'
If set to `FALSE', all ASSERT statements are discarded. The default value is `TRUE'. Caution: Disabling assertions also discards the boolean expression being asserted, including all its side-effects. Therefore, tested expressions in assertions should never produce side-effects.
`Initialize'
If set to `TRUE', variables and memory blocks are automatically initialized to zero. The default is `FALSE'.
`PoisonHeap'
An integer value that defines the byte pattern, which is used to intialize memory taken from the heap by NEW or SYSTEM.NEW. If negative, the allocated objects are either left in an undefined state, or filled with zero bytes, depending on the value of `Initialize'. If non-negative, the bytes of the allocated memory block are set to `PoisonHeap MOD 256'. This pragma variable should be set at the beginning of the module, and not be changed afterwards. The default is `-1'.
`Warnings'
Tells the compiler whether to generate warnings. The default is `FALSE', which disables warning messages.

Pragma variables with the name prefix `COMPILER' identify the compiler in use. Unlike the variables above, changing them has no effect on the compilation process. They should be considered read-only variables, and never be modified by the user.

`COMPILER'
A string describing the compiler or family of compilers. All implementations of OOC define this to `"OOC"'.
`COMPILER_VERSION'
A string containing the compiler version, for example `"1.5.1"'.
`COMPILER_MAJOR'
Major version number of the compiler. That is, the first number from the version string in integer representation.
`COMPILER_MINOR'
Minor version number of the compiler. That is, the second number from the version string in integer representation.

Information about the target system is provided by variables with the name prefix `TARGET'. In this context the term target system refers to the run-time environment for the execution of a compiled program.

`TARGET_OS'
This string describes the target operating system, for example `"Unix"'.
`TARGET_ARCH'
The value of this variable identifiers the target architecture, that is, the CPU family. Examples are `"ix86"', `"PPC"', `"Alpha"', or, for oo2c, `"ANSI-C"'.
`TARGET_ARCH_MINOR'
If the compiler is set to emit code that only runs on a subclass of the general CPU family, this variable names that subset of the family. For example, the `"ix86"' family could be subdivided into `"i386"', `"i486"', and so on. If the generated code works for all members of the target architecture, this variable holds the empty string.
`TARGET_INTEGER'
This is the number of bits in the largest integer type supported for the target. The basic types HUGEINT and SET64 are supported if it is `64' or more.
`TARGET_ADDRESS'
Number of bits used to represent a memory address of the target architecture.
`TARGET_BYTE_ORDER'
This string describes the byte order convention used by the target system. For a little endian target, like `"ix86"', this is `"0123"', for a big endian target, like `"m68k"', it is `"3210"'. If the byte order is not known beforehand, as is the case with oo2c, the variable is set to `"unknown"'.


Go to the first, previous, next, last section, table of contents.