TOC PREV NEXT INDEX DOC LIST MASTER INDEX



Ada Preprocessor


Introduction

The Apex Ada compiler contains a preprocessor capable of macro substitution, conditional compilation and inclusion of named files. Input is Ada source intermixed with preprocessor control lines and macro substitutions. Output is Ada source; all control lines are converted to comments, all sourece files are included and all macro substitutions are replaced.

Preprocessor control lines begin with a sharp character (#) and are recognized only by the preprocessor. Syntax of the Ada preprocessor is Ada-like and can span across multiple control lines. The preprocessor achieves conditional compilation by evaluating expressions. The primary components are identifiers defined either in a directives file or locally in the source. The preprocessor supports a simple form of macro substitution; the value of a defined identifier is replaced wherever that identifier, prefixed by a dollar sign ($), occurs in the Ada source.

The Apex Ada Preprocessor is particularly useful for developing code for multiple platforms in which there may be slight differences required between the versions. The facilities provided by the preprocessor enable maintenance of a single file which can be compiled with options that determine which variant is desired.

The following topics are covered in this section:


Invocation

Preprocessor files use the same naming convention as Ada source files, except that the suffix .app is used instead of .ada. Note also, the .app files for separate subunits need to be named using the fully expanded name of the separate subunit.

When the compiler encounters a file with the suffix .app, it automatically invokes the Ada preprocessor to create a read-only file with the same base name, but with the suffix .ada. Apex maintains dependency information between the .app file and the corresponding .ada file. If the .app file is updated, the next time the compiler is run, the preprocessor will be invoked to update the .ada file. The user is cautioned not to manually update the read-only .ada file. If the timestamp on that file is somehow changed, the preprocessor will be invoked to regenerate the .ada file from the .app file. Any changes made to the .ada file will be lost.


Chapter Conventions

This section describes the syntax and semantics of the Ada PreProcessor (APP) language. The APP language is based on a subset of the Ada language.

Each section introduces its subject, gives any necessary syntax rules and describes the semantics of the corresponding language constructs. Examples, Notes, Differences, and Additional Topics can appear at the end of a section.

Examples illustrate the possible forms of the constructs described. Notes emphasize consequences of the rules described in the section or elsewhere. Differences highlight differences between the preprocessor language and the Ada language. Additional Topics attract the attention of readers to a term or phrase having a technical meaning defined in another section.

The context-free syntax of the APP language is described using a simple variant of Backus-Naur-Form, as described in the Ada Language Reference Manual, (Ada83 LRM 1.5, Ada95 LRM 1.1.4). The sharp character, denoting a control line, does not appear in any of the syntax rules; it is implicit that all constructs must appear on one or more control lines. Rules dealing with how Ada text is intermingled with the constructs and the effects on them are explained in the appropriate sections.

The following topics are covered in this section on Chapter Conventions:

Lexical Elements

APP uses the ISO graphic character set (see Ada83 LRM 2.1, Ada95 LRM 2.1).

The basic lexical elements of the preprocessor consist of delimiters, identifiers, numeric literals, string literals and comments. Rules of composition are those defined by Ada (see Ada83 LRM 2, Ada95 LRM 2).

These lexical elements are recognized on lines that begin with the sharp character (control lines); the sharp has no other effect. A control line contains a sequence of lexical elements (possibly none)

Text on non-control lines consists of a sequence of separate Ada lexical items. An exception to this is a macro substitution, which is composed of the dollar character followed immediately by an identifier.

Intermingling control and non-control lines has no effect on the syntax of either language.

When the possibility exists of interpreting adjacent lexical characters as a single lexical element, you must separate the adjacent lexical elements with an explicit separator. An explicit separator is any space character (except within a comment or string literal), format effector or end of line character. Format effectors other than horizontal tabulation are always separators. Horizontal tabulation is a separator except within a comment.

You can use one or more separators between any two adjacent lexical elements. You must use at least one separator between an identifier or a numeric literal and an adjacent identifier or numeric literal.

A delimiter is any of the following special characters

or one of the following compound delimiters each composed of two adjacent special characters

The following reserved words have significance in the preprocessor language:

abs
else
is
pragma
and
elsif
mod
rem
case
end
not
then
constant
if
or
when
declare
in
others
xor

The remaining Ada reserved words are also reserved but without any significance (see Ada83 LRM 2.9, Ada95 LRM 2.9).

The replacement of the vertical bar, sharp and quotes are supported, as specified in Ada83 LRM 2.10 and Ada95 LRM J.2.

Examples

Examples of identifiers:

Examples of numeric literals:

Examples of string literals:

Examples of comments:

Notes

The following syntactic categories defined in the Ada LRMs, are used throughout this chapter in APP syntactic rules.

The following Ada delimiters are not used in APP.

The set of APP reserved words is a proper subset of the reserved words defined in Ada.

Differences

A character literal is not currently supported.

The sharp and dollar character, which by themselves are illegal lexical items in Ada, have significance only in APP.

Additional Topics

For additional information, refer to these topics

Program Structure

The text of a conditional-compilation program consists of Ada text, interspersed with optional preprocessor control lines. Ada text has no effect on the preprocessing, except for macro substitutions. The legality of the Ada text is checked only to verify that each lexical item is correctly formed.

A conditional-compilation consists of a sequence of preprocessor statements. Each preprocessor statement must be specified on one or more control-lines

A statement defines the action taken by the preprocessor. The basic statements are

Each control-line, when preprocessed, is converted to a comment, i.e., the sharp character is preceded by the comment character sequence --.

Additional Topics

For additional information, refer to these topics

Declarations

The preprocessor language defines only one kind of declared entity, an object. An object is an entity that contains a value of a given type.

A declaration introduces an identifier as the declared entity. After the declaration the identifier is said to be defined

The language rules define a certain region of text called the scope of the declaration. Within its scope and only there, are places where it is possible to use the identifier to refer to the associated declared entity. At such places the identifier is said to be a name of the entity (its simple name); the name is said to denote the associated entity.

A declaration has the following syntax:

An object declaration declares an object whose type is specified by the identifier type. If the object declaration includes the assignment compound delimiter, :=, followed by an expression, the expression specifies an initial value for the declared object; the type of the expression must be that of the object.

The declared object is a constant if the reserved word constant appears in the object declaration; the declaration must then include an explicit initialization. The value of a constant cannot be modified after initialization.

An object that is not a constant is called a variable. The only way to change the value of a variable is by an assignment

Examples

Examples of variable declarations

Examples of constant declarations

Example of declaration of predefined identifiers date, time, file and line variables.

$file and $line appear to expand to the position of the expansion in the preprocessor source.

Notes

The attribute P'DEFINED is used to query whether the identifier is defined.

Differences

An identifier list is not supported.

No constraints are checked during assignment.

Additional Topics

For additional information, refer to these topics

Types

A type is characterized by a set of values and operations. All types are predefined and are not extensible.

Supported types are boolean, numeric and string.

All types share the following basic operations which consist of: assignment, relational operators and explicit type conversions.

Notes

A character type is not yet supported.

BOOLEAN

The type BOOLEAN contains the two values FALSE and TRUE ordered with the relation FALSE < TRUE.

Additional operations consist of: logical operators, short-circuit control forms, logical negation and image attribute.

INTEGER

The type INTEGER is an integer type whose set of values range from some lower bound to some upper bound; where the lower bound is some value < 0 and the upper bound is some value > 0. Integer literals are of the type INTEGER.

Additional operations consist of: membership tests, binary adding operators, unary adding operators, multiplying operators, absolute value, exponentiation and image attribute.

Notes

The type INTEGER corresponds to the Ada predefined type UNIVERSAL_INTEGER.

The lower and upper bounds of the type INTEGER depends on the amount of available memory required for the internal representation.

Additional Topics

For additional information, refer to these topics

REAL

The type REAL is a real type comprised of rational values. Real literals are of the type REAL.

Additional operations consist of: binary adding operators, unary adding operators, multiplying operators, absolute value, exponentiation and image attribute.

Notes

The type REAL corresponds to the Ada predefined type UNIVERSAL_REAL.

With rational values one can assume that (1.0/X)*X = 1.0.

Additional Topics

For additional information, refer to these topics:

STRING

The type STRING is a variable length array of a character component in the range 1.. N, where N is some non-negative integer value.

String literals are basic operations applicable to the type STRING and TEXT.

Additional operations consist of: concatenation, slice operation, length attribute and value attribute.

Notes

A character type is not yet supported.

When N is zero, this denotes the null string "".

A single character component is accessible by a slice operation.

Differences

A STRING object, in Ada, must always be constrained in an object declaration.

TEXT

The type TEXT is a derived type of STRING. The main difference is that the external value is a sequence of graphic characters.

Notes

An external TEXT value is specified in an INFO or DEFINE directive or is used in a macro substitution.

Names

Names can denote objects, slices of objects or attributes of objects.

A simple name for an entity is the identifier associated with the entity by its declaration. The evaluation of a simple name consists of evaluating its definition. The simple name must be defined and have a value.

Notes

The use of an undefined identifier is legal in certain contexts which would prevent the evaluation. These include the if statement, short-circuit forms and any context which is inactive.

Differences

In Ada, the use of an undefined identifier is an error.

An object must contain a value, whereas in Ada, the evaluation yields an erroneous program.

Additional Topics

For additional information, refer to these topics

Slice

A slice denotes a sequence of consecutive characters of a STRING or TEXT object.

A range specifies a subset of values of the type INTEGER. The range L .. R specifies the values from L to R inclusive if the relation L <= R is true. A value V belongs to the range if the relations L <= V and V <= R are both TRUE. A null range is range for which the relation R < L is TRUE.

Except for a null range, the bounds of the range must belong to the range of the sliced object.

An implicit conversion adjusts the bounds from L .. R to 1 .. R-L+1.

Notes

The implicit conversion occurs during assignment and when part of any expression.

The syntax does not allow slicing of a slice, as for example FOO(1..10)(3..6).

Attributes

An attribute denotes a basic operation of an entity given by a prefix.

The applicable attribute designators depend on the prefix.

The following attributes are defined.

Examples

Examples of Attribute Definitions

Differences

The attribute P'DEFINED has no counterpart in Ada.

The attributes P'IMAGE and P'VALUE, where P is REAL, have no counterparts in Ada.

The attributes P'FIRST, P'LAST and P'RANGE are not supported for the string types.

Additional Topics

For additional information, refer to these topics

Expressions

An expression is a formula that defines the computation of a value.

Each primary has a value and a type.

Examples

Examples of primaries:

Examples of expressions:

Notes

The short-circuit forms prevent the evaluation of the right-hand expression. This is useful when referencing an undefined object. For example:

Differences

In APP, erroneous expressions are detected immediately, whereas in Ada an exception is raised, e.g., divide by zero.

Operators and Expression Evaluation

The preprocessor language defines the following classes of predefined operators. The basic properties of these operators are identical to their counterparts in the Ada language (see Ada83 LRM 4.5, Ada95 LRM 4.5). The evaluation of an expression delivers a value or causes an error to be detected.

The operations on numeric types either yield the mathematically correct result or an erroneous result.

Table 5 Operators and Expression Evaluation

Operator
Operation
Left Operand Type
Right Operand Type
Result Type
and
conjunction
BOOLEAN
BOOLEAN
BOOLEAN
or
inclusive disjunction
BOOLEAN
BOOLEAN
BOOLEAN
xor
exclusive
disjunction

BOOLEAN
BOOLEAN
BOOLEAN
=
equality
any type
same type
BOOLEAN
/=
inequality
any type
same type
BOOLEAN
< <= > >=
test for ordering
any type
same type
BOOLEAN
+
identity

INTEGER
REAL

INTEGER
REAL

-
negation

INTEGER
REAL

INTEGER
REAL

+
addition
INTEGER|
REAL

INTEGER
REAL

INTEGER
REAL

-
subtraction
INTEGER,REAL
INTEGER
REAL

INTEGER
REAL

&
catenation
STRING, TEXT
STRING
TEXT

STRING
TEXT

*
multiplication
INTEGER
REAL
REAL
INTEGER

INTEGER
REAL
INTEGER
REAL

INTEGER
REAL
REAL
REAL

/
division
INTEGER
REAL
REAL

INTEGER
REAL
INTEGER

INTEGER
REAL
REAL

mod
modulus
INTEGER
REAL

INTEGER
REAL

INTEGER
REAL

rem
remainder
INTEGER
INTEGER
INTEGER
abs
absolute value
INTEGER
REAL

INTEGER
REAL

INTEGER
REAL

not
logical negation

BOOLEAN
BOOLEAN
**
exponentiation
INTEGER
REAL

INTEGER
REAL

INTEGER
REAL

The catenation of two string objects X and Y yields a new string expression with bounds 1.. X'LENGTH + Y'LENGTH.

For integer division, rem and mod, the right operand must be non-zero.

Exponentiation of an integer requires that the exponent be non-negative.

Type Conversions

The evaluation of a type conversion evaluates the expression given as the operand and converts the resulting value to a specified target type.

A conversion of an operand of a given type to the type itself is allowed. No special restrictions limit the form of the expression.

The allowed type conversions correspond to the following cases:

a . Numeric types

The operand can be either of an INTEGER or REAL type; the value of the operand is converted to the target type which is either of the type INTEGER or REAL. The conversion of a real value to an integer value rounds to the nearest integer. When the value is exactly halfway between two integers, the compiler rounds to the nearest even number, instead of away from zero..

a . String types

The operand can be either of a STRING or TEXT type; the value of the operand is converted to the target type which is either of the type STRING or TEXT. There is no change in representation.

Examples

Examples of numeric type conversions:

Examples of conversions between string types:

Differences

A string literal is allowed as an operand.

In Ada, the rounding mode for the conversion of a real to an integer is implementation-dependent.

Assignment

An assignment statement replaces the current value of a variable with a new value specified by an expression. The named variable and the right-hand expression must be of the same type.

Examples

Examples of assignment statements:

Conditional Processing

Conditional processing is the process of activating or inactivating a section of source text. The source text is a sequence of Ada and preprocessing source lines. Each line is either active or inactive. An active line is preprocessed and any constituents are evaluated; an inactive line is not evaluated. An inactive Ada source line is converted to a comment; the line is preceded by the character sequence --*.

Notes

For an inactive section of text, the APP syntax is still checked.

If Statement

An if statement selects conditionally the enclosed statement sequence, depending on the (truth) value of one or more corresponding conditions.

An expression specifying a condition must be of type BOOLEAN.

For the evaluation of an if statement, the condition specified after if and any conditions specified after elsif, are evaluated in succession (treating a final else as elsif TRUE then), until one evaluates to TRUE or all conditions are evaluated and yield FALSE. If one condition evaluates to TRUE, then the corresponding statement sequence and all Ada source between then-elsif, then-else or else-end, is active; otherwise the remaining statement sequences and Ada text are inactive.

Notes

An if statement achieves conditional evaluation. Evaluation of an object checks that the object is defined and has a value. In the above example, if COND1 is true then COND2 is not evaluated; if COND2 is undefined, an evaluation error is prevented.

Differences

In APP, an undefined identifier can be used in expression, as long as it is not evaluated.

Case Statement

A case statement selects conditionally one of a number of alternative statement sequences; the chosen alternative is defined by the value of an expression.

The expression can be any one of the available types. Each choice in a case statement alternative must be of the same type as the expression; the list of choices specifies for which values of the expression the alternative is chosen (possibly none).

A value of the type of the expression must be represented at most once in the set of choices. A choice defined by a range stands for all values in the corresponding range (none if a null range). The choice others is only allowed for the last alternative and as its only choice; it stands for all values (possibly none) not given in the choices of previous alternatives.

The preprocessing of a case statement consists of the evaluation of the expression followed by the evaluation of each choice and the preprocessing of the chosen (possibly none) statement sequence.

For a chosen alternative, the corresponding statement sequence and all Ada source between the arrow and to the next alternative or end, is active; otherwise the remaining alternatives and Ada text are inactive.

Notes

An others choice is not required and it is possible that no alternative is chosen.

Differences

In Ada the type expression must be a discrete type and all choices specified must represent all possible values of the expression.

Declare Statement

A declare statement encloses a statement sequence, which in effect is a declarative region.

Examples

Example of Declare Statement

Additional Topics

For additional information, refer to these topics

Pragmas

A pragma is used to convey information to the preprocessor. The syntax is

The identifier must be recognized by the preprocessor.

Differences

An unrecognized or illegal pragma is considered an error and not ignored as is the case in Ada.

Pragma INCLUDE

pragma INCLUDE is used to include files in a compilation. The form of this pragma is

This causes the entire contents of the named file to be included at the point of the pragma and be preprocessed. The expression must be of the type STRING or TEXT and evaluate to the simple name of a preprocessor include file, which must end in the suffix .ipp. The named file must exist in the view containing the file being compiled, or in an imported view. It is an error if there is not exactly one file in the view and its imports with the simple name given in the INCLUDE pragma.

Note that the Apex preprocessor does not allow operating system pathnames in the pragma INCLUDE statement. The simple name is always interpreted with respect to the imports of the view in which compilation takes place.

The text of an include file is processed as if it were part of the original source. Include files may contain only preprocessor control lines which may be other INCLUDE pragmas.

It is possible for an include file to be obtained from an Ada library not directly visible from the current Ada library.

When a file containing INCLUDE pragmas is compiled, Apex records dependencies on all the include files that participate in the compilation. If any of these files are modified, the preprocessor will be reinvoked the next time the compiler is executed.

Pragma WARNING

Pragma WARNING is used to issue a warning. The form of this pragma is

The pragma takes an optional STRING or TEXT argument, which, if provided, is used as the warning message.

Pragma ERROR

Pragma ERROR is used to issue an error. The form of this pragma is

The pragma takes an optional STRING or TEXT argument, which, if provided, is used as the error message.

The effect of this pragma is to cause the preprocessing to fail.

Directives

If the value of the APP_DIRECTIVES switch is nonempty, the preprocessor behaves as if the line

appeared at the beginning of the file, where switch-value is the value of the APP_DIRECTIVES switch. The value of this switch follows the rules as names in pragma INCLUDE, that is, it must be the simple name of a file ending in the suffix .ipp, and there must be exactly one file with that name in the view being compiled and its imports. The directives file may contain only preprocessor control lines.

The contents of the directives file is read and preprocessed before the file being compiled is preprocessed. However, the directives files has different visibility rules than an initial INCLUDE pragma. These visibility rules are discussed in Visibility Rules. When a file is preprocessed, Apex records a dependency on the value and contents of the APP_DIRECTIVES switch. If either is changed, the preprocessor is reinvoked the next time the compiler is executed.

The way the preprocessor handles directives when producing .ada files from .app files is controlled by the boolean switch OMIT_PREPROCESSOR_DIRECTIVES. The default value of this switch is False, which causes the directives to be converted to comments. Setting this switch to True causes the directives to be deleted.

Visibility Rules

The rules defining the scope of declarations and the rules defining which identifiers are visible at various points in the preprocessor are described in this section.

Declarative Region

A declarative region is a portion of the program text. A single declarative region is formed by the text of each of the following:

Scope of Declarations

The scope of a declaration that occurs immediately within a declarative region extends from the beginning of the declaration to the end of the declarative region. This implies that the scope of a declaration in a directives file extends to the end of the file being processed.

Visibility

The meaning of the occurrence of an identifier at a given place in the text is defined by the visibility rules.

A declaration is visible only within a certain part of its scope; this part starts at the end of its declaration and extends to the end of the immediate scope of the declaration but excludes places where the declaration is hidden.

A declaration is said to be hidden within an inner declarative region if the inner region contains a homograph of this declaration; the outer declaration is then hidden within the immediate scope of the inner homograph. Each of the two declarations is said to be a homograph of the other if both declarations have the same identifier.

Two declarations that occur immediately within the same declarative region must not be homographs.

No homograph is allowed for any of the predefined types and boolean values.

Examples

Examples of Visibility Rules

Differences

In Ada predefined identifiers can be redefined.

Macro Substitution

A macro substitution is identified by the following lexical item in the Ada text.

The identifier must be defined and have a value.

The output format is specified for each of the types in the table below.

Type
Syntax
BOOLEAN
boolean_value
INTEGER
numeric_value
REAL
rational_value
STRING
string_literal
TEXT
text_literal

Decimal notation is used for integer and real literals.

Examples

Examples of Macro Substitutions

Input
Output
# DEBUG: BOOLEAN := TRUE
if $DEBUG then

--# DEBUG: BOOLEAN := TRUE;
if TRUE then

# BIAS: INTEGER := -16#ff#
ADJUST := ADJUST + ($BIAS);

--# BIAS: INTEGER := -16#ff#;
ADJUST := ADJUST + (-255);

# PI: CONSTANT REAL := 3.14;
RADIANS := 180.0/($PI);

--# PI: CONSTANT REAL := 3.14;
RADIANS:= 180.0/(157.0/50.0);

# S: STRING := "abc" "def"
if PATTERN = $s then

--# S: STRING := "abc" "def";
if PATTERN = "abc""def" then

# NOP: TEXT :=
# "code_0'(op => nop)"
$NOP;

--# NOP: TEXT :=
--# "code_0'(op => nop)";
code_0'(op => nop);

Notes

Macro substitution only occurs in the Ada text, not in APP control lines. The macro substitution cannot exceed the current line length limit. It is not possible for a macro substitution to extend to the next line. A macro substitution cannot be embedded in a comment or string literal, since these are lexical items. A numeric macro substitution must be parenthesized if part of a larger expression.

An example of macro substitution of predefined identifiers can be found in Example of declaration of predefined identifiers date, time, file and line variables.


Example

The following contrived example illustrates some of the possible declarations and the visibility rules involved.

In the following example, the APP_DIRECTIVES switch has the value directives.ipp, which names a file containing:

The file example.2.app contains:

1 . At this point the only objects defined are the predefined types and boolean values.

2 . After processing the directives file, the objects DEBUG and HOST are defined.

3 . Checks whether DEBUG is defined, if not a local declaration is defined with a default value; this avoids any potential evaluation error in the following code. An alternative is to use the expression (DEBUG'DEFINED and then DEBUG) instead of (DEBUG).

4 . Introduces the local constant HOST which hides the outer definition defined in the directives file. Its definition extends to the end of the source.

5 . Introduces an inner definition of HOST which hides the outer definition defined at 4). Its definition extends to the end of the end declare;.

The resulting file is example.2.ada:


Rational Software Corporation 
http://www.rational.com
support@rational.com
techpubs@rational.com
Copyright © 1993-2004, Rational Software Corporation. All rights reserved.
TOC PREV NEXT INDEX DOC LIST MASTER INDEX TECHNOTES APEX TIPS