------------------------------------------------------------------------------ -- -- -- GNAT COMPILER COMPONENTS -- -- -- -- P R J . S T R T -- -- -- -- B o d y -- -- -- -- Copyright (C) 2001-2004 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; with Uintp; use Uintp; 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; Optional_Index : Boolean); -- 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). 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 ("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 => Variable_Kind_Of (Current_Attribute)); Set_Case_Insensitive (Reference, To => Attribute_Kind_Of (Current_Attribute) = Case_Insensitive_Associative_Array); -- Scan past the attribute name Scan; -- 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; 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 (Check_All_Labels : Boolean; Case_Location : Source_Ptr) 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 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 ("?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 ("?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 ("\?{", Case_Location); end if; end loop; 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; 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; Optional_Index : Boolean) 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, Optional_Index => Optional_Index); -- 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 First_Attribute := First_Attribute_Of (Package_Node_Id_Of (Names.Table (1).Name)); -- 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 := First_Attribute_Of (Package_Id_Of (The_Package)); 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; Optional_Index : Boolean) 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, Optional_Index => Optional_Index); -- 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; -- Check for possible index expression if Token = Tok_At then if not Optional_Index then Error_Msg ("index not allowed here", Token_Ptr); Scan; if Token = Tok_Integer_Literal then Scan; end if; -- Set the index value else Scan; 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 ("index cannot be zero", Token_Ptr); else Set_Source_Index_Of (Term_Id, To => Index); end if; end; Scan; end if; end if; end if; 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, Optional_Index => Optional_Index); -- And link the next term to this term Set_Next_Term (Term, To => Next_Term); end if; end Terms; end Prj.Strt;