unit wVariableNumLink2;

{ Feel free to use this unit as you want.
  First developped by Bernard Gisin

  This unit serves to keep a correspondance between a word32 number
  and a variable.
  This allow to have a linke between a number, usually used in
  the messages file '.msg' and a variable used in the program.

  It can make PROGRAM CRASHES, if an association is made between a
  number and a variable, and the variable doesn't exist anymore.
  This can be the case for local variables of procedures, or
  disposed dynamical variables.
  For security, when an association (=link) isn't valid anymore,
  Call  ClearField(wNum, 0, 0, NIL);  to cancel this association.

  Numbers above 2'000'000'000 are reserved for the library.

  This is used used in :
  wErrors0.pp
     All the error text is written in the message file, and
     additional information, such as variable values, can be used
     through this link between the number and the variable.

  wMenus0.pp
     The menus are described in the message file. But if some information
     can change depending of the program, the link between a number
     (used in the message file) and a varible of the program, allows
     to change the content of the menu easilly.

  wDialog0.pp
     Fields in a dialog box are linked directly to variable of the
     program, so the description in the message file is enough
     to modify variable's content of the program.
  ----------------------------------------------------------------}

INTERFACE
//"""""""

uses  wGlobals2;

const
  vt_Boolean       =   1;  // identify that a pointer points on a boolean
  vt_Char          =   2; // identify that a pointer points on a Char
  vt_Byte          =   3; // identify that a pointer points on a byte
  vt_word          =   7; // identify that a pointer points on a word
  vt_word16        =   8; // ...
  vt_word32        =   9;
  vt_Integer       =   4;
  vt_Int16         =   5;
  vt_Int32         =   6;
  vt_FloatS        =  10; // until here, 4 bytes are enough to memorise this variable
  vt_Float         =  11;
//  vt_ComplexS      =  12;
  vt_Complex       =  13;
  vt_ShortString   = 100; // identify that a pointer points on a ShortString

procedure StrField(wRef:word32; var strS:ShortString; wSizeOfStr:word32);
//=======================================================================
// store the association number - pointer on the string.
// wSizeOfStr = size of the string, with the initial byte in strS[0]
// which stores the length.
// Thus the maximum length the string can have is : wSizeOfStr-1
// If a #0 want to be added at the end of the string,
// its length should be less or equal to wSizeOfStr-2

procedure IntegerField(wRef:word32; var nValV:integer);
//=====================================================
// store the association number - pointer on the variable

procedure Int16Field(wRef:word32; var nValV:int16);
//=================================================
// store the association number - pointer on the variable

procedure Int32Field(wRef:word32; var nValV:int32);
//=================================================
// store the association number - pointer on the variable

procedure WordField(wRef:word32; var wValV:word);
//===============================================
// store the association number - pointer on the variable

procedure Word16Field(wRef:word32; var wValV:word16);
//===================================================
// store the association number - pointer on the variable

procedure Word32Field(wRef:word32; var wValV:word32);
//===================================================
// store the association number - pointer on the variable

procedure ByteField(wRef:word32; var bValV:Byte);
//===============================================
// store the association number - pointer on the variable

procedure CharField(wRef:word32; var cValV:Char);
//===============================================
// store the association number - pointer on the variable

procedure BooleanField(wRef:word32; var fValV:Boolean);
//=====================================================
// store the association number - pointer on the variable

procedure FloatField(wRef:word32; var vValV:Float);
//=================================================
// store the association number - pointer on the variable

procedure FloatSField(wRef:word32; var svValV:FloatS);
//===================================================
// store the association number - pointer on the variable

procedure ComplexField(wRef:word32; var zValV:Complex);
//=====================================================
// store the association number - pointer on the variable

procedure ClearField(wRef:word32);
//================================
// Clears the association between a number and a variable.
// For security, if a variable doesn't exist anymore and
// had a link with a number, cancel (=clear) this link by calling
// this procedure, with the number used in the link.
// You can also directly reuse the number to link it with
// another variable, without needing to cler the previous link.

