From 730469c7a96a26ecba362db39c67e97bd07bf157 Mon Sep 17 00:00:00 2001
From: Leo Tenenbaum <pommicket@gmail.com>
Date: Mon, 27 Jan 2020 11:12:16 -0500
Subject: #include

---
 abbrevs.txt   |  1 +
 cgen.c        | 10 ++++++++++
 copy.c        | 11 +++++++++++
 decls_cgen.c  |  5 +++++
 eval.c        |  5 +++++
 incstuff.toc  |  8 ++++++++
 main.c        | 41 ++++++-----------------------------------
 package.c     | 20 ++++++++++++++++++++
 parse.c       | 28 ++++++++++++++++++++++++++++
 sdecls_cgen.c |  5 +++++
 test.toc      |  8 +-------
 toc.c         | 32 ++++++++++++++++++++++++++++++++
 tokenizer.c   |  2 +-
 types.c       | 39 +++++++++++++++++++++++++++++++++++++--
 types.h       | 12 +++++++++++-
 15 files changed, 181 insertions(+), 46 deletions(-)
 create mode 100644 incstuff.toc

diff --git a/abbrevs.txt b/abbrevs.txt
index 643d89b..2449533 100644
--- a/abbrevs.txt
+++ b/abbrevs.txt
@@ -16,6 +16,7 @@ exptr - exporter
 fn - function
 ident - identifier
 imptr - importer
+inc - include
 kw - keyword
 len - length
 num - number
diff --git a/cgen.c b/cgen.c
index d0ebedd..f11a94a 100644
--- a/cgen.c
+++ b/cgen.c
@@ -2028,6 +2028,11 @@ static bool cgen_stmt(CGenerator *g, Statement *s) {
 		if (!cgen_ret(g, has_expr ? &s->ret.expr : NULL))
 			return false;
 	} break;
+	case STMT_INCLUDE:
+		arr_foreach(s->inc.stmts, Statement, sub)
+			if (!cgen_stmt(g, sub))
+				return false;
+	    break;
 	}
 	return true;
 }
@@ -2110,6 +2115,11 @@ static bool cgen_defs_stmt(CGenerator *g, Statement *s) {
 			if (!cgen_defs_expr(g, &s->ret.expr))
 				return false;
 		break;
+	case STMT_INCLUDE:
+		arr_foreach(s->inc.stmts, Statement, sub)
+			if (!cgen_defs_stmt(g, sub))
+				return false;
+		break;
 	}
 	return true;
 }
diff --git a/copy.c b/copy.c
index 8b3db68..0a034f6 100644
--- a/copy.c
+++ b/copy.c
@@ -304,6 +304,17 @@ static void copy_stmt(Copier *c, Statement *out, Statement *in) {
 		if (in->ret.flags & RET_HAS_EXPR)
 			copy_expr(c, &out->ret.expr, &in->ret.expr);
 		break;
+	case STMT_INCLUDE:
+		if (in->flags & STMT_TYPED) {
+			size_t nstmts = arr_len(in->inc.stmts);
+			arr_set_lena(&out->inc.stmts, nstmts, c->allocr);
+			for (size_t i = 0; i < nstmts; ++i) {
+				copy_stmt(c, &out->inc.stmts[i], &in->inc.stmts[i]);
+			}
+		} else {
+			copy_expr(c, &out->inc.filename, &in->inc.filename);
+		}
+		break;
 	case STMT_EXPR:
 		copy_expr(c, &out->expr, &in->expr);
 		break;
diff --git a/decls_cgen.c b/decls_cgen.c
index 99ae190..b0382b5 100644
--- a/decls_cgen.c
+++ b/decls_cgen.c
@@ -291,6 +291,11 @@ static bool cgen_decls_stmt(CGenerator *g, Statement *s) {
 			if (!cgen_decls_expr(g, &s->ret.expr))
 				return false;
 		break;
+	case STMT_INCLUDE:
+		arr_foreach(s->inc.stmts, Statement, sub)
+			if (!cgen_decls_stmt(g, sub))
+				return false;
+		break;
 	}
 	return true;
 }
diff --git a/eval.c b/eval.c
index 3d6cde6..32804e0 100644
--- a/eval.c
+++ b/eval.c
@@ -1668,6 +1668,11 @@ static bool eval_stmt(Evaluator *ev, Statement *stmt) {
 			return false;
 		copy_val(NULL, &ev->ret_val, &r, &stmt->ret.expr.type);
 	} break;
+	case STMT_INCLUDE:
+		arr_foreach(stmt->inc.stmts, Statement, sub)
+			if (!eval_stmt(ev, sub))
+				return false;
+		return false;
 	}
 	return true;
 }
