home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Geek Gadgets 1
/
ADE-1.bin
/
ade-dist
/
gnat-2.06-src.tgz
/
tar.out
/
fsf
/
gnat
/
ada
/
a-stwima.adb
< prev
next >
Wrap
Text File
|
1996-09-28
|
16KB
|
610 lines
------------------------------------------------------------------------------
-- --
-- GNAT RUNTIME COMPONENTS --
-- --
-- A D A . S T R I N G S . W I D E _ M A P S --
-- --
-- B o d y --
-- --
-- $Revision: 1.7 $ --
-- --
-- Copyright (c) 1992,1993,1994 NYU, All Rights Reserved --
-- --
-- The GNAT library is free software; you can redistribute it and/or modify --
-- it under terms of the GNU Library General Public License as published by --
-- the Free Software Foundation; either version 2, or (at your option) any --
-- later version. The GNAT library is distributed in the hope that it will --
-- be useful, but WITHOUT ANY WARRANTY; without even the implied warranty --
-- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU --
-- Library General Public License for more details. You should have --
-- received a copy of the GNU Library General Public License along with --
-- the GNAT library; see the file COPYING.LIB. If not, write to the Free --
-- Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. --
-- --
------------------------------------------------------------------------------
package body Ada.Strings.Wide_Maps is
---------
-- "=" --
---------
-- The sorted, discontiguous form is canonical, so equality can be used
function "=" (Left, Right : in Wide_Character_Set) return Boolean is
begin
return Left.all = Right.all;
end "=";
---------
-- "-" --
---------
function "-"
(Left, Right : in Wide_Character_Set)
return Wide_Character_Set
is
Result : Wide_Character_Ranges (1 .. Left'Length + Right'Length);
-- Each range on the right can generate at least one more range in
-- the result, by splitting one of the left operand ranges.
N : Natural := 0;
R : Natural := 1;
W : Wide_Character;
begin
-- Basic loop is through ranges of left set
for L in Left'Range loop
-- W is lowest element of current left range not dealt with yet
W := Left (L).Low;
-- Skip by ranges of right set that have no impact on us
while R <= Right'Length and then Right (R).High < W loop
R := R + 1;
end loop;
-- Deal with ranges on right that create holes in the left range
while R <= Right'Length and then Right (R).High < Left (L).High loop
N := N + 1;
Result (N).Low := W;
Result (N).High := Right (R).High;
R := R + 1;
end loop;
-- Now we have to output the final piece of the left range if any
if R <= Right'Length and then Right (R).Low <= Left (L).High then
-- Current right range consumes all of the rest of left range
if Right (R).Low < W then
null;
-- Current right range consumes part of the rest of left range
else
N := N + 1;
Result (N).Low := W;
Result (N).High := Wide_Character'Pred (Right (R).Low);
end if;
-- Rest of left range to be retained complete
else
N := N + 1;
Result (N).Low := W;
Result (N).High := Left (L).High;
end if;
end loop;
return new Wide_Character_Ranges'(Result (1 .. N));
end "-";
-----------
-- "and" --
-----------
function "and"
(Left, Right : in Wide_Character_Set)
return Wide_Character_Set
is
Result : Wide_Character_Ranges (1 .. Left.all'Length + Right.all'Length);
N : Natural := 0;
L, R : Natural := 1;
begin
-- Loop to search for overlapping character ranges
loop
exit when L > Left.all'Last;
exit when R > Right.all'Last;
if Left (L).High < Right (R).Low then
L := L + 1;
elsif Right (R).High < Left (L).Low then
R := R + 1;
-- Here we have Left.High >= Right.Low
-- and Right.High >= Left.Low
-- so we have an overlapping range
else
N := N + 1;
Result (N).Low :=
Wide_Character'Max (Left (L).Low, Right (R).Low);
Result (N).High :=
Wide_Character'Min (Left (L).High, Right (R).High);
if Right (R).High = Left (L).High then
L := L + 1;
R := R + 1;
elsif Right (R).High < Left (L).High then
R := R + 1;
else
L := L + 1;
end if;
end if;
end loop;
return new Wide_Character_Ranges'(Result (1 .. N));
end "and";
-----------
-- "not" --
-----------
function "not"
(Right : in Wide_Character_Set)
return Wide_Character_Set
is
Result : Wide_Character_Ranges (1 .. Right.all'Length + 1);
N : Natural := 0;
begin
if Right = Null_Set then
N := 1;
Result (1)
:= (Low => Wide_Character'First, High => Wide_Character'Last);
else
if Right (1).Low /= Wide_Character'First then
N := N + 1;
Result (N).Low := Wide_Character'First;
Result (N).High := Wide_Character'Pred (Right (1).Low);
end if;
for K in 1 .. Right.all'Last - 1 loop
N := N + 1;
Result (N).Low := Wide_Character'Succ (Right (K).High);
Result (N).High := Wide_Character'Pred (Right (K + 1).Low);
end loop;
if Right (Right.all'Last).High /= Wide_Character'Last then
N := N + 1;
Result (N).Low := Wide_Character'Succ (Right (Right'Last).High);
Result (N).High := Wide_Character'Pred (Right (1).Low);
end if;
end if;
return new Wide_Character_Ranges'(Result (1 .. N));
end "not";
----------
-- "or" --
----------
function "or"
(Left, Right : in Wide_Character_Set)
return Wide_Character_Set
is
Result : Wide_Character_Ranges (1 .. Left'Length + Right'Length);
N : Natural;
L, R : Natural;
begin
if Left'Length = 0 then
return Right;
elsif Right'Length = 0 then
return Left;
else
N := 1;
Result (1) := Left (1);
L := 2;
R := 1;
loop
-- Collapse next left range into current result range if possible
if L <= Left'Length
and then Wide_Character'Pos (Left (L).Low) <=
Wide_Character'Pos (Result (N).High) + 1
then
Result (N).High :=
Wide_Character'Max (Result (N).High, Left (L).High);
L := L + 1;
-- Collapse next right range into current result range if possible
elsif R <= Right'Length
and then Wide_Character'Pos (Right (R).Low) <=
Wide_Character'Pos (Result (N).High) + 1
then
Result (N).High :=
Wide_Character'Max (Result (N).High, Right (R).High);
R := R + 1;
-- Otherwise establish new result range
else
if L <= Left'Length then
N := N + 1;
Result (N) := Left (L);
L := L + 1;
elsif R <= Right'Length then
N := N + 1;
Result (N) := Right (R);
R := R + 1;
else
exit;
end if;
end if;
end loop;
end if;
return new Wide_Character_Ranges'(Result (1 .. N));
end "or";
-----------
-- "xor" --
-----------
function "xor"
(Left, Right : in Wide_Character_Set)
return Wide_Character_Set
is
Result : Wide_Character_Ranges (1 .. Left'Length + Right'Length);
N : Natural := 0;
L, R : Natural := 1;
begin
return (Left or Right) - (Left and Right);
end "xor";
-----------
-- Is_In --
-----------
function Is_In
(Element : in Wide_Character;
Set : in Wide_Character_Set)
return Boolean
is
L, R, M : Natural;
begin
L := Set'First;
R := Set'Last;
-- Binary search loop. The invariant is that if Element is in any of
-- of the constituent ranges it is in one between Set (L) and Set (R).
loop
if L > R then
return False;
else
M := (L + R) / 2;
if Element > Set (M).High then
L := M + 1;
elsif Element < Set (M).Low then
R := M - 1;
else
return True;
end if;
end if;
end loop;
end Is_In;
---------------
-- Is_Subset --
---------------
function Is_Subset
(Elements : in Wide_Character_Set;
Set : in Wide_Character_Set)
return Boolean
is
S : Positive := 1;
E : Positive := 1;
begin
loop
-- If no more element ranges, done, and result is true
if E > Elements'Length then
return True;
-- If more element ranges, but no more set ranges, result is false
elsif S > Set'Length then
return False;
-- Remove irrelevant set range
elsif Set (S).High < Elements (E).Low then
S := S + 1;
-- Get rid of element range that is properly covered by set
elsif Set (S).Low <= Elements (E).Low
and then Elements (E).High <= Set (S).High
then
E := E + 1;
-- Otherwise we have a non-covered element range, result is false
else
return False;
end if;
end loop;
end Is_Subset;
---------------
-- To_Domain --
---------------
function To_Domain
(Map : in Wide_Character_Mapping)
return Wide_Character_Sequence
is
begin
return Map.Domain.all;
end To_Domain;
----------------
-- To_Mapping --
----------------
function To_Mapping
(From, To : in Wide_Character_Sequence)
return Wide_Character_Mapping
is
Domain : Wide_Character_Sequence (1 .. From'Length);
Rangev : Wide_Character_Sequence (1 .. To'Length);
N : Natural := 0;
K : Natural := 0;
begin
if From'Length /= To'Length then
raise Translation_Error;
else
for J in From'Range loop
for M in 1 .. N loop
if From (J) = Domain (M) then
raise Translation_Error;
elsif From (J) < Domain (M) then
Domain (M + 1 .. N + 1) := Domain (M .. N);
Domain (M) := From (J);
Rangev (M) := To (J);
goto Continue;
end if;
end loop;
Domain (N + 1) := From (J);
Rangev (N + 1) := To (J);
<<Continue>>
N := N + 1;
end loop;
return (Domain => new Wide_Character_Sequence'(Domain (1 .. N)),
Rangev => new Wide_Character_Sequence'(Rangev (1 .. N)));
end if;
end To_Mapping;
--------------
-- To_Range --
--------------
function To_Range
(Map : in Wide_Character_Mapping)
return Wide_Character_Sequence
is
begin
return Map.Rangev.all;
end To_Range;
---------------
-- To_Ranges --
---------------
function To_Ranges
(Set : in Wide_Character_Set)
return Wide_Character_Ranges
is
begin
return Set.all;
end To_Ranges;
-----------------
-- To_Sequence --
-----------------
function To_Sequence
(Set : in Wide_Character_Set)
return Wide_Character_Sequence
is
Result : Wide_String (Positive range 1 .. 2 ** 16);
N : Natural := 0;
begin
for J in Set'Range loop
for K in Set (J).Low .. Set (J).High loop
N := N + 1;
Result (N) := K;
end loop;
end loop;
return Result (1 .. N);
end To_Sequence;
------------
-- To_Set --
------------
-- Case of multiple range input
function To_Set
(Ranges : in Wide_Character_Ranges)
return Wide_Character_Set
is
Result : Wide_Character_Ranges (Ranges'Range);
N : Natural := 0;
J : Natural;
begin
-- The output of To_Set is required to be sorted by increasing Low
-- values, and discontiguous, so first we sort them as we enter them,
-- using a simple insertion sort.
for J in Ranges'Range loop
for K in 1 .. N loop
if Ranges (J).Low < Result (K).Low then
Result (K + 1 .. N + 1) := Result (K .. N);
Result (K) := Ranges (J);
goto Continue;
end if;
end loop;
Result (N + 1) := Ranges (J);
<<Continue>>
N := N + 1;
end loop;
-- Now collapse any contiguous or overlapping ranges
J := 1;
while J < N loop
if Result (J).High < Result (J).Low then
N := N - 1;
Result (J .. N) := Result (J + 1 .. N + 1);
elsif Wide_Character'Pos (Result (J).High) + 1 >=
Wide_Character'Pos (Result (J + 1).Low)
then
Result (J).High :=
Wide_Character'Max (Result (J).High, Result (J + 1).High);
N := N - 1;
Result (J + 1 .. N) := Result (J + 2 .. N + 1);
else
J := J + 1;
end if;
end loop;
if Result (N).High > Result (N).Low then
N := N - 1;
end if;
return new Wide_Character_Ranges'(Result (1 .. N));
end To_Set;
-- Case of single range input
function To_Set
(Span : in Wide_Character_Range)
return Wide_Character_Set
is
begin
if Span.Low > Span.High then
return Null_Set;
-- This is safe, because there is no procedure with parameter
-- Wide_Character_Set on mode "out" or "in out".
else
return new Wide_Character_Ranges'(1 => Span);
end if;
end To_Set;
-- Case of wide string input
function To_Set
(Sequence : in Wide_Character_Sequence)
return Wide_Character_Set
is
R : Wide_Character_Ranges (1 .. Sequence'Length);
begin
for J in R'Range loop
R (J) := (Sequence (J), Sequence (J));
end loop;
return To_Set (R);
end To_Set;
-- Case of single wide character input
function To_Set
(Singleton : in Wide_Character)
return Wide_Character_Set
is
begin
return new Wide_Character_Ranges'(1 => (Singleton, Singleton));
end To_Set;
-----------
-- Value --
-----------
function Value
(Map : in Wide_Character_Mapping;
Element : in Wide_Character)
return Wide_Character
is
L, R, M : Natural;
begin
L := 1;
R := Map.Domain'Last;
-- Binary search loop
loop
-- If not found, identity
if L > R then
return Element;
-- Otherwise do binary divide
else
M := (L + R) / 2;
if Element < Map.Domain (M) then
R := M - 1;
elsif Element > Map.Domain (M) then
L := M + 1;
else -- Element = Map.Domain (M) then
return Map.Rangev (M);
end if;
end if;
end loop;
end Value;
end Ada.Strings.Wide_Maps;