OSDN Git Service

1f8c91a88723b028c495818b954f16d9be862b64
[joypy/Thun.git] / implementations / C / joy.c
1 /*
2 Copyright © 2023 Simon Forman
3
4 This file is part of Thun
5
6 Thun is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Thun is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Thun.  If not see <http://www.gnu.org/licenses/>.
18
19 */
20 #include <stddef.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24
25 #include <gc.h>
26 #include <gmp.h>
27
28 #include "joy.h"
29
30
31 const char *BLANKS = " \t";
32 const char *FALSE = "false";
33 const char *TRUE = "true";
34
35
36 void*
37 reallocate_function (void *ptr, __attribute__((unused)) size_t old_size, size_t new_size) {
38         return GC_REALLOC(ptr, new_size);
39 }
40
41
42 void
43 deallocate_function (void *ptr, __attribute__((unused)) size_t size) {
44         GC_FREE(ptr);
45 }
46
47
48 void
49 my_callback(GC_PTR void_obj, __attribute__((unused)) GC_PTR void_environment) {
50         mpz_t *obj = (mpz_t*)void_obj;
51         mpz_clear(*obj);
52 }
53
54
55 JoyList
56 push_integer_from_str(char *str, JoyList tail)
57 {
58         JoyList el = newJoyList;
59         el->head.kind = joyInt;
60         mpz_init_set_str(el->head.value.i, str, 10);
61         GC_register_finalizer(el->head.value.i, my_callback, NULL, NULL, NULL);
62         el->tail = tail;
63         return el;
64 }
65
66
67 /* Pre-declare so we can use it in print_node(). */
68 void
69 print_list(JoyList el);
70
71
72 void
73 print_node(JoyType j)
74 {
75         switch (j.kind) {
76         case joyInt:
77                 gmp_printf("%Zd", j.value.i);
78                 break;
79         case joySymbol:
80                 printf("%s", j.value.symbol);
81                 break;
82         case joyTrue:
83                 printf("true");
84                 break;
85         case joyFalse:
86                 printf("false");
87                 break;
88         case joyList:
89                 printf("[");
90                 print_list(j.value.el);
91                 printf("]");
92                 break;
93         default:
94                 printf("wtf");
95         }
96 }
97
98
99 void
100 print_list(JoyList el)
101 {
102         while (NULL != el) {
103                 print_node(el->head);
104                 el = el->tail;
105                 if (NULL != el) {
106                         printf(" ");
107                 }
108         }
109 }
110
111
112 char *
113 trim_leading_blanks(char *str)
114 {
115         size_t offset = strspn(str, BLANKS);
116         return (offset == strlen(str)) ? NULL : (str + offset);
117 }
118
119
120 JoyList
121 make_non_list_node(char *text, size_t size)
122 {
123         char *sym;
124         const struct dict_entry *interned;
125         JoyList node = newJoyList;
126
127         interned = in_word_set(text, size);
128         if (interned) {
129                 node->head.kind = joySymbol;
130                 node->head.value.symbol = interned->name;
131                 return node;
132         }
133
134         sym = GC_malloc(size + 1);  /* one more for the zero, right? */
135         strncat(sym, text, size);
136
137         if (!strncmp(sym, FALSE, 6)) {  /* I know it's wrong to hardcode the length here.  Sorry. */
138                 /* If head was a pointer we could reuse Boolean singletons... */
139                 node->head.kind = joyFalse;
140                 node->head.value.boolean = 0;
141
142         } else if (!strncmp(sym, TRUE, 5)) {  /* I know it's wrong to hardcode the length here.  Sorry. */
143                 node->head.kind = joyTrue;
144                 node->head.value.boolean = 1;
145
146         } else if (mpz_init_set_str(node->head.value.i, sym, 10)) {
147                 /* Non-zero (-1) return value means the string is not an int. */
148                 mpz_clear(node->head.value.i);
149                 node->head.kind = joySymbol;
150                 node->head.value.symbol = sym;
151
152         } else {
153                 node->head.kind = joyInt;
154                 GC_register_finalizer(node->head.value.i, my_callback, NULL, NULL, NULL);
155         }
156
157         return node;
158 }
159
160 /* Create a new list_node with a joyList head. */
161 JoyList
162 make_list_node(JoyList el)
163 {
164         JoyList node = newJoyList;
165         node->head.kind = joyList;
166         node->head.value.el = el;
167         return node;
168 }
169
170
171 /*
172 ██████╗  █████╗ ██████╗ ███████╗███████╗██████╗
173 ██╔══██╗██╔══██╗██╔══██╗██╔════╝██╔════╝██╔══██╗
174 ██████╔╝███████║██████╔╝███████╗█████╗  ██████╔╝
175 ██╔═══╝ ██╔══██║██╔══██╗╚════██║██╔══╝  ██╔══██╗
176 ██║     ██║  ██║██║  ██║███████║███████╗██║  ██║
177 ╚═╝     ╚═╝  ╚═╝╚═╝  ╚═╝╚══════╝╚══════╝╚═╝  ╚═╝
178 */
179
180
181 JoyList
182 parse_list(char **text)
183 {
184 /*
185 Extract terms from the text until a closing bracket is found.
186 */
187         char *rest;
188         ptrdiff_t diff;
189         JoyList result = EMPTY_LIST;
190
191         /* NULL string input? */
192         if (NULL == *text) {
193                 printf("Missing ']' bracket. A\n");
194                 exit(1);
195         };
196
197         *text = trim_leading_blanks(*text);
198
199         if (NULL == *text) {
200                 printf("Missing ']' bracket. B\n");
201                 exit(1);
202         };
203
204         /* Look for blanks or brackets. */
205         rest = strpbrk(*text, " []");
206         /*
207         rest now points to a space or '[' or ']' after a term,
208         -or- it is NULL if the rest of the string is a single term
209         with no spaces nor brackets.  If that's the case then we're
210         missing a closing bracket!
211         */
212         if (NULL == rest) {
213                 printf("Missing ']' bracket. C\n");
214                 exit(1);
215         };
216
217         /* How many chars have we got? */
218         diff = rest - *text;
219
220         if (diff) {
221                 result = make_non_list_node(*text, diff);
222                 *text = rest;
223         } else if ('[' == rest[0]) {
224                 *text = ++rest;
225                 result = make_list_node(parse_list(text));
226         } else if (']' == rest[0]) {
227                 *text = ++rest;
228                 return result;
229         }
230         result->tail = parse_list(text);
231         return result;
232 }
233
234 /*
235 Get the next node from the text, updating text
236 to point to the rest of the, uh, text.
237 */
238 JoyList
239 parse_node(char **text)
240 {
241         char *rest;
242         ptrdiff_t diff;
243         JoyList thing;
244
245         /* NULL string input? */
246         if (NULL == *text) return EMPTY_LIST;
247
248         *text = trim_leading_blanks(*text);
249
250         /* All blanks? */
251         if (NULL == *text) return EMPTY_LIST;
252
253         /* Look for blanks or brackets. */
254         rest = strpbrk(*text, " []");
255         /*
256         rest now points to a space or '[' or ']' after a term,
257         -or- it is NULL if the rest of the string is a single term
258         with no spaces nor brackets.  If that's the case then we're
259         done, and we can just return a list with one symbol in it.
260         */
261         if (NULL == rest) {
262                 thing = make_non_list_node(*text, strlen(*text));
263                 *text = rest;
264                 return thing;
265         }
266
267         /* How many chars have we got? */
268         diff = rest - *text;
269
270         if (diff) {
271                 thing = make_non_list_node(*text, diff);
272                 *text = rest;
273                 return thing;
274         }
275         if ('[' == rest[0]) {
276                 *text = ++rest;
277                 return make_list_node(parse_list(text));
278         }
279         if (']' == rest[0]) {
280                 printf("Extra ']' bracket.\n");
281                 exit(1);
282         }
283         printf("Should be unreachable.");
284         exit(1);
285 }
286
287
288 JoyList
289 text_to_expression(char *text)
290 {
291         JoyList result, head, tail;
292
293         result = parse_node(&text);
294         head = result;
295         tail = parse_node(&text);
296         while (NULL != tail) {
297                 head->tail = tail;
298                 head = tail;
299                 tail = parse_node(&text);
300         }
301         return result;
302 }
303
304
305 void add(JoyList *stack, JoyList *expression) {stack = expression;}
306 void branch(JoyList *stack, JoyList *expression) {stack = expression;}
307 void clear(JoyList *stack, JoyList *expression) {stack = expression;}
308 void div_joyfunc(JoyList *stack, JoyList *expression) {stack = expression;}
309 void eq(JoyList *stack, JoyList *expression) {
310         printf("Hey there from eq!\n");
311         stack = expression;
312 }
313 void ge(JoyList *stack, JoyList *expression) {stack = expression;}
314 void gt(JoyList *stack, JoyList *expression) {stack = expression;}
315 void le(JoyList *stack, JoyList *expression) {stack = expression;}
316 void lt(JoyList *stack, JoyList *expression) {stack = expression;}
317 void mod(JoyList *stack, JoyList *expression) {stack = expression;}
318 void mul(JoyList *stack, JoyList *expression) {stack = expression;}
319 void neq(JoyList *stack, JoyList *expression) {stack = expression;}
320 void sub(JoyList *stack, JoyList *expression) {stack = expression;}
321 void truthy(JoyList *stack, JoyList *expression) {stack = expression;}
322
323
324 void
325 push_thing(JoyType *term, JoyList *stack) {
326         JoyList node = newJoyList;
327         node->head = *term;  /* Copies data, right? */
328         node->tail = *stack;
329         *stack = node;
330 }
331
332
333 void
334 joy(JoyList *stack, JoyList *expression)
335 {
336         char *sym;
337         JoyType *term;
338         const struct dict_entry *interned;
339
340         while (*expression) {
341                 term = &((*expression)->head);
342                 *expression = (*expression)->tail;
343                 switch (term->kind) {
344                 case joyInt:
345                 case joyTrue:
346                 case joyFalse:
347                 case joyList:
348                         push_thing(term, stack);
349                         break;
350
351                 case joySymbol:
352                         sym = term->value.symbol;
353                         interned = in_word_set(sym, strlen(sym));
354                         if (!interned) {
355                                 printf("Unknown: %s\n", sym);
356                                 exit(1);
357                         }
358                         interned->func(stack, expression);
359                 }
360         }
361 }
362
363 int
364 main(void)
365 {
366         char *line;
367         char *status;
368         JoyList stack = EMPTY_LIST;
369         JoyList expression = EMPTY_LIST;
370
371         mp_set_memory_functions(
372                 &GC_malloc,
373                 &reallocate_function,
374                 &deallocate_function
375         );
376
377         line = (char *)GC_malloc(1025);
378
379         while (1) {
380                 printf("\njoy? ");
381                 status = gets_s(line, 1025);
382                 if (NULL == status) {
383                         /*
384                         From the man page:
385
386                         > Upon successful completion, fgets(), gets_s(), and gets() return a
387                         pointer to the string.  If end-of-file occurs before any characters are
388                         read, they return NULL and the buffer contents remain unchanged.  If an
389                         error occurs, they return NULL and the buffer contents are indeterminate.
390                         The fgets(), gets_s(), and gets() functions do not distinguish between
391                         end-of-file and error, and callers must use feof(3) and ferror(3) to
392                         determine which occurred.
393
394                         TODO: "use feof(3) and ferror(3)"...
395
396                         */
397                         printf("bye\n");
398                         break;
399                 }
400                 expression = text_to_expression(line);
401                 joy(&stack, &expression);
402                 print_list(stack);
403                 printf("\n");
404         }
405         return 0;
406 }