/* Copyright Per Bothner 1987. Read the file Q-INFO */
#include <types.h>
//#include <hash.h>
#include <procs.h>
#include <parsefile.h>
#include "expression.h"
#include "newtype.h"
#include <typetabs.h>
#include "gennum.h"
#include <stdlib.h>
#include <ctype.h>
#include "tempbuf.h"

char _CharNum[129] = { /* What value has a letter considered as a digit ? */
    -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,
    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1};
#define CharNum (_CharNum+1)
/* the (u_char) coercion is to map EOF to an invalid char (DEL) -doesn't work*/
#define NumValue(ch, base) ({int __ch__ = ch; CharNum[__ch__]<(base) ? CharNum[__ch__] : -1;})

int SpecialBase = 16;
extern double atof();
extern "C" Double *AllocDouble(double val);
extern Root *ParseUnitNum(istream& istr, double num);

// Read a number from 'ff'. Start with 'ch', unless it is -1.
Expr * ParseNumeric(register struct ParseFile *ff,
		    register int ch /* = -1*/)
{
    register base = 10;
    TempBuf buffer;
    const Root *val;
    struct ExprQuote *quote;
    int base_len = 0;
    int digits_start = 0;
    int decimals_start = 0;
    int len = 0;
    char *str;
    int has_base = 0;
    int has_decimal_point = 0, has_exponent = 0;
    int base_start;
    int imaginary = 0;

    /* perhaps skip spaces? accept signs? */
    base_start = 0;
    digits_start = base_start;
    if (ch == -1)
	ch = ff->get();
    while (NumValue(ch, 10) >= 0) {
	buffer.put(ch);
	ch = ff->get();
    }
    if (ch == 'x' || ch == 'r' || ch == 'R') {
	has_base = ch;
	base_len = buffer.size() - base_start;
	buffer.put(ch);
	digits_start = base_len + 1 + base_start;
	char *str = buffer.string();
	if (has_base == 'x') {
	    if (base_len > 1 || str[base_start] != '0')
		goto failed;
	    base = 16;
	} else if (has_base) {
	    if (base_len > 2) goto failed;
	    base = str[base_start] - '0';
	    if (base_len > 1)
		base = base * 10 + str[base_start+1] - '0';
	    if (base > 36 || base < 2) {
		ParseError(ff, "Illegal radix: %d (must be 2..36)", base);
		goto failed;
	    }
	}
	for (;;) {
	    ch = ff->get();
	    if (NumValue(ch, base) < 0) break;
	    buffer.put(ch);
	}
    }
    if (ch == '.') {
	has_decimal_point++;
	buffer.put(ch);
	decimals_start = buffer.size() + 1;
	for (;;) {
	    ch = ff->get();
	    if (NumValue(ch, base) < 0) break;
	    buffer.put(ch);
	}
    }
    if (ch == 'e' || ch == 'E') {
	has_exponent++;
	buffer.put(ch);
	ch = ff->get();
	if (ch == '+' || ch == '-') {
	    buffer.put(ch);
	    ch = ff->get();
	}
	for (;;) {
	    if (NumValue(ch, 10) < 0) break;
	    buffer.put(ch);
	    ch = ff->get();
	}
    }
    if (ch == 'i')
	imaginary = 1;
    else if (ch != EOF)
      {
	ff->putback(ch);
	if (isalpha(ch))
	  {
	    istream istr(ff->rdbuf());
	    str = buffer.copy(); /* FIXME! */
	    val = ParseUnitNum(istr, strtod(str+digits_start, 0));
	    if (val == NULL)
	      goto failed;
	    goto ok;
	  }
      }
    {
    str = buffer.copy();
    if (has_decimal_point+has_exponent) {
	if (base != 10) goto failed;
#if 1
	val = new Double(strtod(str+digits_start, 0));
#else
	val = new Double(atof(str+digits_start));
#endif
    } else {
	val = StrToInt(str+digits_start, base);
    }
    if (imaginary)
	val = new ComplexPair(*Zero, *(Real*)val);
  ok:
    quote = new ExprQuote((Root*)val);
    quote->text = str;
    }
    return (Expr*)quote;
 failed:
    /* should prabably skip alphamerics */
    return FailedParse;
}
