| 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 | 59 | |
| 60 | 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 | 70 | AssignmentOperator : "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|=" | "&&=" | "||=" |
| 67 | 71 | |
| ... | ... | @@ -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 | 4 | |
| 5 | 5 | // Things to do to make this work: |
| 6 | 6 | // * var args printing |
| 7 | // * update std API | |
| 8 | 7 | // * defer |
| 9 | // * %return | |
| 10 | 8 | // * %% binary operator |
| 11 | 9 | // * %% prefix operator |
| 12 | 10 | // * cast err type to string |
example/guess_number/main.zig+10-7| ... | ... | @@ -8,7 +8,7 @@ pub fn main(args: [][]u8) %void => { |
| 8 | 8 | |
| 9 | 9 | var seed : u32; |
| 10 | 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 | 13 | var rand = rand_new(seed); |
| 14 | 14 | |
| ... | ... | @@ -18,13 +18,16 @@ pub fn main(args: [][]u8) %void => { |
| 18 | 18 | stderr.print_str("\nGuess a number between 1 and 100: "); |
| 19 | 19 | var line_buf : [20]u8; |
| 20 | 20 | |
| 21 | // TODO print error message instead of returning | |
| 22 | const line_len = %return stdin.readline(line_buf); | |
| 21 | const line_len = stdin.read(line_buf) %% |err| { | |
| 22 | stderr.print_str("Unable to read from stdin.\n"); | |
| 23 | return err; | |
| 24 | }; | |
| 23 | 25 | |
| 24 | var guess : u64; | |
| 25 | if (parse_u64(line_buf[0...line_len - 1], 10, &guess)) { | |
| 26 | stderr.print_str("Invalid number format.\n"); | |
| 27 | } else if (guess > answer) { | |
| 26 | const guess = parse_u64(line_buf[0...line_len - 1], 10) %% { | |
| 27 | stderr.print_str("Invalid number.\n"); | |
| 28 | continue; | |
| 29 | }; | |
| 30 | if (guess > answer) { | |
| 28 | 31 | stderr.print_str("Guess lower.\n"); |
| 29 | 32 | } else if (guess < answer) { |
| 30 | 33 | stderr.print_str("Guess higher.\n"); |
src/all_types.hpp+12| ... | ... | @@ -130,6 +130,7 @@ enum NodeType { |
| 130 | 130 | NodeTypeVariableDeclaration, |
| 131 | 131 | NodeTypeErrorValueDecl, |
| 132 | 132 | NodeTypeBinOpExpr, |
| 133 | NodeTypeUnwrapErrorExpr, | |
| 133 | 134 | NodeTypeNumberLiteral, |
| 134 | 135 | NodeTypeStringLiteral, |
| 135 | 136 | NodeTypeCharLiteral, |
| ... | ... | @@ -310,6 +311,16 @@ struct AstNodeBinOpExpr { |
| 310 | 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 | 324 | enum CastOp { |
| 314 | 325 | CastOpNoCast, // signifies the function call expression is not a cast |
| 315 | 326 | CastOpNoop, // fn call expr is a cast, but does nothing |
| ... | ... | @@ -684,6 +695,7 @@ struct AstNode { |
| 684 | 695 | AstNodeVariableDeclaration variable_declaration; |
| 685 | 696 | AstNodeErrorValueDecl error_value_decl; |
| 686 | 697 | AstNodeBinOpExpr bin_op_expr; |
| 698 | AstNodeUnwrapErrorExpr unwrap_err_expr; | |
| 687 | 699 | AstNodeExternBlock extern_block; |
| 688 | 700 | AstNodeDirective directive; |
| 689 | 701 | AstNodePrefixOpExpr prefix_op_expr; |
src/analyze.cpp+73-7| ... | ... | @@ -28,6 +28,8 @@ static AstNode *first_executing_node(AstNode *node) { |
| 28 | 28 | return first_executing_node(node->data.fn_call_expr.fn_ref_expr); |
| 29 | 29 | case NodeTypeBinOpExpr: |
| 30 | 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 | 33 | case NodeTypeArrayAccessExpr: |
| 32 | 34 | return first_executing_node(node->data.array_access_expr.array_ref_expr); |
| 33 | 35 | case NodeTypeSliceExpr: |
| ... | ... | @@ -1076,6 +1078,7 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode |
| 1076 | 1078 | case NodeTypeRoot: |
| 1077 | 1079 | case NodeTypeBlock: |
| 1078 | 1080 | case NodeTypeBinOpExpr: |
| 1081 | case NodeTypeUnwrapErrorExpr: | |
| 1079 | 1082 | case NodeTypeFnCallExpr: |
| 1080 | 1083 | case NodeTypeArrayAccessExpr: |
| 1081 | 1084 | case NodeTypeSliceExpr: |
| ... | ... | @@ -2502,6 +2505,38 @@ static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, Block |
| 2502 | 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 | 2540 | static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import, |
| 2506 | 2541 | BlockContext *context, AstNode *source_node, |
| 2507 | 2542 | AstNodeVariableDeclaration *variable_declaration, |
| ... | ... | @@ -3845,7 +3880,9 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 3845 | 3880 | case NodeTypeBinOpExpr: |
| 3846 | 3881 | return_type = analyze_bin_op_expr(g, import, context, expected_type, node); |
| 3847 | 3882 | break; |
| 3848 | ||
| 3883 | case NodeTypeUnwrapErrorExpr: | |
| 3884 | return_type = analyze_unwrap_error_expr(g, import, context, expected_type, node); | |
| 3885 | break; | |
| 3849 | 3886 | case NodeTypeFnCallExpr: |
| 3850 | 3887 | return_type = analyze_fn_call_expr(g, import, context, expected_type, node); |
| 3851 | 3888 | break; |
| ... | ... | @@ -4035,6 +4072,7 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode |
| 4035 | 4072 | case NodeTypeRoot: |
| 4036 | 4073 | case NodeTypeBlock: |
| 4037 | 4074 | case NodeTypeBinOpExpr: |
| 4075 | case NodeTypeUnwrapErrorExpr: | |
| 4038 | 4076 | case NodeTypeFnCallExpr: |
| 4039 | 4077 | case NodeTypeArrayAccessExpr: |
| 4040 | 4078 | case NodeTypeSliceExpr: |
| ... | ... | @@ -4101,6 +4139,10 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode |
| 4101 | 4139 | collect_expr_decl_deps(g, import, node->data.bin_op_expr.op1, decl_node); |
| 4102 | 4140 | collect_expr_decl_deps(g, import, node->data.bin_op_expr.op2, decl_node); |
| 4103 | 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 | 4146 | case NodeTypeReturnExpr: |
| 4105 | 4147 | collect_expr_decl_deps(g, import, node->data.return_expr.expr, decl_node); |
| 4106 | 4148 | break; |
| ... | ... | @@ -4388,6 +4430,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast |
| 4388 | 4430 | case NodeTypeRoot: |
| 4389 | 4431 | case NodeTypeBlock: |
| 4390 | 4432 | case NodeTypeBinOpExpr: |
| 4433 | case NodeTypeUnwrapErrorExpr: | |
| 4391 | 4434 | case NodeTypeFnCallExpr: |
| 4392 | 4435 | case NodeTypeArrayAccessExpr: |
| 4393 | 4436 | case NodeTypeSliceExpr: |
| ... | ... | @@ -4582,6 +4625,8 @@ Expr *get_resolved_expr(AstNode *node) { |
| 4582 | 4625 | return &node->data.return_expr.resolved_expr; |
| 4583 | 4626 | case NodeTypeBinOpExpr: |
| 4584 | 4627 | return &node->data.bin_op_expr.resolved_expr; |
| 4628 | case NodeTypeUnwrapErrorExpr: | |
| 4629 | return &node->data.unwrap_err_expr.resolved_expr; | |
| 4585 | 4630 | case NodeTypePrefixOpExpr: |
| 4586 | 4631 | return &node->data.prefix_op_expr.resolved_expr; |
| 4587 | 4632 | case NodeTypeFnCallExpr: |
| ... | ... | @@ -4669,6 +4714,7 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) { |
| 4669 | 4714 | case NodeTypeNumberLiteral: |
| 4670 | 4715 | case NodeTypeReturnExpr: |
| 4671 | 4716 | case NodeTypeBinOpExpr: |
| 4717 | case NodeTypeUnwrapErrorExpr: | |
| 4672 | 4718 | case NodeTypePrefixOpExpr: |
| 4673 | 4719 | case NodeTypeFnCallExpr: |
| 4674 | 4720 | case NodeTypeArrayAccessExpr: |
| ... | ... | @@ -4747,10 +4793,30 @@ TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, int size_in_bits) { |
| 4747 | 4793 | } |
| 4748 | 4794 | |
| 4749 | 4795 | bool handle_is_ptr(TypeTableEntry *type_entry) { |
| 4750 | return type_entry->id == TypeTableEntryIdStruct || | |
| 4751 | (type_entry->id == TypeTableEntryIdEnum && type_entry->data.enumeration.gen_field_count != 0) || | |
| 4752 | type_entry->id == TypeTableEntryIdMaybe || | |
| 4753 | type_entry->id == TypeTableEntryIdArray || | |
| 4754 | (type_entry->id == TypeTableEntryIdErrorUnion && type_entry->data.error.child_type->size_in_bits > 0); | |
| 4796 | switch (type_entry->id) { | |
| 4797 | case TypeTableEntryIdInvalid: | |
| 4798 | case TypeTableEntryIdMetaType: | |
| 4799 | case TypeTableEntryIdNumLitFloat: | |
| 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 | 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 | 1330 | static LLVMValueRef gen_return(CodeGen *g, AstNode *source_node, LLVMValueRef value) { |
| 1259 | 1331 | TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.src_return_type; |
| 1260 | 1332 | if (handle_is_ptr(return_type)) { |
| ... | ... | @@ -2047,6 +2119,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { |
| 2047 | 2119 | switch (node->type) { |
| 2048 | 2120 | case NodeTypeBinOpExpr: |
| 2049 | 2121 | return gen_bin_op_expr(g, node); |
| 2122 | case NodeTypeUnwrapErrorExpr: | |
| 2123 | return gen_unwrap_err_expr(g, node); | |
| 2050 | 2124 | case NodeTypeReturnExpr: |
| 2051 | 2125 | return gen_return_expr(g, node); |
| 2052 | 2126 | case NodeTypeVariableDeclaration: |
src/parser.cpp+55-23| ... | ... | @@ -95,6 +95,8 @@ const char *node_type_str(NodeType node_type) { |
| 95 | 95 | return "Block"; |
| 96 | 96 | case NodeTypeBinOpExpr: |
| 97 | 97 | return "BinOpExpr"; |
| 98 | case NodeTypeUnwrapErrorExpr: | |
| 99 | return "UnwrapErrorExpr"; | |
| 98 | 100 | case NodeTypeFnCallExpr: |
| 99 | 101 | return "FnCallExpr"; |
| 100 | 102 | case NodeTypeArrayAccessExpr: |
| ... | ... | @@ -273,6 +275,14 @@ void ast_print(AstNode *node, int indent) { |
| 273 | 275 | ast_print(node->data.bin_op_expr.op1, indent + 2); |
| 274 | 276 | ast_print(node->data.bin_op_expr.op2, indent + 2); |
| 275 | 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 | 286 | case NodeTypeFnCallExpr: |
| 277 | 287 | fprintf(stderr, "%s\n", node_type_str(node->type)); |
| 278 | 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 | 974 | static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory); |
| 965 | 975 | static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory); |
| 966 | 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 | 978 | static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, bool mandatory); |
| 969 | 979 | |
| 970 | 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 | 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 | 1047 | static AstNode *ast_parse_param_decl(ParseContext *pc, int *token_index) { |
| 1038 | 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 | 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 | 1169 | static AstNode *ast_parse_array_type_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| 1160 | 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 | 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 | 1222 | static void ast_parse_asm_output_item(ParseContext *pc, int *token_index, AstNode *node) { |
| 1213 | 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 | 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 | 2147 | static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| 2138 | 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 | 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 | |
| 2460 | static AstNode *ast_parse_unwrap_maybe_expr(ParseContext *pc, int *token_index, bool mandatory) { | |
| 2471 | static AstNode *ast_parse_unwrap_expr(ParseContext *pc, int *token_index, bool mandatory) { | |
| 2461 | 2472 | AstNode *lhs = ast_parse_bool_or_expr(pc, token_index, mandatory); |
| 2462 | 2473 | if (!lhs) |
| 2463 | 2474 | return nullptr; |
| 2464 | 2475 | |
| 2465 | 2476 | Token *token = &pc->tokens->at(*token_index); |
| 2466 | 2477 | |
| 2467 | if (token->id != TokenIdDoubleQuestion) { | |
| 2468 | return lhs; | |
| 2469 | } | |
| 2478 | if (token->id == TokenIdDoubleQuestion) { | |
| 2479 | *token_index += 1; | |
| 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); | |
| 2476 | node->data.bin_op_expr.op1 = lhs; | |
| 2477 | node->data.bin_op_expr.bin_op = BinOpTypeUnwrapMaybe; | |
| 2478 | node->data.bin_op_expr.op2 = rhs; | |
| 2488 | normalize_parent_ptrs(node); | |
| 2489 | return node; | |
| 2490 | } else if (token->id == TokenIdPercentPercent) { | |
| 2491 | *token_index += 1; | |
| 2479 | 2492 | |
| 2480 | normalize_parent_ptrs(node); | |
| 2481 | return node; | |
| 2493 | AstNode *node = ast_create_node(pc, NodeTypeUnwrapErrorExpr, token); | |
| 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 | 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 | 2516 | if (!lhs) |
| 2490 | 2517 | return nullptr; |
| 2491 | 2518 | |
| ... | ... | @@ -2494,7 +2521,7 @@ static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mand |
| 2494 | 2521 | if (ass_op == BinOpTypeInvalid) |
| 2495 | 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 | 2526 | AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); |
| 2500 | 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 | 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 | 2678 | static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mandatory) { |
| 2652 | 2679 | Token *first_token = &pc->tokens->at(*token_index); |
| ... | ... | @@ -3164,6 +3191,11 @@ void normalize_parent_ptrs(AstNode *node) { |
| 3164 | 3191 | set_field(&node->data.bin_op_expr.op1); |
| 3165 | 3192 | set_field(&node->data.bin_op_expr.op2); |
| 3166 | 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 | 3199 | case NodeTypeNumberLiteral: |
| 3168 | 3200 | // none |
| 3169 | 3201 | break; |
src/tokenizer.cpp+6| ... | ... | @@ -593,6 +593,11 @@ void tokenize(Buf *buf, Tokenization *out) { |
| 593 | 593 | end_token(&t); |
| 594 | 594 | t.state = TokenizeStateStart; |
| 595 | 595 | break; |
| 596 | case '%': | |
| 597 | t.cur_tok->id = TokenIdPercentPercent; | |
| 598 | end_token(&t); | |
| 599 | t.state = TokenizeStateStart; | |
| 600 | break; | |
| 596 | 601 | default: |
| 597 | 602 | t.pos -= 1; |
| 598 | 603 | end_token(&t); |
| ... | ... | @@ -1097,6 +1102,7 @@ const char * token_name(TokenId id) { |
| 1097 | 1102 | case TokenIdBitShiftRight: return ">>"; |
| 1098 | 1103 | case TokenIdSlash: return "/"; |
| 1099 | 1104 | case TokenIdPercent: return "%"; |
| 1105 | case TokenIdPercentPercent: return "%%"; | |
| 1100 | 1106 | case TokenIdDot: return "."; |
| 1101 | 1107 | case TokenIdEllipsis: return "..."; |
| 1102 | 1108 | case TokenIdMaybe: return "?"; |
src/tokenizer.hpp+1| ... | ... | @@ -87,6 +87,7 @@ enum TokenId { |
| 87 | 87 | TokenIdBitShiftRight, |
| 88 | 88 | TokenIdSlash, |
| 89 | 89 | TokenIdPercent, |
| 90 | TokenIdPercentPercent, | |
| 90 | 91 | TokenIdDot, |
| 91 | 92 | TokenIdEllipsis, |
| 92 | 93 | TokenIdMaybe, |
std/std.zig+9-9| ... | ... | @@ -130,7 +130,7 @@ pub struct OutStream { |
| 130 | 130 | pub struct InStream { |
| 131 | 131 | fd: isize, |
| 132 | 132 | |
| 133 | pub fn readline(is: &InStream, buf: []u8) %isize => { | |
| 133 | pub fn read(is: &InStream, buf: []u8) %isize => { | |
| 134 | 134 | const amt_read = read(is.fd, buf.ptr, buf.len); |
| 135 | 135 | if (amt_read < 0) { |
| 136 | 136 | return switch (-amt_read) { |
| ... | ... | @@ -144,7 +144,6 @@ pub struct InStream { |
| 144 | 144 | } |
| 145 | 145 | return amt_read; |
| 146 | 146 | } |
| 147 | ||
| 148 | 147 | } |
| 149 | 148 | |
| 150 | 149 | 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 | |
| 164 | pub fn parse_u64(buf: []u8, radix: u8, result: &u64) bool => { | |
| 162 | pub error InvalidChar; | |
| 163 | pub error Overflow; | |
| 164 | ||
| 165 | pub fn parse_u64(buf: []u8, radix: u8) %u64 => { | |
| 165 | 166 | var x : u64 = 0; |
| 166 | 167 | |
| 167 | 168 | for (c, buf) { |
| 168 | 169 | const digit = char_to_digit(c); |
| 169 | 170 | |
| 170 | 171 | if (digit > radix) { |
| 171 | return true; | |
| 172 | return error.InvalidChar; | |
| 172 | 173 | } |
| 173 | 174 | |
| 174 | 175 | // x *= radix |
| 175 | 176 | if (@mul_with_overflow(u64, x, radix, &x)) { |
| 176 | return true; | |
| 177 | return error.Overflow; | |
| 177 | 178 | } |
| 178 | 179 | |
| 179 | 180 | // x += digit |
| 180 | 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 false; | |
| 186 | return x; | |
| 187 | 187 | } |
| 188 | 188 | |
| 189 | 189 | fn char_to_digit(c: u8) u8 => { |