[scripting] Modified if-else-fi branches
[people/lynusvaz/gpxe.git] / src / hci / arith.c
1 /* Recursive descent arithmetic calculator:
2  * Ops: !, ~                            (Highest)
3  *      *, /, %
4  *      +, -
5  *      <<, >>
6  *      <, <=, >, >=
7  *      !=, ==
8  *      &
9  *      |
10  *      ^
11  *      &&
12  *      ||                              (Lowest)
13 */
14
15 #include <ctype.h>
16 #include <stdint.h>
17 #include <string.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <errno.h>
21
22 #include <gpxe/parse.h>
23
24 #define NUM_OPS         20
25 #define MAX_PRIO                11
26 #define MIN_TOK         258
27 #define TOK_PLUS                (MIN_TOK + 5)
28 #define TOK_MINUS       (MIN_TOK + 6)
29 #define TOK_NUMBER      256
30 #define TOK_STRING      257
31 #define TOK_TOTAL               20
32
33 #define SEP1                    -1
34 #define SEP2                    -1, -1
35
36 #define EPARSE          (EINVAL | EUNIQ_01)
37 #define EDIV0                   (EINVAL | EUNIQ_02)
38 #define ENOOP           (EINVAL | EUNIQ_03)
39 #define EWRONGOP        (EINVAL | EUNIQ_04)
40
41 static struct string *input_str;
42 static char *inp_ptr = NULL;
43 static int tok;
44 static int err_val;
45 static union {
46         long num_value;
47         char *str_value;
48 }tok_value;
49
50 /* Here is a table of the operators */
51 static const char op_table[NUM_OPS * 3 + 1] = { '!', SEP2 ,  '~', SEP2, '*', SEP2, '/', SEP2, '%', SEP2, '+', SEP2, '-', SEP2,
52                                                 '<', SEP2, '<', '=', SEP1, '<', '<', SEP1, '>', SEP2, '>', '=', SEP1, '>', '>', SEP1,  '&', SEP2,
53                                                 '|', SEP2, '^', SEP2, '&', '&', SEP1, '|', '|', SEP1, '!', '=', SEP1, '=', '=', SEP1, '\0'
54 };
55
56 static signed const char op_prio[NUM_OPS]       = { 10, 10, 9, 9, 9, 8, 8, 6, 6, 7, 6, 6, 7, 4, 3, 2, 1, 0, 5, 5 };
57
58 static void ignore_whitespace ( void );
59 static int parse_expr ( char **buffer );
60
61 static void input ( void ) {
62         char t_op[3] = { '\0', '\0', '\0'};
63         char *p1, *p2;
64         char *tmp, *strtmp = NULL;
65         char *end;
66         int success;
67         
68         if ( tok == -1 )
69                 return;
70
71         ignore_whitespace();
72         
73         if ( *inp_ptr == '\0' ) {
74                 tok = -1;
75                 return;
76         }
77         tok = 0;
78         
79         tmp = expand_string ( input_str, &inp_ptr, &end, arith_table, 21, 0, &success );
80         if ( !tmp ) {
81                 tok = -1;
82                 err_val = -ENOMEM;
83                 return;
84         }
85         
86         if ( success ) {
87                 strtmp = strndup ( inp_ptr, end - inp_ptr );
88                 if ( isnum ( strtmp, &tok_value.num_value ) ) {
89                         free ( strtmp );
90                         tok = TOK_NUMBER;
91                 } else {
92                         tok_value.str_value = strtmp;
93                         tok = TOK_STRING;
94                 }
95                 inp_ptr = end;
96                 return;
97         }
98         
99         /* Check for an operator */
100         t_op[0] = *inp_ptr++;
101         p1 = strstr ( op_table, t_op );
102         if ( !p1 ) {                    /* The character is not present in the list of operators */
103                 tok = *t_op;
104                 return;
105         }
106         
107         t_op[1] = *inp_ptr;
108         p2 = strstr ( op_table, t_op );
109         if ( !p2 || p1 == p2 ) {
110                 if ( ( p1 - op_table ) % 3 ) {                  /* Without this, it would take '=' as '<=' */
111                         tok = *t_op;
112                         return;
113                 }
114                 
115                 tok = MIN_TOK + ( p1 - op_table ) / 3;
116                 return;
117         }
118         inp_ptr++;
119         tok = MIN_TOK + ( p2 - op_table ) / 3;
120
121 }
122
123 /* Check if a string is a number: "-1" and "+42" is accepted, but not "-1a". If so, place it in num and return 1 else num = 0 and return 0 */
124 int isnum ( char *string, long *num ) {
125         int flag = 0;
126         
127         *num = 0;
128         if ( *string == '+' ) {
129                 string++;
130         } else if ( *string == '-' ) {
131                 flag = 1;
132                 string++;
133         }
134         *num = strtoul ( string, &string, 0 );
135         if ( *string != 0 ) {
136                 *num = 0;
137                 return 0;
138         }
139         if ( flag )
140                 *num = -*num;
141         return 1;
142 }
143
144 static void ignore_whitespace ( void ) {
145         while ( isspace ( *inp_ptr ) ) {
146                 inp_ptr++;
147         }
148 }
149
150 static int accept ( int ch ) {
151         if ( tok == ch ) {
152                 input();
153                 return 1;
154         }
155         return 0;
156 }
157
158 static void skip ( int ch ) {
159         if ( !accept ( ch ) ) {
160                 err_val = -EPARSE;
161         }
162 }
163
164 static int parse_num ( char **buffer ) {
165         long num = 0;
166         int flag = 0;
167
168         if ( tok == TOK_MINUS || tok == TOK_PLUS || tok == '(' || tok == TOK_NUMBER ) {
169         
170                 if ( accept ( TOK_MINUS ) )                             //Handle -NUM and +NUM
171                         flag = 1;
172                 else if ( accept ( TOK_PLUS ) ) {
173                 }
174                         
175                 if ( accept ( '(' ) ) {
176                         parse_expr ( buffer );
177                         if ( err_val )  {
178                                 return ( err_val );
179                         }
180                         skip ( ')' );
181                         if ( err_val )  {
182                                 free ( *buffer );
183                                 return (err_val );
184                         }
185                         if ( flag ) {
186                                 int t;
187                                 t = isnum ( *buffer, &num );
188                                 free ( *buffer );
189                                 if ( t == 0 ) {         /* Trying to do a -string, which should not be permitted */
190                                         return ( err_val = -EWRONGOP );
191                                 }
192                                 return ( ( asprintf ( buffer, "%ld", -num )  < 0 ) ? (err_val = -ENOMEM ) : 0 );
193                         }
194                         return 0;
195                 }
196                 
197                 if ( tok == TOK_NUMBER ) {
198                         if ( flag )
199                                 num = -tok_value.num_value;
200                         else
201                                 num = tok_value.num_value;
202                         input();
203                         return ( ( asprintf ( buffer, "%ld", num ) < 0) ? (err_val = -ENOMEM ) : 0 );
204                 }
205                 return ( err_val = -EPARSE );
206         }
207         if ( tok == TOK_STRING ) {
208                 *buffer = tok_value.str_value;
209                 input();
210                 return 0;
211         }
212         return ( err_val = -EPARSE );
213 }
214
215 static int eval(int op, char *op1, char *op2, char **buffer) {
216         long value;
217         int bothints = 1;
218         long lhs, rhs;
219         
220         if ( op1 ) {
221                 if ( ! isnum ( op1, &lhs ) ) 
222                         bothints = 0;
223         }
224         
225         if ( ! isnum (op2, &rhs ) )
226                 bothints = 0;
227         
228         if ( op <= 17 && ! bothints ) {
229                 return ( err_val = -EWRONGOP );
230         }
231         
232         switch(op)
233         {
234                 case 0:
235                         value = !rhs;
236                         break;
237                 case 1: 
238                         value = ~rhs;
239                         break;
240                 case 2: 
241                         value = lhs * rhs;
242                         break;
243                 case 3: 
244                         if(rhs != 0)
245                                 value = lhs / rhs;
246                         else {
247                                 return ( err_val = -EDIV0 );
248                         }
249                         break;
250                 case 4: 
251                         value = lhs % rhs;
252                         break;
253                 case 5:
254                         value = lhs + rhs;
255                         break;
256                 case 6: 
257                         value = lhs - rhs;
258                         break;
259                 case 7: 
260                         value = lhs < rhs;
261                         break;
262                 case 8: 
263                         value = lhs <= rhs;
264                         break;
265                 case 9: 
266                         value = lhs << rhs;
267                         break;
268                 case 10: 
269                         value = lhs > rhs;
270                         break;
271                 case 11: 
272                         value = lhs >= rhs;
273                         break;
274                 case 12: 
275                         value = lhs >> rhs;
276                         break;
277                 case 13:
278                         value = lhs & rhs;
279                         break;
280                 case 14: 
281                         value = lhs | rhs;
282                         break;
283                 case 15:
284                         value = lhs ^ rhs;
285                         break;
286                 case 16: 
287                         value = lhs && rhs;
288                         break;
289                 case 17: 
290                         value = lhs || rhs;
291                         break;
292                 case 18:
293                         value = strcmp ( op1, op2 ) != 0;
294                         break;
295                 case 19:
296                         value = strcmp ( op1, op2 ) == 0;
297                         break;
298                 default:                /* This means the operator is in the op_table, but not defined in this switch statement */
299                         return ( err_val = -ENOOP );
300         }
301         return ( ( asprintf(buffer, "%ld", value)  < 0) ? ( err_val = -ENOMEM ) : 0 );
302 }
303
304 static int parse_prio(int prio, char **buffer) {
305         int op;
306         char *lc, *rc;
307                 
308         if ( tok < MIN_TOK || tok == TOK_MINUS || tok == TOK_PLUS ) {
309                 parse_num ( &lc );
310         } else {
311                 if ( tok < MIN_TOK + 2 ) {
312                         lc = NULL;
313                 } else {
314                         return ( err_val = -EPARSE );
315                 }
316         }
317         
318         if ( err_val ) {
319                 return err_val;
320         }
321         while( tok != -1 && tok != ')' ) {
322                 long lhs;
323                 if ( tok < MIN_TOK ) {
324                         if ( lc )
325                                 free(lc);
326                         return ( err_val = -EPARSE );
327                 }
328                 if ( op_prio[tok - MIN_TOK] <= prio - ( tok - MIN_TOK <= 1 ) ? 1 : 0 ) {
329                         break;
330                 }
331                 
332                 op  = tok;
333                 input();
334                 parse_prio ( op_prio[op - MIN_TOK], &rc );
335                 
336                 if ( err_val )  {
337                         if ( lc )
338                                 free ( lc );
339                         return err_val;
340                 }
341                 
342                 lhs = eval ( op - MIN_TOK, lc, rc, buffer );
343                 free ( rc );
344                 if ( lc )
345                         free ( lc );
346                 if ( err_val ) {
347                         return err_val;
348                 }
349                 lc = *buffer;
350         }  
351         *buffer = lc;
352         return 0;
353 }
354
355 static int parse_expr ( char **buffer ) {
356         return parse_prio ( -1, buffer );
357 }
358
359 int parse_arith ( struct string *inp, char *orig, char **end ) {
360         char *buffer = NULL;
361         int start;
362         
363         start = orig - inp->value;
364         
365         input_str = inp;
366         err_val = tok = 0;
367         inp_ptr = orig + 1;
368         
369         input();
370         
371         skip ( '(' );
372         parse_expr ( &buffer );
373         if ( !err_val ) {
374                 
375                 if ( tok != ')' ) {
376                         free ( buffer );
377                         err_val = -EPARSE;
378                 } else {
379                         orig = inp->value + start;
380                         *orig = 0;
381                         *end = inp_ptr;
382                         orig = string3cat ( inp, buffer, *end );
383                         *end = orig + start + strlen ( buffer );
384                 }
385         }
386                 
387         if ( err_val )  {               
388                 if ( tok == TOK_STRING )
389                         free ( tok_value.str_value );
390                 switch ( err_val ) {
391                         case -EPARSE:
392                                 printf ( "parse error\n" );
393                                 break;
394                         case -EDIV0:
395                                 printf ( "division by 0\n" );
396                                 break;
397                         case -ENOOP:
398                                 printf ( "operator undefined\n" );
399                                 break;
400                         case -EWRONGOP:
401                                 printf ( "wrong type of operand\n" );
402                                 break;
403                         case -ENOMEM:
404                                 printf("out of memory\n");
405                                 break;
406                 }
407                 free_string ( inp );
408                 return err_val;
409         }
410         
411         return 0;
412 }
413