-- -- -- package Tables.Names Copyright (c) Dmitry A. Kazakov -- -- Implementation Luebeck -- -- Spring, 2003 -- -- -- -- Last revision : 10:00 19 May 2022 -- -- -- -- This library is free software; you can redistribute it and/or -- -- modify it under the terms of the GNU General Public License as -- -- published by the Free Software Foundation; either version 2 of -- -- the License, or (at your option) any later version. This 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 -- -- General Public License for more details. You should have -- -- received a copy of the GNU General Public License along with -- -- this library; if not, write to the Free Software Foundation, -- -- Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -- -- -- -- As a special exception, if other files instantiate generics from -- -- this unit, or you link this unit with other files to produce an -- -- executable, this unit does not by itself cause the resulting -- -- executable to be covered by the GNU General Public License. This -- -- exception does not however invalidate any other reasons why the -- -- executable file might be covered by the GNU Public License. -- --____________________________________________________________________-- with Ada.Characters.Handling; use Ada.Characters.Handling; with Ada.IO_Exceptions; use Ada.IO_Exceptions; with Ada.Strings.Maps; use Ada.Strings.Maps; package body Tables.Names is type Extended_Equality is (Less, Equal, Prefix, Greater); -- -- Compare -- String with a pattern -- -- Source - The string -- Pointer - The position in it -- Item - The pattern -- -- Pointer is advanced if the outcome is Equal or Prefix -- -- Returns : -- -- Comparison result -- function Compare ( Source : String; Pointer : access Integer; Item : String ) return Extended_Equality is pragma Inline (Compare); Index1 : Integer := Pointer.all; Index2 : Integer := Item'First; Symbol1 : Character; Symbol2 : Character; begin loop if Index2 > Item'Last then if Index1 > Source'Last then Pointer.all := Index1; return Equal; else Pointer.all := Index1; return Prefix; end if; else if Index1 > Source'Last then return Less; end if; end if; Symbol1 := Source (Index1); Symbol2 := Item (Index2); if Is_In (Symbol1, Blanks) then if Is_In (Symbol2, Blanks) then Index1 := Index1 + 1; Index2 := Index2 + 1; while Index1 <= Source'Last loop exit when not Is_In (Source (Index1), Blanks); Index1 := Index1 + 1; end loop; while Index2 <= Item'Last loop exit when not Is_In (Item (Index2), Blanks); Index2 := Index2 + 1; end loop; else return Less; end if; else if Is_In (Symbol2, Blanks) then return Greater; else Symbol1 := To_Lower (Symbol1); Symbol2 := To_Lower (Symbol2); if Symbol1 = Symbol2 then Index1 := Index1 + 1; Index2 := Index2 + 1; else if Symbol1 > Symbol2 then return Greater; else return Less; end if; end if; end if; end if; end loop; end Compare; function Search ( Folder : Dictionary; Name : String ) return Integer is Low : Integer := 0; High : Integer := Folder.Size + 1; This : Integer; Index : aliased Integer; begin if High = 1 then return -1; end if; loop This := (Low + High) / 2; Index := Name'First; case Compare (Name, Index'Access, Folder.List (This).Name) is when Less => High := This; if High - Low = 1 then return -This; end if; when Equal => return This; when Prefix | Greater => Low := This; if High - Low = 1 then return -(This + 1); end if; end case; end loop; end Search; procedure Add ( Folder : in out Dictionary; Name : String; Data : Tag ) is begin Check_Spelling (Name); declare Index : constant Integer := Search (Folder, Name); begin if Index > 0 then raise Name_Error; else Insert (Folder, -Index, Name, Data); end if; end; end Add; procedure Add ( Folder : in out Dictionary; Name : String; Data : Tag; Offset : out Positive ) is begin Check_Spelling (Name); declare Index : Integer := Search (Folder, Name); begin if Index > 0 then raise Name_Error; else Index := -Index; Insert (Folder, Index, Name, Data); Offset := Index; end if; end; end Add; procedure Delete (Folder : in out Dictionary; Name : String) is Index : constant Integer := Search (Folder, Name); begin if Index > 0 then Delete (Folder, Index); end if; end Delete; function Find (Folder : Dictionary; Name : String) return Tag is Index : constant Integer := Search (Folder, Name); begin if Index > 0 then return Folder.List (Index).Data; else raise End_Error; end if; end Find; function IsIn (Folder : Dictionary; Name : String) return Boolean is begin return Search (Folder, Name) > 0; end IsIn; procedure Get ( Source : String; Pointer : in out Integer; Folder : Dictionary; Data : out Tag ) is Index : Natural; begin Locate (Source, Pointer, Folder, Index); if Index = 0 then raise End_Error; else Data := Folder.List (Index).Data; end if; end Get; procedure Get ( Source : String; Pointer : in out Integer; Folder : Dictionary; Data : out Tag; Got_It : out Boolean ) is Index : Natural; begin Locate (Source, Pointer, Folder, Index); if Index = 0 then Got_It := False; else Got_It := True; Data := Folder.List (Index).Data; end if; end Get; function Locate (Folder : Dictionary; Name : String) return Natural is Index : constant Integer := Search (Folder, Name); begin if Index > 0 then return Index; else return 0; end if; end Locate; procedure Locate ( Source : String; Pointer : in out Integer; Folder : Dictionary; Offset : out Natural ) is Found : Integer := 0; Low : Integer := 0; High : Integer := Folder.Size + 1; This : Integer; Next : Integer; Index : aliased Integer; begin if ( Pointer < Source'First or else ( Pointer > Source'Last and then Pointer - 1 > Source'Last ) ) then raise Layout_Error; end if; while High - Low /= 1 loop This := (Low + High) / 2; Index := Pointer; case Compare (Source, Index'Access, Folder.List (This).Name) is when Less => High := This; when Equal | Prefix => Found := This; Next := Index; Low := This; when Greater => for Lower in reverse Low + 1 .. This - 1 loop exit when ( Found /= 0 and then ( Folder.List (Found).Name'Length > Folder.List (Lower).Name'Length ) ); Index := Pointer; case Compare ( Source, Index'Access, Folder.List (Lower).Name ) is when Less => -- Rest items could be only exit; -- lesser than this, exit when Equal | Prefix => Found := Lower; -- Here we are. Ignore the rest Next := Index; -- lesser, i.e. shorter, items exit; when Greater => null; -- Undecided, continue end case; end loop; Low := This; end case; end loop; if ( Found = 0 or else ( Next <= Source'Last and then not Check_Matched (Source, Next) ) ) then Offset := 0; else Offset := Found; Pointer := Next; end if; end Locate; procedure Replace ( Folder : in out Dictionary; Name : String; Data : Tag ) is begin Check_Spelling (Name); declare Index : constant Integer := Search (Folder, Name); begin if Index > 0 then Folder.List (Index).Data := Data; else Insert (Folder, -Index, Name, Data); end if; end; end Replace; procedure Replace ( Folder : in out Dictionary; Name : String; Data : Tag; Offset : out Positive ) is begin Check_Spelling (Name); declare Index : Integer := Search (Folder, Name); begin if Index > 0 then Folder.List (Index).Data := Data; Offset := Index; else Index := -Index; Insert (Folder, Index, Name, Data); Offset := Index; end if; end; end Replace; end Tables.Names;