diff --git a/ast.cpp b/ast.cpp index 1a9e1bd..a943f0d 100644 --- a/ast.cpp +++ b/ast.cpp @@ -20,6 +20,8 @@ enum Ast_Kind: U32{ AST_CALL_ITEM, AST_CALL, + AST_CONSTANT_ASSERT, + AST_RUNTIME_ASSERT, AST_SIZE_OF, AST_LENGTH_OF, AST_ALIGN_OF, @@ -119,7 +121,8 @@ struct Ast_Binary: Ast_Expr{ struct Ast_Builtin: Ast_Expr{ Ast_Expr *expr; - U64 padding[3]; // For folding constants into atoms + Intern_String assert_message; + U64 padding[1]; // For folding constants into atoms }; // Problem: We are parsing out of order, in the middle of parsing a function @@ -197,7 +200,6 @@ struct Ast_Scope: Ast{ Ast_Module *module; }; - struct Ast_Module: Ast_Scope{ Array all_loaded_files; }; @@ -507,6 +509,22 @@ ast_module(Intern_String filename){ return result; } +function Ast_Builtin * +ast_runtime_assert(Token *pos, Ast_Expr *expr, Intern_String message){ + AST_NEW(Builtin, RUNTIME_ASSERT, pos, AST_EXPR); + result->expr = expr; + result->assert_message = message; + return result; +} + +function Ast_Builtin * +ast_constant_assert(Token *pos, Ast_Expr *expr, Intern_String message){ + AST_NEW(Builtin, CONSTANT_ASSERT, pos, AST_EXPR); + result->expr = expr; + result->assert_message = message; + return result; +} + function Ast_Builtin * ast_sizeof(Token *pos, Ast_Expr *expr){ AST_NEW(Builtin, SIZE_OF, pos, AST_EXPR); diff --git a/ccodegen.cpp b/ccodegen.cpp index 03d3607..d50f2a2 100644 --- a/ccodegen.cpp +++ b/ccodegen.cpp @@ -365,6 +365,18 @@ function void gen_ast(Ast *ast){ switch(ast->kind){ + CASE(RUNTIME_ASSERT, Builtin){ + if(node->assert_message.len == 0) gen("assert"); + else gen("assert_msg"); + gen("("); + gen_expr(node->expr); + if(node->assert_message.len){ + gen(", \"%s\"", node->assert_message.str); + } + gen(");"); + BREAK(); + } + CASE(RETURN, Return){ gen("return"); if(node->expr){ @@ -504,6 +516,7 @@ gen_ast(Ast *ast){ BREAK(); } + case AST_CONSTANT_ASSERT: case AST_MODULE_NAMESPACE: CASE(FILE_NAMESPACE, File_Namespace){unused(node); BREAK();} diff --git a/compiler.h b/compiler.h index 6b4042a..ee91575 100644 --- a/compiler.h +++ b/compiler.h @@ -164,6 +164,7 @@ Intern_String keyword_false; Intern_String keyword_for; Intern_String keyword_pass; Intern_String keyword_elif; +Intern_String keyword_assert; Intern_String keyword_sizeof; Intern_String keyword_alignof; Intern_String keyword_lengthof; @@ -211,6 +212,7 @@ lex_init(Allocator *token_string_arena, Allocator *map_allocator, Lexer *l){ keyword_true = l->intern("true"_s); keyword_false = l->intern("false"_s); keyword_return = l->intern("return"_s); + keyword_assert = l->intern("assert"_s); keyword_if = l->intern("if"_s); keyword_elif = l->intern("elif"_s); keyword_pass = l->intern("pass"_s); diff --git a/parsing.cpp b/parsing.cpp index 159b009..c63d20b 100644 --- a/parsing.cpp +++ b/parsing.cpp @@ -224,6 +224,30 @@ parse_stmt_scope(Ast_Scope *scope_defined_outside = 0){ scope->stmts.add(ast_pass(token)); } + else if(token_match_keyword(keyword_assert)){ + token_expect(TK_OpenParen); + Ast_Expr *expr = parse_expr(); + Intern_String message = {}; + if(token_match(TK_Comma)){ + Token *t = token_expect(TK_StringLit); + message = t->intern_val; + } + token_expect(TK_CloseParen); + scope->stmts.add(ast_runtime_assert(token, expr, message)); + } + + else if(token_match_pound(keyword_assert)){ + token_expect(TK_OpenParen); + Ast_Expr *expr = parse_expr(); + Intern_String message = {}; + if(token_match(TK_Comma)){ + Token *t = token_expect(TK_StringLit); + message = t->intern_val; + } + token_expect(TK_CloseParen); + scope->stmts.add(ast_constant_assert(token, expr, message)); + } + else if(token_match_keyword(keyword_for)){ Ast_Scope *for_scope = begin_stmt_scope(scratch, token_get()); Ast_Expr *init = 0; diff --git a/programs/main.kl b/programs/main.kl index 6c35855..7f2c169 100644 --- a/programs/main.kl +++ b/programs/main.kl @@ -13,8 +13,6 @@ UTF16_Result :: struct len : S32 error : S32 -BINARY :: 0b1001 - utf8_to_utf32 :: (c: *U8, max_advance: S64): UTF32_Result result: UTF32_Result if (c[0] & 0b10000000) == 0 @@ -91,18 +89,14 @@ utf32_to_utf16 :: (codepoint: U32): UTF16_Result // return result; // } - - +Vec2I :: struct;; x: S32; y: S32 +Vec2 :: struct;; x: F32; y: F32 Windows_Bitmap :: struct size: Vec2I data: *U32 hdc: HDC dib: HBITMAP -Vec2I :: struct;; x: S32; y: S32 -Vec2 :: struct;; x: F32; y: F32 - - create_bitmap :: (size: Vec2I, bottom_up: Bool = true): Windows_Bitmap result: Windows_Bitmap = {size = size} if bottom_up == false @@ -136,6 +130,7 @@ window_procedure :: (hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM): LRE main :: (argc: int, argv: **char): int bitmap := create_bitmap({1280, 720}) result := utf8_to_utf32(&"A"[0], 1) + assert(result.out_str == 'A, "Invalid decode") result = utf8_to_utf32(&"ć"[0], 2) result = utf8_to_utf32(&"ó"[0], 2) diff --git a/typechecking.cpp b/typechecking.cpp index 817a04b..d917b41 100644 --- a/typechecking.cpp +++ b/typechecking.cpp @@ -443,6 +443,20 @@ resolve_stmt(Ast *ast, Ast_Type *ret){ BREAK(); } + CASE(RUNTIME_ASSERT, Builtin){ + resolve_and_require_bool("Assert condition is not boolean", node->expr, AST_CAN_BE_NULL); + BREAK(); + } + + CASE(CONSTANT_ASSERT, Builtin){ + Operand op = resolve_and_require_bool("Assert condition is not boolean", node->expr, AST_CAN_BE_NULL); + if(!op.is_const) compiler_error(node->pos, "#assert expression required to be constant"); + if(op.bool_val == false){ + compiler_error(node->pos, "#assert condition not met :: %.*s", (int)node->assert_message.len, node->assert_message.str); + } + BREAK(); + } + CASE(FOR, For){ if(node->init && node->cond == 0 && node->iter == 0){ if(!is_flag_set(node->init->flags, AST_STMT)){