| author | |
| committer | |
| log | 1f8e3871ee11c71b7965e8b9e97fa37a8e994d68 |
| tree | 130c237a692aad2f42217e221426a64830770ec2 |
| parent | 5943f995106f9fc2c7c5b416c8d4a2ebba2b8315 |
9 files changed, 176 insertions(+), 72 deletions(-)
README.md+3-2| ... | @@ -44,10 +44,11 @@ compromises backward compatibility. | ... | @@ -44,10 +44,11 @@ compromises backward compatibility. |
| 44 | 44 | ||
| 45 | ### Current Status | 45 | ### Current Status |
| 46 | 46 | ||
| 47 | * Core language features are lacking such as structs, enums, loops. | 47 | * Have a look in the examples/ folder to see some code examples. |
| 48 | * Some language features available such as loops, inline assembly, expressions, | ||
| 49 | literals, functions. | ||
| 48 | * Only Linux x86_64 is supported. | 50 | * Only Linux x86_64 is supported. |
| 49 | * Only building for the native target is supported. | 51 | * Only building for the native target is supported. |
| 50 | * Have a look in the examples/ folder to see some code examples. | ||
| 51 | * Optimized machine code that Zig produces is indistinguishable from | 52 | * Optimized machine code that Zig produces is indistinguishable from |
| 52 | optimized machine code produced from equivalent C program. | 53 | optimized machine code produced from equivalent C program. |
| 53 | * Zig can generate dynamic libraries, executables, object files, and C | 54 | * Zig can generate dynamic libraries, executables, object files, and C |
doc/langref.md+5-1| ... | @@ -102,7 +102,11 @@ BoolOrExpression : BoolAndExpression token(BoolOr) BoolOrExpression | BoolAndExp | ... | @@ -102,7 +102,11 @@ BoolOrExpression : BoolAndExpression token(BoolOr) BoolOrExpression | BoolAndExp |
| 102 | 102 | ||
| 103 | ReturnExpression : token(Return) option(Expression) | 103 | ReturnExpression : token(Return) option(Expression) |
| 104 | 104 | ||
| 105 | IfExpression : token(If) Expression Block option(Else | ElseIf) | 105 | IfExpression : IfVarExpression | IfBoolExpression |
| 106 | |||
| 107 | IfBoolExpression : token(If) option((token) Expression Block option(Else | ElseIf) | ||
| 108 | |||
| 109 | IfVarExpression : token(If) (token(Const) | token(Var)) token(Symbol) option(token(Colon) Type) Token(MaybeAssign) Expression Block Option(Else | ElseIf) | ||
| 106 | 110 | ||
| 107 | ElseIf : token(Else) IfExpression | 111 | ElseIf : token(Else) IfExpression |
| 108 | 112 |
example/guess_number/main.zig+7-8| ... | @@ -12,17 +12,16 @@ fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { | ... | @@ -12,17 +12,16 @@ fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { |
| 12 | const answer = rand_int(&rand_state, 0, 100) + 1; | 12 | const answer = rand_int(&rand_state, 0, 100) + 1; |
| 13 | 13 | ||
| 14 | while true { | 14 | while true { |
| 15 | Buffer line = readline("\nGuess a number between 1 and 100: "); | 15 | line = readline("\nGuess a number between 1 and 100: "); |
| 16 | 16 | ||
| 17 | (const err, const guess) = parse_number(line); | 17 | if const guess ?= parse_number(line) { |
| 18 | if err == Error.None { | 18 | if (guess > answer) { |
| 19 | if guess == answer { | 19 | print_str("Guess lower.\n"); |
| 20 | print_str("You win!\n"); | 20 | } else if (guess < answer) { |
| 21 | return 0; | ||
| 22 | } else if guess < answer { | ||
| 23 | print_str("Guess higher.\n"); | 21 | print_str("Guess higher.\n"); |
| 24 | } else { | 22 | } else { |
| 25 | print_str("Guess lower.\n"); | 23 | print_str("You win!\n"); |
| 24 | return 0; | ||
| 26 | } | 25 | } |
| 27 | } else { | 26 | } else { |
| 28 | print_str("Invalid number format.\n"); | 27 | print_str("Invalid number format.\n"); |
src/analyze.cpp+47-29| ... | @@ -45,7 +45,8 @@ static AstNode *first_executing_node(AstNode *node) { | ... | @@ -45,7 +45,8 @@ static AstNode *first_executing_node(AstNode *node) { |
| 45 | case NodeTypeUse: | 45 | case NodeTypeUse: |
| 46 | case NodeTypeVoid: | 46 | case NodeTypeVoid: |
| 47 | case NodeTypeBoolLiteral: | 47 | case NodeTypeBoolLiteral: |
| 48 | case NodeTypeIfExpr: | 48 | case NodeTypeIfBoolExpr: |
| 49 | case NodeTypeIfVarExpr: | ||
| 49 | case NodeTypeLabel: | 50 | case NodeTypeLabel: |
| 50 | case NodeTypeGoto: | 51 | case NodeTypeGoto: |
| 51 | case NodeTypeBreak: | 52 | case NodeTypeBreak: |
| ... | @@ -528,7 +529,8 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, | ... | @@ -528,7 +529,8 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 528 | case NodeTypeSymbol: | 529 | case NodeTypeSymbol: |
| 529 | case NodeTypeCastExpr: | 530 | case NodeTypeCastExpr: |
| 530 | case NodeTypePrefixOpExpr: | 531 | case NodeTypePrefixOpExpr: |
| 531 | case NodeTypeIfExpr: | 532 | case NodeTypeIfBoolExpr: |
| 533 | case NodeTypeIfVarExpr: | ||
| 532 | case NodeTypeWhileExpr: | 534 | case NodeTypeWhileExpr: |
| 533 | case NodeTypeLabel: | 535 | case NodeTypeLabel: |
| 534 | case NodeTypeGoto: | 536 | case NodeTypeGoto: |
| ... | @@ -598,7 +600,8 @@ static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) { | ... | @@ -598,7 +600,8 @@ static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) { |
| 598 | case NodeTypeSymbol: | 600 | case NodeTypeSymbol: |
| 599 | case NodeTypeCastExpr: | 601 | case NodeTypeCastExpr: |
| 600 | case NodeTypePrefixOpExpr: | 602 | case NodeTypePrefixOpExpr: |
| 601 | case NodeTypeIfExpr: | 603 | case NodeTypeIfBoolExpr: |
| 604 | case NodeTypeIfVarExpr: | ||
| 602 | case NodeTypeWhileExpr: | 605 | case NodeTypeWhileExpr: |
| 603 | case NodeTypeLabel: | 606 | case NodeTypeLabel: |
| 604 | case NodeTypeGoto: | 607 | case NodeTypeGoto: |
| ... | @@ -1394,6 +1397,39 @@ static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *impor | ... | @@ -1394,6 +1397,39 @@ static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *impor |
| 1394 | return g->builtin_types.entry_unreachable; | 1397 | return g->builtin_types.entry_unreachable; |
| 1395 | } | 1398 | } |
| 1396 | 1399 | ||
| 1400 | static TypeTableEntry *analyze_if_bool_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | ||
| 1401 | TypeTableEntry *expected_type, AstNode *node) | ||
| 1402 | { | ||
| 1403 | analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.if_bool_expr.condition); | ||
| 1404 | |||
| 1405 | TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type, | ||
| 1406 | node->data.if_bool_expr.then_block); | ||
| 1407 | |||
| 1408 | TypeTableEntry *else_type; | ||
| 1409 | if (node->data.if_bool_expr.else_node) { | ||
| 1410 | else_type = analyze_expression(g, import, context, expected_type, node->data.if_bool_expr.else_node); | ||
| 1411 | } else { | ||
| 1412 | else_type = g->builtin_types.entry_void; | ||
| 1413 | else_type = resolve_type_compatibility(g, context, node, expected_type, else_type); | ||
| 1414 | } | ||
| 1415 | |||
| 1416 | |||
| 1417 | if (expected_type) { | ||
| 1418 | return (then_type->id == TypeTableEntryIdUnreachable) ? else_type : then_type; | ||
| 1419 | } else { | ||
| 1420 | return resolve_peer_type_compatibility(g, context, node, | ||
| 1421 | node->data.if_bool_expr.then_block, node->data.if_bool_expr.else_node, | ||
| 1422 | then_type, else_type); | ||
| 1423 | } | ||
| 1424 | } | ||
| 1425 | |||
| 1426 | static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | ||
| 1427 | TypeTableEntry *expected_type, AstNode *node) | ||
| 1428 | { | ||
| 1429 | assert(node->type == NodeTypeIfVarExpr); | ||
| 1430 | zig_panic("TODO analyze_if_var_expr"); | ||
| 1431 | } | ||
| 1432 | |||
| 1397 | static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, | 1433 | static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 1398 | TypeTableEntry *expected_type, AstNode *node) | 1434 | TypeTableEntry *expected_type, AstNode *node) |
| 1399 | { | 1435 | { |
| ... | @@ -1651,31 +1687,12 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, | ... | @@ -1651,31 +1687,12 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 1651 | } | 1687 | } |
| 1652 | } | 1688 | } |
| 1653 | break; | 1689 | break; |
| 1654 | case NodeTypeIfExpr: | 1690 | case NodeTypeIfBoolExpr: |
| 1655 | { | 1691 | return_type = analyze_if_bool_expr(g, import, context, expected_type, node); |
| 1656 | analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.if_expr.condition); | 1692 | break; |
| 1657 | 1693 | case NodeTypeIfVarExpr: | |
| 1658 | TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type, | 1694 | return_type = analyze_if_var_expr(g, import, context, expected_type, node); |
| 1659 | node->data.if_expr.then_block); | 1695 | break; |
| 1660 | |||
| 1661 | TypeTableEntry *else_type; | ||
| 1662 | if (node->data.if_expr.else_node) { | ||
| 1663 | else_type = analyze_expression(g, import, context, expected_type, node->data.if_expr.else_node); | ||
| 1664 | } else { | ||
| 1665 | else_type = g->builtin_types.entry_void; | ||
| 1666 | else_type = resolve_type_compatibility(g, context, node, expected_type, else_type); | ||
| 1667 | } | ||
| 1668 | |||
| 1669 | |||
| 1670 | if (expected_type) { | ||
| 1671 | return_type = (then_type->id == TypeTableEntryIdUnreachable) ? else_type : then_type; | ||
| 1672 | } else { | ||
| 1673 | return_type = resolve_peer_type_compatibility(g, context, node, | ||
| 1674 | node->data.if_expr.then_block, node->data.if_expr.else_node, | ||
| 1675 | then_type, else_type); | ||
| 1676 | } | ||
| 1677 | break; | ||
| 1678 | } | ||
| 1679 | case NodeTypeWhileExpr: | 1696 | case NodeTypeWhileExpr: |
| 1680 | return_type = analyze_while_expr(g, import, context, expected_type, node); | 1697 | return_type = analyze_while_expr(g, import, context, expected_type, node); |
| 1681 | break; | 1698 | break; |
| ... | @@ -1828,7 +1845,8 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, | ... | @@ -1828,7 +1845,8 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, |
| 1828 | case NodeTypeSymbol: | 1845 | case NodeTypeSymbol: |
| 1829 | case NodeTypeCastExpr: | 1846 | case NodeTypeCastExpr: |
| 1830 | case NodeTypePrefixOpExpr: | 1847 | case NodeTypePrefixOpExpr: |
| 1831 | case NodeTypeIfExpr: | 1848 | case NodeTypeIfBoolExpr: |
| 1849 | case NodeTypeIfVarExpr: | ||
| 1832 | case NodeTypeWhileExpr: | 1850 | case NodeTypeWhileExpr: |
| 1833 | case NodeTypeLabel: | 1851 | case NodeTypeLabel: |
| 1834 | case NodeTypeGoto: | 1852 | case NodeTypeGoto: |
src/codegen.cpp+22-15| ... | @@ -769,18 +769,18 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) { | ... | @@ -769,18 +769,18 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) { |
| 769 | } | 769 | } |
| 770 | } | 770 | } |
| 771 | 771 | ||
| 772 | static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) { | 772 | static LLVMValueRef gen_if_bool_expr(CodeGen *g, AstNode *node) { |
| 773 | assert(node->type == NodeTypeIfExpr); | 773 | assert(node->type == NodeTypeIfBoolExpr); |
| 774 | assert(node->data.if_expr.condition); | 774 | assert(node->data.if_bool_expr.condition); |
| 775 | assert(node->data.if_expr.then_block); | 775 | assert(node->data.if_bool_expr.then_block); |
| 776 | 776 | ||
| 777 | LLVMValueRef cond_value = gen_expr(g, node->data.if_expr.condition); | 777 | LLVMValueRef cond_value = gen_expr(g, node->data.if_bool_expr.condition); |
| 778 | 778 | ||
| 779 | TypeTableEntry *then_type = get_expr_type(node->data.if_expr.then_block); | 779 | TypeTableEntry *then_type = get_expr_type(node->data.if_bool_expr.then_block); |
| 780 | bool use_expr_value = (then_type->id != TypeTableEntryIdUnreachable && | 780 | bool use_expr_value = (then_type->id != TypeTableEntryIdUnreachable && |
| 781 | then_type->id != TypeTableEntryIdVoid); | 781 | then_type->id != TypeTableEntryIdVoid); |
| 782 | 782 | ||
| 783 | if (node->data.if_expr.else_node) { | 783 | if (node->data.if_bool_expr.else_node) { |
| 784 | LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then"); | 784 | LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then"); |
| 785 | LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Else"); | 785 | LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Else"); |
| 786 | LLVMBasicBlockRef endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf"); | 786 | LLVMBasicBlockRef endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf"); |
| ... | @@ -788,13 +788,13 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) { | ... | @@ -788,13 +788,13 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) { |
| 788 | LLVMBuildCondBr(g->builder, cond_value, then_block, else_block); | 788 | LLVMBuildCondBr(g->builder, cond_value, then_block, else_block); |
| 789 | 789 | ||
| 790 | LLVMPositionBuilderAtEnd(g->builder, then_block); | 790 | LLVMPositionBuilderAtEnd(g->builder, then_block); |
| 791 | LLVMValueRef then_expr_result = gen_expr(g, node->data.if_expr.then_block); | 791 | LLVMValueRef then_expr_result = gen_expr(g, node->data.if_bool_expr.then_block); |
| 792 | if (get_expr_type(node->data.if_expr.then_block)->id != TypeTableEntryIdUnreachable) | 792 | if (get_expr_type(node->data.if_bool_expr.then_block)->id != TypeTableEntryIdUnreachable) |
| 793 | LLVMBuildBr(g->builder, endif_block); | 793 | LLVMBuildBr(g->builder, endif_block); |
| 794 | 794 | ||
| 795 | LLVMPositionBuilderAtEnd(g->builder, else_block); | 795 | LLVMPositionBuilderAtEnd(g->builder, else_block); |
| 796 | LLVMValueRef else_expr_result = gen_expr(g, node->data.if_expr.else_node); | 796 | LLVMValueRef else_expr_result = gen_expr(g, node->data.if_bool_expr.else_node); |
| 797 | if (get_expr_type(node->data.if_expr.else_node)->id != TypeTableEntryIdUnreachable) | 797 | if (get_expr_type(node->data.if_bool_expr.else_node)->id != TypeTableEntryIdUnreachable) |
| 798 | LLVMBuildBr(g->builder, endif_block); | 798 | LLVMBuildBr(g->builder, endif_block); |
| 799 | 799 | ||
| 800 | LLVMPositionBuilderAtEnd(g->builder, endif_block); | 800 | LLVMPositionBuilderAtEnd(g->builder, endif_block); |
| ... | @@ -818,14 +818,19 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) { | ... | @@ -818,14 +818,19 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) { |
| 818 | LLVMBuildCondBr(g->builder, cond_value, then_block, endif_block); | 818 | LLVMBuildCondBr(g->builder, cond_value, then_block, endif_block); |
| 819 | 819 | ||
| 820 | LLVMPositionBuilderAtEnd(g->builder, then_block); | 820 | LLVMPositionBuilderAtEnd(g->builder, then_block); |
| 821 | gen_expr(g, node->data.if_expr.then_block); | 821 | gen_expr(g, node->data.if_bool_expr.then_block); |
| 822 | if (get_expr_type(node->data.if_expr.then_block)->id != TypeTableEntryIdUnreachable) | 822 | if (get_expr_type(node->data.if_bool_expr.then_block)->id != TypeTableEntryIdUnreachable) |
| 823 | LLVMBuildBr(g->builder, endif_block); | 823 | LLVMBuildBr(g->builder, endif_block); |
| 824 | 824 | ||
| 825 | LLVMPositionBuilderAtEnd(g->builder, endif_block); | 825 | LLVMPositionBuilderAtEnd(g->builder, endif_block); |
| 826 | return nullptr; | 826 | return nullptr; |
| 827 | } | 827 | } |
| 828 | 828 | ||
| 829 | static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) { | ||
| 830 | assert(node->type == NodeTypeIfVarExpr); | ||
| 831 | zig_panic("TODO gen_if_var_expr"); | ||
| 832 | } | ||
| 833 | |||
| 829 | static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) { | 834 | static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) { |
| 830 | assert(block_node->type == NodeTypeBlock); | 835 | assert(block_node->type == NodeTypeBlock); |
| 831 | 836 | ||
| ... | @@ -1112,8 +1117,10 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { | ... | @@ -1112,8 +1117,10 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { |
| 1112 | return LLVMConstAllOnes(LLVMInt1Type()); | 1117 | return LLVMConstAllOnes(LLVMInt1Type()); |
| 1113 | else | 1118 | else |
| 1114 | return LLVMConstNull(LLVMInt1Type()); | 1119 | return LLVMConstNull(LLVMInt1Type()); |
| 1115 | case NodeTypeIfExpr: | 1120 | case NodeTypeIfBoolExpr: |
| 1116 | return gen_if_expr(g, node); | 1121 | return gen_if_bool_expr(g, node); |
| 1122 | case NodeTypeIfVarExpr: | ||
| 1123 | return gen_if_var_expr(g, node); | ||
| 1117 | case NodeTypeWhileExpr: | 1124 | case NodeTypeWhileExpr: |
| 1118 | return gen_while_expr(g, node); | 1125 | return gen_while_expr(g, node); |
| 1119 | case NodeTypeAsmExpr: | 1126 | case NodeTypeAsmExpr: |
src/parser.cpp+57-14| ... | @@ -114,8 +114,10 @@ const char *node_type_str(NodeType node_type) { | ... | @@ -114,8 +114,10 @@ const char *node_type_str(NodeType node_type) { |
| 114 | return "Void"; | 114 | return "Void"; |
| 115 | case NodeTypeBoolLiteral: | 115 | case NodeTypeBoolLiteral: |
| 116 | return "BoolLiteral"; | 116 | return "BoolLiteral"; |
| 117 | case NodeTypeIfExpr: | 117 | case NodeTypeIfBoolExpr: |
| 118 | return "IfExpr"; | 118 | return "IfBoolExpr"; |
| 119 | case NodeTypeIfVarExpr: | ||
| 120 | return "IfVarExpr"; | ||
| 119 | case NodeTypeWhileExpr: | 121 | case NodeTypeWhileExpr: |
| 120 | return "WhileExpr"; | 122 | return "WhileExpr"; |
| 121 | case NodeTypeLabel: | 123 | case NodeTypeLabel: |
| ... | @@ -321,14 +323,27 @@ void ast_print(AstNode *node, int indent) { | ... | @@ -321,14 +323,27 @@ void ast_print(AstNode *node, int indent) { |
| 321 | case NodeTypeBoolLiteral: | 323 | case NodeTypeBoolLiteral: |
| 322 | fprintf(stderr, "%s '%s'\n", node_type_str(node->type), node->data.bool_literal ? "true" : "false"); | 324 | fprintf(stderr, "%s '%s'\n", node_type_str(node->type), node->data.bool_literal ? "true" : "false"); |
| 323 | break; | 325 | break; |
| 324 | case NodeTypeIfExpr: | 326 | case NodeTypeIfBoolExpr: |
| 325 | fprintf(stderr, "%s\n", node_type_str(node->type)); | 327 | fprintf(stderr, "%s\n", node_type_str(node->type)); |
| 326 | if (node->data.if_expr.condition) | 328 | if (node->data.if_bool_expr.condition) |
| 327 | ast_print(node->data.if_expr.condition, indent + 2); | 329 | ast_print(node->data.if_bool_expr.condition, indent + 2); |
| 328 | ast_print(node->data.if_expr.then_block, indent + 2); | 330 | ast_print(node->data.if_bool_expr.then_block, indent + 2); |
| 329 | if (node->data.if_expr.else_node) | 331 | if (node->data.if_bool_expr.else_node) |
| 330 | ast_print(node->data.if_expr.else_node, indent + 2); | 332 | ast_print(node->data.if_bool_expr.else_node, indent + 2); |
| 331 | break; | 333 | break; |
| 334 | case NodeTypeIfVarExpr: | ||
| 335 | { | ||
| 336 | Buf *name_buf = &node->data.if_var_expr.var_decl.symbol; | ||
| 337 | fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(name_buf)); | ||
| 338 | if (node->data.if_var_expr.var_decl.type) | ||
| 339 | ast_print(node->data.if_var_expr.var_decl.type, indent + 2); | ||
| 340 | if (node->data.if_var_expr.var_decl.expr) | ||
| 341 | ast_print(node->data.if_var_expr.var_decl.expr, indent + 2); | ||
| 342 | ast_print(node->data.if_var_expr.then_block, indent + 2); | ||
| 343 | if (node->data.if_var_expr.else_node) | ||
| 344 | ast_print(node->data.if_var_expr.else_node, indent + 2); | ||
| 345 | break; | ||
| 346 | } | ||
| 332 | case NodeTypeWhileExpr: | 347 | case NodeTypeWhileExpr: |
| 333 | fprintf(stderr, "%s\n", node_type_str(node->type)); | 348 | fprintf(stderr, "%s\n", node_type_str(node->type)); |
| 334 | ast_print(node->data.while_expr.condition, indent + 2); | 349 | ast_print(node->data.while_expr.condition, indent + 2); |
| ... | @@ -1644,7 +1659,9 @@ static AstNode *ast_parse_else_or_else_if(ParseContext *pc, int *token_index, bo | ... | @@ -1644,7 +1659,9 @@ static AstNode *ast_parse_else_or_else_if(ParseContext *pc, int *token_index, bo |
| 1644 | } | 1659 | } |
| 1645 | 1660 | ||
| 1646 | /* | 1661 | /* |
| 1647 | IfExpression : token(If) Expression Block option(Else | ElseIf) | 1662 | IfExpression : IfVarExpression | IfBoolExpression |
| 1663 | IfBoolExpression : token(If) option((token) Expression Block option(Else | ElseIf) | ||
| 1664 | IfVarExpression : token(If) (token(Const) | token(Var)) token(Symbol) option(token(Colon) Type) Token(Eq) Expression Block Option(Else | ElseIf) | ||
| 1648 | */ | 1665 | */ |
| 1649 | static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory) { | 1666 | static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| 1650 | Token *if_tok = &pc->tokens->at(*token_index); | 1667 | Token *if_tok = &pc->tokens->at(*token_index); |
| ... | @@ -1657,11 +1674,37 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool manda | ... | @@ -1657,11 +1674,37 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool manda |
| 1657 | } | 1674 | } |
| 1658 | *token_index += 1; | 1675 | *token_index += 1; |
| 1659 | 1676 | ||
| 1660 | AstNode *node = ast_create_node(pc, NodeTypeIfExpr, if_tok); | 1677 | Token *token = &pc->tokens->at(*token_index); |
| 1661 | node->data.if_expr.condition = ast_parse_expression(pc, token_index, true); | 1678 | if (token->id == TokenIdKeywordConst || token->id == TokenIdKeywordVar) { |
| 1662 | node->data.if_expr.then_block = ast_parse_block(pc, token_index, true); | 1679 | AstNode *node = ast_create_node(pc, NodeTypeIfVarExpr, if_tok); |
| 1663 | node->data.if_expr.else_node = ast_parse_else_or_else_if(pc, token_index, false); | 1680 | node->data.if_var_expr.var_decl.is_const = (token->id == TokenIdKeywordConst); |
| 1664 | return node; | 1681 | *token_index += 1; |
| 1682 | |||
| 1683 | Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol); | ||
| 1684 | ast_buf_from_token(pc, name_token, &node->data.if_var_expr.var_decl.symbol); | ||
| 1685 | |||
| 1686 | Token *eq_or_colon = &pc->tokens->at(*token_index); | ||
| 1687 | *token_index += 1; | ||
| 1688 | if (eq_or_colon->id == TokenIdMaybeAssign) { | ||
| 1689 | node->data.if_var_expr.var_decl.expr = ast_parse_expression(pc, token_index, true); | ||
| 1690 | } else if (eq_or_colon->id == TokenIdColon) { | ||
| 1691 | node->data.if_var_expr.var_decl.type = ast_parse_type(pc, token_index); | ||
| 1692 | |||
| 1693 | ast_eat_token(pc, token_index, TokenIdMaybeAssign); | ||
| 1694 | node->data.if_var_expr.var_decl.expr = ast_parse_expression(pc, token_index, true); | ||
| 1695 | } else { | ||
| 1696 | ast_invalid_token_error(pc, eq_or_colon); | ||
| 1697 | } | ||
| 1698 | node->data.if_var_expr.then_block = ast_parse_block(pc, token_index, true); | ||
| 1699 | node->data.if_var_expr.else_node = ast_parse_else_or_else_if(pc, token_index, false); | ||
| 1700 | return node; | ||
| 1701 | } else { | ||
| 1702 | AstNode *node = ast_create_node(pc, NodeTypeIfBoolExpr, if_tok); | ||
| 1703 | node->data.if_bool_expr.condition = ast_parse_expression(pc, token_index, true); | ||
| 1704 | node->data.if_bool_expr.then_block = ast_parse_block(pc, token_index, true); | ||
| 1705 | node->data.if_bool_expr.else_node = ast_parse_else_or_else_if(pc, token_index, false); | ||
| 1706 | return node; | ||
| 1707 | } | ||
| 1665 | } | 1708 | } |
| 1666 | 1709 | ||
| 1667 | /* | 1710 | /* |
src/parser.hpp+11-3| ... | @@ -44,7 +44,8 @@ enum NodeType { | ... | @@ -44,7 +44,8 @@ enum NodeType { |
| 44 | NodeTypeUse, | 44 | NodeTypeUse, |
| 45 | NodeTypeVoid, | 45 | NodeTypeVoid, |
| 46 | NodeTypeBoolLiteral, | 46 | NodeTypeBoolLiteral, |
| 47 | NodeTypeIfExpr, | 47 | NodeTypeIfBoolExpr, |
| 48 | NodeTypeIfVarExpr, | ||
| 48 | NodeTypeWhileExpr, | 49 | NodeTypeWhileExpr, |
| 49 | NodeTypeLabel, | 50 | NodeTypeLabel, |
| 50 | NodeTypeGoto, | 51 | NodeTypeGoto, |
| ... | @@ -217,12 +218,18 @@ struct AstNodeUse { | ... | @@ -217,12 +218,18 @@ struct AstNodeUse { |
| 217 | ZigList<AstNode *> *directives; | 218 | ZigList<AstNode *> *directives; |
| 218 | }; | 219 | }; |
| 219 | 220 | ||
| 220 | struct AstNodeIfExpr { | 221 | struct AstNodeIfBoolExpr { |
| 221 | AstNode *condition; | 222 | AstNode *condition; |
| 222 | AstNode *then_block; | 223 | AstNode *then_block; |
| 223 | AstNode *else_node; // null, block node, or other if expr node | 224 | AstNode *else_node; // null, block node, or other if expr node |
| 224 | }; | 225 | }; |
| 225 | 226 | ||
| 227 | struct AstNodeIfVarExpr { | ||
| 228 | AstNodeVariableDeclaration var_decl; | ||
| 229 | AstNode *then_block; | ||
| 230 | AstNode *else_node; // null, block node, or other if expr node | ||
| 231 | }; | ||
| 232 | |||
| 226 | struct AstNodeWhileExpr { | 233 | struct AstNodeWhileExpr { |
| 227 | AstNode *condition; | 234 | AstNode *condition; |
| 228 | AstNode *body; | 235 | AstNode *body; |
| ... | @@ -341,7 +348,8 @@ struct AstNode { | ... | @@ -341,7 +348,8 @@ struct AstNode { |
| 341 | AstNodeFnCallExpr fn_call_expr; | 348 | AstNodeFnCallExpr fn_call_expr; |
| 342 | AstNodeArrayAccessExpr array_access_expr; | 349 | AstNodeArrayAccessExpr array_access_expr; |
| 343 | AstNodeUse use; | 350 | AstNodeUse use; |
| 344 | AstNodeIfExpr if_expr; | 351 | AstNodeIfBoolExpr if_bool_expr; |
| 352 | AstNodeIfVarExpr if_var_expr; | ||
| 345 | AstNodeWhileExpr while_expr; | 353 | AstNodeWhileExpr while_expr; |
| 346 | AstNodeLabel label; | 354 | AstNodeLabel label; |
| 347 | AstNodeGoto go_to; | 355 | AstNodeGoto go_to; |
src/tokenizer.cpp+22| ... | @@ -125,6 +125,7 @@ enum TokenizeState { | ... | @@ -125,6 +125,7 @@ enum TokenizeState { |
| 125 | TokenizeStateSawGreaterThanGreaterThan, | 125 | TokenizeStateSawGreaterThanGreaterThan, |
| 126 | TokenizeStateSawDot, | 126 | TokenizeStateSawDot, |
| 127 | TokenizeStateSawDotDot, | 127 | TokenizeStateSawDotDot, |
| 128 | TokenizeStateSawQuestionMark, | ||
| 128 | TokenizeStateError, | 129 | TokenizeStateError, |
| 129 | }; | 130 | }; |
| 130 | 131 | ||
| ... | @@ -402,10 +403,28 @@ void tokenize(Buf *buf, Tokenization *out) { | ... | @@ -402,10 +403,28 @@ void tokenize(Buf *buf, Tokenization *out) { |
| 402 | begin_token(&t, TokenIdDot); | 403 | begin_token(&t, TokenIdDot); |
| 403 | t.state = TokenizeStateSawDot; | 404 | t.state = TokenizeStateSawDot; |
| 404 | break; | 405 | break; |
| 406 | case '?': | ||
| 407 | begin_token(&t, TokenIdMaybe); | ||
| 408 | t.state = TokenizeStateSawQuestionMark; | ||
| 409 | break; | ||
| 405 | default: | 410 | default: |
| 406 | tokenize_error(&t, "invalid character: '%c'", c); | 411 | tokenize_error(&t, "invalid character: '%c'", c); |
| 407 | } | 412 | } |
| 408 | break; | 413 | break; |
| 414 | case TokenizeStateSawQuestionMark: | ||
| 415 | switch (c) { | ||
| 416 | case '=': | ||
| 417 | t.cur_tok->id = TokenIdMaybeAssign; | ||
| 418 | end_token(&t); | ||
| 419 | t.state = TokenizeStateStart; | ||
| 420 | break; | ||
| 421 | default: | ||
| 422 | t.pos -= 1; | ||
| 423 | end_token(&t); | ||
| 424 | t.state = TokenizeStateStart; | ||
| 425 | continue; | ||
| 426 | } | ||
| 427 | break; | ||
| 409 | case TokenizeStateSawDot: | 428 | case TokenizeStateSawDot: |
| 410 | switch (c) { | 429 | switch (c) { |
| 411 | case '.': | 430 | case '.': |
| ... | @@ -917,6 +936,7 @@ void tokenize(Buf *buf, Tokenization *out) { | ... | @@ -917,6 +936,7 @@ void tokenize(Buf *buf, Tokenization *out) { |
| 917 | case TokenizeStateSawGreaterThan: | 936 | case TokenizeStateSawGreaterThan: |
| 918 | case TokenizeStateSawGreaterThanGreaterThan: | 937 | case TokenizeStateSawGreaterThanGreaterThan: |
| 919 | case TokenizeStateSawDot: | 938 | case TokenizeStateSawDot: |
| 939 | case TokenizeStateSawQuestionMark: | ||
| 920 | end_token(&t); | 940 | end_token(&t); |
| 921 | break; | 941 | break; |
| 922 | case TokenizeStateSawDotDot: | 942 | case TokenizeStateSawDotDot: |
| ... | @@ -1012,6 +1032,8 @@ static const char * token_name(Token *token) { | ... | @@ -1012,6 +1032,8 @@ static const char * token_name(Token *token) { |
| 1012 | case TokenIdPercent: return "Percent"; | 1032 | case TokenIdPercent: return "Percent"; |
| 1013 | case TokenIdDot: return "Dot"; | 1033 | case TokenIdDot: return "Dot"; |
| 1014 | case TokenIdEllipsis: return "Ellipsis"; | 1034 | case TokenIdEllipsis: return "Ellipsis"; |
| 1035 | case TokenIdMaybe: return "Maybe"; | ||
| 1036 | case TokenIdMaybeAssign: return "MaybeAssign"; | ||
| 1015 | } | 1037 | } |
| 1016 | return "(invalid token)"; | 1038 | return "(invalid token)"; |
| 1017 | } | 1039 | } |
src/tokenizer.hpp+2| ... | @@ -83,6 +83,8 @@ enum TokenId { | ... | @@ -83,6 +83,8 @@ enum TokenId { |
| 83 | TokenIdPercent, | 83 | TokenIdPercent, |
| 84 | TokenIdDot, | 84 | TokenIdDot, |
| 85 | TokenIdEllipsis, | 85 | TokenIdEllipsis, |
| 86 | TokenIdMaybe, | ||
| 87 | TokenIdMaybeAssign, | ||
| 86 | }; | 88 | }; |
| 87 | 89 | ||
| 88 | struct Token { | 90 | struct Token { |