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.
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.
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
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 *>
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.
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.
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.
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.
CASE
statement does not match any of the labels and no ELSE
part is
specified.
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.
RETURN
statement.
INCL()
, EXCL()
,
IN
, and the set constructor `{a..b}'.
DIV
and MOD
.
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:
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.
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'.
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.
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.
oo2c
, `"ANSI-C"'.
HUGEINT
and SET64
are supported if it
is `64' or more.
oo2c
, the variable
is set to `"unknown"'.
Go to the first, previous, next, last section, table of contents.