|
|
@ -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); |
|
|
|