function GetNumPointer(wNumRef:word32; var wInfoV:word32):pointer;
//================================================================
// wNumRef is the number which is linked to a variable,
// Return a pointer on the variable, which is linked to wNumRef
// Return in wInfoL the type of the linked variable
// If its a string, its maximum size is coded :
// LoWord(wInfoV) ( = wInfoV mod 65536) = vt_ShortString
// HiWord(wInfoV) ( = wInfoV div 65536) = max. length of the sting.
// This is one less that the memory used by the string, since
// strS[0] take one byte to memorise the length.

function ValueWord32(wNumRef:word32; var wInfoV:word32):Word32;
//=============================================================
// wNumRef is the number which is linked to a variable, 
// Return the value of the variable, which is normally
// assumed to be a word32 compatible number, and
// Return in wInfoV the type of the linked variable
// Comparing wInfoV with  vt_...  constants, it is possible
// to determine if the assumed type was the right one.

function ValueInt32(wNumRef:word32; var wInfoV:word32):Int32;
//===========================================================
// wNumRef is the number which is linked to a variable, 
// Return the value of the variable, which is normally
// assumed to be an int32 compatible number, and
// Return in wInfoV the type of the linked variable
// Comparing wInfoV with  vt_...  constants, it is possible
// to determine if the assumed type was the right one.

function ValueFloat(wNumRef:word32; var wInfoV:word32):Float;
//===========================================================
// wNumRef is the number which is linked to a variable, 
// Return the value of the variable, which is normally
// assumed to be a Float compatible number, and
// For float values, return a formatted string, of length wLenP
// with wDecimalP digits after the comma.
// Return in wInfoV the type of the linked variable
// Comparing wInfoV with  vt_...  constants, it is possible
// to determine if the assumed type was the right one.

function ValueFloat(ptr:pointer; wDecimalP:word32):Float;
//=======================================================
// Return the value of the variable pointed by ptr.
// which type is given in the wInfoP parameter.
// It is supposed to be compatible with the Float type.
// If not, returns zero.

function ValueStr(wNumRef:word32; wLenP,wDecimalsP:word32; var wInfoV:word32):ShortString;
//========================================================================================
// wNumRef is the number which is linked to a variable, 
// Return the value of the variable, which is normally
// assumed to be string compatible.
// Return in wInfoL the type of the linked variable
// Comparing wInfoL with  vt_...  constants, it is possible
// to determine if the assumed type was the right one.
// If it is a float, return a sting of length wLenP with wDecimalsP decimals

function ValueStr(wNumRef:word32; var wInfoV:word32):ShortString;
//===============================================================
// wNumRef is the number which is linked to a variable, 
// Return the value of the variable, which is normally
// assumed to be string compatible.
// Return in wInfoL the type of the linked variable
// Comparing wInfoL with  vt_...  constants, it is possible
// to determine if the assumed type was the right one.

function ValueOf(ptr:pointer; wInfoP,wLenP,wDecimalsP:word32):ShortString;
//=======================================================================
// the ptr is a pointer on a variable, which type is given in the
// wInfoL parameter.
// If it is a Float, then it is formatted,
// length = wLenP,  decimals = wDecimalsP
// If the variable is a string, HiWord(wInfoL)
// gives the size of the string. ( = SizeOf(strS))

function ValueOf(ptr:pointer; wInfoL:word32):ShortString;
//=======================================================
// the ptr is a pointer on a variable, which type is given in the
// wInfoL parameter.
// If the variable is a string, HiWord(wInfoL)
// gives the size of the string. ( = SizeOf(strS))

function Interpret(strS:ShortString):ShortString;
//===============================================
// A string containing  @number  will be converted in another string
// using the link between the number and a variable, to put the
// variable content instead of the @number.
// Return the string with the  @number  converted into the value
// of the corresponding varible.
// If no variable is corresponding, the  @number  will remain.

const
  gglwFloatLength   : word32 = 15;
  gglwFloatDecimals : word32 = 9;
  // default length and number of decimals, when returning a float
  // as a string value.

IMPLEMENTATION
//""""""""""""

Uses wHashTable2, wUtiles0, wErrors0, wDebug0;

var nNotUsed : integer; // only to avoid an error message while compliling in DevPascal 1.9

  gloHT       : THashTableW2;
  // This maintain the link between a word32 number and a variable

