! mathpars.h - Parse Big Numbers and other formats in Inform.
!   Version 1.0 (19-Sep-2001)
!
! by Matt Albrecht - [email protected]
!
! (If you need to edit this file, note that indentations are 4 spaces
! and tabs are not used.)
!
! This has been donated to the Public Domain.  Use, abuse, and don't blame me.
!
! To prevent dirtying the global namespace, all members of this file begin with
! "mathpars_", while members private to this file begin with "mathpars__".

! Due to unsigned stuff, requires Z-Machine version 5 or older.


System_file;



! C-like header!
Ifndef MATHPARS__INCLUDED;
Constant MATHPARS__INCLUDED;

Message "Adding Big Number Parser library";


!-------------------------------------------------------------------------------
! Signed Math routines
!-------------------------------------------------------------------------------

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! Used for scoping in the grammar, such as:
!Verb 'type'
!  * mathpars_BigNumber 'on'/'onto'/'in'/'with' Keypad        -> Type reverse
!  * 'on'/'onto'/'in'/'with' Keypad mathpars_BigNumber        -> Type;

[ mathpars_BigNumber
   val text old_cf len old_cw old_wn; ! locals

   old_wn = wn;
   if (NextWordStopped() == -1)
   {
       ! no more words
       return GPR_FAIL;
   }
   wn = old_wn;
   text = WordAddress( wn );
   len = WordLength( wn );

!print "[ length of word ",wn," is ",len," ]^";

   if (len >= 0)
   {
       if (text->0 >= '0' && text->0 <= '9')
       {
           val = -1;
           old_cf = consult_from;
           old_cw = consult_words;
           consult_words = 1;
           consult_from = wn;
           switch (text->(len-1))
           {
               'b' :
!print "[ parsing big number: binary ]^";
                   val = mathpars_readBinValue();
               'h' :
!print "[ parsing big number: hex ]^";
                   val = mathpars_readHexValue();
               '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'd':
!print "[ parsing big number: decimal ]^";
                   val = mathpars_readDecValue();
           }
           consult_from = old_cf;
           consult_words = old_cw;
           if (val ~= -1)
           {
               ++wn;
               parsed_number = val;
!print "[ returning number ",val," ]^";
               return GPR_NUMBER;
           }
       }
   }
!print "[ did not find a number ]^";

   return GPR_FAIL;
];



!-----------------------------------------------------------------
! Reads in from the input parse buffer a hex value, and returns
! it as a number.  If the global Mathpars_Result is non-zero, then
! the value is not a Hex value.  The value read is considered
! the consult part of the buffer.
!-----------------------------------------------------------------
Global Mathpars_Result;

[ mathpars_readHexValue
   val digit text i pos len start;

   Mathpars_Result = 0;

   if (consult_words ~= 1)
   {
       HexResult = -1;
       return -1;
   }
   pos = consult_from;
!print "[readHexValue ";
   text = WordAddress( pos );
!print "(hex = ",(string)text,")";
   len = WordLength( pos );
   if (len >= 1)
   {
       if (text->(len-1) == 'h')
       {
           ! allow for the last character in the string to be a
           ! 'h', for 80x86 assembly compatability mode.
           --len;
       }
   }
   while (text->start == '0')
   {
       ++start;
   }
   if ((len-start) > 4)
   {
       ! we can only process up to 4 hex digits
       Mathpars_Result = -2;
       return -1;
   }
   val = 0;
   for (i = start : i < len : i++)
   {
!print ": digit ",(char)text->i," is decimal ";
       if (text->i >= '0' && text->i <= '9')
       {
           digit = text->i - '0';
       }
       else
       if (text->i >= 'a' && text->i <= 'f')
       {
           digit = text->i - 'a' + 10;
       }
       else
       if (text->i >= 'A' && text->i <= 'F')
       {
           digit = text->i - 'A' + 10;
       }
       else
       {
           ! non-hex value
!print "*not hex value*^";
           Mathpars_Result = text->i;
           return -1;
       }
       ! add in the new digit
       val = val * 16 + digit;
!print digit," (val now ",val,")";
   }
!print ": returning value ",val,"]^";
   return val;
];


!-----------------------------------------------------------------
! Reads in from input parse buffer a binary value, and returns it
! as a number. If the global Mathpars_HexResult non-zero, then
! the value is not a Binary value.  The value read is considered
! the consult part of the buffer.
!-----------------------------------------------------------------
[ mathpars_readBinValue
   val digit text i pos len start; ! locals

   Mathpars_Result = 0;

   if (consult_words ~= 1)
   {
       Mathpars_Result = -1;
       return -1;
   }
   pos = consult_from;
!print "[readBinValue ";
   text = WordAddress( pos );
!print "(bin = ",(string)text,")";
   len = WordLength( pos );
   if (len >= 1)
   {
       if (text->(len-1) == 'b')
       {
           ! allow for the last character in the string to be a
           ! 'b', for 80x86 assembly compatability mode.
           --len;
       }
   }
   while (text->start == '0')
   {
       ++start;
   }
   if ((len-start) > 8)
   {
       ! we can only process up to 8 binary digits
       Mathpars_Result = -2;
       return -1;
   }
   val = 0;
   for (i = 0 : i < len : i++)
   {
!print ": digit ",(char)text->i," is decimal ";
       if (text->i >= '0' && text->i <= '1')
       {
           digit = text->i - '0';
       }
       else
       {
           ! non-bin value
!print "*not binary value*^";
           Mathpars_Result = text->i;
           return -1;
       }
       ! add in the new digit
       val = val * 2 + digit;
!print digit," (val now ",val,")";
   }
!print ": returning value ",val,"]^";
   return val;
];



!-----------------------------------------------------------------
! Reads in from input parse buffer a binary value, and returns it
! as a number. If the global HexResult non-zero, then
! the value is not a Hex value.  The value read is considered
! the consult part of the buffer.
!-----------------------------------------------------------------
[ mathpars_readDecValue
   val digit text i pos len; ! locals

   Mathpars_Result = 0;

   if (consult_words ~= 1)
   {
       Mathpars_Result = -1;
       return -1;
   }
   pos = consult_from;
!print "[readDecValue ";
   text = WordAddress( pos );
!print "(dec = ",(string)text,")";
   len = WordLength( pos );
   if (len >= 1)
   {
       if (text->(len-1) == 'd')
       {
           ! allow for the last character in the string to be a
           ! 'd', for 80x86 assembly compatability mode.
           --len;
       }
   }
   for (i = 0 : i < len : i++)
   {
!print ": digit ",(char)text->i," is decimal ";
       if (text->i >= '0' && text->i <= '9')
       {
           digit = text->i - '0';
       }
       else
       {
           ! non-decimal value
!print "*not decimal value*^";
           Mathpars_Result = text->i;
           return -1;
       }
       ! add in the new digit
       val = val * 10 + digit;
!print digit," (val now ",val,")";
   }
!print ": returning value ",val,"]^";
   return val;
];


Endif; ! MATHPARS_INCLUDED