4. ASIS Overview

This chapter contains a short overview of the ASIS definition as given in the ISO/IEC 15291:1999 ASIS Standard. This overview is aimed at helping an ASIS newcomer find needed information in the ASIS definition.

For more details, please refer to the ASIS definition itself. To gain some initial experience with ASIS, try the examples in ASIS Tutorials.

4.1. Main ASIS Abstractions

ASIS is based on three main abstractions used to describe Ada programs; these abstractions are implemented as Ada private types:

Context

An ASIS Context is a logical handle to an Ada environment, as defined in the Ada Reference Manual, Chapter 10. An ASIS application developer may view an ASIS Context as a way to define a set of compilation units available through the ASIS queries.

Compilation_Unit

An ASIS Compilation_Unit is a logical handle to an Ada compilation unit. It reflects practically all the properties of compilation units defined by the Ada Reference Manual, and it also reflects some properties of ‘physical objects’ used by an underlying Ada implementation to model compilation units. Examples of such properties are the time of the last update, and the name of the object containing the unit’s source text. An ASIS Compilation_Unit provides the ‘black-box’ view of a compilation unit, considering the unit as a whole. It may be decomposed into ASIS Elements and then analyzed in ‘white-box’ fashion.

Element

An ASIS Element is a logical handle to a syntactic component of an ASIS Compilation_Unit (either explicit or implicit).

Some ASIS components use additional abstractions (private types) needed for specific pieces of functionality:

Container

An ASIS Container (defined by the Asis.Ada_Environments.Containers package) provides a means for structuring the content of an ASIS Context; i.e., ASIS Compilation_Units are grouped into Containers.

Line

An ASIS Line (defined by the Asis.Text package) is the abstraction of a line of code in an Ada source text. An ASIS Line has a length, a string image and a number.

Span

An ASIS Span (defined by the Asis.Text package) defines the location of an Element, a Compilation_Unit, or a whole compilation in the corresponding source text.

Id

An ASIS Id (defined by the Asis.Ids package) provides a way to store some ‘image’ of an ASIS Element outside an ASIS application. An application may create an Id value from an Element and store it in a file. Subsequently the same or another application may read this Id value and convert it back into the corresponding Element value.

4.2. ASIS Package Hierarchy

ASIS is defined as a hierarchy of Ada packages. Below is a short description of this hierarchy.

Asis

The root package of the hierarchy. It defines the main ASIS abstractions — Context, Compilation_Unit and Element — as Ada private types. It also contains a set of enumeration types that define the classification hierarchy for ASIS Elements (which closely reflects the Ada syntax defined in the Ada Reference Manual) and the classification of ASIS Compilation_Units. This package does not contain any queries.

Asis.Implementation

Contains subprograms that control an ASIS implementation: initializing and finalizing it, retrieving and resetting diagnosis information. Its child package Asis.Implementation.Permissions contains boolean queries that reflect how ASIS implementation-specific features are implemented.

Asis.Ada_Environments

Contains queries that deal with an ASIS Context: associating and dissociating, opening and closing a Context.

Asis.Compilation_Units

Contains queries that work with ASIS Compilation_Units: obtaining units from a Context, getting semantic dependencies between units and ‘black-box’ unit properties.

Asis.Compilation_Units.Relations

Contains queries that return integrated semantic dependencies among ASIS Compilation_Units; e.g., all the units needed by a given unit to be included in a partition.

Asis.Elements

Contains queries working on Elements and implementing general Element properties: gateway queries from ASIS Compilation Units to ASIS Elements, queries defining the position of an Element in the Element classification hierarchy, queries which define for a given Element its enclosing Compilation_Unit and its enclosing Element. It also contains queries for processing pragmas.

Packages working on specific Elements

This group contains the following packages: Asis.Declarations, Asis.Definitions, Asis.Statements, Asis.Expressions and ASIS.Clauses. Each of these packages contains queries working on Elements of the corresponding kind — that is, representing Ada declarations, definitions, statements, expressions and clauses respectively.

Asis.Text

