| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- P R J . S T R T -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 2001-2003 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 2, 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 COPYING. If not, write -- |
| -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- |
| -- MA 02111-1307, USA. -- |
| -- -- |
| -- 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 Namet; use Namet; |
| with Prj.Attr; use Prj.Attr; |
| with Prj.Err; use Prj.Err; |
| with Prj.Tree; use Prj.Tree; |
| with Scans; use Scans; |
| with Snames; |
| with Table; |
| with Types; use Types; |
| |
| package body Prj.Strt is |
| |
| 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 := 50; |
| |
| 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, |
| 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 indices 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 (External_Value : out Project_Node_Id); |
| -- Parse an external reference. Current token is "external". |
| |
| procedure Attribute_Reference |
| (Reference : out Project_Node_Id; |
| First_Attribute : Attribute_Node_Id; |
| Current_Project : Project_Node_Id; |
| Current_Package : Project_Node_Id); |
| -- Parse an attribute reference. Current token is an apostrophe. |
| |
| procedure Terms |
| (Term : out Project_Node_Id; |
| Expr_Kind : in out Variable_Kind; |
| Current_Project : Project_Node_Id; |
| Current_Package : Project_Node_Id); |
| -- 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 |
| (Reference : out Project_Node_Id; |
| First_Attribute : Attribute_Node_Id; |
| Current_Project : Project_Node_Id; |
| Current_Package : Project_Node_Id) |
| 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); |
| Set_Location_Of (Reference, To => Token_Ptr); |
| Scan; -- 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, To => Token_Name); |
| |
| -- Check if the identifier is one of the attribute identifiers in the |
| -- context (package or project level attributes). |
| |
| while Current_Attribute /= Empty_Attribute |
| and then |
| Attributes.Table (Current_Attribute).Name /= Token_Name |
| loop |
| Current_Attribute := Attributes.Table (Current_Attribute).Next; |
| end loop; |
| |
| -- If the identifier is not allowed, report an error |
| |
| if Current_Attribute = Empty_Attribute then |
| Error_Msg_Name_1 := Token_Name; |
| Error_Msg ("unknown attribute %", Token_Ptr); |
| Reference := Empty_Node; |
| |
| -- Scan past the attribute name |
| |
| Scan; |
| |
| else |
| -- Give its characteristics to this attribute reference |
| |
| Set_Project_Node_Of (Reference, To => Current_Project); |
| Set_Package_Node_Of (Reference, To => Current_Package); |
| Set_Expression_Kind_Of |
| (Reference, To => Attributes.Table (Current_Attribute).Kind_1); |
| Set_Case_Insensitive |
| (Reference, To => Attributes.Table (Current_Attribute).Kind_2 = |
| Case_Insensitive_Associative_Array); |
| |
| -- Scan past the attribute name |
| |
| Scan; |
| |
| -- If the attribute is an associative array, get the index |
| |
| if Attributes.Table (Current_Attribute).Kind_2 /= Single then |
| Expect (Tok_Left_Paren, "`(`"); |
| |
| if Token = Tok_Left_Paren then |
| Scan; |
| Expect (Tok_String_Literal, "literal string"); |
| |
| if Token = Tok_String_Literal then |
| Set_Associative_Array_Index_Of |
| (Reference, To => Token_Name); |
| Scan; |
| Expect (Tok_Right_Paren, "`)`"); |
| |
| if Token = Tok_Right_Paren then |
| Scan; |
| end if; |
| end if; |
| end if; |
| end if; |
| end if; |
| |
| -- Change name of obsolete attributes |
| |
| if Reference /= Empty_Node then |
| case Name_Of (Reference) is |
| when Snames.Name_Specification => |
| Set_Name_Of (Reference, To => Snames.Name_Spec); |
| |
| when Snames.Name_Specification_Suffix => |
| Set_Name_Of (Reference, To => Snames.Name_Spec_Suffix); |
| |
| when Snames.Name_Implementation => |
| Set_Name_Of (Reference, To => Snames.Name_Body); |
| |
| when Snames.Name_Implementation_Suffix => |
| Set_Name_Of (Reference, To => Snames.Name_Body_Suffix); |
| |
| when others => |
| null; |
| end case; |
| end if; |
| end if; |
| end Attribute_Reference; |
| |
| --------------------------- |
| -- End_Case_Construction -- |
| --------------------------- |
| |
| procedure End_Case_Construction is |
| begin |
| -- 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; |
| |
| elsif Choice_Lasts.Last = 2 then |
| -- This is the second case onstruction, set the tables to the first |
| |
| Choice_Lasts.Set_Last (1); |
| Choices.Set_Last (Choice_Lasts.Table (1)); |
| Choice_First := 1; |
| |
| else |
| -- This is the 3rd or more case construction, set the tables to the |
| -- previous one. |
| |
| 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 (External_Value : out Project_Node_Id) is |
| Field_Id : Project_Node_Id := Empty_Node; |
| |
| begin |
| External_Value := |
| Default_Project_Node (Of_Kind => N_External_Value, |
| And_Expr_Kind => Single); |
| Set_Location_Of (External_Value, To => Token_Ptr); |
| |
| -- The current token is External |
| |
| -- Get the left parenthesis |
| |
| Scan; |
| Expect (Tok_Left_Paren, "`(`"); |
| |
| -- Scan past the left parenthesis |
| |
| if Token = Tok_Left_Paren then |
| Scan; |
| 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, |
| And_Expr_Kind => Single); |
| Set_String_Value_Of (Field_Id, To => Token_Name); |
| Set_External_Reference_Of (External_Value, To => Field_Id); |
| |
| -- Scan past the first argument |
| |
| Scan; |
| |
| case Token is |
| |
| when Tok_Right_Paren => |
| |
| -- Scan past the right parenthesis |
| Scan; |
| |
| when Tok_Comma => |
| |
| -- Scan past the comma |
| |
| Scan; |
| |
| Expect (Tok_String_Literal, "literal string"); |
| |
| -- Get the default |
| |
| if Token = Tok_String_Literal then |
| Field_Id := |
| Default_Project_Node (Of_Kind => N_Literal_String, |
| And_Expr_Kind => Single); |
| Set_String_Value_Of (Field_Id, To => Token_Name); |
| Set_External_Default_Of (External_Value, To => Field_Id); |
| Scan; |
| Expect (Tok_Right_Paren, "`)`"); |
| end if; |
| |
| -- Scan past the right parenthesis |
| if Token = Tok_Right_Paren then |
| Scan; |
| end if; |
| |
| when others => |
| Error_Msg ("`,` or `)` expected", Token_Ptr); |
| end case; |
| end if; |
| end External_Reference; |
| |
| ----------------------- |
| -- Parse_Choice_List -- |
| ----------------------- |
| |
| procedure Parse_Choice_List (First_Choice : out Project_Node_Id) 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, |
| 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, To => Token_Ptr); |
| Choice_String := Token_Name; |
| |
| -- Give the string value to the current choice |
| |
| Set_String_Value_Of (Current_Choice, To => Choice_String); |
| |
| -- 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; report an error. |
| |
| Error_Msg_Name_1 := Choice_String; |
| Error_Msg ("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 ("illegal case label {", Token_Ptr); |
| end if; |
| |
| -- Scan past the label |
| |
| Scan; |
| |
| -- 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, |
| And_Expr_Kind => Single); |
| Set_Next_Literal_String (Current_Choice, To => Next_Choice); |
| Current_Choice := Next_Choice; |
| Scan; |
| else |
| exit; |
| end if; |
| end loop; |
| end Parse_Choice_List; |
| |
| ---------------------- |
| -- Parse_Expression -- |
| ---------------------- |
| |
| procedure Parse_Expression |
| (Expression : out Project_Node_Id; |
| Current_Project : Project_Node_Id; |
| Current_Package : Project_Node_Id) |
| 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); |
| Set_Location_Of (Expression, To => Token_Ptr); |
| |
| -- Parse the term or terms of the expression |
| |
| Terms (Term => First_Term, |
| Expr_Kind => Expression_Kind, |
| Current_Project => Current_Project, |
| Current_Package => Current_Package); |
| |
| -- Set the first term and the expression kind |
| |
| Set_First_Term (Expression, To => First_Term); |
| Set_Expression_Kind_Of (Expression, To => Expression_Kind); |
| end Parse_Expression; |
| |
| ---------------------------- |
| -- Parse_String_Type_List -- |
| ---------------------------- |
| |
| procedure Parse_String_Type_List (First_String : out Project_Node_Id) 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, |
| 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, To => String_Value); |
| Set_Location_Of (Last_String, 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) = String_Value then |
| -- This is a repetition, report an error |
| |
| Error_Msg_Name_1 := String_Value; |
| Error_Msg ("duplicate value { in type", Token_Ptr); |
| exit; |
| end if; |
| |
| Current := Next_Literal_String (Current); |
| end loop; |
| end; |
| |
| -- Scan past the literal string |
| |
| Scan; |
| |
| -- 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, |
| And_Expr_Kind => Single); |
| Set_Next_Literal_String (Last_String, To => Next_String); |
| Last_String := Next_String; |
| Scan; |
| end if; |
| end loop; |
| end Parse_String_Type_List; |
| |
| ------------------------------ |
| -- Parse_Variable_Reference -- |
| ------------------------------ |
| |
| procedure Parse_Variable_Reference |
| (Variable : out Project_Node_Id; |
| Current_Project : Project_Node_Id; |
| Current_Package : Project_Node_Id) |
| 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; |
| exit when Token /= Tok_Dot; |
| Scan; |
| 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 |
| |
| for Index in Package_First .. Package_Attributes.Last loop |
| if Package_Attributes.Table (Index).Name = |
| Names.Table (1).Name |
| then |
| First_Attribute := |
| Package_Attributes.Table (Index).First_Attribute; |
| exit; |
| end if; |
| end loop; |
| |
| -- Now, look if it can be a project name |
| |
| The_Project := Imported_Or_Extended_Project_Of |
| (Current_Project, Names.Table (1).Name); |
| |
| if The_Project = Empty_Node 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 ("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); |
| |
| while The_Package /= Empty_Node |
| and then Name_Of (The_Package) /= |
| Names.Table (1).Name |
| loop |
| The_Package := |
| Next_Package_In_Project (The_Package); |
| end loop; |
| |
| -- If it has not been already declared, report an |
| -- error. |
| |
| if The_Package = Empty_Node then |
| Error_Msg_Name_1 := Names.Table (1).Name; |
| Error_Msg ("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)); |
| |
| if Index /= Names.Last - 1 then |
| Add_To_Buffer ("."); |
| 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 ("."); |
| Add_To_Buffer |
| (Get_Name_String (Names.Table (Names.Last).Name)); |
| 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 |
| |
| The_Project := Imported_Or_Extended_Project_Of |
| (Current_Project, Long_Project); |
| |
| -- If the long project exists, then this is the prefix |
| -- of the attribute. |
| |
| if The_Project /= Empty_Node then |
| First_Attribute := Attribute_First; |
| The_Package := Empty_Node; |
| |
| else |
| -- Otherwise, check if the short project is imported |
| -- or extended. |
| |
| The_Project := Imported_Or_Extended_Project_Of |
| (Current_Project, Short_Project); |
| |
| -- If the short project does not exist, we report an |
| -- error. |
| |
| if The_Project = Empty_Node then |
| Error_Msg_Name_1 := Long_Project; |
| Error_Msg_Name_2 := Short_Project; |
| Error_Msg ("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); |
| while The_Package /= Empty_Node |
| and then Name_Of (The_Package) /= |
| Names.Table (Names.Last).Name |
| loop |
| The_Package := |
| Next_Package_In_Project (The_Package); |
| end loop; |
| |
| -- If it has not, then we report an error |
| |
| if The_Package = Empty_Node then |
| Error_Msg_Name_1 := |
| Names.Table (Names.Last).Name; |
| Error_Msg_Name_2 := Short_Project; |
| Error_Msg ("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 := |
| Package_Attributes.Table |
| (Package_Id_Of (The_Package)).First_Attribute; |
| end if; |
| end if; |
| end if; |
| end; |
| end case; |
| |
| Attribute_Reference |
| (Variable, |
| 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); |
| |
| if Look_For_Variable then |
| case Names.Last is |
| when 0 => |
| |
| -- Cannot happen |
| |
| null; |
| |
| when 1 => |
| |
| -- Simple variable name |
| |
| Set_Name_Of (Variable, 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, To => Names.Table (2).Name); |
| |
| -- Check if it can be a package name |
| |
| The_Package := First_Package_Of (Current_Project); |
| |
| while The_Package /= Empty_Node |
| and then Name_Of (The_Package) /= Names.Table (1).Name |
| loop |
| The_Package := Next_Package_In_Project (The_Package); |
| end loop; |
| |
| -- Now look for a possible project name |
| |
| The_Project := Imported_Or_Extended_Project_Of |
| (Current_Project, Names.Table (1).Name); |
| |
| if The_Project /= Empty_Node then |
| Specified_Project := The_Project; |
| |
| elsif The_Package = Empty_Node then |
| Error_Msg_Name_1 := Names.Table (1).Name; |
| Error_Msg ("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, 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)); |
| |
| if Index /= Names.Last - 2 then |
| Add_To_Buffer ("."); |
| 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 ("."); |
| Add_To_Buffer |
| (Get_Name_String (Names.Table (Names.Last - 1).Name)); |
| 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, Long_Project); |
| |
| if The_Project /= Empty_Node 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, Short_Project); |
| |
| if The_Project = Empty_Node then |
| -- Unknown prefix, report an error |
| |
| Error_Msg_Name_1 := Long_Project; |
| Error_Msg_Name_2 := Short_Project; |
| Error_Msg ("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); |
| |
| while The_Package /= Empty_Node |
| and then Name_Of (The_Package) /= |
| Names.Table (Names.Last - 1).Name |
| loop |
| The_Package := |
| Next_Package_In_Project (The_Package); |
| end loop; |
| |
| if The_Package = Empty_Node then |
| -- The package does not vexist, report an error |
| |
| Error_Msg_Name_1 := Names.Table (2).Name; |
| Error_Msg ("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); |
| Set_Project_Node_Of (Variable, To => Specified_Project); |
| Set_Package_Node_Of (Variable, To => Specified_Package); |
| |
| if Specified_Project /= Empty_Node 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 Specified_Package /= Empty_Node then |
| Current_Variable := First_Variable_Of (Specified_Package); |
| |
| while Current_Variable /= Empty_Node |
| and then |
| Name_Of (Current_Variable) /= Variable_Name |
| loop |
| Current_Variable := Next_Variable (Current_Variable); |
| 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 Specified_Project = Empty_Node |
| and then Current_Package /= Empty_Node |
| then |
| Current_Variable := First_Variable_Of (Current_Package); |
| |
| while Current_Variable /= Empty_Node |
| and then Name_Of (Current_Variable) /= Variable_Name |
| loop |
| Current_Variable := Next_Variable (Current_Variable); |
| end loop; |
| end if; |
| |
| -- If we have not found the variable in the package, check if the |
| -- variable has been declared in the project. |
| |
| if Current_Variable = Empty_Node then |
| Current_Variable := First_Variable_Of (The_Project); |
| |
| while Current_Variable /= Empty_Node |
| and then Name_Of (Current_Variable) /= Variable_Name |
| loop |
| Current_Variable := Next_Variable (Current_Variable); |
| end loop; |
| end if; |
| end if; |
| |
| -- If the variable was not found, report an error |
| |
| if Current_Variable = Empty_Node then |
| Error_Msg_Name_1 := Variable_Name; |
| Error_Msg |
| ("unknown variable %", Names.Table (Names.Last).Location); |
| end if; |
| end if; |
| |
| if Current_Variable /= Empty_Node then |
| Set_Expression_Kind_Of |
| (Variable, To => Expression_Kind_Of (Current_Variable)); |
| |
| if Kind_Of (Current_Variable) = N_Typed_Variable_Declaration then |
| Set_String_Type_Of |
| (Variable, To => String_Type_Of (Current_Variable)); |
| 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 ("\variables cannot be associative arrays", Token_Ptr); |
| Scan; |
| Expect (Tok_String_Literal, "literal string"); |
| |
| if Token = Tok_String_Literal then |
| Scan; |
| Expect (Tok_Right_Paren, "`)`"); |
| |
| if Token = Tok_Right_Paren then |
| Scan; |
| end if; |
| end if; |
| end if; |
| end Parse_Variable_Reference; |
| |
| --------------------------------- |
| -- Start_New_Case_Construction -- |
| --------------------------------- |
| |
| procedure Start_New_Case_Construction (String_Type : Project_Node_Id) is |
| Current_String : Project_Node_Id; |
| |
| begin |
| -- Set Choice_First, depending on whether 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 to table Choices the literal of the string type |
| |
| if String_Type /= Empty_Node then |
| Current_String := First_Literal_String (String_Type); |
| |
| while Current_String /= Empty_Node loop |
| Add (This_String => String_Value_Of (Current_String)); |
| Current_String := Next_Literal_String (Current_String); |
| 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 |
| (Term : out Project_Node_Id; |
| Expr_Kind : in out Variable_Kind; |
| Current_Project : Project_Node_Id; |
| Current_Package : Project_Node_Id) |
| 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); |
| Set_Location_Of (Term, 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 |
| ("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, |
| And_Expr_Kind => List); |
| Set_Current_Term (Term, To => Term_Id); |
| Set_Location_Of (Term, To => Token_Ptr); |
| |
| -- Scan past the left parenthesis |
| |
| Scan; |
| |
| -- If the left parenthesis is immediately followed by a right |
| -- parenthesis, the literal string list is empty. |
| |
| if Token = Tok_Right_Paren then |
| Scan; |
| |
| else |
| -- Otherwise, we parse the expression(s) in the literal string |
| -- list. |
| |
| loop |
| Current_Location := Token_Ptr; |
| Parse_Expression (Expression => Next_Expression, |
| Current_Project => Current_Project, |
| Current_Package => Current_Package); |
| |
| -- The expression kind is String list, report an error |
| |
| if Expression_Kind_Of (Next_Expression) = List then |
| Error_Msg ("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 Current_Expression = Empty_Node then |
| Set_First_Expression_In_List |
| (Term_Id, To => Next_Expression); |
| else |
| Set_Next_Expression_In_List |
| (Current_Expression, 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; -- past the comma |
| end loop; |
| |
| -- We expect a closing right parenthesis |
| |
| Expect (Tok_Right_Paren, "`)`"); |
| |
| if Token = Tok_Right_Paren then |
| Scan; |
| 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); |
| Set_Current_Term (Term, To => Term_Id); |
| Set_String_Value_Of (Term_Id, To => Token_Name); |
| |
| -- Scan past the string literal |
| |
| Scan; |
| |
| when Tok_Identifier => |
| Current_Location := Token_Ptr; |
| |
| -- Get the variable or attribute reference |
| |
| Parse_Variable_Reference |
| (Variable => Reference, |
| Current_Project => Current_Project, |
| Current_Package => Current_Package); |
| Set_Current_Term (Term, To => Reference); |
| |
| if Reference /= Empty_Node 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); |
| |
| elsif Expr_Kind = Single |
| and then Expression_Kind_Of (Reference) = 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 |
| ("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; |
| Expect (Tok_Apostrophe, "`'`"); |
| |
| if Token = Tok_Apostrophe then |
| Attribute_Reference |
| (Reference => Reference, |
| First_Attribute => Prj.Attr.Attribute_First, |
| Current_Project => Current_Project, |
| Current_Package => Empty_Node); |
| Set_Current_Term (Term, To => Reference); |
| end if; |
| |
| -- Same checks as above for the expression kind |
| |
| if Reference /= Empty_Node then |
| if Expr_Kind = Undefined then |
| Expr_Kind := Expression_Kind_Of (Reference); |
| |
| elsif Expr_Kind = Single |
| and then Expression_Kind_Of (Reference) = List |
| then |
| Error_Msg |
| ("lists cannot appear in single string expression", |
| Current_Location); |
| end if; |
| end if; |
| |
| when Tok_External => |
| -- An external reference is always a single string |
| |
| if Expr_Kind = Undefined then |
| Expr_Kind := Single; |
| end if; |
| |
| External_Reference (External_Value => Reference); |
| Set_Current_Term (Term, To => Reference); |
| |
| when others => |
| Error_Msg ("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 past the '&' |
| |
| Scan; |
| |
| Terms (Term => Next_Term, |
| Expr_Kind => Expr_Kind, |
| Current_Project => Current_Project, |
| Current_Package => Current_Package); |
| |
| -- And link the next term to this term |
| |
| Set_Next_Term (Term, To => Next_Term); |
| end if; |
| end Terms; |
| |
| end Prj.Strt; |