From 7b4ed0951794fd469ffb1e96809de1d0bbbe13fd Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Thu, 23 Jan 2020 19:38:48 +0100 Subject: [PATCH] Introduced a Lexer context, comment parsing now actually works properly. Token use in decl/ast/expr is moved to SourceRange instead. And the SourceRange used start/end, which is better than using start + len for many uses. --- resources/examples/http.c3 | 4 +- resources/testfragments/super_simple.c3 | 23 + src/compiler/ast.c | 87 +- src/compiler/casts.c | 8 +- src/compiler/compiler.c | 13 +- src/compiler/compiler_internal.h | 153 +- src/compiler/context.c | 49 +- src/compiler/diagnostics.c | 16 +- src/compiler/enums.h | 19 +- src/compiler/expr_analysis.c | 151 +- src/compiler/lexer.c | 721 ++++------ src/compiler/llvm_codegen.c | 6 +- src/compiler/llvm_codegen_debug_info.c | 1 - src/compiler/llvm_codegen_expr.c | 25 +- src/compiler/llvm_codegen_function.c | 11 +- src/compiler/llvm_codegen_internal.h | 2 + src/compiler/llvm_codegen_stmt.c | 27 +- src/compiler/llvm_codegen_type.c | 1 - src/compiler/parser.c | 1707 ++++++++++++----------- src/compiler/sema_name_resolution.c | 6 +- src/compiler/semantic_analyser.c | 525 +++---- src/compiler/source_file.c | 18 +- src/compiler/tokens.c | 6 + src/compiler/types.c | 9 +- src/compiler_tests/tests.c | 11 +- src/utils/lib.h | 3 +- 26 files changed, 1848 insertions(+), 1754 deletions(-) diff --git a/resources/examples/http.c3 b/resources/examples/http.c3 index e39384838..dab7b3e11 100644 --- a/resources/examples/http.c3 +++ b/resources/examples/http.c3 @@ -1,8 +1,8 @@ import curl; -int main(void) +func int main(void) { - try Curl curl = Curl.new(); + try Curl curl = curl::newCurl(); catch (error e) { diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 index 8fcc4b2df..64ae92e8f 100644 --- a/resources/testfragments/super_simple.c3 +++ b/resources/testfragments/super_simple.c3 @@ -2,6 +2,15 @@ module bar; typedef int as Bob; +/* hello *//* there */ +/+ why /+ you /* lucky +/ +/ +// Whut +// Here +// +//--- +/* + Hello + */ struct Test { @@ -91,6 +100,7 @@ func int test3() return 5; } +typedef func void() as FEok; typedef func void(int) as Foo; //typedef int as Foo; @@ -111,6 +121,16 @@ func void bob() } +func int xxxx(int x) +{ + { + defer printf("Defer says hello!\n"); + LABEL: + x--; + } + if (x > 0) goto LABEL; + return 1; +} func int main(int x) { @@ -189,6 +209,9 @@ JUMP: func void test2(int* x, int y, int z) { + *(&(&x)[0]); + float cheat = cast(int, x); + x++; z = 0; z ? y : z; diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 161c4c259..dbd3ca523 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -9,7 +9,9 @@ Decl *decl_new(DeclKind decl_kind, Token name, Visibility visibility) assert(name.string); Decl *decl = CALLOCS(Decl); decl->decl_kind = decl_kind; - decl->name = name; + decl->name_span = name.span; + decl->span = name.span; + decl->name = name.string; decl->visibility = visibility; return decl; } @@ -22,11 +24,11 @@ void decl_set_external_name(Decl *decl) { if (decl->visibility == VISIBLE_EXTERN) { - decl->external_name = decl->name.string; + decl->external_name = decl->name; return; } char buffer[1024]; - uint32_t len = sprintf(buffer, "%s::%s", decl->module->name->module, decl->name.string); + uint32_t len = sprintf(buffer, "%s::%s", decl->module->name->module, decl->name); assert(len); TokenType type = TOKEN_INVALID_TOKEN; decl->external_name = symtab_add(buffer, len, fnv1a(buffer, len), &type); @@ -64,7 +66,6 @@ Decl *decl_new_with_type(Token name, DeclKind decl_type, Visibility visibility) case DECL_ARRAY_VALUE: case DECL_IMPORT: case DECL_MACRO: - case DECL_MULTI_DECL: case DECL_GENERIC: case DECL_CT_IF: case DECL_CT_ELSE: @@ -113,22 +114,29 @@ Decl *struct_find_name(Decl *decl, const char* name) VECEACH(compare_members, i) { Decl *member = compare_members[i]; - if (member->name.type == TOKEN_INVALID_TOKEN) + if (!member->name) { Decl *found = struct_find_name(member, name); if (found) return found; } - else if (member->name.string == name) return member; + else if (member->name == name) return member; } return NULL; } +Ast *ast_from_expr(Expr *expr) +{ + if (!expr_ok(expr)) return &poisoned_ast; + Ast *ast = AST_NEW(AST_EXPR_STMT, expr->span); + ast->expr_stmt = expr; + return ast; +} -Expr *expr_new(ExprKind kind, Token start) +Expr *expr_new(ExprKind kind, SourceRange start) { Expr *expr = malloc_arena(sizeof(Expr)); expr->expr_kind = kind; - expr->loc = start; + expr->span = start; expr->type = NULL; return expr; } @@ -344,21 +352,21 @@ void fprint_type_recursive(FILE *file, Type *type, int indent) fprintf_indented(file, indent, "(type-func %s)\n", type->func.signature->mangled_signature); return; case TYPE_STRUCT: - fprintf_indented(file, indent, "(struct %s::%s)\n", type->decl->module->name, type->decl->name.string); + fprintf_indented(file, indent, "(struct %s::%s)\n", type->decl->module->name, type->decl->name); return; case TYPE_UNION: - fprintf_indented(file, indent, "(union %s::%s)\n", type->decl->module->name, type->decl->name.string); + fprintf_indented(file, indent, "(union %s::%s)\n", type->decl->module->name, type->decl->name); return; case TYPE_ENUM: - fprintf_indented(file, indent, "(enum %s::%s)\n", type->decl->module->name, type->decl->name.string); + fprintf_indented(file, indent, "(enum %s::%s)\n", type->decl->module->name, type->decl->name); return; case TYPE_ERROR: - fprintf_indented(file, indent, "(error %s::%s)\n", type->decl->module->name, type->decl->name.string); + fprintf_indented(file, indent, "(error %s::%s)\n", type->decl->module->name, type->decl->name); return; case TYPE_TYPEDEF: if (type->canonical != type) { - fprintf_indented(file, indent, "(user-defined %s::%s\n", type->decl->module->name, type->decl->name.string); + fprintf_indented(file, indent, "(user-defined %s::%s\n", type->decl->module->name, type->decl->name); fprint_type_recursive(file, type->canonical, indent + 1); fprint_endparen(file, indent); break; @@ -504,7 +512,7 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent) switch (expr->expr_kind) { case EXPR_IDENTIFIER: - fprintf_indented(file, indent, "(ident %s\n", expr->identifier_expr.identifier.string); + fprintf_indented(file, indent, "(ident %s\n", expr->identifier_expr.identifier); fprint_expr_common(file, expr, indent + 1); break; case EXPR_CONST: @@ -675,13 +683,8 @@ void fprint_decl_recursive(FILE *file, Decl *decl, int indent) { switch (decl->decl_kind) { - case DECL_MULTI_DECL: - fprintf_indented(file, indent, "(multi-decl\n"); - fprint_decl_list(file, decl->multi_decl, indent + 1); - fprint_endparen(file, indent); - break; case DECL_VAR: - fprintf_indented(file, indent, "(var-%s %s\n", decl_var_to_string(decl->var.kind), decl->name.string ?: ""); + fprintf_indented(file, indent, "(var-%s %s\n", decl_var_to_string(decl->var.kind), decl->name ?: ""); fprint_type_info_recursive(file, decl->var.type_info, indent + 1); if (decl->var.init_expr) { @@ -690,7 +693,7 @@ void fprint_decl_recursive(FILE *file, Decl *decl, int indent) fprint_endparen(file, indent); break; case DECL_MACRO: - fprintf_indented(file, indent, "(macro %s\n", decl->name.string); + fprintf_indented(file, indent, "(macro %s\n", decl->name); fprint_type_info_recursive(file, decl->macro_decl.rtype, indent + 1); fprint_indent(file, indent + 1); fprintf(file, "(params\n"); @@ -700,7 +703,7 @@ void fprint_decl_recursive(FILE *file, Decl *decl, int indent) fprint_endparen(file, indent); break; case DECL_FUNC: - fprintf_indented(file, indent, "(func %s\n", decl->name.string); + fprintf_indented(file, indent, "(func %s\n", decl->name); if (decl->func.type_parent) { fprint_indent(file, indent + 1); @@ -714,41 +717,41 @@ void fprint_decl_recursive(FILE *file, Decl *decl, int indent) fprint_endparen(file, indent); break; case DECL_STRUCT: - fprintf_indented(file, indent, "(struct %s\n", decl->name.string); + fprintf_indented(file, indent, "(struct %s\n", decl->name); fprint_decl_list(file, decl->strukt.members, indent + 1); fprint_endparen(file, indent); break; case DECL_UNION: - fprintf_indented(file, indent, "(union %s\n", decl->name.string); + fprintf_indented(file, indent, "(union %s\n", decl->name); fprint_decl_list(file, decl->strukt.members, indent + 1); fprint_endparen(file, indent); break; case DECL_ENUM: - fprintf_indented(file, indent, "(enum %s\n", decl->name.string); + fprintf_indented(file, indent, "(enum %s\n", decl->name); fprint_type_info_recursive(file, decl->enums.type_info, indent + 1); fprint_decl_list(file, decl->enums.values, indent + 1); fprint_endparen(file, indent); break; case DECL_ERROR: - fprintf_indented(file, indent, "(error %s\n", decl->name.string); + fprintf_indented(file, indent, "(error %s\n", decl->name); fprint_decl_list(file, decl->error.error_constants, indent + 1); fprint_endparen(file, indent); break; case DECL_ENUM_CONSTANT: if (!decl->enum_constant.expr) { - fprintf_indented(file, indent, "(enum-constant %s)\n", decl->name.string); + fprintf_indented(file, indent, "(enum-constant %s)\n", decl->name); return; } - fprintf_indented(file, indent, "(enum-constant %s\n", decl->name.string); + fprintf_indented(file, indent, "(enum-constant %s\n", decl->name); fprint_expr_recursive(file, decl->enum_constant.expr, indent + 1); fprint_endparen(file, indent); break; case DECL_ERROR_CONSTANT: - fprintf_indented(file, indent, "(error-constant %s)\n", decl->name.string); + fprintf_indented(file, indent, "(error-constant %s)\n", decl->name); break; case DECL_GENERIC: - fprintf_indented(file, indent, "(generic %s\n", decl->name.string); + fprintf_indented(file, indent, "(generic %s\n", decl->name); fprint_indent(file, indent + 1); fprintf(file, "(params\n"); { @@ -771,7 +774,7 @@ void fprint_decl_recursive(FILE *file, Decl *decl, int indent) fprint_endparen(file, indent); break; case DECL_TYPEDEF: - fprintf_indented(file, indent, "(typedef %s\n", decl->name.string); + fprintf_indented(file, indent, "(typedef %s\n", decl->name); if (decl->typedef_decl.is_func) { fprint_func_signature(file, &decl->typedef_decl.function_signature, indent + 1); @@ -816,7 +819,7 @@ void fprint_decl_recursive(FILE *file, Decl *decl, int indent) fprint_endparen(file, indent); return; case DECL_IMPORT: - fprintf_indented(file, indent, "(import %s", decl->name.string); + fprintf_indented(file, indent, "(import %s", decl->name); TODO fprint_endparen(file, indent); break; @@ -848,6 +851,18 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent) fprint_indent(file, indent); switch (ast->ast_kind) { + case AST_FUNCTION_BLOCK_STMT: + if (!ast->compound_stmt.stmts) + { + fprintf(file, "(function_block)\n"); + return; + } + fprintf(file, "(function_block\n"); + { + fprint_asts_recursive(file, ast->compound_stmt.stmts, indent + 1); + } + break; + case AST_COMPOUND_STMT: if (!ast->compound_stmt.stmts) { @@ -931,7 +946,7 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent) } if (ast->for_stmt.incr) { - fprint_expr_recursive(file, ast->for_stmt.incr, indent + 1); + fprint_ast_recursive(file, ast->for_stmt.incr, indent + 1); } else { @@ -1010,10 +1025,10 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent) TODO break; case AST_GOTO_STMT: - fprintf(file, "(goto %s)\n", ast->token.string); + fprintf(file, "(goto %s)\n", ast->goto_stmt.label_name); return; case AST_LABEL: - fprintf(file, "(label %s)\n", ast->token.string); + fprintf(file, "(label %s)\n", ast->label_stmt.name); return; case AST_NOP_STMT: TODO @@ -1040,5 +1055,5 @@ void fprint_decl(FILE *file, Decl *dec) fprint_decl_recursive(file, dec, 0); } Module poisoned_module = { .name = NULL }; -Decl all_error = { .decl_kind = DECL_ERROR, .name = { .type = TOKEN_INVALID_TOKEN, .string = NULL } }; +Decl all_error = { .decl_kind = DECL_ERROR, .name = NULL }; diff --git a/src/compiler/casts.c b/src/compiler/casts.c index c80161848..4bd1bac2b 100644 --- a/src/compiler/casts.c +++ b/src/compiler/casts.c @@ -38,7 +38,7 @@ static bool sema_type_mismatch(Expr *expr, Type *type, CastType cast_type) break; } - SEMA_ERROR(expr->loc, "Cannot %s '%s' to '%s'", action, type_to_error_string(expr->type), type_to_error_string(type)); + SEMA_ERROR(expr, "Cannot %s '%s' to '%s'", action, type_to_error_string(expr->type), type_to_error_string(type)); return false; } @@ -294,7 +294,7 @@ bool sisi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ { if ((i > int_type_max[index]) || (i < int_type_min[index])) { - SEMA_ERROR(left->loc, "'%lld' does not fit into '%s'", i, canonical->name); + SEMA_ERROR(left, "'%lld' does not fit into '%s'", i, canonical->name); return false; } } @@ -383,7 +383,7 @@ bool uisi(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ { if (left->const_expr.i > (uint64_t)int_type_max[index]) { - SEMA_ERROR(left->loc, "Cannot implicitly convert value '%llu' into %s - it will not fit.", left->const_expr.i, type_to_error_string(type)); + SEMA_ERROR(left, "Cannot implicitly convert value '%llu' into %s - it will not fit.", left->const_expr.i, type_to_error_string(type)); return false; } } @@ -413,7 +413,7 @@ bool uiui(Expr* left, Type *from, Type *canonical, Type *type, CastType cast_typ { if (left->const_expr.i > uint_type_max[index]) { - SEMA_ERROR(left->loc, "Cannot implicitly convert value '%llu' into %s - it will not fit.", type_to_error_string(type)); + SEMA_ERROR(left, "Cannot implicitly convert value '%llu' into %s - it will not fit.", type_to_error_string(type)); return false; } } diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 0f289e851..780b0553d 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -20,11 +20,12 @@ static void compiler_lex(BuildTarget *target) bool loaded = false; File *file = source_file_load(target->sources[i], &loaded); if (loaded) continue; - lexer_add_file_for_lexing(file); + Lexer lexer; + lexer_add_file_for_lexing(&lexer, file); printf("# %s\n", file->full_path); while (1) { - Token token = lexer_scan_token(); + Token token = lexer_scan_token(&lexer); printf("%s ", token_type_to_string(token.type)); if (token.type == TOKEN_EOF) break; } @@ -212,9 +213,9 @@ Module *compiler_find_or_create_module(Path *module_name) void compiler_register_public_symbol(Decl *decl) { - Decl *prev = stable_get(&compiler.global_symbols, decl->name.string); + Decl *prev = stable_get(&compiler.global_symbols, decl->name); // If the previous symbol was already declared globally, remove it. - stable_set(&compiler.global_symbols, decl->name.string, prev ? &poisoned_decl : decl); + stable_set(&compiler.global_symbols, decl->name, prev ? &poisoned_decl : decl); STable *sub_module_space = stable_get(&compiler.qualified_symbols, decl->module->name->module); if (!sub_module_space) { @@ -222,6 +223,6 @@ void compiler_register_public_symbol(Decl *decl) stable_init(sub_module_space, 0x100); stable_set(&compiler.qualified_symbols, decl->module->name->module, sub_module_space); } - prev = stable_get(sub_module_space, decl->name.string); - stable_set(sub_module_space, decl->name.string, prev ? &poisoned_decl : decl); + prev = stable_get(sub_module_space, decl->name); + stable_set(sub_module_space, decl->name, prev ? &poisoned_decl : decl); } diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 34576469f..9e017ac30 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -3,6 +3,7 @@ // Use of this source code is governed by the GNU LGPLv3.0 license // a copy of which can be found in the LICENSE file. +#include #include "../utils/common.h" #include "../utils/errors.h" #include "../utils/lib.h" @@ -17,6 +18,7 @@ typedef uint32_t SourceLoc; #define MAX_LOCALS 0xFFFF #define MAX_SCOPE_DEPTH 0xFF #define MAX_PATH 1024 +#define MAX_DEFERS 0xFFFF typedef struct _Ast Ast; typedef struct _Decl Decl; @@ -24,15 +26,21 @@ typedef struct _TypeInfo TypeInfo; typedef struct _Expr Expr; typedef struct _Module Module; typedef struct _Type Type; +typedef uint16_t DeferId; typedef bool(*CastFunc)(Expr *, Type *, Type *, Type *, CastType cast_type); typedef struct { SourceLoc loc; - uint32_t length; + SourceLoc end_loc; } SourceRange; +typedef struct +{ + DeferId start; + DeferId end; +} DeferList; typedef struct { @@ -74,9 +82,8 @@ typedef struct const char *full_path; SourceLoc start_id; SourceLoc end_id; - SourceLoc *line_start; + SourceLoc *lines; SourceLoc current_line_start; - int last_line_found; } File; typedef struct @@ -287,7 +294,9 @@ typedef struct typedef struct _Decl { - Token name; + const char *name; + SourceRange name_span; + SourceRange span; const char *external_name; DeclKind decl_kind : 6; Visibility visibility : 2; @@ -326,7 +335,6 @@ typedef struct _Decl FuncDecl func; AttrDecl attr; TypedefDecl typedef_decl; - Decl** multi_decl; MacroDecl macro_decl; GenericDecl generic_decl; CtIfDecl ct_if_decl; @@ -428,7 +436,7 @@ typedef struct typedef struct { Path *path; - Token identifier; + const char *identifier; bool is_ref; Decl *decl; } ExprIdentifier; @@ -455,7 +463,7 @@ struct _Expr { ExprKind expr_kind : 8; ResolveStatus resolve_status : 3; - Token loc; + SourceRange span; Type *type; union { ExprCast cast_expr; @@ -486,14 +494,15 @@ typedef struct typedef struct { struct _Ast **stmts; -// DeferList defer_list; TODO + DeferList defer_list; } AstCompoundStmt; typedef struct { + const char *name; uint16_t last_goto; bool is_used : 1; - struct _Ast *defer; + Ast *defer; struct _Ast *in_defer; void *backend_value; } AstLabelStmt; @@ -501,7 +510,7 @@ typedef struct typedef struct { Expr *expr; // May be NULL - struct _Ast *defer; + Ast *defer; } AstReturnStmt; typedef struct @@ -515,6 +524,8 @@ typedef struct { Expr *expr; Ast *body; + DeferList expr_defer; + DeferList body_defer; } AstDoStmt; typedef struct @@ -554,8 +565,9 @@ typedef struct { Ast *init; Expr *cond; - Expr *incr; + Ast *incr; Ast *body; + DeferList cond_defer; } AstForStmt; @@ -563,8 +575,9 @@ typedef struct typedef struct { GotoType type : 2; + const char *label_name; Ast *label; - struct _Ast *defer; + Ast *defer; union { struct _Ast *in_defer; @@ -577,6 +590,7 @@ typedef struct _AstDeferStmt bool emit_boolean : 1; struct _Ast *body; // Compound statement struct _Ast *prev_defer; + DeferId id; } AstDeferStmt; typedef struct _AstCatchStmt @@ -626,14 +640,14 @@ typedef struct typedef struct _Ast { + SourceRange span; AstKind ast_kind : 8; ExitType exit : 3; - Token token; - union { AstAttribute attribute; AstCompoundStmt compound_stmt; + AstCompoundStmt function_block_stmt; Decl *declare_stmt; Expr *expr_stmt; Expr *throw_stmt; @@ -690,11 +704,24 @@ typedef struct _DynamicScope ScopeFlags flags_created; unsigned errors; Decl **local_decl_start; - unsigned defer_start; + DeferId defer_top; + DeferId defer_last; ExitType exit; } DynamicScope; +typedef struct +{ + const char *file_begin; + const char *lexing_start; + const char *current; + uint16_t source_file; + File *current_file; + SourceLoc last_in_range; + Token tok; + Token next_tok; +} Lexer; + typedef struct _Context { BuildTarget *target; @@ -705,7 +732,6 @@ typedef struct _Context Decl *specified_imports; Module *module; STable local_symbols; - Decl **header_declarations; Decl **enums; Decl **error_types; Decl **types; @@ -718,6 +744,10 @@ typedef struct _Context Decl **last_local; Ast **labels; Ast **gotos; + Token *comments; + Token *lead_comment; + Token *trailing_comment; + Token *next_lead_comment; DynamicScope *current_scope; Decl *evaluating_macro; Type *rtype; @@ -729,6 +759,18 @@ typedef struct _Context STable external_symbols; Decl **external_symbol_list; }; + STable scratch_table; + Lexer lexer; + SourceLoc prev_tok_end; + Token tok; + Token next_tok; + struct + { + const char *current; + const char *start; + Token tok; + Token next_tok; + } stored; } Context; typedef struct @@ -739,7 +781,6 @@ typedef struct Type **type; } Compiler; -extern Context *current_context; extern Compiler compiler; extern Ast poisoned_ast; extern Decl poisoned_decl; @@ -749,8 +790,6 @@ extern TypeInfo poisoned_type_info; extern Module poisoned_module; extern Diagnostics diagnostics; -extern Token next_tok; -extern Token tok; extern Decl all_error; extern Type *type_bool, *type_void, *type_string, *type_voidptr; @@ -771,20 +810,35 @@ extern Type t_voidstar; extern const char *main_name; -#define AST_NEW(_kind, _token) new_ast(_kind, _token) +#define AST_NEW_TOKEN(_kind, _token) new_ast(_kind, _token.span) +#define AST_NEW(_kind, _loc) new_ast(_kind, _loc) static inline bool ast_ok(Ast *ast) { return ast == NULL || ast->ast_kind != AST_POISONED; } static inline bool ast_poison(Ast *ast) { ast->ast_kind = AST_POISONED; return false; } -static inline Ast *new_ast(AstKind kind, Token token) +Ast *ast_from_expr(Expr *expr); + +static inline Ast *new_ast(AstKind kind, SourceRange range) { Ast *ast = malloc_arena(sizeof(Ast)); memset(ast, 0, sizeof(Ast)); - ast->token = token; + ast->span = range; ast->ast_kind = kind; ast->exit = EXIT_NONE; return ast; } +static inline Ast *extend_ast(Ast *ast, Token token) +{ + ast->span.end_loc = token.span.end_loc; + return ast; +} + +static inline Ast *extend_ast_with_prev_token(Context *context, Ast *ast) +{ + ast->span.end_loc = context->prev_tok_end; + return ast; +} + void builtin_setup(); @@ -852,16 +906,12 @@ Module *compiler_find_or_create_module(Path *module_name); void compiler_register_public_symbol(Decl *decl); Context *context_create(File *file, BuildTarget *target); -void context_push(Context *context); void context_register_global_decl(Context *context, Decl *decl); void context_register_external_symbol(Context *context, Decl *decl); bool context_add_import(Context *context, Path *path, Token symbol, Token alias); bool context_set_module_from_filename(Context *context); bool context_set_module(Context *context, Path *path, Token *generic_parameters); void context_print_ast(Context *context, FILE *file); -Decl *context_find_ident(Context *context, const char *symbol); -void context_add_header_decl(Context *context, Decl *decl); -bool context_add_local(Context *context, Decl *decl); Decl *decl_new(DeclKind decl_kind, Token name, Visibility visibility); Decl *decl_new_with_type(Token name, DeclKind decl_type, Visibility visibility); @@ -883,16 +933,16 @@ void diag_reset(void); void diag_error_range(SourceRange span, const char *message, ...); void diag_verror_range(SourceRange span, const char *message, va_list args); -#define EXPR_NEW_EXPR(_kind, _expr) expr_new(_kind, _expr->loc) -#define EXPR_NEW_TOKEN(_kind, _tok) expr_new(_kind, _tok) -Expr *expr_new(ExprKind kind, Token start); +#define EXPR_NEW_EXPR(_kind, _expr) expr_new(_kind, _expr->span) +#define EXPR_NEW_TOKEN(_kind, _tok) expr_new(_kind, _tok.span) +Expr *expr_new(ExprKind kind, SourceRange start); static inline bool expr_ok(Expr *expr) { return expr == NULL || expr->expr_kind != EXPR_POISONED; } static inline bool expr_poison(Expr *expr) { expr->expr_kind = EXPR_POISONED; expr->resolve_status = RESOLVE_DONE; return false; } static inline void expr_replace(Expr *expr, Expr *replacement) { - Token loc = expr->loc; + SourceRange loc = expr->span; *expr = *replacement; - expr->loc = loc; + expr->span = loc; } void fprint_ast(FILE *file, Ast *ast); @@ -901,31 +951,13 @@ void fprint_type_info_recursive(FILE *file, TypeInfo *type_info, int indent); void fprint_expr_recursive(FILE *file, Expr *expr, int indent); -Token lexer_scan_token(void); -Token lexer_scan_ident_test(const char *scan); -void lexer_test_setup(const char *text, size_t len); -void lexer_add_file_for_lexing(File *file); -File* lexer_current_file(void); +Token lexer_scan_token(Lexer *lexer); +Token lexer_scan_ident_test(Lexer *lexer, const char *scan); +void lexer_test_setup(Lexer *lexer, const char *text, size_t len); +void lexer_add_file_for_lexing(Lexer *lexer, File *file); +File* lexer_current_file(Lexer *lexer); void lexer_check_init(void); -void lexer_store_state(void); -void lexer_restore_state(void); -static inline void advance(void) -{ - tok = next_tok; - while (1) - { - next_tok = lexer_scan_token(); - // printf(">>> %.*s => %s\n", tok.length, tok.start, token_type_to_string(tok.type)); - if (next_tok.type != TOKEN_INVALID_TOKEN) break; - } -} - -static inline void advance_and_verify(TokenType token_type) -{ - assert(tok.type == token_type); - advance(); -} typedef enum { @@ -942,12 +974,12 @@ Path *path_find_parent_path(Path *path); const char *resolve_status_to_string(ResolveStatus status); -#define SEMA_ERROR(_tok, ...) sema_error_range(_tok.span, __VA_ARGS__) -void sema_init(File *file); +#define SEMA_TOKEN_ERROR(_tok, ...) sema_error_range(_tok.span, __VA_ARGS__) +#define SEMA_ERROR(_node, ...) sema_error_range(_node->span, __VA_ARGS__) +#define SEMA_PREV(_node, ...) sema_prev_at_range(_node->span, __VA_ARGS__) void sema_analysis_pass_process_imports(Context *context); void sema_analysis_pass_conditional_compilation(Context *context); void sema_analysis_pass_decls(Context *context); -void sema_analysis_pass_3(Context *context); bool sema_add_local(Context *context, Decl *decl); bool sema_analyse_statement(Context *context, Ast *statement); @@ -958,7 +990,7 @@ void sema_error_at(SourceLoc loc, const char *message, ...); void sema_error_range(SourceRange range, const char *message, ...); void sema_verror_at(SourceLoc loc, const char *message, va_list args); void sema_verror_range(SourceRange range, const char *message, va_list args); -void sema_error(const char *message, ...); +void sema_error(Context *context, const char *message, ...); void sema_prev_at_range(SourceRange span, const char *message, ...); void sema_prev_at(SourceLoc loc, const char *message, ...); void sema_shadow_error(Decl *decl, Decl *old); @@ -969,7 +1001,7 @@ void source_file_append_line_end(File *file, SourceLoc loc); SourcePosition source_file_find_position_in_file(File *file, SourceLoc loc); SourcePosition source_file_find_position(SourceLoc loc); SourceRange source_range_from_ranges(SourceRange first, SourceRange last); - +static inline unsigned source_range_len(SourceRange range) { return range.end_loc - range.loc; } void stable_init(STable *table, uint32_t initial_size); void *stable_set(STable *table, const char *key, void *value); @@ -1112,4 +1144,5 @@ static inline const char* struct_union_name_from_token(TokenType type) #define BACKEND_TYPE_GLOBAL(type) gencontext_get_llvm_type(NULL, type) #define DEBUG_TYPE(type) gencontext_get_debug_type(context, type) - +void advance(Context *context); +void advance_and_verify(Context *context, TokenType token_type); diff --git a/src/compiler/context.c b/src/compiler/context.c index 7c919d6e6..ea6026c73 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -4,7 +4,6 @@ #include "compiler_internal.h" -Context *current_context; Context *context_create(File *file, BuildTarget *target) { @@ -14,43 +13,10 @@ Context *context_create(File *file, BuildTarget *target) context->target = target; stable_init(&context->local_symbols, 256); stable_init(&context->external_symbols, 256); + stable_init(&context->scratch_table, 32); return context; } -void context_push(Context *context) -{ - current_context = context; -} - -void context_add_header_decl(Context *context, Decl *decl) -{ - DEBUG_LOG("Adding %s to header", decl->name.string); - vec_add(context->header_declarations, decl); -} - -bool context_add_local(Context *context, Decl *decl) -{ - Decl *other = context_find_ident(context, decl->name.string); - if (other) - { - sema_shadow_error(decl, other); - decl_poison(decl); - decl_poison(other); - return false; - } - Decl *** vars = &context->active_function_for_analysis->func.annotations->vars; - unsigned num_vars = vec_size(*vars); - if (num_vars == MAX_LOCALS - 1 || context->last_local == &context->locals[MAX_LOCALS - 1]) - { - SEMA_ERROR(decl->name, "Reached the maximum number of locals."); - return false; - } - decl->var.id = num_vars; - *vars = VECADD(*vars, decl); - context->last_local[0] = decl; - context->last_local++; - return true; -} static inline bool create_module_or_check_name(Context *context, Path *module_name) { @@ -74,7 +40,7 @@ bool context_set_module_from_filename(Context *context) int len = filename_to_module(context->file->full_path, buffer); if (!len) { - sema_error("The filename '%s' could not be converted to a valid module name, try using an explicit module name."); + sema_error(context, "The filename '%s' could not be converted to a valid module name, try using an explicit module name."); return false; } @@ -82,7 +48,7 @@ bool context_set_module_from_filename(Context *context) const char *module_name = symtab_add(buffer, (uint32_t) len, fnv1a(buffer, (uint32_t) len), &type); if (type != TOKEN_IDENT) { - sema_error("Generating a filename from the file '%s' resulted in a name that is a reserved keyword, " + sema_error(context, "Generating a filename from the file '%s' resulted in a name that is a reserved keyword, " "try using an explicit module name."); return false; } @@ -162,7 +128,6 @@ void context_register_global_decl(Context *context, Decl *decl) case DECL_ERROR_CONSTANT: case DECL_ARRAY_VALUE: case DECL_IMPORT: - case DECL_MULTI_DECL: case DECL_CT_ELSE: case DECL_CT_ELIF: case DECL_ATTRIBUTE: @@ -173,17 +138,17 @@ void context_register_global_decl(Context *context, Decl *decl) vec_add(context->ct_ifs, decl); return; } - DEBUG_LOG("Registering symbol '%s'.", decl->name.string); + DEBUG_LOG("Registering symbol '%s'.", decl->name); - Decl *old = stable_set(&context->local_symbols, decl->name.string, decl); + Decl *old = stable_set(&context->local_symbols, decl->name, decl); if (!old && decl->visibility != VISIBLE_LOCAL) { - old = stable_set(&context->module->symbols, decl->name.string, decl); + old = stable_set(&context->module->symbols, decl->name, decl); } if (!old && decl->visibility == VISIBLE_PUBLIC) { compiler_register_public_symbol(decl); - old = stable_set(&context->module->public_symbols, decl->name.string, decl); + old = stable_set(&context->module->public_symbols, decl->name, decl); } if (old != NULL) { diff --git a/src/compiler/diagnostics.c b/src/compiler/diagnostics.c index 0d2c7a846..0b0fda1cb 100644 --- a/src/compiler/diagnostics.c +++ b/src/compiler/diagnostics.c @@ -46,15 +46,15 @@ static void print_error(SourceRange source_range, const char *message, PrintType break; } } - size_t lines_in_file = vec_size(position.file->line_start); + size_t lines_in_file = vec_size(position.file->lines); for (unsigned i = LINES_SHOWN; i > 0; i--) { if (position.line < i) continue; uint32_t line_number = position.line + 1 - i; - SourceLoc line_start = position.file->line_start[line_number - 1]; + SourceLoc line_start = position.file->lines[line_number - 1]; SourceLoc line_end = line_number == lines_in_file ? position.file->end_id : - position.file->line_start[line_number]; + position.file->lines[line_number]; uint32_t line_len = line_end - line_start - 1; eprintf(number_buffer, line_number, line_len, position.file->contents + line_start - position.file->start_id); } @@ -74,7 +74,7 @@ static void print_error(SourceRange source_range, const char *message, PrintType eprintf(" "); } } - for (uint32_t i = 0; i < source_range.length; i++) + for (uint32_t i = 0; i < source_range_len(source_range); i++) { eprintf("^"); } @@ -142,7 +142,7 @@ void sema_error_range(SourceRange range, const char *message, ...) void sema_verror_at(SourceLoc loc, const char *message, va_list args) { - vprint_error((SourceRange) { loc, 1 }, message, args); + vprint_error((SourceRange) { loc, loc + 1 }, message, args); diagnostics.errors++; } @@ -152,9 +152,9 @@ void sema_verror_range(SourceRange range, const char *message, va_list args) diagnostics.errors++; } -void sema_error(const char *message, ...) +void sema_error(Context *context, const char *message, ...) { - File *file = lexer_current_file(); + File *file = lexer_current_file(&context->lexer); va_list list; va_start(list, message); eprintf("(%s:0) Error: ", file->name); @@ -179,7 +179,7 @@ void sema_prev_at(SourceLoc loc, const char *message, ...) va_start(args, message); char buffer[256]; vsnprintf(buffer, 256, message, args); - print_error((SourceRange){ loc, 1 }, buffer, PRINT_TYPE_PREV); + print_error((SourceRange){ loc, loc + 1 }, buffer, PRINT_TYPE_PREV); va_end(args); } diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 5bc246f12..38cc9df63 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -88,6 +88,7 @@ typedef enum AST_DO_STMT, AST_EXPR_STMT, AST_FOR_STMT, + AST_FUNCTION_BLOCK_STMT, AST_GENERIC_CASE_STMT, AST_GENERIC_DEFAULT_STMT, AST_GOTO_STMT, @@ -201,7 +202,6 @@ typedef enum DECL_ARRAY_VALUE, DECL_IMPORT, DECL_MACRO, - DECL_MULTI_DECL, DECL_GENERIC, DECL_CT_IF, DECL_CT_ELSE, @@ -251,13 +251,6 @@ typedef enum } GotoType; -typedef enum -{ - LEXER_STATE_NORMAL, - LEXER_STATE_DOCS_PARSE, - LEXER_STATE_DOCS_PARSE_DIRECTIVE, -} LexerState; - typedef enum { NUMBER_TYPE_BOOL, @@ -286,9 +279,8 @@ typedef enum SCOPE_NONE = 0, SCOPE_BREAK = 1 << 0, SCOPE_CONTINUE = 1 << 1, - SCOPE_CONTROL = 1 << 2, - SCOPE_NEXT = 1 << 3, - SCOPE_DEFER = 1 << 4, + SCOPE_NEXT = 1 << 2, + SCOPE_DEFER = 1 << 3, } ScopeFlags; typedef enum @@ -417,12 +409,15 @@ typedef enum // Otherwise we allow things like "@ foo" which would be pretty bad. TOKEN_AT_IDENT, // @foobar TOKEN_HASH_IDENT, // #foobar - TOKEN_CT_IDENT, // $foobar + TOKEN_CT_IDENT, // $foobar TOKEN_STRING, // "Teststring" TOKEN_INTEGER, // 123 0x23 0b10010 0o327 TOKEN_REAL, // 0x23.2p-2a 43.23e23 + TOKEN_COMMENT, // Comment + TOKEN_DOC_COMMENT, // Doc Comment + // Keywords TOKEN_ALIAS, // Reserved TOKEN_AS, diff --git a/src/compiler/expr_analysis.c b/src/compiler/expr_analysis.c index f60972a8a..ed870afc0 100644 --- a/src/compiler/expr_analysis.c +++ b/src/compiler/expr_analysis.c @@ -37,8 +37,11 @@ static bool expr_is_ltype(Expr *expr) static inline bool sema_type_error_on_binop(Expr *expr) { const char *c = token_type_to_string(binaryop_to_token(expr->binary_expr.operator)); - SEMA_ERROR(expr->loc, "Cannot perform '%s' %s '%s'.", - type_to_error_string(expr->binary_expr.left->type), c, type_to_error_string(expr->binary_expr.right->type)); + SEMA_ERROR(expr, + "Cannot perform '%s' %s '%s'.", + type_to_error_string(expr->binary_expr.left->type), + c, + type_to_error_string(expr->binary_expr.right->type)); return false; } @@ -60,7 +63,7 @@ static inline bool sema_expr_analyse_ternary(Context *context, Type *to, Expr *e Type *type = cond->type->canonical; if (type->type_kind != TYPE_BOOL && cast_to_bool_kind(type) == CAST_ERROR) { - SEMA_ERROR(cond->loc, "Cannot convert expression to boolean."); + SEMA_ERROR(cond, "Cannot convert expression to boolean."); return false; } left = cond; @@ -76,8 +79,8 @@ static inline bool sema_expr_analyse_ternary(Context *context, Type *to, Expr *e Type *max = type_find_max_type(left_canonical, right_canonical); if (!max) { - SEMA_ERROR(expr->loc, "Cannot find a common parent type of '%s' and '%s'", - type_to_error_string(left_canonical), type_to_error_string(right_canonical)); + SEMA_ERROR(expr, "Cannot find a common parent type of '%s' and '%s'", + type_to_error_string(left_canonical), type_to_error_string(right_canonical)); return false; } if (!cast_implicit(left, max) || !cast_implicit(right, max)) return false; @@ -92,25 +95,27 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr { // TODO what about struct functions Decl *ambiguous_decl; - Decl *decl = sema_resolve_symbol(context, expr->identifier_expr.identifier.string, expr->identifier_expr.path, &ambiguous_decl); + Decl *decl = sema_resolve_symbol(context, expr->identifier_expr.identifier, expr->identifier_expr.path, &ambiguous_decl); if (!decl) { - SEMA_ERROR(expr->identifier_expr.identifier, "Unknown symbol '%s'.", expr->identifier_expr.identifier.string); + SEMA_ERROR(expr, "Unknown symbol '%s'.", expr->identifier_expr.identifier); return false; } if (ambiguous_decl) { - SEMA_ERROR(expr->identifier_expr.identifier, "Ambiguous symbol '%s' – both defined in %s and %s, please add the module name to resolve the ambiguity", - expr->identifier_expr.identifier.string, - decl->module->name->module, ambiguous_decl->module->name->module); + SEMA_ERROR(expr, + "Ambiguous symbol '%s' – both defined in %s and %s, please add the module name to resolve the ambiguity", + expr->identifier_expr.identifier, + decl->module->name->module, + ambiguous_decl->module->name->module); return false; } if (decl->decl_kind == DECL_FUNC && !expr->identifier_expr.path && decl->module != context->module) { - SEMA_ERROR(expr->identifier_expr.identifier, "Functions from other modules, must be prefixed with the module name"); + SEMA_ERROR(expr, "Functions from other modules, must be prefixed with the module name"); return false; } @@ -191,7 +196,7 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr case DECL_POISONED: return false; default: - SEMA_ERROR(expr->loc, "The expression cannot be called."); + SEMA_ERROR(expr, "The expression cannot be called."); return false; } } @@ -227,7 +232,7 @@ static inline bool sema_expr_analyse_subscript(Context *context, Type *to, Expr inner_type = type_char; break; default: - SEMA_ERROR(expr->subscript_expr.expr->loc, "Cannot index '%s'.", type_to_error_string(type)); + SEMA_ERROR(expr->subscript_expr.expr, "Cannot index '%s'.", type_to_error_string(type)); return false; } @@ -243,13 +248,13 @@ static inline bool sema_expr_analyse_method_function(Context *context, Expr *exp VECEACH(decl->method_functions, i) { Decl *function = decl->method_functions[i]; - if (function->name.string == name) + if (function->name == name) { // TODO return true; } } - SEMA_ERROR(expr->loc, "Cannot find method function '%s.%s'", decl->name.string, name); + SEMA_ERROR(expr, "Cannot find method function '%s.%s'", decl->name, name); return false; } @@ -259,14 +264,14 @@ static inline bool sema_expr_analyse_enum_constant(Context *context, Expr *expr, VECEACH(decl->enums.values, i) { Decl *enum_constant = decl->enums.values[i]; - if (enum_constant->name.string == name) + if (enum_constant->name == name) { assert(enum_constant->resolve_status == RESOLVE_DONE); expr_replace(expr, enum_constant->enum_constant.expr); return true; } } - SEMA_ERROR(expr->loc, "'%s' has no enumeration value '%s'.", decl->name.string, name); + SEMA_ERROR(expr, "'%s' has no enumeration value '%s'.", decl->name, name); return false; } @@ -276,7 +281,7 @@ static inline bool sema_expr_analyse_error_constant(Context *context, Expr *expr VECEACH(decl->error.error_constants, i) { Decl *error_constant = decl->error.error_constants[i]; - if (error_constant->name.string == name) + if (error_constant->name == name) { assert(error_constant->resolve_status == RESOLVE_DONE); expr->type = decl->type; @@ -286,7 +291,7 @@ static inline bool sema_expr_analyse_error_constant(Context *context, Expr *expr return true; } } - SEMA_ERROR(expr->loc, "'%s' has no error type '%s'.", decl->name.string, name); + SEMA_ERROR(expr, "'%s' has no error type '%s'.", decl->name, name); return false; } @@ -296,8 +301,8 @@ static Decl *strukt_recursive_search_member(Decl *strukt, const char *name, int { (*index)++; Decl *member = strukt->strukt.members[i]; - if (member->name.string == name) return member; - if (!member->name.string && decl_is_struct_type(member)) + if (member->name == name) return member; + if (!member->name && decl_is_struct_type(member)) { Decl *result = strukt_recursive_search_member(member, name, index); if (result) return result; @@ -319,7 +324,7 @@ static inline bool sema_expr_analyse_access(Context *context, Type *to, Expr *ex } if (!type_may_have_method_functions(type)) { - SEMA_ERROR(expr->loc, "Cannot access '%s' on '%s'", expr->access_expr.sub_element.string, type_to_error_string(parent_type)); + SEMA_ERROR(expr, "Cannot access '%s' on '%s'", expr->access_expr.sub_element.string, type_to_error_string(parent_type)); return false; } Decl *decl = type->decl; @@ -338,12 +343,12 @@ static inline bool sema_expr_analyse_access(Context *context, Type *to, Expr *ex Decl *member = strukt_recursive_search_member(decl, expr->access_expr.sub_element.string, &index); if (!member) { - SEMA_ERROR(expr->access_expr.sub_element, "There is no element '%s.%s'.", decl->name.string, expr->access_expr.sub_element.string); + SEMA_TOKEN_ERROR(expr->access_expr.sub_element, "There is no element '%s.%s'.", decl->name, expr->access_expr.sub_element.string); return false; } if (is_pointer) { - Expr *deref = expr_new(EXPR_UNARY, expr->loc); + Expr *deref = expr_new(EXPR_UNARY, expr->span); deref->unary_expr.operator = UNARYOP_DEREF; deref->unary_expr.expr = expr->access_expr.parent; deref->resolve_status = RESOLVE_DONE; @@ -361,7 +366,7 @@ static inline bool sema_expr_analyse_type_access(Context *context, Type *to, Exp if (!sema_resolve_type_info(context, type_info)) return false; if (!type_may_have_method_functions(type_info->type)) { - SEMA_ERROR(expr->loc, "'%s' does not have method functions.", type_to_error_string(type_info->type)); + SEMA_ERROR(expr, "'%s' does not have method functions.", type_to_error_string(type_info->type)); return false; } Decl *decl = type_info->type->decl; @@ -384,14 +389,14 @@ static inline bool sema_expr_analyse_type_access(Context *context, Type *to, Exp VECEACH(type_info->type->decl->method_functions, i) { Decl *function = type_info->type->decl->method_functions[i]; - if (expr->type_access.name.string == function->name.string) + if (expr->type_access.name.string == function->name) { expr->type_access.method = function; expr->type = function->func.function_signature.rtype->type; return true; } } - SEMA_ERROR(expr->loc, "No function '%s.%s' found.", type_to_error_string(type_info->type), expr->type_access.name.string); + SEMA_ERROR(expr, "No function '%s.%s' found.", type_to_error_string(type_info->type), expr->type_access.name.string); return false; } @@ -399,7 +404,7 @@ static inline Decl *decl_find_by_name(Decl** decls, const char *name) { VECEACH(decls, i) { - if (decls[i]->name.string == name) return decls[i]; + if (decls[i]->name == name) return decls[i]; } return NULL; } @@ -428,7 +433,7 @@ static inline bool sema_expr_analyse_struct_initializer_list(Context *context, T { if (field->expr_kind == EXPR_IDENTIFIER) { - decl = decl_find_by_name(members, field->identifier_expr.identifier.string); + decl = decl_find_by_name(members, field->identifier_expr.identifier); } TODO } @@ -436,7 +441,7 @@ static inline bool sema_expr_analyse_struct_initializer_list(Context *context, T { if (i >= size) { - SEMA_ERROR(field->loc, "Too many elements in initializer"); + SEMA_ERROR(field, "Too many elements in initializer"); return false; } decl = members[i]; @@ -464,7 +469,7 @@ static inline bool sema_expr_analyse_initializer_list(Context *context, Type *to default: break; } - SEMA_ERROR(expr->loc, "Cannot assign expression to '%s'.", type_to_error_string(to)); + SEMA_ERROR(expr, "Cannot assign expression to '%s'.", type_to_error_string(to)); return false; } @@ -494,9 +499,9 @@ static inline bool sema_expr_analyse_cast(Context *context, Type *to, Expr *expr // TODO above is probably not right, cast type not set. // Overwrite cast. - Token loc = expr->loc; + SourceRange loc = expr->span; *expr = *inner; - expr->loc = loc; + expr->span = loc; return true; } @@ -508,7 +513,7 @@ static bool sema_expr_analyse_assign(Context *context, Type *to, Expr *expr, Exp if (!expr_is_ltype(left)) { - SEMA_ERROR(left->loc, "Expression is not assignable."); + SEMA_ERROR(left, "Expression is not assignable."); return false; } if (!sema_analyse_expr(context, left->type, right)) return false; @@ -527,7 +532,7 @@ static bool sema_expr_analyse_bit_and_assign(Context *context, Type *to, Expr *e if (!type_is_number(left->type)) { - SEMA_ERROR(left->loc, "Expected a numeric type here."); + SEMA_ERROR(left, "Expected a numeric type here."); return false; } @@ -535,7 +540,7 @@ static bool sema_expr_analyse_bit_and_assign(Context *context, Type *to, Expr *e if (!type_is_number(right->type)) { - SEMA_ERROR(right->loc, "Expected a numeric type here."); + SEMA_ERROR(right, "Expected a numeric type here."); return false; } @@ -549,7 +554,7 @@ static bool sema_expr_analyse_bit_or_assign(Context *context, Type *to, Expr *ex if (!type_is_number(left->type)) { - SEMA_ERROR(left->loc, "Expected a numeric type here."); + SEMA_ERROR(left, "Expected a numeric type here."); return false; } @@ -557,7 +562,7 @@ static bool sema_expr_analyse_bit_or_assign(Context *context, Type *to, Expr *ex if (!type_is_number(right->type)) { - SEMA_ERROR(right->loc, "Expected a numeric type here."); + SEMA_ERROR(right, "Expected a numeric type here."); return false; } @@ -571,7 +576,7 @@ static bool sema_expr_analyse_bit_xor_assign(Context *context, Type *to, Expr *e if (!type_is_number(left->type)) { - SEMA_ERROR(left->loc, "Expected a numeric type here."); + SEMA_ERROR(left, "Expected a numeric type here."); return false; } @@ -579,7 +584,7 @@ static bool sema_expr_analyse_bit_xor_assign(Context *context, Type *to, Expr *e if (!type_is_number(right->type)) { - SEMA_ERROR(right->loc, "Expected a numeric type here."); + SEMA_ERROR(right, "Expected a numeric type here."); return false; } @@ -594,7 +599,7 @@ static bool sema_expr_analyse_div_assign(Context *context, Type *to, Expr *expr, if (!type_is_number(left->type)) { - SEMA_ERROR(left->loc, "Expected a numeric type here."); + SEMA_ERROR(left, "Expected a numeric type here."); return false; } @@ -602,7 +607,7 @@ static bool sema_expr_analyse_div_assign(Context *context, Type *to, Expr *expr, if (!type_is_number(right->type)) { - SEMA_ERROR(right->loc, "Expected a numeric type here."); + SEMA_ERROR(right, "Expected a numeric type here."); return false; } @@ -616,7 +621,7 @@ static bool sema_expr_analyse_mult_assign(Context *context, Type *to, Expr *expr if (!type_is_number(left->type)) { - SEMA_ERROR(left->loc, "Expected a numeric type here."); + SEMA_ERROR(left, "Expected a numeric type here."); return false; } @@ -624,7 +629,7 @@ static bool sema_expr_analyse_mult_assign(Context *context, Type *to, Expr *expr if (!type_is_number(right->type)) { - SEMA_ERROR(right->loc, "Expected a numeric type here."); + SEMA_ERROR(right, "Expected a numeric type here."); return false; } @@ -647,7 +652,7 @@ static bool sema_expr_analyse_sub_assign(Context *context, Type *to, Expr *expr, Type *right_type = right->type->canonical; if (!type_is_integer(right_type)) { - SEMA_ERROR(right->loc, "Expected an integer type instead."); + SEMA_ERROR(right, "Expected an integer type instead."); return false; } expr->type = left->type; @@ -658,13 +663,13 @@ static bool sema_expr_analyse_sub_assign(Context *context, Type *to, Expr *expr, if (!type_is_number(left->type)) { - SEMA_ERROR(left->loc, "Expected a numeric type here."); + SEMA_ERROR(left, "Expected a numeric type here."); return false; } if (!type_is_number(right->type)) { - SEMA_ERROR(right->loc, "Expected a numeric type here."); + SEMA_ERROR(right, "Expected a numeric type here."); return false; } @@ -687,7 +692,7 @@ static bool sema_expr_analyse_add_assign(Context *context, Type *to, Expr *expr, Type *right_type = right->type->canonical; if (!type_is_integer(right_type)) { - SEMA_ERROR(right->loc, "Expected an integer type instead."); + SEMA_ERROR(right, "Expected an integer type instead."); return false; } expr->type = left->type; @@ -698,13 +703,13 @@ static bool sema_expr_analyse_add_assign(Context *context, Type *to, Expr *expr, if (!type_is_number(left->type)) { - SEMA_ERROR(left->loc, "Expected a numeric type here."); + SEMA_ERROR(left, "Expected a numeric type here."); return false; } if (!type_is_number(right->type)) { - SEMA_ERROR(right->loc, "Expected a numeric type here."); + SEMA_ERROR(right, "Expected a numeric type here."); return false; } @@ -750,7 +755,7 @@ static bool sema_expr_analyse_sub(Context *context, Type *to, Expr *expr, Expr * return true; ERR: - SEMA_ERROR(expr->loc, "Cannot subtract '%s' from '%s'", type_to_error_string(left_type), type_to_error_string(right_type)); + SEMA_ERROR(expr, "Cannot subtract '%s' from '%s'", type_to_error_string(left_type), type_to_error_string(right_type)); return false; } @@ -787,7 +792,7 @@ static bool sema_expr_analyse_add(Context *context, Type *to, Expr *expr, Expr * return true; ERR: - SEMA_ERROR(expr->loc, "Cannot add '%s' to '%s'", type_to_error_string(left_type), type_to_error_string(right_type)); + SEMA_ERROR(expr, "Cannot add '%s' to '%s'", type_to_error_string(left_type), type_to_error_string(right_type)); return false; } @@ -806,7 +811,7 @@ static bool sema_expr_analyse_mult(Context *context, Type *to, Expr *expr, Expr return true; ERR: - SEMA_ERROR(expr->loc, "Cannot multiply '%s' and '%s'", type_to_error_string(left_type), type_to_error_string(right_type)); + SEMA_ERROR(expr, "Cannot multiply '%s' and '%s'", type_to_error_string(left_type), type_to_error_string(right_type)); return false; } @@ -827,14 +832,14 @@ static bool sema_expr_analyse_div(Context *context, Type *to, Expr *expr, Expr * case CONST_INT: if (right->const_expr.i == 0) { - SEMA_ERROR(right->loc, "Division by zero not allowed."); + SEMA_ERROR(right, "Division by zero not allowed."); return false; } break; case CONST_FLOAT: if (right->const_expr.f == 0) { - SEMA_ERROR(right->loc, "Division by zero not allowed."); + SEMA_ERROR(right, "Division by zero not allowed."); return false; } break; @@ -849,7 +854,7 @@ static bool sema_expr_analyse_div(Context *context, Type *to, Expr *expr, Expr * return true; ERR: - SEMA_ERROR(expr->loc, "Cannot divide '%s' by '%s'", type_to_error_string(left_type), type_to_error_string(right_type)); + SEMA_ERROR(expr, "Cannot divide '%s' by '%s'", type_to_error_string(left_type), type_to_error_string(right_type)); return false; } @@ -862,7 +867,7 @@ static bool sema_expr_analyse_mod(Context *context, Type *to, Expr *expr, Expr * if (right->expr_kind == EXPR_CONST && right->const_expr.i == 0) { - SEMA_ERROR(expr->binary_expr.right->loc, "Cannot perform mod by zero."); + SEMA_ERROR(expr->binary_expr.right, "Cannot perform mod by zero."); return false; } // TODO Insert trap on negative right. @@ -946,7 +951,7 @@ static bool sema_expr_analyse_shr(Context *context, Type *to, Expr *expr, Expr * { if (right->const_expr.i > left->type->canonical->builtin.bitsize) { - SEMA_ERROR(right->loc, "Rightshift exceeds bitsize of '%s'", type_to_error_string(left->type)); + SEMA_ERROR(right, "Rightshift exceeds bitsize of '%s'", type_to_error_string(left->type)); return false; } if (left->expr_kind == EXPR_CONST) @@ -970,7 +975,7 @@ static bool sema_expr_analyse_shr_assign(Context *context, Type *to, Expr *expr, if (!expr_is_ltype(left)) { - SEMA_ERROR(left->loc, "Expression is not assignable."); + SEMA_ERROR(left, "Expression is not assignable."); return false; } @@ -981,7 +986,7 @@ static bool sema_expr_analyse_shr_assign(Context *context, Type *to, Expr *expr, { if (right->const_expr.i > left->type->canonical->builtin.bitsize) { - SEMA_ERROR(right->loc, "Rightshift exceeds bitsize of '%s'", type_to_error_string(left->type)); + SEMA_ERROR(right, "Rightshift exceeds bitsize of '%s'", type_to_error_string(left->type)); return false; } } @@ -1007,7 +1012,7 @@ static bool sema_expr_analyse_shl(Context *context, Type *to, Expr *expr, Expr * { if (right->const_expr.i > left->type->canonical->builtin.bitsize) { - SEMA_ERROR(right->loc, "Leftshift exceeds bitsize of '%s'", type_to_error_string(left->type)); + SEMA_ERROR(right, "Leftshift exceeds bitsize of '%s'", type_to_error_string(left->type)); return false; } if (left->expr_kind == EXPR_CONST) @@ -1031,7 +1036,7 @@ static bool sema_expr_analyse_shl_assign(Context *context, Type *to, Expr *expr, if (!expr_is_ltype(left)) { - SEMA_ERROR(left->loc, "Expression is not assignable."); + SEMA_ERROR(left, "Expression is not assignable."); return false; } @@ -1044,7 +1049,7 @@ static bool sema_expr_analyse_shl_assign(Context *context, Type *to, Expr *expr, { if (right->const_expr.i > left->type->canonical->builtin.bitsize) { - SEMA_ERROR(right->loc, "Leftshift exceeds bitsize of '%s'", type_to_error_string(left->type)); + SEMA_ERROR(right, "Leftshift exceeds bitsize of '%s'", type_to_error_string(left->type)); return false; } } @@ -1095,7 +1100,7 @@ static bool sema_expr_analyse_comp(Context *context, Type *to, Expr *expr, Expr bool success = max && cast_implicit(left, max) && cast_implicit(right, max); if (!success) { - SEMA_ERROR(expr->loc, "Cannot implicitly convert types to evaluate '%s' %s '%s'", type_to_error_string(left_type), token_type_to_string(binaryop_to_token(expr->binary_expr.operator)), type_to_error_string(right_type)); + SEMA_ERROR(expr, "Cannot implicitly convert types to evaluate '%s' %s '%s'", type_to_error_string(left_type), token_type_to_string(binaryop_to_token(expr->binary_expr.operator)), type_to_error_string(right_type)); return false; } if (both_const(left, right)) @@ -1157,12 +1162,12 @@ static bool sema_expr_analyse_deref(Context *context, Type *to, Expr *expr, Expr Type *canonical = inner->type->canonical; if (canonical->type_kind != TYPE_POINTER) { - SEMA_ERROR(inner->loc, "Cannot take the dereference of a value of type '%s'", type_to_error_string(inner->type)); + SEMA_ERROR(inner, "Cannot take the dereference of a value of type '%s'", type_to_error_string(inner->type)); return false; } if (inner->expr_kind == EXPR_CONST) { - SEMA_ERROR(inner->loc, "Dereferencing nil is not allowed."); + SEMA_ERROR(inner, "Dereferencing nil is not allowed."); return false; } Type *deref_type = inner->type->type_kind != TYPE_POINTER ? inner->type : canonical; @@ -1174,7 +1179,7 @@ static bool sema_expr_analyse_addr(Context *context, Type *to, Expr *expr, Expr { if (!expr_is_ltype(inner)) { - SEMA_ERROR(inner->loc, "Cannot take the address of a value of type '%s'", type_to_error_string(inner->type)); + SEMA_ERROR(inner, "Cannot take the address of a value of type '%s'", type_to_error_string(inner->type)); return false; } expr->type = type_get_ptr(inner->type); @@ -1186,7 +1191,7 @@ static bool sema_expr_analyse_neg(Context *context, Type *to, Expr *expr, Expr * Type *canonical = inner->type->canonical; if (!builtin_may_negate(canonical)) { - SEMA_ERROR(expr->loc, "Cannot negate %s.", type_to_error_string(inner->type)); + SEMA_ERROR(expr, "Cannot negate %s.", type_to_error_string(inner->type)); return false; } if (inner->expr_kind != EXPR_CONST) @@ -1214,7 +1219,7 @@ static bool sema_expr_analyse_bit_not(Context *context, Type *to, Expr *expr, Ex Type *canonical = inner->type->canonical; if (!type_is_integer(canonical) && canonical != type_bool) { - SEMA_ERROR(expr->loc, "Cannot bit negate %s.", type_to_error_string(inner->type)); + SEMA_ERROR(expr, "Cannot bit negate %s.", type_to_error_string(inner->type)); } if (inner->expr_kind != EXPR_CONST) { @@ -1296,7 +1301,7 @@ static bool sema_expr_analyse_not(Context *context, Type *to, Expr *expr, Expr * case TYPE_STRING: case TYPE_ENUM: case TYPE_ERROR: - SEMA_ERROR(expr->loc, "Cannot use 'not' on %s", type_to_error_string(inner->type)); + SEMA_ERROR(expr, "Cannot use 'not' on %s", type_to_error_string(inner->type)); return false; } UNREACHABLE @@ -1306,12 +1311,12 @@ static inline bool sema_expr_analyse_incdec(Context *context, Type *to, Expr *ex { if (!expr_is_ltype(inner)) { - SEMA_ERROR(inner->loc, "Expression cannot be assigned to"); + SEMA_ERROR(inner, "Expression cannot be assigned to"); return false; } if (!type_is_number(inner->type->canonical) && inner->type->canonical->type_kind != TYPE_POINTER) { - SEMA_ERROR(inner->loc, "Expression must be a number or a pointer"); + SEMA_ERROR(inner, "Expression must be a number or a pointer"); return false; } expr->type = inner->type; @@ -1516,7 +1521,7 @@ bool sema_analyse_expr(Context *context, Type *to, Expr *expr) expr->resolve_status = RESOLVE_RUNNING; break; case RESOLVE_RUNNING: - SEMA_ERROR(expr->loc, "Recursive resolution of expression"); + SEMA_ERROR(expr, "Recursive resolution of expression"); return expr_poison(expr); case RESOLVE_DONE: return expr_ok(expr); diff --git a/src/compiler/lexer.c b/src/compiler/lexer.c index 27e41cb1e..71119122b 100644 --- a/src/compiler/lexer.c +++ b/src/compiler/lexer.c @@ -4,101 +4,77 @@ #include "compiler_internal.h" -typedef struct -{ - bool lexer_init_complete; - const char *file_begin; - const char *lexing_start; - const char *current; - uint16_t source_file; - LexerState lexer_state; - File *current_file; - //Token saved_tok; Will be used later if doing deferred parsing. - //Token saved_prev_tok; Will be used later is doing deferred parsing. - SourceLoc last_in_range; - struct - { - const char *start; - const char *current; - Token tok; - Token prev_tok; - } stored; -} Lexer; -Lexer lexer; Token next_tok; Token tok; // --- Lexing general methods. -static inline char peek() +static inline char peek(Lexer *lexer) { - return *lexer.current; + return *lexer->current; } -static inline char prev() +static inline char prev(Lexer *lexer) { - return lexer.current[-1]; + return lexer->current[-1]; } -static inline void backtrack() +static inline void backtrack(Lexer *lexer) { - lexer.current--; + lexer->current--; } -void lexer_store_line_end(void) +void lexer_store_line_end(Lexer *lexer) { - source_file_append_line_end(lexer.current_file, lexer.current_file->start_id + lexer.current - lexer.file_begin); -} - -void lexer_store_state(void) -{ - lexer.stored.current = lexer.current; - lexer.stored.start = lexer.lexing_start; - lexer.stored.tok = next_tok; - lexer.stored.prev_tok = tok; -} - -void lexer_restore_state(void) -{ - lexer.current = lexer.stored.current; - lexer.lexing_start = lexer.stored.start; - next_tok = lexer.stored.tok; - tok = lexer.stored.prev_tok; + source_file_append_line_end(lexer->current_file, lexer->current_file->start_id + lexer->current - lexer->file_begin); } -static inline char peek_next() +static inline char peek_next(Lexer *lexer) { - return lexer.current[1]; + return lexer->current[1]; } -static inline char peek_next_next() +static inline char peek_next_next(Lexer *lexer) { - return lexer.current[2]; + return lexer->current[2]; } -static inline char next() +static inline char next(Lexer *lexer) { - return *(lexer.current++); + return *(lexer->current++); } -static inline void skip(int steps) +static inline void skip(Lexer *lexer, int steps) { - lexer.current += steps; + lexer->current += steps; } -static inline bool reached_end(void) +static inline bool reached_end(Lexer *lexer) { - return *lexer.current == '\0'; + return *lexer->current == '\0'; } -static Token error_token(const char *message, ...) +static inline SourceLoc loc_from_ptr(Lexer *lexer, const char *start) +{ + return (SourceLoc) (lexer->current_file->start_id + (start - lexer->file_begin)); +} + +static inline SourceRange range_from_ptr(Lexer *lexer, const char *start, const char *end) +{ + return (SourceRange) { + (lexer->current_file->start_id + (start - lexer->file_begin)), + (lexer->current_file->start_id + (end - lexer->file_begin)), + }; +} + +static Token error_token(Lexer *lexer, const char *message, ...) { Token token = { .type = TOKEN_INVALID_TOKEN, - .span = { (SourceLoc) (lexer.current_file->start_id + (lexer.lexing_start - lexer.file_begin)), 1 }, - .start = lexer.lexing_start + .span = range_from_ptr(lexer, lexer->lexing_start, lexer->current), + .start = lexer->lexing_start }; va_list list; va_start(list, message); @@ -107,129 +83,152 @@ static Token error_token(const char *message, ...) return token; } -static Token make_token(TokenType type, const char *string) +static Token make_token(Lexer *lexer, TokenType type, const char *string) { - size_t token_size = lexer.current - lexer.lexing_start; - if (token_size > TOKEN_MAX_LENGTH) return error_token("Token exceeding max length"); + size_t token_size = lexer->current - lexer->lexing_start; + if (token_size > TOKEN_MAX_LENGTH) return error_token(lexer, "Token exceeding max length"); return (Token) { .type = type, - .span = { .loc = (SourceLoc) (lexer.current_file->start_id + (lexer.lexing_start - lexer.file_begin)), .length = token_size }, + .span = range_from_ptr(lexer, lexer->lexing_start, lexer->current), .start = string }; } -static Token make_string_token(TokenType type, const char* string) + +static Token make_string_token(Lexer *lexer, TokenType type, const char* string) { - size_t token_size = lexer.current - lexer.lexing_start; - if (token_size > TOKEN_MAX_LENGTH) return error_token("Token exceeding max length"); + size_t token_size = lexer->current - lexer->lexing_start; + if (token_size > TOKEN_MAX_LENGTH) return error_token(lexer, "Token exceeding max length"); return (Token) { .type = type, - .span = { .loc = (SourceLoc) (lexer.current_file->start_id + (lexer.lexing_start - lexer.file_begin)), .length = token_size }, + .span = range_from_ptr(lexer, lexer->lexing_start, lexer->current), .string = string, }; } -static inline bool match(char expected) +static inline bool match(Lexer *lexer, char expected) { - if (reached_end()) return false; - if (*lexer.current != expected) return false; - lexer.current++; + if (reached_end(lexer)) return false; + if (*lexer->current != expected) return false; + lexer->current++; return true; } -static inline void match_assert(char expected) +static inline void match_assert(Lexer *lexer, char expected) { - assert(!reached_end()); - assert(lexer.current[0] == expected); - lexer.current++; + assert(!reached_end(lexer)); + assert(lexer->current[0] == expected); + lexer->current++; } -// --- Whitespace handling. -typedef enum +static inline Token parse_line_comment(Lexer *lexer) { - WHITESPACE_SKIPPED_OK, - WHITESPACE_FOUND_DOCS_START, - WHITESPACE_COMMENT_REACHED_EOF, - WHITESPACE_FOUND_EOF, - WHITESPACE_FOUND_DOCS_EOL, -} SkipWhitespaceResult; + // // style comment + // Skip forward to the end. + TokenType comment_type = match(lexer, '/') ? TOKEN_DOC_COMMENT : TOKEN_COMMENT; + while (!reached_end(lexer) && peek(lexer) != '\n') + { + next(lexer); + } + Token token = make_token(lexer, comment_type, lexer->lexing_start); + + // If we found EOL, then walk past '\n' + if (!reached_end(lexer)) + { + lexer_store_line_end(lexer); + next(lexer); + } + + return token; +} + +static inline Token parse_nested_comment(Lexer *lexer) +{ + next(lexer); + int nesting = 0; + // /+ style comment + nesting = 1; + while (!reached_end(lexer) && nesting > 0) + { + switch (peek(lexer)) + { + case '/': + if (peek_next(lexer) == '+') + { + skip(lexer, 2); + nesting++; + continue; + } + break; + case '+': + if (peek_next(lexer) == '/') + { + skip(lexer, 2); + nesting--; + continue; + } + break; + default: + break; + } + next(lexer); + } + if (reached_end(lexer)) + { + return error_token(lexer, "Missing '+/' to end the nested comment."); + } + return make_token(lexer, TOKEN_COMMENT, lexer->lexing_start); +} + +static inline Token parse_multiline_comment(Lexer *lexer) +{ + TokenType type = peek(lexer) == '*' && peek_next(lexer) != '/' ? TOKEN_DOC_COMMENT : TOKEN_COMMENT; + while (1) + { + switch (peek(lexer)) + { + case '*': + if (peek_next(lexer) == '/') + { + skip(lexer, 2); + return make_token(lexer, type, lexer->lexing_start); + } + break; + case '\n': + lexer_store_line_end(lexer); + break; + case '\0': + return error_token(lexer, "Missing '*/' to end the multiline comment."); + } + next(lexer); + } +} /** * Skip regular comments. * * @return the result of the skip (did we enter docs? did we have any errors?) */ -SkipWhitespaceResult skip_whitespace() +void skip_whitespace(Lexer *lexer) { while (1) { - char c = peek(); + char c = peek(lexer); switch (c) { - case '\0': - return WHITESPACE_FOUND_EOF; case '\n': - lexer_store_line_end(); - // If we are currently parsing docs, then end of line is considered meaningful. - if (lexer.lexer_state == LEXER_STATE_DOCS_PARSE_DIRECTIVE) return WHITESPACE_FOUND_DOCS_EOL; + lexer_store_line_end(lexer); case ' ': case '\t': case '\r': case '\f': - next(); + next(lexer); break; - case '/': - if (peek_next() == '/') - { - while (!reached_end() && peek() != '\n') next(); - break; - } - if (peek_next() == '*') - { - // Enter docs parsing on /** - if (peek_next_next() == '*' && lexer.lexer_state == LEXER_STATE_NORMAL) - { - return WHITESPACE_FOUND_DOCS_START; - } - while (1) - { - next(); - if (reached_end()) return WHITESPACE_COMMENT_REACHED_EOF; - if (peek() == '*' && peek_next() == '/') - { - lexer.current += 2; - break; - } - } - break; - } - if (peek_next() == '+') - { - int nesting_depth = 1; - while (1) - { - next(); - if (reached_end()) return WHITESPACE_COMMENT_REACHED_EOF; - if (peek() == '/' && peek_next() == '+') - { - lexer.current += 2; - nesting_depth++; - continue; - } - if (peek() == '+' && peek_next() == '/') - { - lexer.current += 2; - if (--nesting_depth == 0) break; - } - } - break; - } - return WHITESPACE_SKIPPED_OK; default: - return WHITESPACE_SKIPPED_OK; + return; } } } @@ -237,44 +236,44 @@ SkipWhitespaceResult skip_whitespace() // --- Normal scanning methods start here -static inline Token scan_prefixed_ident(TokenType type, TokenType no_ident_type, bool ends_with_bang, const char *start) +static inline Token scan_prefixed_ident(Lexer *lexer, TokenType type, TokenType no_ident_type, bool ends_with_bang, const char *start) { - uint32_t hash = FNV1a(prev(), FNV1_SEED); - while (is_alphanum_(peek())) + uint32_t hash = FNV1a(prev(lexer), FNV1_SEED); + while (is_alphanum_(peek(lexer))) { - hash = FNV1a(next(), hash); + hash = FNV1a(next(lexer), hash); } - if (ends_with_bang && peek() == '!') + if (ends_with_bang && peek(lexer) == '!') { - hash = FNV1a(next(), hash); + hash = FNV1a(next(lexer), hash); } - uint32_t len = (uint32_t)(lexer.current - lexer.lexing_start); - if (len == 1) return make_token(no_ident_type, start); - const char* interned = symtab_add(lexer.lexing_start, len, hash, &type); - return make_string_token(type, interned); + uint32_t len = (uint32_t)(lexer->current - lexer->lexing_start); + if (len == 1) return make_token(lexer, no_ident_type, start); + const char* interned = symtab_add(lexer->lexing_start, len, hash, &type); + return make_string_token(lexer, type, interned); } -static inline void scan_skipped_ident() +static inline void scan_skipped_ident(Lexer *lexer) { - while (is_alphanum_(peek())) next(); + while (is_alphanum_(peek(lexer))) next(lexer); } // Parses identifiers. Note that this is a bit complicated here since // we split identifiers into 2 types + find keywords. -static inline Token scan_ident(void) +static inline Token scan_ident(Lexer *lexer) { TokenType type = 0; uint32_t hash = FNV1_SEED; - while (peek() == '_') + while (peek(lexer) == '_') { - hash = FNV1a(next(), hash); + hash = FNV1a(next(lexer), hash); } while (1) { - switch (peek()) + switch (peek(lexer)) { case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': @@ -301,135 +300,135 @@ static inline Token scan_ident(void) break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - if (!type) return error_token("A letter must preceed any digit"); + if (!type) return error_token(lexer, "A letter must preceed any digit"); case '_': break; default: goto EXIT; } - hash = FNV1a(next(), hash); + hash = FNV1a(next(lexer), hash); } EXIT:; - uint32_t len = lexer.current - lexer.lexing_start; - const char* interned_string = symtab_add(lexer.lexing_start, len, hash, &type); - return make_string_token(type, interned_string); + uint32_t len = lexer->current - lexer->lexing_start; + const char* interned_string = symtab_add(lexer->lexing_start, len, hash, &type); + return make_string_token(lexer, type, interned_string); } #pragma mark ----- Number scanning -static Token scan_oct(void) +static Token scan_oct(Lexer *lexer) { - char o = next(); // Skip the o - if (!is_oct(next())) return error_token("An expression starting with '0%c' would expect to be followed by octal numbers (0-7).", o); - while (is_oct_or_(peek())) next(); - return make_token(TOKEN_INTEGER, lexer.lexing_start); + char o = next(lexer); // Skip the o + if (!is_oct(next(lexer))) return error_token(lexer, "An expression starting with '0%c' would expect to be followed by octal numbers (0-7).", o); + while (is_oct_or_(peek(lexer))) next(lexer); + return make_token(lexer, TOKEN_INTEGER, lexer->lexing_start); } -Token scan_binary(void) +Token scan_binary(Lexer *lexer) { - char b = next(); // Skip the b - if (!is_binary(next())) + char b = next(lexer); // Skip the b + if (!is_binary(next(lexer))) { - return error_token("An expression starting with '0%c' would expect a sequence of zeroes and ones, " + return error_token(lexer, "An expression starting with '0%c' would expect a sequence of zeroes and ones, " "did you try to write a hex value but forgot the '0x'?", b); } - while (is_binary_or_(peek())) next(); - return make_token(TOKEN_INTEGER, lexer.lexing_start); + while (is_binary_or_(peek(lexer))) next(lexer); + return make_token(lexer, TOKEN_INTEGER, lexer->lexing_start); } #define PARSE_SPECIAL_NUMBER(is_num, is_num_with_underscore, exp, EXP) \ -while (is_num_with_underscore(peek())) next(); \ +while (is_num_with_underscore(peek(lexer))) next(lexer); \ bool is_float = false; \ -if (peek() == '.') \ +if (peek(lexer) == '.') \ { \ is_float = true; \ - next(); \ - char c = peek(); \ - if (c == '_') return error_token("Can't parse this as a floating point value due to the '_' directly after decimal point."); \ - if (is_num(c)) next(); \ - while (is_num_with_underscore(peek())) next(); \ + next(lexer); \ + char c = peek(lexer); \ + if (c == '_') return error_token(lexer, "Can't parse this as a floating point value due to the '_' directly after decimal point."); \ + if (is_num(c)) next(lexer); \ + while (is_num_with_underscore(peek(lexer))) next(lexer); \ } \ -char c = peek(); \ +char c = peek(lexer); \ if (c == (exp) || c == (EXP)) \ { \ is_float = true; \ - next(); \ - char c2 = next(); \ - if (c2 == '+' || c2 == '-') c2 = next(); \ - if (!is_num(c2)) return error_token("Parsing the floating point exponent failed, because '%c' is not a number.", c2); \ - while (is_num(peek())) next(); \ + next(lexer); \ + char c2 = next(lexer); \ + if (c2 == '+' || c2 == '-') c2 = next(lexer); \ + if (!is_num(c2)) return error_token(lexer, "Parsing the floating point exponent failed, because '%c' is not a number.", c2); \ + while (is_num(peek(lexer))) next(lexer); \ } \ -if (prev() == '_') return error_token("The number ended with '_', but that character needs to be between, not after, digits."); \ -return make_token(is_float ? TOKEN_FLOAT : TOKEN_INTEGER, lexer.lexing_start) +if (prev(lexer) == '_') return error_token(lexer, "The number ended with '_', but that character needs to be between, not after, digits."); \ +return make_token(lexer, is_float ? TOKEN_FLOAT : TOKEN_INTEGER, lexer->lexing_start) -static inline Token scan_hex(void) +static inline Token scan_hex(Lexer *lexer) { - char x = next(); // skip the x - if (!is_hex(next())) + char x = next(lexer); // skip the x + if (!is_hex(next(lexer))) { - return error_token("'0%c' starts a hexadecimal number, " - "but it was followed by '%c' which is not part of a hexadecimal number.", x, prev()); + return error_token(lexer, "'0%c' starts a hexadecimal number, " + "but it was followed by '%c' which is not part of a hexadecimal number.", x, prev(lexer)); } PARSE_SPECIAL_NUMBER(is_hex, is_hex_or_, 'p', 'P'); } -static inline Token scan_dec(void) +static inline Token scan_dec(Lexer *lexer) { PARSE_SPECIAL_NUMBER(is_digit, is_digit_or_, 'e', 'E'); } #undef PARSE_SPECIAL_NUMBER -static inline Token scan_digit(void) +static inline Token scan_digit(Lexer *lexer) { - if (peek() == '0') + if (peek(lexer) == '0') { - switch (peek_next()) + switch (peek_next(lexer)) { case 'x': case 'X': - skip(2); - return scan_hex(); + skip(lexer, 2); + return scan_hex(lexer); case 'o': case 'O': - skip(2); - return scan_oct(); + skip(lexer, 2); + return scan_oct(lexer); case 'b': case 'B': - skip(2); - return scan_binary(); + skip(lexer, 2); + return scan_binary(lexer); default: break; } } - return scan_dec(); + return scan_dec(lexer); } #pragma mark ----- -static inline Token scan_char() +static inline Token scan_char(Lexer *lexer) { int width = 0; char c; - while ((c = next()) != '\'') + while ((c = next(lexer)) != '\'') { - if (c == '\0' || c == '\n') return error_token("Character literal did not terminate."); + if (c == '\0' || c == '\n') return error_token(lexer, "Character literal did not terminate."); width++; // Was this an escape? if (c == '\\') { // Yes, so check if it's hex: - if (next() == 'x') + if (next(lexer) == 'x') { // Walk through the two characters. for (int i = 0; i < 2; i++) { - if (!is_hex(next())) + if (!is_hex(next(lexer))) { - return error_token( + return error_token(lexer, "An escape sequence starting with " "'\\x' needs to be followed by " "a two digit hexadecimal number."); @@ -440,45 +439,45 @@ static inline Token scan_char() } if (width == 0) { - return error_token("The character literal was empty."); + return error_token(lexer, "The character literal was empty."); } if (width > 2 && width != 4 && width != 8) { - error_token("Character literals may only be 1, 2 or 8 characters wide."); + error_token(lexer, "Character literals may only be 1, 2 or 8 characters wide."); } - return make_token(TOKEN_INTEGER, lexer.lexing_start); + return make_token(lexer, TOKEN_INTEGER, lexer->lexing_start); } -static inline Token scan_string() +static inline Token scan_string(Lexer *lexer) { char c; - while ((c = next()) != '"') + while ((c = next(lexer)) != '"') { - if (c == '\\' && peek() == '"') + if (c == '\\' && peek(lexer) == '"') { - next(); + next(lexer); continue; } - if (reached_end()) + if (reached_end(lexer)) { - return error_token("Reached the end looking for '\"'. Did you forget it?"); + return error_token(lexer, "Reached the end looking for '\"'. Did you forget it?"); } } - return make_token(TOKEN_STRING, lexer.lexing_start); + return make_token(lexer, TOKEN_STRING, lexer->lexing_start); } -static inline void skip_docs_whitespace() +static inline void skip_docs_whitespace(Lexer *lexer) { while (1) { - char c = peek(); + char c = peek(lexer); switch (c) { case ' ': case '\t': case '\r': case '\f': - next(); + next(lexer); break; default: return; @@ -486,270 +485,162 @@ static inline void skip_docs_whitespace() } } -static inline Token scan_docs_directive(void) + +Token lexer_scan_token(Lexer *lexer) { - match_assert('@'); - Token token = scan_prefixed_ident(TOKEN_AT_IDENT, TOKEN_AT, false, "@"); - assert(token.type != TOKEN_AT); - lexer.lexer_state = LEXER_STATE_DOCS_PARSE_DIRECTIVE; - return token; -} - -static inline Token scan_docs(void) -{ - assert(lexer.lexer_state == LEXER_STATE_DOCS_PARSE); - // We assume we stand at the start of a docs comment or after a new line. - // First, skip any whitespace: - skip_docs_whitespace(); - - // At this point we might encounter any number of '*', consume those, unless followed by '/' - while (peek() == '*') - { - // We found the docs end - if (peek_next() == '/') - { - // Reset start - lexer.lexing_start = lexer.current; - - // Consume the '*/' - skip(2); - - // Return end - lexer.lexer_state = LEXER_STATE_NORMAL; - return make_token(TOKEN_DOCS_END, "*/"); - } - - // Otherwise continue consuming - next(); - } - - // This might be followed again by whitespace, such whitespace is skipped. - skip_docs_whitespace(); - - // Reset start - lexer.lexing_start = lexer.current; - - // Now we passed through all of the whitespace. Here we might possibly see a "@", - // if so, we found a directive: - if (peek() == '@' && is_letter(peek_next())) - { - return scan_docs_directive(); - } - - // Otherwise this is just plain row, and we scan to the end of line *or* to a '*/' - - while (1) - { - switch (peek()) - { - case '*': - // Eat all * at the beginning. - while (peek_next() == '*') next(); - - // We found the end, so just make a token out of the rest. - // Note that this line will not get a linebreak at the end. - if (peek_next() == '/') return make_token(TOKEN_DOCS_LINE, "*"); - // Otherwise it's just something in the text, so continue. - next(); - break; - case '\n': - // Normal line of text. - lexer_store_line_end(); - next(); - return make_token(TOKEN_DOCS_LINE, "\n"); - case '\0': - return error_token("The document ended without finding the end of the doc comment. " - "Did you forget a '*/' somewhere?"); - default: - break; - } - } -} - -Token lexer_scan_token(void) -{ - // First we handle our "in docs" state. - if (lexer.lexer_state == LEXER_STATE_DOCS_PARSE) - { - return scan_docs(); - } - // Now skip the whitespace. - SkipWhitespaceResult result = skip_whitespace(); + skip_whitespace(lexer); // Point start to the first non-whitespace character. - lexer.lexing_start = lexer.current; + lexer->lexing_start = lexer->current; - switch (result) + if (reached_end(lexer)) { - case WHITESPACE_FOUND_DOCS_START: - // Here we found '/**', so we skip past that - // and switch state. - skip(3); - lexer.lexer_state = LEXER_STATE_DOCS_PARSE; - return make_token(TOKEN_DOCS_START, "/**"); - case WHITESPACE_COMMENT_REACHED_EOF: - return error_token("Reached the end looking for '*/'. Did you forget it somewhere?"); - case WHITESPACE_FOUND_EOF: - return make_token(TOKEN_EOF, "\n"); - case WHITESPACE_FOUND_DOCS_EOL: - skip(1); - lexer.lexer_state = LEXER_STATE_DOCS_PARSE; - return make_token(TOKEN_DOCS_EOL, "\n"); - case WHITESPACE_SKIPPED_OK: - break; + return make_token(lexer, TOKEN_EOF, "\n"); } - char c = next(); + char c = next(lexer); switch (c) { case '@': - return scan_prefixed_ident(TOKEN_AT_IDENT, TOKEN_AT, true, "@"); + return scan_prefixed_ident(lexer, TOKEN_AT_IDENT, TOKEN_AT, true, "@"); case '\'': - return scan_char(); + return scan_char(lexer); case '"': - return scan_string(); + return scan_string(lexer); case '#': - return scan_prefixed_ident(TOKEN_HASH_IDENT, TOKEN_HASH, false, "#"); + return scan_prefixed_ident(lexer, TOKEN_HASH_IDENT, TOKEN_HASH, false, "#"); case '$': - return scan_prefixed_ident(TOKEN_CT_IDENT, TOKEN_DOLLAR, false, "$"); + return scan_prefixed_ident(lexer, TOKEN_CT_IDENT, TOKEN_DOLLAR, false, "$"); case ',': - return make_token(TOKEN_COMMA, ","); + return make_token(lexer, TOKEN_COMMA, ","); case ';': - return make_token(TOKEN_EOS, ";"); + return make_token(lexer, TOKEN_EOS, ";"); case '{': - return make_token(TOKEN_LBRACE, "{"); + return make_token(lexer, TOKEN_LBRACE, "{"); case '}': - return match(')') ? make_token(TOKEN_RPARBRA, "})") : make_token(TOKEN_RBRACE, "})"); + return match(lexer, ')') ? make_token(lexer, TOKEN_RPARBRA, "})") : make_token(lexer, TOKEN_RBRACE, "})"); case '(': - return match('{') ? make_token(TOKEN_LPARBRA, "({") : make_token(TOKEN_LPAREN, ")"); + return match(lexer, '{') ? make_token(lexer, TOKEN_LPARBRA, "({") : make_token(lexer, TOKEN_LPAREN, ")"); case ')': - return make_token(TOKEN_RPAREN, ")"); + return make_token(lexer, TOKEN_RPAREN, ")"); case '[': - return make_token(TOKEN_LBRACKET, "["); + return make_token(lexer, TOKEN_LBRACKET, "["); case ']': - return make_token(TOKEN_RBRACKET, "]"); + return make_token(lexer, TOKEN_RBRACKET, "]"); case '.': - if (match('.')) return match('.') ? make_token(TOKEN_ELIPSIS, "...") : make_token(TOKEN_DOTDOT, ".."); - return make_token(TOKEN_DOT, "."); + if (match(lexer, '.')) return match(lexer, '.') ? make_token(lexer, TOKEN_ELIPSIS, "...") : make_token(lexer, TOKEN_DOTDOT, ".."); + return make_token(lexer, TOKEN_DOT, "."); case '~': - return make_token(TOKEN_BIT_NOT, "~"); + return make_token(lexer, TOKEN_BIT_NOT, "~"); case ':': - return match(':') ? make_token(TOKEN_SCOPE, "::") : make_token(TOKEN_COLON, ":"); + return match(lexer, ':') ? make_token(lexer, TOKEN_SCOPE, "::") : make_token(lexer, TOKEN_COLON, ":"); case '!': - return match('=') ? make_token(TOKEN_NOT_EQUAL, "!=") : make_token(TOKEN_NOT, "!"); + return match(lexer, '=') ? make_token(lexer, TOKEN_NOT_EQUAL, "!=") : make_token(lexer, TOKEN_NOT, "!"); case '/': - return match('=') ? make_token(TOKEN_DIV_ASSIGN, "/=") : make_token(TOKEN_DIV, "/"); + if (match(lexer, '/')) return parse_line_comment(lexer); + if (match(lexer, '*')) return parse_multiline_comment(lexer); + if (match(lexer, '+')) return parse_nested_comment(lexer); + return match(lexer, '=') ? make_token(lexer, TOKEN_DIV_ASSIGN, "/=") : make_token(lexer, TOKEN_DIV, "/"); case '*': - if (lexer.lexer_state == LEXER_STATE_DOCS_PARSE_DIRECTIVE && match('/')) - { - lexer.lexer_state = LEXER_STATE_NORMAL; - return make_token(TOKEN_DOCS_END, "*/"); - } - if (match('%')) return match('=') ? make_token(TOKEN_MINUS_MOD_ASSIGN, "*%=") : make_token(TOKEN_MULT_MOD, "*%"); - return match('=') ? make_token(TOKEN_MULT_ASSIGN, "*=") : make_token(TOKEN_STAR, "*"); + if (match(lexer, '%')) return match(lexer, '=') ? make_token(lexer, TOKEN_MINUS_MOD_ASSIGN, "*%=") : make_token(lexer, TOKEN_MULT_MOD, "*%"); + return match(lexer, '=') ? make_token(lexer, TOKEN_MULT_ASSIGN, "*=") : make_token(lexer, TOKEN_STAR, "*"); case '=': - return match('=') ? make_token(TOKEN_EQEQ, "==") : make_token(TOKEN_EQ, "="); + return match(lexer, '=') ? make_token(lexer, TOKEN_EQEQ, "==") : make_token(lexer, TOKEN_EQ, "="); case '^': - return match('=') ? make_token(TOKEN_BIT_XOR_ASSIGN, "^=") : make_token(TOKEN_BIT_XOR, "^"); + return match(lexer, '=') ? make_token(lexer, TOKEN_BIT_XOR_ASSIGN, "^=") : make_token(lexer, TOKEN_BIT_XOR, "^"); case '?': - return match(':') ? make_token(TOKEN_ELVIS, "?:") : make_token(TOKEN_QUESTION, "?"); + return match(lexer, ':') ? make_token(lexer, TOKEN_ELVIS, "?:") : make_token(lexer, TOKEN_QUESTION, "?"); case '<': - if (match('<')) return match('=') ? make_token(TOKEN_SHL_ASSIGN, "<<=") : make_token(TOKEN_SHL, "<<"); - return match('=') ? make_token(TOKEN_LESS_EQ, "<=") : make_token(TOKEN_LESS, "<"); + if (match(lexer, '<')) return match(lexer, '=') ? make_token(lexer, TOKEN_SHL_ASSIGN, "<<=") : make_token(lexer, TOKEN_SHL, "<<"); + return match(lexer, '=') ? make_token(lexer, TOKEN_LESS_EQ, "<=") : make_token(lexer, TOKEN_LESS, "<"); case '>': - if (match('>')) return match('=') ? make_token(TOKEN_SHR_ASSIGN, ">>=") : make_token(TOKEN_SHR, ">>"); - return match('=') ? make_token(TOKEN_GREATER_EQ, ">=") : make_token(TOKEN_GREATER, ">"); + if (match(lexer, '>')) return match(lexer, '=') ? make_token(lexer, TOKEN_SHR_ASSIGN, ">>=") : make_token(lexer, TOKEN_SHR, ">>"); + return match(lexer, '=') ? make_token(lexer, TOKEN_GREATER_EQ, ">=") : make_token(lexer, TOKEN_GREATER, ">"); case '%': - return match('=') ? make_token(TOKEN_MOD_ASSIGN, "%=") : make_token(TOKEN_MOD, "%"); + return match(lexer, '=') ? make_token(lexer, TOKEN_MOD_ASSIGN, "%=") : make_token(lexer, TOKEN_MOD, "%"); case '&': - if (match('&')) return make_token(TOKEN_AND, "&&"); - return match('=') ? make_token(TOKEN_BIT_AND_ASSIGN, "&=") : make_token(TOKEN_AMP, "&"); + if (match(lexer, '&')) return make_token(lexer, TOKEN_AND, "&&"); + return match(lexer, '=') ? make_token(lexer, TOKEN_BIT_AND_ASSIGN, "&=") : make_token(lexer, TOKEN_AMP, "&"); case '|': - if (match('|')) return make_token(TOKEN_OR, "||"); - return match('=') ? make_token(TOKEN_BIT_OR_ASSIGN, "|=") : make_token(TOKEN_BIT_OR, "|"); + if (match(lexer, '|')) return make_token(lexer, TOKEN_OR, "||"); + return match(lexer, '=') ? make_token(lexer, TOKEN_BIT_OR_ASSIGN, "|=") : make_token(lexer, TOKEN_BIT_OR, "|"); case '+': - if (match('%')) return match('=') ? make_token(TOKEN_PLUS_MOD_ASSIGN, "+%=") : make_token(TOKEN_PLUS_MOD, "+%"); - if (match('+')) return make_token(TOKEN_PLUSPLUS, "++"); - if (match('=')) return make_token(TOKEN_PLUS_ASSIGN, "+="); - return make_token(TOKEN_PLUS, "+"); + if (match(lexer, '%')) return match(lexer, '=') ? make_token(lexer, TOKEN_PLUS_MOD_ASSIGN, "+%=") : make_token(lexer, TOKEN_PLUS_MOD, "+%"); + if (match(lexer, '+')) return make_token(lexer, TOKEN_PLUSPLUS, "++"); + if (match(lexer, '=')) return make_token(lexer, TOKEN_PLUS_ASSIGN, "+="); + return make_token(lexer, TOKEN_PLUS, "+"); case '-': - if (match('>')) return make_token(TOKEN_ARROW, "->"); - if (match('%')) return match('=') ? make_token(TOKEN_MINUS_MOD_ASSIGN, "-%=") : make_token(TOKEN_MINUS_MOD, "-%"); - if (match('-')) return make_token(TOKEN_MINUSMINUS, "--"); - if (match('=')) return make_token(TOKEN_MINUS_ASSIGN, "-="); - return make_token(TOKEN_MINUS, "-"); + if (match(lexer, '>')) return make_token(lexer, TOKEN_ARROW, "->"); + if (match(lexer, '%')) return match(lexer, '=') ? make_token(lexer, TOKEN_MINUS_MOD_ASSIGN, "-%=") : make_token(lexer, TOKEN_MINUS_MOD, "-%"); + if (match(lexer, '-')) return make_token(lexer, TOKEN_MINUSMINUS, "--"); + if (match(lexer, '=')) return make_token(lexer, TOKEN_MINUS_ASSIGN, "-="); + return make_token(lexer, TOKEN_MINUS, "-"); default: if (is_alphanum_(c)) { - backtrack(); - return is_digit(c) ? scan_digit() : scan_ident(); + backtrack(lexer); + return is_digit(c) ? scan_digit(lexer) : scan_ident(lexer); } if (c < 0) { - return error_token("The 0%x character may not be placed outside of a string or comment, did you perhaps forget a \" somewhere?", (uint8_t)c); + return error_token(lexer, "The 0%x character may not be placed outside of a string or comment, did you perhaps forget a \" somewhere?", (uint8_t)c); } - return error_token("'%c' may not be placed outside of a string or comment, did you perhaps forget a \" somewhere?", c); + return error_token(lexer, "'%c' may not be placed outside of a string or comment, did you perhaps forget a \" somewhere?", c); } } -File* lexer_current_file(void) +File* lexer_current_file(Lexer *lexer) { - return lexer.current_file; + return lexer->current_file; } -void lexer_check_init(void) +void lexer_check_init() { - if (lexer.lexer_init_complete) return; - lexer.lexer_init_complete = true; + static bool symtab_has_init = false; + if (symtab_has_init) return; + symtab_has_init = true; symtab_init(build_options.symtab_size); } -void lexer_add_file_for_lexing(File *file) + +void lexer_add_file_for_lexing(Lexer *lexer, File *file) { lexer_check_init(); - lexer.current_file = file; - lexer.file_begin = lexer.current_file->contents; - lexer.lexing_start = lexer.file_begin; - lexer.current = lexer.lexing_start; - lexer.lexer_state = LEXER_STATE_NORMAL; + lexer->current_file = file; + lexer->file_begin = lexer->current_file->contents; + lexer->lexing_start = lexer->file_begin; + lexer->current = lexer->lexing_start; } -void lexer_test_setup(const char *text, size_t len) +void lexer_test_setup(Lexer *lexer, const char *text, size_t len) { lexer_check_init(); static File helper; - lexer.lexer_state = LEXER_STATE_NORMAL; - lexer.lexing_start = text; - lexer.current = text; - lexer.file_begin = text; - lexer.current_file = &helper; - lexer.current_file->start_id = 0; - lexer.current_file->contents = text; - lexer.current_file->end_id = len; - lexer.current_file->name = "Test"; + lexer->lexing_start = text; + lexer->current = text; + lexer->file_begin = text; + lexer->current_file = &helper; + lexer->current_file->start_id = 0; + lexer->current_file->contents = text; + lexer->current_file->end_id = len; + lexer->current_file->name = "Test"; } -Token lexer_scan_ident_test(const char *scan) +Token lexer_scan_ident_test(Lexer *lexer, const char *scan) { static File helper; - lexer.lexer_state = LEXER_STATE_NORMAL; - lexer.lexing_start = scan; - lexer.current = scan; - lexer.file_begin = scan; - lexer.current_file = &helper; - lexer.current_file->start_id = 0; - lexer.current_file->contents = scan; - lexer.current_file->end_id = 1000; - lexer.current_file->name = "Foo"; + lexer->lexing_start = scan; + lexer->current = scan; + lexer->file_begin = scan; + lexer->current_file = &helper; + lexer->current_file->start_id = 0; + lexer->current_file->contents = scan; + lexer->current_file->end_id = 1000; + lexer->current_file->name = "Foo"; - if (scan[0] == '@' && is_letter(scan[1])) - { - lexer.lexer_state = LEXER_STATE_DOCS_PARSE; - return scan_docs(); - } - return scan_ident(); + return scan_ident(lexer); } diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index f26f9dfce..688ede481 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -46,7 +46,7 @@ static void gencontext_emit_global_variable_definition(GenContext *context, Decl } // TODO fix name - decl->var.backend_ref = LLVMAddGlobal(context->module, decl->type->backend_type, decl->name.string); + decl->var.backend_ref = LLVMAddGlobal(context->module, decl->type->backend_type, decl->name); // If read only: LLVMSetGlobalConstant(decl->var.backend_ref, 1); @@ -70,8 +70,8 @@ static void gencontext_emit_global_variable_definition(GenContext *context, Decl { decl->var.backend_debug_ref = LLVMDIBuilderCreateGlobalVariableExpression(context->debug.builder, NULL /*scope*/, - decl->name.string, - decl->name.span.length, + decl->name, + source_range_len(decl->name_span), "linkagename", 2, context->debug.file, diff --git a/src/compiler/llvm_codegen_debug_info.c b/src/compiler/llvm_codegen_debug_info.c index d94f7b5f4..e030d3261 100644 --- a/src/compiler/llvm_codegen_debug_info.c +++ b/src/compiler/llvm_codegen_debug_info.c @@ -14,7 +14,6 @@ static inline LLVMMetadataRef gencontext_create_debug_type_from_decl(GenContext case DECL_ENUM_CONSTANT: case DECL_POISONED: case DECL_GENERIC: - case DECL_MULTI_DECL: case DECL_MACRO: case DECL_CT_IF: case DECL_CT_ELSE: diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 80aba5f2a..fb1b3941f 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -34,14 +34,16 @@ static inline LLVMValueRef gencontext_emit_sub_int(GenContext *context, Type *ty static inline LLVMValueRef gencontext_emit_subscript_addr(GenContext *context, Expr *expr) { LLVMValueRef index = gencontext_emit_expr(context, expr->subscript_expr.index); - switch (expr->subscript_expr.expr->type->canonical->type_kind) + Type *type = expr->subscript_expr.expr->type->canonical; + switch (type->type_kind) { case TYPE_ARRAY: TODO case TYPE_POINTER: - return LLVMBuildGEP(context->builder, - gencontext_emit_expr(context, expr->subscript_expr.expr), - &index, 1, "[]"); + return LLVMBuildGEP2(context->builder, + BACKEND_TYPE(type->pointer), + gencontext_emit_expr(context, expr->subscript_expr.expr), + &index, 1, "[]"); case TYPE_VARARRAY: case TYPE_SUBARRAY: case TYPE_STRING: @@ -54,7 +56,7 @@ static inline LLVMValueRef gencontext_emit_subscript_addr(GenContext *context, E static inline LLVMValueRef gencontext_emit_access_addr(GenContext *context, Expr *expr) { LLVMValueRef value = gencontext_emit_address(context, expr->access_expr.parent); - return LLVMBuildStructGEP(context->builder, value, (unsigned)expr->access_expr.index, ""); + return LLVMBuildStructGEP2(context->builder, BACKEND_TYPE(expr->access_expr.parent->type), value, (unsigned)expr->access_expr.index, ""); } LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr) @@ -216,7 +218,7 @@ LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr) case UNARYOP_ADDR: return gencontext_emit_address(context, expr->unary_expr.expr); case UNARYOP_DEREF: - return LLVMBuildLoad(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "deref"); + return LLVMBuildLoad2(context->builder, BACKEND_TYPE(expr->unary_expr.expr->type), gencontext_emit_expr(context, expr->unary_expr.expr), "deref"); case UNARYOP_INC: return gencontext_emit_pre_inc_dec(context, expr->unary_expr.expr, 1, false); case UNARYOP_DEC: @@ -514,7 +516,7 @@ LLVMValueRef gencontext_emit_ternary_expr(GenContext *context, Expr *expr) static LLVMValueRef gencontext_emit_identifier_expr(GenContext *context, Expr *expr) { return LLVMBuildLoad2(context->builder, expr->identifier_expr.decl->type->canonical->backend_type, - expr->identifier_expr.decl->var.backend_ref, expr->identifier_expr.decl->name.string); + expr->identifier_expr.decl->var.backend_ref, expr->identifier_expr.decl->name); } LLVMValueRef gencontext_emit_const_expr(GenContext *context, Expr *expr) @@ -575,7 +577,7 @@ static inline LLVMValueRef gencontext_emit_access_expr(GenContext *context, Expr { // Improve, add string description to the access? LLVMValueRef value = gencontext_emit_address(context, expr->access_expr.parent); - LLVMValueRef val = LLVMBuildStructGEP(context->builder, value, (unsigned)expr->access_expr.index, ""); + LLVMValueRef val = LLVMBuildStructGEP2(context->builder, BACKEND_TYPE(expr->access_expr.parent->type), value, (unsigned)expr->access_expr.index, ""); return LLVMBuildLoad2(context->builder, gencontext_get_llvm_type(context, expr->type), val, ""); } @@ -612,6 +614,13 @@ static inline LLVMValueRef gencontext_emit_struct_init_values_expr(GenContext *c TODO } +LLVMValueRef gencontext_emit_ast_expr(GenContext *context, Ast *expr) +{ + assert(expr->ast_kind == AST_EXPR_STMT); + LLVMValueRef value = gencontext_emit_expr(context, expr->expr_stmt); +// gencontext_emit_defer(context, expr); + return value; +} LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr) { diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index acd77f8a9..f9c7efceb 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -78,7 +78,7 @@ static inline void gencontext_emit_parameter(GenContext *context, Decl *decl, un assert(decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_PARAM); // Allocate room on stack and copy. - decl->var.backend_ref = gencontext_emit_alloca(context, BACKEND_TYPE(decl->type), decl->name.string); + decl->var.backend_ref = gencontext_emit_alloca(context, BACKEND_TYPE(decl->type), decl->name); LLVMBuildStore(context->builder, LLVMGetParam(context->function, index), decl->var.backend_ref); } @@ -109,7 +109,7 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl) VECEACH(decl->func.labels, i) { Ast *label = decl->func.labels[i]; - label->label_stmt.backend_value = gencontext_create_free_block(context, label->token.string); + label->label_stmt.backend_value = gencontext_create_free_block(context, label->label_stmt.name); } gencontext_emit_compound_stmt(context, decl->func.body); @@ -178,11 +178,11 @@ void gencontext_emit_function_decl(GenContext *context, Decl *decl) flags |= LLVMDIFlagPublic; break; } - SourcePosition decl_position = source_file_find_position(decl->name.span.loc); + SourcePosition decl_position = source_file_find_position(decl->name_span.loc); context->debug.function = LLVMDIBuilderCreateFunction(context->debug.builder, context->debug.compile_unit, - decl->name.string, decl->name.span.length, - decl->name.string, decl->name.span.length, + decl->name, source_range_len(decl->name_span), + decl->name, source_range_len(decl->name_span), context->debug.file, decl_position.line, decl->type->backend_debug_type, @@ -228,7 +228,6 @@ void gencontext_emit_extern_decl(GenContext *context, Decl *decl) case DECL_ARRAY_VALUE: case DECL_IMPORT: case DECL_MACRO: - case DECL_MULTI_DECL: case DECL_GENERIC: case DECL_CT_IF: case DECL_CT_ELSE: diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 28dab769d..c57a43be8 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -72,7 +72,9 @@ typedef struct void gencontext_begin_module(GenContext *context); void gencontext_end_module(GenContext *context); void gencontext_emit_stmt(GenContext *context, Ast *ast); +void gencontext_emit_defer(GenContext *context, DeferList defer_list); LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr); +LLVMValueRef gencontext_emit_ast_expr(GenContext *context, Ast *expr); LLVMMetadataRef gencontext_get_debug_type(GenContext *context, Type *type); void gencontext_emit_debug_location(GenContext *context, SourceRange location); LLVMMetadataRef gencontext_create_builtin_debug_type(GenContext *context, Type *builtin_type); diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 8c5f58b89..b35ced45b 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -22,7 +22,7 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast) { Decl *decl = ast->declare_stmt; - decl->var.backend_ref = gencontext_emit_alloca(context, BACKEND_TYPE(decl->type), decl->name.string); + decl->var.backend_ref = gencontext_emit_alloca(context, BACKEND_TYPE(decl->type), decl->name); // TODO NRVO // TODO debug info /* @@ -99,6 +99,13 @@ static inline void gencontext_emit_return(GenContext *context, Ast *ast) { // Ensure we are on a branch that is non empty. if (!gencontext_check_block_branch_emit(context)) return; + Ast *defer = ast->return_stmt.defer; + while (defer) + { + gencontext_emit_stmt(context, defer->defer_stmt.body); + // TODO boolean + defer = defer->defer_stmt.prev_defer; + } if (!ast->return_stmt.expr) { LLVMBuildRetVoid(context->builder); @@ -109,7 +116,6 @@ static inline void gencontext_emit_return(GenContext *context, Ast *ast) context->current_block = NULL; LLVMBasicBlockRef post_ret_block = gencontext_create_free_block(context, "ret"); gencontext_emit_block(context, post_ret_block); - } @@ -219,6 +225,7 @@ void gencontext_emit_for_stmt(GenContext *context, Ast *ast) gencontext_emit_br(context, cond_block); gencontext_emit_block(context, cond_block); value = gencontext_emit_expr(context, ast->for_stmt.cond); + gencontext_emit_defer(context, ast->for_stmt.cond_defer); // If we have a body, conditionally jump to it. if (body_block) { @@ -251,7 +258,7 @@ void gencontext_emit_for_stmt(GenContext *context, Ast *ast) { // Emit the block gencontext_emit_block(context, inc_block); - gencontext_emit_expr(context, ast->for_stmt.incr); + gencontext_emit_expr(context, ast->for_stmt.incr->expr_stmt); } // Loop back. @@ -451,11 +458,23 @@ void gencontext_emit_switch(GenContext *context, Ast *ast) gencontext_emit_block(context, exit_block); } +void gencontext_emit_defer(GenContext *context, DeferList defer_list) +{ + if (defer_list.start == defer_list.end) return; + Ast *defer = context->ast_context->defers[defer_list.start - 1]; + while (defer && defer->defer_stmt.id != defer_list.end) + { + gencontext_emit_stmt(context, defer->defer_stmt.body); + // TODO boolean + } +} void gencontext_emit_stmt(GenContext *context, Ast *ast) { switch (ast->ast_kind) { + case AST_FUNCTION_BLOCK_STMT: + TODO case AST_POISONED: UNREACHABLE case AST_EXPR_STMT: @@ -495,9 +514,9 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast) gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].next_block); break; case AST_NOP_STMT: + case AST_DEFER_STMT: break; case AST_CATCH_STMT: - case AST_DEFER_STMT: case AST_TRY_STMT: case AST_THROW_STMT: // Should have been lowered. diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 34e11c1aa..17aab9c5e 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -15,7 +15,6 @@ static inline LLVMTypeRef gencontext_create_llvm_type_from_decl(GenContext *cont case DECL_ENUM_CONSTANT: case DECL_POISONED: case DECL_GENERIC: - case DECL_MULTI_DECL: case DECL_MACRO: case DECL_CT_IF: case DECL_CT_ELSE: diff --git a/src/compiler/parser.c b/src/compiler/parser.c index a75ddd309..9bf22335a 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -7,16 +7,16 @@ const int MAX_DOCS_ROWS = 1024; Token module = { .type = TOKEN_INVALID_TOKEN }; -static Ast *parse_stmt(void); -static Expr *parse_expr(void); -static Expr *parse_paren_expr(void); -static Expr *parse_precedence(Precedence precedence); -static Expr *parse_initializer_list(void); -static Expr *parse_initializer(void); -static bool parse_type_or_expr(Expr **exprPtr, TypeInfo **typePtr); -static Decl *parse_top_level(void); +static Ast *parse_stmt(Context *context); +static Expr *parse_expr(Context *context); +static Expr *parse_paren_expr(Context *context); +static Expr *parse_precedence(Context *context, Precedence precedence); +static Expr *parse_initializer_list(Context *context); +static Expr *parse_initializer(Context *context); +static bool parse_type_or_expr(Context *context, Expr **exprPtr, TypeInfo **typePtr); +static Decl *parse_top_level(Context *context); -typedef Expr *(*ParseFn)(Expr *); +typedef Expr *(*ParseFn)(Context *context, Expr *); typedef struct { @@ -27,13 +27,91 @@ typedef struct extern ParseRule rules[TOKEN_EOF + 1]; -static inline Expr *parse_precedence_with_left_side(Expr *left_side, Precedence precedence) +void context_store_lexer_state(Context *context) { - while (precedence <= rules[tok.type].precedence) + context->stored.current = context->lexer.current; + context->stored.start = context->lexer.lexing_start; + context->stored.tok = context->tok; + context->stored.next_tok = context->next_tok; +} + +void context_restore_lexer_state(Context *context) +{ + context->lexer.current = context->stored.current; + context->lexer.lexing_start = context->stored.start; + context->tok = context->stored.tok; + context->next_tok = context->stored.next_tok; +} + +inline void advance(Context *context) +{ + context->lead_comment = context->next_lead_comment; + context->trailing_comment = NULL; + context->next_lead_comment = NULL; + context->prev_tok_end = context->tok.span.end_loc; + context->tok = context->next_tok; + + while(1) + { + context->next_tok = lexer_scan_token(&context->lexer); + + if (context->next_tok.type == TOKEN_INVALID_TOKEN) continue; + + // Walk through any regular comments + if (context->next_tok.type == TOKEN_COMMENT) + { + vec_add(context->comments, context->next_tok); + continue; + } + if (context->next_tok.type == TOKEN_DOC_COMMENT) + { + SourcePosition current_position = source_file_find_position_in_file(context->file, + context->tok.span.end_loc); + SourcePosition doc_position = source_file_find_position_in_file(context->file, context->next_tok.span.loc); + vec_add(context->comments, context->next_tok); + + if (current_position.line == doc_position.line) + { + if (context->trailing_comment) + { + sema_error_range(context->next_tok.span, "You have multiple trailing doc-style comments, should the second one go on the next line?"); + } + else + { + context->trailing_comment = context->comments + vec_size(context->comments) - 1; + } + } + else + { + if (context->lead_comment) + { + sema_error_range(context->next_tok.span, "You have multiple doc-style comments in a row, are all of them really meant to document the code that follows?"); + } + else + { + context->lead_comment = context->comments + vec_size(context->comments) - 1; + } + } + continue; + } + break; + } + +} + +inline void advance_and_verify(Context *context, TokenType token_type) +{ + assert(context->tok.type == token_type); + advance(context); +} + +static inline Expr *parse_precedence_with_left_side(Context *context, Expr *left_side, Precedence precedence) +{ + while (precedence <= rules[context->tok.type].precedence) { if (!expr_ok(left_side)) return left_side; - ParseFn infix_rule = rules[tok.type].infix; - left_side = infix_rule(left_side); + ParseFn infix_rule = rules[context->tok.type].infix; + left_side = infix_rule(context, left_side); } return left_side; } @@ -41,94 +119,94 @@ static inline Expr *parse_precedence_with_left_side(Expr *left_side, Precedence // --- Parser base methods -bool try_consume(TokenType type) +bool try_consume(Context *context, TokenType type) { - if (tok.type == type) + if (context->tok.type == type) { - advance(); + advance(context); return true; } return false; } -bool consume(TokenType type, const char *message, ...) +bool consume(Context *context, TokenType type, const char *message, ...) { - if (try_consume(type)) + if (try_consume(context, type)) { return true; } va_list args; va_start(args, message); - sema_verror_range(tok.span, message, args); + sema_verror_range(context->tok.span, message, args); va_end(args); return false; } -static inline bool consume_ident(const char* name) +static inline bool consume_ident(Context *context, const char* name) { - if (try_consume(TOKEN_IDENT)) return true; - if (tok.type == TOKEN_TYPE_IDENT || tok.type == TOKEN_CONST_IDENT) + if (try_consume(context, TOKEN_IDENT)) return true; + if (context->tok.type == TOKEN_TYPE_IDENT || context->tok.type == TOKEN_CONST_IDENT) { - SEMA_ERROR(tok, "A %s cannot start with a capital letter.", name); + SEMA_TOKEN_ERROR(context->tok, "A %s cannot start with a capital letter.", name); return false; } - SEMA_ERROR(tok, "A %s was expected.", name); + SEMA_TOKEN_ERROR(context->tok, "A %s was expected.", name); return false; } -static inline bool expect_ident(const char* name) +static inline bool expect_ident(Context *context, const char* name) { - switch (tok.type) + switch (context->tok.type) { case TOKEN_IDENT: return true; case TOKEN_TYPE_IDENT: case TOKEN_CONST_IDENT: - SEMA_ERROR(tok, "A %s cannot start with a capital letter.", name); + SEMA_TOKEN_ERROR(context->tok, "A %s cannot start with a capital letter.", name); return false; default: - SEMA_ERROR(tok, "A %s was expected.", name); + SEMA_TOKEN_ERROR(context->tok, "A %s was expected.", name); return false; } } -static bool consume_type_name(const char* type) +static bool consume_type_name(Context *context, const char* type) { - if (tok.type == TOKEN_IDENT) + if (context->tok.type == TOKEN_IDENT) { - SEMA_ERROR(tok, "Names of %ss must start with an upper case letter.", type); + SEMA_TOKEN_ERROR(context->tok, "Names of %ss must start with an upper case letter.", type); return false; } - if (tok.type == TOKEN_CONST_IDENT) + if (context->tok.type == TOKEN_CONST_IDENT) { - SEMA_ERROR(tok, "Names of %ss cannot be all upper case.", type); + SEMA_TOKEN_ERROR(context->tok, "Names of %ss cannot be all upper case.", type); return false; } - if (!consume(TOKEN_TYPE_IDENT, "'%s' should be followed by the name of the %s.", type, type)) return false; + if (!consume(context, TOKEN_TYPE_IDENT, "'%s' should be followed by the name of the %s.", type, type)) return false; return true; } -static bool consume_const_name(const char* type) +static bool consume_const_name(Context *context, const char* type) { - if (tok.type == TOKEN_IDENT || tok.type == TOKEN_TYPE_IDENT) + if (context->tok.type == TOKEN_IDENT || context->tok.type == TOKEN_TYPE_IDENT) { - SEMA_ERROR(tok, "Names of %ss must be all upper case.", type); + SEMA_TOKEN_ERROR(context->tok, "Names of %ss must be all upper case.", type); return false; } - if (!consume(TOKEN_CONST_IDENT, "'%s' should be followed by the name of the %s.", type, type)) return false; + if (!consume(context, TOKEN_CONST_IDENT, "'%s' should be followed by the name of the %s.", type, type)) return false; return true; } /** * Walk until we find the first top level construct. * (Note that this is the slow path, so no need to inline) */ -static void recover_top_level(void) +static void recover_top_level(Context *context) { - advance(); - while (tok.type != TOKEN_EOF) + advance(context); + while (context->tok.type != TOKEN_EOF) { - switch (tok.type) + switch (context->tok.type) { case TOKEN_PUBLIC: case TOKEN_FUNC: @@ -143,39 +221,40 @@ static void recover_top_level(void) case TOKEN_EXTERN: return; default: - advance(); + advance(context); break; } } } -static inline bool expect(TokenType token_type) +static inline bool expect(Context *context, TokenType token_type) { - if (token_type == tok.type) return true; + if (token_type == context->tok.type) return true; - SEMA_ERROR(tok, "Expected '%s'.", token_type_to_string(token_type)); + SEMA_TOKEN_ERROR(context->tok, "Expected '%s'.", token_type_to_string(token_type)); return false; } -void error_at_current(const char* message, ...) +void error_at_current(Context *context, const char* message, ...) { va_list args; va_start(args, message); - sema_verror_range(next_tok.span, message, args); + sema_verror_range(context->next_tok.span, message, args); va_end(args); } // --- Parsing -#define EXPECT_IDENT_FOR_OR(_name, _res) do { if (!expect_ident(_name)) return _res; } while(0) -#define EXPECT_OR(_tok, _res) do { if (!expect(_tok)) return _res; } while(0) -#define CONSUME_OR(_tok, _res) do { if (!expect(_tok)) return _res; advance(); } while(0) -#define TRY_EXPECT_OR(_tok, _message, _type) do { if (tok.type != _tok) { SEMA_ERROR(tok, _message); return _type; } } while(0) -#define TRY_CONSUME_OR(_tok, _message, _type) do { if (!consume(_tok, _message)) return _type; } while(0) +#define EXPECT_IDENT_FOR_OR(_name, _res) do { if (!expect_ident(context, _name)) return _res; } while(0) +#define EXPECT_OR(_tok, _res) do { if (!expect(context, _tok)) return _res; } while(0) +#define CONSUME_OR(_tok, _res) do { if (!expect(context, _tok)) return _res; advance(context); } while(0) +#define TRY_EXPECT_OR(_tok, _message, _type) do { if (context->tok.type != _tok) { SEMA_TOKEN_ERROR(context->tok, _message); return _type; } } while(0) +#define TRY_CONSUME_OR(_tok, _message, _type) do { if (!consume(context, _tok, _message)) return _type; } while(0) #define TRY_CONSUME(_tok, _message) TRY_CONSUME_OR(_tok, _message, &poisoned_ast) #define TRY_CONSUME_EOS_OR(_res) TRY_CONSUME_OR(TOKEN_EOS, "Expected ';'", _res) #define TRY_CONSUME_EOS() TRY_CONSUME_EOS_OR(&poisoned_ast) +#define RETURN_AFTER_EOS(_ast) extend_ast_with_prev_token(context, ast); TRY_CONSUME_EOS_OR(&poisoned_ast); return _ast #define TRY_CONSUME_LBRACE() TRY_CONSUME(TOKEN_LBRACE, "Expected '{'") #define TRY_AST_OR(_ast_stmt, _res) ({ Ast* _ast = (_ast_stmt); if (!ast_ok(_ast)) return _res; _ast; }) @@ -185,92 +264,96 @@ void error_at_current(const char* message, ...) #define TRY_DECL_OR(_decl_stmt, _res) ({ Decl* _decl = (_decl_stmt); if (!decl_ok(_decl)) return _res; _decl; }) #define COMMA_RPAREN_OR(_res) \ -do { if (!try_consume(TOKEN_COMMA) && tok.type != TOKEN_RPAREN) { \ -SEMA_ERROR(tok, "Expected ',' or ')'"); return _res; } } while(0) +do { if (!try_consume(context, TOKEN_COMMA) && context->tok.type != TOKEN_RPAREN) { \ +SEMA_TOKEN_ERROR(context->tok, "Expected ',' or ')'"); return _res; } } while(0) -static inline Path *parse_module_path() +static inline Path *parse_module_path(Context *context) { - assert(tok.type == TOKEN_IDENT); - char *scratch_ptr = current_context->path_scratch; + assert(context->tok.type == TOKEN_IDENT); + char *scratch_ptr = context->path_scratch; size_t offset = 0; - SourceRange span = tok.span; - memcpy(scratch_ptr, tok.start, tok.span.length); - offset += tok.span.length; + SourceRange span = context->tok.span; + unsigned len = context->tok.span.end_loc - context->tok.span.loc; + memcpy(scratch_ptr, context->tok.start, len); + offset += len; SourceRange last_range; while (1) { - last_range = tok.span; - if (!try_consume(TOKEN_IDENT)) + last_range = context->tok.span; + if (!try_consume(context, TOKEN_IDENT)) { - SEMA_ERROR(tok, "Each '::' must be followed by a regular lower case sub module name."); + SEMA_TOKEN_ERROR(context->tok, "Each '::' must be followed by a regular lower case sub module name."); return NULL; } - if (!try_consume(TOKEN_SCOPE)) + if (!try_consume(context, TOKEN_SCOPE)) { span = source_range_from_ranges(span, last_range); break; } scratch_ptr[offset++] = ':'; scratch_ptr[offset++] = ':'; - memcpy(scratch_ptr + offset, tok.start, tok.span.length); - offset += tok.span.length; + len = context->tok.span.end_loc - context->tok.span.loc; + memcpy(scratch_ptr + offset, context->tok.start, len); + offset += len; } scratch_ptr[offset] = '\0'; return path_create_from_string(scratch_ptr, offset, span); } -static Ast* parse_compound_stmt() +static Ast* parse_compound_stmt(Context *context) { CONSUME_OR(TOKEN_LBRACE, &poisoned_ast); - Ast *ast = AST_NEW(AST_COMPOUND_STMT, tok); - while (!try_consume(TOKEN_RBRACE)) + Ast *ast = AST_NEW_TOKEN(AST_COMPOUND_STMT, context->tok); + while (!try_consume(context, TOKEN_RBRACE)) { - Ast *stmt = TRY_AST(parse_stmt()); + Ast *stmt = TRY_AST(parse_stmt(context)); ast->compound_stmt.stmts = VECADD(ast->compound_stmt.stmts, stmt); } return ast; } -static Ast* parse_function_block() +static Ast* parse_function_block(Context *context) { TODO; CONSUME_OR(TOKEN_LPARBRA, &poisoned_ast); - Ast *ast = AST_NEW(AST_COMPOUND_STMT, tok); - while (!try_consume(TOKEN_RPARBRA)) + Ast *ast = AST_NEW_TOKEN(AST_FUNCTION_BLOCK_STMT, context->tok); + while (!try_consume(context, TOKEN_RPARBRA)) { - Ast *stmt = TRY_AST(parse_stmt()); - ast->compound_stmt.stmts = VECADD(ast->compound_stmt.stmts, stmt); + Ast *stmt = TRY_AST(parse_stmt(context)); + ast->function_block_stmt.stmts = VECADD(ast->function_block_stmt.stmts, stmt); } return ast; } -static Path *parse_path_prefix() +static Path *parse_path_prefix(Context *context) { - if (tok.type != TOKEN_IDENT || next_tok.type != TOKEN_SCOPE) return NULL; + if (context->tok.type != TOKEN_IDENT || context->next_tok.type != TOKEN_SCOPE) return NULL; - char *scratch_ptr = current_context->path_scratch; + char *scratch_ptr = context->path_scratch; size_t offset = 0; Path *path = CALLOCS(Path); - path->span = tok.span; - memcpy(scratch_ptr, tok.start, tok.span.length); - offset += tok.span.length; - SourceRange last_range = tok.span; - advance(); - advance(); - while (tok.type == TOKEN_IDENT && next_tok.type == TOKEN_SCOPE) + path->span = context->tok.span; + unsigned len = context->tok.span.end_loc - context->tok.span.loc; + memcpy(scratch_ptr, context->tok.start, len); + offset += len; + SourceRange last_range = context->tok.span; + advance(context); + advance(context); + while (context->tok.type == TOKEN_IDENT && context->next_tok.type == TOKEN_SCOPE) { - last_range = tok.span; - advance(); - advance(); + last_range = context->tok.span; + advance(context); + advance(context); scratch_ptr[offset++] = ':'; scratch_ptr[offset++] = ':'; - memcpy(scratch_ptr + offset, tok.start, tok.span.length); - offset += tok.span.length; + len = context->tok.span.end_loc - context->tok.span.loc; + memcpy(scratch_ptr + offset, context->tok.start, len); + offset += len; } TokenType type = TOKEN_IDENT; @@ -309,32 +392,32 @@ static Path *parse_path_prefix() * Assume prev_token is the type. * @return Type (poisoned if fails) */ -static inline TypeInfo *parse_base_type(void) +static inline TypeInfo *parse_base_type(Context *context) { - Path *path = parse_path_prefix(); + Path *path = parse_path_prefix(context); if (path) { TypeInfo *type_info = type_info_new(TYPE_INFO_IDENTIFIER); type_info->unresolved.path = path; - type_info->unresolved.name_loc = tok; - if (!consume_type_name("types")) return &poisoned_type_info; + type_info->unresolved.name_loc = context->tok; + if (!consume_type_name(context, "types")) return &poisoned_type_info; return type_info; } TypeInfo *type_info = NULL; Type *type_found = NULL; - switch (tok.type) + switch (context->tok.type) { case TOKEN_TYPE_IDENT: type_info = type_info_new(TYPE_INFO_IDENTIFIER); - type_info->unresolved.name_loc = tok; + type_info->unresolved.name_loc = context->tok; break; case TOKEN_TYPE: type_info = type_info_new(TYPE_INFO_IDENTIFIER); - advance_and_verify(TOKEN_TYPE); + advance_and_verify(context, TOKEN_TYPE); CONSUME_OR(TOKEN_LPAREN, &poisoned_type_info); type_info->resolve_status = RESOLVE_NOT_DONE; - type_info->unresolved_type_expr = TRY_EXPR_OR(parse_initializer(), &poisoned_type_info); + type_info->unresolved_type_expr = TRY_EXPR_OR(parse_initializer(context), &poisoned_type_info); EXPECT_OR(TOKEN_RPAREN, &poisoned_type_info); break; case TOKEN_VOID: @@ -404,10 +487,10 @@ static inline TypeInfo *parse_base_type(void) type_found = type_c_ulonglong; break; default: - SEMA_ERROR(tok, "A type name was expected here."); + SEMA_TOKEN_ERROR(context->tok, "A type name was expected here."); return &poisoned_type_info; } - advance(); + advance(context); if (type_found) { assert(!type_info); @@ -428,19 +511,19 @@ static inline TypeInfo *parse_base_type(void) * @param type the type to wrap, may not be poisoned. * @return type (poisoned if fails) */ -static inline TypeInfo *parse_array_type_index(TypeInfo *type) +static inline TypeInfo *parse_array_type_index(Context *context, TypeInfo *type) { assert(type_info_ok(type)); - advance_and_verify(TOKEN_LBRACKET); - if (try_consume(TOKEN_PLUS)) + advance_and_verify(context, TOKEN_LBRACKET); + if (try_consume(context, TOKEN_PLUS)) { CONSUME_OR(TOKEN_RBRACKET, &poisoned_type_info); TypeInfo *incr_array = type_info_new(TYPE_INFO_INC_ARRAY); incr_array->array.base = type; return incr_array; } - if (try_consume(TOKEN_RBRACKET)) + if (try_consume(context, TOKEN_RBRACKET)) { TypeInfo *array = type_info_new(TYPE_INFO_ARRAY); array->array.base = type; @@ -449,7 +532,7 @@ static inline TypeInfo *parse_array_type_index(TypeInfo *type) } TypeInfo *array = type_info_new(TYPE_INFO_ARRAY); array->array.base = type; - array->array.len = TRY_EXPR_OR(parse_expr(), &poisoned_type_info); + array->array.len = TRY_EXPR_OR(parse_expr(context), &poisoned_type_info); CONSUME_OR(TOKEN_RBRACKET, &poisoned_type_info); return array; } @@ -464,18 +547,18 @@ static inline TypeInfo *parse_array_type_index(TypeInfo *type) * Assume already stepped into. * @return Type, poisoned if parsing is invalid. */ -static TypeInfo *parse_type_expression(void) +static TypeInfo *parse_type_expression(Context *context) { - TypeInfo *type_info = parse_base_type(); + TypeInfo *type_info = parse_base_type(context); while (type_info_ok(type_info)) { - switch (tok.type) + switch (context->tok.type) { case TOKEN_LBRACKET: - type_info = parse_array_type_index(type_info); + type_info = parse_array_type_index(context, type_info); break; case TOKEN_STAR: - advance(); + advance(context); { TypeInfo *ptr_type = type_info_new(TYPE_INFO_POINTER); assert(type_info); @@ -496,30 +579,30 @@ static TypeInfo *parse_type_expression(void) * @param type * @return */ -static inline Decl *parse_decl_after_type(bool local, TypeInfo *type) +static inline Decl *parse_decl_after_type(Context *context, bool local, TypeInfo *type) { - if (tok.type == TOKEN_LPAREN) + if (context->tok.type == TOKEN_LPAREN) { - SEMA_ERROR(tok, "Expected '{'."); + SEMA_TOKEN_ERROR(context->tok, "Expected '{'."); return &poisoned_decl; } EXPECT_IDENT_FOR_OR("variable_name", &poisoned_decl); - Token name = tok; - advance(); + Token name = context->tok; + advance(context); Visibility visibility = local ? VISIBLE_LOCAL : VISIBLE_MODULE; Decl *decl = decl_new_var(name, type, VARDECL_LOCAL, visibility); - if (tok.type == TOKEN_EQ) + if (context->tok.type == TOKEN_EQ) { if (!decl) { - SEMA_ERROR(tok, "Expected an identifier before '='."); + SEMA_TOKEN_ERROR(context->tok, "Expected an identifier before '='."); return &poisoned_decl; } - advance_and_verify(TOKEN_EQ); - decl->var.init_expr = TRY_EXPR_OR(parse_initializer(), &poisoned_decl); + advance_and_verify(context, TOKEN_EQ); + decl->var.init_expr = TRY_EXPR_OR(parse_initializer(context), &poisoned_decl); } return decl; @@ -530,16 +613,16 @@ static inline Decl *parse_decl_after_type(bool local, TypeInfo *type) * * @return Decl* (poisoned on error) */ -static Decl *parse_decl(void) +static Decl *parse_decl(Context *context) { - bool local = tok.type == TOKEN_LOCAL; - bool constant = tok.type == TOKEN_CONST; - if (local || constant) advance(); + bool local = context->tok.type == TOKEN_LOCAL; + bool constant = context->tok.type == TOKEN_CONST; + if (local || constant) advance(context); - TypeInfo *type_info = parse_type_expression(); + TypeInfo *type_info = parse_type_expression(context); TypeInfo *type = TRY_TYPE_OR(type_info, &poisoned_decl); - Decl *decl = TRY_DECL_OR(parse_decl_after_type(local, type), &poisoned_decl); + Decl *decl = TRY_DECL_OR(parse_decl_after_type(context, local, type), &poisoned_decl); if (constant) decl->var.kind = VARDECL_CONST; @@ -553,10 +636,10 @@ static Decl *parse_decl(void) * * @return Ast* (poisoned if parsing fails) */ -static Ast *parse_declaration_stmt(void) +static Ast *parse_declaration_stmt(Context *context) { - Ast *decl_stmt = AST_NEW(AST_DECLARE_STMT, tok); - decl_stmt->declare_stmt = TRY_DECL_OR(parse_decl(), &poisoned_ast); + Ast *decl_stmt = AST_NEW_TOKEN(AST_DECLARE_STMT, context->tok); + decl_stmt->declare_stmt = TRY_DECL_OR(parse_decl(context), &poisoned_ast); CONSUME_OR(TOKEN_EOS, &poisoned_ast); return decl_stmt; } @@ -575,10 +658,10 @@ typedef enum * expr_stmt ::= expression EOS * @return Ast* poisoned if expression fails to parse. */ -static Ast *parse_expr_stmt(void) +static Ast *parse_expr_stmt(Context *context) { - Ast *stmt = AST_NEW(AST_EXPR_STMT, tok); - stmt->expr_stmt = TRY_EXPR_OR(parse_expr(), &poisoned_ast); + Ast *stmt = AST_NEW_TOKEN(AST_EXPR_STMT, context->tok); + stmt->expr_stmt = TRY_EXPR_OR(parse_expr(context), &poisoned_ast); TRY_CONSUME_EOS(); return stmt; } @@ -591,14 +674,14 @@ static Ast *parse_expr_stmt(void) * ; * @return Ast * */ -static inline Expr *parse_expression_list(void) +static inline Expr *parse_expression_list(Context *context) { - Expr *expr_list = expr_new(EXPR_EXPRESSION_LIST, tok); + Expr *expr_list = EXPR_NEW_TOKEN(EXPR_EXPRESSION_LIST, context->tok); do { - Expr *expr = TRY_EXPR_OR(parse_expr(), &poisoned_expr); + Expr *expr = TRY_EXPR_OR(parse_expr(context), &poisoned_expr); vec_add(expr_list->expression_list, expr); - } while (try_consume(TOKEN_COMMA)); + } while (try_consume(context, TOKEN_COMMA)); return expr_list; } @@ -612,31 +695,31 @@ static inline Expr *parse_expression_list(void) * * @return bool */ -static inline Ast *parse_decl_expr_list(void) +static inline Ast *parse_decl_expr_list(Context *context) { - Ast *decl_expr = new_ast(AST_DECL_EXPR_LIST, tok); + Ast *decl_expr = AST_NEW_TOKEN(AST_DECL_EXPR_LIST, context->tok); decl_expr->decl_expr_stmt = NULL; while (1) { Expr *expr = NULL; TypeInfo *type = NULL; - if (!parse_type_or_expr(&expr, &type)) return false; + if (!parse_type_or_expr(context, &expr, &type)) return false; if (expr) { - Ast *stmt = new_ast(AST_EXPR_STMT, expr->loc); + Ast *stmt = AST_NEW(AST_EXPR_STMT, expr->span); stmt->expr_stmt = expr; vec_add(decl_expr->decl_expr_stmt, stmt); } else { - Decl *decl = TRY_DECL_OR(parse_decl_after_type(false, type), &poisoned_ast); - Ast *stmt = new_ast(AST_DECLARE_STMT, decl->name); + Decl *decl = TRY_DECL_OR(parse_decl_after_type(context, false, type), &poisoned_ast); + Ast *stmt = AST_NEW(AST_DECLARE_STMT, decl->span); stmt->declare_stmt = decl; vec_add(decl_expr->decl_expr_stmt, stmt); } - if (!try_consume(TOKEN_COMMA)) break; + if (!try_consume(context, TOKEN_COMMA)) break; } - return decl_expr; + return extend_ast_with_prev_token(context, decl_expr); } /** @@ -647,11 +730,11 @@ static inline Ast *parse_decl_expr_list(void) * * @return true if it succeeds */ -static inline bool parse_control_expression(Ast **decls, Ast **exprs) +static inline bool parse_control_expression(Context *context, Ast **decls, Ast **exprs) { - *exprs = TRY_AST_OR(parse_decl_expr_list(), false); + *exprs = TRY_AST_OR(parse_decl_expr_list(context), false); - if (!try_consume(TOKEN_EOS)) + if (!try_consume(context, TOKEN_EOS)) { *decls = NULL; return true; @@ -659,7 +742,7 @@ static inline bool parse_control_expression(Ast **decls, Ast **exprs) *decls = *exprs; - *exprs = TRY_AST_OR(parse_decl_expr_list(), false); + *exprs = TRY_AST_OR(parse_decl_expr_list(context), false); return true; } @@ -672,38 +755,33 @@ static inline bool parse_control_expression(Ast **decls, Ast **exprs) * * @return */ -static inline Ast* parse_if_stmt(void) +static inline Ast* parse_if_stmt(Context *context) { - Ast *if_ast = AST_NEW(AST_IF_STMT, tok); - advance_and_verify(TOKEN_IF); + Ast *if_ast = AST_NEW_TOKEN(AST_IF_STMT, context->tok); + advance_and_verify(context, TOKEN_IF); CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); - if (!parse_control_expression(&if_ast->if_stmt.decl, &if_ast->if_stmt.cond)) return &poisoned_ast; + if (!parse_control_expression(context, &if_ast->if_stmt.decl, &if_ast->if_stmt.cond)) return &poisoned_ast; CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); - Ast *stmt = TRY_AST(parse_stmt()); + Ast *stmt = TRY_AST(parse_stmt(context)); if_ast->if_stmt.then_body = stmt; - if (stmt->ast_kind != AST_COMPOUND_STMT || tok.type != TOKEN_ELSE) + if (!try_consume(context, TOKEN_ELSE)) { return if_ast; } - advance_and_verify(TOKEN_ELSE); - if (tok.type != TOKEN_LBRACE) - { - SEMA_ERROR(tok, "'{' was expected after 'else'."); - return &poisoned_ast; - } - if_ast->if_stmt.else_body = TRY_AST(parse_stmt()); + advance_and_verify(context, TOKEN_ELSE); + if_ast->if_stmt.else_body = TRY_AST(parse_stmt(context)); return if_ast; } -static inline Ast* parse_while_stmt(void) +static inline Ast* parse_while_stmt(Context *context) { - Ast *while_ast = AST_NEW(AST_WHILE_STMT, tok); + Ast *while_ast = AST_NEW_TOKEN(AST_WHILE_STMT, context->tok); - advance_and_verify(TOKEN_WHILE); + advance_and_verify(context, TOKEN_WHILE); CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); - if (!parse_control_expression(&while_ast->while_stmt.decl, &while_ast->while_stmt.cond)) return &poisoned_ast; + if (!parse_control_expression(context, &while_ast->while_stmt.decl, &while_ast->while_stmt.cond)) return &poisoned_ast; CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); - while_ast->while_stmt.body = TRY_AST(parse_stmt()); + while_ast->while_stmt.body = TRY_AST(parse_stmt(context)); return while_ast; } @@ -714,11 +792,11 @@ static inline Ast* parse_while_stmt(void) * ; * @return */ -static inline Ast* parse_defer_stmt(void) +static inline Ast* parse_defer_stmt(Context *context) { - Ast *defer_stmt = AST_NEW(AST_DEFER_STMT, tok); - advance_and_verify(TOKEN_DEFER); - defer_stmt->defer_stmt.body = TRY_AST(parse_stmt()); + Ast *defer_stmt = AST_NEW_TOKEN(AST_DEFER_STMT, context->tok); + advance_and_verify(context, TOKEN_DEFER); + defer_stmt->defer_stmt.body = TRY_AST(parse_stmt(context)); return defer_stmt; } @@ -730,28 +808,28 @@ static inline Ast* parse_defer_stmt(void) * * @return Ast* */ -static inline Ast* parse_catch_stmt(void) +static inline Ast* parse_catch_stmt(Context *context) { - Ast *catch_stmt = AST_NEW(AST_CATCH_STMT, tok); - advance_and_verify(TOKEN_CATCH); + Ast *catch_stmt = AST_NEW_TOKEN(AST_CATCH_STMT, context->tok); + advance_and_verify(context, TOKEN_CATCH); CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); TypeInfo *type = NULL; - if (!try_consume(TOKEN_ERROR_TYPE)) + if (!try_consume(context, TOKEN_ERROR_TYPE)) { - type = TRY_TYPE_OR(parse_type_expression(), &poisoned_ast); + type = TRY_TYPE_OR(parse_type_expression(context), &poisoned_ast); } EXPECT_IDENT_FOR_OR("error parameter", &poisoned_ast); - Decl *decl = decl_new_var(tok, type, VARDECL_PARAM, VISIBLE_LOCAL); + Decl *decl = decl_new_var(context->tok, type, VARDECL_PARAM, VISIBLE_LOCAL); catch_stmt->catch_stmt.error_param = decl; CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); - catch_stmt->catch_stmt.body = TRY_AST(parse_stmt()); + catch_stmt->catch_stmt.body = TRY_AST(parse_stmt(context)); return catch_stmt; } -static inline Ast* parse_asm_stmt(void) +static inline Ast* parse_asm_stmt(Context *context) { TODO } @@ -760,18 +838,18 @@ static inline Ast* parse_asm_stmt(void) * do_stmt * : DO statement WHILE '(' expression ')' ';' */ -static inline Ast* parse_do_stmt(void) +static inline Ast* parse_do_stmt(Context *context) { - Ast *do_ast = AST_NEW(AST_DO_STMT, tok); + Ast *do_ast = AST_NEW_TOKEN(AST_DO_STMT, context->tok); - advance_and_verify(TOKEN_DO); + advance_and_verify(context, TOKEN_DO); - do_ast->do_stmt.body = TRY_AST(parse_stmt()); + do_ast->do_stmt.body = TRY_AST(parse_stmt(context)); CONSUME_OR(TOKEN_WHILE, &poisoned_ast); CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); - do_ast->do_stmt.expr = TRY_EXPR_OR(parse_expr(), &poisoned_ast); + do_ast->do_stmt.expr = TRY_EXPR_OR(parse_expr(context), &poisoned_ast); CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); CONSUME_OR(TOKEN_EOS, &poisoned_ast); @@ -785,14 +863,14 @@ static inline Ast* parse_do_stmt(void) * * @return */ -static inline Ast* parse_switch_stmt(void) +static inline Ast* parse_switch_stmt(Context *context) { - Ast *switch_ast = AST_NEW(AST_SWITCH_STMT, tok); - advance_and_verify(TOKEN_SWITCH); + Ast *switch_ast = AST_NEW_TOKEN(AST_SWITCH_STMT, context->tok); + advance_and_verify(context, TOKEN_SWITCH); CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); - if (!parse_control_expression(&switch_ast->switch_stmt.decl, &switch_ast->switch_stmt.cond)) return &poisoned_ast; + if (!parse_control_expression(context, &switch_ast->switch_stmt.decl, &switch_ast->switch_stmt.cond)) return &poisoned_ast; CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); - switch_ast->switch_stmt.body = TRY_AST(parse_compound_stmt()); + switch_ast->switch_stmt.body = TRY_AST(parse_compound_stmt(context)); return switch_ast; } @@ -808,15 +886,15 @@ static inline Ast* parse_switch_stmt(void) * * @return Ast* */ -static inline Ast* parse_for_stmt(void) +static inline Ast* parse_for_stmt(Context *context) { - Ast *ast = AST_NEW(AST_FOR_STMT, tok); - advance_and_verify(TOKEN_FOR); + Ast *ast = AST_NEW_TOKEN(AST_FOR_STMT, context->tok); + advance_and_verify(context, TOKEN_FOR); CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); - if (tok.type != TOKEN_EOS) + if (context->tok.type != TOKEN_EOS) { - ast->for_stmt.init = TRY_AST(parse_decl_expr_list()); + ast->for_stmt.init = TRY_AST(parse_decl_expr_list(context)); } else { @@ -825,21 +903,28 @@ static inline Ast* parse_for_stmt(void) CONSUME_OR(TOKEN_EOS, &poisoned_ast); - ast->for_stmt.cond = tok.type == TOKEN_EOS ? NULL : TRY_EXPR_OR(parse_expr(), &poisoned_ast); + if (context->tok.type != TOKEN_EOS) + { + ast->for_stmt.cond = TRY_EXPR_OR(parse_expr(context), &poisoned_ast); + } CONSUME_OR(TOKEN_EOS, &poisoned_ast); - ast->for_stmt.incr = tok.type == TOKEN_RPAREN ? NULL : TRY_EXPR_OR(parse_expression_list(), &poisoned_ast); + if (context->tok.type != TOKEN_RPAREN) + { + ast->for_stmt.incr = TRY_AST(ast_from_expr(parse_expression_list(context))); + } CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); - ast->for_stmt.body = TRY_AST(parse_stmt()); + extend_ast_with_prev_token(context, ast); + ast->for_stmt.body = TRY_AST(parse_stmt(context)); return ast; } -static inline Expr* parse_constant_expr(void) +static inline Expr* parse_constant_expr(Context *context) { - return parse_precedence(PREC_TERNARY); + return parse_precedence(context, PREC_TERNARY); } /** * case_stmt @@ -847,47 +932,44 @@ static inline Expr* parse_constant_expr(void) * * @return Ast* */ -static inline Ast* parse_case_stmt(void) +static inline Ast* parse_case_stmt(Context *context) { - Ast *ast = AST_NEW(AST_CASE_STMT, tok); - advance(); - Expr *expr = TRY_EXPR_OR(parse_constant_expr(), &poisoned_ast); + Ast *ast = AST_NEW_TOKEN(AST_CASE_STMT, context->tok); + advance(context); + Expr *expr = TRY_EXPR_OR(parse_constant_expr(context), &poisoned_ast); ast->case_stmt.expr = expr; TRY_CONSUME(TOKEN_COLON, "Missing ':' after case"); - return ast; + return extend_ast_with_prev_token(context, ast); } -static inline Ast* parse_goto_stmt(void) +static inline Ast* parse_goto_stmt(Context *context) { - advance_and_verify(TOKEN_GOTO); - Ast *ast = AST_NEW(AST_GOTO_STMT, tok); - if (!consume_const_name("label")) return &poisoned_ast; - CONSUME_OR(TOKEN_EOS, &poisoned_ast); - return ast; + Ast *ast = AST_NEW_TOKEN(AST_GOTO_STMT, context->tok); + advance_and_verify(context, TOKEN_GOTO); + ast->goto_stmt.label_name = context->tok.string; + if (!consume_const_name(context, "label")) return &poisoned_ast; + RETURN_AFTER_EOS(ast); } -static inline Ast* parse_continue_stmt(void) +static inline Ast* parse_continue_stmt(Context *context) { - Ast *ast = AST_NEW(AST_CONTINUE_STMT, tok); - advance_and_verify(TOKEN_CONTINUE); - CONSUME_OR(TOKEN_EOS, &poisoned_ast); - return ast; + Ast *ast = AST_NEW_TOKEN(AST_CONTINUE_STMT, context->tok); + advance_and_verify(context, TOKEN_CONTINUE); + RETURN_AFTER_EOS(ast); } -static inline Ast* parse_next_stmt(void) +static inline Ast* parse_next_stmt(Context *context) { - Ast *ast = AST_NEW(AST_NEXT_STMT, tok); - advance_and_verify(TOKEN_NEXT); - CONSUME_OR(TOKEN_EOS, &poisoned_ast); - return ast; + Ast *ast = AST_NEW_TOKEN(AST_NEXT_STMT, context->tok); + advance_and_verify(context, TOKEN_NEXT); + RETURN_AFTER_EOS(ast); } -static inline Ast* parse_break_stmt(void) +static inline Ast* parse_break_stmt(Context *context) { - Ast *ast = AST_NEW(AST_BREAK_STMT, tok); - advance_and_verify(TOKEN_BREAK); - CONSUME_OR(TOKEN_EOS, &poisoned_ast); - return ast; + Ast *ast = AST_NEW_TOKEN(AST_BREAK_STMT, context->tok); + advance_and_verify(context, TOKEN_BREAK); + RETURN_AFTER_EOS(ast); } @@ -906,43 +988,43 @@ static inline Ast* parse_break_stmt(void) * * @return */ -static inline Ast* parse_ct_switch_stmt(void) +static inline Ast* parse_ct_switch_stmt(Context *context) { - Ast *ast = AST_NEW(AST_CT_SWITCH_STMT, tok); - advance_and_verify(TOKEN_CT_SWITCH); - ast->ct_switch_stmt.cond = TRY_EXPR_OR(parse_paren_expr(), &poisoned_ast); + Ast *ast = AST_NEW_TOKEN(AST_CT_SWITCH_STMT, context->tok); + advance_and_verify(context, TOKEN_CT_SWITCH); + ast->ct_switch_stmt.cond = TRY_EXPR_OR(parse_paren_expr(context), &poisoned_ast); CONSUME_OR(TOKEN_LBRACE, &poisoned_ast); Ast **switch_statements = NULL; Ast *stmt = &poisoned_ast; while (stmt) { - switch (tok.type) + switch (context->tok.type) { case TOKEN_CT_CASE: - stmt = AST_NEW(AST_CT_CASE_STMT, tok); - advance(); + stmt = AST_NEW_TOKEN(AST_CT_CASE_STMT, context->tok); + advance(context); while (1) { - TypeInfo *type = TRY_TYPE_OR(parse_type_expression(), &poisoned_ast); + TypeInfo *type = TRY_TYPE_OR(parse_type_expression(context), &poisoned_ast); vec_add(stmt->ct_case_stmt.types, type); - if (!try_consume(TOKEN_COMMA)) break; + if (!try_consume(context, TOKEN_COMMA)) break; } CONSUME_OR(TOKEN_COLON, &poisoned_ast); - stmt->ct_case_stmt.body = TRY_AST_OR(parse_stmt(), &poisoned_ast); + stmt->ct_case_stmt.body = TRY_AST_OR(parse_stmt(context), &poisoned_ast); vec_add(switch_statements, stmt); break; case TOKEN_DEFAULT: - stmt = AST_NEW(AST_CT_CASE_STMT, tok); - advance(); + stmt = AST_NEW_TOKEN(AST_CT_CASE_STMT, context->tok); + advance(context); CONSUME_OR(TOKEN_COLON, &poisoned_ast); - stmt->ct_default_stmt = TRY_AST_OR(parse_stmt(), &poisoned_ast); + stmt->ct_default_stmt = TRY_AST_OR(parse_stmt(context), &poisoned_ast); vec_add(switch_statements, stmt); break; case TOKEN_RBRACE: stmt = NULL; break; default: - SEMA_ERROR(tok, "Expected $case or $default."); + SEMA_TOKEN_ERROR(context->tok, "Expected $case or $default."); return &poisoned_ast; } } @@ -951,11 +1033,11 @@ static inline Ast* parse_ct_switch_stmt(void) return ast; } -static inline Ast* parse_ct_else_stmt(void) +static inline Ast* parse_ct_else_stmt(Context *context) { - Ast *ast = AST_NEW(AST_CT_ELSE_STMT, tok); - advance_and_verify(TOKEN_CT_ELSE); - ast->ct_elif_stmt.then = TRY_AST(parse_compound_stmt()); + Ast *ast = AST_NEW_TOKEN(AST_CT_ELSE_STMT, context->tok); + advance_and_verify(context, TOKEN_CT_ELSE); + ast->ct_elif_stmt.then = TRY_AST(parse_compound_stmt(context)); return ast; } @@ -964,22 +1046,22 @@ static inline Ast* parse_ct_else_stmt(void) * : $elif '(' expression ')' compound_statement * @return */ -static inline Ast *parse_ct_elif_stmt(void) +static inline Ast *parse_ct_elif_stmt(Context *context) { - Ast *ast = AST_NEW(AST_CT_ELIF_STMT, tok); - advance_and_verify(TOKEN_CT_ELIF); + Ast *ast = AST_NEW_TOKEN(AST_CT_ELIF_STMT, context->tok); + advance_and_verify(context, TOKEN_CT_ELIF); - ast->ct_elif_stmt.expr = TRY_EXPR_OR(parse_paren_expr(), &poisoned_ast); + ast->ct_elif_stmt.expr = TRY_EXPR_OR(parse_paren_expr(context), &poisoned_ast); - ast->ct_elif_stmt.then = TRY_AST(parse_compound_stmt()); + ast->ct_elif_stmt.then = TRY_AST(parse_compound_stmt(context)); - if (tok.type == TOKEN_CT_ELIF) + if (context->tok.type == TOKEN_CT_ELIF) { - ast->ct_elif_stmt.elif = TRY_AST(parse_ct_elif_stmt()); + ast->ct_elif_stmt.elif = TRY_AST(parse_ct_elif_stmt(context)); } - else if (tok.type == TOKEN_CT_ELSE) + else if (context->tok.type == TOKEN_CT_ELSE) { - ast->ct_elif_stmt.elif = TRY_AST(parse_ct_else_stmt()); + ast->ct_elif_stmt.elif = TRY_AST(parse_ct_else_stmt(context)); } return ast; } @@ -993,19 +1075,19 @@ static inline Ast *parse_ct_elif_stmt(void) * * @return Ast* */ -static inline Ast* parse_ct_if_stmt(void) +static inline Ast* parse_ct_if_stmt(Context *context) { - Ast *ast = AST_NEW(AST_CT_IF_STMT, tok); - advance_and_verify(TOKEN_CT_IF); - ast->ct_if_stmt.expr = TRY_EXPR_OR(parse_paren_expr(), &poisoned_ast); - ast->ct_if_stmt.then = TRY_AST(parse_compound_stmt()); - if (tok.type == TOKEN_CT_ELIF) + Ast *ast = AST_NEW_TOKEN(AST_CT_IF_STMT, context->tok); + advance_and_verify(context, TOKEN_CT_IF); + ast->ct_if_stmt.expr = TRY_EXPR_OR(parse_paren_expr(context), &poisoned_ast); + ast->ct_if_stmt.then = TRY_AST(parse_compound_stmt(context)); + if (context->tok.type == TOKEN_CT_ELIF) { - ast->ct_if_stmt.elif = TRY_AST(parse_ct_elif_stmt()); + ast->ct_if_stmt.elif = TRY_AST(parse_ct_elif_stmt(context)); } - else if (tok.type == TOKEN_CT_ELSE) + else if (context->tok.type == TOKEN_CT_ELSE) { - ast->ct_if_stmt.elif = TRY_AST(parse_ct_else_stmt()); + ast->ct_if_stmt.elif = TRY_AST(parse_ct_else_stmt(context)); } return ast; } @@ -1019,23 +1101,23 @@ static inline Ast* parse_ct_if_stmt(void) * * @return */ -static inline Ast* parse_ct_for_stmt(void) +static inline Ast* parse_ct_for_stmt(Context *context) { - Ast *ast = AST_NEW(AST_CT_FOR_STMT, tok); - advance_and_verify(TOKEN_CT_FOR); + Ast *ast = AST_NEW_TOKEN(AST_CT_FOR_STMT, context->tok); + advance_and_verify(context, TOKEN_CT_FOR); CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); - if (next_tok.type == TOKEN_COMMA) + if (context->next_tok.type == TOKEN_COMMA) { - ast->ct_for_stmt.index = tok; + ast->ct_for_stmt.index = context->tok; TRY_CONSUME_OR(TOKEN_CT_IDENT, "Expected a compile time index variable", &poisoned_ast); - advance_and_verify(TOKEN_COMMA); + advance_and_verify(context, TOKEN_COMMA); } - ast->ct_for_stmt.value = tok; + ast->ct_for_stmt.value = context->tok; TRY_CONSUME_OR(TOKEN_CT_IDENT, "Expected a compile time variable", &poisoned_ast); TRY_CONSUME_OR(TOKEN_IN, "Expected 'in'.", &poisoned_ast); - ast->ct_for_stmt.expr = TRY_EXPR_OR(parse_expr(), &poisoned_ast); + ast->ct_for_stmt.expr = TRY_EXPR_OR(parse_expr(context), &poisoned_ast); CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); - ast->ct_for_stmt.body = TRY_AST(parse_stmt()); + ast->ct_for_stmt.body = TRY_AST(parse_stmt(context)); return ast; } @@ -1048,43 +1130,41 @@ static inline Ast* parse_ct_for_stmt(void) * * @return Ast* if parsing fails it is poisoned */ -static Ast *parse_return_stmt(void) +static Ast *parse_return_stmt(Context *context) { - advance_and_verify(TOKEN_RETURN); - Ast *ast = AST_NEW(AST_RETURN_STMT, tok); + advance_and_verify(context, TOKEN_RETURN); + Ast *ast = AST_NEW_TOKEN(AST_RETURN_STMT, context->tok); ast->exit = EXIT_RETURN; ast->return_stmt.defer = 0; - if (try_consume(TOKEN_EOS)) + if (try_consume(context, TOKEN_EOS)) { ast->return_stmt.expr = NULL; return ast; } - ast->return_stmt.expr = TRY_EXPR_OR(parse_expr(), &poisoned_ast); - CONSUME_OR(TOKEN_EOS, &poisoned_ast); + ast->return_stmt.expr = TRY_EXPR_OR(parse_expr(context), &poisoned_ast); + RETURN_AFTER_EOS(ast); +} + +static Ast *parse_throw_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_THROW_STMT, context->tok); + advance_and_verify(context, TOKEN_THROW); + ast->throw_stmt = TRY_EXPR_OR(parse_expr(context), &poisoned_ast); + RETURN_AFTER_EOS(ast); +} + +static Ast *parse_volatile_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_VOLATILE_STMT, context->tok); + ast->volatile_stmt = TRY_AST_OR(parse_compound_stmt(context), &poisoned_ast); return ast; } -static Ast *parse_throw_stmt(void) +static Ast *parse_default_stmt(Context *context) { - Ast *ast = AST_NEW(AST_THROW_STMT, tok); - advance_and_verify(TOKEN_THROW); - ast->throw_stmt = TRY_EXPR_OR(parse_expr(), &poisoned_ast); - CONSUME_OR(TOKEN_EOS, &poisoned_ast); - return ast; -} - -static Ast *parse_volatile_stmt(void) -{ - Ast *ast = AST_NEW(AST_VOLATILE_STMT, tok); - ast->volatile_stmt = TRY_AST_OR(parse_compound_stmt(), &poisoned_ast); - return ast; -} - -static Ast *parse_default_stmt(void) -{ - Ast *ast = AST_NEW(AST_DEFAULT_STMT, tok); - advance_and_verify(TOKEN_DEFAULT); + Ast *ast = AST_NEW_TOKEN(AST_DEFAULT_STMT, context->tok); + advance_and_verify(context, TOKEN_DEFAULT); TRY_CONSUME_OR(TOKEN_COLON, "Expected ':' after 'default'.", &poisoned_ast); return ast; } @@ -1106,22 +1186,23 @@ bool is_valid_try_statement(TokenType type) } } -static inline Ast *parse_label_stmt(void) +static inline Ast *parse_label_stmt(Context *context) { - Ast *ast = AST_NEW(AST_LABEL, tok); - advance_and_verify(TOKEN_CONST_IDENT); - advance_and_verify(TOKEN_COLON); - return ast; + Ast *ast = AST_NEW_TOKEN(AST_LABEL, context->tok); + ast->label_stmt.name = context->tok.string; + advance_and_verify(context, TOKEN_CONST_IDENT); + advance_and_verify(context, TOKEN_COLON); + return extend_ast_with_prev_token(context, ast); } -static inline bool is_expr_after_type_ident(void) +static inline bool is_expr_after_type_ident(Context *context) { - return next_tok.type == TOKEN_DOT || next_tok.type == TOKEN_LPAREN; + return context->next_tok.type == TOKEN_DOT || context->next_tok.type == TOKEN_LPAREN; } -static bool parse_type_or_expr(Expr **exprPtr, TypeInfo **typePtr) +static bool parse_type_or_expr(Context *context, Expr **exprPtr, TypeInfo **typePtr) { - switch (tok.type) + switch (context->tok.type) { case TOKEN_VOID: case TOKEN_BYTE: @@ -1147,33 +1228,32 @@ static bool parse_type_or_expr(Expr **exprPtr, TypeInfo **typePtr) case TOKEN_C_ULONG: case TOKEN_C_ULONGLONG: case TOKEN_TYPE_IDENT: - if (next_tok.type == TOKEN_DOT || next_tok.type == TOKEN_LPAREN) break; - *typePtr = parse_type_expression(); + if (context->next_tok.type == TOKEN_DOT || context->next_tok.type == TOKEN_LPAREN) break; + *typePtr = parse_type_expression(context); return type_info_ok(*typePtr); case TOKEN_IDENT: - if (next_tok.type == TOKEN_SCOPE) + if (context->next_tok.type == TOKEN_SCOPE) { // We need a little lookahead to see if this is type or expression. - lexer_store_state(); - module = tok; - advance(); advance(); - if (tok.type == TOKEN_TYPE_IDENT && !is_expr_after_type_ident()) + context_store_lexer_state(context); + advance(context); advance(context); + if (context->tok.type == TOKEN_TYPE_IDENT && !is_expr_after_type_ident(context)) { - lexer_restore_state(); - *typePtr = parse_type_expression(); + context_restore_lexer_state(context); + *typePtr = parse_type_expression(context); return type_info_ok(*typePtr); } - lexer_restore_state(); + context_restore_lexer_state(context); } break; case TOKEN_TYPE: { - Token start = tok; - advance_and_verify(TOKEN_TYPE); + SourceRange start = context->tok.span; + advance_and_verify(context, TOKEN_TYPE); CONSUME_OR(TOKEN_LPAREN, false); Expr* inner_expr = NULL; TypeInfo* inner_type = NULL; - if (!parse_type_or_expr(&inner_expr, &inner_type)) return false; + if (!parse_type_or_expr(context, &inner_expr, &inner_type)) return false; CONSUME_OR(TOKEN_RPAREN, false); if (inner_expr) { @@ -1183,54 +1263,54 @@ static bool parse_type_or_expr(Expr **exprPtr, TypeInfo **typePtr) } Expr *type_expr = expr_new(EXPR_TYPE, start); type_expr->type_expr.type = inner_type; - *exprPtr = parse_precedence_with_left_side(type_expr, PREC_ASSIGNMENT); + *exprPtr = parse_precedence_with_left_side(context, type_expr, PREC_ASSIGNMENT); return expr_ok(*exprPtr); } default: break; } - *exprPtr = parse_expr(); + *exprPtr = parse_expr(context); return expr_ok(*exprPtr); } -static inline Ast *parse_decl_or_expr_stmt(void) +static inline Ast *parse_decl_or_expr_stmt(Context *context) { Expr *expr = NULL; TypeInfo *type = NULL; - if (!parse_type_or_expr(&expr, &type)) return &poisoned_ast; + if (!parse_type_or_expr(context, &expr, &type)) return &poisoned_ast; if (expr) { CONSUME_OR(TOKEN_EOS, &poisoned_ast); - Ast *ast = new_ast(AST_EXPR_STMT, expr->loc); + Ast *ast = AST_NEW(AST_EXPR_STMT, expr->span); ast->expr_stmt = expr; return ast; } else { - Decl *decl = TRY_DECL_OR(parse_decl_after_type(false, type), &poisoned_ast); - Ast *ast = AST_NEW(AST_DECLARE_STMT, decl->name); + Decl *decl = TRY_DECL_OR(parse_decl_after_type(context, false, type), &poisoned_ast); + Ast *ast = AST_NEW(AST_DECLARE_STMT, decl->span); ast->declare_stmt = decl; CONSUME_OR(TOKEN_EOS, &poisoned_ast); return ast; } } -static Ast *parse_stmt(void) +static Ast *parse_stmt(Context *context) { - switch (tok.type) + switch (context->tok.type) { case TOKEN_LBRACE: - return parse_compound_stmt(); + return parse_compound_stmt(context); case TOKEN_LPARBRA: - return parse_function_block(); + return parse_function_block(context); case TOKEN_HALF: case TOKEN_QUAD: - SEMA_ERROR(next_tok, "Type is unsupported by platform."); - advance(); + SEMA_TOKEN_ERROR(context->next_tok, "Type is unsupported by platform."); + advance(context); return &poisoned_ast; case TOKEN_VOID: case TOKEN_BYTE: @@ -1255,79 +1335,79 @@ static Ast *parse_stmt(void) case TOKEN_C_ULONG: case TOKEN_C_ULONGLONG: case TOKEN_TYPE_IDENT: - if (next_tok.type == TOKEN_DOT || next_tok.type == TOKEN_LBRACE) + if (context->next_tok.type == TOKEN_DOT || context->next_tok.type == TOKEN_LBRACE) { - return parse_expr_stmt(); + return parse_expr_stmt(context); } - return parse_declaration_stmt(); + return parse_declaration_stmt(context); case TOKEN_LOCAL: // Local means declaration! case TOKEN_CONST: // Const means declaration! - return parse_declaration_stmt(); + return parse_declaration_stmt(context); case TOKEN_TYPE: - return parse_decl_or_expr_stmt(); + return parse_decl_or_expr_stmt(context); case TOKEN_CONST_IDENT: - if (next_tok.type == TOKEN_COLON) + if (context->next_tok.type == TOKEN_COLON) { - return parse_label_stmt(); + return parse_label_stmt(context); } - return parse_expr_stmt(); + return parse_expr_stmt(context); case TOKEN_IDENT: - if (next_tok.type == TOKEN_SCOPE) + if (context->next_tok.type == TOKEN_SCOPE) { - return parse_decl_or_expr_stmt(); + return parse_decl_or_expr_stmt(context); } - return parse_expr_stmt(); + return parse_expr_stmt(context); case TOKEN_RETURN: - return parse_return_stmt(); + return parse_return_stmt(context); case TOKEN_IF: - return parse_if_stmt(); + return parse_if_stmt(context); case TOKEN_WHILE: - return parse_while_stmt(); + return parse_while_stmt(context); case TOKEN_DEFER: - return parse_defer_stmt(); + return parse_defer_stmt(context); case TOKEN_SWITCH: - return parse_switch_stmt(); + return parse_switch_stmt(context); case TOKEN_GOTO: - return parse_goto_stmt(); + return parse_goto_stmt(context); case TOKEN_DO: - return parse_do_stmt(); + return parse_do_stmt(context); case TOKEN_FOR: - return parse_for_stmt(); + return parse_for_stmt(context); case TOKEN_CATCH: - return parse_catch_stmt(); + return parse_catch_stmt(context); case TOKEN_TRY: - if (is_valid_try_statement(next_tok.type)) + if (is_valid_try_statement(context->next_tok.type)) { - Token try_token = tok; - advance(); - Ast *stmt = TRY_AST(parse_stmt()); - Ast *try_ast = AST_NEW(AST_TRY_STMT, try_token); + Token token = context->tok; + advance(context); + Ast *stmt = TRY_AST(parse_stmt(context)); + Ast *try_ast = AST_NEW_TOKEN(AST_TRY_STMT, token); try_ast->try_stmt = stmt; return try_ast; } - return parse_expr_stmt(); + return parse_expr_stmt(context); case TOKEN_CONTINUE: - return parse_continue_stmt(); + return parse_continue_stmt(context); case TOKEN_CASE: - return parse_case_stmt(); + return parse_case_stmt(context); case TOKEN_BREAK: - return parse_break_stmt(); + return parse_break_stmt(context); case TOKEN_NEXT: - return parse_next_stmt(); + return parse_next_stmt(context); case TOKEN_ASM: - return parse_asm_stmt(); + return parse_asm_stmt(context); case TOKEN_DEFAULT: - return parse_default_stmt(); + return parse_default_stmt(context); case TOKEN_CT_IF: - return parse_ct_if_stmt(); + return parse_ct_if_stmt(context); case TOKEN_CT_SWITCH: - return parse_ct_switch_stmt(); + return parse_ct_switch_stmt(context); case TOKEN_CT_FOR: - return parse_ct_for_stmt(); + return parse_ct_for_stmt(context); case TOKEN_THROW: - return parse_throw_stmt(); + return parse_throw_stmt(context); case TOKEN_VOLATILE: - return parse_volatile_stmt(); + return parse_volatile_stmt(context); case TOKEN_STAR: case TOKEN_AMP: case TOKEN_INTEGER: @@ -1350,9 +1430,9 @@ static Ast *parse_stmt(void) case TOKEN_FALSE: case TOKEN_NIL: case TOKEN_TRUE: - return parse_expr_stmt(); + return parse_expr_stmt(context); case TOKEN_INVALID_TOKEN: - advance(); + advance(context); return &poisoned_ast; case TOKEN_AT: case TOKEN_COLON: @@ -1426,6 +1506,8 @@ static Ast *parse_stmt(void) case TOKEN_DOCS_START: case TOKEN_DOCS_END: case TOKEN_DOCS_EOL: + case TOKEN_DOC_COMMENT: + case TOKEN_COMMENT: case TOKEN_DOCS_LINE: case TOKEN_CT_CASE: case TOKEN_CT_ELIF: @@ -1433,21 +1515,21 @@ static Ast *parse_stmt(void) case TOKEN_CT_DEFAULT: case TOKEN_RPARBRA: case TOKEN_IN: - SEMA_ERROR(tok, "Unexpected '%s' found when expecting a statement.", token_type_to_string(tok.type)); - advance(); + SEMA_TOKEN_ERROR(context->tok, "Unexpected '%s' found when expecting a statement.", token_type_to_string(context->tok.type)); + advance(context); return &poisoned_ast; break; case TOKEN_RPAREN: case TOKEN_RBRACE: case TOKEN_RBRACKET: - SEMA_ERROR(tok, "Mismatched '%s' found.", token_type_to_string(tok.type)); - advance(); + SEMA_TOKEN_ERROR(context->tok, "Mismatched '%s' found.", token_type_to_string(context->tok.type)); + advance(context); return &poisoned_ast; case TOKEN_EOS: - advance(); - return AST_NEW(AST_NOP_STMT, tok); + advance(context); + return AST_NEW_TOKEN(AST_NOP_STMT, context->tok); case TOKEN_EOF: - sema_error_at(tok.span.loc - 1, "Reached the end of the file when expecting a statement."); + sema_error_at(context->tok.span.loc - 1, "Reached the end of the file when expecting a statement."); return &poisoned_ast; } } @@ -1465,29 +1547,29 @@ static Ast *parse_stmt(void) * | module_params ',' module_param * ; */ -static inline bool parse_optional_module_params(Token **tokens) +static inline bool parse_optional_module_params(Context *context, Token **tokens) { *tokens = NULL; - if (!try_consume(TOKEN_LPAREN)) return true; + if (!try_consume(context, TOKEN_LPAREN)) return true; - if (try_consume(TOKEN_RPAREN)) + if (try_consume(context, TOKEN_RPAREN)) { - SEMA_ERROR(tok, "Generic parameter list cannot be empty."); + SEMA_TOKEN_ERROR(context->tok, "Generic parameter list cannot be empty."); return false; } // No params while (1) { - switch (tok.type) + switch (context->tok.type) { case TOKEN_IDENT: - sema_error_range(next_tok.span, "The module parameter must be a $ or #-prefixed name, did you forgot the '$'?"); + sema_error_range(context->next_tok.span, "The module parameter must be a $ or #-prefixed name, did you forgot the '$'?"); return false; case TOKEN_COMMA: - sema_error_range(next_tok.span, "Unexpected ','"); + sema_error_range(context->next_tok.span, "Unexpected ','"); return false; case TOKEN_AT_IDENT: case TOKEN_CT_IDENT: @@ -1495,14 +1577,14 @@ static inline bool parse_optional_module_params(Token **tokens) case TOKEN_TYPE_IDENT: break; default: - SEMA_ERROR(tok, "Only generic parameters are allowed here as parameters to the module."); + SEMA_TOKEN_ERROR(context->tok, "Only generic parameters are allowed here as parameters to the module."); return false; } - *tokens = VECADD(*tokens, next_tok); - advance(); - if (!try_consume(TOKEN_COMMA)) + *tokens = VECADD(*tokens, context->next_tok); + advance(context); + if (!try_consume(context, TOKEN_COMMA)) { - return consume(TOKEN_RPAREN, "Expected ')'."); + return consume(context, TOKEN_RPAREN, "Expected ')'."); } } } @@ -1512,16 +1594,16 @@ static inline bool parse_optional_module_params(Token **tokens) * : MODULE path ';' * | MODULE path '(' module_params ')' ';' */ -static inline void parse_module(void) +static inline void parse_module(Context *context) { - if (!try_consume(TOKEN_MODULE)) + if (!try_consume(context, TOKEN_MODULE)) { - context_set_module_from_filename(current_context); + context_set_module_from_filename(context); return; } - Path *path = parse_module_path(); + Path *path = parse_module_path(context); // Expect the module name if (!path) @@ -1530,20 +1612,20 @@ static inline void parse_module(void) path->len = strlen("INVALID"); path->module = "INVALID"; path->span = INVALID_RANGE; - context_set_module(current_context, path, NULL); - recover_top_level(); + context_set_module(context, path, NULL); + recover_top_level(context); return; } // Is this a generic module? Token *generic_parameters = NULL; - if (!parse_optional_module_params(&generic_parameters)) + if (!parse_optional_module_params(context, &generic_parameters)) { - context_set_module(current_context, path, generic_parameters); - recover_top_level(); + context_set_module(context, path, generic_parameters); + recover_top_level(context); return; } - context_set_module(current_context, path, generic_parameters); + context_set_module(context, path, generic_parameters); TRY_CONSUME_EOS_OR(); } @@ -1558,19 +1640,19 @@ static inline void parse_module(void) * | type_or_expr * | expr */ -static inline bool parse_macro_parameter_list(Expr*** result) +static inline bool parse_macro_parameter_list(Context *context, Expr*** result) { TODO - advance_and_verify(TOKEN_LPAREN); + advance_and_verify(context, TOKEN_LPAREN); *result = NULL; - while (try_consume(TOKEN_RPAREN)) + while (try_consume(context, TOKEN_RPAREN)) { - if (try_consume(TOKEN_COMMA)) + if (try_consume(context, TOKEN_COMMA)) { - sema_error_range(tok.span, "There was an empty value here, did you accidentally add a ',' too many?"); + sema_error_range(context->tok.span, "There was an empty value here, did you accidentally add a ',' too many?"); return false; } - Expr *expr = NULL;// TODO parse_expr(); + Expr *expr = NULL;// TODO parse_expr(context); if (expr->expr_kind == EXPR_POISONED) return false; *result = VECADD(*result, expr); COMMA_RPAREN_OR(false); @@ -1592,33 +1674,33 @@ static inline bool parse_macro_parameter_list(Expr*** result) * * @return true if import succeeded */ -static inline bool parse_import_selective(Path *path) +static inline bool parse_import_selective(Context *context, Path *path) { - if (!token_is_symbol(tok.type)) + if (!token_is_symbol(context->tok.type)) { - SEMA_ERROR(tok, "Expected a symbol name here, the syntax is 'import : '."); + SEMA_TOKEN_ERROR(context->tok, "Expected a symbol name here, the syntax is 'import : '."); return false; } - Token symbol = tok; - advance(); + Token symbol = context->tok; + advance(context); // Alias? - if (!try_consume(TOKEN_AS)) + if (!try_consume(context, TOKEN_AS)) { - return context_add_import(current_context, path, symbol, EMPTY_TOKEN); + return context_add_import(context, path, symbol, EMPTY_TOKEN); } - if (tok.type != symbol.type) + if (context->tok.type != symbol.type) { - if (!token_is_symbol(tok.type)) + if (!token_is_symbol(context->tok.type)) { - SEMA_ERROR(tok, "Expected a symbol name here, the syntax is 'import : AS '."); + SEMA_TOKEN_ERROR(context->tok, "Expected a symbol name here, the syntax is 'import : AS '."); return false; } - SEMA_ERROR(tok, "Expected the alias be the same type of name as the symbol aliased."); + SEMA_TOKEN_ERROR(context->tok, "Expected the alias be the same type of name as the symbol aliased."); return false; } - Token alias = tok; - advance(); - return context_add_import(current_context, path, symbol, alias); + Token alias = context->tok; + advance(context); + return context_add_import(context, path, symbol, alias); } @@ -1637,28 +1719,28 @@ static inline bool parse_import_selective(Path *path) * * @return true if import succeeded */ -static inline bool parse_import() +static inline bool parse_import(Context *context) { - advance_and_verify(TOKEN_IMPORT); + advance_and_verify(context, TOKEN_IMPORT); - if (tok.type != TOKEN_IDENT) + if (context->tok.type != TOKEN_IDENT) { - SEMA_ERROR(tok, "Import statement should be followed by the name of the module to import."); + SEMA_TOKEN_ERROR(context->tok, "Import statement should be followed by the name of the module to import."); return false; } - Path *path = parse_module_path(); - if (tok.type == TOKEN_COLON) + Path *path = parse_module_path(context); + if (context->tok.type == TOKEN_COLON) { while (1) { - if (!parse_import_selective(path)) return false; - if (!try_consume(TOKEN_COMMA)) break; + if (!parse_import_selective(context, path)) return false; + if (!try_consume(context, TOKEN_COMMA)) break; } } else { - context_add_import(current_context, path, EMPTY_TOKEN, EMPTY_TOKEN); + context_add_import(context, path, EMPTY_TOKEN, EMPTY_TOKEN); } TRY_CONSUME_EOS_OR(false); return true; @@ -1666,45 +1748,45 @@ static inline bool parse_import() -static Expr *parse_precedence(Precedence precedence) +static Expr *parse_precedence(Context *context, Precedence precedence) { // Get the rule for the previous token. - ParseFn prefix_rule = rules[tok.type].prefix; + ParseFn prefix_rule = rules[context->tok.type].prefix; if (prefix_rule == NULL) { - SEMA_ERROR(tok, "An expression was expected."); + SEMA_TOKEN_ERROR(context->tok, "An expression was expected."); return &poisoned_expr; } - Expr *left_side = prefix_rule(NULL); + Expr *left_side = prefix_rule(context, NULL); if (!expr_ok(left_side)) return left_side; - return parse_precedence_with_left_side(left_side, precedence); + return parse_precedence_with_left_side(context, left_side, precedence); } -static inline Expr* parse_expr(void) +static inline Expr* parse_expr(Context *context) { - Token start = tok; - bool found_try = try_consume(TOKEN_TRY); - Expr *expr = TRY_EXPR_OR(parse_precedence(PREC_ASSIGNMENT), &poisoned_expr); + SourceRange start = context->tok.span; + bool found_try = try_consume(context, TOKEN_TRY); + Expr *expr = TRY_EXPR_OR(parse_precedence(context, PREC_ASSIGNMENT), &poisoned_expr); if (found_try) { Expr *try_expr = expr_new(EXPR_TRY, start); try_expr->try_expr.expr = expr; - if (try_consume(TOKEN_ELSE)) + if (try_consume(context, TOKEN_ELSE)) { - try_expr->try_expr.else_expr = TRY_EXPR_OR(parse_precedence(PREC_ASSIGNMENT), &poisoned_expr); + try_expr->try_expr.else_expr = TRY_EXPR_OR(parse_precedence(context, PREC_ASSIGNMENT), &poisoned_expr); } return try_expr; } return expr; } -static inline Expr *parse_paren_expr(void) +static inline Expr *parse_paren_expr(Context *context) { CONSUME_OR(TOKEN_LPAREN, &poisoned_expr); - Expr *expr = TRY_EXPR_OR(parse_expr(), &poisoned_expr); + Expr *expr = TRY_EXPR_OR(parse_expr(context), &poisoned_expr); CONSUME_OR(TOKEN_RPAREN, &poisoned_expr); return expr; } @@ -1715,12 +1797,12 @@ static inline Expr *parse_paren_expr(void) * | imports import_decl * ; */ -static inline void parse_imports(void) +static inline void parse_imports(Context *context) { - while (tok.type == TOKEN_IMPORT) + while (context->tok.type == TOKEN_IMPORT) { - if (!parse_import()) recover_top_level(); + if (!parse_import(context)) recover_top_level(context); } } @@ -1732,29 +1814,29 @@ static inline void parse_imports(void) * | 'const' type IDENT '=' const_expr ';' * ; */ -static inline Decl *parse_const_declaration(Visibility visibility) +static inline Decl *parse_const_declaration(Context *context, Visibility visibility) { - advance_and_verify(TOKEN_CONST); + advance_and_verify(context, TOKEN_CONST); - Decl *decl = decl_new_var(tok, NULL, VARDECL_CONST, visibility); + Decl *decl = decl_new_var(context->tok, NULL, VARDECL_CONST, visibility); // Parse the compile time constant. - if (tok.type == TOKEN_CT_IDENT) + if (context->tok.type == TOKEN_CT_IDENT) { - if (!is_all_upper(tok.string)) + if (!is_all_upper(context->tok.string)) { - SEMA_ERROR(tok, "Compile time constants must be all upper characters."); + SEMA_TOKEN_ERROR(context->tok, "Compile time constants must be all upper characters."); return &poisoned_decl; } } else { - if (!consume_const_name("constant")) return &poisoned_decl; - decl->var.type_info = TRY_TYPE_OR(parse_type_expression(), &poisoned_decl); + if (!consume_const_name(context, "constant")) return &poisoned_decl; + decl->var.type_info = TRY_TYPE_OR(parse_type_expression(context), &poisoned_decl); } CONSUME_OR(TOKEN_EQ, &poisoned_decl); - decl->var.init_expr = TRY_EXPR_OR(parse_initializer(), &poisoned_decl); + decl->var.init_expr = TRY_EXPR_OR(parse_initializer(context), &poisoned_decl); CONSUME_OR(TOKEN_EOS, &poisoned_decl); return decl; @@ -1769,17 +1851,17 @@ static inline Decl *parse_const_declaration(Visibility visibility) * @param visibility * @return true if parsing succeeded */ -static inline Decl *parse_global_declaration(Visibility visibility) +static inline Decl *parse_global_declaration(Context *context, Visibility visibility) { - TypeInfo *type = TRY_TYPE_OR(parse_type_expression(), &poisoned_decl); + TypeInfo *type = TRY_TYPE_OR(parse_type_expression(context), &poisoned_decl); - Decl *decl = decl_new_var(tok, type, VARDECL_GLOBAL, visibility); + Decl *decl = decl_new_var(context->tok, type, VARDECL_GLOBAL, visibility); - if (!consume_ident("global variable")) return &poisoned_decl; + if (!consume_ident(context, "global variable")) return &poisoned_decl; - if (try_consume(TOKEN_EQ)) + if (try_consume(context, TOKEN_EQ)) { - decl->var.init_expr = TRY_EXPR_OR(parse_initializer(), &poisoned_decl); + decl->var.init_expr = TRY_EXPR_OR(parse_initializer(context), &poisoned_decl); } TRY_CONSUME_EOS_OR(&poisoned_decl); return decl; @@ -1801,24 +1883,24 @@ static inline Decl *parse_global_declaration(Visibility visibility) * * @return true if parsing succeeded, false if recovery is needed */ -static inline bool parse_attributes(Decl *parent_decl) +static inline bool parse_attributes(Context *context, Decl *parent_decl) { parent_decl->attributes = NULL; - while (tok.type == TOKEN_AT_IDENT || (tok.type == TOKEN_IDENT && next_tok.type == TOKEN_SCOPE)) + while (context->tok.type == TOKEN_AT_IDENT || (context->tok.type == TOKEN_IDENT && context->next_tok.type == TOKEN_SCOPE)) { - Path *path = parse_path_prefix(); + Path *path = parse_path_prefix(context); Attr *attr = malloc_arena(sizeof(Attr)); - attr->name = tok; + attr->name = context->tok; attr->path = path; TRY_CONSUME_OR(TOKEN_AT_IDENT, "Expected an attribute", false); - if (tok.type == TOKEN_LPAREN) + if (context->tok.type == TOKEN_LPAREN) { - attr->expr = TRY_EXPR_OR(parse_paren_expr(), false); + attr->expr = TRY_EXPR_OR(parse_paren_expr(context), false); } const char *name= attr->name.string; VECEACH(parent_decl->attributes, i) @@ -1826,7 +1908,7 @@ static inline bool parse_attributes(Decl *parent_decl) Attr *other_attr = parent_decl->attributes[i]; if (other_attr->name.string == name) { - SEMA_ERROR(attr->name, "Repeat of attribute '%s' here.", name); + SEMA_TOKEN_ERROR(attr->name, "Repeat of attribute '%s' here.", name); return false; } } @@ -1856,71 +1938,71 @@ static inline bool parse_attributes(Decl *parent_decl) * @param parent the direct parent. * @param visible_parent the visible parent when checking duplicate symbols. */ -bool parse_struct_body(Decl *parent, Decl *visible_parent) +bool parse_struct_body(Context *context, Decl *parent, Decl *visible_parent) { CONSUME_OR(TOKEN_LBRACE, false); - while (tok.type != TOKEN_RBRACE) + while (context->tok.type != TOKEN_RBRACE) { - TokenType token_type = tok.type; + TokenType token_type = context->tok.type; if (token_type == TOKEN_STRUCT || token_type == TOKEN_UNION) { DeclKind decl_kind = decl_from_token(token_type); Decl *member; - if (next_tok.type != TOKEN_IDENT) + if (context->next_tok.type != TOKEN_IDENT) { - Token name_replacement = tok; + Token name_replacement = context->tok; name_replacement.string = NULL; member = decl_new_with_type(name_replacement, decl_kind, parent->visibility); - advance(); + advance(context); } else { - advance(); - member = decl_new_with_type(tok, decl_kind, parent->visibility); - Decl *other = struct_find_name(visible_parent, tok.string); + advance(context); + member = decl_new_with_type(context->tok, decl_kind, parent->visibility); + Decl *other = struct_find_name(visible_parent, context->tok.string); if (other) { - SEMA_ERROR(tok, "Duplicate member '%s' found.", tok.string); - sema_prev_at_range(other->name.span, "Previous declaration with the same name was here."); + SEMA_TOKEN_ERROR(context->tok, "Duplicate member '%s' found.", context->tok.string); + SEMA_PREV(other, "Previous declaration with the same name was here."); decl_poison(visible_parent); decl_poison(other); decl_poison(member); } - advance_and_verify(TOKEN_IDENT); + advance_and_verify(context, TOKEN_IDENT); } - if (!parse_attributes(member)) return false; + if (!parse_attributes(context, member)) return false; parent->strukt.members = VECADD(parent->strukt.members, member); - if (!parse_struct_body(member, tok.type == TOKEN_IDENT ? member : visible_parent)) + if (!parse_struct_body(context, member, context->tok.type == TOKEN_IDENT ? member : visible_parent)) { decl_poison(visible_parent); return false; } continue; } - TypeInfo *type = TRY_TYPE_OR(parse_type_expression(), false); + TypeInfo *type = TRY_TYPE_OR(parse_type_expression(context), false); while (1) { EXPECT_OR(TOKEN_IDENT, false); - Decl *member = decl_new_var(tok, type, VARDECL_MEMBER, parent->visibility); - Decl *other = struct_find_name(visible_parent, member->name.string); + Decl *member = decl_new_var(context->tok, type, VARDECL_MEMBER, parent->visibility); + Decl *other = struct_find_name(visible_parent, member->name); if (other) { - SEMA_ERROR(member->name, "Duplicate member '%s' found.", member->name.string); - sema_prev_at_range(other->name.span, "Previous declaration with the same name was here."); + SEMA_ERROR(member, "Duplicate member '%s' found.", member->name); + SEMA_PREV(other, "Previous declaration with the same name was here."); decl_poison(visible_parent); decl_poison(other); decl_poison(member); } parent->strukt.members = VECADD(parent->strukt.members, member); - advance(); - if (tok.type != TOKEN_COMMA) break; + advance(context); + if (context->tok.type != TOKEN_COMMA) break; } CONSUME_OR(TOKEN_EOS, false); } - advance_and_verify(TOKEN_RBRACE); + advance_and_verify(context, TOKEN_RBRACE); return true; } @@ -1932,24 +2014,24 @@ bool parse_struct_body(Decl *parent, Decl *visible_parent) * * @param visibility */ -static inline Decl *parse_struct_declaration(Visibility visibility) +static inline Decl *parse_struct_declaration(Context *context, Visibility visibility) { - TokenType type = tok.type; + TokenType type = context->tok.type; - advance(); + advance(context); const char* type_name = struct_union_name_from_token(type); - Token name = tok; + Token name = context->tok; - if (!consume_type_name(type_name)) return &poisoned_decl; + if (!consume_type_name(context, type_name)) return &poisoned_decl; Decl *decl = decl_new_with_type(name, decl_from_token(type), visibility); - if (!parse_attributes(decl)) + if (!parse_attributes(context, decl)) { return &poisoned_decl; } - if (!parse_struct_body(decl, decl)) + if (!parse_struct_body(context, decl, decl)) { return &poisoned_decl; } @@ -1960,12 +2042,12 @@ static inline Decl *parse_struct_declaration(Visibility visibility) /** * Parse statements up to the next '}', 'case' or 'default' */ -static inline Ast *parse_generics_statements(void) +static inline Ast *parse_generics_statements(Context *context) { - Ast *ast = AST_NEW(AST_COMPOUND_STMT, tok); - while (tok.type != TOKEN_RBRACE && tok.type != TOKEN_CASE && tok.type != TOKEN_DEFAULT) + Ast *ast = AST_NEW_TOKEN(AST_COMPOUND_STMT, context->tok); + while (context->tok.type != TOKEN_RBRACE && context->tok.type != TOKEN_CASE && context->tok.type != TOKEN_DEFAULT) { - Ast *stmt = TRY_AST_OR(parse_stmt(), &poisoned_ast); + Ast *stmt = TRY_AST_OR(parse_stmt(context), &poisoned_ast); ast->compound_stmt.stmts = VECADD(ast->compound_stmt.stmts, stmt); } return ast; @@ -1986,66 +2068,66 @@ static inline Ast *parse_generics_statements(void) * @param visibility * @return */ -static inline Decl *parse_generics_declaration(Visibility visibility) +static inline Decl *parse_generics_declaration(Context *context, Visibility visibility) { - advance_and_verify(TOKEN_GENERIC); + advance_and_verify(context, TOKEN_GENERIC); TypeInfo *rtype = NULL; - if (tok.type != TOKEN_IDENT) + if (context->tok.type != TOKEN_IDENT) { - rtype = TRY_TYPE_OR(parse_type_expression(), &poisoned_decl); + rtype = TRY_TYPE_OR(parse_type_expression(context), &poisoned_decl); } - Path *path = parse_path_prefix(); - Decl *decl = decl_new(DECL_GENERIC, tok, visibility); + Path *path = parse_path_prefix(context); + Decl *decl = decl_new(DECL_GENERIC, context->tok, visibility); decl->generic_decl.path = path; - if (!consume_ident("generic function name")) return &poisoned_decl; + if (!consume_ident(context, "generic function name")) return &poisoned_decl; decl->generic_decl.rtype = rtype; Token *parameters = NULL; CONSUME_OR(TOKEN_LPAREN, &poisoned_decl); - while (!try_consume(TOKEN_RPAREN)) + while (!try_consume(context, TOKEN_RPAREN)) { - if (tok.type != TOKEN_IDENT) + if (context->tok.type != TOKEN_IDENT) { - SEMA_ERROR(tok, "Expected an identifier."); + SEMA_TOKEN_ERROR(context->tok, "Expected an identifier."); return false; } - parameters = VECADD(parameters, tok); - advance(); + parameters = VECADD(parameters, context->tok); + advance(context); COMMA_RPAREN_OR(&poisoned_decl); } CONSUME_OR(TOKEN_LBRACE, &poisoned_decl); Ast **cases = NULL; - while (!try_consume(TOKEN_RBRACE)) + while (!try_consume(context, TOKEN_RBRACE)) { - if (tok.type == TOKEN_CASE) + if (context->tok.type == TOKEN_CASE) { - Ast *generic_case = AST_NEW(AST_GENERIC_CASE_STMT, tok); - advance_and_verify(TOKEN_CASE); + Ast *generic_case = AST_NEW_TOKEN(AST_GENERIC_CASE_STMT, context->tok); + advance_and_verify(context, TOKEN_CASE); TypeInfo **types = NULL; - while (!try_consume(TOKEN_COLON)) + while (!try_consume(context, TOKEN_COLON)) { - TypeInfo *type = TRY_TYPE_OR(parse_type_expression(), &poisoned_decl); + TypeInfo *type = TRY_TYPE_OR(parse_type_expression(context), &poisoned_decl); types = VECADD(types, type); - if (!try_consume(TOKEN_COMMA) && tok.type != TOKEN_COLON) + if (!try_consume(context, TOKEN_COMMA) && context->tok.type != TOKEN_COLON) { - SEMA_ERROR(tok, "Expected ',' or ':'."); + SEMA_TOKEN_ERROR(context->tok, "Expected ',' or ':'."); return &poisoned_decl; } } generic_case->generic_case_stmt.types = types; - generic_case->generic_case_stmt.body = TRY_AST_OR(parse_generics_statements(), &poisoned_decl); + generic_case->generic_case_stmt.body = TRY_AST_OR(parse_generics_statements(context), &poisoned_decl); cases = VECADD(cases, generic_case); continue; } - if (tok.type == TOKEN_DEFAULT) + if (context->tok.type == TOKEN_DEFAULT) { - Ast *generic_case = AST_NEW(AST_GENERIC_DEFAULT_STMT, tok); - advance_and_verify(TOKEN_DEFAULT); + Ast *generic_case = AST_NEW_TOKEN(AST_GENERIC_DEFAULT_STMT, context->tok); + advance_and_verify(context, TOKEN_DEFAULT); CONSUME_OR(TOKEN_COLON, &poisoned_decl); - generic_case->generic_default_stmt = TRY_AST_OR(parse_generics_statements(), &poisoned_decl); + generic_case->generic_default_stmt = TRY_AST_OR(parse_generics_statements(context), &poisoned_decl); cases = VECADD(cases, generic_case); continue; } - SEMA_ERROR(tok, "Expected 'case' or 'default'."); + SEMA_TOKEN_ERROR(context->tok, "Expected 'case' or 'default'."); return &poisoned_decl; } decl->generic_decl.cases = cases; @@ -2061,38 +2143,26 @@ static inline Decl *parse_generics_declaration(Visibility visibility) * | type_expression IDENT '=' initializer * ; */ -static inline bool parse_param_decl(Visibility parent_visibility, Decl*** parameters, bool type_only) +static inline bool parse_param_decl(Context *context, Visibility parent_visibility, Decl*** parameters, bool type_only) { - TypeInfo *type = TRY_TYPE_OR(parse_type_expression(), false); - Decl *param = decl_new_var(tok, type, VARDECL_PARAM, parent_visibility); + TypeInfo *type = TRY_TYPE_OR(parse_type_expression(context), false); + Decl *param = decl_new_var(context->tok, type, VARDECL_PARAM, parent_visibility); - if (!try_consume(TOKEN_IDENT)) + if (!try_consume(context, TOKEN_IDENT)) { - param->name.string = NULL; + param->name = NULL; } - const char *name = param->name.string; + const char *name = param->name; if (!name && !type_only) { - SEMA_ERROR(tok, "The function parameter must be named."); + SEMA_TOKEN_ERROR(context->tok, "The function parameter must be named."); return false; } - if (name && try_consume(TOKEN_EQ)) + if (name && try_consume(context, TOKEN_EQ)) { - param->var.init_expr = TRY_EXPR_OR(parse_initializer(), false); - } - if (param->name.string) - { - VECEACH(*parameters, i) - { - if ((*parameters)[i]->name.string == name) - { - SEMA_ERROR(param->name, "Duplicate parameter name '%s' - parameter %d and %d clash.", - name, i + 1, vec_size(*parameters)); - return false; - } - } + param->var.init_expr = TRY_EXPR_OR(parse_initializer(context), false); } *parameters = VECADD(*parameters, param); @@ -2113,36 +2183,36 @@ static inline bool parse_param_decl(Visibility parent_visibility, Decl*** parame * ; * */ -static inline bool parse_opt_throw_declaration(Visibility visibility, FunctionSignature *signature) +static inline bool parse_opt_throw_declaration(Context *context, Visibility visibility, FunctionSignature *signature) { - if (tok.type == TOKEN_THROW) + if (context->tok.type == TOKEN_THROW) { - SEMA_ERROR(tok, "Did you mean 'throws'?"); + SEMA_TOKEN_ERROR(context->tok, "Did you mean 'throws'?"); return false; } - if (!try_consume(TOKEN_THROWS)) return true; - if (tok.type != TOKEN_TYPE_IDENT) + if (!try_consume(context, TOKEN_THROWS)) return true; + if (context->tok.type != TOKEN_TYPE_IDENT) { VECADD(signature->throws, &all_error); return true; } Decl **throws = NULL; - while (tok.type == TOKEN_TYPE_IDENT) + while (context->tok.type == TOKEN_TYPE_IDENT) { - Decl *error = decl_new(DECL_ERROR, tok, visibility); - advance(); + Decl *error = decl_new(DECL_ERROR, context->tok, visibility); + advance(context); VECADD(throws, error); - if (!try_consume(TOKEN_COMMA)) break; + if (!try_consume(context, TOKEN_COMMA)) break; } - switch (tok.type) + switch (context->tok.type) { case TOKEN_TYPE_IDENT: - SEMA_ERROR(tok, "Expected ',' between each error type."); + SEMA_TOKEN_ERROR(context->tok, "Expected ',' between each error type."); return false; case TOKEN_IDENT: case TOKEN_CONST_IDENT: - SEMA_ERROR(tok, "Expected an error type."); + SEMA_TOKEN_ERROR(context->tok, "Expected an error type."); return false; default: break; @@ -2170,26 +2240,26 @@ static inline bool parse_opt_throw_declaration(Visibility visibility, FunctionSi * ; * */ -static inline bool parse_opt_parameter_type_list(Visibility parent_visibility, FunctionSignature *signature, bool is_interface) +static inline bool parse_opt_parameter_type_list(Context *context, Visibility parent_visibility, FunctionSignature *signature, bool is_interface) { CONSUME_OR(TOKEN_LPAREN, false); - while (!try_consume(TOKEN_RPAREN)) + while (!try_consume(context, TOKEN_RPAREN)) { - if (try_consume(TOKEN_ELIPSIS)) + if (try_consume(context, TOKEN_ELIPSIS)) { signature->variadic = true; } else { - if (!parse_param_decl(parent_visibility, &(signature->params), is_interface)) return false; + if (!parse_param_decl(context, parent_visibility, &(signature->params), is_interface)) return false; } - if (!try_consume(TOKEN_COMMA)) + if (!try_consume(context, TOKEN_COMMA)) { EXPECT_OR(TOKEN_RPAREN, false); } if (signature->variadic) { - SEMA_ERROR(tok, "Variadic arguments should be the last in a parameter list."); + SEMA_TOKEN_ERROR(context->tok, "Variadic arguments should be the last in a parameter list."); return false; } } @@ -2232,32 +2302,32 @@ static AttributeDomains TOKEN_TO_ATTR[TOKEN_EOF + 1] = { * @param visibility * @return Decl* */ -static inline Decl *parse_attribute_declaration(Visibility visibility) +static inline Decl *parse_attribute_declaration(Context *context, Visibility visibility) { - advance_and_verify(TOKEN_ATTRIBUTE); + advance_and_verify(context, TOKEN_ATTRIBUTE); AttributeDomains domains = 0; AttributeDomains last_domain; - last_domain = TOKEN_TO_ATTR[tok.type]; + last_domain = TOKEN_TO_ATTR[context->tok.type]; while (last_domain) { - advance(); + advance(context); if ((domains & last_domain) != 0) { - SEMA_ERROR(tok, "'%s' appeared more than once.", tok.string); + SEMA_TOKEN_ERROR(context->tok, "'%s' appeared more than once.", context->tok.string); continue; } domains |= last_domain; - if (!try_consume(TOKEN_COMMA)) break; - last_domain = TOKEN_TO_ATTR[tok.type]; + if (!try_consume(context, TOKEN_COMMA)) break; + last_domain = TOKEN_TO_ATTR[context->tok.type]; } TRY_CONSUME_OR(TOKEN_AT_IDENT, "Expected an attribute name.", &poisoned_decl); - Decl *decl = decl_new(DECL_ATTRIBUTE, tok, visibility); + Decl *decl = decl_new(DECL_ATTRIBUTE, context->tok, visibility); if (last_domain == 0) { - SEMA_ERROR(tok, "Expected at least one domain for attribute '%s'.", decl->name.string); + SEMA_TOKEN_ERROR(context->tok, "Expected at least one domain for attribute '%s'.", decl->name); return false; } - if (!parse_opt_parameter_type_list(visibility, &decl->attr.attr_signature, false)) return &poisoned_decl; + if (!parse_opt_parameter_type_list(context, visibility, &decl->attr.attr_signature, false)) return &poisoned_decl; TRY_CONSUME_EOS_OR(&poisoned_decl); return decl; } @@ -2271,62 +2341,63 @@ static inline Decl *parse_attribute_declaration(Visibility visibility) * | FUNC type_expression opt_parameter_type_list throw_declaration * ; */ -static inline bool parse_func_typedef(Decl *decl, Visibility visibility) +static inline bool parse_func_typedef(Context *context, Decl *decl, Visibility visibility) { decl->typedef_decl.is_func = true; - advance_and_verify(TOKEN_FUNC); - TypeInfo *type_info = TRY_TYPE_OR(parse_type_expression(), false); + advance_and_verify(context, TOKEN_FUNC); + TypeInfo *type_info = TRY_TYPE_OR(parse_type_expression(context), false); decl->typedef_decl.function_signature.rtype = type_info; - if (!parse_opt_parameter_type_list(visibility, &(decl->typedef_decl.function_signature), true)) + if (!parse_opt_parameter_type_list(context, visibility, &(decl->typedef_decl.function_signature), true)) { return false; } - return parse_opt_throw_declaration(VISIBLE_PUBLIC, &(decl->typedef_decl.function_signature)); + return parse_opt_throw_declaration(context, VISIBLE_PUBLIC, &(decl->typedef_decl.function_signature)); } -static inline Decl *parse_typedef_declaration(Visibility visibility) +static inline Decl *parse_typedef_declaration(Context *context, Visibility visibility) { - advance_and_verify(TOKEN_TYPEDEF); - Decl *decl = decl_new_with_type(tok, DECL_TYPEDEF, visibility); - if (tok.type == TOKEN_FUNC) + advance_and_verify(context, TOKEN_TYPEDEF); + Decl *decl = decl_new_with_type(context->tok, DECL_TYPEDEF, visibility); + if (context->tok.type == TOKEN_FUNC) { - if (!parse_func_typedef(decl, visibility)) return &poisoned_decl; + if (!parse_func_typedef(context, decl, visibility)) return &poisoned_decl; } else { - decl->typedef_decl.type_info = TRY_TYPE_OR(parse_type_expression(), &poisoned_decl); + decl->typedef_decl.type_info = TRY_TYPE_OR(parse_type_expression(context), &poisoned_decl); decl->typedef_decl.is_func = false; } - CONSUME_OR(TOKEN_AS, &poisoned_decl); - decl->name = tok; - decl->type->name = tok.string; - if (!consume_type_name("typedef")) return &poisoned_decl; + CONSUME_OR(TOKEN_AS, &poisoned_decl); + decl->name = context->tok.string; + decl->name_span = context->tok.span; + decl->type->name = context->tok.string; + if (!consume_type_name(context, "typedef")) return &poisoned_decl; CONSUME_OR(TOKEN_EOS, &poisoned_decl); return decl; } -static inline Decl *parse_macro_declaration(Visibility visibility) +static inline Decl *parse_macro_declaration(Context *context, Visibility visibility) { - advance_and_verify(TOKEN_MACRO); + advance_and_verify(context, TOKEN_MACRO); TypeInfo *rtype = NULL; - if (tok.type != TOKEN_AT_IDENT) + if (context->tok.type != TOKEN_AT_IDENT) { - rtype = TRY_TYPE_OR(parse_type_expression(), &poisoned_decl); + rtype = TRY_TYPE_OR(parse_type_expression(context), &poisoned_decl); } - Decl *decl = decl_new(DECL_MACRO, tok, visibility); + Decl *decl = decl_new(DECL_MACRO, context->tok, visibility); decl->macro_decl.rtype = rtype; TRY_CONSUME_OR(TOKEN_AT_IDENT, "Expected a macro name starting with '@'", &poisoned_decl); CONSUME_OR(TOKEN_LPAREN, &poisoned_decl); Decl **params = NULL; - while (!try_consume(TOKEN_RPAREN)) + while (!try_consume(context, TOKEN_RPAREN)) { TypeInfo *parm_type = NULL; TEST_TYPE: - switch (tok.type) + switch (context->tok.type) { case TOKEN_IDENT: case TOKEN_AT_IDENT: @@ -2336,19 +2407,19 @@ static inline Decl *parse_macro_declaration(Visibility visibility) default: if (parm_type) { - SEMA_ERROR(tok, "Expected a macro parameter"); + SEMA_TOKEN_ERROR(context->tok, "Expected a macro parameter"); return &poisoned_decl; } - parm_type = TRY_TYPE_OR(parse_type_expression(), &poisoned_decl); + parm_type = TRY_TYPE_OR(parse_type_expression(context), &poisoned_decl); goto TEST_TYPE; } - Decl *param = decl_new_var(tok, parm_type, VARDECL_PARAM, visibility); - advance(); + Decl *param = decl_new_var(context->tok, parm_type, VARDECL_PARAM, visibility); + advance(context); params = VECADD(params, param); COMMA_RPAREN_OR(&poisoned_decl); } decl->macro_decl.parameters = params; - decl->macro_decl.body = TRY_AST_OR(parse_stmt(), &poisoned_decl); + decl->macro_decl.body = TRY_AST_OR(parse_stmt(context), &poisoned_decl); return decl; } @@ -2375,45 +2446,46 @@ static inline Decl *parse_macro_declaration(Visibility visibility) * @param visibility * @return Decl* */ -static inline Decl *parse_func_definition(Visibility visibility, bool is_interface) +static inline Decl *parse_func_definition(Context *context, Visibility visibility, bool is_interface) { - advance_and_verify(TOKEN_FUNC); + advance_and_verify(context, TOKEN_FUNC); - TypeInfo *return_type = TRY_TYPE_OR(parse_type_expression(), false); + TypeInfo *return_type = TRY_TYPE_OR(parse_type_expression(context), false); - Decl *func = decl_new(DECL_FUNC, tok, visibility); + Decl *func = decl_new(DECL_FUNC, context->tok, visibility); func->func.function_signature.rtype = return_type; - Path *path = parse_path_prefix(); - if (path || tok.type == TOKEN_TYPE_IDENT) + Path *path = parse_path_prefix(context); + if (path || context->tok.type == TOKEN_TYPE_IDENT) { // Special case, actually an extension TRY_EXPECT_OR(TOKEN_TYPE_IDENT, "A type was expected after '::'.", &poisoned_decl); TypeInfo *type = type_info_new(TYPE_INFO_IDENTIFIER); type->unresolved.path = path; - type->unresolved.name_loc = tok; + type->unresolved.name_loc = context->tok; func->func.type_parent = type; - advance_and_verify(TOKEN_TYPE_IDENT); + advance_and_verify(context, TOKEN_TYPE_IDENT); TRY_CONSUME_OR(TOKEN_DOT, "Expected '.' after the type in a method function.", &poisoned_decl); } EXPECT_IDENT_FOR_OR("function name", &poisoned_decl); - func->name = tok; - advance_and_verify(TOKEN_IDENT); + func->name = context->tok.string; + func->name_span = context->tok.span; + advance_and_verify(context, TOKEN_IDENT); - if (!parse_opt_parameter_type_list(visibility, &(func->func.function_signature), is_interface)) return &poisoned_decl; + if (!parse_opt_parameter_type_list(context, visibility, &(func->func.function_signature), is_interface)) return &poisoned_decl; - if (!parse_opt_throw_declaration(visibility, &(func->func.function_signature))) return &poisoned_decl; + if (!parse_opt_throw_declaration(context, visibility, &(func->func.function_signature))) return &poisoned_decl; // TODO remove - is_interface = tok.type == TOKEN_EOS; + is_interface = context->tok.type == TOKEN_EOS; if (is_interface) { - if (tok.type == TOKEN_LBRACE) + if (context->tok.type == TOKEN_LBRACE) { - SEMA_ERROR(next_tok, "Functions bodies are not allowed in interface files."); + SEMA_TOKEN_ERROR(context->next_tok, "Functions bodies are not allowed in interface files."); return &poisoned_decl; } TRY_CONSUME_OR(TOKEN_EOS, "Expected ';' after function declaration.", &poisoned_decl); @@ -2422,9 +2494,9 @@ static inline Decl *parse_func_definition(Visibility visibility, bool is_interfa TRY_EXPECT_OR(TOKEN_LBRACE, "Expected the beginning of a block with '{'", &poisoned_decl); - func->func.body = TRY_AST_OR(parse_compound_stmt(), &poisoned_decl); + func->func.body = TRY_AST_OR(parse_compound_stmt(context), &poisoned_decl); - DEBUG_LOG("Finished parsing function %s", func->name.string); + DEBUG_LOG("Finished parsing function %s", func->name); return func; } @@ -2434,40 +2506,40 @@ static inline Decl *parse_func_definition(Visibility visibility, bool is_interfa * ; * */ -static inline Decl *parse_error_declaration(Visibility visibility) +static inline Decl *parse_error_declaration(Context *context, Visibility visibility) { - advance_and_verify(TOKEN_ERROR_TYPE); + advance_and_verify(context, TOKEN_ERROR_TYPE); - Decl *error_decl = decl_new_with_type(tok, DECL_ERROR, visibility); + Decl *error_decl = decl_new_with_type(context->tok, DECL_ERROR, visibility); - if (!consume_type_name("error type")) return &poisoned_decl; + if (!consume_type_name(context, "error type")) return &poisoned_decl; CONSUME_OR(TOKEN_LBRACE, &poisoned_decl); - while (tok.type == TOKEN_CONST_IDENT) + while (context->tok.type == TOKEN_CONST_IDENT) { - Decl *err_constant = decl_new(DECL_ERROR_CONSTANT, tok, error_decl->visibility); + Decl *err_constant = decl_new(DECL_ERROR_CONSTANT, context->tok, error_decl->visibility); err_constant->error_constant.parent = error_decl; VECEACH(error_decl->error.error_constants, i) { Decl *other_constant = error_decl->error.error_constants[i]; - if (other_constant->name.string == tok.string) + if (other_constant->name == context->tok.string) { - SEMA_ERROR(tok, "This error is declared twice."); - sema_prev_at_range(other_constant->name.span, "The previous declaration was here."); + SEMA_TOKEN_ERROR(context->tok, "This error is declared twice."); + SEMA_PREV(other_constant, "The previous declaration was here."); decl_poison(err_constant); decl_poison(error_decl); break; } } error_decl->error.error_constants = VECADD(error_decl->error.error_constants, err_constant); - advance_and_verify(TOKEN_CONST_IDENT); - if (!try_consume(TOKEN_COMMA)) break; + advance_and_verify(context, TOKEN_CONST_IDENT); + if (!try_consume(context, TOKEN_COMMA)) break; } - if (tok.type == TOKEN_TYPE_IDENT || tok.type == TOKEN_IDENT) + if (context->tok.type == TOKEN_TYPE_IDENT || context->tok.type == TOKEN_IDENT) { - SEMA_ERROR(tok, "Errors must be all upper case."); + SEMA_TOKEN_ERROR(context->tok, "Errors must be all upper case."); return &poisoned_decl; } CONSUME_OR(TOKEN_RBRACE, &poisoned_decl); @@ -2483,49 +2555,49 @@ static inline Decl *parse_error_declaration(Visibility visibility) * * TODO enum extra data? */ -static inline Decl *parse_enum_declaration(Visibility visibility) +static inline Decl *parse_enum_declaration(Context *context, Visibility visibility) { - advance_and_verify(TOKEN_ENUM); + advance_and_verify(context, TOKEN_ENUM); - Decl *decl = decl_new_with_type(tok, DECL_ENUM, visibility); + Decl *decl = decl_new_with_type(context->tok, DECL_ENUM, visibility); - if (!consume_type_name("enum")) return &poisoned_decl; + if (!consume_type_name(context, "enum")) return &poisoned_decl; TypeInfo *type = NULL; - if (try_consume(TOKEN_COLON)) + if (try_consume(context, TOKEN_COLON)) { - type = TRY_TYPE_OR(parse_base_type(), &poisoned_decl); + type = TRY_TYPE_OR(parse_base_type(context), &poisoned_decl); } CONSUME_OR(TOKEN_LBRACE, false); decl->enums.type_info = type ? type : type_info_new_base(type_int); - while (!try_consume(TOKEN_RBRACE)) + while (!try_consume(context, TOKEN_RBRACE)) { - Decl *enum_const = decl_new(DECL_ENUM_CONSTANT, tok, decl->visibility); + Decl *enum_const = decl_new(DECL_ENUM_CONSTANT, context->tok, decl->visibility); enum_const->enum_constant.parent = decl; VECEACH(decl->enums.values, i) { Decl *other_constant = decl->enums.values[i]; - if (other_constant->name.string == tok.string) + if (other_constant->name == context->tok.string) { - SEMA_ERROR(tok, "This enum constant is declared twice."); - sema_prev_at_range(other_constant->name.span, "The previous declaration was here."); + SEMA_TOKEN_ERROR(context->tok, "This enum constant is declared twice."); + SEMA_PREV(other_constant, "The previous declaration was here."); decl_poison(enum_const); break; } } - if (!consume_const_name("enum constant")) + if (!consume_const_name(context, "enum constant")) { return &poisoned_decl; } - if (try_consume(TOKEN_EQ)) + if (try_consume(context, TOKEN_EQ)) { - enum_const->enum_constant.expr = TRY_EXPR_OR(parse_expr(), &poisoned_decl); + enum_const->enum_constant.expr = TRY_EXPR_OR(parse_expr(context), &poisoned_decl); } decl->enums.values = VECADD(decl->enums.values, enum_const); // Allow trailing ',' - if (!try_consume(TOKEN_COMMA)) + if (!try_consume(context, TOKEN_COMMA)) { EXPECT_OR(TOKEN_RBRACE, &poisoned_decl); } @@ -2533,12 +2605,12 @@ static inline Decl *parse_enum_declaration(Visibility visibility) return decl; } -static inline bool parse_conditional_top_level(Decl ***decls) +static inline bool parse_conditional_top_level(Context *context, Decl ***decls) { CONSUME_OR(TOKEN_LBRACE, false); - while (tok.type != TOKEN_RBRACE && tok.type != TOKEN_EOF) + while (context->tok.type != TOKEN_RBRACE && context->tok.type != TOKEN_EOF) { - Decl *decl = parse_top_level(); + Decl *decl = parse_top_level(context); if (decl == NULL) continue; if (decl_ok(decl)) { @@ -2546,64 +2618,64 @@ static inline bool parse_conditional_top_level(Decl ***decls) } else { - recover_top_level(); + recover_top_level(context); } } CONSUME_OR(TOKEN_RBRACE, false); return true; } -static inline Decl *parse_ct_if_top_level(void) +static inline Decl *parse_ct_if_top_level(Context *context) { - Decl *ct = decl_new(DECL_CT_IF, tok, VISIBLE_LOCAL); - advance_and_verify(TOKEN_CT_IF); - ct->ct_if_decl.expr = TRY_EXPR_OR(parse_paren_expr(), &poisoned_decl); + Decl *ct = decl_new(DECL_CT_IF, context->tok, VISIBLE_LOCAL); + advance_and_verify(context, TOKEN_CT_IF); + ct->ct_if_decl.expr = TRY_EXPR_OR(parse_paren_expr(context), &poisoned_decl); - if (!parse_conditional_top_level(&ct->ct_if_decl.then)) return &poisoned_decl; + if (!parse_conditional_top_level(context, &ct->ct_if_decl.then)) return &poisoned_decl; CtIfDecl *ct_if_decl = &ct->ct_if_decl; - while (tok.type == TOKEN_CT_ELIF) + while (context->tok.type == TOKEN_CT_ELIF) { - advance_and_verify(TOKEN_CT_ELIF); - Decl *ct_elif = decl_new(DECL_CT_ELIF, tok, VISIBLE_LOCAL); - ct_elif->ct_elif_decl.expr = TRY_EXPR_OR(parse_paren_expr(), &poisoned_decl); - if (!parse_conditional_top_level(&ct_elif->ct_elif_decl.then)) return &poisoned_decl; + advance_and_verify(context, TOKEN_CT_ELIF); + Decl *ct_elif = decl_new(DECL_CT_ELIF, context->tok, VISIBLE_LOCAL); + ct_elif->ct_elif_decl.expr = TRY_EXPR_OR(parse_paren_expr(context), &poisoned_decl); + if (!parse_conditional_top_level(context, &ct_elif->ct_elif_decl.then)) return &poisoned_decl; ct_if_decl->elif = ct_elif; ct_if_decl = &ct_elif->ct_elif_decl; } - if (tok.type == TOKEN_CT_ELSE) + if (context->tok.type == TOKEN_CT_ELSE) { - advance_and_verify(TOKEN_CT_ELSE); - Decl *ct_else = decl_new(DECL_CT_ELSE, tok, VISIBLE_LOCAL); + advance_and_verify(context, TOKEN_CT_ELSE); + Decl *ct_else = decl_new(DECL_CT_ELSE, context->tok, VISIBLE_LOCAL); ct_if_decl->elif = ct_else; - if (!parse_conditional_top_level(&ct_else->ct_else_decl)) return &poisoned_decl; + if (!parse_conditional_top_level(context, &ct_else->ct_else_decl)) return &poisoned_decl; } return ct; } -static inline Decl *parse_incremental_array(void) +static inline Decl *parse_incremental_array(Context *context) { - Token name = tok; - advance_and_verify(TOKEN_IDENT); + Token name = context->tok; + advance_and_verify(context, TOKEN_IDENT); CONSUME_OR(TOKEN_PLUS_ASSIGN, &poisoned_decl); Decl *decl = decl_new(DECL_ARRAY_VALUE, name, VISIBLE_LOCAL); - decl->incr_array_decl = TRY_EXPR_OR(parse_initializer(), &poisoned_decl); + decl->incr_array_decl = TRY_EXPR_OR(parse_initializer(context), &poisoned_decl); return decl; } -static inline bool check_no_visibility_before(Visibility visibility) +static inline bool check_no_visibility_before(Context *context, Visibility visibility) { switch (visibility) { case VISIBLE_PUBLIC: - SEMA_ERROR(tok, "Unexpected 'public' before '%.*s'.", tok.span.length, tok.start); + SEMA_TOKEN_ERROR(context->tok, "Unexpected 'public' before '%.*s'.", source_range_len(context->tok.span), context->tok.start); return false; case VISIBLE_LOCAL: - SEMA_ERROR(tok, "Unexpected 'local' before '%.*s'.", tok.span.length, tok.start); + SEMA_TOKEN_ERROR(context->tok, "Unexpected 'local' before '%.*s'.", source_range_len(context->tok.span), context->tok.start); return false; case VISIBLE_EXTERN: - SEMA_ERROR(tok, "Unexpected 'extern' before '%.*s'.", tok.span.length, tok.start); + SEMA_TOKEN_ERROR(context->tok, "Unexpected 'extern' before '%.*s'.", source_range_len(context->tok.span), context->tok.start); return false; default: return true; @@ -2628,136 +2700,135 @@ static inline bool check_no_visibility_before(Visibility visibility) * @param visibility * @return true if parsing worked */ -static inline Decl *parse_top_level(void) +static inline Decl *parse_top_level(Context *context) { Visibility visibility = VISIBLE_MODULE; - switch (tok.type) + switch (context->tok.type) { case TOKEN_PUBLIC: visibility = VISIBLE_PUBLIC; - advance(); + advance(context); break; case TOKEN_LOCAL: visibility = VISIBLE_LOCAL; - advance(); + advance(context); break; case TOKEN_EXTERN: visibility = VISIBLE_EXTERN; - advance(); + advance(context); default: break; } - switch (tok.type) + switch (context->tok.type) { case TOKEN_ATTRIBUTE: - return parse_attribute_declaration(visibility); + return parse_attribute_declaration(context, visibility); case TOKEN_FUNC: - return parse_func_definition(visibility, false); + return parse_func_definition(context, visibility, false); case TOKEN_CT_IF: - if (!check_no_visibility_before(visibility)) return false; - return parse_ct_if_top_level(); + if (!check_no_visibility_before(context, visibility)) return false; + return parse_ct_if_top_level(context); case TOKEN_CONST: - return parse_const_declaration(visibility); + return parse_const_declaration(context, visibility); case TOKEN_STRUCT: case TOKEN_UNION: - return parse_struct_declaration(visibility); + return parse_struct_declaration(context, visibility); case TOKEN_GENERIC: - return parse_generics_declaration(visibility); + return parse_generics_declaration(context, visibility); case TOKEN_MACRO: - return parse_macro_declaration(visibility); + return parse_macro_declaration(context, visibility); case TOKEN_ENUM: - return parse_enum_declaration(visibility); + return parse_enum_declaration(context, visibility); case TOKEN_ERROR_TYPE: - return parse_error_declaration(visibility); + return parse_error_declaration(context, visibility); case TOKEN_TYPEDEF: - return parse_typedef_declaration(visibility); + return parse_typedef_declaration(context, visibility); case TOKEN_TYPE: case TOKEN_TYPE_IDENT: // All of these start type - return parse_global_declaration(visibility); + return parse_global_declaration(context, visibility); case TOKEN_IDENT: - if (!check_no_visibility_before(visibility)) return false; - return parse_incremental_array(); + if (!check_no_visibility_before(context, visibility)) return false; + return parse_incremental_array(context); case TOKEN_EOF: assert(visibility != VISIBLE_MODULE); - sema_error_at(tok.span.loc - 1, "Expected a top level declaration'."); + sema_error_at(context->tok.span.loc - 1, "Expected a top level declaration'."); return &poisoned_decl; default: // We could have included all fundamental types above, but do it here instead. - if (token_is_type(tok.type)) + if (token_is_type(context->tok.type)) { - return parse_global_declaration(visibility); + return parse_global_declaration(context, visibility); } - error_at_current("Unexpected token found"); + error_at_current(context, "Unexpected token found"); return &poisoned_decl; } } -void parse_current(void) +void parse_current(Context *context) { // Prime everything - advance(); advance(); - parse_module(); - parse_imports(); - while (tok.type != TOKEN_EOF) + advance(context); advance(context); + parse_module(context); + parse_imports(context); + while (context->tok.type != TOKEN_EOF) { - Decl *decl = parse_top_level(); + Decl *decl = parse_top_level(context); if (decl_ok(decl)) { - context_register_global_decl(current_context, decl); + context_register_global_decl(context, decl); } else { - recover_top_level(); + recover_top_level(context); } } } void parse_file(Context *context) { - lexer_add_file_for_lexing(context->file); - context_push(context); - parse_current(); + lexer_add_file_for_lexing(&context->lexer, context->file); + parse_current(context); } #define CHECK_EXPR(_expr) do { if (!expr_ok(_expr)) return _expr; } while(0) -static Expr *parse_ternary_expr(Expr *left_side) +static Expr *parse_ternary_expr(Context *context, Expr *left_side) { assert(expr_ok(left_side)); Expr *expr_ternary = EXPR_NEW_EXPR(EXPR_TERNARY, left_side); expr_ternary->ternary_expr.cond = left_side; // Check for elvis - if (try_consume(TOKEN_ELVIS)) + if (try_consume(context, TOKEN_ELVIS)) { expr_ternary->ternary_expr.then_expr = NULL; } else { - advance_and_verify(TOKEN_QUESTION); - Expr *true_expr = TRY_EXPR_OR(parse_precedence(PREC_TERNARY + 1), &poisoned_expr); + advance_and_verify(context, TOKEN_QUESTION); + Expr *true_expr = TRY_EXPR_OR(parse_precedence(context, PREC_TERNARY + 1), &poisoned_expr); expr_ternary->ternary_expr.then_expr = true_expr; CONSUME_OR(TOKEN_COLON, &poisoned_expr); } - Expr *false_expr = TRY_EXPR_OR(parse_precedence(PREC_TERNARY + 1), &poisoned_expr); + Expr *false_expr = TRY_EXPR_OR(parse_precedence(context, PREC_TERNARY + 1), &poisoned_expr); expr_ternary->ternary_expr.else_expr = false_expr; return expr_ternary; } -static Expr *parse_unary_expr(Expr *left) +static Expr *parse_unary_expr(Context *context, Expr *left) { assert(!left && "Did not expect a left hand side!"); - TokenType operator_type = tok.type; + TokenType operator_type = context->tok.type; - Expr *unary = EXPR_NEW_TOKEN(EXPR_UNARY, tok); + Expr *unary = EXPR_NEW_TOKEN(EXPR_UNARY, context->tok); unary->unary_expr.operator = unaryop_from_token(operator_type); Precedence rule_precedence = rules[operator_type].precedence; - advance(); - Expr *right_side = parse_precedence(rule_precedence); + advance(context); + Expr *right_side = parse_precedence(context, rule_precedence); CHECK_EXPR(right_side); @@ -2765,13 +2836,13 @@ static Expr *parse_unary_expr(Expr *left) return unary; } -static Expr *parse_post_unary(Expr *left) +static Expr *parse_post_unary(Context *context, Expr *left) { assert(expr_ok(left)); - Expr *unary = EXPR_NEW_TOKEN(EXPR_POST_UNARY, tok); + Expr *unary = EXPR_NEW_TOKEN(EXPR_POST_UNARY, context->tok); unary->post_expr.expr = left; - unary->post_expr.operator = post_unaryop_from_token(tok.type); - advance(); + unary->post_expr.operator = post_unaryop_from_token(context->tok.type); + advance(context); return unary; } @@ -2780,32 +2851,32 @@ static Expr *parse_post_unary(Expr *left) * : '(' expression ')' * ; */ -static Expr *parse_grouping_expr(Expr *left) +static Expr *parse_grouping_expr(Context *context, Expr *left) { assert(!left && "Unexpected left hand side"); - advance_and_verify(TOKEN_LPAREN); - Expr *right = TRY_EXPR_OR(parse_expr(), &poisoned_expr); + advance_and_verify(context, TOKEN_LPAREN); + Expr *right = TRY_EXPR_OR(parse_expr(context), &poisoned_expr); CONSUME_OR(TOKEN_RPAREN, &poisoned_expr); return right; } -static Expr *parse_binary(Expr *left_side) +static Expr *parse_binary(Context *context, Expr *left_side) { assert(left_side && expr_ok(left_side)); // Remember the operator. - TokenType operator_type = tok.type; + TokenType operator_type = context->tok.type; - advance(); + advance(context); Expr *right_side; - if (tok.type == TOKEN_LBRACE && operator_type == TOKEN_EQ) + if (context->tok.type == TOKEN_LBRACE && operator_type == TOKEN_EQ) { - right_side = TRY_EXPR_OR(parse_initializer_list(), &poisoned_expr); + right_side = TRY_EXPR_OR(parse_initializer_list(context), &poisoned_expr); } else { - right_side = TRY_EXPR_OR(parse_precedence(rules[operator_type].precedence + 1), &poisoned_expr); + right_side = TRY_EXPR_OR(parse_precedence(context, rules[operator_type].precedence + 1), &poisoned_expr); } Expr *expr = EXPR_NEW_EXPR(EXPR_BINARY, left_side); @@ -2815,16 +2886,16 @@ static Expr *parse_binary(Expr *left_side) return expr; } -static Expr *parse_call_expr(Expr *left) +static Expr *parse_call_expr(Context *context, Expr *left) { assert(left && expr_ok(left)); - advance_and_verify(TOKEN_LPAREN); + advance_and_verify(context, TOKEN_LPAREN); Expr **params = NULL; - while (!try_consume(TOKEN_RPAREN)) + while (!try_consume(context, TOKEN_RPAREN)) { - Expr *param = TRY_EXPR_OR(parse_expr(), &poisoned_expr); + Expr *param = TRY_EXPR_OR(parse_expr(context), &poisoned_expr); params = VECADD(params, param); COMMA_RPAREN_OR(&poisoned_expr); } @@ -2835,12 +2906,12 @@ static Expr *parse_call_expr(Expr *left) } -static Expr *parse_subscript_expr(Expr *left) +static Expr *parse_subscript_expr(Context *context, Expr *left) { assert(left && expr_ok(left)); - advance_and_verify(TOKEN_LBRACKET); - Expr *index = TRY_EXPR_OR(parse_expr(), &poisoned_expr); + advance_and_verify(context, TOKEN_LBRACKET); + Expr *index = TRY_EXPR_OR(parse_expr(context), &poisoned_expr); CONSUME_OR(TOKEN_RBRACKET, &poisoned_expr); Expr *subscript_ast = EXPR_NEW_EXPR(EXPR_SUBSCRIPT, left); subscript_ast->subscript_expr.expr = left; @@ -2849,13 +2920,13 @@ static Expr *parse_subscript_expr(Expr *left) } -static Expr *parse_access_expr(Expr *left) +static Expr *parse_access_expr(Context *context, Expr *left) { assert(left && expr_ok(left)); - advance_and_verify(TOKEN_DOT); + advance_and_verify(context, TOKEN_DOT); Expr *access_expr = EXPR_NEW_EXPR(EXPR_ACCESS, left); access_expr->access_expr.parent = left; - access_expr->access_expr.sub_element = tok; + access_expr->access_expr.sub_element = context->tok; TRY_CONSUME_OR(TOKEN_IDENT, "Expected identifier", &poisoned_expr); return access_expr; } @@ -2952,32 +3023,32 @@ static int append_esc_string_token(char *restrict dest, const char *restrict src return scanned; } -static Expr *parse_string_literal(Expr *left) +static Expr *parse_string_literal(Context *context, Expr *left) { assert(!left && "Had left hand side"); - Expr *expr_string = EXPR_NEW_TOKEN(EXPR_CONST, tok); + Expr *expr_string = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); expr_string->resolve_status = RESOLVE_DONE; expr_string->type = type_string; char *str = NULL; size_t len = 0; - while (tok.type == TOKEN_STRING) + while (context->tok.type == TOKEN_STRING) { - char *new_string = malloc_arena(len + tok.span.length); + char *new_string = malloc_arena(len + source_range_len(context->tok.span)); if (str) memcpy(new_string, str, len); str = new_string; - for (unsigned i = 1; i < tok.span.length - 1; i++) + for (unsigned i = 1; i < source_range_len(context->tok.span) - 1; i++) { - if (tok.string[i] == '\\') + if (context->tok.string[i] == '\\') { i++; - i += append_esc_string_token(str, tok.string + i, &len) - 1; + i += append_esc_string_token(str, context->tok.string + i, &len) - 1; continue; } - str[len++] = tok.string[i]; + str[len++] = context->tok.string[i]; } - advance_and_verify(TOKEN_STRING); + advance_and_verify(context, TOKEN_STRING); } assert(str); @@ -2991,12 +3062,12 @@ static Expr *parse_string_literal(Expr *left) -static Expr *parse_integer(Expr *left) +static Expr *parse_integer(Context *context, Expr *left) { assert(!left && "Had left hand side"); - Expr *expr_int = EXPR_NEW_TOKEN(EXPR_CONST, tok); - const char *string = tok.start; - const char *end = string + tok.span.length; + Expr *expr_int = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); + const char *string = context->tok.start; + const char *end = string + source_range_len(context->tok.span); uint64_t i = 0; if (string[0] == '\'') { @@ -3057,10 +3128,10 @@ static Expr *parse_integer(Expr *left) expr_int->const_expr.type = CONST_INT; expr_int->type = i > INT64_MAX ? type_compuint : type_compint; expr_int->resolve_status = RESOLVE_DONE; - advance(); + advance(context); return expr_int; } - switch (tok.span.length > 2 ? string[1] : '0') + switch (source_range_len(context->tok.span) > 2 ? string[1] : '0') { case 'x': string += 2; @@ -3070,7 +3141,7 @@ static Expr *parse_integer(Expr *left) if (c == '_') continue; if (i > (UINT64_MAX >> 4u)) { - SEMA_ERROR(tok, "Number is larger than an unsigned 64 bit number."); + SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number."); return &poisoned_expr; } i <<= 4u; @@ -3096,7 +3167,7 @@ static Expr *parse_integer(Expr *left) if (c == '_') continue; if (i > (UINT64_MAX >> 3U)) { - SEMA_ERROR(tok, "Number is larger than an unsigned 64 bit number."); + SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number."); return &poisoned_expr; } i <<= (unsigned) 3; @@ -3111,7 +3182,7 @@ static Expr *parse_integer(Expr *left) if (c == '_') continue; if (i > (UINT64_MAX >> 1U)) { - SEMA_ERROR(tok, "Number is larger than an unsigned 64 bit number."); + SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number."); return &poisoned_expr; } i <<= (unsigned) 1; @@ -3125,7 +3196,7 @@ static Expr *parse_integer(Expr *left) if (c == '_') continue; if (i > (UINT64_MAX / 10)) { - SEMA_ERROR(tok, "Number is larger than an unsigned 64 bit number."); + SEMA_TOKEN_ERROR(context->tok, "Number is larger than an unsigned 64 bit number."); return &poisoned_expr; } i *= 10; @@ -3137,48 +3208,48 @@ static Expr *parse_integer(Expr *left) expr_int->const_expr.i = i; expr_int->const_expr.type = CONST_INT; expr_int->type = i > INT64_MAX ? type_compuint : type_compint; - advance(); + advance(context); return expr_int; } -static Expr *parse_double(Expr *left) +static Expr *parse_double(Context *context, Expr *left) { assert(!left && "Had left hand side"); - Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, tok); + Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); char *end = NULL; // IMPROVE - long double fval = strtold(tok.start, &end); - if (end != tok.span.length + tok.start) + long double fval = strtold(context->tok.start, &end); + if (end != source_range_len(context->tok.span) + context->tok.start) { - SEMA_ERROR(tok, "Invalid float value"); + SEMA_TOKEN_ERROR(context->tok, "Invalid float value"); return &poisoned_expr; } - advance(); + advance(context); number->const_expr.f = fval; number->type = type_compfloat; number->const_expr.type = CONST_FLOAT; return number; } -static Expr *parse_bool(Expr *left) +static Expr *parse_bool(Context *context, Expr *left) { assert(!left && "Had left hand side"); - Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, tok); - number->const_expr = (ExprConst) { .b = tok.type == TOKEN_TRUE, .type = CONST_BOOL }; + Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); + number->const_expr = (ExprConst) { .b = context->tok.type == TOKEN_TRUE, .type = CONST_BOOL }; number->type = type_bool; number->resolve_status = RESOLVE_DONE; - advance(); + advance(context); return number; } -static Expr *parse_nil(Expr *left) +static Expr *parse_nil(Context *context, Expr *left) { assert(!left && "Had left hand side"); - Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, tok); + Expr *number = EXPR_NEW_TOKEN(EXPR_CONST, context->tok); number->const_expr.type = CONST_NIL; number->type = type_voidptr; - advance(); + advance(context); return number; } @@ -3198,32 +3269,32 @@ static Expr *parse_nil(Expr *left) * @param elements * @return */ -static Expr *parse_initializer_list(void) +static Expr *parse_initializer_list(Context *context) { - Expr *initializer_list = EXPR_NEW_TOKEN(EXPR_INITIALIZER_LIST, tok); + Expr *initializer_list = EXPR_NEW_TOKEN(EXPR_INITIALIZER_LIST, context->tok); CONSUME_OR(TOKEN_LBRACE, &poisoned_expr); - while (!try_consume(TOKEN_RBRACE)) + while (!try_consume(context, TOKEN_RBRACE)) { - Expr *expr = TRY_EXPR_OR(parse_initializer(), &poisoned_expr); + Expr *expr = TRY_EXPR_OR(parse_initializer(context), &poisoned_expr); initializer_list->initializer_expr = VECADD(initializer_list->initializer_expr, expr); - if (!try_consume(TOKEN_COMMA) && tok.type != TOKEN_RBRACE) + if (!try_consume(context, TOKEN_COMMA) && context->tok.type != TOKEN_RBRACE) { - SEMA_ERROR(tok, "Expected ',' or '}'"); + SEMA_TOKEN_ERROR(context->tok, "Expected ',' or '}'"); return &poisoned_expr; } } return initializer_list; } -static Expr *parse_initializer(void) +static Expr *parse_initializer(Context *context) { - if (tok.type == TOKEN_LBRACE) + if (context->tok.type == TOKEN_LBRACE) { - return parse_initializer_list(); + return parse_initializer_list(context); } else { - return parse_expr(); + return parse_expr(context); } } @@ -3236,41 +3307,41 @@ static Expr *parse_initializer(void) * @param type * @return Expr */ -static Expr *parse_type_access(TypeInfo *type) +static Expr *parse_type_access(Context *context, TypeInfo *type) { - Expr *expr = EXPR_NEW_TOKEN(EXPR_TYPE_ACCESS, tok); + Expr *expr = EXPR_NEW_TOKEN(EXPR_TYPE_ACCESS, context->tok); expr->type_access.type = type; - advance_and_verify(TOKEN_DOT); - expr->type_access.name = tok; + advance_and_verify(context, TOKEN_DOT); + expr->type_access.name = context->tok; - switch (tok.type) + switch (context->tok.type) { case TOKEN_MACRO: case TOKEN_IDENT: case TOKEN_CONST_IDENT: - advance(); + advance(context); return expr; default: - SEMA_ERROR(tok, "Expected a function name, macro, or constant."); + SEMA_TOKEN_ERROR(context->tok, "Expected a function name, macro, or constant."); return &poisoned_expr; } } -static Expr *parse_identifier_with_path(Path *path) +static Expr *parse_identifier_with_path(Context *context, Path *path) { - Expr *expr = EXPR_NEW_TOKEN(EXPR_IDENTIFIER, tok); - expr->identifier_expr.identifier = tok; + Expr *expr = EXPR_NEW_TOKEN(EXPR_IDENTIFIER, context->tok); + expr->identifier_expr.identifier = context->tok.string; expr->identifier_expr.path = path; - advance(); + advance(context); return expr; } -static Expr *parse_identifier(Expr *left) +static Expr *parse_identifier(Context *context, Expr *left) { assert(!left && "Unexpected left hand side"); - return parse_identifier_with_path(NULL); + return parse_identifier_with_path(context, NULL); } @@ -3283,74 +3354,74 @@ static Expr *parse_identifier(Expr *left) * @param left must be null. * @return Expr* */ -static Expr *parse_type_identifier_with_path(Path *path) +static Expr *parse_type_identifier_with_path(Context *context, Path *path) { TypeInfo *type = type_info_new(TYPE_INFO_IDENTIFIER); type->unresolved.path = path; - type->unresolved.name_loc = tok; - advance_and_verify(TOKEN_TYPE_IDENT); - if (tok.type == TOKEN_LBRACE) + type->unresolved.name_loc = context->tok; + advance_and_verify(context, TOKEN_TYPE_IDENT); + if (context->tok.type == TOKEN_LBRACE) { - Expr *expr = EXPR_NEW_TOKEN(EXPR_STRUCT_VALUE, tok); + Expr *expr = EXPR_NEW_TOKEN(EXPR_STRUCT_VALUE, context->tok); expr->struct_value_expr.type = type; - expr->struct_value_expr.init_expr = TRY_EXPR_OR(parse_initializer_list(), &poisoned_expr); + expr->struct_value_expr.init_expr = TRY_EXPR_OR(parse_initializer_list(context), &poisoned_expr); return expr; } EXPECT_OR(TOKEN_DOT, &poisoned_expr); - return parse_type_access(type); + return parse_type_access(context, type); } /** * @param left must be null. * @return Expr* */ -static Expr *parse_type_identifier(Expr *left) +static Expr *parse_type_identifier(Context *context, Expr *left) { assert(!left && "Unexpected left hand side"); - return parse_type_identifier_with_path(NULL); + return parse_type_identifier_with_path(context, NULL); } -static Expr *parse_maybe_scope(Expr *left) +static Expr *parse_maybe_scope(Context *context, Expr *left) { assert(!left && "Unexpected left hand side"); - Path *path = parse_path_prefix(); - switch (tok.type) + Path *path = parse_path_prefix(context); + switch (context->tok.type) { case TOKEN_IDENT: case TOKEN_CT_IDENT: case TOKEN_AT_IDENT: case TOKEN_CONST_IDENT: - return parse_identifier_with_path(path); + return parse_identifier_with_path(context, path); case TOKEN_TYPE_IDENT: - return parse_type_identifier_with_path(path); + return parse_type_identifier_with_path(context, path); default: - SEMA_ERROR(tok, "Expected a type, function or constant."); + SEMA_TOKEN_ERROR(context->tok, "Expected a type, function or constant."); return &poisoned_expr; } } -static Expr *parse_type_expr(Expr *left) +static Expr *parse_type_expr(Context *context, Expr *left) { assert(!left && "Unexpected left hand side"); - Expr *expr = EXPR_NEW_TOKEN(EXPR_TYPE, tok); - advance_and_verify(TOKEN_TYPE); + Expr *expr = EXPR_NEW_TOKEN(EXPR_TYPE, context->tok); + advance_and_verify(context, TOKEN_TYPE); CONSUME_OR(TOKEN_LPAREN, &poisoned_expr); - TypeInfo *type = TRY_TYPE_OR(parse_type_expression(), &poisoned_expr); + TypeInfo *type = TRY_TYPE_OR(parse_type_expression(context), &poisoned_expr); CONSUME_OR(TOKEN_RPAREN, &poisoned_expr); expr->type_expr.type = type; return expr; } -static Expr *parse_cast_expr(Expr *left) +static Expr *parse_cast_expr(Context *context, Expr *left) { assert(!left && "Unexpected left hand side"); - Expr *expr = EXPR_NEW_TOKEN(EXPR_CAST, tok); - advance_and_verify(TOKEN_CAST); + Expr *expr = EXPR_NEW_TOKEN(EXPR_CAST, context->tok); + advance_and_verify(context, TOKEN_CAST); CONSUME_OR(TOKEN_LPAREN, &poisoned_expr); - expr->cast_expr.type_info = TRY_TYPE_OR(parse_type_expression(), &poisoned_expr); + expr->cast_expr.type_info = TRY_TYPE_OR(parse_type_expression(context), &poisoned_expr); CONSUME_OR(TOKEN_COMMA, &poisoned_expr); - expr->cast_expr.expr = TRY_EXPR_OR(parse_expr(), &poisoned_expr); + expr->cast_expr.expr = TRY_EXPR_OR(parse_expr(context), &poisoned_expr); CONSUME_OR(TOKEN_RPAREN, &poisoned_expr); return expr; } diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 62ecba834..b02e6bb53 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -67,7 +67,7 @@ Decl *sema_resolve_symbol(Context *context, const char *symbol, Path *path, Decl Decl **current = context->last_local - 1; while (current >= first) { - if (current[0]->name.string == symbol) return current[0]; + if (current[0]->name == symbol) return current[0]; current--; } } @@ -108,7 +108,7 @@ Decl *sema_resolve_symbol(Context *context, const char *symbol, Path *path, Decl bool sema_add_local(Context *context, Decl *decl) { Decl *dummy; - Decl *other = sema_resolve_symbol(context, decl->name.string, NULL, &dummy); + Decl *other = sema_resolve_symbol(context, decl->name, NULL, &dummy); if (other) { sema_shadow_error(decl, other); @@ -120,7 +120,7 @@ bool sema_add_local(Context *context, Decl *decl) unsigned num_vars = vec_size(*vars); if (num_vars == MAX_LOCALS - 1 || context->last_local == &context->locals[MAX_LOCALS - 1]) { - SEMA_ERROR(decl->name, "Reached the maximum number of locals."); + SEMA_ERROR(decl, "Reached the maximum number of locals."); return false; } decl->var.id = num_vars; diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 80c3dc345..37c204d9b 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -6,6 +6,12 @@ typedef bool(*AstAnalysis)(Context *, Ast*); +static inline DeferId defer_id_from_ast(Ast *ast) +{ + if (!ast) return 0; + assert(ast->ast_kind == AST_DEFER_STMT); + return ast->defer_stmt.id; +} static inline Type *ast_cond_type(Ast *ast) { assert(ast->ast_kind == AST_DECL_EXPR_LIST); @@ -21,47 +27,11 @@ static inline Type *ast_cond_type(Ast *ast) } } -void sema_init(File *file) -{ -} - void sema_shadow_error(Decl *decl, Decl *old) { - sema_error_range(decl->name.span, "The '%s' would shadow a previous declaration.", decl->name.string); - sema_prev_at_range(old->name.span, "The previous use of '%s' was here.", decl->name.string); -} - -Decl *context_find_ident(Context *context, const char *symbol) -{ - Decl **first = &context->locals[0]; - Decl **current = context->last_local - 1; - while (current >= first) - { - if (current[0]->name.string == symbol) return current[0]; - current--; - } - Decl *found = module_find_symbol(context->module, symbol, MODULE_SYMBOL_SEARCH_THIS); - if (found) return found; - Decl *symbol_found = NULL; - - VECEACH(context->imports, i) - { - Decl *import = context->imports[i]; - // Partial imports - if (import->import.symbol.string && import->import.symbol.string != symbol) continue; - - Decl *decl = module_find_symbol(import->module, symbol, MODULE_SYMBOL_SEARCH_EXTERNAL); - - if (!decl) continue; - if (symbol_found) - { - symbol_found = NULL; - continue; - } - symbol_found = decl; - } - return symbol_found; + SEMA_ERROR(decl, "The '%s' would shadow a previous declaration.", decl->name); + SEMA_PREV(old, "The previous use of '%s' was here.", decl->name); } @@ -72,10 +42,12 @@ static inline void context_push_scope_with_flags(Context *context, ScopeFlags fl FATAL_ERROR("Too deeply nested scopes."); } ScopeFlags previous_flags = context->current_scope->flags; + DeferId parent_defer = context->current_scope->defer_last; context->current_scope++; context->current_scope->exit = EXIT_NONE; context->current_scope->local_decl_start = context->last_local; - context->current_scope->defer_start = vec_size(context->defers); + context->current_scope->defer_top = parent_defer; + context->current_scope->defer_last = parent_defer; context->current_scope->flags = previous_flags | flags; context->current_scope->flags_created = flags; } @@ -85,14 +57,15 @@ static inline void context_push_scope(Context *context) context_push_scope_with_flags(context, SCOPE_NONE); } -static inline void context_pop_scope(Context *context) +static inline void context_pop_scope(Context *context, Ast **ast, Expr **expr) { assert(context->current_scope != &context->scopes[0]); context->last_local = context->current_scope->local_decl_start; - assert(vec_size(context->defers) == context->current_scope->defer_start); - ExitType exit_type = context->current_scope->exit; - vec_resize(context->defers, context->current_scope->defer_start); + if (context->current_scope->defer_top != context->current_scope->defer_last) + { + TODO; + } context->current_scope--; if (context->current_scope->exit < exit_type) { @@ -113,26 +86,6 @@ static bool sema_resolve_ptr_type(Context *context, TypeInfo *type_info) return true; } -static void sema_build_defer_chain(Context *context, Ast ***statement_list) -{ - unsigned size = vec_size(context->defers); - unsigned start = context->current_scope->defer_start; - for (unsigned i = size; i > start; i--) - { - vec_add(*statement_list, context->defers[i - 1]); - } -} - -static void sema_release_defer_chain(Context *context, Ast ***statement_list) -{ - unsigned size = vec_size(context->defers); - unsigned start = context->current_scope->defer_start; - for (unsigned i = size; i > start; i--) - { - vec_add(*statement_list, context->defers[i - 1]->defer_stmt.body); - } - vec_resize(context->defers, start); -} static bool sema_resolve_array_type(Context *context, TypeInfo *type) { @@ -145,7 +98,7 @@ static bool sema_resolve_array_type(Context *context, TypeInfo *type) if (!sema_analyse_expr(context, type_usize, type->array.len)) return type_info_poison(type); if (type->array.len->expr_kind != EXPR_CONST) { - SEMA_ERROR(type->array.len->loc, "Expected a constant value as array size."); + SEMA_ERROR(type->array.len, "Expected a constant value as array size."); return type_info_poison(type); } } @@ -195,7 +148,7 @@ static inline bool sema_analyse_struct_member(Context *context, Decl *decl) static inline bool sema_analyse_struct_union(Context *context, Decl *decl) { - DEBUG_LOG("Beginning analysis of %s.", decl->name.string); + DEBUG_LOG("Beginning analysis of %s.", decl->name); assert(decl->decl_kind == DECL_STRUCT || decl->decl_kind == DECL_UNION); VECEACH(decl->strukt.members, i) { @@ -233,7 +186,7 @@ static inline bool sema_analyse_function_param(Context *context, Decl *param, bo param->type = param->var.type_info->type; if (param->var.init_expr && !is_function) { - SEMA_ERROR(param->var.init_expr->loc, "Function types may not have default arguments."); + SEMA_ERROR(param->var.init_expr, "Function types may not have default arguments."); return false; } if (param->var.init_expr) @@ -242,7 +195,7 @@ static inline bool sema_analyse_function_param(Context *context, Decl *param, bo if (!sema_analyse_expr(context, param->type, expr)) return false; if (expr->expr_kind != EXPR_CONST) { - SEMA_ERROR(expr->loc, "Only constant expressions may be used as default values."); + SEMA_ERROR(expr, "Only constant expressions may be used as default values."); return false; } } @@ -260,7 +213,8 @@ static inline Type *sema_analyse_function_signature(Context *context, FunctionSi type_append_signature_name(signature->rtype->type, buffer, &buffer_write_offset); buffer[buffer_write_offset++] = '('; } - // TODO check parameter name appearing more than once. + STable *names = &context->scratch_table; + stable_clear(names); VECEACH(signature->params, i) { Decl *param = signature->params[i]; @@ -278,8 +232,26 @@ static inline Type *sema_analyse_function_signature(Context *context, FunctionSi buffer[buffer_write_offset++] = ','; } type_append_signature_name(param->var.type_info->type, buffer, &buffer_write_offset); + if (param->name) + { + Decl *prev = stable_set(names, param->name, param); + if (prev) + { + SEMA_ERROR(param, "Duplicate parameter name %s.", param->name); + SEMA_PREV(prev, "Previous use of the name was here."); + decl_poison(prev); + decl_poison(param); + all_ok = false; + } + } + } + if (signature->variadic) + { + buffer[buffer_write_offset++] = ','; + buffer[buffer_write_offset++] = '.'; + buffer[buffer_write_offset++] = '.'; + buffer[buffer_write_offset++] = '.'; } - // TODO variadic buffer[buffer_write_offset++] = ')'; if (vec_size(signature->throws)) { @@ -335,6 +307,7 @@ static inline bool sema_analyse_return_stmt(Context *context, Ast *statement) context->current_scope->exit = EXIT_RETURN; Type *expected_rtype = context->rtype; Expr *return_expr = statement->return_stmt.expr; + statement->return_stmt.defer = VECLAST(context->defers); if (return_expr == NULL) { if (!expected_rtype) @@ -345,7 +318,7 @@ static inline bool sema_analyse_return_stmt(Context *context, Ast *statement) } if (expected_rtype->canonical != type_void) { - SEMA_ERROR(statement->token, "Expected to return a result of type %s.", type_to_error_string(expected_rtype)); + SEMA_ERROR(statement, "Expected to return a result of type %s.", type_to_error_string(expected_rtype)); return false; } return true; @@ -382,7 +355,7 @@ static inline bool sema_analyse_var_decl(Context *context, Decl *decl) static inline Ast *convert_expr_to_ast(Expr *expr) { - Ast *ast = new_ast(AST_EXPR_STMT, expr->loc); + Ast *ast = AST_NEW(AST_EXPR_STMT, expr->span); ast->expr_stmt = expr; return ast; } @@ -391,9 +364,9 @@ static inline Expr *convert_decl_to_expr(Context *context, Decl *decl) assert(decl->decl_kind == DECL_VAR); assert(decl->decl_kind == VARDECL_LOCAL); if (!decl->var.init_expr) return NULL; - Expr *assign_expr = expr_new(EXPR_BINARY, decl->name); + Expr *assign_expr = expr_new(EXPR_BINARY, decl->span); assign_expr->resolve_status = RESOLVE_DONE; - Expr *identifier = expr_new(EXPR_IDENTIFIER, decl->name); + Expr *identifier = expr_new(EXPR_IDENTIFIER, decl->span); identifier->resolve_status = RESOLVE_DONE; identifier->identifier_expr.identifier = decl->name; identifier->identifier_expr.decl = decl; @@ -413,7 +386,7 @@ static inline bool convert_decl_for_cond(Context *context, Decl *decl, Ast*** st { if (is_last) { - SEMA_ERROR(decl->name, "Expected an initializer for '%s'.", decl->name.string); + SEMA_ERROR(decl, "Expected an initializer for '%s'.", decl->name); return false; } // Simply skip declarations if they don't have an initializer, since they're already registered anyway. @@ -431,52 +404,10 @@ static inline bool convert_decl_for_cond(Context *context, Decl *decl, Ast*** st return true; } -static inline bool convert_stmt_for_cond(Context *context, Ast *stmt, Ast*** stmt_list, Expr** last, bool is_last) + +static inline bool sema_analyse_function_block_stmt(Context *context, Ast *stmt) { - if (stmt->ast_kind == AST_EXPR_STMT) - { - if (is_last) - { - *last = stmt->expr_stmt; - return true; - } - *stmt_list = VECADD(*stmt_list, stmt); - return true; - } - assert(stmt->ast_kind == AST_DECLARE_STMT); - Decl *decl = stmt->declare_stmt; - - if (decl->decl_kind != DECL_MULTI_DECL) - { - return convert_decl_for_cond(context, decl, stmt_list, last, is_last); - } - - Decl **decls = decl->multi_decl; - assert(vec_size(decls) > 0); - unsigned last_element = vec_size(decls) - 1; - for (unsigned i = 0; i <= last_element; i++) - { - Decl *sub_decl = decls[i]; - if (!convert_decl_for_cond(context, sub_decl, stmt_list, last, is_last && last_element == i)) return false; - } - return true; -} - -static inline bool decl_or_expr_to_expr_stmt(Context *context, Ast *stmt) -{ - if (stmt->ast_kind == AST_EXPR_STMT) return true; - assert(stmt->ast_kind == AST_DECLARE_STMT); - stmt->ast_kind = AST_EXPR_STMT; - Decl *decl = stmt->declare_stmt; - assert(decl->decl_kind == DECL_VAR); - assert(decl->decl_kind == VARDECL_LOCAL); - if (decl->var.init_expr == NULL) - { - SEMA_ERROR(decl->name, "'%s' needs to be assigned.", decl->name.string); - return false; - } - stmt->expr_stmt = decl->var.init_expr; - return true; + TODO } static inline bool sema_analyse_decl_expr_list(Context *context, Ast *stmt) @@ -498,7 +429,7 @@ static inline bool sema_analyse_cond(Context *context, Ast *stmt, bool cast_to_b size_t size = vec_size(stmt->decl_expr_stmt); if (!size) { - SEMA_ERROR(stmt->token, "Expected a boolean expression"); + SEMA_ERROR(stmt, "Expected a boolean expression"); return false; } @@ -518,13 +449,13 @@ static inline bool sema_analyse_cond(Context *context, Ast *stmt, bool cast_to_b Expr *init = last->declare_stmt->var.init_expr; if (!init) { - SEMA_ERROR(last->token, "Expected a declaration with initializer."); + SEMA_ERROR(last, "Expected a declaration with initializer."); return false; } if (cast_to_bool && init->type->type_kind != TYPE_BOOL && cast_to_bool_kind(last->declare_stmt->var.type_info->type) == CAST_ERROR) { - SEMA_ERROR(last->declare_stmt->var.init_expr->loc, "The expression needs to be convertible to a boolean."); + SEMA_ERROR(last->declare_stmt->var.init_expr, "The expression needs to be convertible to a boolean."); return false; } return true; @@ -539,14 +470,15 @@ static inline bool sema_analyse_while_stmt(Context *context, Ast *statement) Ast *decl = statement->while_stmt.decl; Ast *cond = statement->while_stmt.cond; Ast *body = statement->while_stmt.body; - context_push_scope_with_flags(context, SCOPE_CONTROL); + context_push_scope(context); bool success = !decl || sema_analyse_statement(context, decl); + context_push_scope(context); success = success && sema_analyse_cond(context, cond, true); - context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise) success = success && sema_analyse_statement(context, body); - context_pop_scope(context); - context_pop_scope(context); + context_pop_scope(context, &body, NULL); + context_pop_scope(context, &cond, NULL); + context_pop_scope(context, &decl, NULL); if (!success) return false; return success; } @@ -558,28 +490,15 @@ static inline bool sema_analyse_do_stmt(Context *context, Ast *statement) bool success; context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise) success = sema_analyse_statement(context, body); - context_pop_scope(context); + context_pop_scope(context, &body, NULL); if (!success) return false; - context_push_scope_with_flags(context, SCOPE_CONTROL); + context_push_scope(context); success = sema_analyse_expr(context, type_bool, expr); - context_pop_scope(context); + context_pop_scope(context, NULL, &expr); return success; } -static inline bool sema_analyse_multi_decl(Context *context, Ast *statement) -{ - Decl *decl = statement->declare_stmt; - VECEACH(statement->declare_stmt->multi_decl, i) - { - if (!sema_analyse_var_decl(context, statement->declare_stmt->multi_decl[i])) - { - decl_poison(decl); - return false; - } - } - return true; -} static inline bool sema_analyse_declare_stmt(Context *context, Ast *statement) { @@ -601,34 +520,57 @@ static inline bool sema_analyse_defer_stmt(Context *context, Ast *statement) bool success = sema_analyse_statement(context, statement->defer_stmt.body); - context_pop_scope(context); + context_pop_scope(context, &statement->defer_stmt.body, NULL); if (!success) return false; + statement->defer_stmt.prev_defer = VECLAST(context->defers); vec_add(context->defers, statement); - + statement->defer_stmt.id = vec_size(context->defers); return true; } static inline bool sema_analyse_default_stmt(Context *context, Ast *statement) { - SEMA_ERROR(statement->token, "Unexpected 'default' outside of switch"); + SEMA_ERROR(statement, "Unexpected 'default' outside of switch"); return false; } static inline bool sema_analyse_for_stmt(Context *context, Ast *statement) { - context_push_scope_with_flags(context, SCOPE_CONTROL); + bool success = true; - bool success = !statement->for_stmt.init || sema_analyse_statement(context, statement->for_stmt.init); - - success = success && (!statement->for_stmt.cond || sema_analyse_expr(context, type_bool, statement->for_stmt.cond)); - success = success && (!statement->for_stmt.incr || sema_analyse_expr(context, NULL, statement->for_stmt.incr)); - context_pop_scope(context); + // Enter for scope + context_push_scope(context); + if (statement->for_stmt.init) + { + success = sema_analyse_statement(context, statement->for_stmt.init); + } + if (success && statement->for_stmt.cond) + { + // Conditional scope start + context_push_scope(context); + success = sema_analyse_expr(context, type_bool, statement->for_stmt.cond); + // Conditional scope end + context_pop_scope(context, NULL, &statement->for_stmt.cond); + } + if (success && statement->for_stmt.incr) + { + // Incr scope start + context_push_scope(context); + success = sema_analyse_statement(context, statement->for_stmt.incr); + // Incr scope end + context_pop_scope(context, &statement->for_stmt.incr, NULL); + } if (!success) return false; + + // Create the for body scope. context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise) success = sema_analyse_statement(context, statement->for_stmt.body); - context_pop_scope(context); + // End for body scope + context_pop_scope(context, &statement->for_stmt.body, NULL); + // End for scope + context_pop_scope(context, &statement, NULL); return success; } @@ -637,7 +579,7 @@ static inline bool sema_analyse_goto_stmt(Context *context, Ast *statement) VECEACH(context->labels, i) { Ast *label = context->labels[i]; - if (statement->token.string == label->token.string) + if (statement->goto_stmt.label_name == label->label_stmt.name) { statement->goto_stmt.type = GOTO_JUMP_BACK; label->label_stmt.is_used = true; @@ -655,21 +597,34 @@ static inline bool sema_analyse_if_stmt(Context *context, Ast *statement) // if (!x) A(); else B(); // into // if (x) B(); else A(); - context_push_scope(context); Ast *cond = statement->if_stmt.cond; - context_push_scope_with_flags(context, SCOPE_CONTROL); + context_push_scope(context); bool success = sema_analyse_cond(context, cond, true); + if (statement->if_stmt.else_body) + { + if (statement->if_stmt.then_body->ast_kind != AST_COMPOUND_STMT) + { + SEMA_ERROR(statement->if_stmt.then_body, "if-statements with an 'else' must use '{ }' even around a single statement."); + success = false; + } + if (success && statement->if_stmt.else_body->ast_kind != AST_COMPOUND_STMT) + { + SEMA_ERROR(statement->if_stmt.else_body, + "An 'else' must use '{ }' even around a single statement."); + success = false; + } + } context_push_scope(context); success = success && sema_analyse_statement(context, statement->if_stmt.then_body); - context_pop_scope(context); + context_pop_scope(context, &statement->if_stmt.then_body, NULL); // TODO null flowcheck if (statement->if_stmt.else_body) { context_push_scope(context); success = success && sema_analyse_statement(context, statement->if_stmt.else_body); - context_pop_scope(context); + context_pop_scope(context, &statement->if_stmt.else_body, NULL); } - context_pop_scope(context); + context_pop_scope(context, &statement, NULL); return success; } @@ -678,10 +633,10 @@ static inline bool sema_analyse_label(Context *context, Ast *statement) VECEACH(context->labels, i) { Ast *label = context->labels[i]; - if (label->token.string == statement->token.string) + if (label->label_stmt.name == statement->label_stmt.name) { - SEMA_ERROR(tok, "This duplicate label '%s'.", statement->token.string); - sema_prev_at_range(label->token.span, "The previous declaration was here."); + SEMA_ERROR(statement, "This duplicate label '%s'.", statement->label_stmt.name); + sema_prev_at_range(label->span, "The previous declaration was here."); ast_poison(label); ast_poison(statement); return false; @@ -691,7 +646,7 @@ static inline bool sema_analyse_label(Context *context, Ast *statement) VECEACH(context->gotos, i) { Ast *the_goto = context->gotos[i]; - if (the_goto->token.string == statement->token.string) + if (the_goto->goto_stmt.label_name == statement->label_stmt.name) { the_goto->goto_stmt.type = GOTO_JUMP_FORWARD; the_goto->goto_stmt.label = statement; @@ -723,7 +678,7 @@ static bool sema_analyse_break_stmt(Context *context, Ast *statement) { if (!(context->current_scope->flags & SCOPE_BREAK)) // NOLINT(hicpp-signed-bitwise) { - SEMA_ERROR(statement->token, "'break' is not allowed here."); + SEMA_ERROR(statement, "'break' is not allowed here."); return false; } return true; @@ -731,7 +686,7 @@ static bool sema_analyse_break_stmt(Context *context, Ast *statement) static bool sema_analyse_case_stmt(Context *context, Ast *statement) { - SEMA_ERROR(statement->token, "Unexpected 'case' outside of switch"); + SEMA_ERROR(statement, "Unexpected 'case' outside of switch"); return false; } @@ -739,10 +694,9 @@ static bool sema_analyse_continue_stmt(Context *context, Ast *statement) { if (!(context->current_scope->flags & SCOPE_CONTINUE)) // NOLINT(hicpp-signed-bitwise) { - SEMA_ERROR(statement->token, "'continue' is not allowed here."); + SEMA_ERROR(statement, "'continue' is not allowed here."); return false; } - sema_build_defer_chain(context, &statement->continue_stmt.defers); return true; } @@ -759,7 +713,7 @@ static int sema_check_comp_time_bool(Context *context, Expr *expr) if (!sema_analyse_expr(context, type_bool, expr)) return -1; if (expr->expr_kind != EXPR_CONST) { - SEMA_ERROR(expr->loc, "$if requires a compile time constant value."); + SEMA_ERROR(expr, "$if requires a compile time constant value."); return -1; } return expr->const_expr.b; @@ -807,14 +761,14 @@ static bool sema_analyse_switch_case(Context *context, Ast*** prev_cases, Ast *c if (*prev_case) { // sema_build_defer_chain(context, prev_cases); - context_pop_scope(context); + context_pop_scope(context, prev_case, NULL); *prev_case = NULL; } Expr *case_expr = case_stmt->case_stmt.expr; if (!sema_analyse_expr(context, switch_type, case_expr)) return false; if (case_expr->expr_kind != EXPR_CONST) { - SEMA_ERROR(case_expr->loc, "This must be a constant expression."); + SEMA_ERROR(case_expr, "This must be a constant expression."); return false; } assert(case_expr->const_expr.type == CONST_INT); @@ -826,8 +780,8 @@ static bool sema_analyse_switch_case(Context *context, Ast*** prev_cases, Ast *c { if ((*prev_cases)[i]->case_stmt.val == val) { - SEMA_ERROR(case_stmt->token, "Duplicate case value."); - sema_prev_at_range((*prev_cases)[i]->token.span, "Previous use was here."); + SEMA_ERROR(case_stmt, "Duplicate case value."); + sema_prev_at_range((*prev_cases)[i]->span, "Previous use was here."); return false; } } @@ -842,7 +796,7 @@ static bool sema_analyse_switch_case(Context *context, Ast*** prev_cases, Ast *c case_stmt->case_stmt.block = NULL; if (*prev_case) { - context_pop_scope(context); + context_pop_scope(context, prev_case, NULL); } context_push_scope(context); *prev_case = case_stmt; @@ -851,7 +805,7 @@ static bool sema_analyse_switch_case(Context *context, Ast*** prev_cases, Ast *c } if (!*prev_case) { - SEMA_ERROR(case_stmt->token, "Expected a 'case' or 'default' statement."); + SEMA_ERROR(case_stmt, "Expected a 'case' or 'default' statement."); return false; } if (case_stmt->ast_kind == AST_NEXT_STMT) @@ -865,7 +819,7 @@ static bool sema_analyse_switch_case(Context *context, Ast*** prev_cases, Ast *c } if (!(*prev_case)->case_stmt.block) { - (*prev_case)->case_stmt.block = new_ast(AST_COMPOUND_STMT, (*prev_case)->token); + (*prev_case)->case_stmt.block = AST_NEW(AST_COMPOUND_STMT, (*prev_case)->span); } vec_add((*prev_case)->case_stmt.block->compound_stmt.stmts, case_stmt); return true; @@ -873,7 +827,7 @@ static bool sema_analyse_switch_case(Context *context, Ast*** prev_cases, Ast *c static bool sema_analyse_switch_stmt(Context *context, Ast *statement) { - context_push_scope_with_flags(context, SCOPE_CONTROL); + context_push_scope(context); bool success = sema_analyse_statement(context, statement->switch_stmt.cond); Ast *cond = statement->switch_stmt.cond; success = success && sema_analyse_cond(context, cond, false); @@ -883,7 +837,7 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement) Type *switch_type = ast_cond_type(cond)->canonical; if (!type_is_integer(switch_type)) { - SEMA_ERROR(cond->token, "Expected an integer or enum type, was '%s'.", type_to_error_string(switch_type)); + SEMA_ERROR(cond, "Expected an integer or enum type, was '%s'.", type_to_error_string(switch_type)); return false; } Ast *in_case = NULL; @@ -893,10 +847,10 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement) } if (in_case) { - context_pop_scope(context); + context_pop_scope(context, &in_case, NULL); } - context_pop_scope(context); - context_pop_scope(context); + context_pop_scope(context, &cond, NULL); + context_pop_scope(context, &statement, NULL); return success; } @@ -923,52 +877,116 @@ static bool sema_analyse_compound_stmt(Context *context, Ast *statement) { context_push_scope(context); bool success = sema_analyse_compound_statement_no_scope(context, statement); - context_pop_scope(context); + context_pop_scope(context, &statement, NULL); return success; } -static AstAnalysis AST_ANALYSIS[AST_WHILE_STMT + 1] = +static inline bool sema_analyse_statement_inner(Context *context, Ast *statement) { - [AST_ASM_STMT] = &sema_analyse_asm_stmt, - [AST_ATTRIBUTE] = NULL, - [AST_BREAK_STMT] = &sema_analyse_break_stmt, - [AST_CASE_STMT] = &sema_analyse_case_stmt, - [AST_CATCH_STMT] = &sema_analyse_catch_stmt, - [AST_COMPOUND_STMT] = &sema_analyse_compound_stmt, - [AST_CONTINUE_STMT] = &sema_analyse_continue_stmt, - [AST_CT_IF_STMT] = &sema_analyse_ct_if_stmt, - [AST_DECLARE_STMT] = &sema_analyse_declare_stmt, - [AST_DEFAULT_STMT] = &sema_analyse_default_stmt, - [AST_DEFER_STMT] = &sema_analyse_defer_stmt, - [AST_DO_STMT] = &sema_analyse_do_stmt, - [AST_EXPR_STMT] = &sema_analyse_expr_stmt, - [AST_FOR_STMT] = &sema_analyse_for_stmt, - [AST_GOTO_STMT] = &sema_analyse_goto_stmt, - [AST_IF_STMT] = &sema_analyse_if_stmt, - [AST_LABEL] = &sema_analyse_label, - [AST_NOP_STMT] = &sema_analyse_nop_stmt, - [AST_RETURN_STMT] = &sema_analyse_return_stmt, - [AST_SWITCH_STMT] = &sema_analyse_switch_stmt, - [AST_TRY_STMT] = &sema_analyse_try_stmt, - [AST_THROW_STMT] = &sema_analyse_throw_stmt, - [AST_NEXT_STMT] = NULL, // Never reached - [AST_VOLATILE_STMT] = &sema_analyse_volatile_stmt, - [AST_WHILE_STMT] = &sema_analyse_while_stmt, - [AST_DECL_EXPR_LIST] = &sema_analyse_decl_expr_list, -}; + switch (statement->ast_kind) + { + case AST_POISONED: + return false; + case AST_ATTRIBUTE: + UNREACHABLE + case AST_ASM_STMT: + return sema_analyse_asm_stmt(context, statement); + case AST_BREAK_STMT: + return sema_analyse_break_stmt(context, statement); + case AST_CASE_STMT: + return sema_analyse_case_stmt(context, statement); + case AST_CATCH_STMT: + return sema_analyse_catch_stmt(context, statement); + case AST_COMPOUND_STMT: + return sema_analyse_compound_stmt(context, statement); + case AST_CONTINUE_STMT: + return sema_analyse_continue_stmt(context, statement); + case AST_CT_IF_STMT: + return sema_analyse_ct_if_stmt(context, statement); + case AST_DECLARE_STMT: + return sema_analyse_declare_stmt(context, statement); + case AST_DEFAULT_STMT: + return sema_analyse_default_stmt(context, statement); + case AST_DEFER_STMT: + return sema_analyse_defer_stmt(context, statement); + case AST_DO_STMT: + return sema_analyse_do_stmt(context, statement); + case AST_EXPR_STMT: + return sema_analyse_expr_stmt(context, statement); + case AST_FOR_STMT: + return sema_analyse_for_stmt(context, statement); + case AST_GOTO_STMT: + return sema_analyse_goto_stmt(context, statement); + case AST_IF_STMT: + return sema_analyse_if_stmt(context, statement); + case AST_LABEL: + return sema_analyse_label(context, statement); + case AST_NOP_STMT: + return sema_analyse_nop_stmt(context, statement); + case AST_RETURN_STMT: + return sema_analyse_return_stmt(context, statement); + case AST_SWITCH_STMT: + return sema_analyse_switch_stmt(context, statement); + case AST_THROW_STMT: + return sema_analyse_throw_stmt(context, statement); + case AST_TRY_STMT: + return sema_analyse_try_stmt(context, statement); + case AST_NEXT_STMT: + UNREACHABLE + case AST_VOLATILE_STMT: + return sema_analyse_volatile_stmt(context, statement); + case AST_WHILE_STMT: + return sema_analyse_while_stmt(context, statement); + case AST_DECL_EXPR_LIST: + return sema_analyse_decl_expr_list(context, statement); + case AST_FUNCTION_BLOCK_STMT: + return sema_analyse_function_block_stmt(context, statement); + case AST_CT_ELIF_STMT: + case AST_CT_ELSE_STMT: + UNREACHABLE + case AST_CT_FOR_STMT: + case AST_CT_SWITCH_STMT: + case AST_CT_DEFAULT_STMT: + case AST_CT_CASE_STMT: + case AST_GENERIC_CASE_STMT: + case AST_GENERIC_DEFAULT_STMT: + TODO + } +} bool sema_analyse_statement(Context *context, Ast *statement) { - if (AST_ANALYSIS[statement->ast_kind](context, statement)) return true; + if (sema_analyse_statement_inner(context, statement)) return true; return ast_poison(statement); } +static inline int defer_depth(Ast *defer_stmt) +{ + int depth = 0; + while (defer_stmt) + { + defer_stmt = defer_stmt->defer_stmt.prev_defer; + depth++; + } + return depth; +} + +static inline void defer_list_walk_to_common_depth(Ast **defer_stmt, int this_depth, int other_depth) +{ + int steps = this_depth - other_depth; + for (int i = 0; i < steps; i++) + { + *defer_stmt = (*defer_stmt)->defer_stmt.prev_defer; + } +} + static inline bool sema_analyse_function_body(Context *context, Decl *func) { context->active_function_for_analysis = func; context->rtype = func->func.function_signature.rtype->type; context->current_scope = &context->scopes[0]; - context->current_scope->local_decl_start = 0; + // Clean out the current scope. + memset(context->current_scope, 0, sizeof(*context->current_scope)); context->labels = NULL; context->gotos = NULL; context->last_local = &context->locals[0]; @@ -985,13 +1003,54 @@ static inline bool sema_analyse_function_body(Context *context, Decl *func) { if (func->func.function_signature.rtype->type->canonical != type_void) { - SEMA_ERROR(func->name, "Missing return statement at the end of the function."); + SEMA_ERROR(func, "Missing return statement at the end of the function."); return false; } - sema_release_defer_chain(context, &func->func.body->compound_stmt.stmts); + } + VECEACH(context->gotos, i) + { + Ast *goto_stmt = context->gotos[i]; + Ast *label_target = goto_stmt->goto_stmt.label; + if (!label_target) + { + SEMA_ERROR(goto_stmt, "Goto to a missing label %s.", goto_stmt->goto_stmt.label_name); + return false; + } + + // If there are no defers, then that's fine. + if (!goto_stmt->goto_stmt.defer && !label_target->label_stmt.defer) continue; + + // First we need to search for the common depth. + int label_depth = defer_depth(label_target->label_stmt.defer); + int goto_depth = defer_depth(goto_stmt->goto_stmt.defer); + + Ast *common_depth_label = label_target->label_stmt.defer; + Ast *common_depth_goto = goto_stmt->goto_stmt.defer; + + // Now walk up to the common depth. + defer_list_walk_to_common_depth(&common_depth_label, label_depth, goto_depth); + defer_list_walk_to_common_depth(&common_depth_goto, goto_depth, label_depth); + + // We might still not match, so walk upwards until we have a match: + while (common_depth_goto != common_depth_label) + { + assert(common_depth_goto && common_depth_label); + common_depth_goto = common_depth_goto->defer_stmt.prev_defer; + common_depth_label = common_depth_label->defer_stmt.prev_defer; + } + + // We now know the top defer (which we won't actually generate) + goto_stmt->goto_stmt.defer_end = common_depth_goto; + + // Mark all defers that occur on the way "up" to the common depth conditional. + Ast *current = label_target->label_stmt.defer; + while (current != common_depth_goto) + { + current->defer_stmt.emit_boolean = true; + } } func->func.labels = context->labels; - context_pop_scope(context); + context_pop_scope(context, &func->func.body, NULL); context->current_scope = NULL; return true; } @@ -1003,28 +1062,28 @@ static inline bool sema_analyse_method_function(Context *context, Decl *decl) if (!sema_resolve_type_info(context, parent_type)) return false; if (!type_may_have_method_functions(parent_type->type)) { - SEMA_ERROR(decl->name, "Method functions can not be associated with '%s'", type_to_error_string(decl->func.type_parent->type)); + SEMA_ERROR(decl, "Method functions can not be associated with '%s'", type_to_error_string(decl->func.type_parent->type)); return false; } Decl *parent = parent_type->type->decl; VECEACH(parent->method_functions, i) { Decl *function = parent->method_functions[i]; - if (function->name.string == decl->name.string) + if (function->name == decl->name) { - SEMA_ERROR(decl->name, "Duplicate name '%s' for method function.", function->name); - sema_prev_at_range(function->name.span, "Previous definition here."); + SEMA_ERROR(decl, "Duplicate name '%s' for method function.", function->name); + SEMA_PREV(function, "Previous definition here."); return false; } } - DEBUG_LOG("Method function '%s.%s' analysed.", parent->name.string, decl->name.string); + DEBUG_LOG("Method function '%s.%s' analysed.", parent->name, decl->name); vec_add(parent->method_functions, decl); return true; } static inline bool sema_analyse_func(Context *context, Decl *decl) { - DEBUG_LOG("Analysing function %s", decl->name.string); + DEBUG_LOG("Analysing function %s", decl->name); Type *func_type = sema_analyse_function_signature(context, &decl->func.function_signature, true); decl->type = func_type; if (!func_type) return decl_poison(decl); @@ -1033,11 +1092,11 @@ static inline bool sema_analyse_func(Context *context, Decl *decl) if (!sema_analyse_method_function(context, decl)) return decl_poison(decl); } if (decl->func.body && !sema_analyse_function_body(context, decl)) return decl_poison(decl); - if (decl->name.string == main_name) + if (decl->name == main_name) { if (decl->visibility == VISIBLE_LOCAL) { - SEMA_ERROR(decl->name, "'main' cannot have local visibility."); + SEMA_ERROR(decl, "'main' cannot have local visibility."); return false; } decl->visibility = VISIBLE_EXTERN; @@ -1068,7 +1127,7 @@ static inline bool sema_analyse_global(Context *context, Decl *decl) if (!sema_analyse_expr(context, decl->type, decl->var.init_expr)) return false; if (decl->var.init_expr->expr_kind != EXPR_CONST) { - SEMA_ERROR(decl->var.init_expr->loc, "The expression must be a constant value."); + SEMA_ERROR(decl->var.init_expr, "The expression must be a constant value."); return false; } } @@ -1122,7 +1181,7 @@ static inline bool sema_analyse_enum(Context *context, Decl *decl) Expr *expr = enum_value->enum_constant.expr; if (!expr) { - expr = expr_new(EXPR_CONST, EMPTY_TOKEN); + expr = expr_new(EXPR_CONST, INVALID_RANGE); expr->type = type; expr->resolve_status = RESOLVE_DONE; expr->const_expr.type = CONST_INT; @@ -1138,7 +1197,7 @@ static inline bool sema_analyse_enum(Context *context, Decl *decl) assert(type_is_integer(expr->type->canonical)); if (expr->expr_kind != EXPR_CONST) { - SEMA_ERROR(expr->loc, "Expected a constant expression for enum"); + SEMA_ERROR(expr, "Expected a constant expression for enum"); success = false; } enum_value->resolve_status = RESOLVE_DONE; @@ -1157,10 +1216,10 @@ bool sema_analyse_decl(Context *context, Decl *decl) { if (decl->resolve_status == RESOLVE_DONE) return decl_ok(decl); - DEBUG_LOG("Analyse %s", decl->name.string); + DEBUG_LOG("Analyse %s", decl->name); if (decl->resolve_status == RESOLVE_RUNNING) { - SEMA_ERROR(decl->name, "Recursive dependency on %s.", decl->name.string); + SEMA_ERROR(decl, "Recursive dependency on %s.", decl->name); decl_poison(decl); return false; } @@ -1208,7 +1267,6 @@ bool sema_analyse_decl(Context *context, Decl *decl) case DECL_ENUM_CONSTANT: case DECL_ERROR_CONSTANT: case DECL_ARRAY_VALUE: - case DECL_MULTI_DECL: case DECL_CT_ELSE: case DECL_CT_ELIF: UNREACHABLE @@ -1273,7 +1331,7 @@ void sema_analysis_pass_process_imports(Context *context) Module *module = stable_get(&compiler.modules, path->module); if (!module) { - SEMA_ERROR(import->name, "No module named '%s' could be found.", path->module); + SEMA_ERROR(import, "No module named '%s' could be found.", path->module); decl_poison(import); continue; } @@ -1333,14 +1391,14 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info) if (!decl) { - SEMA_ERROR(type_info->unresolved.name_loc, "Unknown type '%s'.", type_info->unresolved.name_loc.string); + SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, "Unknown type '%s'.", type_info->unresolved.name_loc.string); return type_info_poison(type_info); } if (ambiguous_decl) { - SEMA_ERROR(type_info->unresolved.name_loc, "Ambiguous type '%s' – both defined in %s and %s, please add the module name to resolve the ambiguity", type_info->unresolved.name_loc.string, - decl->module->name->module, ambiguous_decl->module->name->module); + SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, "Ambiguous type '%s' – both defined in %s and %s, please add the module name to resolve the ambiguity", type_info->unresolved.name_loc.string, + decl->module->name->module, ambiguous_decl->module->name->module); return type_info_poison(type_info); } switch (decl->decl_kind) @@ -1376,9 +1434,8 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info) case DECL_IMPORT: case DECL_MACRO: case DECL_GENERIC: - SEMA_ERROR(type_info->unresolved.name_loc, "This is not a type."); + SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, "This is not a type."); return type_info_poison(type_info); - case DECL_MULTI_DECL: case DECL_CT_ELSE: case DECL_CT_IF: case DECL_CT_ELIF: @@ -1396,7 +1453,7 @@ bool sema_resolve_type_shallow(Context *context, TypeInfo *type_info) if (type_info->resolve_status == RESOLVE_RUNNING) { // TODO this is incorrect for unresolved expressions - SEMA_ERROR(type_info->unresolved.name_loc, "Circular dependency resolving type '%s'.", type_info->unresolved.name_loc.string); + SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, "Circular dependency resolving type '%s'.", type_info->unresolved.name_loc.string); return type_info_poison(type_info); } diff --git a/src/compiler/source_file.c b/src/compiler/source_file.c index 0877f1df2..3c813c9a5 100644 --- a/src/compiler/source_file.c +++ b/src/compiler/source_file.c @@ -44,7 +44,7 @@ File *source_file_load(const char *filename, bool *already_loaded) size_t size; const char* source_text = read_file(filename, &size); - File *file = malloc(sizeof(File)); + File *file = CALLOCS(File); file->full_path = full_path; file->start_id = vec_size(source_files.files) ? VECLAST(source_files.files)->end_id : 0; @@ -53,10 +53,10 @@ File *source_file_load(const char *filename, bool *already_loaded) ASSERT(file->start_id + size < UINT32_MAX, "Total files loaded exceeded %d bytes", UINT32_MAX); file->end_id = (SourceLoc) (file->start_id + size); size_t pre_allocated_lines = size / 40; - file->line_start = VECNEW(SourceLoc, pre_allocated_lines < 16 ? 16 : pre_allocated_lines); - VECADD(file->line_start, file->start_id); + file->lines = VECNEW(SourceLoc, pre_allocated_lines < 16 ? 16 : pre_allocated_lines); + vec_add(file->lines, file->start_id); path_get_dir_and_filename_from_full(file->full_path, &file->name, &file->dir_path); - VECADD(source_files.files, file); + vec_add(source_files.files, file); return file; } @@ -64,14 +64,14 @@ void source_file_append_line_end(File *file, SourceLoc loc) { if (file->current_line_start > loc) return; file->current_line_start = loc + 1; - VECADD(file->line_start, file->current_line_start); + vec_add(file->lines, file->current_line_start); } SourceRange source_range_from_ranges(SourceRange first, SourceRange last) { return (SourceRange) { .loc = first.loc, - .length = last.loc - first.loc + last.length + .end_loc = last.end_loc }; } @@ -85,7 +85,7 @@ SourcePosition source_file_find_position_in_file(File *file, SourceLoc loc) { assert(file->start_id <= loc); - size_t lines = vec_size(file->line_start); + size_t lines = vec_size(file->lines); unsigned low = 0; unsigned high = lines; while (1) @@ -95,13 +95,13 @@ SourcePosition source_file_find_position_in_file(File *file, SourceLoc loc) uint32_t mid = (high + low) / 2; // Mid is before the location. - SourceLoc line_start = file->line_start[mid]; + SourceLoc line_start = file->lines[mid]; if (line_start > loc) { high = mid; continue; } - if (mid + 1 != lines && file->line_start[mid + 1] < loc) + if (mid + 1 != lines && file->lines[mid + 1] <= loc) { low = mid; continue; diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index 2411c25cf..29dcacb61 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -159,6 +159,12 @@ const char *token_type_to_string(TokenType type) case TOKEN_REAL: return "FLOAT"; + // Comments + case TOKEN_COMMENT: + return "COMMENT"; + case TOKEN_DOC_COMMENT: + return "DOC_COMMENT"; + // Keywords case TOKEN_ALIAS: return "alias"; diff --git a/src/compiler/types.c b/src/compiler/types.c index e19c1e2cb..55e03face 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -113,7 +113,6 @@ static void type_append_signature_name_user_defined(Decl *decl, char *dst, size_ case DECL_ARRAY_VALUE: case DECL_IMPORT: case DECL_MACRO: - case DECL_MULTI_DECL: case DECL_GENERIC: case DECL_CT_IF: case DECL_CT_ELSE: @@ -124,9 +123,13 @@ static void type_append_signature_name_user_defined(Decl *decl, char *dst, size_ case DECL_UNION: case DECL_ENUM: case DECL_ERROR: - memcpy(dst + *offset, decl->name.string, decl->name.span.length); - *offset += decl->name.span.length; + { + unsigned len = source_range_len(decl->name_span); + memcpy(dst + *offset, decl->name, len); + *offset += len; return; + + } } UNREACHABLE } diff --git a/src/compiler_tests/tests.c b/src/compiler_tests/tests.c index 7a973ee10..5bf83d742 100644 --- a/src/compiler_tests/tests.c +++ b/src/compiler_tests/tests.c @@ -21,6 +21,7 @@ static void test_lexer(void) const int EXPECTED_TOKENS = 12 + 73 + 9; const char* tokens[TOKEN_EOF]; int len[TOKEN_EOF]; + Lexer lexer; lexer_check_init(); for (int i = 1; i < TOKEN_EOF; i++) { @@ -31,7 +32,7 @@ static void test_lexer(void) const char* interned = symtab_add(token, len[i], fnv1a(token, len[i]), &lookup); if (lookup != TOKEN_IDENT) { - Token scanned = lexer_scan_ident_test(token); + Token scanned = lexer_scan_ident_test(&lexer, token); TEST_ASSERT(scanned.type == i, "Mismatch scanning: was '%s', expected '%s' - lookup: %s - interned: %s.", token_type_to_string(scanned.type), token_type_to_string(i), @@ -56,7 +57,7 @@ static void test_lexer(void) { for (int i = 1; i < TOKEN_EOF; i++) { - volatile TokenType t = lexer_scan_ident_test(tokens[i]).type; + volatile TokenType t = lexer_scan_ident_test(&lexer, tokens[i]).type; } } @@ -77,11 +78,11 @@ static void test_lexer(void) size_t test_len = strlen(test_parse); for (int b = 0; b < BENCH_REPEATS; b++) { - lexer_test_setup(test_parse, test_len); + lexer_test_setup(&lexer, test_parse, test_len); Token token; while (1) { - token = lexer_scan_token(); + token = lexer_scan_token(&lexer); if (token.type == TOKEN_EOF) break; TEST_ASSERT(token.type != TOKEN_INVALID_TOKEN, "Got invalid token"); @@ -103,7 +104,7 @@ void test_file(void) File file; memset(&file, 0, sizeof(file)); file.start_id = 3; - VECADD(file.line_start, file.start_id); + VECADD(file.lines, file.start_id); TEST_ASSERT(source_file_find_position_in_file(&file, 3).line == 1, "Expected first line"); TEST_ASSERT(source_file_find_position_in_file(&file, 10).line == 1, "Expected first line"); source_file_append_line_end(&file, 9); diff --git a/src/utils/lib.h b/src/utils/lib.h index 58e89cea9..ddd319111 100644 --- a/src/utils/lib.h +++ b/src/utils/lib.h @@ -274,7 +274,8 @@ static inline void* _expand(void *vec, size_t element_size) _vec = __temp; }) #define vec_add(_vec, _value) do { (_vec) = VECADD((_vec), _value); } while (0) -#define VECLAST(_vec) ( (_vec) ? (_vec)[vec_size(_vec) - 1] : NULL) +#define VECLAST(_vec) ({ unsigned _size = vec_size(_vec); _size ? (_vec)[_size - 1] : NULL; }) + static inline bool is_all_upper(const char* string) {