Author: Dmitry Kazakov
Keywords: parser, Ada, Ada 2022, syntax tree, AST

ADA SYNTAX ANALYZER
version 1.0
by Dmitry A. Kazakov
(mailbox@dmitry-kazakov.de)

This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

As a special exception, if other files instantiate generics from this unit, or you link this unit with other files to produce an executable, this unit does not by itself cause the resulting executable to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU Public License.

The current version provides a full Ada 2022 syntax analyzer library. The analyzer parses the source and creates the syntax tree and cross reference of defining identifiers in the source. Both structures are allocated in an arena pool storage and can be released as a whole. The code source can be file, stream or a user-defined container implementing multi-line source interface. Unicode is fully supported as well as Unicode identifiers normalization checks.

|  |  | ARM | Intel
Download Ada syntax analyzer | Platform: |  | 64- | 32- | 64- | 32bit
Arch packages |  |  | precompiled and packaged for pacman |  |  |  |  |
Fedora packages |  |  | precompiled and packaged using RPM |  |  |  |  |  |
CentOS packages |  |  | precompiled and packaged using RPM |  |  |  |  |
Debian packages |  |  | precompiled and packaged for dpkg |  |  |  |  |
Ubuntu packages |  |  | precompiled and packaged for dpkg |  |  |  |  |
Source distribution (any platform) |  | ada_syntax_analyzer_1_0.tgz (tar + gzip, Windows users may use WinZip) |  |

See also changes log.

## 1. The Ada parser

The package Parsers.Ada_Syntax_Analyzer provides an Ada syntax analyzer:

> `type Ada_Program
> ( Pool : access Root_Storage_Pool'Class;
> Xref : Cross_Reference_Ptr
> ) is new Ada_Expression with private;`

The type is derived from the Ada expression parser from Simple Components. The discriminants are:

- Pool is the pool used to allocate the syntax tree and cross-reference. The pool is allocated by chunks which are freed as a whole;
- Xref is the cross-reference to fill up. When specified as null no cross-reference is created.

The following operations are defined on the type:

> `function Parse
> ( Context : access Ada_Program;
> Code : access Source'Class;
> File : Source_Description_Ptr
> ) return Tokens.Argument_Token;`

This function parses one compilation unit in the source Code and advances the source to the first position following the unit. The result is returned as the topmost node of the syntax tree allocated in the arena pool. The cross-reference Xref if specified is updated with the new defining identifiers declared across the unit and the contexts containing these identifiers. File is the source file description to be used with defining identifiers. It can be null if only one file is used. If multiple files are used the source file description is used to distinguish them in the cross-reference.

The Syntax_Error exception defined in the package Parsers is propagated on syntax errors. Upon an error the source is left at the last parsed position. The syntax error text contains the source primary source location of the error at its end. For example:

> `'end if' is expected at 18:6..10`

The location can be extracted from the text using the function Get_Location. Upon failure the memory pool is cleared. The source can be any descendant of the type Parsers.Multiline_Source.Source_Type. It can be a text file, string, stream or user-defined.

> `function Get_Trace_File (Context : Ada_Program)
> return Ada.Text_IO.File_Access;`

This function returns the file presently used for tracing. It is null if tracing is inactive.

> `procedure Set_Trace_File
> ( Context : in out Ada_Program;
> File : Ada.Text_IO.File_Access := null
> );`

This procedure sets the trace file. When null tracing is deactivated.

> `type Tree_Stub (Context : access Ada_Program'Class) is
> new Ada.Finalization.Limited_Controlled with private;`

An instance of this type marks the pool used by Context. When finalized it frees all memory allocated there since initialization.

### 1.1. Compilation contexts

The following type is the base type of a context containing defining identifiers (ARM 3.1):

> `type Compilation_Context is abstract new Declaration_Node with record
> Parent : Context_Location;
> File : Source_Description_Ptr;
> Scope : Parsers.Multiline_Source.Location := Empty;
> end record;
> type Compilation_Context_Ptr is
> access constant Compilation_Context'Class;;`

A compilation context:

- Parent is the location of the context in its parent. A library unit has no parent;
- File is the source description;
- Scope is the source location where defining identifiers declared in the context are visible.

  `package T is` |  |  |
  `I : Integer; A : Float; procedure X` |  |  |
  `( J : Integer; P : Float; S : String` |  | Location (inherited) | Parent's location
  `) is begin J := J + 1; I := J;` |  | Scope |
  `end X;` |  |  |
  `end T;` |  |  |

The operations to navigate contexts:

> `function Get_Item
> ( Context : Compilation_Context;
> Index : Positive
> ) return Declaration_Ptr is abstract;`

This function returns the declaration node by its index. Nodes are enumerated starting with 1. Constraint_Error is propagated when Index is wrong.

> `function Get_Items_Number (Context : Compilation_Context)
> return Natural is abstract;`

This function returns the number of declaration nodes in the context.

> `type Context_Location is record
> Context : Compilation_Context_Ptr;
> After : Natural := 0;
> end record;`

Values of this type indicates a declaration item:

- Context is the context where the item is declared.
- After is the position after the item.

> `function Is_Library_Unit (Context : Context_Location) return Boolean;`

This function returns true if Context indicates an item declared in a library unit context.

