------------------------------------------------------------------------------
-- Copyright (c) 2013, Natacha Porté --
-- --
-- Permission to use, copy, modify, and distribute this software for any --
-- purpose with or without fee is hereby granted, provided that the above --
-- copyright notice and this permission notice appear in all copies. --
-- --
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES --
-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF --
-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR --
-- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES --
-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN --
-- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF --
-- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. --
------------------------------------------------------------------------------
with Ada.Strings.Fixed;
package body Natools.String_Slices.Slice_Sets is
package Fixed renames Ada.Strings.Fixed;
---------------------------
-- Range_Set subprograms --
---------------------------
function Is_Overlapping (Bounds : String_Range; Set : Range_Set)
return Boolean
is
Cursor : Range_Sets.Cursor := Set.Floor (Bounds);
begin
if Range_Sets.Has_Element (Cursor) then
if Bounds.First <= Last (Range_Sets.Element (Cursor)) then
return True;
end if;
Range_Sets.Next (Cursor);
else
Cursor := Set.First;
end if;
if Range_Sets.Has_Element (Cursor)
and then Range_Sets.Element (Cursor).First <= Last (Bounds)
then
return True;
end if;
return False;
end Is_Overlapping;
function Is_Valid (Set : Range_Set) return Boolean is
Cursor : Range_Sets.Cursor := Set.First;
Prev, Cur : String_Range;
begin
if not Range_Sets.Has_Element (Cursor) then
return True;
end if;
Prev := Range_Sets.Element (Cursor);
if Prev.Length = 0 then
return False;
end if;
Range_Sets.Next (Cursor);
while Range_Sets.Has_Element (Cursor) loop
Cur := Range_Sets.Element (Cursor);
if Cur.Length = 0 then
return False;
end if;
pragma Assert (Prev.First <= Cur.First);
if Is_In (Last (Prev), Cur) then
return False;
end if;
Prev := Cur;
Range_Sets.Next (Cursor);
end loop;
return True;
end Is_Valid;
function Total_Span (Set : Range_Set) return String_Range is
Result : String_Range := (1, 0);
Cursor : Range_Sets.Cursor := Set.First;
begin
if not Range_Sets.Has_Element (Cursor) then
return Result;
end if;
Result.First := Range_Sets.Element (Cursor).First;
Cursor := Set.Last;
Set_Last (Result, Last (Range_Sets.Element (Cursor)));
return Result;
end Total_Span;
procedure Include_Range
(Set : in out Range_Set; Bounds : in String_Range)
is
Cursor : Range_Sets.Cursor := Set.Floor (Bounds);
Next : Range_Sets.Cursor;
Actual : String_Range := Bounds;
R : String_Range;
begin
if Range_Sets.Has_Element (Cursor) then
R := Range_Sets.Element (Cursor);
Next := Range_Sets.Next (Cursor);
-- Do nothing if the given range is already covered
if Is_Subrange (Actual, R) then
return;
end if;
-- Merge with previous range if overlapping
if Is_In (Actual.First, R) then
Set_First (Actual, R.First);
Set.Delete (Cursor);
end if;
else
Next := Set.First;
end if;
while Range_Sets.Has_Element (Next) loop
Cursor := Next;
R := Range_Sets.Element (Cursor);
exit when not Is_In (R.First, Actual);
Next := Range_Sets.Next (Cursor);
if Is_Subrange (R, Actual) then
Set.Delete (Cursor);
else
pragma Assert (Last (R) > Last (Actual));
Set_Last (Actual, Last (R));
Set.Delete (Cursor);
end if;
end loop;
Set.Insert (Actual);
pragma Assert (Is_Valid (Set));
end Include_Range;
procedure Exclude_Range
(Set : in out Range_Set; Bounds : in String_Range)
is
Cursor : Range_Sets.Cursor;
R : String_Range;
begin
if Bounds.Length = 0 then
return;
end if;
Cursor := Set.Floor (Bounds);
if Range_Sets.Has_Element (Cursor) then
R := Range_Sets.Element (Cursor);
if R.First < Bounds.First then
if Is_In (Bounds.First, R) then
if Is_In (Last (Bounds) + 1, R) then
Set.Insert (To_Range (Last (Bounds) + 1, Last (R)));
end if;
Set_Last (R, Bounds.First - 1);
pragma Assert (R.Length > 0);
Set.Replace_Element (Cursor, R);
end if;
Range_Sets.Next (Cursor);
end if;
else
Cursor := Set.First;
end if;
while Range_Sets.Has_Element (Cursor)
and then Is_Subrange (Range_Sets.Element (Cursor), Bounds)
loop
declare
Next : constant Range_Sets.Cursor := Range_Sets.Next (Cursor);
begin
Set.Delete (Cursor);
Cursor := Next;
end;
end loop;
if Range_Sets.Has_Element (Cursor)
and then Is_In (Last (Bounds) + 1, Range_Sets.Element (Cursor))
then
R := Range_Sets.Element (Cursor);
Set_First (R, Last (Bounds) + 1);
Set.Replace_Element (Cursor, R);
end if;
pragma Assert (Is_Valid (Set));
end Exclude_Range;
-------------------------------
-- Public helper subprograms --
-------------------------------
function "<" (Left, Right : String_Range) return Boolean is
begin
return Left.First < Right.First;
end "<";
----------------------------
-- Conversion subprograms --
----------------------------
function To_Slice (S : Slice_Set) return Slice is
use type Ada.Containers.Count_Type;
begin
if S.Ref.Is_Empty then
return Null_Slice;
end if;
if S.Bounds.Is_Empty then
return Slice'(Bounds => (1, 0),
Ref => S.Ref);
elsif S.Bounds.Length = 1 then
return Slice'(Bounds => S.Bounds.First_Element,
Ref => S.Ref);
end if;
return To_Slice (To_String (S));
end To_Slice;
function To_Slice_Set (S : String) return Slice_Set is
function Factory return String;
function Factory return String is
begin
return S;
end Factory;
Result : Slice_Set;
begin
Result.Ref := String_Refs.Create (Factory'Access);
if S'Length > 0 then
Result.Bounds.Insert ((S'First, S'Length));
end if;
return Result;
end To_Slice_Set;
function To_Slice_Set (S : Slice) return Slice_Set is
Result : Slice_Set;
begin
Result.Ref := S.Ref;
if S.Bounds.Length > 0 then
Result.Bounds.Insert (S.Bounds);
end if;
return Result;
end To_Slice_Set;
function To_String (Set : Slice_Set) return String is
Cursor : Range_Sets.Cursor := Set.Bounds.First;
R : String_Range;
I : Positive := 1;
begin
return Result : String (1 .. Set.Total_Length) do
while Range_Sets.Has_Element (Cursor) loop
R := Range_Sets.Element (Cursor);
Result (I .. I + R.Length - 1)
:= Set.Ref.Query.Data.all (R.First .. Last (R));
I := I + R.Length;
Range_Sets.Next (Cursor);
end loop;
pragma Assert (I = Result'Last + 1);
end return;
end To_String;
function To_String (Set : Slice_Set; Subrange : String_Range)
return String is
begin
return Set.Subset (Subrange).To_String;
end To_String;
function To_String (Set : Slice_Set; First : Positive; Last : Natural)
return String is
begin
return Set.Subset (To_Range (First, Last)).To_String;
end To_String;
---------------------------------
-- Basic slice-set subprograms --
---------------------------------
procedure Clear (Set : in out Slice_Set) is
begin
Set.Bounds.Clear;
end Clear;
function Element (Set : Slice_Set; Index : Positive) return Character is
begin
if not Is_In (Set, Index) then
raise Constraint_Error;
end if;
return Set.Ref.Query.Data.all (Index);
end Element;
function First (Set : Slice_Set) return Positive is
Cursor : constant Range_Sets.Cursor := Set.Bounds.First;
begin
if Range_Sets.Has_Element (Cursor) then
return Range_Sets.Element (Cursor).First;
else
return 1;
end if;
end First;
function Is_Empty (Set : Slice_Set) return Boolean is
begin
return Set.Bounds.Is_Empty;
end Is_Empty;
function Is_In (Set : Slice_Set; Index : Natural) return Boolean is
Cursor : Range_Sets.Cursor;
begin
if Index = 0 or else Set.Ref.Is_Empty or else Set.Bounds.Is_Empty then
return False;
end if;
Cursor := Set.Bounds.Floor ((Index, 0));
return Range_Sets.Has_Element (Cursor)
and then Is_In (Index, Range_Sets.Element (Cursor));
end Is_In;
function Is_Null (Set : Slice_Set) return Boolean is
begin
return Set.Ref.Is_Empty;
end Is_Null;
function Is_Valid (Set : Slice_Set) return Boolean is
begin
if Set.Ref.Is_Empty then
return Set.Bounds.Is_Empty;
else
return Is_Subrange (Total_Span (Set.Bounds),
Get_Range (Set.Ref.Query.Data.all))
and then Is_Valid (Set.Bounds);
end if;
end Is_Valid;
function Last (Set : Slice_Set) return Natural is
Cursor : constant Range_Sets.Cursor := Set.Bounds.Last;
begin
if Range_Sets.Has_Element (Cursor) then
return Last (Range_Sets.Element (Cursor));
else
return 0;
end if;
end Last;
-- Multistep version:
-- function Next (Set : Slice_Set; Index : Natural; Steps : Positive := 1)
-- return Natural
-- is
-- Cursor : Range_Sets.Cursor;
-- Target : Positive := Index + Steps;
-- Skipped : Natural;
-- R : String_Range;
-- begin
-- if Index = 0 or else Set.Ref.Is_Empty or else Set.Bounds.Is_Empty then
-- raise Constraint_Error;
-- end if;
--
-- Cursor := Set.Bounds.Floor ((Index, 0));
--
-- if not Range_Sets.Has_Element (Cursor) then
-- raise Constraint_Error with "Next with index out of bounds";
-- end if;
--
-- R := Range_Sets.Element (Cursor);
-- loop
-- if Is_In (Target, R) then
-- return Target;
-- end if;
--
-- Skipped := Last (R) + 1;
-- Range_Sets.Next (Cursor);
-- exit when not Range_Sets.Has_Element (Cursor);
-- R := Range_Sets.Element (Cursor);
-- Skipped := R.First - Skipped;
-- Target := Target + Skipped;
-- end loop;
--
-- return 0;
-- end Next;
function Next (Set : Slice_Set; Index : Natural) return Natural is
Cursor : Range_Sets.Cursor;
begin
if Index = 0 or else Set.Ref.Is_Empty or else Set.Bounds.Is_Empty then
raise Constraint_Error;
end if;
Cursor := Set.Bounds.Floor ((Index, 0));
if not Range_Sets.Has_Element (Cursor) then
raise Constraint_Error with "Next with index out of bounds";
end if;
if Is_In (Index + 1, Range_Sets.Element (Cursor)) then
return Index + 1;
else
Range_Sets.Next (Cursor);
if Range_Sets.Has_Element (Cursor) then
return Range_Sets.Element (Cursor).First;
else
return 0;
end if;
end if;
end Next;
procedure Next (Set : in Slice_Set; Index : in out Natural) is
begin
Index := Next (Set, Index);
end Next;
-- Multistep version:
-- function Previous (Set : Slice_Set; Index : Natural; Steps : Positive := 1)
-- return Natural
-- is
-- Cursor : Range_Sets.Cursor;
-- Target : Positive;
-- Prev_First : Positive;
-- Skipped : Natural;
-- R : String_Range;
-- begin
-- if Index = 0 or else Set.Ref.Is_Empty or else Set.Bounds.Is_Empty then
-- raise Constraint_Error;
-- end if;
--
-- if Steps >= Index then
-- return 0;
-- end if;
-- Target := Index - Steps;
--
-- Cursor := Set.Bounds.Floor ((Index, 0));
-- if not Range_Sets.Has_Element (Cursor) then
-- raise Constraint_Error with "Previous with index out of bounds";
-- end if;
--
-- loop
-- R := Range_Sets.Element (Cursor);
-- if Is_In (Target, R) then
-- return Target;
-- end if;
--
-- Prev_First := R.First;
-- Range_Sets.Previous (Cursor);
-- exit when not Range_Sets.Has_Element (Cursor);
-- R := Range_Sets.Element (Cursor);
--
-- Skipped := Prev_First - (Last (R) + 1);
-- exit when Skipped >= Target;
-- Target := Target - Skipped;
-- end loop;
--
-- return 0;
-- end Previous;
function Previous (Set : Slice_Set; Index : Natural) return Natural is
Cursor : Range_Sets.Cursor;
begin
if Index = 0 or else Set.Ref.Is_Empty or else Set.Bounds.Is_Empty then
raise Constraint_Error;
end if;
Cursor := Set.Bounds.Floor ((Index, 0));
if not Range_Sets.Has_Element (Cursor) then
raise Constraint_Error with "Previous with index out of bounds";
end if;
if Is_In (Index - 1, Range_Sets.Element (Cursor)) then
return Index - 1;
else
Range_Sets.Previous (Cursor);
if Range_Sets.Has_Element (Cursor) then
return Last (Range_Sets.Element (Cursor));
else
return 0;
end if;
end if;
end Previous;
procedure Previous (Set : in Slice_Set; Index : in out Natural) is
begin
Index := Previous (Set, Index);
end Previous;
function Total_Length (Set : Slice_Set) return Natural is
Cursor : Range_Sets.Cursor := Set.Bounds.First;
Result : Natural := 0;
begin
while Range_Sets.Has_Element (Cursor) loop
Result := Result + Range_Sets.Element (Cursor).Length;
Range_Sets.Next (Cursor);
end loop;
return Result;
end Total_Length;
----------------------------
-- Operation on slice set --
----------------------------
procedure Add_Slice (Set : in out Slice_Set; Bounds : in String_Range) is
begin
if Bounds.Length = 0 then
return;
end if;
if Set.Ref.Is_Empty then
raise Constraint_Error with "Cannot add range to null slice set";
end if;
if not Is_Subrange (Bounds, Get_Range (Set.Ref.Query.Data.all)) then
raise Constraint_Error with "Add slice outside of parent";
end if;
if Is_Overlapping (Bounds, Set.Bounds) then
raise Constraint_Error with "Add an overlapping slice to a set";
end if;
Set.Bounds.Insert (Bounds);
end Add_Slice;
procedure Add_Slice (Set : in out Slice_Set; S : in Slice) is
use type String_Refs.Reference;
begin
if S.Bounds.Length = 0 then
return;
end if;
if Set.Ref.Is_Empty then
pragma Assert (Set.Bounds.Is_Empty);
Set.Ref := S.Ref;
Set.Bounds.Insert (S.Bounds);
return;
end if;
if Set.Ref /= S.Ref then
raise Constraint_Error with
"Addition of an unrelated slice to a slice set";
end if;
if Is_Overlapping (S.Bounds, Set.Bounds) then
raise Constraint_Error with
"Addition of an overlapping slice to a slice set";
end if;
Set.Bounds.Insert (S.Bounds);
end Add_Slice;
procedure Add_Slice
(Set : in out Slice_Set;
First : in Positive;
Last : in Natural) is
begin
Add_Slice (Set, To_Range (First, Last));
end Add_Slice;
procedure Include_Slice
(Set : in out Slice_Set; Bounds : in String_Range) is
begin
if Bounds.Length = 0 then
return;
end if;
if Set.Ref.Is_Empty then
raise Constraint_Error with "Cannot include range to null slice set";
end if;
if not Is_Subrange (Bounds, Get_Range (Set.Ref.Query.Data.all)) then
raise Constraint_Error with "Include slice outside of parent";
end if;
Include_Range (Set.Bounds, Bounds);
end Include_Slice;
procedure Include_Slice (Set : in out Slice_Set; S : in Slice) is
use type String_Refs.Reference;
begin
if S.Bounds.Length = 0 then
return;
end if;
if Set.Ref.Is_Empty then
pragma Assert (Set.Bounds.Is_Empty);
Set.Ref := S.Ref;
Set.Bounds.Insert (S.Bounds);
return;
end if;
if Set.Ref /= S.Ref then
raise Constraint_Error with
"Addition of an unrelated slice to a slice set";
end if;
Include_Range (Set.Bounds, S.Bounds);
end Include_Slice;
procedure Include_Slice
(Set : in out Slice_Set;
First : in Positive;
Last : in Natural) is
begin
Include_Slice (Set, To_Range (First, Last));
end Include_Slice;
procedure Exclude_Slice
(Set : in out Slice_Set; Bounds : in String_Range) is
begin
if Bounds.Length = 0 then
return;
end if;
if Set.Ref.Is_Empty then
raise Constraint_Error with
"Cannot exclude range from null slice set";
end if;
Exclude_Range (Set.Bounds, Bounds);
end Exclude_Slice;
procedure Exclude_Slice
(Set : in out Slice_Set;
First : in Positive;
Last : in Natural) is
begin
Exclude_Slice (Set, To_Range (First, Last));
end Exclude_Slice;
procedure Restrict (Set : in out Slice_Set; Bounds : in String_Range) is
begin
if Set.Ref.Is_Empty then
raise Constraint_Error with "Cannot restrict null slice set";
end if;
if Bounds.Length = 0 then
Set.Bounds.Clear;
else
declare
Set_First : constant Positive := Set.First;
Set_Last : constant Natural := Set.Last;
begin
if Set_First < Bounds.First then
Exclude_Range
(Set.Bounds,
To_Range (Set_First, Bounds.First - 1));
end if;
if Set_Last > Last (Bounds) then
Exclude_Range
(Set.Bounds,
To_Range (Last (Bounds) + 1, Set_Last));
end if;
end;
end if;
end Restrict;
procedure Restrict
(Set : in out Slice_Set;
First : in Positive;
Last : in Natural) is
begin
Restrict (Set, To_Range (First, Last));
end Restrict;
function Subset (Set : Slice_Set; Bounds : String_Range) return Slice_Set is
Result : Slice_Set;
Cursor : Range_Sets.Cursor;
R : String_Range;
begin
if Set.Ref.Is_Empty then
raise Constraint_Error with "Subset of null slice set";
end if;
Result.Ref := Set.Ref;
if Bounds.Length = 0 or else Set.Bounds.Is_Empty then
return Result;
end if;
Cursor := Set.Bounds.Floor (Bounds);
if Range_Sets.Has_Element (Cursor) then
R := Range_Sets.Element (Cursor);
if R.First < Bounds.First and then Is_In (Bounds.First, R) then
Set_First (R, Bounds.First);
if Is_In (Last (Bounds), R) then
Set_Last (R, Last (Bounds));
end if;
Result.Bounds.Insert (R);
Range_Sets.Next (Cursor);
end if;
else
Cursor := Set.Bounds.First;
end if;
while Range_Sets.Has_Element (Cursor) loop
R := Range_Sets.Element (Cursor);
if Is_Subrange (R, Bounds) then
Result.Bounds.Insert (R);
else
if Is_In (Last (Bounds), R) then
Set_Last (R, Last (Bounds));
Result.Bounds.Insert (R);
end if;
exit;
end if;
Range_Sets.Next (Cursor);
end loop;
return Result;
end Subset;
function Subset (Set : Slice_Set; First : Positive; Last : Natural)
return Slice_Set is
begin
return Subset (Set, To_Range (First, Last));
end Subset;
procedure Cut_Before (Set : in out Slice_Set; Index : in Positive) is
Cursor : Range_Sets.Cursor;
Lower, Upper : String_Range;
begin
if Set.Ref.Is_Empty or else Set.Bounds.Is_Empty then
raise Constraint_Error;
end if;
Cursor := Set.Bounds.Floor ((Index, 0));
if not Range_Sets.Has_Element (Cursor) then
raise Constraint_Error;
end if;
Lower := Range_Sets.Element (Cursor);
if not Is_In (Index, Lower) then
raise Constraint_Error;
end if;
if Lower.First = Index then
return; -- nothing to do
end if;
Upper := Lower;
Set_Last (Lower, Index - 1);
Set_First (Upper, Index);
Set.Bounds.Delete (Cursor);
Set.Bounds.Insert (Lower);
Set.Bounds.Insert (Upper);
end Cut_Before;
---------------
-- Iterators --
---------------
procedure Trim_Slices
(Set : in out Slice_Set;
Trim : not null access function (Slice : String) return String_Range)
is
Cursor : Range_Sets.Cursor := Set.Bounds.First;
Old_Range, New_Range : String_Range;
begin
while Range_Sets.Has_Element (Cursor) loop
Old_Range := Range_Sets.Element (Cursor);
New_Range := Trim.all
(Set.Ref.Query.Data.all (Old_Range.First .. Last (Old_Range)));
if New_Range.Length = 0 then
declare
Next : constant Range_Sets.Cursor := Range_Sets.Next (Cursor);
begin
Set.Bounds.Delete (Cursor);
Cursor := Next;
end;
else
if not Is_Subrange (New_Range, Old_Range) then
raise Constraint_Error with "Trim not returning a subrange";
end if;
Set.Bounds.Replace_Element (Cursor, New_Range);
Range_Sets.Next (Cursor);
end if;
end loop;
end Trim_Slices;
procedure Query_Slices
(Set : in Slice_Set;
Process : not null access procedure (S : in Slice))
is
Cursor : Range_Sets.Cursor := Set.Bounds.First;
begin
while Range_Sets.Has_Element (Cursor) loop
Process.all (Slice'(Range_Sets.Element (Cursor), Set.Ref));
Range_Sets.Next (Cursor);
end loop;
end Query_Slices;
----------------------
-- Search functions --
----------------------
function Find_Slice
(Set : Slice_Set;
From : Positive;
Test : not null access function (Slice : String) return Boolean;
Going : Ada.Strings.Direction := Ada.Strings.Forward)
return String_Range
is
Cursor : Range_Sets.Cursor;
Update : access procedure (C : in out Range_Sets.Cursor);
R : String_Range;
begin
if Set.Ref.Is_Empty then
raise Constraint_Error with "Find_Slice on null slice set";
end if;
case Going is
when Ada.Strings.Forward => Update := Range_Sets.Next'Access;
when Ada.Strings.Backward => Update := Range_Sets.Previous'Access;
end case;
Cursor := Set.Bounds.Floor ((From, 0));
while Range_Sets.Has_Element (Cursor) loop
R := Range_Sets.Element (Cursor);
if Test.all (Set.Ref.Query.Data.all (R.First .. Last (R))) then
return R;
end if;
Update.all (Cursor);
end loop;
return (1, 0);
end Find_Slice;
function Find_Slice
(Set : Slice_Set;
Test : not null access function (Slice : String) return Boolean;
Going : Ada.Strings.Direction := Ada.Strings.Forward)
return String_Range is
begin
case Going is
when Ada.Strings.Forward =>
return Find_Slice (Set, Set.First, Test, Going);
when Ada.Strings.Backward =>
return Find_Slice (Set, Set.Last, Test, Going);
end case;
end Find_Slice;
function Index
(Source : Slice_Set;
Set : Ada.Strings.Maps.Character_Set;
From : Positive;
Test : Ada.Strings.Membership := Ada.Strings.Inside;
Going : Ada.Strings.Direction := Ada.Strings.Forward)
return Natural
is
Cursor : Range_Sets.Cursor;
Update : access procedure (C : in out Range_Sets.Cursor);
R : String_Range;
Result : Natural := 0;
begin
case Going is
when Ada.Strings.Forward => Update := Range_Sets.Next'Access;
when Ada.Strings.Backward => Update := Range_Sets.Previous'Access;
end case;
Cursor := Source.Bounds.Floor ((From, 0));
if not Range_Sets.Has_Element (Cursor) then
raise Ada.Strings.Index_Error;
end if;
R := Range_Sets.Element (Cursor);
if Is_In (From, R) then
Result := Fixed.Index
(Source.Ref.Query.Data.all (R.First .. Last (R)),
Set,
From,
Test,
Going);
end if;
while Result = 0 loop
Update.all (Cursor);
if not Range_Sets.Has_Element (Cursor) then
return 0;
end if;
R := Range_Sets.Element (Cursor);
Result := Fixed.Index
(Source.Ref.Query.Data.all (R.First .. Last (R)),
Set,
Test,
Going);
end loop;
return Result;
end Index;
function Index
(Source : Slice_Set;
Set : Ada.Strings.Maps.Character_Set;
Test : Ada.Strings.Membership := Ada.Strings.Inside;
Going : Ada.Strings.Direction := Ada.Strings.Forward)
return Natural is
begin
case Going is
when Ada.Strings.Forward =>
return Index (Source, Set, Source.First, Test, Going);
when Ada.Strings.Backward =>
return Index (Source, Set, Source.Last, Test, Going);
end case;
end Index;
end Natools.String_Slices.Slice_Sets;