![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Ada Preprocessor
IntroductionThe 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:
InvocationPreprocessor 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 ConventionsThis 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
- Program Structure
- Declarations
- Types
- Names
- Expressions
- Assignment
- Conditional Processing
- Declare Statement
- Pragmas
- Directives
- Visibility Rules
- Macro Substitution
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
# COUNT X get_symbol Ethelyn Marion # SNOBOL_4 X1 PageCount STORE_NEXT_ITEM
# 12 1E6 123_456 -- integer literals # 12.0 0.456 3.14159_26 -- real literals # 1.34E-12 1.0E+6 -- real literals with exponent # 2#1111_1111# 16#FF# -- integer literals of value 255 # 16#E#E1 8#340# -- integer literals of value 224 # 16#F.FF#E+2 2#1.1111_1111_111#E11 -- real literals of value 4095.0
# "Message of the day:" # "" -- an empty string literal # " " "A" """" -- three string literals of length 1 # "This is a string literal"
Characters such as $, % and} are allowed in string literals.
# end if; -- processing of LINE is complete # -- a long comment may be split onto # -- two or more control lines # --------- the first two hyphens start the comment
Notes
The following syntactic categories defined in the Ada LRMs, are used throughout this chapter in APP syntactic rules.
graphic_character identifier numeric_literal string_literal (see Ada LRM 2.6
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
- character literal, Ada83 LRM 2.5, Ada95 LRM 2.5
- comments, Ada83 LRM 2.7, Ada95 LRM 2.7
- delimiter, Ada83 LRM 2.2, Ada95 LRM 2.2
- format effector, Ada83 LRM 2.1, Ada95 LRM 2.1
- graphic character, Ada83 LRM 2.1, Ada95 LRM 2.1
- identifier, Ada83 LRM 2.3, Ada95 LRM 2.3
- Macro Substitution
- numeric literal, Ada83 LRM 2.4, Ada95 LRM 2.4
- separator, Ada83 LRM 2.2, Ada95 LRM 2.2
- string literal, Ada83 LRM 2.6, Ada95 LRM 2.6
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
conditional_compilation ::= statement_sequence statement_sequence ::= {statement}
A statement defines the action taken by the preprocessor. The basic statements are
statement ::= object_declaration | assignment_statement | if_statement | case_statement | declare_statement | pragma
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:
object_declaration ::= identifier : [constant] type [:= expression];
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
#COUNT : INTEGER; #SORTED : BOOLEAN := FALSE; #MESSAGE : STRING := "Hello World!";
Examples of constant declarations
#LIMIT : constant INTEGER := 10; #VERSION : REAL := 6.21;
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.
inc.ipp: ifile : constant string := $file; iline : constant integer := $line; test.1.app: package test is date : constant string := $date; time : constant string := $time; # pragma include ("inc.ipp"); file : constant string := $file; line : constant integer := $line; another : constant integer := $line; end test;
test.1.ada: package Test is Date : constant String := "Jun 2 1999"; Time : constant String := "15:31:42"; Ifile : constant String := "inc.ipp"; Iline : constant Integer := 2; File : constant String := "crap.1.app"; Line : constant Integer := 6; Another : constant Integer := 8; end Test;
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.
name ::= simple_name | slice | attribute simple_name ::= identifier
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.
slice ::= simple_name(range) range ::= simple_expression .. simple_expression
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.
#MESSAGE(6..10) -- a slice of 5 characters #MESSAGE(1..0) -- a null slice
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.
attribute ::= prefix'attribute_designator prefix ::= identifier | simple_name attribute_designator ::= simple_name [(expression)]
The applicable attribute designators depend on the prefix.
The following attributes are defined.
- P'DEFINED
For a prefix P that is an identifier:
Yields the value TRUE if the identifier P is defined; yields the value FALSE otherwise. The value of this attribute is of the type BOOLEAN.
- P'LENGTH
For a prefix P that denotes a STRING or TEXT object:
Yields the number of character components in the object P. The value of this attribute is of the type INTEGER.
- P'IMAGE
For a prefix P that is the type INTEGER, REAL or BOOLEAN:
This attribute is an operation with a single argument. The actual argument X must be a value of the type P. The result type is the type STRING or TEXT. The result is the image of the value in display form.
The image of an integer value is the corresponding decimal literal; without underlines, leading zeros, exponent or trailing spaces; but with a one character prefix that is either a minus sign or a space.
For a real value, the image is the corresponding real decimal literal if the fractional part of the value is zero. Otherwise, the image is a pair of real decimal literals separated by the divide character, with no underlines, leading zeros, exponents or trailing spaces but with a single-character prefix (either a minus sign or a space).
The image of a boolean value is the corresponding identifier in upper case.
- P'VALUE
For a prefix P that is the type INTEGER, REAL or BOOLEAN
This attribute is an operation with a single argument. The actual argument X must be a value of the type STRING or TEXT. The result type is the type P. Any leading and any trailing spaces that corresponds to X are ignored.
For the type INTEGER, if the sequence of characters has the syntax of a numeric value, the result is this value.
For the type REAL, if the sequence of characters has the syntax of a rational value, the result is this value.
For the type BOOLEAN, if the sequence of characters correspond to the identifiers TRUE or FALSE, the result is this value.
Examples
Examples of Attribute Definitions
#COUNT'DEFINED -- TRUE if COUNT is defined #MESSAGE'LENGTH -- yields the value 12 #REAL'IMAGE(VERSION) -- yields the value " 2.2" #BOOLEAN'VALUE(MESSAGE) -- erroneous
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.
expression ::= relation {and relation} | relation {or relation} | relation {xor relation} | relation {and then relation} | relation {or else relation} relation ::= simple_expression[relational_operator simple_expression] | simple_expression [not] in range simple_expression ::= [unary_adding_operator] term {binary_adding_operator term} term ::= factor {multiplying_operator factor} factor ::= primary [** primary] | abs primary | not primary primary ::= numeric_literal | string_literal | name | type_conversion | (expression)
Each primary has a value and a type.
Examples
# 100 -- integer literal # 4.0 -- real literal # "Hello World!" -- string literal # LIMIT -- constant # COUNT -- variable # MESSAGE'LENGTH -- attribute # MESSAGE(1..5) -- slice # REAL(1) -- conversion # (LIMIT + 1) -- parenthesized expression
# VERSION -- primary # not SORTED -- factor # 2*COUNT -- term # -4.0 -- simple expression # -4.0 + VERSION -- simple expression # B**2 - 4.0*A*C -- simple expression # PASSWORD(1..3) = "BWV" -- relation # LIMIT in 1..10 -- relation # LIMIT not in 1..10 -- relation # COUNT = 0 or ITEM_HIT -- expression # (COLD and SUNNY) or WARM -- expression (parentheses -- are required) # A**(B**C) -- expression (parentheses -- are required)
Notes
The short-circuit forms prevent the evaluation of the right-hand expression. This is useful when referencing an undefined object. For example:
#DEBUGGING: BOOLEAN := DEBUG'DEFINED and then DEBUG;
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.
logical_operator ::= and | or | xor relational_operator ::= = | /= | < | <= | > | >= binary_adding_operator ::= + | - | & unary_adding_operator ::= + | - multiplying_operator ::= * | / | mod | rem highest_precedence_operator ::= ** | abs | not
The operations on numeric types either yield the mathematically correct result or an erroneous result.
Table 5 Operators and Expression Evaluation
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.
type_conversion ::= type(expression)
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 typesThe 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 typesThe 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:
# REAL(1) -- value is 1.0
# INTEGER(1.6) -- value is 2 # INTEGER(1.5) -- value is 2 -- (round-to-nearest-even) # INTEGER(2.5) -- value is 2 -- (round-to-nearest-even) # INTEGER(-1.5) -- value is -2 -- (round-to-nearest-even) # INTEGER(-2.5) -- value is -2 -- (round-to-nearest-even)
Examples of conversions between string types:
# STRING(COMPILER) -- bounds are those of -- COMPILER # TEXT(MESSAGE(6..10)) -- bounds are 1 and 5 # TEXT("") -- bounds are 1 and 0
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.
assignment_statement ::= variable_simple_name := expression;
Examples
Examples of assignment statements:
#SORTED := TRUE; #CONTROL_LINE := "# " & SOURCE_LINE; #CELSIUS := (FAHRENHEIT-32.0) * (5.0/9.0);
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.
if_statement ::= if condition then statement_sequence { elsif condition then statement_sequence} [ else statement_sequence] end if;
condition ::= boolean_expression
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.
# if DEBUG'DEFINED and then DEBUG then TEXT_IO.PUT("Debugging ...."); # end if;
X := # if BIAS >= 0 then X+1 # else X-1 # end if; ;
# if COND1 then -- cond1 part # elsif COND2 then -- cond2 part # else -- else part # end if;
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.
case_statement ::= case expression is case_statement_alternative { case_statement_alternative} end case;
case_statement_alternative ::= when choice {| choice} => statement_sequence
choice ::= simple_expression | range | others
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.
# case DEBUGGING is # when TRUE => TEXT_IO.PUT("Debugging ..."); # end case;
# case TARGET is # when "rt" => rt_specific; # when "sparc" => sparc_specific; # when "mc68020" => mc68020_specific; # when others => # pragma ERROR("unknown TARGET: " & TARGET); # end case;
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.
declare_statement ::= declare statement_sequence end declare;
Examples
# declare # KIND: constant STRING := STRING(COMPILER); TEXT_IO.PUT("Compiler: " & $KIND); # end declare;
Additional Topics
For additional information, refer to these topics
Pragmas
A pragma is used to convey information to the preprocessor. The syntax is
pragma ::= pragma identifier [(expression)];
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
pragma INCLUDE (string_expression);
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
pragma WARNING [(string_expression
)];
The pragma takes an optional STRING or TEXT argument, which, if provided, is used as the warning message.
#pragma WARNING; #pragma WARNING("this text is the warning message");
Pragma ERROR
Pragma ERROR is used to issue an error. The form of this pragma is
pragma ERROR [(string
_expression)];
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.
#pragma ERROR; #pragma ERROR("this text is the error message");
Directives
If the value of the APP_DIRECTIVES switch is nonempty, the preprocessor behaves as if the line
# pragma INCLUDE ("switch-value
");
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:
- A statement sequence of a conditional compilation program.
- A statement sequence of a declare statement.
- The directives file, if any, followed by the entire file being processed
- The entire file being preprocessed, without any directives file.
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
# if not NAME'DEFINED then # NAME: constant TEXT := "undefined"; # end if;
# LEVEL: INTEGER := 1; # if TRUE then # LEVEL: INTEGER := 2; -- illegal; redeclaration # end if; # declare # LEVEL: INTEGER := 3;-- inner homograph of LEVEL # end declare;
# I: INTEGER := I + 1; -- illegal; the declaration # -- of I hides the use of I in # -- the expression
# FOO: BOOLEAN := FOO'DEFINED; -- illegal; the # -- declaration of FOO -- is not yet complete
# BOOLEAN: INTEGER := 1; -- illegal; a predefined -- type cannot be redefined
Differences
In Ada predefined identifiers can be redefined.
Macro Substitution
A macro substitution is identified by the following lexical item in the Ada text.
$identifier
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
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.
ExampleThe 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) # DEBUG : constant BOOLEAN := TRUE; # HOST : constant string := "some_host"; -- 2)
# -- 3) Ensure DEBUG is defiend # if not DEBUG'DEFINED then # DEBUG: constant BOOLEAN := FALSE; # end if; # -- 4) local definition of HOST; hide definition in directives # HOST: constant STRING := "some_other_host
"; # # if DEBUG then with TEXT_IO; with DEBUG_TOOLS; # end if; procedure EXAMPLE is begin # if DEBUG then # declare -- 5) inner definition of HOST; hide local definition # HOST: constant STRING := "yet_another_host
"; TEXT_IO.PUT("Debugging host: " & $HOST); TEXT_IO.PUT(DEBUG_TOOLS.Get_Raise_Location); # end declare; # else null; # end if; end EXAMPLE;
- 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:
-- 1) --# DEBUG : constant BOOLEAN := TRUE; --# HOST : constant string := "some_host
"; -- 2) --# -- 3) Ensure DEBUG is defiend --# if not DEBUG'DEFINED then --# DEBUG: constant BOOLEAN := FALSE; --# end if; --# -- 4) local definition of HOST; hide definition in directives --# HOST: constant STRING := "some_other_host
"; --# --# if DEBUG then with TEXT_IO; with DEBUG_TOOLS; --# end if; procedure EXAMPLE is begin --# if DEBUG then --# declare -- 5) inner definition of HOST; hide local definition --# HOST: constant STRING := "yet_another_host
"; TEXT_IO.PUT("Debugging host: " & "yet_another_host
"); TEXT_IO.PUT(DEBUG_TOOLS.Get_Raise_Location); --# end declare; --# else --* null; --# end if; end EXAMPLE;
Rational Software Corporation http://www.rational.com support@rational.com techpubs@rational.com Copyright © 1993-2004, Rational Software Corporation. All rights reserved. |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |