| 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 | 44 | |
| 45 | 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 | 50 | * Only Linux x86_64 is supported. |
| 49 | 51 | * Only building for the native target is supported. |
| 50 | * Have a look in the examples/ folder to see some code examples. | |
| 51 | 52 | * Optimized machine code that Zig produces is indistinguishable from |
| 52 | 53 | optimized machine code produced from equivalent C program. |
| 53 | 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 | 102 | |
| 103 | 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 | 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 | 12 | const answer = rand_int(&rand_state, 0, 100) + 1; |
| 13 | 13 | |
| 14 | 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); | |
| 18 | if err == Error.None { | |
| 19 | if guess == answer { | |
| 20 | print_str("You win!\n"); | |
| 21 | return 0; | |
| 22 | } else if guess < answer { | |
| 17 | if const guess ?= parse_number(line) { | |
| 18 | if (guess > answer) { | |
| 19 | print_str("Guess lower.\n"); | |
| 20 | } else if (guess < answer) { | |
| 23 | 21 | print_str("Guess higher.\n"); |
| 24 | 22 | } else { |
| 25 | print_str("Guess lower.\n"); | |
| 23 | print_str("You win!\n"); | |
| 24 | return 0; | |
| 26 | 25 | } |
| 27 | 26 | } else { |
| 28 | 27 | print_str("Invalid number format.\n"); |
src/analyze.cpp+47-29| ... | ... | @@ -45,7 +45,8 @@ static AstNode *first_executing_node(AstNode *node) { |
| 45 | 45 | case NodeTypeUse: |
| 46 | 46 | case NodeTypeVoid: |
| 47 | 47 | case NodeTypeBoolLiteral: |
| 48 | case NodeTypeIfExpr: | |
| 48 | case NodeTypeIfBoolExpr: | |
| 49 | case NodeTypeIfVarExpr: | |
| 49 | 50 | case NodeTypeLabel: |
| 50 | 51 | case NodeTypeGoto: |
| 51 | 52 | case NodeTypeBreak: |
| ... | ... | @@ -528,7 +529,8 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, |
| 528 | 529 | case NodeTypeSymbol: |
| 529 | 530 | case NodeTypeCastExpr: |
| 530 | 531 | case NodeTypePrefixOpExpr: |
| 531 | case NodeTypeIfExpr: | |
| 532 | case NodeTypeIfBoolExpr: | |
| 533 | case NodeTypeIfVarExpr: | |
| 532 | 534 | case NodeTypeWhileExpr: |
| 533 | 535 | case NodeTypeLabel: |
| 534 | 536 | case NodeTypeGoto: |
| ... | ... | @@ -598,7 +600,8 @@ static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) { |
| 598 | 600 | case NodeTypeSymbol: |
| 599 | 601 | case NodeTypeCastExpr: |
| 600 | 602 | case NodeTypePrefixOpExpr: |
| 601 | case NodeTypeIfExpr: | |
| 603 | case NodeTypeIfBoolExpr: | |
| 604 | case NodeTypeIfVarExpr: | |
| 602 | 605 | case NodeTypeWhileExpr: |
| 603 | 606 | case NodeTypeLabel: |
| 604 | 607 | case NodeTypeGoto: |
| ... | ... | @@ -1394,6 +1397,39 @@ static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *impor |
| 1394 | 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 | 1433 | static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 1398 | 1434 | TypeTableEntry *expected_type, AstNode *node) |
| 1399 | 1435 | { |
| ... | ... | @@ -1651,31 +1687,12 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 1651 | 1687 | } |
| 1652 | 1688 | } |
| 1653 | 1689 | break; |
| 1654 | case NodeTypeIfExpr: | |
| 1655 | { | |
| 1656 | analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.if_expr.condition); | |
| 1657 | ||
| 1658 | TypeTableEntry *then_type = analyze_expression(g, import, context, expected_type, | |
| 1659 | node->data.if_expr.then_block); | |
| 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 | } | |
| 1690 | case NodeTypeIfBoolExpr: | |
| 1691 | return_type = analyze_if_bool_expr(g, import, context, expected_type, node); | |
| 1692 | break; | |
| 1693 | case NodeTypeIfVarExpr: | |
| 1694 | return_type = analyze_if_var_expr(g, import, context, expected_type, node); | |
| 1695 | break; | |
| 1679 | 1696 | case NodeTypeWhileExpr: |
| 1680 | 1697 | return_type = analyze_while_expr(g, import, context, expected_type, node); |
| 1681 | 1698 | break; |
| ... | ... | @@ -1828,7 +1845,8 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, |
| 1828 | 1845 | case NodeTypeSymbol: |
| 1829 | 1846 | case NodeTypeCastExpr: |
| 1830 | 1847 | case NodeTypePrefixOpExpr: |
| 1831 | case NodeTypeIfExpr: | |
| 1848 | case NodeTypeIfBoolExpr: | |
| 1849 | case NodeTypeIfVarExpr: | |
| 1832 | 1850 | case NodeTypeWhileExpr: |
| 1833 | 1851 | case NodeTypeLabel: |
| 1834 | 1852 | case NodeTypeGoto: |
src/codegen.cpp+22-15| ... | ... | @@ -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) { | |
| 773 | assert(node->type == NodeTypeIfExpr); | |
| 774 | assert(node->data.if_expr.condition); | |
| 775 | assert(node->data.if_expr.then_block); | |
| 772 | static LLVMValueRef gen_if_bool_expr(CodeGen *g, AstNode *node) { | |
| 773 | assert(node->type == NodeTypeIfBoolExpr); | |
| 774 | assert(node->data.if_bool_expr.condition); | |
| 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 | 780 | bool use_expr_value = (then_type->id != TypeTableEntryIdUnreachable && |
| 781 | 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 | 784 | LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then"); |
| 785 | 785 | LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Else"); |
| 786 | 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 | 788 | LLVMBuildCondBr(g->builder, cond_value, then_block, else_block); |
| 789 | 789 | |
| 790 | 790 | LLVMPositionBuilderAtEnd(g->builder, then_block); |
| 791 | LLVMValueRef then_expr_result = gen_expr(g, node->data.if_expr.then_block); | |
| 792 | if (get_expr_type(node->data.if_expr.then_block)->id != TypeTableEntryIdUnreachable) | |
| 791 | LLVMValueRef then_expr_result = gen_expr(g, node->data.if_bool_expr.then_block); | |
| 792 | if (get_expr_type(node->data.if_bool_expr.then_block)->id != TypeTableEntryIdUnreachable) | |
| 793 | 793 | LLVMBuildBr(g->builder, endif_block); |
| 794 | 794 | |
| 795 | 795 | LLVMPositionBuilderAtEnd(g->builder, else_block); |
| 796 | LLVMValueRef else_expr_result = gen_expr(g, node->data.if_expr.else_node); | |
| 797 | if (get_expr_type(node->data.if_expr.else_node)->id != TypeTableEntryIdUnreachable) | |
| 796 | LLVMValueRef else_expr_result = gen_expr(g, node->data.if_bool_expr.else_node); | |
| 797 | if (get_expr_type(node->data.if_bool_expr.else_node)->id != TypeTableEntryIdUnreachable) | |
| 798 | 798 | LLVMBuildBr(g->builder, endif_block); |
| 799 | 799 | |
| 800 | 800 | LLVMPositionBuilderAtEnd(g->builder, endif_block); |
| ... | ... | @@ -818,14 +818,19 @@ static LLVMValueRef gen_if_expr(CodeGen *g, AstNode *node) { |
| 818 | 818 | LLVMBuildCondBr(g->builder, cond_value, then_block, endif_block); |
| 819 | 819 | |
| 820 | 820 | LLVMPositionBuilderAtEnd(g->builder, then_block); |
| 821 | gen_expr(g, node->data.if_expr.then_block); | |
| 822 | if (get_expr_type(node->data.if_expr.then_block)->id != TypeTableEntryIdUnreachable) | |
| 821 | gen_expr(g, node->data.if_bool_expr.then_block); | |
| 822 | if (get_expr_type(node->data.if_bool_expr.then_block)->id != TypeTableEntryIdUnreachable) | |
| 823 | 823 | LLVMBuildBr(g->builder, endif_block); |
| 824 | 824 | |
| 825 | 825 | LLVMPositionBuilderAtEnd(g->builder, endif_block); |
| 826 | 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 | 834 | static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) { |
| 830 | 835 | assert(block_node->type == NodeTypeBlock); |
| 831 | 836 | |
| ... | ... | @@ -1112,8 +1117,10 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { |
| 1112 | 1117 | return LLVMConstAllOnes(LLVMInt1Type()); |
| 1113 | 1118 | else |
| 1114 | 1119 | return LLVMConstNull(LLVMInt1Type()); |
| 1115 | case NodeTypeIfExpr: | |
| 1116 | return gen_if_expr(g, node); | |
| 1120 | case NodeTypeIfBoolExpr: | |
| 1121 | return gen_if_bool_expr(g, node); | |
| 1122 | case NodeTypeIfVarExpr: | |
| 1123 | return gen_if_var_expr(g, node); | |
| 1117 | 1124 | case NodeTypeWhileExpr: |
| 1118 | 1125 | return gen_while_expr(g, node); |
| 1119 | 1126 | case NodeTypeAsmExpr: |
src/parser.cpp+57-14| ... | ... | @@ -114,8 +114,10 @@ const char *node_type_str(NodeType node_type) { |
| 114 | 114 | return "Void"; |
| 115 | 115 | case NodeTypeBoolLiteral: |
| 116 | 116 | return "BoolLiteral"; |
| 117 | case NodeTypeIfExpr: | |
| 118 | return "IfExpr"; | |
| 117 | case NodeTypeIfBoolExpr: | |
| 118 | return "IfBoolExpr"; | |
| 119 | case NodeTypeIfVarExpr: | |
| 120 | return "IfVarExpr"; | |
| 119 | 121 | case NodeTypeWhileExpr: |
| 120 | 122 | return "WhileExpr"; |
| 121 | 123 | case NodeTypeLabel: |
| ... | ... | @@ -321,14 +323,27 @@ void ast_print(AstNode *node, int indent) { |
| 321 | 323 | case NodeTypeBoolLiteral: |
| 322 | 324 | fprintf(stderr, "%s '%s'\n", node_type_str(node->type), node->data.bool_literal ? "true" : "false"); |
| 323 | 325 | break; |
| 324 | case NodeTypeIfExpr: | |
| 326 | case NodeTypeIfBoolExpr: | |
| 325 | 327 | fprintf(stderr, "%s\n", node_type_str(node->type)); |
| 326 | if (node->data.if_expr.condition) | |
| 327 | ast_print(node->data.if_expr.condition, indent + 2); | |
| 328 | ast_print(node->data.if_expr.then_block, indent + 2); | |
| 329 | if (node->data.if_expr.else_node) | |
| 330 | ast_print(node->data.if_expr.else_node, indent + 2); | |
| 328 | if (node->data.if_bool_expr.condition) | |
| 329 | ast_print(node->data.if_bool_expr.condition, indent + 2); | |
| 330 | ast_print(node->data.if_bool_expr.then_block, indent + 2); | |
| 331 | if (node->data.if_bool_expr.else_node) | |
| 332 | ast_print(node->data.if_bool_expr.else_node, indent + 2); | |
| 331 | 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 | 347 | case NodeTypeWhileExpr: |
| 333 | 348 | fprintf(stderr, "%s\n", node_type_str(node->type)); |
| 334 | 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 | 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 | 1666 | static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| 1650 | 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 | 1674 | } |
| 1658 | 1675 | *token_index += 1; |
| 1659 | 1676 | |
| 1660 | AstNode *node = ast_create_node(pc, NodeTypeIfExpr, if_tok); | |
| 1661 | node->data.if_expr.condition = ast_parse_expression(pc, token_index, true); | |
| 1662 | node->data.if_expr.then_block = ast_parse_block(pc, token_index, true); | |
| 1663 | node->data.if_expr.else_node = ast_parse_else_or_else_if(pc, token_index, false); | |
| 1664 | return node; | |
| 1677 | Token *token = &pc->tokens->at(*token_index); | |
| 1678 | if (token->id == TokenIdKeywordConst || token->id == TokenIdKeywordVar) { | |
| 1679 | AstNode *node = ast_create_node(pc, NodeTypeIfVarExpr, if_tok); | |
| 1680 | node->data.if_var_expr.var_decl.is_const = (token->id == TokenIdKeywordConst); | |
| 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 | 44 | NodeTypeUse, |
| 45 | 45 | NodeTypeVoid, |
| 46 | 46 | NodeTypeBoolLiteral, |
| 47 | NodeTypeIfExpr, | |
| 47 | NodeTypeIfBoolExpr, | |
| 48 | NodeTypeIfVarExpr, | |
| 48 | 49 | NodeTypeWhileExpr, |
| 49 | 50 | NodeTypeLabel, |
| 50 | 51 | NodeTypeGoto, |
| ... | ... | @@ -217,12 +218,18 @@ struct AstNodeUse { |
| 217 | 218 | ZigList<AstNode *> *directives; |
| 218 | 219 | }; |
| 219 | 220 | |
| 220 | struct AstNodeIfExpr { | |
| 221 | struct AstNodeIfBoolExpr { | |
| 221 | 222 | AstNode *condition; |
| 222 | 223 | AstNode *then_block; |
| 223 | 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 | 233 | struct AstNodeWhileExpr { |
| 227 | 234 | AstNode *condition; |
| 228 | 235 | AstNode *body; |
| ... | ... | @@ -341,7 +348,8 @@ struct AstNode { |
| 341 | 348 | AstNodeFnCallExpr fn_call_expr; |
| 342 | 349 | AstNodeArrayAccessExpr array_access_expr; |
| 343 | 350 | AstNodeUse use; |
| 344 | AstNodeIfExpr if_expr; | |
| 351 | AstNodeIfBoolExpr if_bool_expr; | |
| 352 | AstNodeIfVarExpr if_var_expr; | |
| 345 | 353 | AstNodeWhileExpr while_expr; |
| 346 | 354 | AstNodeLabel label; |
| 347 | 355 | AstNodeGoto go_to; |
src/tokenizer.cpp+22| ... | ... | @@ -125,6 +125,7 @@ enum TokenizeState { |
| 125 | 125 | TokenizeStateSawGreaterThanGreaterThan, |
| 126 | 126 | TokenizeStateSawDot, |
| 127 | 127 | TokenizeStateSawDotDot, |
| 128 | TokenizeStateSawQuestionMark, | |
| 128 | 129 | TokenizeStateError, |
| 129 | 130 | }; |
| 130 | 131 | |
| ... | ... | @@ -402,10 +403,28 @@ void tokenize(Buf *buf, Tokenization *out) { |
| 402 | 403 | begin_token(&t, TokenIdDot); |
| 403 | 404 | t.state = TokenizeStateSawDot; |
| 404 | 405 | break; |
| 406 | case '?': | |
| 407 | begin_token(&t, TokenIdMaybe); | |
| 408 | t.state = TokenizeStateSawQuestionMark; | |
| 409 | break; | |
| 405 | 410 | default: |
| 406 | 411 | tokenize_error(&t, "invalid character: '%c'", c); |
| 407 | 412 | } |
| 408 | 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 | 428 | case TokenizeStateSawDot: |
| 410 | 429 | switch (c) { |
| 411 | 430 | case '.': |
| ... | ... | @@ -917,6 +936,7 @@ void tokenize(Buf *buf, Tokenization *out) { |
| 917 | 936 | case TokenizeStateSawGreaterThan: |
| 918 | 937 | case TokenizeStateSawGreaterThanGreaterThan: |
| 919 | 938 | case TokenizeStateSawDot: |
| 939 | case TokenizeStateSawQuestionMark: | |
| 920 | 940 | end_token(&t); |
| 921 | 941 | break; |
| 922 | 942 | case TokenizeStateSawDotDot: |
| ... | ... | @@ -1012,6 +1032,8 @@ static const char * token_name(Token *token) { |
| 1012 | 1032 | case TokenIdPercent: return "Percent"; |
| 1013 | 1033 | case TokenIdDot: return "Dot"; |
| 1014 | 1034 | case TokenIdEllipsis: return "Ellipsis"; |
| 1035 | case TokenIdMaybe: return "Maybe"; | |
| 1036 | case TokenIdMaybeAssign: return "MaybeAssign"; | |
| 1015 | 1037 | } |
| 1016 | 1038 | return "(invalid token)"; |
| 1017 | 1039 | } |
src/tokenizer.hpp+2| ... | ... | @@ -83,6 +83,8 @@ enum TokenId { |
| 83 | 83 | TokenIdPercent, |
| 84 | 84 | TokenIdDot, |
| 85 | 85 | TokenIdEllipsis, |
| 86 | TokenIdMaybe, | |
| 87 | TokenIdMaybeAssign, | |
| 86 | 88 | }; |
| 87 | 89 | |
| 88 | 90 | struct Token { |