> `function Get (Context : Context_Location) return Declaration_Ptr;`

This function returns the declaration node specified by Context. Constraint_Error is propagated on errors.

> `package Context_Location_Sets is
> new Generic_Set (Context_Location, (null, 0));`

This instantiation provides sets of context locations.

> `function Precedes (Left, Right : Location) return Boolean;`

This function orders source locations so that a location precedes another if it indicates a source interval preceding than the later or else contains it.

### 1.2. Defining identifiers

The defining identifier is declared as follows:

> `type Named_Term_Ptr is access constant Named_Term'Class;
> type Defining_Identifier is record
> Declared : Context_Location;
> Outer : Program_Node_Ptr;
> Name : Named_Term_Ptr;
> Term : Tokens.Argument_Token;
> File : Source_Description_Ptr;
> Next : Defining_Identifier_Ptr;
> end record;
> type Defining_Identifier_Ptr is access constant Defining_Identifier;`

This type represents either a defining identifier or a defining symbol:

- Declared is the location in a context;
- Outer is the node that contains the identifier declaration;
- Name is a direct pointer to the name of. Name,Value is the string with the name of the identifier or the operator name like '+' etc;
- Term is the expression terms. It is either Identifier or String_Literal.
- File is the source description, e.g. file name etc.
- Next points to the next defining identifier in the same context. All identifiers are linked in the list of.

> `type Defining_Identifier_Array is
> array (Positive range <>) of aliased Defining_Identifier;`

> `type Source_Description is abstract tagged null record;
> type Source_Description_Ptr is
> access constant Source_Description'Class;`

The source description can be used with multiple sources to indicate which source the identifier was declared in. The following operations are defined on source descriptions:

> `Source_Description_Ptr is
> access constant Source_Description'Class;`

The source descriptio

### 1.3. Cross-reference

> `type Cross_Reference
> ( Pool : access Root_Storage_Pool'Class
> ) is new Ada.Finalization.Limited_Controlled with private;
> type Cross_Reference_Ptr is access all Cross_Reference'Class;`

This type represents a cross-reference of the items declared in the parsed units. The discriminant Pool can be the same storage pool as the pool used by the parser. Items added to the reference are allocated after the pool mark. When the parser object is finalized or when the stub is finalized Program_Error is propagated if the items from a still existing cross-reference are deallocated in the result.

The following operations are defined on cross-reference:

> `function Find
> ( Xref : Cross_Reference;
> Name : String;
> Location : Parsers.Multiline_Source.Location;
> File : Source_Description_Ptr := null
> ) return Context_Location_Sets.Set;`

This function searches for the name in the cross-reference Xref. Name is the name to search for. Location is the source location to start at. The result the set of defining identifier locations with the names matching Name. The set contains only identifiers with the visibility scope containing Location in File. The result is a set of potential candidates to resolve Name ignoring the use clauses.

> `function Get
> ( Xref : Cross_Reference;
> Name : String
> ) return Identifier_Entry;`

This function returns the entry corresponding Name. The result is No_Identifier if no entry is found.

> `function Get
> ( Xref : Cross_Reference;
> Location : Parsers.Multiline_Source.Location;
> File : Source_Description_Ptr := null
> ) return Context_Entry;`

This function returns the context entry corresponding to the narrowest scope containing Location in File. The result is No_Entry is no such entry exists.

> `function Get_First
> ( Xref : Cross_Reference
> ) return Identifier_Entry;`

This function returns the first identifier entry in the reference. The result is No_Identifier if the reference is empty.

> `function Get_First
> ( Xref : Cross_Reference
> ) return Context_Entry;`

This function returns the first context entry in the reference. The result is No_Context if the reference is empty.

> `function Get_Last
> ( Xref : Cross_Reference
> ) return Identifier_Entry;`

This function returns the last identifier entry in the reference. The result is No_Identifier if the reference is empty.

> `function Get_Last
> ( Xref : Cross_Reference
> ) return Context_Entry;`

This function returns the last context entry in the reference. The result is No_Context if the reference is empty.

### 1.4. Identifier entries

> `type Identifier_Entry is private;
> No_Identifier : constant Identifier_Entry;`

This type represents an entry in the cross-reference corresponding a name. All items in an entry are defining identifiers having this name.

> `function Get_First (Item : Identifier_Entry)
> return Defining_Identifier_Ptr;`

This function returns the first defining identifier in the entry Item. Constraint_Error is propagated if Item is No_Identifier.

> `function Get_Identifiers_Number (Item : Identifier_Entry)
> return Positive;`

This function returns the number of defining identifiers in the entry Item. The result is zero if Item is No_Identifier.

> `function Get_Last (Item : Identifier_Entry)
> return Defining_Identifier_Ptr;`

This function returns the last defining identifiers in the entry Item. Constraint_Error is propagated if Item is No_Identifier.

> `function Get_Name (Item : Identifier_Entry) return String;`

This function returns the name the entry Item corresponds to. Constraint_Error is propagated if Item is No_Identifier.

> `function Get_Next (Item : Identifier_Entry) return Identifier_Entry;`

This function returns the next entry to Item in the cross-reference. The result is No_Identifier if there is none or if Item is No_Identifier.

