1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- A D A . S T R I N G S . M A P S --
9 -- Copyright (C) 1992-2023, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
30 ------------------------------------------------------------------------------
32 -- Note: parts of this code are derived from the ADAR.CSH public domain
33 -- Ada 83 versions of the Appendix C string handling packages. The main
34 -- differences are that we avoid the use of the minimize function which
35 -- is bit-by-bit or character-by-character and therefore rather slow.
36 -- Generally for character sets we favor the full 32-byte representation.
38 -- Assertions, ghost code and loop invariants in this unit are meant for
39 -- analysis only, not for run-time checking, as it would be too costly
40 -- otherwise. This is enforced by setting the assertion policy to Ignore.
42 pragma Assertion_Policy
(Assert
=> Ignore
,
44 Loop_Invariant
=> Ignore
);
46 package body Ada
.Strings
.Maps
54 function "-" (Left
, Right
: Character_Set
) return Character_Set
is
56 return Left
and not Right
;
63 function "=" (Left
, Right
: Character_Set
) return Boolean is
65 return Character_Set_Internal
(Left
) = Character_Set_Internal
(Right
);
72 function "and" (Left
, Right
: Character_Set
) return Character_Set
is
75 (Character_Set_Internal
(Left
) and Character_Set_Internal
(Right
));
82 function "not" (Right
: Character_Set
) return Character_Set
is
84 return Character_Set
(not Character_Set_Internal
(Right
));
91 function "or" (Left
, Right
: Character_Set
) return Character_Set
is
94 (Character_Set_Internal
(Left
) or Character_Set_Internal
(Right
));
101 function "xor" (Left
, Right
: Character_Set
) return Character_Set
is
104 (Character_Set_Internal
(Left
) xor Character_Set_Internal
(Right
));
112 (Element
: Character;
113 Set
: Character_Set
) return Boolean
122 (Elements
: Character_Set
;
123 Set
: Character_Set
) return Boolean
126 return (Elements
and Set
) = Elements
;
133 function To_Domain
(Map
: Character_Mapping
) return Character_Sequence
is
134 Result
: String (1 .. Map
'Length) with Relaxed_Initialization
;
137 type Character_Index
is array (Character) of Natural with Ghost
;
138 Indexes
: Character_Index
:= [others => 0] with Ghost
;
142 for C
in Map
'Range loop
149 pragma Loop_Invariant
(if Map
= Identity
then J
= 0);
150 pragma Loop_Invariant
(J
<= Character'Pos (C
) + 1);
151 pragma Loop_Invariant
(Result
(1 .. J
)'Initialized);
152 pragma Loop_Invariant
(for all K
in 1 .. J
=> Result
(K
) <= C
);
153 pragma Loop_Invariant
154 (SPARK_Proof_Sorted_Character_Sequence
(Result
(1 .. J
)));
155 pragma Loop_Invariant
156 (for all D
in Map
'First .. C
=>
160 Indexes
(D
) in 1 .. J
161 and then Result
(Indexes
(D
)) = D
));
162 pragma Loop_Invariant
163 (for all Char
of Result
(1 .. J
) => Map
(Char
) /= Char
);
166 return Result
(1 .. J
);
174 (From
, To
: Character_Sequence
) return Character_Mapping
176 Result
: Character_Mapping
with Relaxed_Initialization
;
177 Inserted
: Character_Set
:= Null_Set
;
178 From_Len
: constant Natural := From
'Length;
179 To_Len
: constant Natural := To
'Length;
182 if From_Len
/= To_Len
then
183 raise Strings
.Translation_Error
;
186 for Char
in Character loop
187 Result
(Char
) := Char
;
188 pragma Loop_Invariant
(Result
(Result
'First .. Char
)'Initialized);
189 pragma Loop_Invariant
190 (for all C
in Result
'First .. Char
=> Result
(C
) = C
);
193 for J
in From
'Range loop
194 if Inserted
(From
(J
)) then
195 raise Strings
.Translation_Error
;
198 Result
(From
(J
)) := To
(J
- From
'First + To
'First);
199 Inserted
(From
(J
)) := True;
201 pragma Loop_Invariant
(Result
'Initialized);
202 pragma Loop_Invariant
203 (for all K
in From
'First .. J
=>
204 Result
(From
(K
)) = To
(K
- From
'First + To
'First)
205 and then Inserted
(From
(K
)));
206 pragma Loop_Invariant
207 (for all Char
in Character =>
209 (for some K
in From
'First .. J
=> Char
= From
(K
))));
210 pragma Loop_Invariant
211 (for all Char
in Character =>
212 (if not Inserted
(Char
) then Result
(Char
) = Char
));
213 pragma Loop_Invariant
214 (if (for all K
in From
'First .. J
=>
215 From
(K
) = To
(J
- From
'First + To
'First))
216 then Result
= Identity
);
226 function To_Range
(Map
: Character_Mapping
) return Character_Sequence
is
228 -- Extract from the postcondition of To_Domain the essential properties
229 -- that define Seq as the domain of Map.
231 (Map
: Character_Mapping
;
232 Seq
: Character_Sequence
)
237 SPARK_Proof_Sorted_Character_Sequence
(Seq
)
239 (for all Char
in Character =>
240 (if (for all X
of Seq
=> X
/= Char
)
241 then Map
(Char
) = Char
))
243 (for all Char
of Seq
=> Map
(Char
) /= Char
))
247 -- Given Map, there is a unique sequence Seq for which
248 -- Is_Domain(Map,Seq) holds.
249 procedure Lemma_Domain_Unicity
250 (Map
: Character_Mapping
;
251 Seq1
, Seq2
: Character_Sequence
)
254 Pre
=> Is_Domain
(Map
, Seq1
)
255 and then Is_Domain
(Map
, Seq2
),
258 -- Isolate the proof that To_Domain(Map) returns a sequence for which
260 procedure Lemma_Is_Domain
(Map
: Character_Mapping
)
263 Post
=> Is_Domain
(Map
, To_Domain
(Map
));
265 -- Deduce the alternative expression of sortedness from the one in
266 -- SPARK_Proof_Sorted_Character_Sequence which compares consecutive
268 procedure Lemma_Is_Sorted
(Seq
: Character_Sequence
)
271 Pre
=> SPARK_Proof_Sorted_Character_Sequence
(Seq
),
272 Post
=> (for all J
in Seq
'Range =>
273 (for all K
in Seq
'Range =>
274 (if J
< K
then Seq
(J
) < Seq
(K
))));
276 --------------------------
277 -- Lemma_Domain_Unicity --
278 --------------------------
280 procedure Lemma_Domain_Unicity
281 (Map
: Character_Mapping
;
282 Seq1
, Seq2
: Character_Sequence
)
288 and then J
<= Seq2
'Last
289 and then Seq1
(J
) = Seq2
(J
)
291 pragma Loop_Invariant
292 (Seq1
(Seq1
'First .. J
) = Seq2
(Seq2
'First .. J
));
293 pragma Loop_Variant
(Increases
=> J
);
295 if J
= Positive'Last then
302 Lemma_Is_Sorted
(Seq1
);
303 Lemma_Is_Sorted
(Seq2
);
306 and then J
<= Seq2
'Last
308 if Seq1
(J
) < Seq2
(J
) then
309 pragma Assert
(for all X
of Seq2
=> X
/= Seq1
(J
));
310 pragma Assert
(Map
(Seq1
(J
)) = Seq1
(J
));
311 pragma Assert
(False);
313 pragma Assert
(for all X
of Seq1
=> X
/= Seq2
(J
));
314 pragma Assert
(Map
(Seq2
(J
)) = Seq2
(J
));
315 pragma Assert
(False);
318 elsif J
<= Seq1
'Last then
319 pragma Assert
(for all X
of Seq2
=> X
/= Seq1
(J
));
320 pragma Assert
(Map
(Seq1
(J
)) = Seq1
(J
));
321 pragma Assert
(False);
323 elsif J
<= Seq2
'Last then
324 pragma Assert
(for all X
of Seq1
=> X
/= Seq2
(J
));
325 pragma Assert
(Map
(Seq2
(J
)) = Seq2
(J
));
326 pragma Assert
(False);
328 end Lemma_Domain_Unicity
;
330 ---------------------
331 -- Lemma_Is_Domain --
332 ---------------------
334 procedure Lemma_Is_Domain
(Map
: Character_Mapping
) is
335 Ignore
: constant Character_Sequence
:= To_Domain
(Map
);
340 ---------------------
341 -- Lemma_Is_Sorted --
342 ---------------------
344 procedure Lemma_Is_Sorted
(Seq
: Character_Sequence
) is
346 for A
in Seq
'Range loop
347 exit when A
= Positive'Last;
349 for B
in A
+ 1 .. Seq
'Last loop
350 pragma Loop_Invariant
351 (for all K
in A
+ 1 .. B
=> Seq
(A
) < Seq
(K
));
354 pragma Loop_Invariant
355 (for all J
in Seq
'First .. A
=>
356 (for all K
in Seq
'Range =>
357 (if J
< K
then Seq
(J
) < Seq
(K
))));
363 Result
: String (1 .. Map
'Length) with Relaxed_Initialization
;
366 -- Repeat the computation from To_Domain in ghost code, in order to
367 -- prove the relationship between Result and To_Domain(Map).
369 Domain
: String (1 .. Map
'Length) with Ghost
, Relaxed_Initialization
;
370 type Character_Index
is array (Character) of Natural with Ghost
;
371 Indexes
: Character_Index
:= [others => 0] with Ghost
;
373 -- Start of processing for To_Range
377 for C
in Map
'Range loop
380 Result
(J
) := Map
(C
);
385 -- Repeat the loop invariants from To_Domain regarding Domain and
386 -- Indexes. Add similar loop invariants for Result and Indexes.
388 pragma Loop_Invariant
(J
<= Character'Pos (C
) + 1);
389 pragma Loop_Invariant
(Result
(1 .. J
)'Initialized);
390 pragma Loop_Invariant
(Domain
(1 .. J
)'Initialized);
391 pragma Loop_Invariant
(for all K
in 1 .. J
=> Domain
(K
) <= C
);
392 pragma Loop_Invariant
393 (SPARK_Proof_Sorted_Character_Sequence
(Domain
(1 .. J
)));
394 pragma Loop_Invariant
395 (for all D
in Map
'First .. C
=>
399 Indexes
(D
) in 1 .. J
400 and then Domain
(Indexes
(D
)) = D
401 and then Result
(Indexes
(D
)) = Map
(D
)));
402 pragma Loop_Invariant
403 (for all Char
of Domain
(1 .. J
) => Map
(Char
) /= Char
);
404 pragma Loop_Invariant
405 (for all K
in 1 .. J
=> Result
(K
) = Map
(Domain
(K
)));
408 -- Show the equality of Domain and To_Domain(Map)
410 Lemma_Is_Domain
(Map
);
411 Lemma_Domain_Unicity
(Map
, Domain
(1 .. J
), To_Domain
(Map
));
413 (for all K
in 1 .. J
=> Domain
(K
) = To_Domain
(Map
) (K
));
414 pragma Assert
(To_Domain
(Map
)'Length = J
);
416 return Result
(1 .. J
);
423 function To_Ranges
(Set
: Character_Set
) return Character_Ranges
is
424 Max_Ranges
: Character_Ranges
(1 .. Set
'Length / 2 + 1)
425 with Relaxed_Initialization
;
428 C_Iter
: Character with Ghost
;
431 C
:= Character'First;
437 -- Skip gap between subsets
439 while not Set
(C
) loop
440 pragma Loop_Invariant
441 (Character'Pos (C
) >= Character'Pos (C
'Loop_Entry));
442 pragma Loop_Invariant
443 (for all Char
in C
'Loop_Entry .. C
=> not Set
(Char
));
444 pragma Loop_Variant
(Increases
=> C
);
445 exit when C
= Character'Last;
446 C
:= Character'Succ (C
);
449 exit when not Set
(C
);
451 Range_Num
:= Range_Num
+ 1;
452 Max_Ranges
(Range_Num
).Low
:= C
;
457 pragma Loop_Invariant
458 (Character'Pos (C
) >= Character'Pos (C
'Loop_Entry));
459 pragma Loop_Invariant
460 (for all Char
in C
'Loop_Entry .. C
=>
461 (if Char
/= C
then Set
(Char
)));
462 pragma Loop_Variant
(Increases
=> C
);
463 exit when not Set
(C
) or else C
= Character'Last;
464 C
:= Character'Succ (C
);
468 Max_Ranges
(Range_Num
).High
:= C
;
471 Max_Ranges
(Range_Num
).High
:= Character'Pred (C
);
475 (for all Char
in C_Iter
.. C
=>
477 (Char
in Max_Ranges
(Range_Num
).Low
..
478 Max_Ranges
(Range_Num
).High
)));
480 (for all Char
in Character'First .. C_Iter
=>
481 (if Char
/= C_Iter
then
483 (for some Span
of Max_Ranges
(1 .. Range_Num
- 1) =>
484 Char
in Span
.Low
.. Span
.High
))));
486 pragma Loop_Invariant
(2 * Range_Num
<= Character'Pos (C
) + 1);
487 pragma Loop_Invariant
(Max_Ranges
(1 .. Range_Num
)'Initialized);
488 pragma Loop_Invariant
(not Set
(C
));
489 pragma Loop_Invariant
490 (for all Char
in Character'First .. C
=>
492 (for some Span
of Max_Ranges
(1 .. Range_Num
) =>
493 Char
in Span
.Low
.. Span
.High
)));
494 pragma Loop_Invariant
495 (for all Span
of Max_Ranges
(1 .. Range_Num
) =>
496 (for all Char
in Span
.Low
.. Span
.High
=> Set
(Char
)));
497 pragma Loop_Variant
(Increases
=> Range_Num
);
500 return Max_Ranges
(1 .. Range_Num
);
507 function To_Sequence
(Set
: Character_Set
) return Character_Sequence
is
508 Result
: String (1 .. Character'Pos (Character'Last) + 1)
509 with Relaxed_Initialization
;
510 Count
: Natural := 0;
512 for Char
in Set
'Range loop
515 Result
(Count
) := Char
;
518 pragma Loop_Invariant
(Count
<= Character'Pos (Char
) + 1);
519 pragma Loop_Invariant
(Result
(1 .. Count
)'Initialized);
520 pragma Loop_Invariant
(for all K
in 1 .. Count
=> Result
(K
) <= Char
);
521 pragma Loop_Invariant
522 (SPARK_Proof_Sorted_Character_Sequence
(Result
(1 .. Count
)));
523 pragma Loop_Invariant
524 (for all C
in Set
'First .. Char
=>
525 (Set
(C
) = (for some X
of Result
(1 .. Count
) => C
= X
)));
526 pragma Loop_Invariant
527 (for all Char
of Result
(1 .. Count
) => Is_In
(Char
, Set
));
530 return Result
(1 .. Count
);
537 function To_Set
(Ranges
: Character_Ranges
) return Character_Set
is
538 Result
: Character_Set
:= Null_Set
;
540 for R
in Ranges
'Range loop
541 for C
in Ranges
(R
).Low
.. Ranges
(R
).High
loop
543 pragma Loop_Invariant
544 (for all Char
in Character =>
546 ((for some Prev
in Ranges
'First .. R
- 1 =>
547 Char
in Ranges
(Prev
).Low
.. Ranges
(Prev
).High
)
548 or else Char
in Ranges
(R
).Low
.. C
));
551 pragma Loop_Invariant
552 (for all Char
in Character =>
554 (for some Prev
in Ranges
'First .. R
=>
555 Char
in Ranges
(Prev
).Low
.. Ranges
(Prev
).High
));
561 function To_Set
(Span
: Character_Range
) return Character_Set
is
562 Result
: Character_Set
:= Null_Set
;
564 for C
in Span
.Low
.. Span
.High
loop
566 pragma Loop_Invariant
567 (for all Char
in Character =>
568 Result
(Char
) = (Char
in Span
.Low
.. C
));
574 function To_Set
(Sequence
: Character_Sequence
) return Character_Set
is
575 Result
: Character_Set
:= Null_Set
;
577 for J
in Sequence
'Range loop
578 Result
(Sequence
(J
)) := True;
579 pragma Loop_Invariant
580 (for all Char
in Character =>
582 (for some K
in Sequence
'First .. J
=> Char
= Sequence
(K
)));
588 function To_Set
(Singleton
: Character) return Character_Set
is
589 Result
: Character_Set
:= Null_Set
;
591 Result
(Singleton
) := True;
600 (Map
: Character_Mapping
;
601 Element
: Character) return Character
605 end Ada
.Strings
.Maps
;