| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- P R J . S T R T -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 2001-2015, Free Software Foundation, Inc. -- |
| -- -- |
| -- GNAT is free software; you can redistribute it and/or modify it under -- |
| -- terms of the GNU General Public License as published by the Free Soft- -- |
| -- ware Foundation; either version 3, or (at your option) any later ver- -- |
| -- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- |
| -- OUT 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 distributed with GNAT; see file COPYING3. If not, go to -- |
| -- http://www.gnu.org/licenses for a complete copy of the license. -- |
| -- -- |
| -- GNAT was originally developed by the GNAT team at New York University. -- |
| -- Extensive contributions were provided by Ada Core Technologies Inc. -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| with Err_Vars; use Err_Vars; |
| with Prj.Attr; use Prj.Attr; |
| with Prj.Err; use Prj.Err; |
| with Snames; |
| with Table; |
| with Uintp; use Uintp; |
| |
| package body Prj.Strt is |
| |
| Buffer : String_Access; |
| Buffer_Last : Natural := 0; |
| |
| type Choice_String is record |
| The_String : Name_Id; |
| Already_Used : Boolean := False; |
| end record; |
| -- The string of a case label, and an indication that it has already |
| -- been used (to avoid duplicate case labels). |
| |
| Choices_Initial : constant := 10; |
| Choices_Increment : constant := 100; |
| -- These should be in alloc.ads |
| |
| Choice_Node_Low_Bound : constant := 0; |
| Choice_Node_High_Bound : constant := 099_999_999; |
| -- In practice, infinite |
| |
| type Choice_Node_Id is |
| range Choice_Node_Low_Bound .. Choice_Node_High_Bound; |
| |
| First_Choice_Node_Id : constant Choice_Node_Id := |
| Choice_Node_Low_Bound; |
| |
| package Choices is |
| new Table.Table |
| (Table_Component_Type => Choice_String, |
| Table_Index_Type => Choice_Node_Id'Base, |
| Table_Low_Bound => First_Choice_Node_Id, |
| Table_Initial => Choices_Initial, |
| Table_Increment => Choices_Increment, |
| Table_Name => "Prj.Strt.Choices"); |
| -- Used to store the case labels and check that there is no duplicate |
| |
| package Choice_Lasts is |
| new Table.Table |
| (Table_Component_Type => Choice_Node_Id, |
| Table_Index_Type => Nat, |
| Table_Low_Bound => 1, |
| Table_Initial => 10, |
| Table_Increment => 100, |
| Table_Name => "Prj.Strt.Choice_Lasts"); |
| -- Used to store the indexes of the choices in table Choices, to |
| -- distinguish nested case constructions. |
| |
| Choice_First : Choice_Node_Id := 0; |
| -- Index in table Choices of the first case label of the current |
| -- case construction. Zero means no current case construction. |
| |
| type Name_Location is record |
| Name : Name_Id := No_Name; |
| Location : Source_Ptr := No_Location; |
| end record; |
| -- Store the identifier and the location of a simple name |
| |
| package Names is |
| new Table.Table |
| (Table_Component_Type => Name_Location, |
| Table_Index_Type => Nat, |
| Table_Low_Bound => 1, |
| Table_Initial => 10, |
| Table_Increment => 100, |
| Table_Name => "Prj.Strt.Names"); |
| -- Used to accumulate the single names of a name |
| |
| procedure Add (This_String : Name_Id); |
| -- Add a string to the case label list, indicating that it has not |
| -- yet been used. |
| |
| procedure Add_To_Names (NL : Name_Location); |
| -- Add one single names to table Names |
| |
| procedure External_Reference |
| (In_Tree : Project_Node_Tree_Ref; |
| Current_Project : Project_Node_Id; |
| Current_Package : Project_Node_Id; |
| External_Value : out Project_Node_Id; |
| Expr_Kind : in out Variable_Kind; |
| Flags : Processing_Flags); |
| -- Parse an external reference. Current token is "external" |
| |
| procedure Attribute_Reference |
| (In_Tree : Project_Node_Tree_Ref; |
| Reference : out Project_Node_Id; |
| First_Attribute : Attribute_Node_Id; |
| Current_Project : Project_Node_Id; |
| Current_Package : Project_Node_Id; |
| Flags : Processing_Flags); |
| -- Parse an attribute reference. Current token is an apostrophe |
| |
| procedure Terms |
| (In_Tree : Project_Node_Tree_Ref; |
| Term : out Project_Node_Id; |
| Expr_Kind : in out Variable_Kind; |
| Current_Project : Project_Node_Id; |
| Current_Package : Project_Node_Id; |
| Optional_Index : Boolean; |
| Flags : Processing_Flags); |
| -- Recursive procedure to parse one term or several terms concatenated |
| -- using "&". |
| |
| --------- |
| -- Add -- |
| --------- |
| |
| procedure Add (This_String : Name_Id) is |
| begin |
| Choices.Increment_Last; |
| Choices.Table (Choices.Last) := |
| (The_String => This_String, |
| Already_Used => False); |
| end Add; |
| |
| ------------------ |
| -- Add_To_Names -- |
| ------------------ |
| |
| procedure Add_To_Names (NL : Name_Location) is |
| begin |
| Names.Increment_Last; |
| Names.Table (Names.Last) := NL; |
| end Add_To_Names; |
| |
| ------------------------- |
| -- Attribute_Reference -- |
| ------------------------- |
| |
| procedure Attribute_Reference |
| (In_Tree : Project_Node_Tree_Ref; |
| Reference : out Project_Node_Id; |
| First_Attribute : Attribute_Node_Id; |
| Current_Project : Project_Node_Id; |
| Current_Package : Project_Node_Id; |
| Flags : Processing_Flags) |
| is |
| Current_Attribute : Attribute_Node_Id := First_Attribute; |
| |
| begin |
| -- Declare the node of the attribute reference |
| |
| Reference := |
| Default_Project_Node |
| (Of_Kind => N_Attribute_Reference, In_Tree => In_Tree); |
| Set_Location_Of (Reference, In_Tree, To => Token_Ptr); |
| Scan (In_Tree); -- past apostrophe |
| |
| -- Body may be an attribute name |
| |
| if Token = Tok_Body then |
| Token := Tok_Identifier; |
| Token_Name := Snames.Name_Body; |
| end if; |
| |
| Expect (Tok_Identifier, "identifier"); |
| |
| if Token = Tok_Identifier then |
| Set_Name_Of (Reference, In_Tree, To => Token_Name); |
| |
| -- Check if the identifier is one of the attribute identifiers in the |
| -- context (package or project level attributes). |
| |
| Current_Attribute := |
| Attribute_Node_Id_Of (Token_Name, Starting_At => First_Attribute); |
| |
| -- If the identifier is not allowed, report an error |
| |
| if Current_Attribute = Empty_Attribute then |
| Error_Msg_Name_1 := Token_Name; |
| Error_Msg (Flags, "unknown attribute %%", Token_Ptr); |
| Reference := Empty_Node; |
| |
| -- Scan past the attribute name |
| |
| Scan (In_Tree); |
| |
| -- Skip a possible index for an associative array |
| |
| if Token = Tok_Left_Paren then |
| Scan (In_Tree); |
| |
| if Token = Tok_String_Literal then |
| Scan (In_Tree); |
| |
| if Token = Tok_Right_Paren then |
| Scan (In_Tree); |
| end if; |
| end if; |
| end if; |
| |
| else |
| -- Give its characteristics to this attribute reference |
| |
| Set_Project_Node_Of (Reference, In_Tree, To => Current_Project); |
| Set_Package_Node_Of (Reference, In_Tree, To => Current_Package); |
| Set_Expression_Kind_Of |
| (Reference, In_Tree, To => Variable_Kind_Of (Current_Attribute)); |
| Set_Case_Insensitive |
| (Reference, In_Tree, |
| To => Attribute_Kind_Of (Current_Attribute) in |
| All_Case_Insensitive_Associative_Array); |
| Set_Default_Of |
| (Reference, In_Tree, |
| To => Attribute_Default_Of (Current_Attribute)); |
| |
| -- Scan past the attribute name |
| |
| Scan (In_Tree); |
| |
| -- If the attribute is an associative array, get the index |
| |
| if Attribute_Kind_Of (Current_Attribute) /= Single then |
| Expect (Tok_Left_Paren, "`(`"); |
| |
| if Token = Tok_Left_Paren then |
| Scan (In_Tree); |
| |
| if Others_Allowed_For (Current_Attribute) |
| and then Token = Tok_Others |
| then |
| Set_Associative_Array_Index_Of |
| (Reference, In_Tree, To => All_Other_Names); |
| Scan (In_Tree); |
| |
| else |
| if Others_Allowed_For (Current_Attribute) then |
| Expect |
| (Tok_String_Literal, "literal string or others"); |
| else |
| Expect (Tok_String_Literal, "literal string"); |
| end if; |
| |
| if Token = Tok_String_Literal then |
| Set_Associative_Array_Index_Of |
| (Reference, In_Tree, To => Token_Name); |
| Scan (In_Tree); |
| end if; |
| end if; |
| end if; |
| |
| Expect (Tok_Right_Paren, "`)`"); |
| |
| if Token = Tok_Right_Paren then |
| Scan (In_Tree); |
| end if; |
| end if; |
| end if; |
| |
| -- Change name of obsolete attributes |
| |
| if Present (Reference) then |
| case Name_Of (Reference, In_Tree) is |
| when Snames.Name_Specification => |
| Set_Name_Of (Reference, In_Tree, To => Snames.Name_Spec); |
| |
| when Snames.Name_Specification_Suffix => |
| Set_Name_Of |
| (Reference, In_Tree, To => Snames.Name_Spec_Suffix); |
| |
| when Snames.Name_Implementation => |
| Set_Name_Of (Reference, In_Tree, To => Snames.Name_Body); |
| |
| when Snames.Name_Implementation_Suffix => |
| Set_Name_Of |
| (Reference, In_Tree, To => Snames.Name_Body_Suffix); |
| |
| when others => |
| null; |
| end case; |
| end if; |
| end if; |
| end Attribute_Reference; |
| |
| --------------------------- |
| -- End_Case_Construction -- |
| --------------------------- |
| |
| procedure End_Case_Construction |
| (Check_All_Labels : Boolean; |
| Case_Location : Source_Ptr; |
| Flags : Processing_Flags; |
| String_Type : Boolean) |
| is |
| Non_Used : Natural := 0; |
| First_Non_Used : Choice_Node_Id := First_Choice_Node_Id; |
| |
| begin |
| -- First, if Check_All_Labels is True, check if all values of the string |
| -- type have been used. |
| |
| if Check_All_Labels then |
| if String_Type then |
| for Choice in Choice_First .. Choices.Last loop |
| if not Choices.Table (Choice).Already_Used then |
| Non_Used := Non_Used + 1; |
| |
| if Non_Used = 1 then |
| First_Non_Used := Choice; |
| end if; |
| end if; |
| end loop; |
| |
| -- If only one is not used, report a single warning for this value |
| |
| if Non_Used = 1 then |
| Error_Msg_Name_1 := Choices.Table (First_Non_Used).The_String; |
| Error_Msg |
| (Flags, "?value %% is not used as label", Case_Location); |
| |
| -- If several are not used, report a warning for each one of them |
| |
| elsif Non_Used > 1 then |
| Error_Msg |
| (Flags, "?the following values are not used as labels:", |
| Case_Location); |
| |
| for Choice in First_Non_Used .. Choices.Last loop |
| if not Choices.Table (Choice).Already_Used then |
| Error_Msg_Name_1 := Choices.Table (Choice).The_String; |
| Error_Msg (Flags, "\?%%", Case_Location); |
| end if; |
| end loop; |
| end if; |
| else |
| Error_Msg |
| (Flags, |
| "?no when others for this case construction", |
| Case_Location); |
| end if; |
| end if; |
| |
| -- If this is the only case construction, empty the tables |
| |
| if Choice_Lasts.Last = 1 then |
| Choice_Lasts.Set_Last (0); |
| Choices.Set_Last (First_Choice_Node_Id); |
| Choice_First := 0; |
| |
| -- Second case construction, set the tables to the first |
| |
| elsif Choice_Lasts.Last = 2 then |
| Choice_Lasts.Set_Last (1); |
| Choices.Set_Last (Choice_Lasts.Table (1)); |
| Choice_First := 1; |
| |
| -- Third or more case construction, set the tables to the previous one |
| else |
| Choice_Lasts.Decrement_Last; |
| Choices.Set_Last (Choice_Lasts.Table (Choice_Lasts.Last)); |
| Choice_First := Choice_Lasts.Table (Choice_Lasts.Last - 1) + 1; |
| end if; |
| end End_Case_Construction; |
| |
| ------------------------ |
| -- External_Reference -- |
| ------------------------ |
| |
| procedure External_Reference |
| (In_Tree : Project_Node_Tree_Ref; |
| Current_Project : Project_Node_Id; |
| Current_Package : Project_Node_Id; |
| External_Value : out Project_Node_Id; |
| Expr_Kind : in out Variable_Kind; |
| Flags : Processing_Flags) |
| is |
| Field_Id : Project_Node_Id := Empty_Node; |
| Ext_List : Boolean := False; |
| |
| begin |
| External_Value := |
| Default_Project_Node |
| (Of_Kind => N_External_Value, |
| In_Tree => In_Tree); |
| Set_Location_Of (External_Value, In_Tree, To => Token_Ptr); |
| |
| -- The current token is either external or external_as_list |
| |
| Ext_List := Token = Tok_External_As_List; |
| Scan (In_Tree); |
| |
| if Ext_List then |
| Set_Expression_Kind_Of (External_Value, In_Tree, To => List); |
| else |
| Set_Expression_Kind_Of (External_Value, In_Tree, To => Single); |
| end if; |
| |
| if Expr_Kind = Undefined then |
| if Ext_List then |
| Expr_Kind := List; |
| else |
| Expr_Kind := Single; |
| end if; |
| end if; |
| |
| Expect (Tok_Left_Paren, "`(`"); |
| |
| -- Scan past the left parenthesis |
| |
| if Token = Tok_Left_Paren then |
| Scan (In_Tree); |
| end if; |
| |
| -- Get the name of the external reference |
| |
| Expect (Tok_String_Literal, "literal string"); |
| |
| if Token = Tok_String_Literal then |
| Field_Id := |
| Default_Project_Node |
| (Of_Kind => N_Literal_String, |
| In_Tree => In_Tree, |
| And_Expr_Kind => Single); |
| Set_String_Value_Of (Field_Id, In_Tree, To => Token_Name); |
| Set_External_Reference_Of (External_Value, In_Tree, To => Field_Id); |
| |
| -- Scan past the first argument |
| |
| Scan (In_Tree); |
| |
| case Token is |
| when Tok_Right_Paren => |
| if Ext_List then |
| Error_Msg (Flags, "`,` expected", Token_Ptr); |
| end if; |
| |
| Scan (In_Tree); -- scan past right paren |
| |
| when Tok_Comma => |
| Scan (In_Tree); -- scan past comma |
| |
| -- Get the string expression for the default |
| |
| declare |
| Loc : constant Source_Ptr := Token_Ptr; |
| |
| begin |
| Parse_Expression |
| (In_Tree => In_Tree, |
| Expression => Field_Id, |
| Flags => Flags, |
| Current_Project => Current_Project, |
| Current_Package => Current_Package, |
| Optional_Index => False); |
| |
| if Expression_Kind_Of (Field_Id, In_Tree) = List then |
| Error_Msg |
| (Flags, "expression must be a single string", Loc); |
| else |
| Set_External_Default_Of |
| (External_Value, In_Tree, To => Field_Id); |
| end if; |
| end; |
| |
| Expect (Tok_Right_Paren, "`)`"); |
| |
| if Token = Tok_Right_Paren then |
| Scan (In_Tree); -- scan past right paren |
| end if; |
| |
| when others => |
| if Ext_List then |
| Error_Msg (Flags, "`,` expected", Token_Ptr); |
| else |
| Error_Msg (Flags, "`,` or `)` expected", Token_Ptr); |
| end if; |
| end case; |
| end if; |
| end External_Reference; |
| |
| ----------------------- |
| -- Parse_Choice_List -- |
| ----------------------- |
| |
| procedure Parse_Choice_List |
| (In_Tree : Project_Node_Tree_Ref; |
| First_Choice : out Project_Node_Id; |
| Flags : Processing_Flags; |
| String_Type : Boolean := True) |
| is |
| Current_Choice : Project_Node_Id := Empty_Node; |
| Next_Choice : Project_Node_Id := Empty_Node; |
| Choice_String : Name_Id := No_Name; |
| Found : Boolean := False; |
| |
| begin |
| -- Declare the node of the first choice |
| |
| First_Choice := |
| Default_Project_Node |
| (Of_Kind => N_Literal_String, |
| In_Tree => In_Tree, |
| And_Expr_Kind => Single); |
| |
| -- Initially Current_Choice is the same as First_Choice |
| |
| Current_Choice := First_Choice; |
| |
| loop |
| Expect (Tok_String_Literal, "literal string"); |
| exit when Token /= Tok_String_Literal; |
| Set_Location_Of (Current_Choice, In_Tree, To => Token_Ptr); |
| Choice_String := Token_Name; |
| |
| -- Give the string value to the current choice |
| |
| Set_String_Value_Of (Current_Choice, In_Tree, To => Choice_String); |
| |
| if String_Type then |
| |
| -- Check if the label is part of the string type and if it has not |
| -- been already used. |
| |
| Found := False; |
| for Choice in Choice_First .. Choices.Last loop |
| if Choices.Table (Choice).The_String = Choice_String then |
| |
| -- This label is part of the string type |
| |
| Found := True; |
| |
| if Choices.Table (Choice).Already_Used then |
| |
| -- But it has already appeared in a choice list for this |
| -- case construction so report an error. |
| |
| Error_Msg_Name_1 := Choice_String; |
| Error_Msg (Flags, "duplicate case label %%", Token_Ptr); |
| |
| else |
| Choices.Table (Choice).Already_Used := True; |
| end if; |
| |
| exit; |
| end if; |
| end loop; |
| |
| -- If the label is not part of the string list, report an error |
| |
| if not Found then |
| Error_Msg_Name_1 := Choice_String; |
| Error_Msg (Flags, "illegal case label %%", Token_Ptr); |
| end if; |
| end if; |
| |
| -- Scan past the label |
| |
| Scan (In_Tree); |
| |
| -- If there is no '|', we are done |
| |
| if Token = Tok_Vertical_Bar then |
| |
| -- Otherwise, declare the node of the next choice, link it to |
| -- Current_Choice and set Current_Choice to this new node. |
| |
| Next_Choice := |
| Default_Project_Node |
| (Of_Kind => N_Literal_String, |
| In_Tree => In_Tree, |
| And_Expr_Kind => Single); |
| Set_Next_Literal_String |
| (Current_Choice, In_Tree, To => Next_Choice); |
| Current_Choice := Next_Choice; |
| Scan (In_Tree); |
| else |
| exit; |
| end if; |
| end loop; |
| end Parse_Choice_List; |
| |
| ---------------------- |
| -- Parse_Expression -- |
| ---------------------- |
| |
| procedure Parse_Expression |
| (In_Tree : Project_Node_Tree_Ref; |
| Expression : out Project_Node_Id; |
| Current_Project : Project_Node_Id; |
| Current_Package : Project_Node_Id; |
| Optional_Index : Boolean; |
| Flags : Processing_Flags) |
| is |
| First_Term : Project_Node_Id := Empty_Node; |
| Expression_Kind : Variable_Kind := Undefined; |
| |
| begin |
| -- Declare the node of the expression |
| |
| Expression := |
| Default_Project_Node (Of_Kind => N_Expression, In_Tree => In_Tree); |
| Set_Location_Of (Expression, In_Tree, To => Token_Ptr); |
| |
| -- Parse the term or terms of the expression |
| |
| Terms (In_Tree => In_Tree, |
| Term => First_Term, |
| Expr_Kind => Expression_Kind, |
| Flags => Flags, |
| Current_Project => Current_Project, |
| Current_Package => Current_Package, |
| Optional_Index => Optional_Index); |
| |
| -- Set the first term and the expression kind |
| |
| Set_First_Term (Expression, In_Tree, To => First_Term); |
| Set_Expression_Kind_Of (Expression, In_Tree, To => Expression_Kind); |
| end Parse_Expression; |
| |
| ---------------------------- |
| -- Parse_String_Type_List -- |
| ---------------------------- |
| |
| procedure Parse_String_Type_List |
| (In_Tree : Project_Node_Tree_Ref; |
| First_String : out Project_Node_Id; |
| Flags : Processing_Flags) |
| is |
| Last_String : Project_Node_Id := Empty_Node; |
| Next_String : Project_Node_Id := Empty_Node; |
| String_Value : Name_Id := No_Name; |
| |
| begin |
| -- Declare the node of the first string |
| |
| First_String := |
| Default_Project_Node |
| (Of_Kind => N_Literal_String, |
| In_Tree => In_Tree, |
| And_Expr_Kind => Single); |
| |
| -- Initially, Last_String is the same as First_String |
| |
| Last_String := First_String; |
| |
| loop |
| Expect (Tok_String_Literal, "literal string"); |
| exit when Token /= Tok_String_Literal; |
| String_Value := Token_Name; |
| |
| -- Give its string value to Last_String |
| |
| Set_String_Value_Of (Last_String, In_Tree, To => String_Value); |
| Set_Location_Of (Last_String, In_Tree, To => Token_Ptr); |
| |
| -- Now, check if the string is already part of the string type |
| |
| declare |
| Current : Project_Node_Id := First_String; |
| |
| begin |
| while Current /= Last_String loop |
| if String_Value_Of (Current, In_Tree) = String_Value then |
| |
| -- This is a repetition, report an error |
| |
| Error_Msg_Name_1 := String_Value; |
| Error_Msg (Flags, "duplicate value %% in type", Token_Ptr); |
| exit; |
| end if; |
| |
| Current := Next_Literal_String (Current, In_Tree); |
| end loop; |
| end; |
| |
| -- Scan past the literal string |
| |
| Scan (In_Tree); |
| |
| -- If there is no comma following the literal string, we are done |
| |
| if Token /= Tok_Comma then |
| exit; |
| |
| else |
| -- Declare the next string, link it to Last_String and set |
| -- Last_String to its node. |
| |
| Next_String := |
| Default_Project_Node |
| (Of_Kind => N_Literal_String, |
| In_Tree => In_Tree, |
| And_Expr_Kind => Single); |
| Set_Next_Literal_String (Last_String, In_Tree, To => Next_String); |
| Last_String := Next_String; |
| Scan (In_Tree); |
| end if; |
| end loop; |
| end Parse_String_Type_List; |
| |
| ------------------------------ |
| -- Parse_Variable_Reference -- |
| ------------------------------ |
| |
| procedure Parse_Variable_Reference |
| (In_Tree : Project_Node_Tree_Ref; |
| Variable : out Project_Node_Id; |
| Current_Project : Project_Node_Id; |
| Current_Package : Project_Node_Id; |
| Flags : Processing_Flags) |
| is |
| Current_Variable : Project_Node_Id := Empty_Node; |
| |
| The_Package : Project_Node_Id := Current_Package; |
| The_Project : Project_Node_Id := Current_Project; |
| |
| Specified_Project : Project_Node_Id := Empty_Node; |
| Specified_Package : Project_Node_Id := Empty_Node; |
| Look_For_Variable : Boolean := True; |
| First_Attribute : Attribute_Node_Id := Empty_Attribute; |
| Variable_Name : Name_Id; |
| |
| begin |
| Names.Init; |
| |
| loop |
| Expect (Tok_Identifier, "identifier"); |
| |
| if Token /= Tok_Identifier then |
| Look_For_Variable := False; |
| exit; |
| end if; |
| |
| Add_To_Names (NL => (Name => Token_Name, Location => Token_Ptr)); |
| Scan (In_Tree); |
| exit when Token /= Tok_Dot; |
| Scan (In_Tree); |
| end loop; |
| |
| if Look_For_Variable then |
| |
| if Token = Tok_Apostrophe then |
| |
| -- Attribute reference |
| |
| case Names.Last is |
| when 0 => |
| |
| -- Cannot happen |
| |
| null; |
| |
| when 1 => |
| -- This may be a project name or a package name. |
| -- Project name have precedence. |
| |
| -- First, look if it can be a package name |
| |
| First_Attribute := |
| First_Attribute_Of |
| (Package_Node_Id_Of (Names.Table (1).Name)); |
| |
| -- Now, look if it can be a project name |
| |
| if Names.Table (1).Name = |
| Name_Of (Current_Project, In_Tree) |
| then |
| The_Project := Current_Project; |
| |
| else |
| The_Project := |
| Imported_Or_Extended_Project_Of |
| (Current_Project, In_Tree, Names.Table (1).Name); |
| end if; |
| |
| if No (The_Project) then |
| |
| -- If it is neither a project name nor a package name, |
| -- report an error. |
| |
| if First_Attribute = Empty_Attribute then |
| Error_Msg_Name_1 := Names.Table (1).Name; |
| Error_Msg (Flags, "unknown project %", |
| Names.Table (1).Location); |
| First_Attribute := Attribute_First; |
| |
| else |
| -- If it is a package name, check if the package has |
| -- already been declared in the current project. |
| |
| The_Package := |
| First_Package_Of (Current_Project, In_Tree); |
| |
| while Present (The_Package) |
| and then Name_Of (The_Package, In_Tree) /= |
| Names.Table (1).Name |
| loop |
| The_Package := |
| Next_Package_In_Project (The_Package, In_Tree); |
| end loop; |
| |
| -- If it has not been already declared, report an |
| -- error. |
| |
| if No (The_Package) then |
| Error_Msg_Name_1 := Names.Table (1).Name; |
| Error_Msg (Flags, "package % not yet defined", |
| Names.Table (1).Location); |
| end if; |
| end if; |
| |
| else |
| -- It is a project name |
| |
| First_Attribute := Attribute_First; |
| The_Package := Empty_Node; |
| end if; |
| |
| when others => |
| |
| -- We have either a project name made of several simple |
| -- names (long project), or a project name (short project) |
| -- followed by a package name. The long project name has |
| -- precedence. |
| |
| declare |
| Short_Project : Name_Id; |
| Long_Project : Name_Id; |
| |
| begin |
| -- Clear the Buffer |
| |
| Buffer_Last := 0; |
| |
| -- Get the name of the short project |
| |
| for Index in 1 .. Names.Last - 1 loop |
| Add_To_Buffer |
| (Get_Name_String (Names.Table (Index).Name), |
| Buffer, Buffer_Last); |
| |
| if Index /= Names.Last - 1 then |
| Add_To_Buffer (".", Buffer, Buffer_Last); |
| end if; |
| end loop; |
| |
| Name_Len := Buffer_Last; |
| Name_Buffer (1 .. Buffer_Last) := |
| Buffer (1 .. Buffer_Last); |
| Short_Project := Name_Find; |
| |
| -- Now, add the last simple name to get the name of the |
| -- long project. |
| |
| Add_To_Buffer (".", Buffer, Buffer_Last); |
| Add_To_Buffer |
| (Get_Name_String (Names.Table (Names.Last).Name), |
| Buffer, Buffer_Last); |
| Name_Len := Buffer_Last; |
| Name_Buffer (1 .. Buffer_Last) := |
| Buffer (1 .. Buffer_Last); |
| Long_Project := Name_Find; |
| |
| -- Check if the long project is imported or extended |
| |
| if Long_Project = Name_Of (Current_Project, In_Tree) then |
| The_Project := Current_Project; |
| |
| else |
| The_Project := |
| Imported_Or_Extended_Project_Of |
| (Current_Project, |
| In_Tree, |
| Long_Project); |
| end if; |
| |
| -- If the long project exists, then this is the prefix |
| -- of the attribute. |
| |
| if Present (The_Project) then |
| First_Attribute := Attribute_First; |
| The_Package := Empty_Node; |
| |
| else |
| -- Otherwise, check if the short project is imported |
| -- or extended. |
| |
| if Short_Project = |
| Name_Of (Current_Project, In_Tree) |
| then |
| The_Project := Current_Project; |
| |
| else |
| The_Project := Imported_Or_Extended_Project_Of |
| (Current_Project, In_Tree, |
| Short_Project); |
| end if; |
| |
| -- If short project does not exist, report an error |
| |
| if No (The_Project) then |
| Error_Msg_Name_1 := Long_Project; |
| Error_Msg_Name_2 := Short_Project; |
| Error_Msg (Flags, "unknown projects % or %", |
| Names.Table (1).Location); |
| The_Package := Empty_Node; |
| First_Attribute := Attribute_First; |
| |
| else |
| -- Now, we check if the package has been declared |
| -- in this project. |
| |
| The_Package := |
| First_Package_Of (The_Project, In_Tree); |
| while Present (The_Package) |
| and then Name_Of (The_Package, In_Tree) /= |
| Names.Table (Names.Last).Name |
| loop |
| The_Package := |
| Next_Package_In_Project (The_Package, In_Tree); |
| end loop; |
| |
| -- If it has not, then we report an error |
| |
| if No (The_Package) then |
| Error_Msg_Name_1 := |
| Names.Table (Names.Last).Name; |
| Error_Msg_Name_2 := Short_Project; |
| Error_Msg (Flags, |
| "package % not declared in project %", |
| Names.Table (Names.Last).Location); |
| First_Attribute := Attribute_First; |
| |
| else |
| -- Otherwise, we have the correct project and |
| -- package. |
| |
| First_Attribute := |
| First_Attribute_Of |
| (Package_Id_Of (The_Package, In_Tree)); |
| end if; |
| end if; |
| end if; |
| end; |
| end case; |
| |
| Attribute_Reference |
| (In_Tree, |
| Variable, |
| Flags => Flags, |
| Current_Project => The_Project, |
| Current_Package => The_Package, |
| First_Attribute => First_Attribute); |
| return; |
| end if; |
| end if; |
| |
| Variable := |
| Default_Project_Node |
| (Of_Kind => N_Variable_Reference, In_Tree => In_Tree); |
| |
| if Look_For_Variable then |
| case Names.Last is |
| when 0 => |
| |
| -- Cannot happen (so why null instead of raise PE???) |
| |
| null; |
| |
| when 1 => |
| |
| -- Simple variable name |
| |
| Set_Name_Of (Variable, In_Tree, To => Names.Table (1).Name); |
| |
| when 2 => |
| |
| -- Variable name with a simple name prefix that can be |
| -- a project name or a package name. Project names have |
| -- priority over package names. |
| |
| Set_Name_Of (Variable, In_Tree, To => Names.Table (2).Name); |
| |
| -- Check if it can be a package name |
| |
| The_Package := First_Package_Of (Current_Project, In_Tree); |
| |
| while Present (The_Package) |
| and then Name_Of (The_Package, In_Tree) /= |
| Names.Table (1).Name |
| loop |
| The_Package := |
| Next_Package_In_Project (The_Package, In_Tree); |
| end loop; |
| |
| -- Now look for a possible project name |
| |
| The_Project := Imported_Or_Extended_Project_Of |
| (Current_Project, In_Tree, Names.Table (1).Name); |
| |
| if Present (The_Project) then |
| Specified_Project := The_Project; |
| |
| elsif No (The_Package) then |
| Error_Msg_Name_1 := Names.Table (1).Name; |
| Error_Msg (Flags, "unknown package or project %", |
| Names.Table (1).Location); |
| Look_For_Variable := False; |
| |
| else |
| Specified_Package := The_Package; |
| end if; |
| |
| when others => |
| |
| -- Variable name with a prefix that is either a project name |
| -- made of several simple names, or a project name followed |
| -- by a package name. |
| |
| Set_Name_Of |
| (Variable, In_Tree, To => Names.Table (Names.Last).Name); |
| |
| declare |
| Short_Project : Name_Id; |
| Long_Project : Name_Id; |
| |
| begin |
| -- First, we get the two possible project names |
| |
| -- Clear the buffer |
| |
| Buffer_Last := 0; |
| |
| -- Add all the simple names, except the last two |
| |
| for Index in 1 .. Names.Last - 2 loop |
| Add_To_Buffer |
| (Get_Name_String (Names.Table (Index).Name), |
| Buffer, Buffer_Last); |
| |
| if Index /= Names.Last - 2 then |
| Add_To_Buffer (".", Buffer, Buffer_Last); |
| end if; |
| end loop; |
| |
| Name_Len := Buffer_Last; |
| Name_Buffer (1 .. Name_Len) := Buffer (1 .. Buffer_Last); |
| Short_Project := Name_Find; |
| |
| -- Add the simple name before the name of the variable |
| |
| Add_To_Buffer (".", Buffer, Buffer_Last); |
| Add_To_Buffer |
| (Get_Name_String (Names.Table (Names.Last - 1).Name), |
| Buffer, Buffer_Last); |
| Name_Len := Buffer_Last; |
| Name_Buffer (1 .. Name_Len) := Buffer (1 .. Buffer_Last); |
| Long_Project := Name_Find; |
| |
| -- Check if the prefix is the name of an imported or |
| -- extended project. |
| |
| The_Project := Imported_Or_Extended_Project_Of |
| (Current_Project, In_Tree, Long_Project); |
| |
| if Present (The_Project) then |
| Specified_Project := The_Project; |
| |
| else |
| -- Now check if the prefix may be a project name followed |
| -- by a package name. |
| |
| -- First check for a possible project name |
| |
| The_Project := |
| Imported_Or_Extended_Project_Of |
| (Current_Project, In_Tree, Short_Project); |
| |
| if No (The_Project) then |
| -- Unknown prefix, report an error |
| |
| Error_Msg_Name_1 := Long_Project; |
| Error_Msg_Name_2 := Short_Project; |
| Error_Msg |
| (Flags, "unknown projects % or %", |
| Names.Table (1).Location); |
| Look_For_Variable := False; |
| |
| else |
| Specified_Project := The_Project; |
| |
| -- Now look for the package in this project |
| |
| The_Package := First_Package_Of (The_Project, In_Tree); |
| |
| while Present (The_Package) |
| and then Name_Of (The_Package, In_Tree) /= |
| Names.Table (Names.Last - 1).Name |
| loop |
| The_Package := |
| Next_Package_In_Project (The_Package, In_Tree); |
| end loop; |
| |
| if No (The_Package) then |
| |
| -- The package does not exist, report an error |
| |
| Error_Msg_Name_1 := Names.Table (2).Name; |
| Error_Msg (Flags, "unknown package %", |
| Names.Table (Names.Last - 1).Location); |
| Look_For_Variable := False; |
| |
| else |
| Specified_Package := The_Package; |
| end if; |
| end if; |
| end if; |
| end; |
| end case; |
| end if; |
| |
| if Look_For_Variable then |
| Variable_Name := Name_Of (Variable, In_Tree); |
| Set_Project_Node_Of (Variable, In_Tree, To => Specified_Project); |
| Set_Package_Node_Of (Variable, In_Tree, To => Specified_Package); |
| |
| if Present (Specified_Project) then |
| The_Project := Specified_Project; |
| else |
| The_Project := Current_Project; |
| end if; |
| |
| Current_Variable := Empty_Node; |
| |
| -- Look for this variable |
| |
| -- If a package was specified, check if the variable has been |
| -- declared in this package. |
| |
| if Present (Specified_Package) then |
| Current_Variable := |
| First_Variable_Of (Specified_Package, In_Tree); |
| while Present (Current_Variable) |
| and then |
| Name_Of (Current_Variable, In_Tree) /= Variable_Name |
| loop |
| Current_Variable := Next_Variable (Current_Variable, In_Tree); |
| end loop; |
| |
| else |
| -- Otherwise, if no project has been specified and we are in |
| -- a package, first check if the variable has been declared in |
| -- the package. |
| |
| if No (Specified_Project) |
| and then Present (Current_Package) |
| then |
| Current_Variable := |
| First_Variable_Of (Current_Package, In_Tree); |
| while Present (Current_Variable) |
| and then Name_Of (Current_Variable, In_Tree) /= Variable_Name |
| loop |
| Current_Variable := |
| Next_Variable (Current_Variable, In_Tree); |
| end loop; |
| end if; |
| |
| -- If we have not found the variable in the package, check if the |
| -- variable has been declared in the project, or in any of its |
| -- ancestors, or in any of the project it extends. |
| |
| if No (Current_Variable) then |
| declare |
| Proj : Project_Node_Id := The_Project; |
| |
| begin |
| loop |
| Current_Variable := First_Variable_Of (Proj, In_Tree); |
| while |
| Present (Current_Variable) |
| and then |
| Name_Of (Current_Variable, In_Tree) /= Variable_Name |
| loop |
| Current_Variable := |
| Next_Variable (Current_Variable, In_Tree); |
| end loop; |
| |
| exit when Present (Current_Variable); |
| |
| -- If the current project is a child project, check if |
| -- the variable is declared in its parent. Otherwise, if |
| -- the current project extends another project, check if |
| -- the variable is declared in one of the projects the |
| -- current project extends. |
| |
| if No (Parent_Project_Of (Proj, In_Tree)) then |
| Proj := |
| Extended_Project_Of |
| (Project_Declaration_Of (Proj, In_Tree), In_Tree); |
| else |
| Proj := Parent_Project_Of (Proj, In_Tree); |
| end if; |
| |
| Set_Project_Node_Of (Variable, In_Tree, To => Proj); |
| |
| exit when No (Proj); |
| end loop; |
| end; |
| end if; |
| end if; |
| |
| -- If the variable was not found, report an error |
| |
| if No (Current_Variable) then |
| Error_Msg_Name_1 := Variable_Name; |
| Error_Msg |
| (Flags, "unknown variable %", Names.Table (Names.Last).Location); |
| end if; |
| end if; |
| |
| if Present (Current_Variable) then |
| Set_Expression_Kind_Of |
| (Variable, In_Tree, |
| To => Expression_Kind_Of (Current_Variable, In_Tree)); |
| |
| if Kind_Of (Current_Variable, In_Tree) = |
| N_Typed_Variable_Declaration |
| then |
| Set_String_Type_Of |
| (Variable, In_Tree, |
| To => String_Type_Of (Current_Variable, In_Tree)); |
| end if; |
| end if; |
| |
| -- If the variable is followed by a left parenthesis, report an error |
| -- but attempt to scan the index. |
| |
| if Token = Tok_Left_Paren then |
| Error_Msg |
| (Flags, "\variables cannot be associative arrays", Token_Ptr); |
| Scan (In_Tree); |
| Expect (Tok_String_Literal, "literal string"); |
| |
| if Token = Tok_String_Literal then |
| Scan (In_Tree); |
| Expect (Tok_Right_Paren, "`)`"); |
| |
| if Token = Tok_Right_Paren then |
| Scan (In_Tree); |
| end if; |
| end if; |
| end if; |
| end Parse_Variable_Reference; |
| |
| --------------------------------- |
| -- Start_New_Case_Construction -- |
| --------------------------------- |
| |
| procedure Start_New_Case_Construction |
| (In_Tree : Project_Node_Tree_Ref; |
| String_Type : Project_Node_Id) |
| is |
| Current_String : Project_Node_Id; |
| |
| begin |
| -- Set Choice_First, depending on whether this is the first case |
| -- construction or not. |
| |
| if Choice_First = 0 then |
| Choice_First := 1; |
| Choices.Set_Last (First_Choice_Node_Id); |
| else |
| Choice_First := Choices.Last + 1; |
| end if; |
| |
| -- Add the literal of the string type to the Choices table |
| |
| if Present (String_Type) then |
| Current_String := First_Literal_String (String_Type, In_Tree); |
| while Present (Current_String) loop |
| Add (This_String => String_Value_Of (Current_String, In_Tree)); |
| Current_String := Next_Literal_String (Current_String, In_Tree); |
| end loop; |
| end if; |
| |
| -- Set the value of the last choice in table Choice_Lasts |
| |
| Choice_Lasts.Increment_Last; |
| Choice_Lasts.Table (Choice_Lasts.Last) := Choices.Last; |
| end Start_New_Case_Construction; |
| |
| ----------- |
| -- Terms -- |
| ----------- |
| |
| procedure Terms |
| (In_Tree : Project_Node_Tree_Ref; |
| Term : out Project_Node_Id; |
| Expr_Kind : in out Variable_Kind; |
| Current_Project : Project_Node_Id; |
| Current_Package : Project_Node_Id; |
| Optional_Index : Boolean; |
| Flags : Processing_Flags) |
| is |
| Next_Term : Project_Node_Id := Empty_Node; |
| Term_Id : Project_Node_Id := Empty_Node; |
| Current_Expression : Project_Node_Id := Empty_Node; |
| Next_Expression : Project_Node_Id := Empty_Node; |
| Current_Location : Source_Ptr := No_Location; |
| Reference : Project_Node_Id := Empty_Node; |
| |
| begin |
| -- Declare a new node for the term |
| |
| Term := Default_Project_Node (Of_Kind => N_Term, In_Tree => In_Tree); |
| Set_Location_Of (Term, In_Tree, To => Token_Ptr); |
| |
| case Token is |
| when Tok_Left_Paren => |
| |
| -- If we have a left parenthesis and we don't know the expression |
| -- kind, then this is a string list. |
| |
| case Expr_Kind is |
| when Undefined => |
| Expr_Kind := List; |
| |
| when List => |
| null; |
| |
| when Single => |
| |
| -- If we already know that this is a single string, report |
| -- an error, but set the expression kind to string list to |
| -- avoid several errors. |
| |
| Expr_Kind := List; |
| Error_Msg |
| (Flags, "literal string list cannot appear in a string", |
| Token_Ptr); |
| end case; |
| |
| -- Declare a new node for this literal string list |
| |
| Term_Id := Default_Project_Node |
| (Of_Kind => N_Literal_String_List, |
| In_Tree => In_Tree, |
| And_Expr_Kind => List); |
| Set_Current_Term (Term, In_Tree, To => Term_Id); |
| Set_Location_Of (Term, In_Tree, To => Token_Ptr); |
| |
| -- Scan past the left parenthesis |
| |
| Scan (In_Tree); |
| |
| -- If the left parenthesis is immediately followed by a right |
| -- parenthesis, the literal string list is empty. |
| |
| if Token = Tok_Right_Paren then |
| Scan (In_Tree); |
| |
| else |
| -- Otherwise parse the expression(s) in the literal string list |
| |
| loop |
| Current_Location := Token_Ptr; |
| Parse_Expression |
| (In_Tree => In_Tree, |
| Expression => Next_Expression, |
| Flags => Flags, |
| Current_Project => Current_Project, |
| Current_Package => Current_Package, |
| Optional_Index => Optional_Index); |
| |
| -- The expression kind is String list, report an error |
| |
| if Expression_Kind_Of (Next_Expression, In_Tree) = List then |
| Error_Msg (Flags, "single expression expected", |
| Current_Location); |
| end if; |
| |
| -- If Current_Expression is empty, it means that the |
| -- expression is the first in the string list. |
| |
| if No (Current_Expression) then |
| Set_First_Expression_In_List |
| (Term_Id, In_Tree, To => Next_Expression); |
| else |
| Set_Next_Expression_In_List |
| (Current_Expression, In_Tree, To => Next_Expression); |
| end if; |
| |
| Current_Expression := Next_Expression; |
| |
| -- If there is a comma, continue with the next expression |
| |
| exit when Token /= Tok_Comma; |
| Scan (In_Tree); -- past the comma |
| end loop; |
| |
| -- We expect a closing right parenthesis |
| |
| Expect (Tok_Right_Paren, "`)`"); |
| |
| if Token = Tok_Right_Paren then |
| Scan (In_Tree); |
| end if; |
| end if; |
| |
| when Tok_String_Literal => |
| |
| -- If we don't know the expression kind (first term), then it is |
| -- a simple string. |
| |
| if Expr_Kind = Undefined then |
| Expr_Kind := Single; |
| end if; |
| |
| -- Declare a new node for the string literal |
| |
| Term_Id := |
| Default_Project_Node |
| (Of_Kind => N_Literal_String, In_Tree => In_Tree); |
| Set_Current_Term (Term, In_Tree, To => Term_Id); |
| Set_String_Value_Of (Term_Id, In_Tree, To => Token_Name); |
| |
| -- Scan past the string literal |
| |
| Scan (In_Tree); |
| |
| -- Check for possible index expression |
| |
| if Token = Tok_At then |
| if not Optional_Index then |
| Error_Msg (Flags, "index not allowed here", Token_Ptr); |
| Scan (In_Tree); |
| |
| if Token = Tok_Integer_Literal then |
| Scan (In_Tree); |
| end if; |
| |
| -- Set the index value |
| |
| else |
| Scan (In_Tree); |
| Expect (Tok_Integer_Literal, "integer literal"); |
| |
| if Token = Tok_Integer_Literal then |
| declare |
| Index : constant Int := UI_To_Int (Int_Literal_Value); |
| begin |
| if Index = 0 then |
| Error_Msg |
| (Flags, "index cannot be zero", Token_Ptr); |
| else |
| Set_Source_Index_Of |
| (Term_Id, In_Tree, To => Index); |
| end if; |
| end; |
| |
| Scan (In_Tree); |
| end if; |
| end if; |
| end if; |
| |
| when Tok_Identifier => |
| Current_Location := Token_Ptr; |
| |
| -- Get the variable or attribute reference |
| |
| Parse_Variable_Reference |
| (In_Tree => In_Tree, |
| Variable => Reference, |
| Flags => Flags, |
| Current_Project => Current_Project, |
| Current_Package => Current_Package); |
| Set_Current_Term (Term, In_Tree, To => Reference); |
| |
| if Present (Reference) then |
| |
| -- If we don't know the expression kind (first term), then it |
| -- has the kind of the variable or attribute reference. |
| |
| if Expr_Kind = Undefined then |
| Expr_Kind := Expression_Kind_Of (Reference, In_Tree); |
| |
| elsif Expr_Kind = Single |
| and then Expression_Kind_Of (Reference, In_Tree) = List |
| then |
| -- If the expression is a single list, and the reference is |
| -- a string list, report an error, and set the expression |
| -- kind to string list to avoid multiple errors. |
| |
| Expr_Kind := List; |
| Error_Msg |
| (Flags, |
| "list variable cannot appear in single string expression", |
| Current_Location); |
| end if; |
| end if; |
| |
| when Tok_Project => |
| |
| -- Project can appear in an expression as the prefix of an |
| -- attribute reference of the current project. |
| |
| Current_Location := Token_Ptr; |
| Scan (In_Tree); |
| Expect (Tok_Apostrophe, "`'`"); |
| |
| if Token = Tok_Apostrophe then |
| Attribute_Reference |
| (In_Tree => In_Tree, |
| Reference => Reference, |
| Flags => Flags, |
| First_Attribute => Prj.Attr.Attribute_First, |
| Current_Project => Current_Project, |
| Current_Package => Empty_Node); |
| Set_Current_Term (Term, In_Tree, To => Reference); |
| end if; |
| |
| -- Same checks as above for the expression kind |
| |
| if Present (Reference) then |
| if Expr_Kind = Undefined then |
| Expr_Kind := Expression_Kind_Of (Reference, In_Tree); |
| |
| elsif Expr_Kind = Single |
| and then Expression_Kind_Of (Reference, In_Tree) = List |
| then |
| Error_Msg |
| (Flags, "lists cannot appear in single string expression", |
| Current_Location); |
| end if; |
| end if; |
| |
| when Tok_External | Tok_External_As_List => |
| External_Reference |
| (In_Tree => In_Tree, |
| Flags => Flags, |
| Current_Project => Current_Project, |
| Current_Package => Current_Package, |
| Expr_Kind => Expr_Kind, |
| External_Value => Reference); |
| Set_Current_Term (Term, In_Tree, To => Reference); |
| |
| when others => |
| Error_Msg (Flags, "cannot be part of an expression", Token_Ptr); |
| Term := Empty_Node; |
| return; |
| end case; |
| |
| -- If there is an '&', call Terms recursively |
| |
| if Token = Tok_Ampersand then |
| Scan (In_Tree); -- scan past ampersand |
| |
| Terms |
| (In_Tree => In_Tree, |
| Term => Next_Term, |
| Expr_Kind => Expr_Kind, |
| Flags => Flags, |
| Current_Project => Current_Project, |
| Current_Package => Current_Package, |
| Optional_Index => Optional_Index); |
| |
| -- And link the next term to this term |
| |
| Set_Next_Term (Term, In_Tree, To => Next_Term); |
| end if; |
| end Terms; |
| |
| end Prj.Strt; |