procedure StrField(wRef:word32; var strS:ShortString; wSizeOfStr:word32);
//=======================================================================
// store the association number - pointer on the string.
begin
// Add the link wRef -> strS in the Hash table
// Add information to memorise the type of the variable added, and its length
if (wSizeOfStr = 0) then exit; // not valide
gloHT.AddPointerW2(wRef, 0, vt_ShortString + word32(65536)*(wSizeOfStr-1), @strS);
end;  // StrField

procedure IntegerField(wRef:word32; var nValV:integer);
//=====================================================
// store the association number - pointer on the variable
begin
gloHT.AddPointerW2(wRef, 0, vt_Integer, @nValV);
end;  // IntegerField

procedure Int16Field(wRef:word32; var nValV:int16);
//=================================================
// store the association number - pointer on the variable
begin
gloHT.AddPointerW2(wRef, 0, vt_Int16, @nValV);
end;  // Int16Field

procedure Int32Field(wRef:word32; var nValV:int32);
//=================================================
// store the association number - pointer on the variable
begin
gloHT.AddPointerW2(wRef, 0, vt_Int32, @nValV);
end;  // Int32Field

procedure WordField(wRef:word32; var wValV:word);
//===============================================
// store the association number - pointer on the variable
begin
gloHT.AddPointerW2(wRef, 0, vt_word, @wValV);
end;  // wordField

procedure Word16Field(wRef:word32; var wValV:word16);
//===================================================
// store the association number - pointer on the variable
begin
gloHT.AddPointerW2(wRef, 0, vt_word16, @wValV);
end;  // word16Field

procedure Word32Field(wRef:word32; var wValV:word32);
//===================================================
// store the association number - pointer on the variable
begin
gloHT.AddPointerW2(wRef, 0, vt_word32, @wValV);
end;  // word32Field

procedure ByteField(wRef:word32; var bValV:Byte);
//===============================================
// store the association number - pointer on the variable
begin
gloHT.AddPointerW2(wRef, 0, vt_Byte, @bValV);
end;  // ByteField

procedure CharField(wRef:word32; var cValV:Char);
//===============================================
// store the association number - pointer on the variable
begin
gloHT.AddPointerW2(wRef, 0, vt_Char, @cValV);
end;  // CharField

procedure BooleanField(wRef:word32; var fValV:Boolean);
//=====================================================
// store the association number - pointer on the variable
begin
gloHT.AddPointerW2(wRef, 0, vt_Boolean, @fValV);
end;  // BooleanField

procedure FloatField(wRef:word32; var vValV:Float);
//=================================================
// store the association number - pointer on the variable
begin
gloHT.AddPointerW2(wRef, 0, vt_Float, @vValV);
end;  // FloatField

procedure FloatSField(wRef:word32; var svValV:FloatS);
//===================================================
// store the association number - pointer on the variable
begin
gloHT.AddPointerW2(wRef, 0, vt_FloatS, @svValV);
end;  // FloatSField

procedure ComplexField(wRef:word32; var zValV:Complex);
//=====================================================
// store the association number - pointer on the variable
begin
gloHT.AddPointerW2(wRef, 0, vt_Complex, @zValV);
end;  // ComplexField

procedure ClearField(wRef:word32);
//================================
// Clears the association between a number and a variable.
// For security, if a variable doesn't exist anymore and
// had a link with a number, cancel (=clear) this link by calling
// this procedure, with the number used in the link.
// You can also directly reuse the number to link it with
// another variable, without needing to cler the previous link.
begin
gloHT.AddPointerW2(wRef, 0, 0, NIL);
end;  // ClearField

function GetNumPointer(wNumRef:word32; var wInfoV:word32):pointer;
//================================================================
// wNumRef is the number which is linked to a variable,
// Return a pointer on the variable, which is linked to wNumRef
// Return in wInfoV the type of the linked variable
// If its a string, its maximum size is coded :
// LoWord(wInfoV) ( = wInfoV mod 65536) = vt_ShortString
// HiWord(wInfoV) ( = wInfoV div 65536) = max. length of the sting.
// This is one less that the memory used by the string, since
// strS[0] take one byte to memorise the length.
begin
GetNumPointer:=gloHT.GetPointerW2(wNumRef,0, wInfoV);
end;  //  GetNumPointer

