From ef7365224f48c18d4b3230c544ff93cbb4548884 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Thu, 29 Dec 2022 16:12:03 +0100 Subject: [PATCH] Added $include. --- src/compiler/ast.c | 3 ++ src/compiler/compiler.c | 8 +++- src/compiler/compiler_internal.h | 9 +++- src/compiler/context.c | 2 + src/compiler/copying.c | 2 + src/compiler/enums.h | 3 +- src/compiler/llvm_codegen.c | 1 + src/compiler/parse_global.c | 72 ++++++++++++++++++++++++++++++++ src/compiler/parser.c | 4 ++ src/compiler/sema_decls.c | 1 + src/compiler/sema_expr.c | 1 + src/compiler/sema_passes.c | 5 +++ src/compiler/sema_types.c | 1 + src/compiler/semantic_analyser.c | 5 ++- src/compiler/source_file.c | 8 ++-- src/version.h | 2 +- 16 files changed, 118 insertions(+), 9 deletions(-) diff --git a/src/compiler/ast.c b/src/compiler/ast.c index b65ab973f..ee777f4f3 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -97,6 +97,7 @@ Decl *decl_new_with_type(const char *name, SourceSpan loc, DeclKind decl_type, V case DECL_INITIALIZE: case DECL_FINALIZE: case DECL_CT_ECHO: + case DECL_CT_INCLUDE: UNREACHABLE } Type *type = type_new(kind, name ? name : "$anon"); @@ -172,6 +173,8 @@ const char *decl_to_a_name(Decl *decl) return "a static initializer"; case DECL_FINALIZE: return "a static finalizer"; + case DECL_CT_INCLUDE: + return "an include"; case DECL_VAR: switch (decl->var.kind) { diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 45dc4b9ef..1337cf46c 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -76,7 +76,9 @@ static void compiler_lex(void) VECEACH(global_context.sources, i) { bool loaded = false; - File *file = source_file_load(global_context.sources[i], &loaded); + const char *error; + File *file = source_file_load(global_context.sources[i], &loaded, &error); + if (!file) error_exit(error); if (loaded) continue; Lexer lexer = { .file = file }; lexer_init(&lexer); @@ -97,7 +99,9 @@ void compiler_parse(void) VECEACH(global_context.sources, i) { bool loaded = false; - File *file = source_file_load(global_context.sources[i], &loaded); + const char *error; + File *file = source_file_load(global_context.sources[i], &loaded, &error); + if (!file) error_exit(error); if (loaded) continue; global_context_clear_errors(); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 7ca8089a6..87d7181dc 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -393,6 +393,12 @@ typedef struct Module *module; } ImportDecl; +typedef struct +{ + File *file; + Decl **decls; +} IncludeDecl; + typedef struct { TypeSize size; @@ -696,6 +702,7 @@ typedef struct Decl_ BitStructDecl bitstruct; }; }; + IncludeDecl include; Decl** body_params; ImportDecl import; VarDecl var; @@ -2231,7 +2238,7 @@ void sema_shadow_error(Decl *decl, Decl *old); bool sema_type_error_on_binop(Expr *expr); File *source_file_by_id(FileId file); -File *source_file_load(const char *filename, bool *already_loaded); +File *source_file_load(const char *filename, bool *already_loaded, const char **error); #define RANGE_EXTEND_PREV(x) do { (x)->span = extend_span_with_token((x)->span, c->prev_span); } while (0) diff --git a/src/compiler/context.c b/src/compiler/context.c index 0e6c9fd87..5f76af558 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -148,6 +148,7 @@ void decl_register(Decl *decl) case DECL_LABEL: case DECL_DECLARRAY: case DECL_BODYPARAM: + case DECL_CT_INCLUDE: UNREACHABLE case DECL_ATTRIBUTE: case DECL_BITSTRUCT: @@ -262,6 +263,7 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl) UNREACHABLE case DECL_CT_IF: case DECL_CT_SWITCH: + case DECL_CT_INCLUDE: vec_add(unit->ct_ifs, decl); return; case DECL_CT_ECHO: diff --git a/src/compiler/copying.c b/src/compiler/copying.c index febeea5d5..0fce7f555 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -784,6 +784,8 @@ Decl *copy_decl(CopyStruct *c, Decl *decl) { case DECL_POISONED: break; + case DECL_CT_INCLUDE: + break; case DECL_INITIALIZE: case DECL_FINALIZE: MACRO_COPY_ASTID(copy->xxlizer.init); diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 5ca4f6bab..86f90c652 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -157,6 +157,7 @@ typedef enum DECL_FINALIZE, DECL_GENERIC, DECL_IMPORT, + DECL_CT_INCLUDE, DECL_LABEL, DECL_MACRO, DECL_STRUCT, @@ -171,7 +172,7 @@ typedef enum case DECL_DECLARRAY: case DECL_CT_IF: case DECL_CT_ELSE: case DECL_CT_ELIF: \ case DECL_CT_SWITCH: case DECL_CT_CASE: case DECL_ATTRIBUTE: case DECL_LABEL: \ case DECL_DEFINE: case DECL_CT_ASSERT: case DECL_GENERIC: case DECL_INITIALIZE: \ - case DECL_FINALIZE: case DECL_CT_ECHO + case DECL_FINALIZE: case DECL_CT_ECHO: case DECL_CT_INCLUDE #define NON_RUNTIME_EXPR EXPR_DESIGNATOR: case EXPR_POISONED: \ case EXPR_TYPEINFO: case EXPR_CT_IDENT: case EXPR_HASH_IDENT: \ diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 58253af86..c15ee128e 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -1000,6 +1000,7 @@ LLVMValueRef llvm_get_ref(GenContext *c, Decl *decl) case DECL_FINALIZE: case DECL_BODYPARAM: case DECL_CT_ECHO: + case DECL_CT_INCLUDE: UNREACHABLE; } UNREACHABLE diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 0a13fc6ee..ee533626c 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -6,6 +6,7 @@ static Decl *parse_const_declaration(ParseContext *c, Visibility visibility); static inline Decl *parse_func_definition(ParseContext *c, Visibility visibility, AstId docs, bool is_interface); static inline bool parse_bitstruct_body(ParseContext *c, Decl *decl); static inline Decl *parse_static_top_level(ParseContext *c); +static Decl *parse_include(ParseContext *c); #define DECL_VAR_NEW(type__, var__, visible__) decl_new_var(symstr(c), c->span, type__, var__, visible__); @@ -2531,6 +2532,68 @@ static bool parse_docs(ParseContext *c, AstId *docs_ref) } } +static Decl *parse_include(ParseContext *c) +{ + SourceSpan loc = c->span; + Decl *decl = decl_new(DECL_CT_INCLUDE, NULL, loc, VISIBLE_LOCAL); + advance_and_verify(c, TOKEN_CT_INCLUDE); + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_decl); + const char *str = symstr(c); + CONSUME_OR_RET(TOKEN_STRING, poisoned_decl); + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_decl); + CONSUME_EOS_OR_RET(poisoned_decl); + bool loaded; + const char *error; + char *path; + char *name; + if (file_namesplit(c->unit->file->full_path, &name, &path)) + { + str = file_append_path(path, str); + } + File *file = source_file_load(str, &loaded, &error); + if (!file) + { + sema_error_at(loc, "Failed to load file %s: %s", str, error); + return poisoned_decl; + } + decl->include.file = file; + if (global_context.errors_found) return poisoned_decl; + + Lexer current_lexer = c->lexer; + File *current_file = c->unit->file; + TokenType old_tok = c->tok; + TokenData old_data = c->data; + SourceSpan old_prev = c->prev_span; + SourceSpan old_span = c->span; + + c->lexer = (Lexer){ .file = decl->include.file, .context = c }; + lexer_init(&c->lexer); + // Prime everything + advance(c); + advance(c); + Decl **list = NULL; + while (!tok_is(c, TOKEN_EOF)) + { + Decl *inner = parse_top_level_statement(c, &c); + if (!decl) continue; + if (!decl_ok(decl)) + { + decl_poison(decl); + goto END; + } + vec_add(list, inner); + } + decl->include.decls = list; +END: + c->lexer = current_lexer; + c->tok = old_tok; + c->data = old_data; + c->prev_span = old_prev; + c->span = old_span; + c->unit->file = current_file; + return decl; +} + /** * top_level_statement ::= visibility? top_level * @@ -2690,6 +2753,15 @@ Decl *parse_top_level_statement(ParseContext *c, ParseContext **c_ref) } break; } + case TOKEN_CT_INCLUDE: + if (!check_no_visibility_before(c, visibility)) return poisoned_decl; + if (docs) + { + SEMA_ERROR(astptr(docs), "Unexpected doc comment before $include, did you mean to use a regular comment?"); + return poisoned_decl; + } + ASSIGN_DECL_OR_RET(decl, parse_include(c), poisoned_decl); + break; case TOKEN_BITSTRUCT: { ASSIGN_DECL_OR_RET(decl, parse_bitstruct_declaration(c, visibility), poisoned_decl); diff --git a/src/compiler/parser.c b/src/compiler/parser.c index 681d70c3e..cdf1f2f44 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -5,6 +5,7 @@ #include "compiler_internal.h" #include "parser_internal.h" + // --- Parser base methods /** @@ -55,6 +56,8 @@ bool consume(ParseContext *c, TokenType type, const char *message, ...) // --- Extern functions +static bool parse_include(ParseContext *c, Decl *decl); + /** * module? imports top_level_statement* * @param c @@ -79,6 +82,7 @@ static inline void parse_translation_unit(ParseContext *c) } } + /** * Parse a file, generating a default context for it. * More contexts may be created on demand during parsing. diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 5a16a7213..e03c94ab2 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -2901,6 +2901,7 @@ bool sema_analyse_decl(SemaContext *context, Decl *decl) case DECL_FAULTVALUE: case DECL_DECLARRAY: case DECL_BODYPARAM: + case DECL_CT_INCLUDE: UNREACHABLE } decl->resolve_status = RESOLVE_DONE; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index f3fc2d25f..4a518effd 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -547,6 +547,7 @@ static inline bool sema_cast_ident_rvalue(SemaContext *context, Expr *expr) case DECL_BODYPARAM: case DECL_INITIALIZE: case DECL_FINALIZE: + case DECL_CT_INCLUDE: UNREACHABLE case DECL_POISONED: return expr_poison(expr); diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c index 4fb037317..eb6785860 100644 --- a/src/compiler/sema_passes.c +++ b/src/compiler/sema_passes.c @@ -182,6 +182,7 @@ static inline bool sema_analyse_top_level_if(SemaContext *context, Decl *ct_if) return true; } + static inline bool sema_analyse_top_level_switch(SemaContext *context, Decl *ct_switch) { Expr *cond = ct_switch->ct_switch_decl.expr; @@ -303,6 +304,10 @@ void sema_analysis_pass_conditional_compilation(Module *module) case DECL_CT_SWITCH: success = sema_analyse_top_level_switch(&context, decl); break; + case DECL_CT_INCLUDE: + sema_append_decls(context.unit, decl->include.decls); + success = true; + break; default: UNREACHABLE } diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index 2e85d622f..26777af04 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -243,6 +243,7 @@ static bool sema_resolve_type_identifier(SemaContext *context, TypeInfo *type_in case DECL_CT_ECHO: case DECL_DECLARRAY: case DECL_BODYPARAM: + case DECL_CT_INCLUDE: UNREACHABLE } UNREACHABLE diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 376d79bbb..a28cd408b 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -182,6 +182,7 @@ static void register_generic_decls(CompilationUnit *unit, Decl **decls) case DECL_CT_ELIF: case DECL_CT_ELSE: case DECL_BODYPARAM: + case DECL_CT_INCLUDE: UNREACHABLE case DECL_MACRO: case DECL_DEFINE: @@ -242,7 +243,9 @@ void sema_analysis_run(void) VECEACH(global_context.sources, i) { bool loaded = false; - File *file = source_file_load(global_context.sources[i], &loaded); + const char *error; + File *file = source_file_load(global_context.sources[i], &loaded, &error); + if (!file) error_exit(error); if (loaded) continue; if (!parse_file(file)) has_error = true; } diff --git a/src/compiler/source_file.c b/src/compiler/source_file.c index 0858c4e0f..ddd9482e0 100644 --- a/src/compiler/source_file.c +++ b/src/compiler/source_file.c @@ -22,7 +22,7 @@ File *source_file_by_id(FileId file) return global_context.loaded_sources[file]; } -File *source_file_load(const char *filename, bool *already_loaded) +File *source_file_load(const char *filename, bool *already_loaded, const char **error) { if (already_loaded) *already_loaded = false; if (!global_context.loaded_sources) global_context.loaded_sources = VECNEW(File*, LEXER_FILES_START_CAPACITY); @@ -31,7 +31,8 @@ File *source_file_load(const char *filename, bool *already_loaded) if (!realpath(filename, full_path)) { - error_exit("Failed to resolve %s", filename); + *error = str_printf("Failed to resolve %s", filename); + return NULL; } VECEACH(global_context.loaded_sources, index) @@ -44,7 +45,8 @@ File *source_file_load(const char *filename, bool *already_loaded) } if (vec_size(global_context.loaded_sources) == MAX_FILES) { - error_exit("Exceeded max number of files %d", MAX_FILES); + *error = str_printf("Exceeded max number of files %d", MAX_FILES); + return NULL; } size_t size; diff --git a/src/version.h b/src/version.h index fc1e4ff24..23209430f 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.3.127" \ No newline at end of file +#define COMPILER_VERSION "0.3.128" \ No newline at end of file