> `function Get_Previous (Item : Identifier_Entry) return Identifier_Entry;`

This function returns the previous entry to Item in the cross-reference. The result is No_Identifier if there is none or if Item is No_Identifier.

### 1.5. Context entries

> `type Context_Entry is private;
> No_Context : constant Context_Entry;`

This type represents an entry in the cross-reference corresponding a context. That is a declarative region and its scope. Entries are ordered in the textual order of the scopes where context declarations are directly visible. Note the difference between the location of declarations and the scope where these become visible.

> `function Get_Context (Item : Context_Entry)
> return Compilation_Context_Ptr;`

This function returns the context corresponding to the entry Item. Constraint_Error is propagated if Item is No_Context.

> `function Get_Location (Item : Context_Entry)
> return Parsers.Multiline_Source.Location;`

This function returns the location in the source where context corresponding to entry Item is directly visible. It may or may not include the declaration of the context. For example parameter profile declarations are not visible inside the profile. Constraint_Error is propagated if Item is No_Context. See also Get_Scope which also returns the source description.

> `function Get_Next (Item : Context_Entry) return Context_Entry;`

This function returns the next entry to Item in the cross-reference. The result is No_Identifier if there is none or if Item is No_Context.

> `function Get_Previous (Item : Context_Entry) return Context_Entry;`

This function returns the previous entry to Item in the cross-reference. The result is No_Identifier if there is none or if Item is No_Context.

> `procedure Get_Scope
> ( Item : Context_Entry;
> File : out Source_Description_Ptr;
> Location : out Parsers.Multiline_Source.Location
> );`

This procedure returns the location and source description in the source where context corresponding to the entry Item is directly visible. It may or may not include the declaration of the context. For example parameter profile identifiers are not visible inside the profile itself. Constraint_Error is propagated if Item is No_Context.

## 2. The syntax tree

The syntax tree nodes are derived from Ada expression nodes:

> `type Program_Node is abstract new Node with null record;
> type Program_Node_Ptr is
> access constant Program_Node'Class;`

A whole compilation unit is a single tree node.

All text internally are kept as Ada strings encoded in UTF-8.

> `function Get_Class (Item : Program_Node)
> return Program_Node_Class is abstract;`

The function returns the enumeration type value corresponding to the program node. It can be used to avoid explicit tag testing:

> `type Program_Node_Class is
> ( Abort_Statement_Node,
> Accept_Statement_Node,
> ...
> With_Clause_Node
> );`

### 2.1. Declaration nodes

All declaration nodes are descendants of Declaration_Node:

> `type Declaration_Node is abstract new Program_Node with record
> Location : Parsers.Multiline_Source.Location;
> end record;
> type Declaration_Ptr is access constant Declaration_Node'Class;`

This type is the base type of all declaration nodes.

#### 2.1.1. Common declaration types

The child package Parsers.Ada_Syntax_Analyzer.Declarations provides basic types used in declaration nodes.

> `type Compilation_Context_Type is
> ( Code_Block_Context,
> Generic_Formal_Parameters_Context,
> Library_Unit_Context,
> Package_Interface_Context,
> Package_Private_Context,
> Protected_Interface_Context,
> Protected_Private_Context,
> Protected_Body_Context,
> Record_Component_Context,
> Task_Interface_Context,
> Task_Private_Context
> );`

This enumeration type specifies the type of a declaration context::

> `declare <Code_Block_Context> begin ... end;` |  | `<Library_Unit_Context> generic <Generic_Formal_Parameters_Context> package P <Package_Interface_Context> private <Package_Private_Context> end P;`
> `protected type P is <Protected_Interface_Context> private <Protected_Private_Context> end P;` |  | `protected body P is <Protected_Body_Context> end P;`
> `type T is record <Record_Component_Context> end record;` |  | `task type T is <Task_Interface_Context> private <Task_Private_Context> end P;`

> `function Image (Item : Compilation_Context_Type) return String;`

The declaration context node.

> `type Declaration_Context (Kind_Of : Compilation_Context_Type) is
> new Compilation_Context with
> record
> Declarations : Declaration_Ptr_Array_Ptr;
> Last_Use_Clause : Use_Clause_Ptr;
> end record;
> type Declaration_Context_Ptr is access constant Declaration_Context;`

The declaration context contains a list of declarations:

> `type Declaration_Ptr_Array is
> array (Positive range <>) of Declaration_Ptr;
> type Declaration_Ptr_Array_Ptr is
> access constant Declaration_Ptr_Array;`

and the list of use clauses inside:

> `type Use_Clause is abstract new Context_Clause with record
> Location : Context_Location;
> Previous : Use_Clause_Ptr;
> end record;
> type Use_Clause_Ptr is access constant Use_Clause'Class;`

The use clauses are linked inside the context in order to simplify resolution of names according to visibility.

> `type Context_Clause (Length : Positive) is
> abstract new Declaration_Node with
> record
> List : Argument_List (1..Length);
> end record;`

The context clause is the base type of with-, use- and use-type clause nodes. It contains list of nodes each represents a name.

#### 2.1.2. Formal generic parameter nodes

The child package Parsers.Ada_Syntax_Analyzer.Declarations.Formal_Parameters provides declaration of nodes used for generic formal parameters.

