2015-03-02 Robert Dewar <dewar@adacore.com>
[official-gcc.git] / gcc / ada / a-strsea.adb
blob42d57dfc28352b4144970af6edefd240c2fc0ab7
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT RUN-TIME COMPONENTS --
4 -- --
5 -- A D A . S T R I N G S . S E A R C H --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 1992-2015, Free Software Foundation, Inc. --
10 -- --
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. --
17 -- --
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. --
21 -- --
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/>. --
26 -- --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
29 -- --
30 ------------------------------------------------------------------------------
32 -- Note: This code is derived from the ADAR.CSH public domain Ada 83
33 -- versions of the Appendix C string handling packages (code extracted
34 -- from Ada.Strings.Fixed). A significant change is that we optimize the
35 -- case of identity mappings for Count and Index, and also Index_Non_Blank
36 -- is specialized (rather than using the general Index routine).
38 with Ada.Strings.Maps; use Ada.Strings.Maps;
39 with System; use System;
41 package body Ada.Strings.Search is
43 -----------------------
44 -- Local Subprograms --
45 -----------------------
47 function Belongs
48 (Element : Character;
49 Set : Maps.Character_Set;
50 Test : Membership) return Boolean;
51 pragma Inline (Belongs);
52 -- Determines if the given element is in (Test = Inside) or not in
53 -- (Test = Outside) the given character set.
55 -------------
56 -- Belongs --
57 -------------
59 function Belongs
60 (Element : Character;
61 Set : Maps.Character_Set;
62 Test : Membership) return Boolean
64 begin
65 if Test = Inside then
66 return Is_In (Element, Set);
67 else
68 return not Is_In (Element, Set);
69 end if;
70 end Belongs;
72 -----------
73 -- Count --
74 -----------
76 function Count
77 (Source : String;
78 Pattern : String;
79 Mapping : Maps.Character_Mapping := Maps.Identity) return Natural
81 PL1 : constant Integer := Pattern'Length - 1;
82 Num : Natural;
83 Ind : Natural;
84 Cur : Natural;
86 begin
87 if Pattern = "" then
88 raise Pattern_Error;
89 end if;
91 Num := 0;
92 Ind := Source'First;
94 -- Unmapped case
96 if Mapping'Address = Maps.Identity'Address then
97 while Ind <= Source'Last - PL1 loop
98 if Pattern = Source (Ind .. Ind + PL1) then
99 Num := Num + 1;
100 Ind := Ind + Pattern'Length;
101 else
102 Ind := Ind + 1;
103 end if;
104 end loop;
106 -- Mapped case
108 else
109 while Ind <= Source'Last - PL1 loop
110 Cur := Ind;
111 for K in Pattern'Range loop
112 if Pattern (K) /= Value (Mapping, Source (Cur)) then
113 Ind := Ind + 1;
114 goto Cont;
115 else
116 Cur := Cur + 1;
117 end if;
118 end loop;
120 Num := Num + 1;
121 Ind := Ind + Pattern'Length;
123 <<Cont>>
124 null;
125 end loop;
126 end if;
128 -- Return result
130 return Num;
131 end Count;
133 function Count
134 (Source : String;
135 Pattern : String;
136 Mapping : Maps.Character_Mapping_Function) return Natural
138 PL1 : constant Integer := Pattern'Length - 1;
139 Num : Natural;
140 Ind : Natural;
141 Cur : Natural;
143 begin
144 if Pattern = "" then
145 raise Pattern_Error;
146 end if;
148 -- Check for null pointer in case checks are off
150 if Mapping = null then
151 raise Constraint_Error;
152 end if;
154 Num := 0;
155 Ind := Source'First;
156 while Ind <= Source'Last - PL1 loop
157 Cur := Ind;
158 for K in Pattern'Range loop
159 if Pattern (K) /= Mapping (Source (Cur)) then
160 Ind := Ind + 1;
161 goto Cont;
162 else
163 Cur := Cur + 1;
164 end if;
165 end loop;
167 Num := Num + 1;
168 Ind := Ind + Pattern'Length;
170 <<Cont>>
171 null;
172 end loop;
174 return Num;
175 end Count;
177 function Count
178 (Source : String;
179 Set : Maps.Character_Set) return Natural
181 N : Natural := 0;
183 begin
184 for J in Source'Range loop
185 if Is_In (Source (J), Set) then
186 N := N + 1;
187 end if;
188 end loop;
190 return N;
191 end Count;
193 ----------------
194 -- Find_Token --
195 ----------------
197 procedure Find_Token
198 (Source : String;
199 Set : Maps.Character_Set;
200 From : Positive;
201 Test : Membership;
202 First : out Positive;
203 Last : out Natural)
205 begin
206 -- AI05-031: Raise Index error if Source non-empty and From not in range
208 if Source'Length /= 0 and then From not in Source'Range then
209 raise Index_Error;
210 end if;
212 for J in From .. Source'Last loop
213 if Belongs (Source (J), Set, Test) then
214 First := J;
216 for K in J + 1 .. Source'Last loop
217 if not Belongs (Source (K), Set, Test) then
218 Last := K - 1;
219 return;
220 end if;
221 end loop;
223 -- Here if J indexes first char of token, and all chars after J
224 -- are in the token.
226 Last := Source'Last;
227 return;
228 end if;
229 end loop;
231 -- Here if no token found
233 First := From;
234 Last := 0;
235 end Find_Token;
237 procedure Find_Token
238 (Source : String;
239 Set : Maps.Character_Set;
240 Test : Membership;
241 First : out Positive;
242 Last : out Natural)
244 begin
245 for J in Source'Range loop
246 if Belongs (Source (J), Set, Test) then
247 First := J;
249 for K in J + 1 .. Source'Last loop
250 if not Belongs (Source (K), Set, Test) then
251 Last := K - 1;
252 return;
253 end if;
254 end loop;
256 -- Here if J indexes first char of token, and all chars after J
257 -- are in the token.
259 Last := Source'Last;
260 return;
261 end if;
262 end loop;
264 -- Here if no token found
266 -- RM 2005 A.4.3 (68/1) specifies that an exception must be raised if
267 -- Source'First is not positive and is assigned to First. Formulation
268 -- is slightly different in RM 2012, but the intent seems similar, so
269 -- we check explicitly for that condition.
271 if Source'First not in Positive then
272 raise Constraint_Error;
274 else
275 First := Source'First;
276 Last := 0;
277 end if;
278 end Find_Token;
280 -----------
281 -- Index --
282 -----------
284 function Index
285 (Source : String;
286 Pattern : String;
287 Going : Direction := Forward;
288 Mapping : Maps.Character_Mapping := Maps.Identity) return Natural
290 PL1 : constant Integer := Pattern'Length - 1;
291 Cur : Natural;
293 Ind : Integer;
294 -- Index for start of match check. This can be negative if the pattern
295 -- length is greater than the string length, which is why this variable
296 -- is Integer instead of Natural. In this case, the search loops do not
297 -- execute at all, so this Ind value is never used.
299 begin
300 if Pattern = "" then
301 raise Pattern_Error;
302 end if;
304 -- Forwards case
306 if Going = Forward then
307 Ind := Source'First;
309 -- Unmapped forward case
311 if Mapping'Address = Maps.Identity'Address then
312 for J in 1 .. Source'Length - PL1 loop
313 if Pattern = Source (Ind .. Ind + PL1) then
314 return Ind;
315 else
316 Ind := Ind + 1;
317 end if;
318 end loop;
320 -- Mapped forward case
322 else
323 for J in 1 .. Source'Length - PL1 loop
324 Cur := Ind;
326 for K in Pattern'Range loop
327 if Pattern (K) /= Value (Mapping, Source (Cur)) then
328 goto Cont1;
329 else
330 Cur := Cur + 1;
331 end if;
332 end loop;
334 return Ind;
336 <<Cont1>>
337 Ind := Ind + 1;
338 end loop;
339 end if;
341 -- Backwards case
343 else
344 -- Unmapped backward case
346 Ind := Source'Last - PL1;
348 if Mapping'Address = Maps.Identity'Address then
349 for J in reverse 1 .. Source'Length - PL1 loop
350 if Pattern = Source (Ind .. Ind + PL1) then
351 return Ind;
352 else
353 Ind := Ind - 1;
354 end if;
355 end loop;
357 -- Mapped backward case
359 else
360 for J in reverse 1 .. Source'Length - PL1 loop
361 Cur := Ind;
363 for K in Pattern'Range loop
364 if Pattern (K) /= Value (Mapping, Source (Cur)) then
365 goto Cont2;
366 else
367 Cur := Cur + 1;
368 end if;
369 end loop;
371 return Ind;
373 <<Cont2>>
374 Ind := Ind - 1;
375 end loop;
376 end if;
377 end if;
379 -- Fall through if no match found. Note that the loops are skipped
380 -- completely in the case of the pattern being longer than the source.
382 return 0;
383 end Index;
385 function Index
386 (Source : String;
387 Pattern : String;
388 Going : Direction := Forward;
389 Mapping : Maps.Character_Mapping_Function) return Natural
391 PL1 : constant Integer := Pattern'Length - 1;
392 Ind : Natural;
393 Cur : Natural;
395 begin
396 if Pattern = "" then
397 raise Pattern_Error;
398 end if;
400 -- Check for null pointer in case checks are off
402 if Mapping = null then
403 raise Constraint_Error;
404 end if;
406 -- If Pattern longer than Source it can't be found
408 if Pattern'Length > Source'Length then
409 return 0;
410 end if;
412 -- Forwards case
414 if Going = Forward then
415 Ind := Source'First;
416 for J in 1 .. Source'Length - PL1 loop
417 Cur := Ind;
419 for K in Pattern'Range loop
420 if Pattern (K) /= Mapping.all (Source (Cur)) then
421 goto Cont1;
422 else
423 Cur := Cur + 1;
424 end if;
425 end loop;
427 return Ind;
429 <<Cont1>>
430 Ind := Ind + 1;
431 end loop;
433 -- Backwards case
435 else
436 Ind := Source'Last - PL1;
437 for J in reverse 1 .. Source'Length - PL1 loop
438 Cur := Ind;
440 for K in Pattern'Range loop
441 if Pattern (K) /= Mapping.all (Source (Cur)) then
442 goto Cont2;
443 else
444 Cur := Cur + 1;
445 end if;
446 end loop;
448 return Ind;
450 <<Cont2>>
451 Ind := Ind - 1;
452 end loop;
453 end if;
455 -- Fall through if no match found. Note that the loops are skipped
456 -- completely in the case of the pattern being longer than the source.
458 return 0;
459 end Index;
461 function Index
462 (Source : String;
463 Set : Maps.Character_Set;
464 Test : Membership := Inside;
465 Going : Direction := Forward) return Natural
467 begin
468 -- Forwards case
470 if Going = Forward then
471 for J in Source'Range loop
472 if Belongs (Source (J), Set, Test) then
473 return J;
474 end if;
475 end loop;
477 -- Backwards case
479 else
480 for J in reverse Source'Range loop
481 if Belongs (Source (J), Set, Test) then
482 return J;
483 end if;
484 end loop;
485 end if;
487 -- Fall through if no match
489 return 0;
490 end Index;
492 function Index
493 (Source : String;
494 Pattern : String;
495 From : Positive;
496 Going : Direction := Forward;
497 Mapping : Maps.Character_Mapping := Maps.Identity) return Natural
499 begin
501 -- AI05-056: If source is empty result is always zero
503 if Source'Length = 0 then
504 return 0;
506 elsif Going = Forward then
507 if From < Source'First then
508 raise Index_Error;
509 end if;
511 return
512 Index (Source (From .. Source'Last), Pattern, Forward, Mapping);
514 else
515 if From > Source'Last then
516 raise Index_Error;
517 end if;
519 return
520 Index (Source (Source'First .. From), Pattern, Backward, Mapping);
521 end if;
522 end Index;
524 function Index
525 (Source : String;
526 Pattern : String;
527 From : Positive;
528 Going : Direction := Forward;
529 Mapping : Maps.Character_Mapping_Function) return Natural
531 begin
533 -- AI05-056: If source is empty result is always zero
535 if Source'Length = 0 then
536 return 0;
538 elsif Going = Forward then
539 if From < Source'First then
540 raise Index_Error;
541 end if;
543 return Index
544 (Source (From .. Source'Last), Pattern, Forward, Mapping);
546 else
547 if From > Source'Last then
548 raise Index_Error;
549 end if;
551 return Index
552 (Source (Source'First .. From), Pattern, Backward, Mapping);
553 end if;
554 end Index;
556 function Index
557 (Source : String;
558 Set : Maps.Character_Set;
559 From : Positive;
560 Test : Membership := Inside;
561 Going : Direction := Forward) return Natural
563 begin
565 -- AI05-056 : if source is empty result is always 0.
567 if Source'Length = 0 then
568 return 0;
570 elsif Going = Forward then
571 if From < Source'First then
572 raise Index_Error;
573 end if;
575 return
576 Index (Source (From .. Source'Last), Set, Test, Forward);
578 else
579 if From > Source'Last then
580 raise Index_Error;
581 end if;
583 return
584 Index (Source (Source'First .. From), Set, Test, Backward);
585 end if;
586 end Index;
588 ---------------------
589 -- Index_Non_Blank --
590 ---------------------
592 function Index_Non_Blank
593 (Source : String;
594 Going : Direction := Forward) return Natural
596 begin
597 if Going = Forward then
598 for J in Source'Range loop
599 if Source (J) /= ' ' then
600 return J;
601 end if;
602 end loop;
604 else -- Going = Backward
605 for J in reverse Source'Range loop
606 if Source (J) /= ' ' then
607 return J;
608 end if;
609 end loop;
610 end if;
612 -- Fall through if no match
614 return 0;
615 end Index_Non_Blank;
617 function Index_Non_Blank
618 (Source : String;
619 From : Positive;
620 Going : Direction := Forward) return Natural
622 begin
623 if Going = Forward then
624 if From < Source'First then
625 raise Index_Error;
626 end if;
628 return
629 Index_Non_Blank (Source (From .. Source'Last), Forward);
631 else
632 if From > Source'Last then
633 raise Index_Error;
634 end if;
636 return
637 Index_Non_Blank (Source (Source'First .. From), Backward);
638 end if;
639 end Index_Non_Blank;
641 end Ada.Strings.Search;