2010-11-11 Jakub Jelinek <jakub@redhat.com>
[official-gcc.git] / gcc / ada / a-strsea.adb
blob6f458ff23957d5af323a72d99af0a091c0f7d3ef
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-2010, 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 for J in From .. Source'Last loop
207 if Belongs (Source (J), Set, Test) then
208 First := J;
210 for K in J + 1 .. Source'Last loop
211 if not Belongs (Source (K), Set, Test) then
212 Last := K - 1;
213 return;
214 end if;
215 end loop;
217 -- Here if J indexes first char of token, and all chars after J
218 -- are in the token.
220 Last := Source'Last;
221 return;
222 end if;
223 end loop;
225 -- Here if no token found
227 First := From;
228 Last := 0;
229 end Find_Token;
231 procedure Find_Token
232 (Source : String;
233 Set : Maps.Character_Set;
234 Test : Membership;
235 First : out Positive;
236 Last : out Natural)
238 begin
239 for J in Source'Range loop
240 if Belongs (Source (J), Set, Test) then
241 First := J;
243 for K in J + 1 .. Source'Last loop
244 if not Belongs (Source (K), Set, Test) then
245 Last := K - 1;
246 return;
247 end if;
248 end loop;
250 -- Here if J indexes first char of token, and all chars after J
251 -- are in the token.
253 Last := Source'Last;
254 return;
255 end if;
256 end loop;
258 -- Here if no token found
260 First := Source'First;
261 Last := 0;
262 end Find_Token;
264 -----------
265 -- Index --
266 -----------
268 function Index
269 (Source : String;
270 Pattern : String;
271 Going : Direction := Forward;
272 Mapping : Maps.Character_Mapping := Maps.Identity) return Natural
274 PL1 : constant Integer := Pattern'Length - 1;
275 Cur : Natural;
277 Ind : Integer;
278 -- Index for start of match check. This can be negative if the pattern
279 -- length is greater than the string length, which is why this variable
280 -- is Integer instead of Natural. In this case, the search loops do not
281 -- execute at all, so this Ind value is never used.
283 begin
284 if Pattern = "" then
285 raise Pattern_Error;
286 end if;
288 -- Forwards case
290 if Going = Forward then
291 Ind := Source'First;
293 -- Unmapped forward case
295 if Mapping'Address = Maps.Identity'Address then
296 for J in 1 .. Source'Length - PL1 loop
297 if Pattern = Source (Ind .. Ind + PL1) then
298 return Ind;
299 else
300 Ind := Ind + 1;
301 end if;
302 end loop;
304 -- Mapped forward case
306 else
307 for J in 1 .. Source'Length - PL1 loop
308 Cur := Ind;
310 for K in Pattern'Range loop
311 if Pattern (K) /= Value (Mapping, Source (Cur)) then
312 goto Cont1;
313 else
314 Cur := Cur + 1;
315 end if;
316 end loop;
318 return Ind;
320 <<Cont1>>
321 Ind := Ind + 1;
322 end loop;
323 end if;
325 -- Backwards case
327 else
328 -- Unmapped backward case
330 Ind := Source'Last - PL1;
332 if Mapping'Address = Maps.Identity'Address then
333 for J in reverse 1 .. Source'Length - PL1 loop
334 if Pattern = Source (Ind .. Ind + PL1) then
335 return Ind;
336 else
337 Ind := Ind - 1;
338 end if;
339 end loop;
341 -- Mapped backward case
343 else
344 for J in reverse 1 .. Source'Length - PL1 loop
345 Cur := Ind;
347 for K in Pattern'Range loop
348 if Pattern (K) /= Value (Mapping, Source (Cur)) then
349 goto Cont2;
350 else
351 Cur := Cur + 1;
352 end if;
353 end loop;
355 return Ind;
357 <<Cont2>>
358 Ind := Ind - 1;
359 end loop;
360 end if;
361 end if;
363 -- Fall through if no match found. Note that the loops are skipped
364 -- completely in the case of the pattern being longer than the source.
366 return 0;
367 end Index;
369 function Index
370 (Source : String;
371 Pattern : String;
372 Going : Direction := Forward;
373 Mapping : Maps.Character_Mapping_Function) return Natural
375 PL1 : constant Integer := Pattern'Length - 1;
376 Ind : Natural;
377 Cur : Natural;
379 begin
380 if Pattern = "" then
381 raise Pattern_Error;
382 end if;
384 -- Check for null pointer in case checks are off
386 if Mapping = null then
387 raise Constraint_Error;
388 end if;
390 -- If Pattern longer than Source it can't be found
392 if Pattern'Length > Source'Length then
393 return 0;
394 end if;
396 -- Forwards case
398 if Going = Forward then
399 Ind := Source'First;
400 for J in 1 .. Source'Length - PL1 loop
401 Cur := Ind;
403 for K in Pattern'Range loop
404 if Pattern (K) /= Mapping.all (Source (Cur)) then
405 goto Cont1;
406 else
407 Cur := Cur + 1;
408 end if;
409 end loop;
411 return Ind;
413 <<Cont1>>
414 Ind := Ind + 1;
415 end loop;
417 -- Backwards case
419 else
420 Ind := Source'Last - PL1;
421 for J in reverse 1 .. Source'Length - PL1 loop
422 Cur := Ind;
424 for K in Pattern'Range loop
425 if Pattern (K) /= Mapping.all (Source (Cur)) then
426 goto Cont2;
427 else
428 Cur := Cur + 1;
429 end if;
430 end loop;
432 return Ind;
434 <<Cont2>>
435 Ind := Ind - 1;
436 end loop;
437 end if;
439 -- Fall through if no match found. Note that the loops are skipped
440 -- completely in the case of the pattern being longer than the source.
442 return 0;
443 end Index;
445 function Index
446 (Source : String;
447 Set : Maps.Character_Set;
448 Test : Membership := Inside;
449 Going : Direction := Forward) return Natural
451 begin
452 -- Forwards case
454 if Going = Forward then
455 for J in Source'Range loop
456 if Belongs (Source (J), Set, Test) then
457 return J;
458 end if;
459 end loop;
461 -- Backwards case
463 else
464 for J in reverse Source'Range loop
465 if Belongs (Source (J), Set, Test) then
466 return J;
467 end if;
468 end loop;
469 end if;
471 -- Fall through if no match
473 return 0;
474 end Index;
476 function Index
477 (Source : String;
478 Pattern : String;
479 From : Positive;
480 Going : Direction := Forward;
481 Mapping : Maps.Character_Mapping := Maps.Identity) return Natural
483 begin
484 if Going = Forward then
485 if From < Source'First then
486 raise Index_Error;
487 end if;
489 return
490 Index (Source (From .. Source'Last), Pattern, Forward, Mapping);
492 else
493 if From > Source'Last then
494 raise Index_Error;
495 end if;
497 return
498 Index (Source (Source'First .. From), Pattern, Backward, Mapping);
499 end if;
500 end Index;
502 function Index
503 (Source : String;
504 Pattern : String;
505 From : Positive;
506 Going : Direction := Forward;
507 Mapping : Maps.Character_Mapping_Function) return Natural
509 begin
510 if Going = Forward then
511 if From < Source'First then
512 raise Index_Error;
513 end if;
515 return Index
516 (Source (From .. Source'Last), Pattern, Forward, Mapping);
518 else
519 if From > Source'Last then
520 raise Index_Error;
521 end if;
523 return Index
524 (Source (Source'First .. From), Pattern, Backward, Mapping);
525 end if;
526 end Index;
528 function Index
529 (Source : String;
530 Set : Maps.Character_Set;
531 From : Positive;
532 Test : Membership := Inside;
533 Going : Direction := Forward) return Natural
535 begin
536 if Going = Forward then
537 if From < Source'First then
538 raise Index_Error;
539 end if;
541 return
542 Index (Source (From .. Source'Last), Set, Test, Forward);
544 else
545 if From > Source'Last then
546 raise Index_Error;
547 end if;
549 return
550 Index (Source (Source'First .. From), Set, Test, Backward);
551 end if;
552 end Index;
554 ---------------------
555 -- Index_Non_Blank --
556 ---------------------
558 function Index_Non_Blank
559 (Source : String;
560 Going : Direction := Forward) return Natural
562 begin
563 if Going = Forward then
564 for J in Source'Range loop
565 if Source (J) /= ' ' then
566 return J;
567 end if;
568 end loop;
570 else -- Going = Backward
571 for J in reverse Source'Range loop
572 if Source (J) /= ' ' then
573 return J;
574 end if;
575 end loop;
576 end if;
578 -- Fall through if no match
580 return 0;
581 end Index_Non_Blank;
583 function Index_Non_Blank
584 (Source : String;
585 From : Positive;
586 Going : Direction := Forward) return Natural
588 begin
589 if Going = Forward then
590 if From < Source'First then
591 raise Index_Error;
592 end if;
594 return
595 Index_Non_Blank (Source (From .. Source'Last), Forward);
597 else
598 if From > Source'Last then
599 raise Index_Error;
600 end if;
602 return
603 Index_Non_Blank (Source (Source'First .. From), Backward);
604 end if;
605 end Index_Non_Blank;
607 end Ada.Strings.Search;