summaryrefslogtreecommitdiff
path: root/archive
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2019-10-09 11:16:07 -0400
committerLeo Tenenbaum <pommicket@gmail.com>2019-10-09 11:16:07 -0400
commita2f5cd5069df70436228c8f6cb6ed82edcdcaae8 (patch)
tree85a3aa174a9b30d562a2dab9860e141719261de0 /archive
parent621359c153dbe378264d9ce96caf467b52a29f0c (diff)
started cgen
Diffstat (limited to 'archive')
-rw-r--r--archive/base_cgen.c295
-rw-r--r--archive/cgen.c463
-rw-r--r--archive/decls_cgen.c106
3 files changed, 864 insertions, 0 deletions
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, &param_types[i]);
+ cgen_type_post(g, &param_types[i]);
+ } else {
+ Param *p = &params[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 <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;
+
+ 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;
+}