Contains queries returning information about the source representation of ASIS Compilation_Units and ASIS Elements.

Asis.Exceptions

Defines ASIS exceptions.

Asis.Errors

Defines possible ASIS error status values.

4.3. Structural and Semantic Queries

Queries working on Elements and returning Elements or Element lists are divided into structural and semantic queries. Each structural query (except Enclosing_Element) implements one step of the parent-to-child decomposition of an Ada program according to the ASIS Element classification hierarchy. Asis.Elements.Enclosing_Element query implements the reverse child-to-parent step. (For implicit Elements obtained as results of semantic queries, Enclosing_Element might not correspond to what could be expected from the Ada syntax and semantics; in this case the documentation of a semantic query also defines the effect of Enclosing_Element applied to its result).

A semantic query for a given Element returns the Element or the list of Elements representing some semantic property — e.g., a type declaration for an expression as the expression’s type, a defining identifier as a definition for a simple name, etc.

For example, if we have Element El representing an assignment statement:

X := A + B;

then we can retrieve the structural components of this assignment statement by applying the appropriate structural queries:

El_Var  := Asis.Statements.Assignment_Variable_Name (El); --  X
El_Expr := Asis.Statements.Assignment_Expression    (El); --  A + B

Then we can analyze semantic properties of the variable name represented by El_Var and of the expression represented by El_Expr by means of appropriate semantic queries:

El_Var_Def   :=
   Asis.Expressions.Corresponding_Name_Definition (El_Var);
El_Expt_Type :=
   Asis.Expressions.Corresponding_Expression_Type (El_Expr);

As a result, El_Var_Def will be of A_Defining_Identifier kind and will represent the defining occurrence of X, while El_Expt_Type of a kind An_Ordinary_Type_Declaration will represent the declaration of the type of the expression A + B.

If we apply Asis.Elements.Enclosing_Element to El_Var or to El_Expr, we will get back to the Element representing the assignment statement.

An important difference between classifying queries working on Elements as structural versus semantic is that all the structural queries must be within one ASIS Compilation_Unit, but for semantic queries it is typical for the argument of a query to be in one ASIS Compilation_Unit, while the result of this query is in another ASIS Compilation_Unit.

4.4. ASIS Error Handling Policy

Only ASIS-defined exceptions (and the Ada predefined Storage_Error exception) propagate out from ASIS queries. ASIS exceptions are defined in the Asis.Exceptions package. When an ASIS exception is raised, ASIS sets the Error Status (the possible ASIS error conditions are defined as the values of the Asis.Errors.Error_Kinds type) and forms the Diagnosis string. An application can query the current value of the ASIS Error Status by the Asis.Implementation.Status query, and the current content of the Diagnosis string by Asis.Implementation.Diagnosis query. An application can reset the Error Status and the Diagnosis string by invoking the Asis.Implementation.Set_Status procedure.

Caution: The ASIS way of providing error information is not tasking safe. The Diagnosis string and Error Kind are global to an entire partition, and are not ‘per task’. If ASIS exceptions are raised in more then one task of a multi-tasking ASIS application, the result of querying the error information in a particular task may be incorrect.

4.5. Dynamic Typing of ASIS Queries

