| author | |
| committer | |
| log | 9cfd7dea19c4c54e2754119c04c06cd57cfb759d |
| tree | 3b7950d86f77467b43aa323f80563f678cf6d2fb |
| parent | 78bc62fd3415dc1db72c916075c9956fdec407aa |
| parent | b66547e98c9034e52c5647735b47dc24939c8d15 |
22 files changed, 991 insertions(+), 183 deletions(-)
CMakeLists.txt+1| ... | ... | @@ -429,6 +429,7 @@ set(ZIG_STD_FILES |
| 429 | 429 | "index.zig" |
| 430 | 430 | "io.zig" |
| 431 | 431 | "linked_list.zig" |
| 432 | "macho.zig" | |
| 432 | 433 | "math/acos.zig" |
| 433 | 434 | "math/acosh.zig" |
| 434 | 435 | "math/asin.zig" |
src/analyze.cpp+12-7| ... | ... | @@ -2278,17 +2278,16 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) { |
| 2278 | 2278 | return; |
| 2279 | 2279 | |
| 2280 | 2280 | if (struct_type->data.structure.zero_bits_loop_flag) { |
| 2281 | // If we get here it's due to recursion. From this we conclude that the struct is | |
| 2282 | // not zero bits, and if abi_alignment == 0 we further conclude that the first field | |
| 2283 | // is a pointer to this very struct, or a function pointer with parameters that | |
| 2284 | // reference such a type. | |
| 2281 | // If we get here it's due to recursion. This is a design flaw in the compiler, | |
| 2282 | // we should be able to still figure out alignment, but here we give up and say that | |
| 2283 | // the alignment is pointer width, then assert that the first field is within that | |
| 2284 | // alignment | |
| 2285 | 2285 | struct_type->data.structure.zero_bits_known = true; |
| 2286 | 2286 | if (struct_type->data.structure.abi_alignment == 0) { |
| 2287 | 2287 | if (struct_type->data.structure.layout == ContainerLayoutPacked) { |
| 2288 | 2288 | struct_type->data.structure.abi_alignment = 1; |
| 2289 | 2289 | } else { |
| 2290 | struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, | |
| 2291 | LLVMPointerType(LLVMInt8Type(), 0)); | |
| 2290 | struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, LLVMPointerType(LLVMInt8Type(), 0)); | |
| 2292 | 2291 | } |
| 2293 | 2292 | } |
| 2294 | 2293 | return; |
| ... | ... | @@ -2352,11 +2351,17 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) { |
| 2352 | 2351 | if (gen_field_index == 0) { |
| 2353 | 2352 | if (struct_type->data.structure.layout == ContainerLayoutPacked) { |
| 2354 | 2353 | struct_type->data.structure.abi_alignment = 1; |
| 2355 | } else { | |
| 2354 | } else if (struct_type->data.structure.abi_alignment == 0) { | |
| 2356 | 2355 | // Alignment of structs is the alignment of the first field, for now. |
| 2357 | 2356 | // TODO change this when we re-order struct fields (issue #168) |
| 2358 | 2357 | struct_type->data.structure.abi_alignment = get_abi_alignment(g, field_type); |
| 2359 | 2358 | assert(struct_type->data.structure.abi_alignment != 0); |
| 2359 | } else { | |
| 2360 | // due to a design flaw in the compiler we assumed that alignment was | |
| 2361 | // pointer width, so we assert that this wasn't violated. | |
| 2362 | if (get_abi_alignment(g, field_type) > struct_type->data.structure.abi_alignment) { | |
| 2363 | zig_panic("compiler design flaw: incorrect alignment assumption"); | |
| 2364 | } | |
| 2360 | 2365 | } |
| 2361 | 2366 | } |
| 2362 | 2367 |
src/codegen.cpp+2| ... | ... | @@ -4201,6 +4201,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c |
| 4201 | 4201 | continue; |
| 4202 | 4202 | } |
| 4203 | 4203 | ConstExprValue *field_val = &const_val->data.x_struct.fields[i]; |
| 4204 | assert(field_val->type != nullptr); | |
| 4204 | 4205 | LLVMValueRef val = gen_const_val(g, field_val, ""); |
| 4205 | 4206 | fields[type_struct_field->gen_index] = val; |
| 4206 | 4207 | make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(field_val->type, val); |
| ... | ... | @@ -4373,6 +4374,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c |
| 4373 | 4374 | } |
| 4374 | 4375 | } |
| 4375 | 4376 | } |
| 4377 | zig_unreachable(); | |
| 4376 | 4378 | case TypeTableEntryIdErrorUnion: |
| 4377 | 4379 | { |
| 4378 | 4380 | TypeTableEntry *payload_type = type_entry->data.error_union.payload_type; |
src/ir.cpp+54-31| ... | ... | @@ -4172,7 +4172,13 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod |
| 4172 | 4172 | buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol))); |
| 4173 | 4173 | } |
| 4174 | 4174 | |
| 4175 | // Temporarily set the name of the IrExecutable to the VariableDeclaration | |
| 4176 | // so that the struct or enum from the init expression inherits the name. | |
| 4177 | Buf *old_exec_name = irb->exec->name; | |
| 4178 | irb->exec->name = variable_declaration->symbol; | |
| 4175 | 4179 | IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope); |
| 4180 | irb->exec->name = old_exec_name; | |
| 4181 | ||
| 4176 | 4182 | if (init_value == irb->codegen->invalid_instruction) |
| 4177 | 4183 | return init_value; |
| 4178 | 4184 | |
| ... | ... | @@ -6727,8 +6733,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry |
| 6727 | 6733 | result.id = ConstCastResultIdFnReturnType; |
| 6728 | 6734 | result.data.return_type = allocate_nonzero<ConstCastOnly>(1); |
| 6729 | 6735 | *result.data.return_type = child; |
| 6736 | return result; | |
| 6730 | 6737 | } |
| 6731 | return result; | |
| 6732 | 6738 | } |
| 6733 | 6739 | if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) { |
| 6734 | 6740 | result.id = ConstCastResultIdFnArgCount; |
| ... | ... | @@ -8183,7 +8189,7 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi |
| 8183 | 8189 | } |
| 8184 | 8190 | |
| 8185 | 8191 | if (instr_is_comptime(value)) { |
| 8186 | ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); | |
| 8192 | ConstExprValue *val = ir_resolve_const(ira, value, UndefOk); | |
| 8187 | 8193 | if (!val) |
| 8188 | 8194 | return ira->codegen->invalid_instruction; |
| 8189 | 8195 | bool final_is_const = (value->value.type->id == TypeTableEntryIdMetaType) ? is_const : true; |
| ... | ... | @@ -9975,15 +9981,18 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp |
| 9975 | 9981 | ok = bigint_cmp(&rem_result, &mod_result) == CmpEQ; |
| 9976 | 9982 | } |
| 9977 | 9983 | } else { |
| 9978 | if (float_cmp_zero(&op2->value) == CmpEQ) { | |
| 9984 | IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, resolved_type); | |
| 9985 | if (casted_op2 == ira->codegen->invalid_instruction) | |
| 9986 | return ira->codegen->builtin_types.entry_invalid; | |
| 9987 | if (float_cmp_zero(&casted_op2->value) == CmpEQ) { | |
| 9979 | 9988 | // the division by zero error will be caught later, but we don't |
| 9980 | 9989 | // have a remainder function ambiguity problem |
| 9981 | 9990 | ok = true; |
| 9982 | 9991 | } else { |
| 9983 | 9992 | ConstExprValue rem_result; |
| 9984 | 9993 | ConstExprValue mod_result; |
| 9985 | float_rem(&rem_result, &op1->value, &op2->value); | |
| 9986 | float_mod(&mod_result, &op1->value, &op2->value); | |
| 9994 | float_rem(&rem_result, &op1->value, &casted_op2->value); | |
| 9995 | float_mod(&mod_result, &op1->value, &casted_op2->value); | |
| 9987 | 9996 | ok = float_cmp(&rem_result, &mod_result) == CmpEQ; |
| 9988 | 9997 | } |
| 9989 | 9998 | } |
| ... | ... | @@ -14928,6 +14937,7 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio |
| 14928 | 14937 | ConstExprValue *parent_ptr; |
| 14929 | 14938 | size_t abs_offset; |
| 14930 | 14939 | size_t rel_end; |
| 14940 | bool ptr_is_undef = false; | |
| 14931 | 14941 | if (array_type->id == TypeTableEntryIdArray) { |
| 14932 | 14942 | array_val = const_ptr_pointee(ira->codegen, &ptr_ptr->value); |
| 14933 | 14943 | abs_offset = 0; |
| ... | ... | @@ -14935,7 +14945,12 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio |
| 14935 | 14945 | parent_ptr = nullptr; |
| 14936 | 14946 | } else if (array_type->id == TypeTableEntryIdPointer) { |
| 14937 | 14947 | parent_ptr = const_ptr_pointee(ira->codegen, &ptr_ptr->value); |
| 14938 | switch (parent_ptr->data.x_ptr.special) { | |
| 14948 | if (parent_ptr->special == ConstValSpecialUndef) { | |
| 14949 | array_val = nullptr; | |
| 14950 | abs_offset = 0; | |
| 14951 | rel_end = SIZE_MAX; | |
| 14952 | ptr_is_undef = true; | |
| 14953 | } else switch (parent_ptr->data.x_ptr.special) { | |
| 14939 | 14954 | case ConstPtrSpecialInvalid: |
| 14940 | 14955 | case ConstPtrSpecialDiscard: |
| 14941 | 14956 | zig_unreachable(); |
| ... | ... | @@ -14989,7 +15004,7 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio |
| 14989 | 15004 | } |
| 14990 | 15005 | |
| 14991 | 15006 | uint64_t start_scalar = bigint_as_unsigned(&casted_start->value.data.x_bigint); |
| 14992 | if (start_scalar > rel_end) { | |
| 15007 | if (!ptr_is_undef && start_scalar > rel_end) { | |
| 14993 | 15008 | ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice")); |
| 14994 | 15009 | return ira->codegen->builtin_types.entry_invalid; |
| 14995 | 15010 | } |
| ... | ... | @@ -15000,12 +15015,18 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio |
| 15000 | 15015 | } else { |
| 15001 | 15016 | end_scalar = rel_end; |
| 15002 | 15017 | } |
| 15003 | if (end_scalar > rel_end) { | |
| 15004 | ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice")); | |
| 15005 | return ira->codegen->builtin_types.entry_invalid; | |
| 15018 | if (!ptr_is_undef) { | |
| 15019 | if (end_scalar > rel_end) { | |
| 15020 | ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice")); | |
| 15021 | return ira->codegen->builtin_types.entry_invalid; | |
| 15022 | } | |
| 15023 | if (start_scalar > end_scalar) { | |
| 15024 | ir_add_error(ira, &instruction->base, buf_sprintf("slice start is greater than end")); | |
| 15025 | return ira->codegen->builtin_types.entry_invalid; | |
| 15026 | } | |
| 15006 | 15027 | } |
| 15007 | if (start_scalar > end_scalar) { | |
| 15008 | ir_add_error(ira, &instruction->base, buf_sprintf("slice start is greater than end")); | |
| 15028 | if (ptr_is_undef && start_scalar != end_scalar) { | |
| 15029 | ir_add_error(ira, &instruction->base, buf_sprintf("non-zero length slice of undefined pointer")); | |
| 15009 | 15030 | return ira->codegen->builtin_types.entry_invalid; |
| 15010 | 15031 | } |
| 15011 | 15032 | |
| ... | ... | @@ -15021,25 +15042,27 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio |
| 15021 | 15042 | if (array_type->id == TypeTableEntryIdArray) { |
| 15022 | 15043 | ptr_val->data.x_ptr.mut = ptr_ptr->value.data.x_ptr.mut; |
| 15023 | 15044 | } |
| 15024 | } else { | |
| 15025 | switch (parent_ptr->data.x_ptr.special) { | |
| 15026 | case ConstPtrSpecialInvalid: | |
| 15027 | case ConstPtrSpecialDiscard: | |
| 15028 | zig_unreachable(); | |
| 15029 | case ConstPtrSpecialRef: | |
| 15030 | init_const_ptr_ref(ira->codegen, ptr_val, | |
| 15031 | parent_ptr->data.x_ptr.data.ref.pointee, slice_is_const(return_type)); | |
| 15032 | break; | |
| 15033 | case ConstPtrSpecialBaseArray: | |
| 15034 | zig_unreachable(); | |
| 15035 | case ConstPtrSpecialBaseStruct: | |
| 15036 | zig_panic("TODO"); | |
| 15037 | case ConstPtrSpecialHardCodedAddr: | |
| 15038 | init_const_ptr_hard_coded_addr(ira->codegen, ptr_val, | |
| 15039 | parent_ptr->type->data.pointer.child_type, | |
| 15040 | parent_ptr->data.x_ptr.data.hard_coded_addr.addr + start_scalar, | |
| 15041 | slice_is_const(return_type)); | |
| 15042 | } | |
| 15045 | } else if (ptr_is_undef) { | |
| 15046 | ptr_val->type = get_pointer_to_type(ira->codegen, parent_ptr->type->data.pointer.child_type, | |
| 15047 | slice_is_const(return_type)); | |
| 15048 | ptr_val->special = ConstValSpecialUndef; | |
| 15049 | } else switch (parent_ptr->data.x_ptr.special) { | |
| 15050 | case ConstPtrSpecialInvalid: | |
| 15051 | case ConstPtrSpecialDiscard: | |
| 15052 | zig_unreachable(); | |
| 15053 | case ConstPtrSpecialRef: | |
| 15054 | init_const_ptr_ref(ira->codegen, ptr_val, | |
| 15055 | parent_ptr->data.x_ptr.data.ref.pointee, slice_is_const(return_type)); | |
| 15056 | break; | |
| 15057 | case ConstPtrSpecialBaseArray: | |
| 15058 | zig_unreachable(); | |
| 15059 | case ConstPtrSpecialBaseStruct: | |
| 15060 | zig_panic("TODO"); | |
| 15061 | case ConstPtrSpecialHardCodedAddr: | |
| 15062 | init_const_ptr_hard_coded_addr(ira->codegen, ptr_val, | |
| 15063 | parent_ptr->type->data.pointer.child_type, | |
| 15064 | parent_ptr->data.x_ptr.data.hard_coded_addr.addr + start_scalar, | |
| 15065 | slice_is_const(return_type)); | |
| 15043 | 15066 | } |
| 15044 | 15067 | |
| 15045 | 15068 | ConstExprValue *len_val = &out_val->data.x_struct.fields[slice_len_index]; |
src/tokenizer.cpp-2| ... | ... | @@ -125,7 +125,6 @@ static const struct ZigKeyword zig_keywords[] = { |
| 125 | 125 | {"false", TokenIdKeywordFalse}, |
| 126 | 126 | {"fn", TokenIdKeywordFn}, |
| 127 | 127 | {"for", TokenIdKeywordFor}, |
| 128 | {"goto", TokenIdKeywordGoto}, | |
| 129 | 128 | {"if", TokenIdKeywordIf}, |
| 130 | 129 | {"inline", TokenIdKeywordInline}, |
| 131 | 130 | {"nakedcc", TokenIdKeywordNakedCC}, |
| ... | ... | @@ -1542,7 +1541,6 @@ const char * token_name(TokenId id) { |
| 1542 | 1541 | case TokenIdKeywordFalse: return "false"; |
| 1543 | 1542 | case TokenIdKeywordFn: return "fn"; |
| 1544 | 1543 | case TokenIdKeywordFor: return "for"; |
| 1545 | case TokenIdKeywordGoto: return "goto"; | |
| 1546 | 1544 | case TokenIdKeywordIf: return "if"; |
| 1547 | 1545 | case TokenIdKeywordInline: return "inline"; |
| 1548 | 1546 | case TokenIdKeywordNakedCC: return "nakedcc"; |
src/tokenizer.hpp-1| ... | ... | @@ -66,7 +66,6 @@ enum TokenId { |
| 66 | 66 | TokenIdKeywordFalse, |
| 67 | 67 | TokenIdKeywordFn, |
| 68 | 68 | TokenIdKeywordFor, |
| 69 | TokenIdKeywordGoto, | |
| 70 | 69 | TokenIdKeywordIf, |
| 71 | 70 | TokenIdKeywordInline, |
| 72 | 71 | TokenIdKeywordNakedCC, |
std/debug/index.zig+94-59| ... | ... | @@ -5,6 +5,7 @@ const io = std.io; |
| 5 | 5 | const os = std.os; |
| 6 | 6 | const elf = std.elf; |
| 7 | 7 | const DW = std.dwarf; |
| 8 | const macho = std.macho; | |
| 8 | 9 | const ArrayList = std.ArrayList; |
| 9 | 10 | const builtin = @import("builtin"); |
| 10 | 11 | |
| ... | ... | @@ -47,7 +48,7 @@ pub fn getSelfDebugInfo() !&ElfStackTrace { |
| 47 | 48 | pub fn dumpCurrentStackTrace() void { |
| 48 | 49 | const stderr = getStderrStream() catch return; |
| 49 | 50 | const debug_info = getSelfDebugInfo() catch |err| { |
| 50 | stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return; | |
| 51 | stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; | |
| 51 | 52 | return; |
| 52 | 53 | }; |
| 53 | 54 | defer debug_info.close(); |
| ... | ... | @@ -61,7 +62,7 @@ pub fn dumpCurrentStackTrace() void { |
| 61 | 62 | pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) void { |
| 62 | 63 | const stderr = getStderrStream() catch return; |
| 63 | 64 | const debug_info = getSelfDebugInfo() catch |err| { |
| 64 | stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return; | |
| 65 | stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; | |
| 65 | 66 | return; |
| 66 | 67 | }; |
| 67 | 68 | defer debug_info.close(); |
| ... | ... | @@ -180,43 +181,57 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator, |
| 180 | 181 | } |
| 181 | 182 | |
| 182 | 183 | fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: usize) !void { |
| 183 | if (builtin.os == builtin.Os.windows) { | |
| 184 | return error.UnsupportedDebugInfo; | |
| 185 | } | |
| 186 | 184 | // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal |
| 187 | 185 | // at compile time. I'll call it issue #313 |
| 188 | 186 | const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}"; |
| 189 | 187 | |
| 190 | const compile_unit = findCompileUnit(debug_info, address) catch { | |
| 191 | try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", | |
| 192 | address); | |
| 193 | return; | |
| 194 | }; | |
| 195 | const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); | |
| 196 | if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| { | |
| 197 | defer line_info.deinit(); | |
| 198 | try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ | |
| 199 | DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", | |
| 200 | line_info.file_name, line_info.line, line_info.column, | |
| 201 | address, compile_unit_name); | |
| 202 | if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) { | |
| 203 | if (line_info.column == 0) { | |
| 204 | try out_stream.write("\n"); | |
| 205 | } else { | |
| 206 | {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) { | |
| 207 | try out_stream.writeByte(' '); | |
| 208 | }} | |
| 209 | try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); | |
| 188 | switch (builtin.os) { | |
| 189 | builtin.Os.windows => return error.UnsupportedDebugInfo, | |
| 190 | builtin.Os.macosx => { | |
| 191 | // TODO(bnoordhuis) It's theoretically possible to obtain the | |
| 192 | // compilation unit from the symbtab but it's not that useful | |
| 193 | // in practice because the compiler dumps everything in a single | |
| 194 | // object file. Future improvement: use external dSYM data when | |
| 195 | // available. | |
| 196 | const unknown = macho.Symbol { .name = "???", .address = address }; | |
| 197 | const symbol = debug_info.symbol_table.search(address) ?? &unknown; | |
| 198 | try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++ | |
| 199 | DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n", | |
| 200 | symbol.name, address); | |
| 201 | }, | |
| 202 | else => { | |
| 203 | const compile_unit = findCompileUnit(debug_info, address) catch { | |
| 204 | try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", | |
| 205 | address); | |
| 206 | return; | |
| 207 | }; | |
| 208 | const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); | |
| 209 | if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| { | |
| 210 | defer line_info.deinit(); | |
| 211 | try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ | |
| 212 | DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", | |
| 213 | line_info.file_name, line_info.line, line_info.column, | |
| 214 | address, compile_unit_name); | |
| 215 | if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) { | |
| 216 | if (line_info.column == 0) { | |
| 217 | try out_stream.write("\n"); | |
| 218 | } else { | |
| 219 | {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) { | |
| 220 | try out_stream.writeByte(' '); | |
| 221 | }} | |
| 222 | try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); | |
| 223 | } | |
| 224 | } else |err| switch (err) { | |
| 225 | error.EndOfFile => {}, | |
| 226 | else => return err, | |
| 227 | } | |
| 228 | } else |err| switch (err) { | |
| 229 | error.MissingDebugInfo, error.InvalidDebugInfo => { | |
| 230 | try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name); | |
| 231 | }, | |
| 232 | else => return err, | |
| 210 | 233 | } |
| 211 | } else |err| switch (err) { | |
| 212 | error.EndOfFile => {}, | |
| 213 | else => return err, | |
| 214 | } | |
| 215 | } else |err| switch (err) { | |
| 216 | error.MissingDebugInfo, error.InvalidDebugInfo => { | |
| 217 | try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name); | |
| 218 | 234 | }, |
| 219 | else => return err, | |
| 220 | 235 | } |
| 221 | 236 | } |
| 222 | 237 | |
| ... | ... | @@ -224,6 +239,7 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace { |
| 224 | 239 | switch (builtin.object_format) { |
| 225 | 240 | builtin.ObjectFormat.elf => { |
| 226 | 241 | const st = try allocator.create(ElfStackTrace); |
| 242 | errdefer allocator.destroy(st); | |
| 227 | 243 | *st = ElfStackTrace { |
| 228 | 244 | .self_exe_file = undefined, |
| 229 | 245 | .elf = undefined, |
| ... | ... | @@ -249,12 +265,22 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace { |
| 249 | 265 | try scanAllCompileUnits(st); |
| 250 | 266 | return st; |
| 251 | 267 | }, |
| 268 | builtin.ObjectFormat.macho => { | |
| 269 | var exe_file = try os.openSelfExe(); | |
| 270 | defer exe_file.close(); | |
| 271 | ||
| 272 | const st = try allocator.create(ElfStackTrace); | |
| 273 | errdefer allocator.destroy(st); | |
| 274 | ||
| 275 | *st = ElfStackTrace { | |
| 276 | .symbol_table = try macho.loadSymbols(allocator, &io.FileInStream.init(&exe_file)), | |
| 277 | }; | |
| 278 | ||
| 279 | return st; | |
| 280 | }, | |
| 252 | 281 | builtin.ObjectFormat.coff => { |
| 253 | 282 | return error.TodoSupportCoffDebugInfo; |
| 254 | 283 | }, |
| 255 | builtin.ObjectFormat.macho => { | |
| 256 | return error.TodoSupportMachoDebugInfo; | |
| 257 | }, | |
| 258 | 284 | builtin.ObjectFormat.wasm => { |
| 259 | 285 | return error.TodoSupportCOFFDebugInfo; |
| 260 | 286 | }, |
| ... | ... | @@ -297,31 +323,40 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &con |
| 297 | 323 | } |
| 298 | 324 | } |
| 299 | 325 | |
| 300 | pub const ElfStackTrace = struct { | |
| 301 | self_exe_file: os.File, | |
| 302 | elf: elf.Elf, | |
| 303 | debug_info: &elf.SectionHeader, | |
| 304 | debug_abbrev: &elf.SectionHeader, | |
| 305 | debug_str: &elf.SectionHeader, | |
| 306 | debug_line: &elf.SectionHeader, | |
| 307 | debug_ranges: ?&elf.SectionHeader, | |
| 308 | abbrev_table_list: ArrayList(AbbrevTableHeader), | |
| 309 | compile_unit_list: ArrayList(CompileUnit), | |
| 310 | ||
| 311 | pub fn allocator(self: &const ElfStackTrace) &mem.Allocator { | |
| 312 | return self.abbrev_table_list.allocator; | |
| 313 | } | |
| 326 | pub const ElfStackTrace = switch (builtin.os) { | |
| 327 | builtin.Os.macosx => struct { | |
| 328 | symbol_table: macho.SymbolTable, | |
| 314 | 329 | |
| 315 | pub fn readString(self: &ElfStackTrace) ![]u8 { | |
| 316 | var in_file_stream = io.FileInStream.init(&self.self_exe_file); | |
| 317 | const in_stream = &in_file_stream.stream; | |
| 318 | return readStringRaw(self.allocator(), in_stream); | |
| 319 | } | |
| 330 | pub fn close(self: &ElfStackTrace) void { | |
| 331 | self.symbol_table.deinit(); | |
| 332 | } | |
| 333 | }, | |
| 334 | else => struct { | |
| 335 | self_exe_file: os.File, | |
| 336 | elf: elf.Elf, | |
| 337 | debug_info: &elf.SectionHeader, | |
| 338 | debug_abbrev: &elf.SectionHeader, | |
| 339 | debug_str: &elf.SectionHeader, | |
| 340 | debug_line: &elf.SectionHeader, | |
| 341 | debug_ranges: ?&elf.SectionHeader, | |
| 342 | abbrev_table_list: ArrayList(AbbrevTableHeader), | |
| 343 | compile_unit_list: ArrayList(CompileUnit), | |
| 344 | ||
| 345 | pub fn allocator(self: &const ElfStackTrace) &mem.Allocator { | |
| 346 | return self.abbrev_table_list.allocator; | |
| 347 | } | |
| 320 | 348 | |
| 321 | pub fn close(self: &ElfStackTrace) void { | |
| 322 | self.self_exe_file.close(); | |
| 323 | self.elf.close(); | |
| 324 | } | |
| 349 | pub fn readString(self: &ElfStackTrace) ![]u8 { | |
| 350 | var in_file_stream = io.FileInStream.init(&self.self_exe_file); | |
| 351 | const in_stream = &in_file_stream.stream; | |
| 352 | return readStringRaw(self.allocator(), in_stream); | |
| 353 | } | |
| 354 | ||
| 355 | pub fn close(self: &ElfStackTrace) void { | |
| 356 | self.self_exe_file.close(); | |
| 357 | self.elf.close(); | |
| 358 | } | |
| 359 | }, | |
| 325 | 360 | }; |
| 326 | 361 | |
| 327 | 362 | const PcRange = struct { |
std/fmt/index.zig+4-6| ... | ... | @@ -550,12 +550,6 @@ test "parse unsigned comptime" { |
| 550 | 550 | } |
| 551 | 551 | } |
| 552 | 552 | |
| 553 | // Dummy field because of https://github.com/zig-lang/zig/issues/557. | |
| 554 | // At top level because of https://github.com/zig-lang/zig/issues/675. | |
| 555 | const Struct = struct { | |
| 556 | unused: u8, | |
| 557 | }; | |
| 558 | ||
| 559 | 553 | test "fmt.format" { |
| 560 | 554 | { |
| 561 | 555 | var buf1: [32]u8 = undefined; |
| ... | ... | @@ -588,6 +582,10 @@ test "fmt.format" { |
| 588 | 582 | assert(mem.eql(u8, result, "u3: 5\n")); |
| 589 | 583 | } |
| 590 | 584 | { |
| 585 | // Dummy field because of https://github.com/zig-lang/zig/issues/557. | |
| 586 | const Struct = struct { | |
| 587 | unused: u8, | |
| 588 | }; | |
| 591 | 589 | var buf1: [32]u8 = undefined; |
| 592 | 590 | const value = Struct { |
| 593 | 591 | .unused = 42, |
std/index.zig+2| ... | ... | @@ -21,6 +21,7 @@ pub const endian = @import("endian.zig"); |
| 21 | 21 | pub const fmt = @import("fmt/index.zig"); |
| 22 | 22 | pub const heap = @import("heap.zig"); |
| 23 | 23 | pub const io = @import("io.zig"); |
| 24 | pub const macho = @import("macho.zig"); | |
| 24 | 25 | pub const math = @import("math/index.zig"); |
| 25 | 26 | pub const mem = @import("mem.zig"); |
| 26 | 27 | pub const net = @import("net.zig"); |
| ... | ... | @@ -51,6 +52,7 @@ test "std" { |
| 51 | 52 | _ = @import("endian.zig"); |
| 52 | 53 | _ = @import("fmt/index.zig"); |
| 53 | 54 | _ = @import("io.zig"); |
| 55 | _ = @import("macho.zig"); | |
| 54 | 56 | _ = @import("math/index.zig"); |
| 55 | 57 | _ = @import("mem.zig"); |
| 56 | 58 | _ = @import("heap.zig"); |
std/macho.zig created+170| ... | ... | @@ -0,0 +1,170 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const std = @import("index.zig"); | |
| 3 | const io = std.io; | |
| 4 | const mem = std.mem; | |
| 5 | ||
| 6 | const MH_MAGIC_64 = 0xFEEDFACF; | |
| 7 | const MH_PIE = 0x200000; | |
| 8 | const LC_SYMTAB = 2; | |
| 9 | ||
| 10 | const MachHeader64 = packed struct { | |
| 11 | magic: u32, | |
| 12 | cputype: u32, | |
| 13 | cpusubtype: u32, | |
| 14 | filetype: u32, | |
| 15 | ncmds: u32, | |
| 16 | sizeofcmds: u32, | |
| 17 | flags: u32, | |
| 18 | reserved: u32, | |
| 19 | }; | |
| 20 | ||
| 21 | const LoadCommand = packed struct { | |
| 22 | cmd: u32, | |
| 23 | cmdsize: u32, | |
| 24 | }; | |
| 25 | ||
| 26 | const SymtabCommand = packed struct { | |
| 27 | symoff: u32, | |
| 28 | nsyms: u32, | |
| 29 | stroff: u32, | |
| 30 | strsize: u32, | |
| 31 | }; | |
| 32 | ||
| 33 | const Nlist64 = packed struct { | |
| 34 | n_strx: u32, | |
| 35 | n_type: u8, | |
| 36 | n_sect: u8, | |
| 37 | n_desc: u16, | |
| 38 | n_value: u64, | |
| 39 | }; | |
| 40 | ||
| 41 | pub const Symbol = struct { | |
| 42 | name: []const u8, | |
| 43 | address: u64, | |
| 44 | ||
| 45 | fn addressLessThan(lhs: &const Symbol, rhs: &const Symbol) bool { | |
| 46 | return lhs.address < rhs.address; | |
| 47 | } | |
| 48 | }; | |
| 49 | ||
| 50 | pub const SymbolTable = struct { | |
| 51 | allocator: &mem.Allocator, | |
| 52 | symbols: []const Symbol, | |
| 53 | strings: []const u8, | |
| 54 | ||
| 55 | // Doubles as an eyecatcher to calculate the PIE slide, see loadSymbols(). | |
| 56 | // Ideally we'd use _mh_execute_header because it's always at 0x100000000 | |
| 57 | // in the image but as it's located in a different section than executable | |
| 58 | // code, its displacement is different. | |
| 59 | pub fn deinit(self: &SymbolTable) void { | |
| 60 | self.allocator.free(self.symbols); | |
| 61 | self.symbols = []const Symbol {}; | |
| 62 | ||
| 63 | self.allocator.free(self.strings); | |
| 64 | self.strings = []const u8 {}; | |
| 65 | } | |
| 66 | ||
| 67 | pub fn search(self: &const SymbolTable, address: usize) ?&const Symbol { | |
| 68 | var min: usize = 0; | |
| 69 | var max: usize = self.symbols.len - 1; // Exclude sentinel. | |
| 70 | while (min < max) { | |
| 71 | const mid = min + (max - min) / 2; | |
| 72 | const curr = &self.symbols[mid]; | |
| 73 | const next = &self.symbols[mid + 1]; | |
| 74 | if (address >= next.address) { | |
| 75 | min = mid + 1; | |
| 76 | } else if (address < curr.address) { | |
| 77 | max = mid; | |
| 78 | } else { | |
| 79 | return curr; | |
| 80 | } | |
| 81 | } | |
| 82 | return null; | |
| 83 | } | |
| 84 | }; | |
| 85 | ||
| 86 | pub fn loadSymbols(allocator: &mem.Allocator, in: &io.FileInStream) !SymbolTable { | |
| 87 | var file = in.file; | |
| 88 | try file.seekTo(0); | |
| 89 | ||
| 90 | var hdr: MachHeader64 = undefined; | |
| 91 | try readOneNoEof(in, MachHeader64, &hdr); | |
| 92 | if (hdr.magic != MH_MAGIC_64) return error.MissingDebugInfo; | |
| 93 | const is_pie = MH_PIE == (hdr.flags & MH_PIE); | |
| 94 | ||
| 95 | var pos: usize = @sizeOf(@typeOf(hdr)); | |
| 96 | var ncmd: u32 = hdr.ncmds; | |
| 97 | while (ncmd != 0) : (ncmd -= 1) { | |
| 98 | try file.seekTo(pos); | |
| 99 | var lc: LoadCommand = undefined; | |
| 100 | try readOneNoEof(in, LoadCommand, &lc); | |
| 101 | if (lc.cmd == LC_SYMTAB) break; | |
| 102 | pos += lc.cmdsize; | |
| 103 | } else { | |
| 104 | return error.MissingDebugInfo; | |
| 105 | } | |
| 106 | ||
| 107 | var cmd: SymtabCommand = undefined; | |
| 108 | try readOneNoEof(in, SymtabCommand, &cmd); | |
| 109 | ||
| 110 | try file.seekTo(cmd.symoff); | |
| 111 | var syms = try allocator.alloc(Nlist64, cmd.nsyms); | |
| 112 | defer allocator.free(syms); | |
| 113 | try readNoEof(in, Nlist64, syms); | |
| 114 | ||
| 115 | try file.seekTo(cmd.stroff); | |
| 116 | var strings = try allocator.alloc(u8, cmd.strsize); | |
| 117 | errdefer allocator.free(strings); | |
| 118 | try in.stream.readNoEof(strings); | |
| 119 | ||
| 120 | var nsyms: usize = 0; | |
| 121 | for (syms) |sym| if (isSymbol(sym)) nsyms += 1; | |
| 122 | if (nsyms == 0) return error.MissingDebugInfo; | |
| 123 | ||
| 124 | var symbols = try allocator.alloc(Symbol, nsyms + 1); // Room for sentinel. | |
| 125 | errdefer allocator.free(symbols); | |
| 126 | ||
| 127 | var pie_slide: usize = 0; | |
| 128 | var nsym: usize = 0; | |
| 129 | for (syms) |sym| { | |
| 130 | if (!isSymbol(sym)) continue; | |
| 131 | const start = sym.n_strx; | |
| 132 | const end = ??mem.indexOfScalarPos(u8, strings, start, 0); | |
| 133 | const name = strings[start..end]; | |
| 134 | const address = sym.n_value; | |
| 135 | symbols[nsym] = Symbol { .name = name, .address = address }; | |
| 136 | nsym += 1; | |
| 137 | if (is_pie and mem.eql(u8, name, "_SymbolTable_deinit")) { | |
| 138 | pie_slide = @ptrToInt(SymbolTable.deinit) - address; | |
| 139 | } | |
| 140 | } | |
| 141 | ||
| 142 | // Effectively a no-op, lld emits symbols in ascending order. | |
| 143 | std.sort.insertionSort(Symbol, symbols[0..nsyms], Symbol.addressLessThan); | |
| 144 | ||
| 145 | // Insert the sentinel. Since we don't know where the last function ends, | |
| 146 | // we arbitrarily limit it to the start address + 4 KB. | |
| 147 | const top = symbols[nsyms - 1].address + 4096; | |
| 148 | symbols[nsyms] = Symbol { .name = "", .address = top }; | |
| 149 | ||
| 150 | if (pie_slide != 0) { | |
| 151 | for (symbols) |*symbol| symbol.address += pie_slide; | |
| 152 | } | |
| 153 | ||
| 154 | return SymbolTable { | |
| 155 | .allocator = allocator, | |
| 156 | .symbols = symbols, | |
| 157 | .strings = strings, | |
| 158 | }; | |
| 159 | } | |
| 160 | ||
| 161 | fn readNoEof(in: &io.FileInStream, comptime T: type, result: []T) !void { | |
| 162 | return in.stream.readNoEof(([]u8)(result)); | |
| 163 | } | |
| 164 | fn readOneNoEof(in: &io.FileInStream, comptime T: type, result: &T) !void { | |
| 165 | return readNoEof(in, T, result[0..1]); | |
| 166 | } | |
| 167 | ||
| 168 | fn isSymbol(sym: &const Nlist64) bool { | |
| 169 | return sym.n_value != 0 and sym.n_desc == 0; | |
| 170 | } |
std/mem.zig+7| ... | ... | @@ -42,6 +42,9 @@ pub const Allocator = struct { |
| 42 | 42 | fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29, |
| 43 | 43 | n: usize) ![]align(alignment) T |
| 44 | 44 | { |
| 45 | if (n == 0) { | |
| 46 | return (&align(alignment) T)(undefined)[0..0]; | |
| 47 | } | |
| 45 | 48 | const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory; |
| 46 | 49 | const byte_slice = try self.allocFn(self, byte_count, alignment); |
| 47 | 50 | assert(byte_slice.len == byte_count); |
| ... | ... | @@ -62,6 +65,10 @@ pub const Allocator = struct { |
| 62 | 65 | if (old_mem.len == 0) { |
| 63 | 66 | return self.alloc(T, n); |
| 64 | 67 | } |
| 68 | if (n == 0) { | |
| 69 | self.free(old_mem); | |
| 70 | return (&align(alignment) T)(undefined)[0..0]; | |
| 71 | } | |
| 65 | 72 | |
| 66 | 73 | const old_byte_slice = ([]u8)(old_mem); |
| 67 | 74 | const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory; |
std/os/linux/index.zig+15-4| ... | ... | @@ -329,6 +329,8 @@ pub const TIOCGPKT = 0x80045438; |
| 329 | 329 | pub const TIOCGPTLCK = 0x80045439; |
| 330 | 330 | pub const TIOCGEXCL = 0x80045440; |
| 331 | 331 | |
| 332 | pub const EPOLL_CLOEXEC = O_CLOEXEC; | |
| 333 | ||
| 332 | 334 | pub const EPOLL_CTL_ADD = 1; |
| 333 | 335 | pub const EPOLL_CTL_DEL = 2; |
| 334 | 336 | pub const EPOLL_CTL_MOD = 3; |
| ... | ... | @@ -751,22 +753,31 @@ pub fn fstat(fd: i32, stat_buf: &Stat) usize { |
| 751 | 753 | return arch.syscall2(arch.SYS_fstat, usize(fd), @ptrToInt(stat_buf)); |
| 752 | 754 | } |
| 753 | 755 | |
| 754 | pub const epoll_data = u64; | |
| 756 | pub const epoll_data = extern union { | |
| 757 | ptr: usize, | |
| 758 | fd: i32, | |
| 759 | @"u32": u32, | |
| 760 | @"u64": u64, | |
| 761 | }; | |
| 755 | 762 | |
| 756 | 763 | pub const epoll_event = extern struct { |
| 757 | 764 | events: u32, |
| 758 | data: epoll_data | |
| 765 | data: epoll_data, | |
| 759 | 766 | }; |
| 760 | 767 | |
| 761 | 768 | pub fn epoll_create() usize { |
| 762 | return arch.syscall1(arch.SYS_epoll_create, usize(1)); | |
| 769 | return epoll_create1(0); | |
| 770 | } | |
| 771 | ||
| 772 | pub fn epoll_create1(flags: usize) usize { | |
| 773 | return arch.syscall1(arch.SYS_epoll_create1, flags); | |
| 763 | 774 | } |
| 764 | 775 | |
| 765 | 776 | pub fn epoll_ctl(epoll_fd: i32, op: i32, fd: i32, ev: &epoll_event) usize { |
| 766 | 777 | return arch.syscall4(arch.SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev)); |
| 767 | 778 | } |
| 768 | 779 | |
| 769 | pub fn epoll_wait(epoll_fd: i32, events: &epoll_event, maxevents: i32, timeout: i32) usize { | |
| 780 | pub fn epoll_wait(epoll_fd: i32, events: &epoll_event, maxevents: u32, timeout: i32) usize { | |
| 770 | 781 | return arch.syscall4(arch.SYS_epoll_wait, usize(epoll_fd), @ptrToInt(events), usize(maxevents), usize(timeout)); |
| 771 | 782 | } |
| 772 | 783 |
std/os/linux/test.zig+1-1| ... | ... | @@ -25,7 +25,7 @@ test "timer" { |
| 25 | 25 | |
| 26 | 26 | var event = linux.epoll_event { |
| 27 | 27 | .events = linux.EPOLLIN | linux.EPOLLOUT | linux.EPOLLET, |
| 28 | .data = 0 | |
| 28 | .data = linux.epoll_data { .ptr = 0 }, | |
| 29 | 29 | }; |
| 30 | 30 | |
| 31 | 31 | err = linux.epoll_ctl(i32(epoll_fd), linux.EPOLL_CTL_ADD, i32(timer_fd), &event); |
std/zig/ast.zig+211-6| ... | ... | @@ -6,6 +6,7 @@ const mem = std.mem; |
| 6 | 6 | |
| 7 | 7 | pub const Node = struct { |
| 8 | 8 | id: Id, |
| 9 | comment: ?&NodeLineComment, | |
| 9 | 10 | |
| 10 | 11 | pub const Id = enum { |
| 11 | 12 | Root, |
| ... | ... | @@ -18,7 +19,9 @@ pub const Node = struct { |
| 18 | 19 | PrefixOp, |
| 19 | 20 | IntegerLiteral, |
| 20 | 21 | FloatLiteral, |
| 22 | StringLiteral, | |
| 21 | 23 | BuiltinCall, |
| 24 | LineComment, | |
| 22 | 25 | }; |
| 23 | 26 | |
| 24 | 27 | pub fn iterate(base: &Node, index: usize) ?&Node { |
| ... | ... | @@ -33,7 +36,45 @@ pub const Node = struct { |
| 33 | 36 | Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index), |
| 34 | 37 | Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).iterate(index), |
| 35 | 38 | Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).iterate(index), |
| 39 | Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).iterate(index), | |
| 36 | 40 | Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).iterate(index), |
| 41 | Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).iterate(index), | |
| 42 | }; | |
| 43 | } | |
| 44 | ||
| 45 | pub fn firstToken(base: &Node) Token { | |
| 46 | return switch (base.id) { | |
| 47 | Id.Root => @fieldParentPtr(NodeRoot, "base", base).firstToken(), | |
| 48 | Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).firstToken(), | |
| 49 | Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).firstToken(), | |
| 50 | Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).firstToken(), | |
| 51 | Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).firstToken(), | |
| 52 | Id.Block => @fieldParentPtr(NodeBlock, "base", base).firstToken(), | |
| 53 | Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).firstToken(), | |
| 54 | Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).firstToken(), | |
| 55 | Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).firstToken(), | |
| 56 | Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).firstToken(), | |
| 57 | Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).firstToken(), | |
| 58 | Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).firstToken(), | |
| 59 | Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).firstToken(), | |
| 60 | }; | |
| 61 | } | |
| 62 | ||
| 63 | pub fn lastToken(base: &Node) Token { | |
| 64 | return switch (base.id) { | |
| 65 | Id.Root => @fieldParentPtr(NodeRoot, "base", base).lastToken(), | |
| 66 | Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).lastToken(), | |
| 67 | Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).lastToken(), | |
| 68 | Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).lastToken(), | |
| 69 | Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).lastToken(), | |
| 70 | Id.Block => @fieldParentPtr(NodeBlock, "base", base).lastToken(), | |
| 71 | Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).lastToken(), | |
| 72 | Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).lastToken(), | |
| 73 | Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).lastToken(), | |
| 74 | Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).lastToken(), | |
| 75 | Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).lastToken(), | |
| 76 | Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).lastToken(), | |
| 77 | Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).lastToken(), | |
| 37 | 78 | }; |
| 38 | 79 | } |
| 39 | 80 | }; |
| ... | ... | @@ -41,6 +82,7 @@ pub const Node = struct { |
| 41 | 82 | pub const NodeRoot = struct { |
| 42 | 83 | base: Node, |
| 43 | 84 | decls: ArrayList(&Node), |
| 85 | eof_token: Token, | |
| 44 | 86 | |
| 45 | 87 | pub fn iterate(self: &NodeRoot, index: usize) ?&Node { |
| 46 | 88 | if (index < self.decls.len) { |
| ... | ... | @@ -48,6 +90,14 @@ pub const NodeRoot = struct { |
| 48 | 90 | } |
| 49 | 91 | return null; |
| 50 | 92 | } |
| 93 | ||
| 94 | pub fn firstToken(self: &NodeRoot) Token { | |
| 95 | return if (self.decls.len == 0) self.eof_token else self.decls.at(0).firstToken(); | |
| 96 | } | |
| 97 | ||
| 98 | pub fn lastToken(self: &NodeRoot) Token { | |
| 99 | return if (self.decls.len == 0) self.eof_token else self.decls.at(self.decls.len - 1).lastToken(); | |
| 100 | } | |
| 51 | 101 | }; |
| 52 | 102 | |
| 53 | 103 | pub const NodeVarDecl = struct { |
| ... | ... | @@ -62,6 +112,7 @@ pub const NodeVarDecl = struct { |
| 62 | 112 | type_node: ?&Node, |
| 63 | 113 | align_node: ?&Node, |
| 64 | 114 | init_node: ?&Node, |
| 115 | semicolon_token: Token, | |
| 65 | 116 | |
| 66 | 117 | pub fn iterate(self: &NodeVarDecl, index: usize) ?&Node { |
| 67 | 118 | var i = index; |
| ... | ... | @@ -83,6 +134,18 @@ pub const NodeVarDecl = struct { |
| 83 | 134 | |
| 84 | 135 | return null; |
| 85 | 136 | } |
| 137 | ||
| 138 | pub fn firstToken(self: &NodeVarDecl) Token { | |
| 139 | if (self.visib_token) |visib_token| return visib_token; | |
| 140 | if (self.comptime_token) |comptime_token| return comptime_token; | |
| 141 | if (self.extern_token) |extern_token| return extern_token; | |
| 142 | assert(self.lib_name == null); | |
| 143 | return self.mut_token; | |
| 144 | } | |
| 145 | ||
| 146 | pub fn lastToken(self: &NodeVarDecl) Token { | |
| 147 | return self.semicolon_token; | |
| 148 | } | |
| 86 | 149 | }; |
| 87 | 150 | |
| 88 | 151 | pub const NodeIdentifier = struct { |
| ... | ... | @@ -92,6 +155,14 @@ pub const NodeIdentifier = struct { |
| 92 | 155 | pub fn iterate(self: &NodeIdentifier, index: usize) ?&Node { |
| 93 | 156 | return null; |
| 94 | 157 | } |
| 158 | ||
| 159 | pub fn firstToken(self: &NodeIdentifier) Token { | |
| 160 | return self.name_token; | |
| 161 | } | |
| 162 | ||
| 163 | pub fn lastToken(self: &NodeIdentifier) Token { | |
| 164 | return self.name_token; | |
| 165 | } | |
| 95 | 166 | }; |
| 96 | 167 | |
| 97 | 168 | pub const NodeFnProto = struct { |
| ... | ... | @@ -100,7 +171,7 @@ pub const NodeFnProto = struct { |
| 100 | 171 | fn_token: Token, |
| 101 | 172 | name_token: ?Token, |
| 102 | 173 | params: ArrayList(&Node), |
| 103 | return_type: &Node, | |
| 174 | return_type: ReturnType, | |
| 104 | 175 | var_args_token: ?Token, |
| 105 | 176 | extern_token: ?Token, |
| 106 | 177 | inline_token: ?Token, |
| ... | ... | @@ -109,6 +180,12 @@ pub const NodeFnProto = struct { |
| 109 | 180 | lib_name: ?&Node, // populated if this is an extern declaration |
| 110 | 181 | align_expr: ?&Node, // populated if align(A) is present |
| 111 | 182 | |
| 183 | pub const ReturnType = union(enum) { | |
| 184 | Explicit: &Node, | |
| 185 | Infer: Token, | |
| 186 | InferErrorSet: &Node, | |
| 187 | }; | |
| 188 | ||
| 112 | 189 | pub fn iterate(self: &NodeFnProto, index: usize) ?&Node { |
| 113 | 190 | var i = index; |
| 114 | 191 | |
| ... | ... | @@ -117,8 +194,18 @@ pub const NodeFnProto = struct { |
| 117 | 194 | i -= 1; |
| 118 | 195 | } |
| 119 | 196 | |
| 120 | if (i < 1) return self.return_type; | |
| 121 | i -= 1; | |
| 197 | switch (self.return_type) { | |
| 198 | // TODO allow this and next prong to share bodies since the types are the same | |
| 199 | ReturnType.Explicit => |node| { | |
| 200 | if (i < 1) return node; | |
| 201 | i -= 1; | |
| 202 | }, | |
| 203 | ReturnType.InferErrorSet => |node| { | |
| 204 | if (i < 1) return node; | |
| 205 | i -= 1; | |
| 206 | }, | |
| 207 | ReturnType.Infer => {}, | |
| 208 | } | |
| 122 | 209 | |
| 123 | 210 | if (self.align_expr) |align_expr| { |
| 124 | 211 | if (i < 1) return align_expr; |
| ... | ... | @@ -135,6 +222,25 @@ pub const NodeFnProto = struct { |
| 135 | 222 | |
| 136 | 223 | return null; |
| 137 | 224 | } |
| 225 | ||
| 226 | pub fn firstToken(self: &NodeFnProto) Token { | |
| 227 | if (self.visib_token) |visib_token| return visib_token; | |
| 228 | if (self.extern_token) |extern_token| return extern_token; | |
| 229 | assert(self.lib_name == null); | |
| 230 | if (self.inline_token) |inline_token| return inline_token; | |
| 231 | if (self.cc_token) |cc_token| return cc_token; | |
| 232 | return self.fn_token; | |
| 233 | } | |
| 234 | ||
| 235 | pub fn lastToken(self: &NodeFnProto) Token { | |
| 236 | if (self.body_node) |body_node| return body_node.lastToken(); | |
| 237 | switch (self.return_type) { | |
| 238 | // TODO allow this and next prong to share bodies since the types are the same | |
| 239 | ReturnType.Explicit => |node| return node.lastToken(), | |
| 240 | ReturnType.InferErrorSet => |node| return node.lastToken(), | |
| 241 | ReturnType.Infer => |token| return token, | |
| 242 | } | |
| 243 | } | |
| 138 | 244 | }; |
| 139 | 245 | |
| 140 | 246 | pub const NodeParamDecl = struct { |
| ... | ... | @@ -153,6 +259,18 @@ pub const NodeParamDecl = struct { |
| 153 | 259 | |
| 154 | 260 | return null; |
| 155 | 261 | } |
| 262 | ||
| 263 | pub fn firstToken(self: &NodeParamDecl) Token { | |
| 264 | if (self.comptime_token) |comptime_token| return comptime_token; | |
| 265 | if (self.noalias_token) |noalias_token| return noalias_token; | |
| 266 | if (self.name_token) |name_token| return name_token; | |
| 267 | return self.type_node.firstToken(); | |
| 268 | } | |
| 269 | ||
| 270 | pub fn lastToken(self: &NodeParamDecl) Token { | |
| 271 | if (self.var_args_token) |var_args_token| return var_args_token; | |
| 272 | return self.type_node.lastToken(); | |
| 273 | } | |
| 156 | 274 | }; |
| 157 | 275 | |
| 158 | 276 | pub const NodeBlock = struct { |
| ... | ... | @@ -169,6 +287,14 @@ pub const NodeBlock = struct { |
| 169 | 287 | |
| 170 | 288 | return null; |
| 171 | 289 | } |
| 290 | ||
| 291 | pub fn firstToken(self: &NodeBlock) Token { | |
| 292 | return self.begin_token; | |
| 293 | } | |
| 294 | ||
| 295 | pub fn lastToken(self: &NodeBlock) Token { | |
| 296 | return self.end_token; | |
| 297 | } | |
| 172 | 298 | }; |
| 173 | 299 | |
| 174 | 300 | pub const NodeInfixOp = struct { |
| ... | ... | @@ -181,6 +307,7 @@ pub const NodeInfixOp = struct { |
| 181 | 307 | const InfixOp = enum { |
| 182 | 308 | EqualEqual, |
| 183 | 309 | BangEqual, |
| 310 | Period, | |
| 184 | 311 | }; |
| 185 | 312 | |
| 186 | 313 | pub fn iterate(self: &NodeInfixOp, index: usize) ?&Node { |
| ... | ... | @@ -190,8 +317,9 @@ pub const NodeInfixOp = struct { |
| 190 | 317 | i -= 1; |
| 191 | 318 | |
| 192 | 319 | switch (self.op) { |
| 193 | InfixOp.EqualEqual => {}, | |
| 194 | InfixOp.BangEqual => {}, | |
| 320 | InfixOp.EqualEqual, | |
| 321 | InfixOp.BangEqual, | |
| 322 | InfixOp.Period => {}, | |
| 195 | 323 | } |
| 196 | 324 | |
| 197 | 325 | if (i < 1) return self.rhs; |
| ... | ... | @@ -199,6 +327,14 @@ pub const NodeInfixOp = struct { |
| 199 | 327 | |
| 200 | 328 | return null; |
| 201 | 329 | } |
| 330 | ||
| 331 | pub fn firstToken(self: &NodeInfixOp) Token { | |
| 332 | return self.lhs.firstToken(); | |
| 333 | } | |
| 334 | ||
| 335 | pub fn lastToken(self: &NodeInfixOp) Token { | |
| 336 | return self.rhs.lastToken(); | |
| 337 | } | |
| 202 | 338 | }; |
| 203 | 339 | |
| 204 | 340 | pub const NodePrefixOp = struct { |
| ... | ... | @@ -209,6 +345,7 @@ pub const NodePrefixOp = struct { |
| 209 | 345 | |
| 210 | 346 | const PrefixOp = union(enum) { |
| 211 | 347 | Return, |
| 348 | Try, | |
| 212 | 349 | AddrOf: AddrOfInfo, |
| 213 | 350 | }; |
| 214 | 351 | const AddrOfInfo = struct { |
| ... | ... | @@ -223,7 +360,8 @@ pub const NodePrefixOp = struct { |
| 223 | 360 | var i = index; |
| 224 | 361 | |
| 225 | 362 | switch (self.op) { |
| 226 | PrefixOp.Return => {}, | |
| 363 | PrefixOp.Return, | |
| 364 | PrefixOp.Try => {}, | |
| 227 | 365 | PrefixOp.AddrOf => |addr_of_info| { |
| 228 | 366 | if (addr_of_info.align_expr) |align_expr| { |
| 229 | 367 | if (i < 1) return align_expr; |
| ... | ... | @@ -237,6 +375,14 @@ pub const NodePrefixOp = struct { |
| 237 | 375 | |
| 238 | 376 | return null; |
| 239 | 377 | } |
| 378 | ||
| 379 | pub fn firstToken(self: &NodePrefixOp) Token { | |
| 380 | return self.op_token; | |
| 381 | } | |
| 382 | ||
| 383 | pub fn lastToken(self: &NodePrefixOp) Token { | |
| 384 | return self.rhs.lastToken(); | |
| 385 | } | |
| 240 | 386 | }; |
| 241 | 387 | |
| 242 | 388 | pub const NodeIntegerLiteral = struct { |
| ... | ... | @@ -246,6 +392,14 @@ pub const NodeIntegerLiteral = struct { |
| 246 | 392 | pub fn iterate(self: &NodeIntegerLiteral, index: usize) ?&Node { |
| 247 | 393 | return null; |
| 248 | 394 | } |
| 395 | ||
| 396 | pub fn firstToken(self: &NodeIntegerLiteral) Token { | |
| 397 | return self.token; | |
| 398 | } | |
| 399 | ||
| 400 | pub fn lastToken(self: &NodeIntegerLiteral) Token { | |
| 401 | return self.token; | |
| 402 | } | |
| 249 | 403 | }; |
| 250 | 404 | |
| 251 | 405 | pub const NodeFloatLiteral = struct { |
| ... | ... | @@ -255,12 +409,21 @@ pub const NodeFloatLiteral = struct { |
| 255 | 409 | pub fn iterate(self: &NodeFloatLiteral, index: usize) ?&Node { |
| 256 | 410 | return null; |
| 257 | 411 | } |
| 412 | ||
| 413 | pub fn firstToken(self: &NodeFloatLiteral) Token { | |
| 414 | return self.token; | |
| 415 | } | |
| 416 | ||
| 417 | pub fn lastToken(self: &NodeFloatLiteral) Token { | |
| 418 | return self.token; | |
| 419 | } | |
| 258 | 420 | }; |
| 259 | 421 | |
| 260 | 422 | pub const NodeBuiltinCall = struct { |
| 261 | 423 | base: Node, |
| 262 | 424 | builtin_token: Token, |
| 263 | 425 | params: ArrayList(&Node), |
| 426 | rparen_token: Token, | |
| 264 | 427 | |
| 265 | 428 | pub fn iterate(self: &NodeBuiltinCall, index: usize) ?&Node { |
| 266 | 429 | var i = index; |
| ... | ... | @@ -270,4 +433,46 @@ pub const NodeBuiltinCall = struct { |
| 270 | 433 | |
| 271 | 434 | return null; |
| 272 | 435 | } |
| 436 | ||
| 437 | pub fn firstToken(self: &NodeBuiltinCall) Token { | |
| 438 | return self.builtin_token; | |
| 439 | } | |
| 440 | ||
| 441 | pub fn lastToken(self: &NodeBuiltinCall) Token { | |
| 442 | return self.rparen_token; | |
| 443 | } | |
| 444 | }; | |
| 445 | ||
| 446 | pub const NodeStringLiteral = struct { | |
| 447 | base: Node, | |
| 448 | token: Token, | |
| 449 | ||
| 450 | pub fn iterate(self: &NodeStringLiteral, index: usize) ?&Node { | |
| 451 | return null; | |
| 452 | } | |
| 453 | ||
| 454 | pub fn firstToken(self: &NodeStringLiteral) Token { | |
| 455 | return self.token; | |
| 456 | } | |
| 457 | ||
| 458 | pub fn lastToken(self: &NodeStringLiteral) Token { | |
| 459 | return self.token; | |
| 460 | } | |
| 461 | }; | |
| 462 | ||
| 463 | pub const NodeLineComment = struct { | |
| 464 | base: Node, | |
| 465 | lines: ArrayList(Token), | |
| 466 | ||
| 467 | pub fn iterate(self: &NodeLineComment, index: usize) ?&Node { | |
| 468 | return null; | |
| 469 | } | |
| 470 | ||
| 471 | pub fn firstToken(self: &NodeLineComment) Token { | |
| 472 | return self.lines.at(0); | |
| 473 | } | |
| 474 | ||
| 475 | pub fn lastToken(self: &NodeLineComment) Token { | |
| 476 | return self.lines.at(self.lines.len - 1); | |
| 477 | } | |
| 273 | 478 | }; |
std/zig/parser.zig+304-40| ... | ... | @@ -18,6 +18,7 @@ pub const Parser = struct { |
| 18 | 18 | put_back_tokens: [2]Token, |
| 19 | 19 | put_back_count: usize, |
| 20 | 20 | source_file_name: []const u8, |
| 21 | pending_line_comment_node: ?&ast.NodeLineComment, | |
| 21 | 22 | |
| 22 | 23 | pub const Tree = struct { |
| 23 | 24 | root_node: &ast.NodeRoot, |
| ... | ... | @@ -43,6 +44,7 @@ pub const Parser = struct { |
| 43 | 44 | .put_back_count = 0, |
| 44 | 45 | .source_file_name = source_file_name, |
| 45 | 46 | .utility_bytes = []align(utility_bytes_align) u8{}, |
| 47 | .pending_line_comment_node = null, | |
| 46 | 48 | }; |
| 47 | 49 | } |
| 48 | 50 | |
| ... | ... | @@ -69,6 +71,11 @@ pub const Parser = struct { |
| 69 | 71 | } |
| 70 | 72 | }; |
| 71 | 73 | |
| 74 | const ExpectTokenSave = struct { | |
| 75 | id: Token.Id, | |
| 76 | ptr: &Token, | |
| 77 | }; | |
| 78 | ||
| 72 | 79 | const State = union(enum) { |
| 73 | 80 | TopLevel, |
| 74 | 81 | TopLevelExtern: ?Token, |
| ... | ... | @@ -85,13 +92,17 @@ pub const Parser = struct { |
| 85 | 92 | VarDeclAlign: &ast.NodeVarDecl, |
| 86 | 93 | VarDeclEq: &ast.NodeVarDecl, |
| 87 | 94 | ExpectToken: @TagType(Token.Id), |
| 95 | ExpectTokenSave: ExpectTokenSave, | |
| 88 | 96 | FnProto: &ast.NodeFnProto, |
| 89 | 97 | FnProtoAlign: &ast.NodeFnProto, |
| 98 | FnProtoReturnType: &ast.NodeFnProto, | |
| 90 | 99 | ParamDecl: &ast.NodeFnProto, |
| 91 | 100 | ParamDeclComma, |
| 92 | 101 | FnDef: &ast.NodeFnProto, |
| 93 | 102 | Block: &ast.NodeBlock, |
| 94 | 103 | Statement: &ast.NodeBlock, |
| 104 | ExprListItemOrEnd: &ArrayList(&ast.Node), | |
| 105 | ExprListCommaOrEnd: &ArrayList(&ast.Node), | |
| 95 | 106 | }; |
| 96 | 107 | |
| 97 | 108 | /// Returns an AST tree, allocated with the parser's allocator. |
| ... | ... | @@ -122,6 +133,33 @@ pub const Parser = struct { |
| 122 | 133 | // warn("\n"); |
| 123 | 134 | //} |
| 124 | 135 | |
| 136 | // look for line comments | |
| 137 | while (true) { | |
| 138 | const token = self.getNextToken(); | |
| 139 | if (token.id == Token.Id.LineComment) { | |
| 140 | const node = blk: { | |
| 141 | if (self.pending_line_comment_node) |comment_node| { | |
| 142 | break :blk comment_node; | |
| 143 | } else { | |
| 144 | const comment_node = try arena.create(ast.NodeLineComment); | |
| 145 | *comment_node = ast.NodeLineComment { | |
| 146 | .base = ast.Node { | |
| 147 | .id = ast.Node.Id.LineComment, | |
| 148 | .comment = null, | |
| 149 | }, | |
| 150 | .lines = ArrayList(Token).init(arena), | |
| 151 | }; | |
| 152 | self.pending_line_comment_node = comment_node; | |
| 153 | break :blk comment_node; | |
| 154 | } | |
| 155 | }; | |
| 156 | try node.lines.append(token); | |
| 157 | continue; | |
| 158 | } | |
| 159 | self.putBackToken(token); | |
| 160 | break; | |
| 161 | } | |
| 162 | ||
| 125 | 163 | // This gives us 1 free append that can't fail |
| 126 | 164 | const state = stack.pop(); |
| 127 | 165 | |
| ... | ... | @@ -133,7 +171,10 @@ pub const Parser = struct { |
| 133 | 171 | stack.append(State { .TopLevelExtern = token }) catch unreachable; |
| 134 | 172 | continue; |
| 135 | 173 | }, |
| 136 | Token.Id.Eof => return Tree {.root_node = root_node, .arena_allocator = arena_allocator}, | |
| 174 | Token.Id.Eof => { | |
| 175 | root_node.eof_token = token; | |
| 176 | return Tree {.root_node = root_node, .arena_allocator = arena_allocator}; | |
| 177 | }, | |
| 137 | 178 | else => { |
| 138 | 179 | self.putBackToken(token); |
| 139 | 180 | stack.append(State { .TopLevelExtern = null }) catch unreachable; |
| ... | ... | @@ -176,7 +217,7 @@ pub const Parser = struct { |
| 176 | 217 | stack.append(State.TopLevel) catch unreachable; |
| 177 | 218 | // TODO shouldn't need these casts |
| 178 | 219 | const fn_proto = try self.createAttachFnProto(arena, &root_node.decls, token, |
| 179 | ctx.extern_token, (?Token)(null), (?Token)(null), (?Token)(null)); | |
| 220 | ctx.extern_token, (?Token)(null), ctx.visib_token, (?Token)(null)); | |
| 180 | 221 | try stack.append(State { .FnDef = fn_proto }); |
| 181 | 222 | try stack.append(State { .FnProto = fn_proto }); |
| 182 | 223 | continue; |
| ... | ... | @@ -228,13 +269,19 @@ pub const Parser = struct { |
| 228 | 269 | const token = self.getNextToken(); |
| 229 | 270 | if (token.id == Token.Id.Equal) { |
| 230 | 271 | var_decl.eq_token = token; |
| 231 | stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable; | |
| 272 | stack.append(State { | |
| 273 | .ExpectTokenSave = ExpectTokenSave { | |
| 274 | .id = Token.Id.Semicolon, | |
| 275 | .ptr = &var_decl.semicolon_token, | |
| 276 | }, | |
| 277 | }) catch unreachable; | |
| 232 | 278 | try stack.append(State { |
| 233 | 279 | .Expression = DestPtr {.NullableField = &var_decl.init_node}, |
| 234 | 280 | }); |
| 235 | 281 | continue; |
| 236 | 282 | } |
| 237 | 283 | if (token.id == Token.Id.Semicolon) { |
| 284 | var_decl.semicolon_token = token; | |
| 238 | 285 | continue; |
| 239 | 286 | } |
| 240 | 287 | return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id)); |
| ... | ... | @@ -244,6 +291,11 @@ pub const Parser = struct { |
| 244 | 291 | continue; |
| 245 | 292 | }, |
| 246 | 293 | |
| 294 | State.ExpectTokenSave => |expect_token_save| { | |
| 295 | *expect_token_save.ptr = try self.eatToken(expect_token_save.id); | |
| 296 | continue; | |
| 297 | }, | |
| 298 | ||
| 247 | 299 | State.Expression => |dest_ptr| { |
| 248 | 300 | // save the dest_ptr for later |
| 249 | 301 | stack.append(state) catch unreachable; |
| ... | ... | @@ -261,6 +313,12 @@ pub const Parser = struct { |
| 261 | 313 | try stack.append(State.ExpectOperand); |
| 262 | 314 | continue; |
| 263 | 315 | }, |
| 316 | Token.Id.Keyword_try => { | |
| 317 | try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token, | |
| 318 | ast.NodePrefixOp.PrefixOp.Try) }); | |
| 319 | try stack.append(State.ExpectOperand); | |
| 320 | continue; | |
| 321 | }, | |
| 264 | 322 | Token.Id.Ampersand => { |
| 265 | 323 | const prefix_op = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{ |
| 266 | 324 | .AddrOf = ast.NodePrefixOp.AddrOfInfo { |
| ... | ... | @@ -297,6 +355,40 @@ pub const Parser = struct { |
| 297 | 355 | try stack.append(State.AfterOperand); |
| 298 | 356 | continue; |
| 299 | 357 | }, |
| 358 | Token.Id.Builtin => { | |
| 359 | const node = try arena.create(ast.NodeBuiltinCall); | |
| 360 | *node = ast.NodeBuiltinCall { | |
| 361 | .base = self.initNode(ast.Node.Id.BuiltinCall), | |
| 362 | .builtin_token = token, | |
| 363 | .params = ArrayList(&ast.Node).init(arena), | |
| 364 | .rparen_token = undefined, | |
| 365 | }; | |
| 366 | try stack.append(State { | |
| 367 | .Operand = &node.base | |
| 368 | }); | |
| 369 | try stack.append(State.AfterOperand); | |
| 370 | try stack.append(State {.ExprListItemOrEnd = &node.params }); | |
| 371 | try stack.append(State { | |
| 372 | .ExpectTokenSave = ExpectTokenSave { | |
| 373 | .id = Token.Id.LParen, | |
| 374 | .ptr = &node.rparen_token, | |
| 375 | }, | |
| 376 | }); | |
| 377 | continue; | |
| 378 | }, | |
| 379 | Token.Id.StringLiteral => { | |
| 380 | const node = try arena.create(ast.NodeStringLiteral); | |
| 381 | *node = ast.NodeStringLiteral { | |
| 382 | .base = self.initNode(ast.Node.Id.StringLiteral), | |
| 383 | .token = token, | |
| 384 | }; | |
| 385 | try stack.append(State { | |
| 386 | .Operand = &node.base | |
| 387 | }); | |
| 388 | try stack.append(State.AfterOperand); | |
| 389 | continue; | |
| 390 | }, | |
| 391 | ||
| 300 | 392 | else => return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)), |
| 301 | 393 | } |
| 302 | 394 | }, |
| ... | ... | @@ -321,6 +413,13 @@ pub const Parser = struct { |
| 321 | 413 | try stack.append(State.ExpectOperand); |
| 322 | 414 | continue; |
| 323 | 415 | }, |
| 416 | Token.Id.Period => { | |
| 417 | try stack.append(State { | |
| 418 | .InfixOp = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.Period) | |
| 419 | }); | |
| 420 | try stack.append(State.ExpectOperand); | |
| 421 | continue; | |
| 422 | }, | |
| 324 | 423 | else => { |
| 325 | 424 | // no postfix/infix operator after this operand. |
| 326 | 425 | self.putBackToken(token); |
| ... | ... | @@ -352,6 +451,29 @@ pub const Parser = struct { |
| 352 | 451 | } |
| 353 | 452 | }, |
| 354 | 453 | |
| 454 | State.ExprListItemOrEnd => |params| { | |
| 455 | var token = self.getNextToken(); | |
| 456 | switch (token.id) { | |
| 457 | Token.Id.RParen => continue, | |
| 458 | else => { | |
| 459 | self.putBackToken(token); | |
| 460 | stack.append(State { .ExprListCommaOrEnd = params }) catch unreachable; | |
| 461 | try stack.append(State { .Expression = DestPtr{.List = params} }); | |
| 462 | }, | |
| 463 | } | |
| 464 | }, | |
| 465 | ||
| 466 | State.ExprListCommaOrEnd => |params| { | |
| 467 | var token = self.getNextToken(); | |
| 468 | switch (token.id) { | |
| 469 | Token.Id.Comma => { | |
| 470 | stack.append(State { .ExprListItemOrEnd = params }) catch unreachable; | |
| 471 | }, | |
| 472 | Token.Id.RParen => continue, | |
| 473 | else => return self.parseError(token, "expected ',' or ')', found {}", @tagName(token.id)), | |
| 474 | } | |
| 475 | }, | |
| 476 | ||
| 355 | 477 | State.AddrOfModifiers => |addr_of_info| { |
| 356 | 478 | var token = self.getNextToken(); |
| 357 | 479 | switch (token.id) { |
| ... | ... | @@ -414,11 +536,37 @@ pub const Parser = struct { |
| 414 | 536 | } |
| 415 | 537 | self.putBackToken(token); |
| 416 | 538 | stack.append(State { |
| 417 | .TypeExpr = DestPtr {.Field = &fn_proto.return_type}, | |
| 539 | .FnProtoReturnType = fn_proto, | |
| 418 | 540 | }) catch unreachable; |
| 419 | 541 | continue; |
| 420 | 542 | }, |
| 421 | 543 | |
| 544 | State.FnProtoReturnType => |fn_proto| { | |
| 545 | const token = self.getNextToken(); | |
| 546 | switch (token.id) { | |
| 547 | Token.Id.Keyword_var => { | |
| 548 | fn_proto.return_type = ast.NodeFnProto.ReturnType { .Infer = token }; | |
| 549 | }, | |
| 550 | Token.Id.Bang => { | |
| 551 | fn_proto.return_type = ast.NodeFnProto.ReturnType { .InferErrorSet = undefined }; | |
| 552 | stack.append(State { | |
| 553 | .TypeExpr = DestPtr {.Field = &fn_proto.return_type.InferErrorSet}, | |
| 554 | }) catch unreachable; | |
| 555 | }, | |
| 556 | else => { | |
| 557 | self.putBackToken(token); | |
| 558 | fn_proto.return_type = ast.NodeFnProto.ReturnType { .Explicit = undefined }; | |
| 559 | stack.append(State { | |
| 560 | .TypeExpr = DestPtr {.Field = &fn_proto.return_type.Explicit}, | |
| 561 | }) catch unreachable; | |
| 562 | }, | |
| 563 | } | |
| 564 | if (token.id == Token.Id.Keyword_align) { | |
| 565 | @panic("TODO fn proto align"); | |
| 566 | } | |
| 567 | continue; | |
| 568 | }, | |
| 569 | ||
| 422 | 570 | State.ParamDecl => |fn_proto| { |
| 423 | 571 | var token = self.getNextToken(); |
| 424 | 572 | if (token.id == Token.Id.RParen) { |
| ... | ... | @@ -539,17 +687,25 @@ pub const Parser = struct { |
| 539 | 687 | State.PrefixOp => unreachable, |
| 540 | 688 | State.Operand => unreachable, |
| 541 | 689 | } |
| 542 | @import("std").debug.panic("{}", @tagName(state)); | |
| 543 | //unreachable; | |
| 544 | 690 | } |
| 545 | 691 | } |
| 546 | 692 | |
| 693 | fn initNode(self: &Parser, id: ast.Node.Id) ast.Node { | |
| 694 | if (self.pending_line_comment_node) |comment_node| { | |
| 695 | self.pending_line_comment_node = null; | |
| 696 | return ast.Node {.id = id, .comment = comment_node}; | |
| 697 | } | |
| 698 | return ast.Node {.id = id, .comment = null }; | |
| 699 | } | |
| 700 | ||
| 547 | 701 | fn createRoot(self: &Parser, arena: &mem.Allocator) !&ast.NodeRoot { |
| 548 | 702 | const node = try arena.create(ast.NodeRoot); |
| 549 | 703 | |
| 550 | 704 | *node = ast.NodeRoot { |
| 551 | .base = ast.Node {.id = ast.Node.Id.Root}, | |
| 705 | .base = self.initNode(ast.Node.Id.Root), | |
| 552 | 706 | .decls = ArrayList(&ast.Node).init(arena), |
| 707 | // initialized when we get the eof token | |
| 708 | .eof_token = undefined, | |
| 553 | 709 | }; |
| 554 | 710 | return node; |
| 555 | 711 | } |
| ... | ... | @@ -560,7 +716,7 @@ pub const Parser = struct { |
| 560 | 716 | const node = try arena.create(ast.NodeVarDecl); |
| 561 | 717 | |
| 562 | 718 | *node = ast.NodeVarDecl { |
| 563 | .base = ast.Node {.id = ast.Node.Id.VarDecl}, | |
| 719 | .base = self.initNode(ast.Node.Id.VarDecl), | |
| 564 | 720 | .visib_token = *visib_token, |
| 565 | 721 | .mut_token = *mut_token, |
| 566 | 722 | .comptime_token = *comptime_token, |
| ... | ... | @@ -572,6 +728,7 @@ pub const Parser = struct { |
| 572 | 728 | // initialized later |
| 573 | 729 | .name_token = undefined, |
| 574 | 730 | .eq_token = undefined, |
| 731 | .semicolon_token = undefined, | |
| 575 | 732 | }; |
| 576 | 733 | return node; |
| 577 | 734 | } |
| ... | ... | @@ -582,7 +739,7 @@ pub const Parser = struct { |
| 582 | 739 | const node = try arena.create(ast.NodeFnProto); |
| 583 | 740 | |
| 584 | 741 | *node = ast.NodeFnProto { |
| 585 | .base = ast.Node {.id = ast.Node.Id.FnProto}, | |
| 742 | .base = self.initNode(ast.Node.Id.FnProto), | |
| 586 | 743 | .visib_token = *visib_token, |
| 587 | 744 | .name_token = null, |
| 588 | 745 | .fn_token = *fn_token, |
| ... | ... | @@ -603,7 +760,7 @@ pub const Parser = struct { |
| 603 | 760 | const node = try arena.create(ast.NodeParamDecl); |
| 604 | 761 | |
| 605 | 762 | *node = ast.NodeParamDecl { |
| 606 | .base = ast.Node {.id = ast.Node.Id.ParamDecl}, | |
| 763 | .base = self.initNode(ast.Node.Id.ParamDecl), | |
| 607 | 764 | .comptime_token = null, |
| 608 | 765 | .noalias_token = null, |
| 609 | 766 | .name_token = null, |
| ... | ... | @@ -617,7 +774,7 @@ pub const Parser = struct { |
| 617 | 774 | const node = try arena.create(ast.NodeBlock); |
| 618 | 775 | |
| 619 | 776 | *node = ast.NodeBlock { |
| 620 | .base = ast.Node {.id = ast.Node.Id.Block}, | |
| 777 | .base = self.initNode(ast.Node.Id.Block), | |
| 621 | 778 | .begin_token = *begin_token, |
| 622 | 779 | .end_token = undefined, |
| 623 | 780 | .statements = ArrayList(&ast.Node).init(arena), |
| ... | ... | @@ -629,7 +786,7 @@ pub const Parser = struct { |
| 629 | 786 | const node = try arena.create(ast.NodeInfixOp); |
| 630 | 787 | |
| 631 | 788 | *node = ast.NodeInfixOp { |
| 632 | .base = ast.Node {.id = ast.Node.Id.InfixOp}, | |
| 789 | .base = self.initNode(ast.Node.Id.InfixOp), | |
| 633 | 790 | .op_token = *op_token, |
| 634 | 791 | .lhs = undefined, |
| 635 | 792 | .op = *op, |
| ... | ... | @@ -642,7 +799,7 @@ pub const Parser = struct { |
| 642 | 799 | const node = try arena.create(ast.NodePrefixOp); |
| 643 | 800 | |
| 644 | 801 | *node = ast.NodePrefixOp { |
| 645 | .base = ast.Node {.id = ast.Node.Id.PrefixOp}, | |
| 802 | .base = self.initNode(ast.Node.Id.PrefixOp), | |
| 646 | 803 | .op_token = *op_token, |
| 647 | 804 | .op = *op, |
| 648 | 805 | .rhs = undefined, |
| ... | ... | @@ -654,7 +811,7 @@ pub const Parser = struct { |
| 654 | 811 | const node = try arena.create(ast.NodeIdentifier); |
| 655 | 812 | |
| 656 | 813 | *node = ast.NodeIdentifier { |
| 657 | .base = ast.Node {.id = ast.Node.Id.Identifier}, | |
| 814 | .base = self.initNode(ast.Node.Id.Identifier), | |
| 658 | 815 | .name_token = *name_token, |
| 659 | 816 | }; |
| 660 | 817 | return node; |
| ... | ... | @@ -664,7 +821,7 @@ pub const Parser = struct { |
| 664 | 821 | const node = try arena.create(ast.NodeIntegerLiteral); |
| 665 | 822 | |
| 666 | 823 | *node = ast.NodeIntegerLiteral { |
| 667 | .base = ast.Node {.id = ast.Node.Id.IntegerLiteral}, | |
| 824 | .base = self.initNode(ast.Node.Id.IntegerLiteral), | |
| 668 | 825 | .token = *token, |
| 669 | 826 | }; |
| 670 | 827 | return node; |
| ... | ... | @@ -674,7 +831,7 @@ pub const Parser = struct { |
| 674 | 831 | const node = try arena.create(ast.NodeFloatLiteral); |
| 675 | 832 | |
| 676 | 833 | *node = ast.NodeFloatLiteral { |
| 677 | .base = ast.Node {.id = ast.Node.Id.FloatLiteral}, | |
| 834 | .base = self.initNode(ast.Node.Id.FloatLiteral), | |
| 678 | 835 | .token = *token, |
| 679 | 836 | }; |
| 680 | 837 | return node; |
| ... | ... | @@ -712,11 +869,11 @@ pub const Parser = struct { |
| 712 | 869 | |
| 713 | 870 | fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) (error{ParseError}) { |
| 714 | 871 | const loc = self.tokenizer.getTokenLocation(token); |
| 715 | warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args); | |
| 872 | warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, token.line + 1, token.column + 1, args); | |
| 716 | 873 | warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]); |
| 717 | 874 | { |
| 718 | 875 | var i: usize = 0; |
| 719 | while (i < loc.column) : (i += 1) { | |
| 876 | while (i < token.column) : (i += 1) { | |
| 720 | 877 | warn(" "); |
| 721 | 878 | } |
| 722 | 879 | } |
| ... | ... | @@ -808,11 +965,26 @@ pub const Parser = struct { |
| 808 | 965 | defer self.deinitUtilityArrayList(stack); |
| 809 | 966 | |
| 810 | 967 | { |
| 968 | try stack.append(RenderState { .Text = "\n"}); | |
| 969 | ||
| 811 | 970 | var i = root_node.decls.len; |
| 812 | 971 | while (i != 0) { |
| 813 | 972 | i -= 1; |
| 814 | 973 | const decl = root_node.decls.items[i]; |
| 815 | 974 | try stack.append(RenderState {.TopLevelDecl = decl}); |
| 975 | if (i != 0) { | |
| 976 | try stack.append(RenderState { | |
| 977 | .Text = blk: { | |
| 978 | const prev_node = root_node.decls.at(i - 1); | |
| 979 | const prev_line_index = prev_node.lastToken().line; | |
| 980 | const this_line_index = decl.firstToken().line; | |
| 981 | if (this_line_index - prev_line_index >= 2) { | |
| 982 | break :blk "\n\n"; | |
| 983 | } | |
| 984 | break :blk "\n"; | |
| 985 | }, | |
| 986 | }); | |
| 987 | } | |
| 816 | 988 | } |
| 817 | 989 | } |
| 818 | 990 | |
| ... | ... | @@ -842,7 +1014,6 @@ pub const Parser = struct { |
| 842 | 1014 | |
| 843 | 1015 | try stream.print("("); |
| 844 | 1016 | |
| 845 | try stack.append(RenderState { .Text = "\n" }); | |
| 846 | 1017 | if (fn_proto.body_node == null) { |
| 847 | 1018 | try stack.append(RenderState { .Text = ";" }); |
| 848 | 1019 | } |
| ... | ... | @@ -860,7 +1031,6 @@ pub const Parser = struct { |
| 860 | 1031 | }, |
| 861 | 1032 | ast.Node.Id.VarDecl => { |
| 862 | 1033 | const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", decl); |
| 863 | try stack.append(RenderState { .Text = "\n"}); | |
| 864 | 1034 | try stack.append(RenderState { .VarDecl = var_decl}); |
| 865 | 1035 | |
| 866 | 1036 | }, |
| ... | ... | @@ -927,19 +1097,35 @@ pub const Parser = struct { |
| 927 | 1097 | }, |
| 928 | 1098 | ast.Node.Id.Block => { |
| 929 | 1099 | const block = @fieldParentPtr(ast.NodeBlock, "base", base); |
| 930 | try stream.write("{"); | |
| 931 | try stack.append(RenderState { .Text = "}"}); | |
| 932 | try stack.append(RenderState.PrintIndent); | |
| 933 | try stack.append(RenderState { .Indent = indent}); | |
| 934 | try stack.append(RenderState { .Text = "\n"}); | |
| 935 | var i = block.statements.len; | |
| 936 | while (i != 0) { | |
| 937 | i -= 1; | |
| 938 | const statement_node = block.statements.items[i]; | |
| 939 | try stack.append(RenderState { .Statement = statement_node}); | |
| 1100 | if (block.statements.len == 0) { | |
| 1101 | try stream.write("{}"); | |
| 1102 | } else { | |
| 1103 | try stream.write("{"); | |
| 1104 | try stack.append(RenderState { .Text = "}"}); | |
| 940 | 1105 | try stack.append(RenderState.PrintIndent); |
| 941 | try stack.append(RenderState { .Indent = indent + indent_delta}); | |
| 942 | try stack.append(RenderState { .Text = "\n" }); | |
| 1106 | try stack.append(RenderState { .Indent = indent}); | |
| 1107 | try stack.append(RenderState { .Text = "\n"}); | |
| 1108 | var i = block.statements.len; | |
| 1109 | while (i != 0) { | |
| 1110 | i -= 1; | |
| 1111 | const statement_node = block.statements.items[i]; | |
| 1112 | try stack.append(RenderState { .Statement = statement_node}); | |
| 1113 | try stack.append(RenderState.PrintIndent); | |
| 1114 | try stack.append(RenderState { .Indent = indent + indent_delta}); | |
| 1115 | try stack.append(RenderState { | |
| 1116 | .Text = blk: { | |
| 1117 | if (i != 0) { | |
| 1118 | const prev_statement_node = block.statements.items[i - 1]; | |
| 1119 | const prev_line_index = prev_statement_node.lastToken().line; | |
| 1120 | const this_line_index = statement_node.firstToken().line; | |
| 1121 | if (this_line_index - prev_line_index >= 2) { | |
| 1122 | break :blk "\n\n"; | |
| 1123 | } | |
| 1124 | } | |
| 1125 | break :blk "\n"; | |
| 1126 | }, | |
| 1127 | }); | |
| 1128 | } | |
| 943 | 1129 | } |
| 944 | 1130 | }, |
| 945 | 1131 | ast.Node.Id.InfixOp => { |
| ... | ... | @@ -952,7 +1138,9 @@ pub const Parser = struct { |
| 952 | 1138 | ast.NodeInfixOp.InfixOp.BangEqual => { |
| 953 | 1139 | try stack.append(RenderState { .Text = " != "}); |
| 954 | 1140 | }, |
| 955 | else => unreachable, | |
| 1141 | ast.NodeInfixOp.InfixOp.Period => { | |
| 1142 | try stack.append(RenderState { .Text = "."}); | |
| 1143 | }, | |
| 956 | 1144 | } |
| 957 | 1145 | try stack.append(RenderState { .Expression = prefix_op_node.lhs }); |
| 958 | 1146 | }, |
| ... | ... | @@ -963,6 +1151,9 @@ pub const Parser = struct { |
| 963 | 1151 | ast.NodePrefixOp.PrefixOp.Return => { |
| 964 | 1152 | try stream.write("return "); |
| 965 | 1153 | }, |
| 1154 | ast.NodePrefixOp.PrefixOp.Try => { | |
| 1155 | try stream.write("try "); | |
| 1156 | }, | |
| 966 | 1157 | ast.NodePrefixOp.PrefixOp.AddrOf => |addr_of_info| { |
| 967 | 1158 | try stream.write("&"); |
| 968 | 1159 | if (addr_of_info.volatile_token != null) { |
| ... | ... | @@ -977,7 +1168,6 @@ pub const Parser = struct { |
| 977 | 1168 | try stack.append(RenderState { .Expression = align_expr}); |
| 978 | 1169 | } |
| 979 | 1170 | }, |
| 980 | else => unreachable, | |
| 981 | 1171 | } |
| 982 | 1172 | }, |
| 983 | 1173 | ast.Node.Id.IntegerLiteral => { |
| ... | ... | @@ -988,7 +1178,30 @@ pub const Parser = struct { |
| 988 | 1178 | const float_literal = @fieldParentPtr(ast.NodeFloatLiteral, "base", base); |
| 989 | 1179 | try stream.print("{}", self.tokenizer.getTokenSlice(float_literal.token)); |
| 990 | 1180 | }, |
| 991 | else => unreachable, | |
| 1181 | ast.Node.Id.StringLiteral => { | |
| 1182 | const string_literal = @fieldParentPtr(ast.NodeStringLiteral, "base", base); | |
| 1183 | try stream.print("{}", self.tokenizer.getTokenSlice(string_literal.token)); | |
| 1184 | }, | |
| 1185 | ast.Node.Id.BuiltinCall => { | |
| 1186 | const builtin_call = @fieldParentPtr(ast.NodeBuiltinCall, "base", base); | |
| 1187 | try stream.print("{}(", self.tokenizer.getTokenSlice(builtin_call.builtin_token)); | |
| 1188 | try stack.append(RenderState { .Text = ")"}); | |
| 1189 | var i = builtin_call.params.len; | |
| 1190 | while (i != 0) { | |
| 1191 | i -= 1; | |
| 1192 | const param_node = builtin_call.params.at(i); | |
| 1193 | try stack.append(RenderState { .Expression = param_node}); | |
| 1194 | if (i != 0) { | |
| 1195 | try stack.append(RenderState { .Text = ", " }); | |
| 1196 | } | |
| 1197 | } | |
| 1198 | }, | |
| 1199 | ast.Node.Id.FnProto => @panic("TODO fn proto in an expression"), | |
| 1200 | ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"), | |
| 1201 | ||
| 1202 | ast.Node.Id.Root, | |
| 1203 | ast.Node.Id.VarDecl, | |
| 1204 | ast.Node.Id.ParamDecl => unreachable, | |
| 992 | 1205 | }, |
| 993 | 1206 | RenderState.FnProtoRParen => |fn_proto| { |
| 994 | 1207 | try stream.print(")"); |
| ... | ... | @@ -1000,9 +1213,26 @@ pub const Parser = struct { |
| 1000 | 1213 | try stack.append(RenderState { .Expression = body_node}); |
| 1001 | 1214 | try stack.append(RenderState { .Text = " "}); |
| 1002 | 1215 | } |
| 1003 | try stack.append(RenderState { .Expression = fn_proto.return_type}); | |
| 1216 | switch (fn_proto.return_type) { | |
| 1217 | ast.NodeFnProto.ReturnType.Explicit => |node| { | |
| 1218 | try stack.append(RenderState { .Expression = node}); | |
| 1219 | }, | |
| 1220 | ast.NodeFnProto.ReturnType.Infer => { | |
| 1221 | try stream.print("var"); | |
| 1222 | }, | |
| 1223 | ast.NodeFnProto.ReturnType.InferErrorSet => |node| { | |
| 1224 | try stream.print("!"); | |
| 1225 | try stack.append(RenderState { .Expression = node}); | |
| 1226 | }, | |
| 1227 | } | |
| 1004 | 1228 | }, |
| 1005 | 1229 | RenderState.Statement => |base| { |
| 1230 | if (base.comment) |comment| { | |
| 1231 | for (comment.lines.toSliceConst()) |line_token| { | |
| 1232 | try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token)); | |
| 1233 | try stream.writeByteNTimes(' ', indent); | |
| 1234 | } | |
| 1235 | } | |
| 1006 | 1236 | switch (base.id) { |
| 1007 | 1237 | ast.Node.Id.VarDecl => { |
| 1008 | 1238 | const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base); |
| ... | ... | @@ -1040,10 +1270,7 @@ pub const Parser = struct { |
| 1040 | 1270 | var fixed_buffer_mem: [100 * 1024]u8 = undefined; |
| 1041 | 1271 | |
| 1042 | 1272 | fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 { |
| 1043 | var padded_source: [0x100]u8 = undefined; | |
| 1044 | std.mem.copy(u8, padded_source[0..source.len], source); | |
| 1045 | ||
| 1046 | var tokenizer = Tokenizer.init(padded_source[0..source.len]); | |
| 1273 | var tokenizer = Tokenizer.init(source); | |
| 1047 | 1274 | var parser = Parser.init(&tokenizer, allocator, "(memory buffer)"); |
| 1048 | 1275 | defer parser.deinit(); |
| 1049 | 1276 | |
| ... | ... | @@ -1098,6 +1325,43 @@ fn testCanonical(source: []const u8) !void { |
| 1098 | 1325 | } |
| 1099 | 1326 | |
| 1100 | 1327 | test "zig fmt" { |
| 1328 | try testCanonical( | |
| 1329 | \\const std = @import("std"); | |
| 1330 | \\ | |
| 1331 | \\pub fn main() !void { | |
| 1332 | \\ // If this program is run without stdout attached, exit with an error. | |
| 1333 | \\ // another comment | |
| 1334 | \\ var stdout_file = try std.io.getStdOut; | |
| 1335 | \\} | |
| 1336 | \\ | |
| 1337 | ); | |
| 1338 | ||
| 1339 | try testCanonical( | |
| 1340 | \\const std = @import("std"); | |
| 1341 | \\ | |
| 1342 | \\pub fn main() !void { | |
| 1343 | \\ var stdout_file = try std.io.getStdOut; | |
| 1344 | \\ var stdout_file = try std.io.getStdOut; | |
| 1345 | \\ | |
| 1346 | \\ var stdout_file = try std.io.getStdOut; | |
| 1347 | \\ var stdout_file = try std.io.getStdOut; | |
| 1348 | \\} | |
| 1349 | \\ | |
| 1350 | ); | |
| 1351 | ||
| 1352 | try testCanonical( | |
| 1353 | \\pub fn main() !void {} | |
| 1354 | \\pub fn main() var {} | |
| 1355 | \\pub fn main() i32 {} | |
| 1356 | \\ | |
| 1357 | ); | |
| 1358 | ||
| 1359 | try testCanonical( | |
| 1360 | \\const std = @import("std"); | |
| 1361 | \\const std = @import(); | |
| 1362 | \\ | |
| 1363 | ); | |
| 1364 | ||
| 1101 | 1365 | try testCanonical( |
| 1102 | 1366 | \\extern fn puts(s: &const u8) c_int; |
| 1103 | 1367 | \\ |
std/zig/tokenizer.zig+39-25| ... | ... | @@ -5,6 +5,8 @@ pub const Token = struct { |
| 5 | 5 | id: Id, |
| 6 | 6 | start: usize, |
| 7 | 7 | end: usize, |
| 8 | line: usize, | |
| 9 | column: usize, | |
| 8 | 10 | |
| 9 | 11 | const KeywordId = struct { |
| 10 | 12 | bytes: []const u8, |
| ... | ... | @@ -16,6 +18,7 @@ pub const Token = struct { |
| 16 | 18 | KeywordId{.bytes="and", .id = Id.Keyword_and}, |
| 17 | 19 | KeywordId{.bytes="asm", .id = Id.Keyword_asm}, |
| 18 | 20 | KeywordId{.bytes="break", .id = Id.Keyword_break}, |
| 21 | KeywordId{.bytes="catch", .id = Id.Keyword_catch}, | |
| 19 | 22 | KeywordId{.bytes="comptime", .id = Id.Keyword_comptime}, |
| 20 | 23 | KeywordId{.bytes="const", .id = Id.Keyword_const}, |
| 21 | 24 | KeywordId{.bytes="continue", .id = Id.Keyword_continue}, |
| ... | ... | @@ -28,7 +31,6 @@ pub const Token = struct { |
| 28 | 31 | KeywordId{.bytes="false", .id = Id.Keyword_false}, |
| 29 | 32 | KeywordId{.bytes="fn", .id = Id.Keyword_fn}, |
| 30 | 33 | KeywordId{.bytes="for", .id = Id.Keyword_for}, |
| 31 | KeywordId{.bytes="goto", .id = Id.Keyword_goto}, | |
| 32 | 34 | KeywordId{.bytes="if", .id = Id.Keyword_if}, |
| 33 | 35 | KeywordId{.bytes="inline", .id = Id.Keyword_inline}, |
| 34 | 36 | KeywordId{.bytes="nakedcc", .id = Id.Keyword_nakedcc}, |
| ... | ... | @@ -38,12 +40,14 @@ pub const Token = struct { |
| 38 | 40 | KeywordId{.bytes="packed", .id = Id.Keyword_packed}, |
| 39 | 41 | KeywordId{.bytes="pub", .id = Id.Keyword_pub}, |
| 40 | 42 | KeywordId{.bytes="return", .id = Id.Keyword_return}, |
| 43 | KeywordId{.bytes="section", .id = Id.Keyword_section}, | |
| 41 | 44 | KeywordId{.bytes="stdcallcc", .id = Id.Keyword_stdcallcc}, |
| 42 | 45 | KeywordId{.bytes="struct", .id = Id.Keyword_struct}, |
| 43 | 46 | KeywordId{.bytes="switch", .id = Id.Keyword_switch}, |
| 44 | 47 | KeywordId{.bytes="test", .id = Id.Keyword_test}, |
| 45 | 48 | KeywordId{.bytes="this", .id = Id.Keyword_this}, |
| 46 | 49 | KeywordId{.bytes="true", .id = Id.Keyword_true}, |
| 50 | KeywordId{.bytes="try", .id = Id.Keyword_try}, | |
| 47 | 51 | KeywordId{.bytes="undefined", .id = Id.Keyword_undefined}, |
| 48 | 52 | KeywordId{.bytes="union", .id = Id.Keyword_union}, |
| 49 | 53 | KeywordId{.bytes="unreachable", .id = Id.Keyword_unreachable}, |
| ... | ... | @@ -95,10 +99,12 @@ pub const Token = struct { |
| 95 | 99 | AmpersandEqual, |
| 96 | 100 | IntegerLiteral, |
| 97 | 101 | FloatLiteral, |
| 102 | LineComment, | |
| 98 | 103 | Keyword_align, |
| 99 | 104 | Keyword_and, |
| 100 | 105 | Keyword_asm, |
| 101 | 106 | Keyword_break, |
| 107 | Keyword_catch, | |
| 102 | 108 | Keyword_comptime, |
| 103 | 109 | Keyword_const, |
| 104 | 110 | Keyword_continue, |
| ... | ... | @@ -111,7 +117,6 @@ pub const Token = struct { |
| 111 | 117 | Keyword_false, |
| 112 | 118 | Keyword_fn, |
| 113 | 119 | Keyword_for, |
| 114 | Keyword_goto, | |
| 115 | 120 | Keyword_if, |
| 116 | 121 | Keyword_inline, |
| 117 | 122 | Keyword_nakedcc, |
| ... | ... | @@ -121,12 +126,14 @@ pub const Token = struct { |
| 121 | 126 | Keyword_packed, |
| 122 | 127 | Keyword_pub, |
| 123 | 128 | Keyword_return, |
| 129 | Keyword_section, | |
| 124 | 130 | Keyword_stdcallcc, |
| 125 | 131 | Keyword_struct, |
| 126 | 132 | Keyword_switch, |
| 127 | 133 | Keyword_test, |
| 128 | 134 | Keyword_this, |
| 129 | 135 | Keyword_true, |
| 136 | Keyword_try, | |
| 130 | 137 | Keyword_undefined, |
| 131 | 138 | Keyword_union, |
| 132 | 139 | Keyword_unreachable, |
| ... | ... | @@ -140,21 +147,19 @@ pub const Token = struct { |
| 140 | 147 | pub const Tokenizer = struct { |
| 141 | 148 | buffer: []const u8, |
| 142 | 149 | index: usize, |
| 150 | line: usize, | |
| 151 | column: usize, | |
| 143 | 152 | pending_invalid_token: ?Token, |
| 144 | 153 | |
| 145 | pub const Location = struct { | |
| 146 | line: usize, | |
| 147 | column: usize, | |
| 154 | pub const LineLocation = struct { | |
| 148 | 155 | line_start: usize, |
| 149 | 156 | line_end: usize, |
| 150 | 157 | }; |
| 151 | 158 | |
| 152 | pub fn getTokenLocation(self: &Tokenizer, token: &const Token) Location { | |
| 153 | var loc = Location { | |
| 154 | .line = 0, | |
| 155 | .column = 0, | |
| 159 | pub fn getTokenLocation(self: &Tokenizer, token: &const Token) LineLocation { | |
| 160 | var loc = LineLocation { | |
| 156 | 161 | .line_start = 0, |
| 157 | .line_end = 0, | |
| 162 | .line_end = self.buffer.len, | |
| 158 | 163 | }; |
| 159 | 164 | for (self.buffer) |c, i| { |
| 160 | 165 | if (i == token.start) { |
| ... | ... | @@ -163,11 +168,7 @@ pub const Tokenizer = struct { |
| 163 | 168 | return loc; |
| 164 | 169 | } |
| 165 | 170 | if (c == '\n') { |
| 166 | loc.line += 1; | |
| 167 | loc.column = 0; | |
| 168 | 171 | loc.line_start = i + 1; |
| 169 | } else { | |
| 170 | loc.column += 1; | |
| 171 | 172 | } |
| 172 | 173 | } |
| 173 | 174 | return loc; |
| ... | ... | @@ -182,6 +183,8 @@ pub const Tokenizer = struct { |
| 182 | 183 | return Tokenizer { |
| 183 | 184 | .buffer = buffer, |
| 184 | 185 | .index = 0, |
| 186 | .line = 0, | |
| 187 | .column = 0, | |
| 185 | 188 | .pending_invalid_token = null, |
| 186 | 189 | }; |
| 187 | 190 | } |
| ... | ... | @@ -222,13 +225,21 @@ pub const Tokenizer = struct { |
| 222 | 225 | .id = Token.Id.Eof, |
| 223 | 226 | .start = self.index, |
| 224 | 227 | .end = undefined, |
| 228 | .line = self.line, | |
| 229 | .column = self.column, | |
| 225 | 230 | }; |
| 226 | while (self.index < self.buffer.len) : (self.index += 1) { | |
| 231 | while (self.index < self.buffer.len) { | |
| 227 | 232 | const c = self.buffer[self.index]; |
| 228 | 233 | switch (state) { |
| 229 | 234 | State.Start => switch (c) { |
| 230 | ' ', '\n' => { | |
| 235 | ' ' => { | |
| 236 | result.start = self.index + 1; | |
| 237 | result.column += 1; | |
| 238 | }, | |
| 239 | '\n' => { | |
| 231 | 240 | result.start = self.index + 1; |
| 241 | result.line += 1; | |
| 242 | result.column = 0; | |
| 232 | 243 | }, |
| 233 | 244 | 'c' => { |
| 234 | 245 | state = State.C; |
| ... | ... | @@ -460,7 +471,7 @@ pub const Tokenizer = struct { |
| 460 | 471 | |
| 461 | 472 | State.Slash => switch (c) { |
| 462 | 473 | '/' => { |
| 463 | result.id = undefined; | |
| 474 | result.id = Token.Id.LineComment; | |
| 464 | 475 | state = State.LineComment; |
| 465 | 476 | }, |
| 466 | 477 | else => { |
| ... | ... | @@ -469,14 +480,7 @@ pub const Tokenizer = struct { |
| 469 | 480 | }, |
| 470 | 481 | }, |
| 471 | 482 | State.LineComment => switch (c) { |
| 472 | '\n' => { | |
| 473 | state = State.Start; | |
| 474 | result = Token { | |
| 475 | .id = Token.Id.Eof, | |
| 476 | .start = self.index + 1, | |
| 477 | .end = undefined, | |
| 478 | }; | |
| 479 | }, | |
| 483 | '\n' => break, | |
| 480 | 484 | else => self.checkLiteralCharacter(), |
| 481 | 485 | }, |
| 482 | 486 | State.Zero => switch (c) { |
| ... | ... | @@ -543,6 +547,14 @@ pub const Tokenizer = struct { |
| 543 | 547 | else => break, |
| 544 | 548 | }, |
| 545 | 549 | } |
| 550 | ||
| 551 | self.index += 1; | |
| 552 | if (c == '\n') { | |
| 553 | self.line += 1; | |
| 554 | self.column = 0; | |
| 555 | } else { | |
| 556 | self.column += 1; | |
| 557 | } | |
| 546 | 558 | } else if (self.index == self.buffer.len) { |
| 547 | 559 | switch (state) { |
| 548 | 560 | State.Start, |
| ... | ... | @@ -622,6 +634,8 @@ pub const Tokenizer = struct { |
| 622 | 634 | .id = Token.Id.Invalid, |
| 623 | 635 | .start = self.index, |
| 624 | 636 | .end = self.index + invalid_length, |
| 637 | .line = self.line, | |
| 638 | .column = self.column, | |
| 625 | 639 | }; |
| 626 | 640 | } |
| 627 | 641 |
test/behavior.zig+1| ... | ... | @@ -35,6 +35,7 @@ comptime { |
| 35 | 35 | _ = @import("cases/slice.zig"); |
| 36 | 36 | _ = @import("cases/struct.zig"); |
| 37 | 37 | _ = @import("cases/struct_contains_slice_of_itself.zig"); |
| 38 | _ = @import("cases/struct_contains_null_ptr_itself.zig"); | |
| 38 | 39 | _ = @import("cases/switch.zig"); |
| 39 | 40 | _ = @import("cases/switch_prong_err_enum.zig"); |
| 40 | 41 | _ = @import("cases/switch_prong_implicit_cast.zig"); |
test/cases/eval.zig+7| ... | ... | @@ -388,3 +388,10 @@ test "string literal used as comptime slice is memoized" { |
| 388 | 388 | comptime assert(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node); |
| 389 | 389 | comptime assert(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node); |
| 390 | 390 | } |
| 391 | ||
| 392 | test "comptime slice of undefined pointer of length 0" { | |
| 393 | const slice1 = (&i32)(undefined)[0..0]; | |
| 394 | assert(slice1.len == 0); | |
| 395 | const slice2 = (&i32)(undefined)[100..100]; | |
| 396 | assert(slice2.len == 0); | |
| 397 | } |
test/cases/math.zig+8-1| ... | ... | @@ -394,4 +394,11 @@ fn test_f128() void { |
| 394 | 394 | |
| 395 | 395 | fn should_not_be_zero(x: f128) void { |
| 396 | 396 | assert(x != 0.0); |
| 397 | } | |
| \ No newline at end of file | ||
| 397 | } | |
| 398 | ||
| 399 | test "comptime float rem int" { | |
| 400 | comptime { | |
| 401 | var x = f32(1) % 2; | |
| 402 | assert(x == 1.0); | |
| 403 | } | |
| 404 | } |
test/cases/misc.zig+17| ... | ... | @@ -499,12 +499,29 @@ test "@canImplicitCast" { |
| 499 | 499 | } |
| 500 | 500 | |
| 501 | 501 | test "@typeName" { |
| 502 | const Struct = struct { | |
| 503 | }; | |
| 504 | const Union = union { | |
| 505 | unused: u8, | |
| 506 | }; | |
| 507 | const Enum = enum { | |
| 508 | Unused, | |
| 509 | }; | |
| 502 | 510 | comptime { |
| 503 | 511 | assert(mem.eql(u8, @typeName(i64), "i64")); |
| 504 | 512 | assert(mem.eql(u8, @typeName(&usize), "&usize")); |
| 513 | // https://github.com/zig-lang/zig/issues/675 | |
| 514 | assert(mem.eql(u8, @typeName(TypeFromFn(u8)), "TypeFromFn(u8)")); | |
| 515 | assert(mem.eql(u8, @typeName(Struct), "Struct")); | |
| 516 | assert(mem.eql(u8, @typeName(Union), "Union")); | |
| 517 | assert(mem.eql(u8, @typeName(Enum), "Enum")); | |
| 505 | 518 | } |
| 506 | 519 | } |
| 507 | 520 | |
| 521 | fn TypeFromFn(comptime T: type) type { | |
| 522 | return struct {}; | |
| 523 | } | |
| 524 | ||
| 508 | 525 | test "volatile load and store" { |
| 509 | 526 | var number: i32 = 1234; |
| 510 | 527 | const ptr = (&volatile i32)(&number); |
test/cases/struct_contains_null_ptr_itself.zig created+22| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | const std = @import("std"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | ||
| 4 | test "struct contains null pointer which contains original struct" { | |
| 5 | var x: ?&NodeLineComment = null; | |
| 6 | assert(x == null); | |
| 7 | } | |
| 8 | ||
| 9 | pub const Node = struct { | |
| 10 | id: Id, | |
| 11 | comment: ?&NodeLineComment, | |
| 12 | ||
| 13 | pub const Id = enum { | |
| 14 | Root, | |
| 15 | LineComment, | |
| 16 | }; | |
| 17 | }; | |
| 18 | ||
| 19 | pub const NodeLineComment = struct { | |
| 20 | base: Node, | |
| 21 | }; | |
| 22 |
test/compile_errors.zig+20| ... | ... | @@ -1,6 +1,26 @@ |
| 1 | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | |
| 3 | 3 | pub fn addCases(cases: &tests.CompileErrorContext) void { |
| 4 | cases.add("comptime slice of undefined pointer non-zero len", | |
| 5 | \\export fn entry() void { | |
| 6 | \\ const slice = (&i32)(undefined)[0..1]; | |
| 7 | \\} | |
| 8 | , | |
| 9 | ".tmp_source.zig:2:36: error: non-zero length slice of undefined pointer"); | |
| 10 | ||
| 11 | cases.add("type checking function pointers", | |
| 12 | \\fn a(b: fn (&const u8) void) void { | |
| 13 | \\ b('a'); | |
| 14 | \\} | |
| 15 | \\fn c(d: u8) void { | |
| 16 | \\ @import("std").debug.warn("{c}\n", d); | |
| 17 | \\} | |
| 18 | \\export fn entry() void { | |
| 19 | \\ a(c); | |
| 20 | \\} | |
| 21 | , | |
| 22 | ".tmp_source.zig:8:7: error: expected type 'fn(&const u8) void', found 'fn(u8) void'"); | |
| 23 | ||
| 4 | 24 | cases.add("no else prong on switch on global error set", |
| 5 | 25 | \\export fn entry() void { |
| 6 | 26 | \\ foo(error.A); |