function ValueWord32(wNumRef:word32; var wInfoV:word32):Word32;
//=============================================================
// wNumRef is the number which is linked to a variable, 
// Return the value of the variable, which is normally
// assumed to be a word32 compatible number, and
// Return in wInfoV the type of the linked variable
// Comparing wInfoV with  vt_...  constants, it is possible
// to determine if the assumed type was the right one.
var
  ptr         : pointer; // pointer on the variable corresponding to the number wNumRef
begin
ptr:=gloHT.GetPointerW2(wNumRef,0, wInfoV);
wInfoV:=wInfoV mod 65536;

case wInfoV of
  vt_Boolean    : ValueWord32:=ord(Boolean(ptr^));
  vt_Char       : ValueWord32:=ord(Char(ptr^));
  vt_Byte       : ValueWord32:=byte(ptr^);
  vt_Integer    : ValueWord32:=abs(integer(ptr^));
  vt_Int16      : ValueWord32:=abs(int16(ptr^));
  vt_Int32      : ValueWord32:=abs(int32(ptr^));
  vt_word       : ValueWord32:=word(ptr^);
  vt_word16     : ValueWord32:=word16(ptr^);
  vt_word32     : ValueWord32:=word32(ptr^);
//  vt_FloatS     : ValueWord32:=FloatToStr(FloatS(ptr^), 0);
//  vt_Float      : ValueWord32:=FloatToStr(Float(ptr^), 0);
//  vt_ComplexS      =  12;
//  vt_Complex       =  13;
  vt_ShortString: ValueWord32:=0; // wrong type, incompatible.

  else ValueWord32:=0;  // unknown type
  end; // case
end;  // ValueWord32

function ValueInt32(wNumRef:word32; var wInfoV:word32):Int32;
//===========================================================
// wNumRef is the number which is linked to a variable, 
// Return the value of the variable, which is normally
// assumed to be an int32 compatible number, and
// Return in wInfoV the type of the linked variable
// Comparing wInfoV with  vt_...  constants, it is possible
// to determine if the assumed type was the right one.
var
  ptr         : pointer; // pointer on the variable corresponding to the number wNumRef
begin
ptr:=gloHT.GetPointerW2(wNumRef,0, wInfoV);
wInfoV:=wInfoV mod 65536;

case wInfoV of
  vt_Boolean    : ValueInt32:=ord(Boolean(ptr^));
  vt_Char       : ValueInt32:=ord(Char(ptr^));
  vt_Byte       : ValueInt32:=byte(ptr^);
  vt_Integer    : ValueInt32:=integer(ptr^);
  vt_Int16      : ValueInt32:=int16(ptr^);
  vt_Int32      : ValueInt32:=int32(ptr^);
  vt_word       : ValueInt32:=int32(word(ptr^));
  vt_word16     : ValueInt32:=int32(word16(ptr^));
  vt_word32     : ValueInt32:=int32(word32(ptr^));
//  vt_FloatS     : ValueInt32:=FloatToStr(FloatS(ptr^), 0);
//  vt_Float      : ValueInt32:=FloatToStr(Float(ptr^), 0);
//  vt_ComplexS      =  12;
//  vt_Complex       =  13;
  vt_ShortString: ValueInt32:=0; // wrong type, incompatible.

  else ValueInt32:=0;  // unknown type
  end; // case
end;  // ValueInt32

function ValueFloat(ptr:pointer; wInfoP:word32):Float;
//====================================================
// Return the value of the variable pointed by ptr.
// which type is given in the wInfoP parameter.
// It is supposed to be compatible with the Float type.
// If not, returns zero.
begin
case wInfoP of
  vt_Boolean    : ValueFloat:=ord(Boolean(ptr^));
  vt_Char       : ValueFloat:=ord(Char(ptr^));
  vt_Byte       : ValueFloat:=byte(ptr^);
  vt_Integer    : ValueFloat:=integer(ptr^);
  vt_Int16      : ValueFloat:=int16(ptr^);
  vt_Int32      : ValueFloat:=int32(ptr^);
  vt_word       : ValueFloat:=int32(word(ptr^));
  vt_word16     : ValueFloat:=int32(word16(ptr^));
  vt_word32     : ValueFloat:=int32(word32(ptr^));
  vt_FloatS     : ValueFloat:=FloatS(ptr^);
  vt_Float      : ValueFloat:=Float(ptr^);
