From a2f5cd5069df70436228c8f6cb6ed82edcdcaae8 Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Wed, 9 Oct 2019 11:16:07 -0400 Subject: started cgen --- archive/base_cgen.c | 295 ++++++++++++++++++++++++++++++++ archive/cgen.c | 463 +++++++++++++++++++++++++++++++++++++++++++++++++++ archive/decls_cgen.c | 106 ++++++++++++ 3 files changed, 864 insertions(+) create mode 100644 archive/base_cgen.c create mode 100644 archive/cgen.c create mode 100644 archive/decls_cgen.c (limited to 'archive') diff --git a/archive/base_cgen.c b/archive/base_cgen.c new file mode 100644 index 0000000..d773ba9 --- /dev/null +++ b/archive/base_cgen.c @@ -0,0 +1,295 @@ +/* figures out types and writes function prototypes */ +/* TODO: check ferror */ +typedef enum { + CGEN_WRITING_TO_H, + CGEN_WRITING_TO_C +} CGenWritingTo; +typedef struct { + FILE *c_out; + FILE *h_out; + unsigned long anon_fn_count; + unsigned long anon_var_count; + Block *block; + int indent_level; + bool indent_next; /* should the next thing written be indented? */ + CGenWritingTo writing_to; + Identifier main_ident; +} CGenerator; + +static FILE *cgen_writing_to(CGenerator *g) { + switch (g->writing_to) { + case CGEN_WRITING_TO_H: + return g->h_out; + case CGEN_WRITING_TO_C: + return g->c_out; + } + assert(0); + return NULL; +} + +static void cgen_indent(CGenerator *g) { + if (g->indent_next) { + for (int i = 0; i < g->indent_level; i++) + fprintf(cgen_writing_to(g), "\t"); + g->indent_next = false; + } +} + +static void cgen_vwrite(CGenerator *g, const char *fmt, va_list args) { + cgen_indent(g); + vfprintf(cgen_writing_to(g), fmt, args); +} + +static void cgen_write(CGenerator *g, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + cgen_vwrite(g, fmt, args); + va_end(args); +} + +/* Used to write an UNNECESSARY space */ +static void cgen_space(CGenerator *g) { + cgen_write(g, " "); +} + +/* Used to write something followed by an UNNECESSARY newline */ +static void cgen_writeln(CGenerator *g, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + cgen_vwrite(g, fmt, args); + va_end(args); + cgen_write(g, "\n"); + g->indent_next = true; +} + +/* static void cgen_write_comment(CGenerator *g, const char *fmt, ...) { */ +/* cgen_write(g, "/\* "); */ +/* va_list args; */ +/* va_start(args, fmt); */ +/* cgen_vwrite(g, fmt, args); */ +/* va_end(args); */ +/* cgen_write(g, " *\/"); */ +/* } */ + +static void cgen_write_line_comment(CGenerator *g, const char *fmt, ...) { + /* could switch to // for c99 */ + cgen_write(g, "/* "); + va_list args; + va_start(args, fmt); + cgen_vwrite(g, fmt, args); + va_end(args); + cgen_write(g, " */\n"); +} + + +/* Pass NULL for where if you don't want to check if it's declared */ +static bool cgen_fn_name(CGenerator *g, FnExpr *f, Location *where); +static bool cgen_ident(CGenerator *g, Identifier i, Location *where) { + if (where) { + IdentDecl *id_decl = ident_decl(i); + if (!id_decl) { + err_print(*where, "Identifier undeclared: %s", ident_to_str(i)); + return false; + } + Declaration *decl = id_decl->decl; + if ((decl->flags & DECL_FLAG_HAS_EXPR) && (decl->flags & DECL_FLAG_CONST)) { + if (decl->expr.kind == EXPR_FN) { + cgen_fn_name(g, decl->expr.fn, NULL); + return true; + } + } + } + cgen_indent(g); + fprint_ident_reduced_charset(cgen_writing_to(g), i); + return true; +} + +static const char *builtin_type_to_str(BuiltinType b) { + /* TODO: make this return int/long/etc. if stdint.h is not available */ + switch (b) { + case BUILTIN_I8: return "int8_t"; + case BUILTIN_I16: return "int16_t"; + case BUILTIN_I32: return "int32_t"; + case BUILTIN_I64: return "int64_t"; + case BUILTIN_U8: return "uint8_t"; + case BUILTIN_U16: return "uint16_t"; + case BUILTIN_U32: return "uint32_t"; + case BUILTIN_U64: return "uint64_t"; + case BUILTIN_FLOAT: return "float"; + case BUILTIN_DOUBLE: return "double"; + case BUILTIN_TYPE_COUNT: break; + } + assert(0); + return NULL; +} + +/* will this function use a pointer parameter for output? (e.g. fn()[3]int => void(int (*x)[3]) */ +static bool cgen_fn_uses_out_param(Type *fn_ret_type) { + switch (fn_ret_type->kind) { + case TYPE_TUPLE: + case TYPE_ARR: + return true; + default: + return false; + } +} + + +static void cgen_anon_var(CGenerator *g, unsigned long v) { + cgen_write(g, "av___%lu", v); +} + +static void cgen_anon_fn(CGenerator *g, unsigned long f) { + cgen_write(g, "af___%lu", f); +} + +static void cgen_type_pre(CGenerator *g, Type *t) { + switch (t->kind) { + case TYPE_VOID: + cgen_write(g, "void "); + break; + case TYPE_BUILTIN: + cgen_write(g, "%s ", builtin_type_to_str(t->builtin)); + break; + case TYPE_FN: { + Type *types = t->fn.types.data; + Type *ret_type = &types[0]; + if (cgen_fn_uses_out_param(ret_type)) { + cgen_write(g, "void "); + } else { + cgen_type_pre(g, ret_type); + } + cgen_write(g, "(*"); + } break; + case TYPE_TUPLE: + assert(0); + return; + case TYPE_UNKNOWN: + err_print(t->where, "Type of unknown-typed expression required (x := #C(\"...\") will not work; you need to annotate the type of x)."); + abort(); + case TYPE_ARR: + cgen_type_pre(g, t->arr.of); + break; + } +} + +static void cgen_type_post(CGenerator *g, Type *t); +/* either pass NULL for param_types (x)or for params */ +static void cgen_fn_params(CGenerator *g, Type *param_types, Param *params, size_t nparams, Type *ret_type) { + bool uses_out_param = cgen_fn_uses_out_param(ret_type); + + cgen_write(g, "("); + if (nparams) { + for (size_t i = 0; i < nparams; i++) { + if (i) { + cgen_write(g, ","); + cgen_space(g); + } + if (param_types) { + cgen_type_pre(g, ¶m_types[i]); + cgen_type_post(g, ¶m_types[i]); + } else { + Param *p = ¶ms[i]; + cgen_type_pre(g, &p->type); + cgen_ident(g, p->name, NULL); + cgen_type_post(g, &p->type); + } + } + } else { + if (!uses_out_param) + cgen_write(g, "void"); + } + if (uses_out_param) { + if (nparams) { + cgen_write(g, ","); + cgen_space(g); + } + /* write out param */ + cgen_type_pre(g, ret_type); + cgen_write(g, "(*out__)"); /* TODO: fix this for named return values */ + cgen_type_post(g, ret_type); + + } + cgen_write(g, ")"); +} + +static void cgen_type_post(CGenerator *g, Type *t) { + switch (t->kind) { + case TYPE_VOID: + case TYPE_BUILTIN: + break; + case TYPE_FN: { + Type *types = t->fn.types.data; + Type *ret_type = &types[0]; + Type *param_types = types + 1; + assert(t->fn.types.len > 0); + size_t nparams = t->fn.types.len-1; + bool uses_out_param = cgen_fn_uses_out_param(ret_type); + cgen_write(g, ")"); + cgen_fn_params(g, param_types, NULL, nparams, ret_type); + if (!uses_out_param) { + cgen_type_post(g, ret_type); + } + cgen_space(g); + } break; + case TYPE_TUPLE: + assert(0); + return; + case TYPE_ARR: + cgen_write(g, "[%lu]", t->arr.n); + cgen_type_post(g, t->arr.of); + break; + case TYPE_UNKNOWN: /* we should always do pre first */ + assert(0); + break; + } +} + +static bool cgen_fn_name(CGenerator *g, FnExpr *f, Location *where) { + if (f->name) { + /* TODO: check block */ + if (f->name == g->main_ident) { + cgen_write(g, "main__"); + return true; + } else { + return cgen_ident(g, f->name, where); + } + } else { + cgen_anon_fn(g, f->id); + return true; + } +} + +static bool cgen_fn_header(CGenerator *g, FnExpr *f) { + if (!f->name || g->block != NULL) { + cgen_write(g, "static "); /* anonymous functions only exist in this translation unit */ + } + + bool uses_out_param = cgen_fn_uses_out_param(&f->ret_type); + size_t nparams = f->params.len; + if (uses_out_param) { + cgen_write(g, "void "); + cgen_fn_name(g, f, NULL); + cgen_fn_params(g, NULL, (Param*)f->params.data, nparams, &f->ret_type); + + } else { + cgen_type_pre(g, &f->ret_type); + cgen_fn_name(g, f, NULL); + cgen_fn_params(g, NULL, (Param*)f->params.data, nparams, &f->ret_type); + cgen_type_post(g, &f->ret_type); + } + + return true; +} + +static bool cgen_block_enter(CGenerator *g, Block *b) { + g->block = b; + return block_enter(b, &b->stmts); +} + +static bool cgen_block_exit(CGenerator *g, Block *into) { + Block *b = g->block; + g->block = into; + return block_exit(b, &b->stmts); +} + diff --git a/archive/cgen.c b/archive/cgen.c new file mode 100644 index 0000000..7ff2308 --- /dev/null +++ b/archive/cgen.c @@ -0,0 +1,463 @@ +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 \n" + "#include \n" + "#include \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; + + 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 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 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; +} + +/* 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 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; +} diff --git a/archive/decls_cgen.c b/archive/decls_cgen.c new file mode 100644 index 0000000..a596f53 --- /dev/null +++ b/archive/decls_cgen.c @@ -0,0 +1,106 @@ +/* C declarations of functions and global variables. e must be a function expression */ +static bool cgen_decl_fn(CGenerator *g, FnExpr *f) { + /* assign an ID to the function */ + if (f->name && g->block == NULL) { + f->id = f->name->c_fn_reps++; + } else { + f->id = g->anon_fn_count++; + } + + if (!cgen_fn_header(g, f)) return false; + cgen_writeln(g, ";"); + return true; +} + +static bool cgen_decls_stmt(CGenerator *g, Statement *s); + +static bool cgen_decls_expr(CGenerator *g, Expression *e) { + switch (e->kind) { + case EXPR_FN: { + FnExpr *f = &e->fn; + if (f->name && g->block == NULL) { /* write named function prototypes in global scope to header file */ + g->writing_to = CGEN_WRITING_TO_H; + } else { + g->writing_to = CGEN_WRITING_TO_C; + } + if (!cgen_decl_fn(g, f)) + return false; + g->writing_to = CGEN_WRITING_TO_C; + + + bool ret = true; + Block *prev_block = g->block; + cgen_block_enter(g, &f->body); + arr_foreach(&f->body.stmts, Statement, s) { + if (!cgen_decls_stmt(g, s)) + ret = false; + } + cgen_block_exit(g, prev_block); + return ret; + } + case EXPR_CALL: + if (!cgen_decls_expr(g, e->call.fn)) + return false; + arr_foreach(&e->call.args, Expression, arg) { + if (!cgen_decls_expr(g, arg)) + return false; + } + break; + default: break; + } + return true; +} + + +static bool cgen_expr(CGenerator *g, Expression *e); +static bool cgen_decls_stmt(CGenerator *g, Statement *s) { + switch (s->kind) { + case STMT_EXPR: + return cgen_decls_expr(g, &s->expr); + case STMT_DECL: { + Declaration *d = &s->decl; + bool is_const_fn = (d->flags & DECL_FLAG_HAS_EXPR) && (d->flags & DECL_FLAG_CONST) + && d->expr.kind == EXPR_FN; + + if (is_const_fn) { + /* e.g. foo @= fn() {}; (we want to set the function's name to "foo") */ + d->expr.fn.name = *(Identifier*)d->idents.data; + } + + if (d->flags & DECL_FLAG_HAS_EXPR) { + cgen_decls_expr(g, &d->expr); + } + + if (!is_const_fn) { + if (g->block == NULL) { + /* declare this/these global variable(s) */ + arr_foreach(&d->idents, Identifier, i) { + cgen_type_pre(g, &d->type); + cgen_ident(g, *i, NULL); + cgen_type_post(g, &d->type); + if (d->flags & DECL_FLAG_HAS_EXPR) { /* TODO: check if expr is const */ + cgen_space(g); + cgen_write(g, "="); + cgen_space(g); + if (!cgen_expr(g, &d->expr)) + return false; + } + cgen_write(g, ";"); + cgen_space(g); + } + cgen_writeln(g, ""); + } + } + + } break; + } + return true; +} + +static bool cgen_decls_file(CGenerator *g, ParsedFile *f) { + arr_foreach(&f->stmts, Statement, s) { + if (!cgen_decls_stmt(g, s)) + return false; + } + return true; +} -- cgit v1.2.3