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 >
Text File  |  1996-09-28  |  16KB  |  610 lines

  1. ------------------------------------------------------------------------------
  2. --                                                                          --
  3. --                         GNAT RUNTIME COMPONENTS                          --
  4. --                                                                          --
  5. --                A D A . S T R I N G S . W I D E _ M A P S                 --
  6. --                                                                          --
  7. --                                 B o d y                                  --
  8. --                                                                          --
  9. --                            $Revision: 1.7 $                              --
  10. --                                                                          --
  11. --           Copyright (c) 1992,1993,1994 NYU, All Rights Reserved          --
  12. --                                                                          --
  13. -- The GNAT library is free software; you can redistribute it and/or modify --
  14. -- it under terms of the GNU Library General Public License as published by --
  15. -- the Free Software  Foundation; either version 2, or (at your option) any --
  16. -- later version.  The GNAT library is distributed in the hope that it will --
  17. -- be useful, but WITHOUT ANY WARRANTY;  without even  the implied warranty --
  18. -- of MERCHANTABILITY  or  FITNESS FOR  A PARTICULAR PURPOSE.  See the  GNU --
  19. -- Library  General  Public  License for  more  details.  You  should  have --
  20. -- received  a copy of the GNU  Library  General Public License  along with --
  21. -- the GNAT library;  see the file  COPYING.LIB.  If not, write to the Free --
  22. -- Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.        --
  23. --                                                                          --
  24. ------------------------------------------------------------------------------
  25.  
  26. package body Ada.Strings.Wide_Maps is
  27.  
  28.    ---------
  29.    -- "=" --
  30.    ---------
  31.  
  32.    --  The sorted, discontiguous form is canonical, so equality can be used
  33.  
  34.    function "=" (Left, Right : in Wide_Character_Set) return Boolean is
  35.    begin
  36.       return Left.all = Right.all;
  37.    end "=";
  38.  
  39.    ---------
  40.    -- "-" --
  41.    ---------
  42.  
  43.    function "-"
  44.      (Left, Right : in Wide_Character_Set)
  45.       return        Wide_Character_Set
  46.    is
  47.       Result : Wide_Character_Ranges (1 .. Left'Length + Right'Length);
  48.       --  Each range on the right can generate at least one more range in
  49.       --  the result, by splitting one of the left operand ranges.
  50.  
  51.       N : Natural := 0;
  52.       R : Natural := 1;
  53.       W : Wide_Character;
  54.  
  55.    begin
  56.       --  Basic loop is through ranges of left set
  57.  
  58.       for L in Left'Range loop
  59.  
  60.          --  W is lowest element of current left range not dealt with yet
  61.  
  62.          W := Left (L).Low;
  63.  
  64.          --  Skip by ranges of right set that have no impact on us
  65.  
  66.          while R <= Right'Length and then Right (R).High < W loop
  67.             R := R + 1;
  68.          end loop;
  69.  
  70.          --  Deal with ranges on right that create holes in the left range
  71.  
  72.          while R <= Right'Length and then Right (R).High < Left (L).High loop
  73.             N := N + 1;
  74.             Result (N).Low  := W;
  75.             Result (N).High := Right (R).High;
  76.             R := R + 1;
  77.          end loop;
  78.  
  79.          --  Now we have to output the final piece of the left range if any
  80.  
  81.          if R <= Right'Length and then Right (R).Low <= Left (L).High then
  82.  
  83.             --  Current right range consumes all of the rest of left range
  84.  
  85.             if Right (R).Low < W then
  86.                null;
  87.  
  88.             --  Current right range consumes part of the rest of left range
  89.  
  90.             else
  91.                N := N + 1;
  92.                Result (N).Low  := W;
  93.                Result (N).High := Wide_Character'Pred (Right (R).Low);
  94.             end if;
  95.  
  96.          --  Rest of left range to be retained complete
  97.  
  98.          else
  99.             N := N + 1;
  100.             Result (N).Low  := W;
  101.             Result (N).High := Left (L).High;
  102.          end if;
  103.       end loop;
  104.  
  105.       return new Wide_Character_Ranges'(Result (1 .. N));
  106.    end "-";
  107.  
  108.    -----------
  109.    -- "and" --
  110.    -----------
  111.  
  112.    function "and"
  113.      (Left, Right : in Wide_Character_Set)
  114.       return        Wide_Character_Set
  115.    is
  116.       Result : Wide_Character_Ranges (1 .. Left.all'Length + Right.all'Length);
  117.       N      : Natural := 0;
  118.       L, R   : Natural := 1;
  119.  
  120.    begin
  121.       --  Loop to search for overlapping character ranges
  122.  
  123.       loop
  124.          exit when L > Left.all'Last;
  125.          exit when R > Right.all'Last;
  126.  
  127.          if Left (L).High < Right (R).Low then
  128.             L := L + 1;
  129.  
  130.          elsif Right (R).High < Left (L).Low then
  131.             R := R + 1;
  132.  
  133.          --  Here we have Left.High  >= Right.Low
  134.          --           and Right.High >= Left.Low
  135.          --  so we have an overlapping range
  136.  
  137.          else
  138.             N := N + 1;
  139.             Result (N).Low :=
  140.               Wide_Character'Max (Left (L).Low,  Right (R).Low);
  141.             Result (N).High :=
  142.               Wide_Character'Min (Left (L).High, Right (R).High);
  143.             if Right (R).High = Left (L).High then
  144.                L := L + 1;
  145.                R := R + 1;
  146.             elsif Right (R).High < Left (L).High then
  147.                R := R + 1;
  148.             else
  149.                L := L + 1;
  150.             end if;
  151.          end if;
  152.       end loop;
  153.  
  154.       return new Wide_Character_Ranges'(Result (1 .. N));
  155.    end "and";
  156.  
  157.    -----------
  158.    -- "not" --
  159.    -----------
  160.  
  161.    function "not"
  162.      (Right  : in Wide_Character_Set)
  163.       return Wide_Character_Set
  164.    is
  165.       Result : Wide_Character_Ranges (1 .. Right.all'Length + 1);
  166.       N      : Natural := 0;
  167.  
  168.    begin
  169.       if Right = Null_Set then
  170.          N := 1;
  171.          Result (1)
  172.            := (Low => Wide_Character'First, High => Wide_Character'Last);
  173.       else
  174.          if Right (1).Low /= Wide_Character'First then
  175.             N := N + 1;
  176.             Result (N).Low  := Wide_Character'First;
  177.             Result (N).High := Wide_Character'Pred (Right (1).Low);
  178.          end if;
  179.  
  180.          for K in 1 .. Right.all'Last - 1 loop
  181.             N := N + 1;
  182.             Result (N).Low  := Wide_Character'Succ (Right (K).High);
  183.             Result (N).High := Wide_Character'Pred (Right (K + 1).Low);
  184.          end loop;
  185.  
  186.          if Right (Right.all'Last).High /= Wide_Character'Last then
  187.             N := N + 1;
  188.             Result (N).Low  := Wide_Character'Succ (Right (Right'Last).High);
  189.             Result (N).High := Wide_Character'Pred (Right (1).Low);
  190.          end if;
  191.       end if;
  192.  
  193.       return new Wide_Character_Ranges'(Result (1 .. N));
  194.    end "not";
  195.  
  196.    ----------
  197.    -- "or" --
  198.    ----------
  199.  
  200.    function "or"
  201.      (Left, Right : in Wide_Character_Set)
  202.       return        Wide_Character_Set
  203.    is
  204.       Result : Wide_Character_Ranges (1 .. Left'Length + Right'Length);
  205.       N      : Natural;
  206.       L, R   : Natural;
  207.  
  208.    begin
  209.       if Left'Length = 0 then
  210.          return Right;
  211.  
  212.       elsif Right'Length = 0 then
  213.          return Left;
  214.  
  215.       else
  216.          N := 1;
  217.          Result (1) := Left (1);
  218.          L := 2;
  219.          R := 1;
  220.  
  221.          loop
  222.             --  Collapse next left range into current result range if possible
  223.  
  224.             if L <= Left'Length
  225.               and then Wide_Character'Pos (Left (L).Low) <=
  226.                        Wide_Character'Pos (Result (N).High) + 1
  227.             then
  228.                Result (N).High :=
  229.                  Wide_Character'Max (Result (N).High, Left (L).High);
  230.                L := L + 1;
  231.  
  232.             --  Collapse next right range into current result range if possible
  233.  
  234.             elsif R <= Right'Length
  235.               and then Wide_Character'Pos (Right (R).Low) <=
  236.                        Wide_Character'Pos (Result (N).High) + 1
  237.             then
  238.                Result (N).High :=
  239.                  Wide_Character'Max (Result (N).High, Right (R).High);
  240.                R := R + 1;
  241.  
  242.             --  Otherwise establish new result range
  243.  
  244.             else
  245.                if L <= Left'Length then
  246.                   N := N + 1;
  247.                   Result (N) := Left (L);
  248.                   L := L + 1;
  249.  
  250.                elsif R <= Right'Length then
  251.                   N := N + 1;
  252.                   Result (N) := Right (R);
  253.                   R := R + 1;
  254.  
  255.                else
  256.                   exit;
  257.                end if;
  258.             end if;
  259.          end loop;
  260.       end if;
  261.  
  262.       return new Wide_Character_Ranges'(Result (1 .. N));
  263.    end "or";
  264.  
  265.    -----------
  266.    -- "xor" --
  267.    -----------
  268.  
  269.    function "xor"
  270.      (Left, Right : in Wide_Character_Set)
  271.       return        Wide_Character_Set
  272.    is
  273.       Result : Wide_Character_Ranges (1 .. Left'Length + Right'Length);
  274.       N      : Natural := 0;
  275.       L, R   : Natural := 1;
  276.  
  277.    begin
  278.       return (Left or Right) - (Left and Right);
  279.    end "xor";
  280.  
  281.    -----------
  282.    -- Is_In --
  283.    -----------
  284.  
  285.    function Is_In
  286.      (Element : in Wide_Character;
  287.       Set     : in Wide_Character_Set)
  288.       return    Boolean
  289.    is
  290.       L, R, M : Natural;
  291.  
  292.    begin
  293.       L := Set'First;
  294.       R := Set'Last;
  295.  
  296.       --  Binary search loop. The invariant is that if Element is in any of
  297.       --  of the constituent ranges it is in one between Set (L) and Set (R).
  298.  
  299.       loop
  300.          if L > R then
  301.             return False;
  302.  
  303.          else
  304.             M := (L + R) / 2;
  305.  
  306.             if Element > Set (M).High then
  307.                L := M + 1;
  308.             elsif Element < Set (M).Low then
  309.                R := M - 1;
  310.             else
  311.                return True;
  312.             end if;
  313.          end if;
  314.       end loop;
  315.    end Is_In;
  316.  
  317.    ---------------
  318.    -- Is_Subset --
  319.    ---------------
  320.  
  321.    function Is_Subset
  322.      (Elements : in Wide_Character_Set;
  323.       Set      : in Wide_Character_Set)
  324.       return     Boolean
  325.    is
  326.       S : Positive := 1;
  327.       E : Positive := 1;
  328.  
  329.    begin
  330.       loop
  331.          --  If no more element ranges, done, and result is true
  332.  
  333.          if E > Elements'Length then
  334.             return True;
  335.  
  336.          --  If more element ranges, but no more set ranges, result is false
  337.  
  338.          elsif S > Set'Length then
  339.             return False;
  340.  
  341.          --  Remove irrelevant set range
  342.  
  343.          elsif Set (S).High < Elements (E).Low then
  344.             S := S + 1;
  345.  
  346.          --  Get rid of element range that is properly covered by set
  347.  
  348.          elsif Set (S).Low <= Elements (E).Low
  349.             and then Elements (E).High <= Set (S).High
  350.          then
  351.             E := E + 1;
  352.  
  353.          --  Otherwise we have a non-covered element range, result is false
  354.  
  355.          else
  356.             return False;
  357.          end if;
  358.       end loop;
  359.    end Is_Subset;
  360.  
  361.    ---------------
  362.    -- To_Domain --
  363.    ---------------
  364.  
  365.    function To_Domain
  366.      (Map  : in Wide_Character_Mapping)
  367.       return Wide_Character_Sequence
  368.    is
  369.    begin
  370.       return Map.Domain.all;
  371.    end To_Domain;
  372.  
  373.    ----------------
  374.    -- To_Mapping --
  375.    ----------------
  376.  
  377.    function To_Mapping
  378.      (From, To : in Wide_Character_Sequence)
  379.       return     Wide_Character_Mapping
  380.    is
  381.       Domain : Wide_Character_Sequence (1 .. From'Length);
  382.       Rangev : Wide_Character_Sequence (1 .. To'Length);
  383.       N      : Natural := 0;
  384.       K      : Natural := 0;
  385.  
  386.    begin
  387.       if From'Length /= To'Length then
  388.          raise Translation_Error;
  389.  
  390.       else
  391.          for J in From'Range loop
  392.             for M in 1 .. N loop
  393.                if From (J) = Domain (M) then
  394.                   raise Translation_Error;
  395.                elsif From (J) < Domain (M) then
  396.                   Domain (M + 1 .. N + 1) := Domain (M .. N);
  397.                   Domain (M) := From (J);
  398.                   Rangev (M) := To   (J);
  399.                   goto Continue;
  400.                end if;
  401.             end loop;
  402.  
  403.             Domain (N + 1) := From (J);
  404.             Rangev (N + 1) := To   (J);
  405.  
  406.             <<Continue>>
  407.                N := N + 1;
  408.          end loop;
  409.  
  410.          return (Domain => new Wide_Character_Sequence'(Domain (1 .. N)),
  411.                  Rangev => new Wide_Character_Sequence'(Rangev (1 .. N)));
  412.       end if;
  413.    end To_Mapping;
  414.  
  415.    --------------
  416.    -- To_Range --
  417.    --------------
  418.  
  419.    function To_Range
  420.      (Map  : in Wide_Character_Mapping)
  421.       return Wide_Character_Sequence
  422.    is
  423.    begin
  424.       return Map.Rangev.all;
  425.    end To_Range;
  426.  
  427.    ---------------
  428.    -- To_Ranges --
  429.    ---------------
  430.  
  431.    function To_Ranges
  432.      (Set :  in Wide_Character_Set)
  433.       return Wide_Character_Ranges
  434.    is
  435.    begin
  436.       return Set.all;
  437.    end To_Ranges;
  438.  
  439.    -----------------
  440.    -- To_Sequence --
  441.    -----------------
  442.  
  443.    function To_Sequence
  444.      (Set  : in Wide_Character_Set)
  445.       return Wide_Character_Sequence
  446.    is
  447.       Result : Wide_String (Positive range 1 .. 2 ** 16);
  448.       N      : Natural := 0;
  449.  
  450.    begin
  451.       for J in Set'Range loop
  452.          for K in Set (J).Low .. Set (J).High loop
  453.             N := N + 1;
  454.             Result (N) := K;
  455.          end loop;
  456.       end loop;
  457.  
  458.       return Result (1 .. N);
  459.    end To_Sequence;
  460.  
  461.    ------------
  462.    -- To_Set --
  463.    ------------
  464.  
  465.    --  Case of multiple range input
  466.  
  467.    function To_Set
  468.      (Ranges : in Wide_Character_Ranges)
  469.       return   Wide_Character_Set
  470.    is
  471.       Result : Wide_Character_Ranges (Ranges'Range);
  472.       N      : Natural := 0;
  473.       J      : Natural;
  474.  
  475.    begin
  476.       --  The output of To_Set is required to be sorted by increasing Low
  477.       --  values, and discontiguous, so first we sort them as we enter them,
  478.       --  using a simple insertion sort.
  479.  
  480.       for J in Ranges'Range loop
  481.          for K in 1 .. N loop
  482.             if Ranges (J).Low < Result (K).Low then
  483.                Result (K + 1 .. N + 1) := Result (K .. N);
  484.                Result (K) := Ranges (J);
  485.                goto Continue;
  486.             end if;
  487.          end loop;
  488.  
  489.          Result (N + 1) := Ranges (J);
  490.  
  491.          <<Continue>>
  492.             N := N + 1;
  493.       end loop;
  494.  
  495.       --  Now collapse any contiguous or overlapping ranges
  496.  
  497.       J := 1;
  498.       while J < N loop
  499.          if Result (J).High < Result (J).Low then
  500.             N := N - 1;
  501.             Result (J .. N) := Result (J + 1 .. N + 1);
  502.  
  503.          elsif Wide_Character'Pos (Result (J).High) + 1 >=
  504.             Wide_Character'Pos (Result (J + 1).Low)
  505.          then
  506.             Result (J).High :=
  507.               Wide_Character'Max (Result (J).High, Result (J + 1).High);
  508.  
  509.             N := N - 1;
  510.             Result (J + 1 .. N) := Result (J + 2 .. N + 1);
  511.  
  512.          else
  513.             J := J + 1;
  514.          end if;
  515.       end loop;
  516.  
  517.       if Result (N).High > Result (N).Low then
  518.          N := N - 1;
  519.       end if;
  520.  
  521.       return new Wide_Character_Ranges'(Result (1 .. N));
  522.  
  523.    end To_Set;
  524.  
  525.    --  Case of single range input
  526.  
  527.    function To_Set
  528.      (Span : in Wide_Character_Range)
  529.       return Wide_Character_Set
  530.    is
  531.    begin
  532.       if Span.Low > Span.High then
  533.          return Null_Set;
  534.          --  This is safe, because there is no procedure with parameter
  535.          --  Wide_Character_Set on mode "out" or "in out".
  536.  
  537.       else
  538.          return new Wide_Character_Ranges'(1 => Span);
  539.       end if;
  540.    end To_Set;
  541.  
  542.    --  Case of wide string input
  543.  
  544.    function To_Set
  545.      (Sequence  : in Wide_Character_Sequence)
  546.       return      Wide_Character_Set
  547.    is
  548.       R : Wide_Character_Ranges (1 .. Sequence'Length);
  549.  
  550.    begin
  551.       for J in R'Range loop
  552.          R (J) := (Sequence (J), Sequence (J));
  553.       end loop;
  554.  
  555.       return To_Set (R);
  556.    end To_Set;
  557.  
  558.    --  Case of single wide character input
  559.  
  560.    function To_Set
  561.      (Singleton : in Wide_Character)
  562.       return      Wide_Character_Set
  563.    is
  564.    begin
  565.       return new Wide_Character_Ranges'(1 => (Singleton, Singleton));
  566.    end To_Set;
  567.  
  568.    -----------
  569.    -- Value --
  570.    -----------
  571.  
  572.    function Value
  573.      (Map     : in Wide_Character_Mapping;
  574.       Element : in Wide_Character)
  575.       return    Wide_Character
  576.    is
  577.       L, R, M : Natural;
  578.  
  579.    begin
  580.       L := 1;
  581.       R := Map.Domain'Last;
  582.  
  583.       --  Binary search loop
  584.  
  585.       loop
  586.          --  If not found, identity
  587.  
  588.          if L > R then
  589.             return Element;
  590.  
  591.          --  Otherwise do binary divide
  592.  
  593.          else
  594.             M := (L + R) / 2;
  595.  
  596.             if Element < Map.Domain (M) then
  597.                R := M - 1;
  598.  
  599.             elsif Element > Map.Domain (M) then
  600.                L := M + 1;
  601.  
  602.             else --  Element = Map.Domain (M) then
  603.                return Map.Rangev (M);
  604.             end if;
  605.          end if;
  606.       end loop;
  607.    end Value;
  608.  
  609. end Ada.Strings.Wide_Maps;
  610.