diff --git a/incstuff.toc b/incstuff.toc
new file mode 100644
index 0000000..c192e06
--- /dev/null
+++ b/incstuff.toc
@@ -0,0 +1,8 @@
+
+	x : arr.Arr(char);
+	arr.arr_add(&x, 'H');
+	arr.arr_add(&x, 'e');
+	arr.arr_add(&x, 'l');
+	arr.arr_add(&x, 'l');
+	arr.arr_add(&x, 'o');
+	arr.arr_add(&x, '!');
\ No newline at end of file
diff --git a/main.c b/main.c
index 3b58f8f..6d0ae5c 100644
--- a/main.c
+++ b/main.c
@@ -18,13 +18,8 @@
 
 /* 
 TODO:
-#builtin("sizeof int")
-#builtin("target sizeof int") 
-etc.
-
-allow any global declaration to be used before itself
-
 #include
+nested packages
 constants in structs
 #if
 
@@ -64,40 +59,16 @@ int main(int argc, char **argv) {
 		if (strs_equal(argv[i], "-o"))
 			out_filename = argv[i+1];
 	}
-	
-	FILE *in = fopen(in_filename, "r");
-	if (!in) {
-		fprintf(stderr, "Could not open file: %s.\n", in_filename);
-		return EXIT_FAILURE;
-	}
-	
-	char *contents = err_malloc(4096);
-	contents[0] = 0; /* put 0 byte at the start of the file. see err.c:err_print_location_text to find out why */
-	contents[1] = 0; /* if fgets fails the first time */
-	long contents_cap = 4095;
-	long contents_len = 1;
-	while (fgets(contents + contents_len, (int)(contents_cap - contents_len), in)) {
-		contents_len += (long)strlen(contents + contents_len);
-		
-		if (contents_len >= (long)contents_cap - 1024) {
-			contents_cap *= 2;
-			contents = err_realloc(contents, (size_t)contents_cap + 1);
-		}
-	}
-	++contents;
-	if (ferror(in)) {
-		fprintf(stderr, "Error reading input file: %s.\n", in_filename);
-		return EXIT_FAILURE;
-	}
-	fclose(in);
-	Identifiers idents;
-	idents_create(&idents);
-	Tokenizer t;
 	Allocator main_allocr;
 	allocr_create(&main_allocr);
 	ErrCtx err_ctx = {0};
 	err_ctx.enabled = true;
 	err_ctx.color_enabled = true;
+	char *contents = read_entire_file(&main_allocr, &err_ctx, in_filename);
+	
+	Identifiers idents;
+	idents_create(&idents);
+	Tokenizer t;
 	File file = {0};
 	file.filename = in_filename;
 	file.contents = contents;
diff --git a/package.c b/package.c
index 4483e45..a102a47 100644
--- a/package.c
+++ b/package.c
@@ -1053,6 +1053,17 @@ static bool export_stmt(Exporter *ex, Statement *s) {
 			if (!export_expr(ex, &s->ret.expr))
 				return false;
 	} break;
+	case STMT_INCLUDE:
+		if (s->flags & STMT_TYPED) {
+			export_len(ex, arr_len(s->inc.stmts));
+			arr_foreach(s->inc.stmts, Statement, sub)
+				if (!export_stmt(ex, sub))
+					return false;
+		} else {
+			if (!export_expr(ex, &s->inc.filename))
+				return false;
+		}
+		break;
 	}
 	return true;
 }
@@ -1068,6 +1079,15 @@ static void import_stmt(Importer *im, Statement *s) {
 	case STMT_DECL:
 		import_decl(im, &s->decl);
 		break;
+	case STMT_INCLUDE:
+		if (s->flags & STMT_TYPED) {
+			import_arr(im, &s->inc.stmts);
+			arr_foreach(s->inc.stmts, Statement, sub)
+				import_stmt(im, sub);
+		} else {
+			import_expr(im, &s->inc.filename);
+		}
+		break;
 	case STMT_RET:
 		s->ret.flags = import_u8(im);
 		if (s->ret.flags & RET_HAS_EXPR)
diff --git a/parse.c b/parse.c
index 5e22103..50ebdee 100644
--- a/parse.c
+++ b/parse.c
@@ -1724,6 +1724,7 @@ static bool parse_expr(Parser *p, Expression *e, Token *end) {
 					break;
 				case DIRECT_FOREIGN:
 				case DIRECT_EXPORT:
+				case DIRECT_INCLUDE:
 					tokr_err(t, "Unrecognized expression.");
 					return false;
 				case DIRECT_COUNT: assert(0); break;
@@ -2057,6 +2058,23 @@ static bool parse_stmt(Parser *p, Statement *s, bool *was_a_statement) {
 		}
 		default: break;
 		}
+	} else if (t->token->kind == TOKEN_DIRECT) {
+		switch (t->token->direct) {
+		case DIRECT_INCLUDE: {
+			++t->token;
+			s->kind = STMT_INCLUDE;
+			if (!parse_expr(p, &s->inc.filename, expr_find_end(p, 0)))
+				return false;
+			if (!token_is_kw(t->token, KW_SEMICOLON)) {
+				tokr_err(t, "Expected ; after #include directive");
+				return false;
+			}
+			++t->token;
+			return true;
+		} break;
+		default:
+			break;
+		}
 	}
 	if (is_decl(t)) {
 		s->kind = STMT_DECL;
@@ -2396,6 +2414,16 @@ static void fprint_stmt(FILE *out, Statement *s) {
 			fprint_expr(out, &s->ret.expr);
 		fprintf(out, ";\n");
 		break;
+	case STMT_INCLUDE:
+		if (s->flags & STMT_TYPED) {
+		    arr_foreach(s->inc.stmts, Statement, sub)
+				fprint_stmt(out, sub);
+		} else {
+			fprintf(out, "#include ");
+			fprint_expr(out, &s->inc.filename);
+			fprintf(out, ";\n");
+		}
+		break;
 	}
 }
 
