(*******************************************************************)
(** Release history:                                              **)
(**    17th January 1985   version 1.0.0 First port to new world  **)
(*******************************************************************)
DEFINITION MODULE Dict;

(*
    Title    : Dictionary Management System
    LastEdit : Thu Aug 16 11:26:29 1984
    Author   : LDS & GT
    Acorn Computers VLSI Design Aids Group
*)

(*------------------------------------------------------------------------*)
(* Dictionaries are tables. They support more sophisticated key           *)
(* matching than the old Stab.  Dictionaries cannot be made persistent at *)
(* the present time.                                                      *)
(*     The CONTENTS of dictionaries are entirely the user's responsibility*)
(*  - if she places a string in a dictionary then she must dispose of it  *)
(*  herself afterwards.  ALL OTHER STRINGS involved in dictionaries are   *)
(*  managed by the Dict package itself; i.e. The names of dictionaries,   *)
(*  the tags for their entries, and the expanded strings returned when    *)
(*  a match has been made on an abbreviation. These strings MUST NOT be   *)
(*  disposed of by users; copies of them must be made if necessary.       *)
(*------------------------------------------------------------------------*)



(*------------------------------------------------------------------------*)
(*  WARNING: Destruction of dictionaries is supported by a mechanism      *)
(*  which breaks the type-checking rules, so this should only be          *)
(*  undertaken when the implications are understood.                      *)
(*     There is a field, 'Dispose', in a DICT to which the user may       *)
(*  assign a procedure which is run-time compatible with:                 *)
(*                     PROCEDURE(VAR WORD)                                *)
(*  This procedure is called whenever an item in a dictionary is no-      *)
(*  longer pointed at, i.e. when the dictionary is deleted, when a        *)
(*  particular item is deleted, or when an item is entered under a key    *)
(*  which already exists. In the latter case, the original referent       *)
(*  of the key are passed to this dispose-procedure.                      *)
(*     The type of the procedure is such that WORD(String.Dispose)        *)
(*  will be acceptable.  Use of this feature is ONLY allowed when         *)
(*  ALL the entries in a dictionary are of the same type.                 *)
(*  (Note - the default procedure called when an item is no longer        *)
(*   visible does nothing:  The Dispose procedure MUST be explicitly      *)
(*   set-up by the user before it will be used)                           *)
(*------------------------------------------------------------------------*)


FROM SYSTEM  IMPORT WORD;
FROM Strings  IMPORT String;

EXPORT  QUALIFIED   DICT, KeyMatchOptions, KeyAttr, CSCANPROC, ERRORCODE,
                    NewDict, NewDictS, Enter, EnterS,
                    Found, FoundWith, FoundExp, FoundWithExp,
                    Remove, RemoveS, DeleteDict, ForAllIn,
                    Debug, Trace;

TYPE        HIDDEN;
        CSCANPROC = PROCEDURE(VAR ARRAY OF CHAR, WORD, VAR WORD) :BOOLEAN;
                              (*  ^ Dict key *)
                                              (* ^ correspondent *)
                                                        (* ^ user param *)
	
  KeyMatchOptions = (ExactLength,   NoExactLength,   (* Must be same length *)
                     ExactCase,     NoExactCase);    (* Must be same case   *)
          KeyAttr = SET OF KeyMatchOptions;
	  
        ERRORCODE = (NoError, NotFound, Ambiguous);
	
             DICT = POINTER TO RECORD
		      Silent :BOOLEAN;  (* set by user to suppress msgs. *)
                   ErrorCode :ERRORCODE;(* useful if Silent...           *)
                        Name :String;
              LastKeyMatched :String;   (* set up by 'Found' to expand    *)
                     Dispose :WORD;     (* default procedure just returns.*)
              Implementation :HIDDEN;   (* abbreviation                   *)
                    END;

VAR Debug, Trace: BOOLEAN; (* Use 'Debug' at your own risk.
                              Don't blame me if you get eyestrain.
                              'Trace' is slightly less logorhoeac. *)

PROCEDURE NewDict(name :ARRAY OF CHAR) :DICT;
PROCEDURE NewDictS(name :String) :DICT;
  (* name is used ONLY for error reporting purposes *)
  (*----------------------------------------------------------------------*)
  (*  Each dictionary has a field in which the user can place a 'dispose' *)
  (*  procedure.  This is only useful if all the items in a dictionary    *)
  (*  are of the same type, or contain visible type information.          *)
  (*  The default procedure inserted into the dict when 'NewDict' is      *)
  (*  called simply returns. The type of the procedure is such that       *)
  (*  String.Dispose may be easily used to dispose string values.         *)
  (*----------------------------------------------------------------------*)

PROCEDURE Enter(dict :DICT;
                 Key :ARRAY OF CHAR;  KeyAttributes :KeyAttr;  Item :WORD);
PROCEDURE EnterS(dict :DICT;
                 Key :String;  KeyAttributes :KeyAttr;  Item :WORD);
  (* KeyAttributes will usually be KeyAttr{}, denoting that a minimum    *)
  (* prefix match in any character case will suffice, and that in case   *)
  (* of ambiguity, an error result will be returned.                     *)
  (* However, if KeyAttr{ExactLength} is given, the key string will only *)
  (* match a string of the same length, unless overridden by a parameter *)
  (* of 'KeyAttr{NoExactLength}' in the 'FoundWith' call.                *)
  (* If the key string already exists in the dictionary, the Val field   *)
  (* is passed to the dict dispose procedure.                            *)

(*------------------------------------------------------------------------*)
(*  Note:    Enter (Dict, "key", KeyAttr{}, String.CopyCS(CharArray))     *)
(*        is valid, BUT                                                   *)
(*           StringV := String.CopyCS(CharArray);                         *)
(*           Enter (Dict, "key", KeyAttr{}, StringV);                     *)
(*           String.Dispose (StringV)                                     *)
(*        is NOT. Once bitten twice shy.                                  *)
(*------------------------------------------------------------------------*)

PROCEDURE Found(dict :DICT;  Key :String;  VAR Val :WORD) :BOOLEAN;
PROCEDURE FoundWith(Attribute :KeyAttr;
           dict :DICT;  Key :String;  VAR Val :WORD) :BOOLEAN;
  (* Attributes explicitly given here will over-ride any set in the    *)
  (* 'Enter' call. Attributes not mentioned will be left untouched.    *)
PROCEDURE FoundExp(dict     :DICT;  Key :String;  VAR Val :WORD;
               VAR Expanded :String) :BOOLEAN;
PROCEDURE FoundWithExp(Attribute :KeyAttr;     dict :DICT;
                       Key       :String;  VAR Val  :WORD;
                   VAR Expanded  :String)                   :BOOLEAN;
(* 'Exp' calls return the exact keyword with which 'Key' matched. *)
(* This may be longer that 'Key' or in a different case.          *)
PROCEDURE Remove(dict :DICT; Key: ARRAY OF CHAR);
PROCEDURE RemoveS(dict :DICT;  VAR Key :String);
  (* No boolean is returned, but the Dict return code can be tested.   *)
  (* The Val field is passed to the dictionary dispose procedure.      *)
PROCEDURE DeleteDict(VAR dict :DICT);
  (* entries processed by equivalent of 'Remove', then 'dict' made invalid. *)
PROCEDURE ForAllIn(dict :DICT;  Do :CSCANPROC;  VAR parm :WORD) :BOOLEAN;
  (* continue scanning while CSCANPROC returns FALSE *)
END Dict.
