Browse Source

Add source of chapter 08

T. Meissner 7 months ago
parent
commit
0cb0504889
2 changed files with 239 additions and 0 deletions
  1. 13
    0
      chapter_08/Makefile
  2. 226
    0
      chapter_08/lispy.c

+ 13
- 0
chapter_08/Makefile View File

@@ -0,0 +1,13 @@
1
+MPC_DIR := ../mpc
2
+
3
+
4
+all: lispy
5
+
6
+
7
+%: %.c
8
+	cc -std=c11 -Wall $@.c ${MPC_DIR}/mpc.c -ledit -o $@
9
+
10
+
11
+.PHONY: clean
12
+clean:
13
+	rm -rf lispy

+ 226
- 0
chapter_08/lispy.c View File

@@ -0,0 +1,226 @@
1
+#include <stdio.h>
2
+#include <stdlib.h>
3
+#include "../mpc/mpc.h"
4
+
5
+
6
+/* If we are on Windows compile these functions */
7
+#ifdef _WIN32
8
+#include <string.h>
9
+#include <assert.h>
10
+
11
+static char buffer[2048];
12
+
13
+/* Fake readline function */
14
+char* readline(char* prompt) {
15
+    fputs(prompt, stdout);
16
+    fgets(buffer, 2048, stdin);
17
+    char* cpy = malloc(strlen(buffer)+1);
18
+    assert(cpy != NULL)
19
+    strcpy(cpy, buffer);
20
+    cpy[strlen(cpy)-1] = '\0';
21
+    return cpy;
22
+}
23
+
24
+/* Fake add_history function */
25
+void add_history(char* unused) {}
26
+
27
+/* Otherwise include the editline headers
28
+   could use __APPLE__ for detection of OSX */
29
+#else
30
+#include <editline/readline.h>
31
+#endif
32
+
33
+
34
+/* Declare new lval struct */
35
+typedef struct {
36
+    int type;
37
+    long num;
38
+    int err;
39
+} lval;
40
+
41
+
42
+/* Create enumeration of possible lval types */
43
+enum {LVAL_NUM, LVAL_ERR};
44
+
45
+
46
+/* Create enumeration of possible error types */
47
+enum {LERR_DIV_ZERO, LERR_BAD_OP, LERR_BAD_NUM};
48
+
49
+
50
+/* Create a new number of type lval */
51
+lval lval_num(long x) {
52
+    lval v;
53
+    v.type = LVAL_NUM;
54
+    v.num = x;
55
+    return v;
56
+}
57
+
58
+
59
+/* Create a new error type lval */
60
+lval lval_err(int x) {
61
+    lval v;
62
+    v.type = LVAL_ERR;
63
+    v.err = x;
64
+    return v;
65
+}
66
+
67
+
68
+/* Print an lval */
69
+void lval_print(lval v) {
70
+    switch (v.type) {
71
+        /* In the case the type is a number print it
72
+           Then break out of the switch */
73
+        case LVAL_NUM:
74
+            printf("%li", v.num);
75
+            break;
76
+        /* In the case the type is an error */
77
+        case LVAL_ERR:
78
+            /* Check what type of error it is & print it */
79
+            if (v.err == LERR_DIV_ZERO) {
80
+                printf("Error: Division by zero!");
81
+            }
82
+            if (v.err == LERR_BAD_OP) {
83
+                printf("Error: Invalid operator!");
84
+            }
85
+            if (v.err == LERR_BAD_NUM) {
86
+                printf("Error: Invalid number!");
87
+            }
88
+            break;
89
+    }
90
+}
91
+
92
+
93
+/* Print an lval followed by a newline */
94
+void lval_println(lval v) {
95
+    lval_print(v);
96
+    putchar('\n');
97
+}
98
+
99
+
100
+long min(long x, long y) {
101
+    if (x <= y) {
102
+        return x;
103
+    } else {
104
+        return y;
105
+    }
106
+}
107
+
108
+
109
+long max(long x, long y) {
110
+    if (x >= y) {
111
+        return x;
112
+    } else {
113
+        return y;
114
+    }
115
+}
116
+
117
+
118
+/* Use operator string to see which operation to perform */
119
+lval eval_op(lval x, char* op, lval y) {
120
+
121
+    /* If either value is an error return it */
122
+    if (x.type == LVAL_ERR) {
123
+        return x;
124
+    }
125
+    if (y.type == LVAL_ERR) {
126
+        return y;
127
+    }
128
+
129
+    /* Otherwise do maths on the number values */
130
+
131
+    if (strcmp(op, "+") == 0) {return lval_num(x.num + y.num);}
132
+    if (strcmp(op, "-") == 0) {return lval_num(x.num - y.num);}
133
+    if (strcmp(op, "*") == 0) {return lval_num(x.num * y.num);}
134
+    if (strcmp(op, "/") == 0) {
135
+        /* If second operand is zero return error */
136
+        return y.num == 0 ? lval_err(LERR_DIV_ZERO)
137
+                          : lval_num(x.num / y.num);
138
+    }
139
+    if (strcmp(op, "%") == 0) {return lval_num(x.num % y.num);}
140
+    if (strcmp(op, "^") == 0) {return lval_num(pow(x.num, y.num));}
141
+    if (strcmp(op, "min") == 0) {return lval_num(min(x.num, y.num));}
142
+    if (strcmp(op, "max") == 0) {return lval_num(max(x.num, y.num));}
143
+
144
+    return lval_err(LERR_BAD_OP);
145
+}
146
+
147
+
148
+lval eval(mpc_ast_t* t) {
149
+
150
+    if (strstr(t->tag, "number")) {
151
+        /* Check if there is some error in conversion */
152
+        errno = 0;
153
+        long x = strtol(t->contents, NULL, 10);
154
+        return errno != ERANGE ? lval_num(x) : lval_err(LERR_BAD_NUM);
155
+    }
156
+
157
+    /* The operator is always second child */
158
+    char* op = t->children[1]->contents;
159
+
160
+    /* We store the third children in x */
161
+    lval x = eval(t->children[2]);
162
+
163
+    /* Iterate the remaining children and combining */
164
+    unsigned int i = 3;
165
+    while (strstr(t->children[i]->tag, "expr")) {
166
+        x = eval_op(x, op, eval(t->children[i]));
167
+        i++;
168
+    }
169
+
170
+    return x;
171
+}
172
+
173
+
174
+int main(int argc, char const *argv[]) {
175
+
176
+    /* Create some parsers */
177
+    mpc_parser_t* Number = mpc_new("number");
178
+    mpc_parser_t* Operator = mpc_new("operator");
179
+    mpc_parser_t* Expr = mpc_new("expr");
180
+    mpc_parser_t* Lispy = mpc_new("lispy");
181
+
182
+    /* Define them with the following language */
183
+    mpca_lang(MPCA_LANG_DEFAULT,
184
+        "                                                       \
185
+            number   : /-?[0-9]+/ ;                             \
186
+            operator : '+' | '-' | '*' | '/' | '%' | '^' |      \
187
+            \"min\" | \"max\" ;                                 \
188
+            expr     : <number> | '(' <operator> <expr>+ ')' ;  \
189
+            lispy    : /^/ <operator> <expr>+ /$/ ;             \
190
+        ",
191
+        Number, Operator, Expr, Lispy);
192
+
193
+    /* Print version and exit information */
194
+    puts("Lispy version 0.0.0.0.4");
195
+    puts("Press Ctrl+c to exit\n");
196
+
197
+    /* In a never ending loop */
198
+    while (1) {
199
+        /* Output our prompt and get input */
200
+        char* input = readline("lispy> ");
201
+
202
+        /* Add input to history */
203
+        add_history(input);
204
+
205
+        /* Attempt to parse the user input */
206
+        mpc_result_t r;
207
+        if (mpc_parse("<stdin>", input, Lispy, &r)) {
208
+            /* On success evaluate the user input */
209
+            lval result = eval(r.output);
210
+            lval_println(result);
211
+            mpc_ast_delete(r.output);
212
+        } else {
213
+            /* Otherwise print the error */
214
+            mpc_err_print(r.error);
215
+            mpc_err_delete(r.error);
216
+        }
217
+
218
+        /* Free retrieved input */
219
+        free(input);
220
+    }
221
+
222
+    /* Undefine and delete our parsers */
223
+    mpc_cleanup(4, Number, Operator, Expr, Lispy);
224
+
225
+    return 0;
226
+}