|
1 /* |
|
2 * libtu/numparser2.h |
|
3 * |
|
4 * Copyright (c) Tuomo Valkonen 1999-2000. |
|
5 * |
|
6 * This file is distributed under the terms of the "Artistic License". |
|
7 * See the included file LICENSE for details. |
|
8 */ |
|
9 |
|
10 #define MAX_MANTISSA 10 /* should be enough for our needs */ |
|
11 #define ULONG_SIZE (sizeof(ulong)*8) |
|
12 |
|
13 /* |
|
14 * Can handle quite big numbers (depends on MAX_MANTISSA). This is really |
|
15 * a crappy little hack (especially the generic (non-i386asm) mulby10 |
|
16 * algorithm... hopefully it works...) but this'll do for now. |
|
17 */ |
|
18 |
|
19 enum{ |
|
20 NPNUM_INT, |
|
21 NPNUM_FLOAT |
|
22 }; |
|
23 |
|
24 typedef struct _NPNum{ |
|
25 unsigned char nmantissa; |
|
26 int type; |
|
27 int base; |
|
28 bool negative; |
|
29 ulong mantissa[MAX_MANTISSA]; |
|
30 long exponent; |
|
31 } NPNum; |
|
32 |
|
33 #define NUM_INIT {0, 0, 0, 0, {0,}, 0} |
|
34 |
|
35 #define ADD_EXP(NUM, X) (NUM)->exponent+=(X); |
|
36 |
|
37 #if defined(__GNUG__) && defined(i386) && !defined(LIBTU_NP_NO_I386_ASM) |
|
38 #define LIBTU_NP_I386_ASM |
|
39 #endif |
|
40 |
|
41 static int npnum_mulbase_add(NPNum *num, long base, long v) |
|
42 { |
|
43 long i, j; |
|
44 ulong overflow; |
|
45 #ifndef LIBTU_NP_I386_ASM |
|
46 ulong val; |
|
47 #endif |
|
48 |
|
49 for(i=num->nmantissa;i>=0;i--){ |
|
50 #ifdef LIBTU_NP_I386_ASM |
|
51 __asm__("mul %4\n" |
|
52 : "=a" (num->mantissa[i]), "=d" (overflow) |
|
53 : "0" (num->mantissa[i]), "1" (0), "q" (base) |
|
54 : "eax", "edx"); |
|
55 #else |
|
56 overflow=0; |
|
57 val=num->mantissa[i]; |
|
58 |
|
59 if(val<ULONG_MAX/base){ |
|
60 val*=base; |
|
61 }else if(val){ |
|
62 ulong tmp=val; |
|
63 ulong old=val; |
|
64 for(j=0; j<base; j++){ |
|
65 val+=tmp; |
|
66 if(val<=old) |
|
67 overflow++; |
|
68 old=val; |
|
69 } |
|
70 } |
|
71 num->mantissa[i]=val; |
|
72 #endif |
|
73 if(overflow){ |
|
74 if(i==num->nmantissa){ |
|
75 if(num->nmantissa==MAX_MANTISSA) |
|
76 return E_TOKZ_TOOBIG; |
|
77 num->nmantissa++; |
|
78 } |
|
79 num->mantissa[i+1]+=overflow; |
|
80 } |
|
81 } |
|
82 num->mantissa[0]+=v; |
|
83 |
|
84 return 0; |
|
85 } |
|
86 |
|
87 #undef LIBTU_NP_I386_ASM |
|
88 |
|
89 |
|
90 /* */ |
|
91 |
|
92 |
|
93 static bool is_end(int c) |
|
94 { |
|
95 return ((c!='.' && ispunct(c)) || isspace(c) || iscntrl(c)); |
|
96 } |
|
97 |
|
98 |
|
99 /* */ |
|
100 |
|
101 |
|
102 static int parse_exponent(NPNum *num, Tokenizer *tokz, int c) |
|
103 { |
|
104 long exp=0; |
|
105 bool neg=FALSE; |
|
106 int err=0; |
|
107 |
|
108 c=GETCH(); |
|
109 |
|
110 if(c=='-' || c=='+'){ |
|
111 if(c=='-') |
|
112 neg=TRUE; |
|
113 c=GETCH(); |
|
114 } |
|
115 |
|
116 for(; 1; c=GETCH()){ |
|
117 if(isdigit(c)){ |
|
118 exp*=10; |
|
119 exp+=c-'0'; |
|
120 }else if(is_end(c)){ |
|
121 UNGETCH(c); |
|
122 break; |
|
123 }else{ |
|
124 err=E_TOKZ_NUMFMT; |
|
125 } |
|
126 } |
|
127 |
|
128 if(neg) |
|
129 exp*=-1; |
|
130 |
|
131 ADD_EXP(num, exp); |
|
132 |
|
133 return err; |
|
134 } |
|
135 |
|
136 |
|
137 static int parse_number(NPNum *num, Tokenizer *tokz, int c) |
|
138 { |
|
139 int base=10; |
|
140 int dm=1; |
|
141 int err=0; |
|
142 int tmp; |
|
143 |
|
144 if(c=='-' || c=='+'){ |
|
145 if(c=='-') |
|
146 num->negative=TRUE; |
|
147 c=GETCH(); |
|
148 if(!isdigit(c)) |
|
149 err=E_TOKZ_NUMFMT; |
|
150 }else if(c=='0'){ |
|
151 dm=0; |
|
152 c=GETCH(); |
|
153 if(c=='x' || c=='X'){ |
|
154 base=16; |
|
155 c=GETCH(); |
|
156 }else if(c=='b' || c=='B'){ |
|
157 base=2; |
|
158 c=GETCH(); |
|
159 }else if('0'<=c && c<='7'){ |
|
160 base=7; |
|
161 }else{ |
|
162 dm=2; |
|
163 } |
|
164 } |
|
165 |
|
166 num->base=base; |
|
167 |
|
168 for(; 1; c=GETCH()){ |
|
169 if((c=='e' || c=='E') && dm!=0){ |
|
170 if(dm<2){ |
|
171 err=E_TOKZ_NUMFMT; |
|
172 continue; |
|
173 } |
|
174 tmp=parse_exponent(num, tokz, c); |
|
175 if(err==0) |
|
176 err=tmp; |
|
177 break; |
|
178 } |
|
179 |
|
180 if(isxdigit(c)){ |
|
181 if('0'<=c && c<='9') |
|
182 c-='0'; |
|
183 else if(isupper(c)) |
|
184 c-='A'-10; |
|
185 else |
|
186 c-='a'-10; |
|
187 |
|
188 if(c>=base) |
|
189 err=E_TOKZ_NUMFMT; |
|
190 |
|
191 npnum_mulbase_add(num, base, c); |
|
192 |
|
193 if(dm==1) |
|
194 dm=2; |
|
195 else if(dm==3) |
|
196 ADD_EXP(num, -1); |
|
197 |
|
198 continue; |
|
199 } |
|
200 |
|
201 if(c=='.'){ |
|
202 if(dm!=2) |
|
203 err=E_TOKZ_NUMFMT; |
|
204 else |
|
205 dm=3; |
|
206 continue; |
|
207 } |
|
208 |
|
209 if(is_end(c)){ |
|
210 UNGETCH(c); |
|
211 break; |
|
212 } |
|
213 |
|
214 err=E_TOKZ_NUMFMT; |
|
215 } |
|
216 |
|
217 num->type=(num->exponent==0 ? NPNUM_INT : NPNUM_FLOAT); |
|
218 |
|
219 return err; |
|
220 } |