diff --git a/ast.cpp b/ast.cpp index b8ec86d..2e59e92 100644 --- a/ast.cpp +++ b/ast.cpp @@ -26,6 +26,8 @@ enum Ast_Kind: U32{ AST_LENGTH_OF, AST_ALIGN_OF, + AST_SWITCH, + AST_SWITCH_CASE, AST_VAR_UNPACK, AST_BREAK, AST_COMPOUND, @@ -174,6 +176,18 @@ struct Ast_Array: Ast_Expr{ Ast_Expr *expr; }; +struct Ast_Switch_Case: Ast{ + Array labels; + Ast_Scope *scope; + B32 fallthrough; +}; + +struct Ast_Switch: Ast{ + Ast_Expr *value; + Array cases; + Ast_Scope *default_scope; +}; + /* How does current declaration order resolver works: * First we put all the global declarations into the global scope (when parsing) all unresolved @@ -251,6 +265,18 @@ struct Ast_Decl: Ast{ result->pos = ipos; \ result->di = ++pctx->unique_ids +#define MAKE_AST(T,kind,pos,flags) (T *)ast_new(sizeof(T), kind, pos, flags) +function Ast * +ast_new(SizeU size, Ast_Kind kind, Token *pos, Ast_Flag flags = 0){ + Ast *result = (Ast *)exp_alloc(pctx->perm, size, AF_ZeroMemory); + result->flags = flags; + result->kind = kind; + result->parent_scope = pctx->currently_parsed_scope; + result->pos = pos; + result->di = ++pctx->unique_ids; + return result; +} + function Ast_Atom * ast_str(Token *pos, Intern_String string){ AST_NEW(Atom, VALUE, pos, AST_EXPR | AST_ATOM); diff --git a/ccodegen.cpp b/ccodegen.cpp index f9c4501..fef8411 100644 --- a/ccodegen.cpp +++ b/ccodegen.cpp @@ -192,7 +192,7 @@ gen_value(Value a){ } function void -gen_stmt_scope(Ast_Scope *scope){ +gen_stmt_scope(Ast_Scope *scope, B32 switch_case_gen_break = 0){ gen("{"); global_indent++; For(scope->stmts) { @@ -200,6 +200,7 @@ gen_stmt_scope(Ast_Scope *scope){ genln(""); gen_ast(it); } + if(switch_case_gen_break == 1) genln("break;"); global_indent--; genln("}"); } @@ -570,6 +571,33 @@ gen_ast(Ast *ast){ BREAK(); } + CASE(SWITCH, Switch){ + gen("switch("); + gen_expr(node->value); + gen("){"); + global_indent++; + + For(node->cases){ + For_Named(it->labels, label){ + genln("case "); + gen_expr(label); + gen(":"); + } + + gen_stmt_scope(it->scope, it->fallthrough ? false : true); + } + + if(node->default_scope){ + genln("default: "); + gen_stmt_scope(node->default_scope); + } + + global_indent--; + genln("}"); + + BREAK(); + } + CASE(VAR_UNPACK, Var_Unpack){ For(node->vars) gen_ast(it); @@ -771,6 +799,7 @@ typedef S64 SizeS; typedef float F32; typedef double F64; typedef S32 Bool; +typedef S64 Type; #define true 1 #define false 0 diff --git a/compiler.h b/compiler.h index 01db6ac..04db2b0 100644 --- a/compiler.h +++ b/compiler.h @@ -163,6 +163,8 @@ Intern_String keyword_true; Intern_String keyword_false; Intern_String keyword_for; Intern_String keyword_pass; +Intern_String keyword_default; +Intern_String keyword_switch; Intern_String keyword_break; Intern_String keyword_elif; Intern_String keyword_assert; @@ -219,9 +221,11 @@ lex_init(Allocator *token_string_arena, Allocator *map_allocator, Lexer *l){ keyword_lengthof = l->intern("length_of"_s); keyword_alignof = l->intern("align_of"_s); keyword_true = l->intern("true"_s); + keyword_default = l->intern("default"_s); keyword_break = l->intern("break"_s); keyword_false = l->intern("false"_s); keyword_return = l->intern("return"_s); + keyword_switch = l->intern("switch"_s); keyword_assert = l->intern("assert"_s); keyword_if = l->intern("if"_s); keyword_elif = l->intern("elif"_s); diff --git a/parsing.cpp b/parsing.cpp index 6fe7e09..da67417 100644 --- a/parsing.cpp +++ b/parsing.cpp @@ -234,6 +234,37 @@ parse_stmt_scope(Ast_Scope *scope_defined_outside = 0){ scope->stmts.add(ast_pass(token)); } + else if(token_match_keyword(keyword_switch)){ + Ast_Switch *result = MAKE_AST(Ast_Switch, AST_SWITCH, token, AST_STMT); + result->value = parse_expr(); + result->cases = {scratch}; + + token_expect(OPEN_SCOPE); + do{ + if(token_match_keyword(keyword_default)){ + result->default_scope = parse_stmt_scope(); + continue; + } + + Ast_Switch_Case *switch_case = MAKE_AST(Ast_Switch_Case, AST_SWITCH_CASE, token_get(), AST_STMT); + if(token_match_pound(pctx->intern("fallthrough"_s))) + switch_case->fallthrough = true; + + switch_case->labels = {scratch}; + do{ + switch_case->labels.add(parse_expr()); + }while(token_match(TK_Comma)); + switch_case->labels = switch_case->labels.tight_copy(pctx->perm); + + switch_case->scope = parse_stmt_scope(); + result->cases.add(switch_case); + }while(token_match(SAME_SCOPE)); + token_expect(CLOSE_SCOPE); + result->cases = result->cases.tight_copy(pctx->perm); + + scope->stmts.add(result); + } + else if(token_match_keyword(keyword_assert)){ token_expect(TK_OpenParen); Ast_Expr *expr = parse_expr(); diff --git a/programs/main.kl b/programs/main.kl index a8200cc..1859ac8 100644 --- a/programs/main.kl +++ b/programs/main.kl @@ -41,6 +41,13 @@ create_bitmap :: (size: Vec2I, bottom_up: Bool = true): Windows_Bitmap result.hdc = CreateCompatibleDC(hdc) return result +print :: (type: Type) + switch type + int, S64, S32, S16, S8 + OutputDebugStringA("int") + default + OutputDebugStringA("unknown_type") + app_is_running := true window_procedure :: (hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM): LRESULT if msg == WM_DESTROY diff --git a/typechecking.cpp b/typechecking.cpp index 9c3adb4..39a1d91 100644 --- a/typechecking.cpp +++ b/typechecking.cpp @@ -580,6 +580,21 @@ resolve_stmt(Ast *ast, Ast_Type *ret){ BREAK(); } + CASE(SWITCH, Switch){ + // @todo better typechecking + resolve_expr(node->value, AST_CANT_BE_NULL); + For(node->cases){ + For_Named(it->labels, label){ + Operand op = resolve_expr(label, AST_CANT_BE_NULL); + if(!op.is_const) compiler_error(label->pos, "Switch label required to be constant"); + } + For_Named(it->scope->stmts, stmt) resolve_stmt(stmt, ret); + } + For_Named(node->default_scope->stmts, stmt) resolve_stmt(stmt, ret); + + BREAK(); + } + CASE(VAR_UNPACK, Var_Unpack){ Operand expr_op = resolve_expr(node->expr, AST_CANT_BE_NULL); if(!is_tuple(expr_op.type)) diff --git a/types.h b/types.h index a571101..f1b4b1f 100644 --- a/types.h +++ b/types.h @@ -124,7 +124,7 @@ name(Ast_Type *type){ case TYPE_U64: return "U64"; case TYPE_ANY: return "Any"; case TYPE_TUPLE: return "Tuple"; - case TYPE_TYPE: return "S64"; + case TYPE_TYPE: return "Type"; invalid_default_case; } return "";