rev |
line source |
nuclear@0
|
1 #include <stdio.h>
|
nuclear@0
|
2 #include <stdlib.h>
|
nuclear@0
|
3 #include <ctype.h>
|
nuclear@0
|
4 #include <stdarg.h>
|
nuclear@0
|
5
|
nuclear@3
|
6 enum { SYM_VAR, SYM_FUNC };
|
nuclear@3
|
7
|
nuclear@3
|
8 struct symbol {
|
nuclear@3
|
9 int type;
|
nuclear@3
|
10 char name;
|
nuclear@3
|
11 struct symbol *next;
|
nuclear@3
|
12 };
|
nuclear@3
|
13
|
nuclear@0
|
14 void init(void);
|
nuclear@4
|
15 void program(void);
|
nuclear@5
|
16 void block(int brklb);
|
nuclear@5
|
17 void doif(int brklb);
|
nuclear@4
|
18 void dowhile(void);
|
nuclear@5
|
19 void dobreak(int brklb);
|
nuclear@4
|
20 void condition(void);
|
nuclear@0
|
21 void expression(void);
|
nuclear@3
|
22 void assignment(void);
|
nuclear@0
|
23 void term(void);
|
nuclear@0
|
24 void factor(void);
|
nuclear@4
|
25 void ident(void);
|
nuclear@0
|
26 void add(void);
|
nuclear@0
|
27 void sub(void);
|
nuclear@0
|
28 void mul(void);
|
nuclear@0
|
29 void divide(void);
|
nuclear@0
|
30 void get_char(void);
|
nuclear@0
|
31 void match(char c);
|
nuclear@0
|
32 char get_name(void);
|
nuclear@0
|
33 char get_num(void);
|
nuclear@3
|
34 void skip_white(void);
|
nuclear@0
|
35 void stop(const char *s, ...);
|
nuclear@0
|
36 void expected(const char *s);
|
nuclear@0
|
37 void emit(const char *fmt, ...);
|
nuclear@0
|
38 void vemit(const char *fmt, va_list ap);
|
nuclear@0
|
39 void emitln(const char *fmt, ...);
|
nuclear@0
|
40 int is_addsub(char c);
|
nuclear@3
|
41 int add_symbol(char c, int type);
|
nuclear@4
|
42 int newlabel(void);
|
nuclear@4
|
43 const char *labelstr(int lb);
|
nuclear@4
|
44 void postlabel(int lb);
|
nuclear@4
|
45 void other(void);
|
nuclear@0
|
46
|
nuclear@0
|
47 FILE *infile, *outfile;
|
nuclear@0
|
48 char look;
|
nuclear@0
|
49
|
nuclear@3
|
50 struct symbol *symlist;
|
nuclear@3
|
51
|
nuclear@0
|
52 static const char *prologue =
|
nuclear@0
|
53 "\t.globl _start\n"
|
nuclear@0
|
54 "_start:\n"
|
nuclear@0
|
55 "\tcall main\n"
|
nuclear@0
|
56 "\tmov %eax, %ebx\n"
|
nuclear@0
|
57 "\tmov $1, %eax\n"
|
nuclear@0
|
58 "\tint $0x80\n"
|
nuclear@0
|
59 "main:\n"
|
nuclear@0
|
60 "\t# ---- START ----\n";
|
nuclear@0
|
61
|
nuclear@0
|
62 static const char *epilogue = "\t# ---- END ----\n\tret\n";
|
nuclear@0
|
63
|
nuclear@0
|
64 int main(void)
|
nuclear@0
|
65 {
|
nuclear@0
|
66 infile = stdin;
|
nuclear@0
|
67 outfile = stdout;
|
nuclear@0
|
68
|
nuclear@0
|
69 init();
|
nuclear@0
|
70 /* output prologue */
|
nuclear@0
|
71 fputs(prologue, outfile);
|
nuclear@0
|
72
|
nuclear@4
|
73 program();
|
nuclear@0
|
74
|
nuclear@0
|
75 /* output epilogue */
|
nuclear@0
|
76 fputs(epilogue, outfile);
|
nuclear@3
|
77 /* write all variable declarations */
|
nuclear@3
|
78 fprintf(outfile, "\t.data\n");
|
nuclear@3
|
79 while(symlist) {
|
nuclear@3
|
80 struct symbol *sym = symlist;
|
nuclear@3
|
81 symlist = symlist->next;
|
nuclear@3
|
82 if(sym->type == SYM_VAR) {
|
nuclear@3
|
83 fprintf(outfile, "%c:\t.long 0\n", sym->name);
|
nuclear@3
|
84 }
|
nuclear@3
|
85 free(sym);
|
nuclear@3
|
86 }
|
nuclear@0
|
87 return 0;
|
nuclear@0
|
88 }
|
nuclear@0
|
89
|
nuclear@0
|
90 void init(void)
|
nuclear@0
|
91 {
|
nuclear@0
|
92 get_char();
|
nuclear@3
|
93 skip_white();
|
nuclear@0
|
94 }
|
nuclear@0
|
95
|
nuclear@4
|
96 void program(void)
|
nuclear@4
|
97 {
|
nuclear@5
|
98 block(-1);
|
nuclear@4
|
99 if(look != 'e') {
|
nuclear@4
|
100 expected("End");
|
nuclear@4
|
101 }
|
nuclear@4
|
102 emitln("# END");
|
nuclear@4
|
103 }
|
nuclear@4
|
104
|
nuclear@4
|
105 void other(void)
|
nuclear@4
|
106 {
|
nuclear@4
|
107 emitln("%c", get_name());
|
nuclear@4
|
108 }
|
nuclear@4
|
109
|
nuclear@5
|
110 void block(int brklb)
|
nuclear@4
|
111 {
|
nuclear@4
|
112 while(look != 'e' && look != 'l') {
|
nuclear@4
|
113 switch(look) {
|
nuclear@4
|
114 case 'i':
|
nuclear@5
|
115 doif(brklb);
|
nuclear@4
|
116 break;
|
nuclear@4
|
117
|
nuclear@4
|
118 case 'w':
|
nuclear@4
|
119 dowhile();
|
nuclear@4
|
120 break;
|
nuclear@4
|
121
|
nuclear@5
|
122 case 'b':
|
nuclear@5
|
123 dobreak(brklb);
|
nuclear@5
|
124 break;
|
nuclear@5
|
125
|
nuclear@4
|
126 default:
|
nuclear@4
|
127 other();
|
nuclear@4
|
128 break;
|
nuclear@4
|
129 }
|
nuclear@4
|
130 }
|
nuclear@4
|
131 }
|
nuclear@4
|
132
|
nuclear@5
|
133 void doif(int brklb)
|
nuclear@4
|
134 {
|
nuclear@4
|
135 int lb1, lb2;
|
nuclear@4
|
136
|
nuclear@4
|
137 match('i');
|
nuclear@4
|
138 condition();
|
nuclear@4
|
139
|
nuclear@4
|
140 lb1 = newlabel();
|
nuclear@4
|
141 lb2 = lb1;
|
nuclear@4
|
142
|
nuclear@4
|
143 emitln("jz %s", labelstr(lb1));
|
nuclear@5
|
144 block(brklb);
|
nuclear@4
|
145
|
nuclear@4
|
146 if(look == 'l') {
|
nuclear@4
|
147 match('l');
|
nuclear@4
|
148 lb2 = newlabel();
|
nuclear@4
|
149 emitln("jmp %s", labelstr(lb2));
|
nuclear@4
|
150 postlabel(lb1);
|
nuclear@5
|
151 block(brklb);
|
nuclear@4
|
152 }
|
nuclear@4
|
153 match('e');
|
nuclear@4
|
154 postlabel(lb2);
|
nuclear@4
|
155 }
|
nuclear@4
|
156
|
nuclear@4
|
157 void dowhile(void)
|
nuclear@4
|
158 {
|
nuclear@4
|
159 int lbtop, lbend;
|
nuclear@4
|
160
|
nuclear@4
|
161 match('w');
|
nuclear@4
|
162 lbtop = newlabel();
|
nuclear@4
|
163 lbend = newlabel();
|
nuclear@4
|
164 postlabel(lbtop);
|
nuclear@4
|
165 condition();
|
nuclear@4
|
166 emitln("jz %s", labelstr(lbend));
|
nuclear@5
|
167 block(lbend);
|
nuclear@4
|
168 match('e');
|
nuclear@4
|
169 emitln("jmp %s", labelstr(lbtop));
|
nuclear@4
|
170 postlabel(lbend);
|
nuclear@4
|
171 }
|
nuclear@4
|
172
|
nuclear@5
|
173 void dobreak(int brklb)
|
nuclear@5
|
174 {
|
nuclear@5
|
175 match('b');
|
nuclear@5
|
176 if(brklb == -1) {
|
nuclear@5
|
177 stop("invalid break, not in loop");
|
nuclear@5
|
178 }
|
nuclear@5
|
179 emitln("jmp %s", labelstr(brklb));
|
nuclear@5
|
180 }
|
nuclear@5
|
181
|
nuclear@4
|
182 void condition(void)
|
nuclear@4
|
183 {
|
nuclear@4
|
184 emitln("<condition>");
|
nuclear@4
|
185 }
|
nuclear@4
|
186
|
nuclear@0
|
187 void expression(void)
|
nuclear@0
|
188 {
|
nuclear@0
|
189 if(is_addsub(look)) {
|
nuclear@0
|
190 emitln("xor %%eax, %%eax");
|
nuclear@0
|
191 } else {
|
nuclear@0
|
192 term();
|
nuclear@0
|
193 }
|
nuclear@0
|
194
|
nuclear@0
|
195 while(is_addsub(look)) {
|
nuclear@0
|
196 emitln("push %%eax");
|
nuclear@0
|
197 switch(look) {
|
nuclear@0
|
198 case '+':
|
nuclear@0
|
199 add();
|
nuclear@0
|
200 break;
|
nuclear@0
|
201 case '-':
|
nuclear@0
|
202 sub();
|
nuclear@0
|
203 break;
|
nuclear@0
|
204 }
|
nuclear@0
|
205 }
|
nuclear@0
|
206 }
|
nuclear@0
|
207
|
nuclear@3
|
208 void assignment(void)
|
nuclear@3
|
209 {
|
nuclear@3
|
210 char name = get_name();
|
nuclear@3
|
211 match('=');
|
nuclear@3
|
212
|
nuclear@3
|
213 if(add_symbol(name, SYM_VAR) == -1) {
|
nuclear@3
|
214 stop("variable name '%c' previously defined as another type of symbol\n", name);
|
nuclear@3
|
215 }
|
nuclear@3
|
216
|
nuclear@3
|
217 expression();
|
nuclear@3
|
218 emitln("mov %%eax, (%c)", name);
|
nuclear@3
|
219 }
|
nuclear@3
|
220
|
nuclear@0
|
221 void term(void)
|
nuclear@0
|
222 {
|
nuclear@0
|
223 factor();
|
nuclear@0
|
224 while(look == '*' || look == '/') {
|
nuclear@0
|
225 emitln("push %%eax");
|
nuclear@0
|
226 switch(look) {
|
nuclear@0
|
227 case '*':
|
nuclear@0
|
228 mul();
|
nuclear@0
|
229 break;
|
nuclear@0
|
230 case '/':
|
nuclear@0
|
231 divide();
|
nuclear@0
|
232 break;
|
nuclear@0
|
233 }
|
nuclear@0
|
234 }
|
nuclear@0
|
235 }
|
nuclear@0
|
236
|
nuclear@0
|
237 void factor(void)
|
nuclear@0
|
238 {
|
nuclear@0
|
239 if(look == '(') {
|
nuclear@0
|
240 match('(');
|
nuclear@0
|
241 expression();
|
nuclear@0
|
242 match(')');
|
nuclear@3
|
243 } else if(isalpha(look)) {
|
nuclear@3
|
244 ident();
|
nuclear@0
|
245 } else {
|
nuclear@0
|
246 emitln("mov $%c, %%eax", get_num());
|
nuclear@0
|
247 }
|
nuclear@0
|
248 }
|
nuclear@0
|
249
|
nuclear@4
|
250 void ident(void)
|
nuclear@3
|
251 {
|
nuclear@3
|
252 char name = get_name();
|
nuclear@3
|
253 if(look == '(') {
|
nuclear@3
|
254 /* function call */
|
nuclear@3
|
255 match('(');
|
nuclear@3
|
256 match(')');
|
nuclear@3
|
257
|
nuclear@3
|
258 if(add_symbol(name, SYM_FUNC) == -1) {
|
nuclear@3
|
259 stop("function name '%c' previously defined as another type of symbol\n", name);
|
nuclear@3
|
260 }
|
nuclear@3
|
261 emitln("call %c", name);
|
nuclear@3
|
262 } else {
|
nuclear@3
|
263 /* variable */
|
nuclear@3
|
264 if(add_symbol(name, SYM_VAR) == -1) {
|
nuclear@3
|
265 stop("variable name '%c' previously defined as another type of symbol\n", name);
|
nuclear@3
|
266 }
|
nuclear@3
|
267 emitln("mov (%c), %%eax", name);
|
nuclear@3
|
268 }
|
nuclear@3
|
269 }
|
nuclear@3
|
270
|
nuclear@0
|
271 void add(void)
|
nuclear@0
|
272 {
|
nuclear@0
|
273 match('+');
|
nuclear@0
|
274 term();
|
nuclear@0
|
275 emitln("pop %%ebx");
|
nuclear@0
|
276 emitln("add %%ebx, %%eax");
|
nuclear@0
|
277 }
|
nuclear@0
|
278
|
nuclear@0
|
279 void sub(void)
|
nuclear@0
|
280 {
|
nuclear@0
|
281 match('-');
|
nuclear@0
|
282 term();
|
nuclear@0
|
283 emitln("pop %%ebx");
|
nuclear@0
|
284 emitln("sub %%ebx, %%eax");
|
nuclear@0
|
285 emitln("neg %%eax");
|
nuclear@0
|
286 }
|
nuclear@0
|
287
|
nuclear@0
|
288 void mul(void)
|
nuclear@0
|
289 {
|
nuclear@0
|
290 match('*');
|
nuclear@0
|
291 factor();
|
nuclear@0
|
292 emitln("pop %%ebx");
|
nuclear@0
|
293 emitln("imul %%ebx");
|
nuclear@0
|
294 }
|
nuclear@0
|
295
|
nuclear@0
|
296 void divide(void)
|
nuclear@0
|
297 {
|
nuclear@0
|
298 match('/');
|
nuclear@0
|
299 factor();
|
nuclear@0
|
300 emitln("mov %%eax, %%ebx");
|
nuclear@0
|
301 emitln("pop %%eax");
|
nuclear@0
|
302 emitln("idiv %%ebx");
|
nuclear@0
|
303 }
|
nuclear@0
|
304
|
nuclear@0
|
305 void get_char(void)
|
nuclear@0
|
306 {
|
nuclear@0
|
307 look = fgetc(infile);
|
nuclear@0
|
308 }
|
nuclear@0
|
309
|
nuclear@0
|
310 void match(char c)
|
nuclear@0
|
311 {
|
nuclear@0
|
312 if(look == c) {
|
nuclear@0
|
313 get_char();
|
nuclear@3
|
314 skip_white();
|
nuclear@0
|
315 } else {
|
nuclear@0
|
316 char s[2] = {0, 0};
|
nuclear@0
|
317 s[0] = c;
|
nuclear@0
|
318 expected(s);
|
nuclear@0
|
319 }
|
nuclear@0
|
320 }
|
nuclear@0
|
321
|
nuclear@0
|
322 char get_name(void)
|
nuclear@0
|
323 {
|
nuclear@0
|
324 char res;
|
nuclear@0
|
325 if(!isalpha(look)) {
|
nuclear@0
|
326 expected("name");
|
nuclear@0
|
327 }
|
nuclear@0
|
328 res = toupper(look);
|
nuclear@0
|
329 get_char();
|
nuclear@3
|
330 skip_white();
|
nuclear@0
|
331 return res;
|
nuclear@0
|
332 }
|
nuclear@0
|
333
|
nuclear@0
|
334 char get_num(void)
|
nuclear@0
|
335 {
|
nuclear@0
|
336 char res;
|
nuclear@0
|
337 if(!isdigit(look)) {
|
nuclear@0
|
338 expected("integer");
|
nuclear@0
|
339 }
|
nuclear@0
|
340 res = look;
|
nuclear@0
|
341 get_char();
|
nuclear@3
|
342 skip_white();
|
nuclear@0
|
343 return res;
|
nuclear@0
|
344 }
|
nuclear@0
|
345
|
nuclear@3
|
346 void skip_white(void)
|
nuclear@3
|
347 {
|
nuclear@3
|
348 while(isblank(look)) {
|
nuclear@3
|
349 get_char();
|
nuclear@3
|
350 }
|
nuclear@3
|
351 }
|
nuclear@3
|
352
|
nuclear@0
|
353 void stop(const char *s, ...)
|
nuclear@0
|
354 {
|
nuclear@0
|
355 va_list ap;
|
nuclear@0
|
356
|
nuclear@0
|
357 fprintf(stderr, "error: ");
|
nuclear@0
|
358 va_start(ap, s);
|
nuclear@0
|
359 vfprintf(stderr, s, ap);
|
nuclear@0
|
360 va_end(ap);
|
nuclear@0
|
361 fputc('\n', stderr);
|
nuclear@0
|
362
|
nuclear@0
|
363 abort();
|
nuclear@0
|
364 }
|
nuclear@0
|
365
|
nuclear@0
|
366 void expected(const char *s)
|
nuclear@0
|
367 {
|
nuclear@0
|
368 stop("%s expected", s);
|
nuclear@0
|
369 }
|
nuclear@0
|
370
|
nuclear@0
|
371 void emit(const char *fmt, ...)
|
nuclear@0
|
372 {
|
nuclear@0
|
373 va_list ap;
|
nuclear@0
|
374 va_start(ap, fmt);
|
nuclear@0
|
375 vemit(fmt, ap);
|
nuclear@0
|
376 va_end(ap);
|
nuclear@0
|
377 }
|
nuclear@0
|
378
|
nuclear@0
|
379 void vemit(const char *fmt, va_list ap)
|
nuclear@0
|
380 {
|
nuclear@0
|
381 fputc('\t', outfile);
|
nuclear@0
|
382 vfprintf(outfile, fmt, ap);
|
nuclear@0
|
383 }
|
nuclear@0
|
384
|
nuclear@0
|
385 void emitln(const char *fmt, ...)
|
nuclear@0
|
386 {
|
nuclear@0
|
387 va_list ap;
|
nuclear@0
|
388 va_start(ap, fmt);
|
nuclear@0
|
389 vemit(fmt, ap);
|
nuclear@0
|
390 va_end(ap);
|
nuclear@0
|
391 fputc('\n', outfile);
|
nuclear@0
|
392 }
|
nuclear@0
|
393
|
nuclear@0
|
394
|
nuclear@0
|
395 int is_addsub(char c)
|
nuclear@0
|
396 {
|
nuclear@0
|
397 return c == '+' || c == '-';
|
nuclear@0
|
398 }
|
nuclear@3
|
399
|
nuclear@3
|
400 int add_symbol(char c, int type)
|
nuclear@3
|
401 {
|
nuclear@3
|
402 struct symbol *sym = symlist;
|
nuclear@3
|
403 while(sym) {
|
nuclear@3
|
404 if(sym->name == c) {
|
nuclear@3
|
405 /* we already have it */
|
nuclear@3
|
406 return sym->type == type ? 0 : -1;
|
nuclear@3
|
407 }
|
nuclear@3
|
408 sym = sym->next;
|
nuclear@3
|
409 }
|
nuclear@3
|
410
|
nuclear@3
|
411 if(!(sym = malloc(sizeof *sym))) {
|
nuclear@3
|
412 perror("ICE: failed to allocate memory");
|
nuclear@3
|
413 abort();
|
nuclear@3
|
414 }
|
nuclear@3
|
415 sym->name = c;
|
nuclear@3
|
416 sym->type = type;
|
nuclear@3
|
417 sym->next = symlist;
|
nuclear@3
|
418 symlist = sym;
|
nuclear@3
|
419 return 0;
|
nuclear@3
|
420 }
|
nuclear@4
|
421
|
nuclear@4
|
422 int newlabel(void)
|
nuclear@4
|
423 {
|
nuclear@4
|
424 static int n;
|
nuclear@4
|
425 return n++;
|
nuclear@4
|
426 }
|
nuclear@4
|
427
|
nuclear@4
|
428 const char *labelstr(int lb)
|
nuclear@4
|
429 {
|
nuclear@4
|
430 static char buf[16];
|
nuclear@4
|
431 sprintf(buf, "L%02d", lb);
|
nuclear@4
|
432 return buf;
|
nuclear@4
|
433 }
|
nuclear@4
|
434
|
nuclear@4
|
435 void postlabel(int lb)
|
nuclear@4
|
436 {
|
nuclear@4
|
437 fprintf(outfile, "%s:\n", labelstr(lb));
|
nuclear@4
|
438 }
|