> `type Formal_Parameter is abstract new Declaration_Node with record
> Name : aliased Defining_Identifier;
> end record;`

This is the base type of all formal parameters. The node types declared in the package:

- Formal_Access_Type_Parameter
- Formal_Array_Type_Parameter
- Formal_Derived_Type_Parameter
- Formal_Function_Parameter
- Formal_Incomplete_Type_Parameter
- Formal_Interface_Type_Parameter
- Formal_Package_Parameter
- Formal_Private_Type_Parameter
- Formal_Procedure_Parameter
- Formal_Scalar_Type_Parameter

#### 2.1.3. Specification nodes

The child package Parsers.Ada_Syntax_Analyzer.Declarations.Specifications provides declaration of nodes used for specifications.

> `type Mode_Type is
> ( Inout_Mode, -- in out Sybtype_Mark
> Out_Mode, -- out Sybtype_Mark
> In_Mode, -- in Sybtype_Mark
> Access_To_All, -- access all
> Access_To_Constant, -- access constant
> Access_To_Variable, -- access
> Access_To_Procedure, -- access procedure
> Access_To_Protected_Procedure, -- access protected procedure
> Access_To_Function, -- access function
> Access_To_Protected_Function, -- access protected function
> Object_Mode, -- Subtype_Indication
> Array_Mode, -- array
> Constant_Mode -- <number>
> );
> subtype Parameter_Mode is Mode_Type
> range Inout_Mode..Access_To_Protected_Function;
> subtype Access_Mode is Mode_Type
> range Access_To_All..Access_To_Protected_Function;
> subtype Access_To_Mode_Type is Mode_Type
> range Access_To_Constant..Access_To_Variable;
> subtype Access_To_Subprogram_Mode is Mode_Type
> range Access_To_Procedure..Access_To_Protected_Function;
> subtype Component_Mode is Mode_Type
> range Access_To_All..Object_Mode;
> subtype Result_Mode is Mode_Type
> range In_Mode..Access_To_Protected_Function;`

The type mode enumeration specifies modes applied to a parameter or component.

> `type Anonymous_Access_Definition (Mode : Access_Mode) is record
> Not_Null : Boolean;
> case Mode is
> when Access_To_All..Access_To_Variable =>
> Object : Subtype_Indication;
> when Access_To_Subprogram_Mode =>
> Profile : Parameter_Profile_Ptr;
> case Mode is
> when Access_To_Function | Access_To_Protected_Function =>
> Result : Object_Specification_Ptr;
> when others =>
> null;
> end case;
> end case;
> end record;`

This type defines components of an access type. It can be either an access to object or an access to a subprogram.

> `type Array_Type_Specification
> ( Mode : Component_Mode;
> Dimension : Positive;
> Aliased_Component : Boolean
> ) is
> record
> Indices : Subtype_Indication_Array (1..Dimension);
> case Mode is
> when Access_Mode =>
> Access_Component : Anonymous_Access_Definition (Mode);
> when Object_Mode =>
> Object_Component : Subtype_Indication;
> end case;
> end record;
> type Array_Type_Specification_Ptr is
> access constant Array_Type_Specification;`

This type defines components of an access type. It can be either an access to object or an access to a subprogram.

> `type Object_Specification (Mode : Mode_Type) is record
> case Mode is
> when Object_Mode =>
> Variable : Subtype_Indication;
> when In_Mode | Inout_Mode | Out_Mode =>
> Not_Null : Boolean;
> Result : Subtype_Mark;
> when Access_Mode =>
> Pointer : Anonymous_Access_Definition (Mode);
> when Array_Mode =>
> Array_Object : Array_Type_Specification_Ptr;
> when Constant_Mode =>
> null;
> end case;
> end record;
> type Object_Specification_Ptr is
> access constant Object_Specification;`

This type defines an object, component or result of a subprogram. It can be:

- subtype indication, e.g. in `...return Integer range 1..50`;
- subtype mark with null exclusion e.g. in `...in out Integer`;
- an anonymous access type, e.g. `...access Integer`;
- an anonymous array, e,g, `...array (1..10) of Integer`;
- a named constant, e.g, `...constant := 10`;

> `type Parameter_Profile
> ( Parameters_Count : Natural
> ) is new Compilation_Context with
> record
> Outer : Program_Node_Ptr;
> List : Parameter_Specification_Ptr_Array (1..Parameters_Count);
> end record;
> type Parameter_Profile_Ptr is access constant Parameter_Profile;`

This type defines parameters of a subprogram. The profile is a context since names of parameters are defining identifiers visible in the body of the subprogram.

> `type Parameter_Specification
> ( Mode : Parameter_Mode;
> Aliased_Parameter : Boolean;
> Defaulted : Boolean;
> Length : Natural;
> Parameters_Count : Positive;
> Aspects_Count : Natural
> ) is new Declaration_Node with
> record
> Parameters : Defining_Identifier_Array (1..Parameters_Count);
> Profile : Parameter_Profile_Ptr;
> Aspects : Aspect_Items_Array (1..Aspects_Count);
> Default : Optional (Defaulted);
> case Mode is
> when In_Mode | Inout_Mode | Out_Mode =>
> Not_Null : Boolean;
> Value : Subtype_Mark;
> when Access_Mode =>
> Pointer : Anonymous_Access_Definition (Mode);
> end case;
> end record;
> type Parameter_Specification_Ptr is
> access constant Parameter_Specification;
> type Parameter_Specification_Ptr_Array is
> array (Positive range <>) of Parameter_Specification_Ptr;`

