diff options
Diffstat (limited to 'cgen.c')
-rw-r--r-- | cgen.c | 470 |
1 files changed, 17 insertions, 453 deletions
@@ -1,463 +1,27 @@ -static void cgen_create(CGenerator *g, Identifiers *ids, FILE *c_out, FILE *h_out, const char *h_filename) { - g->c_out = c_out; - g->h_out = h_out; - g->anon_fn_count = 0; - g->anon_var_count = 0; - g->indent_level = 0; - g->block = NULL; - g->indent_next = true; - g->main_ident = ident_get(ids, "main"); - - g->writing_to = CGEN_WRITING_TO_H; - cgen_write(g, "#include <stddef.h>\n" - "#include <stdint.h>\n" - "#include <string.h>\n"); - - g->writing_to = CGEN_WRITING_TO_C; - cgen_write(g, "#include \"%s\"\n", h_filename); - cgen_writeln(g, ""); /* extra newline between includes and code */ -} - -static bool cgen_direct(CGenerator *g, DirectExpr *direct, Location where) { - switch (direct->which) { - case DIRECT_C: { - Expression *args = direct->args.data; - size_t nargs = direct->args.len; - if (nargs != 1) { - err_print(where, "Expected 1 argument to #C directive, but got %lu.", nargs); - } - /* TODO: compile-time constants */ - if (args[0].kind != EXPR_STR_LITERAL) { - err_print(args[0].where, "Argument to #C directive must be a string literal."); - } - cgen_write(g, "%s", args[0].strl.str); - } break; - case DIRECT_COUNT: - assert(0); - return false; - } - return true; -} - - -/* generates C statements which must go before an expression. */ -static bool cgen_expr_pre(CGenerator *g, Expression *e) { - switch (e->kind) { - case EXPR_BINARY_OP: - if (!cgen_expr_pre(g, e->binary.lhs)) return false; - if (!cgen_expr_pre(g, e->binary.rhs)) return false; - /* TODO(eventually): Short-circuiting operators will need some work */ - break; - case EXPR_UNARY_OP: - if (!cgen_expr_pre(g, e->unary.of)) return false; - break; - case EXPR_CALL: { - if (!cgen_expr_pre(g, e->call.fn)) return false; - arr_foreach(&e->call.args, Expression, arg) - if (!cgen_expr_pre(g, arg)) - return false; - Type *fn_types = e->call.fn->type.fn.types.data; - Type *ret_type = &fn_types[0]; - if (cgen_fn_uses_out_param(ret_type)) { - /* generate out call */ - e->call.out_var = g->anon_var_count++; - - /* declaration of out variable */ - cgen_type_pre(g, ret_type); - cgen_anon_var(g, e->call.out_var); - cgen_type_post(g, ret_type); - cgen_writeln(g, ";"); - - cgen_expr(g, e->call.fn); - - cgen_write(g, "("); - arr_foreach(&e->call.args, Expression, arg) { - if (arg != e->call.args.data) { - cgen_write(g, ","); - cgen_space(g); - } - if (!cgen_expr(g, arg)) return false; - } - if (e->call.args.len) { - cgen_write(g, ","); - cgen_space(g); - } - cgen_write(g, "&"); - cgen_anon_var(g, e->call.out_var); - cgen_writeln(g, ");"); - } - } break; - default: break; - } - return true; -} - -static bool cgen_expr(CGenerator *g, Expression *e) { - switch (e->kind) { - case EXPR_INT_LITERAL: - cgen_write(g, "%lld", e->intl); - break; - case EXPR_FLOAT_LITERAL: - /* TODO: more precision */ - cgen_write(g, "%f", (double)e->floatl); - break; - case EXPR_STR_LITERAL: - cgen_write(g, "\""); - /* OPTIM: Maybe don't use i? this will probably be optimized by the compiler though... */ - for (size_t i = 0; i < e->strl.len; i++) { - /* TODO: Print ordinary characters nicely */ - cgen_write(g, "\\x%02x", e->strl.str[i]); - } - cgen_write(g, "\""); - break; - case EXPR_IDENT: - if (!cgen_ident(g, e->ident, &e->where)) return false; - break; - case EXPR_BINARY_OP: - cgen_write(g, "("); - if (!cgen_expr(g, e->binary.lhs)) return false; - switch (e->binary.op) { - case BINARY_PLUS: - cgen_write(g, "+"); - break; - case BINARY_MINUS: - cgen_write(g, "-"); - break; - case BINARY_SET: - cgen_write(g, "="); - break; - case BINARY_AT_INDEX: - cgen_write(g, "["); - break; - case BINARY_COMMA: - assert(0); - return false; - } - if (!cgen_expr(g, e->binary.rhs)) return false; - if (e->binary.op == BINARY_AT_INDEX) { - cgen_write(g, "]"); - } - cgen_write(g, ")"); - break; - case EXPR_UNARY_OP: - cgen_write(g, "("); - switch (e->unary.op) { - case UNARY_MINUS: - cgen_write(g, "-"); - break; - } - if (!cgen_expr(g, e->unary.of)) return false; - cgen_write(g, ")"); - break; - case EXPR_FN: - if (!cgen_fn_name(g, &e->fn, &e->where)) return false; - break; - case EXPR_CALL: { - Type *fn_types = e->call.fn->type.fn.types.data; - Type *ret_type = &fn_types[0]; - if (cgen_fn_uses_out_param(ret_type)) { /* if there's an out parameter, */ - cgen_anon_var(g, e->call.out_var); /* just use the variable we made earlier */ - } else { - if (!cgen_expr(g, e->call.fn)) return false; - cgen_write(g, "("); - arr_foreach(&e->call.args, Expression, arg) { - if (arg != e->call.args.data) { - cgen_write(g, ","); - cgen_space(g); - } - if (!cgen_expr(g, arg)) return false; - } - cgen_write(g, ")"); - } - } break; - case EXPR_DIRECT: - if (!cgen_direct(g, &e->direct, e->where)) return false; - break; - } - return true; -} - -static bool cgen_stmt(CGenerator *g, Statement *s); - -static void cgen_zero_value(CGenerator *g, Type *t) { - switch (t->kind) { - case TYPE_VOID: /* we should never need this */ - assert(0); - break; - case TYPE_FN: - cgen_write(g, "NULL"); - break; - case TYPE_ARR: - cgen_write(g, "{"); - cgen_zero_value(g, t->arr.of); - cgen_write(g, "}"); - break; - case TYPE_TUPLE: - assert(0); - break; - case TYPE_BUILTIN: - if (type_builtin_is_numerical(t->builtin)) { - cgen_write(g, "0"); - } else { - assert(0); - } - break; - case TYPE_UNKNOWN: - assert(0); - break; - } -} - -static bool cgen_decl(CGenerator *g, Declaration *d) { - size_t i = d->idents.len; - if (d->flags & DECL_FLAG_HAS_EXPR) { - if (!cgen_expr_pre(g, &d->expr)) - return false; - } - /* because , is left-associative, we want to go backwards */ - arr_foreach_reverse(&d->idents, Identifier, ident) { - Type *type; - if (d->idents.len > 1) { - /* it's a tuple! */ - type = &(((Type*)d->type.tuple.data)[--i]); - } else { - type = &d->type; - if (type->kind == TYPE_TUPLE) { - /* TODO */ - err_print(d->where, "Direct declaration of tuples is not supported yet."); - return false; - } - } - if (type->kind == TYPE_ARR) { - /* if you do a : [3]int; translates into int64_t av___23[3] = {0}; int64_t *a = av___23; */ - int has_expr = d->flags & DECL_FLAG_HAS_EXPR; - unsigned long var; - if (!has_expr) { - /* int64_t av___23[3] = {0}; */ - var = g->anon_var_count++; - cgen_type_pre(g, type); - cgen_anon_var(g, var); - cgen_type_post(g, type); - cgen_space(g); - cgen_write(g, "="); - cgen_space(g); - cgen_zero_value(g, type); - cgen_writeln(g, ";"); - } - /* int64_t *a = av___23; */ - cgen_type_pre(g, type->arr.of); - cgen_write(g, "(*"); - cgen_ident(g, *ident, NULL); - cgen_write(g, ")"); - cgen_type_post(g, type->arr.of); - cgen_space(g); - cgen_write(g, "="); - cgen_space(g); - if (has_expr) { - if (!cgen_expr(g, &d->expr)) return false; - } else { - cgen_anon_var(g, var); - } - cgen_writeln(g, ";"); - return true; - } - - cgen_type_pre(g, type); - if (d->flags & DECL_FLAG_CONST) { /* TODO: remove this (never actually produce constants) */ - cgen_space(g); - cgen_write(g, "const"); - cgen_space(g); - } - cgen_ident(g, *ident, NULL); - cgen_type_post(g, type); - cgen_space(g); - cgen_write(g, "="); - cgen_space(g); - if (d->flags & DECL_FLAG_HAS_EXPR) { - Expression *expr = &d->expr; - if (d->idents.len > 1) { - if (expr->kind == EXPR_BINARY_OP && expr->binary.op == BINARY_COMMA) { - if (!cgen_expr(g, expr->binary.rhs)) return false; - expr = expr->binary.lhs; /* ((3,4),5),6 => (3,4),5 */ - } else { - /* last iteration */ - if (!cgen_expr(g, expr)) return false; - } - - } else { - if (!cgen_expr(g, expr)) return false; - } - } else { - cgen_zero_value(g, type); - } - cgen_write(g, "; "); - } - cgen_writeln(g, ""); - return true; -} - -static bool cgen_stmt(CGenerator *g, Statement *s) { - switch (s->kind) { - case STMT_EXPR: - if (!cgen_expr(g, &s->expr)) - return false; - cgen_writeln(g, ";"); - break; - case STMT_DECL: { - Declaration *d = &s->decl; - if ((d->flags & DECL_FLAG_HAS_EXPR) && (d->flags & DECL_FLAG_CONST)) - if (d->expr.kind == EXPR_FN) - return true; /* already dealt with below */ - - return cgen_decl(g, &s->decl); - } - } - return true; -} - -static bool cgen_fns_in_stmt(CGenerator *g, Statement *s); - typedef struct { - bool is_return; /* true => this is a function return */ - unsigned long var_no; /* if is_return = false, set the anonymous variable with this number to the return value. */ - const char *exit_with; /* how to exit this block in C, e.g. "break" (not needed if is_return = true). */ -} BlockExitKind; - -/* generates a block but not the functions, etc. inside it */ -static bool cgen_block(CGenerator *g, Block *b, BlockExitKind *exit_kind) { - bool success = true; - cgen_writeln(g, "{"); - g->indent_level++; - arr_foreach(&b->stmts, Statement, s) { - if (!cgen_stmt(g, s)) - success = false; - } - if (exit_kind->is_return) { - /* generate return from function */ - Expression *ret = b->ret_expr; - if (ret && cgen_fn_uses_out_param(&ret->type)) { - if (ret->type.kind == TYPE_ARR) { - /* returning possibly multi-dimensional arrays */ - size_t total_size = 1; /* product of all dimensions */ - Type *type; - for (type = &ret->type; type->kind == TYPE_ARR; type = type->arr.of) - total_size *= type->arr.n; - /* type is now the base type of the array, e.g. [3][3][3]fn() => fn() */ - cgen_write(g, "memcpy(*out__, "); - if (!cgen_expr(g, b->ret_expr)) return false; - cgen_write(g, ", %lu * sizeof(", total_size); - cgen_type_pre(g, type); - cgen_type_post(g, type); - cgen_writeln(g, ")); return;"); - } else { - cgen_write(g, "*out__ = "); - if (!cgen_expr(g, b->ret_expr)) return false; - cgen_writeln(g, ";"); - cgen_writeln(g, "return;"); - } - } else { - cgen_write(g, "return"); - if (b->ret_expr) { - cgen_write(g, " "); - if (!cgen_expr(g, b->ret_expr)) return false; - } - cgen_writeln(g, ";"); - } - } else { - err_print(b->ret_expr->where, "TODO"); - return false; - } - if (success) { - g->indent_level--; - cgen_writeln(g, "}"); - } - return success; -} - -/* Generates function definition, and the definitions of all functions inside this */ -static bool cgen_fn(CGenerator *g, FnExpr *f) { - if (!cgen_fn_header(g, f)) return false; - Block *prev_block = g->block; - cgen_block_enter(g, &f->body); - cgen_space(g); - BlockExitKind e_kind; - e_kind.is_return = 1; - if (!cgen_block(g, &f->body, &e_kind)) return false; + FILE *outc; + long ident_counter; +} CGenerator; - bool ret = true; - arr_foreach(&f->body.stmts, Statement, stmt) { - if (!cgen_fns_in_stmt(g, stmt)) ret = false; - } - cgen_block_exit(g, prev_block); - return ret; +static void cgen_create(CGenerator *g, FILE *out) { + g->outc = out; + g->ident_counter = 0; } -static bool cgen_fns_in_expr(CGenerator *g, Expression *e) { - switch (e->kind) { - case EXPR_FN: - return cgen_fn(g, &e->fn); - case EXPR_CALL: - return cgen_fns_in_expr(g, e->call.fn); - default: return true; - } +static inline FILE *cgen_writing_to(CGenerator *g) { + return g->outc; /* for now */ } -static bool cgen_fns_in_stmt(CGenerator *g, Statement *s) { - switch (s->kind) { - case STMT_EXPR: - if (s->expr.kind == EXPR_FN) { - warn_print(s->where, "Statement of function has no effect (try assigning the function to a variable)."); - } else { - return cgen_fns_in_expr(g, &s->expr); - } - break; - case STMT_DECL: { - Declaration *d = &s->decl; - if (d->flags & DECL_FLAG_HAS_EXPR) - return cgen_fns_in_expr(g, &d->expr); - } break; - } - return true; +static void cgen_write(CGenerator *g, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + vfprintf(cgen_writing_to(g), fmt, args); + va_end(args); } -/* generate a statement at top level, including any functions in it. */ -static bool cgen_stmt_top(CGenerator *g, Statement *s) { - if (!cgen_fns_in_stmt(g, s)) return false; - switch (s->kind) { - case STMT_EXPR: { - Expression *e = &s->expr; - bool ignored = true; - switch (e->kind) { - case EXPR_DIRECT: - switch (e->direct.which) { - case DIRECT_C: - ignored = false; - cgen_direct(g, &e->direct, e->where); - break; - case DIRECT_COUNT: assert(0); break; - } - default: break; - } - if (ignored) - warn_print(e->where, "Expression at top level currently ignored."); /* TODO */ - } break; - case STMT_DECL: break; - } - return true; -} +static void cgen_decls_file(CGenerator *g, ParsedFile *f); -static bool cgen_file(CGenerator *g, ParsedFile *f) { - cgen_write_line_comment(g, "toc"); - bool ret = true; - if (!cgen_decls_file(g, f)) return false; - arr_foreach(&f->stmts, Statement, s) { - if (!cgen_stmt_top(g, s)) return false; - } - g->writing_to = CGEN_WRITING_TO_C; - /* write actual main function */ - cgen_write(g, "\nint main(void) {\n" - "\tmain__();\n" - "\treturn 0;\n" - "}\n"); - return ret; +static void cgen_file(CGenerator *g, ParsedFile *f) { + cgen_decls_file(g, f); + cgen_write(g, "/* code */\n"); } |