diff --git a/sdecls_cgen.c b/sdecls_cgen.c
index 2bd71a0..966b22e 100644
--- a/sdecls_cgen.c
+++ b/sdecls_cgen.c
@@ -108,6 +108,11 @@ static bool cgen_sdecls_stmt(CGenerator *g, Statement *s) {
 			if (!cgen_sdecls_expr(g, &s->ret.expr))
 				return false;
 		break;
+	case STMT_INCLUDE:
+		arr_foreach(s->inc.stmts, Statement, sub)
+			if (!cgen_sdecls_stmt(g, sub))
+				return false;
+	    break;
 	}
 	return true;
 }
diff --git a/test.toc b/test.toc
index 2b645bd..9e82451 100644
--- a/test.toc
+++ b/test.toc
@@ -1,13 +1,7 @@
 arr ::= pkg "std/arr";
 io ::= pkg "std/io";
 main ::= fn() {
-	x : arr.Arr(char);
-	arr.arr_add(&x, 'H');
-	arr.arr_add(&x, 'e');
-	arr.arr_add(&x, 'l');
-	arr.arr_add(&x, 'l');
-	arr.arr_add(&x, 'o');
-	arr.arr_add(&x, '!');
+#include "incstuff.toc";
 
 	s : [1]char;
 	s[0] = (arr.arr_len(x) as char) + '0';
diff --git a/toc.c b/toc.c
index 57c67b5..9bb497e 100644
--- a/toc.c
+++ b/toc.c
@@ -84,6 +84,38 @@ static size_t compiler_sizeof(Type *t);
 #include "copy.c"
 #include "binfile.c"
 
+/* returns NULL on error */
+static char *read_entire_file(Allocator *a, ErrCtx *ectx, const char *filename) {
+	FILE *in = fopen(filename, "r");
+	
+	if (!in) {
+		Location where = {0};
+		File file = {0};
+		file.ctx = ectx;
+		file.filename = filename;
+		where.file = &file;
+		err_print(where, "Could not open file: %s.", filename);
+		return NULL;
+	}
+	char *contents = allocr_malloc(a, 4096);
+	contents[0] = 0; /* put 0 byte at the start of the file. see err.c:err_print_location_text to find out why */
+	contents[1] = 0; /* if fgets fails the first time */
+	long contents_cap = 4095;
+	long contents_len = 1;
+	while (fgets(contents + contents_len, (int)(contents_cap - contents_len), in)) {
+		contents_len += (long)strlen(contents + contents_len);
+		
+		if (contents_len >= (long)contents_cap - 1024) {
+			size_t prev = (size_t)contents_cap + 1;
+			contents_cap *= 2;
+			contents = allocr_realloc(a, contents, prev, (size_t)contents_cap + 1);
+		}
+	}
+	++contents;
+	return contents;
+}
+	
+
 
 #include "identifiers.c"
 #include "tokenizer.c"
diff --git a/tokenizer.c b/tokenizer.c
index 98c0d13..dc194ca 100644
--- a/tokenizer.c
+++ b/tokenizer.c
@@ -20,7 +20,7 @@ static const char *const keywords[KW_COUNT] =
 static inline const char *kw_to_str(Keyword k) { return keywords[k]; }
 
 static const char *directives[DIRECT_COUNT] =
-	{"C", "sizeof", "alignof", "export", "foreign", "builtin"};
+	{"C", "sizeof", "alignof", "export", "foreign", "builtin", "include"};
 
 /* Returns KW_COUNT if it's not a keyword */
 /* OPTIM: don't use strncmp so much */
diff --git a/types.c b/types.c
index a84bf30..c5f0d47 100644
--- a/types.c
+++ b/types.c
@@ -2249,10 +2249,13 @@ static bool types_block(Typer *tr, Block *b) {
 	bool success = true;
 	if (!typer_block_enter(tr, b))
 		return false;
+
 	arr_foreach(b->stmts, Statement, s) {
-		if (!types_stmt(tr, s))
+		if (!types_stmt(tr, s)) {
 			success = false;
-		else if (s->kind == STMT_EXPR && (s->flags & STMT_EXPR_NO_SEMICOLON)) {
+			continue;
+		}
+		if (s->kind == STMT_EXPR && (s->flags & STMT_EXPR_NO_SEMICOLON)) {
 			/* not voided */
 			Expression *e = &s->expr;
 			if (e->type.kind == TYPE_VOID) {
@@ -2489,6 +2492,36 @@ static bool types_stmt(Typer *tr, Statement *s) {
 			}
 		}
 		break;
+	case STMT_INCLUDE: {
+		char *filename = eval_expr_as_cstr(tr, &s->inc.filename, "import filename");
+		if (!filename)
+			return false;
+		char *contents = read_entire_file(tr->allocr, tr->err_ctx, filename);
+		Tokenizer tokr;
+		tokr_create(&tokr, tr->idents, tr->err_ctx, tr->allocr);
+		File *file = typer_calloc(tr, 1, sizeof *file);
+		file->filename = filename;
+		file->contents = contents;
+		file->ctx = tr->err_ctx;
+		if (!tokenize_file(&tokr, file))
+			return false;
+		Parser parser;
+		parser_create(&parser, &tokr, tr->allocr);
+		ParsedFile parsed_file;
+		if (!parse_file(&parser, &parsed_file)) {
+			return false;
+		}
+		Statement *stmts_inc = parsed_file.stmts;
+	    s->inc.stmts = stmts_inc;
+		arr_foreach(stmts_inc, Statement, s_incd) {
+			if (!types_stmt(tr, s_incd))
+				return false;
+			if (s_incd->kind == STMT_DECL) {
+				if (!add_ident_decls(tr->block, &s_incd->decl, SCOPE_CHECK_REDECL))
+					return false;
+			}
+		}
+	} break;
 	}
 	return true;
 }
@@ -2513,6 +2546,7 @@ static void typer_create(Typer *tr, Evaluator *ev, ErrCtx *err_ctx, Allocator *a
 static bool types_file(Typer *tr, ParsedFile *f) {
 	bool ret = true;
 	FILE *pkg_fp = NULL;
+	tr->parsed_file = f;
 	if (f->pkg_name) {
 		Value pkg_name;
 		if (!types_expr(tr, f->pkg_name))
@@ -2548,6 +2582,7 @@ static bool types_file(Typer *tr, ParsedFile *f) {
 		exptr_create(tr->exptr, pkg_fp, pkg_file_name, tr->err_ctx);
 		exptr_start(tr->exptr, pkg_name_str, pkg_name_len);
 	}
+
 	arr_foreach(f->stmts, Statement, s) {
 		if (!types_stmt(tr, s)) {
 			ret = false;
diff --git a/types.h b/types.h
index e7a1503..25ddad6 100644
--- a/types.h
+++ b/types.h
@@ -240,6 +240,7 @@ typedef enum {
 			  DIRECT_EXPORT,
 			  DIRECT_FOREIGN,
 			  DIRECT_BUILTIN,
+			  DIRECT_INCLUDE,
 			  DIRECT_COUNT
 } Directive;
 
@@ -820,7 +821,8 @@ typedef struct Declaration {
 typedef enum {
 			  STMT_DECL,
 			  STMT_EXPR,
-			  STMT_RET
+			  STMT_RET,
+			  STMT_INCLUDE
 } StatementKind;
 
 enum {
@@ -833,7 +835,13 @@ typedef struct Return {
 
 enum {
 	  STMT_EXPR_NO_SEMICOLON = 0x01,
+	  STMT_TYPED = 0x02
 };
+typedef union {
+	Expression filename; /* before typing */
+	struct Statement *stmts; /* after typing */
+} Include;
+
 typedef struct Statement {
 	Location where;
 	StatementKind kind;
@@ -842,6 +850,7 @@ typedef struct Statement {
 		Declaration decl;
 		Expression expr;
 		Return ret;
+		Include inc;
 	};
 } Statement;
 
@@ -912,6 +921,7 @@ typedef struct Typer {
 	/* for checking for problematic struct circular dependencies */
 	bool *is_reference_stack;
 	Package *pkgs; /* all packages which have been imported */
+	ParsedFile *parsed_file;
 } Typer;
 
 typedef struct Exporter {
-- 
cgit v1.2.3