This type defines parameters in the profile having the same definition like X, Y : Integer.

> `` `type Singleton_Parameter is new Compilation_Context with record
> Name : aliased Defining_Identifier;
> Type_Of : Parameter_Type;
> end record;`

Singleton parameter is a context with a single parameter defined, e.g. a loop parameter.

Further nodes are:

- Attribute_Definition_Clause
- Component_Clause_Array
- Entry_Declaration
- Enumeration_Representation_Clause
- Exception_Declaration
- Function_Instantiation
- Package_Declaration
- Package_Instantiation
- Procedure_Declaration
- Procedure_Instantiation
- Single_Protected_Declaration
- Single_Task_Declaration
- Variant_Part

#### 2.1.4. Renaming nodes

The child package Parsers.Ada_Syntax_Analyzer.Declarations.Renamings provides declaration of nodes used for renaming Ada entities.

> `type Abstract_Renaming
> ( Aspects_Count : Natural
> ) is abstract new Declaration_Node with
> record
> Name : aliased Defining_Identifier;
> Target : Tokens.Argument_Token;
> Aspects : Aspect_Items_Array (1..Aspects_Count);
> end record;`

This is the base type of all renaming nodes:

- Exception_Renaming
- Function_Renaming
- Object_Renaming
- Package_Renaming
- Procedure_Renaming

#### 2.1.5. Type declaration nodes

The child package Parsers.Ada_Syntax_Analyzer.Declarations.Types provides declaration of nodes used for type declarations.

> `type Abstract_Type_Declaration
> ( Aspects_Count : Natural
> ) is abstract new Declaration_Node with
> record
> Name : aliased Defining_Identifier;
> Aspects : Aspect_Items_Array (1..Aspects_Count);
> end record;`

This is the base type of all declaration nodes:

- Access_Type_Declaration
- Array_Type_Declaration
- Decimal_Fixed_Point_Type_Declaration
- Derived_Type_Declaration
- Enumeration_Type_Declaration
- Floating_Point_Type_Declaration
- Incomplete_Type_Declaration
- Interface_Type_Declaration
- Modular_Type_Declaration
- Ordinary_Fixed_Point_Type_Declaration
- Private_Type_Declaration
- Private_Extension_Type_Declaration
- Protected_Type_Declaration
- Record_Type_Declaration
- Signed_Integer_Type_Declaration
- Task_Type_Declaration

#### 2.1.6. Stub declaration nodes

The child package Parsers.Ada_Syntax_Analyzer.Declarations.Stubs provides declaration of nodes used for stub declarations.

> `type Abstract_Stub
> ( Aspects_Count : Natural
> ) is abstract new Declaration_Node with
> record
> Name : aliased Defining_Identifier;
> Aspects : Aspect_Items_Array (1..Aspects_Count);
> end record;`

This is the base type of all stub declaration nodes:

- Function_Stub
- Package_Stub
- Procedure_Stub
- Protected_Stub
- Task_Stub

### 2.2. Body nodes

The child package Parsers.Ada_Syntax_Analyzer.Bodies provides declaration of nodes used bodies.

> `type Abstract_Body
> ( Declarations : access constant Declaration_Context;
> Aspects_Count : Natural
> ) is abstract new Declaration_Node with
> record
> Name : aliased Defining_Identifier;
> Aspects : Aspect_Items_Array (1..Aspects_Count);
> end record;`

This is the base type of all body declaration nodes:

- Entry_Body
- Function_Body
- Function_Expression
- Null_Procedure
- Package_Body
- Protected_Body
- Task_Body

### 2.3. Statement nodes

The child package Parsers.Ada_Syntax_Analyzer.Statements provides declaration of nodes used bodies.

> `type Abstract_Statement_Node (Labels_Count : Natural) is abstract
> new Program_Node with
> record
> Location : Parsers.Multiline_Source.Location;
> Path : Statement_Location;
> Labels : Defining_Identifier_Array (1..Labels_Count);
> end record;
> type Statement_Ptr is access constant Abstract_Statement_Node'Class;
> type Statement_Ptr_Array is
> array (Positive range <>) of Statement_Ptr;`

This is the base type of all statement nodes. The statement has a location in the source and a location in a statement sequence. The statement also has a list of labels. The labels are located in front of the statement except for a sequence of statements which has labels at its end.

> `type Statement_Location is record
> Sequence : Statement_Sequence_Ptr;
> Position : Positive;
> end record;`

The statement location points a sequence and position inside it.

> `type Statement_Sequence
> ( Labels_Count : Natural;
> Length : Positive
> ) is new Abstract_Statement_Node (Labels_Count) with
> record
> Context : Context_Location;
> Outer : Program_Node_Ptr;
> List : Statement_Ptr_Array (1..Length);
> end record;
> type Statement_Sequence_Ptr is
> access constant Statement_Sequence'Class;
> type Unhandled_Statement_Sequence_Ptr is
> access constant Statement_Sequence;`