//  vt_ComplexS      =  12;
//  vt_Complex       =  13;
  vt_ShortString: ValueFloat:=0; // wrong type, incompatible.

  else ValueFloat:=0;  // unknown type
  end; // case
end;  // ValueFloat

function ValueFloat(wNumRef:word32; var wInfoV:word32):Float;
//===========================================================
// wNumRef is the number which is linked to a variable, 
// Return the value of the variable, which is normally
// assumed to be a Float compatible number, and
// For float values, return a formatted string, of length wLenP
// with wDecimalP digits after the comma.
// Return in wInfoV the type of the linked variable
// Comparing wInfoV with  vt_...  constants, it is possible
// to determine if the assumed type was the right one.
var
  ptr         : pointer; // pointer on the variable corresponding to the number wNumRef
begin
ptr:=gloHT.GetPointerW2(wNumRef,0, wInfoV);
wInfoV:=wInfoV mod 65536;
ValueFloat:=ValueFloat(ptr,wInfoV)
end;  // ValueFloat

function ValueStr(wNumRef:word32; wLenP,wDecimalsP:word32; var wInfoV:word32):ShortString;
//========================================================================================
// wNumRef is the number which is linked to a variable, 
// Return the value of the variable, which is normally
// assumed to be string compatible.
// Return in wInfoL the type of the linked variable
// Comparing wInfoL with  vt_...  constants, it is possible
// to determine if the assumed type was the right one.
// If it is a float, return a sting of length wLenP with wDecimalsP decimals
var
  ptr         : pointer; // pointer on the variable corresponding to the number wNumRef
begin
ptr:=gloHT.GetPointerW2(wNumRef,0, wInfoV);
wInfoV:=wInfoV mod 65536;

case wInfoV of
  vt_ShortString: ValueStr:=ShortString(ptr^);
  vt_Integer    : ValueStr:=IntToStr(integer(ptr^), 0);
  vt_Int16      : ValueStr:=IntToStr(int16(ptr^), 0);
  vt_Int32      : ValueStr:=IntToStr(int32(ptr^), 0);
  vt_word       : ValueStr:=IntToStr(word(ptr^), 0);
  vt_word16     : ValueStr:=IntToStr(word16(ptr^), 0);
  vt_word32     : ValueStr:=IntToStr(word32(ptr^), 0);
  vt_Byte       : ValueStr:=IntToStr(byte(ptr^), 0);
  vt_Char       : ValueStr:=Char(ptr^);
  vt_Boolean    : if Boolean(ptr^) then ValueStr:='1'
                  else                  ValueStr:='0';
  vt_FloatS     : ValueStr:=FloatToStr(FloatS(ptr^), wLenP, wDecimalsP);
  vt_Float      : ValueStr:=FloatToStr(Float(ptr^), wLenP, wDecimalsP);
//  vt_ComplexS      =  12;
//  vt_Complex       =  13;

  else ValueStr:='@???';
  end; // case
end;  // ValueStr

function ValueStr(wNumRef:word32; var wInfoV:word32):ShortString;
//===============================================================
// wNumRef is the number which is linked to a variable, 
// Return the value of the variable, which is normally
// assumed to be string compatible.
// Return in wInfoV the type of the linked variable
// Comparing wInfoV with  vt_...  constants, it is possible
// to determine if the assumed type was the right one.
begin
ValueStr:=ValueStr(wNumRef, gglwFloatLength, gglwFloatDecimals, wInfoV);
end;  // ValueStr  second definition

