np/numparser2.h

changeset 60
a4033700e35c
child 62
aae5facf9fc5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/np/numparser2.h	Mon Feb 16 18:04:44 2004 +0100
@@ -0,0 +1,272 @@
+/*
+ * libtu/numparser2.h
+ *
+ * Copyright (c) Tuomo Valkonen 1999-2002. 
+ *
+ * You may distribute and modify this library under the terms of either
+ * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
+ */
+
+#define MAX_MANTISSA 10  /* should be enough for our needs */
+#define ULONG_SIZE (sizeof(ulong)*8)
+
+enum{
+	NPNUM_INT,
+	NPNUM_FLOAT
+};
+
+#ifdef NP_SIMPLE_IMPL
+
+typedef struct _NPNum{
+	int type;
+	int base;
+ 	bool negative;
+	double fval;
+	ulong ival;
+} NPNum;
+
+#define NUM_INIT {0, 0, 0, 0.0, 0}
+
+static int npnum_mulbase_add(NPNum *num, long base, long v)
+{
+	double iold=num->ival;
+	
+	num->fval=num->fval*base+(double)v;
+	
+	num->ival*=base;
+	
+	if(num->ival<iold)
+		num->type=NPNUM_FLOAT;
+	
+	num->ival+=v;
+	
+	return 0;
+}
+
+#else /* NP_SIMPLE_IMPL */
+
+typedef struct _NPNum{
+	unsigned char nmantissa;
+	int type;
+	int base;
+ 	bool negative;
+	ulong mantissa[MAX_MANTISSA];
+	long exponent;
+} NPNum;
+
+#define NUM_INIT {0, 0, 0, 0, {0,}, 0}
+
+#define ADD_EXP(NUM, X) (NUM)->exponent+=(X);
+
+#if defined(__GNUG__) && defined(i386) && !defined(NP_NO_I386_ASM)
+ #define NP_I386_ASM
+#endif
+
+static int npnum_mulbase_add(NPNum *num, long base, long v)
+{
+	long i, j;
+	ulong overflow;
+#ifndef NP_I386_ASM
+	ulong val;
+#endif
+	
+	for(i=num->nmantissa;i>=0;i--){
+#ifdef NP_I386_ASM
+		__asm__("mul %4\n"
+				: "=a" (num->mantissa[i]), "=d" (overflow)
+				: "0" (num->mantissa[i]), "1" (0), "q" (base)
+				: "eax", "edx");
+#else
+		overflow=0;
+		val=num->mantissa[i];
+		
+		if(val<ULONG_MAX/base){
+			val*=base;
+		}else if(val){
+			ulong tmp=val;
+			ulong old=val;
+			for(j=0; j<base; j++){
+				val+=tmp;
+				if(val<=old)
+					overflow++;
+				old=val;
+			}
+		}
+		num->mantissa[i]=val;
+#endif
+		if(overflow){
+			if(i==num->nmantissa){
+				if(num->nmantissa==MAX_MANTISSA)
+					return E_TOKZ_TOOBIG;
+				num->nmantissa++;
+			}
+			num->mantissa[i+1]+=overflow;
+		}
+	}
+	num->mantissa[0]+=v;
+	
+	return 0;
+}
+
+#undef NP_I386_ASM
+
+#endif /* NP_SIMPLE_IMPL */
+
+
+/* */
+
+
+static bool is_end(int c)
+{
+	/* oops... EOF was missing */
+	return (c==EOF || (c!='.' && ispunct(c)) || isspace(c) || iscntrl(c));
+}
+
+
+/* */
+
+
+static int parse_exponent(NPNum *num, Tokenizer *tokz, int c)
+{
+	long exp=0;
+	bool neg=FALSE;
+	int err=0;
+	
+	c=GETCH();
+	
+	if(c=='-' || c=='+'){
+		if(c=='-')
+			neg=TRUE;
+		c=GETCH();
+	}
+	
+	for(; 1; c=GETCH()){
+		if(isdigit(c)){
+			exp*=10;
+			exp+=c-'0';
+		}else if(is_end(c)){
+			UNGETCH(c);
+			break;
+		}else{
+			err=E_TOKZ_NUMFMT;
+		}
+	}
+
+	if(neg)
+		exp*=-1;
+
+#ifndef NP_SIMPLE_IMPL
+	ADD_EXP(num, exp);
+#else
+	num->fval*=pow(num->base, exp);
+#endif	
+	return err;
+}
+
+
+static int parse_number(NPNum *num, Tokenizer *tokz, int c)
+{
+	int base=10;
+	int dm=1;
+	int err=0;
+	int tmp;
+#ifdef NP_SIMPLE_IMPL
+	double divisor=base;
+#endif	
+	
+	if(c=='-' || c=='+'){
+		if(c=='-')
+			num->negative=TRUE;
+		c=GETCH();
+		if(!isdigit(c))
+			err=E_TOKZ_NUMFMT;
+	}
+	
+	if(c=='0'){
+		dm=0;
+		c=GETCH();
+		if(c=='x' || c=='X'){
+			base=16;
+			c=GETCH();
+		}else if(c=='b' || c=='B'){
+			base=2;
+			c=GETCH();
+		}else if('0'<=c && c<='7'){
+			base=8;
+		}else{
+			dm=2;
+		}
+	}
+	
+	num->base=base;
+	
+	for(; 1; c=GETCH()){
+		if((c=='e' || c=='E') && dm!=0){
+			if(dm<2){
+				err=E_TOKZ_NUMFMT;
+				continue;
+			}
+			tmp=parse_exponent(num, tokz, c);
+			if(err==0)
+				err=tmp;
+			break;
+		}
+		
+		if(isxdigit(c)){
+			if('0'<=c && c<='9')
+				c-='0';
+			else if(isupper(c))
+				c-='A'-10;
+			else
+				c-='a'-10;
+			
+			if(c>=base)
+				err=E_TOKZ_NUMFMT;
+
+#ifdef NP_SIMPLE_IMPL
+			if(dm==3){
+				num->fval+=(double)c/divisor;
+				divisor*=base;
+			}else
+#endif
+			{
+				tmp=npnum_mulbase_add(num, base, c);
+				if(err==0)
+					err=tmp;
+			}
+			
+			if(dm==1)
+				dm=2;
+#ifndef NP_SIMPLE_IMPL			
+			else if(dm==3)
+				ADD_EXP(num, -1);
+#endif			
+			continue;
+		}
+		
+		if(c=='.'){
+			if(dm!=2){
+				err=E_TOKZ_NUMFMT;
+			}
+			dm=3;
+#ifdef NP_SIMPLE_IMPL
+			num->type=NPNUM_FLOAT;
+			divisor=base;
+#endif
+			continue;
+		}
+		
+		if(is_end(c)){
+			UNGETCH(c);
+			break;
+		}
+		
+		err=E_TOKZ_NUMFMT;
+	}
+	
+#ifndef NP_SIMPLE_IMPL			
+	num->type=(num->exponent==0 ? NPNUM_INT : NPNUM_FLOAT);
+#endif
+
+	return err;
+}

mercurial