! mathpars.h - Parse Big Numbers and other formats in Inform.
!   Version 1.0 (19-Sep-2001)
!
! by Matt Albrecht - groboclown@users.sourceforge.net
!
! (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