function ValueOf(ptr:pointer; wInfoP,wLenP,wDecimalsP:word32):ShortString;
//========================================================================
// the ptr is a pointer on a variable, which type is given in the
// wInfoL parameter.
// If it is a Float, then it is formatted,
// length = wLenP,  decimals = wDecimalsP
// If the variable is a string, HiWord(wInfoL)
// gives the size of the string. ( = SizeOf(strS))
begin
case (wInfoP mod 65536) of
  vt_ShortString: ValueOf:=ShortString(ptr^);
  vt_Integer    : ValueOf:=IntToStr(integer(ptr^), 0);
  vt_Int16      : ValueOf:=IntToStr(int16(ptr^), 0);
  vt_Int32      : ValueOf:=IntToStr(int32(ptr^), 0);
  vt_word       : ValueOf:=IntToStr(word(ptr^), 0);
  vt_word16     : ValueOf:=IntToStr(word16(ptr^), 0);
  vt_word32     : ValueOf:=IntToStr(word32(ptr^), 0);
  vt_Byte       : ValueOf:=IntToStr(byte(ptr^), 0);
  vt_Char       : ValueOf:=Char(ptr^);
  vt_Boolean    : if Boolean(ptr^) then ValueOf:='1'
                  else                  ValueOf:='0';
  vt_FloatS     : ValueOf:=FloatToStr(FloatS(ptr^), wLenP, wDecimalsP);
  vt_Float      : ValueOf:=FloatToStr(Float(ptr^), wLenP, wDecimalsP);
//  vt_ComplexS      =  12;
//  vt_Complex       =  13;

  else ValueOf:='@???';
  end; // case
end;  // ValueOf

function ValueOf(ptr:pointer; wInfoL:word32):ShortString;
//=======================================================
// the ptr is a pointer on a variable, which type is given in the
// wInfoL parameter.
// If the variable is a string, HiWord(wInfoL)
// gives the size of the string. ( = SizeOf(strS))
begin
ValueOf:=ValueOf(ptr, wInfoL,gglwFloatLength,gglwFloatDecimals);
end;  // ValueOf  Second definition

function Interpret(strS:ShortString):ShortString;
//===============================================
// A string containing  @number  will be converted in another string
// using the link between the number and a variable, to put the
// variable content instead of the @number.
// Return the string with the  @number  converted into the value
// of the corresponding varible.
// If no variable is corresponding, the  @number  will remain.
var
  nn,
  nLen,
  nPos        // position of an '@'
              : integer;
  wNumRef,    // variable's reference number.
              // From it, a pointer on the variable is gotten.
  wInfoL      // information about the variable.
              : word32;
  ptr         : pointer; // pointer on the variable corresponding to the number wNumRef
  strOut      : ShortString;

begin
nLen:=length(strS);
nn:=1;
strOut:='';

repeat
   // look for an '@' which designes the begining of a number
   // which correspond to a string.
   while (nn <= nLen) and (strS[nn] <> '@') do begin
      strOut:=strOut + strS[nn];
      inc(nn);
      end;

   if (nn >= nLen) then begin
      // Interpretation finished
      Interpret:=strOut;
      exit;
      end;

   inc(nn); // normally strS[nn] = first digit of the number following the '@'
            // if not, the @ isn't a link to a variable.
            // Continue without interpretation of this '@'

   if (strS[nn]) in ['0'..'9'] then begin
      // Get the number following the '@'
      wNumRef:=GetIntValue(nn, strS, nPos);

      if (nPos <> 0) then begin
         // A number follwed the '@', look for the corresponding variable
         ptr:=gloHT.GetPointerW2(wNumRef,0, wInfoL);

         if (ptr <> NIL) then begin
            // A variable is associated with the number, interpret it.
            strOut:=strOut + ValueOf(ptr, wInfoL);
            nn:=nPos; // skip the '@number', since it has been interpreted.
            end
         else begin
            // the number wNumRef is not associated with a variable
            // Just let it in the string without interpretation.
            strOut:=strOut + '@';
            end;
         end
      else begin
         // Impossible to arrive here normally.
         // the numver followind the @ was not considered as a number
         // using the  "Val"  procedure of pascal.
         // Just ignore it.
         strOut:=strOut + '@';  // Let the '@' in the output sting
         end;
      end // if (strS[nn]) in ['0'..'9'] then begin
   else begin
      // no numbers follow the '@'
      // Just let the '@' as it was.
      strOut:=strOut + '@';
      if (strS[nn] = '@') then inc(nn); // skip one '@' if two are following
      end;

   until FALSE;  // we quite the loop with an exit.
end;  // Interpret

initialization
//""""""""""""
  // Initialises the correspondence table word32 numbre -> pointer on a varable
  gloHT.Init(0);

finalization
//""""""""""
  gloHT.Done;  // free the allocated memory.


End.  // unit wVariableNumLink2;

