aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPierre-Alexandre Bazin <bazin@adacore.com>2021-06-18 12:09:48 +0200
committerPierre-Marie de Rodat <derodat@adacore.com>2021-09-20 12:31:34 +0000
commit6c5ca4cf42f913a44c85071f2ba90e3f8048fa9e (patch)
tree53156b72c7516db8b164ead4e4f81e0f711e1ed0
parent8582e5d07eabd78d7f73f3717993fe7b55dc7fc4 (diff)
downloadgcc-6c5ca4cf42f913a44c85071f2ba90e3f8048fa9e.zip
gcc-6c5ca4cf42f913a44c85071f2ba90e3f8048fa9e.tar.gz
gcc-6c5ca4cf42f913a44c85071f2ba90e3f8048fa9e.tar.bz2
[Ada] SPARK proof of the Ada.Strings.Fixed library
gcc/ada/ * libgnat/a-strfix.adb ("*"): Added loop invariants and lemmas for proof. (Delete): Added assertions for proof, and conditions to avoid overflow. (Head): Added loop invariant. (Insert): Same as Delete. (Move): Declared with SPARK_Mode Off. (Overwrite): Added assertions for proof, and conditions to avoid overflow. (Replace_Slice): Added assertions for proof, and conditions to avoid overflow. (Tail): Added loop invariant and avoided overflows. (Translate): Added loop invariants. (Trim): Ensured empty strings returned start at 1. * libgnat/a-strfix.ads (Index): Rewrote contract cases for easier proof. (Index_Non_Blank): Separated the null string case. (Count): Specified Mapping shouldn't be null. (Find_Token): Specified Source'First should be Positive when no From is given. (Translate): Specified Mapping shouldn't be null. ("*"): Rewrote postcondition for easier proof. * libgnat/a-strsea.adb (Belongs): Added postcondition. (Count): Rewrote loops and added loop invariants to avoid overflows. (Find_Token): Added loop invariants. (Index): Rewrote loops to avoid overflows and added loop invariants for proof. (Index_Non_Blank): Added loop invariants. (Is_Identity): New function isolated without SPARK_Mode. * libgnat/a-strsea.ads: Fix starting comment as package is no longer private. (Match): Declared ghost expression function Match. (Is_Identity): Described identity in the postcondition. (Index, Index_Non_Blank, Count, Find_Token): Added contract from a-strfix.ads.
-rw-r--r--gcc/ada/libgnat/a-strfix.adb280
-rw-r--r--gcc/ada/libgnat/a-strfix.ads376
-rw-r--r--gcc/ada/libgnat/a-strsea.adb319
-rw-r--r--gcc/ada/libgnat/a-strsea.ads540
4 files changed, 1166 insertions, 349 deletions
diff --git a/gcc/ada/libgnat/a-strfix.adb b/gcc/ada/libgnat/a-strfix.adb
index ee72b6b..00967c4 100644
--- a/gcc/ada/libgnat/a-strfix.adb
+++ b/gcc/ada/libgnat/a-strfix.adb
@@ -38,10 +38,17 @@
-- bounds of function return results were also fixed, and use of & removed for
-- efficiency reasons.
+-- Ghost code, loop invariants and assertions in this unit are meant for
+-- analysis only, not for run-time checking, as it would be too costly
+-- otherwise. This is enforced by setting the assertion policy to Ignore.
+
+pragma Assertion_Policy (Ghost => Ignore,
+ Loop_Invariant => Ignore,
+ Assert => Ignore);
+
with Ada.Strings.Maps; use Ada.Strings.Maps;
-with Ada.Strings.Search;
-package body Ada.Strings.Fixed is
+package body Ada.Strings.Fixed with SPARK_Mode is
------------------------
-- Search Subprograms --
@@ -146,9 +153,12 @@ package body Ada.Strings.Fixed is
Right : Character) return String
is
begin
- return Result : String (1 .. Left) do
+ return Result : String (1 .. Left) with Relaxed_Initialization do
for J in Result'Range loop
Result (J) := Right;
+ pragma Loop_Invariant
+ (for all K in 1 .. J =>
+ Result (K)'Initialized and then Result (K) = Right);
end loop;
end return;
end "*";
@@ -157,12 +167,82 @@ package body Ada.Strings.Fixed is
(Left : Natural;
Right : String) return String
is
- Ptr : Integer := 1;
+ Ptr : Integer := 0;
+
+ -- Parts of the proof involving manipulations with the modulo operator
+ -- are complicated for the prover and can't be done automatically in
+ -- the global subprogram. That's why we isolate them in these two ghost
+ -- lemmas.
+
+ procedure Lemma_Mod (K : Integer) with
+ Ghost,
+ Pre =>
+ Right'Length /= 0
+ and then Ptr mod Right'Length = 0
+ and then Ptr in 0 .. Natural'Last - Right'Length
+ and then K in Ptr .. Ptr + Right'Length - 1,
+ Post => K mod Right'Length = K - Ptr;
+ -- Lemma_Mod is applied to an index considered in Lemma_Split to prove
+ -- that it has the right value modulo Right'Length.
+
+ procedure Lemma_Split (Result : String) with
+ Ghost,
+ Relaxed_Initialization => Result,
+ Pre =>
+ Right'Length /= 0
+ and then Result'First = 1
+ and then Result'Last >= 0
+ and then Ptr mod Right'Length = 0
+ and then Ptr in 0 .. Result'Last - Right'Length
+ and then Result (Result'First .. Ptr + Right'Length)'Initialized
+ and then Result (Ptr + 1 .. Ptr + Right'Length) = Right,
+ Post =>
+ (for all K in Ptr + 1 .. Ptr + Right'Length =>
+ Result (K) = Right (Right'First + (K - 1) mod Right'Length));
+ -- Lemma_Split is used after Result (Ptr + 1 .. Ptr + Right'Length) is
+ -- updated to Right and concludes that the characters match for each
+ -- index when taken modulo Right'Length, as the considered slice starts
+ -- at index 1 modulo Right'Length.
+
+ ---------------
+ -- Lemma_Mod --
+ ---------------
+
+ procedure Lemma_Mod (K : Integer) is null;
+
+ -----------------
+ -- Lemma_Split --
+ -----------------
+
+ procedure Lemma_Split (Result : String) is
+ begin
+ for K in Ptr + 1 .. Ptr + Right'Length loop
+ Lemma_Mod (K - 1);
+ pragma Loop_Invariant
+ (for all J in Ptr + 1 .. K =>
+ Result (J) = Right (Right'First + (J - 1) mod Right'Length));
+ end loop;
+ end Lemma_Split;
+
+ -- Start of processing for "*"
+
begin
- return Result : String (1 .. Left * Right'Length) do
+ if Right'Length = 0 then
+ return "";
+ end if;
+
+ return Result : String (1 .. Left * Right'Length)
+ with Relaxed_Initialization
+ do
for J in 1 .. Left loop
- Result (Ptr .. Ptr + Right'Length - 1) := Right;
+ Result (Ptr + 1 .. Ptr + Right'Length) := Right;
+ Lemma_Split (Result);
Ptr := Ptr + Right'Length;
+ pragma Loop_Invariant (Ptr = J * Right'Length);
+ pragma Loop_Invariant (Result (1 .. Ptr)'Initialized);
+ pragma Loop_Invariant
+ (for all K in 1 .. Ptr =>
+ Result (K) = Right (Right'First + (K - 1) mod Right'Length));
end loop;
end return;
end "*";
@@ -176,7 +256,6 @@ package body Ada.Strings.Fixed is
From : Positive;
Through : Natural) return String
is
- Front : Integer;
begin
if From > Through then
declare
@@ -204,13 +283,22 @@ package body Ada.Strings.Fixed is
end if;
else
- Front := From - Source'First;
- return Result : String (1 .. Source'Length - (Through - From + 1)) do
- Result (1 .. Front) :=
- Source (Source'First .. From - 1);
- Result (Front + 1 .. Result'Last) :=
- Source (Through + 1 .. Source'Last);
- end return;
+ declare
+ Front : constant Integer := From - Source'First;
+
+ begin
+ return Result : String (1 .. Source'Length - (Through - From + 1))
+ with Relaxed_Initialization
+ do
+ Result (1 .. Front) :=
+ Source (Source'First .. From - 1);
+
+ if Through < Source'Last then
+ Result (Front + 1 .. Result'Last) :=
+ Source (Through + 1 .. Source'Last);
+ end if;
+ end return;
+ end;
end if;
end Delete;
@@ -219,8 +307,7 @@ package body Ada.Strings.Fixed is
From : Positive;
Through : Natural;
Justify : Alignment := Left;
- Pad : Character := Space)
- is
+ Pad : Character := Space) with SPARK_Mode => Off is
begin
Move (Source => Delete (Source, From, Through),
Target => Source,
@@ -240,16 +327,19 @@ package body Ada.Strings.Fixed is
subtype Result_Type is String (1 .. Count);
begin
- if Count < Source'Length then
+ if Count <= Source'Length then
return
- Result_Type (Source (Source'First .. Source'First + Count - 1));
+ Result_Type (Source (Source'First .. Source'First + (Count - 1)));
else
- return Result : Result_Type do
+ return Result : Result_Type with Relaxed_Initialization do
Result (1 .. Source'Length) := Source;
for J in Source'Length + 1 .. Count loop
Result (J) := Pad;
+ pragma Loop_Invariant
+ (for all K in Source'Length + 1 .. J =>
+ Result (K)'Initialized and then Result (K) = Pad);
end loop;
end return;
end if;
@@ -281,17 +371,31 @@ package body Ada.Strings.Fixed is
Front : constant Integer := Before - Source'First;
begin
- if Before not in Source'First .. Source'Last + 1 then
+ if Before - 1 not in Source'First - 1 .. Source'Last then
raise Index_Error;
end if;
- return Result : String (1 .. Source'Length + New_Item'Length) do
+ return Result : String (1 .. Source'Length + New_Item'Length)
+ with Relaxed_Initialization
+ do
Result (1 .. Front) :=
Source (Source'First .. Before - 1);
Result (Front + 1 .. Front + New_Item'Length) :=
New_Item;
- Result (Front + New_Item'Length + 1 .. Result'Last) :=
- Source (Before .. Source'Last);
+ pragma Assert
+ (Result
+ (Before - Source'First + 1
+ .. Before - Source'First + New_Item'Length)
+ = New_Item);
+
+ if Before <= Source'Last then
+ Result (Front + New_Item'Length + 1 .. Result'Last) :=
+ Source (Before .. Source'Last);
+ end if;
+
+ pragma Assert
+ (Result (1 .. Before - Source'First)
+ = Source (Source'First .. Before - 1));
end return;
end Insert;
@@ -299,8 +403,7 @@ package body Ada.Strings.Fixed is
(Source : in out String;
Before : Positive;
New_Item : String;
- Drop : Truncation := Error)
- is
+ Drop : Truncation := Error) with SPARK_Mode => Off is
begin
Move (Source => Insert (Source, Before, New_Item),
Target => Source,
@@ -316,7 +419,7 @@ package body Ada.Strings.Fixed is
Target : out String;
Drop : Truncation := Error;
Justify : Alignment := Left;
- Pad : Character := Space)
+ Pad : Character := Space) with SPARK_Mode => Off
is
Sfirst : constant Integer := Source'First;
Slast : constant Integer := Source'Last;
@@ -423,7 +526,7 @@ package body Ada.Strings.Fixed is
Position : Positive;
New_Item : String) return String is
begin
- if Position not in Source'First .. Source'Last + 1 then
+ if Position - 1 not in Source'First - 1 .. Source'Last then
raise Index_Error;
end if;
@@ -434,11 +537,32 @@ package body Ada.Strings.Fixed is
Front : constant Integer := Position - Source'First;
begin
- return Result : String (1 .. Result_Length) do
+ return Result : String (1 .. Result_Length)
+ with Relaxed_Initialization
+ do
Result (1 .. Front) := Source (Source'First .. Position - 1);
+ pragma Assert
+ (Result (1 .. Position - Source'First)
+ = Source (Source'First .. Position - 1));
Result (Front + 1 .. Front + New_Item'Length) := New_Item;
- Result (Front + New_Item'Length + 1 .. Result'Length) :=
- Source (Position + New_Item'Length .. Source'Last);
+ pragma Assert
+ (Result
+ (Position - Source'First + 1
+ .. Position - Source'First + New_Item'Length)
+ = New_Item);
+
+ if Position <= Source'Last - New_Item'Length then
+ Result (Front + New_Item'Length + 1 .. Result'Last) :=
+ Source (Position + New_Item'Length .. Source'Last);
+ end if;
+
+ pragma Assert
+ (if Position <= Source'Last - New_Item'Length
+ then
+ Result
+ (Position - Source'First + New_Item'Length + 1
+ .. Result'Last)
+ = Source (Position + New_Item'Length .. Source'Last));
end return;
end;
end Overwrite;
@@ -447,8 +571,7 @@ package body Ada.Strings.Fixed is
(Source : in out String;
Position : Positive;
New_Item : String;
- Drop : Truncation := Right)
- is
+ Drop : Truncation := Right) with SPARK_Mode => Off is
begin
Move (Source => Overwrite (Source, Position, New_Item),
Target => Source,
@@ -463,10 +586,9 @@ package body Ada.Strings.Fixed is
(Source : String;
Low : Positive;
High : Natural;
- By : String) return String
- is
+ By : String) return String is
begin
- if Low > Source'Last + 1 or else High < Source'First - 1 then
+ if Low - 1 > Source'Last or else High < Source'First - 1 then
raise Index_Error;
end if;
@@ -484,11 +606,34 @@ package body Ada.Strings.Fixed is
-- Length of result
begin
- return Result : String (1 .. Result_Length) do
+ return Result : String (1 .. Result_Length)
+ with Relaxed_Initialization do
Result (1 .. Front_Len) := Source (Source'First .. Low - 1);
+ pragma Assert
+ (Result (1 .. Integer'Max (0, Low - Source'First))
+ = Source (Source'First .. Low - 1));
Result (Front_Len + 1 .. Front_Len + By'Length) := By;
- Result (Front_Len + By'Length + 1 .. Result'Length) :=
- Source (High + 1 .. Source'Last);
+
+ if High < Source'Last then
+ Result (Front_Len + By'Length + 1 .. Result'Last) :=
+ Source (High + 1 .. Source'Last);
+ end if;
+
+ pragma Assert
+ (Result (1 .. Integer'Max (0, Low - Source'First))
+ = Source (Source'First .. Low - 1));
+ pragma Assert
+ (Result
+ (Integer'Max (0, Low - Source'First) + 1
+ .. Integer'Max (0, Low - Source'First) + By'Length)
+ = By);
+ pragma Assert
+ (if High < Source'Last
+ then
+ Result
+ (Integer'Max (0, Low - Source'First) + By'Length + 1
+ .. Result'Last)
+ = Source (High + 1 .. Source'Last));
end return;
end;
else
@@ -503,8 +648,7 @@ package body Ada.Strings.Fixed is
By : String;
Drop : Truncation := Error;
Justify : Alignment := Left;
- Pad : Character := Space)
- is
+ Pad : Character := Space) with SPARK_Mode => Off is
begin
Move (Replace_Slice (Source, Low, High, By), Source, Drop, Justify, Pad);
end Replace_Slice;
@@ -521,18 +665,26 @@ package body Ada.Strings.Fixed is
subtype Result_Type is String (1 .. Count);
begin
- if Count < Source'Length then
+ if Count = 0 then
+ return "";
+
+ elsif Count < Source'Length then
return Result_Type (Source (Source'Last - Count + 1 .. Source'Last));
-- Pad on left
else
- return Result : Result_Type do
+ return Result : Result_Type with Relaxed_Initialization do
for J in 1 .. Count - Source'Length loop
Result (J) := Pad;
+ pragma Loop_Invariant
+ (for all K in 1 .. J =>
+ Result (K)'Initialized and then Result (K) = Pad);
end loop;
- Result (Count - Source'Length + 1 .. Count) := Source;
+ if Source'Length /= 0 then
+ Result (Count - Source'Length + 1 .. Count) := Source;
+ end if;
end return;
end if;
end Tail;
@@ -560,9 +712,18 @@ package body Ada.Strings.Fixed is
Mapping : Maps.Character_Mapping) return String
is
begin
- return Result : String (1 .. Source'Length) do
+ return Result : String (1 .. Source'Length)
+ with Relaxed_Initialization
+ do
for J in Source'Range loop
Result (J - (Source'First - 1)) := Value (Mapping, Source (J));
+ pragma Loop_Invariant
+ (for all K in Source'First .. J =>
+ Result (K - (Source'First - 1))'Initialized);
+ pragma Loop_Invariant
+ (for all K in Source'First .. J =>
+ Result (K - (Source'First - 1)) =
+ Value (Mapping, Source (K)));
end loop;
end return;
end Translate;
@@ -574,6 +735,9 @@ package body Ada.Strings.Fixed is
begin
for J in Source'Range loop
Source (J) := Value (Mapping, Source (J));
+ pragma Loop_Invariant
+ (for all K in Source'First .. J =>
+ Source (K) = Value (Mapping, Source'Loop_Entry (K)));
end loop;
end Translate;
@@ -583,9 +747,17 @@ package body Ada.Strings.Fixed is
is
pragma Unsuppress (Access_Check);
begin
- return Result : String (1 .. Source'Length) do
+ return Result : String (1 .. Source'Length)
+ with Relaxed_Initialization
+ do
for J in Source'Range loop
Result (J - (Source'First - 1)) := Mapping.all (Source (J));
+ pragma Loop_Invariant
+ (for all K in Source'First .. J =>
+ Result (K - (Source'First - 1))'Initialized);
+ pragma Loop_Invariant
+ (for all K in Source'First .. J =>
+ Result (K - (Source'First - 1)) = Mapping (Source (K)));
end loop;
end return;
end Translate;
@@ -598,6 +770,9 @@ package body Ada.Strings.Fixed is
begin
for J in Source'Range loop
Source (J) := Mapping.all (Source (J));
+ pragma Loop_Invariant
+ (for all K in Source'First .. J =>
+ Source (K) = Mapping (Source'Loop_Entry (K)));
end loop;
end Translate;
@@ -609,6 +784,9 @@ package body Ada.Strings.Fixed is
(Source : String;
Side : Trim_End) return String
is
+ Empty_String : constant String (1 .. 0) := "";
+ -- Without declaring the empty string as a separate string starting
+ -- at 1, SPARK provers have trouble proving the postcondition.
begin
case Side is
when Strings.Left =>
@@ -618,7 +796,7 @@ package body Ada.Strings.Fixed is
-- All blanks case
if Low = 0 then
- return "";
+ return Empty_String;
end if;
declare
@@ -635,7 +813,7 @@ package body Ada.Strings.Fixed is
-- All blanks case
if High = 0 then
- return "";
+ return Empty_String;
end if;
declare
@@ -652,7 +830,7 @@ package body Ada.Strings.Fixed is
-- All blanks case
if Low = 0 then
- return "";
+ return Empty_String;
end if;
declare
@@ -695,8 +873,7 @@ package body Ada.Strings.Fixed is
return "";
end if;
- High :=
- Index (Source, Set => Right, Test => Outside, Going => Backward);
+ High := Index (Source, Set => Right, Test => Outside, Going => Backward);
-- Case where source comprises only characters in Right
@@ -705,7 +882,8 @@ package body Ada.Strings.Fixed is
end if;
declare
- subtype Result_Type is String (1 .. High - Low + 1);
+ Result_Length : constant Integer := High - Low + 1;
+ subtype Result_Type is String (1 .. Result_Length);
begin
return Result_Type (Source (Low .. High));
diff --git a/gcc/ada/libgnat/a-strfix.ads b/gcc/ada/libgnat/a-strfix.ads
index 4214157..1a5ee94 100644
--- a/gcc/ada/libgnat/a-strfix.ads
+++ b/gcc/ada/libgnat/a-strfix.ads
@@ -13,14 +13,6 @@
-- --
------------------------------------------------------------------------------
--- Preconditions in this unit are meant for analysis only, not for run-time
--- checking, so that the expected exceptions are raised. This is enforced by
--- setting the corresponding assertion policy to Ignore.
-
-pragma Assertion_Policy (Pre => Ignore);
-
-with Ada.Strings.Maps;
-
-- The language-defined package Strings.Fixed provides string-handling
-- subprograms for fixed-length strings; that is, for values of type
-- Standard.String. Several of these subprograms are procedures that modify
@@ -40,6 +32,20 @@ with Ada.Strings.Maps;
-- these effects. Similar control is provided by the string transformation
-- procedures.
+-- Preconditions in this unit are meant for analysis only, not for run-time
+-- checking, so that the expected exceptions are raised. This is enforced by
+-- setting the corresponding assertion policy to Ignore. Postconditions and
+-- contract cases should not be executed at runtime as well, in order not to
+-- slow down the execution of these functions.
+
+pragma Assertion_Policy (Pre => Ignore,
+ Post => Ignore,
+ Contract_Cases => Ignore,
+ Ghost => Ignore);
+
+with Ada.Strings.Maps; use type Ada.Strings.Maps.Character_Mapping_Function;
+with Ada.Strings.Search;
+
package Ada.Strings.Fixed with SPARK_Mode is
pragma Preelaborate;
@@ -108,56 +114,60 @@ package Ada.Strings.Fixed with SPARK_Mode is
Going : Direction := Forward;
Mapping : Maps.Character_Mapping_Function) return Natural
with
- Pre =>
- Pattern'Length /= 0
- and then (if Source'Length /= 0 then From in Source'Range),
+ Pre => Pattern'Length > 0
+ and then Mapping /= null
+ and then (if Source'Length > 0 then From in Source'Range),
Post => Index'Result in 0 | Source'Range,
Contract_Cases =>
- -- If no slice in the considered range of Source matches Pattern,
- -- then 0 is returned.
+ -- If Source is the empty string, then 0 is returned
- ((for all J in Source'Range =>
- (if (if Going = Forward
- then J in From .. Source'Last - Pattern'Length + 1
- else J <= From - Pattern'Length + 1)
- then Translate (Source (J .. J - 1 + Pattern'Length), Mapping)
- /= Pattern))
+ (Source'Length = 0
=>
Index'Result = 0,
- -- Otherwise, a valid index is returned
+ -- If some slice of Source matches Pattern, then a valid index is
+ -- returned.
- others
+ Source'Length > 0
+ and then
+ (for some J in
+ (if Going = Forward then From else Source'First)
+ .. (if Going = Forward then Source'Last else From)
+ - (Pattern'Length - 1) =>
+ Ada.Strings.Search.Match (Source, Pattern, Mapping, J))
=>
-
-- The result is in the considered range of Source
- (if Going = Forward
- then Index'Result in From .. Source'Last - Pattern'Length + 1
- else Index'Result in Source'First .. From - Pattern'Length + 1)
+ Index'Result in
+ (if Going = Forward then From else Source'First)
+ .. (if Going = Forward then Source'Last else From)
+ - (Pattern'Length - 1)
-- The slice beginning at the returned index matches Pattern
and then
- Translate (Source (Index'Result
- .. Index'Result - 1 + Pattern'Length),
- Mapping)
- = Pattern
+ Ada.Strings.Search.Match (Source, Pattern, Mapping, Index'Result)
- -- The result is the smallest or largest index which satisfies the
- -- matching, respectively when Going = Forward and
- -- Going = Backwards.
+ -- The result is the smallest or largest index which satisfies
+ -- the matching, respectively when Going = Forward and Going =
+ -- Backward.
and then
(for all J in Source'Range =>
(if (if Going = Forward
then J in From .. Index'Result - 1
- else J - 1 in Index'Result .. From - Pattern'Length)
- then Translate (Source (J .. J - 1 + Pattern'Length),
- Mapping)
- /= Pattern))),
+ else J - 1 in Index'Result
+ .. From - Pattern'Length)
+ then not (Ada.Strings.Search.Match
+ (Source, Pattern, Mapping, J)))),
+
+ -- Otherwise, 0 is returned
+
+ others
+ =>
+ Index'Result = 0),
Global => null;
pragma Ada_05 (Index);
@@ -168,56 +178,59 @@ package Ada.Strings.Fixed with SPARK_Mode is
Going : Direction := Forward;
Mapping : Maps.Character_Mapping := Maps.Identity) return Natural
with
- Pre =>
- Pattern'Length /= 0
- and then (if Source'Length /= 0 then From in Source'Range),
+ Pre => Pattern'Length > 0
+ and then (if Source'Length > 0 then From in Source'Range),
Post => Index'Result in 0 | Source'Range,
Contract_Cases =>
- -- If no slice in the considered range of Source matches Pattern,
- -- then 0 is returned.
+ -- If Source is the empty string, then 0 is returned
- ((for all J in Source'Range =>
- (if (if Going = Forward
- then J in From .. Source'Last - Pattern'Length + 1
- else J <= From - Pattern'Length + 1)
- then Translate (Source (J .. J - 1 + Pattern'Length), Mapping)
- /= Pattern))
+ (Source'Length = 0
=>
Index'Result = 0,
- -- Otherwise, a valid index is returned
+ -- If some slice of Source matches Pattern, then a valid index is
+ -- returned.
- others
+ Source'Length > 0
+ and then
+ (for some J in
+ (if Going = Forward then From else Source'First)
+ .. (if Going = Forward then Source'Last else From)
+ - (Pattern'Length - 1) =>
+ Ada.Strings.Search.Match (Source, Pattern, Mapping, J))
=>
-
-- The result is in the considered range of Source
- (if Going = Forward
- then Index'Result in From .. Source'Last - Pattern'Length + 1
- else Index'Result in Source'First .. From - Pattern'Length + 1)
+ Index'Result in
+ (if Going = Forward then From else Source'First)
+ .. (if Going = Forward then Source'Last else From)
+ - (Pattern'Length - 1)
- -- The slice beginning at the returned index matches Pattern
+ -- The slice beginning at the returned index matches Pattern
- and then
- Translate (Source (Index'Result
- .. Index'Result - 1 + Pattern'Length),
- Mapping)
- = Pattern
+ and then
+ Ada.Strings.Search.Match (Source, Pattern, Mapping, Index'Result)
-- The result is the smallest or largest index which satisfies the
-- matching, respectively when Going = Forward and
- -- Going = Backwards.
+ -- Going = Backward.
and then
(for all J in Source'Range =>
(if (if Going = Forward
then J in From .. Index'Result - 1
- else J - 1 in Index'Result .. From - Pattern'Length)
- then Translate (Source (J .. J - 1 + Pattern'Length),
- Mapping)
- /= Pattern))),
+ else J - 1 in Index'Result
+ .. From - Pattern'Length)
+ then not (Ada.Strings.Search.Match
+ (Source, Pattern, Mapping, J)))),
+
+ -- Otherwise, 0 is returned
+
+ others
+ =>
+ Index'Result = 0),
Global => null;
pragma Ada_05 (Index);
@@ -245,37 +258,33 @@ package Ada.Strings.Fixed with SPARK_Mode is
Post => Index'Result in 0 | Source'Range,
Contract_Cases =>
- -- If Source is empty, or if no slice of Source matches Pattern, then
- -- 0 is returned.
+ -- If Source is the empty string, then 0 is returned
(Source'Length = 0
- or else
- (for all J in Source'First .. Source'Last - Pattern'Length + 1 =>
- Translate (Source (J .. J - 1 + Pattern'Length), Mapping)
- /= Pattern)
=>
Index'Result = 0,
- -- Otherwise, a valid index is returned
+ -- If some slice of Source matches Pattern, then a valid index is
+ -- returned.
- others
+ Source'Length > 0
+ and then
+ (for some J in
+ Source'First .. Source'Last - (Pattern'Length - 1) =>
+ Ada.Strings.Search.Match (Source, Pattern, Mapping, J))
=>
-
-- The result is in the considered range of Source
- Index'Result in Source'First .. Source'Last - Pattern'Length + 1
+ Index'Result in Source'First .. Source'Last - (Pattern'Length - 1)
-- The slice beginning at the returned index matches Pattern
and then
- Translate (Source (Index'Result
- .. Index'Result - 1 + Pattern'Length),
- Mapping)
- = Pattern
+ Ada.Strings.Search.Match (Source, Pattern, Mapping, Index'Result)
- -- The result is the smallest or largest index which satisfies the
- -- matching, respectively when Going = Forward and
- -- Going = Backwards.
+ -- The result is the smallest or largest index which satisfies
+ -- the matching, respectively when Going = Forward and Going =
+ -- Backward.
and then
(for all J in Source'Range =>
@@ -283,9 +292,14 @@ package Ada.Strings.Fixed with SPARK_Mode is
then J <= Index'Result - 1
else J - 1 in Index'Result
.. Source'Last - Pattern'Length)
- then Translate (Source (J .. J - 1 + Pattern'Length),
- Mapping)
- /= Pattern))),
+ then not (Ada.Strings.Search.Match
+ (Source, Pattern, Mapping, J)))),
+
+ -- Otherwise, 0 is returned
+
+ others
+ =>
+ Index'Result = 0),
Global => null;
function Index
@@ -294,42 +308,38 @@ package Ada.Strings.Fixed with SPARK_Mode is
Going : Direction := Forward;
Mapping : Maps.Character_Mapping_Function) return Natural
with
- Pre => Pattern'Length > 0,
+ Pre => Pattern'Length > 0 and then Mapping /= null,
Post => Index'Result in 0 | Source'Range,
Contract_Cases =>
- -- If Source is empty, or if no slice of Source matches Pattern, then
- -- 0 is returned.
+ -- If Source is the empty string, then 0 is returned
(Source'Length = 0
- or else
- (for all J in Source'First .. Source'Last - Pattern'Length + 1 =>
- Translate (Source (J .. J - 1 + Pattern'Length), Mapping)
- /= Pattern)
=>
Index'Result = 0,
- -- Otherwise, a valid index is returned
+ -- If some slice of Source matches Pattern, then a valid index is
+ -- returned.
- others
+ Source'Length > 0
+ and then
+ (for some J in
+ Source'First .. Source'Last - (Pattern'Length - 1) =>
+ Ada.Strings.Search.Match (Source, Pattern, Mapping, J))
=>
-
-- The result is in the considered range of Source
- Index'Result in Source'First .. Source'Last - Pattern'Length + 1
+ Index'Result in Source'First .. Source'Last - (Pattern'Length - 1)
- -- The slice beginning at the returned index matches Pattern
+ -- The slice beginning at the returned index matches Pattern
- and then
- Translate (Source (Index'Result
- .. Index'Result - 1 + Pattern'Length),
- Mapping)
- = Pattern
+ and then
+ Ada.Strings.Search.Match (Source, Pattern, Mapping, Index'Result)
- -- The result is the smallest or largest index which satisfies the
- -- matching, respectively when Going = Forward and
- -- Going = Backwards.
+ -- The result is the smallest or largest index which satisfies
+ -- the matching, respectively when Going = Forward and Going =
+ -- Backward.
and then
(for all J in Source'Range =>
@@ -337,9 +347,14 @@ package Ada.Strings.Fixed with SPARK_Mode is
then J <= Index'Result - 1
else J - 1 in Index'Result
.. Source'Last - Pattern'Length)
- then Translate (Source (J .. J - 1 + Pattern'Length),
- Mapping)
- /= Pattern))),
+ then not (Ada.Strings.Search.Match
+ (Source, Pattern, Mapping, J)))),
+
+ -- Otherwise, 0 is returned
+
+ others
+ =>
+ Index'Result = 0),
Global => null;
-- If Going = Forward, returns:
@@ -383,9 +398,9 @@ package Ada.Strings.Fixed with SPARK_Mode is
(Test = Inside)
= Ada.Strings.Maps.Is_In (Source (Index'Result), Set)
- -- The result is the smallest or largest index which satisfies the
- -- property, respectively when Going = Forward and
- -- Going = Backwards.
+ -- The result is the smallest or largest index which satisfies
+ -- the property, respectively when Going = Forward and Going =
+ -- Backward.
and then
(for all J in Source'Range =>
@@ -402,22 +417,23 @@ package Ada.Strings.Fixed with SPARK_Mode is
Test : Membership := Inside;
Going : Direction := Forward) return Natural
with
- Pre => (if Source'Length /= 0 then From in Source'Range),
-
+ Pre => (if Source'Length > 0 then From in Source'Range),
Post => Index'Result in 0 | Source'Range,
Contract_Cases =>
- -- If no character in the considered slice of Source satisfies the
- -- property Test on Set, then 0 is returned.
+ -- If Source is the empty string, or no character of the considered
+ -- slice of Source satisfies the property Test on Set, then 0 is
+ -- returned.
- ((for all I in Source'Range =>
- (if I = From
- or else (I > From) = (Going = Forward)
- then (Test = Inside) /= Ada.Strings.Maps.Is_In (Source (I), Set)))
+ (Source'Length = 0
+ or else
+ (for all J in Source'Range =>
+ (if J = From or else (J > From) = (Going = Forward) then
+ (Test = Inside) /= Ada.Strings.Maps.Is_In (Source (J), Set)))
=>
Index'Result = 0,
- -- Otherwise, an index in the range of Source is returned
+ -- Otherwise, a index in the considered range of Source is returned
others
=>
@@ -426,7 +442,8 @@ package Ada.Strings.Fixed with SPARK_Mode is
Index'Result in Source'Range
and then (Index'Result = From
- or else (Index'Result > From) = (Going = Forward))
+ or else
+ (Index'Result > From) = (Going = Forward))
-- The character at the returned index satisfies the property
-- Test on Set.
@@ -435,19 +452,18 @@ package Ada.Strings.Fixed with SPARK_Mode is
(Test = Inside)
= Ada.Strings.Maps.Is_In (Source (Index'Result), Set)
- -- The result is the smallest or largest index which satisfies the
- -- property, respectively when Going = Forward and
- -- Going = Backwards.
+ -- The result is the smallest or largest index which satisfies
+ -- the property, respectively when Going = Forward and Going =
+ -- Backward.
and then
(for all J in Source'Range =>
(if J /= Index'Result
- and then (J < Index'Result) = (Going = Forward)
- and then (J = From
- or else (J > From) = (Going = Forward))
- then
- (Test = Inside)
- /= Ada.Strings.Maps.Is_In (Source (J), Set)))),
+ and then (J < Index'Result) = (Going = Forward)
+ and then (J = From
+ or else (J > From) = (Going = Forward))
+ then (Test = Inside)
+ /= Ada.Strings.Maps.Is_In (Source (J), Set)))),
Global => null;
pragma Ada_05 (Index);
-- Index searches for the first or last occurrence of any of a set of
@@ -469,12 +485,14 @@ package Ada.Strings.Fixed with SPARK_Mode is
Post => Index_Non_Blank'Result in 0 | Source'Range,
Contract_Cases =>
- -- If all characters in the considered slice of Source are Space
- -- characters, then 0 is returned.
+ -- If Source is the empty string, or all characters in the considered
+ -- slice of Source are Space characters, then 0 is returned.
- ((for all J in Source'Range =>
- (if J = From or else (J > From) = (Going = Forward)
- then Source (J) = ' '))
+ (Source'Length = 0
+ or else
+ (for all J in Source'Range =>
+ (if J = From or else (J > From) = (Going = Forward) then
+ Source (J) = ' '))
=>
Index_Non_Blank'Result = 0,
@@ -496,7 +514,7 @@ package Ada.Strings.Fixed with SPARK_Mode is
-- The result is the smallest or largest index which is not a
-- Space character, respectively when Going = Forward and
- -- Going = Backwards.
+ -- Going = Backward.
and then
(for all J in Source'Range =>
@@ -535,8 +553,8 @@ package Ada.Strings.Fixed with SPARK_Mode is
and then Source (Index_Non_Blank'Result) /= ' '
-- The result is the smallest or largest index which is not a
- -- Space character, respectively when Going = Forward and
- -- Going = Backwards.
+ -- Space character, respectively when Going = Forward and Going
+ -- = Backward.
and then
(for all J in Source'Range =>
@@ -560,7 +578,7 @@ package Ada.Strings.Fixed with SPARK_Mode is
Pattern : String;
Mapping : Maps.Character_Mapping_Function) return Natural
with
- Pre => Pattern'Length /= 0,
+ Pre => Pattern'Length /= 0 and then Mapping /= null,
Global => null;
-- Returns the maximum number of nonoverlapping slices of Source that match
@@ -646,6 +664,7 @@ package Ada.Strings.Fixed with SPARK_Mode is
First : out Positive;
Last : out Natural)
with
+ Pre => Source'First > 0,
Contract_Cases =>
-- If Source is the empty string, or if no character of Source
@@ -701,6 +720,7 @@ package Ada.Strings.Fixed with SPARK_Mode is
(Source : String;
Mapping : Maps.Character_Mapping_Function) return String
with
+ Pre => Mapping /= null,
Post =>
-- Lower bound of the returned string is 1
@@ -751,10 +771,11 @@ package Ada.Strings.Fixed with SPARK_Mode is
(Source : in out String;
Mapping : Maps.Character_Mapping_Function)
with
+ Pre => Mapping /= null,
Post =>
- -- Each character in Source after the call is the translation of
- -- the character at the same position before the call, through Mapping.
+ -- Each character in Source after the call is the translation of the
+ -- character at the same position before the call, through Mapping.
(for all J in Source'Range => Source (J) = Mapping (Source'Old (J))),
Global => null;
@@ -765,8 +786,8 @@ package Ada.Strings.Fixed with SPARK_Mode is
with
Post =>
- -- Each character in Source after the call is the translation of
- -- the character at the same position before the call, through Mapping.
+ -- Each character in Source after the call is the translation of the
+ -- character at the same position before the call, through Mapping.
(for all J in Source'Range =>
Source (J) = Ada.Strings.Maps.Value (Mapping, Source'Old (J))),
@@ -778,32 +799,6 @@ package Ada.Strings.Fixed with SPARK_Mode is
-- String Transformation Subprograms --
---------------------------------------
- procedure Replace_Slice
- (Source : in out String;
- Low : Positive;
- High : Natural;
- By : String;
- Drop : Truncation := Error;
- Justify : Alignment := Left;
- Pad : Character := Space)
- with
- Pre =>
-
- -- Incomplete contract
-
- Low - 1 <= Source'Last
- and then High >= Source'First - 1,
- Global => null;
- -- If Low > Source'Last+1, or High < Source'First - 1, then Index_Error is
- -- propagated. Otherwise:
- --
- -- * If High >= Low, then the returned string comprises
- -- Source (Source'First .. Low - 1)
- -- & By & Source(High + 1 .. Source'Last), but with lower bound 1.
- --
- -- * If High < Low, then the returned string is
- -- Insert (Source, Before => Low, New_Item => By).
-
function Replace_Slice
(Source : String;
Low : Positive;
@@ -834,19 +829,19 @@ package Ada.Strings.Fixed with SPARK_Mode is
-- Length of the returned string
Replace_Slice'Result'Length
- = Natural'Max (0, Low - Source'First)
+ = Integer'Max (0, Low - Source'First)
+ By'Length
- + Natural'Max (Source'Last - High, 0)
+ + Integer'Max (Source'Last - High, 0)
-- Elements starting at Low are replaced by elements of By
and then
- Replace_Slice'Result (1 .. Natural'Max (0, Low - Source'First))
+ Replace_Slice'Result (1 .. Integer'Max (0, Low - Source'First))
= Source (Source'First .. Low - 1)
and then
Replace_Slice'Result
- (Natural'Max (0, Low - Source'First) + 1
- .. Natural'Max (0, Low - Source'First) + By'Length)
+ (Integer'Max (0, Low - Source'First) + 1
+ .. Integer'Max (0, Low - Source'First) + By'Length)
= By
-- When there are remaining characters after the replaced slice,
@@ -856,7 +851,7 @@ package Ada.Strings.Fixed with SPARK_Mode is
(if High < Source'Last
then
Replace_Slice'Result
- (Natural'Max (0, Low - Source'First) + By'Length + 1
+ (Integer'Max (0, Low - Source'First) + By'Length + 1
.. Replace_Slice'Result'Last)
= Source (High + 1 .. Source'Last)),
@@ -890,6 +885,30 @@ package Ada.Strings.Fixed with SPARK_Mode is
.. Replace_Slice'Result'Last)
= Source (Low .. Source'Last))),
Global => null;
+ -- If Low > Source'Last + 1, or High < Source'First - 1, then Index_Error
+ -- is propagated. Otherwise:
+ --
+ -- * If High >= Low, then the returned string comprises
+ -- Source (Source'First .. Low - 1)
+ -- & By & Source(High + 1 .. Source'Last), but with lower bound 1.
+ --
+ -- * If High < Low, then the returned string is
+ -- Insert (Source, Before => Low, New_Item => By).
+
+ procedure Replace_Slice
+ (Source : in out String;
+ Low : Positive;
+ High : Natural;
+ By : String;
+ Drop : Truncation := Error;
+ Justify : Alignment := Left;
+ Pad : Character := Space)
+ with
+ Pre => Low - 1 <= Source'Last,
+
+ -- Incomplete contract
+
+ Global => null;
-- Equivalent to:
--
-- Move (Replace_Slice (Source, Low, High, By),
@@ -929,7 +948,7 @@ package Ada.Strings.Fixed with SPARK_Mode is
-- are appended to the returned string.
and then
- (if Before - 1 < Source'Last
+ (if Before <= Source'Last
then
Insert'Result
(Before - Source'First + New_Item'Length + 1
@@ -937,7 +956,7 @@ package Ada.Strings.Fixed with SPARK_Mode is
= Source (Before .. Source'Last)),
Global => null;
-- Propagates Index_Error if Before is not in
- -- Source'First .. Source'Last+1; otherwise, returns
+ -- Source'First .. Source'Last + 1; otherwise, returns
-- Source (Source'First .. Before - 1)
-- & New_Item & Source(Before..Source'Last), but with lower bound 1.
@@ -1384,9 +1403,8 @@ package Ada.Strings.Fixed with SPARK_Mode is
-- Content of the string is Right concatenated with itself Left times
and then
- (for all J in 0 .. Left - 1 =>
- "*"'Result (J * Right'Length + 1 .. (J + 1) * Right'Length)
- = Right),
+ (for all K in "*"'Result'Range =>
+ "*"'Result (K) = Right (Right'First + (K - 1) mod Right'Length)),
Global => null;
-- These functions replicate a character or string a specified number of
diff --git a/gcc/ada/libgnat/a-strsea.adb b/gcc/ada/libgnat/a-strsea.adb
index d96a4c7..243c92c 100644
--- a/gcc/ada/libgnat/a-strsea.adb
+++ b/gcc/ada/libgnat/a-strsea.adb
@@ -35,10 +35,18 @@
-- case of identity mappings for Count and Index, and also Index_Non_Blank
-- is specialized (rather than using the general Index routine).
+-- Ghost code, loop invariants and assertions in this unit are meant for
+-- analysis only, not for run-time checking, as it would be too costly
+-- otherwise. This is enforced by setting the assertion policy to Ignore.
+
+pragma Assertion_Policy (Ghost => Ignore,
+ Loop_Invariant => Ignore,
+ Assert => Ignore);
+
with Ada.Strings.Maps; use Ada.Strings.Maps;
with System; use System;
-package body Ada.Strings.Search is
+package body Ada.Strings.Search with SPARK_Mode is
-----------------------
-- Local Subprograms --
@@ -61,13 +69,9 @@ package body Ada.Strings.Search is
Set : Maps.Character_Set;
Test : Membership) return Boolean
is
- begin
- if Test = Inside then
- return Is_In (Element, Set);
- else
- return not Is_In (Element, Set);
- end if;
- end Belongs;
+ (if Test = Inside then
+ Is_In (Element, Set)
+ else not (Is_In (Element, Set)));
-----------
-- Count --
@@ -81,47 +85,63 @@ package body Ada.Strings.Search is
PL1 : constant Integer := Pattern'Length - 1;
Num : Natural;
Ind : Natural;
- Cur : Natural;
begin
if Pattern = "" then
raise Pattern_Error;
end if;
+ -- Isolating the null string case to ensure Source'First, Source'Last in
+ -- Positive.
+
+ if Source = "" then
+ return 0;
+ end if;
+
Num := 0;
- Ind := Source'First;
+ Ind := Source'First - 1;
-- Unmapped case
- if Mapping'Address = Maps.Identity'Address then
- while Ind <= Source'Last - PL1 loop
+ if Is_Identity (Mapping) then
+ while Ind < Source'Last - PL1 loop
+ Ind := Ind + 1;
if Pattern = Source (Ind .. Ind + PL1) then
Num := Num + 1;
- Ind := Ind + Pattern'Length;
- else
- Ind := Ind + 1;
+ Ind := Ind + PL1;
end if;
+
+ pragma Loop_Invariant (Num <= Ind - (Source'First - 1));
+ pragma Loop_Invariant (Ind >= Source'First);
end loop;
-- Mapped case
else
- while Ind <= Source'Last - PL1 loop
- Cur := Ind;
+ while Ind < Source'Last - PL1 loop
+ Ind := Ind + 1;
for K in Pattern'Range loop
- if Pattern (K) /= Value (Mapping, Source (Cur)) then
- Ind := Ind + 1;
+ if Pattern (K) /= Value (Mapping,
+ Source (Ind + (K - Pattern'First)))
+ then
+ pragma Assert (not (Match (Source, Pattern, Mapping, Ind)));
goto Cont;
- else
- Cur := Cur + 1;
end if;
+
+ pragma Loop_Invariant
+ (for all J in Pattern'First .. K =>
+ Pattern (J) = Value (Mapping,
+ Source (Ind + (J - Pattern'First))));
end loop;
+ pragma Assert (Match (Source, Pattern, Mapping, Ind));
Num := Num + 1;
- Ind := Ind + Pattern'Length;
+ Ind := Ind + PL1;
- <<Cont>>
+ <<Cont>>
null;
+ pragma Loop_Invariant (Num <= Ind - (Source'First - 1));
+ pragma Loop_Invariant (Ind >= Source'First);
end loop;
end if;
@@ -138,13 +158,19 @@ package body Ada.Strings.Search is
PL1 : constant Integer := Pattern'Length - 1;
Num : Natural;
Ind : Natural;
- Cur : Natural;
begin
if Pattern = "" then
raise Pattern_Error;
end if;
+ -- Isolating the null string case to ensure Source'First, Source'Last in
+ -- Positive.
+
+ if Source = "" then
+ return 0;
+ end if;
+
-- Check for null pointer in case checks are off
if Mapping = null then
@@ -152,23 +178,28 @@ package body Ada.Strings.Search is
end if;
Num := 0;
- Ind := Source'First;
- while Ind <= Source'Last - PL1 loop
- Cur := Ind;
+ Ind := Source'First - 1;
+ while Ind < Source'Last - PL1 loop
+ Ind := Ind + 1;
for K in Pattern'Range loop
- if Pattern (K) /= Mapping (Source (Cur)) then
- Ind := Ind + 1;
+ if Pattern (K) /= Mapping (Source (Ind + (K - Pattern'First))) then
+ pragma Assert (not (Match (Source, Pattern, Mapping, Ind)));
goto Cont;
- else
- Cur := Cur + 1;
end if;
+
+ pragma Loop_Invariant
+ (for all J in Pattern'First .. K =>
+ Pattern (J) = Mapping (Source (Ind + (J - Pattern'First))));
end loop;
+ pragma Assert (Match (Source, Pattern, Mapping, Ind));
Num := Num + 1;
- Ind := Ind + Pattern'Length;
+ Ind := Ind + PL1;
<<Cont>>
null;
+ pragma Loop_Invariant (Num <= Ind - (Source'First - 1));
+ pragma Loop_Invariant (Ind >= Source'First);
end loop;
return Num;
@@ -182,6 +213,7 @@ package body Ada.Strings.Search is
begin
for J in Source'Range loop
+ pragma Loop_Invariant (N <= J - Source'First);
if Is_In (Source (J), Set) then
N := N + 1;
end if;
@@ -217,12 +249,18 @@ package body Ada.Strings.Search is
if Belongs (Source (J), Set, Test) then
First := J;
- for K in J + 1 .. Source'Last loop
- if not Belongs (Source (K), Set, Test) then
- Last := K - 1;
- return;
- end if;
- end loop;
+ if J < Source'Last then
+ for K in J + 1 .. Source'Last loop
+ if not Belongs (Source (K), Set, Test) then
+ Last := K - 1;
+ return;
+ end if;
+
+ pragma Loop_Invariant
+ (for all L in J .. K =>
+ Belongs (Source (L), Set, Test));
+ end loop;
+ end if;
-- Here if J indexes first char of token, and all chars after J
-- are in the token.
@@ -230,6 +268,10 @@ package body Ada.Strings.Search is
Last := Source'Last;
return;
end if;
+
+ pragma Loop_Invariant
+ (for all K in Integer'Max (From, Source'First) .. J =>
+ not (Belongs (Source (K), Set, Test)));
end loop;
-- Here if no token found
@@ -250,12 +292,18 @@ package body Ada.Strings.Search is
if Belongs (Source (J), Set, Test) then
First := J;
- for K in J + 1 .. Source'Last loop
- if not Belongs (Source (K), Set, Test) then
- Last := K - 1;
- return;
- end if;
- end loop;
+ if J < Source'Last then
+ for K in J + 1 .. Source'Last loop
+ if not Belongs (Source (K), Set, Test) then
+ Last := K - 1;
+ return;
+ end if;
+
+ pragma Loop_Invariant
+ (for all L in J .. K =>
+ Belongs (Source (L), Set, Test));
+ end loop;
+ end if;
-- Here if J indexes first char of token, and all chars after J
-- are in the token.
@@ -263,6 +311,10 @@ package body Ada.Strings.Search is
Last := Source'Last;
return;
end if;
+
+ pragma Loop_Invariant
+ (for all K in Source'First .. J =>
+ not (Belongs (Source (K), Set, Test)));
end loop;
-- Here if no token found
@@ -292,53 +344,61 @@ package body Ada.Strings.Search is
Mapping : Maps.Character_Mapping := Maps.Identity) return Natural
is
PL1 : constant Integer := Pattern'Length - 1;
- Cur : Natural;
-
- Ind : Integer;
- -- Index for start of match check. This can be negative if the pattern
- -- length is greater than the string length, which is why this variable
- -- is Integer instead of Natural. In this case, the search loops do not
- -- execute at all, so this Ind value is never used.
begin
if Pattern = "" then
raise Pattern_Error;
end if;
+ -- If Pattern is longer than Source, it can't be found
+
+ if Pattern'Length > Source'Length then
+ return 0;
+ end if;
+
-- Forwards case
if Going = Forward then
- Ind := Source'First;
-- Unmapped forward case
- if Mapping'Address = Maps.Identity'Address then
- for J in 1 .. Source'Length - PL1 loop
+ if Is_Identity (Mapping) then
+ for Ind in Source'First .. Source'Last - PL1 loop
if Pattern = Source (Ind .. Ind + PL1) then
+ pragma Assert (Match (Source, Pattern, Mapping, Ind));
return Ind;
- else
- Ind := Ind + 1;
end if;
+
+ pragma Loop_Invariant
+ (for all J in Source'First .. Ind =>
+ not (Match (Source, Pattern, Mapping, J)));
end loop;
-- Mapped forward case
else
- for J in 1 .. Source'Length - PL1 loop
- Cur := Ind;
-
+ for Ind in Source'First .. Source'Last - PL1 loop
for K in Pattern'Range loop
- if Pattern (K) /= Value (Mapping, Source (Cur)) then
+ if Pattern (K) /= Value (Mapping,
+ Source (Ind + (K - Pattern'First)))
+ then
goto Cont1;
- else
- Cur := Cur + 1;
end if;
+
+ pragma Loop_Invariant
+ (for all J in Pattern'First .. K =>
+ Pattern (J) = Value (Mapping,
+ Source (Ind + (J - Pattern'First))));
end loop;
+ pragma Assert (Match (Source, Pattern, Mapping, Ind));
return Ind;
- <<Cont1>>
- Ind := Ind + 1;
+ <<Cont1>>
+ pragma Loop_Invariant
+ (for all J in Source'First .. Ind =>
+ not (Match (Source, Pattern, Mapping, J)));
+ null;
end loop;
end if;
@@ -347,35 +407,43 @@ package body Ada.Strings.Search is
else
-- Unmapped backward case
- Ind := Source'Last - PL1;
-
- if Mapping'Address = Maps.Identity'Address then
- for J in reverse 1 .. Source'Length - PL1 loop
+ if Is_Identity (Mapping) then
+ for Ind in reverse Source'First .. Source'Last - PL1 loop
if Pattern = Source (Ind .. Ind + PL1) then
+ pragma Assert (Match (Source, Pattern, Mapping, Ind));
return Ind;
- else
- Ind := Ind - 1;
end if;
+
+ pragma Loop_Invariant
+ (for all J in Ind .. Source'Last - PL1 =>
+ not (Match (Source, Pattern, Mapping, J)));
end loop;
-- Mapped backward case
else
- for J in reverse 1 .. Source'Length - PL1 loop
- Cur := Ind;
-
+ for Ind in reverse Source'First .. Source'Last - PL1 loop
for K in Pattern'Range loop
- if Pattern (K) /= Value (Mapping, Source (Cur)) then
+ if Pattern (K) /= Value (Mapping,
+ Source (Ind + (K - Pattern'First)))
+ then
goto Cont2;
- else
- Cur := Cur + 1;
end if;
+
+ pragma Loop_Invariant
+ (for all J in Pattern'First .. K =>
+ Pattern (J) = Value (Mapping,
+ Source (Ind + (J - Pattern'First))));
end loop;
+ pragma Assert (Match (Source, Pattern, Mapping, Ind));
return Ind;
- <<Cont2>>
- Ind := Ind - 1;
+ <<Cont2>>
+ pragma Loop_Invariant
+ (for all J in Ind .. Source'Last - PL1 =>
+ not (Match (Source, Pattern, Mapping, J)));
+ null;
end loop;
end if;
end if;
@@ -393,9 +461,6 @@ package body Ada.Strings.Search is
Mapping : Maps.Character_Mapping_Function) return Natural
is
PL1 : constant Integer := Pattern'Length - 1;
- Ind : Natural;
- Cur : Natural;
-
begin
if Pattern = "" then
raise Pattern_Error;
@@ -416,43 +481,52 @@ package body Ada.Strings.Search is
-- Forwards case
if Going = Forward then
- Ind := Source'First;
- for J in 1 .. Source'Length - PL1 loop
- Cur := Ind;
-
+ for Ind in Source'First .. Source'Last - PL1 loop
for K in Pattern'Range loop
- if Pattern (K) /= Mapping.all (Source (Cur)) then
+ if Pattern (K) /= Mapping.all
+ (Source (Ind + (K - Pattern'First)))
+ then
goto Cont1;
- else
- Cur := Cur + 1;
end if;
+
+ pragma Loop_Invariant
+ (for all J in Pattern'First .. K =>
+ Pattern (J) = Mapping (Source (Ind + (J - Pattern'First))));
end loop;
+ pragma Assert (Match (Source, Pattern, Mapping, Ind));
return Ind;
- <<Cont1>>
- Ind := Ind + 1;
+ <<Cont1>>
+ pragma Loop_Invariant
+ (for all J in Source'First .. Ind =>
+ not (Match (Source, Pattern, Mapping, J)));
+ null;
end loop;
-- Backwards case
else
- Ind := Source'Last - PL1;
- for J in reverse 1 .. Source'Length - PL1 loop
- Cur := Ind;
-
+ for Ind in reverse Source'First .. Source'Last - PL1 loop
for K in Pattern'Range loop
- if Pattern (K) /= Mapping.all (Source (Cur)) then
+ if Pattern (K) /= Mapping.all
+ (Source (Ind + (K - Pattern'First)))
+ then
goto Cont2;
- else
- Cur := Cur + 1;
end if;
+
+ pragma Loop_Invariant
+ (for all J in Pattern'First .. K =>
+ Pattern (J) = Mapping (Source (Ind + (J - Pattern'First))));
end loop;
return Ind;
- <<Cont2>>
- Ind := Ind - 1;
+ <<Cont2>>
+ pragma Loop_Invariant
+ (for all J in Ind .. (Source'Last - PL1) =>
+ not (Match (Source, Pattern, Mapping, J)));
+ null;
end loop;
end if;
@@ -476,6 +550,10 @@ package body Ada.Strings.Search is
if Belongs (Source (J), Set, Test) then
return J;
end if;
+
+ pragma Loop_Invariant
+ (for all C of Source (Source'First .. J) =>
+ not (Belongs (C, Set, Test)));
end loop;
-- Backwards case
@@ -485,6 +563,10 @@ package body Ada.Strings.Search is
if Belongs (Source (J), Set, Test) then
return J;
end if;
+
+ pragma Loop_Invariant
+ (for all C of Source (J .. Source'Last) =>
+ not (Belongs (C, Set, Test)));
end loop;
end if;
@@ -500,6 +582,8 @@ package body Ada.Strings.Search is
Going : Direction := Forward;
Mapping : Maps.Character_Mapping := Maps.Identity) return Natural
is
+ Result : Natural;
+ PL1 : constant Integer := Pattern'Length - 1;
begin
-- AI05-056: If source is empty result is always zero
@@ -512,17 +596,29 @@ package body Ada.Strings.Search is
raise Index_Error;
end if;
- return
+ Result :=
Index (Source (From .. Source'Last), Pattern, Forward, Mapping);
+ pragma Assert
+ (if (for some J in From .. Source'Last - PL1 =>
+ Match (Source, Pattern, Mapping, J))
+ then Result in From .. Source'Last - PL1
+ else Result = 0);
else
if From > Source'Last then
raise Index_Error;
end if;
- return
+ Result :=
Index (Source (Source'First .. From), Pattern, Backward, Mapping);
+ pragma Assert
+ (if (for some J in Source'First .. From - PL1 =>
+ Match (Source, Pattern, Mapping, J))
+ then Result in Source'First .. From - PL1
+ else Result = 0);
end if;
+
+ return Result;
end Index;
function Index
@@ -603,6 +699,9 @@ package body Ada.Strings.Search is
if Source (J) /= ' ' then
return J;
end if;
+
+ pragma Loop_Invariant
+ (for all C of Source (Source'First .. J) => C = ' ');
end loop;
else -- Going = Backward
@@ -610,6 +709,9 @@ package body Ada.Strings.Search is
if Source (J) /= ' ' then
return J;
end if;
+
+ pragma Loop_Invariant
+ (for all C of Source (J .. Source'Last) => C = ' ');
end loop;
end if;
@@ -624,6 +726,13 @@ package body Ada.Strings.Search is
Going : Direction := Forward) return Natural
is
begin
+
+ -- For equivalence with Index, if Source is empty the result is 0
+
+ if Source'Length = 0 then
+ return 0;
+ end if;
+
if Going = Forward then
if From < Source'First then
raise Index_Error;
@@ -642,4 +751,12 @@ package body Ada.Strings.Search is
end if;
end Index_Non_Blank;
+ function Is_Identity
+ (Mapping : Maps.Character_Mapping) return Boolean
+ with SPARK_Mode => Off
+ is
+ begin
+ return Mapping'Address = Maps.Identity'Address;
+ end Is_Identity;
+
end Ada.Strings.Search;
diff --git a/gcc/ada/libgnat/a-strsea.ads b/gcc/ada/libgnat/a-strsea.ads
index 623c0f4..4396747 100644
--- a/gcc/ada/libgnat/a-strsea.ads
+++ b/gcc/ada/libgnat/a-strsea.ads
@@ -32,76 +32,489 @@
-- This package contains the search functions from Ada.Strings.Fixed. They
-- are separated out because they are shared by Ada.Strings.Bounded and
-- Ada.Strings.Unbounded, and we don't want to drag in other irrelevant stuff
--- from Ada.Strings.Fixed when using the other two packages. We make this a
--- private package, since user programs should access these subprograms via
--- one of the standard string packages.
+-- from Ada.Strings.Fixed when using the other two packages. Although user
+-- programs should access these subprograms via one of the standard string
+-- packages, we do not make this a private package, since ghost function
+-- Match is used in the contracts of the standard string packages.
-with Ada.Strings.Maps;
+-- Preconditions in this unit are meant for analysis only, not for run-time
+-- checking, so that the expected exceptions are raised. This is enforced
+-- by setting the corresponding assertion policy to Ignore. Postconditions,
+-- contract cases and ghost code should not be executed at runtime as well,
+-- in order not to slow down the execution of these functions.
-private package Ada.Strings.Search is
+pragma Assertion_Policy (Pre => Ignore,
+ Post => Ignore,
+ Contract_Cases => Ignore,
+ Ghost => Ignore);
+
+with Ada.Strings.Maps; use type Ada.Strings.Maps.Character_Mapping_Function;
+
+package Ada.Strings.Search with SPARK_Mode is
pragma Preelaborate;
+ -- The ghost function Match tells whether the slice of Source starting at
+ -- From and of length Pattern'Length matches with Pattern with respect to
+ -- Mapping. Pattern should be non-empty and the considered slice should be
+ -- fully included in Source'Range.
+
+ function Match
+ (Source : String;
+ Pattern : String;
+ Mapping : Maps.Character_Mapping_Function;
+ From : Integer) return Boolean
+ is
+ (for all K in Pattern'Range =>
+ Pattern (K) = Mapping (Source (From + (K - Pattern'First))))
+ with
+ Ghost,
+ Pre => Mapping /= null
+ and then Pattern'Length > 0
+ and then Source'Length > 0
+ and then From in Source'First .. Source'Last - (Pattern'Length - 1),
+ Global => null;
+
+ function Match
+ (Source : String;
+ Pattern : String;
+ Mapping : Maps.Character_Mapping;
+ From : Integer) return Boolean
+ is
+ (for all K in Pattern'Range =>
+ Pattern (K) =
+ Ada.Strings.Maps.Value
+ (Mapping, Source (From + (K - Pattern'First))))
+ with
+ Ghost,
+ Pre => Pattern'Length > 0
+ and then Source'Length > 0
+ and then From in Source'First .. Source'Last - (Pattern'Length - 1),
+ Global => null;
+
+ function Is_Identity
+ (Mapping : Maps.Character_Mapping) return Boolean
+ with
+ Post => (if Is_Identity'Result then
+ (for all K in Character =>
+ Ada.Strings.Maps.Value (Mapping, K) = K)),
+ Global => null;
+
function Index
(Source : String;
Pattern : String;
Going : Direction := Forward;
- Mapping : Maps.Character_Mapping := Maps.Identity) return Natural;
+ Mapping : Maps.Character_Mapping := Maps.Identity) return Natural
+ with
+ Pre => Pattern'Length > 0,
+
+ Post => Index'Result in 0 | Source'Range,
+ Contract_Cases =>
+
+ -- If Source is the empty string, then 0 is returned
+
+ (Source'Length = 0 => Index'Result = 0,
+
+ -- If some slice of Source matches Pattern, then a valid index is
+ -- returned.
+
+ Source'Length > 0
+ and then
+ (for some J in
+ Source'First .. Source'Last - (Pattern'Length - 1) =>
+ Match (Source, Pattern, Mapping, J))
+ =>
+
+ -- The result is in the considered range of Source
+
+ Index'Result in Source'First .. Source'Last - (Pattern'Length - 1)
+
+ -- The slice beginning at the returned index matches Pattern
+
+ and then Match (Source, Pattern, Mapping, Index'Result)
+
+ -- The result is the smallest or largest index which satisfies
+ -- the matching, respectively when Going = Forward and Going =
+ -- Backward.
+
+ and then
+ (for all J in Source'Range =>
+ (if (if Going = Forward
+ then J <= Index'Result - 1
+ else J - 1 in Index'Result
+ .. Source'Last - Pattern'Length)
+ then not (Match (Source, Pattern, Mapping, J)))),
+
+ -- Otherwise, 0 is returned
+
+ others => Index'Result = 0),
+ Global => null;
function Index
(Source : String;
Pattern : String;
Going : Direction := Forward;
- Mapping : Maps.Character_Mapping_Function) return Natural;
+ Mapping : Maps.Character_Mapping_Function) return Natural
+ with
+ Pre => Pattern'Length > 0 and then Mapping /= null,
+ Post => Index'Result in 0 | Source'Range,
+ Contract_Cases =>
+
+ -- If Source is the null string, then 0 is returned
+
+ (Source'Length = 0 => Index'Result = 0,
+
+ -- If some slice of Source matches Pattern, then a valid index is
+ -- returned.
+
+ Source'Length > 0 and then
+ (for some J in Source'First .. Source'Last - (Pattern'Length - 1) =>
+ Match (Source, Pattern, Mapping, J))
+ =>
+
+ -- The result is in the considered range of Source
+
+ Index'Result in Source'First .. Source'Last - (Pattern'Length - 1)
+
+ -- The slice beginning at the returned index matches Pattern
+
+ and then Match (Source, Pattern, Mapping, Index'Result)
+
+ -- The result is the smallest or largest index which satisfies
+ -- the matching, respectively when Going = Forward and Going =
+ -- Backward.
+
+ and then
+ (for all J in Source'Range =>
+ (if (if Going = Forward
+ then J <= Index'Result - 1
+ else J - 1 in Index'Result
+ .. Source'Last - Pattern'Length)
+ then not (Match (Source, Pattern, Mapping, J)))),
+
+ -- Otherwise, 0 is returned
+
+ others => Index'Result = 0),
+ Global => null;
function Index
(Source : String;
Set : Maps.Character_Set;
Test : Membership := Inside;
- Going : Direction := Forward) return Natural;
+ Going : Direction := Forward) return Natural
+ with
+ Post => Index'Result in 0 | Source'Range,
+ Contract_Cases =>
+
+ -- If no character of Source satisfies the property Test on Set, then
+ -- 0 is returned.
+
+ ((for all C of Source =>
+ (Test = Inside) /= Ada.Strings.Maps.Is_In (C, Set))
+ =>
+ Index'Result = 0,
+
+ -- Otherwise, a index in the range of Source is returned
+
+ others =>
+
+ -- The result is in the range of Source
+
+ Index'Result in Source'Range
+
+ -- The character at the returned index satisfies the property
+ -- Test on Set
+
+ and then (Test = Inside)
+ = Ada.Strings.Maps.Is_In (Source (Index'Result), Set)
+
+ -- The result is the smallest or largest index which satisfies
+ -- the property, respectively when Going = Forward and Going =
+ -- Backward.
+
+ and then
+ (for all J in Source'Range =>
+ (if J /= Index'Result
+ and then (J < Index'Result) = (Going = Forward)
+ then (Test = Inside)
+ /= Ada.Strings.Maps.Is_In (Source (J), Set)))),
+ Global => null;
function Index
(Source : String;
Pattern : String;
From : Positive;
Going : Direction := Forward;
- Mapping : Maps.Character_Mapping := Maps.Identity) return Natural;
+ Mapping : Maps.Character_Mapping := Maps.Identity) return Natural
+ with
+ Pre => Pattern'Length > 0
+ and then (if Source'Length > 0 then From in Source'Range),
+
+ Post => Index'Result in 0 | Source'Range,
+ Contract_Cases =>
+
+ -- If Source is the empty string, then 0 is returned
+
+ (Source'Length = 0 => Index'Result = 0,
+
+ -- If some slice of Source matches Pattern, then a valid index is
+ -- returned.
+
+ Source'Length > 0
+ and then
+ (for some J in
+ (if Going = Forward then From else Source'First)
+ .. (if Going = Forward then Source'Last else From)
+ - (Pattern'Length - 1) =>
+ Match (Source, Pattern, Mapping, J))
+ =>
+
+ -- The result is in the considered range of Source
+
+ Index'Result in
+ (if Going = Forward then From else Source'First)
+ .. (if Going = Forward then Source'Last else From)
+ - (Pattern'Length - 1)
+
+ -- The slice beginning at the returned index matches Pattern
+
+ and then Match (Source, Pattern, Mapping, Index'Result)
+
+ -- The result is the smallest or largest index which satisfies
+ -- the matching, respectively when Going = Forward and Going =
+ -- Backward.
+
+ and then
+ (for all J in Source'Range =>
+ (if (if Going = Forward
+ then J in From .. Index'Result - 1
+ else J - 1 in Index'Result
+ .. From - Pattern'Length)
+ then not (Match (Source, Pattern, Mapping, J)))),
+
+ -- Otherwise, 0 is returned
+
+ others => Index'Result = 0),
+ Global => null;
function Index
(Source : String;
Pattern : String;
From : Positive;
Going : Direction := Forward;
- Mapping : Maps.Character_Mapping_Function) return Natural;
+ Mapping : Maps.Character_Mapping_Function) return Natural
+ with
+ Pre => Pattern'Length > 0
+ and then Mapping /= null
+ and then (if Source'Length > 0 then From in Source'Range),
+
+ Post => Index'Result in 0 | Source'Range,
+ Contract_Cases =>
+
+ -- If Source is the empty string, then 0 is returned
+
+ (Source'Length = 0 => Index'Result = 0,
+
+ -- If some slice of Source matches Pattern, then a valid index is
+ -- returned.
+
+ Source'Length > 0
+ and then
+ (for some J in
+ (if Going = Forward then From else Source'First)
+ .. (if Going = Forward then Source'Last else From)
+ - (Pattern'Length - 1) =>
+ Match (Source, Pattern, Mapping, J))
+ =>
+
+ -- The result is in the considered range of Source
+
+ Index'Result in
+ (if Going = Forward then From else Source'First)
+ .. (if Going = Forward then Source'Last else From)
+ - (Pattern'Length - 1)
+
+ -- The slice beginning at the returned index matches Pattern
+
+ and then Match (Source, Pattern, Mapping, Index'Result)
+
+ -- The result is the smallest or largest index which satisfies
+ -- the matching, respectively when Going = Forward and Going =
+ -- Backwards.
+
+ and then
+ (for all J in Source'Range =>
+ (if (if Going = Forward
+ then J in From .. Index'Result - 1
+ else J - 1 in Index'Result
+ .. From - Pattern'Length)
+ then not (Match (Source, Pattern, Mapping, J)))),
+
+ -- Otherwise, 0 is returned
+
+ others => Index'Result = 0),
+ Global => null;
function Index
(Source : String;
Set : Maps.Character_Set;
From : Positive;
Test : Membership := Inside;
- Going : Direction := Forward) return Natural;
+ Going : Direction := Forward) return Natural
+ with
+ Pre => (if Source'Length > 0 then From in Source'Range),
+ Post => Index'Result in 0 | Source'Range,
+ Contract_Cases =>
+
+ -- If Source is the empty string, or no character of the considered
+ -- slice of Source satisfies the property Test on Set, then 0 is
+ -- returned.
+
+ (Source'Length = 0
+ or else
+ (for all J in Source'Range =>
+ (if J = From or else (J > From) = (Going = Forward) then
+ (Test = Inside) /= Ada.Strings.Maps.Is_In (Source (J), Set)))
+ =>
+ Index'Result = 0,
+
+ -- Otherwise, a index in the considered range of Source is returned
+
+ others =>
+
+ -- The result is in the considered range of Source
+
+ Index'Result in Source'Range
+ and then (Index'Result = From
+ or else
+ (Index'Result > From) = (Going = Forward))
+
+ -- The character at the returned index satisfies the property
+ -- Test on Set
+
+ and then
+ (Test = Inside)
+ = Ada.Strings.Maps.Is_In (Source (Index'Result), Set)
+
+ -- The result is the smallest or largest index which satisfies
+ -- the property, respectively when Going = Forward and Going =
+ -- Backward.
+
+ and then
+ (for all J in Source'Range =>
+ (if J /= Index'Result
+ and then (J < Index'Result) = (Going = Forward)
+ and then (J = From
+ or else (J > From) = (Going = Forward))
+ then (Test = Inside)
+ /= Ada.Strings.Maps.Is_In (Source (J), Set)))),
+ Global => null;
function Index_Non_Blank
(Source : String;
- Going : Direction := Forward) return Natural;
+ Going : Direction := Forward) return Natural
+ with
+ Post => Index_Non_Blank'Result in 0 | Source'Range,
+ Contract_Cases =>
+
+ -- If all characters of Source are Space characters, then 0 is
+ -- returned.
+
+ ((for all C of Source => C = ' ') => Index_Non_Blank'Result = 0,
+
+ -- Otherwise, a valid index is returned
+
+ others =>
+
+ -- The result is in the range of Source
+
+ Index_Non_Blank'Result in Source'Range
+
+ -- The character at the returned index is not a Space character
+
+ and then Source (Index_Non_Blank'Result) /= ' '
+
+ -- The result is the smallest or largest index which is not a
+ -- Space character, respectively when Going = Forward and
+ -- Going = Backward.
+
+ and then
+ (for all J in Source'Range =>
+ (if J /= Index_Non_Blank'Result
+ and then (J < Index_Non_Blank'Result)
+ = (Going = Forward)
+ then Source (J) = ' '))),
+ Global => null;
function Index_Non_Blank
(Source : String;
From : Positive;
- Going : Direction := Forward) return Natural;
+ Going : Direction := Forward) return Natural
+ with
+ Pre => (if Source'Length /= 0 then From in Source'Range),
+ Post => Index_Non_Blank'Result in 0 | Source'Range,
+ Contract_Cases =>
+
+ -- If Source is the null string, or all characters in the considered
+ -- slice of Source are Space characters, then 0 is returned.
+
+ (Source'Length = 0
+ or else
+ (for all J in Source'Range =>
+ (if J = From or else (J > From) = (Going = Forward) then
+ Source (J) = ' '))
+ =>
+ Index_Non_Blank'Result = 0,
+
+ -- Otherwise, a valid index is returned
+
+ others =>
+
+ -- The result is in the considered range of Source
+
+ Index_Non_Blank'Result in Source'Range
+ and then (Index_Non_Blank'Result = From
+ or else (Index_Non_Blank'Result > From)
+ = (Going = Forward))
+
+ -- The character at the returned index is not a Space character
+
+ and then Source (Index_Non_Blank'Result) /= ' '
+
+ -- The result is the smallest or largest index which is not a
+ -- Space character, respectively when Going = Forward and
+ -- Going = Backward.
+
+ and then
+ (for all J in Source'Range =>
+ (if J /= Index_Non_Blank'Result
+ and then (J < Index_Non_Blank'Result)
+ = (Going = Forward)
+ and then (J = From or else (J > From)
+ = (Going = Forward))
+ then Source (J) = ' '))),
+ Global => null;
function Count
(Source : String;
Pattern : String;
- Mapping : Maps.Character_Mapping := Maps.Identity) return Natural;
+ Mapping : Maps.Character_Mapping := Maps.Identity) return Natural
+ with
+ Pre => Pattern'Length > 0,
+ Global => null;
function Count
(Source : String;
Pattern : String;
- Mapping : Maps.Character_Mapping_Function) return Natural;
+ Mapping : Maps.Character_Mapping_Function) return Natural
+ with
+ Pre => Pattern'Length > 0 and then Mapping /= null,
+ Global => null;
function Count
(Source : String;
- Set : Maps.Character_Set) return Natural;
+ Set : Maps.Character_Set) return Natural
+ with
+ Global => null;
procedure Find_Token
(Source : String;
@@ -109,13 +522,104 @@ private package Ada.Strings.Search is
From : Positive;
Test : Membership;
First : out Positive;
- Last : out Natural);
+ Last : out Natural)
+ with
+ Pre => (if Source'Length /= 0 then From in Source'Range),
+ Contract_Cases =>
+
+ -- If Source is the empty string, or if no character of the considered
+ -- slice of Source satisfies the property Test on Set, then First is
+ -- set to From and Last is set to 0.
+
+ (Source'Length = 0
+ or else
+ (for all C of Source (From .. Source'Last) =>
+ (Test = Inside) /= Ada.Strings.Maps.Is_In (C, Set))
+ =>
+ First = From and then Last = 0,
+
+ -- Otherwise, First and Last are set to valid indexes
+
+ others =>
+
+ -- First and Last are in the considered range of Source
+
+ First in From .. Source'Last
+ and then Last in First .. Source'Last
+
+ -- No character between From and First satisfies the property Test
+ -- on Set.
+
+ and then
+ (for all C of Source (From .. First - 1) =>
+ (Test = Inside) /= Ada.Strings.Maps.Is_In (C, Set))
+
+ -- All characters between First and Last satisfy the property Test
+ -- on Set.
+
+ and then
+ (for all C of Source (First .. Last) =>
+ (Test = Inside) = Ada.Strings.Maps.Is_In (C, Set))
+
+ -- If Last is not Source'Last, then the character at position
+ -- Last + 1 does not satify the property Test on Set.
+
+ and then
+ (if Last < Source'Last
+ then (Test = Inside)
+ /= Ada.Strings.Maps.Is_In (Source (Last + 1), Set))),
+ Global => null;
procedure Find_Token
(Source : String;
Set : Maps.Character_Set;
Test : Membership;
First : out Positive;
- Last : out Natural);
+ Last : out Natural)
+ with
+ Pre => Source'First > 0,
+ Contract_Cases =>
+
+ -- If Source is the empty string, or if no character of Source
+ -- satisfies the property Test on Set, then First is set to From
+ -- and Last is set to 0.
+
+ (Source'Length = 0
+ or else
+ (for all C of Source =>
+ (Test = Inside) /= Ada.Strings.Maps.Is_In (C, Set))
+ =>
+ First = Source'First and then Last = 0,
+
+ -- Otherwise, First and Last are set to valid indexes
+
+ others =>
+
+ -- First and Last are in the considered range of Source
+
+ First in Source'Range
+ and then Last in First .. Source'Last
+
+ -- No character before First satisfies the property Test on Set
+
+ and then
+ (for all C of Source (Source'First .. First - 1) =>
+ (Test = Inside) /= Ada.Strings.Maps.Is_In (C, Set))
+
+ -- All characters between First and Last satisfy the property Test
+ -- on Set.
+
+ and then
+ (for all C of Source (First .. Last) =>
+ (Test = Inside) = Ada.Strings.Maps.Is_In (C, Set))
+
+ -- If Last is not Source'Last, then the character at position
+ -- Last + 1 does not satify the property Test on Set.
+
+ and then
+ (if Last < Source'Last
+ then (Test = Inside)
+ /= Ada.Strings.Maps.Is_In (Source (Last + 1), Set))),
+ Global => null;
end Ada.Strings.Search;