Mercurial > repo
comparison interps/sceql/sceql-0.1.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 /* | |
2 The Sceql specification is not set in stone, so this | |
3 interpreter may change in the future. Version 0.1 of the specification | |
4 is implemented here. | |
5 */ | |
6 | |
7 #include <stdio.h> | |
8 #include <stdlib.h> | |
9 #include <limits.h> | |
10 | |
11 #define NODESIZE 1000 | |
12 #define LOOPDEPTH 1000 | |
13 | |
14 typedef unsigned char byte; | |
15 | |
16 typedef struct byteqnode | |
17 { | |
18 byte qdata[NODESIZE]; | |
19 int start; /* the first byte in use in qdata */ | |
20 int end; /* the last byte in use in qdata */ | |
21 struct byteqnode *next; | |
22 } byteqnode; | |
23 | |
24 typedef struct | |
25 { | |
26 int len; | |
27 byteqnode *head, *tail; | |
28 } bytequeue; | |
29 | |
30 void error(char *complaint) | |
31 { | |
32 fprintf(stderr, "%s\n", complaint); | |
33 exit(1); | |
34 } | |
35 | |
36 /* create a queue with a zero byte in it */ | |
37 bytequeue *queue_create(void) | |
38 { | |
39 bytequeue *myq; | |
40 myq = malloc(sizeof (bytequeue)); | |
41 if (!myq) | |
42 error("Out of memory"); | |
43 myq->len = 1; | |
44 myq->head = myq->tail = malloc(sizeof (byteqnode)); | |
45 if (!myq->head) | |
46 error("Out of memory"); | |
47 myq->tail->next = NULL; | |
48 myq->tail->start = myq->tail->end = 0; | |
49 myq->tail->qdata[0] = 0; | |
50 return myq; | |
51 } | |
52 | |
53 /* add a byte onto the back of the queue */ | |
54 void queue_enqueue(bytequeue *q, byte val) | |
55 { | |
56 byteqnode *node; | |
57 node = q->tail; | |
58 node->end++; | |
59 if (node->end == NODESIZE) | |
60 { | |
61 node = q->tail = node->next = malloc(sizeof (byteqnode)); | |
62 if (!node) | |
63 error("Out of memory"); | |
64 node->next = NULL; | |
65 node->start = node->end = 0; | |
66 } | |
67 node->qdata[node->end] = val; | |
68 q->len++; | |
69 } | |
70 | |
71 byte queue_peek(bytequeue *q); | |
72 | |
73 /* take a byte from the front of the queue */ | |
74 byte queue_dequeue(bytequeue *q) | |
75 { | |
76 byte result; | |
77 | |
78 result = queue_peek(q); | |
79 | |
80 q->head->start++; | |
81 if (q->head->start == NODESIZE) | |
82 { | |
83 byteqnode *node = q->head->next; | |
84 free(q->head); | |
85 q->head = node; | |
86 /* q->head->start should already be 0 */ | |
87 } | |
88 q->len--; | |
89 return result; | |
90 } | |
91 | |
92 /* peek at a byte from the front of the queue */ | |
93 byte queue_peek(bytequeue *q) | |
94 { | |
95 if (q->len == 0) | |
96 error("peeking at an empty queue"); | |
97 return q->head->qdata[q->head->start]; | |
98 } | |
99 | |
100 /* shove a new byte at the front of the queue replacing what's there */ | |
101 void queue_shove(bytequeue *q, byte b) | |
102 { | |
103 if (q->len == 0) | |
104 error("shoving into an empty queue"); | |
105 q->head->qdata[q->head->start] = b; | |
106 } | |
107 | |
108 void queue_delete(bytequeue *q) | |
109 { | |
110 byteqnode *node; | |
111 while (q->head) | |
112 { | |
113 node = q->head->next; | |
114 free(q->head); | |
115 q->head = node; | |
116 } | |
117 free(q); | |
118 } | |
119 | |
120 #ifdef DEBUGGING | |
121 void queue_debug(bytequeue *q) | |
122 { | |
123 int i; | |
124 i = q->len; | |
125 if (i > 0) | |
126 { | |
127 byte b; | |
128 b = queue_dequeue(q); | |
129 printf("%d", b); | |
130 queue_enqueue(q, b); | |
131 } | |
132 for (i--; i > 0; i--) | |
133 { | |
134 byte b; | |
135 b = queue_dequeue(q); | |
136 printf(", %d", b); | |
137 queue_enqueue(q, b); | |
138 } | |
139 putchar('\n'); | |
140 } | |
141 #endif | |
142 | |
143 typedef enum | |
144 { | |
145 IN_NEXT = 0x01, | |
146 IN_DEC = 0x02, | |
147 IN_INPUT = 0x03, | |
148 IN_OUTPUT = 0x04, | |
149 IN_INC = 0x05, | |
150 IN_GROW = 0x06, | |
151 IN_BEGIN = 0x10, | |
152 IN_END = 0x11 | |
153 #ifdef DEBUGGING | |
154 ,IN_DEBUG = 0xff | |
155 #endif | |
156 } sceql_intype; | |
157 | |
158 typedef struct | |
159 { | |
160 sceql_intype in; | |
161 int r; | |
162 } sceql_instruction; | |
163 | |
164 typedef struct | |
165 { | |
166 sceql_instruction *code; | |
167 int len; | |
168 } sceql_program; | |
169 | |
170 sceql_intype sceql_getinstruction(FILE *f) | |
171 { | |
172 static sceql_intype uc2i[UCHAR_MAX + 1] = { IN_BEGIN }; | |
173 int c; | |
174 sceql_intype i; | |
175 if (uc2i[0] == IN_BEGIN) | |
176 { | |
177 uc2i[0] = 0; | |
178 for (c = 1; c <= (int) UCHAR_MAX; c++) | |
179 { | |
180 i = 0; | |
181 if (c == '\\') | |
182 i = IN_BEGIN; | |
183 else if (c == '/') | |
184 i = IN_END; | |
185 else if (c == '=') | |
186 i = IN_NEXT; | |
187 else if (c == '-') | |
188 i = IN_DEC; | |
189 else if (c == '_') | |
190 i = IN_INC; | |
191 else if (c == '!') | |
192 i = IN_GROW; | |
193 else if (c == '&') | |
194 i = IN_INPUT; | |
195 else if (c == '*') | |
196 i = IN_OUTPUT; | |
197 #ifdef DEBUGGING | |
198 else if (c == '`') | |
199 i = IN_DEBUG; | |
200 #endif | |
201 uc2i[c] = i; | |
202 } | |
203 } | |
204 for (;;) | |
205 { | |
206 c = fgetc(f); | |
207 if (c < 0 || c > 255) | |
208 return 0; | |
209 if ((i = uc2i[c])) | |
210 break; | |
211 } | |
212 return i; | |
213 } | |
214 | |
215 void sceql_load(sceql_program *p, FILE *f) | |
216 { | |
217 sceql_instruction *code; | |
218 sceql_intype in; | |
219 unsigned long filesize; | |
220 int last = 0, depth = 0, begin[LOOPDEPTH]; | |
221 | |
222 fseek(f, 0, SEEK_END); | |
223 filesize = ftell(f); | |
224 if (filesize > INT_MAX) | |
225 error("File too long"); | |
226 fseek(f, 0, SEEK_SET); | |
227 | |
228 code = p->code = malloc(filesize * sizeof (sceql_instruction)); | |
229 if (!code) | |
230 error("Not enough memory"); | |
231 | |
232 if (!(in = sceql_getinstruction(f))) | |
233 error("Program contains no instructions"); | |
234 if (in == IN_END) | |
235 error("Unbalanced slashes"); | |
236 if (in == IN_BEGIN) | |
237 begin[depth++] = 0; | |
238 code[0].in = in; | |
239 code[0].r = 1; | |
240 | |
241 while ((in = sceql_getinstruction(f))) | |
242 { | |
243 if (in == IN_BEGIN) | |
244 { | |
245 last++; | |
246 if (depth < LOOPDEPTH) | |
247 begin[depth] = last; | |
248 depth++; | |
249 } | |
250 else if (in == IN_END && depth <= LOOPDEPTH) | |
251 { | |
252 if (!depth) | |
253 error("Unbalanced slashes"); | |
254 last++; | |
255 code[last].r = last - begin[--depth]; | |
256 code[begin[depth]].r = code[last].r + 1; | |
257 } | |
258 else if (in == IN_END) | |
259 { | |
260 int mydepth = depth, start; | |
261 for (start = last++; start >= 0; start--) | |
262 { | |
263 if (code[start].in == IN_BEGIN) | |
264 { | |
265 if (mydepth == depth) | |
266 break; | |
267 mydepth--; | |
268 } | |
269 else if (code[start].in == IN_END) | |
270 mydepth++; | |
271 } | |
272 code[last].r = last - start; | |
273 code[start].r = code[last].r + 1; | |
274 depth--; | |
275 } | |
276 else | |
277 { | |
278 if (in == code[last].in) | |
279 { | |
280 code[last].r++; | |
281 continue; | |
282 } | |
283 code[++last].r = 1; | |
284 } | |
285 code[last].in = in; | |
286 } | |
287 fclose(f); | |
288 if (depth) | |
289 error("Unbalanced slashes"); | |
290 p->len = last + 1; | |
291 } | |
292 | |
293 void sceql_run(sceql_program *p) | |
294 { | |
295 bytequeue *q; | |
296 sceql_instruction *ip, *end; | |
297 | |
298 ip = p->code; | |
299 end = ip + p->len; | |
300 | |
301 q = queue_create(); | |
302 | |
303 do | |
304 { | |
305 register sceql_intype in; | |
306 register int r; | |
307 | |
308 in = ip->in; | |
309 r = ip->r; | |
310 | |
311 /* run the instruction "in": not just once, but "r" times */ | |
312 | |
313 if (in == IN_NEXT) | |
314 { | |
315 register byte b; | |
316 for (; r; r--) | |
317 { | |
318 b = queue_dequeue(q); | |
319 queue_enqueue(q, b); | |
320 } | |
321 } | |
322 | |
323 /* it just so happens that we can increment or decrement | |
324 r times with a one-liner */ | |
325 else if (in == IN_INC) | |
326 queue_shove(q, queue_peek(q) + r); | |
327 else if (in == IN_DEC) | |
328 queue_shove(q, queue_peek(q) - r); | |
329 | |
330 else if (in == IN_GROW) | |
331 { | |
332 for (; r; r--) | |
333 queue_enqueue(q, 0); | |
334 } | |
335 else if (in == IN_OUTPUT) | |
336 { | |
337 register int c; | |
338 for (; r; r--) | |
339 { | |
340 c = queue_dequeue(q); | |
341 putchar(c); | |
342 queue_enqueue(q, c); | |
343 } | |
344 } | |
345 else if (in == IN_INPUT) | |
346 { | |
347 register int c; | |
348 for (; r; r--) | |
349 { | |
350 c = getchar(); | |
351 if (c == EOF) | |
352 c = 0; | |
353 queue_enqueue(q, c); | |
354 } | |
355 } | |
356 else if (in == IN_BEGIN) | |
357 { | |
358 register byte b; | |
359 if (!(b = queue_peek(q))) | |
360 { | |
361 ip += r; | |
362 continue; | |
363 } | |
364 } | |
365 #ifdef DEBUGGING | |
366 else if (in == IN_DEBUG) | |
367 queue_debug(q); | |
368 #endif | |
369 else /* in == IN_END */ | |
370 { | |
371 ip -= r; | |
372 continue; | |
373 } | |
374 ip++; | |
375 } while (ip < end); | |
376 | |
377 queue_delete(q); | |
378 } | |
379 | |
380 void sceql_freeup(sceql_program *p) | |
381 { | |
382 free(p->code); | |
383 } | |
384 | |
385 void sceql_do(FILE *f) | |
386 { | |
387 sceql_program prog; | |
388 sceql_load(&prog, f); | |
389 sceql_run(&prog); | |
390 sceql_freeup(&prog); | |
391 } | |
392 | |
393 int main(int argc, char *argv[]) | |
394 { | |
395 FILE *codesrc = stdin; | |
396 if (argc != 2) | |
397 error("usage: sceql [sourcefile.sceql]"); | |
398 if (!(codesrc = fopen(argv[1], "r"))) | |
399 { | |
400 error("Unable to open file"); | |
401 } | |
402 sceql_do(codesrc); | |
403 return 0; | |
404 } |