| author | |
| committer | |
| log | c0b37e8514a26f56e3596f86b01771b1ae2bf299 |
| tree | b24c7fd04b2e8582d4e79455455e740ef58ca864 |
| parent | 8b1c6d8b76ad1861f963fc7a2a079af5d8729a70 |
See #8312 files changed, 330 insertions(+), 89 deletions(-)
doc/langref.md+3-1| ... | @@ -69,7 +69,7 @@ AssignmentExpression = UnwrapExpression AssignmentOperator UnwrapExpression | Un | ... | @@ -69,7 +69,7 @@ AssignmentExpression = UnwrapExpression AssignmentOperator UnwrapExpression | Un |
| 69 | 69 | ||
| 70 | AssignmentOperator = "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|=" | "&&=" | "||=" | "*%=" | "+%=" | "-%=" | "<<%=" | 70 | AssignmentOperator = "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|=" | "&&=" | "||=" | "*%=" | "+%=" | "-%=" | "<<%=" |
| 71 | 71 | ||
| 72 | BlockExpression = IfExpression | Block | WhileExpression | ForExpression | SwitchExpression | CompTimeExpression | 72 | BlockExpression = IfExpression | Block | WhileExpression | ForExpression | SwitchExpression | CompTimeExpression | TryExpression |
| 73 | 73 | ||
| 74 | CompTimeExpression = option("comptime") Expression | 74 | CompTimeExpression = option("comptime") Expression |
| 75 | 75 | ||
| ... | @@ -93,6 +93,8 @@ IfExpression = IfVarExpression | IfBoolExpression | ... | @@ -93,6 +93,8 @@ IfExpression = IfVarExpression | IfBoolExpression |
| 93 | 93 | ||
| 94 | IfBoolExpression = "if" "(" Expression ")" Expression option(Else) | 94 | IfBoolExpression = "if" "(" Expression ")" Expression option(Else) |
| 95 | 95 | ||
| 96 | TryExpression = "try" "(" ("const" | "var") option("*") Symbol "=" Expression ")" Expression option("else" option("|" Symbol "|") Expression) | ||
| 97 | |||
| 96 | IfVarExpression = "if" "(" ("const" | "var") option("*") Symbol option(":" TypeExpr) "?=" Expression ")" Expression Option(Else) | 98 | IfVarExpression = "if" "(" ("const" | "var") option("*") Symbol option(":" TypeExpr) "?=" Expression ")" Expression Option(Else) |
| 97 | 99 | ||
| 98 | Else = "else" Expression | 100 | Else = "else" Expression |
doc/vim/syntax/zig.vim+1-1| ... | @@ -11,7 +11,7 @@ let b:current_syntax = "zig" | ... | @@ -11,7 +11,7 @@ let b:current_syntax = "zig" |
| 11 | syn keyword zigStorage const var extern export pub noalias inline comptime nakedcc coldcc | 11 | syn keyword zigStorage const var extern export pub noalias inline comptime nakedcc coldcc |
| 12 | syn keyword zigStructure struct enum union | 12 | syn keyword zigStructure struct enum union |
| 13 | syn keyword zigStatement goto break return continue asm defer | 13 | syn keyword zigStatement goto break return continue asm defer |
| 14 | syn keyword zigConditional if else switch | 14 | syn keyword zigConditional if else switch try |
| 15 | syn keyword zigRepeat while for | 15 | syn keyword zigRepeat while for |
| 16 | 16 | ||
| 17 | syn keyword zigConstant null undefined zeroes this | 17 | syn keyword zigConstant null undefined zeroes this |
src/all_types.hpp+12| ... | @@ -299,6 +299,7 @@ enum NodeType { | ... | @@ -299,6 +299,7 @@ enum NodeType { |
| 299 | NodeTypeErrorType, | 299 | NodeTypeErrorType, |
| 300 | NodeTypeTypeLiteral, | 300 | NodeTypeTypeLiteral, |
| 301 | NodeTypeVarLiteral, | 301 | NodeTypeVarLiteral, |
| 302 | NodeTypeTryExpr, | ||
| 302 | }; | 303 | }; |
| 303 | 304 | ||
| 304 | struct AstNodeRoot { | 305 | struct AstNodeRoot { |
| ... | @@ -511,6 +512,16 @@ struct AstNodeIfBoolExpr { | ... | @@ -511,6 +512,16 @@ struct AstNodeIfBoolExpr { |
| 511 | AstNode *else_node; // null, block node, or other if expr node | 512 | AstNode *else_node; // null, block node, or other if expr node |
| 512 | }; | 513 | }; |
| 513 | 514 | ||
| 515 | struct AstNodeTryExpr { | ||
| 516 | bool var_is_const; | ||
| 517 | Buf *var_symbol; | ||
| 518 | bool var_is_ptr; | ||
| 519 | AstNode *target_node; | ||
| 520 | AstNode *then_node; | ||
| 521 | AstNode *else_node; | ||
| 522 | Buf *err_symbol; | ||
| 523 | }; | ||
| 524 | |||
| 514 | struct AstNodeIfVarExpr { | 525 | struct AstNodeIfVarExpr { |
| 515 | AstNodeVariableDeclaration var_decl; | 526 | AstNodeVariableDeclaration var_decl; |
| 516 | AstNode *then_block; | 527 | AstNode *then_block; |
| ... | @@ -721,6 +732,7 @@ struct AstNode { | ... | @@ -721,6 +732,7 @@ struct AstNode { |
| 721 | AstNodeUse use; | 732 | AstNodeUse use; |
| 722 | AstNodeIfBoolExpr if_bool_expr; | 733 | AstNodeIfBoolExpr if_bool_expr; |
| 723 | AstNodeIfVarExpr if_var_expr; | 734 | AstNodeIfVarExpr if_var_expr; |
| 735 | AstNodeTryExpr try_expr; | ||
| 724 | AstNodeWhileExpr while_expr; | 736 | AstNodeWhileExpr while_expr; |
| 725 | AstNodeForExpr for_expr; | 737 | AstNodeForExpr for_expr; |
| 726 | AstNodeSwitchExpr switch_expr; | 738 | AstNodeSwitchExpr switch_expr; |
src/analyze.cpp+1| ... | @@ -1769,6 +1769,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { | ... | @@ -1769,6 +1769,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { |
| 1769 | case NodeTypeErrorType: | 1769 | case NodeTypeErrorType: |
| 1770 | case NodeTypeTypeLiteral: | 1770 | case NodeTypeTypeLiteral: |
| 1771 | case NodeTypeVarLiteral: | 1771 | case NodeTypeVarLiteral: |
| 1772 | case NodeTypeTryExpr: | ||
| 1772 | zig_unreachable(); | 1773 | zig_unreachable(); |
| 1773 | } | 1774 | } |
| 1774 | } | 1775 | } |
src/ast_render.cpp+21| ... | @@ -223,6 +223,8 @@ static const char *node_type_str(NodeType node_type) { | ... | @@ -223,6 +223,8 @@ static const char *node_type_str(NodeType node_type) { |
| 223 | return "TypeLiteral"; | 223 | return "TypeLiteral"; |
| 224 | case NodeTypeVarLiteral: | 224 | case NodeTypeVarLiteral: |
| 225 | return "VarLiteral"; | 225 | return "VarLiteral"; |
| 226 | case NodeTypeTryExpr: | ||
| 227 | return "TryExpr"; | ||
| 226 | } | 228 | } |
| 227 | zig_unreachable(); | 229 | zig_unreachable(); |
| 228 | } | 230 | } |
| ... | @@ -769,6 +771,25 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { | ... | @@ -769,6 +771,25 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { |
| 769 | } | 771 | } |
| 770 | break; | 772 | break; |
| 771 | } | 773 | } |
| 774 | case NodeTypeTryExpr: | ||
| 775 | { | ||
| 776 | const char *var_str = node->data.try_expr.var_is_const ? "const" : "var"; | ||
| 777 | const char *var_name = buf_ptr(node->data.try_expr.var_symbol); | ||
| 778 | const char *ptr_str = node->data.try_expr.var_is_ptr ? "*" : ""; | ||
| 779 | fprintf(ar->f, "try (%s %s%s", var_str, ptr_str, var_name); | ||
| 780 | fprintf(ar->f, " = "); | ||
| 781 | render_node_grouped(ar, node->data.try_expr.target_node); | ||
| 782 | fprintf(ar->f, ") "); | ||
| 783 | render_node_grouped(ar, node->data.try_expr.then_node); | ||
| 784 | if (node->data.try_expr.else_node) { | ||
| 785 | fprintf(ar->f, " else "); | ||
| 786 | if (node->data.try_expr.err_symbol) { | ||
| 787 | fprintf(ar->f, "|%s| ", buf_ptr(node->data.try_expr.err_symbol)); | ||
| 788 | } | ||
| 789 | render_node_grouped(ar, node->data.try_expr.else_node); | ||
| 790 | } | ||
| 791 | break; | ||
| 792 | } | ||
| 772 | case NodeTypeSwitchExpr: | 793 | case NodeTypeSwitchExpr: |
| 773 | { | 794 | { |
| 774 | AstNodeSwitchExpr *switch_expr = &node->data.switch_expr; | 795 | AstNodeSwitchExpr *switch_expr = &node->data.switch_expr; |
src/ir.cpp+94| ... | @@ -4748,6 +4748,98 @@ static IrInstruction *ir_gen_if_var_expr(IrBuilder *irb, Scope *scope, AstNode * | ... | @@ -4748,6 +4748,98 @@ static IrInstruction *ir_gen_if_var_expr(IrBuilder *irb, Scope *scope, AstNode * |
| 4748 | return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); | 4748 | return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); |
| 4749 | } | 4749 | } |
| 4750 | 4750 | ||
| 4751 | static IrInstruction *ir_gen_try_expr(IrBuilder *irb, Scope *scope, AstNode *node) { | ||
| 4752 | assert(node->type == NodeTypeTryExpr); | ||
| 4753 | |||
| 4754 | AstNode *target_node = node->data.try_expr.target_node; | ||
| 4755 | AstNode *then_node = node->data.try_expr.then_node; | ||
| 4756 | AstNode *else_node = node->data.try_expr.else_node; | ||
| 4757 | bool var_is_ptr = node->data.try_expr.var_is_ptr; | ||
| 4758 | bool var_is_const = node->data.try_expr.var_is_const; | ||
| 4759 | Buf *var_symbol = node->data.try_expr.var_symbol; | ||
| 4760 | Buf *err_symbol = node->data.try_expr.err_symbol; | ||
| 4761 | |||
| 4762 | IrInstruction *err_val_ptr = ir_gen_node_extra(irb, target_node, scope, LValPurposeAddressOf); | ||
| 4763 | if (err_val_ptr == irb->codegen->invalid_instruction) | ||
| 4764 | return err_val_ptr; | ||
| 4765 | |||
| 4766 | IrInstruction *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr); | ||
| 4767 | IrInstruction *is_err = ir_build_test_err(irb, scope, node, err_val); | ||
| 4768 | |||
| 4769 | IrBasicBlock *ok_block = ir_build_basic_block(irb, scope, "TryOk"); | ||
| 4770 | IrBasicBlock *else_block = ir_build_basic_block(irb, scope, "TryElse"); | ||
| 4771 | IrBasicBlock *endif_block = ir_build_basic_block(irb, scope, "TryEnd"); | ||
| 4772 | |||
| 4773 | IrInstruction *is_comptime; | ||
| 4774 | if (ir_should_inline(irb->exec, scope)) { | ||
| 4775 | is_comptime = ir_build_const_bool(irb, scope, node, true); | ||
| 4776 | } else { | ||
| 4777 | is_comptime = ir_build_test_comptime(irb, scope, node, is_err); | ||
| 4778 | } | ||
| 4779 | ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, is_comptime); | ||
| 4780 | |||
| 4781 | ir_set_cursor_at_end(irb, ok_block); | ||
| 4782 | |||
| 4783 | Scope *var_scope; | ||
| 4784 | if (var_symbol) { | ||
| 4785 | IrInstruction *var_type = nullptr; | ||
| 4786 | bool is_shadowable = false; | ||
| 4787 | VariableTableEntry *var = ir_create_var(irb, node, scope, | ||
| 4788 | var_symbol, var_is_const, var_is_const, is_shadowable, is_comptime); | ||
| 4789 | |||
| 4790 | IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, scope, node, err_val_ptr, false); | ||
| 4791 | IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, scope, node, var_ptr_value); | ||
| 4792 | ir_build_var_decl(irb, scope, node, var, var_type, var_value); | ||
| 4793 | var_scope = var->child_scope; | ||
| 4794 | } else { | ||
| 4795 | var_scope = scope; | ||
| 4796 | } | ||
| 4797 | IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope); | ||
| 4798 | if (then_expr_result == irb->codegen->invalid_instruction) | ||
| 4799 | return then_expr_result; | ||
| 4800 | IrBasicBlock *after_then_block = irb->current_basic_block; | ||
| 4801 | if (!instr_is_unreachable(then_expr_result)) | ||
| 4802 | ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); | ||
| 4803 | |||
| 4804 | ir_set_cursor_at_end(irb, else_block); | ||
| 4805 | |||
| 4806 | IrInstruction *else_expr_result; | ||
| 4807 | if (else_node) { | ||
| 4808 | Scope *err_var_scope; | ||
| 4809 | if (err_symbol) { | ||
| 4810 | IrInstruction *var_type = nullptr; | ||
| 4811 | bool is_shadowable = false; | ||
| 4812 | bool is_const = true; | ||
| 4813 | VariableTableEntry *var = ir_create_var(irb, node, scope, | ||
| 4814 | err_symbol, is_const, is_const, is_shadowable, is_comptime); | ||
| 4815 | |||
| 4816 | IrInstruction *var_value = ir_build_unwrap_err_code(irb, scope, node, err_val_ptr); | ||
| 4817 | ir_build_var_decl(irb, scope, node, var, var_type, var_value); | ||
| 4818 | err_var_scope = var->child_scope; | ||
| 4819 | } else { | ||
| 4820 | err_var_scope = scope; | ||
| 4821 | } | ||
| 4822 | else_expr_result = ir_gen_node(irb, else_node, err_var_scope); | ||
| 4823 | if (else_expr_result == irb->codegen->invalid_instruction) | ||
| 4824 | return else_expr_result; | ||
| 4825 | } else { | ||
| 4826 | else_expr_result = ir_build_const_void(irb, scope, node); | ||
| 4827 | } | ||
| 4828 | IrBasicBlock *after_else_block = irb->current_basic_block; | ||
| 4829 | if (!instr_is_unreachable(else_expr_result)) | ||
| 4830 | ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); | ||
| 4831 | |||
| 4832 | ir_set_cursor_at_end(irb, endif_block); | ||
| 4833 | IrInstruction **incoming_values = allocate<IrInstruction *>(2); | ||
| 4834 | incoming_values[0] = then_expr_result; | ||
| 4835 | incoming_values[1] = else_expr_result; | ||
| 4836 | IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2); | ||
| 4837 | incoming_blocks[0] = after_then_block; | ||
| 4838 | incoming_blocks[1] = after_else_block; | ||
| 4839 | |||
| 4840 | return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values); | ||
| 4841 | } | ||
| 4842 | |||
| 4751 | static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *switch_node, AstNode *prong_node, | 4843 | static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *switch_node, AstNode *prong_node, |
| 4752 | IrBasicBlock *end_block, IrInstruction *is_comptime, IrInstruction *target_value_ptr, IrInstruction *prong_value, | 4844 | IrBasicBlock *end_block, IrInstruction *is_comptime, IrInstruction *target_value_ptr, IrInstruction *prong_value, |
| 4753 | ZigList<IrBasicBlock *> *incoming_blocks, ZigList<IrInstruction *> *incoming_values) | 4845 | ZigList<IrBasicBlock *> *incoming_blocks, ZigList<IrInstruction *> *incoming_values) |
| ... | @@ -5291,6 +5383,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop | ... | @@ -5291,6 +5383,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop |
| 5291 | return ir_lval_wrap(irb, scope, ir_gen_var_literal(irb, scope, node), lval); | 5383 | return ir_lval_wrap(irb, scope, ir_gen_var_literal(irb, scope, node), lval); |
| 5292 | case NodeTypeIfVarExpr: | 5384 | case NodeTypeIfVarExpr: |
| 5293 | return ir_lval_wrap(irb, scope, ir_gen_if_var_expr(irb, scope, node), lval); | 5385 | return ir_lval_wrap(irb, scope, ir_gen_if_var_expr(irb, scope, node), lval); |
| 5386 | case NodeTypeTryExpr: | ||
| 5387 | return ir_lval_wrap(irb, scope, ir_gen_try_expr(irb, scope, node), lval); | ||
| 5294 | case NodeTypeSwitchExpr: | 5388 | case NodeTypeSwitchExpr: |
| 5295 | return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node), lval); | 5389 | return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node), lval); |
| 5296 | case NodeTypeGoto: | 5390 | case NodeTypeGoto: |
src/parser.cpp+75-1| ... | @@ -623,6 +623,71 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b | ... | @@ -623,6 +623,71 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b |
| 623 | return node; | 623 | return node; |
| 624 | } | 624 | } |
| 625 | 625 | ||
| 626 | /* | ||
| 627 | TryExpression = "try" "(" ("const" | "var") option("*") Symbol "=" Expression ")" Expression option("else" option("|" Symbol "|") Expression) | ||
| 628 | */ | ||
| 629 | static AstNode *ast_parse_try_expr(ParseContext *pc, size_t *token_index, bool mandatory) { | ||
| 630 | Token *try_token = &pc->tokens->at(*token_index); | ||
| 631 | if (try_token->id == TokenIdKeywordTry) { | ||
| 632 | *token_index += 1; | ||
| 633 | } else if (mandatory) { | ||
| 634 | ast_expect_token(pc, try_token, TokenIdKeywordTry); | ||
| 635 | zig_unreachable(); | ||
| 636 | } else { | ||
| 637 | return nullptr; | ||
| 638 | } | ||
| 639 | |||
| 640 | AstNode *node = ast_create_node(pc, NodeTypeTryExpr, try_token); | ||
| 641 | |||
| 642 | ast_eat_token(pc, token_index, TokenIdLParen); | ||
| 643 | |||
| 644 | Token *var_token = &pc->tokens->at(*token_index); | ||
| 645 | if (var_token->id == TokenIdKeywordVar) { | ||
| 646 | node->data.try_expr.var_is_const = false; | ||
| 647 | *token_index += 1; | ||
| 648 | } else if (var_token->id == TokenIdKeywordConst) { | ||
| 649 | node->data.try_expr.var_is_const = true; | ||
| 650 | *token_index += 1; | ||
| 651 | } else { | ||
| 652 | ast_invalid_token_error(pc, var_token); | ||
| 653 | } | ||
| 654 | |||
| 655 | Token *star_token = &pc->tokens->at(*token_index); | ||
| 656 | if (star_token->id == TokenIdStar) { | ||
| 657 | node->data.try_expr.var_is_ptr = true; | ||
| 658 | *token_index += 1; | ||
| 659 | } | ||
| 660 | |||
| 661 | Token *var_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); | ||
| 662 | node->data.try_expr.var_symbol = token_buf(var_name_tok); | ||
| 663 | |||
| 664 | ast_eat_token(pc, token_index, TokenIdEq); | ||
| 665 | |||
| 666 | node->data.try_expr.target_node = ast_parse_expression(pc, token_index, true); | ||
| 667 | |||
| 668 | ast_eat_token(pc, token_index, TokenIdRParen); | ||
| 669 | |||
| 670 | node->data.try_expr.then_node = ast_parse_expression(pc, token_index, true); | ||
| 671 | |||
| 672 | Token *else_token = &pc->tokens->at(*token_index); | ||
| 673 | if (else_token->id != TokenIdKeywordElse) | ||
| 674 | return node; | ||
| 675 | |||
| 676 | *token_index += 1; | ||
| 677 | Token *open_bar_tok = &pc->tokens->at(*token_index); | ||
| 678 | if (open_bar_tok->id == TokenIdBinOr) { | ||
| 679 | *token_index += 1; | ||
| 680 | |||
| 681 | Token *err_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); | ||
| 682 | node->data.try_expr.err_symbol = token_buf(err_name_tok); | ||
| 683 | |||
| 684 | ast_eat_token(pc, token_index, TokenIdBinOr); | ||
| 685 | } | ||
| 686 | |||
| 687 | node->data.try_expr.else_node = ast_parse_expression(pc, token_index, true); | ||
| 688 | return node; | ||
| 689 | } | ||
| 690 | |||
| 626 | /* | 691 | /* |
| 627 | PrimaryExpression = Number | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl | 692 | PrimaryExpression = Number | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl |
| 628 | KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" | "type" | "this" | 693 | KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" | "type" | "this" |
| ... | @@ -1775,7 +1840,7 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, size_t *token_index, boo | ... | @@ -1775,7 +1840,7 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, size_t *token_index, boo |
| 1775 | } | 1840 | } |
| 1776 | 1841 | ||
| 1777 | /* | 1842 | /* |
| 1778 | BlockExpression = IfExpression | Block | WhileExpression | ForExpression | SwitchExpression | CompTimeExpression | 1843 | BlockExpression = IfExpression | Block | WhileExpression | ForExpression | SwitchExpression | CompTimeExpression | TryExpression |
| 1779 | */ | 1844 | */ |
| 1780 | static AstNode *ast_parse_block_expr(ParseContext *pc, size_t *token_index, bool mandatory) { | 1845 | static AstNode *ast_parse_block_expr(ParseContext *pc, size_t *token_index, bool mandatory) { |
| 1781 | Token *token = &pc->tokens->at(*token_index); | 1846 | Token *token = &pc->tokens->at(*token_index); |
| ... | @@ -1804,6 +1869,10 @@ static AstNode *ast_parse_block_expr(ParseContext *pc, size_t *token_index, bool | ... | @@ -1804,6 +1869,10 @@ static AstNode *ast_parse_block_expr(ParseContext *pc, size_t *token_index, bool |
| 1804 | if (comptime_node) | 1869 | if (comptime_node) |
| 1805 | return comptime_node; | 1870 | return comptime_node; |
| 1806 | 1871 | ||
| 1872 | AstNode *try_node = ast_parse_try_expr(pc, token_index, false); | ||
| 1873 | if (try_node) | ||
| 1874 | return try_node; | ||
| 1875 | |||
| 1807 | if (mandatory) | 1876 | if (mandatory) |
| 1808 | ast_invalid_token_error(pc, token); | 1877 | ast_invalid_token_error(pc, token); |
| 1809 | 1878 | ||
| ... | @@ -2555,6 +2624,11 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont | ... | @@ -2555,6 +2624,11 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont |
| 2555 | visit_field(&node->data.if_var_expr.then_block, visit, context); | 2624 | visit_field(&node->data.if_var_expr.then_block, visit, context); |
| 2556 | visit_field(&node->data.if_var_expr.else_node, visit, context); | 2625 | visit_field(&node->data.if_var_expr.else_node, visit, context); |
| 2557 | break; | 2626 | break; |
| 2627 | case NodeTypeTryExpr: | ||
| 2628 | visit_field(&node->data.try_expr.target_node, visit, context); | ||
| 2629 | visit_field(&node->data.try_expr.then_node, visit, context); | ||
| 2630 | visit_field(&node->data.try_expr.else_node, visit, context); | ||
| 2631 | break; | ||
| 2558 | case NodeTypeWhileExpr: | 2632 | case NodeTypeWhileExpr: |
| 2559 | visit_field(&node->data.while_expr.condition, visit, context); | 2633 | visit_field(&node->data.while_expr.condition, visit, context); |
| 2560 | visit_field(&node->data.while_expr.body, visit, context); | 2634 | visit_field(&node->data.while_expr.body, visit, context); |
src/tokenizer.cpp+86-84| ... | @@ -134,6 +134,7 @@ static const struct ZigKeyword zig_keywords[] = { | ... | @@ -134,6 +134,7 @@ static const struct ZigKeyword zig_keywords[] = { |
| 134 | {"switch", TokenIdKeywordSwitch}, | 134 | {"switch", TokenIdKeywordSwitch}, |
| 135 | {"this", TokenIdKeywordThis}, | 135 | {"this", TokenIdKeywordThis}, |
| 136 | {"true", TokenIdKeywordTrue}, | 136 | {"true", TokenIdKeywordTrue}, |
| 137 | {"try", TokenIdKeywordTry}, | ||
| 137 | {"type", TokenIdKeywordType}, | 138 | {"type", TokenIdKeywordType}, |
| 138 | {"undefined", TokenIdKeywordUndefined}, | 139 | {"undefined", TokenIdKeywordUndefined}, |
| 139 | {"union", TokenIdKeywordUnion}, | 140 | {"union", TokenIdKeywordUnion}, |
| ... | @@ -1444,108 +1445,109 @@ void tokenize(Buf *buf, Tokenization *out) { | ... | @@ -1444,108 +1445,109 @@ void tokenize(Buf *buf, Tokenization *out) { |
| 1444 | 1445 | ||
| 1445 | const char * token_name(TokenId id) { | 1446 | const char * token_name(TokenId id) { |
| 1446 | switch (id) { | 1447 | switch (id) { |
| 1447 | case TokenIdEof: return "EOF"; | 1448 | case TokenIdAmpersand: return "&"; |
| 1448 | case TokenIdSymbol: return "Symbol"; | ||
| 1449 | case TokenIdKeywordFn: return "fn"; | ||
| 1450 | case TokenIdKeywordConst: return "const"; | ||
| 1451 | case TokenIdKeywordVar: return "var"; | ||
| 1452 | case TokenIdKeywordReturn: return "return"; | ||
| 1453 | case TokenIdKeywordExtern: return "extern"; | ||
| 1454 | case TokenIdKeywordPub: return "pub"; | ||
| 1455 | case TokenIdKeywordExport: return "export"; | ||
| 1456 | case TokenIdKeywordUse: return "use"; | ||
| 1457 | case TokenIdKeywordTrue: return "true"; | ||
| 1458 | case TokenIdKeywordFalse: return "false"; | ||
| 1459 | case TokenIdKeywordIf: return "if"; | ||
| 1460 | case TokenIdKeywordElse: return "else"; | ||
| 1461 | case TokenIdKeywordGoto: return "goto"; | ||
| 1462 | case TokenIdKeywordVolatile: return "volatile"; | ||
| 1463 | case TokenIdKeywordAsm: return "asm"; | ||
| 1464 | case TokenIdKeywordStruct: return "struct"; | ||
| 1465 | case TokenIdKeywordEnum: return "enum"; | ||
| 1466 | case TokenIdKeywordUnion: return "union"; | ||
| 1467 | case TokenIdKeywordWhile: return "while"; | ||
| 1468 | case TokenIdKeywordFor: return "for"; | ||
| 1469 | case TokenIdKeywordContinue: return "continue"; | ||
| 1470 | case TokenIdKeywordBreak: return "break"; | ||
| 1471 | case TokenIdKeywordNull: return "null"; | ||
| 1472 | case TokenIdKeywordNoAlias: return "noalias"; | ||
| 1473 | case TokenIdKeywordSwitch: return "switch"; | ||
| 1474 | case TokenIdKeywordUndefined: return "undefined"; | ||
| 1475 | case TokenIdKeywordThis: return "this"; | ||
| 1476 | case TokenIdKeywordError: return "error"; | ||
| 1477 | case TokenIdKeywordType: return "type"; | ||
| 1478 | case TokenIdKeywordInline: return "inline"; | ||
| 1479 | case TokenIdKeywordCompTime: return "comptime"; | ||
| 1480 | case TokenIdKeywordDefer: return "defer"; | ||
| 1481 | case TokenIdKeywordColdCC: return "coldcc"; | ||
| 1482 | case TokenIdKeywordNakedCC: return "nakedcc"; | ||
| 1483 | case TokenIdLParen: return "("; | ||
| 1484 | case TokenIdRParen: return ")"; | ||
| 1485 | case TokenIdComma: return ","; | ||
| 1486 | case TokenIdStar: return "*"; | ||
| 1487 | case TokenIdStarStar: return "**"; | ||
| 1488 | case TokenIdLBrace: return "{"; | ||
| 1489 | case TokenIdRBrace: return "}"; | ||
| 1490 | case TokenIdLBracket: return "["; | ||
| 1491 | case TokenIdRBracket: return "]"; | ||
| 1492 | case TokenIdStringLiteral: return "StringLiteral"; | ||
| 1493 | case TokenIdCharLiteral: return "CharLiteral"; | ||
| 1494 | case TokenIdSemicolon: return ";"; | ||
| 1495 | case TokenIdNumberLiteral: return "NumberLiteral"; | ||
| 1496 | case TokenIdPlus: return "+"; | ||
| 1497 | case TokenIdPlusPlus: return "++"; | ||
| 1498 | case TokenIdColon: return ":"; | ||
| 1499 | case TokenIdArrow: return "->"; | 1449 | case TokenIdArrow: return "->"; |
| 1500 | case TokenIdFatArrow: return "=>"; | 1450 | case TokenIdAtSign: return "@"; |
| 1501 | case TokenIdDash: return "-"; | 1451 | case TokenIdBang: return "!"; |
| 1502 | case TokenIdNumberSign: return "#"; | ||
| 1503 | case TokenIdBinOr: return "|"; | 1452 | case TokenIdBinOr: return "|"; |
| 1504 | case TokenIdAmpersand: return "&"; | ||
| 1505 | case TokenIdBinXor: return "^"; | 1453 | case TokenIdBinXor: return "^"; |
| 1506 | case TokenIdBoolOr: return "||"; | 1454 | case TokenIdBitAndEq: return "&="; |
| 1507 | case TokenIdBoolAnd: return "&&"; | 1455 | case TokenIdBitOrEq: return "|="; |
| 1508 | case TokenIdEq: return "="; | 1456 | case TokenIdBitShiftLeft: return "<<"; |
| 1509 | case TokenIdTimesEq: return "*="; | ||
| 1510 | case TokenIdDivEq: return "/="; | ||
| 1511 | case TokenIdModEq: return "%="; | ||
| 1512 | case TokenIdPlusEq: return "+="; | ||
| 1513 | case TokenIdMinusEq: return "-="; | ||
| 1514 | case TokenIdBitShiftLeftEq: return "<<="; | 1457 | case TokenIdBitShiftLeftEq: return "<<="; |
| 1458 | case TokenIdBitShiftLeftPercent: return "<<%"; | ||
| 1459 | case TokenIdBitShiftLeftPercentEq: return "<<%="; | ||
| 1460 | case TokenIdBitShiftRight: return ">>"; | ||
| 1515 | case TokenIdBitShiftRightEq: return ">>="; | 1461 | case TokenIdBitShiftRightEq: return ">>="; |
| 1516 | case TokenIdBitAndEq: return "&="; | ||
| 1517 | case TokenIdBitXorEq: return "^="; | 1462 | case TokenIdBitXorEq: return "^="; |
| 1518 | case TokenIdBitOrEq: return "|="; | 1463 | case TokenIdBoolAnd: return "&&"; |
| 1519 | case TokenIdBoolAndEq: return "&&="; | 1464 | case TokenIdBoolAndEq: return "&&="; |
| 1465 | case TokenIdBoolOr: return "||"; | ||
| 1520 | case TokenIdBoolOrEq: return "||="; | 1466 | case TokenIdBoolOrEq: return "||="; |
| 1521 | case TokenIdBang: return "!"; | 1467 | case TokenIdCharLiteral: return "CharLiteral"; |
| 1522 | case TokenIdTilde: return "~"; | ||
| 1523 | case TokenIdCmpEq: return "=="; | 1468 | case TokenIdCmpEq: return "=="; |
| 1524 | case TokenIdCmpNotEq: return "!="; | 1469 | case TokenIdCmpGreaterOrEq: return ">="; |
| 1525 | case TokenIdCmpLessThan: return "<"; | ||
| 1526 | case TokenIdCmpGreaterThan: return ">"; | 1470 | case TokenIdCmpGreaterThan: return ">"; |
| 1527 | case TokenIdCmpLessOrEq: return "<="; | 1471 | case TokenIdCmpLessOrEq: return "<="; |
| 1528 | case TokenIdCmpGreaterOrEq: return ">="; | 1472 | case TokenIdCmpLessThan: return "<"; |
| 1529 | case TokenIdBitShiftLeft: return "<<"; | 1473 | case TokenIdCmpNotEq: return "!="; |
| 1530 | case TokenIdBitShiftRight: return ">>"; | 1474 | case TokenIdColon: return ":"; |
| 1531 | case TokenIdSlash: return "/"; | 1475 | case TokenIdComma: return ","; |
| 1532 | case TokenIdPercent: return "%"; | 1476 | case TokenIdDash: return "-"; |
| 1533 | case TokenIdPercentPercent: return "%%"; | 1477 | case TokenIdDivEq: return "/="; |
| 1534 | case TokenIdDot: return "."; | 1478 | case TokenIdDot: return "."; |
| 1479 | case TokenIdDoubleQuestion: return "??"; | ||
| 1535 | case TokenIdEllipsis: return "..."; | 1480 | case TokenIdEllipsis: return "..."; |
| 1481 | case TokenIdEof: return "EOF"; | ||
| 1482 | case TokenIdEq: return "="; | ||
| 1483 | case TokenIdFatArrow: return "=>"; | ||
| 1484 | case TokenIdKeywordAsm: return "asm"; | ||
| 1485 | case TokenIdKeywordBreak: return "break"; | ||
| 1486 | case TokenIdKeywordColdCC: return "coldcc"; | ||
| 1487 | case TokenIdKeywordCompTime: return "comptime"; | ||
| 1488 | case TokenIdKeywordConst: return "const"; | ||
| 1489 | case TokenIdKeywordContinue: return "continue"; | ||
| 1490 | case TokenIdKeywordDefer: return "defer"; | ||
| 1491 | case TokenIdKeywordElse: return "else"; | ||
| 1492 | case TokenIdKeywordEnum: return "enum"; | ||
| 1493 | case TokenIdKeywordError: return "error"; | ||
| 1494 | case TokenIdKeywordExport: return "export"; | ||
| 1495 | case TokenIdKeywordExtern: return "extern"; | ||
| 1496 | case TokenIdKeywordFalse: return "false"; | ||
| 1497 | case TokenIdKeywordFn: return "fn"; | ||
| 1498 | case TokenIdKeywordFor: return "for"; | ||
| 1499 | case TokenIdKeywordGoto: return "goto"; | ||
| 1500 | case TokenIdKeywordIf: return "if"; | ||
| 1501 | case TokenIdKeywordInline: return "inline"; | ||
| 1502 | case TokenIdKeywordNakedCC: return "nakedcc"; | ||
| 1503 | case TokenIdKeywordNoAlias: return "noalias"; | ||
| 1504 | case TokenIdKeywordNull: return "null"; | ||
| 1505 | case TokenIdKeywordPub: return "pub"; | ||
| 1506 | case TokenIdKeywordReturn: return "return"; | ||
| 1507 | case TokenIdKeywordStruct: return "struct"; | ||
| 1508 | case TokenIdKeywordSwitch: return "switch"; | ||
| 1509 | case TokenIdKeywordThis: return "this"; | ||
| 1510 | case TokenIdKeywordTrue: return "true"; | ||
| 1511 | case TokenIdKeywordTry: return "try"; | ||
| 1512 | case TokenIdKeywordType: return "type"; | ||
| 1513 | case TokenIdKeywordUndefined: return "undefined"; | ||
| 1514 | case TokenIdKeywordUnion: return "union"; | ||
| 1515 | case TokenIdKeywordUse: return "use"; | ||
| 1516 | case TokenIdKeywordVar: return "var"; | ||
| 1517 | case TokenIdKeywordVolatile: return "volatile"; | ||
| 1518 | case TokenIdKeywordWhile: return "while"; | ||
| 1519 | case TokenIdLBrace: return "{"; | ||
| 1520 | case TokenIdLBracket: return "["; | ||
| 1521 | case TokenIdLParen: return "("; | ||
| 1536 | case TokenIdMaybe: return "?"; | 1522 | case TokenIdMaybe: return "?"; |
| 1537 | case TokenIdDoubleQuestion: return "??"; | ||
| 1538 | case TokenIdMaybeAssign: return "?="; | 1523 | case TokenIdMaybeAssign: return "?="; |
| 1539 | case TokenIdAtSign: return "@"; | 1524 | case TokenIdMinusEq: return "-="; |
| 1525 | case TokenIdMinusPercent: return "-%"; | ||
| 1526 | case TokenIdMinusPercentEq: return "-%="; | ||
| 1527 | case TokenIdModEq: return "%="; | ||
| 1528 | case TokenIdNumberLiteral: return "NumberLiteral"; | ||
| 1529 | case TokenIdNumberSign: return "#"; | ||
| 1530 | case TokenIdPercent: return "%"; | ||
| 1540 | case TokenIdPercentDot: return "%."; | 1531 | case TokenIdPercentDot: return "%."; |
| 1541 | case TokenIdTimesPercent: return "*%"; | 1532 | case TokenIdPercentPercent: return "%%"; |
| 1542 | case TokenIdTimesPercentEq: return "*%="; | 1533 | case TokenIdPlus: return "+"; |
| 1534 | case TokenIdPlusEq: return "+="; | ||
| 1543 | case TokenIdPlusPercent: return "+%"; | 1535 | case TokenIdPlusPercent: return "+%"; |
| 1544 | case TokenIdPlusPercentEq: return "+%="; | 1536 | case TokenIdPlusPercentEq: return "+%="; |
| 1545 | case TokenIdMinusPercent: return "-%"; | 1537 | case TokenIdPlusPlus: return "++"; |
| 1546 | case TokenIdMinusPercentEq: return "-%="; | 1538 | case TokenIdRBrace: return "}"; |
| 1547 | case TokenIdBitShiftLeftPercent: return "<<%"; | 1539 | case TokenIdRBracket: return "]"; |
| 1548 | case TokenIdBitShiftLeftPercentEq: return "<<%="; | 1540 | case TokenIdRParen: return ")"; |
| 1541 | case TokenIdSemicolon: return ";"; | ||
| 1542 | case TokenIdSlash: return "/"; | ||
| 1543 | case TokenIdStar: return "*"; | ||
| 1544 | case TokenIdStarStar: return "**"; | ||
| 1545 | case TokenIdStringLiteral: return "StringLiteral"; | ||
| 1546 | case TokenIdSymbol: return "Symbol"; | ||
| 1547 | case TokenIdTilde: return "~"; | ||
| 1548 | case TokenIdTimesEq: return "*="; | ||
| 1549 | case TokenIdTimesPercent: return "*%"; | ||
| 1550 | case TokenIdTimesPercentEq: return "*%="; | ||
| 1549 | } | 1551 | } |
| 1550 | return "(invalid token)"; | 1552 | return "(invalid token)"; |
| 1551 | } | 1553 | } |
src/tokenizer.hpp+1| ... | @@ -25,6 +25,7 @@ enum TokenId { | ... | @@ -25,6 +25,7 @@ enum TokenId { |
| 25 | TokenIdKeywordTrue, | 25 | TokenIdKeywordTrue, |
| 26 | TokenIdKeywordFalse, | 26 | TokenIdKeywordFalse, |
| 27 | TokenIdKeywordIf, | 27 | TokenIdKeywordIf, |
| 28 | TokenIdKeywordTry, | ||
| 28 | TokenIdKeywordElse, | 29 | TokenIdKeywordElse, |
| 29 | TokenIdKeywordGoto, | 30 | TokenIdKeywordGoto, |
| 30 | TokenIdKeywordAsm, | 31 | TokenIdKeywordAsm, |
std/debug.zig+2-2| ... | @@ -9,8 +9,8 @@ error MissingDebugInfo; | ... | @@ -9,8 +9,8 @@ error MissingDebugInfo; |
| 9 | error InvalidDebugInfo; | 9 | error InvalidDebugInfo; |
| 10 | error UnsupportedDebugInfo; | 10 | error UnsupportedDebugInfo; |
| 11 | 11 | ||
| 12 | pub fn assert(b: bool) { | 12 | pub fn assert(ok: bool) { |
| 13 | if (!b) @unreachable() | 13 | if (!ok) @unreachable() |
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | pub fn printStackTrace() -> %void { | 16 | pub fn printStackTrace() -> %void { |
test/cases/try.zig created+33| ... | @@ -0,0 +1,33 @@ | ||
| 1 | const assert = @import("std").debug.assert; | ||
| 2 | |||
| 3 | fn tryOnErrorUnion() { | ||
| 4 | @setFnTest(this); | ||
| 5 | |||
| 6 | const x = try (const val = returnsTen()) { | ||
| 7 | val + 1 | ||
| 8 | } else |err| switch (err) { | ||
| 9 | error.ItBroke, error.NoMem => 1, | ||
| 10 | error.CrappedOut => i32(2), | ||
| 11 | }; | ||
| 12 | assert(x == 11); | ||
| 13 | } | ||
| 14 | |||
| 15 | fn tryOnErrorUnionComptime() { | ||
| 16 | @setFnTest(this); | ||
| 17 | |||
| 18 | comptime { | ||
| 19 | const x = try (const val = returnsTen()) { | ||
| 20 | val + 1 | ||
| 21 | } else |err| switch (err) { | ||
| 22 | error.ItBroke, error.NoMem => 1, | ||
| 23 | error.CrappedOut => i32(2), | ||
| 24 | }; | ||
| 25 | assert(x == 11); | ||
| 26 | } | ||
| 27 | } | ||
| 28 | error ItBroke; | ||
| 29 | error NoMem; | ||
| 30 | error CrappedOut; | ||
| 31 | fn returnsTen() -> %i32 { | ||
| 32 | 10 | ||
| 33 | } | ||
test/self_hosted.zig+1| ... | @@ -28,6 +28,7 @@ const test_switch = @import("cases/switch.zig"); | ... | @@ -28,6 +28,7 @@ const test_switch = @import("cases/switch.zig"); |
| 28 | const test_switch_prong_err_enum = @import("cases/switch_prong_err_enum.zig"); | 28 | const test_switch_prong_err_enum = @import("cases/switch_prong_err_enum.zig"); |
| 29 | const test_switch_prong_implicit_cast = @import("cases/switch_prong_implicit_cast.zig"); | 29 | const test_switch_prong_implicit_cast = @import("cases/switch_prong_implicit_cast.zig"); |
| 30 | const test_this = @import("cases/this.zig"); | 30 | const test_this = @import("cases/this.zig"); |
| 31 | const test_try = @import("cases/try.zig"); | ||
| 31 | const test_undefined = @import("cases/undefined.zig"); | 32 | const test_undefined = @import("cases/undefined.zig"); |
| 32 | const test_var_args = @import("cases/var_args.zig"); | 33 | const test_var_args = @import("cases/var_args.zig"); |
| 33 | const test_while = @import("cases/while.zig"); | 34 | const test_while = @import("cases/while.zig"); |