diff options
author | Hristian Kirtchev <kirtchev@adacore.com> | 2018-08-21 14:45:49 +0000 |
---|---|---|
committer | Pierre-Marie de Rodat <pmderodat@gcc.gnu.org> | 2018-08-21 14:45:49 +0000 |
commit | 2201fa7bd34d215e4aeeb961d41f60f3fb80f101 (patch) | |
tree | cb36eda183a044c293786c8783b0e9c4064218ea /gcc | |
parent | c36d21ee42349ea0e8565daa2013ba4f193d4ffe (diff) | |
download | gcc-2201fa7bd34d215e4aeeb961d41f60f3fb80f101.zip gcc-2201fa7bd34d215e4aeeb961d41f60f3fb80f101.tar.gz gcc-2201fa7bd34d215e4aeeb961d41f60f3fb80f101.tar.bz2 |
[Ada] General purpose doubly linked list for compiler and tool use
This patch adds unit GNAT.Lists which currently contains the
implementation of a general purpose doubly linked list intended for use
by the compiler and the tools around it.
2018-08-21 Hristian Kirtchev <kirtchev@adacore.com>
gcc/ada/
* impunit.adb: Add g-lists to the set of non-implementation
units.
* libgnat/g-lists.adb, libgnat/g-lists.ads: New unit.
* Makefile.rtl: Add g-lists to the set of non-tasking units.
* gcc-interface/Make-lang.in: Add g-lists to the set of files
used by gnat1.
gcc/testsuite/
* gnat.dg/linkedlist.adb: New testcase.
From-SVN: r263714
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ada/ChangeLog | 9 | ||||
-rw-r--r-- | gcc/ada/Makefile.rtl | 1 | ||||
-rw-r--r-- | gcc/ada/gcc-interface/Make-lang.in | 1 | ||||
-rw-r--r-- | gcc/ada/impunit.adb | 1 | ||||
-rw-r--r-- | gcc/ada/libgnat/g-lists.adb | 635 | ||||
-rw-r--r-- | gcc/ada/libgnat/g-lists.ads | 245 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 4 | ||||
-rw-r--r-- | gcc/testsuite/gnat.dg/linkedlist.adb | 1184 |
8 files changed, 2080 insertions, 0 deletions
diff --git a/gcc/ada/ChangeLog b/gcc/ada/ChangeLog index d90f01a..f21b11c 100644 --- a/gcc/ada/ChangeLog +++ b/gcc/ada/ChangeLog @@ -1,3 +1,12 @@ +2018-08-21 Hristian Kirtchev <kirtchev@adacore.com> + + * impunit.adb: Add g-lists to the set of non-implementation + units. + * libgnat/g-lists.adb, libgnat/g-lists.ads: New unit. + * Makefile.rtl: Add g-lists to the set of non-tasking units. + * gcc-interface/Make-lang.in: Add g-lists to the set of files + used by gnat1. + 2018-08-21 Ed Schonberg <schonberg@adacore.com> * exp_ch9.adb (Reset_Scopes): Do not recurse into type diff --git a/gcc/ada/Makefile.rtl b/gcc/ada/Makefile.rtl index 7eaa9ba..2e4ee8d 100644 --- a/gcc/ada/Makefile.rtl +++ b/gcc/ada/Makefile.rtl @@ -427,6 +427,7 @@ GNATRTL_NONTASKING_OBJS= \ g-htable$(objext) \ g-io$(objext) \ g-io_aux$(objext) \ + g-lists$(objext) \ g-locfil$(objext) \ g-mbdira$(objext) \ g-mbflra$(objext) \ diff --git a/gcc/ada/gcc-interface/Make-lang.in b/gcc/ada/gcc-interface/Make-lang.in index d51d397..d8dac73 100644 --- a/gcc/ada/gcc-interface/Make-lang.in +++ b/gcc/ada/gcc-interface/Make-lang.in @@ -319,6 +319,7 @@ GNAT_ADA_OBJS = \ ada/libgnat/g-dynhta.o \ ada/libgnat/g-hesora.o \ ada/libgnat/g-htable.o \ + ada/libgnat/g-lists.o \ ada/libgnat/g-spchge.o \ ada/libgnat/g-speche.o \ ada/libgnat/g-u3spch.o \ diff --git a/gcc/ada/impunit.adb b/gcc/ada/impunit.adb index cfa1d5e..7d35902 100644 --- a/gcc/ada/impunit.adb +++ b/gcc/ada/impunit.adb @@ -281,6 +281,7 @@ package body Impunit is ("g-htable", F), -- GNAT.Htable ("g-io ", F), -- GNAT.IO ("g-io_aux", F), -- GNAT.IO_Aux + ("g-lists ", F), -- GNAT.Lists ("g-locfil", F), -- GNAT.Lock_Files ("g-mbdira", F), -- GNAT.MBBS_Discrete_Random ("g-mbflra", F), -- GNAT.MBBS_Float_Random diff --git a/gcc/ada/libgnat/g-lists.adb b/gcc/ada/libgnat/g-lists.adb new file mode 100644 index 0000000..a058f33 --- /dev/null +++ b/gcc/ada/libgnat/g-lists.adb @@ -0,0 +1,635 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT RUN-TIME COMPONENTS -- +-- -- +-- G N A T . L I S T S -- +-- -- +-- B o d y -- +-- -- +-- Copyright (C) 2018, 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. -- +-- -- +-- As a special exception under Section 7 of GPL version 3, you are granted -- +-- additional permissions described in the GCC Runtime Library Exception, -- +-- version 3.1, as published by the Free Software Foundation. -- +-- -- +-- You should have received a copy of the GNU General Public License and -- +-- a copy of the GCC Runtime Library Exception along with this program; -- +-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- +-- <http://www.gnu.org/licenses/>. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- Extensive contributions were provided by Ada Core Technologies Inc. -- +-- -- +------------------------------------------------------------------------------ + +with Ada.Unchecked_Deallocation; + +package body GNAT.Lists is + + package body Doubly_Linked_List is + procedure Delete_Node (L : Instance; Nod : Node_Ptr); + pragma Inline (Delete_Node); + -- Detach and delete node Nod from list L + + procedure Ensure_Circular (Head : Node_Ptr); + pragma Inline (Ensure_Circular); + -- Ensure that dummy head Head is circular with respect to itself + + procedure Ensure_Created (L : Instance); + pragma Inline (Ensure_Created); + -- Verify that list L is created. Raise Not_Created if this is not the + -- case. + + procedure Ensure_Full (L : Instance); + pragma Inline (Ensure_Full); + -- Verify that list L contains at least one element. Raise List_Empty if + -- this is not the case. + + procedure Ensure_Unlocked (L : Instance); + pragma Inline (Ensure_Unlocked); + -- Verify that list L is unlocked. Raise List_Locked if this is not the + -- case. + + function Find_Node + (Head : Node_Ptr; + Elem : Element_Type) return Node_Ptr; + pragma Inline (Find_Node); + -- Travers a list indicated by dummy head Head to determine whethe there + -- exists a node with element Elem. If such a node exists, return it, + -- otherwise return null; + + procedure Free is new Ada.Unchecked_Deallocation (Linked_List, Instance); + + procedure Free is new Ada.Unchecked_Deallocation (Node, Node_Ptr); + + procedure Insert_Between + (L : Instance; + Elem : Element_Type; + Left : Node_Ptr; + Right : Node_Ptr); + pragma Inline (Insert_Between); + -- Insert element Elem between nodes Left and Right of list L + + function Is_Valid (Iter : Iterator) return Boolean; + pragma Inline (Is_Valid); + -- Determine whether iterator Iter refers to a valid element + + function Is_Valid (Nod : Node_Ptr; Head : Node_Ptr) return Boolean; + pragma Inline (Is_Valid); + -- Determine whether node Nod is non-null and does not refer to dummy + -- head Head, thus making it valid. + + procedure Lock (L : Instance); + pragma Inline (Lock); + -- Lock all mutation functionality of list L + + procedure Unlock (L : Instance); + pragma Inline (Unlock); + -- Unlock all mutation functionality of list L + + ------------ + -- Append -- + ------------ + + procedure Append (L : Instance; Elem : Element_Type) is + Head : Node_Ptr; + + begin + Ensure_Created (L); + Ensure_Unlocked (L); + + -- Ensure that the dummy head of an empty list is circular with + -- respect to itself. + + Head := L.Nodes'Access; + Ensure_Circular (Head); + + -- Append the node by inserting it between the last node and the + -- dummy head. + + Insert_Between + (L => L, + Elem => Elem, + Left => Head.Prev, + Right => Head); + end Append; + + ------------ + -- Create -- + ------------ + + function Create return Instance is + begin + return new Linked_List; + end Create; + + -------------- + -- Contains -- + -------------- + + function Contains (L : Instance; Elem : Element_Type) return Boolean is + Head : Node_Ptr; + Nod : Node_Ptr; + + begin + Ensure_Created (L); + + Head := L.Nodes'Access; + Nod := Find_Node (Head, Elem); + + return Is_Valid (Nod, Head); + end Contains; + + ------------ + -- Delete -- + ------------ + + procedure Delete (L : Instance; Elem : Element_Type) is + Head : Node_Ptr; + Nod : Node_Ptr; + + begin + Ensure_Created (L); + Ensure_Full (L); + Ensure_Unlocked (L); + + Head := L.Nodes'Access; + Nod := Find_Node (Head, Elem); + + if Is_Valid (Nod, Head) then + Delete_Node (L, Nod); + end if; + end Delete; + + ------------------ + -- Delete_First -- + ------------------ + + procedure Delete_First (L : Instance) is + Head : Node_Ptr; + Nod : Node_Ptr; + + begin + Ensure_Created (L); + Ensure_Full (L); + Ensure_Unlocked (L); + + Head := L.Nodes'Access; + Nod := Head.Next; + + if Is_Valid (Nod, Head) then + Delete_Node (L, Nod); + end if; + end Delete_First; + + ----------------- + -- Delete_Last -- + ----------------- + + procedure Delete_Last (L : Instance) is + Head : Node_Ptr; + Nod : Node_Ptr; + + begin + Ensure_Created (L); + Ensure_Full (L); + Ensure_Unlocked (L); + + Head := L.Nodes'Access; + Nod := Head.Prev; + + if Is_Valid (Nod, Head) then + Delete_Node (L, Nod); + end if; + end Delete_Last; + + ----------------- + -- Delete_Node -- + ----------------- + + procedure Delete_Node (L : Instance; Nod : Node_Ptr) is + Ref : Node_Ptr := Nod; + + pragma Assert (Ref /= null); + + Next : constant Node_Ptr := Ref.Next; + Prev : constant Node_Ptr := Ref.Prev; + + begin + pragma Assert (L /= null); + pragma Assert (Next /= null); + pragma Assert (Prev /= null); + + Prev.Next := Next; -- Prev ---> Next + Next.Prev := Prev; -- Prev <--> Next + + Ref.Next := null; + Ref.Prev := null; + + L.Elements := L.Elements - 1; + + Free (Ref); + end Delete_Node; + + ------------- + -- Destroy -- + ------------- + + procedure Destroy (L : in out Instance) is + Head : Node_Ptr; + + begin + Ensure_Created (L); + Ensure_Unlocked (L); + + Head := L.Nodes'Access; + + while Is_Valid (Head.Next, Head) loop + Delete_Node (L, Head.Next); + end loop; + + Free (L); + end Destroy; + + --------------------- + -- Ensure_Circular -- + --------------------- + + procedure Ensure_Circular (Head : Node_Ptr) is + pragma Assert (Head /= null); + + begin + if Head.Next = null and then Head.Prev = null then + Head.Next := Head; + Head.Prev := Head; + end if; + end Ensure_Circular; + + -------------------- + -- Ensure_Created -- + -------------------- + + procedure Ensure_Created (L : Instance) is + begin + if L = null then + raise Not_Created; + end if; + end Ensure_Created; + + ----------------- + -- Ensure_Full -- + ----------------- + + procedure Ensure_Full (L : Instance) is + begin + pragma Assert (L /= null); + + if L.Elements = 0 then + raise List_Empty; + end if; + end Ensure_Full; + + --------------------- + -- Ensure_Unlocked -- + --------------------- + + procedure Ensure_Unlocked (L : Instance) is + begin + pragma Assert (L /= null); + + -- The list has at least one outstanding iterator + + if L.Locked > 0 then + raise List_Locked; + end if; + end Ensure_Unlocked; + + --------------- + -- Find_Node -- + --------------- + + function Find_Node + (Head : Node_Ptr; + Elem : Element_Type) return Node_Ptr + is + pragma Assert (Head /= null); + + Nod : Node_Ptr; + + begin + -- Traverse the nodes of the list, looking for a matching element + + Nod := Head.Next; + while Is_Valid (Nod, Head) loop + if Nod.Elem = Elem then + return Nod; + end if; + + Nod := Nod.Next; + end loop; + + return null; + end Find_Node; + + ----------- + -- First -- + ----------- + + function First (L : Instance) return Element_Type is + begin + Ensure_Created (L); + Ensure_Full (L); + + return L.Nodes.Next.Elem; + end First; + + -------------- + -- Has_Next -- + -------------- + + function Has_Next (Iter : Iterator) return Boolean is + Is_OK : constant Boolean := Is_Valid (Iter); + + begin + -- The iterator is no longer valid which indicates that it has been + -- exhausted. Unlock all mutation functionality of the list because + -- the iterator cannot be advanced any further. + + if not Is_OK then + Unlock (Iter.List); + end if; + + return Is_OK; + end Has_Next; + + ------------------ + -- Insert_After -- + ------------------ + + procedure Insert_After + (L : Instance; + After : Element_Type; + Elem : Element_Type) + is + Head : Node_Ptr; + Nod : Node_Ptr; + + begin + Ensure_Created (L); + Ensure_Unlocked (L); + + Head := L.Nodes'Access; + Nod := Find_Node (Head, After); + + if Is_Valid (Nod, Head) then + Insert_Between + (L => L, + Elem => Elem, + Left => Nod, + Right => Nod.Next); + end if; + end Insert_After; + + ------------------- + -- Insert_Before -- + ------------------- + + procedure Insert_Before + (L : Instance; + Before : Element_Type; + Elem : Element_Type) + is + Head : Node_Ptr; + Nod : Node_Ptr; + + begin + Ensure_Created (L); + Ensure_Unlocked (L); + + Head := L.Nodes'Access; + Nod := Find_Node (Head, Before); + + if Is_Valid (Nod, Head) then + Insert_Between + (L => L, + Elem => Elem, + Left => Nod.Prev, + Right => Nod); + end if; + end Insert_Before; + + -------------------- + -- Insert_Between -- + -------------------- + + procedure Insert_Between + (L : Instance; + Elem : Element_Type; + Left : Node_Ptr; + Right : Node_Ptr) + is + pragma Assert (L /= null); + pragma Assert (Left /= null); + pragma Assert (Right /= null); + + Nod : constant Node_Ptr := + new Node'(Elem => Elem, + Next => Right, -- Left Nod ---> Right + Prev => Left); -- Left <--- Nod ---> Right + + begin + Left.Next := Nod; -- Left <--> Nod ---> Right + Right.Prev := Nod; -- Left <--> Nod <--> Right + + L.Elements := L.Elements + 1; + end Insert_Between; + + -------------- + -- Is_Empty -- + -------------- + + function Is_Empty (L : Instance) return Boolean is + begin + Ensure_Created (L); + + return L.Elements = 0; + end Is_Empty; + + -------------- + -- Is_Valid -- + -------------- + + function Is_Valid (Iter : Iterator) return Boolean is + begin + -- The invariant of Iterate and Next ensures that the iterator always + -- refers to a valid node if there exists one. + + return Is_Valid (Iter.Nod, Iter.List.Nodes'Access); + end Is_Valid; + + -------------- + -- Is_Valid -- + -------------- + + function Is_Valid (Nod : Node_Ptr; Head : Node_Ptr) return Boolean is + begin + -- A node is valid if it is non-null, and does not refer to the dummy + -- head of some list. + + return Nod /= null and then Nod /= Head; + end Is_Valid; + + ------------- + -- Iterate -- + ------------- + + function Iterate (L : Instance) return Iterator is + begin + Ensure_Created (L); + + -- Lock all mutation functionality of the list while it is being + -- iterated on. + + Lock (L); + + return (List => L, Nod => L.Nodes.Next); + end Iterate; + + ---------- + -- Last -- + ---------- + + function Last (L : Instance) return Element_Type is + begin + Ensure_Created (L); + Ensure_Full (L); + + return L.Nodes.Prev.Elem; + end Last; + + ------------ + -- Length -- + ------------ + + function Length (L : Instance) return Element_Count_Type is + begin + Ensure_Created (L); + + return L.Elements; + end Length; + + ---------- + -- Lock -- + ---------- + + procedure Lock (L : Instance) is + begin + pragma Assert (L /= null); + + -- The list may be locked multiple times if multiple iterators are + -- operating over it. + + L.Locked := L.Locked + 1; + end Lock; + + ---------- + -- Next -- + ---------- + + procedure Next + (Iter : in out Iterator; + Elem : out Element_Type) + is + Is_OK : constant Boolean := Is_Valid (Iter); + Saved : constant Node_Ptr := Iter.Nod; + + begin + -- The iterator is no linger valid which indicates that it has been + -- exhausted. Unlock all mutation functionality of the list as the + -- iterator cannot be advanced any further. + + if not Is_OK then + Unlock (Iter.List); + raise Iterator_Exhausted; + end if; + + -- Advance to the next node along the list + + Iter.Nod := Iter.Nod.Next; + Elem := Saved.Elem; + end Next; + + ------------- + -- Prepend -- + ------------- + + procedure Prepend (L : Instance; Elem : Element_Type) is + Head : Node_Ptr; + + begin + Ensure_Created (L); + Ensure_Unlocked (L); + + -- Ensure that the dummy head of an empty list is circular with + -- respect to itself. + + Head := L.Nodes'Access; + Ensure_Circular (Head); + + -- Append the node by inserting it between the dummy head and the + -- first node. + + Insert_Between + (L => L, + Elem => Elem, + Left => Head, + Right => Head.Next); + end Prepend; + + ------------- + -- Replace -- + ------------- + + procedure Replace + (L : Instance; + Old_Elem : Element_Type; + New_Elem : Element_Type) + is + Head : Node_Ptr; + Nod : Node_Ptr; + + begin + Ensure_Created (L); + Ensure_Unlocked (L); + + Head := L.Nodes'Access; + Nod := Find_Node (Head, Old_Elem); + + if Is_Valid (Nod, Head) then + Nod.Elem := New_Elem; + end if; + end Replace; + + ------------ + -- Unlock -- + ------------ + + procedure Unlock (L : Instance) is + begin + pragma Assert (L /= null); + + -- The list may be locked multiple times if multiple iterators are + -- operating over it. + + L.Locked := L.Locked - 1; + end Unlock; + end Doubly_Linked_List; + +end GNAT.Lists; diff --git a/gcc/ada/libgnat/g-lists.ads b/gcc/ada/libgnat/g-lists.ads new file mode 100644 index 0000000..777b4f6 --- /dev/null +++ b/gcc/ada/libgnat/g-lists.ads @@ -0,0 +1,245 @@ +------------------------------------------------------------------------------ +-- -- +-- GNAT RUN-TIME COMPONENTS -- +-- -- +-- G N A T . L I S T S -- +-- -- +-- S p e c -- +-- -- +-- Copyright (C) 2018, 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. -- +-- -- +-- As a special exception under Section 7 of GPL version 3, you are granted -- +-- additional permissions described in the GCC Runtime Library Exception, -- +-- version 3.1, as published by the Free Software Foundation. -- +-- -- +-- You should have received a copy of the GNU General Public License and -- +-- a copy of the GCC Runtime Library Exception along with this program; -- +-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- +-- <http://www.gnu.org/licenses/>. -- +-- -- +-- GNAT was originally developed by the GNAT team at New York University. -- +-- Extensive contributions were provided by Ada Core Technologies Inc. -- +-- -- +------------------------------------------------------------------------------ + +pragma Compiler_Unit_Warning; + +package GNAT.Lists is + + ------------------------ + -- Doubly_Linked_List -- + ------------------------ + + -- The following package offers a doubly linked list abstraction with the + -- following characteristics: + -- + -- * Creation of multiple instances, of different sizes. + -- * Iterable elements. + -- + -- The following use pattern must be employed with this list: + -- + -- List : Instance := Create; + -- + -- <various operations> + -- + -- Destroy (List) + -- + -- The destruction of the list reclaims all storage occupied by it. + + -- The following type denotes the number of elements stored in a list + + type Element_Count_Type is range 0 .. 2 ** 31 - 1; + + generic + type Element_Type is private; + + with function "=" + (Left : Element_Type; + Right : Element_Type) return Boolean; + + package Doubly_Linked_List is + + --------------------- + -- List operations -- + --------------------- + + type Instance is private; + Nil : constant Instance; + + List_Empty : exception; + -- This exception is raised when the list is empty, and an attempt is + -- made to delete an element from it. + + List_Locked : exception; + -- This exception is raised when the list is being iterated on, and an + -- attempt is made to mutate its state. + + Not_Created : exception; + -- This exception is raised when the list has not been created by + -- routine Create, and an attempt is made to read or mutate its state. + + procedure Append (L : Instance; Elem : Element_Type); + -- Insert element Elem at the end of list L. This action will raise + -- List_Locked if the list has outstanding iterators. + + function Contains (L : Instance; Elem : Element_Type) return Boolean; + -- Determine whether list L contains element Elem + + function Create return Instance; + -- Create a new list + + procedure Delete (L : Instance; Elem : Element_Type); + -- Delete element Elem from list L. The routine has no effect if Elem is + -- not present. This action will raise + -- + -- * List_Empty if the list is empty. + -- * List_Locked if the list has outstanding iterators. + + procedure Delete_First (L : Instance); + -- Delete an element from the start of list L. This action will raise + -- + -- * List_Empty if the list is empty. + -- * List_Locked if the list has outstanding iterators. + + procedure Delete_Last (L : Instance); + -- Delete an element from the end of list L. This action will raise + -- + -- * List_Empty if the list is empty. + -- * List_Locked if the list has outstanding iterators. + + procedure Destroy (L : in out Instance); + -- Destroy the contents of list L. This routine must be called at the + -- end of a list's lifetime. This action will raise List_Locked if the + -- list has outstanding iterators. + + function First (L : Instance) return Element_Type; + -- Obtain an element from the start of list L. This action will raise + -- List_Empty if the list is empty. + + procedure Insert_After + (L : Instance; + After : Element_Type; + Elem : Element_Type); + -- Insert new element Elem after element After in list L. The routine + -- has no effect if After is not present. This action will raise + -- List_Locked if the list has outstanding iterators. + + procedure Insert_Before + (L : Instance; + Before : Element_Type; + Elem : Element_Type); + -- Insert new element Elem before element Before in list L. The routine + -- has no effect if After is not present. This action will raise + -- List_Locked if the list has outstanding iterators. + + function Is_Empty (L : Instance) return Boolean; + -- Determine whether list L is empty + + function Last (L : Instance) return Element_Type; + -- Obtain an element from the end of list L. This action will raise + -- List_Empty if the list is empty. + + function Length (L : Instance) return Element_Count_Type; + -- Obtain the number of elements in list L + + procedure Prepend (L : Instance; Elem : Element_Type); + -- Insert element Elem at the start of list L. This action will raise + -- List_Locked if the list has outstanding iterators. + + procedure Replace + (L : Instance; + Old_Elem : Element_Type; + New_Elem : Element_Type); + -- Replace old element Old_Elem with new element New_Elem in list L. The + -- routine has no effect if Old_Elem is not present. This action will + -- raise List_Locked if the list has outstanding iterators. + + ------------------------- + -- Iterator operations -- + ------------------------- + + -- The following type represents an element iterator. An iterator locks + -- all mutation operations, and ulocks them once it is exhausted. The + -- iterator must be used with the following pattern: + -- + -- Iter := Iterate (My_List); + -- while Has_Next (Iter) loop + -- Next (Iter, Element); + -- end loop; + -- + -- It is possible to advance the iterator by using Next only, however + -- this risks raising Iterator_Exhausted. + + type Iterator is private; + + Iterator_Exhausted : exception; + -- This exception is raised when an iterator is exhausted and further + -- attempts to advance it are made by calling routine Next. + + function Iterate (L : Instance) return Iterator; + -- Obtain an iterator over the elements of list L. This action locks all + -- mutation functionality of the associated list. + + function Has_Next (Iter : Iterator) return Boolean; + -- Determine whether iterator Iter has more elements to examine. If the + -- iterator has been exhausted, restore all mutation functionality of + -- the associated list. + + procedure Next + (Iter : in out Iterator; + Elem : out Element_Type); + -- Return the current element referenced by iterator Iter and advance + -- to the next available element. If the iterator has been exhausted + -- and further attempts are made to advance it, this routine restores + -- mutation functionality of the associated list, and then raises + -- Iterator_Exhausted. + + private + -- The following type represents a list node + + type Node; + type Node_Ptr is access all Node; + type Node is record + Elem : Element_Type; + + Next : Node_Ptr := null; + Prev : Node_Ptr := null; + end record; + + -- The following type represents a list + + type Linked_List is record + Elements : Element_Count_Type := 0; + -- The number of elements in the list + + Locked : Natural := 0; + -- Number of outstanding iterators + + Nodes : aliased Node; + -- The dummy head of the list + end record; + + type Instance is access all Linked_List; + Nil : constant Instance := null; + + -- The following type represents an element iterator + + type Iterator is record + List : Instance := null; + -- Reference to the associated list + + Nod : Node_Ptr := null; + -- Reference to the current node being examined. The invariant of the + -- iterator requires that this field always points to a valid node. A + -- value of null indicates that the iterator is exhausted. + end record; + end Doubly_Linked_List; + +end GNAT.Lists; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 6947338..f95fe09 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,5 +1,9 @@ 2018-08-21 Hristian Kirtchev <kirtchev@adacore.com> + * gnat.dg/linkedlist.adb: New testcase. + +2018-08-21 Hristian Kirtchev <kirtchev@adacore.com> + * gnat.dg/elab6.adb, gnat.dg/elab6.ads, gnat.dg/elab6_pkg.adb, gnat.dg/elab6_pkg.ads: New testcase. diff --git a/gcc/testsuite/gnat.dg/linkedlist.adb b/gcc/testsuite/gnat.dg/linkedlist.adb new file mode 100644 index 0000000..53a272f --- /dev/null +++ b/gcc/testsuite/gnat.dg/linkedlist.adb @@ -0,0 +1,1184 @@ +-- { dg-do run } + +with Ada.Text_IO; use Ada.Text_IO; +with GNAT.Lists; use GNAT.Lists; + +procedure Linkedlist is + package Integer_Lists is new Doubly_Linked_List + (Element_Type => Integer, + "=" => "="); + use Integer_Lists; + + procedure Check_Empty + (Caller : String; + L : Instance; + Low_Elem : Integer; + High_Elem : Integer); + -- Ensure that none of the elements in the range Low_Elem .. High_Elem are + -- present in list L, and that the list's length is 0. + + procedure Check_Locked_Mutations (Caller : String; L : in out Instance); + -- Ensure that all mutation operations of list L are locked + + procedure Check_Present + (Caller : String; + L : Instance; + Low_Elem : Integer; + High_Elem : Integer); + -- Ensure that all elements in the range Low_Elem .. High_Elem are present + -- in list L. + + procedure Check_Unlocked_Mutations (Caller : String; L : in out Instance); + -- Ensure that all mutation operations of list L are unlocked + + procedure Populate_With_Append + (L : Instance; + Low_Elem : Integer; + High_Elem : Integer); + -- Add elements in the range Low_Elem .. High_Elem in that order in list L + + procedure Test_Append; + -- Verify that Append properly inserts at the tail of a list + + procedure Test_Contains + (Low_Elem : Integer; + High_Elem : Integer); + -- Verify that Contains properly identifies that elements in the range + -- Low_Elem .. High_Elem are within a list. + + procedure Test_Create; + -- Verify that all list operations fail on a non-created list + + procedure Test_Delete + (Low_Elem : Integer; + High_Elem : Integer); + -- Verify that Delete properly removes elements in the range Low_Elem .. + -- High_Elem from a list. + + procedure Test_Delete_First + (Low_Elem : Integer; + High_Elem : Integer); + -- Verify that Delete properly removes elements in the range Low_Elem .. + -- High_Elem from the head of a list. + + procedure Test_Delete_Last + (Low_Elem : Integer; + High_Elem : Integer); + -- Verify that Delete properly removes elements in the range Low_Elem .. + -- High_Elem from the tail of a list. + + procedure Test_First; + -- Verify that First properly returns the head of a list + + procedure Test_Insert_After; + -- Verify that Insert_After properly adds an element after some other + -- element. + + procedure Test_Insert_Before; + -- Vefity that Insert_Before properly adds an element before some other + -- element. + + procedure Test_Is_Empty; + -- Verify that Is_Empty properly returns this status of a list + + procedure Test_Iterate; + -- Verify that iterators properly manipulate mutation operations + + procedure Test_Iterate_Empty; + -- Verify that iterators properly manipulate mutation operations of an + -- empty list. + + procedure Test_Iterate_Forced + (Low_Elem : Integer; + High_Elem : Integer); + -- Verify that an iterator that is forcefully advanced by Next properly + -- unlocks the mutation operations of a list. + + procedure Test_Last; + -- Verify that Last properly returns the tail of a list + + procedure Test_Length; + -- Verify that Length returns the correct length of a list + + procedure Test_Prepend; + -- Verify that Prepend properly inserts at the head of a list + + procedure Test_Replace; + -- Verify that Replace properly substitutes old elements with new ones + + ----------------- + -- Check_Empty -- + ----------------- + + procedure Check_Empty + (Caller : String; + L : Instance; + Low_Elem : Integer; + High_Elem : Integer) + is + Len : constant Element_Count_Type := Length (L); + + begin + for Elem in Low_Elem .. High_Elem loop + if Contains (L, Elem) then + Put_Line ("ERROR: " & Caller & ": extra element" & Elem'Img); + end if; + end loop; + + if Len /= 0 then + Put_Line ("ERROR: " & Caller & ": wrong length"); + Put_Line ("expected: 0"); + Put_Line ("got :" & Len'Img); + end if; + end Check_Empty; + + ---------------------------- + -- Check_Locked_Mutations -- + ---------------------------- + + procedure Check_Locked_Mutations (Caller : String; L : in out Instance) is + begin + begin + Append (L, 1); + Put_Line ("ERROR: " & Caller & ": Append: no exception raised"); + exception + when List_Locked => + null; + when others => + Put_Line ("ERROR: " & Caller & ": Append: unexpected exception"); + end; + + begin + Delete (L, 1); + Put_Line ("ERROR: " & Caller & ": Delete: no exception raised"); + exception + when List_Empty => + null; + when List_Locked => + null; + when others => + Put_Line ("ERROR: " & Caller & ": Delete: unexpected exception"); + end; + + begin + Delete_First (L); + Put_Line ("ERROR: " & Caller & ": Delete_First: no exception raised"); + exception + when List_Empty => + null; + when List_Locked => + null; + when others => + Put_Line + ("ERROR: " & Caller & ": Delete_First: unexpected exception"); + end; + + begin + Delete_Last (L); + Put_Line ("ERROR: " & Caller & ": Delete_List: no exception raised"); + exception + when List_Empty => + null; + when List_Locked => + null; + when others => + Put_Line + ("ERROR: " & Caller & ": Delete_Last: unexpected exception"); + end; + + begin + Destroy (L); + Put_Line ("ERROR: " & Caller & ": Destroy: no exception raised"); + exception + when List_Locked => + null; + when others => + Put_Line ("ERROR: " & Caller & ": Destroy: unexpected exception"); + end; + + begin + Insert_After (L, 1, 2); + Put_Line ("ERROR: " & Caller & ": Insert_After: no exception raised"); + exception + when List_Locked => + null; + when others => + Put_Line + ("ERROR: " & Caller & ": Insert_After: unexpected exception"); + end; + + begin + Insert_Before (L, 1, 2); + Put_Line + ("ERROR: " & Caller & ": Insert_Before: no exception raised"); + exception + when List_Locked => + null; + when others => + Put_Line + ("ERROR: " & Caller & ": Insert_Before: unexpected exception"); + end; + + begin + Prepend (L, 1); + Put_Line ("ERROR: " & Caller & ": Prepend: no exception raised"); + exception + when List_Locked => + null; + when others => + Put_Line ("ERROR: " & Caller & ": Prepend: unexpected exception"); + end; + + begin + Replace (L, 1, 2); + Put_Line ("ERROR: " & Caller & ": Replace: no exception raised"); + exception + when List_Locked => + null; + when others => + Put_Line ("ERROR: " & Caller & ": Replace: unexpected exception"); + end; + end Check_Locked_Mutations; + + ------------------- + -- Check_Present -- + ------------------- + + procedure Check_Present + (Caller : String; + L : Instance; + Low_Elem : Integer; + High_Elem : Integer) + is + Elem : Integer; + Iter : Iterator; + + begin + Iter := Iterate (L); + for Exp_Elem in Low_Elem .. High_Elem loop + Next (Iter, Elem); + + if Elem /= Exp_Elem then + Put_Line ("ERROR: " & Caller & ": Check_Present: wrong element"); + Put_Line ("expected:" & Exp_Elem'Img); + Put_Line ("got :" & Elem'Img); + end if; + end loop; + + -- At this point all elements should have been accounted for. Check for + -- extra elements. + + while Has_Next (Iter) loop + Next (Iter, Elem); + Put_Line + ("ERROR: " & Caller & ": Check_Present: extra element" & Elem'Img); + end loop; + + exception + when Iterator_Exhausted => + Put_Line + ("ERROR: " + & Caller + & "Check_Present: incorrect number of elements"); + end Check_Present; + + ------------------------------ + -- Check_Unlocked_Mutations -- + ------------------------------ + + procedure Check_Unlocked_Mutations (Caller : String; L : in out Instance) is + begin + Append (L, 1); + Append (L, 2); + Append (L, 3); + Delete (L, 1); + Delete_First (L); + Delete_Last (L); + Insert_After (L, 2, 3); + Insert_Before (L, 2, 1); + Prepend (L, 0); + Replace (L, 3, 4); + end Check_Unlocked_Mutations; + + -------------------------- + -- Populate_With_Append -- + -------------------------- + + procedure Populate_With_Append + (L : Instance; + Low_Elem : Integer; + High_Elem : Integer) + is + begin + for Elem in Low_Elem .. High_Elem loop + Append (L, Elem); + end loop; + end Populate_With_Append; + + ----------------- + -- Test_Append -- + ----------------- + + procedure Test_Append is + L : Instance := Create; + + begin + Append (L, 1); + Append (L, 2); + Append (L, 3); + Append (L, 4); + Append (L, 5); + + Check_Present + (Caller => "Test_Append", + L => L, + Low_Elem => 1, + High_Elem => 5); + + Destroy (L); + end Test_Append; + + ------------------- + -- Test_Contains -- + ------------------- + + procedure Test_Contains + (Low_Elem : Integer; + High_Elem : Integer) + is + Low_Bogus : constant Integer := Low_Elem - 1; + High_Bogus : constant Integer := High_Elem + 1; + + L : Instance := Create; + + begin + Populate_With_Append (L, Low_Elem, High_Elem); + + -- Ensure that the elements are contained in the list + + for Elem in Low_Elem .. High_Elem loop + if not Contains (L, Elem) then + Put_Line + ("ERROR: Test_Contains: element" & Elem'Img & " not in list"); + end if; + end loop; + + -- Ensure that arbitrary elements which were not inserted in the list + -- are not contained in the list. + + if Contains (L, Low_Bogus) then + Put_Line + ("ERROR: Test_Contains: element" & Low_Bogus'Img & " in list"); + end if; + + if Contains (L, High_Bogus) then + Put_Line + ("ERROR: Test_Contains: element" & High_Bogus'Img & " in list"); + end if; + + Destroy (L); + end Test_Contains; + + ----------------- + -- Test_Create -- + ----------------- + + procedure Test_Create is + Count : Element_Count_Type; + Flag : Boolean; + Iter : Iterator; + L : Instance; + Val : Integer; + + begin + -- Ensure that every routine defined in the API fails on a list which + -- has not been created yet. + + begin + Append (L, 1); + Put_Line ("ERROR: Test_Create: Append: no exception raised"); + exception + when Not_Created => + null; + when others => + Put_Line ("ERROR: Test_Create: Append: unexpected exception"); + end; + + begin + Flag := Contains (L, 1); + Put_Line ("ERROR: Test_Create: Contains: no exception raised"); + exception + when Not_Created => + null; + when others => + Put_Line ("ERROR: Test_Create: Contains: unexpected exception"); + end; + + begin + Delete (L, 1); + Put_Line ("ERROR: Test_Create: Delete: no exception raised"); + exception + when Not_Created => + null; + when others => + Put_Line ("ERROR: Test_Create: Delete: unexpected exception"); + end; + + begin + Delete_First (L); + Put_Line ("ERROR: Test_Create: Delete_First: no exception raised"); + exception + when Not_Created => + null; + when others => + Put_Line + ("ERROR: Test_Create: Delete_First: unexpected exception"); + end; + + begin + Delete_Last (L); + Put_Line ("ERROR: Test_Create: Delete_Last: no exception raised"); + exception + when Not_Created => + null; + when others => + Put_Line ("ERROR: Test_Create: Delete_Last: unexpected exception"); + end; + + begin + Val := First (L); + Put_Line ("ERROR: Test_Create: First: no exception raised"); + exception + when Not_Created => + null; + when others => + Put_Line ("ERROR: Test_Create: First: unexpected exception"); + end; + + begin + Insert_After (L, 1, 2); + Put_Line ("ERROR: Test_Create: Insert_After: no exception raised"); + exception + when Not_Created => + null; + when others => + Put_Line + ("ERROR: Test_Create: Insert_After: unexpected exception"); + end; + + begin + Insert_Before (L, 1, 2); + Put_Line ("ERROR: Test_Create: Insert_Before: no exception raised"); + exception + when Not_Created => + null; + when others => + Put_Line + ("ERROR: Test_Create: Insert_Before: unexpected exception"); + end; + + begin + Flag := Is_Empty (L); + Put_Line ("ERROR: Test_Create: Is_Empty: no exception raised"); + exception + when Not_Created => + null; + when others => + Put_Line ("ERROR: Test_Create: Is_Empty: unexpected exception"); + end; + + begin + Iter := Iterate (L); + Put_Line ("ERROR: Test_Create: Iterate: no exception raised"); + exception + when Not_Created => + null; + when others => + Put_Line ("ERROR: Test_Create: Iterate: unexpected exception"); + end; + + begin + Val := Last (L); + Put_Line ("ERROR: Test_Create: Last: no exception raised"); + exception + when Not_Created => + null; + when others => + Put_Line ("ERROR: Test_Create: Last: unexpected exception"); + end; + + begin + Count := Length (L); + Put_Line ("ERROR: Test_Create: Length: no exception raised"); + exception + when Not_Created => + null; + when others => + Put_Line ("ERROR: Test_Create: Length: unexpected exception"); + end; + + begin + Prepend (L, 1); + Put_Line ("ERROR: Test_Create: Prepend: no exception raised"); + exception + when Not_Created => + null; + when others => + Put_Line ("ERROR: Test_Create: Prepend: unexpected exception"); + end; + + begin + Replace (L, 1, 2); + Put_Line ("ERROR: Test_Create: Replace: no exception raised"); + exception + when Not_Created => + null; + when others => + Put_Line ("ERROR: Test_Create: Replace: unexpected exception"); + end; + end Test_Create; + + ----------------- + -- Test_Delete -- + ----------------- + + procedure Test_Delete + (Low_Elem : Integer; + High_Elem : Integer) + is + Iter : Iterator; + L : Instance := Create; + + begin + Populate_With_Append (L, Low_Elem, High_Elem); + + -- Delete the first element, which is technically the head + + Delete (L, Low_Elem); + + -- Ensure that all remaining elements except for the head are present in + -- the list. + + Check_Present + (Caller => "Test_Delete", + L => L, + Low_Elem => Low_Elem + 1, + High_Elem => High_Elem); + + -- Delete the last element, which is technically the tail + + Delete (L, High_Elem); + + -- Ensure that all remaining elements except for the head and tail are + -- present in the list. + + Check_Present + (Caller => "Test_Delete", + L => L, + Low_Elem => Low_Elem + 1, + High_Elem => High_Elem - 1); + + -- Delete all even elements + + for Elem in Low_Elem + 1 .. High_Elem - 1 loop + if Elem mod 2 = 0 then + Delete (L, Elem); + end if; + end loop; + + -- Ensure that all remaining elements except the head, tail, and even + -- elements are present in the list. + + for Elem in Low_Elem + 1 .. High_Elem - 1 loop + if Elem mod 2 /= 0 and then not Contains (L, Elem) then + Put_Line ("ERROR: Test_Delete: missing element" & Elem'Img); + end if; + end loop; + + -- Delete all odd elements + + for Elem in Low_Elem + 1 .. High_Elem - 1 loop + if Elem mod 2 /= 0 then + Delete (L, Elem); + end if; + end loop; + + -- At this point the list should be completely empty + + Check_Empty + (Caller => "Test_Delete", + L => L, + Low_Elem => Low_Elem, + High_Elem => High_Elem); + + -- Try to delete an element. This operation should raise List_Empty. + + begin + Delete (L, Low_Elem); + Put_Line ("ERROR: Test_Delete: List_Empty not raised"); + exception + when List_Empty => + null; + when others => + Put_Line ("ERROR: Test_Delete: unexpected exception"); + end; + + Destroy (L); + end Test_Delete; + + ----------------------- + -- Test_Delete_First -- + ----------------------- + + procedure Test_Delete_First + (Low_Elem : Integer; + High_Elem : Integer) + is + L : Instance := Create; + + begin + Populate_With_Append (L, Low_Elem, High_Elem); + + -- Delete the head of the list, and verify that the remaining elements + -- are still present in the list. + + for Elem in Low_Elem .. High_Elem loop + Delete_First (L); + + Check_Present + (Caller => "Test_Delete_First", + L => L, + Low_Elem => Elem + 1, + High_Elem => High_Elem); + end loop; + + -- At this point the list should be completely empty + + Check_Empty + (Caller => "Test_Delete_First", + L => L, + Low_Elem => Low_Elem, + High_Elem => High_Elem); + + -- Try to delete an element. This operation should raise List_Empty. + + begin + Delete_First (L); + Put_Line ("ERROR: Test_Delete_First: List_Empty not raised"); + exception + when List_Empty => + null; + when others => + Put_Line ("ERROR: Test_Delete_First: unexpected exception"); + end; + + Destroy (L); + end Test_Delete_First; + + ---------------------- + -- Test_Delete_Last -- + ---------------------- + + procedure Test_Delete_Last + (Low_Elem : Integer; + High_Elem : Integer) + is + L : Instance := Create; + + begin + Populate_With_Append (L, Low_Elem, High_Elem); + + -- Delete the tail of the list, and verify that the remaining elements + -- are still present in the list. + + for Elem in reverse Low_Elem .. High_Elem loop + Delete_Last (L); + + Check_Present + (Caller => "Test_Delete_Last", + L => L, + Low_Elem => Low_Elem, + High_Elem => Elem - 1); + end loop; + + -- At this point the list should be completely empty + + Check_Empty + (Caller => "Test_Delete_Last", + L => L, + Low_Elem => Low_Elem, + High_Elem => High_Elem); + + -- Try to delete an element. This operation should raise List_Empty. + + begin + Delete_Last (L); + Put_Line ("ERROR: Test_Delete_Last: List_Empty not raised"); + exception + when List_Empty => + null; + when others => + Put_Line ("ERROR: Test_Delete_First: unexpected exception"); + end; + + Destroy (L); + end Test_Delete_Last; + + ---------------- + -- Test_First -- + ---------------- + + procedure Test_First is + Elem : Integer; + L : Instance := Create; + + begin + -- Try to obtain the head. This operation should raise List_Empty. + + begin + Elem := First (L); + Put_Line ("ERROR: Test_First: List_Empty not raised"); + exception + when List_Empty => + null; + when others => + Put_Line ("ERROR: Test_First: unexpected exception"); + end; + + Populate_With_Append (L, 1, 2); + + -- Obtain the head + + Elem := First (L); + + if Elem /= 1 then + Put_Line ("ERROR: Test_First: wrong element"); + Put_Line ("expected: 1"); + Put_Line ("got :" & Elem'Img); + end if; + + Destroy (L); + end Test_First; + + ----------------------- + -- Test_Insert_After -- + ----------------------- + + procedure Test_Insert_After is + L : Instance := Create; + + begin + -- Try to insert after a non-inserted element, in an empty list + + Insert_After (L, 1, 2); + + -- At this point the list should be completely empty + + Check_Empty + (Caller => "Test_Insert_After", + L => L, + Low_Elem => 0, + High_Elem => -1); + + Append (L, 1); -- 1 + + Insert_After (L, 1, 3); -- 1, 3 + Insert_After (L, 1, 2); -- 1, 2, 3 + Insert_After (L, 3, 4); -- 1, 2, 3, 4 + + -- Try to insert after a non-inserted element, in a full list + + Insert_After (L, 10, 11); + + Check_Present + (Caller => "Test_Insert_After", + L => L, + Low_Elem => 1, + High_Elem => 4); + + Destroy (L); + end Test_Insert_After; + + ------------------------ + -- Test_Insert_Before -- + ------------------------ + + procedure Test_Insert_Before is + L : Instance := Create; + + begin + -- Try to insert before a non-inserted element, in an empty list + + Insert_Before (L, 1, 2); + + -- At this point the list should be completely empty + + Check_Empty + (Caller => "Test_Insert_Before", + L => L, + Low_Elem => 0, + High_Elem => -1); + + Append (L, 4); -- 4 + + Insert_Before (L, 4, 2); -- 2, 4 + Insert_Before (L, 2, 1); -- 1, 2, 4 + Insert_Before (L, 4, 3); -- 1, 2, 3, 4 + + -- Try to insert before a non-inserted element, in a full list + + Insert_Before (L, 10, 11); + + Check_Present + (Caller => "Test_Insert_Before", + L => L, + Low_Elem => 1, + High_Elem => 4); + + Destroy (L); + end Test_Insert_Before; + + ------------------- + -- Test_Is_Empty -- + ------------------- + + procedure Test_Is_Empty is + L : Instance := Create; + + begin + if not Is_Empty (L) then + Put_Line ("ERROR: Test_Is_Empty: list is not empty"); + end if; + + Append (L, 1); + + if Is_Empty (L) then + Put_Line ("ERROR: Test_Is_Empty: list is empty"); + end if; + + Delete_First (L); + + if not Is_Empty (L) then + Put_Line ("ERROR: Test_Is_Empty: list is not empty"); + end if; + + Destroy (L); + end Test_Is_Empty; + + ------------------ + -- Test_Iterate -- + ------------------ + + procedure Test_Iterate is + Elem : Integer; + Iter_1 : Iterator; + Iter_2 : Iterator; + L : Instance := Create; + + begin + Populate_With_Append (L, 1, 5); + + -- Obtain an iterator. This action must lock all mutation operations of + -- the list. + + Iter_1 := Iterate (L); + + -- Ensure that every mutation routine defined in the API fails on a list + -- with at least one outstanding iterator. + + Check_Locked_Mutations + (Caller => "Test_Iterate", + L => L); + + -- Obtain another iterator + + Iter_2 := Iterate (L); + + -- Ensure that every mutation is still locked + + Check_Locked_Mutations + (Caller => "Test_Iterate", + L => L); + + -- Exhaust the first itertor + + while Has_Next (Iter_1) loop + Next (Iter_1, Elem); + end loop; + + -- Ensure that every mutation is still locked + + Check_Locked_Mutations + (Caller => "Test_Iterate", + L => L); + + -- Exhaust the second itertor + + while Has_Next (Iter_2) loop + Next (Iter_2, Elem); + end loop; + + -- Ensure that all mutation operations are once again callable + + Check_Unlocked_Mutations + (Caller => "Test_Iterate", + L => L); + + Destroy (L); + end Test_Iterate; + + ------------------------ + -- Test_Iterate_Empty -- + ------------------------ + + procedure Test_Iterate_Empty is + Elem : Integer; + Iter : Iterator; + L : Instance := Create; + + begin + -- Obtain an iterator. This action must lock all mutation operations of + -- the list. + + Iter := Iterate (L); + + -- Ensure that every mutation routine defined in the API fails on a list + -- with at least one outstanding iterator. + + Check_Locked_Mutations + (Caller => "Test_Iterate_Empty", + L => L); + + -- Attempt to iterate over the elements + + while Has_Next (Iter) loop + Next (Iter, Elem); + + Put_Line + ("ERROR: Test_Iterate_Empty: element" & Elem'Img & " exists"); + end loop; + + -- Ensure that all mutation operations are once again callable + + Check_Unlocked_Mutations + (Caller => "Test_Iterate_Empty", + L => L); + + Destroy (L); + end Test_Iterate_Empty; + + ------------------------- + -- Test_Iterate_Forced -- + ------------------------- + + procedure Test_Iterate_Forced + (Low_Elem : Integer; + High_Elem : Integer) + is + Elem : Integer; + Iter : Iterator; + L : Instance := Create; + + begin + Populate_With_Append (L, Low_Elem, High_Elem); + + -- Obtain an iterator. This action must lock all mutation operations of + -- the list. + + Iter := Iterate (L); + + -- Ensure that every mutation routine defined in the API fails on a list + -- with at least one outstanding iterator. + + Check_Locked_Mutations + (Caller => "Test_Iterate_Forced", + L => L); + + -- Forcibly advance the iterator until it raises an exception + + begin + for Guard in Low_Elem .. High_Elem + 1 loop + Next (Iter, Elem); + end loop; + + Put_Line + ("ERROR: Test_Iterate_Forced: Iterator_Exhausted not raised"); + exception + when Iterator_Exhausted => + null; + when others => + Put_Line ("ERROR: Test_Iterate_Forced: unexpected exception"); + end; + + -- Ensure that all mutation operations are once again callable + + Check_Unlocked_Mutations + (Caller => "Test_Iterate_Forced", + L => L); + + Destroy (L); + end Test_Iterate_Forced; + + --------------- + -- Test_Last -- + --------------- + + procedure Test_Last is + Elem : Integer; + L : Instance := Create; + + begin + -- Try to obtain the tail. This operation should raise List_Empty. + + begin + Elem := First (L); + Put_Line ("ERROR: Test_Last: List_Empty not raised"); + exception + when List_Empty => + null; + when others => + Put_Line ("ERROR: Test_Last: unexpected exception"); + end; + + Populate_With_Append (L, 1, 2); + + -- Obtain the tail + + Elem := Last (L); + + if Elem /= 2 then + Put_Line ("ERROR: Test_Last: wrong element"); + Put_Line ("expected: 2"); + Put_Line ("got :" & Elem'Img); + end if; + + Destroy (L); + end Test_Last; + + ----------------- + -- Test_Length -- + ----------------- + + procedure Test_Length is + L : Instance := Create; + Len : Element_Count_Type; + + begin + Len := Length (L); + + if Len /= 0 then + Put_Line ("ERROR: Test_Length: wrong length"); + Put_Line ("expected: 0"); + Put_Line ("got :" & Len'Img); + end if; + + Populate_With_Append (L, 1, 2); + Len := Length (L); + + if Len /= 2 then + Put_Line ("ERROR: Test_Length: wrong length"); + Put_Line ("expected: 2"); + Put_Line ("got :" & Len'Img); + end if; + + Populate_With_Append (L, 3, 6); + Len := Length (L); + + if Len /= 6 then + Put_Line ("ERROR: Test_Length: wrong length"); + Put_Line ("expected: 6"); + Put_Line ("got :" & Len'Img); + end if; + + Destroy (L); + end Test_Length; + + ------------------ + -- Test_Prepend -- + ------------------ + + procedure Test_Prepend is + L : Instance := Create; + + begin + Prepend (L, 5); + Prepend (L, 4); + Prepend (L, 3); + Prepend (L, 2); + Prepend (L, 1); + + Check_Present + (Caller => "Test_Prepend", + L => L, + Low_Elem => 1, + High_Elem => 5); + + Destroy (L); + end Test_Prepend; + + ------------------ + -- Test_Replace -- + ------------------ + + procedure Test_Replace is + L : Instance := Create; + + begin + Populate_With_Append (L, 1, 5); + + Replace (L, 3, 8); + Replace (L, 1, 6); + Replace (L, 4, 9); + Replace (L, 5, 10); + Replace (L, 2, 7); + + Replace (L, 11, 12); + + Check_Present + (Caller => "Test_Replace", + L => L, + Low_Elem => 6, + High_Elem => 10); + + Destroy (L); + end Test_Replace; + +-- Start of processing for Operations + +begin + Test_Append; + + Test_Contains + (Low_Elem => 1, + High_Elem => 5); + + Test_Create; + + Test_Delete + (Low_Elem => 1, + High_Elem => 10); + + Test_Delete_First + (Low_Elem => 1, + High_Elem => 5); + + Test_Delete_Last + (Low_Elem => 1, + High_Elem => 5); + + Test_First; + Test_Insert_After; + Test_Insert_Before; + Test_Is_Empty; + Test_Iterate; + Test_Iterate_Empty; + + Test_Iterate_Forced + (Low_Elem => 1, + High_Elem => 5); + + Test_Last; + Test_Length; + Test_Prepend; + Test_Replace; +end Linkedlist; |