| |
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 } |