Browse Source

Implement some bonus marks

* Extend parsing and evaluation to support
  decimal types using a double field
* Remainder operator is already supported
T. Meissner 7 months 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) {
44 44
 }
45 45
 
46 46
 
47
+/* Construct a pointer to a new decimal lval */
48
+lval* lval_dec(double x) {
49
+    lval* v = malloc(sizeof(lval));
50
+    assert(v != NULL);
51
+    v->type = LVAL_DEC;
52
+    v->dec = x;
53
+    return v;
54
+}
55
+
56
+
47 57
 /* Construct a pointer to a new error lval */
48 58
 lval* lval_err(char* m) {
49 59
     lval* v = malloc(sizeof(lval));
@@ -83,8 +93,9 @@ lval* lval_sexpr(void) {
83 93
 void lval_del(lval* v) {
84 94
 
85 95
     switch (v->type) {
86
-        /* Do nothing special for number type */
96
+        /* Do nothing special for number / decimal type */
87 97
         case LVAL_NUM:
98
+        case LVAL_DEC:
88 99
             break;
89 100
 
90 101
         /* For err or sym free the string data */
@@ -112,9 +123,15 @@ void lval_del(lval* v) {
112 123
 
113 124
 lval* lval_read_num(mpc_ast_t* t) {
114 125
     errno = 0;
115
-    long x = strtol(t->contents, NULL, 10);
116
-    return errno != ERANGE ? lval_num(x)
117
-                           : lval_err("Invalid number");
126
+    if (strstr(t->contents, ".")) {
127
+        double x = strtod(t->contents, NULL);
128
+        return errno != ERANGE ? lval_dec(x)
129
+                               : lval_err("Invalid number");
130
+    } else {
131
+        long x = strtol(t->contents, NULL, 10);
132
+        return errno != ERANGE ? lval_num(x)
133
+                               : lval_err("Invalid number");
134
+    }
118 135
 }
119 136
 
120 137
 
@@ -194,6 +211,9 @@ void lval_print(lval* v) {
194 211
         case LVAL_NUM:
195 212
             printf("%li", v->num);
196 213
             break;
214
+        case LVAL_DEC:
215
+            printf("%.2f", v->dec);
216
+            break;
197 217
         /* In the case the type is an error */
198 218
         case LVAL_ERR:
199 219
             printf("Error: %s", v->err);
@@ -302,9 +322,9 @@ lval* builtin_op(lval* a, char* op) {
302 322
 
303 323
     /* Ensure all arguments are numbers */
304 324
     for (size_t i = 0; i < a->count; i++) {
305
-        if (a->cell[i]->type != LVAL_NUM) {
325
+        if (a->cell[i]->type != LVAL_NUM && a->cell[i]->type != LVAL_DEC) {
306 326
             lval_del(a);
307
-            return lval_err("Cannot operate on non-number!");
327
+            return lval_err("Cannot operate on non-number/non-decimal!");
308 328
         }
309 329
     }
310 330
 
@@ -313,31 +333,60 @@ lval* builtin_op(lval* a, char* op) {
313 333
 
314 334
     /* If no arguments and sub then perform unary negation */
315 335
     if (strcmp(op, "-") == 0 &&  a->count == 0) {
316
-        x->num = -x->num;
336
+        if (x->type == LVAL_NUM) {
337
+            x->num = -x->num;
338
+        } else {
339
+            x->dec = -x->dec;
340
+        }
317 341
     }
318 342
 
319 343
     /* While there are still elements remaining */
320 344
     while (a->count > 0) {
321 345
         /* Pop the next element */
322 346
         lval* y = lval_pop(a, 0);
323
-
324
-        if (strcmp(op, "+") == 0) {x->num += y->num;}
325
-        if (strcmp(op, "-") == 0) {x->num -= y->num;}
326
-        if (strcmp(op, "*") == 0) {x->num *= y->num;}
327
-        if (strcmp(op, "/") == 0) {
328
-            /* If second operand is zero return error */
329
-            if (y->num == 0) {
330
-                lval_del(x);
331
-                lval_del(y);
332
-                x = lval_err("Division by zero!");
333
-                break;
347
+        if (x->type == LVAL_NUM && y->type == LVAL_NUM) {
348
+            if (strcmp(op, "+") == 0) {x->num += y->num;}
349
+            if (strcmp(op, "-") == 0) {x->num -= y->num;}
350
+            if (strcmp(op, "*") == 0) {x->num *= y->num;}
351
+            if (strcmp(op, "/") == 0) {
352
+                /* If second operand is zero return error */
353
+                if (y->num == 0) {
354
+                    lval_del(x);
355
+                    lval_del(y);
356
+                    x = lval_err("Division by zero!");
357
+                    break;
358
+                }
359
+                x->num /= y->num;
360
+            }
361
+            if (strcmp(op, "%") == 0) {x->num %= y->num;}
362
+            if (strcmp(op, "^") == 0) {x->num = (pow(x->num, y->num));}
363
+            if (strcmp(op, "min") == 0) {x->num = min(x->num, y->num);}
364
+            if (strcmp(op, "max") == 0) {x->num = max(x->num, y->num);}
365
+        } else {
366
+            /* Cast integer number into double if necessary */
367
+            double b = x->type == LVAL_NUM ? (double) x->num : x->dec;
368
+            double c = y->type == LVAL_NUM ? (double) y->num : y->dec;
369
+            /* Perform all operations on double */
370
+            if (strcmp(op, "+") == 0) {b += c;}
371
+            if (strcmp(op, "-") == 0) {b -= c;}
372
+            if (strcmp(op, "*") == 0) {b *= c;}
373
+            if (strcmp(op, "/") == 0) {
374
+                /* If second operand is zero return error */
375
+                if (c == 0) {
376
+                    lval_del(x);
377
+                    lval_del(y);
378
+                    x = lval_err("Division by zero!");
379
+                    break;
380
+                }
381
+                b /= c;
334 382
             }
335
-            x->num /= y->num;
383
+            if (strcmp(op, "%") == 0) {b = fmod(b, c);}
384
+            if (strcmp(op, "^") == 0) {b = (pow(b, c));}
385
+            if (strcmp(op, "min") == 0) {b = fmin(b, c);}
386
+            if (strcmp(op, "max") == 0) {b = fmax(b, c);}
387
+            x->type = LVAL_DEC;
388
+            x->dec = b;
336 389
         }
337
-        if (strcmp(op, "%") == 0) {x->num %= y->num;}
338
-        if (strcmp(op, "^") == 0) {x->num = (pow(x->num, y->num));}
339
-        if (strcmp(op, "min") == 0) {x->num = min(x->num, y->num);}
340
-        if (strcmp(op, "max") == 0) {x->num = max(x->num, y->num);}
341 390
     }
342 391
 
343 392
     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";
4 4
 /* Parser language defintion */
5 5
 static char* parser =
6 6
 "                                                   \
7
-    number   : /-?[0-9]+/ ;                         \
8
-    symbol : '+' | '-' | '*' | '/' | '%' | '^' |  \
7
+    number   : /-?[0-9]+([.][0-9]*|[0-9]*)/ ;       \
8
+    symbol : '+' | '-' | '*' | '/' | '%' | '^' |    \
9 9
     \"min\" | \"max\" ;                             \
10 10
     sexpr    : '(' <expr>* ')' ;                    \
11
-    expr     : <number> | <symbol> | <sexpr> ;    \
11
+    expr     : <number> | <symbol> | <sexpr> ;      \
12 12
     lispy    : /^/ <expr>* /$/ ;                    \
13 13
 ";
14 14
 
@@ -17,6 +17,7 @@ static char* parser =
17 17
 typedef struct lval {
18 18
     int type;
19 19
     long num;
20
+    double dec;
20 21
     /* Error & symbol types have some string data */
21 22
     char* err;
22 23
     char* sym;
@@ -27,10 +28,11 @@ typedef struct lval {
27 28
 
28 29
 
29 30
 /* Create enumeration of possible lval types */
30
-enum {LVAL_NUM, LVAL_SYM, LVAL_SEXPR, LVAL_ERR};
31
+enum {LVAL_NUM, LVAL_DEC, LVAL_SYM, LVAL_SEXPR, LVAL_ERR};
31 32
 
32 33
 /* lval constructor functions */
33 34
 lval* lval_num(long x);
35
+lval* lval_dec(double x);
34 36
 lval* lval_err(char* m);
35 37
 lval* lval_sym(char* s);
36 38
 lval* lval_sexpr(void);