------------------------------- ------------------------------------------------- -- -- -- OCARINA COMPONENTS -- -- -- -- O C A R I N A . A A D L . P A R S E R . C O M P O N E N T S -- -- -- -- B o d y -- -- -- -- Copyright (C) 2004-2007, GET-Telecom Paris. -- -- -- -- Ocarina is free software; you can redistribute it and/or modify -- -- it under terms of the GNU General Public License as published by the -- -- Free Software Foundation; either version 2, or (at your option) any -- -- later version. Ocarina is distributed in the hope that it will be -- -- useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -- -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -- -- Public License for more details. You should have received a copy of the -- -- GNU General Public License distributed with Ocarina; see file COPYING. -- -- If not, write to the Free Software Foundation, 51 Franklin Street, Fifth -- -- Floor, Boston, MA 02111-1301, USA. -- -- -- -- As a special exception, if other files instantiate generics from this -- -- unit, or you link this unit with other files to produce an executable, -- -- this unit does not by itself cause the resulting executable to be -- -- covered by the GNU General Public License. This exception does not -- -- however invalidate any other reasons why the executable file might be -- -- covered by the GNU Public License. -- -- -- -- Ocarina is maintained by the Ocarina team -- -- (ocarina-users@listes.enst.fr) -- -- -- ------------------------------------------------------------------------------ -- This package gathers all the functions that are related to -- component parsing. with Locations; use Locations; with Ocarina.Nodes; with Ocarina.Nutils; with Namet; with Ocarina.AADL.Tokens; with Ocarina.AADL.Lexer; with Ocarina.AADL.Parser.Identifiers; with Ocarina.AADL.Parser.Annexes; with Ocarina.AADL.Parser.Components.Connections; with Ocarina.AADL.Parser.Components.Flows; with Ocarina.AADL.Parser.Components.Features; with Ocarina.AADL.Parser.Components.Subcomponents; with Ocarina.AADL.Parser.Components.Modes; with Ocarina.AADL.Parser.Components.Subprogram_Calls; with Ocarina.AADL.Parser.Properties; with Ocarina.Builder.Components; package body Ocarina.AADL.Parser.Components is function P_Expected_Component_Implementation_Name (Expected_Id : node_id) return Boolean; function P_Component_Implementation (Namespace : Types.node_id; Start_Loc : location; Category : component_category; Private_Declaration : Boolean) return node_id; -- Parse Component_Implementation, Component_Implementation_Extension function P_Component_Type (Namespace : Types.node_id; Start_Loc : location; Category : component_category; Private_Declaration : Boolean) return node_id; -- Parse Component_Type, Component_Type_Extension function P_Unique_Component_Impl_Name return node_id; function Is_A_Component_Implementation_Name (Identifier : Types.node_id) return Boolean; -- Check if the identifier name contains a T_Dot ---------------------------------------------- -- P_Expected_Component_Implementation_Name -- ---------------------------------------------- function P_Expected_Component_Implementation_Name (Expected_Id : node_id) return Boolean is use Tokens; use Lexer; use Ocarina.Nodes; use Namet; use Ocarina.Nutils; Loc2, Start_Loc : location; Node : constant node_id := New_Node (k_identifier, Token_Location); begin Set_Corresponding_Entity (Node, No_Node); Save_Lexer (Start_Loc); Scan_Token; if Token = t_identifier then Set_Name (Node, Token_Name); Set_Display_Name (Node, Token_Display_Name); else Restore_Lexer (Start_Loc); return False; end if; Save_Lexer (Loc2); Scan_Token; if Token = t_dot then Get_Name_String (Name (Node)); Add_Str_To_Name_Buffer (Image (t_dot)); Set_Name (Node, Name_Find); Get_Name_String (Display_Name (Node)); Add_Str_To_Name_Buffer (Image (t_dot)); Set_Display_Name (Node, Name_Find); else Restore_Lexer (Start_Loc); return False; end if; Scan_Token; if Token = t_identifier then Get_Name_String (Name (Node)); Get_Name_String_And_Append (Token_Name); Set_Name (Node, Name_Find); Get_Name_String (Display_Name (Node)); Get_Name_String_And_Append (Token_Display_Name); Set_Display_Name (Node, Name_Find); else Restore_Lexer (Start_Loc); return False; end if; if Name (Node) = Name (Expected_Id) then return True; else DPE (pc_defining_identifier, Display_Name (Expected_Id)); Restore_Lexer (Start_Loc); return False; end if; end P_Expected_Component_Implementation_Name; ----------------- -- P_Component -- ----------------- function P_Component (Namespace : Types.node_id; Private_Declaration : Boolean := False) return node_id is use Tokens; use Lexer; Category : component_category; Loc : location; Start_Loc : location; begin Start_Loc := Token_Location; Category := P_Component_Category; Save_Lexer (Loc); Scan_Token; if Token = t_implementation then return P_Component_Implementation (Namespace, Start_Loc, Category, Private_Declaration); else Restore_Lexer (Loc); return P_Component_Type (Namespace, Start_Loc, Category, Private_Declaration); end if; end P_Component; -------------------------- -- P_Component_Category -- -------------------------- -- component_category ::= software_category | platform_category -- | composite_category -- software_category ::= data | subprogram | thread | thread group -- | process -- platform_category ::= memory | processor | bus | device -- composite_category ::= system function P_Component_Category return component_category is use Lexer; use Tokens; Loc : location; begin case Token is when t_data => return cc_data; when t_subprogram => return cc_subprogram; when t_thread => Save_Lexer (Loc); Scan_Token; if Token = t_group then return cc_threadgroup; else Restore_Lexer (Loc); return cc_thread; end if; when t_process => return cc_process; when t_memory => return cc_memory; when t_processor => return cc_processor; when t_bus => return cc_bus; when t_device => return cc_device; when t_system => return cc_system; when others => DPE (pc_component_category); return cc_unknown; end case; end P_Component_Category; -------------------------- -- P_Component_Category -- -------------------------- function P_Component_Category (Container : Types.node_id) return node_id is use Ocarina.Nodes; use Ocarina.Nutils; use Lexer; pragma unreferenced (Container); Comp_Cat : node_id; Category : component_category; begin Comp_Cat := New_Node (k_component_category, Token_Location); Scan_Token; Category := P_Component_Category; if Category = cc_unknown then return No_Node; end if; Set_Category (Comp_Cat, component_category'pos (Category)); return Comp_Cat; end P_Component_Category; -------------------------------- -- P_Component_Implementation -- -------------------------------- -- component_implementation ::= -- component_category implementation component_type_identifier . -- defining_component_implementation_identifier -- [ refines type ( { feature_refinement }+ | none_statement ) ] -- [ subcomponents ( { subcomponent }+ | none_statement ) ] -- [ calls ( { subprogram_call_sequence }+ | none_statement ) ] -- [ connections ( { connection }+ | none_statement ) ] -- [ flows ( { flow_implementation | end_to_end_flow_spec }+ | -- none_statement ) ] -- [ modes ( { mode }+ { mode_transition }* | none_statement ) ] -- [ properties ( { property_association }+ | none_statement ) ] -- { annex_subclause }* -- end component_type_identifier . -- defining_component_implementation_identifier ; -- component_implementation_extension ::= -- component_category implementation component_type_identifier . -- defining_component_implementation_identifier -- extends unique_component_implementation_name -- [ refines type ( { feature_refinement }+ | none_statement ) ] -- [ subcomponents ( { subcomponent | subcomponent_refinement }+ | -- none_statement ) ] -- [ calls ( { subprogram_call_sequence }+ | none_statement ) ] -- [ connections ( { connection | connection_refinement }+ | -- none_statement ) ] -- [ flows ( { flow_implementation | flow_implementation_refinement | -- end_to_end_flow_spec }+ | none_statement ) ] -- [ modes ( { mode | mode_refinement | mode_transition }+ | -- none_statement ) ] -- [ properties ( { property_association }+ | none_statement ) ] -- { annex_subclause }* -- end component_type_identifier . -- defining_component_implementation_identifier ; function P_Component_Implementation (Namespace : Types.node_id; Start_Loc : location; Category : component_category; Private_Declaration : Boolean) return node_id is use Namet; use Ocarina.Nodes; use Ocarina.Nutils; use Ocarina.Builder.Components; use Tokens; use Lexer; use Parser.Identifiers; use Parser.Annexes; use Parser.Components.Connections; use Parser.Components.Flows; use Parser.Components.Features; use Parser.Components.Subcomponents; use Parser.Components.Modes; use Parser.Properties; use Parser.Components.Subprogram_Calls; Impl : node_id; -- output Loc : location; Comp_Identifier : node_id; -- component_identifier Impl_Identifier : node_id; -- implementation_identifier Parent : node_id; Refinable : Boolean; -- Is Component_Implementation_Extension ? Code : parsing_code := pc_component_implementation; Current_Annex : node_id; Nb_Items : Integer; begin Comp_Identifier := P_Identifier (No_Node); if No (Comp_Identifier) then DPE (pc_component_implementation, t_identifier); Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; Scan_Token; if Token /= t_dot then DPE (pc_component_implementation, t_dot); Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; Impl_Identifier := P_Identifier (No_Node); if No (Impl_Identifier) then DPE (pc_component_implementation, t_identifier); Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; Get_Name_String (Name (Comp_Identifier)); Add_Str_To_Name_Buffer (Image (t_dot)); Add_Str_To_Name_Buffer (Get_Name_String (Name (Impl_Identifier))); Set_Name (Impl_Identifier, Name_Find); Get_Name_String (Display_Name (Comp_Identifier)); Add_Str_To_Name_Buffer (Image (t_dot)); Add_Str_To_Name_Buffer (Get_Name_String (Display_Name (Impl_Identifier))); Set_Display_Name (Impl_Identifier, Name_Find); -- The implementation name is actually the concatenation of the -- type and the implementation. Thus we have a unique -- identifier. Impl := Add_New_Component_Implementation (Start_Loc, Impl_Identifier, Namespace, Category, Is_Private => Private_Declaration); if Impl = No_Node then Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; Set_Component_Type_Identifier (Impl, Comp_Identifier); Save_Lexer (Loc); Scan_Token; -- Component_Implementation or Component_Implementation_Extension ? if Token = t_extends then Parent := P_Unique_Component_Impl_Name; if No (Parent) then -- Error when parsing Unique_Component_Implementation_Name, quit Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; Refinable := True; Code := pc_component_implementation_extension; else Restore_Lexer (Loc); Parent := No_Node; Refinable := False; end if; -- Parse component implementation elements -- -- Refines types -- Save_Lexer (Loc); Scan_Token; if Token = t_refines then Scan_Token; -- parse 'type' if Token /= t_type then DPE (pc_refines_type, t_type); Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; Nb_Items := P_Items_List (P_Feature_Refinement'access, Impl, pc_refines_type); if Nb_Items < 0 then Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; else Restore_Lexer (Loc); end if; Save_Lexer (Loc); Scan_Token; -- -- Subcomponents -- if Token = t_subcomponents then Nb_Items := P_Items_List (P_Subcomponent'access, Impl, Refinable, pc_subcomponents); if Nb_Items < 0 then Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; else Restore_Lexer (Loc); end if; Save_Lexer (Loc); Scan_Token; -- -- Call sequences -- if Token = t_calls then Nb_Items := P_Items_List (P_Subprogram_Call_Sequence'access, Impl, pc_subprogram_call_sequences); if Nb_Items < 0 then Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; else Restore_Lexer (Loc); end if; Save_Lexer (Loc); Scan_Token; -- -- Connections -- if Token = t_connections then Nb_Items := P_Items_List (P_Connection'access, Impl, Refinable, pc_connections); if Nb_Items < 0 then Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; else Restore_Lexer (Loc); end if; Save_Lexer (Loc); Scan_Token; -- -- Flows -- if Token = t_flows then Nb_Items := P_Items_List (P_Flow_Implementation_Or_End_To_End_Flow_Spec'access, Impl, Refinable, pc_flow_implementations); if Nb_Items < 0 then Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; else Restore_Lexer (Loc); end if; Save_Lexer (Loc); Scan_Token; -- -- Modes -- if Token = t_modes then Nb_Items := P_Items_List (Func => P_Mode_Or_Mode_Transition'access, Container => Impl, Refinable => Refinable, Code => pc_mode_or_mode_transition, At_Least_One => True); if Nb_Items < 0 then Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; else Restore_Lexer (Loc); end if; Save_Lexer (Loc); Scan_Token; -- -- Properties -- if Token = t_properties then Nb_Items := P_Items_List (P_Property_Association_In_Component_Implementation'access, Impl, pc_properties); if Nb_Items < 0 then Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; else Restore_Lexer (Loc); end if; -- -- Annexes -- -- we make a loop, as there can be several annex declarations loop Scan_Token; if Token = t_annex then Current_Annex := P_Annex_Subclause (Impl); if not Present (Current_Annex) then Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; elsif Token = t_end then exit; else DPE (Code); Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; end loop; if not P_Expected_Component_Implementation_Name (Impl_Identifier) then Skip_Tokens (t_semicolon); return No_Node; end if; Save_Lexer (Loc); Scan_Token; -- parse ';' if Token /= t_semicolon then DPE (Code, t_semicolon); Restore_Lexer (Loc); return No_Node; end if; Set_Parent (Impl, Parent); return Impl; end P_Component_Implementation; ---------------------- -- P_Component_Type -- ---------------------- -- component_type ::= -- component_category defining_component_type_identifier -- [ features ( { feature }+ | none_statement ) ] -- [ flows ( { flow_spec }+ | none_statement ) ] -- [ properties ( { component_type_property_association }+ | -- none_statement ) ] -- { annex_subclause }* -- end defining_component_type_identifier ; -- component_type_extension ::= -- component_category defining_component_type_identifier -- extends unique_component_type_identifier -- [ features ( { feature | feature_refinement }+ | none_statement ) ] -- [ flows ( { flow_spec | flow_spec_refinement }+ | none_statement ) ] -- [ properties ( { component_type_property_association }+ | -- none_statement ) ] -- { annex_subclause }* -- end defining_component_type_identifier ; function P_Component_Type (Namespace : Types.node_id; Start_Loc : location; Category : component_category; Private_Declaration : Boolean) return node_id is use Ocarina.Nodes; use Ocarina.Nutils; use Tokens; use Lexer; use Parser.Identifiers; use Parser.Annexes; use Parser.Components.Flows; use Parser.Components.Features; use Parser.Properties; use Ocarina.Builder.Components; Component : node_id; Loc : location; Identifier : node_id; -- component identifier Parent : node_id; Refinable : Boolean; -- Is Component_Type_Extension ? Current_Annex : node_id; Code : parsing_code := pc_component_type; Nb_Items : Integer; begin Identifier := P_Identifier (No_Node); if No (Identifier) then DPE (Code, t_identifier); Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; Component := Add_New_Component_Type (Start_Loc, Identifier, Namespace, Category, Is_Private => Private_Declaration); if No_Node = Component then Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; Save_Lexer (Loc); Scan_Token; -- Component_Type or Component_Type_Extension ? if Token = t_extends then Parent := P_Entity_Reference (pc_unique_component_type_identifier); -- if Second_Id (Parent) /= No_Node then if Next_Node (First_Node (Path (Parent))) /= No_Node then Display_Parsing_Error (pc_unique_component_type_identifier, emc_extends_incompatible_entity); Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; if No (Parent) then -- Error when parsing Unique_Component_Type_Identifier, quit Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; Refinable := True; Code := pc_component_type_extension; else Restore_Lexer (Loc); Parent := No_Node; Refinable := False; end if; -- Parse component elements Save_Lexer (Loc); Scan_Token; -- -- Features -- if Token = t_features then Nb_Items := P_Items_List (Func => P_Feature'access, Container => Component, Refinable => Refinable, Code => pc_features, At_Least_One => True); if Nb_Items < 0 then Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; else Restore_Lexer (Loc); end if; Save_Lexer (Loc); Scan_Token; -- -- Flow specifications -- if Token = t_flows then Nb_Items := P_Items_List (Func => P_Flow_Spec'access, Container => Component, Refinable => Refinable, Code => pc_flow_specifications, At_Least_One => True); if Nb_Items < 0 then Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; else Restore_Lexer (Loc); end if; Save_Lexer (Loc); Scan_Token; -- -- Properties -- if Token = t_properties then Nb_Items := P_Items_List (P_Property_Association'access, Component, pc_properties); if Nb_Items < 0 then Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; else Restore_Lexer (Loc); end if; -- -- Annexes -- -- We make a loop, as there can be several annex declarations loop Scan_Token; if Token = t_annex then Current_Annex := P_Annex_Subclause (Component); if not Present (Current_Annex) then Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; elsif Token = t_end then exit; else DPE (Code); Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; end loop; if not P_Expected_Identifier (Identifier) then -- Error when parsing Defining_Identifier, quit Skip_Tokens (t_semicolon); return No_Node; end if; Save_Lexer (Loc); Scan_Token; if Token /= t_semicolon then DPE (Code, t_semicolon); Restore_Lexer (Loc); return No_Node; end if; Set_Parent (Component, Parent); return Component; end P_Component_Type; ---------------------------------- -- P_Unique_Component_Impl_Name -- ---------------------------------- -- unique_component_implementation_name ::= -- [package_name :: ] component_type_identifier . -- component_implementation_identifier function P_Unique_Component_Impl_Name return node_id is use Ocarina.Nodes; use Ocarina.Nutils; use Lexer; use Tokens; use Parser.Identifiers; Comp_Impl_Name : node_id; begin Comp_Impl_Name := P_Entity_Reference (pc_unique_component_impl_name); if No (Comp_Impl_Name) then -- Error when parsing Identifier_With_Package_Name, quit DPE (pc_unique_component_impl_name, t_identifier); return No_Node; else -- We must ensure the name is something like -- [package::]type.implementation if Is_A_Component_Implementation_Name (Identifier (Comp_Impl_Name)) then return Comp_Impl_Name; else DPE (pc_unique_component_impl_name, t_identifier); return No_Node; end if; end if; end P_Unique_Component_Impl_Name; ---------------------------------------- -- Is_A_Component_Implementation_Name -- ---------------------------------------- function Is_A_Component_Implementation_Name (Identifier : Types.node_id) return Boolean is use Namet; use Ocarina.Nodes; use Ocarina.AADL.Tokens; pragma assert (Identifier /= No_Node and then Kind (Identifier) = k_identifier); Name : constant name_id := Ocarina.Nodes.Name (Identifier); begin Get_Name_String (Name); for Index in 1 .. Name_Len loop if Name_Buffer (Index .. Index) = Image (t_dot) then return True; end if; end loop; return False; end Is_A_Component_Implementation_Name; ----------------------- -- P_Port_Group_Type -- ----------------------- -- port_group_type ::= port group defining_identifier -- ( features { port_spec | port_group_spec }* -- [ inverse of unique_port_group_type_reference ] -- | -- inverse of unique_port_group_type_reference ) -- [ properties -- ( { portgroup_property_association }+ | none_statement ) ] -- ( annex_subclause )* -- end defining_identifier ; -- port_group_type_extension ::= port group defining_identifier -- extends unique_port_group_type_reference -- ( features { port_spec | port_refinement | -- port_group_spec | port_group_refinement }* -- [ inverse of unique_port_group_type_reference ] -- | -- inverse of unique_port_group_type_reference ) -- [ properties -- ( { portgroup_property_association }+ | none_statement ) ] -- ( annex_subclause )* -- end defining_identifier ; function P_Port_Group_Type (Namespace : Types.node_id; Start_Loc : location; Private_Declaration : Boolean := False) return node_id is use Ocarina.Nodes; use Ocarina.Nutils; use Lexer; use Tokens; use Ocarina.AADL.Parser.Annexes; use Ocarina.AADL.Parser.Properties; use Ocarina.AADL.Parser.Identifiers; use Ocarina.AADL.Parser.Components.Features; use Ocarina.Builder.Components; Port_Group_Type : node_id; Loc : location; Identifier : node_id; Inverse_Of : node_id := No_Node; Parent : node_id; Refinable : Boolean; Code : parsing_code := pc_port_group_type; Current_Annex : node_id; Nb_Items : Integer; begin Scan_Token; if Token /= t_identifier then DPE (Code, t_identifier); Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; Identifier := New_Node (k_identifier, Token_Location); Set_Name (Identifier, Token_Name); Set_Display_Name (Identifier, Token_Display_Name); Port_Group_Type := Add_New_Port_Group (Loc => Start_Loc, Name => Identifier, Namespace => Namespace, Is_Private => Private_Declaration); if Port_Group_Type = No_Node then Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; Save_Lexer (Loc); Scan_Token; -- Port_Group_Type or Port_Group_Type_Extension ? if Token = t_extends then Parent := P_Entity_Reference (pc_unique_port_group_type_reference); if No (Parent) then -- Error when parsing Unique_Port_Group_Type_Reference, quit Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; Refinable := True; Code := pc_port_group_type_extension; else Restore_Lexer (Loc); Parent := No_Node; Refinable := False; end if; -- Parse port group elements Save_Lexer (Loc); Scan_Token; if Token = t_features then Nb_Items := P_Items_List (Func => P_Port_Spec_Or_Port_Group_Spec'access, Container => Port_Group_Type, Refinable => Refinable, Code => Code, At_Least_One => False); if Nb_Items < 0 then Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; else Restore_Lexer (Loc); end if; Save_Lexer (Loc); Scan_Token; if Token = t_inverse then Scan_Token; if Token /= t_of then DPE (Code, t_of); Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; Inverse_Of := P_Entity_Reference (Code); if No (Inverse_Of) then -- Error when parsing Unique_Port_Group_Type_Reference, quit Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; else if Nb_Items < 0 then -- no features, no inverse_of, error DPE (Code, (t_features, t_inverse)); Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; Restore_Lexer (Loc); end if; loop Scan_Token; case Token is when t_properties => Nb_Items := P_Items_List (Func => P_Property_Association'access, Container => Port_Group_Type, Code => pc_properties); if Nb_Items <= 0 then Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; when t_annex => Current_Annex := P_Annex_Subclause (Port_Group_Type); if not Present (Current_Annex) then Skip_Tokens ((t_end, t_semicolon)); return No_Node; end if; when t_end => exit; when others => if No (Inverse_Of) then DPE (Code, (t_inverse, t_properties, t_annex, t_end)); else DPE (Code, (t_properties, t_annex, t_end)); end if; Skip_Tokens ((t_end, t_semicolon)); return No_Node; end case; end loop; if not P_Expected_Identifier (Identifier) then -- Error when parsing defining_identifier, quit Skip_Tokens (t_semicolon); return No_Node; end if; Save_Lexer (Loc); Scan_Token; if Token /= t_semicolon then DPE (Code, t_semicolon); Restore_Lexer (Loc); return No_Node; end if; Set_Inverse_Of (Port_Group_Type, Inverse_Of); Set_Parent (Port_Group_Type, Parent); return Port_Group_Type; end P_Port_Group_Type; end Ocarina.AADL.Parser.Components;