Mercurial > repo
comparison interps/underload/underload.c @ 996:859f9b4339e6
<Gregor> tar xf egobot.tar.xz
author | HackBot |
---|---|
date | Sun, 09 Dec 2012 19:30:08 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
995:6883f5911eb7 | 996:859f9b4339e6 |
---|---|
1 #include <stdio.h> | |
2 #include <stdlib.h> | |
3 #include <stdint.h> | |
4 #include <string.h> | |
5 | |
6 #define CAT -1 /* inner should be treated normally, except continue with | |
7 this el's next once it's evaluated */ | |
8 #define INNER -2 /* inner is a quotation */ | |
9 #define STACK -3 /* inner is a stack element */ | |
10 | |
11 typedef uint_fast8_t fint; | |
12 | |
13 typedef struct tag_el { | |
14 int_least32_t val; /* character, or a special value if negative */ | |
15 size_t refcount; /* reference count from inner, next, or globals */ | |
16 /* this counts the number of locations referencing, even if they're | |
17 referenced multiple times themselves they only count as 1 */ | |
18 struct tag_el* next; /* the element after this one */ | |
19 struct tag_el* inner; /* the element inside this one, if val is negative */ | |
20 } el; | |
21 | |
22 enum {unknown, underload, underlambda} lang = unknown; | |
23 | |
24 int debuglevel = 0; | |
25 | |
26 void serialize(el* e, FILE* f, fint stackdir); | |
27 | |
28 void error(const char* errstr) { | |
29 fprintf(stderr,"Error: %s\n",errstr); | |
30 exit(EXIT_FAILURE); } | |
31 | |
32 el* malloc_el(void) { | |
33 el* x = malloc(sizeof (el)); | |
34 if(!x) error("Out of memory"); | |
35 return x; } | |
36 void checkfree(el* e) { | |
37 if(!e) return; | |
38 if(!(e->refcount)) { | |
39 e->refcount=1; | |
40 if(debuglevel >= 6) fprintf(stderr,"[free] "); | |
41 if(debuglevel >= 6) serialize(e,stderr,1); | |
42 if(debuglevel >= 6) fputc('\n',stderr); | |
43 if(e->next) e->next->refcount--; | |
44 checkfree(e->next); | |
45 if(e->inner) e->inner->refcount--; | |
46 checkfree(e->inner); | |
47 free(e); }} | |
48 | |
49 void uniputc(int_least32_t c, FILE* f) { | |
50 if (c < 0) | |
51 error("Attempt to output a negative-coded character"); | |
52 if (c < 0x80) | |
53 putc(c,f); | |
54 else if(c < 0x800) { | |
55 putc(0xc0| (c>> 6),f); | |
56 putc(0x80|( c &0x3f),f); } | |
57 else if(c < 0x10000) { | |
58 putc(0xd0| (c>>12),f); | |
59 putc(0x80|((c>> 6)&0x3f),f); | |
60 putc(0x80| (c &0x3f),f); } | |
61 else if(c < 0x200000) { | |
62 putc(0xf0| (c>>18),f); | |
63 putc(0x80|((c>>12)&0x3f),f); | |
64 putc(0x80|((c>> 6)&0x3f),f); | |
65 putc(0x80|( c &0x3f),f); } | |
66 else if(c < 0x4000000) { | |
67 putc(0xf8| (c>>24),f); | |
68 putc(0x80|((c>>18)&0x3f),f); | |
69 putc(0x80|((c>>12)&0x3f),f); | |
70 putc(0x80|((c>> 6)&0x3f),f); | |
71 putc(0x80|( c &0x3f),f); } | |
72 else { | |
73 putc(0xfc| (c>>30),f); | |
74 putc(0x80|((c>>24)&0x3f),f); | |
75 putc(0x80|((c>>18)&0x3f),f); | |
76 putc(0x80|((c>>12)&0x3f),f); | |
77 putc(0x80|((c>> 6)&0x3f),f); | |
78 putc(0x80|( c &0x3f),f); }} | |
79 int_least32_t unigetc(FILE* f) { | |
80 int_least32_t rv=0; | |
81 int_least32_t min=0x40; | |
82 fint count=0; | |
83 int c=getc(f); | |
84 unsigned char mask = 0xc0; | |
85 if(c < 0x80) return c; | |
86 while(c & mask) {count++; c &= ~mask; mask>>=1;} | |
87 if(count < 1) error("UTF-8 input contains invalid first byte in character"); | |
88 if(count > 1) min <<= 1; | |
89 rv = c; | |
90 while(count--) { | |
91 rv *= 1<<6; | |
92 min <<= 5; | |
93 c = getc(f); | |
94 if((c&0xc0) != 0x80) | |
95 error("UTF-8 input contains invalid subsequent byte in character"); | |
96 rv += (c & 0x3f); } | |
97 if(rv < min) error("UTF-8 character has a non-shortest representation"); | |
98 return rv; } | |
99 | |
100 el* popstack(el** stack, const char* errmsg) { | |
101 el* rv; | |
102 el* temp; | |
103 if(!*stack) error(errmsg); | |
104 if((**stack).val != STACK) error("Internal error: Stack is not a stack"); | |
105 if((**stack).refcount != 1) error("Internal error: Overreferenced stack"); | |
106 rv = (**stack).inner; | |
107 temp = (**stack).next; | |
108 free(*stack); | |
109 /* temp.refcount stays the same */ | |
110 *stack = temp; | |
111 if(rv) rv->refcount--; | |
112 return rv; } | |
113 void pushstack(el** stack, el* e) { | |
114 el* temp = malloc_el(); | |
115 if(e) e->refcount++; | |
116 temp->val = STACK; | |
117 temp->refcount = 1; | |
118 temp->inner = e; | |
119 temp->next = *stack; | |
120 /* *stack.refcount stays the same */ | |
121 *stack = temp; } | |
122 | |
123 void serialize_underload(el* e, FILE* f, fint stackdir) { | |
124 serialise_tco: /* tail-call optimisation of this function by hand */; | |
125 if(!e) return; | |
126 if(debuglevel >= 6) fprintf(f,"[%lu]",(unsigned long)e->refcount); | |
127 switch(e->val) { | |
128 case STACK: | |
129 if(!stackdir) { | |
130 if(debuglevel >+ 6) fprintf(f,"[z]"); | |
131 if(e->inner) serialize_underload(e->inner,f,stackdir); | |
132 break; } | |
133 if(e->next) serialize_underload(e->next,f,stackdir); | |
134 putc('{',f); | |
135 if(e->inner) serialize_underload(e->inner,f,stackdir); | |
136 fputs("} ",f); | |
137 return; | |
138 case INNER: | |
139 putc('(',f); | |
140 if(e->inner) serialize_underload(e->inner,f,stackdir); | |
141 putc(')',f); | |
142 break; | |
143 case CAT: | |
144 serialize_underload(e->inner,f,stackdir); break; | |
145 default: | |
146 uniputc(e->val,f); break; } | |
147 if(debuglevel >= 5 && !e->refcount) | |
148 error("Sanity check failed: This should be free by now"); | |
149 e=e->next; goto serialise_tco; } | |
150 void serialize(el* e, FILE* f, fint stackdir) { | |
151 /* //tk: Efficient serialization for Underlambda */ | |
152 serialize_underload(e, f, stackdir); } | |
153 | |
154 el *stack; /* the stack */ | |
155 el *ip; /* current point in the program we're executing */ | |
156 el *zerostack; /* stack of places to go if we hit null next pointers */ | |
157 int main(int argc, char **argv) { | |
158 FILE* in; | |
159 int usingstdin; | |
160 el **working; | |
161 el *temp, *temp2, *temp3; | |
162 int_least32_t c; | |
163 if(!argc||!argv[0]) error("Could not determine executable name"); | |
164 char* a0 = argv[0]; | |
165 if(strrchr(argv[0],'/')) a0=strrchr(argv[0],'/')+1; | |
166 if(!strcmp(a0,"derlo")) lang=underload; | |
167 if(!strcmp(a0,"derla")) lang=underlambda; | |
168 while(argc>=2) { | |
169 if(!strcmp(argv[1],"-o")) {lang=underload; argv++; argc--;} | |
170 else if(!strcmp(argv[1],"-a")) {lang=underlambda; argv++; argc--;} | |
171 else if(!strncmp(argv[1],"-d",2)) { | |
172 debuglevel=argv[1][2]-'0'; | |
173 if(debuglevel < 0 || debuglevel > 7) debuglevel = 0; | |
174 argv++; argc--; } | |
175 else break; } | |
176 if(lang==unknown || argc > 2) { | |
177 puts("Usage: derl (-o|-a) [inputfile]"); | |
178 puts("Options:"); | |
179 puts(" -o Interpret input as Underload"); | |
180 puts(" -a Interpret input as Underlambda"); | |
181 puts(" -d0 No debugging"); | |
182 puts(" -d1: enable warnings"); | |
183 puts(" -d2: free all used resources at program exit"); | |
184 puts(" -d3: show program and stack every step"); | |
185 puts(" -d5: enable internal sanity checks"); | |
186 puts(" -d6: debug memory allocation"); | |
187 puts(" -d7: debug internal interpreter program flow"); | |
188 puts("Instead of using the -o or -a option, you can invoke this program"); | |
189 puts("with the name 'derlo' or 'derla'. All -d options include all"); | |
190 puts("previous -d options except -d0. -d0 is the default."); | |
191 return argc==1 ? 0 : EXIT_FAILURE; } | |
192 if(argc==2) { | |
193 in = fopen(argv[1],"rb"); | |
194 if(!in) {perror(argv[1]); return EXIT_FAILURE;} | |
195 } else {in = stdin; usingstdin = 1;} | |
196 stack = 0; | |
197 ip = 0; | |
198 zerostack = 0; | |
199 working = &ip; | |
200 while(((c=unigetc(in)))!=EOF) { | |
201 if(c==')') { | |
202 temp = popstack(&zerostack,"Unmatched )"); | |
203 working = &(temp->next); | |
204 continue; } | |
205 if(c=='(') { | |
206 *working = malloc_el(); | |
207 (**working).refcount = 1; | |
208 (**working).val = INNER; | |
209 (**working).inner = 0; | |
210 (**working).next = 0; | |
211 pushstack(&zerostack,*working); | |
212 working = &((**working).inner); | |
213 continue; } | |
214 /* //tk: string parsing for Underlambda */ | |
215 /* //tk: EOF parsing for Underlambda */ | |
216 *working = malloc_el(); | |
217 (**working).refcount = 1; | |
218 (**working).val = c; | |
219 (**working).inner = 0; | |
220 (**working).next = 0; | |
221 working = &((**working).next); } | |
222 if(zerostack) error("Expected ) at end of input"); | |
223 | |
224 while(ip || zerostack) { | |
225 if(!ip) { | |
226 if(debuglevel >= 7) fprintf(stderr,"Moving back in zerostack.\n"); | |
227 ip = popstack(&zerostack,"Internal error: Zerostack disappeared"); | |
228 if(ip) ip->refcount++; | |
229 continue; } | |
230 if(debuglevel >= 3) { | |
231 putc('\t',stderr); | |
232 serialize(stack,stderr,1); | |
233 serialize(ip,stderr,0); | |
234 serialize(zerostack,stderr,0); | |
235 putc('\n',stderr); } | |
236 switch(ip->val) { | |
237 case STACK: | |
238 error("Internal error: Stack element found in program"); | |
239 case CAT: | |
240 /* not a command, but an optimisation to avoid duplicating things; | |
241 put ip->next on the zerostack so we can go back to it later, and | |
242 continue with ip->inner */ | |
243 if(debuglevel >= 7) fprintf(stderr,"Evaluating a lazy cat.\n"); | |
244 if(!ip->inner) break; /* continue with ip->next */ | |
245 if(debuglevel >= 7) fprintf(stderr,"The cat was nontrivial.\n"); | |
246 pushstack(&zerostack,ip->next); | |
247 temp = ip->inner; | |
248 ip->refcount--; | |
249 temp->refcount++; | |
250 checkfree(ip); | |
251 ip = temp; | |
252 continue; | |
253 case INNER: | |
254 if(debuglevel >= 7) fprintf(stderr,"Pushing to the stack.\n"); | |
255 if(ip->inner) | |
256 pushstack(&stack,ip->inner); | |
257 else | |
258 pushstack(&stack,0); | |
259 break; | |
260 case '!': | |
261 if(debuglevel >= 7) fprintf(stderr,"Popping the stack.\n"); | |
262 temp = popstack(&stack,"Stack underflow in !"); | |
263 checkfree(temp); | |
264 break; | |
265 case 'S': | |
266 if(debuglevel >= 7) fprintf(stderr,"Serialising TOS.\n"); | |
267 temp = popstack(&stack,"Stack underflow in S"); | |
268 if(temp) temp->refcount++; | |
269 serialize(temp,stdout,0); | |
270 fflush(stdout); | |
271 if(temp) temp->refcount--; | |
272 if(debuglevel >= 3) putc('\n',stdout); | |
273 checkfree(temp); | |
274 break; | |
275 case '~': | |
276 if(debuglevel >= 7) fprintf(stderr,"Swapping stack elements.\n"); | |
277 temp = popstack(&stack,"Stack underflow in ~"); | |
278 temp2 = popstack(&stack,"Stack underflow in ~"); | |
279 pushstack(&stack,temp); | |
280 pushstack(&stack,temp2); | |
281 break; | |
282 case 'a': | |
283 if(debuglevel >= 7) fprintf(stderr,"Wrapping the top stack element.\n"); | |
284 temp = malloc_el(); | |
285 temp->refcount = 0; | |
286 temp->inner = popstack(&stack,"Stack underflow in a"); | |
287 if(temp->inner) temp->inner->refcount++; | |
288 temp->val = INNER; | |
289 temp->next = 0; | |
290 pushstack(&stack,temp); | |
291 break; | |
292 case ':': | |
293 if(debuglevel >= 7) fprintf(stderr,"Duplicating the top stack element.\n"); | |
294 temp = popstack(&stack,"Stack underflow in :"); | |
295 pushstack(&stack,temp); | |
296 pushstack(&stack,temp); | |
297 break; | |
298 case '*': | |
299 if(debuglevel >= 7) fprintf(stderr,"Catting two stack elements.\n"); | |
300 temp = popstack(&stack,"Stack underflow in *"); | |
301 temp2 = popstack(&stack,"Stack underflow in *"); | |
302 /* The degenerate cases of catting a null string need to be handled */ | |
303 if(!temp ) {pushstack(&stack,temp2); break;} | |
304 if(!temp2) {pushstack(&stack,temp ); break;} | |
305 temp->refcount++; /* if temp and temp2 are aliased, this correctly | |
306 prevents the optimisation happening */ | |
307 if(temp2->refcount == 0) { | |
308 /* Tailcat optimisation: if nothing else is using the stack | |
309 element we're catting to, just change its next pointer to do | |
310 the catting directly. */ | |
311 if(debuglevel >= 7) fprintf(stderr,"Trying tailcat optimisation.\n"); | |
312 temp3 = temp2; | |
313 while(temp3->next) { | |
314 if(temp3->refcount > 1) goto cantoptimise_star; | |
315 temp3 = temp3->next; } | |
316 if(temp3->refcount > 1) goto cantoptimise_star; | |
317 if(debuglevel >= 7) fprintf(stderr,"Tailcat optimisation succeeded.\n"); | |
318 temp3->next = temp; | |
319 temp3 = temp2; } | |
320 else { | |
321 cantoptimise_star: ; | |
322 temp3 = malloc_el(); | |
323 temp3->refcount = 0; | |
324 temp3->next = temp; | |
325 temp3->inner = temp2; | |
326 temp2->refcount++; | |
327 temp3->val = CAT; } | |
328 pushstack(&stack,temp3); | |
329 break; | |
330 case '^': | |
331 if(debuglevel >= 7) fprintf(stderr,"Evalling the stack.\n"); | |
332 temp = popstack(&stack,"Stack underflow in ^"); | |
333 if(!temp) break; /* continue with ip->next */ | |
334 while(ip->refcount == 1 && !(ip->next) && zerostack) { | |
335 /* Tailcall optimisation. */ | |
336 if(debuglevel >= 7) fprintf(stderr,"Tailcall optimisation.\n"); | |
337 ip->next = popstack(&zerostack, | |
338 "Internal error: Zerostack disappeared"); } | |
339 if(ip->next) ip->next->refcount++; | |
340 if(temp->refcount == 0) { | |
341 /* Tailcat optimisation on the call stack; replace the return at | |
342 the end of the called code with a goto, as long as it isn't | |
343 used anywhere else */ | |
344 if(debuglevel >= 7) fprintf(stderr,"Trying tailcat optimisation.\n"); | |
345 temp3 = temp; | |
346 while(temp3->next) { | |
347 if(temp3->refcount > 1) goto cantoptimise_caret; | |
348 temp3 = temp3->next; } | |
349 if(temp3->refcount > 1) goto cantoptimise_caret; | |
350 temp3->next = ip->next; | |
351 if(debuglevel >= 7) fprintf(stderr,"Tailcat optimisation succeeded.\n"); | |
352 temp2 = temp; } | |
353 else { | |
354 cantoptimise_caret: ; | |
355 temp2 = malloc_el(); | |
356 temp2->refcount = 0; | |
357 temp2->next = ip->next; | |
358 temp2->inner = temp; | |
359 temp->refcount++; | |
360 temp2->val = CAT; } | |
361 ip->refcount--; | |
362 if(temp2) temp2->refcount++; | |
363 checkfree(ip); | |
364 ip = temp2; | |
365 continue; | |
366 default: | |
367 fprintf(stderr, "Attempt to execute unknown command %d\n",ip->val); | |
368 return EXIT_FAILURE; } | |
369 temp = ip->next; | |
370 ip->refcount--; | |
371 if(temp) temp->refcount++; | |
372 checkfree(ip); | |
373 ip = temp; } | |
374 if(!usingstdin) fclose(in); | |
375 if(debuglevel >= 1 && stack) | |
376 fprintf(stderr, "Warning: Stack was not empty at program exit\n"); | |
377 if(debuglevel >= 3) { | |
378 serialize(stack,stderr,1); | |
379 putc('\n',stderr); } | |
380 if(debuglevel >= 2) | |
381 while(stack) | |
382 checkfree(popstack(&stack,"Internal error: Stack has inconsistent size")); | |
383 return 0; } |