| author | |
| committer | |
| log | 6db6609df84bedafebc709bc5865e7f18c862cc6 |
| tree | ecaf2701dd8b0e9a6022b1592e212d748e287b85 |
| parent | bcb18338cd15dfd7a4e3e80e74d1fe395870fa48 |
See #2310 files changed, 247 insertions(+), 51 deletions(-)
doc/langref.md+7-3| ... | @@ -59,9 +59,13 @@ AsmInputItem : "[" "Symbol" "]" "String" "(" Expression ")" | ... | @@ -59,9 +59,13 @@ AsmInputItem : "[" "Symbol" "]" "String" "(" Expression ")" |
| 59 | 59 | ||
| 60 | AsmClobbers: ":" list("String", ",") | 60 | AsmClobbers: ":" list("String", ",") |
| 61 | 61 | ||
| 62 | UnwrapMaybeExpression : BoolOrExpression "??" BoolOrExpression | BoolOrExpression | 62 | UnwrapExpression : BoolOrExpression (UnwrapMaybe | UnwrapError) | BoolOrExpression |
| 63 | 63 | ||
| 64 | AssignmentExpression : UnwrapMaybeExpression AssignmentOperator UnwrapMaybeExpression | UnwrapMaybeExpression | 64 | UnwrapMaybe : "??" BoolOrExpression |
| 65 | |||
| 66 | UnwrapError : "%%" option("|" "Symbol" "|") BoolOrExpression | ||
| 67 | |||
| 68 | AssignmentExpression : UnwrapExpression AssignmentOperator UnwrapExpression | UnwrapExpression | ||
| 65 | 69 | ||
| 66 | AssignmentOperator : "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|=" | "&&=" | "||=" | 70 | AssignmentOperator : "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|=" | "&&=" | "||=" |
| 67 | 71 | ||
| ... | @@ -161,7 +165,7 @@ x{} | ... | @@ -161,7 +165,7 @@ x{} |
| 161 | == != < > <= >= | 165 | == != < > <= >= |
| 162 | && | 166 | && |
| 163 | || | 167 | || |
| 164 | ?? | 168 | ?? %% |
| 165 | = *= /= %= += -= <<= >>= &= ^= |= &&= ||= | 169 | = *= /= %= += -= <<= >>= &= ^= |= &&= ||= |
| 166 | ``` | 170 | ``` |
| 167 | 171 |
example/cat/main.zig-2| ... | @@ -4,9 +4,7 @@ import "std.zig"; | ... | @@ -4,9 +4,7 @@ import "std.zig"; |
| 4 | 4 | ||
| 5 | // Things to do to make this work: | 5 | // Things to do to make this work: |
| 6 | // * var args printing | 6 | // * var args printing |
| 7 | // * update std API | ||
| 8 | // * defer | 7 | // * defer |
| 9 | // * %return | ||
| 10 | // * %% binary operator | 8 | // * %% binary operator |
| 11 | // * %% prefix operator | 9 | // * %% prefix operator |
| 12 | // * cast err type to string | 10 | // * cast err type to string |
example/guess_number/main.zig+10-7| ... | @@ -8,7 +8,7 @@ pub fn main(args: [][]u8) %void => { | ... | @@ -8,7 +8,7 @@ pub fn main(args: [][]u8) %void => { |
| 8 | 8 | ||
| 9 | var seed : u32; | 9 | var seed : u32; |
| 10 | const seed_bytes = (&u8)(&seed)[0...4]; | 10 | const seed_bytes = (&u8)(&seed)[0...4]; |
| 11 | os_get_random_bytes(seed_bytes); | 11 | os_get_random_bytes(seed_bytes) %% unreachable{}; |
| 12 | 12 | ||
| 13 | var rand = rand_new(seed); | 13 | var rand = rand_new(seed); |
| 14 | 14 | ||
| ... | @@ -18,13 +18,16 @@ pub fn main(args: [][]u8) %void => { | ... | @@ -18,13 +18,16 @@ pub fn main(args: [][]u8) %void => { |
| 18 | stderr.print_str("\nGuess a number between 1 and 100: "); | 18 | stderr.print_str("\nGuess a number between 1 and 100: "); |
| 19 | var line_buf : [20]u8; | 19 | var line_buf : [20]u8; |
| 20 | 20 | ||
| 21 | // TODO print error message instead of returning | 21 | const line_len = stdin.read(line_buf) %% |err| { |
| 22 | const line_len = %return stdin.readline(line_buf); | 22 | stderr.print_str("Unable to read from stdin.\n"); |
| 23 | return err; | ||
| 24 | }; | ||
| 23 | 25 | ||
| 24 | var guess : u64; | 26 | const guess = parse_u64(line_buf[0...line_len - 1], 10) %% { |
| 25 | if (parse_u64(line_buf[0...line_len - 1], 10, &guess)) { | 27 | stderr.print_str("Invalid number.\n"); |
| 26 | stderr.print_str("Invalid number format.\n"); | 28 | continue; |
| 27 | } else if (guess > answer) { | 29 | }; |
| 30 | if (guess > answer) { | ||
| 28 | stderr.print_str("Guess lower.\n"); | 31 | stderr.print_str("Guess lower.\n"); |
| 29 | } else if (guess < answer) { | 32 | } else if (guess < answer) { |
| 30 | stderr.print_str("Guess higher.\n"); | 33 | stderr.print_str("Guess higher.\n"); |
src/all_types.hpp+12| ... | @@ -130,6 +130,7 @@ enum NodeType { | ... | @@ -130,6 +130,7 @@ enum NodeType { |
| 130 | NodeTypeVariableDeclaration, | 130 | NodeTypeVariableDeclaration, |
| 131 | NodeTypeErrorValueDecl, | 131 | NodeTypeErrorValueDecl, |
| 132 | NodeTypeBinOpExpr, | 132 | NodeTypeBinOpExpr, |
| 133 | NodeTypeUnwrapErrorExpr, | ||
| 133 | NodeTypeNumberLiteral, | 134 | NodeTypeNumberLiteral, |
| 134 | NodeTypeStringLiteral, | 135 | NodeTypeStringLiteral, |
| 135 | NodeTypeCharLiteral, | 136 | NodeTypeCharLiteral, |
| ... | @@ -310,6 +311,16 @@ struct AstNodeBinOpExpr { | ... | @@ -310,6 +311,16 @@ struct AstNodeBinOpExpr { |
| 310 | Expr resolved_expr; | 311 | Expr resolved_expr; |
| 311 | }; | 312 | }; |
| 312 | 313 | ||
| 314 | struct AstNodeUnwrapErrorExpr { | ||
| 315 | AstNode *op1; | ||
| 316 | AstNode *symbol; // can be null | ||
| 317 | AstNode *op2; | ||
| 318 | |||
| 319 | // populated by semantic analyzer: | ||
| 320 | Expr resolved_expr; | ||
| 321 | VariableTableEntry *var; | ||
| 322 | }; | ||
| 323 | |||
| 313 | enum CastOp { | 324 | enum CastOp { |
| 314 | CastOpNoCast, // signifies the function call expression is not a cast | 325 | CastOpNoCast, // signifies the function call expression is not a cast |
| 315 | CastOpNoop, // fn call expr is a cast, but does nothing | 326 | CastOpNoop, // fn call expr is a cast, but does nothing |
| ... | @@ -684,6 +695,7 @@ struct AstNode { | ... | @@ -684,6 +695,7 @@ struct AstNode { |
| 684 | AstNodeVariableDeclaration variable_declaration; | 695 | AstNodeVariableDeclaration variable_declaration; |
| 685 | AstNodeErrorValueDecl error_value_decl; | 696 | AstNodeErrorValueDecl error_value_decl; |
| 686 | AstNodeBinOpExpr bin_op_expr; | 697 | AstNodeBinOpExpr bin_op_expr; |
| 698 | AstNodeUnwrapErrorExpr unwrap_err_expr; | ||
| 687 | AstNodeExternBlock extern_block; | 699 | AstNodeExternBlock extern_block; |
| 688 | AstNodeDirective directive; | 700 | AstNodeDirective directive; |
| 689 | AstNodePrefixOpExpr prefix_op_expr; | 701 | AstNodePrefixOpExpr prefix_op_expr; |
src/analyze.cpp+73-7| ... | @@ -28,6 +28,8 @@ static AstNode *first_executing_node(AstNode *node) { | ... | @@ -28,6 +28,8 @@ static AstNode *first_executing_node(AstNode *node) { |
| 28 | return first_executing_node(node->data.fn_call_expr.fn_ref_expr); | 28 | return first_executing_node(node->data.fn_call_expr.fn_ref_expr); |
| 29 | case NodeTypeBinOpExpr: | 29 | case NodeTypeBinOpExpr: |
| 30 | return first_executing_node(node->data.bin_op_expr.op1); | 30 | return first_executing_node(node->data.bin_op_expr.op1); |
| 31 | case NodeTypeUnwrapErrorExpr: | ||
| 32 | return first_executing_node(node->data.unwrap_err_expr.op1); | ||
| 31 | case NodeTypeArrayAccessExpr: | 33 | case NodeTypeArrayAccessExpr: |
| 32 | return first_executing_node(node->data.array_access_expr.array_ref_expr); | 34 | return first_executing_node(node->data.array_access_expr.array_ref_expr); |
| 33 | case NodeTypeSliceExpr: | 35 | case NodeTypeSliceExpr: |
| ... | @@ -1076,6 +1078,7 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode | ... | @@ -1076,6 +1078,7 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode |
| 1076 | case NodeTypeRoot: | 1078 | case NodeTypeRoot: |
| 1077 | case NodeTypeBlock: | 1079 | case NodeTypeBlock: |
| 1078 | case NodeTypeBinOpExpr: | 1080 | case NodeTypeBinOpExpr: |
| 1081 | case NodeTypeUnwrapErrorExpr: | ||
| 1079 | case NodeTypeFnCallExpr: | 1082 | case NodeTypeFnCallExpr: |
| 1080 | case NodeTypeArrayAccessExpr: | 1083 | case NodeTypeArrayAccessExpr: |
| 1081 | case NodeTypeSliceExpr: | 1084 | case NodeTypeSliceExpr: |
| ... | @@ -2502,6 +2505,38 @@ static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, Block | ... | @@ -2502,6 +2505,38 @@ static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, Block |
| 2502 | return variable_entry; | 2505 | return variable_entry; |
| 2503 | } | 2506 | } |
| 2504 | 2507 | ||
| 2508 | static TypeTableEntry *analyze_unwrap_error_expr(CodeGen *g, ImportTableEntry *import, | ||
| 2509 | BlockContext *parent_context, TypeTableEntry *expected_type, AstNode *node) | ||
| 2510 | { | ||
| 2511 | AstNode *op1 = node->data.unwrap_err_expr.op1; | ||
| 2512 | AstNode *op2 = node->data.unwrap_err_expr.op2; | ||
| 2513 | AstNode *var_node = node->data.unwrap_err_expr.symbol; | ||
| 2514 | |||
| 2515 | TypeTableEntry *lhs_type = analyze_expression(g, import, parent_context, nullptr, op1); | ||
| 2516 | if (lhs_type->id == TypeTableEntryIdInvalid) { | ||
| 2517 | return lhs_type; | ||
| 2518 | } else if (lhs_type->id == TypeTableEntryIdErrorUnion) { | ||
| 2519 | TypeTableEntry *child_type = lhs_type->data.error.child_type; | ||
| 2520 | BlockContext *child_context; | ||
| 2521 | if (var_node) { | ||
| 2522 | child_context = new_block_context(node, parent_context); | ||
| 2523 | Buf *var_name = &var_node->data.symbol_expr.symbol; | ||
| 2524 | node->data.unwrap_err_expr.var = add_local_var(g, var_node, child_context, var_name, | ||
| 2525 | g->builtin_types.entry_pure_error, true); | ||
| 2526 | } else { | ||
| 2527 | child_context = parent_context; | ||
| 2528 | } | ||
| 2529 | |||
| 2530 | analyze_expression(g, import, child_context, child_type, op2); | ||
| 2531 | return child_type; | ||
| 2532 | } else { | ||
| 2533 | add_node_error(g, op1, | ||
| 2534 | buf_sprintf("expected error type, got '%s'", buf_ptr(&lhs_type->name))); | ||
| 2535 | return g->builtin_types.entry_invalid; | ||
| 2536 | } | ||
| 2537 | } | ||
| 2538 | |||
| 2539 | |||
| 2505 | static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import, | 2540 | static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import, |
| 2506 | BlockContext *context, AstNode *source_node, | 2541 | BlockContext *context, AstNode *source_node, |
| 2507 | AstNodeVariableDeclaration *variable_declaration, | 2542 | AstNodeVariableDeclaration *variable_declaration, |
| ... | @@ -3845,7 +3880,9 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, | ... | @@ -3845,7 +3880,9 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 3845 | case NodeTypeBinOpExpr: | 3880 | case NodeTypeBinOpExpr: |
| 3846 | return_type = analyze_bin_op_expr(g, import, context, expected_type, node); | 3881 | return_type = analyze_bin_op_expr(g, import, context, expected_type, node); |
| 3847 | break; | 3882 | break; |
| 3848 | 3883 | case NodeTypeUnwrapErrorExpr: | |
| 3884 | return_type = analyze_unwrap_error_expr(g, import, context, expected_type, node); | ||
| 3885 | break; | ||
| 3849 | case NodeTypeFnCallExpr: | 3886 | case NodeTypeFnCallExpr: |
| 3850 | return_type = analyze_fn_call_expr(g, import, context, expected_type, node); | 3887 | return_type = analyze_fn_call_expr(g, import, context, expected_type, node); |
| 3851 | break; | 3888 | break; |
| ... | @@ -4035,6 +4072,7 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode | ... | @@ -4035,6 +4072,7 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode |
| 4035 | case NodeTypeRoot: | 4072 | case NodeTypeRoot: |
| 4036 | case NodeTypeBlock: | 4073 | case NodeTypeBlock: |
| 4037 | case NodeTypeBinOpExpr: | 4074 | case NodeTypeBinOpExpr: |
| 4075 | case NodeTypeUnwrapErrorExpr: | ||
| 4038 | case NodeTypeFnCallExpr: | 4076 | case NodeTypeFnCallExpr: |
| 4039 | case NodeTypeArrayAccessExpr: | 4077 | case NodeTypeArrayAccessExpr: |
| 4040 | case NodeTypeSliceExpr: | 4078 | case NodeTypeSliceExpr: |
| ... | @@ -4101,6 +4139,10 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode | ... | @@ -4101,6 +4139,10 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode |
| 4101 | collect_expr_decl_deps(g, import, node->data.bin_op_expr.op1, decl_node); | 4139 | collect_expr_decl_deps(g, import, node->data.bin_op_expr.op1, decl_node); |
| 4102 | collect_expr_decl_deps(g, import, node->data.bin_op_expr.op2, decl_node); | 4140 | collect_expr_decl_deps(g, import, node->data.bin_op_expr.op2, decl_node); |
| 4103 | break; | 4141 | break; |
| 4142 | case NodeTypeUnwrapErrorExpr: | ||
| 4143 | collect_expr_decl_deps(g, import, node->data.unwrap_err_expr.op1, decl_node); | ||
| 4144 | collect_expr_decl_deps(g, import, node->data.unwrap_err_expr.op2, decl_node); | ||
| 4145 | break; | ||
| 4104 | case NodeTypeReturnExpr: | 4146 | case NodeTypeReturnExpr: |
| 4105 | collect_expr_decl_deps(g, import, node->data.return_expr.expr, decl_node); | 4147 | collect_expr_decl_deps(g, import, node->data.return_expr.expr, decl_node); |
| 4106 | break; | 4148 | break; |
| ... | @@ -4388,6 +4430,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast | ... | @@ -4388,6 +4430,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast |
| 4388 | case NodeTypeRoot: | 4430 | case NodeTypeRoot: |
| 4389 | case NodeTypeBlock: | 4431 | case NodeTypeBlock: |
| 4390 | case NodeTypeBinOpExpr: | 4432 | case NodeTypeBinOpExpr: |
| 4433 | case NodeTypeUnwrapErrorExpr: | ||
| 4391 | case NodeTypeFnCallExpr: | 4434 | case NodeTypeFnCallExpr: |
| 4392 | case NodeTypeArrayAccessExpr: | 4435 | case NodeTypeArrayAccessExpr: |
| 4393 | case NodeTypeSliceExpr: | 4436 | case NodeTypeSliceExpr: |
| ... | @@ -4582,6 +4625,8 @@ Expr *get_resolved_expr(AstNode *node) { | ... | @@ -4582,6 +4625,8 @@ Expr *get_resolved_expr(AstNode *node) { |
| 4582 | return &node->data.return_expr.resolved_expr; | 4625 | return &node->data.return_expr.resolved_expr; |
| 4583 | case NodeTypeBinOpExpr: | 4626 | case NodeTypeBinOpExpr: |
| 4584 | return &node->data.bin_op_expr.resolved_expr; | 4627 | return &node->data.bin_op_expr.resolved_expr; |
| 4628 | case NodeTypeUnwrapErrorExpr: | ||
| 4629 | return &node->data.unwrap_err_expr.resolved_expr; | ||
| 4585 | case NodeTypePrefixOpExpr: | 4630 | case NodeTypePrefixOpExpr: |
| 4586 | return &node->data.prefix_op_expr.resolved_expr; | 4631 | return &node->data.prefix_op_expr.resolved_expr; |
| 4587 | case NodeTypeFnCallExpr: | 4632 | case NodeTypeFnCallExpr: |
| ... | @@ -4669,6 +4714,7 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) { | ... | @@ -4669,6 +4714,7 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) { |
| 4669 | case NodeTypeNumberLiteral: | 4714 | case NodeTypeNumberLiteral: |
| 4670 | case NodeTypeReturnExpr: | 4715 | case NodeTypeReturnExpr: |
| 4671 | case NodeTypeBinOpExpr: | 4716 | case NodeTypeBinOpExpr: |
| 4717 | case NodeTypeUnwrapErrorExpr: | ||
| 4672 | case NodeTypePrefixOpExpr: | 4718 | case NodeTypePrefixOpExpr: |
| 4673 | case NodeTypeFnCallExpr: | 4719 | case NodeTypeFnCallExpr: |
| 4674 | case NodeTypeArrayAccessExpr: | 4720 | case NodeTypeArrayAccessExpr: |
| ... | @@ -4747,10 +4793,30 @@ TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, int size_in_bits) { | ... | @@ -4747,10 +4793,30 @@ TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, int size_in_bits) { |
| 4747 | } | 4793 | } |
| 4748 | 4794 | ||
| 4749 | bool handle_is_ptr(TypeTableEntry *type_entry) { | 4795 | bool handle_is_ptr(TypeTableEntry *type_entry) { |
| 4750 | return type_entry->id == TypeTableEntryIdStruct || | 4796 | switch (type_entry->id) { |
| 4751 | (type_entry->id == TypeTableEntryIdEnum && type_entry->data.enumeration.gen_field_count != 0) || | 4797 | case TypeTableEntryIdInvalid: |
| 4752 | type_entry->id == TypeTableEntryIdMaybe || | 4798 | case TypeTableEntryIdMetaType: |
| 4753 | type_entry->id == TypeTableEntryIdArray || | 4799 | case TypeTableEntryIdNumLitFloat: |
| 4754 | (type_entry->id == TypeTableEntryIdErrorUnion && type_entry->data.error.child_type->size_in_bits > 0); | 4800 | case TypeTableEntryIdNumLitInt: |
| 4801 | case TypeTableEntryIdUndefLit: | ||
| 4802 | zig_unreachable(); | ||
| 4803 | case TypeTableEntryIdUnreachable: | ||
| 4804 | case TypeTableEntryIdVoid: | ||
| 4805 | case TypeTableEntryIdBool: | ||
| 4806 | case TypeTableEntryIdInt: | ||
| 4807 | case TypeTableEntryIdFloat: | ||
| 4808 | case TypeTableEntryIdPointer: | ||
| 4809 | case TypeTableEntryIdPureError: | ||
| 4810 | case TypeTableEntryIdFn: | ||
| 4811 | return false; | ||
| 4812 | case TypeTableEntryIdArray: | ||
| 4813 | case TypeTableEntryIdStruct: | ||
| 4814 | case TypeTableEntryIdMaybe: | ||
| 4815 | return true; | ||
| 4816 | case TypeTableEntryIdErrorUnion: | ||
| 4817 | return type_entry->data.error.child_type->size_in_bits > 0; | ||
| 4818 | case TypeTableEntryIdEnum: | ||
| 4819 | return type_entry->data.enumeration.gen_field_count != 0; | ||
| 4820 | } | ||
| 4821 | zig_unreachable(); | ||
| 4755 | } | 4822 | } |
| 4756 |
src/codegen.cpp+74| ... | @@ -1255,6 +1255,78 @@ static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { | ... | @@ -1255,6 +1255,78 @@ static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { |
| 1255 | zig_unreachable(); | 1255 | zig_unreachable(); |
| 1256 | } | 1256 | } |
| 1257 | 1257 | ||
| 1258 | static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) { | ||
| 1259 | assert(node->type == NodeTypeUnwrapErrorExpr); | ||
| 1260 | |||
| 1261 | AstNode *op1 = node->data.unwrap_err_expr.op1; | ||
| 1262 | AstNode *op2 = node->data.unwrap_err_expr.op2; | ||
| 1263 | VariableTableEntry *var = node->data.unwrap_err_expr.var; | ||
| 1264 | |||
| 1265 | LLVMValueRef expr_val = gen_expr(g, op1); | ||
| 1266 | TypeTableEntry *expr_type = get_expr_type(op1); | ||
| 1267 | TypeTableEntry *op2_type = get_expr_type(op2); | ||
| 1268 | assert(expr_type->id == TypeTableEntryIdErrorUnion); | ||
| 1269 | TypeTableEntry *child_type = expr_type->data.error.child_type; | ||
| 1270 | LLVMValueRef err_val; | ||
| 1271 | add_debug_source_node(g, node); | ||
| 1272 | if (handle_is_ptr(expr_type)) { | ||
| 1273 | LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 0, ""); | ||
| 1274 | err_val = LLVMBuildLoad(g->builder, err_val_ptr, ""); | ||
| 1275 | } else { | ||
| 1276 | err_val = expr_val; | ||
| 1277 | } | ||
| 1278 | LLVMValueRef zero = LLVMConstNull(g->err_tag_type->type_ref); | ||
| 1279 | LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, ""); | ||
| 1280 | |||
| 1281 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrOk"); | ||
| 1282 | LLVMBasicBlockRef err_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrError"); | ||
| 1283 | LLVMBasicBlockRef end_block; | ||
| 1284 | bool err_reachable = op2_type->id != TypeTableEntryIdUnreachable; | ||
| 1285 | bool have_end_block = err_reachable && (child_type->size_in_bits > 0); | ||
| 1286 | if (have_end_block) { | ||
| 1287 | end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "UnwrapErrEnd"); | ||
| 1288 | } | ||
| 1289 | |||
| 1290 | LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block); | ||
| 1291 | |||
| 1292 | LLVMPositionBuilderAtEnd(g->builder, err_block); | ||
| 1293 | if (var) { | ||
| 1294 | LLVMBuildStore(g->builder, err_val, var->value_ref); | ||
| 1295 | } | ||
| 1296 | LLVMValueRef err_result = gen_expr(g, op2); | ||
| 1297 | add_debug_source_node(g, node); | ||
| 1298 | if (have_end_block) { | ||
| 1299 | LLVMBuildBr(g->builder, end_block); | ||
| 1300 | } else if (err_reachable) { | ||
| 1301 | LLVMBuildBr(g->builder, ok_block); | ||
| 1302 | } | ||
| 1303 | |||
| 1304 | LLVMPositionBuilderAtEnd(g->builder, ok_block); | ||
| 1305 | if (child_type->size_in_bits == 0) { | ||
| 1306 | return nullptr; | ||
| 1307 | } | ||
| 1308 | LLVMValueRef child_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 1, ""); | ||
| 1309 | LLVMValueRef child_val; | ||
| 1310 | if (handle_is_ptr(child_type)) { | ||
| 1311 | child_val = child_val_ptr; | ||
| 1312 | } else { | ||
| 1313 | child_val = LLVMBuildLoad(g->builder, child_val_ptr, ""); | ||
| 1314 | } | ||
| 1315 | |||
| 1316 | if (!have_end_block) { | ||
| 1317 | return child_val; | ||
| 1318 | } | ||
| 1319 | |||
| 1320 | LLVMBuildBr(g->builder, end_block); | ||
| 1321 | |||
| 1322 | LLVMPositionBuilderAtEnd(g->builder, end_block); | ||
| 1323 | LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(err_result), ""); | ||
| 1324 | LLVMValueRef incoming_values[2] = {child_val, err_result}; | ||
| 1325 | LLVMBasicBlockRef incoming_blocks[2] = {ok_block, err_block}; | ||
| 1326 | LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2); | ||
| 1327 | return phi; | ||
| 1328 | } | ||
| 1329 | |||
| 1258 | static LLVMValueRef gen_return(CodeGen *g, AstNode *source_node, LLVMValueRef value) { | 1330 | static LLVMValueRef gen_return(CodeGen *g, AstNode *source_node, LLVMValueRef value) { |
| 1259 | TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.src_return_type; | 1331 | TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.src_return_type; |
| 1260 | if (handle_is_ptr(return_type)) { | 1332 | if (handle_is_ptr(return_type)) { |
| ... | @@ -2047,6 +2119,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { | ... | @@ -2047,6 +2119,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { |
| 2047 | switch (node->type) { | 2119 | switch (node->type) { |
| 2048 | case NodeTypeBinOpExpr: | 2120 | case NodeTypeBinOpExpr: |
| 2049 | return gen_bin_op_expr(g, node); | 2121 | return gen_bin_op_expr(g, node); |
| 2122 | case NodeTypeUnwrapErrorExpr: | ||
| 2123 | return gen_unwrap_err_expr(g, node); | ||
| 2050 | case NodeTypeReturnExpr: | 2124 | case NodeTypeReturnExpr: |
| 2051 | return gen_return_expr(g, node); | 2125 | return gen_return_expr(g, node); |
| 2052 | case NodeTypeVariableDeclaration: | 2126 | case NodeTypeVariableDeclaration: |
src/parser.cpp+55-23| ... | @@ -95,6 +95,8 @@ const char *node_type_str(NodeType node_type) { | ... | @@ -95,6 +95,8 @@ const char *node_type_str(NodeType node_type) { |
| 95 | return "Block"; | 95 | return "Block"; |
| 96 | case NodeTypeBinOpExpr: | 96 | case NodeTypeBinOpExpr: |
| 97 | return "BinOpExpr"; | 97 | return "BinOpExpr"; |
| 98 | case NodeTypeUnwrapErrorExpr: | ||
| 99 | return "UnwrapErrorExpr"; | ||
| 98 | case NodeTypeFnCallExpr: | 100 | case NodeTypeFnCallExpr: |
| 99 | return "FnCallExpr"; | 101 | return "FnCallExpr"; |
| 100 | case NodeTypeArrayAccessExpr: | 102 | case NodeTypeArrayAccessExpr: |
| ... | @@ -273,6 +275,14 @@ void ast_print(AstNode *node, int indent) { | ... | @@ -273,6 +275,14 @@ void ast_print(AstNode *node, int indent) { |
| 273 | ast_print(node->data.bin_op_expr.op1, indent + 2); | 275 | ast_print(node->data.bin_op_expr.op1, indent + 2); |
| 274 | ast_print(node->data.bin_op_expr.op2, indent + 2); | 276 | ast_print(node->data.bin_op_expr.op2, indent + 2); |
| 275 | break; | 277 | break; |
| 278 | case NodeTypeUnwrapErrorExpr: | ||
| 279 | fprintf(stderr, "%s\n", node_type_str(node->type)); | ||
| 280 | ast_print(node->data.unwrap_err_expr.op1, indent + 2); | ||
| 281 | if (node->data.unwrap_err_expr.symbol) { | ||
| 282 | ast_print(node->data.unwrap_err_expr.symbol, indent + 2); | ||
| 283 | } | ||
| 284 | ast_print(node->data.unwrap_err_expr.op2, indent + 2); | ||
| 285 | break; | ||
| 276 | case NodeTypeFnCallExpr: | 286 | case NodeTypeFnCallExpr: |
| 277 | fprintf(stderr, "%s\n", node_type_str(node->type)); | 287 | fprintf(stderr, "%s\n", node_type_str(node->type)); |
| 278 | ast_print(node->data.fn_call_expr.fn_ref_expr, indent + 2); | 288 | ast_print(node->data.fn_call_expr.fn_ref_expr, indent + 2); |
| ... | @@ -964,7 +974,7 @@ static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool ma | ... | @@ -964,7 +974,7 @@ static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool ma |
| 964 | static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory); | 974 | static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory); |
| 965 | static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory); | 975 | static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory); |
| 966 | static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool mandatory); | 976 | static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool mandatory); |
| 967 | static AstNode *ast_parse_unwrap_maybe_expr(ParseContext *pc, int *token_index, bool mandatory); | 977 | static AstNode *ast_parse_unwrap_expr(ParseContext *pc, int *token_index, bool mandatory); |
| 968 | static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, bool mandatory); | 978 | static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, bool mandatory); |
| 969 | 979 | ||
| 970 | static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) { | 980 | static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) { |
| ... | @@ -1032,7 +1042,7 @@ static void ast_parse_directives(ParseContext *pc, int *token_index, | ... | @@ -1032,7 +1042,7 @@ static void ast_parse_directives(ParseContext *pc, int *token_index, |
| 1032 | } | 1042 | } |
| 1033 | 1043 | ||
| 1034 | /* | 1044 | /* |
| 1035 | ParamDecl : option(token(NoAlias)) token(Symbol) token(Colon) UnwrapMaybeExpression | token(Ellipsis) | 1045 | ParamDecl : option("noalias") "Symbol" ":" PrefixOpExpression | "..." |
| 1036 | */ | 1046 | */ |
| 1037 | static AstNode *ast_parse_param_decl(ParseContext *pc, int *token_index) { | 1047 | static AstNode *ast_parse_param_decl(ParseContext *pc, int *token_index) { |
| 1038 | Token *first_token = &pc->tokens->at(*token_index); | 1048 | Token *first_token = &pc->tokens->at(*token_index); |
| ... | @@ -1154,7 +1164,7 @@ static AstNode *ast_parse_grouped_expr(ParseContext *pc, int *token_index, bool | ... | @@ -1154,7 +1164,7 @@ static AstNode *ast_parse_grouped_expr(ParseContext *pc, int *token_index, bool |
| 1154 | } | 1164 | } |
| 1155 | 1165 | ||
| 1156 | /* | 1166 | /* |
| 1157 | ArrayType : token(LBracket) option(Expression) token(RBracket) option(token(Const)) UnwrapMaybeExpression | 1167 | ArrayType : "[" option(Expression) "]" option("const") PrefixOpExpression |
| 1158 | */ | 1168 | */ |
| 1159 | static AstNode *ast_parse_array_type_expr(ParseContext *pc, int *token_index, bool mandatory) { | 1169 | static AstNode *ast_parse_array_type_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| 1160 | Token *l_bracket = &pc->tokens->at(*token_index); | 1170 | Token *l_bracket = &pc->tokens->at(*token_index); |
| ... | @@ -1207,7 +1217,7 @@ static void ast_parse_asm_input_item(ParseContext *pc, int *token_index, AstNode | ... | @@ -1207,7 +1217,7 @@ static void ast_parse_asm_input_item(ParseContext *pc, int *token_index, AstNode |
| 1207 | } | 1217 | } |
| 1208 | 1218 | ||
| 1209 | /* | 1219 | /* |
| 1210 | AsmOutputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) (token(Symbol) | token(Arrow) UnwrapMaybeExpression token(RParen) | 1220 | AsmOutputItem : "[" "Symbol" "]" "String" "(" ("Symbol" | "->" PrefixOpExpression) ")" |
| 1211 | */ | 1221 | */ |
| 1212 | static void ast_parse_asm_output_item(ParseContext *pc, int *token_index, AstNode *node) { | 1222 | static void ast_parse_asm_output_item(ParseContext *pc, int *token_index, AstNode *node) { |
| 1213 | ast_eat_token(pc, token_index, TokenIdLBracket); | 1223 | ast_eat_token(pc, token_index, TokenIdLBracket); |
| ... | @@ -2132,7 +2142,7 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m | ... | @@ -2132,7 +2142,7 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m |
| 2132 | } | 2142 | } |
| 2133 | 2143 | ||
| 2134 | /* | 2144 | /* |
| 2135 | VariableDeclaration : option(FnVisibleMod) (token(Var) | token(Const)) token(Symbol) (token(Eq) Expression | token(Colon) UnwrapMaybeExpression option(token(Eq) Expression)) | 2145 | VariableDeclaration : option(FnVisibleMod) ("var" | "const") "Symbol" ("=" Expression | ":" PrefixOpExpression option("=" Expression)) |
| 2136 | */ | 2146 | */ |
| 2137 | static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token_index, bool mandatory) { | 2147 | static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| 2138 | Token *first_token = &pc->tokens->at(*token_index); | 2148 | Token *first_token = &pc->tokens->at(*token_index); |
| ... | @@ -2454,38 +2464,55 @@ static BinOpType ast_parse_ass_op(ParseContext *pc, int *token_index, bool manda | ... | @@ -2454,38 +2464,55 @@ static BinOpType ast_parse_ass_op(ParseContext *pc, int *token_index, bool manda |
| 2454 | } | 2464 | } |
| 2455 | 2465 | ||
| 2456 | /* | 2466 | /* |
| 2457 | UnwrapMaybeExpression : BoolOrExpression token(DoubleQuestion) BoolOrExpression | BoolOrExpression | 2467 | UnwrapExpression : BoolOrExpression (UnwrapMaybe | UnwrapError) | BoolOrExpression |
| 2468 | UnwrapMaybe : "??" BoolOrExpression | ||
| 2469 | UnwrapError : "%%" option("|" "Symbol" "|") BoolOrExpression | ||
| 2458 | */ | 2470 | */ |
| 2459 | // this is currently the first child expression of assignment | 2471 | static AstNode *ast_parse_unwrap_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| 2460 | static AstNode *ast_parse_unwrap_maybe_expr(ParseContext *pc, int *token_index, bool mandatory) { | ||
| 2461 | AstNode *lhs = ast_parse_bool_or_expr(pc, token_index, mandatory); | 2472 | AstNode *lhs = ast_parse_bool_or_expr(pc, token_index, mandatory); |
| 2462 | if (!lhs) | 2473 | if (!lhs) |
| 2463 | return nullptr; | 2474 | return nullptr; |
| 2464 | 2475 | ||
| 2465 | Token *token = &pc->tokens->at(*token_index); | 2476 | Token *token = &pc->tokens->at(*token_index); |
| 2466 | 2477 | ||
| 2467 | if (token->id != TokenIdDoubleQuestion) { | 2478 | if (token->id == TokenIdDoubleQuestion) { |
| 2468 | return lhs; | 2479 | *token_index += 1; |
| 2469 | } | ||
| 2470 | 2480 | ||
| 2471 | *token_index += 1; | 2481 | AstNode *rhs = ast_parse_bool_or_expr(pc, token_index, true); |
| 2472 | 2482 | ||
| 2473 | AstNode *rhs = ast_parse_bool_or_expr(pc, token_index, true); | 2483 | AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); |
| 2484 | node->data.bin_op_expr.op1 = lhs; | ||
| 2485 | node->data.bin_op_expr.bin_op = BinOpTypeUnwrapMaybe; | ||
| 2486 | node->data.bin_op_expr.op2 = rhs; | ||
| 2474 | 2487 | ||
| 2475 | AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); | 2488 | normalize_parent_ptrs(node); |
| 2476 | node->data.bin_op_expr.op1 = lhs; | 2489 | return node; |
| 2477 | node->data.bin_op_expr.bin_op = BinOpTypeUnwrapMaybe; | 2490 | } else if (token->id == TokenIdPercentPercent) { |
| 2478 | node->data.bin_op_expr.op2 = rhs; | 2491 | *token_index += 1; |
| 2479 | 2492 | ||
| 2480 | normalize_parent_ptrs(node); | 2493 | AstNode *node = ast_create_node(pc, NodeTypeUnwrapErrorExpr, token); |
| 2481 | return node; | 2494 | node->data.unwrap_err_expr.op1 = lhs; |
| 2495 | |||
| 2496 | Token *maybe_bar_tok = &pc->tokens->at(*token_index); | ||
| 2497 | if (maybe_bar_tok->id == TokenIdBinOr) { | ||
| 2498 | *token_index += 1; | ||
| 2499 | node->data.unwrap_err_expr.symbol = ast_parse_symbol(pc, token_index); | ||
| 2500 | ast_eat_token(pc, token_index, TokenIdBinOr); | ||
| 2501 | } | ||
| 2502 | node->data.unwrap_err_expr.op2 = ast_parse_expression(pc, token_index, true); | ||
| 2503 | |||
| 2504 | normalize_parent_ptrs(node); | ||
| 2505 | return node; | ||
| 2506 | } else { | ||
| 2507 | return lhs; | ||
| 2508 | } | ||
| 2482 | } | 2509 | } |
| 2483 | 2510 | ||
| 2484 | /* | 2511 | /* |
| 2485 | AssignmentExpression : UnwrapMaybeExpression AssignmentOperator UnwrapMaybeExpression | UnwrapMaybeExpression | 2512 | AssignmentExpression : UnwrapExpression AssignmentOperator UnwrapExpression | UnwrapExpression |
| 2486 | */ | 2513 | */ |
| 2487 | static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mandatory) { | 2514 | static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| 2488 | AstNode *lhs = ast_parse_unwrap_maybe_expr(pc, token_index, mandatory); | 2515 | AstNode *lhs = ast_parse_unwrap_expr(pc, token_index, mandatory); |
| 2489 | if (!lhs) | 2516 | if (!lhs) |
| 2490 | return nullptr; | 2517 | return nullptr; |
| 2491 | 2518 | ||
| ... | @@ -2494,7 +2521,7 @@ static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mand | ... | @@ -2494,7 +2521,7 @@ static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mand |
| 2494 | if (ass_op == BinOpTypeInvalid) | 2521 | if (ass_op == BinOpTypeInvalid) |
| 2495 | return lhs; | 2522 | return lhs; |
| 2496 | 2523 | ||
| 2497 | AstNode *rhs = ast_parse_unwrap_maybe_expr(pc, token_index, true); | 2524 | AstNode *rhs = ast_parse_unwrap_expr(pc, token_index, true); |
| 2498 | 2525 | ||
| 2499 | AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); | 2526 | AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); |
| 2500 | node->data.bin_op_expr.op1 = lhs; | 2527 | node->data.bin_op_expr.op1 = lhs; |
| ... | @@ -2646,7 +2673,7 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato | ... | @@ -2646,7 +2673,7 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato |
| 2646 | } | 2673 | } |
| 2647 | 2674 | ||
| 2648 | /* | 2675 | /* |
| 2649 | FnProto : many(Directive) option(FnVisibleMod) token(Fn) token(Symbol) ParamDeclList option(UnwrapMaybeExpression) | 2676 | FnProto : many(Directive) option(FnVisibleMod) "fn" "Symbol" ParamDeclList option(PrefixOpExpression) |
| 2650 | */ | 2677 | */ |
| 2651 | static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mandatory) { | 2678 | static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mandatory) { |
| 2652 | Token *first_token = &pc->tokens->at(*token_index); | 2679 | Token *first_token = &pc->tokens->at(*token_index); |
| ... | @@ -3164,6 +3191,11 @@ void normalize_parent_ptrs(AstNode *node) { | ... | @@ -3164,6 +3191,11 @@ void normalize_parent_ptrs(AstNode *node) { |
| 3164 | set_field(&node->data.bin_op_expr.op1); | 3191 | set_field(&node->data.bin_op_expr.op1); |
| 3165 | set_field(&node->data.bin_op_expr.op2); | 3192 | set_field(&node->data.bin_op_expr.op2); |
| 3166 | break; | 3193 | break; |
| 3194 | case NodeTypeUnwrapErrorExpr: | ||
| 3195 | set_field(&node->data.unwrap_err_expr.op1); | ||
| 3196 | set_field(&node->data.unwrap_err_expr.symbol); | ||
| 3197 | set_field(&node->data.unwrap_err_expr.op2); | ||
| 3198 | break; | ||
| 3167 | case NodeTypeNumberLiteral: | 3199 | case NodeTypeNumberLiteral: |
| 3168 | // none | 3200 | // none |
| 3169 | break; | 3201 | break; |
src/tokenizer.cpp+6| ... | @@ -593,6 +593,11 @@ void tokenize(Buf *buf, Tokenization *out) { | ... | @@ -593,6 +593,11 @@ void tokenize(Buf *buf, Tokenization *out) { |
| 593 | end_token(&t); | 593 | end_token(&t); |
| 594 | t.state = TokenizeStateStart; | 594 | t.state = TokenizeStateStart; |
| 595 | break; | 595 | break; |
| 596 | case '%': | ||
| 597 | t.cur_tok->id = TokenIdPercentPercent; | ||
| 598 | end_token(&t); | ||
| 599 | t.state = TokenizeStateStart; | ||
| 600 | break; | ||
| 596 | default: | 601 | default: |
| 597 | t.pos -= 1; | 602 | t.pos -= 1; |
| 598 | end_token(&t); | 603 | end_token(&t); |
| ... | @@ -1097,6 +1102,7 @@ const char * token_name(TokenId id) { | ... | @@ -1097,6 +1102,7 @@ const char * token_name(TokenId id) { |
| 1097 | case TokenIdBitShiftRight: return ">>"; | 1102 | case TokenIdBitShiftRight: return ">>"; |
| 1098 | case TokenIdSlash: return "/"; | 1103 | case TokenIdSlash: return "/"; |
| 1099 | case TokenIdPercent: return "%"; | 1104 | case TokenIdPercent: return "%"; |
| 1105 | case TokenIdPercentPercent: return "%%"; | ||
| 1100 | case TokenIdDot: return "."; | 1106 | case TokenIdDot: return "."; |
| 1101 | case TokenIdEllipsis: return "..."; | 1107 | case TokenIdEllipsis: return "..."; |
| 1102 | case TokenIdMaybe: return "?"; | 1108 | case TokenIdMaybe: return "?"; |
src/tokenizer.hpp+1| ... | @@ -87,6 +87,7 @@ enum TokenId { | ... | @@ -87,6 +87,7 @@ enum TokenId { |
| 87 | TokenIdBitShiftRight, | 87 | TokenIdBitShiftRight, |
| 88 | TokenIdSlash, | 88 | TokenIdSlash, |
| 89 | TokenIdPercent, | 89 | TokenIdPercent, |
| 90 | TokenIdPercentPercent, | ||
| 90 | TokenIdDot, | 91 | TokenIdDot, |
| 91 | TokenIdEllipsis, | 92 | TokenIdEllipsis, |
| 92 | TokenIdMaybe, | 93 | TokenIdMaybe, |
std/std.zig+9-9| ... | @@ -130,7 +130,7 @@ pub struct OutStream { | ... | @@ -130,7 +130,7 @@ pub struct OutStream { |
| 130 | pub struct InStream { | 130 | pub struct InStream { |
| 131 | fd: isize, | 131 | fd: isize, |
| 132 | 132 | ||
| 133 | pub fn readline(is: &InStream, buf: []u8) %isize => { | 133 | pub fn read(is: &InStream, buf: []u8) %isize => { |
| 134 | const amt_read = read(is.fd, buf.ptr, buf.len); | 134 | const amt_read = read(is.fd, buf.ptr, buf.len); |
| 135 | if (amt_read < 0) { | 135 | if (amt_read < 0) { |
| 136 | return switch (-amt_read) { | 136 | return switch (-amt_read) { |
| ... | @@ -144,7 +144,6 @@ pub struct InStream { | ... | @@ -144,7 +144,6 @@ pub struct InStream { |
| 144 | } | 144 | } |
| 145 | return amt_read; | 145 | return amt_read; |
| 146 | } | 146 | } |
| 147 | |||
| 148 | } | 147 | } |
| 149 | 148 | ||
| 150 | pub fn os_get_random_bytes(buf: []u8) %void => { | 149 | pub fn os_get_random_bytes(buf: []u8) %void => { |
| ... | @@ -160,30 +159,31 @@ pub fn os_get_random_bytes(buf: []u8) %void => { | ... | @@ -160,30 +159,31 @@ pub fn os_get_random_bytes(buf: []u8) %void => { |
| 160 | } | 159 | } |
| 161 | 160 | ||
| 162 | 161 | ||
| 163 | // TODO return %u64 when we support errors | 162 | pub error InvalidChar; |
| 164 | pub fn parse_u64(buf: []u8, radix: u8, result: &u64) bool => { | 163 | pub error Overflow; |
| 164 | |||
| 165 | pub fn parse_u64(buf: []u8, radix: u8) %u64 => { | ||
| 165 | var x : u64 = 0; | 166 | var x : u64 = 0; |
| 166 | 167 | ||
| 167 | for (c, buf) { | 168 | for (c, buf) { |
| 168 | const digit = char_to_digit(c); | 169 | const digit = char_to_digit(c); |
| 169 | 170 | ||
| 170 | if (digit > radix) { | 171 | if (digit > radix) { |
| 171 | return true; | 172 | return error.InvalidChar; |
| 172 | } | 173 | } |
| 173 | 174 | ||
| 174 | // x *= radix | 175 | // x *= radix |
| 175 | if (@mul_with_overflow(u64, x, radix, &x)) { | 176 | if (@mul_with_overflow(u64, x, radix, &x)) { |
| 176 | return true; | 177 | return error.Overflow; |
| 177 | } | 178 | } |
| 178 | 179 | ||
| 179 | // x += digit | 180 | // x += digit |
| 180 | if (@add_with_overflow(u64, x, digit, &x)) { | 181 | if (@add_with_overflow(u64, x, digit, &x)) { |
| 181 | return true; | 182 | return error.Overflow; |
| 182 | } | 183 | } |
| 183 | } | 184 | } |
| 184 | 185 | ||
| 185 | *result = x; | 186 | return x; |
| 186 | return false; | ||
| 187 | } | 187 | } |
| 188 | 188 | ||
| 189 | fn char_to_digit(c: u8) u8 => { | 189 | fn char_to_digit(c: u8) u8 => { |