Browse Source

Implement some bonus marks

* Extend parsing and evaluation to support
  decimal types using a double field
* Remainder operator is already supported
master
T. Meissner 5 years ago
parent
commit
987530bf33
2 changed files with 78 additions and 27 deletions
  1. +72
    -23
      chapter_09/lispy.c
  2. +6
    -4
      chapter_09/lispy.h

+ 72
- 23
chapter_09/lispy.c View File

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


+ 6
- 4
chapter_09/lispy.h View File

@ -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 : <number> | <symbol> | <sexpr> ; \
expr : <number> | <symbol> | <sexpr> ; \
lispy : /^/ <expr>* /$/ ; \
";
@ -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);


Loading…
Cancel
Save