1 /* Recursive descent arithmetic calculator:
22 #ifndef __ARITH_TEST__
29 #define TOK_PLUS (MIN_TOK + 5)
30 #define TOK_MINUS (MIN_TOK + 6)
31 #define TOK_NUMBER 256
32 #define TOK_STRING 257
38 #ifndef __ARITH_TEST__
39 #define EPARSE EUNIQ_01
40 #define EDIV0 EUNIQ_02
41 #define ENOOP EUNIQ_03
42 #define EWRONGOP EUNIQ_04
50 char *inp, *prev, *orig;
58 /* Here is a table of the operators */
59 const char op_table[NUM_OPS * 3 + 1] = { '!', SEP2 , '~', SEP2, '*', SEP2, '/', SEP2, '%', SEP2, '+', SEP2, '-', SEP2,
60 '<', SEP2, '<', '=', SEP1, '<', '<', SEP1, '>', SEP2, '>', '=', SEP1, '>', '>', SEP1, '&', SEP2,
61 '|', SEP2, '^', SEP2, '&', '&', SEP1, '|', '|', SEP1, '!', '=', SEP1, '=', '=', SEP1, '\0'
64 const char *keyword_table = " \t\v()!~*/%+-<=>&|^"; /* Characters that cannot appear in a string */
65 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 };
67 static void ignore_whitespace ( void );
68 static int parse_expr ( char **buffer );
70 static void input ( void ) {
71 char t_op[3] = { '\0', '\0', '\0'};
86 /* Check for a number */
87 if ( isdigit ( *inp ) ) {
89 tok_value.num_value = strtoul ( inp, &inp, 0 );
93 /* Check for a string */
94 len = strcspn ( inp, keyword_table );
100 if ( ( tok_value.str_value = malloc ( len + 1 ) ) == NULL ) {
104 strncpy ( tok_value.str_value, inp - len, len );
105 tok_value.str_value[len] = 0;
109 /* Check for an operator */
111 p1 = strstr ( op_table, t_op );
112 if ( !p1 ) { /* The character is not present in the list of operators */
118 p2 = strstr ( op_table, t_op );
119 if ( !p2 || p1 == p2 ) {
120 if ( ( p1 - op_table ) % 3 ) { /* Without this, it would take '=' as '<=' */
125 tok = MIN_TOK + ( p1 - op_table ) / 3;
129 tok = MIN_TOK + ( p2 - op_table ) / 3;
132 /* 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 */
133 static int isnum ( char *string, long *num ) {
137 if ( *string == '+' ) {
139 } else if ( *string == '-' ) {
143 *num = strtoul ( string, &string, 0 );
144 if ( *string != 0 ) {
153 static void ignore_whitespace ( void ) {
154 while ( isspace ( *inp ) ) {
159 static int accept ( int ch ) {
167 static void skip ( int ch ) {
168 if ( !accept ( ch ) ) {
173 static int parse_num ( char **buffer ) {
177 if ( tok == TOK_MINUS || tok == TOK_PLUS || tok == '(' || tok == TOK_NUMBER ) {
179 if ( accept ( TOK_MINUS ) ) //Handle -NUM and +NUM
181 else if ( accept ( TOK_PLUS ) ) {
184 if ( accept ( '(' ) ) {
185 parse_expr ( buffer );
196 t = isnum ( *buffer, &num );
198 if ( t == 0 ) { /* Trying to do a -string, which should not be permitted */
199 return ( err_val = -EWRONGOP );
201 return ( ( asprintf ( buffer, "%ld", -num ) < 0 ) ? (err_val = -ENOMEM ) : 0 );
206 if ( tok == TOK_NUMBER ) {
208 num = -tok_value.num_value;
210 num = tok_value.num_value;
212 return ( ( asprintf ( buffer, "%ld", num ) < 0) ? (err_val = -ENOMEM ) : 0 );
214 return ( err_val = -EPARSE );
216 if ( tok == TOK_STRING ) {
217 *buffer = tok_value.str_value;
221 return ( err_val = -EPARSE );
224 static int eval(int op, char *op1, char *op2, char **buffer) {
230 if ( ! isnum ( op1, &lhs ) )
234 if ( ! isnum (op2, &rhs ) )
237 if ( op <= 17 && ! bothints ) {
238 return ( err_val = -EWRONGOP );
256 return ( err_val = -EDIV0 );
302 value = strcmp ( op1, op2 ) != 0;
305 value = strcmp ( op1, op2 ) == 0;
307 default: /* This means the operator is in the op_table, but not defined in this switch statement */
308 return ( err_val = -ENOOP );
310 return ( ( asprintf(buffer, "%ld", value) < 0) ? ( err_val = -ENOMEM ) : 0 );
313 static int parse_prio(int prio, char **buffer) {
317 if ( tok < MIN_TOK || tok == TOK_MINUS || tok == TOK_PLUS ) {
320 if ( tok < MIN_TOK + 2 ) {
323 return ( err_val = -EPARSE );
330 while( tok != -1 && tok != ')' ) {
332 if ( tok < MIN_TOK ) {
335 return ( err_val = -EPARSE );
337 if ( op_prio[tok - MIN_TOK] <= prio - ( tok - MIN_TOK <= 1 ) ? 1 : 0 ) {
343 parse_prio ( op_prio[op - MIN_TOK], &rc );
351 lhs = eval ( op - MIN_TOK, lc, rc, buffer );
364 static int parse_expr ( char **buffer ) {
365 return parse_prio ( -1, buffer );
368 int parse_arith ( char *inp_string, char **end, char **buffer ) {
370 orig = inp = inp_string;
375 parse_expr ( buffer );
385 if ( err_val ) { //Read till we get a ')'
387 if ( tok == TOK_STRING )
388 free ( tok_value.str_value );
391 printf ( "parse error:\n%s\n", orig );
394 printf ( "division by 0\n" );
397 printf ( "operator undefined\n" );
400 printf ( "wrong type of operand\n" );
403 printf("out of memory\n");
406 return - ( EINVAL | -err_val );
412 #ifdef __ARITH_TEST__
413 int main ( int argc, char *argv[] ) {
416 char *head, *tail, *string, *t;
419 while ( !feof ( stdin ) ) {
420 fgets ( line, 100, stdin );
421 if ( line[strlen ( line ) - 1] == '\n' )
422 line[strlen ( line ) - 1] = 0;
423 asprintf ( &string, "%s", line );
424 while ( ( head = strstr ( string, "$(" ) ) != NULL ) {
426 r = parse_arith ( head, &tail, &ret_val );
429 asprintf ( &string, "%s%s%s", string, ret_val, tail );
436 printf ( "Line: %s\n", string );