├── .clang-format ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── codegen_x64.c ├── dict.h ├── lexer.c ├── list.h ├── main.c ├── mzcc.h ├── parser.c ├── sample └── nqueen.c ├── tests ├── arith.c ├── array.c ├── comp.c ├── control.c ├── decl.c ├── driver.sh ├── float.c ├── function.c ├── global.c ├── long.c ├── pointer.c ├── pointer_arith.c ├── scope.c ├── struct.c └── union.c ├── util.h └── verbose.c /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Chromium 2 | Language: Cpp 3 | MaxEmptyLinesToKeep: 3 4 | IndentCaseLabels: false 5 | AllowShortIfStatementsOnASingleLine: false 6 | AllowShortCaseLabelsOnASingleLine: false 7 | AllowShortLoopsOnASingleLine: false 8 | DerivePointerAlignment: false 9 | PointerAlignment: Right 10 | SpaceAfterCStyleCast: true 11 | TabWidth: 4 12 | UseTab: Never 13 | IndentWidth: 4 14 | BreakBeforeBraces: Linux 15 | AccessModifierOffset: -4 16 | ForEachMacros: 17 | - foreach 18 | - Q_FOREACH 19 | - BOOST_FOREACH 20 | - list_for_each 21 | - list_for_each_safe 22 | - list_for_each_entry 23 | - list_for_each_entry_safe 24 | - hlist_for_each_entry 25 | - rb_list_foreach 26 | - rb_list_foreach_safe 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | mzcc 2 | *.o 3 | *.o.d 4 | tmp.* 5 | nqueen.s 6 | nqueen 7 | *.bin 8 | *.dSYM 9 | .cbuild 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019-2020 National Cheng Kung University, Taiwan. 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 22 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET = mzcc 2 | 3 | CFLAGS = -Wall -Werror -std=gnu99 -g -I. 4 | 5 | UNAME_S := $(shell uname -s) 6 | ifeq ($(UNAME_S),Linux) 7 | CFLAGS += -no-pie 8 | endif 9 | 10 | SHELL_HACK := $(shell echo CBUILD=\"$(CC) $(CFLAGS)\" > .cbuild) 11 | 12 | # Control the build verbosity 13 | # `make V=1` is equal to `make VERBOSE=1` 14 | ifeq ("$(origin V)", "command line") 15 | VERBOSE = $(V) 16 | endif 17 | ifeq ("$(VERBOSE)","1") 18 | Q := 19 | VECHO = @true 20 | else 21 | Q := @ 22 | VECHO = @printf 23 | endif 24 | 25 | OBJS = lexer.o codegen_x64.o parser.o verbose.o main.o 26 | deps := $(OBJS:%.o=.%.o.d) 27 | 28 | %.o: %.c 29 | $(VECHO) " CC\t$@\n" 30 | $(Q)$(CC) -o $@ $(CFLAGS) -c -MMD -MF .$@.d $< 31 | 32 | $(TARGET): $(OBJS) 33 | $(VECHO) " LD\t$@\n" 34 | $(Q)$(CC) $(CFLAGS) -o $@ $^ 35 | 36 | TESTS := $(patsubst %.c,%.bin,$(wildcard tests/*.c)) 37 | 38 | PASS_COLOR = \e[32;01m 39 | NO_COLOR = \e[0m 40 | pass = printf "[ $(PASS_COLOR)Passed$(NO_COLOR) ]\n" 41 | 42 | check: nqueen $(TESTS) 43 | @echo 44 | @for test in $(TESTS); do \ 45 | printf "*** verify $$test ***\n" ; \ 46 | head -n 1 `echo $$test | sed s/.bin/.c/`; \ 47 | ./$$test; \ 48 | $(call pass,$$test); \ 49 | echo; \ 50 | done 51 | tests/driver.sh 52 | 53 | tests/%.s: tests/%.c $(TARGET) 54 | ./mzcc -o $@ $< 55 | 56 | tests/%.bin: tests/%.s $(TARGET) 57 | $(VECHO) " CC\t$@\n" 58 | $(Q)$(CC) $(CFLAGS) -o $@ $< 59 | 60 | nqueen: sample/nqueen.c $(TARGET) 61 | $(VECHO) " MazuCC\t$<\n" 62 | $(Q)./mzcc -o ${<:.c=.s} $< 63 | $(VECHO) " AS+LD\t\t$@\n" 64 | $(Q)$(CC) $(CFLAGS) -o sample/nqueen sample/nqueen.s 65 | 66 | .PHONY: clean check 67 | clean: 68 | $(RM) $(TARGET) $(TESTS) $(OBJS) $(deps) .cbuild 69 | $(RM) sample/*.o sample/nqueen.s sample/nqueen 70 | 71 | -include $(deps) 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MazuCC 2 | 3 | MazuCC is a minimalist C compiler with x86_64 code generation. 4 | It is intended to support partial C99 language features while keeping the code 5 | as small and simple as possible. 6 | 7 | 8 | ## Build 9 | 10 | Run make to build: 11 | ```shell 12 | $ make 13 | ``` 14 | 15 | MazuCC comes with unit tests. To run the tests, give "check" as an argument: 16 | ```shell 17 | $ make check 18 | ``` 19 | 20 | MazuCC is known to work on both GNU/Linux and macOS. 21 | 22 | Use MazuCC to compile C source: 23 | ```shell 24 | $ ./mzcc sample/nqueen.c 25 | ``` 26 | 27 | Alternatively, MazuCC accepts the stream from standard input. The equivalent 28 | form for the above command is: 29 | 30 | ```shell 31 | $ cat sample/nqueen.c | ./mzcc - 32 | ``` 33 | 34 | You will get the generated x86_64 assembly in AT&T syntax. The output can be 35 | assembled and linked into a valid executable: 36 | ```shell 37 | $ ./mzcc -o sample/nqueen.s sample/nqueen.c 38 | $ gcc -no-pie -o sample/nqueen sample/nqueen.s 39 | ``` 40 | 41 | If MazuCC is compiled and executed on macOS, the above argument `-no-pie` 42 | should be eliminated. 43 | 44 | Reference output of MazuCC-compiled `sample/nqueen`: 45 | ``` 46 | Q . . . . . . . 47 | . . . . Q . . . 48 | . . . . . . . Q 49 | . . . . . Q . . 50 | . . Q . . . . . 51 | . . . . . . Q . 52 | . Q . . . . . . 53 | . . . Q . . . . 54 | ``` 55 | 56 | Alternatively, you can dump internal abstract syntax tree: 57 | ```shell 58 | echo 'struct {int x; char y; struct { int t; } z; } a;' | ./mzcc --dump-ast - 59 | ``` 60 | 61 | The expected output in S-expression form: 62 | ``` 63 | (decl (struct (int) 64 | (char) 65 | ((struct (int)))) a) 66 | ``` 67 | 68 | ## Acknowledge 69 | 70 | MazuCC is heavily inspired by [8cc](https://github.com/rui314/8cc). 71 | 72 | ## License 73 | 74 | MazuCC is freely redistributable under the BSD 2 clause license. Use of 75 | this source code is governed by a BSD-style license that can be found in the 76 | LICENSE file. 77 | -------------------------------------------------------------------------------- /codegen_x64.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "mzcc.h" 4 | 5 | static char *REGS[] = {"rdi", "rsi", "rdx", "rcx", "r8", "r9"}; 6 | static int TAB = 8; 7 | static List *functions = &EMPTY_LIST; 8 | 9 | /* FIXME: main program should take extern variables from codegen. */ 10 | FILE *outfp; 11 | static int stackpos; 12 | 13 | static void emit_expr(Ast *ast); 14 | static void emit_load_deref(Ctype *result_type, Ctype *operand_type, int off); 15 | 16 | #define emit(...) emitf(__LINE__, "\t" __VA_ARGS__) 17 | #define emit_label(...) emitf(__LINE__, __VA_ARGS__) 18 | 19 | #define UNUSED __attribute__((unused)) 20 | 21 | #define SAVE \ 22 | int save_hook __attribute__((cleanup(pop_function))) UNUSED; \ 23 | list_push(functions, (void *) __func__) 24 | 25 | static void pop_function(void *ignore UNUSED) 26 | { 27 | list_pop(functions); 28 | } 29 | 30 | static char *get_caller_list(void) 31 | { 32 | String s = make_string(); 33 | for (Iter i = list_iter(functions); !iter_end(i);) { 34 | string_appendf(&s, "%s", iter_next(&i)); 35 | if (!iter_end(i)) 36 | string_appendf(&s, " -> "); 37 | } 38 | return get_cstring(s); 39 | } 40 | 41 | static void emitf(int line, char *fmt, ...) 42 | { 43 | va_list args; 44 | va_start(args, fmt); 45 | int col = vfprintf(outfp, fmt, args); 46 | va_end(args); 47 | 48 | for (char *p = fmt; *p; p++) 49 | if (*p == '\t') 50 | col += TAB - 1; 51 | int space = (28 - col) > 0 ? (30 - col) : 2; 52 | fprintf(outfp, "%*c %s:%d\n", space, '#', get_caller_list(), line); 53 | } 54 | 55 | static char *get_int_reg(Ctype *ctype, char r) 56 | { 57 | assert(r == 'a' || r == 'c'); 58 | switch (ctype->size) { 59 | case 1: 60 | return (r == 'a') ? "al" : "cl"; 61 | case 4: 62 | return (r == 'a') ? "eax" : "ecx"; 63 | case 8: 64 | return (r == 'a') ? "rax" : "rcx"; 65 | default: 66 | error("Unknown data size: %s: %d", ctype_to_string(ctype), ctype->size); 67 | return 0; /* non-reachable */ 68 | } 69 | } 70 | 71 | static void push_xmm(int reg) 72 | { 73 | SAVE; 74 | emit("sub $8, %%rsp"); 75 | emit("movsd %%xmm%d, (%%rsp)", reg); 76 | stackpos += 8; 77 | } 78 | 79 | static void pop_xmm(int reg) 80 | { 81 | SAVE; 82 | emit("movsd (%%rsp), %%xmm%d", reg); 83 | emit("add $8, %%rsp"); 84 | stackpos -= 8; 85 | assert(stackpos >= 0); 86 | } 87 | 88 | static void push(char *reg) 89 | { 90 | SAVE; 91 | emit("push %%%s", reg); 92 | stackpos += 8; 93 | } 94 | 95 | static void pop(char *reg) 96 | { 97 | SAVE; 98 | emit("pop %%%s", reg); 99 | stackpos -= 8; 100 | assert(stackpos >= 0); 101 | } 102 | 103 | static void emit_gload(Ctype *ctype, char *label, int off) 104 | { 105 | SAVE; 106 | if (ctype->type == CTYPE_ARRAY) { 107 | if (off) 108 | emit("lea %s+%d(%%rip), %%rax", label, off); 109 | else 110 | emit("lea %s(%%rip), %%rax", label); 111 | return; 112 | } 113 | char *reg = get_int_reg(ctype, 'a'); 114 | if (ctype->size == 1) 115 | emit("mov $0, %%eax"); 116 | if (off) 117 | emit("mov %s+%d(%%rip), %%%s", label, off, reg); 118 | else 119 | emit("mov %s(%%rip), %%%s", label, reg); 120 | } 121 | 122 | static void emit_toint(Ctype *ctype) 123 | { 124 | SAVE; 125 | if (!is_flotype(ctype)) 126 | return; 127 | emit("cvttsd2si %%xmm0, %%eax"); 128 | } 129 | 130 | static void emit_todouble(Ctype *ctype) 131 | { 132 | SAVE; 133 | if (is_flotype(ctype)) 134 | return; 135 | emit("cvtsi2sd %%eax, %%xmm0"); 136 | } 137 | 138 | static void emit_lload(Ctype *ctype, int off) 139 | { 140 | SAVE; 141 | if (ctype->type == CTYPE_ARRAY) { 142 | emit("lea %d(%%rbp), %%rax", off); 143 | } else if (ctype->type == CTYPE_FLOAT) { 144 | emit("cvtps2pd %d(%%rbp), %%xmm0", off); 145 | } else if (ctype->type == CTYPE_DOUBLE) { 146 | emit("movsd %d(%%rbp), %%xmm0", off); 147 | } else { 148 | char *reg = get_int_reg(ctype, 'a'); 149 | if (ctype->size == 1) 150 | emit("mov $0, %%eax"); 151 | emit("mov %d(%%rbp), %%%s", off, reg); 152 | } 153 | } 154 | 155 | static void emit_gsave(char *varname, Ctype *ctype, int off) 156 | { 157 | SAVE; 158 | assert(ctype->type != CTYPE_ARRAY); 159 | char *reg = get_int_reg(ctype, 'a'); 160 | if (off) 161 | emit("mov %%%s, %s+%d(%%rip)", reg, varname, off); 162 | else 163 | emit("mov %%%s, %s(%%rip)", reg, varname); 164 | } 165 | 166 | static void emit_lsave(Ctype *ctype, int off) 167 | { 168 | SAVE; 169 | if (ctype->type == CTYPE_FLOAT) { 170 | emit("cvtpd2ps %%xmm0, %d(%%rbp)", off); 171 | } else if (ctype->type == CTYPE_DOUBLE) { 172 | emit("movsd %%xmm0, %d(%%rbp)", off); 173 | } else { 174 | char *reg = get_int_reg(ctype, 'a'); 175 | emit("mov %%%s, %d(%%rbp)", reg, off); 176 | } 177 | } 178 | 179 | static void emit_assign_deref_int(Ctype *ctype, int off) 180 | { 181 | SAVE; 182 | emit("mov (%%rsp), %%rcx"); 183 | char *reg = get_int_reg(ctype, 'c'); 184 | if (off) 185 | emit("mov %%%s, %d(%%rax)", reg, off); 186 | else 187 | emit("mov %%%s, (%%rax)", reg); 188 | pop("rax"); 189 | } 190 | 191 | static void emit_assign_deref(Ast *var) 192 | { 193 | SAVE; 194 | push("rax"); 195 | emit_expr(var->operand); 196 | emit_assign_deref_int(var->operand->ctype->ptr, 0); 197 | } 198 | 199 | static void emit_pointer_arith(char op UNUSED, Ast *left, Ast *right) 200 | { 201 | SAVE; 202 | emit_expr(left); 203 | push("rax"); 204 | emit_expr(right); 205 | int size = left->ctype->ptr->size; 206 | if (size > 1) 207 | emit("imul $%d, %%rax", size); 208 | emit("mov %%rax, %%rcx"); 209 | pop("rax"); 210 | emit("add %%rcx, %%rax"); 211 | } 212 | 213 | static void emit_assign_struct_ref(Ast *struc, Ctype *field, int off) 214 | { 215 | SAVE; 216 | switch (struc->type) { 217 | case AST_LVAR: 218 | emit_lsave(field, struc->loff + field->offset + off); 219 | break; 220 | case AST_GVAR: 221 | emit_gsave(struc->varname, field, field->offset + off); 222 | break; 223 | case AST_STRUCT_REF: 224 | emit_assign_struct_ref(struc->struc, field, off + struc->ctype->offset); 225 | break; 226 | case AST_DEREF: 227 | push("rax"); 228 | emit_expr(struc->operand); 229 | emit_assign_deref_int(field, field->offset + off); 230 | break; 231 | default: 232 | error("internal error: %s", ast_to_string(struc)); 233 | } 234 | } 235 | 236 | static void emit_load_struct_ref(Ast *struc, Ctype *field, int off) 237 | { 238 | SAVE; 239 | switch (struc->type) { 240 | case AST_LVAR: 241 | emit_lload(field, struc->loff + field->offset + off); 242 | break; 243 | case AST_GVAR: 244 | emit_gload(field, struc->varname, field->offset + off); 245 | break; 246 | case AST_STRUCT_REF: 247 | emit_load_struct_ref(struc->struc, field, struc->ctype->offset + off); 248 | break; 249 | case AST_DEREF: 250 | emit_expr(struc->operand); 251 | emit_load_deref(struc->ctype, field, field->offset + off); 252 | break; 253 | default: 254 | error("internal error: %s", ast_to_string(struc)); 255 | } 256 | } 257 | 258 | static void emit_assign(Ast *var) 259 | { 260 | SAVE; 261 | switch (var->type) { 262 | case AST_DEREF: 263 | emit_assign_deref(var); 264 | break; 265 | case AST_STRUCT_REF: 266 | emit_assign_struct_ref(var->struc, var->ctype, 0); 267 | break; 268 | case AST_LVAR: 269 | emit_lsave(var->ctype, var->loff); 270 | break; 271 | case AST_GVAR: 272 | emit_gsave(var->varname, var->ctype, 0); 273 | break; 274 | default: 275 | error("internal error"); 276 | } 277 | } 278 | 279 | static void emit_comp(char *inst, Ast *ast) 280 | { 281 | SAVE; 282 | if (is_flotype(ast->ctype)) { 283 | emit_expr(ast->left); 284 | emit_todouble(ast->left->ctype); 285 | push_xmm(0); 286 | emit_expr(ast->right); 287 | emit_todouble(ast->right->ctype); 288 | pop_xmm(1); 289 | emit("ucomisd %%xmm0, %%xmm1"); 290 | } else { 291 | emit_expr(ast->left); 292 | emit_toint(ast->left->ctype); 293 | push("rax"); 294 | emit_expr(ast->right); 295 | emit_toint(ast->right->ctype); 296 | pop("rcx"); 297 | emit("cmp %%rax, %%rcx"); 298 | } 299 | emit("%s %%al", inst); 300 | emit("movzb %%al, %%eax"); 301 | } 302 | 303 | static void emit_binop_int_arith(Ast *ast) 304 | { 305 | SAVE; 306 | char *op = NULL; 307 | switch (ast->type) { 308 | case '+': 309 | op = "add"; 310 | break; 311 | case '-': 312 | op = "sub"; 313 | break; 314 | case '*': 315 | op = "imul"; 316 | break; 317 | case '/': 318 | break; 319 | case PUNCT_LSHIFT: 320 | op = "sal"; 321 | break; 322 | case PUNCT_RSHIFT: 323 | op = "sar"; 324 | break; 325 | default: 326 | error("invalid operator '%d'", ast->type); 327 | } 328 | emit_expr(ast->left); 329 | emit_toint(ast->left->ctype); 330 | push("rax"); 331 | emit_expr(ast->right); 332 | emit_toint(ast->right->ctype); 333 | emit("mov %%rax, %%rcx"); 334 | pop("rax"); 335 | if (ast->type == '/') { 336 | emit("mov $0, %%edx"); 337 | emit("idiv %%rcx"); 338 | } else if (ast->type == PUNCT_LSHIFT || ast->type == PUNCT_RSHIFT) { 339 | emit("%s %%cl, %%rax", op); 340 | } else { 341 | emit("%s %%rcx, %%rax", op); 342 | } 343 | } 344 | 345 | static void emit_binop_float_arith(Ast *ast) 346 | { 347 | SAVE; 348 | char *op; 349 | switch (ast->type) { 350 | case '+': 351 | op = "addsd"; 352 | break; 353 | case '-': 354 | op = "subsd"; 355 | break; 356 | case '*': 357 | op = "mulsd"; 358 | break; 359 | case '/': 360 | op = "divsd"; 361 | break; 362 | default: 363 | error("invalid operator '%d'", ast->type); 364 | return; /* non-reachable */ 365 | } 366 | emit_expr(ast->left); 367 | emit_todouble(ast->left->ctype); 368 | push_xmm(0); 369 | emit_expr(ast->right); 370 | emit_todouble(ast->right->ctype); 371 | emit("movsd %%xmm0, %%xmm1"); 372 | pop_xmm(0); 373 | emit("%s %%xmm1, %%xmm0", op); 374 | } 375 | 376 | static void emit_binop(Ast *ast) 377 | { 378 | SAVE; 379 | if (ast->type == '=') { 380 | emit_expr(ast->right); 381 | if (is_flotype(ast->ctype)) 382 | emit_todouble(ast->right->ctype); 383 | else 384 | emit_toint(ast->right->ctype); 385 | emit_assign(ast->left); 386 | return; 387 | } 388 | if (ast->type == PUNCT_EQ) { 389 | emit_comp("sete", ast); 390 | return; 391 | } 392 | if (ast->ctype->type == CTYPE_PTR) { 393 | emit_pointer_arith(ast->type, ast->left, ast->right); 394 | return; 395 | } 396 | switch (ast->type) { 397 | case '<': 398 | emit_comp("setl", ast); 399 | return; 400 | case '>': 401 | emit_comp("setg", ast); 402 | return; 403 | } 404 | if (is_inttype(ast->ctype)) 405 | emit_binop_int_arith(ast); 406 | else if (is_flotype(ast->ctype)) 407 | emit_binop_float_arith(ast); 408 | else 409 | error("internal error"); 410 | } 411 | 412 | static void emit_inc_dec(Ast *ast, char *op) 413 | { 414 | SAVE; 415 | emit_expr(ast->operand); 416 | push("rax"); 417 | emit("%s $1, %%rax", op); 418 | emit_assign(ast->operand); 419 | pop("rax"); 420 | } 421 | 422 | static void emit_load_deref(Ctype *result_type, Ctype *operand_type, int off) 423 | { 424 | SAVE; 425 | if (operand_type->type == CTYPE_PTR && 426 | operand_type->ptr->type == CTYPE_ARRAY) 427 | return; 428 | char *reg = get_int_reg(result_type, 'c'); 429 | if (result_type->size == 1) 430 | emit("mov $0, %%ecx"); 431 | if (off) 432 | emit("mov %d(%%rax), %%%s", off, reg); 433 | else 434 | emit("mov (%%rax), %%%s", reg); 435 | emit("mov %%rcx, %%rax"); 436 | } 437 | 438 | static void emit_expr(Ast *ast) 439 | { 440 | SAVE; 441 | switch (ast->type) { 442 | case AST_LITERAL: 443 | switch (ast->ctype->type) { 444 | case CTYPE_CHAR: 445 | emit("mov $%d, %%rax", ast->ival); 446 | break; 447 | case CTYPE_INT: 448 | emit("mov $%d, %%eax", ast->ival); 449 | break; 450 | case CTYPE_LONG: 451 | emit("mov $%lu, %%rax", (unsigned long) ast->ival); 452 | break; 453 | case CTYPE_FLOAT: 454 | case CTYPE_DOUBLE: 455 | emit("movsd %s(%%rip), %%xmm0", ast->flabel); 456 | break; 457 | default: 458 | error("internal error"); 459 | } 460 | break; 461 | case AST_STRING: 462 | emit("lea %s(%%rip), %%rax", ast->slabel); 463 | break; 464 | case AST_LVAR: 465 | emit_lload(ast->ctype, ast->loff); 466 | break; 467 | case AST_GVAR: 468 | emit_gload(ast->ctype, ast->glabel, 0); 469 | break; 470 | case AST_FUNCALL: { 471 | int ireg = 0; 472 | int xreg = 0; 473 | for (Iter i = list_iter(ast->args); !iter_end(i);) { 474 | Ast *v = iter_next(&i); 475 | if (is_flotype(v->ctype)) 476 | push_xmm(xreg++); 477 | else 478 | push(REGS[ireg++]); 479 | } 480 | for (Iter i = list_iter(ast->args); !iter_end(i);) { 481 | Ast *v = iter_next(&i); 482 | emit_expr(v); 483 | if (is_flotype(v->ctype)) 484 | push_xmm(0); 485 | else 486 | push("rax"); 487 | } 488 | int ir = ireg; 489 | int xr = xreg; 490 | List *reverse = list_reverse(ast->args); 491 | for (Iter i = list_iter(reverse); !iter_end(i);) { 492 | Ast *v = iter_next(&i); 493 | if (is_flotype(v->ctype)) 494 | pop_xmm(--xr); 495 | else 496 | pop(REGS[--ir]); 497 | } 498 | emit("mov $%d, %%eax", xreg); 499 | if (stackpos % 16) 500 | emit("sub $8, %%rsp"); 501 | #ifdef __APPLE__ 502 | emit("call _%s", ast->fname); 503 | #else 504 | emit("call %s", ast->fname); 505 | #endif 506 | if (stackpos % 16) 507 | emit("add $8, %%rsp"); 508 | for (Iter i = list_iter(reverse); !iter_end(i);) { 509 | Ast *v = iter_next(&i); 510 | if (is_flotype(v->ctype)) 511 | pop_xmm(--xreg); 512 | else 513 | pop(REGS[--ireg]); 514 | } 515 | ListNode *node, *tmp; 516 | list_for_each_safe (node, tmp, reverse) 517 | free(node); 518 | free(reverse); 519 | break; 520 | } 521 | case AST_DECL: { 522 | if (!ast->declinit) 523 | return; 524 | if (ast->declinit->type == AST_ARRAY_INIT) { 525 | int off = 0; 526 | for (Iter iter = list_iter(ast->declinit->arrayinit); 527 | !iter_end(iter);) { 528 | emit_expr(iter_next(&iter)); 529 | emit_lsave(ast->declvar->ctype->ptr, ast->declvar->loff + off); 530 | off += ast->declvar->ctype->ptr->size; 531 | } 532 | } else if (ast->declvar->ctype->type == CTYPE_ARRAY) { 533 | assert(ast->declinit->type == AST_STRING); 534 | int i = 0; 535 | for (char *p = ast->declinit->sval; *p; p++, i++) 536 | emit("movb $%d, %d(%%rbp)", *p, ast->declvar->loff + i); 537 | emit("movb $0, %d(%%rbp)", ast->declvar->loff + i); 538 | } else if (ast->declinit->type == AST_STRING) { 539 | emit_gload(ast->declinit->ctype, ast->declinit->slabel, 0); 540 | emit_lsave(ast->declvar->ctype, ast->declvar->loff); 541 | } else { 542 | emit_expr(ast->declinit); 543 | emit_lsave(ast->declvar->ctype, ast->declvar->loff); 544 | } 545 | return; 546 | } 547 | case AST_ADDR: 548 | switch (ast->operand->type) { 549 | case AST_LVAR: 550 | emit("lea %d(%%rbp), %%rax", ast->operand->loff); 551 | break; 552 | case AST_GVAR: 553 | emit("lea %s(%%rip), %%rax", ast->operand->glabel); 554 | break; 555 | default: 556 | error("internal error"); 557 | } 558 | break; 559 | case AST_DEREF: 560 | emit_expr(ast->operand); 561 | emit_load_deref(ast->ctype, ast->operand->ctype, 0); 562 | break; 563 | case AST_IF: 564 | case AST_TERNARY: { 565 | emit_expr(ast->cond); 566 | char *ne = make_label(); 567 | emit("test %%rax, %%rax"); 568 | emit("je %s", ne); 569 | emit_expr(ast->then); 570 | if (ast->els) { 571 | char *end = make_label(); 572 | emit("jmp %s", end); 573 | emit("%s:", ne); 574 | emit_expr(ast->els); 575 | emit("%s:", end); 576 | } else { 577 | emit("%s:", ne); 578 | } 579 | break; 580 | } 581 | case AST_FOR: { 582 | if (ast->forinit) 583 | emit_expr(ast->forinit); 584 | char *begin = make_label(); 585 | char *end = make_label(); 586 | emit("%s:", begin); 587 | if (ast->forcond) { 588 | emit_expr(ast->forcond); 589 | emit("test %%rax, %%rax"); 590 | emit("je %s", end); 591 | } 592 | emit_expr(ast->forbody); 593 | if (ast->forstep) 594 | emit_expr(ast->forstep); 595 | emit("jmp %s", begin); 596 | emit("%s:", end); 597 | break; 598 | } 599 | case AST_RETURN: 600 | emit_expr(ast->retval); 601 | emit("leave"); 602 | emit("ret"); 603 | break; 604 | case AST_COMPOUND_STMT: 605 | for (Iter i = list_iter(ast->stmts); !iter_end(i);) { 606 | emit_expr(iter_next(&i)); 607 | emit("#;"); 608 | } 609 | break; 610 | case AST_STRUCT_REF: 611 | emit_load_struct_ref(ast->struc, ast->ctype, 0); 612 | break; 613 | case PUNCT_INC: 614 | emit_inc_dec(ast, "add"); 615 | break; 616 | case PUNCT_DEC: 617 | emit_inc_dec(ast, "sub"); 618 | break; 619 | case '!': 620 | emit_expr(ast->operand); 621 | emit("cmp $0, %%rax"); 622 | emit("sete %%al"); 623 | emit("movzb %%al, %%eax"); 624 | break; 625 | case '&': 626 | emit_expr(ast->left); 627 | push("rax"); 628 | emit_expr(ast->right); 629 | pop("rcx"); 630 | emit("and %%rcx, %%rax"); 631 | break; 632 | case '|': 633 | emit_expr(ast->left); 634 | push("rax"); 635 | emit_expr(ast->right); 636 | pop("rcx"); 637 | emit("or %%rcx, %%rax"); 638 | break; 639 | case PUNCT_LOGAND: { 640 | char *end = make_label(); 641 | emit_expr(ast->left); 642 | emit("test %%rax, %%rax"); 643 | emit("mov $0, %%rax"); 644 | emit("je %s", end); 645 | emit_expr(ast->right); 646 | emit("test %%rax, %%rax"); 647 | emit("mov $0, %%rax"); 648 | emit("je %s", end); 649 | emit("mov $1, %%rax"); 650 | emit("%s:", end); 651 | break; 652 | } 653 | case PUNCT_LOGOR: { 654 | char *end = make_label(); 655 | emit_expr(ast->left); 656 | emit("test %%rax, %%rax"); 657 | emit("mov $1, %%rax"); 658 | emit("jne %s", end); 659 | emit_expr(ast->right); 660 | emit("test %%rax, %%rax"); 661 | emit("mov $1, %%rax"); 662 | emit("jne %s", end); 663 | emit("mov $0, %%rax"); 664 | emit("%s:", end); 665 | break; 666 | } 667 | default: 668 | emit_binop(ast); 669 | } 670 | } 671 | 672 | static void emit_data_int(Ast *data) 673 | { 674 | SAVE; 675 | assert(data->ctype->type != CTYPE_ARRAY); 676 | switch (data->ctype->size) { 677 | case 1: 678 | emit(".byte %d", data->ival); 679 | break; 680 | case 4: 681 | emit(".long %d", data->ival); 682 | break; 683 | case 8: 684 | emit(".quad %d", data->ival); 685 | break; 686 | default: 687 | error("internal error"); 688 | } 689 | } 690 | 691 | static void emit_data(Ast *v) 692 | { 693 | SAVE; 694 | emit_label(".global %s", v->declvar->varname); 695 | emit_label("%s:", v->declvar->varname); 696 | if (v->declinit->type == AST_ARRAY_INIT) { 697 | for (Iter iter = list_iter(v->declinit->arrayinit); !iter_end(iter);) { 698 | emit_data_int(iter_next(&iter)); 699 | } 700 | return; 701 | } 702 | assert(v->declinit->type == AST_LITERAL && is_inttype(v->declinit->ctype)); 703 | emit_data_int(v->declinit); 704 | } 705 | 706 | static void emit_bss(Ast *v) 707 | { 708 | SAVE; 709 | emit(".lcomm %s, %d", v->declvar->varname, v->declvar->ctype->size); 710 | } 711 | 712 | static void emit_global_var(Ast *v) 713 | { 714 | SAVE; 715 | if (v->declinit) 716 | emit_data(v); 717 | else 718 | emit_bss(v); 719 | } 720 | 721 | void emit_data_section(void) 722 | { 723 | SAVE; 724 | emit(".data"); 725 | for (Iter i = list_iter(strings); !iter_end(i);) { 726 | Ast *v = iter_next(&i); 727 | emit_label("%s:", v->slabel); 728 | emit(".string \"%s\"", quote_cstring(v->sval)); 729 | } 730 | for (Iter i = list_iter(flonums); !iter_end(i);) { 731 | Ast *v = iter_next(&i); 732 | char *label = make_label(); 733 | v->flabel = label; 734 | emit_label("%s:", label); 735 | emit(".long %d", v->lval[0]); 736 | emit(".long %d", v->lval[1]); 737 | } 738 | } 739 | 740 | static int align(int n, int m) 741 | { 742 | int rem = n % m; 743 | return (rem == 0) ? n : n - rem + m; 744 | } 745 | 746 | static void emit_func_prologue(Ast *func) 747 | { 748 | SAVE; 749 | emit(".text"); 750 | #ifdef __APPLE__ 751 | emit_label(".global _%s", func->fname); 752 | emit_label("_%s:", func->fname); 753 | #else 754 | emit_label(".global %s", func->fname); 755 | emit_label("%s:", func->fname); 756 | #endif 757 | push("rbp"); 758 | emit("mov %%rsp, %%rbp"); 759 | int off = 0; 760 | int ireg = 0; 761 | int xreg = 0; 762 | for (Iter i = list_iter(func->params); !iter_end(i);) { 763 | Ast *v = iter_next(&i); 764 | if (v->ctype->type == CTYPE_FLOAT) { 765 | emit("cvtpd2ps %%xmm%d, %%xmm%d", xreg, xreg); 766 | push_xmm(xreg++); 767 | } else if (v->ctype->type == CTYPE_DOUBLE) { 768 | push_xmm(xreg++); 769 | } else { 770 | push(REGS[ireg++]); 771 | } 772 | off -= align(v->ctype->size, 8); 773 | v->loff = off; 774 | } 775 | for (Iter i = list_iter(func->localvars); !iter_end(i);) { 776 | Ast *v = iter_next(&i); 777 | off -= align(v->ctype->size, 8); 778 | v->loff = off; 779 | } 780 | if (off) 781 | emit("add $%d, %%rsp", off); 782 | stackpos += -(off - 8); 783 | } 784 | 785 | static void emit_func_epilogue(void) 786 | { 787 | SAVE; 788 | emit("leave"); 789 | emit("ret"); 790 | } 791 | 792 | void emit_toplevel(Ast *v) 793 | { 794 | stackpos = 0; 795 | if (v->type == AST_FUNC) { 796 | emit_func_prologue(v); 797 | emit_expr(v->body); 798 | emit_func_epilogue(); 799 | } else if (v->type == AST_DECL) { 800 | emit_global_var(v); 801 | } else { 802 | error("internal error"); 803 | } 804 | } 805 | -------------------------------------------------------------------------------- /dict.h: -------------------------------------------------------------------------------- 1 | #ifndef MAZUCC_DICT_H 2 | #define MAZUCC_DICT_H 3 | 4 | #include 5 | #include 6 | #include "list.h" 7 | 8 | typedef struct Dict { 9 | List *list; 10 | struct Dict *parent; 11 | } Dict; 12 | 13 | #define EMPTY_DICT ((Dict){&EMPTY_LIST, NULL}) 14 | 15 | typedef struct { 16 | char *key; 17 | void *val; 18 | } DictEntry; 19 | 20 | static inline void *make_dict(void *parent) 21 | { 22 | Dict *r = malloc(sizeof(Dict)); 23 | r->list = make_list(); 24 | r->parent = parent; 25 | return r; 26 | } 27 | 28 | static inline void *dict_get(Dict *dict, char *key) 29 | { 30 | for (; dict; dict = dict->parent) { 31 | for (Iter i = list_iter(dict->list); !iter_end(i);) { 32 | DictEntry *e = iter_next(&i); 33 | if (!strcmp(key, e->key)) 34 | return e->val; 35 | } 36 | } 37 | return NULL; 38 | } 39 | 40 | static inline void dict_put(Dict *dict, char *key, void *val) 41 | { 42 | DictEntry *e = malloc(sizeof(DictEntry)); 43 | e->key = key; 44 | e->val = val; 45 | list_push(dict->list, e); 46 | } 47 | 48 | static inline List *dict_keys(Dict *dict) 49 | { 50 | List *r = make_list(); 51 | for (; dict; dict = dict->parent) 52 | for (Iter i = list_iter(dict->list); !iter_end(i);) 53 | list_push(r, ((DictEntry *) iter_next(&i))->key); 54 | return r; 55 | } 56 | 57 | static inline List *dict_values(Dict *dict) 58 | { 59 | List *r = make_list(); 60 | for (; dict; dict = dict->parent) 61 | for (Iter i = list_iter(dict->list); !iter_end(i);) 62 | list_push(r, ((DictEntry *) iter_next(&i))->val); 63 | return r; 64 | } 65 | 66 | static inline void *dict_parent(Dict *dict) 67 | { 68 | void *r = dict->parent; 69 | list_free(dict->list); 70 | free(dict->list); 71 | free(dict); 72 | return r; 73 | } 74 | 75 | #endif /* MAZUCC_DICT_H */ 76 | -------------------------------------------------------------------------------- /lexer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "mzcc.h" 5 | 6 | #define make_null(x) make_token(TTYPE_NULL, (uintptr_t) 0) 7 | #define make_strtok(x) make_token(TTYPE_STRING, (uintptr_t) get_cstring(x)) 8 | #define make_ident(x) make_token(TTYPE_IDENT, (uintptr_t) get_cstring(x)) 9 | #define make_punct(x) make_token(TTYPE_PUNCT, (uintptr_t)(x)) 10 | #define make_number(x) make_token(TTYPE_NUMBER, (uintptr_t)(x)) 11 | #define make_char(x) make_token(TTYPE_CHAR, (uintptr_t)(x)) 12 | 13 | static bool ungotten = false; 14 | static Token ungotten_buf = {0}; 15 | 16 | static Token make_token(enum TokenType type, uintptr_t data) 17 | { 18 | return (Token){ 19 | .type = type, 20 | .priv = data, 21 | }; 22 | } 23 | 24 | static int getc_nonspace(void) 25 | { 26 | int c; 27 | while ((c = getc(stdin)) != EOF) { 28 | if (isspace(c) || c == '\n' || c == '\r') 29 | continue; 30 | return c; 31 | } 32 | return EOF; 33 | } 34 | 35 | static Token read_number(char c) 36 | { 37 | String s = make_string(); 38 | string_append(&s, c); 39 | while (1) { 40 | int c = getc(stdin); 41 | if (!isdigit(c) && !isalpha(c) && c != '.') { 42 | ungetc(c, stdin); 43 | return make_number(get_cstring(s)); 44 | } 45 | string_append(&s, c); 46 | } 47 | } 48 | 49 | static Token read_char(void) 50 | { 51 | char c = getc(stdin); 52 | if (c == EOF) 53 | goto err; 54 | if (c == '\\') { 55 | c = getc(stdin); 56 | if (c == EOF) 57 | goto err; 58 | } 59 | char c2 = getc(stdin); 60 | if (c2 == EOF) 61 | goto err; 62 | if (c2 != '\'') 63 | error("Malformed char literal"); 64 | return make_char(c); 65 | err: 66 | error("Unterminated char"); 67 | return make_null(); /* non-reachable */ 68 | } 69 | 70 | static Token read_string(void) 71 | { 72 | String s = make_string(); 73 | while (1) { 74 | int c = getc(stdin); 75 | if (c == EOF) 76 | error("Unterminated string"); 77 | if (c == '"') 78 | break; 79 | if (c == '\\') { 80 | c = getc(stdin); 81 | switch (c) { 82 | case EOF: 83 | error("Unterminated \\"); 84 | case '\"': 85 | break; 86 | case 'n': 87 | c = '\n'; 88 | break; 89 | default: 90 | error("Unknown quote: %c", c); 91 | } 92 | } 93 | string_append(&s, c); 94 | } 95 | return make_strtok(s); 96 | } 97 | 98 | static Token read_ident(char c) 99 | { 100 | String s = make_string(); 101 | string_append(&s, c); 102 | while (1) { 103 | int c2 = getc(stdin); 104 | if (isalnum(c2) || c2 == '_') { 105 | string_append(&s, c2); 106 | } else { 107 | ungetc(c2, stdin); 108 | return make_ident(s); 109 | } 110 | } 111 | } 112 | 113 | static void skip_line_comment(void) 114 | { 115 | while (1) { 116 | int c = getc(stdin); 117 | if (c == '\n' || c == EOF) 118 | return; 119 | } 120 | } 121 | 122 | static void skip_block_comment(void) 123 | { 124 | enum { in_comment, asterisk_read } state = in_comment; 125 | while (1) { 126 | int c = getc(stdin); 127 | if (state == in_comment) { 128 | if (c == '*') 129 | state = asterisk_read; 130 | } else if (c == '/') { 131 | return; 132 | } 133 | } 134 | } 135 | 136 | static Token read_rep(int expect, int t1, int t2) 137 | { 138 | int c = getc(stdin); 139 | if (c == expect) 140 | return make_punct(t2); 141 | ungetc(c, stdin); 142 | return make_punct(t1); 143 | } 144 | 145 | static Token read_token_int(void) 146 | { 147 | int c = getc_nonspace(); 148 | switch (c) { 149 | case '0' ... '9': 150 | return read_number(c); 151 | case 'a' ... 'z': 152 | case 'A' ... 'Z': 153 | case '_': 154 | return read_ident(c); 155 | case '/': { 156 | c = getc(stdin); 157 | if (c == '/') { 158 | skip_line_comment(); 159 | return read_token_int(); 160 | } 161 | if (c == '*') { 162 | skip_block_comment(); 163 | return read_token_int(); 164 | } 165 | ungetc(c, stdin); 166 | return make_punct('/'); 167 | } 168 | case '*': 169 | case '(': 170 | case ')': 171 | case ',': 172 | case ';': 173 | case '.': 174 | case '[': 175 | case ']': 176 | case '{': 177 | case '}': 178 | case '!': 179 | case '?': 180 | case ':': 181 | return make_punct(c); 182 | case '-': 183 | c = getc(stdin); 184 | if (c == '-') 185 | return make_punct(PUNCT_DEC); 186 | if (c == '>') 187 | return make_punct(PUNCT_ARROW); 188 | ungetc(c, stdin); 189 | return make_punct('-'); 190 | case '=': 191 | return read_rep('=', '=', PUNCT_EQ); 192 | case '+': 193 | return read_rep('+', '+', PUNCT_INC); 194 | case '&': 195 | return read_rep('&', '&', PUNCT_LOGAND); 196 | case '|': 197 | return read_rep('|', '|', PUNCT_LOGOR); 198 | case '<': 199 | return read_rep('<', '<', PUNCT_LSHIFT); 200 | case '>': 201 | return read_rep('>', '>', PUNCT_RSHIFT); 202 | case '"': 203 | return read_string(); 204 | case '\'': 205 | return read_char(); 206 | case EOF: 207 | return make_null(); 208 | default: 209 | error("Unexpected character: '%c'", c); 210 | return make_null(); /* non-reachable */ 211 | } 212 | } 213 | 214 | bool is_punct(const Token tok, int c) 215 | { 216 | return (get_ttype(tok) == TTYPE_PUNCT) && (get_punct(tok) == c); 217 | } 218 | 219 | void unget_token(const Token tok) 220 | { 221 | if (get_ttype(tok) == TTYPE_NULL) 222 | return; 223 | if (ungotten) 224 | error("Push back buffer is already full"); 225 | ungotten = true; 226 | ungotten_buf = make_token(tok.type, tok.priv); 227 | } 228 | 229 | Token peek_token(void) 230 | { 231 | Token tok = read_token(); 232 | unget_token(tok); 233 | return tok; 234 | } 235 | 236 | Token read_token(void) 237 | { 238 | if (ungotten) { 239 | ungotten = false; 240 | return make_token(ungotten_buf.type, ungotten_buf.priv); 241 | } 242 | return read_token_int(); 243 | } 244 | -------------------------------------------------------------------------------- /list.h: -------------------------------------------------------------------------------- 1 | #ifndef MAZUCC_LIST_H 2 | #define MAZUCC_LIST_H 3 | 4 | #include 5 | #include 6 | 7 | typedef struct __ListNode { 8 | void *elem; 9 | struct __ListNode *next, *prev; 10 | } ListNode; 11 | 12 | typedef struct { 13 | int len; 14 | ListNode *head, *tail; 15 | } List; 16 | 17 | typedef struct { 18 | ListNode *ptr; 19 | } Iter; 20 | 21 | #define EMPTY_LIST ((List){.len = 0, .head = NULL, .tail = NULL}) 22 | 23 | static inline List *make_list(void) 24 | { 25 | List *r = malloc(sizeof(List)); 26 | r->len = 0; 27 | r->head = r->tail = NULL; 28 | return r; 29 | } 30 | 31 | static inline void *make_node(void *elem) 32 | { 33 | ListNode *r = malloc(sizeof(ListNode)); 34 | r->elem = elem; 35 | r->next = NULL; 36 | r->prev = NULL; 37 | return r; 38 | } 39 | 40 | static inline void list_push(List *list, void *elem) 41 | { 42 | ListNode *node = make_node(elem); 43 | if (!list->head) { 44 | list->head = node; 45 | } else { 46 | list->tail->next = node; 47 | node->prev = list->tail; 48 | } 49 | list->tail = node; 50 | list->len++; 51 | } 52 | 53 | static inline void *list_pop(List *list) 54 | { 55 | if (!list->head) 56 | return NULL; 57 | ListNode *tail = list->tail; 58 | void *r = tail->elem; 59 | list->tail = tail->prev; 60 | if (list->tail) 61 | list->tail->next = NULL; 62 | else 63 | list->head = NULL; 64 | free(tail); 65 | return r; 66 | } 67 | 68 | static void list_unshift(List *list, void *elem) 69 | { 70 | ListNode *node = make_node(elem); 71 | node->next = list->head; 72 | if (list->head) 73 | list->head->prev = node; 74 | list->head = node; 75 | if (!list->tail) 76 | list->tail = node; 77 | list->len++; 78 | } 79 | 80 | static inline Iter list_iter(void *ptr) 81 | { 82 | return (Iter){ 83 | .ptr = ((List *) ptr)->head, 84 | }; 85 | } 86 | 87 | static inline bool iter_end(const Iter iter) 88 | { 89 | return !iter.ptr; 90 | } 91 | 92 | static inline void *iter_next(Iter *iter) 93 | { 94 | if (!iter->ptr) 95 | return NULL; 96 | void *r = iter->ptr->elem; 97 | iter->ptr = iter->ptr->next; 98 | return r; 99 | } 100 | 101 | static inline List *list_reverse(List *list) 102 | { 103 | List *r = make_list(); 104 | for (Iter i = list_iter(list); !iter_end(i);) 105 | list_unshift(r, iter_next(&i)); 106 | return r; 107 | } 108 | 109 | static inline int list_len(List *list) 110 | { 111 | return list->len; 112 | } 113 | 114 | #define list_safe_next(node) ((node) ? (node)->next : NULL) 115 | #define list_for_each_safe(node, tmp, list) \ 116 | for ((node) = (list)->head, (tmp) = list_safe_next(node); (node); \ 117 | (node) = (tmp), (tmp) = list_safe_next(node)) 118 | static inline void list_free(List *list) 119 | { 120 | ListNode *node, *tmp; 121 | list_for_each_safe (node, tmp, list) { 122 | free(node->elem); 123 | free(node); 124 | } 125 | } 126 | #endif /* MAZUCC_LIST_H */ 127 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "mzcc.h" 5 | 6 | static char *outfile = NULL, *infile = NULL; 7 | extern FILE *outfp; 8 | static bool dump_ast; 9 | 10 | static void usage() 11 | { 12 | fprintf(stdout, 13 | "mzcc [options] filename\n" 14 | "OPTIONS\n" 15 | " -o filename Write output to the specified file.\n" 16 | " --dump-ast Dump abstract syntax tree(AST)\n"); 17 | } 18 | 19 | static void print_usage_and_exit() 20 | { 21 | usage(); 22 | exit(1); 23 | } 24 | 25 | static void parse_args(int argc, char **argv) 26 | { 27 | if (argc < 2) { 28 | print_usage_and_exit(); 29 | } 30 | 31 | while (true) { 32 | argc--; 33 | argv++; 34 | if (!argc) { 35 | break; 36 | } 37 | 38 | if ((*argv)[0] == '-') { 39 | switch ((*argv)[1]) { 40 | case '\0': 41 | infile = "/dev/stdin"; 42 | break; 43 | case 'o': 44 | argc--; 45 | argv++; 46 | outfile = *argv; 47 | break; 48 | case '-': 49 | if (!strcmp(*argv, "--dump-ast")) { 50 | dump_ast = true; 51 | break; 52 | } 53 | default: 54 | print_usage_and_exit(); 55 | } 56 | } else { 57 | if (infile) { 58 | // The second non-option argument is not what we expect. 59 | print_usage_and_exit(); 60 | } 61 | infile = argv[0]; 62 | } 63 | } 64 | } 65 | 66 | static void open_output_file() 67 | { 68 | if (outfile) { 69 | if (!(outfp = fopen(outfile, "w"))) { 70 | printf("Can not open file %s\n", outfile); 71 | exit(1); 72 | } 73 | } else { 74 | outfp = stdout; 75 | } 76 | } 77 | 78 | static void open_input_file() 79 | { 80 | if (!infile) { 81 | printf("Input file is not specified\n\n"); 82 | print_usage_and_exit(); 83 | } 84 | 85 | if (!freopen(infile, "r", stdin)) { 86 | printf("Can not open file %s\n", infile); 87 | exit(1); 88 | } 89 | } 90 | 91 | int main(int argc, char **argv) 92 | { 93 | parse_args(argc, argv); 94 | open_input_file(); 95 | open_output_file(); 96 | 97 | List *toplevels = read_toplevels(); 98 | if (!dump_ast) 99 | emit_data_section(); 100 | 101 | for (Iter i = list_iter(toplevels); !iter_end(i);) { 102 | Ast *v = iter_next(&i); 103 | if (dump_ast) 104 | printf("%s", ast_to_string(v)); 105 | else 106 | emit_toplevel(v); 107 | } 108 | list_free(cstrings); 109 | list_free(ctypes); 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /mzcc.h: -------------------------------------------------------------------------------- 1 | #ifndef MAZUCC_H 2 | #define MAZUCC_H 3 | 4 | #include 5 | #include 6 | #include "dict.h" 7 | #include "list.h" 8 | #include "util.h" 9 | 10 | enum TokenType { 11 | TTYPE_NULL, 12 | TTYPE_IDENT, 13 | TTYPE_PUNCT, 14 | TTYPE_NUMBER, 15 | TTYPE_CHAR, 16 | TTYPE_STRING, 17 | }; 18 | 19 | typedef struct { 20 | int type; 21 | uintptr_t priv; 22 | } Token; 23 | 24 | enum { 25 | AST_LITERAL = 256, 26 | AST_STRING, 27 | AST_LVAR, 28 | AST_GVAR, 29 | AST_FUNCALL, 30 | AST_FUNC, 31 | AST_DECL, 32 | AST_ARRAY_INIT, 33 | AST_ADDR, 34 | AST_DEREF, 35 | AST_IF, 36 | AST_TERNARY, 37 | AST_FOR, 38 | AST_RETURN, 39 | AST_COMPOUND_STMT, 40 | AST_STRUCT_REF, 41 | PUNCT_EQ, 42 | PUNCT_INC, 43 | PUNCT_DEC, 44 | PUNCT_LOGAND, 45 | PUNCT_LOGOR, 46 | PUNCT_ARROW, 47 | PUNCT_LSHIFT, 48 | PUNCT_RSHIFT, 49 | }; 50 | 51 | enum { 52 | CTYPE_VOID, 53 | CTYPE_CHAR, 54 | CTYPE_INT, 55 | CTYPE_LONG, 56 | CTYPE_FLOAT, 57 | CTYPE_DOUBLE, 58 | CTYPE_ARRAY, 59 | CTYPE_PTR, 60 | CTYPE_STRUCT, 61 | }; 62 | 63 | typedef struct __Ctype { 64 | int type; 65 | int size; 66 | struct __Ctype *ptr; /* pointer or array */ 67 | int len; /* array length */ 68 | /* struct */ 69 | Dict *fields; 70 | int offset; 71 | } Ctype; 72 | 73 | typedef struct __Ast { 74 | int type; 75 | Ctype *ctype; 76 | union { 77 | /* char, int, or long */ 78 | long ival; 79 | 80 | /* float or double */ 81 | struct { 82 | union { 83 | double fval; 84 | int lval[2]; 85 | }; 86 | char *flabel; 87 | }; 88 | 89 | /* string literal */ 90 | struct { 91 | char *sval; 92 | char *slabel; 93 | }; 94 | 95 | /* Local/global variable */ 96 | struct { 97 | char *varname; 98 | struct { 99 | int loff; 100 | char *glabel; 101 | }; 102 | }; 103 | 104 | /* Binary operator */ 105 | struct { 106 | struct __Ast *left; 107 | struct __Ast *right; 108 | }; 109 | 110 | /* Unary operator */ 111 | struct { 112 | struct __Ast *operand; 113 | }; 114 | 115 | /* Function call or function declaration */ 116 | struct { 117 | char *fname; 118 | struct { 119 | List *args; 120 | struct { 121 | List *params; 122 | List *localvars; 123 | struct __Ast *body; 124 | }; 125 | }; 126 | }; 127 | 128 | /* Declaration */ 129 | struct { 130 | struct __Ast *declvar; 131 | struct __Ast *declinit; 132 | }; 133 | 134 | /* Array initializer */ 135 | List *arrayinit; 136 | 137 | /* if statement or ternary operator */ 138 | struct { 139 | struct __Ast *cond; 140 | struct __Ast *then; 141 | struct __Ast *els; 142 | }; 143 | 144 | /* for statement */ 145 | struct { 146 | struct __Ast *forinit; 147 | struct __Ast *forcond; 148 | struct __Ast *forstep; 149 | struct __Ast *forbody; 150 | }; 151 | 152 | /* return statement */ 153 | struct __Ast *retval; 154 | 155 | /* Compound statement */ 156 | List *stmts; 157 | 158 | /* Struct reference */ 159 | struct { 160 | struct __Ast *struc; 161 | char *field; /* specific to ast_to_string only */ 162 | }; 163 | }; 164 | } Ast; 165 | 166 | /* verbose.c */ 167 | extern char *token_to_string(const Token tok); 168 | extern char *ast_to_string(Ast *ast); 169 | extern char *ctype_to_string(Ctype *ctype); 170 | 171 | /* lexer.c */ 172 | extern bool is_punct(const Token tok, int c); 173 | extern void unget_token(const Token tok); 174 | extern Token peek_token(void); 175 | extern Token read_token(void); 176 | 177 | #define get_priv(tok, type) \ 178 | ({ \ 179 | assert(__builtin_types_compatible_p(typeof(tok), Token)); \ 180 | ((type) tok.priv); \ 181 | }) 182 | 183 | #define get_ttype(tok) \ 184 | ({ \ 185 | assert(__builtin_types_compatible_p(typeof(tok), Token)); \ 186 | (tok.type); \ 187 | }) 188 | 189 | #define get_token(tok, ttype, priv_type) \ 190 | ({ \ 191 | assert(get_ttype(tok) == ttype); \ 192 | get_priv(tok, priv_type); \ 193 | }) 194 | 195 | #define get_char(tok) get_token(tok, TTYPE_CHAR, char) 196 | #define get_strtok(tok) get_token(tok, TTYPE_STRING, char *) 197 | #define get_ident(tok) get_token(tok, TTYPE_IDENT, char *) 198 | #define get_number(tok) get_token(tok, TTYPE_NUMBER, char *) 199 | #define get_punct(tok) get_token(tok, TTYPE_PUNCT, int) 200 | 201 | /* parser.c */ 202 | extern List *strings; 203 | extern List *flonums; 204 | extern List *ctypes; 205 | extern char *make_label(void); 206 | extern List *read_toplevels(void); 207 | extern bool is_inttype(Ctype *ctype); 208 | extern bool is_flotype(Ctype *ctype); 209 | 210 | /* codegen_x64.c */ 211 | extern void emit_data_section(void); 212 | extern void emit_toplevel(Ast *v); 213 | 214 | #endif /* MAZUCC_H */ 215 | -------------------------------------------------------------------------------- /parser.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "mzcc.h" 8 | 9 | #define MAX_ARGS 6 10 | #define MAX_OP_PRIO 16 11 | #define MAX_ALIGN 16 12 | 13 | List *ctypes = &EMPTY_LIST; 14 | List *strings = &EMPTY_LIST; 15 | List *flonums = &EMPTY_LIST; 16 | 17 | static Dict *globalenv = &EMPTY_DICT; 18 | static Dict *localenv = NULL; 19 | static Dict *struct_defs = &EMPTY_DICT; 20 | static Dict *union_defs = &EMPTY_DICT; 21 | static List *localvars = NULL; 22 | 23 | static Ctype *ctype_void = &(Ctype){CTYPE_VOID, 0, NULL}; 24 | static Ctype *ctype_int = &(Ctype){CTYPE_INT, 4, NULL}; 25 | static Ctype *ctype_long = &(Ctype){CTYPE_LONG, 8, NULL}; 26 | static Ctype *ctype_char = &(Ctype){CTYPE_CHAR, 1, NULL}; 27 | static Ctype *ctype_float = &(Ctype){CTYPE_FLOAT, 4, NULL}; 28 | static Ctype *ctype_double = &(Ctype){CTYPE_DOUBLE, 8, NULL}; 29 | 30 | static int labelseq = 0; 31 | 32 | static Ast *read_expr(void); 33 | static Ctype *make_ptr_type(Ctype *ctype); 34 | static Ctype *make_array_type(Ctype *ctype, int size); 35 | static Ast *read_compound_stmt(void); 36 | static Ast *read_decl_or_stmt(void); 37 | static Ctype *result_type(char op, Ctype *a, Ctype *b); 38 | static Ctype *convert_array(Ctype *ctype); 39 | static Ast *read_stmt(void); 40 | static Ctype *read_decl_int(Token *name); 41 | 42 | static Ast *ast_uop(int type, Ctype *ctype, Ast *operand) 43 | { 44 | Ast *r = malloc(sizeof(Ast)); 45 | r->type = type; 46 | r->ctype = ctype; 47 | r->operand = operand; 48 | return r; 49 | } 50 | 51 | static Ast *ast_binop(int type, Ast *left, Ast *right) 52 | { 53 | Ast *r = malloc(sizeof(Ast)); 54 | r->type = type; 55 | r->ctype = result_type(type, left->ctype, right->ctype); 56 | if (type != '=' && convert_array(left->ctype)->type != CTYPE_PTR && 57 | convert_array(right->ctype)->type == CTYPE_PTR) { 58 | r->left = right; 59 | r->right = left; 60 | } else { 61 | r->left = left; 62 | r->right = right; 63 | } 64 | return r; 65 | } 66 | 67 | static Ast *ast_inttype(Ctype *ctype, long val) 68 | { 69 | Ast *r = malloc(sizeof(Ast)); 70 | r->type = AST_LITERAL; 71 | r->ctype = ctype; 72 | r->ival = val; 73 | return r; 74 | } 75 | 76 | static Ast *ast_double(double val) 77 | { 78 | Ast *r = malloc(sizeof(Ast)); 79 | r->type = AST_LITERAL; 80 | r->ctype = ctype_double; 81 | r->fval = val; 82 | list_push(flonums, r); 83 | return r; 84 | } 85 | 86 | char *make_label(void) 87 | { 88 | String s = make_string(); 89 | string_appendf(&s, ".L%d", labelseq++); 90 | return get_cstring(s); 91 | } 92 | 93 | static Ast *ast_lvar(Ctype *ctype, char *name) 94 | { 95 | Ast *r = malloc(sizeof(Ast)); 96 | r->type = AST_LVAR; 97 | r->ctype = ctype; 98 | r->varname = name; 99 | dict_put(localenv, name, r); 100 | if (localvars) 101 | list_push(localvars, r); 102 | return r; 103 | } 104 | 105 | static Ast *ast_gvar(Ctype *ctype, char *name, bool filelocal) 106 | { 107 | Ast *r = malloc(sizeof(Ast)); 108 | r->type = AST_GVAR; 109 | r->ctype = ctype; 110 | r->varname = name; 111 | r->glabel = filelocal ? make_label() : name; 112 | dict_put(globalenv, name, r); 113 | return r; 114 | } 115 | 116 | static Ast *ast_string(char *str) 117 | { 118 | Ast *r = malloc(sizeof(Ast)); 119 | r->type = AST_STRING; 120 | r->ctype = make_array_type(ctype_char, strlen(str) + 1); 121 | r->sval = str; 122 | r->slabel = make_label(); 123 | return r; 124 | } 125 | 126 | static Ast *ast_funcall(Ctype *ctype, char *fname, List *args) 127 | { 128 | Ast *r = malloc(sizeof(Ast)); 129 | r->type = AST_FUNCALL; 130 | r->ctype = ctype; 131 | r->fname = fname; 132 | r->args = args; 133 | return r; 134 | } 135 | 136 | static Ast *ast_func(Ctype *rettype, 137 | char *fname, 138 | List *params, 139 | Ast *body, 140 | List *localvars) 141 | { 142 | Ast *r = malloc(sizeof(Ast)); 143 | r->type = AST_FUNC; 144 | r->ctype = rettype; 145 | r->fname = fname; 146 | r->params = params; 147 | r->localvars = localvars; 148 | r->body = body; 149 | return r; 150 | } 151 | 152 | static Ast *ast_decl(Ast *var, Ast *init) 153 | { 154 | Ast *r = malloc(sizeof(Ast)); 155 | r->type = AST_DECL; 156 | r->ctype = NULL; 157 | r->declvar = var; 158 | r->declinit = init; 159 | return r; 160 | } 161 | 162 | static Ast *ast_array_init(List *arrayinit) 163 | { 164 | Ast *r = malloc(sizeof(Ast)); 165 | r->type = AST_ARRAY_INIT; 166 | r->ctype = NULL; 167 | r->arrayinit = arrayinit; 168 | return r; 169 | } 170 | 171 | static Ast *ast_if(Ast *cond, Ast *then, Ast *els) 172 | { 173 | Ast *r = malloc(sizeof(Ast)); 174 | r->type = AST_IF; 175 | r->ctype = NULL; 176 | r->cond = cond; 177 | r->then = then; 178 | r->els = els; 179 | return r; 180 | } 181 | 182 | static Ast *ast_ternary(Ctype *ctype, Ast *cond, Ast *then, Ast *els) 183 | { 184 | Ast *r = malloc(sizeof(Ast)); 185 | r->type = AST_TERNARY; 186 | r->ctype = ctype; 187 | r->cond = cond; 188 | r->then = then; 189 | r->els = els; 190 | return r; 191 | } 192 | 193 | static Ast *ast_for(Ast *init, Ast *cond, Ast *step, Ast *body) 194 | { 195 | Ast *r = malloc(sizeof(Ast)); 196 | r->type = AST_FOR; 197 | r->ctype = NULL; 198 | r->forinit = init; 199 | r->forcond = cond; 200 | r->forstep = step; 201 | r->forbody = body; 202 | return r; 203 | } 204 | 205 | static Ast *ast_return(Ast *retval) 206 | { 207 | Ast *r = malloc(sizeof(Ast)); 208 | r->type = AST_RETURN; 209 | r->ctype = NULL; 210 | r->retval = retval; 211 | return r; 212 | } 213 | 214 | static Ast *ast_compound_stmt(List *stmts) 215 | { 216 | Ast *r = malloc(sizeof(Ast)); 217 | r->type = AST_COMPOUND_STMT; 218 | r->ctype = NULL; 219 | r->stmts = stmts; 220 | return r; 221 | } 222 | 223 | static Ast *ast_struct_ref(Ctype *ctype, Ast *struc, char *name) 224 | { 225 | Ast *r = malloc(sizeof(Ast)); 226 | r->type = AST_STRUCT_REF; 227 | r->ctype = ctype; 228 | r->struc = struc; 229 | r->field = name; 230 | return r; 231 | } 232 | 233 | static Ctype *make_ptr_type(Ctype *ctype) 234 | { 235 | Ctype *r = malloc(sizeof(Ctype)); 236 | r->type = CTYPE_PTR; 237 | r->ptr = ctype; 238 | r->size = 8; 239 | list_push(ctypes, r); 240 | return r; 241 | } 242 | 243 | static Ctype *make_array_type(Ctype *ctype, int len) 244 | { 245 | Ctype *r = malloc(sizeof(Ctype)); 246 | r->type = CTYPE_ARRAY; 247 | r->ptr = ctype; 248 | r->size = (len < 0) ? -1 : ctype->size * len; 249 | r->len = len; 250 | list_push(ctypes, r); 251 | return r; 252 | } 253 | 254 | static Ctype *make_struct_field_type(Ctype *ctype, int offset) 255 | { 256 | Ctype *r = malloc(sizeof(Ctype)); 257 | memcpy(r, ctype, sizeof(Ctype)); 258 | r->offset = offset; 259 | list_push(ctypes, r); 260 | return r; 261 | } 262 | 263 | static Ctype *make_struct_type(Dict *fields, int size) 264 | { 265 | Ctype *r = malloc(sizeof(Ctype)); 266 | r->type = CTYPE_STRUCT; 267 | r->fields = fields; 268 | r->size = size; 269 | list_push(ctypes, r); 270 | return r; 271 | } 272 | 273 | bool is_inttype(Ctype *ctype) 274 | { 275 | return ctype->type == CTYPE_CHAR || ctype->type == CTYPE_INT || 276 | ctype->type == CTYPE_LONG; 277 | } 278 | 279 | bool is_flotype(Ctype *ctype) 280 | { 281 | return ctype->type == CTYPE_FLOAT || ctype->type == CTYPE_DOUBLE; 282 | } 283 | 284 | static void ensure_lvalue(Ast *ast) 285 | { 286 | switch (ast->type) { 287 | case AST_LVAR: 288 | case AST_GVAR: 289 | case AST_DEREF: 290 | case AST_STRUCT_REF: 291 | return; 292 | default: 293 | error("lvalue expected, but got %s", ast_to_string(ast)); 294 | } 295 | } 296 | 297 | static void expect(char punct) 298 | { 299 | Token tok = read_token(); 300 | if (!is_punct(tok, punct)) 301 | error("'%c' expected, but got %s", punct, token_to_string(tok)); 302 | } 303 | 304 | static bool is_ident(const Token tok, char *s) 305 | { 306 | return get_ttype(tok) == TTYPE_IDENT && !strcmp(get_ident(tok), s); 307 | } 308 | 309 | static bool is_right_assoc(const Token tok) 310 | { 311 | return get_punct(tok) == '='; 312 | } 313 | 314 | static int eval_intexpr(Ast *ast) 315 | { 316 | switch (ast->type) { 317 | case AST_LITERAL: 318 | if (is_inttype(ast->ctype)) 319 | return ast->ival; 320 | error("Integer expression expected, but got %s", ast_to_string(ast)); 321 | case '+': 322 | return eval_intexpr(ast->left) + eval_intexpr(ast->right); 323 | case '-': 324 | return eval_intexpr(ast->left) - eval_intexpr(ast->right); 325 | case '*': 326 | return eval_intexpr(ast->left) * eval_intexpr(ast->right); 327 | case '/': 328 | return eval_intexpr(ast->left) / eval_intexpr(ast->right); 329 | case PUNCT_LSHIFT: 330 | return eval_intexpr(ast->left) << eval_intexpr(ast->right); 331 | case PUNCT_RSHIFT: 332 | return eval_intexpr(ast->left) >> eval_intexpr(ast->right); 333 | default: 334 | error("Integer expression expected, but got %s", ast_to_string(ast)); 335 | return 0; /* non-reachable */ 336 | } 337 | } 338 | 339 | static int priority(const Token tok) 340 | { 341 | switch (get_punct(tok)) { 342 | case '[': 343 | case '.': 344 | case PUNCT_ARROW: 345 | return 1; 346 | case PUNCT_INC: 347 | case PUNCT_DEC: 348 | return 2; 349 | case '*': 350 | case '/': 351 | return 3; 352 | case '+': 353 | case '-': 354 | return 4; 355 | case PUNCT_LSHIFT: 356 | case PUNCT_RSHIFT: 357 | return 5; 358 | case '<': 359 | case '>': 360 | return 6; 361 | case '&': 362 | return 8; 363 | case '|': 364 | return 10; 365 | case PUNCT_EQ: 366 | return 7; 367 | case PUNCT_LOGAND: 368 | return 11; 369 | case PUNCT_LOGOR: 370 | return 12; 371 | case '?': 372 | return 13; 373 | case '=': 374 | return 14; 375 | default: 376 | return -1; 377 | } 378 | } 379 | 380 | static Ast *read_func_args(char *fname) 381 | { 382 | List *args = make_list(); 383 | while (1) { 384 | Token tok = read_token(); 385 | if (is_punct(tok, ')')) 386 | break; 387 | unget_token(tok); 388 | list_push(args, read_expr()); 389 | tok = read_token(); 390 | if (is_punct(tok, ')')) 391 | break; 392 | if (!is_punct(tok, ',')) 393 | error("Unexpected token: '%s'", token_to_string(tok)); 394 | } 395 | if (MAX_ARGS < list_len(args)) 396 | error("Too many arguments: %s", fname); 397 | return ast_funcall(ctype_int, fname, args); 398 | } 399 | 400 | static Ast *read_ident_or_func(char *name) 401 | { 402 | Token tok = read_token(); 403 | if (is_punct(tok, '(')) 404 | return read_func_args(name); 405 | unget_token(tok); 406 | Ast *v = dict_get(localenv, name); 407 | if (!v) 408 | error("Undefined varaible: %s", name); 409 | return v; 410 | } 411 | 412 | static bool is_long_token(char *p) 413 | { 414 | for (; *p; p++) { 415 | if (!isdigit(*p)) 416 | return (*p == 'L' || *p == 'l') && p[1] == '\0'; 417 | } 418 | return false; 419 | } 420 | 421 | static bool is_int_token(char *p) 422 | { 423 | for (; *p; p++) 424 | if (!isdigit(*p)) 425 | return false; 426 | return true; 427 | } 428 | 429 | static bool is_float_token(char *p) 430 | { 431 | for (; *p; p++) 432 | if (!isdigit(*p)) 433 | break; 434 | if (*p++ != '.') 435 | return false; 436 | for (; *p; p++) 437 | if (!isdigit(*p)) 438 | return false; 439 | return true; 440 | } 441 | 442 | static Ast *read_prim(void) 443 | { 444 | Token tok = read_token(); 445 | switch (get_ttype(tok)) { 446 | case TTYPE_NULL: 447 | return NULL; 448 | case TTYPE_IDENT: 449 | return read_ident_or_func(get_ident(tok)); 450 | case TTYPE_NUMBER: { 451 | char *number = get_number(tok); 452 | if (is_long_token(number)) 453 | return ast_inttype(ctype_long, atol(number)); 454 | if (is_int_token(number)) { 455 | long val = atol(number); 456 | if (val & ~(long) UINT_MAX) 457 | return ast_inttype(ctype_long, val); 458 | return ast_inttype(ctype_int, val); 459 | } 460 | if (is_float_token(number)) 461 | return ast_double(atof(number)); 462 | error("Malformed number: %s", token_to_string(tok)); 463 | } 464 | case TTYPE_CHAR: 465 | return ast_inttype(ctype_char, get_char(tok)); 466 | case TTYPE_STRING: { 467 | Ast *r = ast_string(get_strtok(tok)); 468 | list_push(strings, r); 469 | return r; 470 | } 471 | case TTYPE_PUNCT: 472 | unget_token(tok); 473 | return NULL; 474 | default: 475 | error("internal error: unknown token type: %d", get_ttype(tok)); 476 | return NULL; /* non-reachable */ 477 | } 478 | } 479 | 480 | #define swap(a, b) \ 481 | { \ 482 | typeof(a) tmp = b; \ 483 | b = a; \ 484 | a = tmp; \ 485 | } 486 | 487 | static Ctype *result_type_int(jmp_buf *jmpbuf, char op, Ctype *a, Ctype *b) 488 | { 489 | if (a->type > b->type) 490 | swap(a, b); 491 | if (b->type == CTYPE_PTR) { 492 | if (op == '=') 493 | return a; 494 | if (op != '+' && op != '-') 495 | goto err; 496 | if (!is_inttype(a)) 497 | goto err; 498 | return b; 499 | } 500 | switch (a->type) { 501 | case CTYPE_VOID: 502 | goto err; 503 | case CTYPE_CHAR: 504 | case CTYPE_INT: 505 | switch (b->type) { 506 | case CTYPE_CHAR: 507 | case CTYPE_INT: 508 | return ctype_int; 509 | case CTYPE_LONG: 510 | return ctype_long; 511 | case CTYPE_FLOAT: 512 | case CTYPE_DOUBLE: 513 | return ctype_double; 514 | case CTYPE_ARRAY: 515 | case CTYPE_PTR: 516 | return b; 517 | } 518 | error("internal error"); 519 | case CTYPE_LONG: 520 | switch (b->type) { 521 | case CTYPE_LONG: 522 | return ctype_long; 523 | case CTYPE_FLOAT: 524 | case CTYPE_DOUBLE: 525 | return ctype_double; 526 | case CTYPE_ARRAY: 527 | case CTYPE_PTR: 528 | return b; 529 | } 530 | error("internal error"); 531 | case CTYPE_FLOAT: 532 | if (b->type == CTYPE_FLOAT || b->type == CTYPE_DOUBLE) 533 | return ctype_double; 534 | goto err; 535 | case CTYPE_DOUBLE: 536 | if (b->type == CTYPE_DOUBLE) 537 | return ctype_double; 538 | goto err; 539 | case CTYPE_ARRAY: 540 | if (b->type != CTYPE_ARRAY) 541 | goto err; 542 | return result_type_int(jmpbuf, op, a->ptr, b->ptr); 543 | default: 544 | error("internal error: %s %s", ctype_to_string(a), ctype_to_string(b)); 545 | } 546 | err: 547 | longjmp(*jmpbuf, 1); 548 | } 549 | 550 | static Ast *read_subscript_expr(Ast *ast) 551 | { 552 | Ast *sub = read_expr(); 553 | expect(']'); 554 | Ast *t = ast_binop('+', ast, sub); 555 | return ast_uop(AST_DEREF, t->ctype->ptr, t); 556 | } 557 | 558 | static Ctype *convert_array(Ctype *ctype) 559 | { 560 | if (ctype->type != CTYPE_ARRAY) 561 | return ctype; 562 | return make_ptr_type(ctype->ptr); 563 | } 564 | 565 | static Ctype *result_type(char op, Ctype *a, Ctype *b) 566 | { 567 | jmp_buf jmpbuf; 568 | if (setjmp(jmpbuf) == 0) 569 | return result_type_int(&jmpbuf, op, convert_array(a), convert_array(b)); 570 | error("incompatible operands: %c: <%s> and <%s>", op, ctype_to_string(a), 571 | ctype_to_string(b)); 572 | return NULL; /* non-reachable */ 573 | } 574 | 575 | static Ast *read_unary_expr(void) 576 | { 577 | Token tok = read_token(); 578 | if (get_ttype(tok) != TTYPE_PUNCT) { 579 | unget_token(tok); 580 | return read_prim(); 581 | } 582 | if (is_punct(tok, '(')) { 583 | Ast *r = read_expr(); 584 | expect(')'); 585 | return r; 586 | } 587 | if (is_punct(tok, '&')) { 588 | Ast *operand = read_unary_expr(); 589 | ensure_lvalue(operand); 590 | return ast_uop(AST_ADDR, make_ptr_type(operand->ctype), operand); 591 | } 592 | if (is_punct(tok, '!')) { 593 | Ast *operand = read_unary_expr(); 594 | return ast_uop('!', ctype_int, operand); 595 | } 596 | if (is_punct(tok, '*')) { 597 | Ast *operand = read_unary_expr(); 598 | Ctype *ctype = convert_array(operand->ctype); 599 | if (ctype->type != CTYPE_PTR) 600 | error("pointer type expected, but got %s", ast_to_string(operand)); 601 | if (ctype->ptr == ctype_void) 602 | error("pointer to void can not be dereferenced, but got %s", 603 | ast_to_string(operand)); 604 | return ast_uop(AST_DEREF, operand->ctype->ptr, operand); 605 | } 606 | unget_token(tok); 607 | return read_prim(); 608 | } 609 | 610 | static Ast *read_cond_expr(Ast *cond) 611 | { 612 | Ast *then = read_expr(); 613 | expect(':'); 614 | Ast *els = read_expr(); 615 | return ast_ternary(then->ctype, cond, then, els); 616 | } 617 | 618 | static Ast *read_struct_field(Ast *struc) 619 | { 620 | if (struc->ctype->type != CTYPE_STRUCT) 621 | error("struct expected, but got %s", ast_to_string(struc)); 622 | Token name = read_token(); 623 | if (get_ttype(name) != TTYPE_IDENT) 624 | error("field name expected, but got %s", token_to_string(name)); 625 | char *ident = get_ident(name); 626 | Ctype *field = dict_get(struc->ctype->fields, ident); 627 | return ast_struct_ref(field, struc, ident); 628 | } 629 | 630 | static Ast *read_expr_int(int prec) 631 | { 632 | Ast *ast = read_unary_expr(); 633 | if (!ast) 634 | return NULL; 635 | while (1) { 636 | Token tok = read_token(); 637 | if (get_ttype(tok) != TTYPE_PUNCT) { 638 | unget_token(tok); 639 | return ast; 640 | } 641 | int prec2 = priority(tok); 642 | if (prec2 < 0 || prec <= prec2) { 643 | unget_token(tok); 644 | return ast; 645 | } 646 | if (is_punct(tok, '?')) { 647 | ast = read_cond_expr(ast); 648 | continue; 649 | } 650 | if (is_punct(tok, '.')) { 651 | ast = read_struct_field(ast); 652 | continue; 653 | } 654 | if (is_punct(tok, PUNCT_ARROW)) { 655 | if (ast->ctype->type != CTYPE_PTR) 656 | error("pointer type expected, but got %s %s", 657 | ctype_to_string(ast->ctype), ast_to_string(ast)); 658 | ast = ast_uop(AST_DEREF, ast->ctype->ptr, ast); 659 | ast = read_struct_field(ast); 660 | continue; 661 | } 662 | if (is_punct(tok, '[')) { 663 | ast = read_subscript_expr(ast); 664 | continue; 665 | } 666 | // this is BUG!! ++ should be in read_unary_expr() , I think. 667 | if (is_punct(tok, PUNCT_INC) || is_punct(tok, PUNCT_DEC)) { 668 | ensure_lvalue(ast); 669 | ast = ast_uop(get_punct(tok), ast->ctype, ast); 670 | continue; 671 | } 672 | if (is_punct(tok, '=')) 673 | ensure_lvalue(ast); 674 | Ast *rest = read_expr_int(prec2 + (is_right_assoc(tok) ? 1 : 0)); 675 | if (!rest) 676 | error("second operand missing"); 677 | if (is_punct(tok, PUNCT_LSHIFT) || is_punct(tok, PUNCT_RSHIFT)) { 678 | if ((ast->ctype != ctype_int && ast->ctype != ctype_char) || 679 | (rest->ctype != ctype_int && rest->ctype != ctype_char)) 680 | error("invalid operand to shift"); 681 | } 682 | ast = ast_binop(get_punct(tok), ast, rest); 683 | } 684 | } 685 | 686 | static Ast *read_expr(void) 687 | { 688 | return read_expr_int(MAX_OP_PRIO); 689 | } 690 | 691 | static Ctype *get_ctype(const Token tok) 692 | { 693 | if (get_ttype(tok) != TTYPE_IDENT) 694 | return NULL; 695 | char *ident = get_ident(tok); 696 | if (!strcmp(ident, "void")) 697 | return ctype_void; 698 | if (!strcmp(ident, "int")) 699 | return ctype_int; 700 | if (!strcmp(ident, "long")) 701 | return ctype_long; 702 | if (!strcmp(ident, "char")) 703 | return ctype_char; 704 | if (!strcmp(ident, "float")) 705 | return ctype_float; 706 | if (!strcmp(ident, "double")) 707 | return ctype_double; 708 | return NULL; 709 | } 710 | 711 | static bool is_type_keyword(const Token tok) 712 | { 713 | return get_ctype(tok) || is_ident(tok, "struct") || is_ident(tok, "union"); 714 | } 715 | 716 | static Ast *read_decl_array_init_int(Ctype *ctype) 717 | { 718 | Token tok = read_token(); 719 | if (ctype->ptr->type == CTYPE_CHAR && get_ttype(tok) == TTYPE_STRING) 720 | return ast_string(get_strtok(tok)); 721 | if (!is_punct(tok, '{')) 722 | error("Expected an initializer list for %s, but got %s", 723 | ctype_to_string(ctype), token_to_string(tok)); 724 | List *initlist = make_list(); 725 | while (1) { 726 | Token tok = read_token(); 727 | if (is_punct(tok, '}')) 728 | break; 729 | unget_token(tok); 730 | Ast *init = read_expr(); 731 | list_push(initlist, init); 732 | result_type('=', init->ctype, ctype->ptr); 733 | tok = read_token(); 734 | if (!is_punct(tok, ',')) 735 | unget_token(tok); 736 | } 737 | return ast_array_init(initlist); 738 | } 739 | 740 | static char *read_struct_union_tag(void) 741 | { 742 | Token tok = read_token(); 743 | if (get_ttype(tok) == TTYPE_IDENT) 744 | return get_ident(tok); 745 | unget_token(tok); 746 | return NULL; 747 | } 748 | 749 | static Dict *read_struct_union_fields(void) 750 | { 751 | Dict *r = make_dict(NULL); 752 | expect('{'); 753 | while (1) { 754 | if (!is_type_keyword(peek_token())) 755 | break; 756 | Token name; 757 | Ctype *fieldtype = read_decl_int(&name); 758 | dict_put(r, get_ident(name), make_struct_field_type(fieldtype, 0)); 759 | expect(';'); 760 | } 761 | expect('}'); 762 | return r; 763 | } 764 | 765 | static Ctype *read_union_def(void) 766 | { 767 | char *tag = read_struct_union_tag(); 768 | Ctype *ctype = dict_get(union_defs, tag); 769 | if (ctype) 770 | return ctype; 771 | Dict *fields = read_struct_union_fields(); 772 | int maxsize = 0; 773 | for (Iter i = list_iter(dict_values(fields)); !iter_end(i);) { 774 | Ctype *fieldtype = iter_next(&i); 775 | maxsize = (maxsize < fieldtype->size) ? fieldtype->size : maxsize; 776 | } 777 | Ctype *r = make_struct_type(fields, maxsize); 778 | if (tag) 779 | dict_put(union_defs, tag, r); 780 | return r; 781 | } 782 | 783 | static Ctype *read_struct_def(void) 784 | { 785 | char *tag = read_struct_union_tag(); 786 | Ctype *ctype = dict_get(struct_defs, tag); 787 | if (ctype) 788 | return ctype; 789 | Dict *fields = read_struct_union_fields(); 790 | int offset = 0; 791 | for (Iter i = list_iter(dict_values(fields)); !iter_end(i);) { 792 | Ctype *fieldtype = iter_next(&i); 793 | int size = (fieldtype->size < MAX_ALIGN) ? fieldtype->size : MAX_ALIGN; 794 | if (offset % size != 0) 795 | offset += size - offset % size; 796 | fieldtype->offset = offset; 797 | offset += fieldtype->size; 798 | } 799 | Ctype *r = make_struct_type(fields, offset); 800 | if (tag) 801 | dict_put(struct_defs, tag, r); 802 | return r; 803 | } 804 | 805 | static Ctype *read_decl_spec(void) 806 | { 807 | Token tok = read_token(); 808 | Ctype *ctype = 809 | is_ident(tok, "struct") 810 | ? read_struct_def() 811 | : is_ident(tok, "union") ? read_union_def() : get_ctype(tok); 812 | if (!ctype) 813 | error("Type expected, but got %s", token_to_string(tok)); 814 | while (1) { 815 | tok = read_token(); 816 | if (!is_punct(tok, '*')) { 817 | unget_token(tok); 818 | return ctype; 819 | } 820 | ctype = make_ptr_type(ctype); 821 | } 822 | } 823 | 824 | static Ast *read_decl_init_val(Ast *var) 825 | { 826 | if (var->ctype->type == CTYPE_ARRAY) { 827 | Ast *init = read_decl_array_init_int(var->ctype); 828 | int len = (init->type == AST_STRING) ? strlen(init->sval) + 1 829 | : list_len(init->arrayinit); 830 | if (var->ctype->len == -1) { 831 | var->ctype->len = len; 832 | var->ctype->size = len * var->ctype->ptr->size; 833 | } else if (var->ctype->len != len) { 834 | error("Invalid array initializer: expected %d items but got %d", 835 | var->ctype->len, len); 836 | } 837 | expect(';'); 838 | return ast_decl(var, init); 839 | } 840 | Ast *init = read_expr(); 841 | expect(';'); 842 | if (var->type == AST_GVAR) 843 | init = ast_inttype(ctype_int, eval_intexpr(init)); 844 | return ast_decl(var, init); 845 | } 846 | 847 | static Ctype *read_array_dimensions_int(Ctype *basetype) 848 | { 849 | Token tok = read_token(); 850 | if (!is_punct(tok, '[')) { 851 | unget_token(tok); 852 | return NULL; 853 | } 854 | int dim = -1; 855 | if (!is_punct(peek_token(), ']')) { 856 | Ast *size = read_expr(); 857 | dim = eval_intexpr(size); 858 | } 859 | expect(']'); 860 | Ctype *sub = read_array_dimensions_int(basetype); 861 | if (sub) { 862 | if (sub->len == -1 && dim == -1) 863 | error("Array size is not specified"); 864 | return make_array_type(sub, dim); 865 | } 866 | return make_array_type(basetype, dim); 867 | } 868 | 869 | static Ctype *read_array_dimensions(Ctype *basetype) 870 | { 871 | Ctype *ctype = read_array_dimensions_int(basetype); 872 | return ctype ? ctype : basetype; 873 | } 874 | 875 | static Ast *read_decl_init(Ast *var) 876 | { 877 | Token tok = read_token(); 878 | if (is_punct(tok, '=')) 879 | return read_decl_init_val(var); 880 | if (var->ctype->len == -1) 881 | error("Missing array initializer"); 882 | unget_token(tok); 883 | expect(';'); 884 | return ast_decl(var, NULL); 885 | } 886 | 887 | static Ctype *read_decl_int(Token *name) 888 | { 889 | Ctype *ctype = read_decl_spec(); 890 | *name = read_token(); 891 | if (get_ttype((*name)) != TTYPE_IDENT) 892 | error("Identifier expected, but got %s", token_to_string(*name)); 893 | return read_array_dimensions(ctype); 894 | } 895 | 896 | static Ast *read_decl(void) 897 | { 898 | Token varname; 899 | Ctype *ctype = read_decl_int(&varname); 900 | if (ctype == ctype_void) 901 | error("Storage size of '%s' is not known", token_to_string(varname)); 902 | Ast *var = ast_lvar(ctype, get_ident(varname)); 903 | return read_decl_init(var); 904 | } 905 | 906 | static Ast *read_if_stmt(void) 907 | { 908 | expect('('); 909 | Ast *cond = read_expr(); 910 | expect(')'); 911 | Ast *then = read_stmt(); 912 | Token tok = read_token(); 913 | if (get_ttype(tok) != TTYPE_IDENT || strcmp(get_ident(tok), "else")) { 914 | unget_token(tok); 915 | return ast_if(cond, then, NULL); 916 | } 917 | Ast *els = read_stmt(); 918 | return ast_if(cond, then, els); 919 | } 920 | 921 | static Ast *read_opt_decl_or_stmt(void) 922 | { 923 | Token tok = read_token(); 924 | if (is_punct(tok, ';')) 925 | return NULL; 926 | unget_token(tok); 927 | return read_decl_or_stmt(); 928 | } 929 | 930 | static Ast *read_opt_expr(void) 931 | { 932 | Token tok = read_token(); 933 | if (is_punct(tok, ';')) 934 | return NULL; 935 | unget_token(tok); 936 | Ast *r = read_expr(); 937 | expect(';'); 938 | return r; 939 | } 940 | 941 | static Ast *read_for_stmt(void) 942 | { 943 | expect('('); 944 | localenv = make_dict(localenv); 945 | Ast *init = read_opt_decl_or_stmt(); 946 | Ast *cond = read_opt_expr(); 947 | Ast *step = is_punct(peek_token(), ')') ? NULL : read_expr(); 948 | expect(')'); 949 | Ast *body = read_stmt(); 950 | localenv = dict_parent(localenv); 951 | return ast_for(init, cond, step, body); 952 | } 953 | 954 | static Ast *read_return_stmt(void) 955 | { 956 | Ast *retval = read_expr(); 957 | expect(';'); 958 | return ast_return(retval); 959 | } 960 | 961 | static Ast *read_stmt(void) 962 | { 963 | Token tok = read_token(); 964 | if (is_ident(tok, "if")) 965 | return read_if_stmt(); 966 | if (is_ident(tok, "for")) 967 | return read_for_stmt(); 968 | if (is_ident(tok, "return")) 969 | return read_return_stmt(); 970 | if (is_punct(tok, '{')) 971 | return read_compound_stmt(); 972 | unget_token(tok); 973 | Ast *r = read_expr(); 974 | expect(';'); 975 | return r; 976 | } 977 | 978 | static Ast *read_decl_or_stmt(void) 979 | { 980 | Token tok = peek_token(); 981 | if (get_ttype(tok) == TTYPE_NULL) 982 | return NULL; 983 | return is_type_keyword(tok) ? read_decl() : read_stmt(); 984 | } 985 | 986 | static Ast *read_compound_stmt(void) 987 | { 988 | localenv = make_dict(localenv); 989 | List *list = make_list(); 990 | while (1) { 991 | Ast *stmt = read_decl_or_stmt(); 992 | if (stmt) 993 | list_push(list, stmt); 994 | if (!stmt) 995 | break; 996 | Token tok = read_token(); 997 | if (is_punct(tok, '}')) 998 | break; 999 | unget_token(tok); 1000 | } 1001 | localenv = dict_parent(localenv); 1002 | return ast_compound_stmt(list); 1003 | } 1004 | 1005 | static List *read_params(void) 1006 | { 1007 | List *params = make_list(); 1008 | Token tok = read_token(); 1009 | if (is_punct(tok, ')')) 1010 | return params; 1011 | unget_token(tok); 1012 | while (1) { 1013 | Ctype *ctype = read_decl_spec(); 1014 | Token pname = read_token(); 1015 | if (get_ttype(pname) != TTYPE_IDENT) 1016 | error("Identifier expected, but got %s", token_to_string(pname)); 1017 | ctype = read_array_dimensions(ctype); 1018 | if (ctype->type == CTYPE_ARRAY) 1019 | ctype = make_ptr_type(ctype->ptr); 1020 | list_push(params, ast_lvar(ctype, get_ident(pname))); 1021 | Token tok = read_token(); 1022 | if (is_punct(tok, ')')) 1023 | return params; 1024 | if (!is_punct(tok, ',')) 1025 | error("Comma expected, but got %s", token_to_string(tok)); 1026 | } 1027 | } 1028 | 1029 | static Ast *read_func_def(Ctype *rettype, char *fname) 1030 | { 1031 | expect('('); 1032 | localenv = make_dict(globalenv); 1033 | List *params = read_params(); 1034 | expect('{'); 1035 | localenv = make_dict(localenv); 1036 | localvars = make_list(); 1037 | Ast *body = read_compound_stmt(); 1038 | Ast *r = ast_func(rettype, fname, params, body, localvars); 1039 | localenv = dict_parent(localenv); 1040 | localenv = dict_parent(localenv); 1041 | localvars = NULL; 1042 | return r; 1043 | } 1044 | 1045 | static Ast *read_decl_or_func_def(void) 1046 | { 1047 | Token tok = peek_token(); 1048 | if (get_ttype(tok) == TTYPE_NULL) 1049 | return NULL; 1050 | Ctype *ctype = read_decl_spec(); 1051 | Token name = read_token(); 1052 | char *ident; 1053 | if (get_ttype(name) != TTYPE_IDENT) 1054 | error("Identifier expected, but got %s", token_to_string(name)); 1055 | ident = get_ident(name); 1056 | tok = peek_token(); 1057 | if (is_punct(tok, '(')) 1058 | return read_func_def(ctype, ident); 1059 | if (ctype == ctype_void) 1060 | error("Storage size of '%s' is not known", token_to_string(name)); 1061 | ctype = read_array_dimensions(ctype); 1062 | if (is_punct(tok, '=') || ctype->type == CTYPE_ARRAY) { 1063 | Ast *var = ast_gvar(ctype, ident, false); 1064 | return read_decl_init(var); 1065 | } 1066 | if (is_punct(tok, ';')) { 1067 | read_token(); 1068 | Ast *var = ast_gvar(ctype, ident, false); 1069 | return ast_decl(var, NULL); 1070 | } 1071 | error("Don't know how to handle %s", token_to_string(tok)); 1072 | return NULL; /* non-reachable */ 1073 | } 1074 | 1075 | List *read_toplevels(void) 1076 | { 1077 | List *r = make_list(); 1078 | while (1) { 1079 | Ast *ast = read_decl_or_func_def(); 1080 | if (!ast) 1081 | return r; 1082 | list_push(r, ast); 1083 | } 1084 | list_free(globalenv->list); 1085 | return r; 1086 | } 1087 | -------------------------------------------------------------------------------- /sample/nqueen.c: -------------------------------------------------------------------------------- 1 | int conflict(int board[][8], int row, int col) 2 | { 3 | for (int i = 0; i < row; i++) { 4 | if (board[i][col]) 5 | return 1; 6 | int j = row - i; 7 | if (0 < col - j + 1 && board[i][col - j]) 8 | return 1; 9 | if (col + j < 8 && board[i][col + j]) 10 | return 1; 11 | } 12 | return 0; 13 | } 14 | 15 | int print_board(int board[][8]) 16 | { 17 | for (int i = 0; i < 8; i++) { 18 | for (int j = 0; j < 8; j++) 19 | printf(board[i][j] ? "Q " : ". "); 20 | printf("\n"); 21 | } 22 | printf("\n\n"); 23 | } 24 | 25 | int solve(int board[][8], int row) 26 | { 27 | if (row == 8) { 28 | print_board(board); 29 | return 0; 30 | } 31 | for (int i = 0; i < 8; i++) { 32 | if (!conflict(board, row, i)) { 33 | board[row][i] = 1; 34 | solve(board, row + 1); 35 | board[row][i] = 0; 36 | } 37 | } 38 | } 39 | 40 | int main() 41 | { 42 | int board[64]; 43 | for (int i = 0; i < 64; i++) 44 | board[i] = 0; 45 | solve(board, 0); 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /tests/arith.c: -------------------------------------------------------------------------------- 1 | /* Test basic arithmetic */ 2 | 3 | int expect(int a, int b) 4 | { 5 | if (!(a == b)) { 6 | printf("Failed\n"); 7 | printf(" %d expected, but got %d\n", a, b); 8 | exit(1); 9 | } 10 | } 11 | 12 | int test_basic() 13 | { 14 | expect(0, 0); 15 | expect(3, 1 + 2); 16 | expect(3, 1 + 2); 17 | expect(10, 1 + 2 + 3 + 4); 18 | expect(11, 1 + 2 * 3 + 4); 19 | expect(14, 1 * 2 + 3 * 4); 20 | expect(4, 4 / 2 + 6 / 3); 21 | expect(4, 24 / 2 / 3); 22 | expect(98, 'a' + 1); 23 | int a = 0 - 1; 24 | expect(0 - 1, a); 25 | expect(0, a + 1); 26 | } 27 | 28 | int test_inc_dec() 29 | { 30 | int a = 15; 31 | expect(15, a++); 32 | expect(16, a); 33 | expect(16, a--); 34 | expect(15, a); 35 | } 36 | 37 | int test_bool() 38 | { 39 | expect(0, !1); 40 | expect(1, !0); 41 | } 42 | 43 | int test_ternary() 44 | { 45 | expect(51, (1 + 2) ? 51 : 52); 46 | expect(52, (1 - 1) ? 51 : 52); 47 | expect(26, (1 - 1) ? 51 : 52 / 2); 48 | expect(17, (1 - 0) ? 51 / 3 : 52); 49 | } 50 | 51 | int test_logand() 52 | { 53 | expect(1, 55 && 2); 54 | expect(0, 55 && 0); 55 | expect(0, 0 && 55); 56 | } 57 | 58 | int test_bitand() 59 | { 60 | expect(3, 1 | 2); 61 | expect(1, 1 & 3); 62 | } 63 | 64 | int test_shift() 65 | { 66 | expect(8, 4 << 1); 67 | expect(3, 7 >> 1); 68 | } 69 | 70 | int main() 71 | { 72 | test_basic(); 73 | test_inc_dec(); 74 | test_bool(); 75 | test_ternary(); 76 | test_logand(); 77 | test_bitand(); 78 | test_shift(); 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /tests/array.c: -------------------------------------------------------------------------------- 1 | /* Test array */ 2 | 3 | int expect(int a, int b) 4 | { 5 | if (!(a == b)) { 6 | printf("Failed\n"); 7 | printf(" %d expected, but got %d\n", a, b); 8 | exit(1); 9 | } 10 | } 11 | 12 | int t1() 13 | { 14 | int a[2][3]; 15 | int *p = a; 16 | *p = 1; 17 | expect(1, *p); 18 | } 19 | 20 | int t2() 21 | { 22 | int a[2][3]; 23 | int *p = a + 1; 24 | *p = 1; 25 | int *q = a; 26 | *p = 32; 27 | expect(32, *(q + 3)); 28 | } 29 | 30 | int t3() 31 | { 32 | int a[4][5]; 33 | int *p = a; 34 | *(*(a + 1) + 2) = 62; 35 | expect(62, *(p + 7)); 36 | } 37 | 38 | int t4() 39 | { 40 | int a[3] = {1, 2, 3}; 41 | expect(1, a[0]); 42 | expect(2, a[1]); 43 | expect(3, a[2]); 44 | } 45 | 46 | int t5() 47 | { 48 | int a[2][3]; 49 | a[0][1] = 1; 50 | a[1][1] = 2; 51 | int *p = a; 52 | expect(1, p[1]); 53 | expect(2, p[4]); 54 | } 55 | 56 | int t6a(int e, int x[][3]) 57 | { 58 | expect(e, *(*(x + 1) + 1)); 59 | } 60 | 61 | int t6() 62 | { 63 | int a[2][3]; 64 | int *p = a; 65 | *(p + 4) = 65; 66 | t6a(65, a); 67 | } 68 | 69 | int t7() 70 | { 71 | int a[3 * 3]; // integer constant expression 72 | a[8] = 68; 73 | expect(68, a[8]); 74 | } 75 | 76 | int main() 77 | { 78 | t1(); 79 | t2(); 80 | t3(); 81 | t4(); 82 | t5(); 83 | t6(); 84 | t7(); 85 | 86 | return 0; 87 | } 88 | -------------------------------------------------------------------------------- /tests/comp.c: -------------------------------------------------------------------------------- 1 | /* Test comparison operators */ 2 | 3 | int expect(int a, int b) 4 | { 5 | if (!(a == b)) { 6 | printf("Failed\n"); 7 | printf(" %d expected, but got %d\n", a, b); 8 | exit(1); 9 | } 10 | } 11 | 12 | int main() 13 | { 14 | expect(1, 1 < 2); 15 | expect(0, 2 < 1); 16 | expect(1, 1 == 1); 17 | expect(0, 1 == 2); 18 | 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /tests/control.c: -------------------------------------------------------------------------------- 1 | /* Test control flow */ 2 | 3 | int expect(int a, int b) 4 | { 5 | if (!(a == b)) { 6 | printf("Failed\n"); 7 | printf(" %d expected, but got %d\n", a, b); 8 | exit(1); 9 | } 10 | } 11 | 12 | int testif1() 13 | { 14 | if (1) { 15 | return 'a'; 16 | } 17 | return 0; 18 | } 19 | int testif2() 20 | { 21 | if (0) { 22 | return 0; 23 | } 24 | return 'b'; 25 | } 26 | int testif3() 27 | { 28 | if (1) { 29 | return 'c'; 30 | } else { 31 | return 0; 32 | } 33 | return 0; 34 | } 35 | int testif4() 36 | { 37 | if (0) { 38 | return 0; 39 | } else { 40 | return 'd'; 41 | } 42 | return 0; 43 | } 44 | int testif5() 45 | { 46 | if (1) 47 | return 'e'; 48 | return 0; 49 | } 50 | int testif6() 51 | { 52 | if (0) 53 | return 0; 54 | return 'f'; 55 | } 56 | int testif7() 57 | { 58 | if (1) 59 | return 'g'; 60 | else 61 | return 0; 62 | return 0; 63 | } 64 | int testif8() 65 | { 66 | if (0) 67 | return 0; 68 | else 69 | return 'h'; 70 | return 0; 71 | } 72 | int testif9() 73 | { 74 | if (0 + 1) 75 | return 'i'; 76 | return 0; 77 | } 78 | int testif10() 79 | { 80 | if (1 - 1) 81 | return 0; 82 | return 'j'; 83 | } 84 | 85 | int testif() 86 | { 87 | expect('a', testif1()); 88 | expect('b', testif2()); 89 | expect('c', testif3()); 90 | expect('d', testif4()); 91 | expect('e', testif5()); 92 | expect('f', testif6()); 93 | expect('g', testif7()); 94 | expect('h', testif8()); 95 | expect('i', testif9()); 96 | expect('j', testif10()); 97 | } 98 | 99 | int testfor() 100 | { 101 | int i; 102 | int acc = 0; 103 | for (i = 0; i < 5; i = i + 1) { 104 | acc = acc + i; 105 | } 106 | expect(10, acc); 107 | 108 | acc = 0; 109 | for (i = 0; i < 5; i = i + 1) 110 | acc = acc + i; 111 | expect(10, acc); 112 | } 113 | 114 | int main() 115 | { 116 | testif(); 117 | testfor(); 118 | 119 | return 0; 120 | } 121 | -------------------------------------------------------------------------------- /tests/decl.c: -------------------------------------------------------------------------------- 1 | /* Test declaration */ 2 | 3 | int expect(int a, int b) 4 | { 5 | if (!(a == b)) { 6 | printf("Failed\n"); 7 | printf(" %d expected, but got %d\n", a, b); 8 | exit(1); 9 | } 10 | } 11 | 12 | int t1() 13 | { 14 | int a = 1; 15 | expect(3, a + 2); 16 | } 17 | 18 | int t2() 19 | { 20 | int a = 1; 21 | int b = 48 + 2; 22 | int c = a + b; 23 | expect(102, c * 2); 24 | } 25 | 26 | int t3() 27 | { 28 | int a[] = {55}; 29 | int *b = a; 30 | expect(55, *b); 31 | } 32 | 33 | int t4() 34 | { 35 | int a[] = {55, 67}; 36 | int *b = a + 1; 37 | expect(67, *b); 38 | } 39 | 40 | int t5() 41 | { 42 | int a[] = {20, 30, 40}; 43 | int *b = a + 1; 44 | expect(30, *b); 45 | } 46 | 47 | int t6() 48 | { 49 | int a[] = {20, 30, 40}; 50 | expect(20, *a); 51 | } 52 | 53 | int main() 54 | { 55 | t1(); 56 | t2(); 57 | t3(); 58 | t4(); 59 | t5(); 60 | t6(); 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /tests/driver.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | eval `cat .cbuild` 4 | 5 | function compile { 6 | echo "$1" > /dev/stderr 7 | echo "$1" | ./mzcc > tmp.s || echo "Failed to compile $1" 8 | if [ $? -ne 0 ]; then 9 | echo "Failed to compile $1" 10 | exit 11 | fi 12 | $(CBUILD) -o tmp.out tmp.s 13 | if [ $? -ne 0 ]; then 14 | echo "GCC failed: $1" 15 | exit 16 | fi 17 | } 18 | 19 | function assert_equal { 20 | if [ "$1" != "$2" ]; then 21 | echo "Test failed: $2 expected but got $1" 22 | exit 23 | fi 24 | } 25 | 26 | function test_astf { 27 | result="$(echo "$2" | ./mzcc --dump-ast -)" 28 | if [ $? -ne 0 ]; then 29 | echo "Failed to compile $2" 30 | exit 31 | fi 32 | assert_equal "$result" "$1" 33 | } 34 | 35 | function test_ast { 36 | test_astf "$1" "int f(){$2}" 37 | } 38 | 39 | function test_fail { 40 | expr="int f(){$1}" 41 | echo "$expr" | ./mzcc > /dev/null 2>&1 42 | if [ $? -eq 0 ]; then 43 | echo "Should fail to compile, but succeded: $expr" 44 | exit 45 | fi 46 | } 47 | 48 | # Parser 49 | test_ast '(int)f(){1;}' '1;' 50 | test_ast '(int)f(){1L;}' '1L;' 51 | test_ast '(int)f(){1152921504606846976L;}' '1152921504606846976;' 52 | test_ast '(int)f(){(+ (- (+ 1 2) 3) 4);}' '1+2-3+4;' 53 | test_ast '(int)f(){(+ (+ 1 (* 2 3)) 4);}' '1+2*3+4;' 54 | test_ast '(int)f(){(+ (* 1 2) (* 3 4));}' '1*2+3*4;' 55 | test_ast '(int)f(){(+ (/ 4 2) (/ 6 3));}' '4/2+6/3;' 56 | test_ast '(int)f(){(/ (/ 24 2) 4);}' '24/2/4;' 57 | test_ast '(int)f(){(decl int a 3);}' 'int a=3;' 58 | test_ast "(int)f(){(decl char c 'a');}" "char c='a';" 59 | test_ast '(int)f(){(decl *char s "abcd");}' 'char *s="abcd";' 60 | test_ast '(int)f(){(decl [5]char s "asdf");}' 'char s[5]="asdf";' 61 | test_ast '(int)f(){(decl [5]char s "asdf");}' 'char s[]="asdf";' 62 | test_ast '(int)f(){(decl [3]int a {1,2,3});}' 'int a[3]={1,2,3};' 63 | test_ast '(int)f(){(decl [3]int a {1,2,3});}' 'int a[]={1,2,3};' 64 | test_ast '(int)f(){(decl [3][5]int a);}' 'int a[3][5];' 65 | test_ast '(int)f(){(decl [5]*int a);}' 'int *a[5];' 66 | test_ast '(int)f(){(decl int a 1);(decl int b 2);(= a (= b 3));}' 'int a=1;int b=2;a=b=3;' 67 | test_ast '(int)f(){(decl int a 3);(addr a);}' 'int a=3;&a;' 68 | test_ast '(int)f(){(decl int a 3);(deref (addr a));}' 'int a=3;*&a;' 69 | test_ast '(int)f(){(decl int a 3);(decl *int b (addr a));(deref b);}' 'int a=3;int *b=&a;*b;' 70 | test_ast '(int)f(){(if 1 {2;});}' 'if(1){2;}' 71 | test_ast '(int)f(){(if 1 {2;} {3;});}' 'if(1){2;}else{3;}' 72 | test_ast '(int)f(){(for (decl int a 1) 3 7 {5;});}' 'for(int a=1;3;7){5;}' 73 | test_ast '(int)f(){"abcd";}' '"abcd";' 74 | test_ast "(int)f(){'c';}" "'c';" 75 | test_ast '(int)f(){(int)a();}' 'a();' 76 | test_ast '(int)f(){(int)a(1,2,3,4,5,6);}' 'a(1,2,3,4,5,6);' 77 | test_ast '(int)f(){(return 1);}' 'return 1;' 78 | test_ast '(int)f(){(< 1 2);}' '1<2;' 79 | test_ast '(int)f(){(> 1 2);}' '1>2;' 80 | test_ast '(int)f(){(== 1 2);}' '1==2;' 81 | test_ast '(int)f(){(deref (+ 1 2));}' '1[2];' 82 | test_ast '(int)f(){(decl int a 1);(++ a);}' 'int a=1;a++;' 83 | test_ast '(int)f(){(decl int a 1);(-- a);}' 'int a=1;a--;' 84 | test_ast '(int)f(){(! 1);}' '!1;' 85 | test_ast '(int)f(){(? 1 2 3);}' '1?2:3;' 86 | test_ast '(int)f(){(and 1 2);}' '1&&2;' 87 | test_ast '(int)f(){(or 1 2);}' '1||2;' 88 | test_ast '(int)f(){(& 1 2);}' '1&2;' 89 | test_ast '(int)f(){(| 1 2);}' '1|2;' 90 | test_ast '(int)f(){1.200000;}' '1.2;' 91 | test_ast '(int)f(){(+ 1.200000 1);}' '1.2+1;' 92 | 93 | test_astf '(int)f(int c){c;}' 'int f(int c){c;}' 94 | test_astf '(int)f(int c){c;}(int)g(int d){d;}' 'int f(int c){c;} int g(int d){d;}' 95 | test_astf '(decl int a 3)' 'int a=3;' 96 | 97 | test_astf '(decl (struct) a)' 'struct {} a;' 98 | test_astf '(decl (struct (int) (char)) a)' 'struct {int x; char y;} a;' 99 | test_astf '(decl (struct ([3]int)) a)' 'struct {int x[3];} a;' 100 | test_ast '(int)f(){(decl (struct (int)) a);(decl *(struct (int)) p);(deref p).x;}' 'struct tag {int x;} a; struct tag *p; p->x;' 101 | test_ast '(int)f(){(decl (struct (int)) a);a.x;}' 'struct {int x;} a; a.x;' 102 | 103 | test_fail '0abc;' 104 | test_fail '1+;' 105 | test_fail '1=2;' 106 | 107 | # & is only applicable to an lvalue 108 | test_fail '&"a";' 109 | test_fail '&1;' 110 | test_fail '&a();' 111 | 112 | echo "All tests passed" 113 | -------------------------------------------------------------------------------- /tests/float.c: -------------------------------------------------------------------------------- 1 | /* Test floating point */ 2 | 3 | int expect(float a, float b) 4 | { 5 | if (!(a == b)) { 6 | printf("Failed\n"); 7 | printf(" %f expected, but got %f\n", a, b); 8 | exit(1); 9 | } 10 | } 11 | 12 | int main() 13 | { 14 | expect(1.0, 1.0); 15 | expect(1.5, 1.0 + 0.5); 16 | expect(0.5, 1.0 - 0.5); 17 | expect(2.0, 1.0 * 2.0); 18 | expect(0.25, 1.0 / 4.0); 19 | 20 | expect(3.0, 1.0 + 2); 21 | expect(2.5, 5 - 2.5); 22 | expect(2.0, 1.0 * 2); 23 | expect(0.25, 1.0 / 4); 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /tests/function.c: -------------------------------------------------------------------------------- 1 | /* Test function */ 2 | 3 | int expect(int a, int b) 4 | { 5 | if (!(a == b)) { 6 | printf("Failed\n"); 7 | printf(" %d expected, but got %d\n", a, b); 8 | exit(1); 9 | } 10 | } 11 | 12 | int t1() 13 | { 14 | return 77; 15 | } 16 | 17 | int t2(int a) 18 | { 19 | expect(79, a); 20 | } 21 | 22 | int t3(int a, int b, int c, int d, int e, int f) 23 | { 24 | expect(1, a); 25 | expect(2, b); 26 | expect(3, c); 27 | expect(4, d); 28 | expect(5, e); 29 | expect(6, f); 30 | } 31 | 32 | int t4a(int *p) 33 | { 34 | return *p; 35 | } 36 | 37 | int t4() 38 | { 39 | int a[] = {98}; 40 | expect(98, t4a(a)); 41 | } 42 | 43 | int t5a(int *p) 44 | { 45 | expect(99, *p); 46 | p = p + 1; 47 | expect(98, *p); 48 | p = p + 1; 49 | expect(97, *p); 50 | } 51 | 52 | int t5b(int p[]) 53 | { 54 | expect(99, *p); 55 | p = p + 1; 56 | expect(98, *p); 57 | p = p + 1; 58 | expect(97, *p); 59 | } 60 | 61 | int t5() 62 | { 63 | int a[] = {1, 2, 3}; 64 | int *p = a; 65 | *p = 99; 66 | p = p + 1; 67 | *p = 98; 68 | p = p + 1; 69 | *p = 97; 70 | t5a(a); 71 | t5b(a); 72 | } 73 | 74 | int main() 75 | { 76 | expect(77, t1()); 77 | t2(79); 78 | t3(1, 2, 3, 4, 5, 6); 79 | t4(); 80 | t5(); 81 | 82 | return 0; 83 | } 84 | -------------------------------------------------------------------------------- /tests/global.c: -------------------------------------------------------------------------------- 1 | /* Test global variable */ 2 | 3 | int val = 21; 4 | int a1[3]; 5 | int a2[3] = {24, 25, 26}; 6 | 7 | int expect(int a, int b) 8 | { 9 | if (!(a == b)) { 10 | printf("Failed\n"); 11 | printf(" %d expected, but got %d\n", a, b); 12 | exit(1); 13 | } 14 | } 15 | 16 | int main() 17 | { 18 | expect(21, val); 19 | val = 22; 20 | expect(22, val); 21 | 22 | a1[1] = 23; 23 | expect(23, a1[1]); 24 | expect(25, a2[1]); 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /tests/long.c: -------------------------------------------------------------------------------- 1 | /* Test long integer */ 2 | 3 | int expect(long a, long b) 4 | { 5 | if (!(a == b)) { 6 | printf("Failed\n"); 7 | printf(" %ld expected, but got %ld\n", a, b); 8 | exit(1); 9 | } 10 | } 11 | 12 | int main() 13 | { 14 | expect(10L, 10L); 15 | expect(1152921504606846976, 1152921504606846976); 16 | expect(1152921504606846977, 1152921504606846976 + 1); 17 | 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /tests/pointer.c: -------------------------------------------------------------------------------- 1 | /* Test pointer */ 2 | 3 | int expect(int a, int b) 4 | { 5 | if (!(a == b)) { 6 | printf("Failed\n"); 7 | printf(" %d expected, but got %d\n", a, b); 8 | exit(1); 9 | } 10 | } 11 | 12 | int t1() 13 | { 14 | int a = 61; 15 | int *b = &a; 16 | expect(61, *b); 17 | } 18 | 19 | int t2() 20 | { 21 | char *c = "ab"; 22 | expect(97, *c); 23 | } 24 | 25 | int t3() 26 | { 27 | char *c = "ab" + 1; 28 | expect(98, *c); 29 | } 30 | 31 | int t4() 32 | { 33 | char s[] = "xyz"; 34 | char *c = s + 2; 35 | expect(122, *c); 36 | } 37 | 38 | int t5() 39 | { 40 | char s[] = "xyz"; 41 | *s = 65; 42 | expect(65, *s); 43 | } 44 | 45 | int main() 46 | { 47 | t1(); 48 | t2(); 49 | t3(); 50 | t4(); 51 | t5(); 52 | 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /tests/pointer_arith.c: -------------------------------------------------------------------------------- 1 | /* Test pointer arithmetic*/ 2 | 3 | int expect(int a, int b) 4 | { 5 | if (!(a == b)) { 6 | printf("Failed\n"); 7 | printf(" %d expected, but got %d\n", a, b); 8 | exit(1); 9 | } 10 | } 11 | 12 | int t1() 13 | { 14 | char *s = "abcdefghi"; 15 | char *x = s; 16 | char *t = x + 1; 17 | expect(98, *t); 18 | } 19 | 20 | int t2() 21 | { 22 | char *s = "abcdefghi"; 23 | int *x = s; 24 | char *t = x + 1; 25 | expect(101, *t); 26 | } 27 | 28 | int t3() 29 | { 30 | char *s = "abcdefghi"; 31 | long *x = s; 32 | char *t = x + 1; 33 | expect(105, *t); 34 | } 35 | 36 | int t4() 37 | { 38 | char *s = "abcdefghi"; 39 | void *x = s; 40 | char *t = x + 1; 41 | expect(98, *t); 42 | } 43 | 44 | int main() 45 | { 46 | t1(); 47 | t2(); 48 | t3(); 49 | t4(); 50 | } 51 | -------------------------------------------------------------------------------- /tests/scope.c: -------------------------------------------------------------------------------- 1 | /* Test scope */ 2 | 3 | int expect(int a, int b) 4 | { 5 | if (!(a == b)) { 6 | printf("Failed\n"); 7 | printf(" %d expected, but got %d\n", a, b); 8 | exit(1); 9 | } 10 | } 11 | 12 | int main() 13 | { 14 | int a = 31; 15 | { 16 | int a = 64; 17 | } 18 | expect(31, a); 19 | { 20 | int a = 64; 21 | expect(64, a); 22 | } 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /tests/struct.c: -------------------------------------------------------------------------------- 1 | /* Test struct */ 2 | 3 | int expect(int a, int b) 4 | { 5 | if (!(a == b)) { 6 | printf("Failed\n"); 7 | printf(" %d expected, but got %d\n", a, b); 8 | exit(1); 9 | } 10 | } 11 | 12 | int t1() 13 | { 14 | struct { 15 | int a; 16 | } x; 17 | x.a = 61; 18 | expect(61, x.a); 19 | } 20 | 21 | int t2() 22 | { 23 | struct { 24 | int a; 25 | int b; 26 | } x; 27 | x.a = 61; 28 | x.b = 2; 29 | expect(63, x.a + x.b); 30 | } 31 | 32 | int t3() 33 | { 34 | struct { 35 | int a; 36 | struct { 37 | char b; 38 | int c; 39 | } y; 40 | } x; 41 | x.a = 61; 42 | x.y.b = 3; 43 | x.y.c = 3; 44 | expect(67, x.a + x.y.b + x.y.c); 45 | } 46 | 47 | int t4() 48 | { 49 | struct tag4 { 50 | int a; 51 | struct { 52 | char b; 53 | int c; 54 | } y; 55 | } x; 56 | struct tag4 s; 57 | s.a = 61; 58 | s.y.b = 3; 59 | s.y.c = 3; 60 | expect(67, s.a + s.y.b + s.y.c); 61 | } 62 | 63 | int t5() 64 | { 65 | struct tag5 { 66 | int a; 67 | } x; 68 | struct tag5 *p = &x; 69 | x.a = 68; 70 | expect(68, (*p).a); 71 | } 72 | 73 | int t6() 74 | { 75 | struct tag6 { 76 | int a; 77 | } x; 78 | struct tag6 *p = &x; 79 | (*p).a = 69; 80 | expect(69, x.a); 81 | } 82 | 83 | int t7() 84 | { 85 | struct tag7 { 86 | int a; 87 | int b; 88 | } x; 89 | struct tag7 *p = &x; 90 | x.b = 71; 91 | expect(71, (*p).b); 92 | } 93 | 94 | int t8() 95 | { 96 | struct tag8 { 97 | int a; 98 | int b; 99 | } x; 100 | struct tag8 *p = &x; 101 | (*p).b = 72; 102 | expect(72, x.b); 103 | } 104 | 105 | int t9() 106 | { 107 | struct tag9 { 108 | int a[3]; 109 | int b[3]; 110 | } x; 111 | x.a[0] = 73; 112 | expect(73, x.a[0]); 113 | x.b[1] = 74; 114 | expect(74, x.b[1]); 115 | expect(74, x.a[4]); 116 | } 117 | 118 | struct tag10 { 119 | int a; 120 | struct tag10a { 121 | char b; 122 | int c; 123 | } y; 124 | } v10; 125 | int t10() 126 | { 127 | v10.a = 71; 128 | v10.y.b = 3; 129 | v10.y.c = 3; 130 | expect(77, v10.a + v10.y.b + v10.y.c); 131 | } 132 | 133 | struct tag11 { 134 | int a; 135 | } v11; 136 | int t11() 137 | { 138 | struct tag11 *p = &v11; 139 | v11.a = 78; 140 | expect(78, (*p).a); 141 | expect(78, v11.a); 142 | expect(78, p->a); 143 | p->a = 79; 144 | expect(79, (*p).a); 145 | expect(79, v11.a); 146 | expect(79, p->a); 147 | } 148 | 149 | struct tag12 { 150 | int a; 151 | int b; 152 | } x; 153 | int t12() 154 | { 155 | struct tag12 a[3]; 156 | a[0].a = 83; 157 | expect(83, a[0].a); 158 | a[0].b = 84; 159 | expect(84, a[0].b); 160 | a[1].b = 85; 161 | expect(85, a[1].b); 162 | int *p = a; 163 | expect(85, p[3]); 164 | } 165 | 166 | int main() 167 | { 168 | t1(); 169 | t2(); 170 | t3(); 171 | t4(); 172 | t5(); 173 | t6(); 174 | t7(); 175 | t8(); 176 | t9(); 177 | t10(); 178 | t11(); 179 | t12(); 180 | 181 | return 0; 182 | } 183 | -------------------------------------------------------------------------------- /tests/union.c: -------------------------------------------------------------------------------- 1 | /* Test union */ 2 | 3 | int expect(int a, int b) 4 | { 5 | if (!(a == b)) { 6 | printf("Failed\n"); 7 | printf(" %d expected, but got %d\n", a, b); 8 | exit(1); 9 | } 10 | } 11 | 12 | int t1() 13 | { 14 | union { 15 | int a; 16 | int b; 17 | } x; 18 | x.a = 90; 19 | expect(90, x.b); 20 | } 21 | 22 | int t2() 23 | { 24 | union { 25 | char a[4]; 26 | int b; 27 | } x; 28 | x.b = 0; 29 | x.a[1] = 1; 30 | expect(256, x.b); 31 | } 32 | 33 | int t3() 34 | { 35 | union { 36 | char a[4]; 37 | int b; 38 | } x; 39 | x.a[0] = x.a[1] = x.a[2] = x.a[3] = 0; 40 | x.a[1] = 1; 41 | expect(256, x.b); 42 | } 43 | 44 | int main() 45 | { 46 | t1(); 47 | t2(); 48 | t3(); 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | #ifndef MAZUCC_UTIL_H 2 | #define MAZUCC_UTIL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "list.h" 9 | 10 | typedef struct { 11 | char *body; 12 | int nalloc, len; 13 | } String; 14 | 15 | static List *cstrings = &EMPTY_LIST; 16 | 17 | #define INIT_SIZE 8 18 | 19 | static inline String make_string(void) 20 | { 21 | return (String){ 22 | .body = calloc(1, INIT_SIZE), 23 | .nalloc = INIT_SIZE, 24 | .len = 0, 25 | }; 26 | } 27 | 28 | static inline void realloc_body(String *s) 29 | { 30 | int newsize = s->nalloc * 2; 31 | char *body = realloc(s->body, newsize); 32 | s->body = body; 33 | s->nalloc = newsize; 34 | } 35 | 36 | static inline char *get_cstring(const String s) 37 | { 38 | char *r = s.body; 39 | list_push(cstrings, r); 40 | return r; 41 | } 42 | 43 | static inline void string_append(String *s, char c) 44 | { 45 | if (s->nalloc == (s->len + 1)) 46 | realloc_body(s); 47 | s->body[s->len++] = c; 48 | s->body[s->len] = '\0'; 49 | } 50 | 51 | static inline void string_appendf(String *s, char *fmt, ...) 52 | { 53 | va_list args; 54 | while (1) { 55 | int avail = s->nalloc - s->len; 56 | va_start(args, fmt); 57 | int written = vsnprintf(s->body + s->len, avail, fmt, args); 58 | va_end(args); 59 | if (avail <= written) { 60 | realloc_body(s); 61 | continue; 62 | } 63 | s->len += written; 64 | return; 65 | } 66 | } 67 | 68 | #define error(...) errorf(__FILE__, __LINE__, __VA_ARGS__) 69 | 70 | #define assert(expr) \ 71 | do { \ 72 | if (!(expr)) \ 73 | error("Assertion failed: " #expr); \ 74 | } while (0) 75 | 76 | static inline void errorf(char *file, int line, char *fmt, ...) 77 | { 78 | fprintf(stderr, "%s:%d: ", file, line); 79 | va_list args; 80 | va_start(args, fmt); 81 | vfprintf(stderr, fmt, args); 82 | fprintf(stderr, "\n"); 83 | va_end(args); 84 | exit(1); 85 | } 86 | 87 | static inline char *quote_cstring(char *p) 88 | { 89 | String s = make_string(); 90 | for (; *p; p++) { 91 | if (*p == '\"' || *p == '\\') 92 | string_appendf(&s, "\\%c", *p); 93 | else if (*p == '\n') 94 | string_appendf(&s, "\\n"); 95 | else 96 | string_append(&s, *p); 97 | } 98 | return get_cstring(s); 99 | } 100 | 101 | #endif /* MAZUCC_UTIL_H */ 102 | -------------------------------------------------------------------------------- /verbose.c: -------------------------------------------------------------------------------- 1 | #include "mzcc.h" 2 | 3 | char *ctype_to_string(Ctype *ctype) 4 | { 5 | if (!ctype) 6 | return "(nil)"; 7 | switch (ctype->type) { 8 | case CTYPE_VOID: 9 | return "void"; 10 | case CTYPE_INT: 11 | return "int"; 12 | case CTYPE_LONG: 13 | return "long"; 14 | case CTYPE_CHAR: 15 | return "char"; 16 | case CTYPE_FLOAT: 17 | return "float"; 18 | case CTYPE_DOUBLE: 19 | return "double"; 20 | case CTYPE_PTR: { 21 | String s = make_string(); 22 | string_appendf(&s, "*%s", ctype_to_string(ctype->ptr)); 23 | return get_cstring(s); 24 | } 25 | case CTYPE_ARRAY: { 26 | String s = make_string(); 27 | string_appendf(&s, "[%d]%s", ctype->len, ctype_to_string(ctype->ptr)); 28 | return get_cstring(s); 29 | } 30 | case CTYPE_STRUCT: { 31 | String s = make_string(); 32 | string_appendf(&s, "(struct"); 33 | for (Iter i = list_iter(dict_values(ctype->fields)); !iter_end(i);) 34 | string_appendf(&s, " (%s)", ctype_to_string(iter_next(&i))); 35 | string_appendf(&s, ")"); 36 | return get_cstring(s); 37 | } 38 | default: 39 | error("Unknown ctype: %d", ctype); 40 | return NULL; /* non-reachable */ 41 | } 42 | } 43 | 44 | static void uop_to_string(String *buf, char *op, Ast *ast) 45 | { 46 | string_appendf(buf, "(%s %s)", op, ast_to_string(ast->operand)); 47 | } 48 | 49 | static void binop_to_string(String *buf, char *op, Ast *ast) 50 | { 51 | string_appendf(buf, "(%s %s %s)", op, ast_to_string(ast->left), 52 | ast_to_string(ast->right)); 53 | } 54 | 55 | static void ast_to_string_int(String *buf, Ast *ast) 56 | { 57 | if (!ast) { 58 | string_appendf(buf, "(nil)"); 59 | return; 60 | } 61 | switch (ast->type) { 62 | case AST_LITERAL: 63 | switch (ast->ctype->type) { 64 | case CTYPE_CHAR: 65 | if (ast->ival == '\n') 66 | string_appendf(buf, "'\n'"); 67 | else if (ast->ival == '\\') 68 | string_appendf(buf, "'\\\\'"); 69 | else 70 | string_appendf(buf, "'%c'", ast->ival); 71 | break; 72 | case CTYPE_INT: 73 | string_appendf(buf, "%d", ast->ival); 74 | break; 75 | case CTYPE_LONG: 76 | string_appendf(buf, "%ldL", ast->ival); 77 | break; 78 | case CTYPE_FLOAT: 79 | case CTYPE_DOUBLE: 80 | string_appendf(buf, "%f", ast->fval); 81 | break; 82 | default: 83 | error("internal error"); 84 | } 85 | break; 86 | case AST_STRING: 87 | string_appendf(buf, "\"%s\"", quote_cstring(ast->sval)); 88 | break; 89 | case AST_LVAR: 90 | case AST_GVAR: 91 | string_appendf(buf, "%s", ast->varname); 92 | break; 93 | case AST_FUNCALL: { 94 | string_appendf(buf, "(%s)%s(", ctype_to_string(ast->ctype), ast->fname); 95 | for (Iter i = list_iter(ast->args); !iter_end(i);) { 96 | string_appendf(buf, "%s", ast_to_string(iter_next(&i))); 97 | if (!iter_end(i)) 98 | string_appendf(buf, ","); 99 | } 100 | string_appendf(buf, ")"); 101 | break; 102 | } 103 | case AST_FUNC: { 104 | string_appendf(buf, "(%s)%s(", ctype_to_string(ast->ctype), ast->fname); 105 | for (Iter i = list_iter(ast->params); !iter_end(i);) { 106 | Ast *param = iter_next(&i); 107 | string_appendf(buf, "%s %s", ctype_to_string(param->ctype), 108 | ast_to_string(param)); 109 | if (!iter_end(i)) 110 | string_appendf(buf, ","); 111 | } 112 | string_appendf(buf, ")"); 113 | ast_to_string_int(buf, ast->body); 114 | break; 115 | } 116 | case AST_DECL: 117 | string_appendf(buf, "(decl %s %s", ctype_to_string(ast->declvar->ctype), 118 | ast->declvar->varname); 119 | if (ast->declinit) 120 | string_appendf(buf, " %s)", ast_to_string(ast->declinit)); 121 | else 122 | string_appendf(buf, ")"); 123 | break; 124 | case AST_ARRAY_INIT: 125 | string_appendf(buf, "{"); 126 | for (Iter i = list_iter(ast->arrayinit); !iter_end(i);) { 127 | ast_to_string_int(buf, iter_next(&i)); 128 | if (!iter_end(i)) 129 | string_appendf(buf, ","); 130 | } 131 | string_appendf(buf, "}"); 132 | break; 133 | case AST_IF: 134 | string_appendf(buf, "(if %s %s", ast_to_string(ast->cond), 135 | ast_to_string(ast->then)); 136 | if (ast->els) 137 | string_appendf(buf, " %s", ast_to_string(ast->els)); 138 | string_appendf(buf, ")"); 139 | break; 140 | case AST_TERNARY: 141 | string_appendf(buf, "(? %s %s %s)", ast_to_string(ast->cond), 142 | ast_to_string(ast->then), ast_to_string(ast->els)); 143 | break; 144 | case AST_FOR: 145 | string_appendf(buf, "(for %s %s %s ", ast_to_string(ast->forinit), 146 | ast_to_string(ast->forcond), 147 | ast_to_string(ast->forstep)); 148 | string_appendf(buf, "%s)", ast_to_string(ast->forbody)); 149 | break; 150 | case AST_RETURN: 151 | string_appendf(buf, "(return %s)", ast_to_string(ast->retval)); 152 | break; 153 | case AST_COMPOUND_STMT: { 154 | string_appendf(buf, "{"); 155 | for (Iter i = list_iter(ast->stmts); !iter_end(i);) { 156 | ast_to_string_int(buf, iter_next(&i)); 157 | string_appendf(buf, ";"); 158 | } 159 | string_appendf(buf, "}"); 160 | break; 161 | } 162 | case AST_STRUCT_REF: 163 | ast_to_string_int(buf, ast->struc); 164 | string_appendf(buf, "."); 165 | string_appendf(buf, ast->field); 166 | break; 167 | case AST_ADDR: 168 | uop_to_string(buf, "addr", ast); 169 | break; 170 | case AST_DEREF: 171 | uop_to_string(buf, "deref", ast); 172 | break; 173 | case PUNCT_INC: 174 | uop_to_string(buf, "++", ast); 175 | break; 176 | case PUNCT_DEC: 177 | uop_to_string(buf, "--", ast); 178 | break; 179 | case PUNCT_LOGAND: 180 | binop_to_string(buf, "and", ast); 181 | break; 182 | case PUNCT_LOGOR: 183 | binop_to_string(buf, "or", ast); 184 | break; 185 | case '!': 186 | uop_to_string(buf, "!", ast); 187 | break; 188 | case '&': 189 | binop_to_string(buf, "&", ast); 190 | break; 191 | case '|': 192 | binop_to_string(buf, "|", ast); 193 | break; 194 | default: { 195 | char *left = ast_to_string(ast->left); 196 | char *right = ast_to_string(ast->right); 197 | if (ast->type == PUNCT_EQ) 198 | string_appendf(buf, "(== "); 199 | else 200 | string_appendf(buf, "(%c ", ast->type); 201 | string_appendf(buf, "%s %s)", left, right); 202 | } 203 | } 204 | } 205 | 206 | char *ast_to_string(Ast *ast) 207 | { 208 | String s = make_string(); 209 | ast_to_string_int(&s, ast); 210 | return get_cstring(s); 211 | } 212 | 213 | char *token_to_string(const Token tok) 214 | { 215 | enum TokenType ttype = get_ttype(tok); 216 | if (ttype == TTYPE_NULL) 217 | return "(null)"; 218 | String s = make_string(); 219 | switch (ttype) { 220 | case TTYPE_NULL: 221 | error("internal error: unknown token type: %d", get_ttype(tok)); 222 | case TTYPE_IDENT: 223 | return get_ident(tok); 224 | case TTYPE_PUNCT: 225 | if (is_punct(tok, PUNCT_EQ)) 226 | string_appendf(&s, "=="); 227 | else 228 | string_appendf(&s, "%c", get_punct(tok)); 229 | return get_cstring(s); 230 | case TTYPE_CHAR: 231 | string_append(&s, get_char(tok)); 232 | return get_cstring(s); 233 | case TTYPE_NUMBER: 234 | return get_number(tok); 235 | case TTYPE_STRING: 236 | string_appendf(&s, "\"%s\"", get_strtok(tok)); 237 | return get_cstring(s); 238 | } 239 | error("internal error: unknown token type: %d", get_ttype(tok)); 240 | return NULL; /* non-reachable */ 241 | } 242 | --------------------------------------------------------------------------------