forked from orangeduck/BuildYourOwnLisp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathchapter8_error_handling.html
401 lines (287 loc) · 17.9 KB
/
chapter8_error_handling.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
<h1>Error Handling <small>• Chapter 8</small></h1>
<h2>Crashes</h2> <hr/>
<p>Some of you may have noticed a problem with the previous chapter's program. Try entering this into the prompt and see what happens.</p>
<pre><code data-language='lispy'>Lispy Version 0.0.0.0.3
Press Ctrl+c to Exit
lispy> / 10 0</code></pre>
<p>Ouch. The program crashed upon trying to divide by zero. It's okay if a program crashes during development, but our final program would hopefully never crash, and should always explain to the user what went wrong.</p>
<div class='pull-left alert alert-warning' style="margin: 15px; text-align: center;">
<img src="/static/img/walterwhite.png" alt="walterwhite" class="img-responsive"/>
<p><small>Walter White • Heisenberg</small></p>
</div>
<p>At the moment our program can produce syntax errors but it still has no functionality for reporting errors in the evaluation of expressions. We need to build in some kind of error handling functionality to do this. It can be awkward in C, but if we start off on the right track, it will pay off later on when our system gets more complicated.</p>
<p>C programs crashing is a fact of life. If anything goes wrong the operating system kicks them out. They can crash for many different reasons, and in many different ways. You will see at least one <a href="http://en.wikipedia.org/wiki/Heisenbug">Heisenbug</a>.</p>
<p>But there is no magic in how C programs work. If you face a really troublesome bug don't give up or sit and stare at the screen till your eyes bleed. Take this chance to properly learn how to use <code>gdb</code> and <code>valgrind</code>. These will be more weapons in your toolkit, and after the up front investment, save you a lot of time and pain.</p>
<h2>Lisp Value</h2> <hr/>
<p>There are several ways to deal with errors in C, but in this context my preferred method is to make errors a possible result of evaluating an expression. Then we can say that, in Lispy, an expression will evaluate to <em>either</em> a <em>number</em>, or an <em>error</em>. For example <code>+ 1 2</code> will evaluate to a number, but <code>/ 10 0</code> will evaluate to an error.</p>
<p>For this we need a data structure that can act as either one thing or anything. For simplicity sake we are just going to use a <code>struct</code> with fields specific to each thing that can be represented, and a special field <code>type</code> to tell us exactly what fields are meaningful to access.</p>
<p>This we are going to call an <code>lval</code>, which stands for <em>Lisp Value</em>.</p>
<pre><code data-language='c'>/* Declare New lval Struct */
typedef struct {
int type;
long num;
int err;
} lval;</code></pre>
<h2>Enumerations</h2> <hr/>
<p>You'll notice the type of the fields <code>type</code>, and <code>err</code>, is <code>int</code>. This means they are represented by a single integer number.</p>
<p>The reason we pick <code>int</code> is because we will assign meaning to each integer value, to encode what we require. For example we can make a rule <em>"If <code>type</code> is <code>0</code> then the structure is a Number."</em>, or <em>"If <code>type</code> is <code>1</code> then the structure is an Error."</em> This is a good way of doing things. It is simple and effective.</p>
<p>But if we litter our code with stray <code>0</code> and <code>1</code> then it is going to become increasingly unclear as to what is happening. Instead we can use named constants that have been assigned these integer values. This gives the reader an indication as to <em>why</em> one might be comparing a number to <code>0</code> or <code>1</code> and <em>what</em> is meant in this context.</p>
<p>In C this is supported using something called an <code>enum</code>.</p>
<pre><code data-language='c'>/* Create Enumeration of Possible lval Types */
enum { LVAL_NUM, LVAL_ERR };</code></pre>
<p>An <code>enum</code> is a declaration of variables which under the hood are automatically assigned integer constant values. Above is how we would declare some enumerated values for the <code>type</code> field.</p>
<p>We also want to declare an enumeration for the <em>error</em> field. We have three error cases in our particular program. There is division by zero, an unknown operator, or being passed a number that is too large to be represented internally using a <code>long</code>. These can be enumerated as follows.</p>
<pre><code data-language='c'>/* Create Enumeration of Possible Error Types */
enum { LERR_DIV_ZERO, LERR_BAD_OP, LERR_BAD_NUM };</code></pre>
<h2>Lisp Value Functions</h2> <hr/>
<p>Our <code>lval</code> type is almost ready to go. Unlike the previous <code>long</code> type we have no current method for creating new instances of it. To do this we can declare two functions that construct an <code>lval</code> of either an <em>error</em> type or a <em>number</em> type.</p>
<pre><code data-language='c'>/* Create a new number type lval */
lval lval_num(long x) {
lval v;
v.type = LVAL_NUM;
v.num = x;
return v;
}
/* Create a new error type lval */
lval lval_err(int x) {
lval v;
v.type = LVAL_ERR;
v.err = x;
return v;
}</code></pre>
<p>These functions first create an <code>lval</code> called <code>v</code>, and assign the fields before returning it.</p>
<p>Because our <code>lval</code> function can now be one of two things we can no longer just use <code>printf</code> to output it. We will want to do different behaviour depending upon the type of the <code>lval</code> that is given. There is a concise way to do this in C using the <code>switch</code> statement. This takes some value as input and compares it to other known values, known as <em>cases</em>. When the values are equal it executes the code that follows up until the next <code>break</code> statement.</p>
<p>Using this we can build a function that can print an <code>lval</code> of any type like this.</p>
<pre><code data-language='c'>/* Print an "lval" */
void lval_print(lval v) {
switch (v.type) {
/* In the case the type is a number print it, then 'break' out of the switch. */
case LVAL_NUM: printf("%li", v.num); break;
/* In the case the type is an error */
case LVAL_ERR:
/* Check What exact type of error it is and print it */
if (v.err == LERR_DIV_ZERO) { printf("Error: Division By Zero!"); }
if (v.err == LERR_BAD_OP) { printf("Error: Invalid Operator!"); }
if (v.err == LERR_BAD_NUM) { printf("Error: Invalid Number!"); }
break;
}
}
/* Print an "lval" followed by a newline */
void lval_println(lval v) { lval_print(v); putchar('\n'); }</code></pre>
<h2>Evaluating Errors</h2> <hr/>
<p>Now that we know how to work with the <code>lval</code> type, we need to change our evaluation functions to use it instead of <code>long</code>.</p>
<p>As well as changing the type signatures we need to change the functions such that they work correctly upon encountering either an <em>error</em> as input, or a <em>number</em> as input.</p>
<p>In our <code>eval_op</code> function, if we encounter an error we should return it right away, and only do computation if both the arguments are numbers. We should modify our code to return an error rather than attempt to divide by zero. This will fix the crash from right at the beginning of this chapter.</p>
<pre><code data-language='c'>lval eval_op(lval x, char* op, lval y) {
/* If either value is an error return it */
if (x.type == LVAL_ERR) { return x; }
if (y.type == LVAL_ERR) { return 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 instead of result */
return y.num == 0 ? lval_err(LERR_DIV_ZERO) : lval_num(x.num / y.num);
}
return lval_err(LERR_BAD_OP);
}</code></pre>
<div class="alert alert-warning">
<p><strong>What is that <code>?</code> doing there?</strong></p>
<p>You'll notice that for division to check if the second argument is zero we use a question mark symbol <code>?</code>, followed by a colon <code>:</code>. This is called the <em>ternary operator</em>, and it allows you to write conditional expressions on one line.</p>
<p>It works something like this. <code><condition> ? <then> : <else></code>. In other words, if the condition is true it returns what follows the <code>?</code>, otherwise it returns what follows <code>:</code>.</p>
<p>Some people dislike this operator because they believe it makes code unclear. If you are unfamiliar with the ternary operator, initially, you may feel awkward to use it; but once you get to know it there are rarely problems.</p>
</div>
<p>We need to give a similar treatment to our <code>eval</code> function. In this case because we've defined <code>eval_op</code> to robustly handle errors we just need to add the error conditions to our number conversion function.</p>
<p>In this case we use the <code>strtol</code> function to convert from string to <code>long</code>. This allows us to check a special variable <code>errno</code> to ensure the conversion goes correctly. This is a more robust way to convert numbers than our previous method using <code>atoi</code>.</p>
<pre><code data-language='c'>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);
}
char* op = t->children[1]->contents;
lval x = eval(t->children[2]);
int i = 3;
while (strstr(t->children[i]->tag, "expr")) {
x = eval_op(x, op, eval(t->children[i]));
i++;
}
return x;
}</code></pre>
<p>The final small step is to change how we print the result found by our evaluation to use our newly defined printing function which can print any type of <code>lval</code>.</p>
<pre><code data-language='c'>lval result = eval(r.output);
lval_println(result);
mpc_ast_delete(r.output);</code></pre>
<p>And we are done! Try running this new program and make sure there are no crashes when dividing by zero!</p>
<pre><code data-language='lispy'>lispy> / 10 0
Error: Division By Zero!
lispy> / 10 2
5</code></pre>
<h2>Plumbing</h2> <hr/>
<div class='pull-right alert alert-warning' style="margin: 15px; text-align: center;">
<img src="/static/img/plumbing.png" alt="plumbing" class="img-responsive"/>
<p><small>Plumbing • Harder than you think</small></p>
</div>
<p>Some of you who have gotten this far in the book may feel uncomfortable with how it is progressing. You may feel you've managed to follow instructions well enough, but don't have a clear understanding of all of the underlying mechanisms going on behind the scenes.</p>
<p>If this is the case I want to reassure you that you are doing well. If you don't understand the internals it's because I have not explained everything in sufficient depth. This is okay.</p>
<p>To be able to progress and get code to work under these conditions is a really great skill in programming, and if you've made it this far it shows you have it.</p>
<p>In programming we call this <em>plumbing</em>. Roughly speaking this is following instructions to try and tie together a bunch libraries or components, without fully understanding how they work internally.</p>
<p>It requires <em>faith</em> and <em>intuition</em>. <em>Faith</em> is required to believe that if the stars align, and every incantation is correctly performed for this magical machine, the right thing will really happen. And <em>intuition</em> is required to work out what has gone wrong, and how to fix things when they don't go as planned.</p>
<p>Unfortunately these can't be taught directly, so if you've made it this far then <em>congratulations!</em> You've made it over a difficult hump, and in the following chapters I promise we'll finish up with the plumbing, and actually start to programming that feels fresh and wholesome.</p>
<h2>Reference</h2> <hr/>
<div class="panel-group alert alert-warning" id="accordion">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseOne">
error_handling.c
</a>
</h4>
</div>
<div id="collapseOne" class="panel-collapse collapse">
<div class="panel-body">
<pre><code data-language='c'>#include "mpc.h"
#ifdef _WIN32
static char buffer[2048];
char* readline(char* prompt) {
fputs(prompt, stdout);
fgets(buffer, 2048, stdin);
char* cpy = malloc(strlen(buffer)+1);
strcpy(cpy, buffer);
cpy[strlen(cpy)-1] = '\0';
return cpy;
}
void add_history(char* unused) {}
#else
#include <editline/readline.h>
#include <editline/history.h>
#endif
/* Create Enumeration of Possible Error Types */
enum { LERR_DIV_ZERO, LERR_BAD_OP, LERR_BAD_NUM };
/* Create Enumeration of Possible lval Types */
enum { LVAL_NUM, LVAL_ERR };
/* Declare New lval Struct */
typedef struct {
int type;
long num;
int err;
} lval;
/* Create a new number type lval */
lval lval_num(long x) {
lval v;
v.type = LVAL_NUM;
v.num = x;
return v;
}
/* Create a new error type lval */
lval lval_err(int x) {
lval v;
v.type = LVAL_ERR;
v.err = x;
return v;
}
/* Print an "lval" */
void lval_print(lval v) {
switch (v.type) {
/* In the case the type is a number print it, then 'break' out of the switch. */
case LVAL_NUM: printf("%li", v.num); break;
/* In the case the type is an error */
case LVAL_ERR:
/* Check What exact type of error it is and print it */
if (v.err == LERR_DIV_ZERO) { printf("Error: Division By Zero!"); }
if (v.err == LERR_BAD_OP) { printf("Error: Invalid Operator!"); }
if (v.err == LERR_BAD_NUM) { printf("Error: Invalid Number!"); }
break;
}
}
/* Print an "lval" followed by a newline */
void lval_println(lval v) { lval_print(v); putchar('\n'); }
lval eval_op(lval x, char* op, lval y) {
/* If either value is an error return it */
if (x.type == LVAL_ERR) { return x; }
if (y.type == LVAL_ERR) { return 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 instead of result */
return y.num == 0 ? lval_err(LERR_DIV_ZERO) : lval_num(x.num / y.num);
}
return lval_err(LERR_BAD_OP);
}
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);
}
char* op = t->children[1]->contents;
lval x = eval(t->children[2]);
int i = 3;
while (strstr(t->children[i]->tag, "expr")) {
x = eval_op(x, op, eval(t->children[i]));
i++;
}
return x;
}
int main(int argc, char** argv) {
mpc_parser_t* Number = mpc_new("number");
mpc_parser_t* Operator = mpc_new("operator");
mpc_parser_t* Expr = mpc_new("expr");
mpc_parser_t* Lispy = mpc_new("lispy");
mpca_lang(MPCA_LANG_DEFAULT,
" \
number : /-?[0-9]+/ ; \
operator : '+' | '-' | '*' | '/' ; \
expr : <number> | '(' <operator> <expr>+ ')' ; \
lispy : /^/ <operator> <expr>+ /$/ ; \
",
Number, Operator, Expr, Lispy);
puts("Lispy Version 0.0.0.0.4");
puts("Press Ctrl+c to Exit\n");
while (1) {
char* input = readline("lispy> ");
add_history(input);
mpc_result_t r;
if (mpc_parse("<stdin>", input, Lispy, &r)) {
lval result = eval(r.output);
lval_println(result);
mpc_ast_delete(r.output);
} else {
mpc_err_print(r.error);
mpc_err_delete(r.error);
}
free(input);
}
mpc_cleanup(4, Number, Operator, Expr, Lispy);
return 0;
}
</code></pre>
</div>
</div>
</div>
</div>
<h2>Bonus Marks</h2> <hr/>
<div class="alert alert-warning">
<ul class="list-group">
<li class="list-group-item">› Run the previous chapter's code through <code>gdb</code> and crash it. See what happens.</li>
<li class="list-group-item">› How do you give an <code>enum</code> a name?</li>
<li class="list-group-item">› What are <code>union</code> data types and how do they work?</li>
<li class="list-group-item">› Can you change how <code>lval</code> is defined to use <code>union</code> instead of <code>struct</code>?</li>
<li class="list-group-item">› What are the advantages over using a <code>union</code> instead of <code>struct</code>?</li>
<li class="list-group-item">› Extend parsing and evaluation to support the remainder operator <code>%</code>.</li>
<li class="list-group-item">› Extend parsing and evaluation to support decimal types using a <code>double</code> field.</li>
</ul>
</div>
<h2>Navigation</h2>
<table class="table" style='table-layout: fixed;'>
<tr>
<td class="text-left"><a href="chapter7_evaluation"><h4>‹ Evaluation</h4></a></td>
<td class="text-center"><a href="contents"><h4>• Contents •</h4></a></td>
<td class="text-right"><a href="chapter9_s_expressions"><h4>S-Expressions ›</h4></a></td>
</tr>
</table>