diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 6cf38d780..b65ab973f 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -96,6 +96,7 @@ Decl *decl_new_with_type(const char *name, SourceSpan loc, DeclKind decl_type, V case DECL_BODYPARAM: case DECL_INITIALIZE: case DECL_FINALIZE: + case DECL_CT_ECHO: UNREACHABLE } Type *type = type_new(kind, name ? name : "$anon"); @@ -126,6 +127,8 @@ const char *decl_to_a_name(Decl *decl) return "a poisoned decl"; case DECL_CT_ASSERT: return "a compile time assert"; + case DECL_CT_ECHO: + return "a compile time echo"; case DECL_CT_CASE: return "a compile time case"; case DECL_CT_ELIF: diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 91540683b..7b96a2a8b 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -710,6 +710,7 @@ typedef struct Decl_ CtSwitchDecl ct_switch_decl; CtCaseDecl ct_case_decl; Ast *ct_assert_decl; + Ast *ct_echo_decl; Decl** ct_else_decl; }; @@ -1549,6 +1550,7 @@ struct CompilationUnit_ }; Decl **ct_ifs; Decl **ct_asserts; + Decl **ct_echos; Decl **xxlizers; Decl **vars; Decl **macros; @@ -2190,6 +2192,7 @@ bool sema_analyse_decl(SemaContext *context, Decl *decl); bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl); bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local); bool sema_analyse_ct_assert_stmt(SemaContext *context, Ast *statement); +bool sema_analyse_ct_echo_stmt(SemaContext *context, Ast *statement); bool sema_analyse_statement(SemaContext *context, Ast *statement); bool sema_expr_analyse_assign_right_side(SemaContext *context, Expr *expr, Type *left_type, Expr *right, bool is_unwrapped_var); bool sema_expr_analyse_initializer_list(SemaContext *context, Type *to, Expr *expr); diff --git a/src/compiler/context.c b/src/compiler/context.c index 40ef1a296..0e6c9fd87 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -141,6 +141,7 @@ void decl_register(Decl *decl) case DECL_CT_IF: case DECL_CT_SWITCH: case DECL_CT_ASSERT: + case DECL_CT_ECHO: case DECL_ENUM_CONSTANT: case DECL_FAULTVALUE: case DECL_IMPORT: @@ -263,6 +264,9 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl) case DECL_CT_SWITCH: vec_add(unit->ct_ifs, decl); return; + case DECL_CT_ECHO: + vec_add(unit->ct_echos, decl); + return; case DECL_CT_ASSERT: vec_add(unit->ct_asserts, decl); return; diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 59509e2fd..691df539f 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -586,6 +586,7 @@ RETRY: copy_reg_ref(c, source, ast); fixup_astid(c, &ast->defer_stmt.prev_defer); break; + case AST_CT_ECHO_STMT: case AST_EXPR_STMT: MACRO_COPY_EXPR(ast->expr_stmt); break; @@ -863,6 +864,9 @@ Decl *copy_decl(CopyStruct *c, Decl *decl) MACRO_COPY_DECL(decl->ct_if_decl.elif); MACRO_COPY_DECL_LIST(decl->ct_if_decl.then); break; + case DECL_CT_ECHO: + MACRO_COPY_AST(decl->ct_echo_decl); + break; case DECL_CT_ASSERT: MACRO_COPY_AST(decl->ct_assert_decl); break; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 2261198fb..7f08d1700 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -53,10 +53,11 @@ typedef enum AST_COMPOUND_STMT, AST_CONTINUE_STMT, AST_CT_ASSERT, - AST_CT_IF_STMT, + AST_CT_ECHO_STMT, AST_CT_ELSE_STMT, - AST_CT_FOR_STMT, AST_CT_FOREACH_STMT, + AST_CT_FOR_STMT, + AST_CT_IF_STMT, AST_CT_SWITCH_STMT, AST_DECLARE_STMT, AST_DEFAULT_STMT, @@ -138,6 +139,7 @@ typedef enum DECL_CT_IF, DECL_CT_SWITCH, DECL_CT_ASSERT, + DECL_CT_ECHO, DECL_DEFINE, DECL_DISTINCT, DECL_ENUM, @@ -162,7 +164,8 @@ typedef enum #define NON_TYPE_DECLS DECL_IMPORT: case DECL_MACRO: \ case DECL_DECLARRAY: case DECL_CT_IF: case DECL_CT_ELSE: case DECL_CT_ELIF: \ case DECL_CT_SWITCH: case DECL_CT_CASE: case DECL_ATTRIBUTE: case DECL_LABEL: \ - case DECL_DEFINE: case DECL_CT_ASSERT: case DECL_GENERIC: case DECL_INITIALIZE: case DECL_FINALIZE + case DECL_DEFINE: case DECL_CT_ASSERT: case DECL_GENERIC: case DECL_INITIALIZE: \ + case DECL_FINALIZE: case DECL_CT_ECHO #define NON_RUNTIME_EXPR EXPR_DESIGNATOR: case EXPR_POISONED: \ case EXPR_TYPEINFO: case EXPR_CT_IDENT: case EXPR_HASH_IDENT: \ @@ -563,17 +566,18 @@ typedef enum TOKEN_CT_CHECKS, // $checks TOKEN_CT_DEFAULT, // $default TOKEN_CT_DEFINED, // $defined - TOKEN_CT_FOR, // $for - TOKEN_CT_FOREACH, // $foreach + TOKEN_CT_ECHO, // $echo TOKEN_CT_ELIF, // $elif TOKEN_CT_ELSE, // $else - TOKEN_CT_EVAL, // $eval - TOKEN_CT_EVALTYPE, // $evaltype - TOKEN_CT_ENDIF, // $endif - TOKEN_CT_ENDSWITCH, // $endswitch TOKEN_CT_ENDFOR, // $endfor TOKEN_CT_ENDFOREACH, // $endforeach + TOKEN_CT_ENDIF, // $endif + TOKEN_CT_ENDSWITCH, // $endswitch + TOKEN_CT_EVAL, // $eval + TOKEN_CT_EVALTYPE, // $evaltype TOKEN_CT_EXTNAMEOF, // $extnameof + TOKEN_CT_FOR, // $for + TOKEN_CT_FOREACH, // $foreach TOKEN_CT_IF, // $if TOKEN_CT_NAMEOF, // $nameof TOKEN_CT_OFFSETOF, // $offsetof @@ -809,6 +813,7 @@ typedef enum ANALYSIS_REGISTER_GLOBALS, ANALYSIS_CONDITIONAL_COMPILATION, ANALYSIS_DECLS, + ANALYSIS_CT_ECHO, ANALYSIS_CT_ASSERT, ANALYSIS_FUNCTIONS, ANALYSIS_LAST = ANALYSIS_FUNCTIONS diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 34de6175e..d9954154a 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -1001,6 +1001,7 @@ LLVMValueRef llvm_get_ref(GenContext *c, Decl *decl) case DECL_INITIALIZE: case DECL_FINALIZE: case DECL_BODYPARAM: + case DECL_CT_ECHO: UNREACHABLE; } UNREACHABLE diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 00dedb395..67ccaa84b 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -1412,6 +1412,7 @@ void llvm_emit_stmt(GenContext *c, Ast *ast) case AST_CT_SWITCH_STMT: case AST_CASE_STMT: case AST_DEFAULT_STMT: + case AST_CT_ECHO_STMT: case AST_CT_FOREACH_STMT: UNREACHABLE case AST_SWITCH_STMT: diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index d1a633241..824e87740 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1470,7 +1470,11 @@ bool parse_struct_body(ParseContext *c, Decl *parent) while (1) { - EXPECT_OR_RET(TOKEN_IDENT, false); + if (!tok_is(c, TOKEN_IDENT)) + { + SEMA_ERROR_HERE("A valid member name was expected here."); + return false; + } Decl *member = DECL_VAR_NEW(type, VARDECL_MEMBER, parent->visibility); vec_add(parent->strukt.members, member); index++; @@ -2635,6 +2639,19 @@ Decl *parse_top_level_statement(ParseContext *c, ParseContext **c_ref) } return decl; } + case TOKEN_CT_ECHO: + if (!check_no_visibility_before(c, visibility)) return poisoned_decl; + { + ASSIGN_AST_OR_RET(Ast *ast, parse_ct_echo_stmt(c), poisoned_decl); + decl = decl_new_ct(DECL_CT_ECHO, ast->span); + decl->ct_echo_decl = ast; + if (docs) + { + SEMA_ERROR(astptr(docs), "Unexpected doc comment before $echo, did you mean to use a regular comment?"); + return poisoned_decl; + } + return decl; + } case TOKEN_CT_IF: { if (!check_no_visibility_before(c, visibility)) return poisoned_decl; diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index c4f0ae5b9..6a5554e65 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -1097,6 +1097,27 @@ Ast *parse_ct_assert_stmt(ParseContext *c) return ast; } +Ast *parse_ct_echo_stmt(ParseContext *c) +{ + Ast *ast = ast_new_curr(c, AST_CT_ECHO_STMT); + advance_and_verify(c, TOKEN_CT_ECHO); + TRY_CONSUME_OR_RET(TOKEN_LPAREN, "'$echo' needs a '(' here, did you forget it?", poisoned_ast); + ASSIGN_EXPR_OR_RET(ast->expr_stmt, parse_constant_expr(c), poisoned_ast); + + TRY_CONSUME_OR_RET(TOKEN_RPAREN, "The ending ')' was expected here.", poisoned_ast); + do + { + if (!tok_is(c, TOKEN_EOS)) + { + sema_error_at_after(c->prev_span, "Expected ';'"); + return poisoned_ast; + } + advance(c); + } + while (0); + return ast; +} + Ast *parse_stmt(ParseContext *c) { switch (c->tok) @@ -1166,6 +1187,8 @@ Ast *parse_stmt(ParseContext *c) SEMA_ERROR_HERE("'default' was found outside of 'switch', did you mismatch a '{ }' pair?"); advance(c); return poisoned_ast; + case TOKEN_CT_ECHO: + return parse_ct_echo_stmt(c); case TOKEN_CT_ASSERT: return parse_ct_assert_stmt(c); case TOKEN_CT_IF: diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index 1d4a41dff..0295e2414 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -18,6 +18,7 @@ Decl *parse_top_level_statement(ParseContext *c, ParseContext **new_context); Ast *parse_ct_assert_stmt(ParseContext *c); +Ast *parse_ct_echo_stmt(ParseContext *c); Ast *parse_stmt(ParseContext *c); Path *parse_path_prefix(ParseContext *c, bool *had_error); Expr *parse_type_expression_with_path(ParseContext *c, Path *path); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index c30d90d4b..a5fc6f2fe 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -2850,6 +2850,7 @@ bool sema_analyse_decl(SemaContext *context, Decl *decl) case DECL_CT_CASE: case DECL_CT_IF: case DECL_CT_ASSERT: + case DECL_CT_ECHO: case DECL_FAULTVALUE: case DECL_DECLARRAY: case DECL_BODYPARAM: diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 8b4ce348e..5d256558a 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -566,6 +566,7 @@ static inline bool sema_cast_ident_rvalue(SemaContext *context, Expr *expr) case DECL_ATTRIBUTE: case DECL_CT_ASSERT: case DECL_DEFINE: + case DECL_CT_ECHO: UNREACHABLE } switch (decl->var.kind) diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 370051dcc..38a7bb8f4 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -68,6 +68,7 @@ void sema_analysis_pass_register_globals(Module *module); void sema_analysis_pass_conditional_compilation(Module *module); void sema_analysis_pass_decls(Module *module); void sema_analysis_pass_ct_assert(Module *module); +void sema_analysis_pass_ct_echo(Module *module); void sema_analysis_pass_functions(Module *module); void sema_analyze_stage(Module *module, AnalysisStage stage); diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c index 8b16f459c..4fb037317 100644 --- a/src/compiler/sema_passes.c +++ b/src/compiler/sema_passes.c @@ -337,6 +337,29 @@ void sema_analysis_pass_ct_assert(Module *module) DEBUG_LOG("Pass finished with %d error(s).", global_context.errors_found); } +void sema_analysis_pass_ct_echo(Module *module) +{ + DEBUG_LOG("Pass: $echo checks %s", module->name->module); + VECEACH(module->units, index) + { + SemaContext context; + sema_context_init(&context, module->units[index]); + Decl **echos = context.unit->ct_echos; + bool success = true; + VECEACH(echos, i) + { + if (!sema_analyse_ct_echo_stmt(&context, echos[i]->ct_echo_decl)) + { + success = false; + break; + } + } + sema_context_destroy(&context); + if (!success) break; + } + DEBUG_LOG("Pass finished with %d error(s).", global_context.errors_found); +} + static inline bool analyse_func_body(SemaContext *context, Decl *decl) { if (!decl->func_decl.body) return true; diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 1e56c71a6..975ac1688 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -2402,9 +2402,6 @@ static inline bool sema_analyse_switch_stmt(SemaContext *context, Ast *statement return true; } - - - bool sema_analyse_ct_assert_stmt(SemaContext *context, Ast *statement) { Expr *expr = exprptr(statement->assert_stmt.expr); @@ -2424,7 +2421,7 @@ bool sema_analyse_ct_assert_stmt(SemaContext *context, Ast *statement) if (res == -1) return false; if (!res) { - if (message) + if (message_expr) { SEMA_ERROR(expr, "Compile time assert - %.*s", EXPAND_EXPR_STRING(message_expr)); } @@ -2438,6 +2435,51 @@ bool sema_analyse_ct_assert_stmt(SemaContext *context, Ast *statement) return true; } +bool sema_analyse_ct_echo_stmt(SemaContext *context, Ast *statement) +{ + Expr *message = statement->expr_stmt; + if (!sema_analyse_expr(context, message)) return false; + if (message->expr_kind != EXPR_CONST) + { + SEMA_ERROR(message, "Expected a constant value."); + return false; + } + printf("] "); + switch (message->const_expr.const_kind) + { + case CONST_FLOAT: + printf("%f\n", (double)message->const_expr.fxx.f); + break; + case CONST_INTEGER: + puts(int_to_str(message->const_expr.ixx, 10)); + break; + case CONST_BOOL: + puts(message->const_expr.b ? "true" : "false"); + break; + case CONST_ENUM: + case CONST_ERR: + puts(message->const_expr.enum_err_val->name); + break; + case CONST_STRING: + printf("%.*s\n", EXPAND_EXPR_STRING(message)); + break; + case CONST_POINTER: + printf("%p\n", (void*)message->const_expr.ptr); + break; + case CONST_TYPEID: + puts(type_to_error_string(message->const_expr.typeid)); + break; + case CONST_BYTES: + case CONST_INITIALIZER: + case CONST_UNTYPED_LIST: + case CONST_MEMBER: + SEMA_ERROR(message, "Unsupported type for '$echo'"); + break; + } + statement->ast_kind = AST_NOP_STMT; + return true; +} + /** * $for(, , ): */ @@ -2560,6 +2602,8 @@ static inline bool sema_analyse_statement_inner(SemaContext *context, Ast *state return sema_analyse_ct_assert_stmt(context, statement); case AST_CT_IF_STMT: return sema_analyse_ct_if_stmt(context, statement); + case AST_CT_ECHO_STMT: + return sema_analyse_ct_echo_stmt(context, statement); case AST_DECLARE_STMT: return sema_analyse_declare_stmt(context, statement); case AST_DEFAULT_STMT: diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index ce00b5cff..88efcfd04 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -240,6 +240,7 @@ static bool sema_resolve_type_identifier(SemaContext *context, TypeInfo *type_in case DECL_CT_SWITCH: case DECL_CT_CASE: case DECL_CT_ASSERT: + case DECL_CT_ECHO: case DECL_DECLARRAY: case DECL_BODYPARAM: UNREACHABLE diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index cd943fd15..376d79bbb 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -141,6 +141,9 @@ void sema_analyze_stage(Module *module, AnalysisStage stage) case ANALYSIS_DECLS: sema_analysis_pass_decls(module); break; + case ANALYSIS_CT_ECHO: + sema_analysis_pass_ct_echo(module); + break; case ANALYSIS_CT_ASSERT: sema_analysis_pass_ct_assert(module); break; @@ -166,6 +169,7 @@ static void register_generic_decls(CompilationUnit *unit, Decl **decls) case DECL_IMPORT: case DECL_LABEL: case DECL_CT_ASSERT: + case DECL_CT_ECHO: case DECL_DECLARRAY: case DECL_INITIALIZE: case DECL_FINALIZE: diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index 8ea080f26..77f0d2608 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -390,6 +390,8 @@ const char *token_type_to_string(TokenType type) return "$typeof"; case TOKEN_CT_STRINGIFY: return "$stringify"; + case TOKEN_CT_ECHO: + return "$echo"; case TOKEN_EOF: return "EOF"; diff --git a/src/version.h b/src/version.h index b45473eaf..a89ecbedb 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.3.111" \ No newline at end of file +#define COMPILER_VERSION "0.3.112" \ No newline at end of file