diff options
Diffstat (limited to 'shell/math.c')
-rw-r--r-- | shell/math.c | 684 |
1 files changed, 361 insertions, 323 deletions
diff --git a/shell/math.c b/shell/math.c index a4c55a4d0..760645d0f 100644 --- a/shell/math.c +++ b/shell/math.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * arithmetic code ripped out of ash shell for code sharing | 2 | * Arithmetic code ripped out of ash shell for code sharing. |
3 | * | 3 | * |
4 | * This code is derived from software contributed to Berkeley by | 4 | * This code is derived from software contributed to Berkeley by |
5 | * Kenneth Almquist. | 5 | * Kenneth Almquist. |
@@ -26,43 +26,41 @@ | |||
26 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 26 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
27 | */ | 27 | */ |
28 | /* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com> | 28 | /* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com> |
29 | 29 | * | |
30 | Permission is hereby granted, free of charge, to any person obtaining | 30 | * Permission is hereby granted, free of charge, to any person obtaining |
31 | a copy of this software and associated documentation files (the | 31 | * a copy of this software and associated documentation files (the |
32 | "Software"), to deal in the Software without restriction, including | 32 | * "Software"), to deal in the Software without restriction, including |
33 | without limitation the rights to use, copy, modify, merge, publish, | 33 | * without limitation the rights to use, copy, modify, merge, publish, |
34 | distribute, sublicense, and/or sell copies of the Software, and to | 34 | * distribute, sublicense, and/or sell copies of the Software, and to |
35 | permit persons to whom the Software is furnished to do so, subject to | 35 | * permit persons to whom the Software is furnished to do so, subject to |
36 | the following conditions: | 36 | * the following conditions: |
37 | 37 | * | |
38 | The above copyright notice and this permission notice shall be | 38 | * The above copyright notice and this permission notice shall be |
39 | included in all copies or substantial portions of the Software. | 39 | * included in all copies or substantial portions of the Software. |
40 | 40 | * | |
41 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 41 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
42 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | 42 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
43 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | 43 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
44 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | 44 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
45 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | 45 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
46 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | 46 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
47 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 47 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
48 | */ | 48 | */ |
49 | 49 | ||
50 | /* This is my infix parser/evaluator. It is optimized for size, intended | 50 | /* This is my infix parser/evaluator. It is optimized for size, intended |
51 | * as a replacement for yacc-based parsers. However, it may well be faster | 51 | * as a replacement for yacc-based parsers. However, it may well be faster |
52 | * than a comparable parser written in yacc. The supported operators are | 52 | * than a comparable parser written in yacc. The supported operators are |
53 | * listed in #defines below. Parens, order of operations, and error handling | 53 | * listed in #defines below. Parens, order of operations, and error handling |
54 | * are supported. This code is thread safe. The exact expression format should | 54 | * are supported. This code is thread safe. The exact expression format should |
55 | * be that which POSIX specifies for shells. */ | 55 | * be that which POSIX specifies for shells. |
56 | 56 | * | |
57 | /* The code uses a simple two-stack algorithm. See | 57 | * The code uses a simple two-stack algorithm. See |
58 | * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html | 58 | * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html |
59 | * for a detailed explanation of the infix-to-postfix algorithm on which | 59 | * for a detailed explanation of the infix-to-postfix algorithm on which |
60 | * this is based (this code differs in that it applies operators immediately | 60 | * this is based (this code differs in that it applies operators immediately |
61 | * to the stack instead of adding them to a queue to end up with an | 61 | * to the stack instead of adding them to a queue to end up with an |
62 | * expression). */ | 62 | * expression). |
63 | 63 | */ | |
64 | /* To use the routine, call it with an expression string and error return | ||
65 | * pointer */ | ||
66 | 64 | ||
67 | /* | 65 | /* |
68 | * Aug 24, 2001 Manuel Novoa III | 66 | * Aug 24, 2001 Manuel Novoa III |
@@ -104,28 +102,23 @@ | |||
104 | * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru> | 102 | * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru> |
105 | * | 103 | * |
106 | * - allow access to variable, | 104 | * - allow access to variable, |
107 | * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6) | 105 | * use recursive value indirection: c="2*2"; a="c"; echo $((a+=2)) produce 6 |
108 | * - realize assign syntax (VAR=expr, +=, *= etc) | 106 | * - implement assign syntax (VAR=expr, +=, *= etc) |
109 | * - realize exponentiation (** operator) | 107 | * - implement exponentiation (** operator) |
110 | * - realize comma separated - expr, expr | 108 | * - implement comma separated - expr, expr |
111 | * - realise ++expr --expr expr++ expr-- | 109 | * - implement ++expr --expr expr++ expr-- |
112 | * - realise expr ? expr : expr (but, second expr calculate always) | 110 | * - implement expr ? expr : expr (but second expr is always calculated) |
113 | * - allow hexadecimal and octal numbers | 111 | * - allow hexadecimal and octal numbers |
114 | * - was restored loses XOR operator | 112 | * - restore lost XOR operator |
115 | * - remove one goto label, added three ;-) | 113 | * - protect $((num num)) as true zero expr (Manuel's error) |
116 | * - protect $((num num)) as true zero expr (Manuel`s error) | ||
117 | * - always use special isspace(), see comment from bash ;-) | 114 | * - always use special isspace(), see comment from bash ;-) |
118 | */ | 115 | */ |
119 | #include "libbb.h" | 116 | #include "libbb.h" |
120 | #include "math.h" | 117 | #include "math.h" |
121 | 118 | ||
122 | #define a_e_h_t arith_eval_hooks_t | 119 | #define lookupvar (math_state->lookupvar) |
123 | #define lookupvar (math_hooks->lookupvar) | 120 | #define setvar (math_state->setvar ) |
124 | #define setvar (math_hooks->setvar ) | 121 | //#define endofname (math_state->endofname) |
125 | //#define endofname (math_hooks->endofname) | ||
126 | |||
127 | #define arith_isspace(arithval) \ | ||
128 | (arithval == ' ' || arithval == '\n' || arithval == '\t') | ||
129 | 122 | ||
130 | typedef unsigned char operator; | 123 | typedef unsigned char operator; |
131 | 124 | ||
@@ -133,181 +126,199 @@ typedef unsigned char operator; | |||
133 | * precedence, and 3 high bits are an ID unique across operators of that | 126 | * precedence, and 3 high bits are an ID unique across operators of that |
134 | * precedence. The ID portion is so that multiple operators can have the | 127 | * precedence. The ID portion is so that multiple operators can have the |
135 | * same precedence, ensuring that the leftmost one is evaluated first. | 128 | * same precedence, ensuring that the leftmost one is evaluated first. |
136 | * Consider * and /. */ | 129 | * Consider * and / |
137 | 130 | */ | |
138 | #define tok_decl(prec,id) (((id)<<5)|(prec)) | 131 | #define tok_decl(prec,id) (((id)<<5) | (prec)) |
139 | #define PREC(op) ((op) & 0x1F) | 132 | #define PREC(op) ((op) & 0x1F) |
140 | |||
141 | #define TOK_LPAREN tok_decl(0,0) | ||
142 | 133 | ||
143 | #define TOK_COMMA tok_decl(1,0) | 134 | #define TOK_LPAREN tok_decl(0,0) |
144 | 135 | ||
145 | #define TOK_ASSIGN tok_decl(2,0) | 136 | #define TOK_COMMA tok_decl(1,0) |
146 | #define TOK_AND_ASSIGN tok_decl(2,1) | ||
147 | #define TOK_OR_ASSIGN tok_decl(2,2) | ||
148 | #define TOK_XOR_ASSIGN tok_decl(2,3) | ||
149 | #define TOK_PLUS_ASSIGN tok_decl(2,4) | ||
150 | #define TOK_MINUS_ASSIGN tok_decl(2,5) | ||
151 | #define TOK_LSHIFT_ASSIGN tok_decl(2,6) | ||
152 | #define TOK_RSHIFT_ASSIGN tok_decl(2,7) | ||
153 | 137 | ||
154 | #define TOK_MUL_ASSIGN tok_decl(3,0) | 138 | /* All assignments are right associative and have the same precedence, |
155 | #define TOK_DIV_ASSIGN tok_decl(3,1) | 139 | * but there are 11 of them, which doesn't fit into 3 bits for unique id. |
156 | #define TOK_REM_ASSIGN tok_decl(3,2) | 140 | * Abusing another precedence level: |
141 | */ | ||
142 | #define TOK_ASSIGN tok_decl(2,0) | ||
143 | #define TOK_AND_ASSIGN tok_decl(2,1) | ||
144 | #define TOK_OR_ASSIGN tok_decl(2,2) | ||
145 | #define TOK_XOR_ASSIGN tok_decl(2,3) | ||
146 | #define TOK_PLUS_ASSIGN tok_decl(2,4) | ||
147 | #define TOK_MINUS_ASSIGN tok_decl(2,5) | ||
148 | #define TOK_LSHIFT_ASSIGN tok_decl(2,6) | ||
149 | #define TOK_RSHIFT_ASSIGN tok_decl(2,7) | ||
157 | 150 | ||
158 | /* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */ | 151 | #define TOK_MUL_ASSIGN tok_decl(3,0) |
159 | #define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0) | 152 | #define TOK_DIV_ASSIGN tok_decl(3,1) |
153 | #define TOK_REM_ASSIGN tok_decl(3,2) | ||
160 | 154 | ||
161 | /* conditional is right associativity too */ | 155 | #define fix_assignment_prec(prec) do { if (prec == 3) prec = 2; } while (0) |
162 | #define TOK_CONDITIONAL tok_decl(4,0) | ||
163 | #define TOK_CONDITIONAL_SEP tok_decl(4,1) | ||
164 | 156 | ||
165 | #define TOK_OR tok_decl(5,0) | 157 | /* Ternary conditional operator is right associative too */ |
158 | #define TOK_CONDITIONAL tok_decl(4,0) | ||
159 | #define TOK_CONDITIONAL_SEP tok_decl(4,1) | ||
166 | 160 | ||
167 | #define TOK_AND tok_decl(6,0) | 161 | #define TOK_OR tok_decl(5,0) |
168 | 162 | ||
169 | #define TOK_BOR tok_decl(7,0) | 163 | #define TOK_AND tok_decl(6,0) |
170 | 164 | ||
171 | #define TOK_BXOR tok_decl(8,0) | 165 | #define TOK_BOR tok_decl(7,0) |
172 | 166 | ||
173 | #define TOK_BAND tok_decl(9,0) | 167 | #define TOK_BXOR tok_decl(8,0) |
174 | 168 | ||
175 | #define TOK_EQ tok_decl(10,0) | 169 | #define TOK_BAND tok_decl(9,0) |
176 | #define TOK_NE tok_decl(10,1) | ||
177 | 170 | ||
178 | #define TOK_LT tok_decl(11,0) | 171 | #define TOK_EQ tok_decl(10,0) |
179 | #define TOK_GT tok_decl(11,1) | 172 | #define TOK_NE tok_decl(10,1) |
180 | #define TOK_GE tok_decl(11,2) | ||
181 | #define TOK_LE tok_decl(11,3) | ||
182 | 173 | ||
183 | #define TOK_LSHIFT tok_decl(12,0) | 174 | #define TOK_LT tok_decl(11,0) |
184 | #define TOK_RSHIFT tok_decl(12,1) | 175 | #define TOK_GT tok_decl(11,1) |
176 | #define TOK_GE tok_decl(11,2) | ||
177 | #define TOK_LE tok_decl(11,3) | ||
185 | 178 | ||
186 | #define TOK_ADD tok_decl(13,0) | 179 | #define TOK_LSHIFT tok_decl(12,0) |
187 | #define TOK_SUB tok_decl(13,1) | 180 | #define TOK_RSHIFT tok_decl(12,1) |
188 | 181 | ||
189 | #define TOK_MUL tok_decl(14,0) | 182 | #define TOK_ADD tok_decl(13,0) |
190 | #define TOK_DIV tok_decl(14,1) | 183 | #define TOK_SUB tok_decl(13,1) |
191 | #define TOK_REM tok_decl(14,2) | ||
192 | 184 | ||
193 | /* exponent is right associativity */ | 185 | #define TOK_MUL tok_decl(14,0) |
194 | #define TOK_EXPONENT tok_decl(15,1) | 186 | #define TOK_DIV tok_decl(14,1) |
187 | #define TOK_REM tok_decl(14,2) | ||
195 | 188 | ||
196 | /* For now unary operators. */ | 189 | /* Exponent is right associative */ |
197 | #define UNARYPREC 16 | 190 | #define TOK_EXPONENT tok_decl(15,1) |
198 | #define TOK_BNOT tok_decl(UNARYPREC,0) | ||
199 | #define TOK_NOT tok_decl(UNARYPREC,1) | ||
200 | 191 | ||
201 | #define TOK_UMINUS tok_decl(UNARYPREC+1,0) | 192 | /* Unary operators */ |
202 | #define TOK_UPLUS tok_decl(UNARYPREC+1,1) | 193 | #define UNARYPREC 16 |
194 | #define TOK_BNOT tok_decl(UNARYPREC,0) | ||
195 | #define TOK_NOT tok_decl(UNARYPREC,1) | ||
203 | 196 | ||
204 | #define PREC_PRE (UNARYPREC+2) | 197 | #define TOK_UMINUS tok_decl(UNARYPREC+1,0) |
198 | #define TOK_UPLUS tok_decl(UNARYPREC+1,1) | ||
205 | 199 | ||
206 | #define TOK_PRE_INC tok_decl(PREC_PRE, 0) | 200 | #define PREC_PRE (UNARYPREC+2) |
207 | #define TOK_PRE_DEC tok_decl(PREC_PRE, 1) | ||
208 | 201 | ||
209 | #define PREC_POST (UNARYPREC+3) | 202 | #define TOK_PRE_INC tok_decl(PREC_PRE, 0) |
203 | #define TOK_PRE_DEC tok_decl(PREC_PRE, 1) | ||
210 | 204 | ||
211 | #define TOK_POST_INC tok_decl(PREC_POST, 0) | 205 | #define PREC_POST (UNARYPREC+3) |
212 | #define TOK_POST_DEC tok_decl(PREC_POST, 1) | ||
213 | 206 | ||
214 | #define SPEC_PREC (UNARYPREC+4) | 207 | #define TOK_POST_INC tok_decl(PREC_POST, 0) |
208 | #define TOK_POST_DEC tok_decl(PREC_POST, 1) | ||
215 | 209 | ||
216 | #define TOK_NUM tok_decl(SPEC_PREC, 0) | 210 | #define SPEC_PREC (UNARYPREC+4) |
217 | #define TOK_RPAREN tok_decl(SPEC_PREC, 1) | ||
218 | 211 | ||
219 | #define NUMPTR (*numstackptr) | 212 | #define TOK_NUM tok_decl(SPEC_PREC, 0) |
213 | #define TOK_RPAREN tok_decl(SPEC_PREC, 1) | ||
220 | 214 | ||
221 | static int | 215 | static int |
222 | tok_have_assign(operator op) | 216 | is_assign_op(operator op) |
223 | { | 217 | { |
224 | operator prec = PREC(op); | 218 | operator prec = PREC(op); |
225 | 219 | fix_assignment_prec(prec); | |
226 | convert_prec_is_assing(prec); | 220 | return prec == PREC(TOK_ASSIGN) |
227 | return (prec == PREC(TOK_ASSIGN) || | 221 | || prec == PREC_PRE |
228 | prec == PREC_PRE || prec == PREC_POST); | 222 | || prec == PREC_POST; |
229 | } | 223 | } |
230 | 224 | ||
231 | static int | 225 | static int |
232 | is_right_associativity(operator prec) | 226 | is_right_associative(operator prec) |
233 | { | 227 | { |
234 | return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT) | 228 | return prec == PREC(TOK_ASSIGN) |
235 | || prec == PREC(TOK_CONDITIONAL)); | 229 | || prec == PREC(TOK_EXPONENT) |
230 | || prec == PREC(TOK_CONDITIONAL); | ||
236 | } | 231 | } |
237 | 232 | ||
233 | |||
238 | typedef struct { | 234 | typedef struct { |
239 | arith_t val; | 235 | arith_t val; |
240 | arith_t contidional_second_val; | 236 | /* We acquire second_val only when "expr1 : expr2" part |
241 | char contidional_second_val_initialized; | 237 | * of ternary ?: op is evaluated. |
242 | char *var; /* if NULL then is regular number, | 238 | * We treat ?: as two binary ops: (expr ? (expr1 : expr2)). |
243 | else is variable name */ | 239 | * ':' produces a new value which has two parts, val and second_val; |
244 | } v_n_t; | 240 | * then '?' selects one of them based on its left side. |
245 | 241 | */ | |
246 | typedef struct chk_var_recursive_looped_t { | 242 | arith_t second_val; |
243 | char second_val_present; | ||
244 | /* If NULL then it's just a number, else it's a named variable */ | ||
245 | char *var; | ||
246 | } var_or_num_t; | ||
247 | |||
248 | typedef struct remembered_name { | ||
249 | struct remembered_name *next; | ||
247 | const char *var; | 250 | const char *var; |
248 | struct chk_var_recursive_looped_t *next; | 251 | } remembered_name; |
249 | } chk_var_recursive_looped_t; | ||
250 | 252 | ||
251 | static chk_var_recursive_looped_t *prev_chk_var_recursive; | ||
252 | 253 | ||
253 | static int | 254 | static arith_t FAST_FUNC |
254 | arith_lookup_val(v_n_t *t, a_e_h_t *math_hooks) | 255 | evaluate_string(arith_state_t *math_state, const char *expr); |
256 | |||
257 | static const char* | ||
258 | arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) | ||
255 | { | 259 | { |
256 | if (t->var) { | 260 | if (t->var) { |
257 | const char *p = lookupvar(t->var); | 261 | const char *p = lookupvar(t->var); |
258 | |||
259 | if (p) { | 262 | if (p) { |
260 | int errcode; | 263 | remembered_name *cur; |
261 | 264 | remembered_name cur_save; | |
262 | /* recursive try as expression */ | ||
263 | chk_var_recursive_looped_t *cur; | ||
264 | chk_var_recursive_looped_t cur_save; | ||
265 | 265 | ||
266 | for (cur = prev_chk_var_recursive; cur; cur = cur->next) { | 266 | /* did we already see this name? |
267 | * testcase: a=b; b=a; echo $((a)) | ||
268 | */ | ||
269 | for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) { | ||
267 | if (strcmp(cur->var, t->var) == 0) { | 270 | if (strcmp(cur->var, t->var) == 0) { |
268 | /* expression recursion loop detected */ | 271 | /* Yes */ |
269 | return -5; | 272 | return "expression recursion loop detected"; |
270 | } | 273 | } |
271 | } | 274 | } |
272 | /* save current lookuped var name */ | 275 | |
273 | cur = prev_chk_var_recursive; | 276 | /* push current var name */ |
277 | cur = math_state->list_of_recursed_names; | ||
274 | cur_save.var = t->var; | 278 | cur_save.var = t->var; |
275 | cur_save.next = cur; | 279 | cur_save.next = cur; |
276 | prev_chk_var_recursive = &cur_save; | 280 | math_state->list_of_recursed_names = &cur_save; |
281 | |||
282 | /* recursively evaluate p as expression */ | ||
283 | t->val = evaluate_string(math_state, p); | ||
277 | 284 | ||
278 | t->val = arith (p, &errcode, math_hooks); | 285 | /* pop current var name */ |
279 | /* restore previous ptr after recursiving */ | 286 | math_state->list_of_recursed_names = cur; |
280 | prev_chk_var_recursive = cur; | 287 | |
281 | return errcode; | 288 | return math_state->errmsg; |
282 | } | 289 | } |
283 | /* allow undefined var as 0 */ | 290 | /* treat undefined var as 0 */ |
284 | t->val = 0; | 291 | t->val = 0; |
285 | } | 292 | } |
286 | return 0; | 293 | return 0; |
287 | } | 294 | } |
288 | 295 | ||
289 | /* "applying" a token means performing it on the top elements on the integer | 296 | /* "Applying" a token means performing it on the top elements on the integer |
290 | * stack. For a unary operator it will only change the top element, but a | 297 | * stack. For an unary operator it will only change the top element, but a |
291 | * binary operator will pop two arguments and push a result */ | 298 | * binary operator will pop two arguments and push the result */ |
292 | static NOINLINE int | 299 | static NOINLINE const char* |
293 | arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr, a_e_h_t *math_hooks) | 300 | arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_or_num_t **numstackptr) |
294 | { | 301 | { |
295 | v_n_t *numptr_m1; | 302 | #define NUMPTR (*numstackptr) |
296 | arith_t numptr_val, rez; | 303 | |
297 | int ret_arith_lookup_val; | 304 | var_or_num_t *top_of_stack; |
305 | arith_t rez; | ||
306 | const char *err; | ||
298 | 307 | ||
299 | /* There is no operator that can work without arguments */ | 308 | /* There is no operator that can work without arguments */ |
300 | if (NUMPTR == numstack) goto err; | 309 | if (NUMPTR == numstack) |
301 | numptr_m1 = NUMPTR - 1; | 310 | goto err; |
311 | |||
312 | top_of_stack = NUMPTR - 1; | ||
302 | 313 | ||
303 | /* check operand is var with noninteger value */ | 314 | /* Resolve name to value, if needed */ |
304 | ret_arith_lookup_val = arith_lookup_val(numptr_m1, math_hooks); | 315 | err = arith_lookup_val(math_state, top_of_stack); |
305 | if (ret_arith_lookup_val) | 316 | if (err) |
306 | return ret_arith_lookup_val; | 317 | return err; |
307 | 318 | ||
308 | rez = numptr_m1->val; | 319 | rez = top_of_stack->val; |
309 | if (op == TOK_UMINUS) | 320 | if (op == TOK_UMINUS) |
310 | rez *= -1; | 321 | rez = -rez; |
311 | else if (op == TOK_NOT) | 322 | else if (op == TOK_NOT) |
312 | rez = !rez; | 323 | rez = !rez; |
313 | else if (op == TOK_BNOT) | 324 | else if (op == TOK_BNOT) |
@@ -318,118 +329,123 @@ arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr, a_e_h_t *math_hoo | |||
318 | rez--; | 329 | rez--; |
319 | else if (op != TOK_UPLUS) { | 330 | else if (op != TOK_UPLUS) { |
320 | /* Binary operators */ | 331 | /* Binary operators */ |
332 | arith_t right_side_val; | ||
333 | char bad_second_val; | ||
321 | 334 | ||
322 | /* check and binary operators need two arguments */ | 335 | /* Binary operators need two arguments */ |
323 | if (numptr_m1 == numstack) goto err; | 336 | if (top_of_stack == numstack) |
324 | |||
325 | /* ... and they pop one */ | ||
326 | --NUMPTR; | ||
327 | numptr_val = rez; | ||
328 | if (op == TOK_CONDITIONAL) { | ||
329 | if (!numptr_m1->contidional_second_val_initialized) { | ||
330 | /* protect $((expr1 ? expr2)) without ": expr" */ | ||
331 | goto err; | ||
332 | } | ||
333 | rez = numptr_m1->contidional_second_val; | ||
334 | } else if (numptr_m1->contidional_second_val_initialized) { | ||
335 | /* protect $((expr1 : expr2)) without "expr ? " */ | ||
336 | goto err; | 337 | goto err; |
338 | /* ...and they pop one */ | ||
339 | NUMPTR = top_of_stack; /* this decrements NUMPTR */ | ||
340 | |||
341 | bad_second_val = top_of_stack->second_val_present; | ||
342 | if (op == TOK_CONDITIONAL) { /* ? operation */ | ||
343 | /* Make next if (...) protect against | ||
344 | * $((expr1 ? expr2)) - that is, missing ": expr" */ | ||
345 | bad_second_val = !bad_second_val; | ||
346 | } | ||
347 | if (bad_second_val) { | ||
348 | /* Protect against $((expr <not_?_op> expr1 : expr2)) */ | ||
349 | return "malformed ?: operator"; | ||
337 | } | 350 | } |
338 | numptr_m1 = NUMPTR - 1; | 351 | |
352 | top_of_stack--; /* now points to left side */ | ||
353 | |||
339 | if (op != TOK_ASSIGN) { | 354 | if (op != TOK_ASSIGN) { |
340 | /* check operand is var with noninteger value for not '=' */ | 355 | /* Resolve left side value (unless the op is '=') */ |
341 | ret_arith_lookup_val = arith_lookup_val(numptr_m1, math_hooks); | 356 | err = arith_lookup_val(math_state, top_of_stack); |
342 | if (ret_arith_lookup_val) | 357 | if (err) |
343 | return ret_arith_lookup_val; | 358 | return err; |
344 | } | 359 | } |
345 | if (op == TOK_CONDITIONAL) { | 360 | |
346 | numptr_m1->contidional_second_val = rez; | 361 | right_side_val = rez; |
362 | rez = top_of_stack->val; | ||
363 | if (op == TOK_CONDITIONAL) /* ? operation */ | ||
364 | rez = (rez ? right_side_val : top_of_stack[1].second_val); | ||
365 | else if (op == TOK_CONDITIONAL_SEP) { /* : operation */ | ||
366 | if (top_of_stack == numstack) { | ||
367 | /* Protect against $((expr : expr)) */ | ||
368 | return "malformed ?: operator"; | ||
369 | } | ||
370 | top_of_stack->second_val_present = op; | ||
371 | top_of_stack->second_val = right_side_val; | ||
347 | } | 372 | } |
348 | rez = numptr_m1->val; | 373 | else if (op == TOK_BOR || op == TOK_OR_ASSIGN) |
349 | if (op == TOK_BOR || op == TOK_OR_ASSIGN) | 374 | rez |= right_side_val; |
350 | rez |= numptr_val; | ||
351 | else if (op == TOK_OR) | 375 | else if (op == TOK_OR) |
352 | rez = numptr_val || rez; | 376 | rez = right_side_val || rez; |
353 | else if (op == TOK_BAND || op == TOK_AND_ASSIGN) | 377 | else if (op == TOK_BAND || op == TOK_AND_ASSIGN) |
354 | rez &= numptr_val; | 378 | rez &= right_side_val; |
355 | else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN) | 379 | else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN) |
356 | rez ^= numptr_val; | 380 | rez ^= right_side_val; |
357 | else if (op == TOK_AND) | 381 | else if (op == TOK_AND) |
358 | rez = rez && numptr_val; | 382 | rez = rez && right_side_val; |
359 | else if (op == TOK_EQ) | 383 | else if (op == TOK_EQ) |
360 | rez = (rez == numptr_val); | 384 | rez = (rez == right_side_val); |
361 | else if (op == TOK_NE) | 385 | else if (op == TOK_NE) |
362 | rez = (rez != numptr_val); | 386 | rez = (rez != right_side_val); |
363 | else if (op == TOK_GE) | 387 | else if (op == TOK_GE) |
364 | rez = (rez >= numptr_val); | 388 | rez = (rez >= right_side_val); |
365 | else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN) | 389 | else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN) |
366 | rez >>= numptr_val; | 390 | rez >>= right_side_val; |
367 | else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN) | 391 | else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN) |
368 | rez <<= numptr_val; | 392 | rez <<= right_side_val; |
369 | else if (op == TOK_GT) | 393 | else if (op == TOK_GT) |
370 | rez = (rez > numptr_val); | 394 | rez = (rez > right_side_val); |
371 | else if (op == TOK_LT) | 395 | else if (op == TOK_LT) |
372 | rez = (rez < numptr_val); | 396 | rez = (rez < right_side_val); |
373 | else if (op == TOK_LE) | 397 | else if (op == TOK_LE) |
374 | rez = (rez <= numptr_val); | 398 | rez = (rez <= right_side_val); |
375 | else if (op == TOK_MUL || op == TOK_MUL_ASSIGN) | 399 | else if (op == TOK_MUL || op == TOK_MUL_ASSIGN) |
376 | rez *= numptr_val; | 400 | rez *= right_side_val; |
377 | else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN) | 401 | else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN) |
378 | rez += numptr_val; | 402 | rez += right_side_val; |
379 | else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN) | 403 | else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN) |
380 | rez -= numptr_val; | 404 | rez -= right_side_val; |
381 | else if (op == TOK_ASSIGN || op == TOK_COMMA) | 405 | else if (op == TOK_ASSIGN || op == TOK_COMMA) |
382 | rez = numptr_val; | 406 | rez = right_side_val; |
383 | else if (op == TOK_CONDITIONAL_SEP) { | 407 | else if (op == TOK_EXPONENT) { |
384 | if (numptr_m1 == numstack) { | 408 | arith_t c; |
385 | /* protect $((expr : expr)) without "expr ? " */ | 409 | if (right_side_val < 0) |
386 | goto err; | 410 | return "exponent less than 0"; |
387 | } | 411 | c = 1; |
388 | numptr_m1->contidional_second_val_initialized = op; | 412 | while (--right_side_val >= 0) |
389 | numptr_m1->contidional_second_val = numptr_val; | 413 | c *= rez; |
390 | } else if (op == TOK_CONDITIONAL) { | 414 | rez = c; |
391 | rez = rez ? | 415 | } |
392 | numptr_val : numptr_m1->contidional_second_val; | 416 | else if (right_side_val == 0) |
393 | } else if (op == TOK_EXPONENT) { | 417 | return "divide by zero"; |
394 | if (numptr_val < 0) | ||
395 | return -3; /* exponent less than 0 */ | ||
396 | else { | ||
397 | arith_t c = 1; | ||
398 | |||
399 | if (numptr_val) | ||
400 | while (numptr_val--) | ||
401 | c *= rez; | ||
402 | rez = c; | ||
403 | } | ||
404 | } else if (numptr_val==0) /* zero divisor check */ | ||
405 | return -2; | ||
406 | else if (op == TOK_DIV || op == TOK_DIV_ASSIGN) | 418 | else if (op == TOK_DIV || op == TOK_DIV_ASSIGN) |
407 | rez /= numptr_val; | 419 | rez /= right_side_val; |
408 | else if (op == TOK_REM || op == TOK_REM_ASSIGN) | 420 | else if (op == TOK_REM || op == TOK_REM_ASSIGN) |
409 | rez %= numptr_val; | 421 | rez %= right_side_val; |
410 | } | 422 | } |
411 | if (tok_have_assign(op)) { | 423 | |
424 | if (is_assign_op(op)) { | ||
412 | char buf[sizeof(arith_t)*3 + 2]; | 425 | char buf[sizeof(arith_t)*3 + 2]; |
413 | 426 | ||
414 | if (numptr_m1->var == NULL) { | 427 | if (top_of_stack->var == NULL) { |
415 | /* Hmm, 1=2 ? */ | 428 | /* Hmm, 1=2 ? */ |
429 | //TODO: actually, bash allows ++7 but for some reason it evals to 7, not 8 | ||
416 | goto err; | 430 | goto err; |
417 | } | 431 | } |
418 | /* save to shell variable */ | 432 | /* Save to shell variable */ |
419 | sprintf(buf, arith_t_fmt, rez); | 433 | sprintf(buf, ARITH_FMT, rez); |
420 | setvar(numptr_m1->var, buf); | 434 | setvar(top_of_stack->var, buf); |
421 | /* after saving, make previous value for v++ or v-- */ | 435 | /* After saving, make previous value for v++ or v-- */ |
422 | if (op == TOK_POST_INC) | 436 | if (op == TOK_POST_INC) |
423 | rez--; | 437 | rez--; |
424 | else if (op == TOK_POST_DEC) | 438 | else if (op == TOK_POST_DEC) |
425 | rez++; | 439 | rez++; |
426 | } | 440 | } |
427 | numptr_m1->val = rez; | 441 | |
428 | /* protect geting var value, is number now */ | 442 | top_of_stack->val = rez; |
429 | numptr_m1->var = NULL; | 443 | /* Erase var name, it is just a number now */ |
430 | return 0; | 444 | top_of_stack->var = NULL; |
445 | return NULL; | ||
431 | err: | 446 | err: |
432 | return -1; | 447 | return "arithmetic syntax error"; |
448 | #undef NUMPTR | ||
433 | } | 449 | } |
434 | 450 | ||
435 | /* longest must be first */ | 451 | /* longest must be first */ |
@@ -476,8 +492,7 @@ static const char op_tokens[] ALIGN1 = { | |||
476 | '(', 0, TOK_LPAREN, | 492 | '(', 0, TOK_LPAREN, |
477 | 0 | 493 | 0 |
478 | }; | 494 | }; |
479 | /* ptr to ")" */ | 495 | #define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7]) |
480 | #define endexpression (&op_tokens[sizeof(op_tokens)-7]) | ||
481 | 496 | ||
482 | const char* FAST_FUNC | 497 | const char* FAST_FUNC |
483 | endofname(const char *name) | 498 | endofname(const char *name) |
@@ -491,35 +506,40 @@ endofname(const char *name) | |||
491 | return name; | 506 | return name; |
492 | } | 507 | } |
493 | 508 | ||
494 | arith_t | 509 | static arith_t FAST_FUNC |
495 | arith(const char *expr, int *perrcode, a_e_h_t *math_hooks) | 510 | evaluate_string(arith_state_t *math_state, const char *expr) |
496 | { | 511 | { |
497 | char arithval; /* Current character under analysis */ | 512 | operator lasttok; |
498 | operator lasttok, op; | 513 | const char *errmsg; |
499 | operator prec; | 514 | const char *start_expr = expr = skip_whitespace(expr); |
500 | operator *stack, *stackptr; | 515 | unsigned expr_len = strlen(expr) + 2; |
501 | const char *p = endexpression; | ||
502 | int errcode; | ||
503 | v_n_t *numstack, *numstackptr; | ||
504 | unsigned datasizes = strlen(expr) + 2; | ||
505 | |||
506 | /* Stack of integers */ | 516 | /* Stack of integers */ |
507 | /* The proof that there can be no more than strlen(startbuf)/2+1 integers | 517 | /* The proof that there can be no more than strlen(startbuf)/2+1 |
508 | * in any given correct or incorrect expression is left as an exercise to | 518 | * integers in any given correct or incorrect expression |
509 | * the reader. */ | 519 | * is left as an exercise to the reader. */ |
510 | numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0])); | 520 | var_or_num_t *const numstack = alloca((expr_len / 2) * sizeof(numstack[0])); |
521 | var_or_num_t *numstackptr = numstack; | ||
511 | /* Stack of operator tokens */ | 522 | /* Stack of operator tokens */ |
512 | stackptr = stack = alloca(datasizes * sizeof(stack[0])); | 523 | operator *const stack = alloca(expr_len * sizeof(stack[0])); |
524 | operator *stackptr = stack; | ||
513 | 525 | ||
514 | *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */ | 526 | /* Start with a left paren */ |
515 | *perrcode = errcode = 0; | 527 | *stackptr++ = lasttok = TOK_LPAREN; |
528 | errmsg = NULL; | ||
516 | 529 | ||
517 | while (1) { | 530 | while (1) { |
531 | const char *p; | ||
532 | operator op; | ||
533 | operator prec; | ||
534 | char arithval; | ||
535 | |||
536 | expr = skip_whitespace(expr); | ||
518 | arithval = *expr; | 537 | arithval = *expr; |
519 | if (arithval == 0) { | 538 | if (arithval == '\0') { |
520 | if (p == endexpression) { | 539 | if (expr == start_expr) { |
521 | /* Null expression. */ | 540 | /* Null expression */ |
522 | return 0; | 541 | numstack->val = 0; |
542 | goto ret; | ||
523 | } | 543 | } |
524 | 544 | ||
525 | /* This is only reached after all tokens have been extracted from the | 545 | /* This is only reached after all tokens have been extracted from the |
@@ -527,77 +547,80 @@ arith(const char *expr, int *perrcode, a_e_h_t *math_hooks) | |||
527 | * are to be applied in order. At the end, there should be a final | 547 | * are to be applied in order. At the end, there should be a final |
528 | * result on the integer stack */ | 548 | * result on the integer stack */ |
529 | 549 | ||
530 | if (expr != endexpression + 1) { | 550 | if (expr != ptr_to_rparen + 1) { |
531 | /* If we haven't done so already, */ | 551 | /* If we haven't done so already, |
532 | /* append a closing right paren */ | 552 | * append a closing right paren |
533 | expr = endexpression; | 553 | * and let the loop process it */ |
534 | /* and let the loop process it. */ | 554 | expr = ptr_to_rparen; |
535 | continue; | 555 | continue; |
536 | } | 556 | } |
537 | /* At this point, we're done with the expression. */ | 557 | /* At this point, we're done with the expression */ |
538 | if (numstackptr != numstack+1) { | 558 | if (numstackptr != numstack + 1) { |
539 | /* ... but if there isn't, it's bad */ | 559 | /* ...but if there isn't, it's bad */ |
540 | err: | 560 | goto err; |
541 | *perrcode = -1; | ||
542 | return *perrcode; | ||
543 | } | 561 | } |
544 | if (numstack->var) { | 562 | if (numstack->var) { |
545 | /* expression is $((var)) only, lookup now */ | 563 | /* expression is $((var)) only, lookup now */ |
546 | errcode = arith_lookup_val(numstack, math_hooks); | 564 | errmsg = arith_lookup_val(math_state, numstack); |
547 | } | 565 | } |
548 | ret: | 566 | goto ret; |
549 | *perrcode = errcode; | ||
550 | return numstack->val; | ||
551 | } | 567 | } |
552 | 568 | ||
553 | /* Continue processing the expression. */ | ||
554 | if (arith_isspace(arithval)) { | ||
555 | /* Skip whitespace */ | ||
556 | goto prologue; | ||
557 | } | ||
558 | p = endofname(expr); | 569 | p = endofname(expr); |
559 | if (p != expr) { | 570 | if (p != expr) { |
560 | size_t var_name_size = (p-expr) + 1; /* trailing zero */ | 571 | /* Name */ |
561 | 572 | size_t var_name_size = (p-expr) + 1; /* +1 for NUL */ | |
562 | numstackptr->var = alloca(var_name_size); | 573 | numstackptr->var = alloca(var_name_size); |
563 | safe_strncpy(numstackptr->var, expr, var_name_size); | 574 | safe_strncpy(numstackptr->var, expr, var_name_size); |
564 | expr = p; | 575 | expr = p; |
565 | num: | 576 | num: |
566 | numstackptr->contidional_second_val_initialized = 0; | 577 | numstackptr->second_val_present = 0; |
567 | numstackptr++; | 578 | numstackptr++; |
568 | lasttok = TOK_NUM; | 579 | lasttok = TOK_NUM; |
569 | continue; | 580 | continue; |
570 | } | 581 | } |
582 | |||
571 | if (isdigit(arithval)) { | 583 | if (isdigit(arithval)) { |
584 | /* Number */ | ||
572 | numstackptr->var = NULL; | 585 | numstackptr->var = NULL; |
573 | errno = 0; | 586 | errno = 0; |
574 | /* call strtoul[l]: */ | 587 | numstackptr->val = strto_arith_t(expr, (char**) &expr, 0); |
575 | numstackptr->val = strto_arith_t(expr, (char **) &expr, 0); | ||
576 | if (errno) | 588 | if (errno) |
577 | numstackptr->val = 0; /* bash compat */ | 589 | numstackptr->val = 0; /* bash compat */ |
578 | goto num; | 590 | goto num; |
579 | } | 591 | } |
580 | for (p = op_tokens; ; p++) { | ||
581 | const char *o; | ||
582 | 592 | ||
583 | if (*p == 0) { | 593 | /* Should be an operator */ |
584 | /* strange operator not found */ | 594 | p = op_tokens; |
585 | goto err; | 595 | while (1) { |
586 | } | 596 | // TODO: bash allows 7+++v, treats it as 7 + ++v |
587 | for (o = expr; *p && *o == *p; p++) | 597 | // we treat it as 7++ + v and reject |
588 | o++; | 598 | /* Compare expr to current op_tokens[] element */ |
589 | if (!*p) { | 599 | const char *e = expr; |
590 | /* found */ | 600 | while (1) { |
591 | expr = o - 1; | 601 | if (*p == '\0') { |
592 | break; | 602 | /* Match: operator is found */ |
603 | expr = e; | ||
604 | goto tok_found; | ||
605 | } | ||
606 | if (*p != *e) | ||
607 | break; | ||
608 | p++; | ||
609 | e++; | ||
593 | } | 610 | } |
594 | /* skip tail uncompared token */ | 611 | /* No match, go to next element of op_tokens[] */ |
595 | while (*p) | 612 | while (*p) |
596 | p++; | 613 | p++; |
597 | /* skip zero delim */ | 614 | p += 2; /* skip NUL and TOK_foo bytes */ |
598 | p++; | 615 | if (*p == '\0') { |
616 | /* No next element, operator not found */ | ||
617 | //math_state->syntax_error_at = expr; | ||
618 | goto err; | ||
619 | } | ||
599 | } | 620 | } |
600 | op = p[1]; | 621 | tok_found: |
622 | op = p[1]; /* fetch TOK_foo value */ | ||
623 | /* NB: expr now points past the operator */ | ||
601 | 624 | ||
602 | /* post grammar: a++ reduce to num */ | 625 | /* post grammar: a++ reduce to num */ |
603 | if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC) | 626 | if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC) |
@@ -626,13 +649,13 @@ arith(const char *expr, int *perrcode, a_e_h_t *math_hooks) | |||
626 | /* We don't want an unary operator to cause recursive descent on the | 649 | /* We don't want an unary operator to cause recursive descent on the |
627 | * stack, because there can be many in a row and it could cause an | 650 | * stack, because there can be many in a row and it could cause an |
628 | * operator to be evaluated before its argument is pushed onto the | 651 | * operator to be evaluated before its argument is pushed onto the |
629 | * integer stack. */ | 652 | * integer stack. |
630 | /* But for binary operators, "apply" everything on the operator | 653 | * But for binary operators, "apply" everything on the operator |
631 | * stack until we find an operator with a lesser priority than the | 654 | * stack until we find an operator with a lesser priority than the |
632 | * one we have just extracted. */ | 655 | * one we have just extracted. If op is right-associative, |
633 | /* Left paren is given the lowest priority so it will never be | 656 | * then stop "applying" on the equal priority too. |
657 | * Left paren is given the lowest priority so it will never be | ||
634 | * "applied" in this way. | 658 | * "applied" in this way. |
635 | * if associativity is right and priority eq, applied also skip | ||
636 | */ | 659 | */ |
637 | prec = PREC(op); | 660 | prec = PREC(op); |
638 | if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) { | 661 | if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) { |
@@ -642,41 +665,56 @@ arith(const char *expr, int *perrcode, a_e_h_t *math_hooks) | |||
642 | goto err; | 665 | goto err; |
643 | } | 666 | } |
644 | while (stackptr != stack) { | 667 | while (stackptr != stack) { |
668 | operator prev_op = *--stackptr; | ||
645 | if (op == TOK_RPAREN) { | 669 | if (op == TOK_RPAREN) { |
646 | /* The algorithm employed here is simple: while we don't | 670 | /* The algorithm employed here is simple: while we don't |
647 | * hit an open paren nor the bottom of the stack, pop | 671 | * hit an open paren nor the bottom of the stack, pop |
648 | * tokens and apply them */ | 672 | * tokens and apply them */ |
649 | if (stackptr[-1] == TOK_LPAREN) { | 673 | if (prev_op == TOK_LPAREN) { |
650 | --stackptr; | 674 | /* Any operator directly after a |
651 | /* Any operator directly after a */ | 675 | * close paren should consider itself binary */ |
652 | lasttok = TOK_NUM; | 676 | lasttok = TOK_NUM; |
653 | /* close paren should consider itself binary */ | 677 | goto next; |
654 | goto prologue; | ||
655 | } | 678 | } |
656 | } else { | 679 | } else { |
657 | operator prev_prec = PREC(stackptr[-1]); | 680 | operator prev_prec = PREC(prev_op); |
658 | 681 | fix_assignment_prec(prec); | |
659 | convert_prec_is_assing(prec); | 682 | fix_assignment_prec(prev_prec); |
660 | convert_prec_is_assing(prev_prec); | 683 | if (prev_prec < prec |
661 | if (prev_prec < prec) | 684 | || (prev_prec == prec && is_right_associative(prec)) |
662 | break; | 685 | ) { |
663 | /* check right assoc */ | 686 | stackptr++; |
664 | if (prev_prec == prec && is_right_associativity(prec)) | ||
665 | break; | 687 | break; |
688 | } | ||
666 | } | 689 | } |
667 | errcode = arith_apply(*--stackptr, numstack, &numstackptr, math_hooks); | 690 | errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr); |
668 | if (errcode) goto ret; | 691 | if (errmsg) |
692 | goto err_with_custom_msg; | ||
669 | } | 693 | } |
670 | if (op == TOK_RPAREN) { | 694 | if (op == TOK_RPAREN) |
671 | goto err; | 695 | goto err; |
672 | } | ||
673 | } | 696 | } |
674 | 697 | ||
675 | /* Push this operator to the stack and remember it. */ | 698 | /* Push this operator to the stack and remember it */ |
676 | *stackptr++ = lasttok = op; | 699 | *stackptr++ = lasttok = op; |
677 | prologue: | 700 | next: ; |
678 | ++expr; | 701 | } /* while (1) */ |
679 | } /* while */ | 702 | |
703 | err: | ||
704 | errmsg = "arithmetic syntax error"; | ||
705 | err_with_custom_msg: | ||
706 | numstack->val = -1; | ||
707 | ret: | ||
708 | math_state->errmsg = errmsg; | ||
709 | return numstack->val; | ||
710 | } | ||
711 | |||
712 | arith_t FAST_FUNC | ||
713 | arith(arith_state_t *math_state, const char *expr) | ||
714 | { | ||
715 | math_state->errmsg = NULL; | ||
716 | math_state->list_of_recursed_names = NULL; | ||
717 | return evaluate_string(math_state, expr); | ||
680 | } | 718 | } |
681 | 719 | ||
682 | /* | 720 | /* |