The ASIS type Element covers all Ada syntactic constructs, and Compilation_Unit covers all Ada compilation units. ASIS defines an Element classification hierarchy (which reflects very closely the hierarchy of Ada syntactic categories defined in the Ada Reference Manual, and ASIS similarly defines a classification scheme for ASIS Compilation_Units. For any Element you can get its position in the Element classification hierarchy by means of classification queries defined in the package Asis.Elements.

The classification queries for Compilation_Units are defined in the package Asis.Compilation_Units. Many of the queries working on Elements and Compilation_Units can be applied only to specific kinds of Elements and Compilation_Units respectively. For example, it does not make sense to query Assignment_Variable_Name for an Element of An_Ordinary_Type_Declaration kind. An attempt to perform such an operation will be detected at run-time, and an exception will be raised as explained in the next paragraph.

ASIS may be viewed as a dynamically typed interface. For any Element structural or semantic query (that is, for a query having an Element as an argument and returning either an Element or Element list as a result) a list of appropriate Element kinds is explicitly defined in the query documentation which immediately follows the declaration of the corresponding subprogram in the code of the ASIS package. This means that the query can be applied only to argument Elements being of the kinds from this list. If the kind of the argument Element does not belong to this list, the corresponding call to this query raises the Asis.Exceptions.ASIS_Inappropriate_Element exception with Asis.Errors.Value_Error error status set.

The situation for the queries working on Compilation_Units is similar. If a query lists appropriate unit kinds in its documentation, then this query can work only on Compilation_Units of the kinds from this list. The query should raise Asis.Exceptions.ASIS_Inappropriate_Compilation_Unit with Asis.Errors.Value_Error error status set when called for any Compilation_Unit with a kind not from the list of the appropriate unit kinds.

If a query has a list of expected Element kinds or expected Compilation_Unit kinds in its documentation, this query does not raise any exception when called with any argument, but it produces a meaningful result only when called with an argument with the kind from this list. For example, if Asis.Elements.Statement_Kind query is called for an argument of A_Declaration kind, it just returns Not_A_Statement, but without raising any exception.

4.6. ASIS Iterator

ASIS provides a powerful mechanism to traverse an Ada unit, the generic procedure Asis.Iterator.Traverse_Element. This procedure makes a top-down left-to-right (or depth-first) traversal of the ASIS tree (that is, of the syntax structure of the Ada code viewed as the hierarchy of ASIS Elements). In the course of this traversal, it applies to each Element the formal Pre_Operation procedure when visiting this Element for the first time, and the formal Post_Operation procedure when leaving this Element. By providing specific procedures for Pre_Operation and Post_Operation when instantiating the generic unit, you can automatically process all ASIS Elements found in a given ASIS tree.

For example, suppose we have an assignment statement:

X := F (Y);

When called for an Element representing this statement, a Traverse_Element instantiation does the following (below Pre_Op and Post_Op stand for actual procedures provided for formal Pre_Operation and Post_Operation, and numbers indicate the sequence of calls to Pre_Op and Post_Op during traversal):

             (1 Pre_Op)  X := F (Y) (10 Post_Op)
                             |
                             |
           -----------------------------------
           |                                 |
(2 Pre_Op) X (3 Post_Op)                     |
                                             |
                                (4 Pre_Op) F(Y) (9 Post_Op)
                                             |
                                             |
                                ---------------------------
                                |                         |
                    (5 Pre_Op)  F (6 Post_Op)  (7 Pre_Op) Y (8 Post_Op)

To see in more detail how Traverse_Element may be used for rapid development of a number of useful ASIS applications, try the examples in ASIS Tutorials.

4.7. How to Navigate through the Asis Package Hierarchy

The following hints and tips may be useful when looking for some specific information in the ASIS source files:

  • Use the short overview of the ASIS packages given in ASIS Package Hierarchy, to limit your browsing to a smaller set of ASIS packages (e.g., if you are interested in what can be done with Compilation_Units then look only in Asis.Compilation_Units; if you are looking for queries that can be used to decompose and analyze declarations, limit your search to Asis.Declarations).

  • Inside ASIS packages working with particular kinds of Elements (Asis.Declarations, Asis.Definitions, Asis.Statements, Asis.Expressions and ASIS.Clauses) queries are ordered according to the order of the description of the corresponding constructions in the Ada Reference Manual (e.g., package Asis.Statements starts from a query retrieving labels and ends with the query decomposing a code statement).

  • The names of all the semantic queries (and only ones) start from Corresponding_... or Implicit_...

  • Use comment sentinels given in the specification of the ASIS packages. A sentinel of the form ‘--|ER’ (from ‘Element Reference’) introduces a new Element kind, and it is followed by a group of sentinels of the form ‘--|CR’ (from ‘Child Reference’), which list queries yielding the child Elements for the Element just introduced.