[
  {
    "path": ".clang-format",
    "content": "BasedOnStyle: Chromium\nLanguage: Cpp\nMaxEmptyLinesToKeep: 3\nIndentCaseLabels: false\nAllowShortIfStatementsOnASingleLine: false\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortLoopsOnASingleLine: false\nDerivePointerAlignment: false\nPointerAlignment: Right\nSpaceAfterCStyleCast: true\nTabWidth: 4\nUseTab: Never\nIndentWidth: 4\nBreakBeforeBraces: Linux\nAccessModifierOffset: -4\nForEachMacros:   \n  - foreach\n  - Q_FOREACH\n  - BOOST_FOREACH\n  - list_for_each\n  - list_for_each_safe\n  - list_for_each_entry\n  - list_for_each_entry_safe\n  - hlist_for_each_entry\n  - rb_list_foreach\n  - rb_list_foreach_safe\n"
  },
  {
    "path": ".gitignore",
    "content": "mzcc\n*.o\n*.o.d\ntmp.*\nnqueen.s\nnqueen\n*.bin\n*.dSYM\n.cbuild\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2019-2020 National Cheng Kung University, Taiwan.\n\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice,\n  this list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "Makefile",
    "content": "TARGET = mzcc\n\nCFLAGS = -Wall -Werror -std=gnu99 -g -I.\n\nUNAME_S := $(shell uname -s)\nifeq ($(UNAME_S),Linux)\nCFLAGS += -no-pie\nendif\n\nSHELL_HACK := $(shell echo CBUILD=\\\"$(CC) $(CFLAGS)\\\" > .cbuild)\n\n# Control the build verbosity\n# `make V=1` is equal to `make VERBOSE=1`\nifeq (\"$(origin V)\", \"command line\")\n    VERBOSE = $(V)\nendif\nifeq (\"$(VERBOSE)\",\"1\")\n    Q :=\n    VECHO = @true\nelse\n    Q := @\n    VECHO = @printf\nendif\n\nOBJS = lexer.o codegen_x64.o parser.o verbose.o main.o\ndeps := $(OBJS:%.o=.%.o.d)\n\n%.o: %.c\n\t$(VECHO) \"  CC\\t$@\\n\"\n\t$(Q)$(CC) -o $@ $(CFLAGS) -c -MMD -MF .$@.d $<\n\n$(TARGET): $(OBJS)\n\t$(VECHO) \"  LD\\t$@\\n\"\n\t$(Q)$(CC) $(CFLAGS) -o $@ $^\n\nTESTS := $(patsubst %.c,%.bin,$(wildcard tests/*.c))\n\nPASS_COLOR = \\e[32;01m\nNO_COLOR = \\e[0m\npass = printf \"[ $(PASS_COLOR)Passed$(NO_COLOR) ]\\n\"\n\ncheck: nqueen $(TESTS)\n\t@echo\n\t@for test in $(TESTS); do                        \\\n\t    printf \"*** verify $$test ***\\n\"\t;        \\\n\t    head -n 1 `echo $$test | sed s/.bin/.c/`;    \\\n\t    ./$$test;                                    \\\n\t    $(call pass,$$test);                         \\\n\t    echo;                                        \\\n\tdone\n\ttests/driver.sh\n\ntests/%.s: tests/%.c $(TARGET)\n\t./mzcc -o $@ $<\n\ntests/%.bin: tests/%.s $(TARGET)\n\t$(VECHO) \"  CC\\t$@\\n\"\n\t$(Q)$(CC) $(CFLAGS) -o $@ $<\n\nnqueen: sample/nqueen.c $(TARGET)\n\t$(VECHO) \"  MazuCC\\t$<\\n\"\n\t$(Q)./mzcc -o ${<:.c=.s} $<\n\t$(VECHO) \"  AS+LD\\t\\t$@\\n\"\n\t$(Q)$(CC) $(CFLAGS) -o sample/nqueen sample/nqueen.s\n\n.PHONY: clean check\nclean:\n\t$(RM) $(TARGET) $(TESTS) $(OBJS) $(deps) .cbuild\n\t$(RM) sample/*.o sample/nqueen.s sample/nqueen\n\n-include $(deps)\n"
  },
  {
    "path": "README.md",
    "content": "# MazuCC\n\nMazuCC is a minimalist C compiler with x86_64 code generation.\nIt is intended to support partial C99 language features while keeping the code\nas small and simple as possible.\n\n\n## Build\n\nRun make to build:\n```shell\n$ make\n```\n\nMazuCC comes with unit tests. To run the tests, give \"check\" as an argument:\n```shell\n$ make check\n```\n\nMazuCC is known to work on both GNU/Linux and macOS.\n\nUse MazuCC to compile C source:\n```shell\n$ ./mzcc  sample/nqueen.c\n```\n\nAlternatively, MazuCC accepts the stream from standard input. The equivalent\n form for the above command is:\n\n ```shell\n $ cat sample/nqueen.c | ./mzcc -\n ```\n\nYou will get the generated x86_64 assembly in AT&T syntax. The output can be\nassembled and linked into a valid executable:\n```shell\n$ ./mzcc -o sample/nqueen.s sample/nqueen.c\n$ gcc -no-pie -o sample/nqueen sample/nqueen.s\n```\n\nIf MazuCC is compiled and executed on macOS, the above argument `-no-pie`\nshould be eliminated.\n\nReference output of MazuCC-compiled `sample/nqueen`:\n```\nQ . . . . . . .\n. . . . Q . . .\n. . . . . . . Q\n. . . . . Q . .\n. . Q . . . . .\n. . . . . . Q .\n. Q . . . . . .\n. . . Q . . . .\n```\n\nAlternatively, you can dump internal abstract syntax tree:\n```shell\necho 'struct {int x; char y; struct { int t; } z; } a;' | ./mzcc --dump-ast -\n```\n\nThe expected output in S-expression form:\n```\n(decl (struct (int)\n              (char)\n              ((struct (int)))) a)\n```\n\n## Acknowledge\n\nMazuCC is heavily inspired by [8cc](https://github.com/rui314/8cc).\n\n## License\n\nMazuCC is freely redistributable under the BSD 2 clause license. Use of\nthis source code is governed by a BSD-style license that can be found in the\nLICENSE file.\n"
  },
  {
    "path": "codegen_x64.c",
    "content": "#include <stdarg.h>\n#include <stdio.h>\n#include \"mzcc.h\"\n\nstatic char *REGS[] = {\"rdi\", \"rsi\", \"rdx\", \"rcx\", \"r8\", \"r9\"};\nstatic int TAB = 8;\nstatic List *functions = &EMPTY_LIST;\n\n/* FIXME: main program should take extern variables from codegen. */\nFILE *outfp;\nstatic int stackpos;\n\nstatic void emit_expr(Ast *ast);\nstatic void emit_load_deref(Ctype *result_type, Ctype *operand_type, int off);\n\n#define emit(...) emitf(__LINE__, \"\\t\" __VA_ARGS__)\n#define emit_label(...) emitf(__LINE__, __VA_ARGS__)\n\n#define UNUSED __attribute__((unused))\n\n#define SAVE                                                     \\\n    int save_hook __attribute__((cleanup(pop_function))) UNUSED; \\\n    list_push(functions, (void *) __func__)\n\nstatic void pop_function(void *ignore UNUSED)\n{\n    list_pop(functions);\n}\n\nstatic char *get_caller_list(void)\n{\n    String s = make_string();\n    for (Iter i = list_iter(functions); !iter_end(i);) {\n        string_appendf(&s, \"%s\", iter_next(&i));\n        if (!iter_end(i))\n            string_appendf(&s, \" -> \");\n    }\n    return get_cstring(s);\n}\n\nstatic void emitf(int line, char *fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n    int col = vfprintf(outfp, fmt, args);\n    va_end(args);\n\n    for (char *p = fmt; *p; p++)\n        if (*p == '\\t')\n            col += TAB - 1;\n    int space = (28 - col) > 0 ? (30 - col) : 2;\n    fprintf(outfp, \"%*c %s:%d\\n\", space, '#', get_caller_list(), line);\n}\n\nstatic char *get_int_reg(Ctype *ctype, char r)\n{\n    assert(r == 'a' || r == 'c');\n    switch (ctype->size) {\n    case 1:\n        return (r == 'a') ? \"al\" : \"cl\";\n    case 4:\n        return (r == 'a') ? \"eax\" : \"ecx\";\n    case 8:\n        return (r == 'a') ? \"rax\" : \"rcx\";\n    default:\n        error(\"Unknown data size: %s: %d\", ctype_to_string(ctype), ctype->size);\n        return 0; /* non-reachable */\n    }\n}\n\nstatic void push_xmm(int reg)\n{\n    SAVE;\n    emit(\"sub $8, %%rsp\");\n    emit(\"movsd %%xmm%d, (%%rsp)\", reg);\n    stackpos += 8;\n}\n\nstatic void pop_xmm(int reg)\n{\n    SAVE;\n    emit(\"movsd (%%rsp), %%xmm%d\", reg);\n    emit(\"add $8, %%rsp\");\n    stackpos -= 8;\n    assert(stackpos >= 0);\n}\n\nstatic void push(char *reg)\n{\n    SAVE;\n    emit(\"push %%%s\", reg);\n    stackpos += 8;\n}\n\nstatic void pop(char *reg)\n{\n    SAVE;\n    emit(\"pop %%%s\", reg);\n    stackpos -= 8;\n    assert(stackpos >= 0);\n}\n\nstatic void emit_gload(Ctype *ctype, char *label, int off)\n{\n    SAVE;\n    if (ctype->type == CTYPE_ARRAY) {\n        if (off)\n            emit(\"lea %s+%d(%%rip), %%rax\", label, off);\n        else\n            emit(\"lea %s(%%rip), %%rax\", label);\n        return;\n    }\n    char *reg = get_int_reg(ctype, 'a');\n    if (ctype->size == 1)\n        emit(\"mov $0, %%eax\");\n    if (off)\n        emit(\"mov %s+%d(%%rip), %%%s\", label, off, reg);\n    else\n        emit(\"mov %s(%%rip), %%%s\", label, reg);\n}\n\nstatic void emit_toint(Ctype *ctype)\n{\n    SAVE;\n    if (!is_flotype(ctype))\n        return;\n    emit(\"cvttsd2si %%xmm0, %%eax\");\n}\n\nstatic void emit_todouble(Ctype *ctype)\n{\n    SAVE;\n    if (is_flotype(ctype))\n        return;\n    emit(\"cvtsi2sd %%eax, %%xmm0\");\n}\n\nstatic void emit_lload(Ctype *ctype, int off)\n{\n    SAVE;\n    if (ctype->type == CTYPE_ARRAY) {\n        emit(\"lea %d(%%rbp), %%rax\", off);\n    } else if (ctype->type == CTYPE_FLOAT) {\n        emit(\"cvtps2pd %d(%%rbp), %%xmm0\", off);\n    } else if (ctype->type == CTYPE_DOUBLE) {\n        emit(\"movsd %d(%%rbp), %%xmm0\", off);\n    } else {\n        char *reg = get_int_reg(ctype, 'a');\n        if (ctype->size == 1)\n            emit(\"mov $0, %%eax\");\n        emit(\"mov %d(%%rbp), %%%s\", off, reg);\n    }\n}\n\nstatic void emit_gsave(char *varname, Ctype *ctype, int off)\n{\n    SAVE;\n    assert(ctype->type != CTYPE_ARRAY);\n    char *reg = get_int_reg(ctype, 'a');\n    if (off)\n        emit(\"mov %%%s, %s+%d(%%rip)\", reg, varname, off);\n    else\n        emit(\"mov %%%s, %s(%%rip)\", reg, varname);\n}\n\nstatic void emit_lsave(Ctype *ctype, int off)\n{\n    SAVE;\n    if (ctype->type == CTYPE_FLOAT) {\n        emit(\"cvtpd2ps %%xmm0, %d(%%rbp)\", off);\n    } else if (ctype->type == CTYPE_DOUBLE) {\n        emit(\"movsd %%xmm0, %d(%%rbp)\", off);\n    } else {\n        char *reg = get_int_reg(ctype, 'a');\n        emit(\"mov %%%s, %d(%%rbp)\", reg, off);\n    }\n}\n\nstatic void emit_assign_deref_int(Ctype *ctype, int off)\n{\n    SAVE;\n    emit(\"mov (%%rsp), %%rcx\");\n    char *reg = get_int_reg(ctype, 'c');\n    if (off)\n        emit(\"mov %%%s, %d(%%rax)\", reg, off);\n    else\n        emit(\"mov %%%s, (%%rax)\", reg);\n    pop(\"rax\");\n}\n\nstatic void emit_assign_deref(Ast *var)\n{\n    SAVE;\n    push(\"rax\");\n    emit_expr(var->operand);\n    emit_assign_deref_int(var->operand->ctype->ptr, 0);\n}\n\nstatic void emit_pointer_arith(char op UNUSED, Ast *left, Ast *right)\n{\n    SAVE;\n    emit_expr(left);\n    push(\"rax\");\n    emit_expr(right);\n    int size = left->ctype->ptr->size;\n    if (size > 1)\n        emit(\"imul $%d, %%rax\", size);\n    emit(\"mov %%rax, %%rcx\");\n    pop(\"rax\");\n    emit(\"add %%rcx, %%rax\");\n}\n\nstatic void emit_assign_struct_ref(Ast *struc, Ctype *field, int off)\n{\n    SAVE;\n    switch (struc->type) {\n    case AST_LVAR:\n        emit_lsave(field, struc->loff + field->offset + off);\n        break;\n    case AST_GVAR:\n        emit_gsave(struc->varname, field, field->offset + off);\n        break;\n    case AST_STRUCT_REF:\n        emit_assign_struct_ref(struc->struc, field, off + struc->ctype->offset);\n        break;\n    case AST_DEREF:\n        push(\"rax\");\n        emit_expr(struc->operand);\n        emit_assign_deref_int(field, field->offset + off);\n        break;\n    default:\n        error(\"internal error: %s\", ast_to_string(struc));\n    }\n}\n\nstatic void emit_load_struct_ref(Ast *struc, Ctype *field, int off)\n{\n    SAVE;\n    switch (struc->type) {\n    case AST_LVAR:\n        emit_lload(field, struc->loff + field->offset + off);\n        break;\n    case AST_GVAR:\n        emit_gload(field, struc->varname, field->offset + off);\n        break;\n    case AST_STRUCT_REF:\n        emit_load_struct_ref(struc->struc, field, struc->ctype->offset + off);\n        break;\n    case AST_DEREF:\n        emit_expr(struc->operand);\n        emit_load_deref(struc->ctype, field, field->offset + off);\n        break;\n    default:\n        error(\"internal error: %s\", ast_to_string(struc));\n    }\n}\n\nstatic void emit_assign(Ast *var)\n{\n    SAVE;\n    switch (var->type) {\n    case AST_DEREF:\n        emit_assign_deref(var);\n        break;\n    case AST_STRUCT_REF:\n        emit_assign_struct_ref(var->struc, var->ctype, 0);\n        break;\n    case AST_LVAR:\n        emit_lsave(var->ctype, var->loff);\n        break;\n    case AST_GVAR:\n        emit_gsave(var->varname, var->ctype, 0);\n        break;\n    default:\n        error(\"internal error\");\n    }\n}\n\nstatic void emit_comp(char *inst, Ast *ast)\n{\n    SAVE;\n    if (is_flotype(ast->ctype)) {\n        emit_expr(ast->left);\n        emit_todouble(ast->left->ctype);\n        push_xmm(0);\n        emit_expr(ast->right);\n        emit_todouble(ast->right->ctype);\n        pop_xmm(1);\n        emit(\"ucomisd %%xmm0, %%xmm1\");\n    } else {\n        emit_expr(ast->left);\n        emit_toint(ast->left->ctype);\n        push(\"rax\");\n        emit_expr(ast->right);\n        emit_toint(ast->right->ctype);\n        pop(\"rcx\");\n        emit(\"cmp %%rax, %%rcx\");\n    }\n    emit(\"%s %%al\", inst);\n    emit(\"movzb %%al, %%eax\");\n}\n\nstatic void emit_binop_int_arith(Ast *ast)\n{\n    SAVE;\n    char *op = NULL;\n    switch (ast->type) {\n    case '+':\n        op = \"add\";\n        break;\n    case '-':\n        op = \"sub\";\n        break;\n    case '*':\n        op = \"imul\";\n        break;\n    case '/':\n        break;\n    case PUNCT_LSHIFT:\n        op = \"sal\";\n        break;\n    case PUNCT_RSHIFT:\n        op = \"sar\";\n        break;\n    default:\n        error(\"invalid operator '%d'\", ast->type);\n    }\n    emit_expr(ast->left);\n    emit_toint(ast->left->ctype);\n    push(\"rax\");\n    emit_expr(ast->right);\n    emit_toint(ast->right->ctype);\n    emit(\"mov %%rax, %%rcx\");\n    pop(\"rax\");\n    if (ast->type == '/') {\n        emit(\"mov $0, %%edx\");\n        emit(\"idiv %%rcx\");\n    } else if (ast->type == PUNCT_LSHIFT || ast->type == PUNCT_RSHIFT) {\n        emit(\"%s %%cl, %%rax\", op);\n    } else {\n        emit(\"%s %%rcx, %%rax\", op);\n    }\n}\n\nstatic void emit_binop_float_arith(Ast *ast)\n{\n    SAVE;\n    char *op;\n    switch (ast->type) {\n    case '+':\n        op = \"addsd\";\n        break;\n    case '-':\n        op = \"subsd\";\n        break;\n    case '*':\n        op = \"mulsd\";\n        break;\n    case '/':\n        op = \"divsd\";\n        break;\n    default:\n        error(\"invalid operator '%d'\", ast->type);\n        return; /* non-reachable */\n    }\n    emit_expr(ast->left);\n    emit_todouble(ast->left->ctype);\n    push_xmm(0);\n    emit_expr(ast->right);\n    emit_todouble(ast->right->ctype);\n    emit(\"movsd %%xmm0, %%xmm1\");\n    pop_xmm(0);\n    emit(\"%s %%xmm1, %%xmm0\", op);\n}\n\nstatic void emit_binop(Ast *ast)\n{\n    SAVE;\n    if (ast->type == '=') {\n        emit_expr(ast->right);\n        if (is_flotype(ast->ctype))\n            emit_todouble(ast->right->ctype);\n        else\n            emit_toint(ast->right->ctype);\n        emit_assign(ast->left);\n        return;\n    }\n    if (ast->type == PUNCT_EQ) {\n        emit_comp(\"sete\", ast);\n        return;\n    }\n    if (ast->ctype->type == CTYPE_PTR) {\n        emit_pointer_arith(ast->type, ast->left, ast->right);\n        return;\n    }\n    switch (ast->type) {\n    case '<':\n        emit_comp(\"setl\", ast);\n        return;\n    case '>':\n        emit_comp(\"setg\", ast);\n        return;\n    }\n    if (is_inttype(ast->ctype))\n        emit_binop_int_arith(ast);\n    else if (is_flotype(ast->ctype))\n        emit_binop_float_arith(ast);\n    else\n        error(\"internal error\");\n}\n\nstatic void emit_inc_dec(Ast *ast, char *op)\n{\n    SAVE;\n    emit_expr(ast->operand);\n    push(\"rax\");\n    emit(\"%s $1, %%rax\", op);\n    emit_assign(ast->operand);\n    pop(\"rax\");\n}\n\nstatic void emit_load_deref(Ctype *result_type, Ctype *operand_type, int off)\n{\n    SAVE;\n    if (operand_type->type == CTYPE_PTR &&\n        operand_type->ptr->type == CTYPE_ARRAY)\n        return;\n    char *reg = get_int_reg(result_type, 'c');\n    if (result_type->size == 1)\n        emit(\"mov $0, %%ecx\");\n    if (off)\n        emit(\"mov %d(%%rax), %%%s\", off, reg);\n    else\n        emit(\"mov (%%rax), %%%s\", reg);\n    emit(\"mov %%rcx, %%rax\");\n}\n\nstatic void emit_expr(Ast *ast)\n{\n    SAVE;\n    switch (ast->type) {\n    case AST_LITERAL:\n        switch (ast->ctype->type) {\n        case CTYPE_CHAR:\n            emit(\"mov $%d, %%rax\", ast->ival);\n            break;\n        case CTYPE_INT:\n            emit(\"mov $%d, %%eax\", ast->ival);\n            break;\n        case CTYPE_LONG:\n            emit(\"mov $%lu, %%rax\", (unsigned long) ast->ival);\n            break;\n        case CTYPE_FLOAT:\n        case CTYPE_DOUBLE:\n            emit(\"movsd %s(%%rip), %%xmm0\", ast->flabel);\n            break;\n        default:\n            error(\"internal error\");\n        }\n        break;\n    case AST_STRING:\n        emit(\"lea %s(%%rip), %%rax\", ast->slabel);\n        break;\n    case AST_LVAR:\n        emit_lload(ast->ctype, ast->loff);\n        break;\n    case AST_GVAR:\n        emit_gload(ast->ctype, ast->glabel, 0);\n        break;\n    case AST_FUNCALL: {\n        int ireg = 0;\n        int xreg = 0;\n        for (Iter i = list_iter(ast->args); !iter_end(i);) {\n            Ast *v = iter_next(&i);\n            if (is_flotype(v->ctype))\n                push_xmm(xreg++);\n            else\n                push(REGS[ireg++]);\n        }\n        for (Iter i = list_iter(ast->args); !iter_end(i);) {\n            Ast *v = iter_next(&i);\n            emit_expr(v);\n            if (is_flotype(v->ctype))\n                push_xmm(0);\n            else\n                push(\"rax\");\n        }\n        int ir = ireg;\n        int xr = xreg;\n        List *reverse = list_reverse(ast->args);\n        for (Iter i = list_iter(reverse); !iter_end(i);) {\n            Ast *v = iter_next(&i);\n            if (is_flotype(v->ctype))\n                pop_xmm(--xr);\n            else\n                pop(REGS[--ir]);\n        }\n        emit(\"mov $%d, %%eax\", xreg);\n        if (stackpos % 16)\n            emit(\"sub $8, %%rsp\");\n#ifdef __APPLE__\n        emit(\"call _%s\", ast->fname);\n#else\n        emit(\"call %s\", ast->fname);\n#endif\n        if (stackpos % 16)\n            emit(\"add $8, %%rsp\");\n        for (Iter i = list_iter(reverse); !iter_end(i);) {\n            Ast *v = iter_next(&i);\n            if (is_flotype(v->ctype))\n                pop_xmm(--xreg);\n            else\n                pop(REGS[--ireg]);\n        }\n        ListNode *node, *tmp;\n        list_for_each_safe (node, tmp, reverse)\n            free(node);\n        free(reverse);\n        break;\n    }\n    case AST_DECL: {\n        if (!ast->declinit)\n            return;\n        if (ast->declinit->type == AST_ARRAY_INIT) {\n            int off = 0;\n            for (Iter iter = list_iter(ast->declinit->arrayinit);\n                 !iter_end(iter);) {\n                emit_expr(iter_next(&iter));\n                emit_lsave(ast->declvar->ctype->ptr, ast->declvar->loff + off);\n                off += ast->declvar->ctype->ptr->size;\n            }\n        } else if (ast->declvar->ctype->type == CTYPE_ARRAY) {\n            assert(ast->declinit->type == AST_STRING);\n            int i = 0;\n            for (char *p = ast->declinit->sval; *p; p++, i++)\n                emit(\"movb $%d, %d(%%rbp)\", *p, ast->declvar->loff + i);\n            emit(\"movb $0, %d(%%rbp)\", ast->declvar->loff + i);\n        } else if (ast->declinit->type == AST_STRING) {\n            emit_gload(ast->declinit->ctype, ast->declinit->slabel, 0);\n            emit_lsave(ast->declvar->ctype, ast->declvar->loff);\n        } else {\n            emit_expr(ast->declinit);\n            emit_lsave(ast->declvar->ctype, ast->declvar->loff);\n        }\n        return;\n    }\n    case AST_ADDR:\n        switch (ast->operand->type) {\n        case AST_LVAR:\n            emit(\"lea %d(%%rbp), %%rax\", ast->operand->loff);\n            break;\n        case AST_GVAR:\n            emit(\"lea %s(%%rip), %%rax\", ast->operand->glabel);\n            break;\n        default:\n            error(\"internal error\");\n        }\n        break;\n    case AST_DEREF:\n        emit_expr(ast->operand);\n        emit_load_deref(ast->ctype, ast->operand->ctype, 0);\n        break;\n    case AST_IF:\n    case AST_TERNARY: {\n        emit_expr(ast->cond);\n        char *ne = make_label();\n        emit(\"test %%rax, %%rax\");\n        emit(\"je %s\", ne);\n        emit_expr(ast->then);\n        if (ast->els) {\n            char *end = make_label();\n            emit(\"jmp %s\", end);\n            emit(\"%s:\", ne);\n            emit_expr(ast->els);\n            emit(\"%s:\", end);\n        } else {\n            emit(\"%s:\", ne);\n        }\n        break;\n    }\n    case AST_FOR: {\n        if (ast->forinit)\n            emit_expr(ast->forinit);\n        char *begin = make_label();\n        char *end = make_label();\n        emit(\"%s:\", begin);\n        if (ast->forcond) {\n            emit_expr(ast->forcond);\n            emit(\"test %%rax, %%rax\");\n            emit(\"je %s\", end);\n        }\n        emit_expr(ast->forbody);\n        if (ast->forstep)\n            emit_expr(ast->forstep);\n        emit(\"jmp %s\", begin);\n        emit(\"%s:\", end);\n        break;\n    }\n    case AST_RETURN:\n        emit_expr(ast->retval);\n        emit(\"leave\");\n        emit(\"ret\");\n        break;\n    case AST_COMPOUND_STMT:\n        for (Iter i = list_iter(ast->stmts); !iter_end(i);) {\n            emit_expr(iter_next(&i));\n            emit(\"#;\");\n        }\n        break;\n    case AST_STRUCT_REF:\n        emit_load_struct_ref(ast->struc, ast->ctype, 0);\n        break;\n    case PUNCT_INC:\n        emit_inc_dec(ast, \"add\");\n        break;\n    case PUNCT_DEC:\n        emit_inc_dec(ast, \"sub\");\n        break;\n    case '!':\n        emit_expr(ast->operand);\n        emit(\"cmp $0, %%rax\");\n        emit(\"sete %%al\");\n        emit(\"movzb %%al, %%eax\");\n        break;\n    case '&':\n        emit_expr(ast->left);\n        push(\"rax\");\n        emit_expr(ast->right);\n        pop(\"rcx\");\n        emit(\"and %%rcx, %%rax\");\n        break;\n    case '|':\n        emit_expr(ast->left);\n        push(\"rax\");\n        emit_expr(ast->right);\n        pop(\"rcx\");\n        emit(\"or %%rcx, %%rax\");\n        break;\n    case PUNCT_LOGAND: {\n        char *end = make_label();\n        emit_expr(ast->left);\n        emit(\"test %%rax, %%rax\");\n        emit(\"mov $0, %%rax\");\n        emit(\"je %s\", end);\n        emit_expr(ast->right);\n        emit(\"test %%rax, %%rax\");\n        emit(\"mov $0, %%rax\");\n        emit(\"je %s\", end);\n        emit(\"mov $1, %%rax\");\n        emit(\"%s:\", end);\n        break;\n    }\n    case PUNCT_LOGOR: {\n        char *end = make_label();\n        emit_expr(ast->left);\n        emit(\"test %%rax, %%rax\");\n        emit(\"mov $1, %%rax\");\n        emit(\"jne %s\", end);\n        emit_expr(ast->right);\n        emit(\"test %%rax, %%rax\");\n        emit(\"mov $1, %%rax\");\n        emit(\"jne %s\", end);\n        emit(\"mov $0, %%rax\");\n        emit(\"%s:\", end);\n        break;\n    }\n    default:\n        emit_binop(ast);\n    }\n}\n\nstatic void emit_data_int(Ast *data)\n{\n    SAVE;\n    assert(data->ctype->type != CTYPE_ARRAY);\n    switch (data->ctype->size) {\n    case 1:\n        emit(\".byte %d\", data->ival);\n        break;\n    case 4:\n        emit(\".long %d\", data->ival);\n        break;\n    case 8:\n        emit(\".quad %d\", data->ival);\n        break;\n    default:\n        error(\"internal error\");\n    }\n}\n\nstatic void emit_data(Ast *v)\n{\n    SAVE;\n    emit_label(\".global %s\", v->declvar->varname);\n    emit_label(\"%s:\", v->declvar->varname);\n    if (v->declinit->type == AST_ARRAY_INIT) {\n        for (Iter iter = list_iter(v->declinit->arrayinit); !iter_end(iter);) {\n            emit_data_int(iter_next(&iter));\n        }\n        return;\n    }\n    assert(v->declinit->type == AST_LITERAL && is_inttype(v->declinit->ctype));\n    emit_data_int(v->declinit);\n}\n\nstatic void emit_bss(Ast *v)\n{\n    SAVE;\n    emit(\".lcomm %s, %d\", v->declvar->varname, v->declvar->ctype->size);\n}\n\nstatic void emit_global_var(Ast *v)\n{\n    SAVE;\n    if (v->declinit)\n        emit_data(v);\n    else\n        emit_bss(v);\n}\n\nvoid emit_data_section(void)\n{\n    SAVE;\n    emit(\".data\");\n    for (Iter i = list_iter(strings); !iter_end(i);) {\n        Ast *v = iter_next(&i);\n        emit_label(\"%s:\", v->slabel);\n        emit(\".string \\\"%s\\\"\", quote_cstring(v->sval));\n    }\n    for (Iter i = list_iter(flonums); !iter_end(i);) {\n        Ast *v = iter_next(&i);\n        char *label = make_label();\n        v->flabel = label;\n        emit_label(\"%s:\", label);\n        emit(\".long %d\", v->lval[0]);\n        emit(\".long %d\", v->lval[1]);\n    }\n}\n\nstatic int align(int n, int m)\n{\n    int rem = n % m;\n    return (rem == 0) ? n : n - rem + m;\n}\n\nstatic void emit_func_prologue(Ast *func)\n{\n    SAVE;\n    emit(\".text\");\n#ifdef __APPLE__\n    emit_label(\".global _%s\", func->fname);\n    emit_label(\"_%s:\", func->fname);\n#else\n    emit_label(\".global %s\", func->fname);\n    emit_label(\"%s:\", func->fname);\n#endif\n    push(\"rbp\");\n    emit(\"mov %%rsp, %%rbp\");\n    int off = 0;\n    int ireg = 0;\n    int xreg = 0;\n    for (Iter i = list_iter(func->params); !iter_end(i);) {\n        Ast *v = iter_next(&i);\n        if (v->ctype->type == CTYPE_FLOAT) {\n            emit(\"cvtpd2ps %%xmm%d, %%xmm%d\", xreg, xreg);\n            push_xmm(xreg++);\n        } else if (v->ctype->type == CTYPE_DOUBLE) {\n            push_xmm(xreg++);\n        } else {\n            push(REGS[ireg++]);\n        }\n        off -= align(v->ctype->size, 8);\n        v->loff = off;\n    }\n    for (Iter i = list_iter(func->localvars); !iter_end(i);) {\n        Ast *v = iter_next(&i);\n        off -= align(v->ctype->size, 8);\n        v->loff = off;\n    }\n    if (off)\n        emit(\"add $%d, %%rsp\", off);\n    stackpos += -(off - 8);\n}\n\nstatic void emit_func_epilogue(void)\n{\n    SAVE;\n    emit(\"leave\");\n    emit(\"ret\");\n}\n\nvoid emit_toplevel(Ast *v)\n{\n    stackpos = 0;\n    if (v->type == AST_FUNC) {\n        emit_func_prologue(v);\n        emit_expr(v->body);\n        emit_func_epilogue();\n    } else if (v->type == AST_DECL) {\n        emit_global_var(v);\n    } else {\n        error(\"internal error\");\n    }\n}\n"
  },
  {
    "path": "dict.h",
    "content": "#ifndef MAZUCC_DICT_H\n#define MAZUCC_DICT_H\n\n#include <stdlib.h>\n#include <string.h>\n#include \"list.h\"\n\ntypedef struct Dict {\n    List *list;\n    struct Dict *parent;\n} Dict;\n\n#define EMPTY_DICT ((Dict){&EMPTY_LIST, NULL})\n\ntypedef struct {\n    char *key;\n    void *val;\n} DictEntry;\n\nstatic inline void *make_dict(void *parent)\n{\n    Dict *r = malloc(sizeof(Dict));\n    r->list = make_list();\n    r->parent = parent;\n    return r;\n}\n\nstatic inline void *dict_get(Dict *dict, char *key)\n{\n    for (; dict; dict = dict->parent) {\n        for (Iter i = list_iter(dict->list); !iter_end(i);) {\n            DictEntry *e = iter_next(&i);\n            if (!strcmp(key, e->key))\n                return e->val;\n        }\n    }\n    return NULL;\n}\n\nstatic inline void dict_put(Dict *dict, char *key, void *val)\n{\n    DictEntry *e = malloc(sizeof(DictEntry));\n    e->key = key;\n    e->val = val;\n    list_push(dict->list, e);\n}\n\nstatic inline List *dict_keys(Dict *dict)\n{\n    List *r = make_list();\n    for (; dict; dict = dict->parent)\n        for (Iter i = list_iter(dict->list); !iter_end(i);)\n            list_push(r, ((DictEntry *) iter_next(&i))->key);\n    return r;\n}\n\nstatic inline List *dict_values(Dict *dict)\n{\n    List *r = make_list();\n    for (; dict; dict = dict->parent)\n        for (Iter i = list_iter(dict->list); !iter_end(i);)\n            list_push(r, ((DictEntry *) iter_next(&i))->val);\n    return r;\n}\n\nstatic inline void *dict_parent(Dict *dict)\n{\n    void *r = dict->parent;\n    list_free(dict->list);\n    free(dict->list);\n    free(dict);\n    return r;\n}\n\n#endif /* MAZUCC_DICT_H */\n"
  },
  {
    "path": "lexer.c",
    "content": "#include <ctype.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include \"mzcc.h\"\n\n#define make_null(x) make_token(TTYPE_NULL, (uintptr_t) 0)\n#define make_strtok(x) make_token(TTYPE_STRING, (uintptr_t) get_cstring(x))\n#define make_ident(x) make_token(TTYPE_IDENT, (uintptr_t) get_cstring(x))\n#define make_punct(x) make_token(TTYPE_PUNCT, (uintptr_t)(x))\n#define make_number(x) make_token(TTYPE_NUMBER, (uintptr_t)(x))\n#define make_char(x) make_token(TTYPE_CHAR, (uintptr_t)(x))\n\nstatic bool ungotten = false;\nstatic Token ungotten_buf = {0};\n\nstatic Token make_token(enum TokenType type, uintptr_t data)\n{\n    return (Token){\n        .type = type,\n        .priv = data,\n    };\n}\n\nstatic int getc_nonspace(void)\n{\n    int c;\n    while ((c = getc(stdin)) != EOF) {\n        if (isspace(c) || c == '\\n' || c == '\\r')\n            continue;\n        return c;\n    }\n    return EOF;\n}\n\nstatic Token read_number(char c)\n{\n    String s = make_string();\n    string_append(&s, c);\n    while (1) {\n        int c = getc(stdin);\n        if (!isdigit(c) && !isalpha(c) && c != '.') {\n            ungetc(c, stdin);\n            return make_number(get_cstring(s));\n        }\n        string_append(&s, c);\n    }\n}\n\nstatic Token read_char(void)\n{\n    char c = getc(stdin);\n    if (c == EOF)\n        goto err;\n    if (c == '\\\\') {\n        c = getc(stdin);\n        if (c == EOF)\n            goto err;\n    }\n    char c2 = getc(stdin);\n    if (c2 == EOF)\n        goto err;\n    if (c2 != '\\'')\n        error(\"Malformed char literal\");\n    return make_char(c);\nerr:\n    error(\"Unterminated char\");\n    return make_null(); /* non-reachable */\n}\n\nstatic Token read_string(void)\n{\n    String s = make_string();\n    while (1) {\n        int c = getc(stdin);\n        if (c == EOF)\n            error(\"Unterminated string\");\n        if (c == '\"')\n            break;\n        if (c == '\\\\') {\n            c = getc(stdin);\n            switch (c) {\n            case EOF:\n                error(\"Unterminated \\\\\");\n            case '\\\"':\n                break;\n            case 'n':\n                c = '\\n';\n                break;\n            default:\n                error(\"Unknown quote: %c\", c);\n            }\n        }\n        string_append(&s, c);\n    }\n    return make_strtok(s);\n}\n\nstatic Token read_ident(char c)\n{\n    String s = make_string();\n    string_append(&s, c);\n    while (1) {\n        int c2 = getc(stdin);\n        if (isalnum(c2) || c2 == '_') {\n            string_append(&s, c2);\n        } else {\n            ungetc(c2, stdin);\n            return make_ident(s);\n        }\n    }\n}\n\nstatic void skip_line_comment(void)\n{\n    while (1) {\n        int c = getc(stdin);\n        if (c == '\\n' || c == EOF)\n            return;\n    }\n}\n\nstatic void skip_block_comment(void)\n{\n    enum { in_comment, asterisk_read } state = in_comment;\n    while (1) {\n        int c = getc(stdin);\n        if (state == in_comment) {\n            if (c == '*')\n                state = asterisk_read;\n        } else if (c == '/') {\n            return;\n        }\n    }\n}\n\nstatic Token read_rep(int expect, int t1, int t2)\n{\n    int c = getc(stdin);\n    if (c == expect)\n        return make_punct(t2);\n    ungetc(c, stdin);\n    return make_punct(t1);\n}\n\nstatic Token read_token_int(void)\n{\n    int c = getc_nonspace();\n    switch (c) {\n    case '0' ... '9':\n        return read_number(c);\n    case 'a' ... 'z':\n    case 'A' ... 'Z':\n    case '_':\n        return read_ident(c);\n    case '/': {\n        c = getc(stdin);\n        if (c == '/') {\n            skip_line_comment();\n            return read_token_int();\n        }\n        if (c == '*') {\n            skip_block_comment();\n            return read_token_int();\n        }\n        ungetc(c, stdin);\n        return make_punct('/');\n    }\n    case '*':\n    case '(':\n    case ')':\n    case ',':\n    case ';':\n    case '.':\n    case '[':\n    case ']':\n    case '{':\n    case '}':\n    case '!':\n    case '?':\n    case ':':\n        return make_punct(c);\n    case '-':\n        c = getc(stdin);\n        if (c == '-')\n            return make_punct(PUNCT_DEC);\n        if (c == '>')\n            return make_punct(PUNCT_ARROW);\n        ungetc(c, stdin);\n        return make_punct('-');\n    case '=':\n        return read_rep('=', '=', PUNCT_EQ);\n    case '+':\n        return read_rep('+', '+', PUNCT_INC);\n    case '&':\n        return read_rep('&', '&', PUNCT_LOGAND);\n    case '|':\n        return read_rep('|', '|', PUNCT_LOGOR);\n    case '<':\n        return read_rep('<', '<', PUNCT_LSHIFT);\n    case '>':\n        return read_rep('>', '>', PUNCT_RSHIFT);\n    case '\"':\n        return read_string();\n    case '\\'':\n        return read_char();\n    case EOF:\n        return make_null();\n    default:\n        error(\"Unexpected character: '%c'\", c);\n        return make_null(); /* non-reachable */\n    }\n}\n\nbool is_punct(const Token tok, int c)\n{\n    return (get_ttype(tok) == TTYPE_PUNCT) && (get_punct(tok) == c);\n}\n\nvoid unget_token(const Token tok)\n{\n    if (get_ttype(tok) == TTYPE_NULL)\n        return;\n    if (ungotten)\n        error(\"Push back buffer is already full\");\n    ungotten = true;\n    ungotten_buf = make_token(tok.type, tok.priv);\n}\n\nToken peek_token(void)\n{\n    Token tok = read_token();\n    unget_token(tok);\n    return tok;\n}\n\nToken read_token(void)\n{\n    if (ungotten) {\n        ungotten = false;\n        return make_token(ungotten_buf.type, ungotten_buf.priv);\n    }\n    return read_token_int();\n}\n"
  },
  {
    "path": "list.h",
    "content": "#ifndef MAZUCC_LIST_H\n#define MAZUCC_LIST_H\n\n#include <stdbool.h>\n#include <stdlib.h>\n\ntypedef struct __ListNode {\n    void *elem;\n    struct __ListNode *next, *prev;\n} ListNode;\n\ntypedef struct {\n    int len;\n    ListNode *head, *tail;\n} List;\n\ntypedef struct {\n    ListNode *ptr;\n} Iter;\n\n#define EMPTY_LIST ((List){.len = 0, .head = NULL, .tail = NULL})\n\nstatic inline List *make_list(void)\n{\n    List *r = malloc(sizeof(List));\n    r->len = 0;\n    r->head = r->tail = NULL;\n    return r;\n}\n\nstatic inline void *make_node(void *elem)\n{\n    ListNode *r = malloc(sizeof(ListNode));\n    r->elem = elem;\n    r->next = NULL;\n    r->prev = NULL;\n    return r;\n}\n\nstatic inline void list_push(List *list, void *elem)\n{\n    ListNode *node = make_node(elem);\n    if (!list->head) {\n        list->head = node;\n    } else {\n        list->tail->next = node;\n        node->prev = list->tail;\n    }\n    list->tail = node;\n    list->len++;\n}\n\nstatic inline void *list_pop(List *list)\n{\n    if (!list->head)\n        return NULL;\n    ListNode *tail = list->tail;\n    void *r = tail->elem;\n    list->tail = tail->prev;\n    if (list->tail)\n        list->tail->next = NULL;\n    else\n        list->head = NULL;\n    free(tail);\n    return r;\n}\n\nstatic void list_unshift(List *list, void *elem)\n{\n    ListNode *node = make_node(elem);\n    node->next = list->head;\n    if (list->head)\n        list->head->prev = node;\n    list->head = node;\n    if (!list->tail)\n        list->tail = node;\n    list->len++;\n}\n\nstatic inline Iter list_iter(void *ptr)\n{\n    return (Iter){\n        .ptr = ((List *) ptr)->head,\n    };\n}\n\nstatic inline bool iter_end(const Iter iter)\n{\n    return !iter.ptr;\n}\n\nstatic inline void *iter_next(Iter *iter)\n{\n    if (!iter->ptr)\n        return NULL;\n    void *r = iter->ptr->elem;\n    iter->ptr = iter->ptr->next;\n    return r;\n}\n\nstatic inline List *list_reverse(List *list)\n{\n    List *r = make_list();\n    for (Iter i = list_iter(list); !iter_end(i);)\n        list_unshift(r, iter_next(&i));\n    return r;\n}\n\nstatic inline int list_len(List *list)\n{\n    return list->len;\n}\n\n#define list_safe_next(node) ((node) ? (node)->next : NULL)\n#define list_for_each_safe(node, tmp, list)                           \\\n    for ((node) = (list)->head, (tmp) = list_safe_next(node); (node); \\\n         (node) = (tmp), (tmp) = list_safe_next(node))\nstatic inline void list_free(List *list)\n{\n    ListNode *node, *tmp;\n    list_for_each_safe (node, tmp, list) {\n        free(node->elem);\n        free(node);\n    }\n}\n#endif /* MAZUCC_LIST_H */\n"
  },
  {
    "path": "main.c",
    "content": "#include <stdbool.h>\n#include <stdio.h>\n#include <string.h>\n#include \"mzcc.h\"\n\nstatic char *outfile = NULL, *infile = NULL;\nextern FILE *outfp;\nstatic bool dump_ast;\n\nstatic void usage()\n{\n    fprintf(stdout,\n            \"mzcc [options] filename\\n\"\n            \"OPTIONS\\n\"\n            \"  -o filename            Write output to the specified file.\\n\"\n            \"  --dump-ast             Dump abstract syntax tree(AST)\\n\");\n}\n\nstatic void print_usage_and_exit()\n{\n    usage();\n    exit(1);\n}\n\nstatic void parse_args(int argc, char **argv)\n{\n    if (argc < 2) {\n        print_usage_and_exit();\n    }\n\n    while (true) {\n        argc--;\n        argv++;\n        if (!argc) {\n            break;\n        }\n\n        if ((*argv)[0] == '-') {\n            switch ((*argv)[1]) {\n            case '\\0':\n                infile = \"/dev/stdin\";\n                break;\n            case 'o':\n                argc--;\n                argv++;\n                outfile = *argv;\n                break;\n            case '-':\n                if (!strcmp(*argv, \"--dump-ast\")) {\n                    dump_ast = true;\n                    break;\n                }\n            default:\n                print_usage_and_exit();\n            }\n        } else {\n            if (infile) {\n                // The second non-option argument is not what we expect.\n                print_usage_and_exit();\n            }\n            infile = argv[0];\n        }\n    }\n}\n\nstatic void open_output_file()\n{\n    if (outfile) {\n        if (!(outfp = fopen(outfile, \"w\"))) {\n            printf(\"Can not open file %s\\n\", outfile);\n            exit(1);\n        }\n    } else {\n        outfp = stdout;\n    }\n}\n\nstatic void open_input_file()\n{\n    if (!infile) {\n        printf(\"Input file is not specified\\n\\n\");\n        print_usage_and_exit();\n    }\n\n    if (!freopen(infile, \"r\", stdin)) {\n        printf(\"Can not open file %s\\n\", infile);\n        exit(1);\n    }\n}\n\nint main(int argc, char **argv)\n{\n    parse_args(argc, argv);\n    open_input_file();\n    open_output_file();\n\n    List *toplevels = read_toplevels();\n    if (!dump_ast)\n        emit_data_section();\n\n    for (Iter i = list_iter(toplevels); !iter_end(i);) {\n        Ast *v = iter_next(&i);\n        if (dump_ast)\n            printf(\"%s\", ast_to_string(v));\n        else\n            emit_toplevel(v);\n    }\n    list_free(cstrings);\n    list_free(ctypes);\n    return 0;\n}\n"
  },
  {
    "path": "mzcc.h",
    "content": "#ifndef MAZUCC_H\n#define MAZUCC_H\n\n#include <stdbool.h>\n#include <stdint.h>\n#include \"dict.h\"\n#include \"list.h\"\n#include \"util.h\"\n\nenum TokenType {\n    TTYPE_NULL,\n    TTYPE_IDENT,\n    TTYPE_PUNCT,\n    TTYPE_NUMBER,\n    TTYPE_CHAR,\n    TTYPE_STRING,\n};\n\ntypedef struct {\n    int type;\n    uintptr_t priv;\n} Token;\n\nenum {\n    AST_LITERAL = 256,\n    AST_STRING,\n    AST_LVAR,\n    AST_GVAR,\n    AST_FUNCALL,\n    AST_FUNC,\n    AST_DECL,\n    AST_ARRAY_INIT,\n    AST_ADDR,\n    AST_DEREF,\n    AST_IF,\n    AST_TERNARY,\n    AST_FOR,\n    AST_RETURN,\n    AST_COMPOUND_STMT,\n    AST_STRUCT_REF,\n    PUNCT_EQ,\n    PUNCT_INC,\n    PUNCT_DEC,\n    PUNCT_LOGAND,\n    PUNCT_LOGOR,\n    PUNCT_ARROW,\n    PUNCT_LSHIFT,\n    PUNCT_RSHIFT,\n};\n\nenum {\n    CTYPE_VOID,\n    CTYPE_CHAR,\n    CTYPE_INT,\n    CTYPE_LONG,\n    CTYPE_FLOAT,\n    CTYPE_DOUBLE,\n    CTYPE_ARRAY,\n    CTYPE_PTR,\n    CTYPE_STRUCT,\n};\n\ntypedef struct __Ctype {\n    int type;\n    int size;\n    struct __Ctype *ptr; /* pointer or array */\n    int len;             /* array length */\n    /* struct */\n    Dict *fields;\n    int offset;\n} Ctype;\n\ntypedef struct __Ast {\n    int type;\n    Ctype *ctype;\n    union {\n        /* char, int, or long */\n        long ival;\n\n        /* float or double */\n        struct {\n            union {\n                double fval;\n                int lval[2];\n            };\n            char *flabel;\n        };\n\n        /* string literal */\n        struct {\n            char *sval;\n            char *slabel;\n        };\n\n        /* Local/global variable */\n        struct {\n            char *varname;\n            struct {\n                int loff;\n                char *glabel;\n            };\n        };\n\n        /* Binary operator */\n        struct {\n            struct __Ast *left;\n            struct __Ast *right;\n        };\n\n        /* Unary operator */\n        struct {\n            struct __Ast *operand;\n        };\n\n        /* Function call or function declaration */\n        struct {\n            char *fname;\n            struct {\n                List *args;\n                struct {\n                    List *params;\n                    List *localvars;\n                    struct __Ast *body;\n                };\n            };\n        };\n\n        /* Declaration */\n        struct {\n            struct __Ast *declvar;\n            struct __Ast *declinit;\n        };\n\n        /* Array initializer */\n        List *arrayinit;\n\n        /* if statement or ternary operator */\n        struct {\n            struct __Ast *cond;\n            struct __Ast *then;\n            struct __Ast *els;\n        };\n\n        /* for statement */\n        struct {\n            struct __Ast *forinit;\n            struct __Ast *forcond;\n            struct __Ast *forstep;\n            struct __Ast *forbody;\n        };\n\n        /* return statement */\n        struct __Ast *retval;\n\n        /* Compound statement */\n        List *stmts;\n\n        /* Struct reference */\n        struct {\n            struct __Ast *struc;\n            char *field; /* specific to ast_to_string only */\n        };\n    };\n} Ast;\n\n/* verbose.c */\nextern char *token_to_string(const Token tok);\nextern char *ast_to_string(Ast *ast);\nextern char *ctype_to_string(Ctype *ctype);\n\n/* lexer.c */\nextern bool is_punct(const Token tok, int c);\nextern void unget_token(const Token tok);\nextern Token peek_token(void);\nextern Token read_token(void);\n\n#define get_priv(tok, type)                                       \\\n    ({                                                            \\\n        assert(__builtin_types_compatible_p(typeof(tok), Token)); \\\n        ((type) tok.priv);                                        \\\n    })\n\n#define get_ttype(tok)                                            \\\n    ({                                                            \\\n        assert(__builtin_types_compatible_p(typeof(tok), Token)); \\\n        (tok.type);                                               \\\n    })\n\n#define get_token(tok, ttype, priv_type) \\\n    ({                                   \\\n        assert(get_ttype(tok) == ttype); \\\n        get_priv(tok, priv_type);        \\\n    })\n\n#define get_char(tok) get_token(tok, TTYPE_CHAR, char)\n#define get_strtok(tok) get_token(tok, TTYPE_STRING, char *)\n#define get_ident(tok) get_token(tok, TTYPE_IDENT, char *)\n#define get_number(tok) get_token(tok, TTYPE_NUMBER, char *)\n#define get_punct(tok) get_token(tok, TTYPE_PUNCT, int)\n\n/* parser.c */\nextern List *strings;\nextern List *flonums;\nextern List *ctypes;\nextern char *make_label(void);\nextern List *read_toplevels(void);\nextern bool is_inttype(Ctype *ctype);\nextern bool is_flotype(Ctype *ctype);\n\n/* codegen_x64.c */\nextern void emit_data_section(void);\nextern void emit_toplevel(Ast *v);\n\n#endif /* MAZUCC_H */\n"
  },
  {
    "path": "parser.c",
    "content": "#include <ctype.h>\n#include <limits.h>\n#include <setjmp.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"mzcc.h\"\n\n#define MAX_ARGS 6\n#define MAX_OP_PRIO 16\n#define MAX_ALIGN 16\n\nList *ctypes = &EMPTY_LIST;\nList *strings = &EMPTY_LIST;\nList *flonums = &EMPTY_LIST;\n\nstatic Dict *globalenv = &EMPTY_DICT;\nstatic Dict *localenv = NULL;\nstatic Dict *struct_defs = &EMPTY_DICT;\nstatic Dict *union_defs = &EMPTY_DICT;\nstatic List *localvars = NULL;\n\nstatic Ctype *ctype_void = &(Ctype){CTYPE_VOID, 0, NULL};\nstatic Ctype *ctype_int = &(Ctype){CTYPE_INT, 4, NULL};\nstatic Ctype *ctype_long = &(Ctype){CTYPE_LONG, 8, NULL};\nstatic Ctype *ctype_char = &(Ctype){CTYPE_CHAR, 1, NULL};\nstatic Ctype *ctype_float = &(Ctype){CTYPE_FLOAT, 4, NULL};\nstatic Ctype *ctype_double = &(Ctype){CTYPE_DOUBLE, 8, NULL};\n\nstatic int labelseq = 0;\n\nstatic Ast *read_expr(void);\nstatic Ctype *make_ptr_type(Ctype *ctype);\nstatic Ctype *make_array_type(Ctype *ctype, int size);\nstatic Ast *read_compound_stmt(void);\nstatic Ast *read_decl_or_stmt(void);\nstatic Ctype *result_type(char op, Ctype *a, Ctype *b);\nstatic Ctype *convert_array(Ctype *ctype);\nstatic Ast *read_stmt(void);\nstatic Ctype *read_decl_int(Token *name);\n\nstatic Ast *ast_uop(int type, Ctype *ctype, Ast *operand)\n{\n    Ast *r = malloc(sizeof(Ast));\n    r->type = type;\n    r->ctype = ctype;\n    r->operand = operand;\n    return r;\n}\n\nstatic Ast *ast_binop(int type, Ast *left, Ast *right)\n{\n    Ast *r = malloc(sizeof(Ast));\n    r->type = type;\n    r->ctype = result_type(type, left->ctype, right->ctype);\n    if (type != '=' && convert_array(left->ctype)->type != CTYPE_PTR &&\n        convert_array(right->ctype)->type == CTYPE_PTR) {\n        r->left = right;\n        r->right = left;\n    } else {\n        r->left = left;\n        r->right = right;\n    }\n    return r;\n}\n\nstatic Ast *ast_inttype(Ctype *ctype, long val)\n{\n    Ast *r = malloc(sizeof(Ast));\n    r->type = AST_LITERAL;\n    r->ctype = ctype;\n    r->ival = val;\n    return r;\n}\n\nstatic Ast *ast_double(double val)\n{\n    Ast *r = malloc(sizeof(Ast));\n    r->type = AST_LITERAL;\n    r->ctype = ctype_double;\n    r->fval = val;\n    list_push(flonums, r);\n    return r;\n}\n\nchar *make_label(void)\n{\n    String s = make_string();\n    string_appendf(&s, \".L%d\", labelseq++);\n    return get_cstring(s);\n}\n\nstatic Ast *ast_lvar(Ctype *ctype, char *name)\n{\n    Ast *r = malloc(sizeof(Ast));\n    r->type = AST_LVAR;\n    r->ctype = ctype;\n    r->varname = name;\n    dict_put(localenv, name, r);\n    if (localvars)\n        list_push(localvars, r);\n    return r;\n}\n\nstatic Ast *ast_gvar(Ctype *ctype, char *name, bool filelocal)\n{\n    Ast *r = malloc(sizeof(Ast));\n    r->type = AST_GVAR;\n    r->ctype = ctype;\n    r->varname = name;\n    r->glabel = filelocal ? make_label() : name;\n    dict_put(globalenv, name, r);\n    return r;\n}\n\nstatic Ast *ast_string(char *str)\n{\n    Ast *r = malloc(sizeof(Ast));\n    r->type = AST_STRING;\n    r->ctype = make_array_type(ctype_char, strlen(str) + 1);\n    r->sval = str;\n    r->slabel = make_label();\n    return r;\n}\n\nstatic Ast *ast_funcall(Ctype *ctype, char *fname, List *args)\n{\n    Ast *r = malloc(sizeof(Ast));\n    r->type = AST_FUNCALL;\n    r->ctype = ctype;\n    r->fname = fname;\n    r->args = args;\n    return r;\n}\n\nstatic Ast *ast_func(Ctype *rettype,\n                     char *fname,\n                     List *params,\n                     Ast *body,\n                     List *localvars)\n{\n    Ast *r = malloc(sizeof(Ast));\n    r->type = AST_FUNC;\n    r->ctype = rettype;\n    r->fname = fname;\n    r->params = params;\n    r->localvars = localvars;\n    r->body = body;\n    return r;\n}\n\nstatic Ast *ast_decl(Ast *var, Ast *init)\n{\n    Ast *r = malloc(sizeof(Ast));\n    r->type = AST_DECL;\n    r->ctype = NULL;\n    r->declvar = var;\n    r->declinit = init;\n    return r;\n}\n\nstatic Ast *ast_array_init(List *arrayinit)\n{\n    Ast *r = malloc(sizeof(Ast));\n    r->type = AST_ARRAY_INIT;\n    r->ctype = NULL;\n    r->arrayinit = arrayinit;\n    return r;\n}\n\nstatic Ast *ast_if(Ast *cond, Ast *then, Ast *els)\n{\n    Ast *r = malloc(sizeof(Ast));\n    r->type = AST_IF;\n    r->ctype = NULL;\n    r->cond = cond;\n    r->then = then;\n    r->els = els;\n    return r;\n}\n\nstatic Ast *ast_ternary(Ctype *ctype, Ast *cond, Ast *then, Ast *els)\n{\n    Ast *r = malloc(sizeof(Ast));\n    r->type = AST_TERNARY;\n    r->ctype = ctype;\n    r->cond = cond;\n    r->then = then;\n    r->els = els;\n    return r;\n}\n\nstatic Ast *ast_for(Ast *init, Ast *cond, Ast *step, Ast *body)\n{\n    Ast *r = malloc(sizeof(Ast));\n    r->type = AST_FOR;\n    r->ctype = NULL;\n    r->forinit = init;\n    r->forcond = cond;\n    r->forstep = step;\n    r->forbody = body;\n    return r;\n}\n\nstatic Ast *ast_return(Ast *retval)\n{\n    Ast *r = malloc(sizeof(Ast));\n    r->type = AST_RETURN;\n    r->ctype = NULL;\n    r->retval = retval;\n    return r;\n}\n\nstatic Ast *ast_compound_stmt(List *stmts)\n{\n    Ast *r = malloc(sizeof(Ast));\n    r->type = AST_COMPOUND_STMT;\n    r->ctype = NULL;\n    r->stmts = stmts;\n    return r;\n}\n\nstatic Ast *ast_struct_ref(Ctype *ctype, Ast *struc, char *name)\n{\n    Ast *r = malloc(sizeof(Ast));\n    r->type = AST_STRUCT_REF;\n    r->ctype = ctype;\n    r->struc = struc;\n    r->field = name;\n    return r;\n}\n\nstatic Ctype *make_ptr_type(Ctype *ctype)\n{\n    Ctype *r = malloc(sizeof(Ctype));\n    r->type = CTYPE_PTR;\n    r->ptr = ctype;\n    r->size = 8;\n    list_push(ctypes, r);\n    return r;\n}\n\nstatic Ctype *make_array_type(Ctype *ctype, int len)\n{\n    Ctype *r = malloc(sizeof(Ctype));\n    r->type = CTYPE_ARRAY;\n    r->ptr = ctype;\n    r->size = (len < 0) ? -1 : ctype->size * len;\n    r->len = len;\n    list_push(ctypes, r);\n    return r;\n}\n\nstatic Ctype *make_struct_field_type(Ctype *ctype, int offset)\n{\n    Ctype *r = malloc(sizeof(Ctype));\n    memcpy(r, ctype, sizeof(Ctype));\n    r->offset = offset;\n    list_push(ctypes, r);\n    return r;\n}\n\nstatic Ctype *make_struct_type(Dict *fields, int size)\n{\n    Ctype *r = malloc(sizeof(Ctype));\n    r->type = CTYPE_STRUCT;\n    r->fields = fields;\n    r->size = size;\n    list_push(ctypes, r);\n    return r;\n}\n\nbool is_inttype(Ctype *ctype)\n{\n    return ctype->type == CTYPE_CHAR || ctype->type == CTYPE_INT ||\n           ctype->type == CTYPE_LONG;\n}\n\nbool is_flotype(Ctype *ctype)\n{\n    return ctype->type == CTYPE_FLOAT || ctype->type == CTYPE_DOUBLE;\n}\n\nstatic void ensure_lvalue(Ast *ast)\n{\n    switch (ast->type) {\n    case AST_LVAR:\n    case AST_GVAR:\n    case AST_DEREF:\n    case AST_STRUCT_REF:\n        return;\n    default:\n        error(\"lvalue expected, but got %s\", ast_to_string(ast));\n    }\n}\n\nstatic void expect(char punct)\n{\n    Token tok = read_token();\n    if (!is_punct(tok, punct))\n        error(\"'%c' expected, but got %s\", punct, token_to_string(tok));\n}\n\nstatic bool is_ident(const Token tok, char *s)\n{\n    return get_ttype(tok) == TTYPE_IDENT && !strcmp(get_ident(tok), s);\n}\n\nstatic bool is_right_assoc(const Token tok)\n{\n    return get_punct(tok) == '=';\n}\n\nstatic int eval_intexpr(Ast *ast)\n{\n    switch (ast->type) {\n    case AST_LITERAL:\n        if (is_inttype(ast->ctype))\n            return ast->ival;\n        error(\"Integer expression expected, but got %s\", ast_to_string(ast));\n    case '+':\n        return eval_intexpr(ast->left) + eval_intexpr(ast->right);\n    case '-':\n        return eval_intexpr(ast->left) - eval_intexpr(ast->right);\n    case '*':\n        return eval_intexpr(ast->left) * eval_intexpr(ast->right);\n    case '/':\n        return eval_intexpr(ast->left) / eval_intexpr(ast->right);\n    case PUNCT_LSHIFT:\n        return eval_intexpr(ast->left) << eval_intexpr(ast->right);\n    case PUNCT_RSHIFT:\n        return eval_intexpr(ast->left) >> eval_intexpr(ast->right);\n    default:\n        error(\"Integer expression expected, but got %s\", ast_to_string(ast));\n        return 0; /* non-reachable */\n    }\n}\n\nstatic int priority(const Token tok)\n{\n    switch (get_punct(tok)) {\n    case '[':\n    case '.':\n    case PUNCT_ARROW:\n        return 1;\n    case PUNCT_INC:\n    case PUNCT_DEC:\n        return 2;\n    case '*':\n    case '/':\n        return 3;\n    case '+':\n    case '-':\n        return 4;\n    case PUNCT_LSHIFT:\n    case PUNCT_RSHIFT:\n        return 5;\n    case '<':\n    case '>':\n        return 6;\n    case '&':\n        return 8;\n    case '|':\n        return 10;\n    case PUNCT_EQ:\n        return 7;\n    case PUNCT_LOGAND:\n        return 11;\n    case PUNCT_LOGOR:\n        return 12;\n    case '?':\n        return 13;\n    case '=':\n        return 14;\n    default:\n        return -1;\n    }\n}\n\nstatic Ast *read_func_args(char *fname)\n{\n    List *args = make_list();\n    while (1) {\n        Token tok = read_token();\n        if (is_punct(tok, ')'))\n            break;\n        unget_token(tok);\n        list_push(args, read_expr());\n        tok = read_token();\n        if (is_punct(tok, ')'))\n            break;\n        if (!is_punct(tok, ','))\n            error(\"Unexpected token: '%s'\", token_to_string(tok));\n    }\n    if (MAX_ARGS < list_len(args))\n        error(\"Too many arguments: %s\", fname);\n    return ast_funcall(ctype_int, fname, args);\n}\n\nstatic Ast *read_ident_or_func(char *name)\n{\n    Token tok = read_token();\n    if (is_punct(tok, '('))\n        return read_func_args(name);\n    unget_token(tok);\n    Ast *v = dict_get(localenv, name);\n    if (!v)\n        error(\"Undefined varaible: %s\", name);\n    return v;\n}\n\nstatic bool is_long_token(char *p)\n{\n    for (; *p; p++) {\n        if (!isdigit(*p))\n            return (*p == 'L' || *p == 'l') && p[1] == '\\0';\n    }\n    return false;\n}\n\nstatic bool is_int_token(char *p)\n{\n    for (; *p; p++)\n        if (!isdigit(*p))\n            return false;\n    return true;\n}\n\nstatic bool is_float_token(char *p)\n{\n    for (; *p; p++)\n        if (!isdigit(*p))\n            break;\n    if (*p++ != '.')\n        return false;\n    for (; *p; p++)\n        if (!isdigit(*p))\n            return false;\n    return true;\n}\n\nstatic Ast *read_prim(void)\n{\n    Token tok = read_token();\n    switch (get_ttype(tok)) {\n    case TTYPE_NULL:\n        return NULL;\n    case TTYPE_IDENT:\n        return read_ident_or_func(get_ident(tok));\n    case TTYPE_NUMBER: {\n        char *number = get_number(tok);\n        if (is_long_token(number))\n            return ast_inttype(ctype_long, atol(number));\n        if (is_int_token(number)) {\n            long val = atol(number);\n            if (val & ~(long) UINT_MAX)\n                return ast_inttype(ctype_long, val);\n            return ast_inttype(ctype_int, val);\n        }\n        if (is_float_token(number))\n            return ast_double(atof(number));\n        error(\"Malformed number: %s\", token_to_string(tok));\n    }\n    case TTYPE_CHAR:\n        return ast_inttype(ctype_char, get_char(tok));\n    case TTYPE_STRING: {\n        Ast *r = ast_string(get_strtok(tok));\n        list_push(strings, r);\n        return r;\n    }\n    case TTYPE_PUNCT:\n        unget_token(tok);\n        return NULL;\n    default:\n        error(\"internal error: unknown token type: %d\", get_ttype(tok));\n        return NULL; /* non-reachable */\n    }\n}\n\n#define swap(a, b)         \\\n    {                      \\\n        typeof(a) tmp = b; \\\n        b = a;             \\\n        a = tmp;           \\\n    }\n\nstatic Ctype *result_type_int(jmp_buf *jmpbuf, char op, Ctype *a, Ctype *b)\n{\n    if (a->type > b->type)\n        swap(a, b);\n    if (b->type == CTYPE_PTR) {\n        if (op == '=')\n            return a;\n        if (op != '+' && op != '-')\n            goto err;\n        if (!is_inttype(a))\n            goto err;\n        return b;\n    }\n    switch (a->type) {\n    case CTYPE_VOID:\n        goto err;\n    case CTYPE_CHAR:\n    case CTYPE_INT:\n        switch (b->type) {\n        case CTYPE_CHAR:\n        case CTYPE_INT:\n            return ctype_int;\n        case CTYPE_LONG:\n            return ctype_long;\n        case CTYPE_FLOAT:\n        case CTYPE_DOUBLE:\n            return ctype_double;\n        case CTYPE_ARRAY:\n        case CTYPE_PTR:\n            return b;\n        }\n        error(\"internal error\");\n    case CTYPE_LONG:\n        switch (b->type) {\n        case CTYPE_LONG:\n            return ctype_long;\n        case CTYPE_FLOAT:\n        case CTYPE_DOUBLE:\n            return ctype_double;\n        case CTYPE_ARRAY:\n        case CTYPE_PTR:\n            return b;\n        }\n        error(\"internal error\");\n    case CTYPE_FLOAT:\n        if (b->type == CTYPE_FLOAT || b->type == CTYPE_DOUBLE)\n            return ctype_double;\n        goto err;\n    case CTYPE_DOUBLE:\n        if (b->type == CTYPE_DOUBLE)\n            return ctype_double;\n        goto err;\n    case CTYPE_ARRAY:\n        if (b->type != CTYPE_ARRAY)\n            goto err;\n        return result_type_int(jmpbuf, op, a->ptr, b->ptr);\n    default:\n        error(\"internal error: %s %s\", ctype_to_string(a), ctype_to_string(b));\n    }\nerr:\n    longjmp(*jmpbuf, 1);\n}\n\nstatic Ast *read_subscript_expr(Ast *ast)\n{\n    Ast *sub = read_expr();\n    expect(']');\n    Ast *t = ast_binop('+', ast, sub);\n    return ast_uop(AST_DEREF, t->ctype->ptr, t);\n}\n\nstatic Ctype *convert_array(Ctype *ctype)\n{\n    if (ctype->type != CTYPE_ARRAY)\n        return ctype;\n    return make_ptr_type(ctype->ptr);\n}\n\nstatic Ctype *result_type(char op, Ctype *a, Ctype *b)\n{\n    jmp_buf jmpbuf;\n    if (setjmp(jmpbuf) == 0)\n        return result_type_int(&jmpbuf, op, convert_array(a), convert_array(b));\n    error(\"incompatible operands: %c: <%s> and <%s>\", op, ctype_to_string(a),\n          ctype_to_string(b));\n    return NULL; /* non-reachable */\n}\n\nstatic Ast *read_unary_expr(void)\n{\n    Token tok = read_token();\n    if (get_ttype(tok) != TTYPE_PUNCT) {\n        unget_token(tok);\n        return read_prim();\n    }\n    if (is_punct(tok, '(')) {\n        Ast *r = read_expr();\n        expect(')');\n        return r;\n    }\n    if (is_punct(tok, '&')) {\n        Ast *operand = read_unary_expr();\n        ensure_lvalue(operand);\n        return ast_uop(AST_ADDR, make_ptr_type(operand->ctype), operand);\n    }\n    if (is_punct(tok, '!')) {\n        Ast *operand = read_unary_expr();\n        return ast_uop('!', ctype_int, operand);\n    }\n    if (is_punct(tok, '*')) {\n        Ast *operand = read_unary_expr();\n        Ctype *ctype = convert_array(operand->ctype);\n        if (ctype->type != CTYPE_PTR)\n            error(\"pointer type expected, but got %s\", ast_to_string(operand));\n        if (ctype->ptr == ctype_void)\n            error(\"pointer to void can not be dereferenced, but got %s\",\n                  ast_to_string(operand));\n        return ast_uop(AST_DEREF, operand->ctype->ptr, operand);\n    }\n    unget_token(tok);\n    return read_prim();\n}\n\nstatic Ast *read_cond_expr(Ast *cond)\n{\n    Ast *then = read_expr();\n    expect(':');\n    Ast *els = read_expr();\n    return ast_ternary(then->ctype, cond, then, els);\n}\n\nstatic Ast *read_struct_field(Ast *struc)\n{\n    if (struc->ctype->type != CTYPE_STRUCT)\n        error(\"struct expected, but got %s\", ast_to_string(struc));\n    Token name = read_token();\n    if (get_ttype(name) != TTYPE_IDENT)\n        error(\"field name expected, but got %s\", token_to_string(name));\n    char *ident = get_ident(name);\n    Ctype *field = dict_get(struc->ctype->fields, ident);\n    return ast_struct_ref(field, struc, ident);\n}\n\nstatic Ast *read_expr_int(int prec)\n{\n    Ast *ast = read_unary_expr();\n    if (!ast)\n        return NULL;\n    while (1) {\n        Token tok = read_token();\n        if (get_ttype(tok) != TTYPE_PUNCT) {\n            unget_token(tok);\n            return ast;\n        }\n        int prec2 = priority(tok);\n        if (prec2 < 0 || prec <= prec2) {\n            unget_token(tok);\n            return ast;\n        }\n        if (is_punct(tok, '?')) {\n            ast = read_cond_expr(ast);\n            continue;\n        }\n        if (is_punct(tok, '.')) {\n            ast = read_struct_field(ast);\n            continue;\n        }\n        if (is_punct(tok, PUNCT_ARROW)) {\n            if (ast->ctype->type != CTYPE_PTR)\n                error(\"pointer type expected, but got %s %s\",\n                      ctype_to_string(ast->ctype), ast_to_string(ast));\n            ast = ast_uop(AST_DEREF, ast->ctype->ptr, ast);\n            ast = read_struct_field(ast);\n            continue;\n        }\n        if (is_punct(tok, '[')) {\n            ast = read_subscript_expr(ast);\n            continue;\n        }\n        // this is BUG!! ++ should be in read_unary_expr() , I think.\n        if (is_punct(tok, PUNCT_INC) || is_punct(tok, PUNCT_DEC)) {\n            ensure_lvalue(ast);\n            ast = ast_uop(get_punct(tok), ast->ctype, ast);\n            continue;\n        }\n        if (is_punct(tok, '='))\n            ensure_lvalue(ast);\n        Ast *rest = read_expr_int(prec2 + (is_right_assoc(tok) ? 1 : 0));\n        if (!rest)\n            error(\"second operand missing\");\n        if (is_punct(tok, PUNCT_LSHIFT) || is_punct(tok, PUNCT_RSHIFT)) {\n            if ((ast->ctype != ctype_int && ast->ctype != ctype_char) ||\n                (rest->ctype != ctype_int && rest->ctype != ctype_char))\n                error(\"invalid operand to shift\");\n        }\n        ast = ast_binop(get_punct(tok), ast, rest);\n    }\n}\n\nstatic Ast *read_expr(void)\n{\n    return read_expr_int(MAX_OP_PRIO);\n}\n\nstatic Ctype *get_ctype(const Token tok)\n{\n    if (get_ttype(tok) != TTYPE_IDENT)\n        return NULL;\n    char *ident = get_ident(tok);\n    if (!strcmp(ident, \"void\"))\n        return ctype_void;\n    if (!strcmp(ident, \"int\"))\n        return ctype_int;\n    if (!strcmp(ident, \"long\"))\n        return ctype_long;\n    if (!strcmp(ident, \"char\"))\n        return ctype_char;\n    if (!strcmp(ident, \"float\"))\n        return ctype_float;\n    if (!strcmp(ident, \"double\"))\n        return ctype_double;\n    return NULL;\n}\n\nstatic bool is_type_keyword(const Token tok)\n{\n    return get_ctype(tok) || is_ident(tok, \"struct\") || is_ident(tok, \"union\");\n}\n\nstatic Ast *read_decl_array_init_int(Ctype *ctype)\n{\n    Token tok = read_token();\n    if (ctype->ptr->type == CTYPE_CHAR && get_ttype(tok) == TTYPE_STRING)\n        return ast_string(get_strtok(tok));\n    if (!is_punct(tok, '{'))\n        error(\"Expected an initializer list for %s, but got %s\",\n              ctype_to_string(ctype), token_to_string(tok));\n    List *initlist = make_list();\n    while (1) {\n        Token tok = read_token();\n        if (is_punct(tok, '}'))\n            break;\n        unget_token(tok);\n        Ast *init = read_expr();\n        list_push(initlist, init);\n        result_type('=', init->ctype, ctype->ptr);\n        tok = read_token();\n        if (!is_punct(tok, ','))\n            unget_token(tok);\n    }\n    return ast_array_init(initlist);\n}\n\nstatic char *read_struct_union_tag(void)\n{\n    Token tok = read_token();\n    if (get_ttype(tok) == TTYPE_IDENT)\n        return get_ident(tok);\n    unget_token(tok);\n    return NULL;\n}\n\nstatic Dict *read_struct_union_fields(void)\n{\n    Dict *r = make_dict(NULL);\n    expect('{');\n    while (1) {\n        if (!is_type_keyword(peek_token()))\n            break;\n        Token name;\n        Ctype *fieldtype = read_decl_int(&name);\n        dict_put(r, get_ident(name), make_struct_field_type(fieldtype, 0));\n        expect(';');\n    }\n    expect('}');\n    return r;\n}\n\nstatic Ctype *read_union_def(void)\n{\n    char *tag = read_struct_union_tag();\n    Ctype *ctype = dict_get(union_defs, tag);\n    if (ctype)\n        return ctype;\n    Dict *fields = read_struct_union_fields();\n    int maxsize = 0;\n    for (Iter i = list_iter(dict_values(fields)); !iter_end(i);) {\n        Ctype *fieldtype = iter_next(&i);\n        maxsize = (maxsize < fieldtype->size) ? fieldtype->size : maxsize;\n    }\n    Ctype *r = make_struct_type(fields, maxsize);\n    if (tag)\n        dict_put(union_defs, tag, r);\n    return r;\n}\n\nstatic Ctype *read_struct_def(void)\n{\n    char *tag = read_struct_union_tag();\n    Ctype *ctype = dict_get(struct_defs, tag);\n    if (ctype)\n        return ctype;\n    Dict *fields = read_struct_union_fields();\n    int offset = 0;\n    for (Iter i = list_iter(dict_values(fields)); !iter_end(i);) {\n        Ctype *fieldtype = iter_next(&i);\n        int size = (fieldtype->size < MAX_ALIGN) ? fieldtype->size : MAX_ALIGN;\n        if (offset % size != 0)\n            offset += size - offset % size;\n        fieldtype->offset = offset;\n        offset += fieldtype->size;\n    }\n    Ctype *r = make_struct_type(fields, offset);\n    if (tag)\n        dict_put(struct_defs, tag, r);\n    return r;\n}\n\nstatic Ctype *read_decl_spec(void)\n{\n    Token tok = read_token();\n    Ctype *ctype =\n        is_ident(tok, \"struct\")\n            ? read_struct_def()\n            : is_ident(tok, \"union\") ? read_union_def() : get_ctype(tok);\n    if (!ctype)\n        error(\"Type expected, but got %s\", token_to_string(tok));\n    while (1) {\n        tok = read_token();\n        if (!is_punct(tok, '*')) {\n            unget_token(tok);\n            return ctype;\n        }\n        ctype = make_ptr_type(ctype);\n    }\n}\n\nstatic Ast *read_decl_init_val(Ast *var)\n{\n    if (var->ctype->type == CTYPE_ARRAY) {\n        Ast *init = read_decl_array_init_int(var->ctype);\n        int len = (init->type == AST_STRING) ? strlen(init->sval) + 1\n                                             : list_len(init->arrayinit);\n        if (var->ctype->len == -1) {\n            var->ctype->len = len;\n            var->ctype->size = len * var->ctype->ptr->size;\n        } else if (var->ctype->len != len) {\n            error(\"Invalid array initializer: expected %d items but got %d\",\n                  var->ctype->len, len);\n        }\n        expect(';');\n        return ast_decl(var, init);\n    }\n    Ast *init = read_expr();\n    expect(';');\n    if (var->type == AST_GVAR)\n        init = ast_inttype(ctype_int, eval_intexpr(init));\n    return ast_decl(var, init);\n}\n\nstatic Ctype *read_array_dimensions_int(Ctype *basetype)\n{\n    Token tok = read_token();\n    if (!is_punct(tok, '[')) {\n        unget_token(tok);\n        return NULL;\n    }\n    int dim = -1;\n    if (!is_punct(peek_token(), ']')) {\n        Ast *size = read_expr();\n        dim = eval_intexpr(size);\n    }\n    expect(']');\n    Ctype *sub = read_array_dimensions_int(basetype);\n    if (sub) {\n        if (sub->len == -1 && dim == -1)\n            error(\"Array size is not specified\");\n        return make_array_type(sub, dim);\n    }\n    return make_array_type(basetype, dim);\n}\n\nstatic Ctype *read_array_dimensions(Ctype *basetype)\n{\n    Ctype *ctype = read_array_dimensions_int(basetype);\n    return ctype ? ctype : basetype;\n}\n\nstatic Ast *read_decl_init(Ast *var)\n{\n    Token tok = read_token();\n    if (is_punct(tok, '='))\n        return read_decl_init_val(var);\n    if (var->ctype->len == -1)\n        error(\"Missing array initializer\");\n    unget_token(tok);\n    expect(';');\n    return ast_decl(var, NULL);\n}\n\nstatic Ctype *read_decl_int(Token *name)\n{\n    Ctype *ctype = read_decl_spec();\n    *name = read_token();\n    if (get_ttype((*name)) != TTYPE_IDENT)\n        error(\"Identifier expected, but got %s\", token_to_string(*name));\n    return read_array_dimensions(ctype);\n}\n\nstatic Ast *read_decl(void)\n{\n    Token varname;\n    Ctype *ctype = read_decl_int(&varname);\n    if (ctype == ctype_void)\n        error(\"Storage size of '%s' is not known\", token_to_string(varname));\n    Ast *var = ast_lvar(ctype, get_ident(varname));\n    return read_decl_init(var);\n}\n\nstatic Ast *read_if_stmt(void)\n{\n    expect('(');\n    Ast *cond = read_expr();\n    expect(')');\n    Ast *then = read_stmt();\n    Token tok = read_token();\n    if (get_ttype(tok) != TTYPE_IDENT || strcmp(get_ident(tok), \"else\")) {\n        unget_token(tok);\n        return ast_if(cond, then, NULL);\n    }\n    Ast *els = read_stmt();\n    return ast_if(cond, then, els);\n}\n\nstatic Ast *read_opt_decl_or_stmt(void)\n{\n    Token tok = read_token();\n    if (is_punct(tok, ';'))\n        return NULL;\n    unget_token(tok);\n    return read_decl_or_stmt();\n}\n\nstatic Ast *read_opt_expr(void)\n{\n    Token tok = read_token();\n    if (is_punct(tok, ';'))\n        return NULL;\n    unget_token(tok);\n    Ast *r = read_expr();\n    expect(';');\n    return r;\n}\n\nstatic Ast *read_for_stmt(void)\n{\n    expect('(');\n    localenv = make_dict(localenv);\n    Ast *init = read_opt_decl_or_stmt();\n    Ast *cond = read_opt_expr();\n    Ast *step = is_punct(peek_token(), ')') ? NULL : read_expr();\n    expect(')');\n    Ast *body = read_stmt();\n    localenv = dict_parent(localenv);\n    return ast_for(init, cond, step, body);\n}\n\nstatic Ast *read_return_stmt(void)\n{\n    Ast *retval = read_expr();\n    expect(';');\n    return ast_return(retval);\n}\n\nstatic Ast *read_stmt(void)\n{\n    Token tok = read_token();\n    if (is_ident(tok, \"if\"))\n        return read_if_stmt();\n    if (is_ident(tok, \"for\"))\n        return read_for_stmt();\n    if (is_ident(tok, \"return\"))\n        return read_return_stmt();\n    if (is_punct(tok, '{'))\n        return read_compound_stmt();\n    unget_token(tok);\n    Ast *r = read_expr();\n    expect(';');\n    return r;\n}\n\nstatic Ast *read_decl_or_stmt(void)\n{\n    Token tok = peek_token();\n    if (get_ttype(tok) == TTYPE_NULL)\n        return NULL;\n    return is_type_keyword(tok) ? read_decl() : read_stmt();\n}\n\nstatic Ast *read_compound_stmt(void)\n{\n    localenv = make_dict(localenv);\n    List *list = make_list();\n    while (1) {\n        Ast *stmt = read_decl_or_stmt();\n        if (stmt)\n            list_push(list, stmt);\n        if (!stmt)\n            break;\n        Token tok = read_token();\n        if (is_punct(tok, '}'))\n            break;\n        unget_token(tok);\n    }\n    localenv = dict_parent(localenv);\n    return ast_compound_stmt(list);\n}\n\nstatic List *read_params(void)\n{\n    List *params = make_list();\n    Token tok = read_token();\n    if (is_punct(tok, ')'))\n        return params;\n    unget_token(tok);\n    while (1) {\n        Ctype *ctype = read_decl_spec();\n        Token pname = read_token();\n        if (get_ttype(pname) != TTYPE_IDENT)\n            error(\"Identifier expected, but got %s\", token_to_string(pname));\n        ctype = read_array_dimensions(ctype);\n        if (ctype->type == CTYPE_ARRAY)\n            ctype = make_ptr_type(ctype->ptr);\n        list_push(params, ast_lvar(ctype, get_ident(pname)));\n        Token tok = read_token();\n        if (is_punct(tok, ')'))\n            return params;\n        if (!is_punct(tok, ','))\n            error(\"Comma expected, but got %s\", token_to_string(tok));\n    }\n}\n\nstatic Ast *read_func_def(Ctype *rettype, char *fname)\n{\n    expect('(');\n    localenv = make_dict(globalenv);\n    List *params = read_params();\n    expect('{');\n    localenv = make_dict(localenv);\n    localvars = make_list();\n    Ast *body = read_compound_stmt();\n    Ast *r = ast_func(rettype, fname, params, body, localvars);\n    localenv = dict_parent(localenv);\n    localenv = dict_parent(localenv);\n    localvars = NULL;\n    return r;\n}\n\nstatic Ast *read_decl_or_func_def(void)\n{\n    Token tok = peek_token();\n    if (get_ttype(tok) == TTYPE_NULL)\n        return NULL;\n    Ctype *ctype = read_decl_spec();\n    Token name = read_token();\n    char *ident;\n    if (get_ttype(name) != TTYPE_IDENT)\n        error(\"Identifier expected, but got %s\", token_to_string(name));\n    ident = get_ident(name);\n    tok = peek_token();\n    if (is_punct(tok, '('))\n        return read_func_def(ctype, ident);\n    if (ctype == ctype_void)\n        error(\"Storage size of '%s' is not known\", token_to_string(name));\n    ctype = read_array_dimensions(ctype);\n    if (is_punct(tok, '=') || ctype->type == CTYPE_ARRAY) {\n        Ast *var = ast_gvar(ctype, ident, false);\n        return read_decl_init(var);\n    }\n    if (is_punct(tok, ';')) {\n        read_token();\n        Ast *var = ast_gvar(ctype, ident, false);\n        return ast_decl(var, NULL);\n    }\n    error(\"Don't know how to handle %s\", token_to_string(tok));\n    return NULL; /* non-reachable */\n}\n\nList *read_toplevels(void)\n{\n    List *r = make_list();\n    while (1) {\n        Ast *ast = read_decl_or_func_def();\n        if (!ast)\n            return r;\n        list_push(r, ast);\n    }\n    list_free(globalenv->list);\n    return r;\n}\n"
  },
  {
    "path": "sample/nqueen.c",
    "content": "int conflict(int board[][8], int row, int col)\n{\n    for (int i = 0; i < row; i++) {\n        if (board[i][col])\n            return 1;\n        int j = row - i;\n        if (0 < col - j + 1 && board[i][col - j])\n            return 1;\n        if (col + j < 8 && board[i][col + j])\n            return 1;\n    }\n    return 0;\n}\n\nint print_board(int board[][8])\n{\n    for (int i = 0; i < 8; i++) {\n        for (int j = 0; j < 8; j++)\n            printf(board[i][j] ? \"Q \" : \". \");\n        printf(\"\\n\");\n    }\n    printf(\"\\n\\n\");\n}\n\nint solve(int board[][8], int row)\n{\n    if (row == 8) {\n        print_board(board);\n        return 0;\n    }\n    for (int i = 0; i < 8; i++) {\n        if (!conflict(board, row, i)) {\n            board[row][i] = 1;\n            solve(board, row + 1);\n            board[row][i] = 0;\n        }\n    }\n}\n\nint main()\n{\n    int board[64];\n    for (int i = 0; i < 64; i++)\n        board[i] = 0;\n    solve(board, 0);\n    return 0;\n}\n"
  },
  {
    "path": "tests/arith.c",
    "content": "/* Test basic arithmetic */\n\nint expect(int a, int b)\n{\n    if (!(a == b)) {\n        printf(\"Failed\\n\");\n        printf(\"  %d expected, but got %d\\n\", a, b);\n        exit(1);\n    }\n}\n\nint test_basic()\n{\n    expect(0, 0);\n    expect(3, 1 + 2);\n    expect(3, 1 + 2);\n    expect(10, 1 + 2 + 3 + 4);\n    expect(11, 1 + 2 * 3 + 4);\n    expect(14, 1 * 2 + 3 * 4);\n    expect(4, 4 / 2 + 6 / 3);\n    expect(4, 24 / 2 / 3);\n    expect(98, 'a' + 1);\n    int a = 0 - 1;\n    expect(0 - 1, a);\n    expect(0, a + 1);\n}\n\nint test_inc_dec()\n{\n    int a = 15;\n    expect(15, a++);\n    expect(16, a);\n    expect(16, a--);\n    expect(15, a);\n}\n\nint test_bool()\n{\n    expect(0, !1);\n    expect(1, !0);\n}\n\nint test_ternary()\n{\n    expect(51, (1 + 2) ? 51 : 52);\n    expect(52, (1 - 1) ? 51 : 52);\n    expect(26, (1 - 1) ? 51 : 52 / 2);\n    expect(17, (1 - 0) ? 51 / 3 : 52);\n}\n\nint test_logand()\n{\n    expect(1, 55 && 2);\n    expect(0, 55 && 0);\n    expect(0, 0 && 55);\n}\n\nint test_bitand()\n{\n    expect(3, 1 | 2);\n    expect(1, 1 & 3);\n}\n\nint test_shift()\n{\n    expect(8, 4 << 1);\n    expect(3, 7 >> 1);\n}\n\nint main()\n{\n    test_basic();\n    test_inc_dec();\n    test_bool();\n    test_ternary();\n    test_logand();\n    test_bitand();\n    test_shift();\n    return 0;\n}\n"
  },
  {
    "path": "tests/array.c",
    "content": "/* Test array */\n\nint expect(int a, int b)\n{\n    if (!(a == b)) {\n        printf(\"Failed\\n\");\n        printf(\"  %d expected, but got %d\\n\", a, b);\n        exit(1);\n    }\n}\n\nint t1()\n{\n    int a[2][3];\n    int *p = a;\n    *p = 1;\n    expect(1, *p);\n}\n\nint t2()\n{\n    int a[2][3];\n    int *p = a + 1;\n    *p = 1;\n    int *q = a;\n    *p = 32;\n    expect(32, *(q + 3));\n}\n\nint t3()\n{\n    int a[4][5];\n    int *p = a;\n    *(*(a + 1) + 2) = 62;\n    expect(62, *(p + 7));\n}\n\nint t4()\n{\n    int a[3] = {1, 2, 3};\n    expect(1, a[0]);\n    expect(2, a[1]);\n    expect(3, a[2]);\n}\n\nint t5()\n{\n    int a[2][3];\n    a[0][1] = 1;\n    a[1][1] = 2;\n    int *p = a;\n    expect(1, p[1]);\n    expect(2, p[4]);\n}\n\nint t6a(int e, int x[][3])\n{\n    expect(e, *(*(x + 1) + 1));\n}\n\nint t6()\n{\n    int a[2][3];\n    int *p = a;\n    *(p + 4) = 65;\n    t6a(65, a);\n}\n\nint t7()\n{\n    int a[3 * 3];  // integer constant expression\n    a[8] = 68;\n    expect(68, a[8]);\n}\n\nint main()\n{\n    t1();\n    t2();\n    t3();\n    t4();\n    t5();\n    t6();\n    t7();\n\n    return 0;\n}\n"
  },
  {
    "path": "tests/comp.c",
    "content": "/* Test comparison operators */\n\nint expect(int a, int b)\n{\n    if (!(a == b)) {\n        printf(\"Failed\\n\");\n        printf(\"  %d expected, but got %d\\n\", a, b);\n        exit(1);\n    }\n}\n\nint main()\n{\n    expect(1, 1 < 2);\n    expect(0, 2 < 1);\n    expect(1, 1 == 1);\n    expect(0, 1 == 2);\n\n    return 0;\n}\n"
  },
  {
    "path": "tests/control.c",
    "content": "/* Test control flow */\n\nint expect(int a, int b)\n{\n    if (!(a == b)) {\n        printf(\"Failed\\n\");\n        printf(\"  %d expected, but got %d\\n\", a, b);\n        exit(1);\n    }\n}\n\nint testif1()\n{\n    if (1) {\n        return 'a';\n    }\n    return 0;\n}\nint testif2()\n{\n    if (0) {\n        return 0;\n    }\n    return 'b';\n}\nint testif3()\n{\n    if (1) {\n        return 'c';\n    } else {\n        return 0;\n    }\n    return 0;\n}\nint testif4()\n{\n    if (0) {\n        return 0;\n    } else {\n        return 'd';\n    }\n    return 0;\n}\nint testif5()\n{\n    if (1)\n        return 'e';\n    return 0;\n}\nint testif6()\n{\n    if (0)\n        return 0;\n    return 'f';\n}\nint testif7()\n{\n    if (1)\n        return 'g';\n    else\n        return 0;\n    return 0;\n}\nint testif8()\n{\n    if (0)\n        return 0;\n    else\n        return 'h';\n    return 0;\n}\nint testif9()\n{\n    if (0 + 1)\n        return 'i';\n    return 0;\n}\nint testif10()\n{\n    if (1 - 1)\n        return 0;\n    return 'j';\n}\n\nint testif()\n{\n    expect('a', testif1());\n    expect('b', testif2());\n    expect('c', testif3());\n    expect('d', testif4());\n    expect('e', testif5());\n    expect('f', testif6());\n    expect('g', testif7());\n    expect('h', testif8());\n    expect('i', testif9());\n    expect('j', testif10());\n}\n\nint testfor()\n{\n    int i;\n    int acc = 0;\n    for (i = 0; i < 5; i = i + 1) {\n        acc = acc + i;\n    }\n    expect(10, acc);\n\n    acc = 0;\n    for (i = 0; i < 5; i = i + 1)\n        acc = acc + i;\n    expect(10, acc);\n}\n\nint main()\n{\n    testif();\n    testfor();\n\n    return 0;\n}\n"
  },
  {
    "path": "tests/decl.c",
    "content": "/* Test declaration */\n\nint expect(int a, int b)\n{\n    if (!(a == b)) {\n        printf(\"Failed\\n\");\n        printf(\"  %d expected, but got %d\\n\", a, b);\n        exit(1);\n    }\n}\n\nint t1()\n{\n    int a = 1;\n    expect(3, a + 2);\n}\n\nint t2()\n{\n    int a = 1;\n    int b = 48 + 2;\n    int c = a + b;\n    expect(102, c * 2);\n}\n\nint t3()\n{\n    int a[] = {55};\n    int *b = a;\n    expect(55, *b);\n}\n\nint t4()\n{\n    int a[] = {55, 67};\n    int *b = a + 1;\n    expect(67, *b);\n}\n\nint t5()\n{\n    int a[] = {20, 30, 40};\n    int *b = a + 1;\n    expect(30, *b);\n}\n\nint t6()\n{\n    int a[] = {20, 30, 40};\n    expect(20, *a);\n}\n\nint main()\n{\n    t1();\n    t2();\n    t3();\n    t4();\n    t5();\n    t6();\n\n    return 0;\n}\n"
  },
  {
    "path": "tests/driver.sh",
    "content": "#!/usr/bin/env bash\n\neval `cat .cbuild`\n\nfunction compile {\n    echo \"$1\" > /dev/stderr\n    echo \"$1\" | ./mzcc > tmp.s || echo \"Failed to compile $1\"\n    if [ $? -ne 0 ]; then\n        echo \"Failed to compile $1\"\n        exit\n    fi\n    $(CBUILD) -o tmp.out tmp.s\n    if [ $? -ne 0 ]; then\n        echo \"GCC failed: $1\"\n        exit\n    fi\n}\n\nfunction assert_equal {\n    if [ \"$1\" != \"$2\" ]; then\n        echo \"Test failed: $2 expected but got $1\"\n        exit\n    fi\n}\n\nfunction test_astf {\n    result=\"$(echo \"$2\" | ./mzcc --dump-ast -)\"\n    if [ $? -ne 0 ]; then\n        echo \"Failed to compile $2\"\n        exit\n    fi\n    assert_equal \"$result\" \"$1\"\n}\n\nfunction test_ast {\n    test_astf \"$1\" \"int f(){$2}\"\n}\n\nfunction test_fail {\n    expr=\"int f(){$1}\"\n    echo \"$expr\" | ./mzcc > /dev/null 2>&1\n    if [ $? -eq 0 ]; then\n        echo \"Should fail to compile, but succeded: $expr\"\n        exit\n    fi\n}\n\n# Parser\ntest_ast '(int)f(){1;}' '1;'\ntest_ast '(int)f(){1L;}' '1L;'\ntest_ast '(int)f(){1152921504606846976L;}' '1152921504606846976;'\ntest_ast '(int)f(){(+ (- (+ 1 2) 3) 4);}' '1+2-3+4;'\ntest_ast '(int)f(){(+ (+ 1 (* 2 3)) 4);}' '1+2*3+4;'\ntest_ast '(int)f(){(+ (* 1 2) (* 3 4));}' '1*2+3*4;'\ntest_ast '(int)f(){(+ (/ 4 2) (/ 6 3));}' '4/2+6/3;'\ntest_ast '(int)f(){(/ (/ 24 2) 4);}' '24/2/4;'\ntest_ast '(int)f(){(decl int a 3);}' 'int a=3;'\ntest_ast \"(int)f(){(decl char c 'a');}\" \"char c='a';\"\ntest_ast '(int)f(){(decl *char s \"abcd\");}' 'char *s=\"abcd\";'\ntest_ast '(int)f(){(decl [5]char s \"asdf\");}' 'char s[5]=\"asdf\";'\ntest_ast '(int)f(){(decl [5]char s \"asdf\");}' 'char s[]=\"asdf\";'\ntest_ast '(int)f(){(decl [3]int a {1,2,3});}' 'int a[3]={1,2,3};'\ntest_ast '(int)f(){(decl [3]int a {1,2,3});}' 'int a[]={1,2,3};'\ntest_ast '(int)f(){(decl [3][5]int a);}' 'int a[3][5];'\ntest_ast '(int)f(){(decl [5]*int a);}' 'int *a[5];'\ntest_ast '(int)f(){(decl int a 1);(decl int b 2);(= a (= b 3));}' 'int a=1;int b=2;a=b=3;'\ntest_ast '(int)f(){(decl int a 3);(addr a);}' 'int a=3;&a;'\ntest_ast '(int)f(){(decl int a 3);(deref (addr a));}' 'int a=3;*&a;'\ntest_ast '(int)f(){(decl int a 3);(decl *int b (addr a));(deref b);}' 'int a=3;int *b=&a;*b;'\ntest_ast '(int)f(){(if 1 {2;});}' 'if(1){2;}'\ntest_ast '(int)f(){(if 1 {2;} {3;});}' 'if(1){2;}else{3;}'\ntest_ast '(int)f(){(for (decl int a 1) 3 7 {5;});}' 'for(int a=1;3;7){5;}'\ntest_ast '(int)f(){\"abcd\";}' '\"abcd\";'\ntest_ast \"(int)f(){'c';}\" \"'c';\"\ntest_ast '(int)f(){(int)a();}' 'a();'\ntest_ast '(int)f(){(int)a(1,2,3,4,5,6);}' 'a(1,2,3,4,5,6);'\ntest_ast '(int)f(){(return 1);}' 'return 1;'\ntest_ast '(int)f(){(< 1 2);}' '1<2;'\ntest_ast '(int)f(){(> 1 2);}' '1>2;'\ntest_ast '(int)f(){(== 1 2);}' '1==2;'\ntest_ast '(int)f(){(deref (+ 1 2));}' '1[2];'\ntest_ast '(int)f(){(decl int a 1);(++ a);}' 'int a=1;a++;'\ntest_ast '(int)f(){(decl int a 1);(-- a);}' 'int a=1;a--;'\ntest_ast '(int)f(){(! 1);}' '!1;'\ntest_ast '(int)f(){(? 1 2 3);}' '1?2:3;'\ntest_ast '(int)f(){(and 1 2);}' '1&&2;'\ntest_ast '(int)f(){(or 1 2);}' '1||2;'\ntest_ast '(int)f(){(& 1 2);}' '1&2;'\ntest_ast '(int)f(){(| 1 2);}' '1|2;'\ntest_ast '(int)f(){1.200000;}' '1.2;'\ntest_ast '(int)f(){(+ 1.200000 1);}' '1.2+1;'\n\ntest_astf '(int)f(int c){c;}' 'int f(int c){c;}'\ntest_astf '(int)f(int c){c;}(int)g(int d){d;}' 'int f(int c){c;} int g(int d){d;}'\ntest_astf '(decl int a 3)' 'int a=3;'\n\ntest_astf '(decl (struct) a)' 'struct {} a;'\ntest_astf '(decl (struct (int) (char)) a)' 'struct {int x; char y;} a;'\ntest_astf '(decl (struct ([3]int)) a)' 'struct {int x[3];} a;'\ntest_ast '(int)f(){(decl (struct (int)) a);(decl *(struct (int)) p);(deref p).x;}' 'struct tag {int x;} a; struct tag *p; p->x;'\ntest_ast '(int)f(){(decl (struct (int)) a);a.x;}' 'struct {int x;} a; a.x;'\n\ntest_fail '0abc;'\ntest_fail '1+;'\ntest_fail '1=2;'\n\n# & is only applicable to an lvalue\ntest_fail '&\"a\";'\ntest_fail '&1;'\ntest_fail '&a();'\n\necho \"All tests passed\"\n"
  },
  {
    "path": "tests/float.c",
    "content": "/* Test floating point */\n\nint expect(float a, float b)\n{\n    if (!(a == b)) {\n        printf(\"Failed\\n\");\n        printf(\"  %f expected, but got %f\\n\", a, b);\n        exit(1);\n    }\n}\n\nint main()\n{\n    expect(1.0, 1.0);\n    expect(1.5, 1.0 + 0.5);\n    expect(0.5, 1.0 - 0.5);\n    expect(2.0, 1.0 * 2.0);\n    expect(0.25, 1.0 / 4.0);\n\n    expect(3.0, 1.0 + 2);\n    expect(2.5, 5 - 2.5);\n    expect(2.0, 1.0 * 2);\n    expect(0.25, 1.0 / 4);\n\n    return 0;\n}\n"
  },
  {
    "path": "tests/function.c",
    "content": "/* Test function */\n\nint expect(int a, int b)\n{\n    if (!(a == b)) {\n        printf(\"Failed\\n\");\n        printf(\"  %d expected, but got %d\\n\", a, b);\n        exit(1);\n    }\n}\n\nint t1()\n{\n    return 77;\n}\n\nint t2(int a)\n{\n    expect(79, a);\n}\n\nint t3(int a, int b, int c, int d, int e, int f)\n{\n    expect(1, a);\n    expect(2, b);\n    expect(3, c);\n    expect(4, d);\n    expect(5, e);\n    expect(6, f);\n}\n\nint t4a(int *p)\n{\n    return *p;\n}\n\nint t4()\n{\n    int a[] = {98};\n    expect(98, t4a(a));\n}\n\nint t5a(int *p)\n{\n    expect(99, *p);\n    p = p + 1;\n    expect(98, *p);\n    p = p + 1;\n    expect(97, *p);\n}\n\nint t5b(int p[])\n{\n    expect(99, *p);\n    p = p + 1;\n    expect(98, *p);\n    p = p + 1;\n    expect(97, *p);\n}\n\nint t5()\n{\n    int a[] = {1, 2, 3};\n    int *p = a;\n    *p = 99;\n    p = p + 1;\n    *p = 98;\n    p = p + 1;\n    *p = 97;\n    t5a(a);\n    t5b(a);\n}\n\nint main()\n{\n    expect(77, t1());\n    t2(79);\n    t3(1, 2, 3, 4, 5, 6);\n    t4();\n    t5();\n\n    return 0;\n}\n"
  },
  {
    "path": "tests/global.c",
    "content": "/* Test global variable */\n\nint val = 21;\nint a1[3];\nint a2[3] = {24, 25, 26};\n\nint expect(int a, int b)\n{\n    if (!(a == b)) {\n        printf(\"Failed\\n\");\n        printf(\"  %d expected, but got %d\\n\", a, b);\n        exit(1);\n    }\n}\n\nint main()\n{\n    expect(21, val);\n    val = 22;\n    expect(22, val);\n\n    a1[1] = 23;\n    expect(23, a1[1]);\n    expect(25, a2[1]);\n\n    return 0;\n}\n"
  },
  {
    "path": "tests/long.c",
    "content": "/* Test long integer */\n\nint expect(long a, long b)\n{\n    if (!(a == b)) {\n        printf(\"Failed\\n\");\n        printf(\"  %ld expected, but got %ld\\n\", a, b);\n        exit(1);\n    }\n}\n\nint main()\n{\n    expect(10L, 10L);\n    expect(1152921504606846976, 1152921504606846976);\n    expect(1152921504606846977, 1152921504606846976 + 1);\n\n    return 0;\n}\n"
  },
  {
    "path": "tests/pointer.c",
    "content": "/* Test pointer */\n\nint expect(int a, int b)\n{\n    if (!(a == b)) {\n        printf(\"Failed\\n\");\n        printf(\"  %d expected, but got %d\\n\", a, b);\n        exit(1);\n    }\n}\n\nint t1()\n{\n    int a = 61;\n    int *b = &a;\n    expect(61, *b);\n}\n\nint t2()\n{\n    char *c = \"ab\";\n    expect(97, *c);\n}\n\nint t3()\n{\n    char *c = \"ab\" + 1;\n    expect(98, *c);\n}\n\nint t4()\n{\n    char s[] = \"xyz\";\n    char *c = s + 2;\n    expect(122, *c);\n}\n\nint t5()\n{\n    char s[] = \"xyz\";\n    *s = 65;\n    expect(65, *s);\n}\n\nint main()\n{\n    t1();\n    t2();\n    t3();\n    t4();\n    t5();\n\n    return 0;\n}\n"
  },
  {
    "path": "tests/pointer_arith.c",
    "content": "/* Test pointer arithmetic*/\n\nint expect(int a, int b)\n{\n    if (!(a == b)) {\n        printf(\"Failed\\n\");\n        printf(\"  %d expected, but got %d\\n\", a, b);\n        exit(1);\n    }\n}\n\nint t1()\n{\n    char *s = \"abcdefghi\";\n    char *x = s;\n    char *t = x + 1;\n    expect(98, *t);\n}\n\nint t2()\n{\n    char *s = \"abcdefghi\";\n    int *x = s;\n    char *t = x + 1;\n    expect(101, *t);\n}\n\nint t3()\n{\n    char *s = \"abcdefghi\";\n    long *x = s;\n    char *t = x + 1;\n    expect(105, *t);\n}\n\nint t4()\n{\n    char *s = \"abcdefghi\";\n    void *x = s;\n    char *t = x + 1;\n    expect(98, *t);\n}\n\nint main()\n{\n    t1();\n    t2();\n    t3();\n    t4();\n}\n"
  },
  {
    "path": "tests/scope.c",
    "content": "/* Test scope */\n\nint expect(int a, int b)\n{\n    if (!(a == b)) {\n        printf(\"Failed\\n\");\n        printf(\"  %d expected, but got %d\\n\", a, b);\n        exit(1);\n    }\n}\n\nint main()\n{\n    int a = 31;\n    {\n        int a = 64;\n    }\n    expect(31, a);\n    {\n        int a = 64;\n        expect(64, a);\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "tests/struct.c",
    "content": "/* Test struct */\n\nint expect(int a, int b)\n{\n    if (!(a == b)) {\n        printf(\"Failed\\n\");\n        printf(\"  %d expected, but got %d\\n\", a, b);\n        exit(1);\n    }\n}\n\nint t1()\n{\n    struct {\n        int a;\n    } x;\n    x.a = 61;\n    expect(61, x.a);\n}\n\nint t2()\n{\n    struct {\n        int a;\n        int b;\n    } x;\n    x.a = 61;\n    x.b = 2;\n    expect(63, x.a + x.b);\n}\n\nint t3()\n{\n    struct {\n        int a;\n        struct {\n            char b;\n            int c;\n        } y;\n    } x;\n    x.a = 61;\n    x.y.b = 3;\n    x.y.c = 3;\n    expect(67, x.a + x.y.b + x.y.c);\n}\n\nint t4()\n{\n    struct tag4 {\n        int a;\n        struct {\n            char b;\n            int c;\n        } y;\n    } x;\n    struct tag4 s;\n    s.a = 61;\n    s.y.b = 3;\n    s.y.c = 3;\n    expect(67, s.a + s.y.b + s.y.c);\n}\n\nint t5()\n{\n    struct tag5 {\n        int a;\n    } x;\n    struct tag5 *p = &x;\n    x.a = 68;\n    expect(68, (*p).a);\n}\n\nint t6()\n{\n    struct tag6 {\n        int a;\n    } x;\n    struct tag6 *p = &x;\n    (*p).a = 69;\n    expect(69, x.a);\n}\n\nint t7()\n{\n    struct tag7 {\n        int a;\n        int b;\n    } x;\n    struct tag7 *p = &x;\n    x.b = 71;\n    expect(71, (*p).b);\n}\n\nint t8()\n{\n    struct tag8 {\n        int a;\n        int b;\n    } x;\n    struct tag8 *p = &x;\n    (*p).b = 72;\n    expect(72, x.b);\n}\n\nint t9()\n{\n    struct tag9 {\n        int a[3];\n        int b[3];\n    } x;\n    x.a[0] = 73;\n    expect(73, x.a[0]);\n    x.b[1] = 74;\n    expect(74, x.b[1]);\n    expect(74, x.a[4]);\n}\n\nstruct tag10 {\n    int a;\n    struct tag10a {\n        char b;\n        int c;\n    } y;\n} v10;\nint t10()\n{\n    v10.a = 71;\n    v10.y.b = 3;\n    v10.y.c = 3;\n    expect(77, v10.a + v10.y.b + v10.y.c);\n}\n\nstruct tag11 {\n    int a;\n} v11;\nint t11()\n{\n    struct tag11 *p = &v11;\n    v11.a = 78;\n    expect(78, (*p).a);\n    expect(78, v11.a);\n    expect(78, p->a);\n    p->a = 79;\n    expect(79, (*p).a);\n    expect(79, v11.a);\n    expect(79, p->a);\n}\n\nstruct tag12 {\n    int a;\n    int b;\n} x;\nint t12()\n{\n    struct tag12 a[3];\n    a[0].a = 83;\n    expect(83, a[0].a);\n    a[0].b = 84;\n    expect(84, a[0].b);\n    a[1].b = 85;\n    expect(85, a[1].b);\n    int *p = a;\n    expect(85, p[3]);\n}\n\nint main()\n{\n    t1();\n    t2();\n    t3();\n    t4();\n    t5();\n    t6();\n    t7();\n    t8();\n    t9();\n    t10();\n    t11();\n    t12();\n\n    return 0;\n}\n"
  },
  {
    "path": "tests/union.c",
    "content": "/* Test union */\n\nint expect(int a, int b)\n{\n    if (!(a == b)) {\n        printf(\"Failed\\n\");\n        printf(\"  %d expected, but got %d\\n\", a, b);\n        exit(1);\n    }\n}\n\nint t1()\n{\n    union {\n        int a;\n        int b;\n    } x;\n    x.a = 90;\n    expect(90, x.b);\n}\n\nint t2()\n{\n    union {\n        char a[4];\n        int b;\n    } x;\n    x.b = 0;\n    x.a[1] = 1;\n    expect(256, x.b);\n}\n\nint t3()\n{\n    union {\n        char a[4];\n        int b;\n    } x;\n    x.a[0] = x.a[1] = x.a[2] = x.a[3] = 0;\n    x.a[1] = 1;\n    expect(256, x.b);\n}\n\nint main()\n{\n    t1();\n    t2();\n    t3();\n\n    return 0;\n}\n"
  },
  {
    "path": "util.h",
    "content": "#ifndef MAZUCC_UTIL_H\n#define MAZUCC_UTIL_H\n\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"list.h\"\n\ntypedef struct {\n    char *body;\n    int nalloc, len;\n} String;\n\nstatic List *cstrings = &EMPTY_LIST;\n\n#define INIT_SIZE 8\n\nstatic inline String make_string(void)\n{\n    return (String){\n        .body = calloc(1, INIT_SIZE),\n        .nalloc = INIT_SIZE,\n        .len = 0,\n    };\n}\n\nstatic inline void realloc_body(String *s)\n{\n    int newsize = s->nalloc * 2;\n    char *body = realloc(s->body, newsize);\n    s->body = body;\n    s->nalloc = newsize;\n}\n\nstatic inline char *get_cstring(const String s)\n{\n    char *r = s.body;\n    list_push(cstrings, r);\n    return r;\n}\n\nstatic inline void string_append(String *s, char c)\n{\n    if (s->nalloc == (s->len + 1))\n        realloc_body(s);\n    s->body[s->len++] = c;\n    s->body[s->len] = '\\0';\n}\n\nstatic inline void string_appendf(String *s, char *fmt, ...)\n{\n    va_list args;\n    while (1) {\n        int avail = s->nalloc - s->len;\n        va_start(args, fmt);\n        int written = vsnprintf(s->body + s->len, avail, fmt, args);\n        va_end(args);\n        if (avail <= written) {\n            realloc_body(s);\n            continue;\n        }\n        s->len += written;\n        return;\n    }\n}\n\n#define error(...) errorf(__FILE__, __LINE__, __VA_ARGS__)\n\n#define assert(expr)                           \\\n    do {                                       \\\n        if (!(expr))                           \\\n            error(\"Assertion failed: \" #expr); \\\n    } while (0)\n\nstatic inline void errorf(char *file, int line, char *fmt, ...)\n{\n    fprintf(stderr, \"%s:%d: \", file, line);\n    va_list args;\n    va_start(args, fmt);\n    vfprintf(stderr, fmt, args);\n    fprintf(stderr, \"\\n\");\n    va_end(args);\n    exit(1);\n}\n\nstatic inline char *quote_cstring(char *p)\n{\n    String s = make_string();\n    for (; *p; p++) {\n        if (*p == '\\\"' || *p == '\\\\')\n            string_appendf(&s, \"\\\\%c\", *p);\n        else if (*p == '\\n')\n            string_appendf(&s, \"\\\\n\");\n        else\n            string_append(&s, *p);\n    }\n    return get_cstring(s);\n}\n\n#endif /* MAZUCC_UTIL_H */\n"
  },
  {
    "path": "verbose.c",
    "content": "#include \"mzcc.h\"\n\nchar *ctype_to_string(Ctype *ctype)\n{\n    if (!ctype)\n        return \"(nil)\";\n    switch (ctype->type) {\n    case CTYPE_VOID:\n        return \"void\";\n    case CTYPE_INT:\n        return \"int\";\n    case CTYPE_LONG:\n        return \"long\";\n    case CTYPE_CHAR:\n        return \"char\";\n    case CTYPE_FLOAT:\n        return \"float\";\n    case CTYPE_DOUBLE:\n        return \"double\";\n    case CTYPE_PTR: {\n        String s = make_string();\n        string_appendf(&s, \"*%s\", ctype_to_string(ctype->ptr));\n        return get_cstring(s);\n    }\n    case CTYPE_ARRAY: {\n        String s = make_string();\n        string_appendf(&s, \"[%d]%s\", ctype->len, ctype_to_string(ctype->ptr));\n        return get_cstring(s);\n    }\n    case CTYPE_STRUCT: {\n        String s = make_string();\n        string_appendf(&s, \"(struct\");\n        for (Iter i = list_iter(dict_values(ctype->fields)); !iter_end(i);)\n            string_appendf(&s, \" (%s)\", ctype_to_string(iter_next(&i)));\n        string_appendf(&s, \")\");\n        return get_cstring(s);\n    }\n    default:\n        error(\"Unknown ctype: %d\", ctype);\n        return NULL; /* non-reachable */\n    }\n}\n\nstatic void uop_to_string(String *buf, char *op, Ast *ast)\n{\n    string_appendf(buf, \"(%s %s)\", op, ast_to_string(ast->operand));\n}\n\nstatic void binop_to_string(String *buf, char *op, Ast *ast)\n{\n    string_appendf(buf, \"(%s %s %s)\", op, ast_to_string(ast->left),\n                   ast_to_string(ast->right));\n}\n\nstatic void ast_to_string_int(String *buf, Ast *ast)\n{\n    if (!ast) {\n        string_appendf(buf, \"(nil)\");\n        return;\n    }\n    switch (ast->type) {\n    case AST_LITERAL:\n        switch (ast->ctype->type) {\n        case CTYPE_CHAR:\n            if (ast->ival == '\\n')\n                string_appendf(buf, \"'\\n'\");\n            else if (ast->ival == '\\\\')\n                string_appendf(buf, \"'\\\\\\\\'\");\n            else\n                string_appendf(buf, \"'%c'\", ast->ival);\n            break;\n        case CTYPE_INT:\n            string_appendf(buf, \"%d\", ast->ival);\n            break;\n        case CTYPE_LONG:\n            string_appendf(buf, \"%ldL\", ast->ival);\n            break;\n        case CTYPE_FLOAT:\n        case CTYPE_DOUBLE:\n            string_appendf(buf, \"%f\", ast->fval);\n            break;\n        default:\n            error(\"internal error\");\n        }\n        break;\n    case AST_STRING:\n        string_appendf(buf, \"\\\"%s\\\"\", quote_cstring(ast->sval));\n        break;\n    case AST_LVAR:\n    case AST_GVAR:\n        string_appendf(buf, \"%s\", ast->varname);\n        break;\n    case AST_FUNCALL: {\n        string_appendf(buf, \"(%s)%s(\", ctype_to_string(ast->ctype), ast->fname);\n        for (Iter i = list_iter(ast->args); !iter_end(i);) {\n            string_appendf(buf, \"%s\", ast_to_string(iter_next(&i)));\n            if (!iter_end(i))\n                string_appendf(buf, \",\");\n        }\n        string_appendf(buf, \")\");\n        break;\n    }\n    case AST_FUNC: {\n        string_appendf(buf, \"(%s)%s(\", ctype_to_string(ast->ctype), ast->fname);\n        for (Iter i = list_iter(ast->params); !iter_end(i);) {\n            Ast *param = iter_next(&i);\n            string_appendf(buf, \"%s %s\", ctype_to_string(param->ctype),\n                           ast_to_string(param));\n            if (!iter_end(i))\n                string_appendf(buf, \",\");\n        }\n        string_appendf(buf, \")\");\n        ast_to_string_int(buf, ast->body);\n        break;\n    }\n    case AST_DECL:\n        string_appendf(buf, \"(decl %s %s\", ctype_to_string(ast->declvar->ctype),\n                       ast->declvar->varname);\n        if (ast->declinit)\n            string_appendf(buf, \" %s)\", ast_to_string(ast->declinit));\n        else\n            string_appendf(buf, \")\");\n        break;\n    case AST_ARRAY_INIT:\n        string_appendf(buf, \"{\");\n        for (Iter i = list_iter(ast->arrayinit); !iter_end(i);) {\n            ast_to_string_int(buf, iter_next(&i));\n            if (!iter_end(i))\n                string_appendf(buf, \",\");\n        }\n        string_appendf(buf, \"}\");\n        break;\n    case AST_IF:\n        string_appendf(buf, \"(if %s %s\", ast_to_string(ast->cond),\n                       ast_to_string(ast->then));\n        if (ast->els)\n            string_appendf(buf, \" %s\", ast_to_string(ast->els));\n        string_appendf(buf, \")\");\n        break;\n    case AST_TERNARY:\n        string_appendf(buf, \"(? %s %s %s)\", ast_to_string(ast->cond),\n                       ast_to_string(ast->then), ast_to_string(ast->els));\n        break;\n    case AST_FOR:\n        string_appendf(buf, \"(for %s %s %s \", ast_to_string(ast->forinit),\n                       ast_to_string(ast->forcond),\n                       ast_to_string(ast->forstep));\n        string_appendf(buf, \"%s)\", ast_to_string(ast->forbody));\n        break;\n    case AST_RETURN:\n        string_appendf(buf, \"(return %s)\", ast_to_string(ast->retval));\n        break;\n    case AST_COMPOUND_STMT: {\n        string_appendf(buf, \"{\");\n        for (Iter i = list_iter(ast->stmts); !iter_end(i);) {\n            ast_to_string_int(buf, iter_next(&i));\n            string_appendf(buf, \";\");\n        }\n        string_appendf(buf, \"}\");\n        break;\n    }\n    case AST_STRUCT_REF:\n        ast_to_string_int(buf, ast->struc);\n        string_appendf(buf, \".\");\n        string_appendf(buf, ast->field);\n        break;\n    case AST_ADDR:\n        uop_to_string(buf, \"addr\", ast);\n        break;\n    case AST_DEREF:\n        uop_to_string(buf, \"deref\", ast);\n        break;\n    case PUNCT_INC:\n        uop_to_string(buf, \"++\", ast);\n        break;\n    case PUNCT_DEC:\n        uop_to_string(buf, \"--\", ast);\n        break;\n    case PUNCT_LOGAND:\n        binop_to_string(buf, \"and\", ast);\n        break;\n    case PUNCT_LOGOR:\n        binop_to_string(buf, \"or\", ast);\n        break;\n    case '!':\n        uop_to_string(buf, \"!\", ast);\n        break;\n    case '&':\n        binop_to_string(buf, \"&\", ast);\n        break;\n    case '|':\n        binop_to_string(buf, \"|\", ast);\n        break;\n    default: {\n        char *left = ast_to_string(ast->left);\n        char *right = ast_to_string(ast->right);\n        if (ast->type == PUNCT_EQ)\n            string_appendf(buf, \"(== \");\n        else\n            string_appendf(buf, \"(%c \", ast->type);\n        string_appendf(buf, \"%s %s)\", left, right);\n    }\n    }\n}\n\nchar *ast_to_string(Ast *ast)\n{\n    String s = make_string();\n    ast_to_string_int(&s, ast);\n    return get_cstring(s);\n}\n\nchar *token_to_string(const Token tok)\n{\n    enum TokenType ttype = get_ttype(tok);\n    if (ttype == TTYPE_NULL)\n        return \"(null)\";\n    String s = make_string();\n    switch (ttype) {\n    case TTYPE_NULL:\n        error(\"internal error: unknown token type: %d\", get_ttype(tok));\n    case TTYPE_IDENT:\n        return get_ident(tok);\n    case TTYPE_PUNCT:\n        if (is_punct(tok, PUNCT_EQ))\n            string_appendf(&s, \"==\");\n        else\n            string_appendf(&s, \"%c\", get_punct(tok));\n        return get_cstring(s);\n    case TTYPE_CHAR:\n        string_append(&s, get_char(tok));\n        return get_cstring(s);\n    case TTYPE_NUMBER:\n        return get_number(tok);\n    case TTYPE_STRING:\n        string_appendf(&s, \"\\\"%s\\\"\", get_strtok(tok));\n        return get_cstring(s);\n    }\n    error(\"internal error: unknown token type: %d\", get_ttype(tok));\n    return NULL; /* non-reachable */\n}\n"
  }
]