| author | |
| committer | |
| log | 3c3be10a605085d8855e54b8c6a769d6ad1e00d9 |
| tree | f3c31aa240dcd35937a9e5d99a266b393db5800a |
| parent | dfb48a2c6bc034dab8d7c17db8d67089fc68043d |
7 files changed, 226 insertions(+), 20 deletions(-)
example/expressions/expressions.zig+16| ... | @@ -1,3 +1,5 @@ | ... | @@ -1,3 +1,5 @@ |
| 1 | export executable "expressions"; | ||
| 2 | |||
| 1 | #link("c") | 3 | #link("c") |
| 2 | extern { | 4 | extern { |
| 3 | fn puts(s: *const u8) -> i32; | 5 | fn puts(s: *const u8) -> i32; |
| ... | @@ -28,6 +30,8 @@ export fn _start() -> unreachable { | ... | @@ -28,6 +30,8 @@ export fn _start() -> unreachable { |
| 28 | 30 | ||
| 29 | void_fun(1, void, 2); | 31 | void_fun(1, void, 2); |
| 30 | 32 | ||
| 33 | test_mutable_vars(); | ||
| 34 | |||
| 31 | other_exit(); | 35 | other_exit(); |
| 32 | } | 36 | } |
| 33 | 37 | ||
| ... | @@ -38,3 +42,15 @@ fn void_fun(a : i32, b : void, c : i32) -> void { | ... | @@ -38,3 +42,15 @@ fn void_fun(a : i32, b : void, c : i32) -> void { |
| 38 | let w : void = z; // void | 42 | let w : void = z; // void |
| 39 | if (x + y == 4) { return w; } | 43 | if (x + y == 4) { return w; } |
| 40 | } | 44 | } |
| 45 | |||
| 46 | fn test_mutable_vars() { | ||
| 47 | let mut i = 0; | ||
| 48 | loop_start: | ||
| 49 | if i == 3 { | ||
| 50 | goto done; | ||
| 51 | } | ||
| 52 | puts("loop"); | ||
| 53 | i = i + 1; | ||
| 54 | goto loop_start; | ||
| 55 | done: | ||
| 56 | } |
src/analyze.cpp+46-3| ... | @@ -362,6 +362,13 @@ static BlockContext *new_block_context(AstNode *node, BlockContext *parent) { | ... | @@ -362,6 +362,13 @@ static BlockContext *new_block_context(AstNode *node, BlockContext *parent) { |
| 362 | else | 362 | else |
| 363 | context->root = context; | 363 | context->root = context; |
| 364 | context->variable_table.init(8); | 364 | context->variable_table.init(8); |
| 365 | |||
| 366 | AstNode *fn_def_node = context->root->node; | ||
| 367 | assert(fn_def_node->type == NodeTypeFnDef); | ||
| 368 | assert(fn_def_node->codegen_node); | ||
| 369 | FnDefNode *fn_def_info = &fn_def_node->codegen_node->data.fn_def_node; | ||
| 370 | fn_def_info->all_block_contexts.append(context); | ||
| 371 | |||
| 365 | return context; | 372 | return context; |
| 366 | } | 373 | } |
| 367 | 374 | ||
| ... | @@ -388,8 +395,13 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, | ... | @@ -388,8 +395,13 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 388 | return_type = g->builtin_types.entry_void; | 395 | return_type = g->builtin_types.entry_void; |
| 389 | for (int i = 0; i < node->data.block.statements.length; i += 1) { | 396 | for (int i = 0; i < node->data.block.statements.length; i += 1) { |
| 390 | AstNode *child = node->data.block.statements.at(i); | 397 | AstNode *child = node->data.block.statements.at(i); |
| 391 | if (child->type == NodeTypeLabel) | 398 | if (child->type == NodeTypeLabel) { |
| 399 | LabelTableEntry *label_entry = child->codegen_node->data.label_entry; | ||
| 400 | assert(label_entry); | ||
| 401 | label_entry->entered_from_fallthrough = (return_type != g->builtin_types.entry_unreachable); | ||
| 402 | return_type = g->builtin_types.entry_void; | ||
| 392 | continue; | 403 | continue; |
| 404 | } | ||
| 393 | if (return_type == g->builtin_types.entry_unreachable) { | 405 | if (return_type == g->builtin_types.entry_unreachable) { |
| 394 | if (child->type == NodeTypeVoid) { | 406 | if (child->type == NodeTypeVoid) { |
| 395 | // {unreachable;void;void} is allowed. | 407 | // {unreachable;void;void} is allowed. |
| ... | @@ -457,6 +469,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, | ... | @@ -457,6 +469,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 457 | LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1); | 469 | LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1); |
| 458 | buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol); | 470 | buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol); |
| 459 | variable_entry->type = type; | 471 | variable_entry->type = type; |
| 472 | variable_entry->is_const = variable_declaration->is_const; | ||
| 473 | variable_entry->decl_node = node; | ||
| 460 | context->variable_table.put(&variable_entry->name, variable_entry); | 474 | context->variable_table.put(&variable_entry->name, variable_entry); |
| 461 | } | 475 | } |
| 462 | return_type = g->builtin_types.entry_void; | 476 | return_type = g->builtin_types.entry_void; |
| ... | @@ -482,6 +496,32 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, | ... | @@ -482,6 +496,32 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 482 | case NodeTypeBinOpExpr: | 496 | case NodeTypeBinOpExpr: |
| 483 | { | 497 | { |
| 484 | switch (node->data.bin_op_expr.bin_op) { | 498 | switch (node->data.bin_op_expr.bin_op) { |
| 499 | case BinOpTypeAssign: | ||
| 500 | { | ||
| 501 | AstNode *lhs_node = node->data.bin_op_expr.op1; | ||
| 502 | if (lhs_node->type == NodeTypeSymbol) { | ||
| 503 | Buf *name = &lhs_node->data.symbol; | ||
| 504 | LocalVariableTableEntry *var = find_local_variable(context, name); | ||
| 505 | if (var) { | ||
| 506 | if (var->is_const) { | ||
| 507 | add_node_error(g, lhs_node, | ||
| 508 | buf_sprintf("cannot assign to constant variable")); | ||
| 509 | } else { | ||
| 510 | analyze_expression(g, import, context, var->type, | ||
| 511 | node->data.bin_op_expr.op2); | ||
| 512 | } | ||
| 513 | } else { | ||
| 514 | add_node_error(g, lhs_node, | ||
| 515 | buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name))); | ||
| 516 | } | ||
| 517 | |||
| 518 | } else { | ||
| 519 | add_node_error(g, lhs_node, | ||
| 520 | buf_sprintf("expected a bare identifier")); | ||
| 521 | } | ||
| 522 | return_type = g->builtin_types.entry_void; | ||
| 523 | break; | ||
| 524 | } | ||
| 485 | case BinOpTypeBoolOr: | 525 | case BinOpTypeBoolOr: |
| 486 | case BinOpTypeBoolAnd: | 526 | case BinOpTypeBoolAnd: |
| 487 | analyze_expression(g, import, context, g->builtin_types.entry_bool, | 527 | analyze_expression(g, import, context, g->builtin_types.entry_bool, |
| ... | @@ -721,7 +761,10 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, | ... | @@ -721,7 +761,10 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, |
| 721 | AstNode *fn_proto_node = node->data.fn_def.fn_proto; | 761 | AstNode *fn_proto_node = node->data.fn_def.fn_proto; |
| 722 | assert(fn_proto_node->type == NodeTypeFnProto); | 762 | assert(fn_proto_node->type == NodeTypeFnProto); |
| 723 | 763 | ||
| 764 | assert(!node->codegen_node); | ||
| 765 | node->codegen_node = allocate<CodeGenNode>(1); | ||
| 724 | BlockContext *context = new_block_context(node, nullptr); | 766 | BlockContext *context = new_block_context(node, nullptr); |
| 767 | node->codegen_node->data.fn_def_node.block_context = context; | ||
| 725 | 768 | ||
| 726 | AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto; | 769 | AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto; |
| 727 | for (int i = 0; i < fn_proto->params.length; i += 1) { | 770 | for (int i = 0; i < fn_proto->params.length; i += 1) { |
| ... | @@ -736,6 +779,8 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, | ... | @@ -736,6 +779,8 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, |
| 736 | LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1); | 779 | LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1); |
| 737 | buf_init_from_buf(&variable_entry->name, &param_decl->name); | 780 | buf_init_from_buf(&variable_entry->name, &param_decl->name); |
| 738 | variable_entry->type = type; | 781 | variable_entry->type = type; |
| 782 | variable_entry->is_const = true; | ||
| 783 | variable_entry->decl_node = param_decl_node; | ||
| 739 | 784 | ||
| 740 | LocalVariableTableEntry *existing_entry = find_local_variable(context, &variable_entry->name); | 785 | LocalVariableTableEntry *existing_entry = find_local_variable(context, &variable_entry->name); |
| 741 | if (!existing_entry) { | 786 | if (!existing_entry) { |
| ... | @@ -756,9 +801,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, | ... | @@ -756,9 +801,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, |
| 756 | TypeTableEntry *expected_type = fn_proto->return_type->codegen_node->data.type_node.entry; | 801 | TypeTableEntry *expected_type = fn_proto->return_type->codegen_node->data.type_node.entry; |
| 757 | TypeTableEntry *block_return_type = analyze_expression(g, import, context, expected_type, node->data.fn_def.body); | 802 | TypeTableEntry *block_return_type = analyze_expression(g, import, context, expected_type, node->data.fn_def.body); |
| 758 | 803 | ||
| 759 | node->codegen_node = allocate<CodeGenNode>(1); | ||
| 760 | node->codegen_node->data.fn_def_node.implicit_return_type = block_return_type; | 804 | node->codegen_node->data.fn_def_node.implicit_return_type = block_return_type; |
| 761 | node->codegen_node->data.fn_def_node.block_context = context; | ||
| 762 | 805 | ||
| 763 | { | 806 | { |
| 764 | FnTableEntry *fn_table_entry = fn_proto_node->codegen_node->data.fn_proto_node.fn_table_entry; | 807 | FnTableEntry *fn_table_entry = fn_proto_node->codegen_node->data.fn_proto_node.fn_table_entry; |
src/codegen.cpp+68-8| ... | @@ -102,6 +102,7 @@ static int count_non_void_params(CodeGen *g, ZigList<AstNode *> *params) { | ... | @@ -102,6 +102,7 @@ static int count_non_void_params(CodeGen *g, ZigList<AstNode *> *params) { |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | static void add_debug_source_node(CodeGen *g, AstNode *node) { | 104 | static void add_debug_source_node(CodeGen *g, AstNode *node) { |
| 105 | // TODO g->block_scopes.last() is not always correct and should probably integrate with BlockContext | ||
| 105 | LLVMZigSetCurrentDebugLocation(g->builder, node->line + 1, node->column + 1, g->block_scopes.last()); | 106 | LLVMZigSetCurrentDebugLocation(g->builder, node->line + 1, node->column + 1, g->block_scopes.last()); |
| 106 | } | 107 | } |
| 107 | 108 | ||
| ... | @@ -210,6 +211,8 @@ static LLVMValueRef gen_arithmetic_bin_op_expr(CodeGen *g, AstNode *node) { | ... | @@ -210,6 +211,8 @@ static LLVMValueRef gen_arithmetic_bin_op_expr(CodeGen *g, AstNode *node) { |
| 210 | LLVMValueRef val2 = gen_expr(g, node->data.bin_op_expr.op2); | 211 | LLVMValueRef val2 = gen_expr(g, node->data.bin_op_expr.op2); |
| 211 | 212 | ||
| 212 | switch (node->data.bin_op_expr.bin_op) { | 213 | switch (node->data.bin_op_expr.bin_op) { |
| 214 | case BinOpTypeAssign: | ||
| 215 | zig_panic("TODO assignment"); | ||
| 213 | case BinOpTypeBinOr: | 216 | case BinOpTypeBinOr: |
| 214 | add_debug_source_node(g, node); | 217 | add_debug_source_node(g, node); |
| 215 | return LLVMBuildOr(g->builder, val1, val2, ""); | 218 | return LLVMBuildOr(g->builder, val1, val2, ""); |
| ... | @@ -358,8 +361,28 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) { | ... | @@ -358,8 +361,28 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) { |
| 358 | return phi; | 361 | return phi; |
| 359 | } | 362 | } |
| 360 | 363 | ||
| 364 | static LLVMValueRef gen_assign_expr(CodeGen *g, AstNode *node) { | ||
| 365 | assert(node->type == NodeTypeBinOpExpr); | ||
| 366 | |||
| 367 | AstNode *symbol_node = node->data.bin_op_expr.op1; | ||
| 368 | assert(symbol_node->type == NodeTypeSymbol); | ||
| 369 | |||
| 370 | LocalVariableTableEntry *var = find_local_variable(node->codegen_node->expr_node.block_context, | ||
| 371 | &symbol_node->data.symbol); | ||
| 372 | |||
| 373 | // semantic checking ensures no variables are constant | ||
| 374 | assert(!var->is_const); | ||
| 375 | |||
| 376 | LLVMValueRef value = gen_expr(g, node->data.bin_op_expr.op2); | ||
| 377 | |||
| 378 | add_debug_source_node(g, node); | ||
| 379 | return LLVMBuildStore(g->builder, value, var->value_ref); | ||
| 380 | } | ||
| 381 | |||
| 361 | static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { | 382 | static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { |
| 362 | switch (node->data.bin_op_expr.bin_op) { | 383 | switch (node->data.bin_op_expr.bin_op) { |
| 384 | case BinOpTypeAssign: | ||
| 385 | return gen_assign_expr(g, node); | ||
| 363 | case BinOpTypeInvalid: | 386 | case BinOpTypeInvalid: |
| 364 | zig_unreachable(); | 387 | zig_unreachable(); |
| 365 | case BinOpTypeBoolOr: | 388 | case BinOpTypeBoolOr: |
| ... | @@ -498,9 +521,20 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { | ... | @@ -498,9 +521,20 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { |
| 498 | case NodeTypeVariableDeclaration: | 521 | case NodeTypeVariableDeclaration: |
| 499 | { | 522 | { |
| 500 | LocalVariableTableEntry *variable = find_local_variable(node->codegen_node->expr_node.block_context, &node->data.variable_declaration.symbol); | 523 | LocalVariableTableEntry *variable = find_local_variable(node->codegen_node->expr_node.block_context, &node->data.variable_declaration.symbol); |
| 501 | assert(node->data.variable_declaration.expr); | 524 | if (variable->is_const) { |
| 502 | variable->value_ref = gen_expr(g, node->data.variable_declaration.expr); | 525 | assert(node->data.variable_declaration.expr); |
| 503 | return nullptr; | 526 | variable->value_ref = gen_expr(g, node->data.variable_declaration.expr); |
| 527 | return nullptr; | ||
| 528 | } else { | ||
| 529 | if (node->data.variable_declaration.expr) { | ||
| 530 | LLVMValueRef value = gen_expr(g, node->data.variable_declaration.expr); | ||
| 531 | |||
| 532 | add_debug_source_node(g, node); | ||
| 533 | return LLVMBuildStore(g->builder, value, variable->value_ref); | ||
| 534 | } else { | ||
| 535 | |||
| 536 | } | ||
| 537 | } | ||
| 504 | } | 538 | } |
| 505 | case NodeTypeCastExpr: | 539 | case NodeTypeCastExpr: |
| 506 | return gen_cast_expr(g, node); | 540 | return gen_cast_expr(g, node); |
| ... | @@ -542,7 +576,11 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { | ... | @@ -542,7 +576,11 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { |
| 542 | case NodeTypeSymbol: | 576 | case NodeTypeSymbol: |
| 543 | { | 577 | { |
| 544 | LocalVariableTableEntry *variable = find_local_variable(node->codegen_node->expr_node.block_context, &node->data.symbol); | 578 | LocalVariableTableEntry *variable = find_local_variable(node->codegen_node->expr_node.block_context, &node->data.symbol); |
| 545 | return variable->value_ref; | 579 | if (variable->is_const) { |
| 580 | return variable->value_ref; | ||
| 581 | } else { | ||
| 582 | return LLVMBuildLoad(g->builder, variable->value_ref, ""); | ||
| 583 | } | ||
| 546 | } | 584 | } |
| 547 | case NodeTypeBlock: | 585 | case NodeTypeBlock: |
| 548 | return gen_block(g, node, nullptr); | 586 | return gen_block(g, node, nullptr); |
| ... | @@ -551,11 +589,15 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { | ... | @@ -551,11 +589,15 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { |
| 551 | return LLVMBuildBr(g->builder, node->codegen_node->data.label_entry->basic_block); | 589 | return LLVMBuildBr(g->builder, node->codegen_node->data.label_entry->basic_block); |
| 552 | case NodeTypeLabel: | 590 | case NodeTypeLabel: |
| 553 | { | 591 | { |
| 554 | LLVMBasicBlockRef basic_block = node->codegen_node->data.label_entry->basic_block; | 592 | LabelTableEntry *label_entry = node->codegen_node->data.label_entry; |
| 555 | add_debug_source_node(g, node); | 593 | assert(label_entry); |
| 556 | LLVMValueRef result = LLVMBuildBr(g->builder, basic_block); | 594 | LLVMBasicBlockRef basic_block = label_entry->basic_block; |
| 595 | if (label_entry->entered_from_fallthrough) { | ||
| 596 | add_debug_source_node(g, node); | ||
| 597 | LLVMBuildBr(g->builder, basic_block); | ||
| 598 | } | ||
| 557 | LLVMPositionBuilderAtEnd(g->builder, basic_block); | 599 | LLVMPositionBuilderAtEnd(g->builder, basic_block); |
| 558 | return result; | 600 | return nullptr; |
| 559 | } | 601 | } |
| 560 | case NodeTypeRoot: | 602 | case NodeTypeRoot: |
| 561 | case NodeTypeRootExportDecl: | 603 | case NodeTypeRootExportDecl: |
| ... | @@ -696,6 +738,24 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -696,6 +738,24 @@ static void do_code_gen(CodeGen *g) { |
| 696 | 738 | ||
| 697 | build_label_blocks(g, fn_def_node->data.fn_def.body); | 739 | build_label_blocks(g, fn_def_node->data.fn_def.body); |
| 698 | 740 | ||
| 741 | // allocate all local variables | ||
| 742 | for (int i = 0; i < codegen_fn_def->all_block_contexts.length; i += 1) { | ||
| 743 | BlockContext *block_context = codegen_fn_def->all_block_contexts.at(i); | ||
| 744 | |||
| 745 | auto it = block_context->variable_table.entry_iterator(); | ||
| 746 | for (;;) { | ||
| 747 | auto *entry = it.next(); | ||
| 748 | if (!entry) | ||
| 749 | break; | ||
| 750 | |||
| 751 | LocalVariableTableEntry *var = entry->value; | ||
| 752 | if (!var->is_const) { | ||
| 753 | add_debug_source_node(g, var->decl_node); | ||
| 754 | var->value_ref = LLVMBuildAlloca(g->builder, var->type->type_ref, buf_ptr(&var->name)); | ||
| 755 | } | ||
| 756 | } | ||
| 757 | } | ||
| 758 | |||
| 699 | TypeTableEntry *implicit_return_type = codegen_fn_def->implicit_return_type; | 759 | TypeTableEntry *implicit_return_type = codegen_fn_def->implicit_return_type; |
| 700 | gen_block(g, fn_def_node->data.fn_def.body, implicit_return_type); | 760 | gen_block(g, fn_def_node->data.fn_def.body, implicit_return_type); |
| 701 | 761 |
src/parser.cpp+44-7| ... | @@ -33,6 +33,7 @@ static const char *bin_op_str(BinOpType bin_op) { | ... | @@ -33,6 +33,7 @@ static const char *bin_op_str(BinOpType bin_op) { |
| 33 | case BinOpTypeMult: return "*"; | 33 | case BinOpTypeMult: return "*"; |
| 34 | case BinOpTypeDiv: return "/"; | 34 | case BinOpTypeDiv: return "/"; |
| 35 | case BinOpTypeMod: return "%"; | 35 | case BinOpTypeMod: return "%"; |
| 36 | case BinOpTypeAssign: return "="; | ||
| 36 | } | 37 | } |
| 37 | zig_unreachable(); | 38 | zig_unreachable(); |
| 38 | } | 39 | } |
| ... | @@ -1096,7 +1097,7 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m | ... | @@ -1096,7 +1097,7 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m |
| 1096 | } | 1097 | } |
| 1097 | 1098 | ||
| 1098 | /* | 1099 | /* |
| 1099 | VariableDeclaration : token(Let) token(Symbole) (token(Eq) Expression | token(Colon) Type option(token(Eq) Expression)) | 1100 | VariableDeclaration : token(Let) option(token(Mut)) token(Symbol) (token(Eq) Expression | token(Colon) Type option(token(Eq) Expression)) |
| 1100 | */ | 1101 | */ |
| 1101 | static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token_index, bool mandatory) { | 1102 | static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| 1102 | Token *let_tok = &pc->tokens->at(*token_index); | 1103 | Token *let_tok = &pc->tokens->at(*token_index); |
| ... | @@ -1104,9 +1105,21 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token | ... | @@ -1104,9 +1105,21 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token |
| 1104 | *token_index += 1; | 1105 | *token_index += 1; |
| 1105 | AstNode *node = ast_create_node(pc, NodeTypeVariableDeclaration, let_tok); | 1106 | AstNode *node = ast_create_node(pc, NodeTypeVariableDeclaration, let_tok); |
| 1106 | 1107 | ||
| 1107 | Token *name_token = &pc->tokens->at(*token_index); | 1108 | Token *name_token; |
| 1109 | Token *token = &pc->tokens->at(*token_index); | ||
| 1110 | if (token->id == TokenIdKeywordMut) { | ||
| 1111 | node->data.variable_declaration.is_const = false; | ||
| 1112 | *token_index += 1; | ||
| 1113 | name_token = &pc->tokens->at(*token_index); | ||
| 1114 | ast_expect_token(pc, name_token, TokenIdSymbol); | ||
| 1115 | } else if (token->id == TokenIdSymbol) { | ||
| 1116 | node->data.variable_declaration.is_const = true; | ||
| 1117 | name_token = token; | ||
| 1118 | } else { | ||
| 1119 | ast_invalid_token_error(pc, token); | ||
| 1120 | } | ||
| 1121 | |||
| 1108 | *token_index += 1; | 1122 | *token_index += 1; |
| 1109 | ast_expect_token(pc, name_token, TokenIdSymbol); | ||
| 1110 | ast_buf_from_token(pc, name_token, &node->data.variable_declaration.symbol); | 1123 | ast_buf_from_token(pc, name_token, &node->data.variable_declaration.symbol); |
| 1111 | 1124 | ||
| 1112 | Token *eq_or_colon = &pc->tokens->at(*token_index); | 1125 | Token *eq_or_colon = &pc->tokens->at(*token_index); |
| ... | @@ -1178,7 +1191,30 @@ static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool ma | ... | @@ -1178,7 +1191,30 @@ static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool ma |
| 1178 | } | 1191 | } |
| 1179 | 1192 | ||
| 1180 | /* | 1193 | /* |
| 1181 | NonBlockExpression : ReturnExpression | VariableDeclaration | BoolOrExpression | 1194 | AssignmentExpression : BoolOrExpression token(Equal) BoolOrExpression | BoolOrExpression |
| 1195 | */ | ||
| 1196 | static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mandatory) { | ||
| 1197 | AstNode *lhs = ast_parse_bool_or_expr(pc, token_index, mandatory); | ||
| 1198 | if (!lhs) | ||
| 1199 | return lhs; | ||
| 1200 | |||
| 1201 | Token *token = &pc->tokens->at(*token_index); | ||
| 1202 | if (token->id != TokenIdEq) | ||
| 1203 | return lhs; | ||
| 1204 | *token_index += 1; | ||
| 1205 | |||
| 1206 | AstNode *rhs = ast_parse_bool_or_expr(pc, token_index, true); | ||
| 1207 | |||
| 1208 | AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); | ||
| 1209 | node->data.bin_op_expr.op1 = lhs; | ||
| 1210 | node->data.bin_op_expr.bin_op = BinOpTypeAssign; | ||
| 1211 | node->data.bin_op_expr.op2 = rhs; | ||
| 1212 | |||
| 1213 | return node; | ||
| 1214 | } | ||
| 1215 | |||
| 1216 | /* | ||
| 1217 | NonBlockExpression : ReturnExpression | VariableDeclaration | AssignmentExpression | ||
| 1182 | */ | 1218 | */ |
| 1183 | static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, bool mandatory) { | 1219 | static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| 1184 | Token *token = &pc->tokens->at(*token_index); | 1220 | Token *token = &pc->tokens->at(*token_index); |
| ... | @@ -1191,9 +1227,10 @@ static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, boo | ... | @@ -1191,9 +1227,10 @@ static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, boo |
| 1191 | if (variable_declaration_expr) | 1227 | if (variable_declaration_expr) |
| 1192 | return variable_declaration_expr; | 1228 | return variable_declaration_expr; |
| 1193 | 1229 | ||
| 1194 | AstNode *bool_or_expr = ast_parse_bool_or_expr(pc, token_index, false); | 1230 | |
| 1195 | if (bool_or_expr) | 1231 | AstNode *ass_expr = ast_parse_ass_expr(pc, token_index, false); |
| 1196 | return bool_or_expr; | 1232 | if (ass_expr) |
| 1233 | return ass_expr; | ||
| 1197 | 1234 | ||
| 1198 | if (mandatory) | 1235 | if (mandatory) |
| 1199 | ast_invalid_token_error(pc, token); | 1236 | ast_invalid_token_error(pc, token); |
src/parser.hpp+2-1| ... | @@ -101,6 +101,7 @@ struct AstNodeReturnExpr { | ... | @@ -101,6 +101,7 @@ struct AstNodeReturnExpr { |
| 101 | 101 | ||
| 102 | struct AstNodeVariableDeclaration { | 102 | struct AstNodeVariableDeclaration { |
| 103 | Buf symbol; | 103 | Buf symbol; |
| 104 | bool is_const; | ||
| 104 | // one or both of type and expr will be non null | 105 | // one or both of type and expr will be non null |
| 105 | AstNode *type; | 106 | AstNode *type; |
| 106 | AstNode *expr; | 107 | AstNode *expr; |
| ... | @@ -108,7 +109,7 @@ struct AstNodeVariableDeclaration { | ... | @@ -108,7 +109,7 @@ struct AstNodeVariableDeclaration { |
| 108 | 109 | ||
| 109 | enum BinOpType { | 110 | enum BinOpType { |
| 110 | BinOpTypeInvalid, | 111 | BinOpTypeInvalid, |
| 111 | // TODO: include assignment? | 112 | BinOpTypeAssign, |
| 112 | BinOpTypeBoolOr, | 113 | BinOpTypeBoolOr, |
| 113 | BinOpTypeBoolAnd, | 114 | BinOpTypeBoolAnd, |
| 114 | BinOpTypeCmpEq, | 115 | BinOpTypeCmpEq, |
src/semantic_info.hpp+9| ... | @@ -42,6 +42,7 @@ struct LabelTableEntry { | ... | @@ -42,6 +42,7 @@ struct LabelTableEntry { |
| 42 | AstNode *label_node; | 42 | AstNode *label_node; |
| 43 | LLVMBasicBlockRef basic_block; | 43 | LLVMBasicBlockRef basic_block; |
| 44 | bool used; | 44 | bool used; |
| 45 | bool entered_from_fallthrough; | ||
| 45 | }; | 46 | }; |
| 46 | 47 | ||
| 47 | struct FnTableEntry { | 48 | struct FnTableEntry { |
| ... | @@ -116,6 +117,8 @@ struct LocalVariableTableEntry { | ... | @@ -116,6 +117,8 @@ struct LocalVariableTableEntry { |
| 116 | Buf name; | 117 | Buf name; |
| 117 | TypeTableEntry *type; | 118 | TypeTableEntry *type; |
| 118 | LLVMValueRef value_ref; | 119 | LLVMValueRef value_ref; |
| 120 | bool is_const; | ||
| 121 | AstNode *decl_node; | ||
| 119 | }; | 122 | }; |
| 120 | 123 | ||
| 121 | struct BlockContext { | 124 | struct BlockContext { |
| ... | @@ -137,6 +140,7 @@ struct FnDefNode { | ... | @@ -137,6 +140,7 @@ struct FnDefNode { |
| 137 | TypeTableEntry *implicit_return_type; | 140 | TypeTableEntry *implicit_return_type; |
| 138 | BlockContext *block_context; | 141 | BlockContext *block_context; |
| 139 | bool skip; | 142 | bool skip; |
| 143 | ZigList<BlockContext *> all_block_contexts; | ||
| 140 | }; | 144 | }; |
| 141 | 145 | ||
| 142 | struct ExprNode { | 146 | struct ExprNode { |
| ... | @@ -146,12 +150,17 @@ struct ExprNode { | ... | @@ -146,12 +150,17 @@ struct ExprNode { |
| 146 | BlockContext *block_context; | 150 | BlockContext *block_context; |
| 147 | }; | 151 | }; |
| 148 | 152 | ||
| 153 | struct AssignNode { | ||
| 154 | LocalVariableTableEntry *var_entry; | ||
| 155 | }; | ||
| 156 | |||
| 149 | struct CodeGenNode { | 157 | struct CodeGenNode { |
| 150 | union { | 158 | union { |
| 151 | TypeNode type_node; // for NodeTypeType | 159 | TypeNode type_node; // for NodeTypeType |
| 152 | FnDefNode fn_def_node; // for NodeTypeFnDef | 160 | FnDefNode fn_def_node; // for NodeTypeFnDef |
| 153 | FnProtoNode fn_proto_node; // for NodeTypeFnProto | 161 | FnProtoNode fn_proto_node; // for NodeTypeFnProto |
| 154 | LabelTableEntry *label_entry; // for NodeTypeGoto and NodeTypeLabel | 162 | LabelTableEntry *label_entry; // for NodeTypeGoto and NodeTypeLabel |
| 163 | AssignNode assign_node; // for NodeTypeBinOpExpr where op is BinOpTypeAssign | ||
| 155 | } data; | 164 | } data; |
| 156 | ExprNode expr_node; // for all the expression nodes | 165 | ExprNode expr_node; // for all the expression nodes |
| 157 | }; | 166 | }; |
test/run_tests.cpp+41-1| ... | @@ -331,6 +331,27 @@ fn void_fun(a : i32, b : void, c : i32) { | ... | @@ -331,6 +331,27 @@ fn void_fun(a : i32, b : void, c : i32) { |
| 331 | return vv; | 331 | return vv; |
| 332 | } | 332 | } |
| 333 | )SOURCE", "OK\n"); | 333 | )SOURCE", "OK\n"); |
| 334 | |||
| 335 | add_simple_case("void parameters", R"SOURCE( | ||
| 336 | #link("c") | ||
| 337 | extern { | ||
| 338 | fn puts(s: *const u8) -> i32; | ||
| 339 | fn exit(code: i32) -> unreachable; | ||
| 340 | } | ||
| 341 | |||
| 342 | export fn _start() -> unreachable { | ||
| 343 | let mut i = 0; | ||
| 344 | loop_start: | ||
| 345 | if i == 3 { | ||
| 346 | goto done; | ||
| 347 | } | ||
| 348 | puts("loop"); | ||
| 349 | i = i + 1; | ||
| 350 | goto loop_start; | ||
| 351 | done: | ||
| 352 | exit(0); | ||
| 353 | } | ||
| 354 | )SOURCE", "loop\nloop\nloop\n"); | ||
| 334 | } | 355 | } |
| 335 | 356 | ||
| 336 | static void add_compile_failure_test_cases(void) { | 357 | static void add_compile_failure_test_cases(void) { |
| ... | @@ -467,10 +488,29 @@ export fn f(a : void) {} | ... | @@ -467,10 +488,29 @@ export fn f(a : void) {} |
| 467 | )SOURCE", 1, ".tmp_source.zig:2:17: error: parameter of type 'void' not allowed on exported functions"); | 488 | )SOURCE", 1, ".tmp_source.zig:2:17: error: parameter of type 'void' not allowed on exported functions"); |
| 468 | 489 | ||
| 469 | add_compile_fail_case("unused label", R"SOURCE( | 490 | add_compile_fail_case("unused label", R"SOURCE( |
| 470 | export fn f() { | 491 | fn f() { |
| 471 | a_label: | 492 | a_label: |
| 472 | } | 493 | } |
| 473 | )SOURCE", 1, ".tmp_source.zig:3:1: error: label 'a_label' defined but not used"); | 494 | )SOURCE", 1, ".tmp_source.zig:3:1: error: label 'a_label' defined but not used"); |
| 495 | |||
| 496 | add_compile_fail_case("expected bare identifier", R"SOURCE( | ||
| 497 | fn f() { | ||
| 498 | 3 = 3; | ||
| 499 | } | ||
| 500 | )SOURCE", 1, ".tmp_source.zig:3:5: error: expected a bare identifier"); | ||
| 501 | |||
| 502 | add_compile_fail_case("assign to constant variable", R"SOURCE( | ||
| 503 | fn f() { | ||
| 504 | let a = 3; | ||
| 505 | a = 4; | ||
| 506 | } | ||
| 507 | )SOURCE", 1, ".tmp_source.zig:4:5: error: cannot assign to constant variable"); | ||
| 508 | |||
| 509 | add_compile_fail_case("use of undeclared identifier", R"SOURCE( | ||
| 510 | fn f() { | ||
| 511 | b = 3; | ||
| 512 | } | ||
| 513 | )SOURCE", 1, ".tmp_source.zig:3:5: error: use of undeclared identifier 'b'"); | ||
| 474 | } | 514 | } |
| 475 | 515 | ||
| 476 | static void print_compiler_invocation(TestCase *test_case, Buf *zig_stderr) { | 516 | static void print_compiler_invocation(TestCase *test_case, Buf *zig_stderr) { |