A statement sequence is a statement that contains other statements. Context is the location of context in where the sequence is located. Outer is the node that contains the sequence. List is the statements list. For example:

> `procedure P is` |  |  |
> `I : Integer; A : Float;` |  | Context |
> `begin` |  |  |
> `if I < 0 then` |  |  |
> `A := 0.0;` |  | Sequence | Outer (if statement)
> `end if;` |  |  |
> `end P;` |  |  |

> `type Handled_Statement_Sequence
> ( Labels_Count : Natural;
> Length : Positive;
> Handlers_Count : Natural
> ) is new Statement_Sequence
> ( Labels_Count => Labels_Count,
> Length => Length
> ) with
> record
> Handlers : Exception_Handler_Ptr_Array (1..Handlers_Count);
> end record;
> type Handled_Statement_Sequence_Ptr is
> access constant Handled_Statement_Sequence;`

A handled statement sequence has exception handlers attached to it.

> `type Exception_Handler
> ( Has_Parameter : Boolean;
> Choices_Count : Natural
> ) is
> record
> Outer : Handled_Statement_Sequence_Ptr;
> Sequence : Unhandled_Statement_Sequence_Ptr;
> Choices : Argument_List (1..Choices_Count);
> case Has_Parameter is
> when True =>
> Parameter : aliased Singleton_Parameter;
> when False =>
> null;
> end case;
> end record;
> type Exception_Handler_Ptr is access constant Exception_Handler;
> type Exception_Handler_Ptr_Array is
> array (Positive range <>) of Exception_Handler_Ptr;`

A handler has a list of choices, sequence of statements and optionally a parameter.

The statement nodes are:

- Abort_Statement
- Accept_Statement
- Assignment_Statement
- Asynchronous_Transfer_Control
- Block_Statement
- Conditional_Entry_Call
- Call_Statement
- Case_Statement
- Delay_Statement
- Exit_Statement
- Extended_Return_Statement
- For_Loop_Statement
- Goto_Statement
- If_Statement
- Parallel_Block_Statement
- Raise_Statement
- Requeue_Statement
- Selective_Accept
- Simple_Return_Statement
- Timed_Entry_Call
- While_Loop_Statement

### 2.4. Tree and cross-reference output

The child package Parsers.Ada_Syntax_Analyzer.Text_IO provides procedures to output the tree and cross-reference.

> `procedure Put
> ( Tree : Tokens.Argument_Token;
> Output : File_Type := Standard_Output;
> Prefix : String := "";
> Nested : Nesting := Top
> );
> procedure Put
> ( Tree : Tokens.Argument_Token;
> Name : String;
> Prefix : String := "";
> Nested : Nesting:= Top
> );`

These procedures output the abstract syntax tree. For example, the output of Hello-World procedure:

> `<library>
> declarations
> |__with at 10:1..13:16
> | |__Ada.Text_IO at 8:6..16
> |__use at 10:1..13:16
> |__Ada.Text_IO at 8:24..34
> procedure Hello_World at 10:1..13:16
> begin
> |__*() at 12:13..28
> |__Put_Line at 12:4..11
> |__"Hello world!" at 12:14..27`

> `procedure Put_Contexts (File : File_Type; Xref : Cross_Reference);
> procedure Put_Contexts (Xref : Cross_Reference);`

These procedures output the contexts from the cross-reference. The output contains the list context visibility scopes with the context type, its location and the chain of it parent contexts. For example the cross-reference of this program:

> `01` |  |  | `--`
> `02` |  |  | `-- ARM 3.10(21-26) Access Types`
> `03` |  |  | `--`
> `04` |  |  | `-- Success: 21:1..39:13 Tree *`
> `05` |  |  | `--`
> `06` |  |  | `-- package body ARM_3_10 is`
> `07` |  |  | `-- type Frame is access Matrix;`
> `08` |  |  | `-- type Peripheral_Ref is not null access Peripheral;`
> `09` |  |  | `-- type Binop_Ptr is access all Binary_Operation'Class;`
> `10` |  |  | `-- subtype Drum_Ref is Peripheral_Ref (Drum);`
> `11` |  |  | `-- type Message_Procedure is access procedure (M : String := "Error!");`
> `12` |  |  | `-- procedure Default_Message_Procedure (M : String);`
> `13` |  |  | `-- Give_Message : Message_Procedure := Default_Message_Procedure'Access;`
> `14` |  |  | `-- procedure Other_Procedure (M : String);`
> `15` |  |  | `-- begin`
> `16` |  |  | `-- Give_Message := Other_Procedure'Access;`
> `17` |  |  | `-- Give_Message ("File not found.");`
> `18` |  |  | `-- Give_Message.all;`
> `19` |  |  | `-- end ARM_3_10;`
> `20` |  |  | `--`
> `21` |  |  | `package body ARM_3_10 is`
> `22` |  |  | `type Frame is access Matrix; -- see 3.6`
> `23` |  |  | `type Peripheral_Ref is not null access Peripheral; -- see 3.8.1`
> `24` |  |  | `type Binop_Ptr is access all Binary_Operation'Class;`
> `25` |  |  | `-- general access-to-class-wide, see 3.9.1`
> `26` |  |  | `subtype Drum_Ref is Peripheral_Ref(Drum); -- see 3.8.1`
> `27` |  |  |
> `28` |  |  | `type Message_Procedure is access procedure (M : in String := "Error!");`
> `29` |  |  | `procedure Default_Message_Procedure(M : in String);`
> `30` |  |  | `Give_Message : Message_Procedure := Default_Message_Procedure'Access;`
> `31` |  |  |
> `32` |  |  | `procedure Other_Procedure(M : in String);`
> `33` |  |  | `begin`
> `34` |  |  | `Give_Message := Other_Procedure'Access;`
> `35` |  |  |
> `36` |  |  | `Give_Message("File not found.");`
> `37` |  |  | `-- call with parameter (.all is optional)`
> `38` |  |  | `Give_Message.all; -- call with no parameters`
> `39` |  |  | `end ARM_3_10;`

