From 987530bf33a1aeed1f9f6be53cfc18401e01cfac Mon Sep 17 00:00:00 2001 From: tmeissner Date: Tue, 7 May 2019 13:14:18 +0200 Subject: [PATCH] Implement some bonus marks * Extend parsing and evaluation to support decimal types using a double field * Remainder operator is already supported --- chapter_09/lispy.c | 95 +++++++++++++++++++++++++++++++++++----------- chapter_09/lispy.h | 10 +++-- 2 files changed, 78 insertions(+), 27 deletions(-) diff --git a/chapter_09/lispy.c b/chapter_09/lispy.c index cd91ab7..7285069 100644 --- a/chapter_09/lispy.c +++ b/chapter_09/lispy.c @@ -44,6 +44,16 @@ lval* lval_num(long x) { } +/* Construct a pointer to a new decimal lval */ +lval* lval_dec(double x) { + lval* v = malloc(sizeof(lval)); + assert(v != NULL); + v->type = LVAL_DEC; + v->dec = x; + return v; +} + + /* Construct a pointer to a new error lval */ lval* lval_err(char* m) { lval* v = malloc(sizeof(lval)); @@ -83,8 +93,9 @@ lval* lval_sexpr(void) { void lval_del(lval* v) { switch (v->type) { - /* Do nothing special for number type */ + /* Do nothing special for number / decimal type */ case LVAL_NUM: + case LVAL_DEC: break; /* For err or sym free the string data */ @@ -112,9 +123,15 @@ void lval_del(lval* v) { lval* lval_read_num(mpc_ast_t* t) { errno = 0; - long x = strtol(t->contents, NULL, 10); - return errno != ERANGE ? lval_num(x) - : lval_err("Invalid number"); + if (strstr(t->contents, ".")) { + double x = strtod(t->contents, NULL); + return errno != ERANGE ? lval_dec(x) + : lval_err("Invalid number"); + } else { + long x = strtol(t->contents, NULL, 10); + return errno != ERANGE ? lval_num(x) + : lval_err("Invalid number"); + } } @@ -194,6 +211,9 @@ void lval_print(lval* v) { case LVAL_NUM: printf("%li", v->num); break; + case LVAL_DEC: + printf("%.2f", v->dec); + break; /* In the case the type is an error */ case LVAL_ERR: printf("Error: %s", v->err); @@ -302,9 +322,9 @@ lval* builtin_op(lval* a, char* op) { /* Ensure all arguments are numbers */ for (size_t i = 0; i < a->count; i++) { - if (a->cell[i]->type != LVAL_NUM) { + if (a->cell[i]->type != LVAL_NUM && a->cell[i]->type != LVAL_DEC) { lval_del(a); - return lval_err("Cannot operate on non-number!"); + return lval_err("Cannot operate on non-number/non-decimal!"); } } @@ -313,31 +333,60 @@ lval* builtin_op(lval* a, char* op) { /* If no arguments and sub then perform unary negation */ if (strcmp(op, "-") == 0 && a->count == 0) { - x->num = -x->num; + if (x->type == LVAL_NUM) { + x->num = -x->num; + } else { + x->dec = -x->dec; + } } /* While there are still elements remaining */ while (a->count > 0) { /* Pop the next element */ lval* y = lval_pop(a, 0); - - if (strcmp(op, "+") == 0) {x->num += y->num;} - if (strcmp(op, "-") == 0) {x->num -= y->num;} - if (strcmp(op, "*") == 0) {x->num *= y->num;} - if (strcmp(op, "/") == 0) { - /* If second operand is zero return error */ - if (y->num == 0) { - lval_del(x); - lval_del(y); - x = lval_err("Division by zero!"); - break; + if (x->type == LVAL_NUM && y->type == LVAL_NUM) { + if (strcmp(op, "+") == 0) {x->num += y->num;} + if (strcmp(op, "-") == 0) {x->num -= y->num;} + if (strcmp(op, "*") == 0) {x->num *= y->num;} + if (strcmp(op, "/") == 0) { + /* If second operand is zero return error */ + if (y->num == 0) { + lval_del(x); + lval_del(y); + x = lval_err("Division by zero!"); + break; + } + x->num /= y->num; + } + if (strcmp(op, "%") == 0) {x->num %= y->num;} + if (strcmp(op, "^") == 0) {x->num = (pow(x->num, y->num));} + if (strcmp(op, "min") == 0) {x->num = min(x->num, y->num);} + if (strcmp(op, "max") == 0) {x->num = max(x->num, y->num);} + } else { + /* Cast integer number into double if necessary */ + double b = x->type == LVAL_NUM ? (double) x->num : x->dec; + double c = y->type == LVAL_NUM ? (double) y->num : y->dec; + /* Perform all operations on double */ + if (strcmp(op, "+") == 0) {b += c;} + if (strcmp(op, "-") == 0) {b -= c;} + if (strcmp(op, "*") == 0) {b *= c;} + if (strcmp(op, "/") == 0) { + /* If second operand is zero return error */ + if (c == 0) { + lval_del(x); + lval_del(y); + x = lval_err("Division by zero!"); + break; + } + b /= c; } - x->num /= y->num; + if (strcmp(op, "%") == 0) {b = fmod(b, c);} + if (strcmp(op, "^") == 0) {b = (pow(b, c));} + if (strcmp(op, "min") == 0) {b = fmin(b, c);} + if (strcmp(op, "max") == 0) {b = fmax(b, c);} + x->type = LVAL_DEC; + x->dec = b; } - if (strcmp(op, "%") == 0) {x->num %= y->num;} - if (strcmp(op, "^") == 0) {x->num = (pow(x->num, y->num));} - if (strcmp(op, "min") == 0) {x->num = min(x->num, y->num);} - if (strcmp(op, "max") == 0) {x->num = max(x->num, y->num);} } lval_del(a); diff --git a/chapter_09/lispy.h b/chapter_09/lispy.h index e4e9762..45a7e45 100644 --- a/chapter_09/lispy.h +++ b/chapter_09/lispy.h @@ -4,11 +4,11 @@ static char* lispy_version = "Lispy version 0.0.0.0.5"; /* Parser language defintion */ static char* parser = " \ - number : /-?[0-9]+/ ; \ - symbol : '+' | '-' | '*' | '/' | '%' | '^' | \ + number : /-?[0-9]+([.][0-9]*|[0-9]*)/ ; \ + symbol : '+' | '-' | '*' | '/' | '%' | '^' | \ \"min\" | \"max\" ; \ sexpr : '(' * ')' ; \ - expr : | | ; \ + expr : | | ; \ lispy : /^/ * /$/ ; \ "; @@ -17,6 +17,7 @@ static char* parser = typedef struct lval { int type; long num; + double dec; /* Error & symbol types have some string data */ char* err; char* sym; @@ -27,10 +28,11 @@ typedef struct lval { /* Create enumeration of possible lval types */ -enum {LVAL_NUM, LVAL_SYM, LVAL_SEXPR, LVAL_ERR}; +enum {LVAL_NUM, LVAL_DEC, LVAL_SYM, LVAL_SEXPR, LVAL_ERR}; /* lval constructor functions */ lval* lval_num(long x); +lval* lval_dec(double x); lval* lval_err(char* m); lval* lval_sym(char* s); lval* lval_sexpr(void);