| author | |
| committer | |
| log | 1090b289eca8dadba482f176f19364c172d01f2e |
| tree | cb081f933826c09675a97a4d4040dea615728634 |
| parent | 4d0b660f4bcde2544f3096225ec28f7940162c30 |
| parent | 3a8dc4e90ddf6b3dc2bdf640c89061c00eee7d45 |
33 files changed, 3540 insertions(+), 1593 deletions(-)
CMakeLists.txt+3| ... | ... | @@ -415,6 +415,9 @@ set(ZIG_CPP_SOURCES |
| 415 | 415 | |
| 416 | 416 | set(ZIG_STD_FILES |
| 417 | 417 | "array_list.zig" |
| 418 | "atomic/index.zig" | |
| 419 | "atomic/stack.zig" | |
| 420 | "atomic/queue.zig" | |
| 418 | 421 | "base64.zig" |
| 419 | 422 | "buf_map.zig" |
| 420 | 423 | "buf_set.zig" |
src/analyze.cpp+1-2| ... | ... | @@ -1258,7 +1258,7 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou |
| 1258 | 1258 | } |
| 1259 | 1259 | |
| 1260 | 1260 | fn_type_id->param_count = fn_proto->params.length; |
| 1261 | fn_type_id->param_info = allocate_nonzero<FnTypeParamInfo>(param_count_alloc); | |
| 1261 | fn_type_id->param_info = allocate<FnTypeParamInfo>(param_count_alloc); | |
| 1262 | 1262 | fn_type_id->next_param_index = 0; |
| 1263 | 1263 | fn_type_id->is_var_args = fn_proto->is_var_args; |
| 1264 | 1264 | } |
| ... | ... | @@ -6131,4 +6131,3 @@ bool type_can_fail(TypeTableEntry *type_entry) { |
| 6131 | 6131 | bool fn_type_can_fail(FnTypeId *fn_type_id) { |
| 6132 | 6132 | return type_can_fail(fn_type_id->return_type) || fn_type_id->cc == CallingConventionAsync; |
| 6133 | 6133 | } |
| 6134 |
src/ir.cpp+47-27| ... | ... | @@ -6166,16 +6166,10 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A |
| 6166 | 6166 | buf_init_from_buf(&err_set_type->name, type_name); |
| 6167 | 6167 | err_set_type->is_copyable = true; |
| 6168 | 6168 | err_set_type->data.error_set.err_count = err_count; |
| 6169 | ||
| 6170 | if (err_count == 0) { | |
| 6171 | err_set_type->zero_bits = true; | |
| 6172 | err_set_type->di_type = irb->codegen->builtin_types.entry_void->di_type; | |
| 6173 | } else { | |
| 6174 | err_set_type->type_ref = irb->codegen->builtin_types.entry_global_error_set->type_ref; | |
| 6175 | err_set_type->di_type = irb->codegen->builtin_types.entry_global_error_set->di_type; | |
| 6176 | irb->codegen->error_di_types.append(&err_set_type->di_type); | |
| 6177 | err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(err_count); | |
| 6178 | } | |
| 6169 | err_set_type->type_ref = irb->codegen->builtin_types.entry_global_error_set->type_ref; | |
| 6170 | err_set_type->di_type = irb->codegen->builtin_types.entry_global_error_set->di_type; | |
| 6171 | irb->codegen->error_di_types.append(&err_set_type->di_type); | |
| 6172 | err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(err_count); | |
| 6179 | 6173 | |
| 6180 | 6174 | ErrorTableEntry **errors = allocate<ErrorTableEntry *>(irb->codegen->errors_by_index.length + err_count); |
| 6181 | 6175 | |
| ... | ... | @@ -8117,7 +8111,7 @@ static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t * |
| 8117 | 8111 | *errors = reallocate(*errors, old_errors_count, *errors_count); |
| 8118 | 8112 | } |
| 8119 | 8113 | |
| 8120 | static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, IrInstruction **instructions, size_t instruction_count) { | |
| 8114 | static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, TypeTableEntry *expected_type, IrInstruction **instructions, size_t instruction_count) { | |
| 8121 | 8115 | assert(instruction_count >= 1); |
| 8122 | 8116 | IrInstruction *prev_inst = instructions[0]; |
| 8123 | 8117 | if (type_is_invalid(prev_inst->value.type)) { |
| ... | ... | @@ -8164,16 +8158,6 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod |
| 8164 | 8158 | continue; |
| 8165 | 8159 | } |
| 8166 | 8160 | |
| 8167 | if (prev_type->id == TypeTableEntryIdNullLit) { | |
| 8168 | prev_inst = cur_inst; | |
| 8169 | continue; | |
| 8170 | } | |
| 8171 | ||
| 8172 | if (cur_type->id == TypeTableEntryIdNullLit) { | |
| 8173 | any_are_null = true; | |
| 8174 | continue; | |
| 8175 | } | |
| 8176 | ||
| 8177 | 8161 | if (prev_type->id == TypeTableEntryIdErrorSet) { |
| 8178 | 8162 | assert(err_set_type != nullptr); |
| 8179 | 8163 | if (cur_type->id == TypeTableEntryIdErrorSet) { |
| ... | ... | @@ -8433,6 +8417,16 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod |
| 8433 | 8417 | } |
| 8434 | 8418 | } |
| 8435 | 8419 | |
| 8420 | if (prev_type->id == TypeTableEntryIdNullLit) { | |
| 8421 | prev_inst = cur_inst; | |
| 8422 | continue; | |
| 8423 | } | |
| 8424 | ||
| 8425 | if (cur_type->id == TypeTableEntryIdNullLit) { | |
| 8426 | any_are_null = true; | |
| 8427 | continue; | |
| 8428 | } | |
| 8429 | ||
| 8436 | 8430 | if (types_match_const_cast_only(ira, prev_type, cur_type, source_node).id == ConstCastResultIdOk) { |
| 8437 | 8431 | continue; |
| 8438 | 8432 | } |
| ... | ... | @@ -8616,6 +8610,10 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod |
| 8616 | 8610 | } else if (err_set_type != nullptr) { |
| 8617 | 8611 | if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) { |
| 8618 | 8612 | return err_set_type; |
| 8613 | } else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) { | |
| 8614 | return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type->data.error_union.payload_type); | |
| 8615 | } else if (expected_type != nullptr && expected_type->id == TypeTableEntryIdErrorUnion) { | |
| 8616 | return get_error_union_type(ira->codegen, err_set_type, expected_type->data.error_union.payload_type); | |
| 8619 | 8617 | } else { |
| 8620 | 8618 | if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt || |
| 8621 | 8619 | prev_inst->value.type->id == TypeTableEntryIdNumLitFloat) |
| ... | ... | @@ -8627,8 +8625,6 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod |
| 8627 | 8625 | ir_add_error_node(ira, source_node, |
| 8628 | 8626 | buf_sprintf("unable to make error union out of null literal")); |
| 8629 | 8627 | return ira->codegen->builtin_types.entry_invalid; |
| 8630 | } else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) { | |
| 8631 | return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type->data.error_union.payload_type); | |
| 8632 | 8628 | } else { |
| 8633 | 8629 | return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type); |
| 8634 | 8630 | } |
| ... | ... | @@ -10651,7 +10647,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp |
| 10651 | 10647 | } |
| 10652 | 10648 | |
| 10653 | 10649 | IrInstruction *instructions[] = {op1, op2}; |
| 10654 | TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, source_node, instructions, 2); | |
| 10650 | TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, source_node, nullptr, instructions, 2); | |
| 10655 | 10651 | if (type_is_invalid(resolved_type)) |
| 10656 | 10652 | return resolved_type; |
| 10657 | 10653 | type_ensure_zero_bits_known(ira->codegen, resolved_type); |
| ... | ... | @@ -11041,7 +11037,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp |
| 11041 | 11037 | IrInstruction *op1 = bin_op_instruction->op1->other; |
| 11042 | 11038 | IrInstruction *op2 = bin_op_instruction->op2->other; |
| 11043 | 11039 | IrInstruction *instructions[] = {op1, op2}; |
| 11044 | TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, instructions, 2); | |
| 11040 | TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, nullptr, instructions, 2); | |
| 11045 | 11041 | if (type_is_invalid(resolved_type)) |
| 11046 | 11042 | return resolved_type; |
| 11047 | 11043 | IrBinOp op_id = bin_op_instruction->op_id; |
| ... | ... | @@ -13010,7 +13006,7 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP |
| 13010 | 13006 | return first_value->value.type; |
| 13011 | 13007 | } |
| 13012 | 13008 | |
| 13013 | TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node, | |
| 13009 | TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node, nullptr, | |
| 13014 | 13010 | new_incoming_values.items, new_incoming_values.length); |
| 13015 | 13011 | if (type_is_invalid(resolved_type)) |
| 13016 | 13012 | return resolved_type; |
| ... | ... | @@ -13863,6 +13859,15 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru |
| 13863 | 13859 | } |
| 13864 | 13860 | } else if (child_type->id == TypeTableEntryIdFn) { |
| 13865 | 13861 | if (buf_eql_str(field_name, "ReturnType")) { |
| 13862 | if (child_type->data.fn.fn_type_id.return_type == nullptr) { | |
| 13863 | // Return type can only ever be null, if the function is generic | |
| 13864 | assert(child_type->data.fn.is_generic); | |
| 13865 | ||
| 13866 | ir_add_error(ira, &field_ptr_instruction->base, | |
| 13867 | buf_sprintf("ReturnType has not been resolved because '%s' is generic", buf_ptr(&child_type->name))); | |
| 13868 | return ira->codegen->builtin_types.entry_invalid; | |
| 13869 | } | |
| 13870 | ||
| 13866 | 13871 | bool ptr_is_const = true; |
| 13867 | 13872 | bool ptr_is_volatile = false; |
| 13868 | 13873 | return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, |
| ... | ... | @@ -17864,6 +17869,16 @@ static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruc |
| 17864 | 17869 | |
| 17865 | 17870 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); |
| 17866 | 17871 | out_val->data.x_type = fn_type_id->param_info[arg_index].type; |
| 17872 | if (out_val->data.x_type == nullptr) { | |
| 17873 | // Args are only unresolved if our function is generic. | |
| 17874 | assert(fn_type->data.fn.is_generic); | |
| 17875 | ||
| 17876 | ir_add_error(ira, arg_index_inst, | |
| 17877 | buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_u64 " because '%s' is generic", | |
| 17878 | arg_index, buf_ptr(&fn_type->name))); | |
| 17879 | return ira->codegen->builtin_types.entry_invalid; | |
| 17880 | } | |
| 17881 | ||
| 17867 | 17882 | return ira->codegen->builtin_types.entry_type; |
| 17868 | 17883 | } |
| 17869 | 17884 | |
| ... | ... | @@ -18169,6 +18184,11 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr |
| 18169 | 18184 | } else { |
| 18170 | 18185 | if (!ir_resolve_atomic_order(ira, instruction->ordering->other, &ordering)) |
| 18171 | 18186 | return ira->codegen->builtin_types.entry_invalid; |
| 18187 | if (ordering == AtomicOrderUnordered) { | |
| 18188 | ir_add_error(ira, instruction->ordering, | |
| 18189 | buf_sprintf("@atomicRmw atomic ordering must not be Unordered")); | |
| 18190 | return ira->codegen->builtin_types.entry_invalid; | |
| 18191 | } | |
| 18172 | 18192 | } |
| 18173 | 18193 | |
| 18174 | 18194 | if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) |
| ... | ... | @@ -18702,7 +18722,7 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl |
| 18702 | 18722 | } else if (ira->src_implicit_return_type_list.length == 0) { |
| 18703 | 18723 | return codegen->builtin_types.entry_unreachable; |
| 18704 | 18724 | } else { |
| 18705 | return ir_resolve_peer_types(ira, expected_type_source_node, ira->src_implicit_return_type_list.items, | |
| 18725 | return ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items, | |
| 18706 | 18726 | ira->src_implicit_return_type_list.length); |
| 18707 | 18727 | } |
| 18708 | 18728 | } |
std/atomic/index.zig created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | pub const Stack = @import("stack.zig").Stack; | |
| 2 | pub const Queue = @import("queue.zig").Queue; | |
| 3 | ||
| 4 | test "std.atomic" { | |
| 5 | _ = @import("stack.zig").Stack; | |
| 6 | _ = @import("queue.zig").Queue; | |
| 7 | } |
std/atomic/queue.zig created+120| ... | ... | @@ -0,0 +1,120 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const AtomicOrder = builtin.AtomicOrder; | |
| 3 | const AtomicRmwOp = builtin.AtomicRmwOp; | |
| 4 | ||
| 5 | /// Many reader, many writer, non-allocating, thread-safe, lock-free | |
| 6 | pub fn Queue(comptime T: type) type { | |
| 7 | return struct { | |
| 8 | head: &Node, | |
| 9 | tail: &Node, | |
| 10 | root: Node, | |
| 11 | ||
| 12 | pub const Self = this; | |
| 13 | ||
| 14 | pub const Node = struct { | |
| 15 | next: ?&Node, | |
| 16 | data: T, | |
| 17 | }; | |
| 18 | ||
| 19 | // TODO: well defined copy elision: https://github.com/zig-lang/zig/issues/287 | |
| 20 | pub fn init(self: &Self) void { | |
| 21 | self.root.next = null; | |
| 22 | self.head = &self.root; | |
| 23 | self.tail = &self.root; | |
| 24 | } | |
| 25 | ||
| 26 | pub fn put(self: &Self, node: &Node) void { | |
| 27 | node.next = null; | |
| 28 | ||
| 29 | const tail = @atomicRmw(&Node, &self.tail, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst); | |
| 30 | _ = @atomicRmw(?&Node, &tail.next, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst); | |
| 31 | } | |
| 32 | ||
| 33 | pub fn get(self: &Self) ?&Node { | |
| 34 | var head = @atomicLoad(&Node, &self.head, AtomicOrder.Acquire); | |
| 35 | while (true) { | |
| 36 | const node = head.next ?? return null; | |
| 37 | head = @cmpxchgWeak(&Node, &self.head, head, node, AtomicOrder.Release, AtomicOrder.Acquire) ?? return node; | |
| 38 | } | |
| 39 | } | |
| 40 | }; | |
| 41 | } | |
| 42 | ||
| 43 | const std = @import("std"); | |
| 44 | const Context = struct { | |
| 45 | allocator: &std.mem.Allocator, | |
| 46 | queue: &Queue(i32), | |
| 47 | put_sum: isize, | |
| 48 | get_sum: isize, | |
| 49 | get_count: usize, | |
| 50 | puts_done: u8, // TODO make this a bool | |
| 51 | }; | |
| 52 | const puts_per_thread = 10000; | |
| 53 | const put_thread_count = 3; | |
| 54 | ||
| 55 | test "std.atomic.queue" { | |
| 56 | var direct_allocator = std.heap.DirectAllocator.init(); | |
| 57 | defer direct_allocator.deinit(); | |
| 58 | ||
| 59 | var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 64 * 1024 * 1024); | |
| 60 | defer direct_allocator.allocator.free(plenty_of_memory); | |
| 61 | ||
| 62 | var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); | |
| 63 | var a = &fixed_buffer_allocator.allocator; | |
| 64 | ||
| 65 | var queue: Queue(i32) = undefined; | |
| 66 | queue.init(); | |
| 67 | var context = Context { | |
| 68 | .allocator = a, | |
| 69 | .queue = &queue, | |
| 70 | .put_sum = 0, | |
| 71 | .get_sum = 0, | |
| 72 | .puts_done = 0, | |
| 73 | .get_count = 0, | |
| 74 | }; | |
| 75 | ||
| 76 | var putters: [put_thread_count]&std.os.Thread = undefined; | |
| 77 | for (putters) |*t| { | |
| 78 | *t = try std.os.spawnThread(&context, startPuts); | |
| 79 | } | |
| 80 | var getters: [put_thread_count]&std.os.Thread = undefined; | |
| 81 | for (getters) |*t| { | |
| 82 | *t = try std.os.spawnThread(&context, startGets); | |
| 83 | } | |
| 84 | ||
| 85 | for (putters) |t| t.wait(); | |
| 86 | _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); | |
| 87 | for (getters) |t| t.wait(); | |
| 88 | ||
| 89 | std.debug.assert(context.put_sum == context.get_sum); | |
| 90 | std.debug.assert(context.get_count == puts_per_thread * put_thread_count); | |
| 91 | } | |
| 92 | ||
| 93 | fn startPuts(ctx: &Context) u8 { | |
| 94 | var put_count: usize = puts_per_thread; | |
| 95 | var r = std.rand.DefaultPrng.init(0xdeadbeef); | |
| 96 | while (put_count != 0) : (put_count -= 1) { | |
| 97 | std.os.time.sleep(0, 1); // let the os scheduler be our fuzz | |
| 98 | const x = @bitCast(i32, r.random.scalar(u32)); | |
| 99 | const node = ctx.allocator.create(Queue(i32).Node) catch unreachable; | |
| 100 | node.data = x; | |
| 101 | ctx.queue.put(node); | |
| 102 | _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst); | |
| 103 | } | |
| 104 | return 0; | |
| 105 | } | |
| 106 | ||
| 107 | fn startGets(ctx: &Context) u8 { | |
| 108 | while (true) { | |
| 109 | while (ctx.queue.get()) |node| { | |
| 110 | std.os.time.sleep(0, 1); // let the os scheduler be our fuzz | |
| 111 | _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst); | |
| 112 | _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst); | |
| 113 | } | |
| 114 | ||
| 115 | if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) { | |
| 116 | break; | |
| 117 | } | |
| 118 | } | |
| 119 | return 0; | |
| 120 | } |
std/atomic/stack.zig created+126| ... | ... | @@ -0,0 +1,126 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const AtomicOrder = builtin.AtomicOrder; | |
| 3 | ||
| 4 | /// Many reader, many writer, non-allocating, thread-safe, lock-free | |
| 5 | pub fn Stack(comptime T: type) type { | |
| 6 | return struct { | |
| 7 | root: ?&Node, | |
| 8 | ||
| 9 | pub const Self = this; | |
| 10 | ||
| 11 | pub const Node = struct { | |
| 12 | next: ?&Node, | |
| 13 | data: T, | |
| 14 | }; | |
| 15 | ||
| 16 | pub fn init() Self { | |
| 17 | return Self { | |
| 18 | .root = null, | |
| 19 | }; | |
| 20 | } | |
| 21 | ||
| 22 | /// push operation, but only if you are the first item in the stack. if you did not succeed in | |
| 23 | /// being the first item in the stack, returns the other item that was there. | |
| 24 | pub fn pushFirst(self: &Self, node: &Node) ?&Node { | |
| 25 | node.next = null; | |
| 26 | return @cmpxchgStrong(?&Node, &self.root, null, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst); | |
| 27 | } | |
| 28 | ||
| 29 | pub fn push(self: &Self, node: &Node) void { | |
| 30 | var root = @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst); | |
| 31 | while (true) { | |
| 32 | node.next = root; | |
| 33 | root = @cmpxchgWeak(?&Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? break; | |
| 34 | } | |
| 35 | } | |
| 36 | ||
| 37 | pub fn pop(self: &Self) ?&Node { | |
| 38 | var root = @atomicLoad(?&Node, &self.root, AtomicOrder.Acquire); | |
| 39 | while (true) { | |
| 40 | root = @cmpxchgWeak(?&Node, &self.root, root, (root ?? return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return root; | |
| 41 | } | |
| 42 | } | |
| 43 | ||
| 44 | pub fn isEmpty(self: &Self) bool { | |
| 45 | return @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst) == null; | |
| 46 | } | |
| 47 | }; | |
| 48 | } | |
| 49 | ||
| 50 | const std = @import("std"); | |
| 51 | const Context = struct { | |
| 52 | allocator: &std.mem.Allocator, | |
| 53 | stack: &Stack(i32), | |
| 54 | put_sum: isize, | |
| 55 | get_sum: isize, | |
| 56 | get_count: usize, | |
| 57 | puts_done: u8, // TODO make this a bool | |
| 58 | }; | |
| 59 | const puts_per_thread = 1000; | |
| 60 | const put_thread_count = 3; | |
| 61 | ||
| 62 | test "std.atomic.stack" { | |
| 63 | var direct_allocator = std.heap.DirectAllocator.init(); | |
| 64 | defer direct_allocator.deinit(); | |
| 65 | ||
| 66 | var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 64 * 1024 * 1024); | |
| 67 | defer direct_allocator.allocator.free(plenty_of_memory); | |
| 68 | ||
| 69 | var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); | |
| 70 | var a = &fixed_buffer_allocator.allocator; | |
| 71 | ||
| 72 | var stack = Stack(i32).init(); | |
| 73 | var context = Context { | |
| 74 | .allocator = a, | |
| 75 | .stack = &stack, | |
| 76 | .put_sum = 0, | |
| 77 | .get_sum = 0, | |
| 78 | .puts_done = 0, | |
| 79 | .get_count = 0, | |
| 80 | }; | |
| 81 | ||
| 82 | var putters: [put_thread_count]&std.os.Thread = undefined; | |
| 83 | for (putters) |*t| { | |
| 84 | *t = try std.os.spawnThread(&context, startPuts); | |
| 85 | } | |
| 86 | var getters: [put_thread_count]&std.os.Thread = undefined; | |
| 87 | for (getters) |*t| { | |
| 88 | *t = try std.os.spawnThread(&context, startGets); | |
| 89 | } | |
| 90 | ||
| 91 | for (putters) |t| t.wait(); | |
| 92 | _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); | |
| 93 | for (getters) |t| t.wait(); | |
| 94 | ||
| 95 | std.debug.assert(context.put_sum == context.get_sum); | |
| 96 | std.debug.assert(context.get_count == puts_per_thread * put_thread_count); | |
| 97 | } | |
| 98 | ||
| 99 | fn startPuts(ctx: &Context) u8 { | |
| 100 | var put_count: usize = puts_per_thread; | |
| 101 | var r = std.rand.DefaultPrng.init(0xdeadbeef); | |
| 102 | while (put_count != 0) : (put_count -= 1) { | |
| 103 | std.os.time.sleep(0, 1); // let the os scheduler be our fuzz | |
| 104 | const x = @bitCast(i32, r.random.scalar(u32)); | |
| 105 | const node = ctx.allocator.create(Stack(i32).Node) catch unreachable; | |
| 106 | node.data = x; | |
| 107 | ctx.stack.push(node); | |
| 108 | _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst); | |
| 109 | } | |
| 110 | return 0; | |
| 111 | } | |
| 112 | ||
| 113 | fn startGets(ctx: &Context) u8 { | |
| 114 | while (true) { | |
| 115 | while (ctx.stack.pop()) |node| { | |
| 116 | std.os.time.sleep(0, 1); // let the os scheduler be our fuzz | |
| 117 | _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst); | |
| 118 | _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst); | |
| 119 | } | |
| 120 | ||
| 121 | if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) { | |
| 122 | break; | |
| 123 | } | |
| 124 | } | |
| 125 | return 0; | |
| 126 | } |
std/c/darwin.zig+5| ... | ... | @@ -81,3 +81,8 @@ pub const sockaddr = extern struct { |
| 81 | 81 | }; |
| 82 | 82 | |
| 83 | 83 | pub const sa_family_t = u8; |
| 84 | ||
| 85 | pub const pthread_attr_t = extern struct { | |
| 86 | __sig: c_long, | |
| 87 | __opaque: [56]u8, | |
| 88 | }; |
std/c/index.zig+10| ... | ... | @@ -53,3 +53,13 @@ pub extern "c" fn malloc(usize) ?&c_void; |
| 53 | 53 | pub extern "c" fn realloc(&c_void, usize) ?&c_void; |
| 54 | 54 | pub extern "c" fn free(&c_void) void; |
| 55 | 55 | pub extern "c" fn posix_memalign(memptr: &&c_void, alignment: usize, size: usize) c_int; |
| 56 | ||
| 57 | pub extern "pthread" fn pthread_create(noalias newthread: &pthread_t, | |
| 58 | noalias attr: ?&const pthread_attr_t, start_routine: extern fn(?&c_void) ?&c_void, | |
| 59 | noalias arg: ?&c_void) c_int; | |
| 60 | pub extern "pthread" fn pthread_attr_init(attr: &pthread_attr_t) c_int; | |
| 61 | pub extern "pthread" fn pthread_attr_setstack(attr: &pthread_attr_t, stackaddr: &c_void, stacksize: usize) c_int; | |
| 62 | pub extern "pthread" fn pthread_attr_destroy(attr: &pthread_attr_t) c_int; | |
| 63 | pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?&?&c_void) c_int; | |
| 64 | ||
| 65 | pub const pthread_t = &@OpaqueType(); |
std/c/linux.zig+5| ... | ... | @@ -3,3 +3,8 @@ pub use @import("../os/linux/errno.zig"); |
| 3 | 3 | pub extern "c" fn getrandom(buf_ptr: &u8, buf_len: usize, flags: c_uint) c_int; |
| 4 | 4 | extern "c" fn __errno_location() &c_int; |
| 5 | 5 | pub const _errno = __errno_location; |
| 6 | ||
| 7 | pub const pthread_attr_t = extern struct { | |
| 8 | __size: [56]u8, | |
| 9 | __align: c_long, | |
| 10 | }; |
std/fmt/errol/index.zig+76-3| ... | ... | @@ -12,13 +12,79 @@ pub const FloatDecimal = struct { |
| 12 | 12 | exp: i32, |
| 13 | 13 | }; |
| 14 | 14 | |
| 15 | pub const RoundMode = enum { | |
| 16 | // Round only the fractional portion (e.g. 1234.23 has precision 2) | |
| 17 | Decimal, | |
| 18 | // Round the entire whole/fractional portion (e.g. 1.23423e3 has precision 5) | |
| 19 | Scientific, | |
| 20 | }; | |
| 21 | ||
| 22 | /// Round a FloatDecimal as returned by errol3 to the specified fractional precision. | |
| 23 | /// All digits after the specified precision should be considered invalid. | |
| 24 | pub fn roundToPrecision(float_decimal: &FloatDecimal, precision: usize, mode: RoundMode) void { | |
| 25 | // The round digit refers to the index which we should look at to determine | |
| 26 | // whether we need to round to match the specified precision. | |
| 27 | var round_digit: usize = 0; | |
| 28 | ||
| 29 | switch (mode) { | |
| 30 | RoundMode.Decimal => { | |
| 31 | if (float_decimal.exp >= 0) { | |
| 32 | round_digit = precision + usize(float_decimal.exp); | |
| 33 | } else { | |
| 34 | // if a small negative exp, then adjust we need to offset by the number | |
| 35 | // of leading zeros that will occur. | |
| 36 | const min_exp_required = usize(-float_decimal.exp); | |
| 37 | if (precision > min_exp_required) { | |
| 38 | round_digit = precision - min_exp_required; | |
| 39 | } | |
| 40 | } | |
| 41 | }, | |
| 42 | RoundMode.Scientific => { | |
| 43 | round_digit = 1 + precision; | |
| 44 | }, | |
| 45 | } | |
| 46 | ||
| 47 | // It suffices to look at just this digit. We don't round and propagate say 0.04999 to 0.05 | |
| 48 | // first, and then to 0.1 in the case of a {.1} single precision. | |
| 49 | ||
| 50 | // Find the digit which will signify the round point and start rounding backwards. | |
| 51 | if (round_digit < float_decimal.digits.len and float_decimal.digits[round_digit] - '0' >= 5) { | |
| 52 | assert(round_digit >= 0); | |
| 53 | ||
| 54 | var i = round_digit; | |
| 55 | while (true) { | |
| 56 | if (i == 0) { | |
| 57 | // Rounded all the way past the start. This was of the form 9.999... | |
| 58 | // Slot the new digit in place and increase the exponent. | |
| 59 | float_decimal.exp += 1; | |
| 60 | ||
| 61 | // Re-size the buffer to use the reserved leading byte. | |
| 62 | const one_before = @intToPtr(&u8, @ptrToInt(&float_decimal.digits[0]) - 1); | |
| 63 | float_decimal.digits = one_before[0..float_decimal.digits.len + 1]; | |
| 64 | float_decimal.digits[0] = '1'; | |
| 65 | return; | |
| 66 | } | |
| 67 | ||
| 68 | i -= 1; | |
| 69 | ||
| 70 | const new_value = (float_decimal.digits[i] - '0' + 1) % 10; | |
| 71 | float_decimal.digits[i] = new_value + '0'; | |
| 72 | ||
| 73 | // must continue rounding until non-9 | |
| 74 | if (new_value != 0) { | |
| 75 | return; | |
| 76 | } | |
| 77 | } | |
| 78 | } | |
| 79 | } | |
| 80 | ||
| 15 | 81 | /// Corrected Errol3 double to ASCII conversion. |
| 16 | 82 | pub fn errol3(value: f64, buffer: []u8) FloatDecimal { |
| 17 | 83 | const bits = @bitCast(u64, value); |
| 18 | 84 | const i = tableLowerBound(bits); |
| 19 | 85 | if (i < enum3.len and enum3[i] == bits) { |
| 20 | 86 | const data = enum3_data[i]; |
| 21 | const digits = buffer[0..data.str.len]; | |
| 87 | const digits = buffer[1..data.str.len + 1]; | |
| 22 | 88 | mem.copy(u8, digits, data.str); |
| 23 | 89 | return FloatDecimal { |
| 24 | 90 | .digits = digits, |
| ... | ... | @@ -98,7 +164,11 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal { |
| 98 | 164 | } |
| 99 | 165 | |
| 100 | 166 | // digit generation |
| 101 | var buf_index: usize = 0; | |
| 167 | ||
| 168 | // We generate digits starting at index 1. If rounding a buffer later then it may be | |
| 169 | // required to generate a preceeding digit in some cases (9.999) in which case we use | |
| 170 | // the 0-index for this extra digit. | |
| 171 | var buf_index: usize = 1; | |
| 102 | 172 | while (true) { |
| 103 | 173 | var hdig = u8(math.floor(high.val)); |
| 104 | 174 | if ((high.val == f64(hdig)) and (high.off < 0)) |
| ... | ... | @@ -128,7 +198,7 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal { |
| 128 | 198 | buf_index += 1; |
| 129 | 199 | |
| 130 | 200 | return FloatDecimal { |
| 131 | .digits = buffer[0..buf_index], | |
| 201 | .digits = buffer[1..buf_index], | |
| 132 | 202 | .exp = exp, |
| 133 | 203 | }; |
| 134 | 204 | } |
| ... | ... | @@ -189,6 +259,9 @@ fn gethi(in: f64) f64 { |
| 189 | 259 | /// Normalize the number by factoring in the error. |
| 190 | 260 | /// @hp: The float pair. |
| 191 | 261 | fn hpNormalize(hp: &HP) void { |
| 262 | // Required to avoid segfaults causing buffer overrun during errol3 digit output termination. | |
| 263 | @setFloatMode(this, @import("builtin").FloatMode.Strict); | |
| 264 | ||
| 192 | 265 | const val = hp.val; |
| 193 | 266 | |
| 194 | 267 | hp.val += hp.off; |
std/fmt/index.zig+432-107| ... | ... | @@ -4,7 +4,7 @@ const debug = std.debug; |
| 4 | 4 | const assert = debug.assert; |
| 5 | 5 | const mem = std.mem; |
| 6 | 6 | const builtin = @import("builtin"); |
| 7 | const errol3 = @import("errol/index.zig").errol3; | |
| 7 | const errol = @import("errol/index.zig"); | |
| 8 | 8 | |
| 9 | 9 | const max_int_digits = 65; |
| 10 | 10 | |
| ... | ... | @@ -22,6 +22,8 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), |
| 22 | 22 | IntegerWidth, |
| 23 | 23 | Float, |
| 24 | 24 | FloatWidth, |
| 25 | FloatScientific, | |
| 26 | FloatScientificWidth, | |
| 25 | 27 | Character, |
| 26 | 28 | Buf, |
| 27 | 29 | BufWidth, |
| ... | ... | @@ -87,6 +89,9 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), |
| 87 | 89 | 's' => { |
| 88 | 90 | state = State.Buf; |
| 89 | 91 | }, |
| 92 | 'e' => { | |
| 93 | state = State.FloatScientific; | |
| 94 | }, | |
| 90 | 95 | '.' => { |
| 91 | 96 | state = State.Float; |
| 92 | 97 | }, |
| ... | ... | @@ -133,9 +138,33 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), |
| 133 | 138 | '0' ... '9' => {}, |
| 134 | 139 | else => @compileError("Unexpected character in format string: " ++ []u8{c}), |
| 135 | 140 | }, |
| 141 | State.FloatScientific => switch (c) { | |
| 142 | '}' => { | |
| 143 | try formatFloatScientific(args[next_arg], null, context, Errors, output); | |
| 144 | next_arg += 1; | |
| 145 | state = State.Start; | |
| 146 | start_index = i + 1; | |
| 147 | }, | |
| 148 | '0' ... '9' => { | |
| 149 | width_start = i; | |
| 150 | state = State.FloatScientificWidth; | |
| 151 | }, | |
| 152 | else => @compileError("Unexpected character in format string: " ++ []u8{c}), | |
| 153 | }, | |
| 154 | State.FloatScientificWidth => switch (c) { | |
| 155 | '}' => { | |
| 156 | width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable); | |
| 157 | try formatFloatScientific(args[next_arg], width, context, Errors, output); | |
| 158 | next_arg += 1; | |
| 159 | state = State.Start; | |
| 160 | start_index = i + 1; | |
| 161 | }, | |
| 162 | '0' ... '9' => {}, | |
| 163 | else => @compileError("Unexpected character in format string: " ++ []u8{c}), | |
| 164 | }, | |
| 136 | 165 | State.Float => switch (c) { |
| 137 | 166 | '}' => { |
| 138 | try formatFloatDecimal(args[next_arg], 0, context, Errors, output); | |
| 167 | try formatFloatDecimal(args[next_arg], null, context, Errors, output); | |
| 139 | 168 | next_arg += 1; |
| 140 | 169 | state = State.Start; |
| 141 | 170 | start_index = i + 1; |
| ... | ... | @@ -199,7 +228,7 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@ |
| 199 | 228 | return formatInt(value, 10, false, 0, context, Errors, output); |
| 200 | 229 | }, |
| 201 | 230 | builtin.TypeId.Float => { |
| 202 | return formatFloat(value, context, Errors, output); | |
| 231 | return formatFloatScientific(value, null, context, Errors, output); | |
| 203 | 232 | }, |
| 204 | 233 | builtin.TypeId.Void => { |
| 205 | 234 | return output(context, "void"); |
| ... | ... | @@ -257,81 +286,237 @@ pub fn formatBuf(buf: []const u8, width: usize, |
| 257 | 286 | } |
| 258 | 287 | } |
| 259 | 288 | |
| 260 | pub fn formatFloat(value: var, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { | |
| 289 | // Print a float in scientific notation to the specified precision. Null uses full precision. | |
| 290 | // It should be the case that every full precision, printed value can be re-parsed back to the | |
| 291 | // same type unambiguously. | |
| 292 | pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { | |
| 261 | 293 | var x = f64(value); |
| 262 | 294 | |
| 263 | 295 | // Errol doesn't handle these special cases. |
| 264 | if (math.isNan(x)) { | |
| 265 | return output(context, "NaN"); | |
| 266 | } | |
| 267 | 296 | if (math.signbit(x)) { |
| 268 | 297 | try output(context, "-"); |
| 269 | 298 | x = -x; |
| 270 | 299 | } |
| 300 | ||
| 301 | if (math.isNan(x)) { | |
| 302 | return output(context, "nan"); | |
| 303 | } | |
| 271 | 304 | if (math.isPositiveInf(x)) { |
| 272 | return output(context, "Infinity"); | |
| 305 | return output(context, "inf"); | |
| 273 | 306 | } |
| 274 | 307 | if (x == 0.0) { |
| 275 | return output(context, "0.0"); | |
| 308 | try output(context, "0"); | |
| 309 | ||
| 310 | if (maybe_precision) |precision| { | |
| 311 | if (precision != 0) { | |
| 312 | try output(context, "."); | |
| 313 | var i: usize = 0; | |
| 314 | while (i < precision) : (i += 1) { | |
| 315 | try output(context, "0"); | |
| 316 | } | |
| 317 | } | |
| 318 | } else { | |
| 319 | try output(context, ".0"); | |
| 320 | } | |
| 321 | ||
| 322 | try output(context, "e+00"); | |
| 323 | return; | |
| 276 | 324 | } |
| 277 | 325 | |
| 278 | 326 | var buffer: [32]u8 = undefined; |
| 279 | const float_decimal = errol3(x, buffer[0..]); | |
| 280 | try output(context, float_decimal.digits[0..1]); | |
| 281 | try output(context, "."); | |
| 282 | if (float_decimal.digits.len > 1) { | |
| 283 | const num_digits = if (@typeOf(value) == f32) | |
| 284 | math.min(usize(9), float_decimal.digits.len) | |
| 285 | else | |
| 286 | float_decimal.digits.len; | |
| 287 | try output(context, float_decimal.digits[1 .. num_digits]); | |
| 327 | var float_decimal = errol.errol3(x, buffer[0..]); | |
| 328 | ||
| 329 | if (maybe_precision) |precision| { | |
| 330 | errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific); | |
| 331 | ||
| 332 | try output(context, float_decimal.digits[0..1]); | |
| 333 | ||
| 334 | // {e0} case prints no `.` | |
| 335 | if (precision != 0) { | |
| 336 | try output(context, "."); | |
| 337 | ||
| 338 | var printed: usize = 0; | |
| 339 | if (float_decimal.digits.len > 1) { | |
| 340 | const num_digits = math.min(float_decimal.digits.len, precision + 1); | |
| 341 | try output(context, float_decimal.digits[1 .. num_digits]); | |
| 342 | printed += num_digits - 1; | |
| 343 | } | |
| 344 | ||
| 345 | while (printed < precision) : (printed += 1) { | |
| 346 | try output(context, "0"); | |
| 347 | } | |
| 348 | } | |
| 288 | 349 | } else { |
| 289 | try output(context, "0"); | |
| 350 | try output(context, float_decimal.digits[0..1]); | |
| 351 | try output(context, "."); | |
| 352 | if (float_decimal.digits.len > 1) { | |
| 353 | const num_digits = if (@typeOf(value) == f32) | |
| 354 | math.min(usize(9), float_decimal.digits.len) | |
| 355 | else | |
| 356 | float_decimal.digits.len; | |
| 357 | ||
| 358 | try output(context, float_decimal.digits[1 .. num_digits]); | |
| 359 | } else { | |
| 360 | try output(context, "0"); | |
| 361 | } | |
| 290 | 362 | } |
| 291 | 363 | |
| 292 | if (float_decimal.exp != 1) { | |
| 293 | try output(context, "e"); | |
| 294 | try formatInt(float_decimal.exp - 1, 10, false, 0, context, Errors, output); | |
| 364 | try output(context, "e"); | |
| 365 | const exp = float_decimal.exp - 1; | |
| 366 | ||
| 367 | if (exp >= 0) { | |
| 368 | try output(context, "+"); | |
| 369 | if (exp > -10 and exp < 10) { | |
| 370 | try output(context, "0"); | |
| 371 | } | |
| 372 | try formatInt(exp, 10, false, 0, context, Errors, output); | |
| 373 | } else { | |
| 374 | try output(context, "-"); | |
| 375 | if (exp > -10 and exp < 10) { | |
| 376 | try output(context, "0"); | |
| 377 | } | |
| 378 | try formatInt(-exp, 10, false, 0, context, Errors, output); | |
| 295 | 379 | } |
| 296 | 380 | } |
| 297 | 381 | |
| 298 | pub fn formatFloatDecimal(value: var, precision: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { | |
| 382 | // Print a float of the format x.yyyyy where the number of y is specified by the precision argument. | |
| 383 | // By default floats are printed at full precision (no rounding). | |
| 384 | pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { | |
| 299 | 385 | var x = f64(value); |
| 300 | 386 | |
| 301 | 387 | // Errol doesn't handle these special cases. |
| 302 | if (math.isNan(x)) { | |
| 303 | return output(context, "NaN"); | |
| 304 | } | |
| 305 | 388 | if (math.signbit(x)) { |
| 306 | 389 | try output(context, "-"); |
| 307 | 390 | x = -x; |
| 308 | 391 | } |
| 392 | ||
| 393 | if (math.isNan(x)) { | |
| 394 | return output(context, "nan"); | |
| 395 | } | |
| 309 | 396 | if (math.isPositiveInf(x)) { |
| 310 | return output(context, "Infinity"); | |
| 397 | return output(context, "inf"); | |
| 311 | 398 | } |
| 312 | 399 | if (x == 0.0) { |
| 313 | return output(context, "0.0"); | |
| 400 | try output(context, "0"); | |
| 401 | ||
| 402 | if (maybe_precision) |precision| { | |
| 403 | if (precision != 0) { | |
| 404 | try output(context, "."); | |
| 405 | var i: usize = 0; | |
| 406 | while (i < precision) : (i += 1) { | |
| 407 | try output(context, "0"); | |
| 408 | } | |
| 409 | } else { | |
| 410 | try output(context, ".0"); | |
| 411 | } | |
| 412 | } else { | |
| 413 | try output(context, "0"); | |
| 414 | } | |
| 415 | ||
| 416 | return; | |
| 314 | 417 | } |
| 315 | 418 | |
| 419 | // non-special case, use errol3 | |
| 316 | 420 | var buffer: [32]u8 = undefined; |
| 317 | const float_decimal = errol3(x, buffer[0..]); | |
| 318 | ||
| 319 | const num_left_digits = if (float_decimal.exp > 0) usize(float_decimal.exp) else 1; | |
| 320 | ||
| 321 | try output(context, float_decimal.digits[0 .. num_left_digits]); | |
| 322 | try output(context, "."); | |
| 323 | if (float_decimal.digits.len > 1) { | |
| 324 | const num_valid_digtis = if (@typeOf(value) == f32) math.min(usize(7), float_decimal.digits.len) | |
| 325 | else | |
| 326 | float_decimal.digits.len; | |
| 327 | ||
| 328 | const num_right_digits = if (precision != 0) | |
| 329 | math.min(precision, (num_valid_digtis-num_left_digits)) | |
| 330 | else | |
| 331 | num_valid_digtis - num_left_digits; | |
| 332 | try output(context, float_decimal.digits[num_left_digits .. (num_left_digits + num_right_digits)]); | |
| 421 | var float_decimal = errol.errol3(x, buffer[0..]); | |
| 422 | ||
| 423 | if (maybe_precision) |precision| { | |
| 424 | errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal); | |
| 425 | ||
| 426 | // exp < 0 means the leading is always 0 as errol result is normalized. | |
| 427 | var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0; | |
| 428 | ||
| 429 | // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. | |
| 430 | var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); | |
| 431 | ||
| 432 | if (num_digits_whole > 0) { | |
| 433 | // We may have to zero pad, for instance 1e4 requires zero padding. | |
| 434 | try output(context, float_decimal.digits[0 .. num_digits_whole_no_pad]); | |
| 435 | ||
| 436 | var i = num_digits_whole_no_pad; | |
| 437 | while (i < num_digits_whole) : (i += 1) { | |
| 438 | try output(context, "0"); | |
| 439 | } | |
| 440 | } else { | |
| 441 | try output(context , "0"); | |
| 442 | } | |
| 443 | ||
| 444 | // {.0} special case doesn't want a trailing '.' | |
| 445 | if (precision == 0) { | |
| 446 | return; | |
| 447 | } | |
| 448 | ||
| 449 | try output(context, "."); | |
| 450 | ||
| 451 | // Keep track of fractional count printed for case where we pre-pad then post-pad with 0's. | |
| 452 | var printed: usize = 0; | |
| 453 | ||
| 454 | // Zero-fill until we reach significant digits or run out of precision. | |
| 455 | if (float_decimal.exp <= 0) { | |
| 456 | const zero_digit_count = usize(-float_decimal.exp); | |
| 457 | const zeros_to_print = math.min(zero_digit_count, precision); | |
| 458 | ||
| 459 | var i: usize = 0; | |
| 460 | while (i < zeros_to_print) : (i += 1) { | |
| 461 | try output(context, "0"); | |
| 462 | printed += 1; | |
| 463 | } | |
| 464 | ||
| 465 | if (printed >= precision) { | |
| 466 | return; | |
| 467 | } | |
| 468 | } | |
| 469 | ||
| 470 | // Remaining fractional portion, zero-padding if insufficient. | |
| 471 | debug.assert(precision >= printed); | |
| 472 | if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) { | |
| 473 | try output(context, float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]); | |
| 474 | return; | |
| 475 | } else { | |
| 476 | try output(context, float_decimal.digits[num_digits_whole_no_pad ..]); | |
| 477 | printed += float_decimal.digits.len - num_digits_whole_no_pad; | |
| 478 | ||
| 479 | while (printed < precision) : (printed += 1) { | |
| 480 | try output(context, "0"); | |
| 481 | } | |
| 482 | } | |
| 333 | 483 | } else { |
| 334 | try output(context, "0"); | |
| 484 | // exp < 0 means the leading is always 0 as errol result is normalized. | |
| 485 | var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0; | |
| 486 | ||
| 487 | // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. | |
| 488 | var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); | |
| 489 | ||
| 490 | if (num_digits_whole > 0) { | |
| 491 | // We may have to zero pad, for instance 1e4 requires zero padding. | |
| 492 | try output(context, float_decimal.digits[0 .. num_digits_whole_no_pad]); | |
| 493 | ||
| 494 | var i = num_digits_whole_no_pad; | |
| 495 | while (i < num_digits_whole) : (i += 1) { | |
| 496 | try output(context, "0"); | |
| 497 | } | |
| 498 | } else { | |
| 499 | try output(context , "0"); | |
| 500 | } | |
| 501 | ||
| 502 | // Omit `.` if no fractional portion | |
| 503 | if (float_decimal.exp >= 0 and num_digits_whole_no_pad == float_decimal.digits.len) { | |
| 504 | return; | |
| 505 | } | |
| 506 | ||
| 507 | try output(context, "."); | |
| 508 | ||
| 509 | // Zero-fill until we reach significant digits or run out of precision. | |
| 510 | if (float_decimal.exp < 0) { | |
| 511 | const zero_digit_count = usize(-float_decimal.exp); | |
| 512 | ||
| 513 | var i: usize = 0; | |
| 514 | while (i < zero_digit_count) : (i += 1) { | |
| 515 | try output(context, "0"); | |
| 516 | } | |
| 517 | } | |
| 518 | ||
| 519 | try output(context, float_decimal.digits[num_digits_whole_no_pad ..]); | |
| 335 | 520 | } |
| 336 | 521 | } |
| 337 | 522 | |
| ... | ... | @@ -594,70 +779,210 @@ test "fmt.format" { |
| 594 | 779 | const result = try bufPrint(buf1[0..], "pointer: {}\n", &value); |
| 595 | 780 | assert(mem.startsWith(u8, result, "pointer: Struct@")); |
| 596 | 781 | } |
| 597 | ||
| 598 | // TODO get these tests passing in release modes | |
| 599 | // https://github.com/zig-lang/zig/issues/564 | |
| 600 | if (builtin.mode == builtin.Mode.Debug) { | |
| 601 | { | |
| 602 | var buf1: [32]u8 = undefined; | |
| 603 | const value: f32 = 12.34; | |
| 604 | const result = try bufPrint(buf1[0..], "f32: {}\n", value); | |
| 605 | assert(mem.eql(u8, result, "f32: 1.23400001e1\n")); | |
| 606 | } | |
| 607 | { | |
| 608 | var buf1: [32]u8 = undefined; | |
| 609 | const value: f64 = -12.34e10; | |
| 610 | const result = try bufPrint(buf1[0..], "f64: {}\n", value); | |
| 611 | assert(mem.eql(u8, result, "f64: -1.234e11\n")); | |
| 612 | } | |
| 613 | { | |
| 614 | var buf1: [32]u8 = undefined; | |
| 615 | const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64); | |
| 616 | assert(mem.eql(u8, result, "f64: NaN\n")); | |
| 617 | } | |
| 618 | { | |
| 619 | var buf1: [32]u8 = undefined; | |
| 620 | const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64); | |
| 621 | assert(mem.eql(u8, result, "f64: Infinity\n")); | |
| 622 | } | |
| 623 | { | |
| 624 | var buf1: [32]u8 = undefined; | |
| 625 | const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64); | |
| 626 | assert(mem.eql(u8, result, "f64: -Infinity\n")); | |
| 627 | } | |
| 628 | { | |
| 629 | var buf1: [32]u8 = undefined; | |
| 630 | const value: f32 = 1.1234; | |
| 631 | const result = try bufPrint(buf1[0..], "f32: {.1}\n", value); | |
| 632 | assert(mem.eql(u8, result, "f32: 1.1\n")); | |
| 633 | } | |
| 634 | { | |
| 635 | var buf1: [32]u8 = undefined; | |
| 636 | const value: f32 = 1234.567; | |
| 637 | const result = try bufPrint(buf1[0..], "f32: {.2}\n", value); | |
| 638 | assert(mem.eql(u8, result, "f32: 1234.56\n")); | |
| 639 | } | |
| 640 | { | |
| 641 | var buf1: [32]u8 = undefined; | |
| 642 | const value: f32 = -11.1234; | |
| 643 | const result = try bufPrint(buf1[0..], "f32: {.4}\n", value); | |
| 644 | // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). | |
| 645 | // -11.12339... is truncated to -11.1233 | |
| 646 | assert(mem.eql(u8, result, "f32: -11.1233\n")); | |
| 647 | } | |
| 648 | { | |
| 649 | var buf1: [32]u8 = undefined; | |
| 650 | const value: f32 = 91.12345; | |
| 651 | const result = try bufPrint(buf1[0..], "f32: {.}\n", value); | |
| 652 | assert(mem.eql(u8, result, "f32: 91.12345\n")); | |
| 653 | } | |
| 654 | { | |
| 782 | { | |
| 783 | var buf1: [32]u8 = undefined; | |
| 784 | const value: f32 = 1.34; | |
| 785 | const result = try bufPrint(buf1[0..], "f32: {e}\n", value); | |
| 786 | assert(mem.eql(u8, result, "f32: 1.34000003e+00\n")); | |
| 787 | } | |
| 788 | { | |
| 789 | var buf1: [32]u8 = undefined; | |
| 790 | const value: f32 = 12.34; | |
| 791 | const result = try bufPrint(buf1[0..], "f32: {e}\n", value); | |
| 792 | assert(mem.eql(u8, result, "f32: 1.23400001e+01\n")); | |
| 793 | } | |
| 794 | { | |
| 795 | var buf1: [32]u8 = undefined; | |
| 796 | const value: f64 = -12.34e10; | |
| 797 | const result = try bufPrint(buf1[0..], "f64: {e}\n", value); | |
| 798 | assert(mem.eql(u8, result, "f64: -1.234e+11\n")); | |
| 799 | } | |
| 800 | { | |
| 801 | // This fails on release due to a minor rounding difference. | |
| 802 | // --release-fast outputs 9.999960000000001e-40 vs. the expected. | |
| 803 | if (builtin.mode == builtin.Mode.Debug) { | |
| 655 | 804 | var buf1: [32]u8 = undefined; |
| 656 | const value: f64 = 91.12345678901235; | |
| 657 | const result = try bufPrint(buf1[0..], "f64: {.10}\n", value); | |
| 658 | assert(mem.eql(u8, result, "f64: 91.1234567890\n")); | |
| 805 | const value: f64 = 9.999960e-40; | |
| 806 | const result = try bufPrint(buf1[0..], "f64: {e}\n", value); | |
| 807 | assert(mem.eql(u8, result, "f64: 9.99996e-40\n")); | |
| 659 | 808 | } |
| 660 | ||
| 809 | } | |
| 810 | { | |
| 811 | var buf1: [32]u8 = undefined; | |
| 812 | const value: f64 = 1.409706e-42; | |
| 813 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); | |
| 814 | assert(mem.eql(u8, result, "f64: 1.40971e-42\n")); | |
| 815 | } | |
| 816 | { | |
| 817 | var buf1: [32]u8 = undefined; | |
| 818 | const value: f64 = @bitCast(f32, u32(814313563)); | |
| 819 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); | |
| 820 | assert(mem.eql(u8, result, "f64: 1.00000e-09\n")); | |
| 821 | } | |
| 822 | { | |
| 823 | var buf1: [32]u8 = undefined; | |
| 824 | const value: f64 = @bitCast(f32, u32(1006632960)); | |
| 825 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); | |
| 826 | assert(mem.eql(u8, result, "f64: 7.81250e-03\n")); | |
| 827 | } | |
| 828 | { | |
| 829 | // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. | |
| 830 | // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. | |
| 831 | var buf1: [32]u8 = undefined; | |
| 832 | const value: f64 = @bitCast(f32, u32(1203982400)); | |
| 833 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); | |
| 834 | assert(mem.eql(u8, result, "f64: 1.00001e+05\n")); | |
| 835 | } | |
| 836 | { | |
| 837 | var buf1: [32]u8 = undefined; | |
| 838 | const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64); | |
| 839 | assert(mem.eql(u8, result, "f64: nan\n")); | |
| 840 | } | |
| 841 | { | |
| 842 | var buf1: [32]u8 = undefined; | |
| 843 | const result = try bufPrint(buf1[0..], "f64: {}\n", -math.nan_f64); | |
| 844 | assert(mem.eql(u8, result, "f64: -nan\n")); | |
| 845 | } | |
| 846 | { | |
| 847 | var buf1: [32]u8 = undefined; | |
| 848 | const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64); | |
| 849 | assert(mem.eql(u8, result, "f64: inf\n")); | |
| 850 | } | |
| 851 | { | |
| 852 | var buf1: [32]u8 = undefined; | |
| 853 | const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64); | |
| 854 | assert(mem.eql(u8, result, "f64: -inf\n")); | |
| 855 | } | |
| 856 | { | |
| 857 | var buf1: [64]u8 = undefined; | |
| 858 | const value: f64 = 1.52314e+29; | |
| 859 | const result = try bufPrint(buf1[0..], "f64: {.}\n", value); | |
| 860 | assert(mem.eql(u8, result, "f64: 152314000000000000000000000000\n")); | |
| 861 | } | |
| 862 | { | |
| 863 | var buf1: [32]u8 = undefined; | |
| 864 | const value: f32 = 1.1234; | |
| 865 | const result = try bufPrint(buf1[0..], "f32: {.1}\n", value); | |
| 866 | assert(mem.eql(u8, result, "f32: 1.1\n")); | |
| 867 | } | |
| 868 | { | |
| 869 | var buf1: [32]u8 = undefined; | |
| 870 | const value: f32 = 1234.567; | |
| 871 | const result = try bufPrint(buf1[0..], "f32: {.2}\n", value); | |
| 872 | assert(mem.eql(u8, result, "f32: 1234.57\n")); | |
| 873 | } | |
| 874 | { | |
| 875 | var buf1: [32]u8 = undefined; | |
| 876 | const value: f32 = -11.1234; | |
| 877 | const result = try bufPrint(buf1[0..], "f32: {.4}\n", value); | |
| 878 | // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). | |
| 879 | // -11.12339... is rounded back up to -11.1234 | |
| 880 | assert(mem.eql(u8, result, "f32: -11.1234\n")); | |
| 881 | } | |
| 882 | { | |
| 883 | var buf1: [32]u8 = undefined; | |
| 884 | const value: f32 = 91.12345; | |
| 885 | const result = try bufPrint(buf1[0..], "f32: {.5}\n", value); | |
| 886 | assert(mem.eql(u8, result, "f32: 91.12345\n")); | |
| 887 | } | |
| 888 | { | |
| 889 | var buf1: [32]u8 = undefined; | |
| 890 | const value: f64 = 91.12345678901235; | |
| 891 | const result = try bufPrint(buf1[0..], "f64: {.10}\n", value); | |
| 892 | assert(mem.eql(u8, result, "f64: 91.1234567890\n")); | |
| 893 | } | |
| 894 | { | |
| 895 | var buf1: [32]u8 = undefined; | |
| 896 | const value: f64 = 0.0; | |
| 897 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | |
| 898 | assert(mem.eql(u8, result, "f64: 0.00000\n")); | |
| 899 | } | |
| 900 | { | |
| 901 | var buf1: [32]u8 = undefined; | |
| 902 | const value: f64 = 5.700; | |
| 903 | const result = try bufPrint(buf1[0..], "f64: {.0}\n", value); | |
| 904 | assert(mem.eql(u8, result, "f64: 6\n")); | |
| 905 | } | |
| 906 | { | |
| 907 | var buf1: [32]u8 = undefined; | |
| 908 | const value: f64 = 9.999; | |
| 909 | const result = try bufPrint(buf1[0..], "f64: {.1}\n", value); | |
| 910 | assert(mem.eql(u8, result, "f64: 10.0\n")); | |
| 911 | } | |
| 912 | { | |
| 913 | var buf1: [32]u8 = undefined; | |
| 914 | const value: f64 = 1.0; | |
| 915 | const result = try bufPrint(buf1[0..], "f64: {.3}\n", value); | |
| 916 | assert(mem.eql(u8, result, "f64: 1.000\n")); | |
| 917 | } | |
| 918 | { | |
| 919 | var buf1: [32]u8 = undefined; | |
| 920 | const value: f64 = 0.0003; | |
| 921 | const result = try bufPrint(buf1[0..], "f64: {.8}\n", value); | |
| 922 | assert(mem.eql(u8, result, "f64: 0.00030000\n")); | |
| 923 | } | |
| 924 | { | |
| 925 | var buf1: [32]u8 = undefined; | |
| 926 | const value: f64 = 1.40130e-45; | |
| 927 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | |
| 928 | assert(mem.eql(u8, result, "f64: 0.00000\n")); | |
| 929 | } | |
| 930 | { | |
| 931 | var buf1: [32]u8 = undefined; | |
| 932 | const value: f64 = 9.999960e-40; | |
| 933 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | |
| 934 | assert(mem.eql(u8, result, "f64: 0.00000\n")); | |
| 935 | } | |
| 936 | // libc checks | |
| 937 | { | |
| 938 | var buf1: [32]u8 = undefined; | |
| 939 | const value: f64 = f64(@bitCast(f32, u32(916964781))); | |
| 940 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | |
| 941 | assert(mem.eql(u8, result, "f64: 0.00001\n")); | |
| 942 | } | |
| 943 | { | |
| 944 | var buf1: [32]u8 = undefined; | |
| 945 | const value: f64 = f64(@bitCast(f32, u32(925353389))); | |
| 946 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | |
| 947 | assert(mem.eql(u8, result, "f64: 0.00001\n")); | |
| 948 | } | |
| 949 | { | |
| 950 | var buf1: [32]u8 = undefined; | |
| 951 | const value: f64 = f64(@bitCast(f32, u32(1036831278))); | |
| 952 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | |
| 953 | assert(mem.eql(u8, result, "f64: 0.10000\n")); | |
| 954 | } | |
| 955 | { | |
| 956 | var buf1: [32]u8 = undefined; | |
| 957 | const value: f64 = f64(@bitCast(f32, u32(1065353133))); | |
| 958 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | |
| 959 | assert(mem.eql(u8, result, "f64: 1.00000\n")); | |
| 960 | } | |
| 961 | { | |
| 962 | var buf1: [32]u8 = undefined; | |
| 963 | const value: f64 = f64(@bitCast(f32, u32(1092616192))); | |
| 964 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | |
| 965 | assert(mem.eql(u8, result, "f64: 10.00000\n")); | |
| 966 | } | |
| 967 | // libc differences | |
| 968 | { | |
| 969 | var buf1: [32]u8 = undefined; | |
| 970 | // This is 0.015625 exactly according to gdb. We thus round down, | |
| 971 | // however glibc rounds up for some reason. This occurs for all | |
| 972 | // floats of the form x.yyyy25 on a precision point. | |
| 973 | const value: f64 = f64(@bitCast(f32, u32(1015021568))); | |
| 974 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | |
| 975 | assert(mem.eql(u8, result, "f64: 0.01563\n")); | |
| 976 | } | |
| 977 | // std-windows-x86_64-Debug-bare test case fails | |
| 978 | { | |
| 979 | // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 | |
| 980 | // also rounds to 630 so I'm inclined to believe libc is not | |
| 981 | // optimal here. | |
| 982 | var buf1: [32]u8 = undefined; | |
| 983 | const value: f64 = f64(@bitCast(f32, u32(1518338049))); | |
| 984 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | |
| 985 | assert(mem.eql(u8, result, "f64: 18014400656965630.00000\n")); | |
| 661 | 986 | } |
| 662 | 987 | } |
| 663 | 988 |
std/heap.zig+59-11| ... | ... | @@ -47,13 +47,6 @@ pub const DirectAllocator = struct { |
| 47 | 47 | |
| 48 | 48 | const HeapHandle = if (builtin.os == Os.windows) os.windows.HANDLE else void; |
| 49 | 49 | |
| 50 | //pub const canary_bytes = []u8 {48, 239, 128, 46, 18, 49, 147, 9, 195, 59, 203, 3, 245, 54, 9, 122}; | |
| 51 | //pub const want_safety = switch (builtin.mode) { | |
| 52 | // builtin.Mode.Debug => true, | |
| 53 | // builtin.Mode.ReleaseSafe => true, | |
| 54 | // else => false, | |
| 55 | //}; | |
| 56 | ||
| 57 | 50 | pub fn init() DirectAllocator { |
| 58 | 51 | return DirectAllocator { |
| 59 | 52 | .allocator = Allocator { |
| ... | ... | @@ -98,7 +91,7 @@ pub const DirectAllocator = struct { |
| 98 | 91 | const unused_start = addr; |
| 99 | 92 | const unused_len = aligned_addr - 1 - unused_start; |
| 100 | 93 | |
| 101 | var err = p.munmap(@intToPtr(&u8, unused_start), unused_len); | |
| 94 | var err = p.munmap(unused_start, unused_len); | |
| 102 | 95 | debug.assert(p.getErrno(err) == 0); |
| 103 | 96 | |
| 104 | 97 | //It is impossible that there is an unoccupied page at the top of our |
| ... | ... | @@ -139,7 +132,7 @@ pub const DirectAllocator = struct { |
| 139 | 132 | const rem = @rem(new_addr_end, os.page_size); |
| 140 | 133 | const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem); |
| 141 | 134 | if (old_addr_end > new_addr_end_rounded) { |
| 142 | _ = os.posix.munmap(@intToPtr(&u8, new_addr_end_rounded), old_addr_end - new_addr_end_rounded); | |
| 135 | _ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded); | |
| 143 | 136 | } |
| 144 | 137 | return old_mem[0..new_size]; |
| 145 | 138 | } |
| ... | ... | @@ -177,7 +170,7 @@ pub const DirectAllocator = struct { |
| 177 | 170 | |
| 178 | 171 | switch (builtin.os) { |
| 179 | 172 | Os.linux, Os.macosx, Os.ios => { |
| 180 | _ = os.posix.munmap(bytes.ptr, bytes.len); | |
| 173 | _ = os.posix.munmap(@ptrToInt(bytes.ptr), bytes.len); | |
| 181 | 174 | }, |
| 182 | 175 | Os.windows => { |
| 183 | 176 | const record_addr = @ptrToInt(bytes.ptr) + bytes.len; |
| ... | ... | @@ -298,7 +291,7 @@ pub const FixedBufferAllocator = struct { |
| 298 | 291 | |
| 299 | 292 | fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 { |
| 300 | 293 | const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator); |
| 301 | const addr = @ptrToInt(&self.buffer[self.end_index]); | |
| 294 | const addr = @ptrToInt(self.buffer.ptr) + self.end_index; | |
| 302 | 295 | const rem = @rem(addr, alignment); |
| 303 | 296 | const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); |
| 304 | 297 | const adjusted_index = self.end_index + march_forward_bytes; |
| ... | ... | @@ -325,6 +318,54 @@ pub const FixedBufferAllocator = struct { |
| 325 | 318 | fn free(allocator: &Allocator, bytes: []u8) void { } |
| 326 | 319 | }; |
| 327 | 320 | |
| 321 | /// lock free | |
| 322 | pub const ThreadSafeFixedBufferAllocator = struct { | |
| 323 | allocator: Allocator, | |
| 324 | end_index: usize, | |
| 325 | buffer: []u8, | |
| 326 | ||
| 327 | pub fn init(buffer: []u8) ThreadSafeFixedBufferAllocator { | |
| 328 | return ThreadSafeFixedBufferAllocator { | |
| 329 | .allocator = Allocator { | |
| 330 | .allocFn = alloc, | |
| 331 | .reallocFn = realloc, | |
| 332 | .freeFn = free, | |
| 333 | }, | |
| 334 | .buffer = buffer, | |
| 335 | .end_index = 0, | |
| 336 | }; | |
| 337 | } | |
| 338 | ||
| 339 | fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 { | |
| 340 | const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator); | |
| 341 | var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst); | |
| 342 | while (true) { | |
| 343 | const addr = @ptrToInt(self.buffer.ptr) + end_index; | |
| 344 | const rem = @rem(addr, alignment); | |
| 345 | const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); | |
| 346 | const adjusted_index = end_index + march_forward_bytes; | |
| 347 | const new_end_index = adjusted_index + n; | |
| 348 | if (new_end_index > self.buffer.len) { | |
| 349 | return error.OutOfMemory; | |
| 350 | } | |
| 351 | end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, | |
| 352 | builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) ?? return self.buffer[adjusted_index .. new_end_index]; | |
| 353 | } | |
| 354 | } | |
| 355 | ||
| 356 | fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { | |
| 357 | if (new_size <= old_mem.len) { | |
| 358 | return old_mem[0..new_size]; | |
| 359 | } else { | |
| 360 | const result = try alloc(allocator, new_size, alignment); | |
| 361 | mem.copy(u8, result, old_mem); | |
| 362 | return result; | |
| 363 | } | |
| 364 | } | |
| 365 | ||
| 366 | fn free(allocator: &Allocator, bytes: []u8) void { } | |
| 367 | }; | |
| 368 | ||
| 328 | 369 | |
| 329 | 370 | |
| 330 | 371 | test "c_allocator" { |
| ... | ... | @@ -363,6 +404,13 @@ test "FixedBufferAllocator" { |
| 363 | 404 | try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator); |
| 364 | 405 | } |
| 365 | 406 | |
| 407 | test "ThreadSafeFixedBufferAllocator" { | |
| 408 | var fixed_buffer_allocator = ThreadSafeFixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]); | |
| 409 | ||
| 410 | try testAllocator(&fixed_buffer_allocator.allocator); | |
| 411 | try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator); | |
| 412 | } | |
| 413 | ||
| 366 | 414 | fn testAllocator(allocator: &mem.Allocator) !void { |
| 367 | 415 | var slice = try allocator.alloc(&i32, 100); |
| 368 | 416 |
std/index.zig+2| ... | ... | @@ -8,6 +8,7 @@ pub const HashMap = @import("hash_map.zig").HashMap; |
| 8 | 8 | pub const LinkedList = @import("linked_list.zig").LinkedList; |
| 9 | 9 | pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList; |
| 10 | 10 | |
| 11 | pub const atomic = @import("atomic/index.zig"); | |
| 11 | 12 | pub const base64 = @import("base64.zig"); |
| 12 | 13 | pub const build = @import("build.zig"); |
| 13 | 14 | pub const c = @import("c/index.zig"); |
| ... | ... | @@ -34,6 +35,7 @@ pub const zig = @import("zig/index.zig"); |
| 34 | 35 | |
| 35 | 36 | test "std" { |
| 36 | 37 | // run tests from these |
| 38 | _ = @import("atomic/index.zig"); | |
| 37 | 39 | _ = @import("array_list.zig"); |
| 38 | 40 | _ = @import("buf_map.zig"); |
| 39 | 41 | _ = @import("buf_set.zig"); |
std/mem.zig+17-2| ... | ... | @@ -32,10 +32,25 @@ pub const Allocator = struct { |
| 32 | 32 | freeFn: fn (self: &Allocator, old_mem: []u8) void, |
| 33 | 33 | |
| 34 | 34 | fn create(self: &Allocator, comptime T: type) !&T { |
| 35 | if (@sizeOf(T) == 0) return &{}; | |
| 35 | 36 | const slice = try self.alloc(T, 1); |
| 36 | 37 | return &slice[0]; |
| 37 | 38 | } |
| 38 | 39 | |
| 40 | // TODO once #733 is solved, this will replace create | |
| 41 | fn construct(self: &Allocator, init: var) t: { | |
| 42 | // TODO this is a workaround for type getting parsed as Error!&const T | |
| 43 | const T = @typeOf(init).Child; | |
| 44 | break :t Error!&T; | |
| 45 | } { | |
| 46 | const T = @typeOf(init).Child; | |
| 47 | if (@sizeOf(T) == 0) return &{}; | |
| 48 | const slice = try self.alloc(T, 1); | |
| 49 | const ptr = &slice[0]; | |
| 50 | *ptr = *init; | |
| 51 | return ptr; | |
| 52 | } | |
| 53 | ||
| 39 | 54 | fn destroy(self: &Allocator, ptr: var) void { |
| 40 | 55 | self.free(ptr[0..1]); |
| 41 | 56 | } |
| ... | ... | @@ -53,7 +68,7 @@ pub const Allocator = struct { |
| 53 | 68 | const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory; |
| 54 | 69 | const byte_slice = try self.allocFn(self, byte_count, alignment); |
| 55 | 70 | assert(byte_slice.len == byte_count); |
| 56 | // This loop should get optimized out in ReleaseFast mode | |
| 71 | // This loop gets optimized out in ReleaseFast mode | |
| 57 | 72 | for (byte_slice) |*byte| { |
| 58 | 73 | *byte = undefined; |
| 59 | 74 | } |
| ... | ... | @@ -80,7 +95,7 @@ pub const Allocator = struct { |
| 80 | 95 | const byte_slice = try self.reallocFn(self, old_byte_slice, byte_count, alignment); |
| 81 | 96 | assert(byte_slice.len == byte_count); |
| 82 | 97 | if (n > old_mem.len) { |
| 83 | // This loop should get optimized out in ReleaseFast mode | |
| 98 | // This loop gets optimized out in ReleaseFast mode | |
| 84 | 99 | for (byte_slice[old_byte_slice.len..]) |*byte| { |
| 85 | 100 | *byte = undefined; |
| 86 | 101 | } |
std/os/darwin.zig+4-4| ... | ... | @@ -184,7 +184,7 @@ pub fn write(fd: i32, buf: &const u8, nbyte: usize) usize { |
| 184 | 184 | return errnoWrap(c.write(fd, @ptrCast(&const c_void, buf), nbyte)); |
| 185 | 185 | } |
| 186 | 186 | |
| 187 | pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, | |
| 187 | pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32, | |
| 188 | 188 | offset: isize) usize |
| 189 | 189 | { |
| 190 | 190 | const ptr_result = c.mmap(@ptrCast(&c_void, address), length, |
| ... | ... | @@ -193,8 +193,8 @@ pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, |
| 193 | 193 | return errnoWrap(isize_result); |
| 194 | 194 | } |
| 195 | 195 | |
| 196 | pub fn munmap(address: &u8, length: usize) usize { | |
| 197 | return errnoWrap(c.munmap(@ptrCast(&c_void, address), length)); | |
| 196 | pub fn munmap(address: usize, length: usize) usize { | |
| 197 | return errnoWrap(c.munmap(@intToPtr(&c_void, address), length)); | |
| 198 | 198 | } |
| 199 | 199 | |
| 200 | 200 | pub fn unlink(path: &const u8) usize { |
| ... | ... | @@ -341,4 +341,4 @@ pub const timeval = c.timeval; |
| 341 | 341 | pub const mach_timebase_info_data = c.mach_timebase_info_data; |
| 342 | 342 | |
| 343 | 343 | pub const mach_absolute_time = c.mach_absolute_time; |
| 344 | pub const mach_timebase_info = c.mach_timebase_info; | |
| \ No newline at end of file | ||
| 344 | pub const mach_timebase_info = c.mach_timebase_info; |
std/os/index.zig+357-170| ... | ... | @@ -2,6 +2,11 @@ const std = @import("../index.zig"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const Os = builtin.Os; |
| 4 | 4 | const is_windows = builtin.os == Os.windows; |
| 5 | const is_posix = switch (builtin.os) { | |
| 6 | builtin.Os.linux, | |
| 7 | builtin.Os.macosx => true, | |
| 8 | else => false, | |
| 9 | }; | |
| 5 | 10 | const os = this; |
| 6 | 11 | |
| 7 | 12 | test "std.os" { |
| ... | ... | @@ -20,9 +25,10 @@ pub const windows = @import("windows/index.zig"); |
| 20 | 25 | pub const darwin = @import("darwin.zig"); |
| 21 | 26 | pub const linux = @import("linux/index.zig"); |
| 22 | 27 | pub const zen = @import("zen.zig"); |
| 23 | pub const posix = switch(builtin.os) { | |
| 28 | pub const posix = switch (builtin.os) { | |
| 24 | 29 | Os.linux => linux, |
| 25 | Os.macosx, Os.ios => darwin, | |
| 30 | Os.macosx, | |
| 31 | Os.ios => darwin, | |
| 26 | 32 | Os.zen => zen, |
| 27 | 33 | else => @compileError("Unsupported OS"), |
| 28 | 34 | }; |
| ... | ... | @@ -54,7 +60,7 @@ pub const windowsWrite = windows_util.windowsWrite; |
| 54 | 60 | pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty; |
| 55 | 61 | pub const windowsOpen = windows_util.windowsOpen; |
| 56 | 62 | pub const windowsLoadDll = windows_util.windowsLoadDll; |
| 57 | pub const windowsUnloadDll = windows_util.windowsUnloadDll; | |
| 63 | pub const windowsUnloadDll = windows_util.windowsUnloadDll; | |
| 58 | 64 | pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock; |
| 59 | 65 | |
| 60 | 66 | pub const WindowsWaitError = windows_util.WaitError; |
| ... | ... | @@ -93,9 +99,9 @@ pub fn getRandomBytes(buf: []u8) !void { |
| 93 | 99 | switch (err) { |
| 94 | 100 | posix.EINVAL => unreachable, |
| 95 | 101 | posix.EFAULT => unreachable, |
| 96 | posix.EINTR => continue, | |
| 102 | posix.EINTR => continue, | |
| 97 | 103 | posix.ENOSYS => { |
| 98 | const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY|posix.O_CLOEXEC, 0); | |
| 104 | const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0); | |
| 99 | 105 | defer close(fd); |
| 100 | 106 | |
| 101 | 107 | try posixRead(fd, buf); |
| ... | ... | @@ -106,8 +112,9 @@ pub fn getRandomBytes(buf: []u8) !void { |
| 106 | 112 | } |
| 107 | 113 | return; |
| 108 | 114 | }, |
| 109 | Os.macosx, Os.ios => { | |
| 110 | const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY|posix.O_CLOEXEC, 0); | |
| 115 | Os.macosx, | |
| 116 | Os.ios => { | |
| 117 | const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0); | |
| 111 | 118 | defer close(fd); |
| 112 | 119 | |
| 113 | 120 | try posixRead(fd, buf); |
| ... | ... | @@ -130,7 +137,20 @@ pub fn getRandomBytes(buf: []u8) !void { |
| 130 | 137 | } |
| 131 | 138 | }, |
| 132 | 139 | Os.zen => { |
| 133 | const randomness = []u8 {42, 1, 7, 12, 22, 17, 99, 16, 26, 87, 41, 45}; | |
| 140 | const randomness = []u8 { | |
| 141 | 42, | |
| 142 | 1, | |
| 143 | 7, | |
| 144 | 12, | |
| 145 | 22, | |
| 146 | 17, | |
| 147 | 99, | |
| 148 | 16, | |
| 149 | 26, | |
| 150 | 87, | |
| 151 | 41, | |
| 152 | 45, | |
| 153 | }; | |
| 134 | 154 | var i: usize = 0; |
| 135 | 155 | while (i < buf.len) : (i += 1) { |
| 136 | 156 | if (i > randomness.len) return error.Unknown; |
| ... | ... | @@ -155,7 +175,9 @@ pub fn abort() noreturn { |
| 155 | 175 | c.abort(); |
| 156 | 176 | } |
| 157 | 177 | switch (builtin.os) { |
| 158 | Os.linux, Os.macosx, Os.ios => { | |
| 178 | Os.linux, | |
| 179 | Os.macosx, | |
| 180 | Os.ios => { | |
| 159 | 181 | _ = posix.raise(posix.SIGABRT); |
| 160 | 182 | _ = posix.raise(posix.SIGKILL); |
| 161 | 183 | while (true) {} |
| ... | ... | @@ -177,7 +199,9 @@ pub fn exit(status: u8) noreturn { |
| 177 | 199 | c.exit(status); |
| 178 | 200 | } |
| 179 | 201 | switch (builtin.os) { |
| 180 | Os.linux, Os.macosx, Os.ios => { | |
| 202 | Os.linux, | |
| 203 | Os.macosx, | |
| 204 | Os.ios => { | |
| 181 | 205 | posix.exit(status); |
| 182 | 206 | }, |
| 183 | 207 | Os.windows => { |
| ... | ... | @@ -226,12 +250,14 @@ pub fn posixRead(fd: i32, buf: []u8) !void { |
| 226 | 250 | if (err > 0) { |
| 227 | 251 | return switch (err) { |
| 228 | 252 | posix.EINTR => continue, |
| 229 | posix.EINVAL, posix.EFAULT => unreachable, | |
| 253 | posix.EINVAL, | |
| 254 | posix.EFAULT => unreachable, | |
| 230 | 255 | posix.EAGAIN => error.WouldBlock, |
| 231 | 256 | posix.EBADF => error.FileClosed, |
| 232 | 257 | posix.EIO => error.InputOutput, |
| 233 | 258 | posix.EISDIR => error.IsDir, |
| 234 | posix.ENOBUFS, posix.ENOMEM => error.SystemResources, | |
| 259 | posix.ENOBUFS, | |
| 260 | posix.ENOMEM => error.SystemResources, | |
| 235 | 261 | else => unexpectedErrorPosix(err), |
| 236 | 262 | }; |
| 237 | 263 | } |
| ... | ... | @@ -265,18 +291,19 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void { |
| 265 | 291 | const write_err = posix.getErrno(rc); |
| 266 | 292 | if (write_err > 0) { |
| 267 | 293 | return switch (write_err) { |
| 268 | posix.EINTR => continue, | |
| 269 | posix.EINVAL, posix.EFAULT => unreachable, | |
| 294 | posix.EINTR => continue, | |
| 295 | posix.EINVAL, | |
| 296 | posix.EFAULT => unreachable, | |
| 270 | 297 | posix.EAGAIN => PosixWriteError.WouldBlock, |
| 271 | 298 | posix.EBADF => PosixWriteError.FileClosed, |
| 272 | 299 | posix.EDESTADDRREQ => PosixWriteError.DestinationAddressRequired, |
| 273 | 300 | posix.EDQUOT => PosixWriteError.DiskQuota, |
| 274 | posix.EFBIG => PosixWriteError.FileTooBig, | |
| 275 | posix.EIO => PosixWriteError.InputOutput, | |
| 301 | posix.EFBIG => PosixWriteError.FileTooBig, | |
| 302 | posix.EIO => PosixWriteError.InputOutput, | |
| 276 | 303 | posix.ENOSPC => PosixWriteError.NoSpaceLeft, |
| 277 | posix.EPERM => PosixWriteError.AccessDenied, | |
| 278 | posix.EPIPE => PosixWriteError.BrokenPipe, | |
| 279 | else => unexpectedErrorPosix(write_err), | |
| 304 | posix.EPERM => PosixWriteError.AccessDenied, | |
| 305 | posix.EPIPE => PosixWriteError.BrokenPipe, | |
| 306 | else => unexpectedErrorPosix(write_err), | |
| 280 | 307 | }; |
| 281 | 308 | } |
| 282 | 309 | index += rc; |
| ... | ... | @@ -322,7 +349,8 @@ pub fn posixOpenC(file_path: &const u8, flags: u32, perm: usize) !i32 { |
| 322 | 349 | posix.EFAULT => unreachable, |
| 323 | 350 | posix.EINVAL => unreachable, |
| 324 | 351 | posix.EACCES => return PosixOpenError.AccessDenied, |
| 325 | posix.EFBIG, posix.EOVERFLOW => return PosixOpenError.FileTooBig, | |
| 352 | posix.EFBIG, | |
| 353 | posix.EOVERFLOW => return PosixOpenError.FileTooBig, | |
| 326 | 354 | posix.EISDIR => return PosixOpenError.IsDir, |
| 327 | 355 | posix.ELOOP => return PosixOpenError.SymLinkLoop, |
| 328 | 356 | posix.EMFILE => return PosixOpenError.ProcessFdQuotaExceeded, |
| ... | ... | @@ -347,7 +375,8 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) !void { |
| 347 | 375 | const err = posix.getErrno(posix.dup2(old_fd, new_fd)); |
| 348 | 376 | if (err > 0) { |
| 349 | 377 | return switch (err) { |
| 350 | posix.EBUSY, posix.EINTR => continue, | |
| 378 | posix.EBUSY, | |
| 379 | posix.EINTR => continue, | |
| 351 | 380 | posix.EMFILE => error.ProcessFdQuotaExceeded, |
| 352 | 381 | posix.EINVAL => unreachable, |
| 353 | 382 | else => unexpectedErrorPosix(err), |
| ... | ... | @@ -382,7 +411,7 @@ pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap) |
| 382 | 411 | |
| 383 | 412 | pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void { |
| 384 | 413 | for (envp_buf) |env| { |
| 385 | const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break; | |
| 414 | const env_buf = if (env) |ptr| ptr[0..cstr.len(ptr) + 1] else break; | |
| 386 | 415 | allocator.free(env_buf); |
| 387 | 416 | } |
| 388 | 417 | allocator.free(envp_buf); |
| ... | ... | @@ -393,9 +422,7 @@ pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void { |
| 393 | 422 | /// pointers after the args and after the environment variables. |
| 394 | 423 | /// `argv[0]` is the executable path. |
| 395 | 424 | /// This function also uses the PATH environment variable to get the full path to the executable. |
| 396 | pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, | |
| 397 | allocator: &Allocator) !void | |
| 398 | { | |
| 425 | pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, allocator: &Allocator) !void { | |
| 399 | 426 | const argv_buf = try allocator.alloc(?&u8, argv.len + 1); |
| 400 | 427 | mem.set(?&u8, argv_buf, null); |
| 401 | 428 | defer { |
| ... | ... | @@ -434,7 +461,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, |
| 434 | 461 | while (it.next()) |search_path| { |
| 435 | 462 | mem.copy(u8, path_buf, search_path); |
| 436 | 463 | path_buf[search_path.len] = '/'; |
| 437 | mem.copy(u8, path_buf[search_path.len + 1 ..], exe_path); | |
| 464 | mem.copy(u8, path_buf[search_path.len + 1..], exe_path); | |
| 438 | 465 | path_buf[search_path.len + exe_path.len + 1] = 0; |
| 439 | 466 | err = posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr)); |
| 440 | 467 | assert(err > 0); |
| ... | ... | @@ -466,10 +493,17 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError { |
| 466 | 493 | assert(err > 0); |
| 467 | 494 | return switch (err) { |
| 468 | 495 | posix.EFAULT => unreachable, |
| 469 | posix.E2BIG, posix.EMFILE, posix.ENAMETOOLONG, posix.ENFILE, posix.ENOMEM => error.SystemResources, | |
| 470 | posix.EACCES, posix.EPERM => error.AccessDenied, | |
| 471 | posix.EINVAL, posix.ENOEXEC => error.InvalidExe, | |
| 472 | posix.EIO, posix.ELOOP => error.FileSystem, | |
| 496 | posix.E2BIG, | |
| 497 | posix.EMFILE, | |
| 498 | posix.ENAMETOOLONG, | |
| 499 | posix.ENFILE, | |
| 500 | posix.ENOMEM => error.SystemResources, | |
| 501 | posix.EACCES, | |
| 502 | posix.EPERM => error.AccessDenied, | |
| 503 | posix.EINVAL, | |
| 504 | posix.ENOEXEC => error.InvalidExe, | |
| 505 | posix.EIO, | |
| 506 | posix.ELOOP => error.FileSystem, | |
| 473 | 507 | posix.EISDIR => error.IsDir, |
| 474 | 508 | posix.ENOENT => error.FileNotFound, |
| 475 | 509 | posix.ENOTDIR => error.NotDir, |
| ... | ... | @@ -478,7 +512,7 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError { |
| 478 | 512 | }; |
| 479 | 513 | } |
| 480 | 514 | |
| 481 | pub var linux_aux_raw = []usize{0} ** 38; | |
| 515 | pub var linux_aux_raw = []usize {0} ** 38; | |
| 482 | 516 | pub var posix_environ_raw: []&u8 = undefined; |
| 483 | 517 | |
| 484 | 518 | /// Caller must free result when done. |
| ... | ... | @@ -492,8 +526,7 @@ pub fn getEnvMap(allocator: &Allocator) !BufMap { |
| 492 | 526 | |
| 493 | 527 | var i: usize = 0; |
| 494 | 528 | while (true) { |
| 495 | if (ptr[i] == 0) | |
| 496 | return result; | |
| 529 | if (ptr[i] == 0) return result; | |
| 497 | 530 | |
| 498 | 531 | const key_start = i; |
| 499 | 532 | |
| ... | ... | @@ -531,8 +564,7 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 { |
| 531 | 564 | var line_i: usize = 0; |
| 532 | 565 | while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} |
| 533 | 566 | const this_key = ptr[0..line_i]; |
| 534 | if (!mem.eql(u8, key, this_key)) | |
| 535 | continue; | |
| 567 | if (!mem.eql(u8, key, this_key)) continue; | |
| 536 | 568 | |
| 537 | 569 | var end_i: usize = line_i; |
| 538 | 570 | while (ptr[end_i] != 0) : (end_i += 1) {} |
| ... | ... | @@ -685,8 +717,10 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path: |
| 685 | 717 | const err = posix.getErrno(posix.symlink(existing_buf.ptr, new_buf.ptr)); |
| 686 | 718 | if (err > 0) { |
| 687 | 719 | return switch (err) { |
| 688 | posix.EFAULT, posix.EINVAL => unreachable, | |
| 689 | posix.EACCES, posix.EPERM => error.AccessDenied, | |
| 720 | posix.EFAULT, | |
| 721 | posix.EINVAL => unreachable, | |
| 722 | posix.EACCES, | |
| 723 | posix.EPERM => error.AccessDenied, | |
| 690 | 724 | posix.EDQUOT => error.DiskQuota, |
| 691 | 725 | posix.EEXIST => error.PathAlreadyExists, |
| 692 | 726 | posix.EIO => error.FileSystem, |
| ... | ... | @@ -703,9 +737,7 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path: |
| 703 | 737 | } |
| 704 | 738 | |
| 705 | 739 | // here we replace the standard +/ with -_ so that it can be used in a file name |
| 706 | const b64_fs_encoder = base64.Base64Encoder.init( | |
| 707 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", | |
| 708 | base64.standard_pad_char); | |
| 740 | const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char); | |
| 709 | 741 | |
| 710 | 742 | pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void { |
| 711 | 743 | if (symLink(allocator, existing_path, new_path)) { |
| ... | ... | @@ -724,7 +756,7 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: |
| 724 | 756 | tmp_path[dirname.len] = os.path.sep; |
| 725 | 757 | while (true) { |
| 726 | 758 | try getRandomBytes(rand_buf[0..]); |
| 727 | b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf); | |
| 759 | b64_fs_encoder.encode(tmp_path[dirname.len + 1..], rand_buf); | |
| 728 | 760 | |
| 729 | 761 | if (symLink(allocator, existing_path, tmp_path)) { |
| 730 | 762 | return rename(allocator, tmp_path, new_path); |
| ... | ... | @@ -733,7 +765,6 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: |
| 733 | 765 | else => return err, // TODO zig should know this set does not include PathAlreadyExists |
| 734 | 766 | } |
| 735 | 767 | } |
| 736 | ||
| 737 | 768 | } |
| 738 | 769 | |
| 739 | 770 | pub fn deleteFile(allocator: &Allocator, file_path: []const u8) !void { |
| ... | ... | @@ -756,7 +787,8 @@ pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) !void { |
| 756 | 787 | return switch (err) { |
| 757 | 788 | windows.ERROR.FILE_NOT_FOUND => error.FileNotFound, |
| 758 | 789 | windows.ERROR.ACCESS_DENIED => error.AccessDenied, |
| 759 | windows.ERROR.FILENAME_EXCED_RANGE, windows.ERROR.INVALID_PARAMETER => error.NameTooLong, | |
| 790 | windows.ERROR.FILENAME_EXCED_RANGE, | |
| 791 | windows.ERROR.INVALID_PARAMETER => error.NameTooLong, | |
| 760 | 792 | else => unexpectedErrorWindows(err), |
| 761 | 793 | }; |
| 762 | 794 | } |
| ... | ... | @@ -772,9 +804,11 @@ pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) !void { |
| 772 | 804 | const err = posix.getErrno(posix.unlink(buf.ptr)); |
| 773 | 805 | if (err > 0) { |
| 774 | 806 | return switch (err) { |
| 775 | posix.EACCES, posix.EPERM => error.AccessDenied, | |
| 807 | posix.EACCES, | |
| 808 | posix.EPERM => error.AccessDenied, | |
| 776 | 809 | posix.EBUSY => error.FileBusy, |
| 777 | posix.EFAULT, posix.EINVAL => unreachable, | |
| 810 | posix.EFAULT, | |
| 811 | posix.EINVAL => unreachable, | |
| 778 | 812 | posix.EIO => error.FileSystem, |
| 779 | 813 | posix.EISDIR => error.IsDir, |
| 780 | 814 | posix.ELOOP => error.SymLinkLoop, |
| ... | ... | @@ -852,7 +886,7 @@ pub const AtomicFile = struct { |
| 852 | 886 | |
| 853 | 887 | while (true) { |
| 854 | 888 | try getRandomBytes(rand_buf[0..]); |
| 855 | b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf); | |
| 889 | b64_fs_encoder.encode(tmp_path[dirname.len + 1..], rand_buf); | |
| 856 | 890 | |
| 857 | 891 | const file = os.File.openWriteNoClobber(allocator, tmp_path, mode) catch |err| switch (err) { |
| 858 | 892 | error.PathAlreadyExists => continue, |
| ... | ... | @@ -903,7 +937,7 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) |
| 903 | 937 | new_buf[new_path.len] = 0; |
| 904 | 938 | |
| 905 | 939 | if (is_windows) { |
| 906 | const flags = windows.MOVEFILE_REPLACE_EXISTING|windows.MOVEFILE_WRITE_THROUGH; | |
| 940 | const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH; | |
| 907 | 941 | if (windows.MoveFileExA(old_buf.ptr, new_buf.ptr, flags) == 0) { |
| 908 | 942 | const err = windows.GetLastError(); |
| 909 | 943 | return switch (err) { |
| ... | ... | @@ -914,10 +948,12 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) |
| 914 | 948 | const err = posix.getErrno(posix.rename(old_buf.ptr, new_buf.ptr)); |
| 915 | 949 | if (err > 0) { |
| 916 | 950 | return switch (err) { |
| 917 | posix.EACCES, posix.EPERM => error.AccessDenied, | |
| 951 | posix.EACCES, | |
| 952 | posix.EPERM => error.AccessDenied, | |
| 918 | 953 | posix.EBUSY => error.FileBusy, |
| 919 | 954 | posix.EDQUOT => error.DiskQuota, |
| 920 | posix.EFAULT, posix.EINVAL => unreachable, | |
| 955 | posix.EFAULT, | |
| 956 | posix.EINVAL => unreachable, | |
| 921 | 957 | posix.EISDIR => error.IsDir, |
| 922 | 958 | posix.ELOOP => error.SymLinkLoop, |
| 923 | 959 | posix.EMLINK => error.LinkQuotaExceeded, |
| ... | ... | @@ -926,7 +962,8 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8) |
| 926 | 962 | posix.ENOTDIR => error.NotDir, |
| 927 | 963 | posix.ENOMEM => error.SystemResources, |
| 928 | 964 | posix.ENOSPC => error.NoSpaceLeft, |
| 929 | posix.EEXIST, posix.ENOTEMPTY => error.PathAlreadyExists, | |
| 965 | posix.EEXIST, | |
| 966 | posix.ENOTEMPTY => error.PathAlreadyExists, | |
| 930 | 967 | posix.EROFS => error.ReadOnlyFileSystem, |
| 931 | 968 | posix.EXDEV => error.RenameAcrossMountPoints, |
| 932 | 969 | else => unexpectedErrorPosix(err), |
| ... | ... | @@ -964,7 +1001,8 @@ pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) !void { |
| 964 | 1001 | const err = posix.getErrno(posix.mkdir(path_buf.ptr, 0o755)); |
| 965 | 1002 | if (err > 0) { |
| 966 | 1003 | return switch (err) { |
| 967 | posix.EACCES, posix.EPERM => error.AccessDenied, | |
| 1004 | posix.EACCES, | |
| 1005 | posix.EPERM => error.AccessDenied, | |
| 968 | 1006 | posix.EDQUOT => error.DiskQuota, |
| 969 | 1007 | posix.EEXIST => error.PathAlreadyExists, |
| 970 | 1008 | posix.EFAULT => unreachable, |
| ... | ... | @@ -994,27 +1032,23 @@ pub fn makePath(allocator: &Allocator, full_path: []const u8) !void { |
| 994 | 1032 | // TODO stat the file and return an error if it's not a directory |
| 995 | 1033 | // this is important because otherwise a dangling symlink |
| 996 | 1034 | // could cause an infinite loop |
| 997 | if (end_index == resolved_path.len) | |
| 998 | return; | |
| 1035 | if (end_index == resolved_path.len) return; | |
| 999 | 1036 | } else if (err == error.FileNotFound) { |
| 1000 | 1037 | // march end_index backward until next path component |
| 1001 | 1038 | while (true) { |
| 1002 | 1039 | end_index -= 1; |
| 1003 | if (os.path.isSep(resolved_path[end_index])) | |
| 1004 | break; | |
| 1040 | if (os.path.isSep(resolved_path[end_index])) break; | |
| 1005 | 1041 | } |
| 1006 | 1042 | continue; |
| 1007 | 1043 | } else { |
| 1008 | 1044 | return err; |
| 1009 | 1045 | } |
| 1010 | 1046 | }; |
| 1011 | if (end_index == resolved_path.len) | |
| 1012 | return; | |
| 1047 | if (end_index == resolved_path.len) return; | |
| 1013 | 1048 | // march end_index forward until next path component |
| 1014 | 1049 | while (true) { |
| 1015 | 1050 | end_index += 1; |
| 1016 | if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index])) | |
| 1017 | break; | |
| 1051 | if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index])) break; | |
| 1018 | 1052 | } |
| 1019 | 1053 | } |
| 1020 | 1054 | } |
| ... | ... | @@ -1031,15 +1065,18 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void { |
| 1031 | 1065 | const err = posix.getErrno(posix.rmdir(path_buf.ptr)); |
| 1032 | 1066 | if (err > 0) { |
| 1033 | 1067 | return switch (err) { |
| 1034 | posix.EACCES, posix.EPERM => error.AccessDenied, | |
| 1068 | posix.EACCES, | |
| 1069 | posix.EPERM => error.AccessDenied, | |
| 1035 | 1070 | posix.EBUSY => error.FileBusy, |
| 1036 | posix.EFAULT, posix.EINVAL => unreachable, | |
| 1071 | posix.EFAULT, | |
| 1072 | posix.EINVAL => unreachable, | |
| 1037 | 1073 | posix.ELOOP => error.SymLinkLoop, |
| 1038 | 1074 | posix.ENAMETOOLONG => error.NameTooLong, |
| 1039 | 1075 | posix.ENOENT => error.FileNotFound, |
| 1040 | 1076 | posix.ENOMEM => error.SystemResources, |
| 1041 | 1077 | posix.ENOTDIR => error.NotDir, |
| 1042 | posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty, | |
| 1078 | posix.EEXIST, | |
| 1079 | posix.ENOTEMPTY => error.DirNotEmpty, | |
| 1043 | 1080 | posix.EROFS => error.ReadOnlyFileSystem, |
| 1044 | 1081 | else => unexpectedErrorPosix(err), |
| 1045 | 1082 | }; |
| ... | ... | @@ -1049,7 +1086,7 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void { |
| 1049 | 1086 | /// Whether ::full_path describes a symlink, file, or directory, this function |
| 1050 | 1087 | /// removes it. If it cannot be removed because it is a non-empty directory, |
| 1051 | 1088 | /// this function recursively removes its entries and then tries again. |
| 1052 | // TODO non-recursive implementation | |
| 1089 | /// TODO non-recursive implementation | |
| 1053 | 1090 | const DeleteTreeError = error { |
| 1054 | 1091 | OutOfMemory, |
| 1055 | 1092 | AccessDenied, |
| ... | ... | @@ -1091,8 +1128,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError! |
| 1091 | 1128 | error.NotDir, |
| 1092 | 1129 | error.FileSystem, |
| 1093 | 1130 | error.FileBusy, |
| 1094 | error.Unexpected | |
| 1095 | => return err, | |
| 1131 | error.Unexpected => return err, | |
| 1096 | 1132 | } |
| 1097 | 1133 | { |
| 1098 | 1134 | var dir = Dir.open(allocator, full_path) catch |err| switch (err) { |
| ... | ... | @@ -1116,8 +1152,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError! |
| 1116 | 1152 | error.SystemResources, |
| 1117 | 1153 | error.NoSpaceLeft, |
| 1118 | 1154 | error.PathAlreadyExists, |
| 1119 | error.Unexpected | |
| 1120 | => return err, | |
| 1155 | error.Unexpected => return err, | |
| 1121 | 1156 | }; |
| 1122 | 1157 | defer dir.close(); |
| 1123 | 1158 | |
| ... | ... | @@ -1147,7 +1182,8 @@ pub const Dir = struct { |
| 1147 | 1182 | end_index: usize, |
| 1148 | 1183 | |
| 1149 | 1184 | const darwin_seek_t = switch (builtin.os) { |
| 1150 | Os.macosx, Os.ios => i64, | |
| 1185 | Os.macosx, | |
| 1186 | Os.ios => i64, | |
| 1151 | 1187 | else => void, |
| 1152 | 1188 | }; |
| 1153 | 1189 | |
| ... | ... | @@ -1171,12 +1207,14 @@ pub const Dir = struct { |
| 1171 | 1207 | pub fn open(allocator: &Allocator, dir_path: []const u8) !Dir { |
| 1172 | 1208 | const fd = switch (builtin.os) { |
| 1173 | 1209 | Os.windows => @compileError("TODO support Dir.open for windows"), |
| 1174 | Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0), | |
| 1175 | Os.macosx, Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_NONBLOCK|posix.O_DIRECTORY|posix.O_CLOEXEC, 0), | |
| 1210 | Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, 0), | |
| 1211 | Os.macosx, | |
| 1212 | Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC, 0), | |
| 1176 | 1213 | else => @compileError("Dir.open is not supported for this platform"), |
| 1177 | 1214 | }; |
| 1178 | 1215 | const darwin_seek_init = switch (builtin.os) { |
| 1179 | Os.macosx, Os.ios => 0, | |
| 1216 | Os.macosx, | |
| 1217 | Os.ios => 0, | |
| 1180 | 1218 | else => {}, |
| 1181 | 1219 | }; |
| 1182 | 1220 | return Dir { |
| ... | ... | @@ -1199,7 +1237,8 @@ pub const Dir = struct { |
| 1199 | 1237 | pub fn next(self: &Dir) !?Entry { |
| 1200 | 1238 | switch (builtin.os) { |
| 1201 | 1239 | Os.linux => return self.nextLinux(), |
| 1202 | Os.macosx, Os.ios => return self.nextDarwin(), | |
| 1240 | Os.macosx, | |
| 1241 | Os.ios => return self.nextDarwin(), | |
| 1203 | 1242 | Os.windows => return self.nextWindows(), |
| 1204 | 1243 | else => @compileError("Dir.next not supported on " ++ @tagName(builtin.os)), |
| 1205 | 1244 | } |
| ... | ... | @@ -1213,12 +1252,13 @@ pub const Dir = struct { |
| 1213 | 1252 | } |
| 1214 | 1253 | |
| 1215 | 1254 | while (true) { |
| 1216 | const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len, | |
| 1217 | &self.darwin_seek); | |
| 1255 | const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len, &self.darwin_seek); | |
| 1218 | 1256 | const err = posix.getErrno(result); |
| 1219 | 1257 | if (err > 0) { |
| 1220 | 1258 | switch (err) { |
| 1221 | posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, | |
| 1259 | posix.EBADF, | |
| 1260 | posix.EFAULT, | |
| 1261 | posix.ENOTDIR => unreachable, | |
| 1222 | 1262 | posix.EINVAL => { |
| 1223 | 1263 | self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2); |
| 1224 | 1264 | continue; |
| ... | ... | @@ -1226,14 +1266,13 @@ pub const Dir = struct { |
| 1226 | 1266 | else => return unexpectedErrorPosix(err), |
| 1227 | 1267 | } |
| 1228 | 1268 | } |
| 1229 | if (result == 0) | |
| 1230 | return null; | |
| 1269 | if (result == 0) return null; | |
| 1231 | 1270 | self.index = 0; |
| 1232 | 1271 | self.end_index = result; |
| 1233 | 1272 | break; |
| 1234 | 1273 | } |
| 1235 | 1274 | } |
| 1236 | const darwin_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]); | |
| 1275 | const darwin_entry = @ptrCast(&align(1) posix.dirent, &self.buf[self.index]); | |
| 1237 | 1276 | const next_index = self.index + darwin_entry.d_reclen; |
| 1238 | 1277 | self.index = next_index; |
| 1239 | 1278 | |
| ... | ... | @@ -1278,7 +1317,9 @@ pub const Dir = struct { |
| 1278 | 1317 | const err = posix.getErrno(result); |
| 1279 | 1318 | if (err > 0) { |
| 1280 | 1319 | switch (err) { |
| 1281 | posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, | |
| 1320 | posix.EBADF, | |
| 1321 | posix.EFAULT, | |
| 1322 | posix.ENOTDIR => unreachable, | |
| 1282 | 1323 | posix.EINVAL => { |
| 1283 | 1324 | self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2); |
| 1284 | 1325 | continue; |
| ... | ... | @@ -1286,14 +1327,13 @@ pub const Dir = struct { |
| 1286 | 1327 | else => return unexpectedErrorPosix(err), |
| 1287 | 1328 | } |
| 1288 | 1329 | } |
| 1289 | if (result == 0) | |
| 1290 | return null; | |
| 1330 | if (result == 0) return null; | |
| 1291 | 1331 | self.index = 0; |
| 1292 | 1332 | self.end_index = result; |
| 1293 | 1333 | break; |
| 1294 | 1334 | } |
| 1295 | 1335 | } |
| 1296 | const linux_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]); | |
| 1336 | const linux_entry = @ptrCast(&align(1) posix.dirent, &self.buf[self.index]); | |
| 1297 | 1337 | const next_index = self.index + linux_entry.d_reclen; |
| 1298 | 1338 | self.index = next_index; |
| 1299 | 1339 | |
| ... | ... | @@ -1362,7 +1402,8 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 { |
| 1362 | 1402 | if (err > 0) { |
| 1363 | 1403 | return switch (err) { |
| 1364 | 1404 | posix.EACCES => error.AccessDenied, |
| 1365 | posix.EFAULT, posix.EINVAL => unreachable, | |
| 1405 | posix.EFAULT, | |
| 1406 | posix.EINVAL => unreachable, | |
| 1366 | 1407 | posix.EIO => error.FileSystem, |
| 1367 | 1408 | posix.ELOOP => error.SymLinkLoop, |
| 1368 | 1409 | posix.ENAMETOOLONG => error.NameTooLong, |
| ... | ... | @@ -1455,8 +1496,7 @@ pub const ArgIteratorPosix = struct { |
| 1455 | 1496 | } |
| 1456 | 1497 | |
| 1457 | 1498 | pub fn next(self: &ArgIteratorPosix) ?[]const u8 { |
| 1458 | if (self.index == self.count) | |
| 1459 | return null; | |
| 1499 | if (self.index == self.count) return null; | |
| 1460 | 1500 | |
| 1461 | 1501 | const s = raw[self.index]; |
| 1462 | 1502 | self.index += 1; |
| ... | ... | @@ -1464,8 +1504,7 @@ pub const ArgIteratorPosix = struct { |
| 1464 | 1504 | } |
| 1465 | 1505 | |
| 1466 | 1506 | pub fn skip(self: &ArgIteratorPosix) bool { |
| 1467 | if (self.index == self.count) | |
| 1468 | return false; | |
| 1507 | if (self.index == self.count) return false; | |
| 1469 | 1508 | |
| 1470 | 1509 | self.index += 1; |
| 1471 | 1510 | return true; |
| ... | ... | @@ -1483,7 +1522,9 @@ pub const ArgIteratorWindows = struct { |
| 1483 | 1522 | quote_count: usize, |
| 1484 | 1523 | seen_quote_count: usize, |
| 1485 | 1524 | |
| 1486 | pub const NextError = error{OutOfMemory}; | |
| 1525 | pub const NextError = error { | |
| 1526 | OutOfMemory, | |
| 1527 | }; | |
| 1487 | 1528 | |
| 1488 | 1529 | pub fn init() ArgIteratorWindows { |
| 1489 | 1530 | return initWithCmdLine(windows.GetCommandLineA()); |
| ... | ... | @@ -1506,7 +1547,8 @@ pub const ArgIteratorWindows = struct { |
| 1506 | 1547 | const byte = self.cmd_line[self.index]; |
| 1507 | 1548 | switch (byte) { |
| 1508 | 1549 | 0 => return null, |
| 1509 | ' ', '\t' => continue, | |
| 1550 | ' ', | |
| 1551 | '\t' => continue, | |
| 1510 | 1552 | else => break, |
| 1511 | 1553 | } |
| 1512 | 1554 | } |
| ... | ... | @@ -1520,7 +1562,8 @@ pub const ArgIteratorWindows = struct { |
| 1520 | 1562 | const byte = self.cmd_line[self.index]; |
| 1521 | 1563 | switch (byte) { |
| 1522 | 1564 | 0 => return false, |
| 1523 | ' ', '\t' => continue, | |
| 1565 | ' ', | |
| 1566 | '\t' => continue, | |
| 1524 | 1567 | else => break, |
| 1525 | 1568 | } |
| 1526 | 1569 | } |
| ... | ... | @@ -1539,7 +1582,8 @@ pub const ArgIteratorWindows = struct { |
| 1539 | 1582 | '\\' => { |
| 1540 | 1583 | backslash_count += 1; |
| 1541 | 1584 | }, |
| 1542 | ' ', '\t' => { | |
| 1585 | ' ', | |
| 1586 | '\t' => { | |
| 1543 | 1587 | if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) { |
| 1544 | 1588 | return true; |
| 1545 | 1589 | } |
| ... | ... | @@ -1579,7 +1623,8 @@ pub const ArgIteratorWindows = struct { |
| 1579 | 1623 | '\\' => { |
| 1580 | 1624 | backslash_count += 1; |
| 1581 | 1625 | }, |
| 1582 | ' ', '\t' => { | |
| 1626 | ' ', | |
| 1627 | '\t' => { | |
| 1583 | 1628 | try self.emitBackslashes(&buf, backslash_count); |
| 1584 | 1629 | backslash_count = 0; |
| 1585 | 1630 | if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) { |
| ... | ... | @@ -1623,7 +1668,6 @@ pub const ArgIteratorWindows = struct { |
| 1623 | 1668 | } |
| 1624 | 1669 | } |
| 1625 | 1670 | } |
| 1626 | ||
| 1627 | 1671 | }; |
| 1628 | 1672 | |
| 1629 | 1673 | pub const ArgIterator = struct { |
| ... | ... | @@ -1638,7 +1682,7 @@ pub const ArgIterator = struct { |
| 1638 | 1682 | } |
| 1639 | 1683 | |
| 1640 | 1684 | pub const NextError = ArgIteratorWindows.NextError; |
| 1641 | ||
| 1685 | ||
| 1642 | 1686 | /// You must free the returned memory when done. |
| 1643 | 1687 | pub fn next(self: &ArgIterator, allocator: &Allocator) ?(NextError![]u8) { |
| 1644 | 1688 | if (builtin.os == Os.windows) { |
| ... | ... | @@ -1713,15 +1757,47 @@ pub fn argsFree(allocator: &mem.Allocator, args_alloc: []const []u8) void { |
| 1713 | 1757 | } |
| 1714 | 1758 | |
| 1715 | 1759 | test "windows arg parsing" { |
| 1716 | testWindowsCmdLine(c"a b\tc d", [][]const u8{"a", "b", "c", "d"}); | |
| 1717 | testWindowsCmdLine(c"\"abc\" d e", [][]const u8{"abc", "d", "e"}); | |
| 1718 | testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8{"a\\\\\\b", "de fg", "h"}); | |
| 1719 | testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8{"a\\\"b", "c", "d"}); | |
| 1720 | testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8{"a\\\\b c", "d", "e"}); | |
| 1721 | testWindowsCmdLine(c"a b\tc \"d f", [][]const u8{"a", "b", "c", "\"d", "f"}); | |
| 1722 | ||
| 1723 | testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", | |
| 1724 | [][]const u8{".\\..\\zig-cache\\build", "bin\\zig.exe", ".\\..", ".\\..\\zig-cache", "--help"}); | |
| 1760 | testWindowsCmdLine(c"a b\tc d", [][]const u8 { | |
| 1761 | "a", | |
| 1762 | "b", | |
| 1763 | "c", | |
| 1764 | "d", | |
| 1765 | }); | |
| 1766 | testWindowsCmdLine(c"\"abc\" d e", [][]const u8 { | |
| 1767 | "abc", | |
| 1768 | "d", | |
| 1769 | "e", | |
| 1770 | }); | |
| 1771 | testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8 { | |
| 1772 | "a\\\\\\b", | |
| 1773 | "de fg", | |
| 1774 | "h", | |
| 1775 | }); | |
| 1776 | testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8 { | |
| 1777 | "a\\\"b", | |
| 1778 | "c", | |
| 1779 | "d", | |
| 1780 | }); | |
| 1781 | testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8 { | |
| 1782 | "a\\\\b c", | |
| 1783 | "d", | |
| 1784 | "e", | |
| 1785 | }); | |
| 1786 | testWindowsCmdLine(c"a b\tc \"d f", [][]const u8 { | |
| 1787 | "a", | |
| 1788 | "b", | |
| 1789 | "c", | |
| 1790 | "\"d", | |
| 1791 | "f", | |
| 1792 | }); | |
| 1793 | ||
| 1794 | testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", [][]const u8 { | |
| 1795 | ".\\..\\zig-cache\\build", | |
| 1796 | "bin\\zig.exe", | |
| 1797 | ".\\..", | |
| 1798 | ".\\..\\zig-cache", | |
| 1799 | "--help", | |
| 1800 | }); | |
| 1725 | 1801 | } |
| 1726 | 1802 | |
| 1727 | 1803 | fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const u8) void { |
| ... | ... | @@ -1768,7 +1844,8 @@ pub fn openSelfExe() !os.File { |
| 1768 | 1844 | var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); |
| 1769 | 1845 | return os.File.openRead(&fixed_allocator.allocator, proc_file_path); |
| 1770 | 1846 | }, |
| 1771 | Os.macosx, Os.ios => { | |
| 1847 | Os.macosx, | |
| 1848 | Os.ios => { | |
| 1772 | 1849 | var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined; |
| 1773 | 1850 | var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); |
| 1774 | 1851 | const self_exe_path = try selfExePath(&fixed_allocator.allocator); |
| ... | ... | @@ -1780,8 +1857,10 @@ pub fn openSelfExe() !os.File { |
| 1780 | 1857 | |
| 1781 | 1858 | test "openSelfExe" { |
| 1782 | 1859 | switch (builtin.os) { |
| 1783 | Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(), | |
| 1784 | else => return, // Unsupported OS. | |
| 1860 | Os.linux, | |
| 1861 | Os.macosx, | |
| 1862 | Os.ios => (try openSelfExe()).close(), | |
| 1863 | else => return, // Unsupported OS. | |
| 1785 | 1864 | } |
| 1786 | 1865 | } |
| 1787 | 1866 | |
| ... | ... | @@ -1818,7 +1897,8 @@ pub fn selfExePath(allocator: &mem.Allocator) ![]u8 { |
| 1818 | 1897 | try out_path.resize(new_len); |
| 1819 | 1898 | } |
| 1820 | 1899 | }, |
| 1821 | Os.macosx, Os.ios => { | |
| 1900 | Os.macosx, | |
| 1901 | Os.ios => { | |
| 1822 | 1902 | var u32_len: u32 = 0; |
| 1823 | 1903 | const ret1 = c._NSGetExecutablePath(undefined, &u32_len); |
| 1824 | 1904 | assert(ret1 != 0); |
| ... | ... | @@ -1846,7 +1926,9 @@ pub fn selfExeDirPath(allocator: &mem.Allocator) ![]u8 { |
| 1846 | 1926 | const dir = path.dirname(full_exe_path); |
| 1847 | 1927 | return allocator.shrink(u8, full_exe_path, dir.len); |
| 1848 | 1928 | }, |
| 1849 | Os.windows, Os.macosx, Os.ios => { | |
| 1929 | Os.windows, | |
| 1930 | Os.macosx, | |
| 1931 | Os.ios => { | |
| 1850 | 1932 | const self_exe_path = try selfExePath(allocator); |
| 1851 | 1933 | errdefer allocator.free(self_exe_path); |
| 1852 | 1934 | const dirname = os.path.dirname(self_exe_path); |
| ... | ... | @@ -1903,7 +1985,8 @@ pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 { |
| 1903 | 1985 | posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable, |
| 1904 | 1986 | posix.EMFILE => return PosixSocketError.ProcessFdQuotaExceeded, |
| 1905 | 1987 | posix.ENFILE => return PosixSocketError.SystemFdQuotaExceeded, |
| 1906 | posix.ENOBUFS, posix.ENOMEM => return PosixSocketError.SystemResources, | |
| 1988 | posix.ENOBUFS, | |
| 1989 | posix.ENOMEM => return PosixSocketError.SystemResources, | |
| 1907 | 1990 | posix.EPROTONOSUPPORT => return PosixSocketError.ProtocolNotSupported, |
| 1908 | 1991 | else => return unexpectedErrorPosix(err), |
| 1909 | 1992 | } |
| ... | ... | @@ -1934,7 +2017,7 @@ pub const PosixBindError = error { |
| 1934 | 2017 | |
| 1935 | 2018 | /// A nonexistent interface was requested or the requested address was not local. |
| 1936 | 2019 | AddressNotAvailable, |
| 1937 | ||
| 2020 | ||
| 1938 | 2021 | /// addr points outside the user's accessible address space. |
| 1939 | 2022 | PageFault, |
| 1940 | 2023 | |
| ... | ... | @@ -2023,7 +2106,7 @@ pub const PosixAcceptError = error { |
| 2023 | 2106 | FileDescriptorClosed, |
| 2024 | 2107 | |
| 2025 | 2108 | ConnectionAborted, |
| 2026 | ||
| 2109 | ||
| 2027 | 2110 | /// The addr argument is not in a writable part of the user address space. |
| 2028 | 2111 | PageFault, |
| 2029 | 2112 | |
| ... | ... | @@ -2036,7 +2119,7 @@ pub const PosixAcceptError = error { |
| 2036 | 2119 | |
| 2037 | 2120 | /// The system-wide limit on the total number of open files has been reached. |
| 2038 | 2121 | SystemFdQuotaExceeded, |
| 2039 | ||
| 2122 | ||
| 2040 | 2123 | /// Not enough free memory. This often means that the memory allocation is limited |
| 2041 | 2124 | /// by the socket buffer limits, not by the system memory. |
| 2042 | 2125 | SystemResources, |
| ... | ... | @@ -2072,7 +2155,8 @@ pub fn posixAccept(fd: i32, addr: &posix.sockaddr, flags: u32) PosixAcceptError! |
| 2072 | 2155 | posix.EINVAL => return PosixAcceptError.InvalidSyscall, |
| 2073 | 2156 | posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded, |
| 2074 | 2157 | posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded, |
| 2075 | posix.ENOBUFS, posix.ENOMEM => return PosixAcceptError.SystemResources, | |
| 2158 | posix.ENOBUFS, | |
| 2159 | posix.ENOMEM => return PosixAcceptError.SystemResources, | |
| 2076 | 2160 | posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket, |
| 2077 | 2161 | posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported, |
| 2078 | 2162 | posix.EPROTO => return PosixAcceptError.ProtocolFailure, |
| ... | ... | @@ -2283,7 +2367,8 @@ pub fn posixConnectAsync(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConn |
| 2283 | 2367 | const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr)); |
| 2284 | 2368 | const err = posix.getErrno(rc); |
| 2285 | 2369 | switch (err) { |
| 2286 | 0, posix.EINPROGRESS => return, | |
| 2370 | 0, | |
| 2371 | posix.EINPROGRESS => return, | |
| 2287 | 2372 | else => return unexpectedErrorPosix(err), |
| 2288 | 2373 | |
| 2289 | 2374 | posix.EACCES => return PosixConnectError.PermissionDenied, |
| ... | ... | @@ -2343,24 +2428,58 @@ pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { |
| 2343 | 2428 | } |
| 2344 | 2429 | |
| 2345 | 2430 | pub const Thread = struct { |
| 2346 | pid: i32, | |
| 2347 | allocator: ?&mem.Allocator, | |
| 2348 | stack: []u8, | |
| 2431 | data: Data, | |
| 2432 | ||
| 2433 | pub const use_pthreads = is_posix and builtin.link_libc; | |
| 2434 | const Data = if (use_pthreads) struct { | |
| 2435 | handle: c.pthread_t, | |
| 2436 | stack_addr: usize, | |
| 2437 | stack_len: usize, | |
| 2438 | } else switch (builtin.os) { | |
| 2439 | builtin.Os.linux => struct { | |
| 2440 | pid: i32, | |
| 2441 | stack_addr: usize, | |
| 2442 | stack_len: usize, | |
| 2443 | }, | |
| 2444 | builtin.Os.windows => struct { | |
| 2445 | handle: windows.HANDLE, | |
| 2446 | alloc_start: &c_void, | |
| 2447 | heap_handle: windows.HANDLE, | |
| 2448 | }, | |
| 2449 | else => @compileError("Unsupported OS"), | |
| 2450 | }; | |
| 2349 | 2451 | |
| 2350 | 2452 | pub fn wait(self: &const Thread) void { |
| 2351 | while (true) { | |
| 2352 | const pid_value = @atomicLoad(i32, &self.pid, builtin.AtomicOrder.SeqCst); | |
| 2353 | if (pid_value == 0) break; | |
| 2354 | const rc = linux.futex_wait(@ptrToInt(&self.pid), linux.FUTEX_WAIT, pid_value, null); | |
| 2355 | switch (linux.getErrno(rc)) { | |
| 2356 | 0 => continue, | |
| 2357 | posix.EINTR => continue, | |
| 2358 | posix.EAGAIN => continue, | |
| 2453 | if (use_pthreads) { | |
| 2454 | const err = c.pthread_join(self.data.handle, null); | |
| 2455 | switch (err) { | |
| 2456 | 0 => {}, | |
| 2457 | posix.EINVAL => unreachable, | |
| 2458 | posix.ESRCH => unreachable, | |
| 2459 | posix.EDEADLK => unreachable, | |
| 2359 | 2460 | else => unreachable, |
| 2360 | 2461 | } |
| 2361 | } | |
| 2362 | if (self.allocator) |a| { | |
| 2363 | a.free(self.stack); | |
| 2462 | assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0); | |
| 2463 | } else switch (builtin.os) { | |
| 2464 | builtin.Os.linux => { | |
| 2465 | while (true) { | |
| 2466 | const pid_value = @atomicLoad(i32, &self.data.pid, builtin.AtomicOrder.SeqCst); | |
| 2467 | if (pid_value == 0) break; | |
| 2468 | const rc = linux.futex_wait(@ptrToInt(&self.data.pid), linux.FUTEX_WAIT, pid_value, null); | |
| 2469 | switch (linux.getErrno(rc)) { | |
| 2470 | 0 => continue, | |
| 2471 | posix.EINTR => continue, | |
| 2472 | posix.EAGAIN => continue, | |
| 2473 | else => unreachable, | |
| 2474 | } | |
| 2475 | } | |
| 2476 | assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0); | |
| 2477 | }, | |
| 2478 | builtin.Os.windows => { | |
| 2479 | assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0); | |
| 2480 | assert(windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start) != 0); | |
| 2481 | }, | |
| 2482 | else => @compileError("Unsupported OS"), | |
| 2364 | 2483 | } |
| 2365 | 2484 | } |
| 2366 | 2485 | }; |
| ... | ... | @@ -2385,38 +2504,91 @@ pub const SpawnThreadError = error { |
| 2385 | 2504 | /// be copied. |
| 2386 | 2505 | SystemResources, |
| 2387 | 2506 | |
| 2507 | /// Not enough userland memory to spawn the thread. | |
| 2508 | OutOfMemory, | |
| 2509 | ||
| 2388 | 2510 | Unexpected, |
| 2389 | 2511 | }; |
| 2390 | 2512 | |
| 2391 | pub const SpawnThreadAllocatorError = SpawnThreadError || error{OutOfMemory}; | |
| 2392 | ||
| 2393 | 2513 | /// caller must call wait on the returned thread |
| 2394 | 2514 | /// fn startFn(@typeOf(context)) T |
| 2395 | 2515 | /// where T is u8, noreturn, void, or !void |
| 2396 | pub fn spawnThreadAllocator(allocator: &mem.Allocator, context: var, comptime startFn: var) SpawnThreadAllocatorError!&Thread { | |
| 2516 | /// caller must call wait on the returned thread | |
| 2517 | pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread { | |
| 2397 | 2518 | // TODO compile-time call graph analysis to determine stack upper bound |
| 2398 | 2519 | // https://github.com/zig-lang/zig/issues/157 |
| 2399 | 2520 | const default_stack_size = 8 * 1024 * 1024; |
| 2400 | const stack_bytes = try allocator.alloc(u8, default_stack_size); | |
| 2401 | const thread = try spawnThread(stack_bytes, context, startFn); | |
| 2402 | thread.allocator = allocator; | |
| 2403 | return thread; | |
| 2404 | } | |
| 2405 | 2521 | |
| 2406 | /// stack must be big enough to store one Thread and one @typeOf(context), each with default alignment, at the end | |
| 2407 | /// fn startFn(@typeOf(context)) T | |
| 2408 | /// where T is u8, noreturn, void, or !void | |
| 2409 | /// caller must call wait on the returned thread | |
| 2410 | pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThreadError!&Thread { | |
| 2411 | 2522 | const Context = @typeOf(context); |
| 2412 | 2523 | comptime assert(@ArgType(@typeOf(startFn), 0) == Context); |
| 2413 | 2524 | |
| 2414 | var stack_end: usize = @ptrToInt(stack.ptr) + stack.len; | |
| 2525 | if (builtin.os == builtin.Os.windows) { | |
| 2526 | const WinThread = struct { | |
| 2527 | const OuterContext = struct { | |
| 2528 | thread: Thread, | |
| 2529 | inner: Context, | |
| 2530 | }; | |
| 2531 | extern fn threadMain(arg: windows.LPVOID) windows.DWORD { | |
| 2532 | if (@sizeOf(Context) == 0) { | |
| 2533 | return startFn({}); | |
| 2534 | } else { | |
| 2535 | return startFn(*@ptrCast(&Context, @alignCast(@alignOf(Context), arg))); | |
| 2536 | } | |
| 2537 | } | |
| 2538 | }; | |
| 2539 | ||
| 2540 | const heap_handle = windows.GetProcessHeap() ?? return SpawnThreadError.OutOfMemory; | |
| 2541 | const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext); | |
| 2542 | const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) ?? return SpawnThreadError.OutOfMemory; | |
| 2543 | errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0); | |
| 2544 | const bytes = @ptrCast(&u8, bytes_ptr)[0..byte_count]; | |
| 2545 | const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable; | |
| 2546 | outer_context.inner = context; | |
| 2547 | outer_context.thread.data.heap_handle = heap_handle; | |
| 2548 | outer_context.thread.data.alloc_start = bytes_ptr; | |
| 2549 | ||
| 2550 | const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(&c_void, &outer_context.inner); | |
| 2551 | outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) ?? { | |
| 2552 | const err = windows.GetLastError(); | |
| 2553 | return switch (err) { | |
| 2554 | else => os.unexpectedErrorWindows(err), | |
| 2555 | }; | |
| 2556 | }; | |
| 2557 | return &outer_context.thread; | |
| 2558 | } | |
| 2559 | ||
| 2560 | const MainFuncs = struct { | |
| 2561 | extern fn linuxThreadMain(ctx_addr: usize) u8 { | |
| 2562 | if (@sizeOf(Context) == 0) { | |
| 2563 | return startFn({}); | |
| 2564 | } else { | |
| 2565 | return startFn(*@intToPtr(&const Context, ctx_addr)); | |
| 2566 | } | |
| 2567 | } | |
| 2568 | extern fn posixThreadMain(ctx: ?&c_void) ?&c_void { | |
| 2569 | if (@sizeOf(Context) == 0) { | |
| 2570 | _ = startFn({}); | |
| 2571 | return null; | |
| 2572 | } else { | |
| 2573 | _ = startFn(*@ptrCast(&const Context, @alignCast(@alignOf(Context), ctx))); | |
| 2574 | return null; | |
| 2575 | } | |
| 2576 | } | |
| 2577 | }; | |
| 2578 | ||
| 2579 | const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0; | |
| 2580 | ||
| 2581 | const mmap_len = default_stack_size; | |
| 2582 | const stack_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0); | |
| 2583 | if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory; | |
| 2584 | errdefer assert(posix.munmap(stack_addr, mmap_len) == 0); | |
| 2585 | ||
| 2586 | var stack_end: usize = stack_addr + mmap_len; | |
| 2415 | 2587 | var arg: usize = undefined; |
| 2416 | 2588 | if (@sizeOf(Context) != 0) { |
| 2417 | 2589 | stack_end -= @sizeOf(Context); |
| 2418 | 2590 | stack_end -= stack_end % @alignOf(Context); |
| 2419 | assert(stack_end >= @ptrToInt(stack.ptr)); | |
| 2591 | assert(stack_end >= stack_addr); | |
| 2420 | 2592 | const context_ptr = @alignCast(@alignOf(Context), @intToPtr(&Context, stack_end)); |
| 2421 | 2593 | *context_ptr = context; |
| 2422 | 2594 | arg = stack_end; |
| ... | ... | @@ -2424,36 +2596,51 @@ pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThread |
| 2424 | 2596 | |
| 2425 | 2597 | stack_end -= @sizeOf(Thread); |
| 2426 | 2598 | stack_end -= stack_end % @alignOf(Thread); |
| 2427 | assert(stack_end >= @ptrToInt(stack.ptr)); | |
| 2599 | assert(stack_end >= stack_addr); | |
| 2428 | 2600 | const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(&Thread, stack_end)); |
| 2429 | thread_ptr.stack = stack; | |
| 2430 | thread_ptr.allocator = null; | |
| 2431 | 2601 | |
| 2432 | const threadMain = struct { | |
| 2433 | extern fn threadMain(ctx_addr: usize) u8 { | |
| 2434 | if (@sizeOf(Context) == 0) { | |
| 2435 | return startFn({}); | |
| 2436 | } else { | |
| 2437 | return startFn(*@intToPtr(&const Context, ctx_addr)); | |
| 2438 | } | |
| 2439 | } | |
| 2440 | }.threadMain; | |
| 2602 | thread_ptr.data.stack_addr = stack_addr; | |
| 2603 | thread_ptr.data.stack_len = mmap_len; | |
| 2441 | 2604 | |
| 2442 | const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | |
| 2443 | | posix.CLONE_THREAD | posix.CLONE_SYSVSEM // | posix.CLONE_SETTLS | |
| 2444 | | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED; | |
| 2445 | const newtls: usize = 0; | |
| 2446 | const rc = posix.clone(threadMain, stack_end, flags, arg, &thread_ptr.pid, newtls, &thread_ptr.pid); | |
| 2447 | const err = posix.getErrno(rc); | |
| 2448 | switch (err) { | |
| 2449 | 0 => return thread_ptr, | |
| 2450 | posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded, | |
| 2451 | posix.EINVAL => unreachable, | |
| 2452 | posix.ENOMEM => return SpawnThreadError.SystemResources, | |
| 2453 | posix.ENOSPC => unreachable, | |
| 2454 | posix.EPERM => unreachable, | |
| 2455 | posix.EUSERS => unreachable, | |
| 2456 | else => return unexpectedErrorPosix(err), | |
| 2605 | if (builtin.os == builtin.Os.windows) { | |
| 2606 | // use windows API directly | |
| 2607 | @compileError("TODO support spawnThread for Windows"); | |
| 2608 | } else if (Thread.use_pthreads) { | |
| 2609 | // use pthreads | |
| 2610 | var attr: c.pthread_attr_t = undefined; | |
| 2611 | if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources; | |
| 2612 | defer assert(c.pthread_attr_destroy(&attr) == 0); | |
| 2613 | ||
| 2614 | // align to page | |
| 2615 | stack_end -= stack_end % os.page_size; | |
| 2616 | assert(c.pthread_attr_setstack(&attr, @intToPtr(&c_void, stack_addr), stack_end - stack_addr) == 0); | |
| 2617 | ||
| 2618 | const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg)); | |
| 2619 | switch (err) { | |
| 2620 | 0 => return thread_ptr, | |
| 2621 | posix.EAGAIN => return SpawnThreadError.SystemResources, | |
| 2622 | posix.EPERM => unreachable, | |
| 2623 | posix.EINVAL => unreachable, | |
| 2624 | else => return unexpectedErrorPosix(usize(err)), | |
| 2625 | } | |
| 2626 | } else if (builtin.os == builtin.Os.linux) { | |
| 2627 | // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly | |
| 2628 | const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED; | |
| 2629 | const newtls: usize = 0; | |
| 2630 | const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.pid, newtls, &thread_ptr.data.pid); | |
| 2631 | const err = posix.getErrno(rc); | |
| 2632 | switch (err) { | |
| 2633 | 0 => return thread_ptr, | |
| 2634 | posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded, | |
| 2635 | posix.EINVAL => unreachable, | |
| 2636 | posix.ENOMEM => return SpawnThreadError.SystemResources, | |
| 2637 | posix.ENOSPC => unreachable, | |
| 2638 | posix.EPERM => unreachable, | |
| 2639 | posix.EUSERS => unreachable, | |
| 2640 | else => return unexpectedErrorPosix(err), | |
| 2641 | } | |
| 2642 | } else { | |
| 2643 | @compileError("Unsupported OS"); | |
| 2457 | 2644 | } |
| 2458 | 2645 | } |
| 2459 | 2646 |
std/os/linux/index.zig+3-3| ... | ... | @@ -706,13 +706,13 @@ pub fn umount2(special: &const u8, flags: u32) usize { |
| 706 | 706 | return syscall2(SYS_umount2, @ptrToInt(special), flags); |
| 707 | 707 | } |
| 708 | 708 | |
| 709 | pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, offset: isize) usize { | |
| 709 | pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { | |
| 710 | 710 | return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd), |
| 711 | 711 | @bitCast(usize, offset)); |
| 712 | 712 | } |
| 713 | 713 | |
| 714 | pub fn munmap(address: &u8, length: usize) usize { | |
| 715 | return syscall2(SYS_munmap, @ptrToInt(address), length); | |
| 714 | pub fn munmap(address: usize, length: usize) usize { | |
| 715 | return syscall2(SYS_munmap, address, length); | |
| 716 | 716 | } |
| 717 | 717 | |
| 718 | 718 | pub fn read(fd: i32, buf: &u8, count: usize) usize { |
std/os/test.zig+4-16| ... | ... | @@ -44,24 +44,12 @@ test "access file" { |
| 44 | 44 | } |
| 45 | 45 | |
| 46 | 46 | test "spawn threads" { |
| 47 | if (builtin.os != builtin.Os.linux) { | |
| 48 | // TODO implement threads on macos and windows | |
| 49 | return; | |
| 50 | } | |
| 51 | ||
| 52 | var direct_allocator = std.heap.DirectAllocator.init(); | |
| 53 | defer direct_allocator.deinit(); | |
| 54 | ||
| 55 | 47 | var shared_ctx: i32 = 1; |
| 56 | 48 | |
| 57 | const thread1 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, {}, start1); | |
| 58 | const thread4 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, &shared_ctx, start2); | |
| 59 | ||
| 60 | var stack1: [1024]u8 = undefined; | |
| 61 | var stack2: [1024]u8 = undefined; | |
| 62 | ||
| 63 | const thread2 = try std.os.spawnThread(stack1[0..], &shared_ctx, start2); | |
| 64 | const thread3 = try std.os.spawnThread(stack2[0..], &shared_ctx, start2); | |
| 49 | const thread1 = try std.os.spawnThread({}, start1); | |
| 50 | const thread2 = try std.os.spawnThread(&shared_ctx, start2); | |
| 51 | const thread3 = try std.os.spawnThread(&shared_ctx, start2); | |
| 52 | const thread4 = try std.os.spawnThread(&shared_ctx, start2); | |
| 65 | 53 | |
| 66 | 54 | thread1.wait(); |
| 67 | 55 | thread2.wait(); |
std/os/time.zig+1-1| ... | ... | @@ -281,7 +281,7 @@ test "os.time.Timer" { |
| 281 | 281 | debug.assert(time_0 > 0 and time_0 < margin); |
| 282 | 282 | |
| 283 | 283 | const time_1 = timer.lap(); |
| 284 | debug.assert(time_1 > time_0); | |
| 284 | debug.assert(time_1 >= time_0); | |
| 285 | 285 | |
| 286 | 286 | timer.reset(); |
| 287 | 287 | debug.assert(timer.read() < time_1); |
std/os/windows/index.zig+6| ... | ... | @@ -28,6 +28,9 @@ pub extern "kernel32" stdcallcc fn CreateProcessA(lpApplicationName: ?LPCSTR, lp |
| 28 | 28 | pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(lpSymlinkFileName: LPCSTR, lpTargetFileName: LPCSTR, |
| 29 | 29 | dwFlags: DWORD) BOOLEAN; |
| 30 | 30 | |
| 31 | ||
| 32 | pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE; | |
| 33 | ||
| 31 | 34 | pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL; |
| 32 | 35 | |
| 33 | 36 | pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; |
| ... | ... | @@ -318,6 +321,9 @@ pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000; |
| 318 | 321 | pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004; |
| 319 | 322 | pub const HEAP_NO_SERIALIZE = 0x00000001; |
| 320 | 323 | |
| 324 | pub const PTHREAD_START_ROUTINE = extern fn(LPVOID) DWORD; | |
| 325 | pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE; | |
| 326 | ||
| 321 | 327 | test "import" { |
| 322 | 328 | _ = @import("util.zig"); |
| 323 | 329 | } |
std/special/bootstrap_lib.zig+2-1| ... | ... | @@ -1,9 +1,10 @@ |
| 1 | 1 | // This file is included in the compilation unit when exporting a library on windows. |
| 2 | 2 | |
| 3 | 3 | const std = @import("std"); |
| 4 | const builtin = @import("builtin"); | |
| 4 | 5 | |
| 5 | 6 | comptime { |
| 6 | @export("_DllMainCRTStartup", _DllMainCRTStartup); | |
| 7 | @export("_DllMainCRTStartup", _DllMainCRTStartup, builtin.GlobalLinkage.Strong); | |
| 7 | 8 | } |
| 8 | 9 | |
| 9 | 10 | stdcallcc fn _DllMainCRTStartup(hinstDLL: std.os.windows.HINSTANCE, fdwReason: std.os.windows.DWORD, |
std/special/compiler_rt/index.zig+17-4| ... | ... | @@ -32,10 +32,6 @@ comptime { |
| 32 | 32 | @export("__fixunstfti", @import("fixunstfti.zig").__fixunstfti, linkage); |
| 33 | 33 | |
| 34 | 34 | @export("__udivmoddi4", @import("udivmoddi4.zig").__udivmoddi4, linkage); |
| 35 | @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4, linkage); | |
| 36 | ||
| 37 | @export("__udivti3", @import("udivti3.zig").__udivti3, linkage); | |
| 38 | @export("__umodti3", @import("umodti3.zig").__umodti3, linkage); | |
| 39 | 35 | |
| 40 | 36 | @export("__udivsi3", __udivsi3, linkage); |
| 41 | 37 | @export("__udivdi3", __udivdi3, linkage); |
| ... | ... | @@ -62,9 +58,16 @@ comptime { |
| 62 | 58 | @export("__chkstk", __chkstk, strong_linkage); |
| 63 | 59 | @export("___chkstk_ms", ___chkstk_ms, linkage); |
| 64 | 60 | } |
| 61 | @export("__udivti3", @import("udivti3.zig").__udivti3_windows_x86_64, linkage); | |
| 62 | @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4_windows_x86_64, linkage); | |
| 63 | @export("__umodti3", @import("umodti3.zig").__umodti3_windows_x86_64, linkage); | |
| 65 | 64 | }, |
| 66 | 65 | else => {}, |
| 67 | 66 | } |
| 67 | } else { | |
| 68 | @export("__udivti3", @import("udivti3.zig").__udivti3, linkage); | |
| 69 | @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4, linkage); | |
| 70 | @export("__umodti3", @import("umodti3.zig").__umodti3, linkage); | |
| 68 | 71 | } |
| 69 | 72 | } |
| 70 | 73 | |
| ... | ... | @@ -83,6 +86,16 @@ pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn |
| 83 | 86 | } |
| 84 | 87 | } |
| 85 | 88 | |
| 89 | pub fn setXmm0(comptime T: type, value: T) void { | |
| 90 | comptime assert(builtin.arch == builtin.Arch.x86_64); | |
| 91 | const aligned_value: T align(16) = value; | |
| 92 | asm volatile ( | |
| 93 | \\movaps (%[ptr]), %%xmm0 | |
| 94 | : | |
| 95 | : [ptr] "r" (&aligned_value) | |
| 96 | : "xmm0"); | |
| 97 | } | |
| 98 | ||
| 86 | 99 | extern fn __udivdi3(a: u64, b: u64) u64 { |
| 87 | 100 | @setRuntimeSafety(is_test); |
| 88 | 101 | return __udivmoddi4(a, b, null); |
std/special/compiler_rt/udivmodti4.zig+6| ... | ... | @@ -1,11 +1,17 @@ |
| 1 | 1 | const udivmod = @import("udivmod.zig").udivmod; |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | const compiler_rt = @import("index.zig"); | |
| 3 | 4 | |
| 4 | 5 | pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?&u128) u128 { |
| 5 | 6 | @setRuntimeSafety(builtin.is_test); |
| 6 | 7 | return udivmod(u128, a, b, maybe_rem); |
| 7 | 8 | } |
| 8 | 9 | |
| 10 | pub extern fn __udivmodti4_windows_x86_64(a: &const u128, b: &const u128, maybe_rem: ?&u128) void { | |
| 11 | @setRuntimeSafety(builtin.is_test); | |
| 12 | compiler_rt.setXmm0(u128, udivmod(u128, *a, *b, maybe_rem)); | |
| 13 | } | |
| 14 | ||
| 9 | 15 | test "import udivmodti4" { |
| 10 | 16 | _ = @import("udivmodti4_test.zig"); |
| 11 | 17 | } |
std/special/compiler_rt/udivti3.zig+7-2| ... | ... | @@ -1,7 +1,12 @@ |
| 1 | const __udivmodti4 = @import("udivmodti4.zig").__udivmodti4; | |
| 1 | const udivmodti4 = @import("udivmodti4.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | |
| 4 | 4 | pub extern fn __udivti3(a: u128, b: u128) u128 { |
| 5 | 5 | @setRuntimeSafety(builtin.is_test); |
| 6 | return __udivmodti4(a, b, null); | |
| 6 | return udivmodti4.__udivmodti4(a, b, null); | |
| 7 | } | |
| 8 | ||
| 9 | pub extern fn __udivti3_windows_x86_64(a: &const u128, b: &const u128) void { | |
| 10 | @setRuntimeSafety(builtin.is_test); | |
| 11 | udivmodti4.__udivmodti4_windows_x86_64(a, b, null); | |
| 7 | 12 | } |
std/special/compiler_rt/umodti3.zig+8-2| ... | ... | @@ -1,9 +1,15 @@ |
| 1 | const __udivmodti4 = @import("udivmodti4.zig").__udivmodti4; | |
| 1 | const udivmodti4 = @import("udivmodti4.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | const compiler_rt = @import("index.zig"); | |
| 3 | 4 | |
| 4 | 5 | pub extern fn __umodti3(a: u128, b: u128) u128 { |
| 5 | 6 | @setRuntimeSafety(builtin.is_test); |
| 6 | 7 | var r: u128 = undefined; |
| 7 | _ = __udivmodti4(a, b, &r); | |
| 8 | _ = udivmodti4.__udivmodti4(a, b, &r); | |
| 8 | 9 | return r; |
| 9 | 10 | } |
| 11 | ||
| 12 | pub extern fn __umodti3_windows_x86_64(a: &const u128, b: &const u128) void { | |
| 13 | @setRuntimeSafety(builtin.is_test); | |
| 14 | compiler_rt.setXmm0(u128, __umodti3(*a, *b)); | |
| 15 | } |
std/unicode.zig+151-12| ... | ... | @@ -1,6 +1,16 @@ |
| 1 | 1 | const std = @import("./index.zig"); |
| 2 | 2 | const debug = std.debug; |
| 3 | 3 | |
| 4 | /// Returns how many bytes the UTF-8 representation would require | |
| 5 | /// for the given codepoint. | |
| 6 | pub fn utf8CodepointSequenceLength(c: u32) !u3 { | |
| 7 | if (c < 0x80) return u3(1); | |
| 8 | if (c < 0x800) return u3(2); | |
| 9 | if (c < 0x10000) return u3(3); | |
| 10 | if (c < 0x110000) return u3(4); | |
| 11 | return error.CodepointTooLarge; | |
| 12 | } | |
| 13 | ||
| 4 | 14 | /// Given the first byte of a UTF-8 codepoint, |
| 5 | 15 | /// returns a number 1-4 indicating the total length of the codepoint in bytes. |
| 6 | 16 | /// If this byte does not match the form of a UTF-8 start byte, returns Utf8InvalidStartByte. |
| ... | ... | @@ -12,11 +22,47 @@ pub fn utf8ByteSequenceLength(first_byte: u8) !u3 { |
| 12 | 22 | return error.Utf8InvalidStartByte; |
| 13 | 23 | } |
| 14 | 24 | |
| 25 | /// Encodes the given codepoint into a UTF-8 byte sequence. | |
| 26 | /// c: the codepoint. | |
| 27 | /// out: the out buffer to write to. Must have a len >= utf8CodepointSequenceLength(c). | |
| 28 | /// Errors: if c cannot be encoded in UTF-8. | |
| 29 | /// Returns: the number of bytes written to out. | |
| 30 | pub fn utf8Encode(c: u32, out: []u8) !u3 { | |
| 31 | const length = try utf8CodepointSequenceLength(c); | |
| 32 | debug.assert(out.len >= length); | |
| 33 | switch (length) { | |
| 34 | // The pattern for each is the same | |
| 35 | // - Increasing the initial shift by 6 each time | |
| 36 | // - Each time after the first shorten the shifted | |
| 37 | // value to a max of 0b111111 (63) | |
| 38 | 1 => out[0] = u8(c), // Can just do 0 + codepoint for initial range | |
| 39 | 2 => { | |
| 40 | out[0] = u8(0b11000000 | (c >> 6)); | |
| 41 | out[1] = u8(0b10000000 | (c & 0b111111)); | |
| 42 | }, | |
| 43 | 3 => { | |
| 44 | if (0xd800 <= c and c <= 0xdfff) return error.Utf8CannotEncodeSurrogateHalf; | |
| 45 | out[0] = u8(0b11100000 | (c >> 12)); | |
| 46 | out[1] = u8(0b10000000 | ((c >> 6) & 0b111111)); | |
| 47 | out[2] = u8(0b10000000 | (c & 0b111111)); | |
| 48 | }, | |
| 49 | 4 => { | |
| 50 | out[0] = u8(0b11110000 | (c >> 18)); | |
| 51 | out[1] = u8(0b10000000 | ((c >> 12) & 0b111111)); | |
| 52 | out[2] = u8(0b10000000 | ((c >> 6) & 0b111111)); | |
| 53 | out[3] = u8(0b10000000 | (c & 0b111111)); | |
| 54 | }, | |
| 55 | else => unreachable, | |
| 56 | } | |
| 57 | return length; | |
| 58 | } | |
| 59 | ||
| 60 | const Utf8DecodeError = Utf8Decode2Error || Utf8Decode3Error || Utf8Decode4Error; | |
| 15 | 61 | /// Decodes the UTF-8 codepoint encoded in the given slice of bytes. |
| 16 | 62 | /// bytes.len must be equal to utf8ByteSequenceLength(bytes[0]) catch unreachable. |
| 17 | 63 | /// If you already know the length at comptime, you can call one of |
| 18 | 64 | /// utf8Decode2,utf8Decode3,utf8Decode4 directly instead of this function. |
| 19 | pub fn utf8Decode(bytes: []const u8) !u32 { | |
| 65 | pub fn utf8Decode(bytes: []const u8) Utf8DecodeError!u32 { | |
| 20 | 66 | return switch (bytes.len) { |
| 21 | 67 | 1 => u32(bytes[0]), |
| 22 | 68 | 2 => utf8Decode2(bytes), |
| ... | ... | @@ -25,7 +71,12 @@ pub fn utf8Decode(bytes: []const u8) !u32 { |
| 25 | 71 | else => unreachable, |
| 26 | 72 | }; |
| 27 | 73 | } |
| 28 | pub fn utf8Decode2(bytes: []const u8) !u32 { | |
| 74 | ||
| 75 | const Utf8Decode2Error = error{ | |
| 76 | Utf8ExpectedContinuation, | |
| 77 | Utf8OverlongEncoding, | |
| 78 | }; | |
| 79 | pub fn utf8Decode2(bytes: []const u8) Utf8Decode2Error!u32 { | |
| 29 | 80 | debug.assert(bytes.len == 2); |
| 30 | 81 | debug.assert(bytes[0] & 0b11100000 == 0b11000000); |
| 31 | 82 | var value: u32 = bytes[0] & 0b00011111; |
| ... | ... | @@ -38,7 +89,13 @@ pub fn utf8Decode2(bytes: []const u8) !u32 { |
| 38 | 89 | |
| 39 | 90 | return value; |
| 40 | 91 | } |
| 41 | pub fn utf8Decode3(bytes: []const u8) !u32 { | |
| 92 | ||
| 93 | const Utf8Decode3Error = error{ | |
| 94 | Utf8ExpectedContinuation, | |
| 95 | Utf8OverlongEncoding, | |
| 96 | Utf8EncodesSurrogateHalf, | |
| 97 | }; | |
| 98 | pub fn utf8Decode3(bytes: []const u8) Utf8Decode3Error!u32 { | |
| 42 | 99 | debug.assert(bytes.len == 3); |
| 43 | 100 | debug.assert(bytes[0] & 0b11110000 == 0b11100000); |
| 44 | 101 | var value: u32 = bytes[0] & 0b00001111; |
| ... | ... | @@ -56,7 +113,13 @@ pub fn utf8Decode3(bytes: []const u8) !u32 { |
| 56 | 113 | |
| 57 | 114 | return value; |
| 58 | 115 | } |
| 59 | pub fn utf8Decode4(bytes: []const u8) !u32 { | |
| 116 | ||
| 117 | const Utf8Decode4Error = error{ | |
| 118 | Utf8ExpectedContinuation, | |
| 119 | Utf8OverlongEncoding, | |
| 120 | Utf8CodepointTooLarge, | |
| 121 | }; | |
| 122 | pub fn utf8Decode4(bytes: []const u8) Utf8Decode4Error!u32 { | |
| 60 | 123 | debug.assert(bytes.len == 4); |
| 61 | 124 | debug.assert(bytes[0] & 0b11111000 == 0b11110000); |
| 62 | 125 | var value: u32 = bytes[0] & 0b00000111; |
| ... | ... | @@ -158,19 +221,67 @@ const Utf8Iterator = struct { |
| 158 | 221 | pub fn nextCodepoint(it: &Utf8Iterator) ?u32 { |
| 159 | 222 | const slice = it.nextCodepointSlice() ?? return null; |
| 160 | 223 | |
| 161 | const r = switch (slice.len) { | |
| 162 | 1 => u32(slice[0]), | |
| 163 | 2 => utf8Decode2(slice), | |
| 164 | 3 => utf8Decode3(slice), | |
| 165 | 4 => utf8Decode4(slice), | |
| 224 | switch (slice.len) { | |
| 225 | 1 => return u32(slice[0]), | |
| 226 | 2 => return utf8Decode2(slice) catch unreachable, | |
| 227 | 3 => return utf8Decode3(slice) catch unreachable, | |
| 228 | 4 => return utf8Decode4(slice) catch unreachable, | |
| 166 | 229 | else => unreachable, |
| 167 | }; | |
| 168 | ||
| 169 | return r catch unreachable; | |
| 230 | } | |
| 170 | 231 | } |
| 171 | 232 | }; |
| 172 | 233 | |
| 234 | test "utf8 encode" { | |
| 235 | comptime testUtf8Encode() catch unreachable; | |
| 236 | try testUtf8Encode(); | |
| 237 | } | |
| 238 | fn testUtf8Encode() !void { | |
| 239 | // A few taken from wikipedia a few taken elsewhere | |
| 240 | var array: [4]u8 = undefined; | |
| 241 | debug.assert((try utf8Encode(try utf8Decode("€"), array[0..])) == 3); | |
| 242 | debug.assert(array[0] == 0b11100010); | |
| 243 | debug.assert(array[1] == 0b10000010); | |
| 244 | debug.assert(array[2] == 0b10101100); | |
| 245 | ||
| 246 | debug.assert((try utf8Encode(try utf8Decode("$"), array[0..])) == 1); | |
| 247 | debug.assert(array[0] == 0b00100100); | |
| 248 | ||
| 249 | debug.assert((try utf8Encode(try utf8Decode("¢"), array[0..])) == 2); | |
| 250 | debug.assert(array[0] == 0b11000010); | |
| 251 | debug.assert(array[1] == 0b10100010); | |
| 252 | ||
| 253 | debug.assert((try utf8Encode(try utf8Decode("𐍈"), array[0..])) == 4); | |
| 254 | debug.assert(array[0] == 0b11110000); | |
| 255 | debug.assert(array[1] == 0b10010000); | |
| 256 | debug.assert(array[2] == 0b10001101); | |
| 257 | debug.assert(array[3] == 0b10001000); | |
| 258 | } | |
| 259 | ||
| 260 | test "utf8 encode error" { | |
| 261 | comptime testUtf8EncodeError(); | |
| 262 | testUtf8EncodeError(); | |
| 263 | } | |
| 264 | fn testUtf8EncodeError() void { | |
| 265 | var array: [4]u8 = undefined; | |
| 266 | testErrorEncode(0xd800, array[0..], error.Utf8CannotEncodeSurrogateHalf); | |
| 267 | testErrorEncode(0xdfff, array[0..], error.Utf8CannotEncodeSurrogateHalf); | |
| 268 | testErrorEncode(0x110000, array[0..], error.CodepointTooLarge); | |
| 269 | testErrorEncode(0xffffffff, array[0..], error.CodepointTooLarge); | |
| 270 | } | |
| 271 | ||
| 272 | fn testErrorEncode(codePoint: u32, array: []u8, expectedErr: error) void { | |
| 273 | if (utf8Encode(codePoint, array)) |_| { | |
| 274 | unreachable; | |
| 275 | } else |err| { | |
| 276 | debug.assert(err == expectedErr); | |
| 277 | } | |
| 278 | } | |
| 279 | ||
| 173 | 280 | test "utf8 iterator on ascii" { |
| 281 | comptime testUtf8IteratorOnAscii(); | |
| 282 | testUtf8IteratorOnAscii(); | |
| 283 | } | |
| 284 | fn testUtf8IteratorOnAscii() void { | |
| 174 | 285 | const s = Utf8View.initComptime("abc"); |
| 175 | 286 | |
| 176 | 287 | var it1 = s.iterator(); |
| ... | ... | @@ -187,6 +298,10 @@ test "utf8 iterator on ascii" { |
| 187 | 298 | } |
| 188 | 299 | |
| 189 | 300 | test "utf8 view bad" { |
| 301 | comptime testUtf8ViewBad(); | |
| 302 | testUtf8ViewBad(); | |
| 303 | } | |
| 304 | fn testUtf8ViewBad() void { | |
| 190 | 305 | // Compile-time error. |
| 191 | 306 | // const s3 = Utf8View.initComptime("\xfe\xf2"); |
| 192 | 307 | |
| ... | ... | @@ -195,6 +310,10 @@ test "utf8 view bad" { |
| 195 | 310 | } |
| 196 | 311 | |
| 197 | 312 | test "utf8 view ok" { |
| 313 | comptime testUtf8ViewOk(); | |
| 314 | testUtf8ViewOk(); | |
| 315 | } | |
| 316 | fn testUtf8ViewOk() void { | |
| 198 | 317 | const s = Utf8View.initComptime("東京市"); |
| 199 | 318 | |
| 200 | 319 | var it1 = s.iterator(); |
| ... | ... | @@ -211,6 +330,10 @@ test "utf8 view ok" { |
| 211 | 330 | } |
| 212 | 331 | |
| 213 | 332 | test "bad utf8 slice" { |
| 333 | comptime testBadUtf8Slice(); | |
| 334 | testBadUtf8Slice(); | |
| 335 | } | |
| 336 | fn testBadUtf8Slice() void { | |
| 214 | 337 | debug.assert(utf8ValidateSlice("abc")); |
| 215 | 338 | debug.assert(!utf8ValidateSlice("abc\xc0")); |
| 216 | 339 | debug.assert(!utf8ValidateSlice("abc\xc0abc")); |
| ... | ... | @@ -218,6 +341,10 @@ test "bad utf8 slice" { |
| 218 | 341 | } |
| 219 | 342 | |
| 220 | 343 | test "valid utf8" { |
| 344 | comptime testValidUtf8(); | |
| 345 | testValidUtf8(); | |
| 346 | } | |
| 347 | fn testValidUtf8() void { | |
| 221 | 348 | testValid("\x00", 0x0); |
| 222 | 349 | testValid("\x20", 0x20); |
| 223 | 350 | testValid("\x7f", 0x7f); |
| ... | ... | @@ -233,6 +360,10 @@ test "valid utf8" { |
| 233 | 360 | } |
| 234 | 361 | |
| 235 | 362 | test "invalid utf8 continuation bytes" { |
| 363 | comptime testInvalidUtf8ContinuationBytes(); | |
| 364 | testInvalidUtf8ContinuationBytes(); | |
| 365 | } | |
| 366 | fn testInvalidUtf8ContinuationBytes() void { | |
| 236 | 367 | // unexpected continuation |
| 237 | 368 | testError("\x80", error.Utf8InvalidStartByte); |
| 238 | 369 | testError("\xbf", error.Utf8InvalidStartByte); |
| ... | ... | @@ -261,6 +392,10 @@ test "invalid utf8 continuation bytes" { |
| 261 | 392 | } |
| 262 | 393 | |
| 263 | 394 | test "overlong utf8 codepoint" { |
| 395 | comptime testOverlongUtf8Codepoint(); | |
| 396 | testOverlongUtf8Codepoint(); | |
| 397 | } | |
| 398 | fn testOverlongUtf8Codepoint() void { | |
| 264 | 399 | testError("\xc0\x80", error.Utf8OverlongEncoding); |
| 265 | 400 | testError("\xc1\xbf", error.Utf8OverlongEncoding); |
| 266 | 401 | testError("\xe0\x80\x80", error.Utf8OverlongEncoding); |
| ... | ... | @@ -270,6 +405,10 @@ test "overlong utf8 codepoint" { |
| 270 | 405 | } |
| 271 | 406 | |
| 272 | 407 | test "misc invalid utf8" { |
| 408 | comptime testMiscInvalidUtf8(); | |
| 409 | testMiscInvalidUtf8(); | |
| 410 | } | |
| 411 | fn testMiscInvalidUtf8() void { | |
| 273 | 412 | // codepoint out of bounds |
| 274 | 413 | testError("\xf4\x90\x80\x80", error.Utf8CodepointTooLarge); |
| 275 | 414 | testError("\xf7\xbf\xbf\xbf", error.Utf8CodepointTooLarge); |
std/zig/ast.zig+112-10| ... | ... | @@ -6,6 +6,7 @@ const mem = std.mem; |
| 6 | 6 | |
| 7 | 7 | pub const Node = struct { |
| 8 | 8 | id: Id, |
| 9 | same_line_comment: ?&Token, | |
| 9 | 10 | |
| 10 | 11 | pub const Id = enum { |
| 11 | 12 | // Top level |
| ... | ... | @@ -34,6 +35,7 @@ pub const Node = struct { |
| 34 | 35 | VarType, |
| 35 | 36 | ErrorType, |
| 36 | 37 | FnProto, |
| 38 | PromiseType, | |
| 37 | 39 | |
| 38 | 40 | // Primary expressions |
| 39 | 41 | IntegerLiteral, |
| ... | ... | @@ -57,6 +59,7 @@ pub const Node = struct { |
| 57 | 59 | |
| 58 | 60 | // Misc |
| 59 | 61 | LineComment, |
| 62 | DocComment, | |
| 60 | 63 | SwitchCase, |
| 61 | 64 | SwitchElse, |
| 62 | 65 | Else, |
| ... | ... | @@ -66,6 +69,7 @@ pub const Node = struct { |
| 66 | 69 | StructField, |
| 67 | 70 | UnionTag, |
| 68 | 71 | EnumTag, |
| 72 | ErrorTag, | |
| 69 | 73 | AsmInput, |
| 70 | 74 | AsmOutput, |
| 71 | 75 | AsyncAttribute, |
| ... | ... | @@ -73,6 +77,13 @@ pub const Node = struct { |
| 73 | 77 | FieldInitializer, |
| 74 | 78 | }; |
| 75 | 79 | |
| 80 | pub fn cast(base: &Node, comptime T: type) ?&T { | |
| 81 | if (base.id == comptime typeToId(T)) { | |
| 82 | return @fieldParentPtr(T, "base", base); | |
| 83 | } | |
| 84 | return null; | |
| 85 | } | |
| 86 | ||
| 76 | 87 | pub fn iterate(base: &Node, index: usize) ?&Node { |
| 77 | 88 | comptime var i = 0; |
| 78 | 89 | inline while (i < @memberCount(Id)) : (i += 1) { |
| ... | ... | @@ -118,6 +129,7 @@ pub const Node = struct { |
| 118 | 129 | |
| 119 | 130 | pub const Root = struct { |
| 120 | 131 | base: Node, |
| 132 | doc_comments: ?&DocComment, | |
| 121 | 133 | decls: ArrayList(&Node), |
| 122 | 134 | eof_token: Token, |
| 123 | 135 | |
| ... | ... | @@ -139,7 +151,7 @@ pub const Node = struct { |
| 139 | 151 | |
| 140 | 152 | pub const VarDecl = struct { |
| 141 | 153 | base: Node, |
| 142 | comments: ?&LineComment, | |
| 154 | doc_comments: ?&DocComment, | |
| 143 | 155 | visib_token: ?Token, |
| 144 | 156 | name_token: Token, |
| 145 | 157 | eq_token: Token, |
| ... | ... | @@ -188,6 +200,7 @@ pub const Node = struct { |
| 188 | 200 | |
| 189 | 201 | pub const Use = struct { |
| 190 | 202 | base: Node, |
| 203 | doc_comments: ?&DocComment, | |
| 191 | 204 | visib_token: ?Token, |
| 192 | 205 | expr: &Node, |
| 193 | 206 | semicolon_token: Token, |
| ... | ... | @@ -258,7 +271,7 @@ pub const Node = struct { |
| 258 | 271 | |
| 259 | 272 | const InitArg = union(enum) { |
| 260 | 273 | None, |
| 261 | Enum, | |
| 274 | Enum: ?&Node, | |
| 262 | 275 | Type: &Node, |
| 263 | 276 | }; |
| 264 | 277 | |
| ... | ... | @@ -291,6 +304,7 @@ pub const Node = struct { |
| 291 | 304 | |
| 292 | 305 | pub const StructField = struct { |
| 293 | 306 | base: Node, |
| 307 | doc_comments: ?&DocComment, | |
| 294 | 308 | visib_token: ?Token, |
| 295 | 309 | name_token: Token, |
| 296 | 310 | type_expr: &Node, |
| ... | ... | @@ -316,8 +330,10 @@ pub const Node = struct { |
| 316 | 330 | |
| 317 | 331 | pub const UnionTag = struct { |
| 318 | 332 | base: Node, |
| 333 | doc_comments: ?&DocComment, | |
| 319 | 334 | name_token: Token, |
| 320 | 335 | type_expr: ?&Node, |
| 336 | value_expr: ?&Node, | |
| 321 | 337 | |
| 322 | 338 | pub fn iterate(self: &UnionTag, index: usize) ?&Node { |
| 323 | 339 | var i = index; |
| ... | ... | @@ -327,6 +343,11 @@ pub const Node = struct { |
| 327 | 343 | i -= 1; |
| 328 | 344 | } |
| 329 | 345 | |
| 346 | if (self.value_expr) |value_expr| { | |
| 347 | if (i < 1) return value_expr; | |
| 348 | i -= 1; | |
| 349 | } | |
| 350 | ||
| 330 | 351 | return null; |
| 331 | 352 | } |
| 332 | 353 | |
| ... | ... | @@ -335,6 +356,9 @@ pub const Node = struct { |
| 335 | 356 | } |
| 336 | 357 | |
| 337 | 358 | pub fn lastToken(self: &UnionTag) Token { |
| 359 | if (self.value_expr) |value_expr| { | |
| 360 | return value_expr.lastToken(); | |
| 361 | } | |
| 338 | 362 | if (self.type_expr) |type_expr| { |
| 339 | 363 | return type_expr.lastToken(); |
| 340 | 364 | } |
| ... | ... | @@ -345,6 +369,7 @@ pub const Node = struct { |
| 345 | 369 | |
| 346 | 370 | pub const EnumTag = struct { |
| 347 | 371 | base: Node, |
| 372 | doc_comments: ?&DocComment, | |
| 348 | 373 | name_token: Token, |
| 349 | 374 | value: ?&Node, |
| 350 | 375 | |
| ... | ... | @@ -372,6 +397,31 @@ pub const Node = struct { |
| 372 | 397 | } |
| 373 | 398 | }; |
| 374 | 399 | |
| 400 | pub const ErrorTag = struct { | |
| 401 | base: Node, | |
| 402 | doc_comments: ?&DocComment, | |
| 403 | name_token: Token, | |
| 404 | ||
| 405 | pub fn iterate(self: &ErrorTag, index: usize) ?&Node { | |
| 406 | var i = index; | |
| 407 | ||
| 408 | if (self.doc_comments) |comments| { | |
| 409 | if (i < 1) return &comments.base; | |
| 410 | i -= 1; | |
| 411 | } | |
| 412 | ||
| 413 | return null; | |
| 414 | } | |
| 415 | ||
| 416 | pub fn firstToken(self: &ErrorTag) Token { | |
| 417 | return self.name_token; | |
| 418 | } | |
| 419 | ||
| 420 | pub fn lastToken(self: &ErrorTag) Token { | |
| 421 | return self.name_token; | |
| 422 | } | |
| 423 | }; | |
| 424 | ||
| 375 | 425 | pub const Identifier = struct { |
| 376 | 426 | base: Node, |
| 377 | 427 | token: Token, |
| ... | ... | @@ -421,7 +471,7 @@ pub const Node = struct { |
| 421 | 471 | |
| 422 | 472 | pub const FnProto = struct { |
| 423 | 473 | base: Node, |
| 424 | comments: ?&LineComment, | |
| 474 | doc_comments: ?&DocComment, | |
| 425 | 475 | visib_token: ?Token, |
| 426 | 476 | fn_token: Token, |
| 427 | 477 | name_token: ?Token, |
| ... | ... | @@ -494,6 +544,37 @@ pub const Node = struct { |
| 494 | 544 | } |
| 495 | 545 | }; |
| 496 | 546 | |
| 547 | pub const PromiseType = struct { | |
| 548 | base: Node, | |
| 549 | promise_token: Token, | |
| 550 | result: ?Result, | |
| 551 | ||
| 552 | pub const Result = struct { | |
| 553 | arrow_token: Token, | |
| 554 | return_type: &Node, | |
| 555 | }; | |
| 556 | ||
| 557 | pub fn iterate(self: &PromiseType, index: usize) ?&Node { | |
| 558 | var i = index; | |
| 559 | ||
| 560 | if (self.result) |result| { | |
| 561 | if (i < 1) return result.return_type; | |
| 562 | i -= 1; | |
| 563 | } | |
| 564 | ||
| 565 | return null; | |
| 566 | } | |
| 567 | ||
| 568 | pub fn firstToken(self: &PromiseType) Token { | |
| 569 | return self.promise_token; | |
| 570 | } | |
| 571 | ||
| 572 | pub fn lastToken(self: &PromiseType) Token { | |
| 573 | if (self.result) |result| return result.return_type.lastToken(); | |
| 574 | return self.promise_token; | |
| 575 | } | |
| 576 | }; | |
| 577 | ||
| 497 | 578 | pub const ParamDecl = struct { |
| 498 | 579 | base: Node, |
| 499 | 580 | comptime_token: ?Token, |
| ... | ... | @@ -584,6 +665,7 @@ pub const Node = struct { |
| 584 | 665 | |
| 585 | 666 | pub const Comptime = struct { |
| 586 | 667 | base: Node, |
| 668 | doc_comments: ?&DocComment, | |
| 587 | 669 | comptime_token: Token, |
| 588 | 670 | expr: &Node, |
| 589 | 671 | |
| ... | ... | @@ -718,7 +800,8 @@ pub const Node = struct { |
| 718 | 800 | base: Node, |
| 719 | 801 | switch_token: Token, |
| 720 | 802 | expr: &Node, |
| 721 | cases: ArrayList(&SwitchCase), | |
| 803 | /// these can be SwitchCase nodes or LineComment nodes | |
| 804 | cases: ArrayList(&Node), | |
| 722 | 805 | rbrace: Token, |
| 723 | 806 | |
| 724 | 807 | pub fn iterate(self: &Switch, index: usize) ?&Node { |
| ... | ... | @@ -727,7 +810,7 @@ pub const Node = struct { |
| 727 | 810 | if (i < 1) return self.expr; |
| 728 | 811 | i -= 1; |
| 729 | 812 | |
| 730 | if (i < self.cases.len) return &self.cases.at(i).base; | |
| 813 | if (i < self.cases.len) return self.cases.at(i); | |
| 731 | 814 | i -= self.cases.len; |
| 732 | 815 | |
| 733 | 816 | return null; |
| ... | ... | @@ -1186,7 +1269,7 @@ pub const Node = struct { |
| 1186 | 1269 | ArrayAccess: &Node, |
| 1187 | 1270 | Slice: SliceRange, |
| 1188 | 1271 | ArrayInitializer: ArrayList(&Node), |
| 1189 | StructInitializer: ArrayList(&FieldInitializer), | |
| 1272 | StructInitializer: ArrayList(&Node), | |
| 1190 | 1273 | }; |
| 1191 | 1274 | |
| 1192 | 1275 | const CallInfo = struct { |
| ... | ... | @@ -1228,7 +1311,7 @@ pub const Node = struct { |
| 1228 | 1311 | i -= exprs.len; |
| 1229 | 1312 | }, |
| 1230 | 1313 | Op.StructInitializer => |fields| { |
| 1231 | if (i < fields.len) return &fields.at(i).base; | |
| 1314 | if (i < fields.len) return fields.at(i); | |
| 1232 | 1315 | i -= fields.len; |
| 1233 | 1316 | }, |
| 1234 | 1317 | } |
| ... | ... | @@ -1337,6 +1420,7 @@ pub const Node = struct { |
| 1337 | 1420 | |
| 1338 | 1421 | pub const Suspend = struct { |
| 1339 | 1422 | base: Node, |
| 1423 | label: ?Token, | |
| 1340 | 1424 | suspend_token: Token, |
| 1341 | 1425 | payload: ?&Node, |
| 1342 | 1426 | body: ?&Node, |
| ... | ... | @@ -1358,6 +1442,7 @@ pub const Node = struct { |
| 1358 | 1442 | } |
| 1359 | 1443 | |
| 1360 | 1444 | pub fn firstToken(self: &Suspend) Token { |
| 1445 | if (self.label) |label| return label; | |
| 1361 | 1446 | return self.suspend_token; |
| 1362 | 1447 | } |
| 1363 | 1448 | |
| ... | ... | @@ -1715,24 +1800,41 @@ pub const Node = struct { |
| 1715 | 1800 | |
| 1716 | 1801 | pub const LineComment = struct { |
| 1717 | 1802 | base: Node, |
| 1718 | lines: ArrayList(Token), | |
| 1803 | token: Token, | |
| 1719 | 1804 | |
| 1720 | 1805 | pub fn iterate(self: &LineComment, index: usize) ?&Node { |
| 1721 | 1806 | return null; |
| 1722 | 1807 | } |
| 1723 | 1808 | |
| 1724 | 1809 | pub fn firstToken(self: &LineComment) Token { |
| 1725 | return self.lines.at(0); | |
| 1810 | return self.token; | |
| 1726 | 1811 | } |
| 1727 | 1812 | |
| 1728 | 1813 | pub fn lastToken(self: &LineComment) Token { |
| 1814 | return self.token; | |
| 1815 | } | |
| 1816 | }; | |
| 1817 | ||
| 1818 | pub const DocComment = struct { | |
| 1819 | base: Node, | |
| 1820 | lines: ArrayList(Token), | |
| 1821 | ||
| 1822 | pub fn iterate(self: &DocComment, index: usize) ?&Node { | |
| 1823 | return null; | |
| 1824 | } | |
| 1825 | ||
| 1826 | pub fn firstToken(self: &DocComment) Token { | |
| 1827 | return self.lines.at(0); | |
| 1828 | } | |
| 1829 | ||
| 1830 | pub fn lastToken(self: &DocComment) Token { | |
| 1729 | 1831 | return self.lines.at(self.lines.len - 1); |
| 1730 | 1832 | } |
| 1731 | 1833 | }; |
| 1732 | 1834 | |
| 1733 | 1835 | pub const TestDecl = struct { |
| 1734 | 1836 | base: Node, |
| 1735 | comments: ?&LineComment, | |
| 1837 | doc_comments: ?&DocComment, | |
| 1736 | 1838 | test_token: Token, |
| 1737 | 1839 | name: &Node, |
| 1738 | 1840 | body_node: &Node, |
std/zig/parser.zig+607-1195| ... | ... | @@ -55,6 +55,7 @@ pub const Parser = struct { |
| 55 | 55 | visib_token: ?Token, |
| 56 | 56 | extern_export_inline_token: ?Token, |
| 57 | 57 | lib_name: ?&ast.Node, |
| 58 | comments: ?&ast.Node.DocComment, | |
| 58 | 59 | }; |
| 59 | 60 | |
| 60 | 61 | const VarDeclCtx = struct { |
| ... | ... | @@ -64,18 +65,19 @@ pub const Parser = struct { |
| 64 | 65 | extern_export_token: ?Token, |
| 65 | 66 | lib_name: ?&ast.Node, |
| 66 | 67 | list: &ArrayList(&ast.Node), |
| 67 | comments: ?&ast.Node.LineComment, | |
| 68 | comments: ?&ast.Node.DocComment, | |
| 68 | 69 | }; |
| 69 | 70 | |
| 70 | 71 | const TopLevelExternOrFieldCtx = struct { |
| 71 | 72 | visib_token: Token, |
| 72 | 73 | container_decl: &ast.Node.ContainerDecl, |
| 74 | comments: ?&ast.Node.DocComment, | |
| 73 | 75 | }; |
| 74 | 76 | |
| 75 | 77 | const ExternTypeCtx = struct { |
| 76 | 78 | opt_ctx: OptionalCtx, |
| 77 | 79 | extern_token: Token, |
| 78 | comments: ?&ast.Node.LineComment, | |
| 80 | comments: ?&ast.Node.DocComment, | |
| 79 | 81 | }; |
| 80 | 82 | |
| 81 | 83 | const ContainerKindCtx = struct { |
| ... | ... | @@ -182,6 +184,11 @@ pub const Parser = struct { |
| 182 | 184 | } |
| 183 | 185 | }; |
| 184 | 186 | |
| 187 | const AddCommentsCtx = struct { | |
| 188 | node_ptr: &&ast.Node, | |
| 189 | comments: ?&ast.Node.DocComment, | |
| 190 | }; | |
| 191 | ||
| 185 | 192 | const State = union(enum) { |
| 186 | 193 | TopLevel, |
| 187 | 194 | TopLevelExtern: TopLevelDeclCtx, |
| ... | ... | @@ -221,6 +228,8 @@ pub const Parser = struct { |
| 221 | 228 | Statement: &ast.Node.Block, |
| 222 | 229 | ComptimeStatement: ComptimeStatementCtx, |
| 223 | 230 | Semicolon: &&ast.Node, |
| 231 | LookForSameLineComment: &&ast.Node, | |
| 232 | LookForSameLineCommentDirect: &ast.Node, | |
| 224 | 233 | |
| 225 | 234 | AsmOutputItems: &ArrayList(&ast.Node.AsmOutput), |
| 226 | 235 | AsmOutputReturnOrType: &ast.Node.AsmOutput, |
| ... | ... | @@ -229,13 +238,14 @@ pub const Parser = struct { |
| 229 | 238 | |
| 230 | 239 | ExprListItemOrEnd: ExprListCtx, |
| 231 | 240 | ExprListCommaOrEnd: ExprListCtx, |
| 232 | FieldInitListItemOrEnd: ListSave(&ast.Node.FieldInitializer), | |
| 233 | FieldInitListCommaOrEnd: ListSave(&ast.Node.FieldInitializer), | |
| 241 | FieldInitListItemOrEnd: ListSave(&ast.Node), | |
| 242 | FieldInitListCommaOrEnd: ListSave(&ast.Node), | |
| 234 | 243 | FieldListCommaOrEnd: &ast.Node.ContainerDecl, |
| 235 | IdentifierListItemOrEnd: ListSave(&ast.Node), | |
| 236 | IdentifierListCommaOrEnd: ListSave(&ast.Node), | |
| 237 | SwitchCaseOrEnd: ListSave(&ast.Node.SwitchCase), | |
| 238 | SwitchCaseCommaOrEnd: ListSave(&ast.Node.SwitchCase), | |
| 244 | FieldInitValue: OptionalCtx, | |
| 245 | ErrorTagListItemOrEnd: ListSave(&ast.Node), | |
| 246 | ErrorTagListCommaOrEnd: ListSave(&ast.Node), | |
| 247 | SwitchCaseOrEnd: ListSave(&ast.Node), | |
| 248 | SwitchCaseCommaOrEnd: ListSave(&ast.Node), | |
| 239 | 249 | SwitchCaseFirstItem: &ArrayList(&ast.Node), |
| 240 | 250 | SwitchCaseItem: &ArrayList(&ast.Node), |
| 241 | 251 | SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node), |
| ... | ... | @@ -290,6 +300,7 @@ pub const Parser = struct { |
| 290 | 300 | ErrorTypeOrSetDecl: ErrorTypeOrSetDeclCtx, |
| 291 | 301 | StringLiteral: OptionalCtx, |
| 292 | 302 | Identifier: OptionalCtx, |
| 303 | ErrorTag: &&ast.Node, | |
| 293 | 304 | |
| 294 | 305 | |
| 295 | 306 | IfToken: @TagType(Token.Id), |
| ... | ... | @@ -314,6 +325,7 @@ pub const Parser = struct { |
| 314 | 325 | ast.Node.Root { |
| 315 | 326 | .base = undefined, |
| 316 | 327 | .decls = ArrayList(&ast.Node).init(arena), |
| 328 | .doc_comments = null, | |
| 317 | 329 | // initialized when we get the eof token |
| 318 | 330 | .eof_token = undefined, |
| 319 | 331 | } |
| ... | ... | @@ -339,31 +351,38 @@ pub const Parser = struct { |
| 339 | 351 | |
| 340 | 352 | switch (state) { |
| 341 | 353 | State.TopLevel => { |
| 342 | const comments = try self.eatComments(arena); | |
| 354 | while (try self.eatLineComment(arena)) |line_comment| { | |
| 355 | try root_node.decls.append(&line_comment.base); | |
| 356 | } | |
| 357 | ||
| 358 | const comments = try self.eatDocComments(arena); | |
| 343 | 359 | const token = self.getNextToken(); |
| 344 | 360 | switch (token.id) { |
| 345 | 361 | Token.Id.Keyword_test => { |
| 346 | 362 | stack.append(State.TopLevel) catch unreachable; |
| 347 | 363 | |
| 348 | const block = try self.createNode(arena, ast.Node.Block, | |
| 349 | ast.Node.Block { | |
| 350 | .base = undefined, | |
| 351 | .label = null, | |
| 352 | .lbrace = undefined, | |
| 353 | .statements = ArrayList(&ast.Node).init(arena), | |
| 354 | .rbrace = undefined, | |
| 355 | } | |
| 356 | ); | |
| 357 | const test_node = try self.createAttachNode(arena, &root_node.decls, ast.Node.TestDecl, | |
| 358 | ast.Node.TestDecl { | |
| 359 | .base = undefined, | |
| 360 | .comments = comments, | |
| 361 | .test_token = token, | |
| 362 | .name = undefined, | |
| 363 | .body_node = &block.base, | |
| 364 | } | |
| 365 | ); | |
| 366 | stack.append(State { .Block = block }) catch unreachable; | |
| 364 | const block = try arena.construct(ast.Node.Block { | |
| 365 | .base = ast.Node { | |
| 366 | .id = ast.Node.Id.Block, | |
| 367 | .same_line_comment = null, | |
| 368 | }, | |
| 369 | .label = null, | |
| 370 | .lbrace = undefined, | |
| 371 | .statements = ArrayList(&ast.Node).init(arena), | |
| 372 | .rbrace = undefined, | |
| 373 | }); | |
| 374 | const test_node = try arena.construct(ast.Node.TestDecl { | |
| 375 | .base = ast.Node { | |
| 376 | .id = ast.Node.Id.TestDecl, | |
| 377 | .same_line_comment = null, | |
| 378 | }, | |
| 379 | .doc_comments = comments, | |
| 380 | .test_token = token, | |
| 381 | .name = undefined, | |
| 382 | .body_node = &block.base, | |
| 383 | }); | |
| 384 | try root_node.decls.append(&test_node.base); | |
| 385 | try stack.append(State { .Block = block }); | |
| 367 | 386 | try stack.append(State { |
| 368 | 387 | .ExpectTokenSave = ExpectTokenSave { |
| 369 | 388 | .id = Token.Id.LBrace, |
| ... | ... | @@ -375,7 +394,11 @@ pub const Parser = struct { |
| 375 | 394 | }, |
| 376 | 395 | Token.Id.Eof => { |
| 377 | 396 | root_node.eof_token = token; |
| 378 | return Tree {.root_node = root_node, .arena_allocator = arena_allocator}; | |
| 397 | root_node.doc_comments = comments; | |
| 398 | return Tree { | |
| 399 | .root_node = root_node, | |
| 400 | .arena_allocator = arena_allocator, | |
| 401 | }; | |
| 379 | 402 | }, |
| 380 | 403 | Token.Id.Keyword_pub => { |
| 381 | 404 | stack.append(State.TopLevel) catch unreachable; |
| ... | ... | @@ -385,6 +408,7 @@ pub const Parser = struct { |
| 385 | 408 | .visib_token = token, |
| 386 | 409 | .extern_export_inline_token = null, |
| 387 | 410 | .lib_name = null, |
| 411 | .comments = comments, | |
| 388 | 412 | } |
| 389 | 413 | }); |
| 390 | 414 | continue; |
| ... | ... | @@ -404,6 +428,7 @@ pub const Parser = struct { |
| 404 | 428 | .base = undefined, |
| 405 | 429 | .comptime_token = token, |
| 406 | 430 | .expr = &block.base, |
| 431 | .doc_comments = comments, | |
| 407 | 432 | } |
| 408 | 433 | ); |
| 409 | 434 | stack.append(State.TopLevel) catch unreachable; |
| ... | ... | @@ -425,6 +450,7 @@ pub const Parser = struct { |
| 425 | 450 | .visib_token = null, |
| 426 | 451 | .extern_export_inline_token = null, |
| 427 | 452 | .lib_name = null, |
| 453 | .comments = comments, | |
| 428 | 454 | } |
| 429 | 455 | }); |
| 430 | 456 | continue; |
| ... | ... | @@ -441,6 +467,7 @@ pub const Parser = struct { |
| 441 | 467 | .visib_token = ctx.visib_token, |
| 442 | 468 | .extern_export_inline_token = token, |
| 443 | 469 | .lib_name = null, |
| 470 | .comments = ctx.comments, | |
| 444 | 471 | }, |
| 445 | 472 | }) catch unreachable; |
| 446 | 473 | continue; |
| ... | ... | @@ -452,6 +479,7 @@ pub const Parser = struct { |
| 452 | 479 | .visib_token = ctx.visib_token, |
| 453 | 480 | .extern_export_inline_token = token, |
| 454 | 481 | .lib_name = null, |
| 482 | .comments = ctx.comments, | |
| 455 | 483 | }, |
| 456 | 484 | }) catch unreachable; |
| 457 | 485 | continue; |
| ... | ... | @@ -478,12 +506,12 @@ pub const Parser = struct { |
| 478 | 506 | .visib_token = ctx.visib_token, |
| 479 | 507 | .extern_export_inline_token = ctx.extern_export_inline_token, |
| 480 | 508 | .lib_name = lib_name, |
| 509 | .comments = ctx.comments, | |
| 481 | 510 | }, |
| 482 | 511 | }) catch unreachable; |
| 483 | 512 | continue; |
| 484 | 513 | }, |
| 485 | 514 | State.TopLevelDecl => |ctx| { |
| 486 | const comments = try self.eatComments(arena); | |
| 487 | 515 | const token = self.getNextToken(); |
| 488 | 516 | switch (token.id) { |
| 489 | 517 | Token.Id.Keyword_use => { |
| ... | ... | @@ -497,6 +525,7 @@ pub const Parser = struct { |
| 497 | 525 | .visib_token = ctx.visib_token, |
| 498 | 526 | .expr = undefined, |
| 499 | 527 | .semicolon_token = undefined, |
| 528 | .doc_comments = ctx.comments, | |
| 500 | 529 | } |
| 501 | 530 | ); |
| 502 | 531 | stack.append(State { |
| ... | ... | @@ -515,9 +544,9 @@ pub const Parser = struct { |
| 515 | 544 | } |
| 516 | 545 | } |
| 517 | 546 | |
| 518 | stack.append(State { | |
| 547 | try stack.append(State { | |
| 519 | 548 | .VarDecl = VarDeclCtx { |
| 520 | .comments = comments, | |
| 549 | .comments = ctx.comments, | |
| 521 | 550 | .visib_token = ctx.visib_token, |
| 522 | 551 | .lib_name = ctx.lib_name, |
| 523 | 552 | .comptime_token = null, |
| ... | ... | @@ -525,29 +554,31 @@ pub const Parser = struct { |
| 525 | 554 | .mut_token = token, |
| 526 | 555 | .list = ctx.decls |
| 527 | 556 | } |
| 528 | }) catch unreachable; | |
| 557 | }); | |
| 529 | 558 | continue; |
| 530 | 559 | }, |
| 531 | 560 | Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc, |
| 532 | 561 | Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => { |
| 533 | const fn_proto = try self.createAttachNode(arena, ctx.decls, ast.Node.FnProto, | |
| 534 | ast.Node.FnProto { | |
| 535 | .base = undefined, | |
| 536 | .comments = comments, | |
| 537 | .visib_token = ctx.visib_token, | |
| 538 | .name_token = null, | |
| 539 | .fn_token = undefined, | |
| 540 | .params = ArrayList(&ast.Node).init(arena), | |
| 541 | .return_type = undefined, | |
| 542 | .var_args_token = null, | |
| 543 | .extern_export_inline_token = ctx.extern_export_inline_token, | |
| 544 | .cc_token = null, | |
| 545 | .async_attr = null, | |
| 546 | .body_node = null, | |
| 547 | .lib_name = ctx.lib_name, | |
| 548 | .align_expr = null, | |
| 549 | } | |
| 550 | ); | |
| 562 | const fn_proto = try arena.construct(ast.Node.FnProto { | |
| 563 | .base = ast.Node { | |
| 564 | .id = ast.Node.Id.FnProto, | |
| 565 | .same_line_comment = null, | |
| 566 | }, | |
| 567 | .doc_comments = ctx.comments, | |
| 568 | .visib_token = ctx.visib_token, | |
| 569 | .name_token = null, | |
| 570 | .fn_token = undefined, | |
| 571 | .params = ArrayList(&ast.Node).init(arena), | |
| 572 | .return_type = undefined, | |
| 573 | .var_args_token = null, | |
| 574 | .extern_export_inline_token = ctx.extern_export_inline_token, | |
| 575 | .cc_token = null, | |
| 576 | .async_attr = null, | |
| 577 | .body_node = null, | |
| 578 | .lib_name = ctx.lib_name, | |
| 579 | .align_expr = null, | |
| 580 | }); | |
| 581 | try ctx.decls.append(&fn_proto.base); | |
| 551 | 582 | stack.append(State { .FnDef = fn_proto }) catch unreachable; |
| 552 | 583 | try stack.append(State { .FnProto = fn_proto }); |
| 553 | 584 | |
| ... | ... | @@ -597,14 +628,18 @@ pub const Parser = struct { |
| 597 | 628 | State.TopLevelExternOrField => |ctx| { |
| 598 | 629 | if (self.eatToken(Token.Id.Identifier)) |identifier| { |
| 599 | 630 | std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct); |
| 600 | const node = try self.createAttachNode(arena, &ctx.container_decl.fields_and_decls, ast.Node.StructField, | |
| 601 | ast.Node.StructField { | |
| 602 | .base = undefined, | |
| 603 | .visib_token = ctx.visib_token, | |
| 604 | .name_token = identifier, | |
| 605 | .type_expr = undefined, | |
| 606 | } | |
| 607 | ); | |
| 631 | const node = try arena.construct(ast.Node.StructField { | |
| 632 | .base = ast.Node { | |
| 633 | .id = ast.Node.Id.StructField, | |
| 634 | .same_line_comment = null, | |
| 635 | }, | |
| 636 | .doc_comments = ctx.comments, | |
| 637 | .visib_token = ctx.visib_token, | |
| 638 | .name_token = identifier, | |
| 639 | .type_expr = undefined, | |
| 640 | }); | |
| 641 | const node_ptr = try ctx.container_decl.fields_and_decls.addOne(); | |
| 642 | *node_ptr = &node.base; | |
| 608 | 643 | |
| 609 | 644 | stack.append(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable; |
| 610 | 645 | try stack.append(State { .Expression = OptionalCtx { .Required = &node.type_expr } }); |
| ... | ... | @@ -619,11 +654,21 @@ pub const Parser = struct { |
| 619 | 654 | .visib_token = ctx.visib_token, |
| 620 | 655 | .extern_export_inline_token = null, |
| 621 | 656 | .lib_name = null, |
| 657 | .comments = ctx.comments, | |
| 622 | 658 | } |
| 623 | 659 | }); |
| 624 | 660 | continue; |
| 625 | 661 | }, |
| 626 | 662 | |
| 663 | State.FieldInitValue => |ctx| { | |
| 664 | const eq_tok = self.getNextToken(); | |
| 665 | if (eq_tok.id != Token.Id.Equal) { | |
| 666 | self.putBackToken(eq_tok); | |
| 667 | continue; | |
| 668 | } | |
| 669 | stack.append(State { .Expression = ctx }) catch unreachable; | |
| 670 | continue; | |
| 671 | }, | |
| 627 | 672 | |
| 628 | 673 | State.ContainerKind => |ctx| { |
| 629 | 674 | const token = self.getNextToken(); |
| ... | ... | @@ -670,7 +715,16 @@ pub const Parser = struct { |
| 670 | 715 | const init_arg_token = self.getNextToken(); |
| 671 | 716 | switch (init_arg_token.id) { |
| 672 | 717 | Token.Id.Keyword_enum => { |
| 673 | container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg.Enum; | |
| 718 | container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg {.Enum = null}; | |
| 719 | const lparen_tok = self.getNextToken(); | |
| 720 | if (lparen_tok.id == Token.Id.LParen) { | |
| 721 | try stack.append(State { .ExpectToken = Token.Id.RParen } ); | |
| 722 | try stack.append(State { .Expression = OptionalCtx { | |
| 723 | .RequiredNull = &container_decl.init_arg_expr.Enum, | |
| 724 | } }); | |
| 725 | } else { | |
| 726 | self.putBackToken(lparen_tok); | |
| 727 | } | |
| 674 | 728 | }, |
| 675 | 729 | else => { |
| 676 | 730 | self.putBackToken(init_arg_token); |
| ... | ... | @@ -680,22 +734,32 @@ pub const Parser = struct { |
| 680 | 734 | } |
| 681 | 735 | continue; |
| 682 | 736 | }, |
| 737 | ||
| 683 | 738 | State.ContainerDecl => |container_decl| { |
| 739 | while (try self.eatLineComment(arena)) |line_comment| { | |
| 740 | try container_decl.fields_and_decls.append(&line_comment.base); | |
| 741 | } | |
| 742 | ||
| 743 | const comments = try self.eatDocComments(arena); | |
| 684 | 744 | const token = self.getNextToken(); |
| 685 | 745 | switch (token.id) { |
| 686 | 746 | Token.Id.Identifier => { |
| 687 | 747 | switch (container_decl.kind) { |
| 688 | 748 | ast.Node.ContainerDecl.Kind.Struct => { |
| 689 | const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.StructField, | |
| 690 | ast.Node.StructField { | |
| 691 | .base = undefined, | |
| 692 | .visib_token = null, | |
| 693 | .name_token = token, | |
| 694 | .type_expr = undefined, | |
| 695 | } | |
| 696 | ); | |
| 749 | const node = try arena.construct(ast.Node.StructField { | |
| 750 | .base = ast.Node { | |
| 751 | .id = ast.Node.Id.StructField, | |
| 752 | .same_line_comment = null, | |
| 753 | }, | |
| 754 | .doc_comments = comments, | |
| 755 | .visib_token = null, | |
| 756 | .name_token = token, | |
| 757 | .type_expr = undefined, | |
| 758 | }); | |
| 759 | const node_ptr = try container_decl.fields_and_decls.addOne(); | |
| 760 | *node_ptr = &node.base; | |
| 697 | 761 | |
| 698 | stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; | |
| 762 | try stack.append(State { .FieldListCommaOrEnd = container_decl }); | |
| 699 | 763 | try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } }); |
| 700 | 764 | try stack.append(State { .ExpectToken = Token.Id.Colon }); |
| 701 | 765 | continue; |
| ... | ... | @@ -706,10 +770,13 @@ pub const Parser = struct { |
| 706 | 770 | .base = undefined, |
| 707 | 771 | .name_token = token, |
| 708 | 772 | .type_expr = null, |
| 773 | .value_expr = null, | |
| 774 | .doc_comments = comments, | |
| 709 | 775 | } |
| 710 | 776 | ); |
| 711 | 777 | |
| 712 | 778 | stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable; |
| 779 | try stack.append(State { .FieldInitValue = OptionalCtx { .RequiredNull = &node.value_expr } }); | |
| 713 | 780 | try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } }); |
| 714 | 781 | try stack.append(State { .IfToken = Token.Id.Colon }); |
| 715 | 782 | continue; |
| ... | ... | @@ -720,6 +787,7 @@ pub const Parser = struct { |
| 720 | 787 | .base = undefined, |
| 721 | 788 | .name_token = token, |
| 722 | 789 | .value = null, |
| 790 | .doc_comments = comments, | |
| 723 | 791 | } |
| 724 | 792 | ); |
| 725 | 793 | |
| ... | ... | @@ -737,6 +805,7 @@ pub const Parser = struct { |
| 737 | 805 | .TopLevelExternOrField = TopLevelExternOrFieldCtx { |
| 738 | 806 | .visib_token = token, |
| 739 | 807 | .container_decl = container_decl, |
| 808 | .comments = comments, | |
| 740 | 809 | } |
| 741 | 810 | }); |
| 742 | 811 | continue; |
| ... | ... | @@ -749,6 +818,7 @@ pub const Parser = struct { |
| 749 | 818 | .visib_token = token, |
| 750 | 819 | .extern_export_inline_token = null, |
| 751 | 820 | .lib_name = null, |
| 821 | .comments = comments, | |
| 752 | 822 | } |
| 753 | 823 | }); |
| 754 | 824 | continue; |
| ... | ... | @@ -763,11 +833,15 @@ pub const Parser = struct { |
| 763 | 833 | .visib_token = token, |
| 764 | 834 | .extern_export_inline_token = null, |
| 765 | 835 | .lib_name = null, |
| 836 | .comments = comments, | |
| 766 | 837 | } |
| 767 | 838 | }); |
| 768 | 839 | continue; |
| 769 | 840 | }, |
| 770 | 841 | Token.Id.RBrace => { |
| 842 | if (comments != null) { | |
| 843 | return self.parseError(token, "doc comments must be attached to a node"); | |
| 844 | } | |
| 771 | 845 | container_decl.rbrace_token = token; |
| 772 | 846 | continue; |
| 773 | 847 | }, |
| ... | ... | @@ -780,6 +854,7 @@ pub const Parser = struct { |
| 780 | 854 | .visib_token = null, |
| 781 | 855 | .extern_export_inline_token = null, |
| 782 | 856 | .lib_name = null, |
| 857 | .comments = comments, | |
| 783 | 858 | } |
| 784 | 859 | }); |
| 785 | 860 | continue; |
| ... | ... | @@ -789,26 +864,29 @@ pub const Parser = struct { |
| 789 | 864 | |
| 790 | 865 | |
| 791 | 866 | State.VarDecl => |ctx| { |
| 792 | const var_decl = try self.createAttachNode(arena, ctx.list, ast.Node.VarDecl, | |
| 793 | ast.Node.VarDecl { | |
| 794 | .base = undefined, | |
| 795 | .comments = ctx.comments, | |
| 796 | .visib_token = ctx.visib_token, | |
| 797 | .mut_token = ctx.mut_token, | |
| 798 | .comptime_token = ctx.comptime_token, | |
| 799 | .extern_export_token = ctx.extern_export_token, | |
| 800 | .type_node = null, | |
| 801 | .align_node = null, | |
| 802 | .init_node = null, | |
| 803 | .lib_name = ctx.lib_name, | |
| 804 | // initialized later | |
| 805 | .name_token = undefined, | |
| 806 | .eq_token = undefined, | |
| 807 | .semicolon_token = undefined, | |
| 808 | } | |
| 809 | ); | |
| 867 | const var_decl = try arena.construct(ast.Node.VarDecl { | |
| 868 | .base = ast.Node { | |
| 869 | .id = ast.Node.Id.VarDecl, | |
| 870 | .same_line_comment = null, | |
| 871 | }, | |
| 872 | .doc_comments = ctx.comments, | |
| 873 | .visib_token = ctx.visib_token, | |
| 874 | .mut_token = ctx.mut_token, | |
| 875 | .comptime_token = ctx.comptime_token, | |
| 876 | .extern_export_token = ctx.extern_export_token, | |
| 877 | .type_node = null, | |
| 878 | .align_node = null, | |
| 879 | .init_node = null, | |
| 880 | .lib_name = ctx.lib_name, | |
| 881 | // initialized later | |
| 882 | .name_token = undefined, | |
| 883 | .eq_token = undefined, | |
| 884 | .semicolon_token = undefined, | |
| 885 | }); | |
| 886 | try ctx.list.append(&var_decl.base); | |
| 810 | 887 | |
| 811 | stack.append(State { .VarDeclAlign = var_decl }) catch unreachable; | |
| 888 | try stack.append(State { .LookForSameLineCommentDirect = &var_decl.base }); | |
| 889 | try stack.append(State { .VarDeclAlign = var_decl }); | |
| 812 | 890 | try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} }); |
| 813 | 891 | try stack.append(State { .IfToken = Token.Id.Colon }); |
| 814 | 892 | try stack.append(State { |
| ... | ... | @@ -820,7 +898,7 @@ pub const Parser = struct { |
| 820 | 898 | continue; |
| 821 | 899 | }, |
| 822 | 900 | State.VarDeclAlign => |var_decl| { |
| 823 | stack.append(State { .VarDeclEq = var_decl }) catch unreachable; | |
| 901 | try stack.append(State { .VarDeclEq = var_decl }); | |
| 824 | 902 | |
| 825 | 903 | const next_token = self.getNextToken(); |
| 826 | 904 | if (next_token.id == Token.Id.Keyword_align) { |
| ... | ... | @@ -1048,6 +1126,22 @@ pub const Parser = struct { |
| 1048 | 1126 | }) catch unreachable; |
| 1049 | 1127 | continue; |
| 1050 | 1128 | }, |
| 1129 | Token.Id.Keyword_suspend => { | |
| 1130 | const node = try arena.construct(ast.Node.Suspend { | |
| 1131 | .base = ast.Node { | |
| 1132 | .id = ast.Node.Id.Suspend, | |
| 1133 | .same_line_comment = null, | |
| 1134 | }, | |
| 1135 | .label = ctx.label, | |
| 1136 | .suspend_token = token, | |
| 1137 | .payload = null, | |
| 1138 | .body = null, | |
| 1139 | }); | |
| 1140 | ctx.opt_ctx.store(&node.base); | |
| 1141 | stack.append(State { .SuspendBody = node }) catch unreachable; | |
| 1142 | try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } }); | |
| 1143 | continue; | |
| 1144 | }, | |
| 1051 | 1145 | Token.Id.Keyword_inline => { |
| 1052 | 1146 | stack.append(State { |
| 1053 | 1147 | .Inline = InlineCtx { |
| ... | ... | @@ -1185,13 +1279,20 @@ pub const Parser = struct { |
| 1185 | 1279 | else => { |
| 1186 | 1280 | self.putBackToken(token); |
| 1187 | 1281 | stack.append(State { .Block = block }) catch unreachable; |
| 1282 | ||
| 1283 | var any_comments = false; | |
| 1284 | while (try self.eatLineComment(arena)) |line_comment| { | |
| 1285 | try block.statements.append(&line_comment.base); | |
| 1286 | any_comments = true; | |
| 1287 | } | |
| 1288 | if (any_comments) continue; | |
| 1289 | ||
| 1188 | 1290 | try stack.append(State { .Statement = block }); |
| 1189 | 1291 | continue; |
| 1190 | 1292 | }, |
| 1191 | 1293 | } |
| 1192 | 1294 | }, |
| 1193 | 1295 | State.Statement => |block| { |
| 1194 | const comments = try self.eatComments(arena); | |
| 1195 | 1296 | const token = self.getNextToken(); |
| 1196 | 1297 | switch (token.id) { |
| 1197 | 1298 | Token.Id.Keyword_comptime => { |
| ... | ... | @@ -1206,7 +1307,7 @@ pub const Parser = struct { |
| 1206 | 1307 | Token.Id.Keyword_var, Token.Id.Keyword_const => { |
| 1207 | 1308 | stack.append(State { |
| 1208 | 1309 | .VarDecl = VarDeclCtx { |
| 1209 | .comments = comments, | |
| 1310 | .comments = null, | |
| 1210 | 1311 | .visib_token = null, |
| 1211 | 1312 | .comptime_token = null, |
| 1212 | 1313 | .extern_export_token = null, |
| ... | ... | @@ -1218,19 +1319,23 @@ pub const Parser = struct { |
| 1218 | 1319 | continue; |
| 1219 | 1320 | }, |
| 1220 | 1321 | Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => { |
| 1221 | const node = try self.createAttachNode(arena, &block.statements, ast.Node.Defer, | |
| 1222 | ast.Node.Defer { | |
| 1223 | .base = undefined, | |
| 1224 | .defer_token = token, | |
| 1225 | .kind = switch (token.id) { | |
| 1226 | Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional, | |
| 1227 | Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error, | |
| 1228 | else => unreachable, | |
| 1229 | }, | |
| 1230 | .expr = undefined, | |
| 1231 | } | |
| 1232 | ); | |
| 1233 | stack.append(State { .Semicolon = &&node.base }) catch unreachable; | |
| 1322 | const node = try arena.construct(ast.Node.Defer { | |
| 1323 | .base = ast.Node { | |
| 1324 | .id = ast.Node.Id.Defer, | |
| 1325 | .same_line_comment = null, | |
| 1326 | }, | |
| 1327 | .defer_token = token, | |
| 1328 | .kind = switch (token.id) { | |
| 1329 | Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional, | |
| 1330 | Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error, | |
| 1331 | else => unreachable, | |
| 1332 | }, | |
| 1333 | .expr = undefined, | |
| 1334 | }); | |
| 1335 | const node_ptr = try block.statements.addOne(); | |
| 1336 | *node_ptr = &node.base; | |
| 1337 | ||
| 1338 | stack.append(State { .Semicolon = node_ptr }) catch unreachable; | |
| 1234 | 1339 | try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } }); |
| 1235 | 1340 | continue; |
| 1236 | 1341 | }, |
| ... | ... | @@ -1249,21 +1354,21 @@ pub const Parser = struct { |
| 1249 | 1354 | }, |
| 1250 | 1355 | else => { |
| 1251 | 1356 | self.putBackToken(token); |
| 1252 | const statememt = try block.statements.addOne(); | |
| 1253 | stack.append(State { .Semicolon = statememt }) catch unreachable; | |
| 1254 | try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statememt } }); | |
| 1357 | const statement = try block.statements.addOne(); | |
| 1358 | stack.append(State { .LookForSameLineComment = statement }) catch unreachable; | |
| 1359 | try stack.append(State { .Semicolon = statement }); | |
| 1360 | try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } }); | |
| 1255 | 1361 | continue; |
| 1256 | 1362 | } |
| 1257 | 1363 | } |
| 1258 | 1364 | }, |
| 1259 | 1365 | State.ComptimeStatement => |ctx| { |
| 1260 | const comments = try self.eatComments(arena); | |
| 1261 | 1366 | const token = self.getNextToken(); |
| 1262 | 1367 | switch (token.id) { |
| 1263 | 1368 | Token.Id.Keyword_var, Token.Id.Keyword_const => { |
| 1264 | 1369 | stack.append(State { |
| 1265 | 1370 | .VarDecl = VarDeclCtx { |
| 1266 | .comments = comments, | |
| 1371 | .comments = null, | |
| 1267 | 1372 | .visib_token = null, |
| 1268 | 1373 | .comptime_token = ctx.comptime_token, |
| 1269 | 1374 | .extern_export_token = null, |
| ... | ... | @@ -1293,6 +1398,16 @@ pub const Parser = struct { |
| 1293 | 1398 | continue; |
| 1294 | 1399 | }, |
| 1295 | 1400 | |
| 1401 | State.LookForSameLineComment => |node_ptr| { | |
| 1402 | try self.lookForSameLineComment(arena, *node_ptr); | |
| 1403 | continue; | |
| 1404 | }, | |
| 1405 | ||
| 1406 | State.LookForSameLineCommentDirect => |node| { | |
| 1407 | try self.lookForSameLineComment(arena, node); | |
| 1408 | continue; | |
| 1409 | }, | |
| 1410 | ||
| 1296 | 1411 | |
| 1297 | 1412 | State.AsmOutputItems => |items| { |
| 1298 | 1413 | const lbracket = self.getNextToken(); |
| ... | ... | @@ -1395,20 +1510,25 @@ pub const Parser = struct { |
| 1395 | 1510 | } |
| 1396 | 1511 | }, |
| 1397 | 1512 | State.FieldInitListItemOrEnd => |list_state| { |
| 1513 | while (try self.eatLineComment(arena)) |line_comment| { | |
| 1514 | try list_state.list.append(&line_comment.base); | |
| 1515 | } | |
| 1516 | ||
| 1398 | 1517 | if (self.eatToken(Token.Id.RBrace)) |rbrace| { |
| 1399 | 1518 | *list_state.ptr = rbrace; |
| 1400 | 1519 | continue; |
| 1401 | 1520 | } |
| 1402 | 1521 | |
| 1403 | const node = try self.createNode(arena, ast.Node.FieldInitializer, | |
| 1404 | ast.Node.FieldInitializer { | |
| 1405 | .base = undefined, | |
| 1406 | .period_token = undefined, | |
| 1407 | .name_token = undefined, | |
| 1408 | .expr = undefined, | |
| 1409 | } | |
| 1410 | ); | |
| 1411 | try list_state.list.append(node); | |
| 1522 | const node = try arena.construct(ast.Node.FieldInitializer { | |
| 1523 | .base = ast.Node { | |
| 1524 | .id = ast.Node.Id.FieldInitializer, | |
| 1525 | .same_line_comment = null, | |
| 1526 | }, | |
| 1527 | .period_token = undefined, | |
| 1528 | .name_token = undefined, | |
| 1529 | .expr = undefined, | |
| 1530 | }); | |
| 1531 | try list_state.list.append(&node.base); | |
| 1412 | 1532 | |
| 1413 | 1533 | stack.append(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable; |
| 1414 | 1534 | try stack.append(State { .Expression = OptionalCtx{ .Required = &node.expr } }); |
| ... | ... | @@ -1440,60 +1560,78 @@ pub const Parser = struct { |
| 1440 | 1560 | if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| { |
| 1441 | 1561 | container_decl.rbrace_token = end; |
| 1442 | 1562 | continue; |
| 1443 | } else { | |
| 1444 | stack.append(State { .ContainerDecl = container_decl }) catch unreachable; | |
| 1445 | continue; | |
| 1446 | 1563 | } |
| 1564 | ||
| 1565 | try self.lookForSameLineComment(arena, container_decl.fields_and_decls.toSlice()[container_decl.fields_and_decls.len - 1]); | |
| 1566 | try stack.append(State { .ContainerDecl = container_decl }); | |
| 1567 | continue; | |
| 1447 | 1568 | }, |
| 1448 | State.IdentifierListItemOrEnd => |list_state| { | |
| 1569 | State.ErrorTagListItemOrEnd => |list_state| { | |
| 1570 | while (try self.eatLineComment(arena)) |line_comment| { | |
| 1571 | try list_state.list.append(&line_comment.base); | |
| 1572 | } | |
| 1573 | ||
| 1449 | 1574 | if (self.eatToken(Token.Id.RBrace)) |rbrace| { |
| 1450 | 1575 | *list_state.ptr = rbrace; |
| 1451 | 1576 | continue; |
| 1452 | 1577 | } |
| 1453 | 1578 | |
| 1454 | stack.append(State { .IdentifierListCommaOrEnd = list_state }) catch unreachable; | |
| 1455 | try stack.append(State { .Identifier = OptionalCtx { .Required = try list_state.list.addOne() } }); | |
| 1579 | const node_ptr = try list_state.list.addOne(); | |
| 1580 | ||
| 1581 | try stack.append(State { .ErrorTagListCommaOrEnd = list_state }); | |
| 1582 | try stack.append(State { .ErrorTag = node_ptr }); | |
| 1456 | 1583 | continue; |
| 1457 | 1584 | }, |
| 1458 | State.IdentifierListCommaOrEnd => |list_state| { | |
| 1585 | State.ErrorTagListCommaOrEnd => |list_state| { | |
| 1459 | 1586 | if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| { |
| 1460 | 1587 | *list_state.ptr = end; |
| 1461 | 1588 | continue; |
| 1462 | 1589 | } else { |
| 1463 | stack.append(State { .IdentifierListItemOrEnd = list_state }) catch unreachable; | |
| 1590 | stack.append(State { .ErrorTagListItemOrEnd = list_state }) catch unreachable; | |
| 1464 | 1591 | continue; |
| 1465 | 1592 | } |
| 1466 | 1593 | }, |
| 1467 | 1594 | State.SwitchCaseOrEnd => |list_state| { |
| 1595 | while (try self.eatLineComment(arena)) |line_comment| { | |
| 1596 | try list_state.list.append(&line_comment.base); | |
| 1597 | } | |
| 1598 | ||
| 1468 | 1599 | if (self.eatToken(Token.Id.RBrace)) |rbrace| { |
| 1469 | 1600 | *list_state.ptr = rbrace; |
| 1470 | 1601 | continue; |
| 1471 | 1602 | } |
| 1472 | 1603 | |
| 1473 | const node = try self.createNode(arena, ast.Node.SwitchCase, | |
| 1474 | ast.Node.SwitchCase { | |
| 1475 | .base = undefined, | |
| 1476 | .items = ArrayList(&ast.Node).init(arena), | |
| 1477 | .payload = null, | |
| 1478 | .expr = undefined, | |
| 1479 | } | |
| 1480 | ); | |
| 1481 | try list_state.list.append(node); | |
| 1482 | stack.append(State { .SwitchCaseCommaOrEnd = list_state }) catch unreachable; | |
| 1604 | const comments = try self.eatDocComments(arena); | |
| 1605 | const node = try arena.construct(ast.Node.SwitchCase { | |
| 1606 | .base = ast.Node { | |
| 1607 | .id = ast.Node.Id.SwitchCase, | |
| 1608 | .same_line_comment = null, | |
| 1609 | }, | |
| 1610 | .items = ArrayList(&ast.Node).init(arena), | |
| 1611 | .payload = null, | |
| 1612 | .expr = undefined, | |
| 1613 | }); | |
| 1614 | try list_state.list.append(&node.base); | |
| 1615 | try stack.append(State { .SwitchCaseCommaOrEnd = list_state }); | |
| 1483 | 1616 | try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } }); |
| 1484 | 1617 | try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } }); |
| 1485 | 1618 | try stack.append(State { .SwitchCaseFirstItem = &node.items }); |
| 1619 | ||
| 1486 | 1620 | continue; |
| 1487 | 1621 | }, |
| 1622 | ||
| 1488 | 1623 | State.SwitchCaseCommaOrEnd => |list_state| { |
| 1489 | 1624 | if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| { |
| 1490 | 1625 | *list_state.ptr = end; |
| 1491 | 1626 | continue; |
| 1492 | } else { | |
| 1493 | stack.append(State { .SwitchCaseOrEnd = list_state }) catch unreachable; | |
| 1494 | continue; | |
| 1495 | 1627 | } |
| 1628 | ||
| 1629 | const node = list_state.list.toSlice()[list_state.list.len - 1]; | |
| 1630 | try self.lookForSameLineComment(arena, node); | |
| 1631 | try stack.append(State { .SwitchCaseOrEnd = list_state }); | |
| 1632 | continue; | |
| 1496 | 1633 | }, |
| 1634 | ||
| 1497 | 1635 | State.SwitchCaseFirstItem => |case_items| { |
| 1498 | 1636 | const token = self.getNextToken(); |
| 1499 | 1637 | if (token.id == Token.Id.Keyword_else) { |
| ... | ... | @@ -1576,24 +1714,26 @@ pub const Parser = struct { |
| 1576 | 1714 | |
| 1577 | 1715 | State.ExternType => |ctx| { |
| 1578 | 1716 | if (self.eatToken(Token.Id.Keyword_fn)) |fn_token| { |
| 1579 | const fn_proto = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.FnProto, | |
| 1580 | ast.Node.FnProto { | |
| 1581 | .base = undefined, | |
| 1582 | .comments = ctx.comments, | |
| 1583 | .visib_token = null, | |
| 1584 | .name_token = null, | |
| 1585 | .fn_token = fn_token, | |
| 1586 | .params = ArrayList(&ast.Node).init(arena), | |
| 1587 | .return_type = undefined, | |
| 1588 | .var_args_token = null, | |
| 1589 | .extern_export_inline_token = ctx.extern_token, | |
| 1590 | .cc_token = null, | |
| 1591 | .async_attr = null, | |
| 1592 | .body_node = null, | |
| 1593 | .lib_name = null, | |
| 1594 | .align_expr = null, | |
| 1595 | } | |
| 1596 | ); | |
| 1717 | const fn_proto = try arena.construct(ast.Node.FnProto { | |
| 1718 | .base = ast.Node { | |
| 1719 | .id = ast.Node.Id.FnProto, | |
| 1720 | .same_line_comment = null, | |
| 1721 | }, | |
| 1722 | .doc_comments = ctx.comments, | |
| 1723 | .visib_token = null, | |
| 1724 | .name_token = null, | |
| 1725 | .fn_token = fn_token, | |
| 1726 | .params = ArrayList(&ast.Node).init(arena), | |
| 1727 | .return_type = undefined, | |
| 1728 | .var_args_token = null, | |
| 1729 | .extern_export_inline_token = ctx.extern_token, | |
| 1730 | .cc_token = null, | |
| 1731 | .async_attr = null, | |
| 1732 | .body_node = null, | |
| 1733 | .lib_name = null, | |
| 1734 | .align_expr = null, | |
| 1735 | }); | |
| 1736 | ctx.opt_ctx.store(&fn_proto.base); | |
| 1597 | 1737 | stack.append(State { .FnProto = fn_proto }) catch unreachable; |
| 1598 | 1738 | continue; |
| 1599 | 1739 | } |
| ... | ... | @@ -2210,7 +2350,7 @@ pub const Parser = struct { |
| 2210 | 2350 | .base = undefined, |
| 2211 | 2351 | .lhs = lhs, |
| 2212 | 2352 | .op = ast.Node.SuffixOp.Op { |
| 2213 | .StructInitializer = ArrayList(&ast.Node.FieldInitializer).init(arena), | |
| 2353 | .StructInitializer = ArrayList(&ast.Node).init(arena), | |
| 2214 | 2354 | }, |
| 2215 | 2355 | .rtoken = undefined, |
| 2216 | 2356 | } |
| ... | ... | @@ -2218,7 +2358,7 @@ pub const Parser = struct { |
| 2218 | 2358 | stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable; |
| 2219 | 2359 | try stack.append(State { .IfToken = Token.Id.LBrace }); |
| 2220 | 2360 | try stack.append(State { |
| 2221 | .FieldInitListItemOrEnd = ListSave(&ast.Node.FieldInitializer) { | |
| 2361 | .FieldInitListItemOrEnd = ListSave(&ast.Node) { | |
| 2222 | 2362 | .list = &node.op.StructInitializer, |
| 2223 | 2363 | .ptr = &node.rtoken, |
| 2224 | 2364 | } |
| ... | ... | @@ -2443,6 +2583,29 @@ pub const Parser = struct { |
| 2443 | 2583 | _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token); |
| 2444 | 2584 | continue; |
| 2445 | 2585 | }, |
| 2586 | Token.Id.Keyword_promise => { | |
| 2587 | const node = try arena.construct(ast.Node.PromiseType { | |
| 2588 | .base = ast.Node { | |
| 2589 | .id = ast.Node.Id.PromiseType, | |
| 2590 | .same_line_comment = null, | |
| 2591 | }, | |
| 2592 | .promise_token = token, | |
| 2593 | .result = null, | |
| 2594 | }); | |
| 2595 | opt_ctx.store(&node.base); | |
| 2596 | const next_token = self.getNextToken(); | |
| 2597 | if (next_token.id != Token.Id.Arrow) { | |
| 2598 | self.putBackToken(next_token); | |
| 2599 | continue; | |
| 2600 | } | |
| 2601 | node.result = ast.Node.PromiseType.Result { | |
| 2602 | .arrow_token = next_token, | |
| 2603 | .return_type = undefined, | |
| 2604 | }; | |
| 2605 | const return_type_ptr = &((??node.result).return_type); | |
| 2606 | try stack.append(State { .Expression = OptionalCtx { .Required = return_type_ptr, } }); | |
| 2607 | continue; | |
| 2608 | }, | |
| 2446 | 2609 | Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => { |
| 2447 | 2610 | opt_ctx.store((try self.parseStringLiteral(arena, token)) ?? unreachable); |
| 2448 | 2611 | continue; |
| ... | ... | @@ -2546,46 +2709,50 @@ pub const Parser = struct { |
| 2546 | 2709 | continue; |
| 2547 | 2710 | }, |
| 2548 | 2711 | Token.Id.Keyword_fn => { |
| 2549 | const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.Node.FnProto, | |
| 2550 | ast.Node.FnProto { | |
| 2551 | .base = undefined, | |
| 2552 | .comments = null, | |
| 2553 | .visib_token = null, | |
| 2554 | .name_token = null, | |
| 2555 | .fn_token = token, | |
| 2556 | .params = ArrayList(&ast.Node).init(arena), | |
| 2557 | .return_type = undefined, | |
| 2558 | .var_args_token = null, | |
| 2559 | .extern_export_inline_token = null, | |
| 2560 | .cc_token = null, | |
| 2561 | .async_attr = null, | |
| 2562 | .body_node = null, | |
| 2563 | .lib_name = null, | |
| 2564 | .align_expr = null, | |
| 2565 | } | |
| 2566 | ); | |
| 2712 | const fn_proto = try arena.construct(ast.Node.FnProto { | |
| 2713 | .base = ast.Node { | |
| 2714 | .id = ast.Node.Id.FnProto, | |
| 2715 | .same_line_comment = null, | |
| 2716 | }, | |
| 2717 | .doc_comments = null, | |
| 2718 | .visib_token = null, | |
| 2719 | .name_token = null, | |
| 2720 | .fn_token = token, | |
| 2721 | .params = ArrayList(&ast.Node).init(arena), | |
| 2722 | .return_type = undefined, | |
| 2723 | .var_args_token = null, | |
| 2724 | .extern_export_inline_token = null, | |
| 2725 | .cc_token = null, | |
| 2726 | .async_attr = null, | |
| 2727 | .body_node = null, | |
| 2728 | .lib_name = null, | |
| 2729 | .align_expr = null, | |
| 2730 | }); | |
| 2731 | opt_ctx.store(&fn_proto.base); | |
| 2567 | 2732 | stack.append(State { .FnProto = fn_proto }) catch unreachable; |
| 2568 | 2733 | continue; |
| 2569 | 2734 | }, |
| 2570 | 2735 | Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => { |
| 2571 | const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.Node.FnProto, | |
| 2572 | ast.Node.FnProto { | |
| 2573 | .base = undefined, | |
| 2574 | .comments = null, | |
| 2575 | .visib_token = null, | |
| 2576 | .name_token = null, | |
| 2577 | .fn_token = undefined, | |
| 2578 | .params = ArrayList(&ast.Node).init(arena), | |
| 2579 | .return_type = undefined, | |
| 2580 | .var_args_token = null, | |
| 2581 | .extern_export_inline_token = null, | |
| 2582 | .cc_token = token, | |
| 2583 | .async_attr = null, | |
| 2584 | .body_node = null, | |
| 2585 | .lib_name = null, | |
| 2586 | .align_expr = null, | |
| 2587 | } | |
| 2588 | ); | |
| 2736 | const fn_proto = try arena.construct(ast.Node.FnProto { | |
| 2737 | .base = ast.Node { | |
| 2738 | .id = ast.Node.Id.FnProto, | |
| 2739 | .same_line_comment = null, | |
| 2740 | }, | |
| 2741 | .doc_comments = null, | |
| 2742 | .visib_token = null, | |
| 2743 | .name_token = null, | |
| 2744 | .fn_token = undefined, | |
| 2745 | .params = ArrayList(&ast.Node).init(arena), | |
| 2746 | .return_type = undefined, | |
| 2747 | .var_args_token = null, | |
| 2748 | .extern_export_inline_token = null, | |
| 2749 | .cc_token = token, | |
| 2750 | .async_attr = null, | |
| 2751 | .body_node = null, | |
| 2752 | .lib_name = null, | |
| 2753 | .align_expr = null, | |
| 2754 | }); | |
| 2755 | opt_ctx.store(&fn_proto.base); | |
| 2589 | 2756 | stack.append(State { .FnProto = fn_proto }) catch unreachable; |
| 2590 | 2757 | try stack.append(State { |
| 2591 | 2758 | .ExpectTokenSave = ExpectTokenSave { |
| ... | ... | @@ -2659,17 +2826,19 @@ pub const Parser = struct { |
| 2659 | 2826 | continue; |
| 2660 | 2827 | } |
| 2661 | 2828 | |
| 2662 | const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.ErrorSetDecl, | |
| 2663 | ast.Node.ErrorSetDecl { | |
| 2664 | .base = undefined, | |
| 2665 | .error_token = ctx.error_token, | |
| 2666 | .decls = ArrayList(&ast.Node).init(arena), | |
| 2667 | .rbrace_token = undefined, | |
| 2668 | } | |
| 2669 | ); | |
| 2829 | const node = try arena.construct(ast.Node.ErrorSetDecl { | |
| 2830 | .base = ast.Node { | |
| 2831 | .id = ast.Node.Id.ErrorSetDecl, | |
| 2832 | .same_line_comment = null, | |
| 2833 | }, | |
| 2834 | .error_token = ctx.error_token, | |
| 2835 | .decls = ArrayList(&ast.Node).init(arena), | |
| 2836 | .rbrace_token = undefined, | |
| 2837 | }); | |
| 2838 | ctx.opt_ctx.store(&node.base); | |
| 2670 | 2839 | |
| 2671 | 2840 | stack.append(State { |
| 2672 | .IdentifierListItemOrEnd = ListSave(&ast.Node) { | |
| 2841 | .ErrorTagListItemOrEnd = ListSave(&ast.Node) { | |
| 2673 | 2842 | .list = &node.decls, |
| 2674 | 2843 | .ptr = &node.rbrace_token, |
| 2675 | 2844 | } |
| ... | ... | @@ -2689,6 +2858,7 @@ pub const Parser = struct { |
| 2689 | 2858 | } |
| 2690 | 2859 | ); |
| 2691 | 2860 | }, |
| 2861 | ||
| 2692 | 2862 | State.Identifier => |opt_ctx| { |
| 2693 | 2863 | if (self.eatToken(Token.Id.Identifier)) |ident_token| { |
| 2694 | 2864 | _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token); |
| ... | ... | @@ -2701,6 +2871,25 @@ pub const Parser = struct { |
| 2701 | 2871 | } |
| 2702 | 2872 | }, |
| 2703 | 2873 | |
| 2874 | State.ErrorTag => |node_ptr| { | |
| 2875 | const comments = try self.eatDocComments(arena); | |
| 2876 | const ident_token = self.getNextToken(); | |
| 2877 | if (ident_token.id != Token.Id.Identifier) { | |
| 2878 | return self.parseError(ident_token, "expected {}, found {}", | |
| 2879 | @tagName(Token.Id.Identifier), @tagName(ident_token.id)); | |
| 2880 | } | |
| 2881 | ||
| 2882 | const node = try arena.construct(ast.Node.ErrorTag { | |
| 2883 | .base = ast.Node { | |
| 2884 | .id = ast.Node.Id.ErrorTag, | |
| 2885 | .same_line_comment = null, | |
| 2886 | }, | |
| 2887 | .doc_comments = comments, | |
| 2888 | .name_token = ident_token, | |
| 2889 | }); | |
| 2890 | *node_ptr = &node.base; | |
| 2891 | continue; | |
| 2892 | }, | |
| 2704 | 2893 | |
| 2705 | 2894 | State.ExpectToken => |token_id| { |
| 2706 | 2895 | _ = try self.expectToken(token_id); |
| ... | ... | @@ -2739,21 +2928,21 @@ pub const Parser = struct { |
| 2739 | 2928 | } |
| 2740 | 2929 | } |
| 2741 | 2930 | |
| 2742 | fn eatComments(self: &Parser, arena: &mem.Allocator) !?&ast.Node.LineComment { | |
| 2743 | var result: ?&ast.Node.LineComment = null; | |
| 2931 | fn eatDocComments(self: &Parser, arena: &mem.Allocator) !?&ast.Node.DocComment { | |
| 2932 | var result: ?&ast.Node.DocComment = null; | |
| 2744 | 2933 | while (true) { |
| 2745 | if (self.eatToken(Token.Id.LineComment)) |line_comment| { | |
| 2934 | if (self.eatToken(Token.Id.DocComment)) |line_comment| { | |
| 2746 | 2935 | const node = blk: { |
| 2747 | 2936 | if (result) |comment_node| { |
| 2748 | 2937 | break :blk comment_node; |
| 2749 | 2938 | } else { |
| 2750 | const comment_node = try arena.create(ast.Node.LineComment); | |
| 2751 | *comment_node = ast.Node.LineComment { | |
| 2939 | const comment_node = try arena.construct(ast.Node.DocComment { | |
| 2752 | 2940 | .base = ast.Node { |
| 2753 | .id = ast.Node.Id.LineComment, | |
| 2941 | .id = ast.Node.Id.DocComment, | |
| 2942 | .same_line_comment = null, | |
| 2754 | 2943 | }, |
| 2755 | 2944 | .lines = ArrayList(Token).init(arena), |
| 2756 | }; | |
| 2945 | }); | |
| 2757 | 2946 | result = comment_node; |
| 2758 | 2947 | break :blk comment_node; |
| 2759 | 2948 | } |
| ... | ... | @@ -2766,6 +2955,17 @@ pub const Parser = struct { |
| 2766 | 2955 | return result; |
| 2767 | 2956 | } |
| 2768 | 2957 | |
| 2958 | fn eatLineComment(self: &Parser, arena: &mem.Allocator) !?&ast.Node.LineComment { | |
| 2959 | const token = self.eatToken(Token.Id.LineComment) ?? return null; | |
| 2960 | return try arena.construct(ast.Node.LineComment { | |
| 2961 | .base = ast.Node { | |
| 2962 | .id = ast.Node.Id.LineComment, | |
| 2963 | .same_line_comment = null, | |
| 2964 | }, | |
| 2965 | .token = token, | |
| 2966 | }); | |
| 2967 | } | |
| 2968 | ||
| 2769 | 2969 | fn requireSemiColon(node: &const ast.Node) bool { |
| 2770 | 2970 | var n = node; |
| 2771 | 2971 | while (true) { |
| ... | ... | @@ -2783,6 +2983,7 @@ pub const Parser = struct { |
| 2783 | 2983 | ast.Node.Id.SwitchCase, |
| 2784 | 2984 | ast.Node.Id.SwitchElse, |
| 2785 | 2985 | ast.Node.Id.FieldInitializer, |
| 2986 | ast.Node.Id.DocComment, | |
| 2786 | 2987 | ast.Node.Id.LineComment, |
| 2787 | 2988 | ast.Node.Id.TestDecl => return false, |
| 2788 | 2989 | ast.Node.Id.While => { |
| ... | ... | @@ -2838,6 +3039,25 @@ pub const Parser = struct { |
| 2838 | 3039 | } |
| 2839 | 3040 | } |
| 2840 | 3041 | |
| 3042 | fn lookForSameLineComment(self: &Parser, arena: &mem.Allocator, node: &ast.Node) !void { | |
| 3043 | const node_last_token = node.lastToken(); | |
| 3044 | ||
| 3045 | const line_comment_token = self.getNextToken(); | |
| 3046 | if (line_comment_token.id != Token.Id.DocComment and line_comment_token.id != Token.Id.LineComment) { | |
| 3047 | self.putBackToken(line_comment_token); | |
| 3048 | return; | |
| 3049 | } | |
| 3050 | ||
| 3051 | const offset_loc = self.tokenizer.getTokenLocation(node_last_token.end, line_comment_token); | |
| 3052 | const different_line = offset_loc.line != 0; | |
| 3053 | if (different_line) { | |
| 3054 | self.putBackToken(line_comment_token); | |
| 3055 | return; | |
| 3056 | } | |
| 3057 | ||
| 3058 | node.same_line_comment = try arena.construct(line_comment_token); | |
| 3059 | } | |
| 3060 | ||
| 2841 | 3061 | fn parseStringLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !?&ast.Node { |
| 2842 | 3062 | switch (token.id) { |
| 2843 | 3063 | Token.Id.StringLiteral => { |
| ... | ... | @@ -2875,6 +3095,7 @@ pub const Parser = struct { |
| 2875 | 3095 | const node = try self.createToCtxNode(arena, ctx, ast.Node.Suspend, |
| 2876 | 3096 | ast.Node.Suspend { |
| 2877 | 3097 | .base = undefined, |
| 3098 | .label = null, | |
| 2878 | 3099 | .suspend_token = *token, |
| 2879 | 3100 | .payload = null, |
| 2880 | 3101 | .body = null, |
| ... | ... | @@ -2928,18 +3149,20 @@ pub const Parser = struct { |
| 2928 | 3149 | return true; |
| 2929 | 3150 | }, |
| 2930 | 3151 | Token.Id.Keyword_switch => { |
| 2931 | const node = try self.createToCtxNode(arena, ctx, ast.Node.Switch, | |
| 2932 | ast.Node.Switch { | |
| 2933 | .base = undefined, | |
| 2934 | .switch_token = *token, | |
| 2935 | .expr = undefined, | |
| 2936 | .cases = ArrayList(&ast.Node.SwitchCase).init(arena), | |
| 2937 | .rbrace = undefined, | |
| 2938 | } | |
| 2939 | ); | |
| 3152 | const node = try arena.construct(ast.Node.Switch { | |
| 3153 | .base = ast.Node { | |
| 3154 | .id = ast.Node.Id.Switch, | |
| 3155 | .same_line_comment = null, | |
| 3156 | }, | |
| 3157 | .switch_token = *token, | |
| 3158 | .expr = undefined, | |
| 3159 | .cases = ArrayList(&ast.Node).init(arena), | |
| 3160 | .rbrace = undefined, | |
| 3161 | }); | |
| 3162 | ctx.store(&node.base); | |
| 2940 | 3163 | |
| 2941 | 3164 | stack.append(State { |
| 2942 | .SwitchCaseOrEnd = ListSave(&ast.Node.SwitchCase) { | |
| 3165 | .SwitchCaseOrEnd = ListSave(&ast.Node) { | |
| 2943 | 3166 | .list = &node.cases, |
| 2944 | 3167 | .ptr = &node.rbrace, |
| 2945 | 3168 | }, |
| ... | ... | @@ -2956,6 +3179,7 @@ pub const Parser = struct { |
| 2956 | 3179 | .base = undefined, |
| 2957 | 3180 | .comptime_token = *token, |
| 2958 | 3181 | .expr = undefined, |
| 3182 | .doc_comments = null, | |
| 2959 | 3183 | } |
| 2960 | 3184 | ); |
| 2961 | 3185 | try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } }); |
| ... | ... | @@ -3096,7 +3320,10 @@ pub const Parser = struct { |
| 3096 | 3320 | *node = *init_to; |
| 3097 | 3321 | node.base = blk: { |
| 3098 | 3322 | const id = ast.Node.typeToId(T); |
| 3099 | break :blk ast.Node {.id = id}; | |
| 3323 | break :blk ast.Node { | |
| 3324 | .id = id, | |
| 3325 | .same_line_comment = null, | |
| 3326 | }; | |
| 3100 | 3327 | }; |
| 3101 | 3328 | |
| 3102 | 3329 | return node; |
| ... | ... | @@ -3229,9 +3456,9 @@ pub const Parser = struct { |
| 3229 | 3456 | Expression: &ast.Node, |
| 3230 | 3457 | VarDecl: &ast.Node.VarDecl, |
| 3231 | 3458 | Statement: &ast.Node, |
| 3232 | FieldInitializer: &ast.Node.FieldInitializer, | |
| 3233 | 3459 | PrintIndent, |
| 3234 | 3460 | Indent: usize, |
| 3461 | PrintSameLineComment: ?&Token, | |
| 3235 | 3462 | }; |
| 3236 | 3463 | |
| 3237 | 3464 | pub fn renderSource(self: &Parser, stream: var, root_node: &ast.Node.Root) !void { |
| ... | ... | @@ -3266,6 +3493,7 @@ pub const Parser = struct { |
| 3266 | 3493 | while (stack.popOrNull()) |state| { |
| 3267 | 3494 | switch (state) { |
| 3268 | 3495 | RenderState.TopLevelDecl => |decl| { |
| 3496 | try stack.append(RenderState { .PrintSameLineComment = decl.same_line_comment } ); | |
| 3269 | 3497 | switch (decl.id) { |
| 3270 | 3498 | ast.Node.Id.FnProto => { |
| 3271 | 3499 | const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl); |
| ... | ... | @@ -3291,6 +3519,7 @@ pub const Parser = struct { |
| 3291 | 3519 | }, |
| 3292 | 3520 | ast.Node.Id.VarDecl => { |
| 3293 | 3521 | const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl); |
| 3522 | try self.renderComments(stream, var_decl, indent); | |
| 3294 | 3523 | try stack.append(RenderState { .VarDecl = var_decl}); |
| 3295 | 3524 | }, |
| 3296 | 3525 | ast.Node.Id.TestDecl => { |
| ... | ... | @@ -3303,16 +3532,26 @@ pub const Parser = struct { |
| 3303 | 3532 | }, |
| 3304 | 3533 | ast.Node.Id.StructField => { |
| 3305 | 3534 | const field = @fieldParentPtr(ast.Node.StructField, "base", decl); |
| 3535 | try self.renderComments(stream, field, indent); | |
| 3306 | 3536 | if (field.visib_token) |visib_token| { |
| 3307 | 3537 | try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token)); |
| 3308 | 3538 | } |
| 3309 | 3539 | try stream.print("{}: ", self.tokenizer.getTokenSlice(field.name_token)); |
| 3540 | try stack.append(RenderState { .Text = "," }); | |
| 3310 | 3541 | try stack.append(RenderState { .Expression = field.type_expr}); |
| 3311 | 3542 | }, |
| 3312 | 3543 | ast.Node.Id.UnionTag => { |
| 3313 | 3544 | const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl); |
| 3545 | try self.renderComments(stream, tag, indent); | |
| 3314 | 3546 | try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token)); |
| 3315 | 3547 | |
| 3548 | try stack.append(RenderState { .Text = "," }); | |
| 3549 | ||
| 3550 | if (tag.value_expr) |value_expr| { | |
| 3551 | try stack.append(RenderState { .Expression = value_expr }); | |
| 3552 | try stack.append(RenderState { .Text = " = " }); | |
| 3553 | } | |
| 3554 | ||
| 3316 | 3555 | if (tag.type_expr) |type_expr| { |
| 3317 | 3556 | try stream.print(": "); |
| 3318 | 3557 | try stack.append(RenderState { .Expression = type_expr}); |
| ... | ... | @@ -3320,35 +3559,40 @@ pub const Parser = struct { |
| 3320 | 3559 | }, |
| 3321 | 3560 | ast.Node.Id.EnumTag => { |
| 3322 | 3561 | const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl); |
| 3562 | try self.renderComments(stream, tag, indent); | |
| 3323 | 3563 | try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token)); |
| 3324 | 3564 | |
| 3565 | try stack.append(RenderState { .Text = "," }); | |
| 3325 | 3566 | if (tag.value) |value| { |
| 3326 | 3567 | try stream.print(" = "); |
| 3327 | 3568 | try stack.append(RenderState { .Expression = value}); |
| 3328 | 3569 | } |
| 3329 | 3570 | }, |
| 3571 | ast.Node.Id.ErrorTag => { | |
| 3572 | const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", decl); | |
| 3573 | try self.renderComments(stream, tag, indent); | |
| 3574 | try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token)); | |
| 3575 | }, | |
| 3330 | 3576 | ast.Node.Id.Comptime => { |
| 3331 | 3577 | if (requireSemiColon(decl)) { |
| 3332 | 3578 | try stack.append(RenderState { .Text = ";" }); |
| 3333 | 3579 | } |
| 3334 | 3580 | try stack.append(RenderState { .Expression = decl }); |
| 3335 | 3581 | }, |
| 3582 | ast.Node.Id.LineComment => { | |
| 3583 | const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", decl); | |
| 3584 | try stream.write(self.tokenizer.getTokenSlice(line_comment_node.token)); | |
| 3585 | }, | |
| 3336 | 3586 | else => unreachable, |
| 3337 | 3587 | } |
| 3338 | 3588 | }, |
| 3339 | 3589 | |
| 3340 | RenderState.FieldInitializer => |field_init| { | |
| 3341 | //TODO try self.renderComments(stream, field_init, indent); | |
| 3342 | try stream.print(".{}", self.tokenizer.getTokenSlice(field_init.name_token)); | |
| 3343 | try stream.print(" = "); | |
| 3344 | try stack.append(RenderState { .Expression = field_init.expr }); | |
| 3345 | }, | |
| 3346 | ||
| 3347 | 3590 | RenderState.VarDecl => |var_decl| { |
| 3348 | 3591 | try stack.append(RenderState { .Text = ";" }); |
| 3349 | 3592 | if (var_decl.init_node) |init_node| { |
| 3350 | 3593 | try stack.append(RenderState { .Expression = init_node }); |
| 3351 | try stack.append(RenderState { .Text = " = " }); | |
| 3594 | const text = if (init_node.id == ast.Node.Id.MultilineStringLiteral) " =" else " = "; | |
| 3595 | try stack.append(RenderState { .Text = text }); | |
| 3352 | 3596 | } |
| 3353 | 3597 | if (var_decl.align_node) |align_node| { |
| 3354 | 3598 | try stack.append(RenderState { .Text = ")" }); |
| ... | ... | @@ -3385,7 +3629,6 @@ pub const Parser = struct { |
| 3385 | 3629 | |
| 3386 | 3630 | RenderState.ParamDecl => |base| { |
| 3387 | 3631 | const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base); |
| 3388 | // TODO try self.renderComments(stream, param_decl, indent); | |
| 3389 | 3632 | if (param_decl.comptime_token) |comptime_token| { |
| 3390 | 3633 | try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token)); |
| 3391 | 3634 | } |
| ... | ... | @@ -3467,6 +3710,9 @@ pub const Parser = struct { |
| 3467 | 3710 | }, |
| 3468 | 3711 | ast.Node.Id.Suspend => { |
| 3469 | 3712 | const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); |
| 3713 | if (suspend_node.label) |label| { | |
| 3714 | try stream.print("{}: ", self.tokenizer.getTokenSlice(label)); | |
| 3715 | } | |
| 3470 | 3716 | try stream.print("{}", self.tokenizer.getTokenSlice(suspend_node.suspend_token)); |
| 3471 | 3717 | |
| 3472 | 3718 | if (suspend_node.body) |body| { |
| ... | ... | @@ -3635,19 +3881,41 @@ pub const Parser = struct { |
| 3635 | 3881 | try stack.append(RenderState { .Expression = suffix_op.lhs }); |
| 3636 | 3882 | continue; |
| 3637 | 3883 | } |
| 3884 | if (field_inits.len == 1) { | |
| 3885 | const field_init = field_inits.at(0); | |
| 3886 | ||
| 3887 | try stack.append(RenderState { .Text = " }" }); | |
| 3888 | try stack.append(RenderState { .Expression = field_init }); | |
| 3889 | try stack.append(RenderState { .Text = "{ " }); | |
| 3890 | try stack.append(RenderState { .Expression = suffix_op.lhs }); | |
| 3891 | continue; | |
| 3892 | } | |
| 3638 | 3893 | try stack.append(RenderState { .Text = "}"}); |
| 3639 | 3894 | try stack.append(RenderState.PrintIndent); |
| 3640 | 3895 | try stack.append(RenderState { .Indent = indent }); |
| 3896 | try stack.append(RenderState { .Text = "\n" }); | |
| 3641 | 3897 | var i = field_inits.len; |
| 3642 | 3898 | while (i != 0) { |
| 3643 | 3899 | i -= 1; |
| 3644 | 3900 | const field_init = field_inits.at(i); |
| 3645 | try stack.append(RenderState { .Text = ",\n" }); | |
| 3646 | try stack.append(RenderState { .FieldInitializer = field_init }); | |
| 3901 | if (field_init.id != ast.Node.Id.LineComment) { | |
| 3902 | try stack.append(RenderState { .Text = "," }); | |
| 3903 | } | |
| 3904 | try stack.append(RenderState { .Expression = field_init }); | |
| 3647 | 3905 | try stack.append(RenderState.PrintIndent); |
| 3906 | if (i != 0) { | |
| 3907 | try stack.append(RenderState { .Text = blk: { | |
| 3908 | const prev_node = field_inits.at(i - 1); | |
| 3909 | const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, field_init.firstToken()); | |
| 3910 | if (loc.line >= 2) { | |
| 3911 | break :blk "\n\n"; | |
| 3912 | } | |
| 3913 | break :blk "\n"; | |
| 3914 | }}); | |
| 3915 | } | |
| 3648 | 3916 | } |
| 3649 | 3917 | try stack.append(RenderState { .Indent = indent + indent_delta }); |
| 3650 | try stack.append(RenderState { .Text = " {\n"}); | |
| 3918 | try stack.append(RenderState { .Text = "{\n"}); | |
| 3651 | 3919 | try stack.append(RenderState { .Expression = suffix_op.lhs }); |
| 3652 | 3920 | }, |
| 3653 | 3921 | ast.Node.SuffixOp.Op.ArrayInitializer => |exprs| { |
| ... | ... | @@ -3656,6 +3924,16 @@ pub const Parser = struct { |
| 3656 | 3924 | try stack.append(RenderState { .Expression = suffix_op.lhs }); |
| 3657 | 3925 | continue; |
| 3658 | 3926 | } |
| 3927 | if (exprs.len == 1) { | |
| 3928 | const expr = exprs.at(0); | |
| 3929 | ||
| 3930 | try stack.append(RenderState { .Text = "}" }); | |
| 3931 | try stack.append(RenderState { .Expression = expr }); | |
| 3932 | try stack.append(RenderState { .Text = "{" }); | |
| 3933 | try stack.append(RenderState { .Expression = suffix_op.lhs }); | |
| 3934 | continue; | |
| 3935 | } | |
| 3936 | ||
| 3659 | 3937 | try stack.append(RenderState { .Text = "}"}); |
| 3660 | 3938 | try stack.append(RenderState.PrintIndent); |
| 3661 | 3939 | try stack.append(RenderState { .Indent = indent }); |
| ... | ... | @@ -3668,7 +3946,7 @@ pub const Parser = struct { |
| 3668 | 3946 | try stack.append(RenderState.PrintIndent); |
| 3669 | 3947 | } |
| 3670 | 3948 | try stack.append(RenderState { .Indent = indent + indent_delta }); |
| 3671 | try stack.append(RenderState { .Text = " {\n"}); | |
| 3949 | try stack.append(RenderState { .Text = "{\n"}); | |
| 3672 | 3950 | try stack.append(RenderState { .Expression = suffix_op.lhs }); |
| 3673 | 3951 | }, |
| 3674 | 3952 | } |
| ... | ... | @@ -3802,45 +4080,49 @@ pub const Parser = struct { |
| 3802 | 4080 | ast.Node.ContainerDecl.Kind.Union => try stream.print("union"), |
| 3803 | 4081 | } |
| 3804 | 4082 | |
| 3805 | try stack.append(RenderState { .Text = "}"}); | |
| 3806 | try stack.append(RenderState.PrintIndent); | |
| 3807 | try stack.append(RenderState { .Indent = indent }); | |
| 3808 | try stack.append(RenderState { .Text = "\n"}); | |
| 3809 | ||
| 3810 | 4083 | const fields_and_decls = container_decl.fields_and_decls.toSliceConst(); |
| 3811 | var i = fields_and_decls.len; | |
| 3812 | while (i != 0) { | |
| 3813 | i -= 1; | |
| 3814 | const node = fields_and_decls[i]; | |
| 3815 | switch (node.id) { | |
| 3816 | ast.Node.Id.StructField, | |
| 3817 | ast.Node.Id.UnionTag, | |
| 3818 | ast.Node.Id.EnumTag => { | |
| 3819 | try stack.append(RenderState { .Text = "," }); | |
| 3820 | }, | |
| 3821 | else => { } | |
| 3822 | } | |
| 3823 | try stack.append(RenderState { .TopLevelDecl = node}); | |
| 4084 | if (fields_and_decls.len == 0) { | |
| 4085 | try stack.append(RenderState { .Text = "{}"}); | |
| 4086 | } else { | |
| 4087 | try stack.append(RenderState { .Text = "}"}); | |
| 3824 | 4088 | try stack.append(RenderState.PrintIndent); |
| 3825 | try stack.append(RenderState { | |
| 3826 | .Text = blk: { | |
| 3827 | if (i != 0) { | |
| 3828 | const prev_node = fields_and_decls[i - 1]; | |
| 3829 | const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken()); | |
| 3830 | if (loc.line >= 2) { | |
| 3831 | break :blk "\n\n"; | |
| 4089 | try stack.append(RenderState { .Indent = indent }); | |
| 4090 | try stack.append(RenderState { .Text = "\n"}); | |
| 4091 | ||
| 4092 | var i = fields_and_decls.len; | |
| 4093 | while (i != 0) { | |
| 4094 | i -= 1; | |
| 4095 | const node = fields_and_decls[i]; | |
| 4096 | try stack.append(RenderState { .TopLevelDecl = node}); | |
| 4097 | try stack.append(RenderState.PrintIndent); | |
| 4098 | try stack.append(RenderState { | |
| 4099 | .Text = blk: { | |
| 4100 | if (i != 0) { | |
| 4101 | const prev_node = fields_and_decls[i - 1]; | |
| 4102 | const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken()); | |
| 4103 | if (loc.line >= 2) { | |
| 4104 | break :blk "\n\n"; | |
| 4105 | } | |
| 3832 | 4106 | } |
| 3833 | } | |
| 3834 | break :blk "\n"; | |
| 3835 | }, | |
| 3836 | }); | |
| 4107 | break :blk "\n"; | |
| 4108 | }, | |
| 4109 | }); | |
| 4110 | } | |
| 4111 | try stack.append(RenderState { .Indent = indent + indent_delta}); | |
| 4112 | try stack.append(RenderState { .Text = "{"}); | |
| 3837 | 4113 | } |
| 3838 | try stack.append(RenderState { .Indent = indent + indent_delta}); | |
| 3839 | try stack.append(RenderState { .Text = "{"}); | |
| 3840 | 4114 | |
| 3841 | 4115 | switch (container_decl.init_arg_expr) { |
| 3842 | 4116 | ast.Node.ContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}), |
| 3843 | ast.Node.ContainerDecl.InitArg.Enum => try stack.append(RenderState { .Text = "(enum) "}), | |
| 4117 | ast.Node.ContainerDecl.InitArg.Enum => |enum_tag_type| { | |
| 4118 | if (enum_tag_type) |expr| { | |
| 4119 | try stack.append(RenderState { .Text = ")) "}); | |
| 4120 | try stack.append(RenderState { .Expression = expr}); | |
| 4121 | try stack.append(RenderState { .Text = "(enum("}); | |
| 4122 | } else { | |
| 4123 | try stack.append(RenderState { .Text = "(enum) "}); | |
| 4124 | } | |
| 4125 | }, | |
| 3844 | 4126 | ast.Node.ContainerDecl.InitArg.Type => |type_expr| { |
| 3845 | 4127 | try stack.append(RenderState { .Text = ") "}); |
| 3846 | 4128 | try stack.append(RenderState { .Expression = type_expr}); |
| ... | ... | @@ -3850,20 +4132,47 @@ pub const Parser = struct { |
| 3850 | 4132 | }, |
| 3851 | 4133 | ast.Node.Id.ErrorSetDecl => { |
| 3852 | 4134 | const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base); |
| 3853 | try stream.print("error "); | |
| 4135 | ||
| 4136 | const decls = err_set_decl.decls.toSliceConst(); | |
| 4137 | if (decls.len == 0) { | |
| 4138 | try stream.write("error{}"); | |
| 4139 | continue; | |
| 4140 | } | |
| 4141 | ||
| 4142 | if (decls.len == 1) blk: { | |
| 4143 | const node = decls[0]; | |
| 4144 | ||
| 4145 | // if there are any doc comments or same line comments | |
| 4146 | // don't try to put it all on one line | |
| 4147 | if (node.same_line_comment != null) break :blk; | |
| 4148 | if (node.cast(ast.Node.ErrorTag)) |tag| { | |
| 4149 | if (tag.doc_comments != null) break :blk; | |
| 4150 | } else { | |
| 4151 | break :blk; | |
| 4152 | } | |
| 4153 | ||
| 4154 | ||
| 4155 | try stream.write("error{"); | |
| 4156 | try stack.append(RenderState { .Text = "}" }); | |
| 4157 | try stack.append(RenderState { .TopLevelDecl = node }); | |
| 4158 | continue; | |
| 4159 | } | |
| 4160 | ||
| 4161 | try stream.write("error{"); | |
| 3854 | 4162 | |
| 3855 | 4163 | try stack.append(RenderState { .Text = "}"}); |
| 3856 | 4164 | try stack.append(RenderState.PrintIndent); |
| 3857 | 4165 | try stack.append(RenderState { .Indent = indent }); |
| 3858 | 4166 | try stack.append(RenderState { .Text = "\n"}); |
| 3859 | 4167 | |
| 3860 | const decls = err_set_decl.decls.toSliceConst(); | |
| 3861 | 4168 | var i = decls.len; |
| 3862 | 4169 | while (i != 0) { |
| 3863 | 4170 | i -= 1; |
| 3864 | 4171 | const node = decls[i]; |
| 3865 | try stack.append(RenderState { .Text = "," }); | |
| 3866 | try stack.append(RenderState { .Expression = node }); | |
| 4172 | if (node.id != ast.Node.Id.LineComment) { | |
| 4173 | try stack.append(RenderState { .Text = "," }); | |
| 4174 | } | |
| 4175 | try stack.append(RenderState { .TopLevelDecl = node }); | |
| 3867 | 4176 | try stack.append(RenderState.PrintIndent); |
| 3868 | 4177 | try stack.append(RenderState { |
| 3869 | 4178 | .Text = blk: { |
| ... | ... | @@ -3879,7 +4188,6 @@ pub const Parser = struct { |
| 3879 | 4188 | }); |
| 3880 | 4189 | } |
| 3881 | 4190 | try stack.append(RenderState { .Indent = indent + indent_delta}); |
| 3882 | try stack.append(RenderState { .Text = "{"}); | |
| 3883 | 4191 | }, |
| 3884 | 4192 | ast.Node.Id.MultilineStringLiteral => { |
| 3885 | 4193 | const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base); |
| ... | ... | @@ -3891,7 +4199,7 @@ pub const Parser = struct { |
| 3891 | 4199 | try stream.writeByteNTimes(' ', indent + indent_delta); |
| 3892 | 4200 | try stream.print("{}", self.tokenizer.getTokenSlice(t)); |
| 3893 | 4201 | } |
| 3894 | try stream.writeByteNTimes(' ', indent + indent_delta); | |
| 4202 | try stream.writeByteNTimes(' ', indent); | |
| 3895 | 4203 | }, |
| 3896 | 4204 | ast.Node.Id.UndefinedLiteral => { |
| 3897 | 4205 | const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base); |
| ... | ... | @@ -3974,7 +4282,19 @@ pub const Parser = struct { |
| 3974 | 4282 | try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(visib_token) }); |
| 3975 | 4283 | } |
| 3976 | 4284 | }, |
| 3977 | ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"), | |
| 4285 | ast.Node.Id.PromiseType => { | |
| 4286 | const promise_type = @fieldParentPtr(ast.Node.PromiseType, "base", base); | |
| 4287 | try stream.write(self.tokenizer.getTokenSlice(promise_type.promise_token)); | |
| 4288 | if (promise_type.result) |result| { | |
| 4289 | try stream.write(self.tokenizer.getTokenSlice(result.arrow_token)); | |
| 4290 | try stack.append(RenderState { .Expression = result.return_type}); | |
| 4291 | } | |
| 4292 | }, | |
| 4293 | ast.Node.Id.LineComment => { | |
| 4294 | const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", base); | |
| 4295 | try stream.write(self.tokenizer.getTokenSlice(line_comment_node.token)); | |
| 4296 | }, | |
| 4297 | ast.Node.Id.DocComment => unreachable, // doc comments are attached to nodes | |
| 3978 | 4298 | ast.Node.Id.Switch => { |
| 3979 | 4299 | const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base); |
| 3980 | 4300 | try stream.print("{} (", self.tokenizer.getTokenSlice(switch_node.switch_token)); |
| ... | ... | @@ -3989,8 +4309,7 @@ pub const Parser = struct { |
| 3989 | 4309 | while (i != 0) { |
| 3990 | 4310 | i -= 1; |
| 3991 | 4311 | const node = cases[i]; |
| 3992 | try stack.append(RenderState { .Text = ","}); | |
| 3993 | try stack.append(RenderState { .Expression = &node.base}); | |
| 4312 | try stack.append(RenderState { .Expression = node}); | |
| 3994 | 4313 | try stack.append(RenderState.PrintIndent); |
| 3995 | 4314 | try stack.append(RenderState { |
| 3996 | 4315 | .Text = blk: { |
| ... | ... | @@ -4012,6 +4331,8 @@ pub const Parser = struct { |
| 4012 | 4331 | ast.Node.Id.SwitchCase => { |
| 4013 | 4332 | const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base); |
| 4014 | 4333 | |
| 4334 | try stack.append(RenderState { .PrintSameLineComment = base.same_line_comment }); | |
| 4335 | try stack.append(RenderState { .Text = "," }); | |
| 4015 | 4336 | try stack.append(RenderState { .Expression = switch_case.expr }); |
| 4016 | 4337 | if (switch_case.payload) |payload| { |
| 4017 | 4338 | try stack.append(RenderState { .Text = " " }); |
| ... | ... | @@ -4321,6 +4642,7 @@ pub const Parser = struct { |
| 4321 | 4642 | ast.Node.Id.StructField, |
| 4322 | 4643 | ast.Node.Id.UnionTag, |
| 4323 | 4644 | ast.Node.Id.EnumTag, |
| 4645 | ast.Node.Id.ErrorTag, | |
| 4324 | 4646 | ast.Node.Id.Root, |
| 4325 | 4647 | ast.Node.Id.VarDecl, |
| 4326 | 4648 | ast.Node.Id.Use, |
| ... | ... | @@ -4328,10 +4650,10 @@ pub const Parser = struct { |
| 4328 | 4650 | ast.Node.Id.ParamDecl => unreachable, |
| 4329 | 4651 | }, |
| 4330 | 4652 | RenderState.Statement => |base| { |
| 4653 | try stack.append(RenderState { .PrintSameLineComment = base.same_line_comment } ); | |
| 4331 | 4654 | switch (base.id) { |
| 4332 | 4655 | ast.Node.Id.VarDecl => { |
| 4333 | 4656 | const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base); |
| 4334 | try self.renderComments(stream, var_decl, indent); | |
| 4335 | 4657 | try stack.append(RenderState { .VarDecl = var_decl}); |
| 4336 | 4658 | }, |
| 4337 | 4659 | else => { |
| ... | ... | @@ -4344,12 +4666,16 @@ pub const Parser = struct { |
| 4344 | 4666 | }, |
| 4345 | 4667 | RenderState.Indent => |new_indent| indent = new_indent, |
| 4346 | 4668 | RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent), |
| 4669 | RenderState.PrintSameLineComment => |maybe_comment| blk: { | |
| 4670 | const comment_token = maybe_comment ?? break :blk; | |
| 4671 | try stream.print(" {}", self.tokenizer.getTokenSlice(comment_token)); | |
| 4672 | }, | |
| 4347 | 4673 | } |
| 4348 | 4674 | } |
| 4349 | 4675 | } |
| 4350 | 4676 | |
| 4351 | 4677 | fn renderComments(self: &Parser, stream: var, node: var, indent: usize) !void { |
| 4352 | const comment = node.comments ?? return; | |
| 4678 | const comment = node.doc_comments ?? return; | |
| 4353 | 4679 | for (comment.lines.toSliceConst()) |line_token| { |
| 4354 | 4680 | try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token)); |
| 4355 | 4681 | try stream.writeByteNTimes(' ', indent); |
| ... | ... | @@ -4373,920 +4699,6 @@ pub const Parser = struct { |
| 4373 | 4699 | |
| 4374 | 4700 | }; |
| 4375 | 4701 | |
| 4376 | var fixed_buffer_mem: [100 * 1024]u8 = undefined; | |
| 4377 | ||
| 4378 | fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 { | |
| 4379 | var tokenizer = Tokenizer.init(source); | |
| 4380 | var parser = Parser.init(&tokenizer, allocator, "(memory buffer)"); | |
| 4381 | defer parser.deinit(); | |
| 4382 | ||
| 4383 | var tree = try parser.parse(); | |
| 4384 | defer tree.deinit(); | |
| 4385 | ||
| 4386 | var buffer = try std.Buffer.initSize(allocator, 0); | |
| 4387 | errdefer buffer.deinit(); | |
| 4388 | ||
| 4389 | var buffer_out_stream = io.BufferOutStream.init(&buffer); | |
| 4390 | try parser.renderSource(&buffer_out_stream.stream, tree.root_node); | |
| 4391 | return buffer.toOwnedSlice(); | |
| 4392 | } | |
| 4393 | ||
| 4394 | fn testCanonical(source: []const u8) !void { | |
| 4395 | const needed_alloc_count = x: { | |
| 4396 | // Try it once with unlimited memory, make sure it works | |
| 4397 | var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | |
| 4398 | var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize)); | |
| 4399 | const result_source = try testParse(source, &failing_allocator.allocator); | |
| 4400 | if (!mem.eql(u8, result_source, source)) { | |
| 4401 | warn("\n====== expected this output: =========\n"); | |
| 4402 | warn("{}", source); | |
| 4403 | warn("\n======== instead found this: =========\n"); | |
| 4404 | warn("{}", result_source); | |
| 4405 | warn("\n======================================\n"); | |
| 4406 | return error.TestFailed; | |
| 4407 | } | |
| 4408 | failing_allocator.allocator.free(result_source); | |
| 4409 | break :x failing_allocator.index; | |
| 4410 | }; | |
| 4411 | ||
| 4412 | var fail_index: usize = 0; | |
| 4413 | while (fail_index < needed_alloc_count) : (fail_index += 1) { | |
| 4414 | var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | |
| 4415 | var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index); | |
| 4416 | if (testParse(source, &failing_allocator.allocator)) |_| { | |
| 4417 | return error.NondeterministicMemoryUsage; | |
| 4418 | } else |err| switch (err) { | |
| 4419 | error.OutOfMemory => { | |
| 4420 | if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) { | |
| 4421 | warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n", | |
| 4422 | fail_index, needed_alloc_count, | |
| 4423 | failing_allocator.allocated_bytes, failing_allocator.freed_bytes, | |
| 4424 | failing_allocator.index, failing_allocator.deallocations); | |
| 4425 | return error.MemoryLeakDetected; | |
| 4426 | } | |
| 4427 | }, | |
| 4428 | error.ParseError => @panic("test failed"), | |
| 4429 | } | |
| 4430 | } | |
| 4431 | } | |
| 4432 | ||
| 4433 | test "zig fmt: preserve top level comments" { | |
| 4434 | try testCanonical( | |
| 4435 | \\// top level comment | |
| 4436 | \\test "hi" {} | |
| 4437 | \\ | |
| 4438 | ); | |
| 4439 | } | |
| 4440 | ||
| 4441 | test "zig fmt: get stdout or fail" { | |
| 4442 | try testCanonical( | |
| 4443 | \\const std = @import("std"); | |
| 4444 | \\ | |
| 4445 | \\pub fn main() !void { | |
| 4446 | \\ // If this program is run without stdout attached, exit with an error. | |
| 4447 | \\ // another comment | |
| 4448 | \\ var stdout_file = try std.io.getStdOut; | |
| 4449 | \\} | |
| 4450 | \\ | |
| 4451 | ); | |
| 4452 | } | |
| 4453 | ||
| 4454 | test "zig fmt: preserve spacing" { | |
| 4455 | try testCanonical( | |
| 4456 | \\const std = @import("std"); | |
| 4457 | \\ | |
| 4458 | \\pub fn main() !void { | |
| 4459 | \\ var stdout_file = try std.io.getStdOut; | |
| 4460 | \\ var stdout_file = try std.io.getStdOut; | |
| 4461 | \\ | |
| 4462 | \\ var stdout_file = try std.io.getStdOut; | |
| 4463 | \\ var stdout_file = try std.io.getStdOut; | |
| 4464 | \\} | |
| 4465 | \\ | |
| 4466 | ); | |
| 4467 | } | |
| 4468 | ||
| 4469 | test "zig fmt: return types" { | |
| 4470 | try testCanonical( | |
| 4471 | \\pub fn main() !void {} | |
| 4472 | \\pub fn main() var {} | |
| 4473 | \\pub fn main() i32 {} | |
| 4474 | \\ | |
| 4475 | ); | |
| 4476 | } | |
| 4477 | ||
| 4478 | test "zig fmt: imports" { | |
| 4479 | try testCanonical( | |
| 4480 | \\const std = @import("std"); | |
| 4481 | \\const std = @import(); | |
| 4482 | \\ | |
| 4483 | ); | |
| 4484 | } | |
| 4485 | ||
| 4486 | test "zig fmt: global declarations" { | |
| 4487 | try testCanonical( | |
| 4488 | \\const a = b; | |
| 4489 | \\pub const a = b; | |
| 4490 | \\var a = b; | |
| 4491 | \\pub var a = b; | |
| 4492 | \\const a: i32 = b; | |
| 4493 | \\pub const a: i32 = b; | |
| 4494 | \\var a: i32 = b; | |
| 4495 | \\pub var a: i32 = b; | |
| 4496 | \\extern const a: i32 = b; | |
| 4497 | \\pub extern const a: i32 = b; | |
| 4498 | \\extern var a: i32 = b; | |
| 4499 | \\pub extern var a: i32 = b; | |
| 4500 | \\extern "a" const a: i32 = b; | |
| 4501 | \\pub extern "a" const a: i32 = b; | |
| 4502 | \\extern "a" var a: i32 = b; | |
| 4503 | \\pub extern "a" var a: i32 = b; | |
| 4504 | \\ | |
| 4505 | ); | |
| 4506 | } | |
| 4507 | ||
| 4508 | test "zig fmt: extern declaration" { | |
| 4509 | try testCanonical( | |
| 4510 | \\extern var foo: c_int; | |
| 4511 | \\ | |
| 4512 | ); | |
| 4513 | } | |
| 4514 | ||
| 4515 | test "zig fmt: alignment" { | |
| 4516 | try testCanonical( | |
| 4517 | \\var foo: c_int align(1); | |
| 4518 | \\ | |
| 4519 | ); | |
| 4520 | } | |
| 4521 | ||
| 4522 | test "zig fmt: C main" { | |
| 4523 | try testCanonical( | |
| 4524 | \\fn main(argc: c_int, argv: &&u8) c_int { | |
| 4525 | \\ const a = b; | |
| 4526 | \\} | |
| 4527 | \\ | |
| 4528 | ); | |
| 4529 | } | |
| 4530 | ||
| 4531 | test "zig fmt: return" { | |
| 4532 | try testCanonical( | |
| 4533 | \\fn foo(argc: c_int, argv: &&u8) c_int { | |
| 4534 | \\ return 0; | |
| 4535 | \\} | |
| 4536 | \\ | |
| 4537 | \\fn bar() void { | |
| 4538 | \\ return; | |
| 4539 | \\} | |
| 4540 | \\ | |
| 4541 | ); | |
| 4542 | } | |
| 4543 | ||
| 4544 | test "zig fmt: pointer attributes" { | |
| 4545 | try testCanonical( | |
| 4546 | \\extern fn f1(s: &align(&u8) u8) c_int; | |
| 4547 | \\extern fn f2(s: &&align(1) &const &volatile u8) c_int; | |
| 4548 | \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int; | |
| 4549 | \\extern fn f4(s: &align(1) const volatile u8) c_int; | |
| 4550 | \\ | |
| 4551 | ); | |
| 4552 | } | |
| 4553 | ||
| 4554 | test "zig fmt: slice attributes" { | |
| 4555 | try testCanonical( | |
| 4556 | \\extern fn f1(s: &align(&u8) u8) c_int; | |
| 4557 | \\extern fn f2(s: &&align(1) &const &volatile u8) c_int; | |
| 4558 | \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int; | |
| 4559 | \\extern fn f4(s: &align(1) const volatile u8) c_int; | |
| 4560 | \\ | |
| 4561 | ); | |
| 4562 | } | |
| 4563 | ||
| 4564 | test "zig fmt: test declaration" { | |
| 4565 | try testCanonical( | |
| 4566 | \\test "test name" { | |
| 4567 | \\ const a = 1; | |
| 4568 | \\ var b = 1; | |
| 4569 | \\} | |
| 4570 | \\ | |
| 4571 | ); | |
| 4572 | } | |
| 4573 | ||
| 4574 | test "zig fmt: infix operators" { | |
| 4575 | try testCanonical( | |
| 4576 | \\test "infix operators" { | |
| 4577 | \\ var i = undefined; | |
| 4578 | \\ i = 2; | |
| 4579 | \\ i *= 2; | |
| 4580 | \\ i |= 2; | |
| 4581 | \\ i ^= 2; | |
| 4582 | \\ i <<= 2; | |
| 4583 | \\ i >>= 2; | |
| 4584 | \\ i &= 2; | |
| 4585 | \\ i *= 2; | |
| 4586 | \\ i *%= 2; | |
| 4587 | \\ i -= 2; | |
| 4588 | \\ i -%= 2; | |
| 4589 | \\ i += 2; | |
| 4590 | \\ i +%= 2; | |
| 4591 | \\ i /= 2; | |
| 4592 | \\ i %= 2; | |
| 4593 | \\ _ = i == i; | |
| 4594 | \\ _ = i != i; | |
| 4595 | \\ _ = i != i; | |
| 4596 | \\ _ = i.i; | |
| 4597 | \\ _ = i || i; | |
| 4598 | \\ _ = i!i; | |
| 4599 | \\ _ = i ** i; | |
| 4600 | \\ _ = i ++ i; | |
| 4601 | \\ _ = i ?? i; | |
| 4602 | \\ _ = i % i; | |
| 4603 | \\ _ = i / i; | |
| 4604 | \\ _ = i *% i; | |
| 4605 | \\ _ = i * i; | |
| 4606 | \\ _ = i -% i; | |
| 4607 | \\ _ = i - i; | |
| 4608 | \\ _ = i +% i; | |
| 4609 | \\ _ = i + i; | |
| 4610 | \\ _ = i << i; | |
| 4611 | \\ _ = i >> i; | |
| 4612 | \\ _ = i & i; | |
| 4613 | \\ _ = i ^ i; | |
| 4614 | \\ _ = i | i; | |
| 4615 | \\ _ = i >= i; | |
| 4616 | \\ _ = i <= i; | |
| 4617 | \\ _ = i > i; | |
| 4618 | \\ _ = i < i; | |
| 4619 | \\ _ = i and i; | |
| 4620 | \\ _ = i or i; | |
| 4621 | \\} | |
| 4622 | \\ | |
| 4623 | ); | |
| 4624 | } | |
| 4625 | ||
| 4626 | test "zig fmt: precedence" { | |
| 4627 | try testCanonical( | |
| 4628 | \\test "precedence" { | |
| 4629 | \\ a!b(); | |
| 4630 | \\ (a!b)(); | |
| 4631 | \\ !a!b; | |
| 4632 | \\ !(a!b); | |
| 4633 | \\ !a{}; | |
| 4634 | \\ !(a{}); | |
| 4635 | \\ a + b{}; | |
| 4636 | \\ (a + b){}; | |
| 4637 | \\ a << b + c; | |
| 4638 | \\ (a << b) + c; | |
| 4639 | \\ a & b << c; | |
| 4640 | \\ (a & b) << c; | |
| 4641 | \\ a ^ b & c; | |
| 4642 | \\ (a ^ b) & c; | |
| 4643 | \\ a | b ^ c; | |
| 4644 | \\ (a | b) ^ c; | |
| 4645 | \\ a == b | c; | |
| 4646 | \\ (a == b) | c; | |
| 4647 | \\ a and b == c; | |
| 4648 | \\ (a and b) == c; | |
| 4649 | \\ a or b and c; | |
| 4650 | \\ (a or b) and c; | |
| 4651 | \\ (a or b) and c; | |
| 4652 | \\} | |
| 4653 | \\ | |
| 4654 | ); | |
| 4655 | } | |
| 4656 | ||
| 4657 | test "zig fmt: prefix operators" { | |
| 4658 | try testCanonical( | |
| 4659 | \\test "prefix operators" { | |
| 4660 | \\ try return --%~??!*&0; | |
| 4661 | \\} | |
| 4662 | \\ | |
| 4663 | ); | |
| 4664 | } | |
| 4665 | ||
| 4666 | test "zig fmt: call expression" { | |
| 4667 | try testCanonical( | |
| 4668 | \\test "test calls" { | |
| 4669 | \\ a(); | |
| 4670 | \\ a(1); | |
| 4671 | \\ a(1, 2); | |
| 4672 | \\ a(1, 2) + a(1, 2); | |
| 4673 | \\} | |
| 4674 | \\ | |
| 4675 | ); | |
| 4676 | } | |
| 4677 | ||
| 4678 | test "zig fmt: var args" { | |
| 4679 | try testCanonical( | |
| 4680 | \\fn print(args: ...) void {} | |
| 4681 | \\ | |
| 4682 | ); | |
| 4683 | } | |
| 4684 | ||
| 4685 | test "zig fmt: var type" { | |
| 4686 | try testCanonical( | |
| 4687 | \\fn print(args: var) var {} | |
| 4688 | \\const Var = var; | |
| 4689 | \\const i: var = 0; | |
| 4690 | \\ | |
| 4691 | ); | |
| 4692 | } | |
| 4693 | ||
| 4694 | test "zig fmt: functions" { | |
| 4695 | try testCanonical( | |
| 4696 | \\extern fn puts(s: &const u8) c_int; | |
| 4697 | \\extern "c" fn puts(s: &const u8) c_int; | |
| 4698 | \\export fn puts(s: &const u8) c_int; | |
| 4699 | \\inline fn puts(s: &const u8) c_int; | |
| 4700 | \\pub extern fn puts(s: &const u8) c_int; | |
| 4701 | \\pub extern "c" fn puts(s: &const u8) c_int; | |
| 4702 | \\pub export fn puts(s: &const u8) c_int; | |
| 4703 | \\pub inline fn puts(s: &const u8) c_int; | |
| 4704 | \\pub extern fn puts(s: &const u8) align(2 + 2) c_int; | |
| 4705 | \\pub extern "c" fn puts(s: &const u8) align(2 + 2) c_int; | |
| 4706 | \\pub export fn puts(s: &const u8) align(2 + 2) c_int; | |
| 4707 | \\pub inline fn puts(s: &const u8) align(2 + 2) c_int; | |
| 4708 | \\ | |
| 4709 | ); | |
| 4710 | } | |
| 4711 | ||
| 4712 | test "zig fmt: multiline string" { | |
| 4713 | try testCanonical( | |
| 4714 | \\const s = | |
| 4715 | \\ \\ something | |
| 4716 | \\ \\ something else | |
| 4717 | \\ ; | |
| 4718 | \\ | |
| 4719 | ); | |
| 4720 | } | |
| 4721 | ||
| 4722 | test "zig fmt: values" { | |
| 4723 | try testCanonical( | |
| 4724 | \\test "values" { | |
| 4725 | \\ 1; | |
| 4726 | \\ 1.0; | |
| 4727 | \\ "string"; | |
| 4728 | \\ c"cstring"; | |
| 4729 | \\ 'c'; | |
| 4730 | \\ true; | |
| 4731 | \\ false; | |
| 4732 | \\ null; | |
| 4733 | \\ undefined; | |
| 4734 | \\ error; | |
| 4735 | \\ this; | |
| 4736 | \\ unreachable; | |
| 4737 | \\} | |
| 4738 | \\ | |
| 4739 | ); | |
| 4740 | } | |
| 4741 | ||
| 4742 | test "zig fmt: indexing" { | |
| 4743 | try testCanonical( | |
| 4744 | \\test "test index" { | |
| 4745 | \\ a[0]; | |
| 4746 | \\ a[0 + 5]; | |
| 4747 | \\ a[0..]; | |
| 4748 | \\ a[0..5]; | |
| 4749 | \\ a[a[0]]; | |
| 4750 | \\ a[a[0..]]; | |
| 4751 | \\ a[a[0..5]]; | |
| 4752 | \\ a[a[0]..]; | |
| 4753 | \\ a[a[0..5]..]; | |
| 4754 | \\ a[a[0]..a[0]]; | |
| 4755 | \\ a[a[0..5]..a[0]]; | |
| 4756 | \\ a[a[0..5]..a[0..5]]; | |
| 4757 | \\} | |
| 4758 | \\ | |
| 4759 | ); | |
| 4760 | } | |
| 4761 | ||
| 4762 | test "zig fmt: struct declaration" { | |
| 4763 | try testCanonical( | |
| 4764 | \\const S = struct { | |
| 4765 | \\ const Self = this; | |
| 4766 | \\ f1: u8, | |
| 4767 | \\ pub f3: u8, | |
| 4768 | \\ | |
| 4769 | \\ fn method(self: &Self) Self { | |
| 4770 | \\ return *self; | |
| 4771 | \\ } | |
| 4772 | \\ | |
| 4773 | \\ f2: u8, | |
| 4774 | \\}; | |
| 4775 | \\ | |
| 4776 | \\const Ps = packed struct { | |
| 4777 | \\ a: u8, | |
| 4778 | \\ pub b: u8, | |
| 4779 | \\ | |
| 4780 | \\ c: u8, | |
| 4781 | \\}; | |
| 4782 | \\ | |
| 4783 | \\const Es = extern struct { | |
| 4784 | \\ a: u8, | |
| 4785 | \\ pub b: u8, | |
| 4786 | \\ | |
| 4787 | \\ c: u8, | |
| 4788 | \\}; | |
| 4789 | \\ | |
| 4790 | ); | |
| 4791 | } | |
| 4792 | ||
| 4793 | test "zig fmt: enum declaration" { | |
| 4794 | try testCanonical( | |
| 4795 | \\const E = enum { | |
| 4796 | \\ Ok, | |
| 4797 | \\ SomethingElse = 0, | |
| 4798 | \\}; | |
| 4799 | \\ | |
| 4800 | \\const E2 = enum(u8) { | |
| 4801 | \\ Ok, | |
| 4802 | \\ SomethingElse = 255, | |
| 4803 | \\ SomethingThird, | |
| 4804 | \\}; | |
| 4805 | \\ | |
| 4806 | \\const Ee = extern enum { | |
| 4807 | \\ Ok, | |
| 4808 | \\ SomethingElse, | |
| 4809 | \\ SomethingThird, | |
| 4810 | \\}; | |
| 4811 | \\ | |
| 4812 | \\const Ep = packed enum { | |
| 4813 | \\ Ok, | |
| 4814 | \\ SomethingElse, | |
| 4815 | \\ SomethingThird, | |
| 4816 | \\}; | |
| 4817 | \\ | |
| 4818 | ); | |
| 4819 | } | |
| 4820 | ||
| 4821 | test "zig fmt: union declaration" { | |
| 4822 | try testCanonical( | |
| 4823 | \\const U = union { | |
| 4824 | \\ Int: u8, | |
| 4825 | \\ Float: f32, | |
| 4826 | \\ None, | |
| 4827 | \\ Bool: bool, | |
| 4828 | \\}; | |
| 4829 | \\ | |
| 4830 | \\const Ue = union(enum) { | |
| 4831 | \\ Int: u8, | |
| 4832 | \\ Float: f32, | |
| 4833 | \\ None, | |
| 4834 | \\ Bool: bool, | |
| 4835 | \\}; | |
| 4836 | \\ | |
| 4837 | \\const E = enum { | |
| 4838 | \\ Int, | |
| 4839 | \\ Float, | |
| 4840 | \\ None, | |
| 4841 | \\ Bool, | |
| 4842 | \\}; | |
| 4843 | \\ | |
| 4844 | \\const Ue2 = union(E) { | |
| 4845 | \\ Int: u8, | |
| 4846 | \\ Float: f32, | |
| 4847 | \\ None, | |
| 4848 | \\ Bool: bool, | |
| 4849 | \\}; | |
| 4850 | \\ | |
| 4851 | \\const Eu = extern union { | |
| 4852 | \\ Int: u8, | |
| 4853 | \\ Float: f32, | |
| 4854 | \\ None, | |
| 4855 | \\ Bool: bool, | |
| 4856 | \\}; | |
| 4857 | \\ | |
| 4858 | ); | |
| 4859 | } | |
| 4860 | ||
| 4861 | test "zig fmt: error set declaration" { | |
| 4862 | try testCanonical( | |
| 4863 | \\const E = error { | |
| 4864 | \\ A, | |
| 4865 | \\ B, | |
| 4866 | \\ | |
| 4867 | \\ C, | |
| 4868 | \\}; | |
| 4869 | \\ | |
| 4870 | ); | |
| 4871 | } | |
| 4872 | ||
| 4873 | test "zig fmt: arrays" { | |
| 4874 | try testCanonical( | |
| 4875 | \\test "test array" { | |
| 4876 | \\ const a: [2]u8 = [2]u8 { | |
| 4877 | \\ 1, | |
| 4878 | \\ 2, | |
| 4879 | \\ }; | |
| 4880 | \\ const a: [2]u8 = []u8 { | |
| 4881 | \\ 1, | |
| 4882 | \\ 2, | |
| 4883 | \\ }; | |
| 4884 | \\ const a: [0]u8 = []u8{}; | |
| 4885 | \\} | |
| 4886 | \\ | |
| 4887 | ); | |
| 4888 | } | |
| 4889 | ||
| 4890 | test "zig fmt: container initializers" { | |
| 4891 | try testCanonical( | |
| 4892 | \\const a1 = []u8{}; | |
| 4893 | \\const a2 = []u8 { | |
| 4894 | \\ 1, | |
| 4895 | \\ 2, | |
| 4896 | \\ 3, | |
| 4897 | \\ 4, | |
| 4898 | \\}; | |
| 4899 | \\const s1 = S{}; | |
| 4900 | \\const s2 = S { | |
| 4901 | \\ .a = 1, | |
| 4902 | \\ .b = 2, | |
| 4903 | \\}; | |
| 4904 | \\ | |
| 4905 | ); | |
| 4906 | } | |
| 4907 | ||
| 4908 | test "zig fmt: catch" { | |
| 4909 | try testCanonical( | |
| 4910 | \\test "catch" { | |
| 4911 | \\ const a: error!u8 = 0; | |
| 4912 | \\ _ = a catch return; | |
| 4913 | \\ _ = a catch |err| return; | |
| 4914 | \\} | |
| 4915 | \\ | |
| 4916 | ); | |
| 4917 | } | |
| 4918 | ||
| 4919 | test "zig fmt: blocks" { | |
| 4920 | try testCanonical( | |
| 4921 | \\test "blocks" { | |
| 4922 | \\ { | |
| 4923 | \\ const a = 0; | |
| 4924 | \\ const b = 0; | |
| 4925 | \\ } | |
| 4926 | \\ | |
| 4927 | \\ blk: { | |
| 4928 | \\ const a = 0; | |
| 4929 | \\ const b = 0; | |
| 4930 | \\ } | |
| 4931 | \\ | |
| 4932 | \\ const r = blk: { | |
| 4933 | \\ const a = 0; | |
| 4934 | \\ const b = 0; | |
| 4935 | \\ }; | |
| 4936 | \\} | |
| 4937 | \\ | |
| 4938 | ); | |
| 4939 | } | |
| 4940 | ||
| 4941 | test "zig fmt: switch" { | |
| 4942 | try testCanonical( | |
| 4943 | \\test "switch" { | |
| 4944 | \\ switch (0) { | |
| 4945 | \\ 0 => {}, | |
| 4946 | \\ 1 => unreachable, | |
| 4947 | \\ 2, | |
| 4948 | \\ 3 => {}, | |
| 4949 | \\ 4 ... 7 => {}, | |
| 4950 | \\ 1 + 4 * 3 + 22 => {}, | |
| 4951 | \\ else => { | |
| 4952 | \\ const a = 1; | |
| 4953 | \\ const b = a; | |
| 4954 | \\ }, | |
| 4955 | \\ } | |
| 4956 | \\ | |
| 4957 | \\ const res = switch (0) { | |
| 4958 | \\ 0 => 0, | |
| 4959 | \\ 1 => 2, | |
| 4960 | \\ 1 => a = 4, | |
| 4961 | \\ else => 4, | |
| 4962 | \\ }; | |
| 4963 | \\ | |
| 4964 | \\ const Union = union(enum) { | |
| 4965 | \\ Int: i64, | |
| 4966 | \\ Float: f64, | |
| 4967 | \\ }; | |
| 4968 | \\ | |
| 4969 | \\ const u = Union { | |
| 4970 | \\ .Int = 0, | |
| 4971 | \\ }; | |
| 4972 | \\ switch (u) { | |
| 4973 | \\ Union.Int => |int| {}, | |
| 4974 | \\ Union.Float => |*float| unreachable, | |
| 4975 | \\ } | |
| 4976 | \\} | |
| 4977 | \\ | |
| 4978 | ); | |
| 4979 | } | |
| 4980 | ||
| 4981 | test "zig fmt: while" { | |
| 4982 | try testCanonical( | |
| 4983 | \\test "while" { | |
| 4984 | \\ while (10 < 1) { | |
| 4985 | \\ unreachable; | |
| 4986 | \\ } | |
| 4987 | \\ | |
| 4988 | \\ while (10 < 1) | |
| 4989 | \\ unreachable; | |
| 4990 | \\ | |
| 4991 | \\ var i: usize = 0; | |
| 4992 | \\ while (i < 10) : (i += 1) { | |
| 4993 | \\ continue; | |
| 4994 | \\ } | |
| 4995 | \\ | |
| 4996 | \\ i = 0; | |
| 4997 | \\ while (i < 10) : (i += 1) | |
| 4998 | \\ continue; | |
| 4999 | \\ | |
| 5000 | \\ i = 0; | |
| 5001 | \\ var j: usize = 0; | |
| 5002 | \\ while (i < 10) : ({ | |
| 5003 | \\ i += 1; | |
| 5004 | \\ j += 1; | |
| 5005 | \\ }) { | |
| 5006 | \\ continue; | |
| 5007 | \\ } | |
| 5008 | \\ | |
| 5009 | \\ var a: ?u8 = 2; | |
| 5010 | \\ while (a) |v| : (a = null) { | |
| 5011 | \\ continue; | |
| 5012 | \\ } | |
| 5013 | \\ | |
| 5014 | \\ while (a) |v| : (a = null) | |
| 5015 | \\ unreachable; | |
| 5016 | \\ | |
| 5017 | \\ label: while (10 < 0) { | |
| 5018 | \\ unreachable; | |
| 5019 | \\ } | |
| 5020 | \\ | |
| 5021 | \\ const res = while (0 < 10) { | |
| 5022 | \\ break 7; | |
| 5023 | \\ } else { | |
| 5024 | \\ unreachable; | |
| 5025 | \\ }; | |
| 5026 | \\ | |
| 5027 | \\ const res = while (0 < 10) | |
| 5028 | \\ break 7 | |
| 5029 | \\ else | |
| 5030 | \\ unreachable; | |
| 5031 | \\ | |
| 5032 | \\ var a: error!u8 = 0; | |
| 5033 | \\ while (a) |v| { | |
| 5034 | \\ a = error.Err; | |
| 5035 | \\ } else |err| { | |
| 5036 | \\ i = 1; | |
| 5037 | \\ } | |
| 5038 | \\ | |
| 5039 | \\ comptime var k: usize = 0; | |
| 5040 | \\ inline while (i < 10) : (i += 1) | |
| 5041 | \\ j += 2; | |
| 5042 | \\} | |
| 5043 | \\ | |
| 5044 | ); | |
| 5045 | } | |
| 5046 | ||
| 5047 | test "zig fmt: for" { | |
| 5048 | try testCanonical( | |
| 5049 | \\test "for" { | |
| 5050 | \\ const a = []u8 { | |
| 5051 | \\ 1, | |
| 5052 | \\ 2, | |
| 5053 | \\ 3, | |
| 5054 | \\ }; | |
| 5055 | \\ for (a) |v| { | |
| 5056 | \\ continue; | |
| 5057 | \\ } | |
| 5058 | \\ | |
| 5059 | \\ for (a) |v| | |
| 5060 | \\ continue; | |
| 5061 | \\ | |
| 5062 | \\ for (a) |*v| | |
| 5063 | \\ continue; | |
| 5064 | \\ | |
| 5065 | \\ for (a) |v, i| { | |
| 5066 | \\ continue; | |
| 5067 | \\ } | |
| 5068 | \\ | |
| 5069 | \\ for (a) |v, i| | |
| 5070 | \\ continue; | |
| 5071 | \\ | |
| 5072 | \\ const res = for (a) |v, i| { | |
| 5073 | \\ break v; | |
| 5074 | \\ } else { | |
| 5075 | \\ unreachable; | |
| 5076 | \\ }; | |
| 5077 | \\ | |
| 5078 | \\ var num: usize = 0; | |
| 5079 | \\ inline for (a) |v, i| { | |
| 5080 | \\ num += v; | |
| 5081 | \\ num += i; | |
| 5082 | \\ } | |
| 5083 | \\} | |
| 5084 | \\ | |
| 5085 | ); | |
| 5086 | } | |
| 5087 | ||
| 5088 | test "zig fmt: if" { | |
| 5089 | try testCanonical( | |
| 5090 | \\test "if" { | |
| 5091 | \\ if (10 < 0) { | |
| 5092 | \\ unreachable; | |
| 5093 | \\ } | |
| 5094 | \\ | |
| 5095 | \\ if (10 < 0) unreachable; | |
| 5096 | \\ | |
| 5097 | \\ if (10 < 0) { | |
| 5098 | \\ unreachable; | |
| 5099 | \\ } else { | |
| 5100 | \\ const a = 20; | |
| 5101 | \\ } | |
| 5102 | \\ | |
| 5103 | \\ if (10 < 0) { | |
| 5104 | \\ unreachable; | |
| 5105 | \\ } else if (5 < 0) { | |
| 5106 | \\ unreachable; | |
| 5107 | \\ } else { | |
| 5108 | \\ const a = 20; | |
| 5109 | \\ } | |
| 5110 | \\ | |
| 5111 | \\ const is_world_broken = if (10 < 0) true else false; | |
| 5112 | \\ const some_number = 1 + if (10 < 0) 2 else 3; | |
| 5113 | \\ | |
| 5114 | \\ const a: ?u8 = 10; | |
| 5115 | \\ const b: ?u8 = null; | |
| 5116 | \\ if (a) |v| { | |
| 5117 | \\ const some = v; | |
| 5118 | \\ } else if (b) |*v| { | |
| 5119 | \\ unreachable; | |
| 5120 | \\ } else { | |
| 5121 | \\ const some = 10; | |
| 5122 | \\ } | |
| 5123 | \\ | |
| 5124 | \\ const non_null_a = if (a) |v| v else 0; | |
| 5125 | \\ | |
| 5126 | \\ const a_err: error!u8 = 0; | |
| 5127 | \\ if (a_err) |v| { | |
| 5128 | \\ const p = v; | |
| 5129 | \\ } else |err| { | |
| 5130 | \\ unreachable; | |
| 5131 | \\ } | |
| 5132 | \\} | |
| 5133 | \\ | |
| 5134 | ); | |
| 5135 | } | |
| 5136 | ||
| 5137 | test "zig fmt: defer" { | |
| 5138 | try testCanonical( | |
| 5139 | \\test "defer" { | |
| 5140 | \\ var i: usize = 0; | |
| 5141 | \\ defer i = 1; | |
| 5142 | \\ defer { | |
| 5143 | \\ i += 2; | |
| 5144 | \\ i *= i; | |
| 5145 | \\ } | |
| 5146 | \\ | |
| 5147 | \\ errdefer i += 3; | |
| 5148 | \\ errdefer { | |
| 5149 | \\ i += 2; | |
| 5150 | \\ i /= i; | |
| 5151 | \\ } | |
| 5152 | \\} | |
| 5153 | \\ | |
| 5154 | ); | |
| 5155 | } | |
| 5156 | ||
| 5157 | test "zig fmt: comptime" { | |
| 5158 | try testCanonical( | |
| 5159 | \\fn a() u8 { | |
| 5160 | \\ return 5; | |
| 5161 | \\} | |
| 5162 | \\ | |
| 5163 | \\fn b(comptime i: u8) u8 { | |
| 5164 | \\ return i; | |
| 5165 | \\} | |
| 5166 | \\ | |
| 5167 | \\const av = comptime a(); | |
| 5168 | \\const av2 = comptime blk: { | |
| 5169 | \\ var res = a(); | |
| 5170 | \\ res *= b(2); | |
| 5171 | \\ break :blk res; | |
| 5172 | \\}; | |
| 5173 | \\ | |
| 5174 | \\comptime { | |
| 5175 | \\ _ = a(); | |
| 5176 | \\} | |
| 5177 | \\ | |
| 5178 | \\test "comptime" { | |
| 5179 | \\ const av3 = comptime a(); | |
| 5180 | \\ const av4 = comptime blk: { | |
| 5181 | \\ var res = a(); | |
| 5182 | \\ res *= a(); | |
| 5183 | \\ break :blk res; | |
| 5184 | \\ }; | |
| 5185 | \\ | |
| 5186 | \\ comptime var i = 0; | |
| 5187 | \\ comptime { | |
| 5188 | \\ i = a(); | |
| 5189 | \\ i += b(i); | |
| 5190 | \\ } | |
| 5191 | \\} | |
| 5192 | \\ | |
| 5193 | ); | |
| 5194 | } | |
| 5195 | ||
| 5196 | test "zig fmt: fn type" { | |
| 5197 | try testCanonical( | |
| 5198 | \\fn a(i: u8) u8 { | |
| 5199 | \\ return i + 1; | |
| 5200 | \\} | |
| 5201 | \\ | |
| 5202 | \\const a: fn(u8) u8 = undefined; | |
| 5203 | \\const b: extern fn(u8) u8 = undefined; | |
| 5204 | \\const c: nakedcc fn(u8) u8 = undefined; | |
| 5205 | \\const ap: fn(u8) u8 = a; | |
| 5206 | \\ | |
| 5207 | ); | |
| 5208 | } | |
| 5209 | ||
| 5210 | test "zig fmt: inline asm" { | |
| 5211 | try testCanonical( | |
| 5212 | \\pub fn syscall1(number: usize, arg1: usize) usize { | |
| 5213 | \\ return asm volatile ("syscall" | |
| 5214 | \\ : [ret] "={rax}" (-> usize) | |
| 5215 | \\ : [number] "{rax}" (number), | |
| 5216 | \\ [arg1] "{rdi}" (arg1) | |
| 5217 | \\ : "rcx", "r11"); | |
| 5218 | \\} | |
| 5219 | \\ | |
| 5220 | ); | |
| 5221 | } | |
| 5222 | ||
| 5223 | test "zig fmt: coroutines" { | |
| 5224 | try testCanonical( | |
| 5225 | \\async fn simpleAsyncFn() void { | |
| 5226 | \\ const a = async a.b(); | |
| 5227 | \\ x += 1; | |
| 5228 | \\ suspend; | |
| 5229 | \\ x += 1; | |
| 5230 | \\ suspend |p| {} | |
| 5231 | \\ const p = async simpleAsyncFn() catch unreachable; | |
| 5232 | \\ await p; | |
| 5233 | \\} | |
| 5234 | \\ | |
| 5235 | \\test "coroutine suspend, resume, cancel" { | |
| 5236 | \\ const p = try async<std.debug.global_allocator> testAsyncSeq(); | |
| 5237 | \\ resume p; | |
| 5238 | \\ cancel p; | |
| 5239 | \\} | |
| 5240 | \\ | |
| 5241 | ); | |
| 5242 | } | |
| 5243 | ||
| 5244 | test "zig fmt: Block after if" { | |
| 5245 | try testCanonical( | |
| 5246 | \\test "Block after if" { | |
| 5247 | \\ if (true) { | |
| 5248 | \\ const a = 0; | |
| 5249 | \\ } | |
| 5250 | \\ | |
| 5251 | \\ { | |
| 5252 | \\ const a = 0; | |
| 5253 | \\ } | |
| 5254 | \\} | |
| 5255 | \\ | |
| 5256 | ); | |
| 5257 | } | |
| 5258 | ||
| 5259 | test "zig fmt: use" { | |
| 5260 | try testCanonical( | |
| 5261 | \\use @import("std"); | |
| 5262 | \\pub use @import("std"); | |
| 5263 | \\ | |
| 5264 | ); | |
| 5265 | } | |
| 5266 | ||
| 5267 | test "zig fmt: string identifier" { | |
| 5268 | try testCanonical( | |
| 5269 | \\const @"a b" = @"c d".@"e f"; | |
| 5270 | \\fn @"g h"() void {} | |
| 5271 | \\ | |
| 5272 | ); | |
| 5273 | } | |
| 5274 | ||
| 5275 | test "zig fmt: error return" { | |
| 5276 | try testCanonical( | |
| 5277 | \\fn err() error { | |
| 5278 | \\ call(); | |
| 5279 | \\ return error.InvalidArgs; | |
| 5280 | \\} | |
| 5281 | \\ | |
| 5282 | ); | |
| 5283 | } | |
| 5284 | ||
| 5285 | test "zig fmt: struct literals with fields on each line" { | |
| 5286 | try testCanonical( | |
| 5287 | \\var self = BufSet { | |
| 5288 | \\ .hash_map = BufSetHashMap.init(a), | |
| 5289 | \\}; | |
| 5290 | \\ | |
| 5291 | ); | |
| 4702 | test "std.zig.parser" { | |
| 4703 | _ = @import("parser_test.zig"); | |
| 5292 | 4704 | } |
std/zig/parser_test.zig created+1139| ... | ... | @@ -0,0 +1,1139 @@ |
| 1 | test "zig fmt: line comments in struct initializer" { | |
| 2 | try testCanonical( | |
| 3 | \\fn foo() void { | |
| 4 | \\ return Self{ | |
| 5 | \\ .a = b, | |
| 6 | \\ | |
| 7 | \\ // Initialize these two fields to buffer_size so that | |
| 8 | \\ // in `readFn` we treat the state as being able to read | |
| 9 | \\ .start_index = buffer_size, | |
| 10 | \\ .end_index = buffer_size, | |
| 11 | \\ | |
| 12 | \\ // middle | |
| 13 | \\ | |
| 14 | \\ .a = b, | |
| 15 | \\ | |
| 16 | \\ // end | |
| 17 | \\ }; | |
| 18 | \\} | |
| 19 | \\ | |
| 20 | ); | |
| 21 | } | |
| 22 | ||
| 23 | //TODO | |
| 24 | //test "zig fmt: same-line comptime" { | |
| 25 | // try testCanonical( | |
| 26 | // \\test "" { | |
| 27 | // \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt | |
| 28 | // \\} | |
| 29 | // \\ | |
| 30 | // ); | |
| 31 | //} | |
| 32 | ||
| 33 | ||
| 34 | //TODO | |
| 35 | //test "zig fmt: number literals" { | |
| 36 | // try testCanonical( | |
| 37 | // \\pub const f64_true_min = 4.94065645841246544177e-324; | |
| 38 | // \\ | |
| 39 | // ); | |
| 40 | //} | |
| 41 | ||
| 42 | test "zig fmt: doc comments before struct field" { | |
| 43 | try testCanonical( | |
| 44 | \\pub const Allocator = struct { | |
| 45 | \\ /// Allocate byte_count bytes and return them in a slice, with the | |
| 46 | \\ /// slice's pointer aligned at least to alignment bytes. | |
| 47 | \\ allocFn: fn() void, | |
| 48 | \\}; | |
| 49 | \\ | |
| 50 | ); | |
| 51 | } | |
| 52 | ||
| 53 | test "zig fmt: error set declaration" { | |
| 54 | try testCanonical( | |
| 55 | \\const E = error{ | |
| 56 | \\ A, | |
| 57 | \\ B, | |
| 58 | \\ | |
| 59 | \\ C, | |
| 60 | \\}; | |
| 61 | \\ | |
| 62 | \\const Error = error{ | |
| 63 | \\ /// no more memory | |
| 64 | \\ OutOfMemory, | |
| 65 | \\}; | |
| 66 | \\ | |
| 67 | \\const Error = error{ | |
| 68 | \\ /// no more memory | |
| 69 | \\ OutOfMemory, | |
| 70 | \\ | |
| 71 | \\ /// another | |
| 72 | \\ Another, | |
| 73 | \\ | |
| 74 | \\ // end | |
| 75 | \\}; | |
| 76 | \\ | |
| 77 | \\const Error = error{OutOfMemory}; | |
| 78 | \\const Error = error{}; | |
| 79 | \\ | |
| 80 | ); | |
| 81 | } | |
| 82 | ||
| 83 | test "zig fmt: union(enum(u32)) with assigned enum values" { | |
| 84 | try testCanonical( | |
| 85 | \\const MultipleChoice = union(enum(u32)) { | |
| 86 | \\ A = 20, | |
| 87 | \\ B = 40, | |
| 88 | \\ C = 60, | |
| 89 | \\ D = 1000, | |
| 90 | \\}; | |
| 91 | \\ | |
| 92 | ); | |
| 93 | } | |
| 94 | ||
| 95 | test "zig fmt: labeled suspend" { | |
| 96 | try testCanonical( | |
| 97 | \\fn foo() void { | |
| 98 | \\ s: suspend |p| { | |
| 99 | \\ break :s; | |
| 100 | \\ } | |
| 101 | \\} | |
| 102 | \\ | |
| 103 | ); | |
| 104 | } | |
| 105 | ||
| 106 | test "zig fmt: comments before error set decl" { | |
| 107 | try testCanonical( | |
| 108 | \\const UnexpectedError = error{ | |
| 109 | \\ /// The Operating System returned an undocumented error code. | |
| 110 | \\ Unexpected, | |
| 111 | \\ // another | |
| 112 | \\ Another, | |
| 113 | \\ | |
| 114 | \\ // in between | |
| 115 | \\ | |
| 116 | \\ // at end | |
| 117 | \\}; | |
| 118 | \\ | |
| 119 | ); | |
| 120 | } | |
| 121 | ||
| 122 | test "zig fmt: comments before switch prong" { | |
| 123 | try testCanonical( | |
| 124 | \\test "" { | |
| 125 | \\ switch (err) { | |
| 126 | \\ error.PathAlreadyExists => continue, | |
| 127 | \\ | |
| 128 | \\ // comment 1 | |
| 129 | \\ | |
| 130 | \\ // comment 2 | |
| 131 | \\ else => return err, | |
| 132 | \\ // at end | |
| 133 | \\ } | |
| 134 | \\} | |
| 135 | \\ | |
| 136 | ); | |
| 137 | } | |
| 138 | ||
| 139 | test "zig fmt: same-line comment after switch prong" { | |
| 140 | try testCanonical( | |
| 141 | \\test "" { | |
| 142 | \\ switch (err) { | |
| 143 | \\ error.PathAlreadyExists => {}, // comment 2 | |
| 144 | \\ else => return err, // comment 1 | |
| 145 | \\ } | |
| 146 | \\} | |
| 147 | \\ | |
| 148 | ); | |
| 149 | } | |
| 150 | ||
| 151 | test "zig fmt: comments before var decl in struct" { | |
| 152 | try testCanonical( | |
| 153 | \\pub const vfs_cap_data = extern struct { | |
| 154 | \\ // All of these are mandated as little endian | |
| 155 | \\ // when on disk. | |
| 156 | \\ const Data = struct { | |
| 157 | \\ permitted: u32, | |
| 158 | \\ inheritable: u32, | |
| 159 | \\ }; | |
| 160 | \\ | |
| 161 | \\ // in between | |
| 162 | \\ | |
| 163 | \\ /// All of these are mandated as little endian | |
| 164 | \\ /// when on disk. | |
| 165 | \\ const Data = struct { | |
| 166 | \\ permitted: u32, | |
| 167 | \\ inheritable: u32, | |
| 168 | \\ }; | |
| 169 | \\ | |
| 170 | \\ // at end | |
| 171 | \\}; | |
| 172 | \\ | |
| 173 | ); | |
| 174 | } | |
| 175 | ||
| 176 | test "zig fmt: same-line comment after var decl in struct" { | |
| 177 | try testCanonical( | |
| 178 | \\pub const vfs_cap_data = extern struct { | |
| 179 | \\ const Data = struct {}; // when on disk. | |
| 180 | \\}; | |
| 181 | \\ | |
| 182 | ); | |
| 183 | } | |
| 184 | ||
| 185 | test "zig fmt: same-line comment after field decl" { | |
| 186 | try testCanonical( | |
| 187 | \\pub const dirent = extern struct { | |
| 188 | \\ d_name: u8, | |
| 189 | \\ d_name: u8, // comment 1 | |
| 190 | \\ d_name: u8, | |
| 191 | \\ d_name: u8, // comment 2 | |
| 192 | \\ d_name: u8, | |
| 193 | \\}; | |
| 194 | \\ | |
| 195 | ); | |
| 196 | } | |
| 197 | ||
| 198 | test "zig fmt: array literal with 1 item on 1 line" { | |
| 199 | try testCanonical( | |
| 200 | \\var s = []const u64{0} ** 25; | |
| 201 | \\ | |
| 202 | ); | |
| 203 | } | |
| 204 | ||
| 205 | test "zig fmt: same-line comment after a statement" { | |
| 206 | try testCanonical( | |
| 207 | \\test "" { | |
| 208 | \\ a = b; | |
| 209 | \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption | |
| 210 | \\ a = b; | |
| 211 | \\} | |
| 212 | \\ | |
| 213 | ); | |
| 214 | } | |
| 215 | ||
| 216 | test "zig fmt: comments before global variables" { | |
| 217 | try testCanonical( | |
| 218 | \\/// Foo copies keys and values before they go into the map, and | |
| 219 | \\/// frees them when they get removed. | |
| 220 | \\pub const Foo = struct {}; | |
| 221 | \\ | |
| 222 | ); | |
| 223 | } | |
| 224 | ||
| 225 | test "zig fmt: comments in statements" { | |
| 226 | try testCanonical( | |
| 227 | \\test "std" { | |
| 228 | \\ // statement comment | |
| 229 | \\ _ = @import("foo/bar.zig"); | |
| 230 | \\ | |
| 231 | \\ // middle | |
| 232 | \\ // middle2 | |
| 233 | \\ | |
| 234 | \\ // end | |
| 235 | \\} | |
| 236 | \\ | |
| 237 | ); | |
| 238 | } | |
| 239 | ||
| 240 | test "zig fmt: comments before test decl" { | |
| 241 | try testCanonical( | |
| 242 | \\/// top level doc comment | |
| 243 | \\test "hi" {} | |
| 244 | \\ | |
| 245 | \\// top level normal comment | |
| 246 | \\test "hi" {} | |
| 247 | \\ | |
| 248 | \\// middle | |
| 249 | \\ | |
| 250 | \\// end | |
| 251 | \\ | |
| 252 | ); | |
| 253 | } | |
| 254 | ||
| 255 | test "zig fmt: preserve spacing" { | |
| 256 | try testCanonical( | |
| 257 | \\const std = @import("std"); | |
| 258 | \\ | |
| 259 | \\pub fn main() !void { | |
| 260 | \\ var stdout_file = try std.io.getStdOut; | |
| 261 | \\ var stdout_file = try std.io.getStdOut; | |
| 262 | \\ | |
| 263 | \\ var stdout_file = try std.io.getStdOut; | |
| 264 | \\ var stdout_file = try std.io.getStdOut; | |
| 265 | \\} | |
| 266 | \\ | |
| 267 | ); | |
| 268 | } | |
| 269 | ||
| 270 | test "zig fmt: return types" { | |
| 271 | try testCanonical( | |
| 272 | \\pub fn main() !void {} | |
| 273 | \\pub fn main() var {} | |
| 274 | \\pub fn main() i32 {} | |
| 275 | \\ | |
| 276 | ); | |
| 277 | } | |
| 278 | ||
| 279 | test "zig fmt: imports" { | |
| 280 | try testCanonical( | |
| 281 | \\const std = @import("std"); | |
| 282 | \\const std = @import(); | |
| 283 | \\ | |
| 284 | ); | |
| 285 | } | |
| 286 | ||
| 287 | test "zig fmt: global declarations" { | |
| 288 | try testCanonical( | |
| 289 | \\const a = b; | |
| 290 | \\pub const a = b; | |
| 291 | \\var a = b; | |
| 292 | \\pub var a = b; | |
| 293 | \\const a: i32 = b; | |
| 294 | \\pub const a: i32 = b; | |
| 295 | \\var a: i32 = b; | |
| 296 | \\pub var a: i32 = b; | |
| 297 | \\extern const a: i32 = b; | |
| 298 | \\pub extern const a: i32 = b; | |
| 299 | \\extern var a: i32 = b; | |
| 300 | \\pub extern var a: i32 = b; | |
| 301 | \\extern "a" const a: i32 = b; | |
| 302 | \\pub extern "a" const a: i32 = b; | |
| 303 | \\extern "a" var a: i32 = b; | |
| 304 | \\pub extern "a" var a: i32 = b; | |
| 305 | \\ | |
| 306 | ); | |
| 307 | } | |
| 308 | ||
| 309 | test "zig fmt: extern declaration" { | |
| 310 | try testCanonical( | |
| 311 | \\extern var foo: c_int; | |
| 312 | \\ | |
| 313 | ); | |
| 314 | } | |
| 315 | ||
| 316 | test "zig fmt: alignment" { | |
| 317 | try testCanonical( | |
| 318 | \\var foo: c_int align(1); | |
| 319 | \\ | |
| 320 | ); | |
| 321 | } | |
| 322 | ||
| 323 | test "zig fmt: C main" { | |
| 324 | try testCanonical( | |
| 325 | \\fn main(argc: c_int, argv: &&u8) c_int { | |
| 326 | \\ const a = b; | |
| 327 | \\} | |
| 328 | \\ | |
| 329 | ); | |
| 330 | } | |
| 331 | ||
| 332 | test "zig fmt: return" { | |
| 333 | try testCanonical( | |
| 334 | \\fn foo(argc: c_int, argv: &&u8) c_int { | |
| 335 | \\ return 0; | |
| 336 | \\} | |
| 337 | \\ | |
| 338 | \\fn bar() void { | |
| 339 | \\ return; | |
| 340 | \\} | |
| 341 | \\ | |
| 342 | ); | |
| 343 | } | |
| 344 | ||
| 345 | test "zig fmt: pointer attributes" { | |
| 346 | try testCanonical( | |
| 347 | \\extern fn f1(s: &align(&u8) u8) c_int; | |
| 348 | \\extern fn f2(s: &&align(1) &const &volatile u8) c_int; | |
| 349 | \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int; | |
| 350 | \\extern fn f4(s: &align(1) const volatile u8) c_int; | |
| 351 | \\ | |
| 352 | ); | |
| 353 | } | |
| 354 | ||
| 355 | test "zig fmt: slice attributes" { | |
| 356 | try testCanonical( | |
| 357 | \\extern fn f1(s: &align(&u8) u8) c_int; | |
| 358 | \\extern fn f2(s: &&align(1) &const &volatile u8) c_int; | |
| 359 | \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int; | |
| 360 | \\extern fn f4(s: &align(1) const volatile u8) c_int; | |
| 361 | \\ | |
| 362 | ); | |
| 363 | } | |
| 364 | ||
| 365 | test "zig fmt: test declaration" { | |
| 366 | try testCanonical( | |
| 367 | \\test "test name" { | |
| 368 | \\ const a = 1; | |
| 369 | \\ var b = 1; | |
| 370 | \\} | |
| 371 | \\ | |
| 372 | ); | |
| 373 | } | |
| 374 | ||
| 375 | test "zig fmt: infix operators" { | |
| 376 | try testCanonical( | |
| 377 | \\test "infix operators" { | |
| 378 | \\ var i = undefined; | |
| 379 | \\ i = 2; | |
| 380 | \\ i *= 2; | |
| 381 | \\ i |= 2; | |
| 382 | \\ i ^= 2; | |
| 383 | \\ i <<= 2; | |
| 384 | \\ i >>= 2; | |
| 385 | \\ i &= 2; | |
| 386 | \\ i *= 2; | |
| 387 | \\ i *%= 2; | |
| 388 | \\ i -= 2; | |
| 389 | \\ i -%= 2; | |
| 390 | \\ i += 2; | |
| 391 | \\ i +%= 2; | |
| 392 | \\ i /= 2; | |
| 393 | \\ i %= 2; | |
| 394 | \\ _ = i == i; | |
| 395 | \\ _ = i != i; | |
| 396 | \\ _ = i != i; | |
| 397 | \\ _ = i.i; | |
| 398 | \\ _ = i || i; | |
| 399 | \\ _ = i!i; | |
| 400 | \\ _ = i ** i; | |
| 401 | \\ _ = i ++ i; | |
| 402 | \\ _ = i ?? i; | |
| 403 | \\ _ = i % i; | |
| 404 | \\ _ = i / i; | |
| 405 | \\ _ = i *% i; | |
| 406 | \\ _ = i * i; | |
| 407 | \\ _ = i -% i; | |
| 408 | \\ _ = i - i; | |
| 409 | \\ _ = i +% i; | |
| 410 | \\ _ = i + i; | |
| 411 | \\ _ = i << i; | |
| 412 | \\ _ = i >> i; | |
| 413 | \\ _ = i & i; | |
| 414 | \\ _ = i ^ i; | |
| 415 | \\ _ = i | i; | |
| 416 | \\ _ = i >= i; | |
| 417 | \\ _ = i <= i; | |
| 418 | \\ _ = i > i; | |
| 419 | \\ _ = i < i; | |
| 420 | \\ _ = i and i; | |
| 421 | \\ _ = i or i; | |
| 422 | \\} | |
| 423 | \\ | |
| 424 | ); | |
| 425 | } | |
| 426 | ||
| 427 | test "zig fmt: precedence" { | |
| 428 | try testCanonical( | |
| 429 | \\test "precedence" { | |
| 430 | \\ a!b(); | |
| 431 | \\ (a!b)(); | |
| 432 | \\ !a!b; | |
| 433 | \\ !(a!b); | |
| 434 | \\ !a{}; | |
| 435 | \\ !(a{}); | |
| 436 | \\ a + b{}; | |
| 437 | \\ (a + b){}; | |
| 438 | \\ a << b + c; | |
| 439 | \\ (a << b) + c; | |
| 440 | \\ a & b << c; | |
| 441 | \\ (a & b) << c; | |
| 442 | \\ a ^ b & c; | |
| 443 | \\ (a ^ b) & c; | |
| 444 | \\ a | b ^ c; | |
| 445 | \\ (a | b) ^ c; | |
| 446 | \\ a == b | c; | |
| 447 | \\ (a == b) | c; | |
| 448 | \\ a and b == c; | |
| 449 | \\ (a and b) == c; | |
| 450 | \\ a or b and c; | |
| 451 | \\ (a or b) and c; | |
| 452 | \\ (a or b) and c; | |
| 453 | \\} | |
| 454 | \\ | |
| 455 | ); | |
| 456 | } | |
| 457 | ||
| 458 | test "zig fmt: prefix operators" { | |
| 459 | try testCanonical( | |
| 460 | \\test "prefix operators" { | |
| 461 | \\ try return --%~??!*&0; | |
| 462 | \\} | |
| 463 | \\ | |
| 464 | ); | |
| 465 | } | |
| 466 | ||
| 467 | test "zig fmt: call expression" { | |
| 468 | try testCanonical( | |
| 469 | \\test "test calls" { | |
| 470 | \\ a(); | |
| 471 | \\ a(1); | |
| 472 | \\ a(1, 2); | |
| 473 | \\ a(1, 2) + a(1, 2); | |
| 474 | \\} | |
| 475 | \\ | |
| 476 | ); | |
| 477 | } | |
| 478 | ||
| 479 | test "zig fmt: var args" { | |
| 480 | try testCanonical( | |
| 481 | \\fn print(args: ...) void {} | |
| 482 | \\ | |
| 483 | ); | |
| 484 | } | |
| 485 | ||
| 486 | test "zig fmt: var type" { | |
| 487 | try testCanonical( | |
| 488 | \\fn print(args: var) var {} | |
| 489 | \\const Var = var; | |
| 490 | \\const i: var = 0; | |
| 491 | \\ | |
| 492 | ); | |
| 493 | } | |
| 494 | ||
| 495 | test "zig fmt: functions" { | |
| 496 | try testCanonical( | |
| 497 | \\extern fn puts(s: &const u8) c_int; | |
| 498 | \\extern "c" fn puts(s: &const u8) c_int; | |
| 499 | \\export fn puts(s: &const u8) c_int; | |
| 500 | \\inline fn puts(s: &const u8) c_int; | |
| 501 | \\pub extern fn puts(s: &const u8) c_int; | |
| 502 | \\pub extern "c" fn puts(s: &const u8) c_int; | |
| 503 | \\pub export fn puts(s: &const u8) c_int; | |
| 504 | \\pub inline fn puts(s: &const u8) c_int; | |
| 505 | \\pub extern fn puts(s: &const u8) align(2 + 2) c_int; | |
| 506 | \\pub extern "c" fn puts(s: &const u8) align(2 + 2) c_int; | |
| 507 | \\pub export fn puts(s: &const u8) align(2 + 2) c_int; | |
| 508 | \\pub inline fn puts(s: &const u8) align(2 + 2) c_int; | |
| 509 | \\ | |
| 510 | ); | |
| 511 | } | |
| 512 | ||
| 513 | test "zig fmt: multiline string" { | |
| 514 | try testCanonical( | |
| 515 | \\test "" { | |
| 516 | \\ const s1 = | |
| 517 | \\ \\one | |
| 518 | \\ \\two) | |
| 519 | \\ \\three | |
| 520 | \\ ; | |
| 521 | \\ const s2 = | |
| 522 | \\ c\\one | |
| 523 | \\ c\\two) | |
| 524 | \\ c\\three | |
| 525 | \\ ; | |
| 526 | \\} | |
| 527 | \\ | |
| 528 | ); | |
| 529 | } | |
| 530 | ||
| 531 | test "zig fmt: values" { | |
| 532 | try testCanonical( | |
| 533 | \\test "values" { | |
| 534 | \\ 1; | |
| 535 | \\ 1.0; | |
| 536 | \\ "string"; | |
| 537 | \\ c"cstring"; | |
| 538 | \\ 'c'; | |
| 539 | \\ true; | |
| 540 | \\ false; | |
| 541 | \\ null; | |
| 542 | \\ undefined; | |
| 543 | \\ error; | |
| 544 | \\ this; | |
| 545 | \\ unreachable; | |
| 546 | \\} | |
| 547 | \\ | |
| 548 | ); | |
| 549 | } | |
| 550 | ||
| 551 | test "zig fmt: indexing" { | |
| 552 | try testCanonical( | |
| 553 | \\test "test index" { | |
| 554 | \\ a[0]; | |
| 555 | \\ a[0 + 5]; | |
| 556 | \\ a[0..]; | |
| 557 | \\ a[0..5]; | |
| 558 | \\ a[a[0]]; | |
| 559 | \\ a[a[0..]]; | |
| 560 | \\ a[a[0..5]]; | |
| 561 | \\ a[a[0]..]; | |
| 562 | \\ a[a[0..5]..]; | |
| 563 | \\ a[a[0]..a[0]]; | |
| 564 | \\ a[a[0..5]..a[0]]; | |
| 565 | \\ a[a[0..5]..a[0..5]]; | |
| 566 | \\} | |
| 567 | \\ | |
| 568 | ); | |
| 569 | } | |
| 570 | ||
| 571 | test "zig fmt: struct declaration" { | |
| 572 | try testCanonical( | |
| 573 | \\const S = struct { | |
| 574 | \\ const Self = this; | |
| 575 | \\ f1: u8, | |
| 576 | \\ pub f3: u8, | |
| 577 | \\ | |
| 578 | \\ fn method(self: &Self) Self { | |
| 579 | \\ return *self; | |
| 580 | \\ } | |
| 581 | \\ | |
| 582 | \\ f2: u8, | |
| 583 | \\}; | |
| 584 | \\ | |
| 585 | \\const Ps = packed struct { | |
| 586 | \\ a: u8, | |
| 587 | \\ pub b: u8, | |
| 588 | \\ | |
| 589 | \\ c: u8, | |
| 590 | \\}; | |
| 591 | \\ | |
| 592 | \\const Es = extern struct { | |
| 593 | \\ a: u8, | |
| 594 | \\ pub b: u8, | |
| 595 | \\ | |
| 596 | \\ c: u8, | |
| 597 | \\}; | |
| 598 | \\ | |
| 599 | ); | |
| 600 | } | |
| 601 | ||
| 602 | test "zig fmt: enum declaration" { | |
| 603 | try testCanonical( | |
| 604 | \\const E = enum { | |
| 605 | \\ Ok, | |
| 606 | \\ SomethingElse = 0, | |
| 607 | \\}; | |
| 608 | \\ | |
| 609 | \\const E2 = enum(u8) { | |
| 610 | \\ Ok, | |
| 611 | \\ SomethingElse = 255, | |
| 612 | \\ SomethingThird, | |
| 613 | \\}; | |
| 614 | \\ | |
| 615 | \\const Ee = extern enum { | |
| 616 | \\ Ok, | |
| 617 | \\ SomethingElse, | |
| 618 | \\ SomethingThird, | |
| 619 | \\}; | |
| 620 | \\ | |
| 621 | \\const Ep = packed enum { | |
| 622 | \\ Ok, | |
| 623 | \\ SomethingElse, | |
| 624 | \\ SomethingThird, | |
| 625 | \\}; | |
| 626 | \\ | |
| 627 | ); | |
| 628 | } | |
| 629 | ||
| 630 | test "zig fmt: union declaration" { | |
| 631 | try testCanonical( | |
| 632 | \\const U = union { | |
| 633 | \\ Int: u8, | |
| 634 | \\ Float: f32, | |
| 635 | \\ None, | |
| 636 | \\ Bool: bool, | |
| 637 | \\}; | |
| 638 | \\ | |
| 639 | \\const Ue = union(enum) { | |
| 640 | \\ Int: u8, | |
| 641 | \\ Float: f32, | |
| 642 | \\ None, | |
| 643 | \\ Bool: bool, | |
| 644 | \\}; | |
| 645 | \\ | |
| 646 | \\const E = enum { | |
| 647 | \\ Int, | |
| 648 | \\ Float, | |
| 649 | \\ None, | |
| 650 | \\ Bool, | |
| 651 | \\}; | |
| 652 | \\ | |
| 653 | \\const Ue2 = union(E) { | |
| 654 | \\ Int: u8, | |
| 655 | \\ Float: f32, | |
| 656 | \\ None, | |
| 657 | \\ Bool: bool, | |
| 658 | \\}; | |
| 659 | \\ | |
| 660 | \\const Eu = extern union { | |
| 661 | \\ Int: u8, | |
| 662 | \\ Float: f32, | |
| 663 | \\ None, | |
| 664 | \\ Bool: bool, | |
| 665 | \\}; | |
| 666 | \\ | |
| 667 | ); | |
| 668 | } | |
| 669 | ||
| 670 | test "zig fmt: arrays" { | |
| 671 | try testCanonical( | |
| 672 | \\test "test array" { | |
| 673 | \\ const a: [2]u8 = [2]u8{ | |
| 674 | \\ 1, | |
| 675 | \\ 2, | |
| 676 | \\ }; | |
| 677 | \\ const a: [2]u8 = []u8{ | |
| 678 | \\ 1, | |
| 679 | \\ 2, | |
| 680 | \\ }; | |
| 681 | \\ const a: [0]u8 = []u8{}; | |
| 682 | \\} | |
| 683 | \\ | |
| 684 | ); | |
| 685 | } | |
| 686 | ||
| 687 | test "zig fmt: container initializers" { | |
| 688 | try testCanonical( | |
| 689 | \\const a0 = []u8{}; | |
| 690 | \\const a1 = []u8{1}; | |
| 691 | \\const a2 = []u8{ | |
| 692 | \\ 1, | |
| 693 | \\ 2, | |
| 694 | \\ 3, | |
| 695 | \\ 4, | |
| 696 | \\}; | |
| 697 | \\const s0 = S{}; | |
| 698 | \\const s1 = S{ .a = 1 }; | |
| 699 | \\const s2 = S{ | |
| 700 | \\ .a = 1, | |
| 701 | \\ .b = 2, | |
| 702 | \\}; | |
| 703 | \\ | |
| 704 | ); | |
| 705 | } | |
| 706 | ||
| 707 | test "zig fmt: catch" { | |
| 708 | try testCanonical( | |
| 709 | \\test "catch" { | |
| 710 | \\ const a: error!u8 = 0; | |
| 711 | \\ _ = a catch return; | |
| 712 | \\ _ = a catch |err| return; | |
| 713 | \\} | |
| 714 | \\ | |
| 715 | ); | |
| 716 | } | |
| 717 | ||
| 718 | test "zig fmt: blocks" { | |
| 719 | try testCanonical( | |
| 720 | \\test "blocks" { | |
| 721 | \\ { | |
| 722 | \\ const a = 0; | |
| 723 | \\ const b = 0; | |
| 724 | \\ } | |
| 725 | \\ | |
| 726 | \\ blk: { | |
| 727 | \\ const a = 0; | |
| 728 | \\ const b = 0; | |
| 729 | \\ } | |
| 730 | \\ | |
| 731 | \\ const r = blk: { | |
| 732 | \\ const a = 0; | |
| 733 | \\ const b = 0; | |
| 734 | \\ }; | |
| 735 | \\} | |
| 736 | \\ | |
| 737 | ); | |
| 738 | } | |
| 739 | ||
| 740 | test "zig fmt: switch" { | |
| 741 | try testCanonical( | |
| 742 | \\test "switch" { | |
| 743 | \\ switch (0) { | |
| 744 | \\ 0 => {}, | |
| 745 | \\ 1 => unreachable, | |
| 746 | \\ 2, | |
| 747 | \\ 3 => {}, | |
| 748 | \\ 4 ... 7 => {}, | |
| 749 | \\ 1 + 4 * 3 + 22 => {}, | |
| 750 | \\ else => { | |
| 751 | \\ const a = 1; | |
| 752 | \\ const b = a; | |
| 753 | \\ }, | |
| 754 | \\ } | |
| 755 | \\ | |
| 756 | \\ const res = switch (0) { | |
| 757 | \\ 0 => 0, | |
| 758 | \\ 1 => 2, | |
| 759 | \\ 1 => a = 4, | |
| 760 | \\ else => 4, | |
| 761 | \\ }; | |
| 762 | \\ | |
| 763 | \\ const Union = union(enum) { | |
| 764 | \\ Int: i64, | |
| 765 | \\ Float: f64, | |
| 766 | \\ }; | |
| 767 | \\ | |
| 768 | \\ switch (u) { | |
| 769 | \\ Union.Int => |int| {}, | |
| 770 | \\ Union.Float => |*float| unreachable, | |
| 771 | \\ } | |
| 772 | \\} | |
| 773 | \\ | |
| 774 | ); | |
| 775 | } | |
| 776 | ||
| 777 | test "zig fmt: while" { | |
| 778 | try testCanonical( | |
| 779 | \\test "while" { | |
| 780 | \\ while (10 < 1) { | |
| 781 | \\ unreachable; | |
| 782 | \\ } | |
| 783 | \\ | |
| 784 | \\ while (10 < 1) | |
| 785 | \\ unreachable; | |
| 786 | \\ | |
| 787 | \\ var i: usize = 0; | |
| 788 | \\ while (i < 10) : (i += 1) { | |
| 789 | \\ continue; | |
| 790 | \\ } | |
| 791 | \\ | |
| 792 | \\ i = 0; | |
| 793 | \\ while (i < 10) : (i += 1) | |
| 794 | \\ continue; | |
| 795 | \\ | |
| 796 | \\ i = 0; | |
| 797 | \\ var j: usize = 0; | |
| 798 | \\ while (i < 10) : ({ | |
| 799 | \\ i += 1; | |
| 800 | \\ j += 1; | |
| 801 | \\ }) { | |
| 802 | \\ continue; | |
| 803 | \\ } | |
| 804 | \\ | |
| 805 | \\ var a: ?u8 = 2; | |
| 806 | \\ while (a) |v| : (a = null) { | |
| 807 | \\ continue; | |
| 808 | \\ } | |
| 809 | \\ | |
| 810 | \\ while (a) |v| : (a = null) | |
| 811 | \\ unreachable; | |
| 812 | \\ | |
| 813 | \\ label: while (10 < 0) { | |
| 814 | \\ unreachable; | |
| 815 | \\ } | |
| 816 | \\ | |
| 817 | \\ const res = while (0 < 10) { | |
| 818 | \\ break 7; | |
| 819 | \\ } else { | |
| 820 | \\ unreachable; | |
| 821 | \\ }; | |
| 822 | \\ | |
| 823 | \\ const res = while (0 < 10) | |
| 824 | \\ break 7 | |
| 825 | \\ else | |
| 826 | \\ unreachable; | |
| 827 | \\ | |
| 828 | \\ var a: error!u8 = 0; | |
| 829 | \\ while (a) |v| { | |
| 830 | \\ a = error.Err; | |
| 831 | \\ } else |err| { | |
| 832 | \\ i = 1; | |
| 833 | \\ } | |
| 834 | \\ | |
| 835 | \\ comptime var k: usize = 0; | |
| 836 | \\ inline while (i < 10) : (i += 1) | |
| 837 | \\ j += 2; | |
| 838 | \\} | |
| 839 | \\ | |
| 840 | ); | |
| 841 | } | |
| 842 | ||
| 843 | test "zig fmt: for" { | |
| 844 | try testCanonical( | |
| 845 | \\test "for" { | |
| 846 | \\ for (a) |v| { | |
| 847 | \\ continue; | |
| 848 | \\ } | |
| 849 | \\ | |
| 850 | \\ for (a) |v| | |
| 851 | \\ continue; | |
| 852 | \\ | |
| 853 | \\ for (a) |*v| | |
| 854 | \\ continue; | |
| 855 | \\ | |
| 856 | \\ for (a) |v, i| { | |
| 857 | \\ continue; | |
| 858 | \\ } | |
| 859 | \\ | |
| 860 | \\ for (a) |v, i| | |
| 861 | \\ continue; | |
| 862 | \\ | |
| 863 | \\ const res = for (a) |v, i| { | |
| 864 | \\ break v; | |
| 865 | \\ } else { | |
| 866 | \\ unreachable; | |
| 867 | \\ }; | |
| 868 | \\ | |
| 869 | \\ var num: usize = 0; | |
| 870 | \\ inline for (a) |v, i| { | |
| 871 | \\ num += v; | |
| 872 | \\ num += i; | |
| 873 | \\ } | |
| 874 | \\} | |
| 875 | \\ | |
| 876 | ); | |
| 877 | } | |
| 878 | ||
| 879 | test "zig fmt: if" { | |
| 880 | try testCanonical( | |
| 881 | \\test "if" { | |
| 882 | \\ if (10 < 0) { | |
| 883 | \\ unreachable; | |
| 884 | \\ } | |
| 885 | \\ | |
| 886 | \\ if (10 < 0) unreachable; | |
| 887 | \\ | |
| 888 | \\ if (10 < 0) { | |
| 889 | \\ unreachable; | |
| 890 | \\ } else { | |
| 891 | \\ const a = 20; | |
| 892 | \\ } | |
| 893 | \\ | |
| 894 | \\ if (10 < 0) { | |
| 895 | \\ unreachable; | |
| 896 | \\ } else if (5 < 0) { | |
| 897 | \\ unreachable; | |
| 898 | \\ } else { | |
| 899 | \\ const a = 20; | |
| 900 | \\ } | |
| 901 | \\ | |
| 902 | \\ const is_world_broken = if (10 < 0) true else false; | |
| 903 | \\ const some_number = 1 + if (10 < 0) 2 else 3; | |
| 904 | \\ | |
| 905 | \\ const a: ?u8 = 10; | |
| 906 | \\ const b: ?u8 = null; | |
| 907 | \\ if (a) |v| { | |
| 908 | \\ const some = v; | |
| 909 | \\ } else if (b) |*v| { | |
| 910 | \\ unreachable; | |
| 911 | \\ } else { | |
| 912 | \\ const some = 10; | |
| 913 | \\ } | |
| 914 | \\ | |
| 915 | \\ const non_null_a = if (a) |v| v else 0; | |
| 916 | \\ | |
| 917 | \\ const a_err: error!u8 = 0; | |
| 918 | \\ if (a_err) |v| { | |
| 919 | \\ const p = v; | |
| 920 | \\ } else |err| { | |
| 921 | \\ unreachable; | |
| 922 | \\ } | |
| 923 | \\} | |
| 924 | \\ | |
| 925 | ); | |
| 926 | } | |
| 927 | ||
| 928 | test "zig fmt: defer" { | |
| 929 | try testCanonical( | |
| 930 | \\test "defer" { | |
| 931 | \\ var i: usize = 0; | |
| 932 | \\ defer i = 1; | |
| 933 | \\ defer { | |
| 934 | \\ i += 2; | |
| 935 | \\ i *= i; | |
| 936 | \\ } | |
| 937 | \\ | |
| 938 | \\ errdefer i += 3; | |
| 939 | \\ errdefer { | |
| 940 | \\ i += 2; | |
| 941 | \\ i /= i; | |
| 942 | \\ } | |
| 943 | \\} | |
| 944 | \\ | |
| 945 | ); | |
| 946 | } | |
| 947 | ||
| 948 | test "zig fmt: comptime" { | |
| 949 | try testCanonical( | |
| 950 | \\fn a() u8 { | |
| 951 | \\ return 5; | |
| 952 | \\} | |
| 953 | \\ | |
| 954 | \\fn b(comptime i: u8) u8 { | |
| 955 | \\ return i; | |
| 956 | \\} | |
| 957 | \\ | |
| 958 | \\const av = comptime a(); | |
| 959 | \\const av2 = comptime blk: { | |
| 960 | \\ var res = a(); | |
| 961 | \\ res *= b(2); | |
| 962 | \\ break :blk res; | |
| 963 | \\}; | |
| 964 | \\ | |
| 965 | \\comptime { | |
| 966 | \\ _ = a(); | |
| 967 | \\} | |
| 968 | \\ | |
| 969 | \\test "comptime" { | |
| 970 | \\ const av3 = comptime a(); | |
| 971 | \\ const av4 = comptime blk: { | |
| 972 | \\ var res = a(); | |
| 973 | \\ res *= a(); | |
| 974 | \\ break :blk res; | |
| 975 | \\ }; | |
| 976 | \\ | |
| 977 | \\ comptime var i = 0; | |
| 978 | \\ comptime { | |
| 979 | \\ i = a(); | |
| 980 | \\ i += b(i); | |
| 981 | \\ } | |
| 982 | \\} | |
| 983 | \\ | |
| 984 | ); | |
| 985 | } | |
| 986 | ||
| 987 | test "zig fmt: fn type" { | |
| 988 | try testCanonical( | |
| 989 | \\fn a(i: u8) u8 { | |
| 990 | \\ return i + 1; | |
| 991 | \\} | |
| 992 | \\ | |
| 993 | \\const a: fn(u8) u8 = undefined; | |
| 994 | \\const b: extern fn(u8) u8 = undefined; | |
| 995 | \\const c: nakedcc fn(u8) u8 = undefined; | |
| 996 | \\const ap: fn(u8) u8 = a; | |
| 997 | \\ | |
| 998 | ); | |
| 999 | } | |
| 1000 | ||
| 1001 | test "zig fmt: inline asm" { | |
| 1002 | try testCanonical( | |
| 1003 | \\pub fn syscall1(number: usize, arg1: usize) usize { | |
| 1004 | \\ return asm volatile ("syscall" | |
| 1005 | \\ : [ret] "={rax}" (-> usize) | |
| 1006 | \\ : [number] "{rax}" (number), | |
| 1007 | \\ [arg1] "{rdi}" (arg1) | |
| 1008 | \\ : "rcx", "r11"); | |
| 1009 | \\} | |
| 1010 | \\ | |
| 1011 | ); | |
| 1012 | } | |
| 1013 | ||
| 1014 | test "zig fmt: coroutines" { | |
| 1015 | try testCanonical( | |
| 1016 | \\async fn simpleAsyncFn() void { | |
| 1017 | \\ const a = async a.b(); | |
| 1018 | \\ x += 1; | |
| 1019 | \\ suspend; | |
| 1020 | \\ x += 1; | |
| 1021 | \\ suspend |p| {} | |
| 1022 | \\ const p: promise->void = async simpleAsyncFn() catch unreachable; | |
| 1023 | \\ await p; | |
| 1024 | \\} | |
| 1025 | \\ | |
| 1026 | \\test "coroutine suspend, resume, cancel" { | |
| 1027 | \\ const p: promise = try async<std.debug.global_allocator> testAsyncSeq(); | |
| 1028 | \\ resume p; | |
| 1029 | \\ cancel p; | |
| 1030 | \\} | |
| 1031 | \\ | |
| 1032 | ); | |
| 1033 | } | |
| 1034 | ||
| 1035 | test "zig fmt: Block after if" { | |
| 1036 | try testCanonical( | |
| 1037 | \\test "Block after if" { | |
| 1038 | \\ if (true) { | |
| 1039 | \\ const a = 0; | |
| 1040 | \\ } | |
| 1041 | \\ | |
| 1042 | \\ { | |
| 1043 | \\ const a = 0; | |
| 1044 | \\ } | |
| 1045 | \\} | |
| 1046 | \\ | |
| 1047 | ); | |
| 1048 | } | |
| 1049 | ||
| 1050 | test "zig fmt: use" { | |
| 1051 | try testCanonical( | |
| 1052 | \\use @import("std"); | |
| 1053 | \\pub use @import("std"); | |
| 1054 | \\ | |
| 1055 | ); | |
| 1056 | } | |
| 1057 | ||
| 1058 | test "zig fmt: string identifier" { | |
| 1059 | try testCanonical( | |
| 1060 | \\const @"a b" = @"c d".@"e f"; | |
| 1061 | \\fn @"g h"() void {} | |
| 1062 | \\ | |
| 1063 | ); | |
| 1064 | } | |
| 1065 | ||
| 1066 | test "zig fmt: error return" { | |
| 1067 | try testCanonical( | |
| 1068 | \\fn err() error { | |
| 1069 | \\ call(); | |
| 1070 | \\ return error.InvalidArgs; | |
| 1071 | \\} | |
| 1072 | \\ | |
| 1073 | ); | |
| 1074 | } | |
| 1075 | ||
| 1076 | const std = @import("std"); | |
| 1077 | const mem = std.mem; | |
| 1078 | const warn = std.debug.warn; | |
| 1079 | const Tokenizer = std.zig.Tokenizer; | |
| 1080 | const Parser = std.zig.Parser; | |
| 1081 | const io = std.io; | |
| 1082 | ||
| 1083 | var fixed_buffer_mem: [100 * 1024]u8 = undefined; | |
| 1084 | ||
| 1085 | fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 { | |
| 1086 | var tokenizer = Tokenizer.init(source); | |
| 1087 | var parser = Parser.init(&tokenizer, allocator, "(memory buffer)"); | |
| 1088 | defer parser.deinit(); | |
| 1089 | ||
| 1090 | var tree = try parser.parse(); | |
| 1091 | defer tree.deinit(); | |
| 1092 | ||
| 1093 | var buffer = try std.Buffer.initSize(allocator, 0); | |
| 1094 | errdefer buffer.deinit(); | |
| 1095 | ||
| 1096 | var buffer_out_stream = io.BufferOutStream.init(&buffer); | |
| 1097 | try parser.renderSource(&buffer_out_stream.stream, tree.root_node); | |
| 1098 | return buffer.toOwnedSlice(); | |
| 1099 | } | |
| 1100 | ||
| 1101 | fn testCanonical(source: []const u8) !void { | |
| 1102 | const needed_alloc_count = x: { | |
| 1103 | // Try it once with unlimited memory, make sure it works | |
| 1104 | var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | |
| 1105 | var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize)); | |
| 1106 | const result_source = try testParse(source, &failing_allocator.allocator); | |
| 1107 | if (!mem.eql(u8, result_source, source)) { | |
| 1108 | warn("\n====== expected this output: =========\n"); | |
| 1109 | warn("{}", source); | |
| 1110 | warn("\n======== instead found this: =========\n"); | |
| 1111 | warn("{}", result_source); | |
| 1112 | warn("\n======================================\n"); | |
| 1113 | return error.TestFailed; | |
| 1114 | } | |
| 1115 | failing_allocator.allocator.free(result_source); | |
| 1116 | break :x failing_allocator.index; | |
| 1117 | }; | |
| 1118 | ||
| 1119 | var fail_index: usize = 0; | |
| 1120 | while (fail_index < needed_alloc_count) : (fail_index += 1) { | |
| 1121 | var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | |
| 1122 | var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index); | |
| 1123 | if (testParse(source, &failing_allocator.allocator)) |_| { | |
| 1124 | return error.NondeterministicMemoryUsage; | |
| 1125 | } else |err| switch (err) { | |
| 1126 | error.OutOfMemory => { | |
| 1127 | if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) { | |
| 1128 | warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n", | |
| 1129 | fail_index, needed_alloc_count, | |
| 1130 | failing_allocator.allocated_bytes, failing_allocator.freed_bytes, | |
| 1131 | failing_allocator.index, failing_allocator.deallocations); | |
| 1132 | return error.MemoryLeakDetected; | |
| 1133 | } | |
| 1134 | }, | |
| 1135 | error.ParseError => @panic("test failed"), | |
| 1136 | } | |
| 1137 | } | |
| 1138 | } | |
| 1139 |
std/zig/tokenizer.zig+116-21| ... | ... | @@ -40,6 +40,7 @@ pub const Token = struct { |
| 40 | 40 | KeywordId{.bytes="null", .id = Id.Keyword_null}, |
| 41 | 41 | KeywordId{.bytes="or", .id = Id.Keyword_or}, |
| 42 | 42 | KeywordId{.bytes="packed", .id = Id.Keyword_packed}, |
| 43 | KeywordId{.bytes="promise", .id = Id.Keyword_promise}, | |
| 43 | 44 | KeywordId{.bytes="pub", .id = Id.Keyword_pub}, |
| 44 | 45 | KeywordId{.bytes="resume", .id = Id.Keyword_resume}, |
| 45 | 46 | KeywordId{.bytes="return", .id = Id.Keyword_return}, |
| ... | ... | @@ -137,6 +138,7 @@ pub const Token = struct { |
| 137 | 138 | IntegerLiteral, |
| 138 | 139 | FloatLiteral, |
| 139 | 140 | LineComment, |
| 141 | DocComment, | |
| 140 | 142 | Keyword_align, |
| 141 | 143 | Keyword_and, |
| 142 | 144 | Keyword_asm, |
| ... | ... | @@ -165,6 +167,7 @@ pub const Token = struct { |
| 165 | 167 | Keyword_null, |
| 166 | 168 | Keyword_or, |
| 167 | 169 | Keyword_packed, |
| 170 | Keyword_promise, | |
| 168 | 171 | Keyword_pub, |
| 169 | 172 | Keyword_resume, |
| 170 | 173 | Keyword_return, |
| ... | ... | @@ -257,7 +260,10 @@ pub const Tokenizer = struct { |
| 257 | 260 | Asterisk, |
| 258 | 261 | AsteriskPercent, |
| 259 | 262 | Slash, |
| 263 | LineCommentStart, | |
| 260 | 264 | LineComment, |
| 265 | DocCommentStart, | |
| 266 | DocComment, | |
| 261 | 267 | Zero, |
| 262 | 268 | IntegerLiteral, |
| 263 | 269 | IntegerLiteralWithRadix, |
| ... | ... | @@ -822,8 +828,8 @@ pub const Tokenizer = struct { |
| 822 | 828 | |
| 823 | 829 | State.Slash => switch (c) { |
| 824 | 830 | '/' => { |
| 831 | state = State.LineCommentStart; | |
| 825 | 832 | result.id = Token.Id.LineComment; |
| 826 | state = State.LineComment; | |
| 827 | 833 | }, |
| 828 | 834 | '=' => { |
| 829 | 835 | result.id = Token.Id.SlashEqual; |
| ... | ... | @@ -835,7 +841,31 @@ pub const Tokenizer = struct { |
| 835 | 841 | break; |
| 836 | 842 | }, |
| 837 | 843 | }, |
| 838 | State.LineComment => switch (c) { | |
| 844 | State.LineCommentStart => switch (c) { | |
| 845 | '/' => { | |
| 846 | state = State.DocCommentStart; | |
| 847 | }, | |
| 848 | '\n' => break, | |
| 849 | else => { | |
| 850 | state = State.LineComment; | |
| 851 | self.checkLiteralCharacter(); | |
| 852 | }, | |
| 853 | }, | |
| 854 | State.DocCommentStart => switch (c) { | |
| 855 | '/' => { | |
| 856 | state = State.LineComment; | |
| 857 | }, | |
| 858 | '\n' => { | |
| 859 | result.id = Token.Id.DocComment; | |
| 860 | break; | |
| 861 | }, | |
| 862 | else => { | |
| 863 | state = State.DocComment; | |
| 864 | result.id = Token.Id.DocComment; | |
| 865 | self.checkLiteralCharacter(); | |
| 866 | }, | |
| 867 | }, | |
| 868 | State.LineComment, State.DocComment => switch (c) { | |
| 839 | 869 | '\n' => break, |
| 840 | 870 | else => self.checkLiteralCharacter(), |
| 841 | 871 | }, |
| ... | ... | @@ -920,8 +950,12 @@ pub const Tokenizer = struct { |
| 920 | 950 | result.id = id; |
| 921 | 951 | } |
| 922 | 952 | }, |
| 953 | State.LineCommentStart, | |
| 923 | 954 | State.LineComment => { |
| 924 | result.id = Token.Id.Eof; | |
| 955 | result.id = Token.Id.LineComment; | |
| 956 | }, | |
| 957 | State.DocComment, State.DocCommentStart => { | |
| 958 | result.id = Token.Id.DocComment; | |
| 925 | 959 | }, |
| 926 | 960 | |
| 927 | 961 | State.NumberDot, |
| ... | ... | @@ -1092,41 +1126,77 @@ test "tokenizer - invalid literal/comment characters" { |
| 1092 | 1126 | Token.Id.Invalid, |
| 1093 | 1127 | }); |
| 1094 | 1128 | testTokenize("//\x00", []Token.Id { |
| 1129 | Token.Id.LineComment, | |
| 1095 | 1130 | Token.Id.Invalid, |
| 1096 | 1131 | }); |
| 1097 | 1132 | testTokenize("//\x1f", []Token.Id { |
| 1133 | Token.Id.LineComment, | |
| 1098 | 1134 | Token.Id.Invalid, |
| 1099 | 1135 | }); |
| 1100 | 1136 | testTokenize("//\x7f", []Token.Id { |
| 1137 | Token.Id.LineComment, | |
| 1101 | 1138 | Token.Id.Invalid, |
| 1102 | 1139 | }); |
| 1103 | 1140 | } |
| 1104 | 1141 | |
| 1105 | 1142 | test "tokenizer - utf8" { |
| 1106 | testTokenize("//\xc2\x80", []Token.Id{}); | |
| 1107 | testTokenize("//\xf4\x8f\xbf\xbf", []Token.Id{}); | |
| 1143 | testTokenize("//\xc2\x80", []Token.Id{Token.Id.LineComment}); | |
| 1144 | testTokenize("//\xf4\x8f\xbf\xbf", []Token.Id{Token.Id.LineComment}); | |
| 1108 | 1145 | } |
| 1109 | 1146 | |
| 1110 | 1147 | test "tokenizer - invalid utf8" { |
| 1111 | testTokenize("//\x80", []Token.Id{Token.Id.Invalid}); | |
| 1112 | testTokenize("//\xbf", []Token.Id{Token.Id.Invalid}); | |
| 1113 | testTokenize("//\xf8", []Token.Id{Token.Id.Invalid}); | |
| 1114 | testTokenize("//\xff", []Token.Id{Token.Id.Invalid}); | |
| 1115 | testTokenize("//\xc2\xc0", []Token.Id{Token.Id.Invalid}); | |
| 1116 | testTokenize("//\xe0", []Token.Id{Token.Id.Invalid}); | |
| 1117 | testTokenize("//\xf0", []Token.Id{Token.Id.Invalid}); | |
| 1118 | testTokenize("//\xf0\x90\x80\xc0", []Token.Id{Token.Id.Invalid}); | |
| 1148 | testTokenize("//\x80", []Token.Id{ | |
| 1149 | Token.Id.LineComment, | |
| 1150 | Token.Id.Invalid, | |
| 1151 | }); | |
| 1152 | testTokenize("//\xbf", []Token.Id{ | |
| 1153 | Token.Id.LineComment, | |
| 1154 | Token.Id.Invalid, | |
| 1155 | }); | |
| 1156 | testTokenize("//\xf8", []Token.Id{ | |
| 1157 | Token.Id.LineComment, | |
| 1158 | Token.Id.Invalid, | |
| 1159 | }); | |
| 1160 | testTokenize("//\xff", []Token.Id{ | |
| 1161 | Token.Id.LineComment, | |
| 1162 | Token.Id.Invalid, | |
| 1163 | }); | |
| 1164 | testTokenize("//\xc2\xc0", []Token.Id{ | |
| 1165 | Token.Id.LineComment, | |
| 1166 | Token.Id.Invalid, | |
| 1167 | }); | |
| 1168 | testTokenize("//\xe0", []Token.Id{ | |
| 1169 | Token.Id.LineComment, | |
| 1170 | Token.Id.Invalid, | |
| 1171 | }); | |
| 1172 | testTokenize("//\xf0", []Token.Id{ | |
| 1173 | Token.Id.LineComment, | |
| 1174 | Token.Id.Invalid, | |
| 1175 | }); | |
| 1176 | testTokenize("//\xf0\x90\x80\xc0", []Token.Id{ | |
| 1177 | Token.Id.LineComment, | |
| 1178 | Token.Id.Invalid, | |
| 1179 | }); | |
| 1119 | 1180 | } |
| 1120 | 1181 | |
| 1121 | 1182 | test "tokenizer - illegal unicode codepoints" { |
| 1122 | 1183 | // unicode newline characters.U+0085, U+2028, U+2029 |
| 1123 | testTokenize("//\xc2\x84", []Token.Id{}); | |
| 1124 | testTokenize("//\xc2\x85", []Token.Id{Token.Id.Invalid}); | |
| 1125 | testTokenize("//\xc2\x86", []Token.Id{}); | |
| 1126 | testTokenize("//\xe2\x80\xa7", []Token.Id{}); | |
| 1127 | testTokenize("//\xe2\x80\xa8", []Token.Id{Token.Id.Invalid}); | |
| 1128 | testTokenize("//\xe2\x80\xa9", []Token.Id{Token.Id.Invalid}); | |
| 1129 | testTokenize("//\xe2\x80\xaa", []Token.Id{}); | |
| 1184 | testTokenize("//\xc2\x84", []Token.Id{Token.Id.LineComment}); | |
| 1185 | testTokenize("//\xc2\x85", []Token.Id{ | |
| 1186 | Token.Id.LineComment, | |
| 1187 | Token.Id.Invalid, | |
| 1188 | }); | |
| 1189 | testTokenize("//\xc2\x86", []Token.Id{Token.Id.LineComment}); | |
| 1190 | testTokenize("//\xe2\x80\xa7", []Token.Id{Token.Id.LineComment}); | |
| 1191 | testTokenize("//\xe2\x80\xa8", []Token.Id{ | |
| 1192 | Token.Id.LineComment, | |
| 1193 | Token.Id.Invalid, | |
| 1194 | }); | |
| 1195 | testTokenize("//\xe2\x80\xa9", []Token.Id{ | |
| 1196 | Token.Id.LineComment, | |
| 1197 | Token.Id.Invalid, | |
| 1198 | }); | |
| 1199 | testTokenize("//\xe2\x80\xaa", []Token.Id{Token.Id.LineComment}); | |
| 1130 | 1200 | } |
| 1131 | 1201 | |
| 1132 | 1202 | test "tokenizer - string identifier and builtin fns" { |
| ... | ... | @@ -1153,11 +1223,36 @@ test "tokenizer - pipe and then invalid" { |
| 1153 | 1223 | }); |
| 1154 | 1224 | } |
| 1155 | 1225 | |
| 1226 | test "tokenizer - line comment and doc comment" { | |
| 1227 | testTokenize("//", []Token.Id{Token.Id.LineComment}); | |
| 1228 | testTokenize("// a / b", []Token.Id{Token.Id.LineComment}); | |
| 1229 | testTokenize("// /", []Token.Id{Token.Id.LineComment}); | |
| 1230 | testTokenize("/// a", []Token.Id{Token.Id.DocComment}); | |
| 1231 | testTokenize("///", []Token.Id{Token.Id.DocComment}); | |
| 1232 | testTokenize("////", []Token.Id{Token.Id.LineComment}); | |
| 1233 | } | |
| 1234 | ||
| 1235 | test "tokenizer - line comment followed by identifier" { | |
| 1236 | testTokenize( | |
| 1237 | \\ Unexpected, | |
| 1238 | \\ // another | |
| 1239 | \\ Another, | |
| 1240 | , []Token.Id{ | |
| 1241 | Token.Id.Identifier, | |
| 1242 | Token.Id.Comma, | |
| 1243 | Token.Id.LineComment, | |
| 1244 | Token.Id.Identifier, | |
| 1245 | Token.Id.Comma, | |
| 1246 | }); | |
| 1247 | } | |
| 1248 | ||
| 1156 | 1249 | fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void { |
| 1157 | 1250 | var tokenizer = Tokenizer.init(source); |
| 1158 | 1251 | for (expected_tokens) |expected_token_id| { |
| 1159 | 1252 | const token = tokenizer.next(); |
| 1160 | std.debug.assert(@TagType(Token.Id)(token.id) == @TagType(Token.Id)(expected_token_id)); | |
| 1253 | if (@TagType(Token.Id)(token.id) != @TagType(Token.Id)(expected_token_id)) { | |
| 1254 | std.debug.panic("expected {}, found {}\n", @tagName(@TagType(Token.Id)(expected_token_id)), @tagName(@TagType(Token.Id)(token.id))); | |
| 1255 | } | |
| 1161 | 1256 | switch (expected_token_id) { |
| 1162 | 1257 | Token.Id.StringLiteral => |expected_kind| { |
| 1163 | 1258 | std.debug.assert(expected_kind == switch (token.id) { Token.Id.StringLiteral => |kind| kind, else => unreachable }); |
test/cases/error.zig+66| ... | ... | @@ -175,3 +175,69 @@ fn baz_1() !i32 { |
| 175 | 175 | fn quux_1() !i32 { |
| 176 | 176 | return error.C; |
| 177 | 177 | } |
| 178 | ||
| 179 | ||
| 180 | test "error: fn returning empty error set can be passed as fn returning any error" { | |
| 181 | entry(); | |
| 182 | comptime entry(); | |
| 183 | } | |
| 184 | ||
| 185 | fn entry() void { | |
| 186 | foo2(bar2); | |
| 187 | } | |
| 188 | ||
| 189 | fn foo2(f: fn()error!void) void { | |
| 190 | const x = f(); | |
| 191 | } | |
| 192 | ||
| 193 | fn bar2() (error{}!void) { } | |
| 194 | ||
| 195 | ||
| 196 | test "error: Zero sized error set returned with value payload crash" { | |
| 197 | _ = foo3(0); | |
| 198 | _ = comptime foo3(0); | |
| 199 | } | |
| 200 | ||
| 201 | const Error = error{}; | |
| 202 | fn foo3(b: usize) Error!usize { | |
| 203 | return b; | |
| 204 | } | |
| 205 | ||
| 206 | ||
| 207 | test "error: Infer error set from literals" { | |
| 208 | _ = nullLiteral("n") catch |err| handleErrors(err); | |
| 209 | _ = floatLiteral("n") catch |err| handleErrors(err); | |
| 210 | _ = intLiteral("n") catch |err| handleErrors(err); | |
| 211 | _ = comptime nullLiteral("n") catch |err| handleErrors(err); | |
| 212 | _ = comptime floatLiteral("n") catch |err| handleErrors(err); | |
| 213 | _ = comptime intLiteral("n") catch |err| handleErrors(err); | |
| 214 | } | |
| 215 | ||
| 216 | fn handleErrors(err: var) noreturn { | |
| 217 | switch (err) { | |
| 218 | error.T => {} | |
| 219 | } | |
| 220 | ||
| 221 | unreachable; | |
| 222 | } | |
| 223 | ||
| 224 | fn nullLiteral(str: []const u8) !?i64 { | |
| 225 | if (str[0] == 'n') | |
| 226 | return null; | |
| 227 | ||
| 228 | return error.T; | |
| 229 | } | |
| 230 | ||
| 231 | fn floatLiteral(str: []const u8) !?f64 { | |
| 232 | if (str[0] == 'n') | |
| 233 | return 1.0; | |
| 234 | ||
| 235 | return error.T; | |
| 236 | } | |
| 237 | ||
| 238 | fn intLiteral(str: []const u8) !?i64 { | |
| 239 | if (str[0] == 'n') | |
| 240 | return 1; | |
| 241 | ||
| 242 | return error.T; | |
| 243 | } |
test/cases/eval.zig+7| ... | ... | @@ -529,3 +529,10 @@ test "comptime shlWithOverflow" { |
| 529 | 529 | |
| 530 | 530 | assert(ct_shifted == rt_shifted); |
| 531 | 531 | } |
| 532 | ||
| 533 | test "runtime 128 bit integer division" { | |
| 534 | var a: u128 = 152313999999999991610955792383; | |
| 535 | var b: u128 = 10000000000000000000; | |
| 536 | var c = a / b; | |
| 537 | assert(c == 15231399999); | |
| 538 | } |
test/compile_errors.zig+17| ... | ... | @@ -3209,4 +3209,21 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { |
| 3209 | 3209 | \\} |
| 3210 | 3210 | , |
| 3211 | 3211 | ".tmp_source.zig:5:42: error: zero-bit field 'val' in struct 'Empty' has no offset"); |
| 3212 | ||
| 3213 | cases.add("getting return type of generic function", | |
| 3214 | \\fn generic(a: var) void {} | |
| 3215 | \\comptime { | |
| 3216 | \\ _ = @typeOf(generic).ReturnType; | |
| 3217 | \\} | |
| 3218 | , | |
| 3219 | ".tmp_source.zig:3:25: error: ReturnType has not been resolved because 'fn(var)var' is generic"); | |
| 3220 | ||
| 3221 | cases.add("getting @ArgType of generic function", | |
| 3222 | \\fn generic(a: var) void {} | |
| 3223 | \\comptime { | |
| 3224 | \\ _ = @ArgType(@typeOf(generic), 0); | |
| 3225 | \\} | |
| 3226 | , | |
| 3227 | ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic"); | |
| 3228 | ||
| 3212 | 3229 | } |