looks like::

> `Scopes Defining identifiers context
> ------ ----------------------------
> 21:1..39:13 library unit context at 21:1 (0 items)
> 22:4..39:0 declarative part at 22:4..33:0 (8 items)
> after library unit context at 21:1 (0 items)`

There are two contexts. The library unit context with no items declared. The package body is attached to that context. The visibility scope covers all package body. The package body has a declaration context with 8 items declared in it. Its parent context is the library unit context.

> `procedure Put_Defining_Identifiers
> ( File : File_Type;
> Xref : Cross_Reference;
> Field : Output_Length := 50
> );
> procedure Put_Defining_Identifiers
> ( Xref : Cross_Reference;
> Field : Output_Length := 50
> );`

These procedures output the list of declared items in the cross-reference. The parameter Field specified the maximum identifier name length. If the name exceeds this limit, it is truncated in the output. For the program above:

> `Identifier Locations
> ---------- ---------
> ARM_3_10 21:14..21
> Binop_Ptr 24:9..17
> Default_M...rocedure 29:14..38
> Drum_Ref 26:12..19
> Frame 22:9..13
> Give_Message 30:4..15
> M 28:48..48, 29:40..40, 32:30..30
> Message_Procedure 28:9..25
> Other_Procedure 32:14..28
> Peripheral_Ref 23:9..22`

### 2.5. Serialization

The child package Parsers.Ada_Syntax_Analyzer.Serialization provides syntax tree serialization. The following subprograms are defined in the package:

> `procedure Restore
> ( Stream : in out Root_Stream_Type'Class;
> Pool : in out Root_Storage_Pool'Class;
> Offset : in out Stream_Element_Offset;
> Result : out Tokens.Argument_Token;
> Xref : Cross_Reference_Ptr := null;
> File : Source_Description_Ptr := null;
> Trace : File_Access := null;
> Prefix : String := ""
> );`

This procedure restores a syntax tree from Stream. The tree is allocated in Pool. Offset is incremented by the number of storage elements read. Result is the tree. Xref is the cross-reference to update with the defining identifiers and scopes read. File is the source description to apply to the input. Trace is the trace file. Prefix is the prefix to use with the trace file output. Constraint_Error is propagated on errors.

> `function Restore
> ( Stream : access Root_Stream_Type'Class;
> Pool : access Root_Storage_Pool'Class;
> Xref : Cross_Reference_Ptr := null;
> File : Source_Description_Ptr := null;
> Trace : File_Access := null;
> Prefix : String := ""
> ) return Tokens.Argument_Token;`

This function is a variant of the above procedure.

> `procedure Store
> ( Stream : in out Root_Stream_Type'Class;
> Tree : Tokens.Argument_Token;
> [ Offset : in out Stream_Element_Offset; ]
> Trace : File_Access := null;
> Prefix : String := ""
> );`

This procedure stores the syntax tree Tree into Stream. Offset is incremented by the number of storage elements written. Trace is the trace file. Prefix is the prefix to use with the trace file output. Constraint_Error is propagated on errors.

## 3. Test suite

The test suite is provided for testing the analyzer. The suite is located in the subdirectory ./test_suite. Some of the test sources are:

- the Ada Reference Manual code samples
- the ACATS
- the Ada Programming Wiki Book
- Blady-Com repository of compilable AARM examples

### 3.1. The test program

The test suite directory contains the project test_suite.gpr. The arguments of the program is the list of files or directories containing tests. When the keyword trace appears then tracing is enabled for the following arguments. Without file or directory arguments the subdirectory tests is used. The program scans each directory and uses each file as a test. It stops at the first test failed. The program exist code is success only when all tests pass.

### 3.2. The syntax of a test file

The test is a source of an Ada program that may consist of several compilation units. The source starts with a comment. The comment specifies the required outcome.

#### 3.2.1. Success tests

A succes test contains the keyword Success followed by colon. For example:

> `01` |  |  | `--`
> `02` |  |  | `-- Description: Hello world test`
> `03` |  |  | `--`
> `04` |  |  | `-- Success: 10:1..13:16 Tree *`
> `05` |  |  | `--`
> `06` |  |  | `-- with Ada.Text_IO; use Ada.Text_IO; procedure Hello_World is begin Put_Line ("Hello world!"); end Hello_World;`
> `07` |  |  | `--`
> `08` |  |  | `with Ada.Text_IO; use Ada.Text_IO;`
> `09` |  |  |
> `10` |  |  | `procedure Hello_World is`
> `11` |  |  | `begin`
> `12` |  |  | `Put_Line ("Hello world!");`
> `13` |  |  | `end Hello_World;`

