diff --git a/chapter_08/lispy.c b/chapter_08/lispy.c index a8584ed..548f07b 100644 --- a/chapter_08/lispy.c +++ b/chapter_08/lispy.c @@ -34,17 +34,20 @@ void add_history(char* unused) {} /* Declare new lval struct */ typedef struct { int type; - long num; + union { + long num; + double dec; + }; int err; } lval; /* Create enumeration of possible lval types */ -enum {LVAL_NUM, LVAL_ERR}; +enum lval_types {LVAL_NUM, LVAL_DEC, LVAL_ERR}; /* Create enumeration of possible error types */ -enum {LERR_DIV_ZERO, LERR_BAD_OP, LERR_BAD_NUM}; +enum lval_errors {LERR_DIV_ZERO, LERR_BAD_OP, LERR_BAD_NUM}; /* Create a new number of type lval */ @@ -56,6 +59,15 @@ lval lval_num(long x) { } +/* Create a new decimal of type lval */ +lval lval_dec(double x) { + lval v; + v.type = LVAL_DEC; + v.dec = x; + return v; +} + + /* Create a new error type lval */ lval lval_err(int x) { lval v; @@ -73,6 +85,11 @@ void lval_print(lval v) { case LVAL_NUM: printf("%li", v.num); break; + /* In the case the type is a decimal print it + Then break out of the switch */ + case LVAL_DEC: + printf("%.2f", v.dec); + break; /* In the case the type is an error */ case LVAL_ERR: /* Check what type of error it is & print it */ @@ -127,19 +144,37 @@ lval eval_op(lval x, char* op, lval y) { } /* Otherwise do maths on the number values */ - - if (strcmp(op, "+") == 0) {return lval_num(x.num + y.num);} - if (strcmp(op, "-") == 0) {return lval_num(x.num - y.num);} - if (strcmp(op, "*") == 0) {return lval_num(x.num * y.num);} - if (strcmp(op, "/") == 0) { - /* If second operand is zero return error */ - return y.num == 0 ? lval_err(LERR_DIV_ZERO) - : lval_num(x.num / y.num); + if (x.type == LVAL_NUM && y.type == LVAL_NUM) { + if (strcmp(op, "+") == 0) {return lval_num(x.num + y.num);} + if (strcmp(op, "-") == 0) {return lval_num(x.num - y.num);} + if (strcmp(op, "*") == 0) {return lval_num(x.num * y.num);} + if (strcmp(op, "/") == 0) { + /* If second operand is zero return error */ + return y.num == 0 ? lval_err(LERR_DIV_ZERO) + : lval_num(x.num / y.num); + } + if (strcmp(op, "%") == 0) {return lval_num(x.num % y.num);} + if (strcmp(op, "^") == 0) {return lval_num(pow(x.num, y.num));} + if (strcmp(op, "min") == 0) {return lval_num(min(x.num, y.num));} + if (strcmp(op, "max") == 0) {return lval_num(max(x.num, y.num));} + } else { + /* Cast integer number into double if necessary */ + double a = x.type == LVAL_NUM ? (double) x.num : x.dec; + double b = y.type == LVAL_NUM ? (double) y.num : y.dec; + /* Perform all operations on double */ + if (strcmp(op, "+") == 0) {return lval_dec(a + b);} + if (strcmp(op, "-") == 0) {return lval_dec(a - b);} + if (strcmp(op, "*") == 0) {return lval_dec(a * b);} + if (strcmp(op, "/") == 0) { + /* If second operand is zero return error */ + return b == 0 ? lval_err(LERR_DIV_ZERO) + : lval_dec(a / b); + } + if (strcmp(op, "%") == 0) {return lval_dec(fmod(a, b));} + if (strcmp(op, "^") == 0) {return lval_dec(pow(a, b));} + if (strcmp(op, "min") == 0) {return lval_dec(fmin(a, b));} + if (strcmp(op, "max") == 0) {return lval_dec(fmax(a, b));} } - if (strcmp(op, "%") == 0) {return lval_num(x.num % y.num);} - if (strcmp(op, "^") == 0) {return lval_num(pow(x.num, y.num));} - if (strcmp(op, "min") == 0) {return lval_num(min(x.num, y.num));} - if (strcmp(op, "max") == 0) {return lval_num(max(x.num, y.num));} return lval_err(LERR_BAD_OP); } @@ -150,8 +185,14 @@ lval eval(mpc_ast_t* t) { if (strstr(t->tag, "number")) { /* Check if there is some error in conversion */ errno = 0; - long x = strtol(t->contents, NULL, 10); - return errno != ERANGE ? lval_num(x) : lval_err(LERR_BAD_NUM); + /* Check for decimal or integer number */ + if (strstr(t->contents, ".")) { + double x = strtof(t->contents, NULL); + return errno != ERANGE ? lval_dec(x) : lval_err(LERR_BAD_NUM); + } else { + long x = strtol(t->contents, NULL, 10); + return errno != ERANGE ? lval_num(x) : lval_err(LERR_BAD_NUM); + } } /* The operator is always second child */ @@ -182,7 +223,7 @@ int main(int argc, char const *argv[]) { /* Define them with the following language */ mpca_lang(MPCA_LANG_DEFAULT, " \ - number : /-?[0-9]+/ ; \ + number : /-?[0-9]+([.][0-9]*|[0-9]*)/ ; \ operator : '+' | '-' | '*' | '/' | '%' | '^' | \ \"min\" | \"max\" ; \ expr : | '(' + ')' ; \