The keyword Success is followed by a comma-separated list of excepted locations. For each unit in the test there is one item in the list. Then the following optional elements may follow:

- The tree output. It starts with the keyword Tree followed by the output file name where the trees of the parsed units are stored. If the name is specified as *, the output file has the name and extension of the test file and the extension .tree;
- The cross-reference checks;
- The expected result of tree output.

A cross-reference check starts with the keyword find. The has the syntax

> `<check> ::= find <name> at <location> = <places-set> | empty
> <places-set> ::= <place> [,<places-set>]
> <place> ::= <location>(<position>)`

The set of places specifies the list of locations where an identifier matching the specified one is declared. The position is one of the declaration. The check is made against the function Find. For example:

> `01` |  |  | `--`
> `02` |  |  | `-- Object orientation https://en.wikibooks.org/wiki/Ada_Programming/Object_Orientation`
> `03` |  |  | `--`
> `04` |  |  | `-- Success: 26:1..37:15 find Object at 27:9..14 = empty`
> `05` |  |  | `-- find Class_Member_1 at 35:14..27 = 27:4..37:0(2)`
> `06` |  |  | `-- find New_Class_Member at 37:0 = 27:4..37:0(3)`
> `07` |  |  | `-- find Skilled_In at 31:23..35 = empty`
> `08` |  |  | `-- find Skilled_In at 32:1..1 = 31:1..32:6(1)`
> `09` |  |  | `-- find Programmer at 27:1 = 25:1..26:0(2)`
> `10` |  |  | `--`
> `11` |  |  | `-- with Person;`
> `12` |  |  | `-- package Programmer is`
> `13` |  |  | `-- type Object is new Person.Object`
> `14` |  |  | `-- and Printable.Object`
> `15` |  |  | `-- with`
> `16` |  |  | `-- record`
> `17` |  |  | `-- Skilled_In : Language_List;`
> `18` |  |  | `-- end record;`
> `19` |  |  | `-- overriding`
> `20` |  |  | `-- procedure Class_Member_1 (This : Object);`
> `21` |  |  | `-- not overriding`
> `22` |  |  | `-- procedure New_Class_Member (This : Object; That : String);`
> `23` |  |  | `-- end Programmer;`
> `24` |  |  | `--`
> `25` |  |  | `with Person;`
> `26` |  |  | `package Programmer is`
> `27` |  |  | `type Object is new Person.Object`
> `28` |  |  | `and Printable.Object`
> `29` |  |  | `with`
> `30` |  |  | `record`
> `31` |  |  | `Skilled_In : Language_List;`
> `32` |  |  | `end record;`
> `33` |  |  | `overriding`
> `34` |  |  | `procedure Class_Member_1 (This : in Object);`
> `35` |  |  | `not overriding`
> `36` |  |  | `procedure New_Class_Member (This : Object; That : String);`
> `37` |  |  | `end Programmer;`

If ! appears before Success the test is the last test to run.

#### 3.2.2. Failure tests

A test for invalid program contains the keyword Failure followed by colon. The expected error message is specified on the same line. For example:

> `01` |  |  | `--`
> `02` |  |  | `-- ACATS B55A01V`
> `03` |  |  | `--`
> `04` |  |  | `-- Failure: Expression is expected at 12:20`
> `05` |  |  | `--`
> `06` |  |  | `PROCEDURE B55A01V IS`
> `07` |  |  |
> `08` |  |  | `VBF : BOOLEAN := FALSE;`
> `09` |  |  |
> `10` |  |  | `BEGIN`
> `11` |  |  |
> `12` |  |  | `WHILE VBF LOOP; -- ERROR: ';'.`
> `13` |  |  | `NULL;`
> `14` |  |  | `END LOOP;`
> `15` |  |  |
> `16` |  |  | `END B55A01V;`

If ! appears before Failure the test is the last test to run.

## 4. Changes log

The following versions were tested with the compilers:

- GNAT 16.1.1 20260625
- GNAT Studio Community 2020 (20200427)
- GNAT Community 2019 (20190517-83)
- GNAT Community 2018 (20180523-73)
- GNAT 12.2.0
- GNAT 8-12

First release 1.0

##

## 5. Table of Contents

1 The Ada parser 1.1. Compilation contexts 1.2. Defining identifiers 1.3. Cross-reference 1.4. Identifier entries 1.5. Context_Entries 2 The syntax tree 2.1. Declaration nodes 2.1.1.Common declaration types 2.1.2.Formal generic parameter nodes 2.1.3.Specification nodes 2.1.4.Renaming nodes 2.1.5.Type declaration nodes 2.1.6.Stub declaration nodes 2.2. Body nodes 2.3. Statement nodes 2.4. Tree and cross-reference nodes 2.5. Serialization | 3 Test suite 3.1. The test program 3.2. The syntax of a test file 3.2.1. Success test 3.2.2. Failure test 4 Changes log 5 Table of contents