authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-02-23 12:56:41-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-02-23 12:56:41-05:00
log9cfd7dea19c4c54e2754119c04c06cd57cfb759d
tree3b7950d86f77467b43aa323f80563f678cf6d2fb
parent78bc62fd3415dc1db72c916075c9956fdec407aa
parentb66547e98c9034e52c5647735b47dc24939c8d15

Merge remote-tracking branch 'origin/master' into llvm6


22 files changed, 991 insertions(+), 183 deletions(-)

CMakeLists.txt+1
...@@ -429,6 +429,7 @@ set(ZIG_STD_FILES...@@ -429,6 +429,7 @@ set(ZIG_STD_FILES
429 "index.zig"429 "index.zig"
430 "io.zig"430 "io.zig"
431 "linked_list.zig"431 "linked_list.zig"
432 "macho.zig"
432 "math/acos.zig"433 "math/acos.zig"
433 "math/acosh.zig"434 "math/acosh.zig"
434 "math/asin.zig"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,17 +2278,16 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
2278 return;2278 return;
22792279
2280 if (struct_type->data.structure.zero_bits_loop_flag) {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 is2281 // If we get here it's due to recursion. This is a design flaw in the compiler,
2282 // not zero bits, and if abi_alignment == 0 we further conclude that the first field2282 // we should be able to still figure out alignment, but here we give up and say that
2283 // is a pointer to this very struct, or a function pointer with parameters that2283 // the alignment is pointer width, then assert that the first field is within that
2284 // reference such a type.2284 // alignment
2285 struct_type->data.structure.zero_bits_known = true;2285 struct_type->data.structure.zero_bits_known = true;
2286 if (struct_type->data.structure.abi_alignment == 0) {2286 if (struct_type->data.structure.abi_alignment == 0) {
2287 if (struct_type->data.structure.layout == ContainerLayoutPacked) {2287 if (struct_type->data.structure.layout == ContainerLayoutPacked) {
2288 struct_type->data.structure.abi_alignment = 1;2288 struct_type->data.structure.abi_alignment = 1;
2289 } else {2289 } else {
2290 struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref,2290 struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, LLVMPointerType(LLVMInt8Type(), 0));
2291 LLVMPointerType(LLVMInt8Type(), 0));
2292 }2291 }
2293 }2292 }
2294 return;2293 return;
...@@ -2352,11 +2351,17 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {...@@ -2352,11 +2351,17 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
2352 if (gen_field_index == 0) {2351 if (gen_field_index == 0) {
2353 if (struct_type->data.structure.layout == ContainerLayoutPacked) {2352 if (struct_type->data.structure.layout == ContainerLayoutPacked) {
2354 struct_type->data.structure.abi_alignment = 1;2353 struct_type->data.structure.abi_alignment = 1;
2355 } else {2354 } else if (struct_type->data.structure.abi_alignment == 0) {
2356 // Alignment of structs is the alignment of the first field, for now.2355 // Alignment of structs is the alignment of the first field, for now.
2357 // TODO change this when we re-order struct fields (issue #168)2356 // TODO change this when we re-order struct fields (issue #168)
2358 struct_type->data.structure.abi_alignment = get_abi_alignment(g, field_type);2357 struct_type->data.structure.abi_alignment = get_abi_alignment(g, field_type);
2359 assert(struct_type->data.structure.abi_alignment != 0);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 }
23622367
src/codegen.cpp+2
...@@ -4201,6 +4201,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c...@@ -4201,6 +4201,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
4201 continue;4201 continue;
4202 }4202 }
4203 ConstExprValue *field_val = &const_val->data.x_struct.fields[i];4203 ConstExprValue *field_val = &const_val->data.x_struct.fields[i];
4204 assert(field_val->type != nullptr);
4204 LLVMValueRef val = gen_const_val(g, field_val, "");4205 LLVMValueRef val = gen_const_val(g, field_val, "");
4205 fields[type_struct_field->gen_index] = val;4206 fields[type_struct_field->gen_index] = val;
4206 make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(field_val->type, val);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,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 case TypeTableEntryIdErrorUnion:4378 case TypeTableEntryIdErrorUnion:
4377 {4379 {
4378 TypeTableEntry *payload_type = type_entry->data.error_union.payload_type;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,7 +4172,13 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod
4172 buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));4172 buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));
4173 }4173 }
41744174
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 IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope);4179 IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope);
4180 irb->exec->name = old_exec_name;
4181
4176 if (init_value == irb->codegen->invalid_instruction)4182 if (init_value == irb->codegen->invalid_instruction)
4177 return init_value;4183 return init_value;
41784184
...@@ -6727,8 +6733,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry...@@ -6727,8 +6733,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
6727 result.id = ConstCastResultIdFnReturnType;6733 result.id = ConstCastResultIdFnReturnType;
6728 result.data.return_type = allocate_nonzero<ConstCastOnly>(1);6734 result.data.return_type = allocate_nonzero<ConstCastOnly>(1);
6729 *result.data.return_type = child;6735 *result.data.return_type = child;
6736 return result;
6730 }6737 }
6731 return result;
6732 }6738 }
6733 if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) {6739 if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) {
6734 result.id = ConstCastResultIdFnArgCount;6740 result.id = ConstCastResultIdFnArgCount;
...@@ -8183,7 +8189,7 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi...@@ -8183,7 +8189,7 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi
8183 }8189 }
81848190
8185 if (instr_is_comptime(value)) {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 if (!val)8193 if (!val)
8188 return ira->codegen->invalid_instruction;8194 return ira->codegen->invalid_instruction;
8189 bool final_is_const = (value->value.type->id == TypeTableEntryIdMetaType) ? is_const : true;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,15 +9981,18 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
9975 ok = bigint_cmp(&rem_result, &mod_result) == CmpEQ;9981 ok = bigint_cmp(&rem_result, &mod_result) == CmpEQ;
9976 }9982 }
9977 } else {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 // the division by zero error will be caught later, but we don't9988 // the division by zero error will be caught later, but we don't
9980 // have a remainder function ambiguity problem9989 // have a remainder function ambiguity problem
9981 ok = true;9990 ok = true;
9982 } else {9991 } else {
9983 ConstExprValue rem_result;9992 ConstExprValue rem_result;
9984 ConstExprValue mod_result;9993 ConstExprValue mod_result;
9985 float_rem(&rem_result, &op1->value, &op2->value);9994 float_rem(&rem_result, &op1->value, &casted_op2->value);
9986 float_mod(&mod_result, &op1->value, &op2->value);9995 float_mod(&mod_result, &op1->value, &casted_op2->value);
9987 ok = float_cmp(&rem_result, &mod_result) == CmpEQ;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,6 +14937,7 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
14928 ConstExprValue *parent_ptr;14937 ConstExprValue *parent_ptr;
14929 size_t abs_offset;14938 size_t abs_offset;
14930 size_t rel_end;14939 size_t rel_end;
14940 bool ptr_is_undef = false;
14931 if (array_type->id == TypeTableEntryIdArray) {14941 if (array_type->id == TypeTableEntryIdArray) {
14932 array_val = const_ptr_pointee(ira->codegen, &ptr_ptr->value);14942 array_val = const_ptr_pointee(ira->codegen, &ptr_ptr->value);
14933 abs_offset = 0;14943 abs_offset = 0;
...@@ -14935,7 +14945,12 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio...@@ -14935,7 +14945,12 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
14935 parent_ptr = nullptr;14945 parent_ptr = nullptr;
14936 } else if (array_type->id == TypeTableEntryIdPointer) {14946 } else if (array_type->id == TypeTableEntryIdPointer) {
14937 parent_ptr = const_ptr_pointee(ira->codegen, &ptr_ptr->value);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 case ConstPtrSpecialInvalid:14954 case ConstPtrSpecialInvalid:
14940 case ConstPtrSpecialDiscard:14955 case ConstPtrSpecialDiscard:
14941 zig_unreachable();14956 zig_unreachable();
...@@ -14989,7 +15004,7 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio...@@ -14989,7 +15004,7 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
14989 }15004 }
1499015005
14991 uint64_t start_scalar = bigint_as_unsigned(&casted_start->value.data.x_bigint);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 ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice"));15008 ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice"));
14994 return ira->codegen->builtin_types.entry_invalid;15009 return ira->codegen->builtin_types.entry_invalid;
14995 }15010 }
...@@ -15000,12 +15015,18 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio...@@ -15000,12 +15015,18 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
15000 } else {15015 } else {
15001 end_scalar = rel_end;15016 end_scalar = rel_end;
15002 }15017 }
15003 if (end_scalar > rel_end) {15018 if (!ptr_is_undef) {
15004 ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice"));15019 if (end_scalar > rel_end) {
15005 return ira->codegen->builtin_types.entry_invalid;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) {15028 if (ptr_is_undef && start_scalar != end_scalar) {
15008 ir_add_error(ira, &instruction->base, buf_sprintf("slice start is greater than end"));15029 ir_add_error(ira, &instruction->base, buf_sprintf("non-zero length slice of undefined pointer"));
15009 return ira->codegen->builtin_types.entry_invalid;15030 return ira->codegen->builtin_types.entry_invalid;
15010 }15031 }
1501115032
...@@ -15021,25 +15042,27 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio...@@ -15021,25 +15042,27 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
15021 if (array_type->id == TypeTableEntryIdArray) {15042 if (array_type->id == TypeTableEntryIdArray) {
15022 ptr_val->data.x_ptr.mut = ptr_ptr->value.data.x_ptr.mut;15043 ptr_val->data.x_ptr.mut = ptr_ptr->value.data.x_ptr.mut;
15023 }15044 }
15024 } else {15045 } else if (ptr_is_undef) {
15025 switch (parent_ptr->data.x_ptr.special) {15046 ptr_val->type = get_pointer_to_type(ira->codegen, parent_ptr->type->data.pointer.child_type,
15026 case ConstPtrSpecialInvalid:15047 slice_is_const(return_type));
15027 case ConstPtrSpecialDiscard:15048 ptr_val->special = ConstValSpecialUndef;
15028 zig_unreachable();15049 } else switch (parent_ptr->data.x_ptr.special) {
15029 case ConstPtrSpecialRef:15050 case ConstPtrSpecialInvalid:
15030 init_const_ptr_ref(ira->codegen, ptr_val,15051 case ConstPtrSpecialDiscard:
15031 parent_ptr->data.x_ptr.data.ref.pointee, slice_is_const(return_type));15052 zig_unreachable();
15032 break;15053 case ConstPtrSpecialRef:
15033 case ConstPtrSpecialBaseArray:15054 init_const_ptr_ref(ira->codegen, ptr_val,
15034 zig_unreachable();15055 parent_ptr->data.x_ptr.data.ref.pointee, slice_is_const(return_type));
15035 case ConstPtrSpecialBaseStruct:15056 break;
15036 zig_panic("TODO");15057 case ConstPtrSpecialBaseArray:
15037 case ConstPtrSpecialHardCodedAddr:15058 zig_unreachable();
15038 init_const_ptr_hard_coded_addr(ira->codegen, ptr_val,15059 case ConstPtrSpecialBaseStruct:
15039 parent_ptr->type->data.pointer.child_type,15060 zig_panic("TODO");
15040 parent_ptr->data.x_ptr.data.hard_coded_addr.addr + start_scalar,15061 case ConstPtrSpecialHardCodedAddr:
15041 slice_is_const(return_type));15062 init_const_ptr_hard_coded_addr(ira->codegen, ptr_val,
15042 }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 }
1504415067
15045 ConstExprValue *len_val = &out_val->data.x_struct.fields[slice_len_index];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,7 +125,6 @@ static const struct ZigKeyword zig_keywords[] = {
125 {"false", TokenIdKeywordFalse},125 {"false", TokenIdKeywordFalse},
126 {"fn", TokenIdKeywordFn},126 {"fn", TokenIdKeywordFn},
127 {"for", TokenIdKeywordFor},127 {"for", TokenIdKeywordFor},
128 {"goto", TokenIdKeywordGoto},
129 {"if", TokenIdKeywordIf},128 {"if", TokenIdKeywordIf},
130 {"inline", TokenIdKeywordInline},129 {"inline", TokenIdKeywordInline},
131 {"nakedcc", TokenIdKeywordNakedCC},130 {"nakedcc", TokenIdKeywordNakedCC},
...@@ -1542,7 +1541,6 @@ const char * token_name(TokenId id) {...@@ -1542,7 +1541,6 @@ const char * token_name(TokenId id) {
1542 case TokenIdKeywordFalse: return "false";1541 case TokenIdKeywordFalse: return "false";
1543 case TokenIdKeywordFn: return "fn";1542 case TokenIdKeywordFn: return "fn";
1544 case TokenIdKeywordFor: return "for";1543 case TokenIdKeywordFor: return "for";
1545 case TokenIdKeywordGoto: return "goto";
1546 case TokenIdKeywordIf: return "if";1544 case TokenIdKeywordIf: return "if";
1547 case TokenIdKeywordInline: return "inline";1545 case TokenIdKeywordInline: return "inline";
1548 case TokenIdKeywordNakedCC: return "nakedcc";1546 case TokenIdKeywordNakedCC: return "nakedcc";
src/tokenizer.hpp-1
...@@ -66,7 +66,6 @@ enum TokenId {...@@ -66,7 +66,6 @@ enum TokenId {
66 TokenIdKeywordFalse,66 TokenIdKeywordFalse,
67 TokenIdKeywordFn,67 TokenIdKeywordFn,
68 TokenIdKeywordFor,68 TokenIdKeywordFor,
69 TokenIdKeywordGoto,
70 TokenIdKeywordIf,69 TokenIdKeywordIf,
71 TokenIdKeywordInline,70 TokenIdKeywordInline,
72 TokenIdKeywordNakedCC,71 TokenIdKeywordNakedCC,
std/debug/index.zig+94-59
...@@ -5,6 +5,7 @@ const io = std.io;...@@ -5,6 +5,7 @@ const io = std.io;
5const os = std.os;5const os = std.os;
6const elf = std.elf;6const elf = std.elf;
7const DW = std.dwarf;7const DW = std.dwarf;
8const macho = std.macho;
8const ArrayList = std.ArrayList;9const ArrayList = std.ArrayList;
9const builtin = @import("builtin");10const builtin = @import("builtin");
1011
...@@ -47,7 +48,7 @@ pub fn getSelfDebugInfo() !&ElfStackTrace {...@@ -47,7 +48,7 @@ pub fn getSelfDebugInfo() !&ElfStackTrace {
47pub fn dumpCurrentStackTrace() void {48pub fn dumpCurrentStackTrace() void {
48 const stderr = getStderrStream() catch return;49 const stderr = getStderrStream() catch return;
49 const debug_info = getSelfDebugInfo() catch |err| {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 return;52 return;
52 };53 };
53 defer debug_info.close();54 defer debug_info.close();
...@@ -61,7 +62,7 @@ pub fn dumpCurrentStackTrace() void {...@@ -61,7 +62,7 @@ pub fn dumpCurrentStackTrace() void {
61pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) void {62pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) void {
62 const stderr = getStderrStream() catch return;63 const stderr = getStderrStream() catch return;
63 const debug_info = getSelfDebugInfo() catch |err| {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 return;66 return;
66 };67 };
67 defer debug_info.close();68 defer debug_info.close();
...@@ -180,43 +181,57 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator,...@@ -180,43 +181,57 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator,
180}181}
181182
182fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: usize) !void {183fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: usize) !void {
183 if (builtin.os == builtin.Os.windows) {
184 return error.UnsupportedDebugInfo;
185 }
186 // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal184 // TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal
187 // at compile time. I'll call it issue #313185 // at compile time. I'll call it issue #313
188 const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";186 const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
189187
190 const compile_unit = findCompileUnit(debug_info, address) catch {188 switch (builtin.os) {
191 try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",189 builtin.Os.windows => return error.UnsupportedDebugInfo,
192 address);190 builtin.Os.macosx => {
193 return;191 // TODO(bnoordhuis) It's theoretically possible to obtain the
194 };192 // compilation unit from the symbtab but it's not that useful
195 const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);193 // in practice because the compiler dumps everything in a single
196 if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| {194 // object file. Future improvement: use external dSYM data when
197 defer line_info.deinit();195 // available.
198 try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++196 const unknown = macho.Symbol { .name = "???", .address = address };
199 DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",197 const symbol = debug_info.symbol_table.search(address) ?? &unknown;
200 line_info.file_name, line_info.line, line_info.column,198 try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++
201 address, compile_unit_name);199 DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n",
202 if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {200 symbol.name, address);
203 if (line_info.column == 0) {201 },
204 try out_stream.write("\n");202 else => {
205 } else {203 const compile_unit = findCompileUnit(debug_info, address) catch {
206 {var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {204 try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
207 try out_stream.writeByte(' ');205 address);
208 }}206 return;
209 try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");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}
222237
...@@ -224,6 +239,7 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace {...@@ -224,6 +239,7 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace {
224 switch (builtin.object_format) {239 switch (builtin.object_format) {
225 builtin.ObjectFormat.elf => {240 builtin.ObjectFormat.elf => {
226 const st = try allocator.create(ElfStackTrace);241 const st = try allocator.create(ElfStackTrace);
242 errdefer allocator.destroy(st);
227 *st = ElfStackTrace {243 *st = ElfStackTrace {
228 .self_exe_file = undefined,244 .self_exe_file = undefined,
229 .elf = undefined,245 .elf = undefined,
...@@ -249,12 +265,22 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace {...@@ -249,12 +265,22 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace {
249 try scanAllCompileUnits(st);265 try scanAllCompileUnits(st);
250 return st;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 builtin.ObjectFormat.coff => {281 builtin.ObjectFormat.coff => {
253 return error.TodoSupportCoffDebugInfo;282 return error.TodoSupportCoffDebugInfo;
254 },283 },
255 builtin.ObjectFormat.macho => {
256 return error.TodoSupportMachoDebugInfo;
257 },
258 builtin.ObjectFormat.wasm => {284 builtin.ObjectFormat.wasm => {
259 return error.TodoSupportCOFFDebugInfo;285 return error.TodoSupportCOFFDebugInfo;
260 },286 },
...@@ -297,31 +323,40 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &con...@@ -297,31 +323,40 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &con
297 }323 }
298}324}
299325
300pub const ElfStackTrace = struct {326pub const ElfStackTrace = switch (builtin.os) {
301 self_exe_file: os.File,327 builtin.Os.macosx => struct {
302 elf: elf.Elf,328 symbol_table: macho.SymbolTable,
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 }
314329
315 pub fn readString(self: &ElfStackTrace) ![]u8 {330 pub fn close(self: &ElfStackTrace) void {
316 var in_file_stream = io.FileInStream.init(&self.self_exe_file);331 self.symbol_table.deinit();
317 const in_stream = &in_file_stream.stream;332 }
318 return readStringRaw(self.allocator(), in_stream);333 },
319 }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 }
320348
321 pub fn close(self: &ElfStackTrace) void {349 pub fn readString(self: &ElfStackTrace) ![]u8 {
322 self.self_exe_file.close();350 var in_file_stream = io.FileInStream.init(&self.self_exe_file);
323 self.elf.close();351 const in_stream = &in_file_stream.stream;
324 }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};
326361
327const PcRange = struct {362const PcRange = struct {
std/fmt/index.zig+4-6
...@@ -550,12 +550,6 @@ test "parse unsigned comptime" {...@@ -550,12 +550,6 @@ test "parse unsigned comptime" {
550 }550 }
551}551}
552552
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.
555const Struct = struct {
556 unused: u8,
557};
558
559test "fmt.format" {553test "fmt.format" {
560 {554 {
561 var buf1: [32]u8 = undefined;555 var buf1: [32]u8 = undefined;
...@@ -588,6 +582,10 @@ test "fmt.format" {...@@ -588,6 +582,10 @@ test "fmt.format" {
588 assert(mem.eql(u8, result, "u3: 5\n"));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 var buf1: [32]u8 = undefined;589 var buf1: [32]u8 = undefined;
592 const value = Struct {590 const value = Struct {
593 .unused = 42,591 .unused = 42,
std/index.zig+2
...@@ -21,6 +21,7 @@ pub const endian = @import("endian.zig");...@@ -21,6 +21,7 @@ pub const endian = @import("endian.zig");
21pub const fmt = @import("fmt/index.zig");21pub const fmt = @import("fmt/index.zig");
22pub const heap = @import("heap.zig");22pub const heap = @import("heap.zig");
23pub const io = @import("io.zig");23pub const io = @import("io.zig");
24pub const macho = @import("macho.zig");
24pub const math = @import("math/index.zig");25pub const math = @import("math/index.zig");
25pub const mem = @import("mem.zig");26pub const mem = @import("mem.zig");
26pub const net = @import("net.zig");27pub const net = @import("net.zig");
...@@ -51,6 +52,7 @@ test "std" {...@@ -51,6 +52,7 @@ test "std" {
51 _ = @import("endian.zig");52 _ = @import("endian.zig");
52 _ = @import("fmt/index.zig");53 _ = @import("fmt/index.zig");
53 _ = @import("io.zig");54 _ = @import("io.zig");
55 _ = @import("macho.zig");
54 _ = @import("math/index.zig");56 _ = @import("math/index.zig");
55 _ = @import("mem.zig");57 _ = @import("mem.zig");
56 _ = @import("heap.zig");58 _ = @import("heap.zig");
std/macho.zig created+170
...@@ -0,0 +1,170 @@
1const builtin = @import("builtin");
2const std = @import("index.zig");
3const io = std.io;
4const mem = std.mem;
5
6const MH_MAGIC_64 = 0xFEEDFACF;
7const MH_PIE = 0x200000;
8const LC_SYMTAB = 2;
9
10const 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
21const LoadCommand = packed struct {
22 cmd: u32,
23 cmdsize: u32,
24};
25
26const SymtabCommand = packed struct {
27 symoff: u32,
28 nsyms: u32,
29 stroff: u32,
30 strsize: u32,
31};
32
33const 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
41pub 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
50pub 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
86pub 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
161fn readNoEof(in: &io.FileInStream, comptime T: type, result: []T) !void {
162 return in.stream.readNoEof(([]u8)(result));
163}
164fn readOneNoEof(in: &io.FileInStream, comptime T: type, result: &T) !void {
165 return readNoEof(in, T, result[0..1]);
166}
167
168fn 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,6 +42,9 @@ pub const Allocator = struct {
42 fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29,42 fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29,
43 n: usize) ![]align(alignment) T43 n: usize) ![]align(alignment) T
44 {44 {
45 if (n == 0) {
46 return (&align(alignment) T)(undefined)[0..0];
47 }
45 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;48 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
46 const byte_slice = try self.allocFn(self, byte_count, alignment);49 const byte_slice = try self.allocFn(self, byte_count, alignment);
47 assert(byte_slice.len == byte_count);50 assert(byte_slice.len == byte_count);
...@@ -62,6 +65,10 @@ pub const Allocator = struct {...@@ -62,6 +65,10 @@ pub const Allocator = struct {
62 if (old_mem.len == 0) {65 if (old_mem.len == 0) {
63 return self.alloc(T, n);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 }
6572
66 const old_byte_slice = ([]u8)(old_mem);73 const old_byte_slice = ([]u8)(old_mem);
67 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;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,6 +329,8 @@ pub const TIOCGPKT = 0x80045438;
329pub const TIOCGPTLCK = 0x80045439;329pub const TIOCGPTLCK = 0x80045439;
330pub const TIOCGEXCL = 0x80045440;330pub const TIOCGEXCL = 0x80045440;
331331
332pub const EPOLL_CLOEXEC = O_CLOEXEC;
333
332pub const EPOLL_CTL_ADD = 1;334pub const EPOLL_CTL_ADD = 1;
333pub const EPOLL_CTL_DEL = 2;335pub const EPOLL_CTL_DEL = 2;
334pub const EPOLL_CTL_MOD = 3;336pub const EPOLL_CTL_MOD = 3;
...@@ -751,22 +753,31 @@ pub fn fstat(fd: i32, stat_buf: &Stat) usize {...@@ -751,22 +753,31 @@ pub fn fstat(fd: i32, stat_buf: &Stat) usize {
751 return arch.syscall2(arch.SYS_fstat, usize(fd), @ptrToInt(stat_buf));753 return arch.syscall2(arch.SYS_fstat, usize(fd), @ptrToInt(stat_buf));
752}754}
753755
754pub const epoll_data = u64;756pub const epoll_data = extern union {
757 ptr: usize,
758 fd: i32,
759 @"u32": u32,
760 @"u64": u64,
761};
755762
756pub const epoll_event = extern struct {763pub const epoll_event = extern struct {
757 events: u32,764 events: u32,
758 data: epoll_data765 data: epoll_data,
759};766};
760767
761pub fn epoll_create() usize {768pub fn epoll_create() usize {
762 return arch.syscall1(arch.SYS_epoll_create, usize(1));769 return epoll_create1(0);
770}
771
772pub fn epoll_create1(flags: usize) usize {
773 return arch.syscall1(arch.SYS_epoll_create1, flags);
763}774}
764775
765pub fn epoll_ctl(epoll_fd: i32, op: i32, fd: i32, ev: &epoll_event) usize {776pub fn epoll_ctl(epoll_fd: i32, op: i32, fd: i32, ev: &epoll_event) usize {
766 return arch.syscall4(arch.SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev));777 return arch.syscall4(arch.SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev));
767}778}
768779
769pub fn epoll_wait(epoll_fd: i32, events: &epoll_event, maxevents: i32, timeout: i32) usize {780pub fn epoll_wait(epoll_fd: i32, events: &epoll_event, maxevents: u32, timeout: i32) usize {
770 return arch.syscall4(arch.SYS_epoll_wait, usize(epoll_fd), @ptrToInt(events), usize(maxevents), usize(timeout));781 return arch.syscall4(arch.SYS_epoll_wait, usize(epoll_fd), @ptrToInt(events), usize(maxevents), usize(timeout));
771}782}
772783
std/os/linux/test.zig+1-1
...@@ -25,7 +25,7 @@ test "timer" {...@@ -25,7 +25,7 @@ test "timer" {
2525
26 var event = linux.epoll_event {26 var event = linux.epoll_event {
27 .events = linux.EPOLLIN | linux.EPOLLOUT | linux.EPOLLET,27 .events = linux.EPOLLIN | linux.EPOLLOUT | linux.EPOLLET,
28 .data = 028 .data = linux.epoll_data { .ptr = 0 },
29 };29 };
3030
31 err = linux.epoll_ctl(i32(epoll_fd), linux.EPOLL_CTL_ADD, i32(timer_fd), &event);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 +6,7 @@ const mem = std.mem;
66
7pub const Node = struct {7pub const Node = struct {
8 id: Id,8 id: Id,
9 comment: ?&NodeLineComment,
910
10 pub const Id = enum {11 pub const Id = enum {
11 Root,12 Root,
...@@ -18,7 +19,9 @@ pub const Node = struct {...@@ -18,7 +19,9 @@ pub const Node = struct {
18 PrefixOp,19 PrefixOp,
19 IntegerLiteral,20 IntegerLiteral,
20 FloatLiteral,21 FloatLiteral,
22 StringLiteral,
21 BuiltinCall,23 BuiltinCall,
24 LineComment,
22 };25 };
2326
24 pub fn iterate(base: &Node, index: usize) ?&Node {27 pub fn iterate(base: &Node, index: usize) ?&Node {
...@@ -33,7 +36,45 @@ pub const Node = struct {...@@ -33,7 +36,45 @@ pub const Node = struct {
33 Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index),36 Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index),
34 Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).iterate(index),37 Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).iterate(index),
35 Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).iterate(index),38 Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).iterate(index),
39 Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).iterate(index),
36 Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).iterate(index),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,6 +82,7 @@ pub const Node = struct {
41pub const NodeRoot = struct {82pub const NodeRoot = struct {
42 base: Node,83 base: Node,
43 decls: ArrayList(&Node),84 decls: ArrayList(&Node),
85 eof_token: Token,
4486
45 pub fn iterate(self: &NodeRoot, index: usize) ?&Node {87 pub fn iterate(self: &NodeRoot, index: usize) ?&Node {
46 if (index < self.decls.len) {88 if (index < self.decls.len) {
...@@ -48,6 +90,14 @@ pub const NodeRoot = struct {...@@ -48,6 +90,14 @@ pub const NodeRoot = struct {
48 }90 }
49 return null;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};
52102
53pub const NodeVarDecl = struct {103pub const NodeVarDecl = struct {
...@@ -62,6 +112,7 @@ pub const NodeVarDecl = struct {...@@ -62,6 +112,7 @@ pub const NodeVarDecl = struct {
62 type_node: ?&Node,112 type_node: ?&Node,
63 align_node: ?&Node,113 align_node: ?&Node,
64 init_node: ?&Node,114 init_node: ?&Node,
115 semicolon_token: Token,
65116
66 pub fn iterate(self: &NodeVarDecl, index: usize) ?&Node {117 pub fn iterate(self: &NodeVarDecl, index: usize) ?&Node {
67 var i = index;118 var i = index;
...@@ -83,6 +134,18 @@ pub const NodeVarDecl = struct {...@@ -83,6 +134,18 @@ pub const NodeVarDecl = struct {
83134
84 return null;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};
87150
88pub const NodeIdentifier = struct {151pub const NodeIdentifier = struct {
...@@ -92,6 +155,14 @@ pub const NodeIdentifier = struct {...@@ -92,6 +155,14 @@ pub const NodeIdentifier = struct {
92 pub fn iterate(self: &NodeIdentifier, index: usize) ?&Node {155 pub fn iterate(self: &NodeIdentifier, index: usize) ?&Node {
93 return null;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};
96167
97pub const NodeFnProto = struct {168pub const NodeFnProto = struct {
...@@ -100,7 +171,7 @@ pub const NodeFnProto = struct {...@@ -100,7 +171,7 @@ pub const NodeFnProto = struct {
100 fn_token: Token,171 fn_token: Token,
101 name_token: ?Token,172 name_token: ?Token,
102 params: ArrayList(&Node),173 params: ArrayList(&Node),
103 return_type: &Node,174 return_type: ReturnType,
104 var_args_token: ?Token,175 var_args_token: ?Token,
105 extern_token: ?Token,176 extern_token: ?Token,
106 inline_token: ?Token,177 inline_token: ?Token,
...@@ -109,6 +180,12 @@ pub const NodeFnProto = struct {...@@ -109,6 +180,12 @@ pub const NodeFnProto = struct {
109 lib_name: ?&Node, // populated if this is an extern declaration180 lib_name: ?&Node, // populated if this is an extern declaration
110 align_expr: ?&Node, // populated if align(A) is present181 align_expr: ?&Node, // populated if align(A) is present
111182
183 pub const ReturnType = union(enum) {
184 Explicit: &Node,
185 Infer: Token,
186 InferErrorSet: &Node,
187 };
188
112 pub fn iterate(self: &NodeFnProto, index: usize) ?&Node {189 pub fn iterate(self: &NodeFnProto, index: usize) ?&Node {
113 var i = index;190 var i = index;
114191
...@@ -117,8 +194,18 @@ pub const NodeFnProto = struct {...@@ -117,8 +194,18 @@ pub const NodeFnProto = struct {
117 i -= 1;194 i -= 1;
118 }195 }
119196
120 if (i < 1) return self.return_type;197 switch (self.return_type) {
121 i -= 1;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 }
122209
123 if (self.align_expr) |align_expr| {210 if (self.align_expr) |align_expr| {
124 if (i < 1) return align_expr;211 if (i < 1) return align_expr;
...@@ -135,6 +222,25 @@ pub const NodeFnProto = struct {...@@ -135,6 +222,25 @@ pub const NodeFnProto = struct {
135222
136 return null;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};
139245
140pub const NodeParamDecl = struct {246pub const NodeParamDecl = struct {
...@@ -153,6 +259,18 @@ pub const NodeParamDecl = struct {...@@ -153,6 +259,18 @@ pub const NodeParamDecl = struct {
153259
154 return null;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};
157275
158pub const NodeBlock = struct {276pub const NodeBlock = struct {
...@@ -169,6 +287,14 @@ pub const NodeBlock = struct {...@@ -169,6 +287,14 @@ pub const NodeBlock = struct {
169287
170 return null;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};
173299
174pub const NodeInfixOp = struct {300pub const NodeInfixOp = struct {
...@@ -181,6 +307,7 @@ pub const NodeInfixOp = struct {...@@ -181,6 +307,7 @@ pub const NodeInfixOp = struct {
181 const InfixOp = enum {307 const InfixOp = enum {
182 EqualEqual,308 EqualEqual,
183 BangEqual,309 BangEqual,
310 Period,
184 };311 };
185312
186 pub fn iterate(self: &NodeInfixOp, index: usize) ?&Node {313 pub fn iterate(self: &NodeInfixOp, index: usize) ?&Node {
...@@ -190,8 +317,9 @@ pub const NodeInfixOp = struct {...@@ -190,8 +317,9 @@ pub const NodeInfixOp = struct {
190 i -= 1;317 i -= 1;
191318
192 switch (self.op) {319 switch (self.op) {
193 InfixOp.EqualEqual => {},320 InfixOp.EqualEqual,
194 InfixOp.BangEqual => {},321 InfixOp.BangEqual,
322 InfixOp.Period => {},
195 }323 }
196324
197 if (i < 1) return self.rhs;325 if (i < 1) return self.rhs;
...@@ -199,6 +327,14 @@ pub const NodeInfixOp = struct {...@@ -199,6 +327,14 @@ pub const NodeInfixOp = struct {
199327
200 return null;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};
203339
204pub const NodePrefixOp = struct {340pub const NodePrefixOp = struct {
...@@ -209,6 +345,7 @@ pub const NodePrefixOp = struct {...@@ -209,6 +345,7 @@ pub const NodePrefixOp = struct {
209345
210 const PrefixOp = union(enum) {346 const PrefixOp = union(enum) {
211 Return,347 Return,
348 Try,
212 AddrOf: AddrOfInfo,349 AddrOf: AddrOfInfo,
213 };350 };
214 const AddrOfInfo = struct {351 const AddrOfInfo = struct {
...@@ -223,7 +360,8 @@ pub const NodePrefixOp = struct {...@@ -223,7 +360,8 @@ pub const NodePrefixOp = struct {
223 var i = index;360 var i = index;
224361
225 switch (self.op) {362 switch (self.op) {
226 PrefixOp.Return => {},363 PrefixOp.Return,
364 PrefixOp.Try => {},
227 PrefixOp.AddrOf => |addr_of_info| {365 PrefixOp.AddrOf => |addr_of_info| {
228 if (addr_of_info.align_expr) |align_expr| {366 if (addr_of_info.align_expr) |align_expr| {
229 if (i < 1) return align_expr;367 if (i < 1) return align_expr;
...@@ -237,6 +375,14 @@ pub const NodePrefixOp = struct {...@@ -237,6 +375,14 @@ pub const NodePrefixOp = struct {
237375
238 return null;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};
241387
242pub const NodeIntegerLiteral = struct {388pub const NodeIntegerLiteral = struct {
...@@ -246,6 +392,14 @@ pub const NodeIntegerLiteral = struct {...@@ -246,6 +392,14 @@ pub const NodeIntegerLiteral = struct {
246 pub fn iterate(self: &NodeIntegerLiteral, index: usize) ?&Node {392 pub fn iterate(self: &NodeIntegerLiteral, index: usize) ?&Node {
247 return null;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};
250404
251pub const NodeFloatLiteral = struct {405pub const NodeFloatLiteral = struct {
...@@ -255,12 +409,21 @@ pub const NodeFloatLiteral = struct {...@@ -255,12 +409,21 @@ pub const NodeFloatLiteral = struct {
255 pub fn iterate(self: &NodeFloatLiteral, index: usize) ?&Node {409 pub fn iterate(self: &NodeFloatLiteral, index: usize) ?&Node {
256 return null;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};
259421
260pub const NodeBuiltinCall = struct {422pub const NodeBuiltinCall = struct {
261 base: Node,423 base: Node,
262 builtin_token: Token,424 builtin_token: Token,
263 params: ArrayList(&Node),425 params: ArrayList(&Node),
426 rparen_token: Token,
264427
265 pub fn iterate(self: &NodeBuiltinCall, index: usize) ?&Node {428 pub fn iterate(self: &NodeBuiltinCall, index: usize) ?&Node {
266 var i = index;429 var i = index;
...@@ -270,4 +433,46 @@ pub const NodeBuiltinCall = struct {...@@ -270,4 +433,46 @@ pub const NodeBuiltinCall = struct {
270433
271 return null;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
446pub 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
463pub 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,6 +18,7 @@ pub const Parser = struct {
18 put_back_tokens: [2]Token,18 put_back_tokens: [2]Token,
19 put_back_count: usize,19 put_back_count: usize,
20 source_file_name: []const u8,20 source_file_name: []const u8,
21 pending_line_comment_node: ?&ast.NodeLineComment,
2122
22 pub const Tree = struct {23 pub const Tree = struct {
23 root_node: &ast.NodeRoot,24 root_node: &ast.NodeRoot,
...@@ -43,6 +44,7 @@ pub const Parser = struct {...@@ -43,6 +44,7 @@ pub const Parser = struct {
43 .put_back_count = 0,44 .put_back_count = 0,
44 .source_file_name = source_file_name,45 .source_file_name = source_file_name,
45 .utility_bytes = []align(utility_bytes_align) u8{},46 .utility_bytes = []align(utility_bytes_align) u8{},
47 .pending_line_comment_node = null,
46 };48 };
47 }49 }
4850
...@@ -69,6 +71,11 @@ pub const Parser = struct {...@@ -69,6 +71,11 @@ pub const Parser = struct {
69 }71 }
70 };72 };
7173
74 const ExpectTokenSave = struct {
75 id: Token.Id,
76 ptr: &Token,
77 };
78
72 const State = union(enum) {79 const State = union(enum) {
73 TopLevel,80 TopLevel,
74 TopLevelExtern: ?Token,81 TopLevelExtern: ?Token,
...@@ -85,13 +92,17 @@ pub const Parser = struct {...@@ -85,13 +92,17 @@ pub const Parser = struct {
85 VarDeclAlign: &ast.NodeVarDecl,92 VarDeclAlign: &ast.NodeVarDecl,
86 VarDeclEq: &ast.NodeVarDecl,93 VarDeclEq: &ast.NodeVarDecl,
87 ExpectToken: @TagType(Token.Id),94 ExpectToken: @TagType(Token.Id),
95 ExpectTokenSave: ExpectTokenSave,
88 FnProto: &ast.NodeFnProto,96 FnProto: &ast.NodeFnProto,
89 FnProtoAlign: &ast.NodeFnProto,97 FnProtoAlign: &ast.NodeFnProto,
98 FnProtoReturnType: &ast.NodeFnProto,
90 ParamDecl: &ast.NodeFnProto,99 ParamDecl: &ast.NodeFnProto,
91 ParamDeclComma,100 ParamDeclComma,
92 FnDef: &ast.NodeFnProto,101 FnDef: &ast.NodeFnProto,
93 Block: &ast.NodeBlock,102 Block: &ast.NodeBlock,
94 Statement: &ast.NodeBlock,103 Statement: &ast.NodeBlock,
104 ExprListItemOrEnd: &ArrayList(&ast.Node),
105 ExprListCommaOrEnd: &ArrayList(&ast.Node),
95 };106 };
96107
97 /// Returns an AST tree, allocated with the parser's allocator.108 /// Returns an AST tree, allocated with the parser's allocator.
...@@ -122,6 +133,33 @@ pub const Parser = struct {...@@ -122,6 +133,33 @@ pub const Parser = struct {
122 // warn("\n");133 // warn("\n");
123 //}134 //}
124135
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 // This gives us 1 free append that can't fail163 // This gives us 1 free append that can't fail
126 const state = stack.pop();164 const state = stack.pop();
127165
...@@ -133,7 +171,10 @@ pub const Parser = struct {...@@ -133,7 +171,10 @@ pub const Parser = struct {
133 stack.append(State { .TopLevelExtern = token }) catch unreachable;171 stack.append(State { .TopLevelExtern = token }) catch unreachable;
134 continue;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 else => {178 else => {
138 self.putBackToken(token);179 self.putBackToken(token);
139 stack.append(State { .TopLevelExtern = null }) catch unreachable;180 stack.append(State { .TopLevelExtern = null }) catch unreachable;
...@@ -176,7 +217,7 @@ pub const Parser = struct {...@@ -176,7 +217,7 @@ pub const Parser = struct {
176 stack.append(State.TopLevel) catch unreachable;217 stack.append(State.TopLevel) catch unreachable;
177 // TODO shouldn't need these casts218 // TODO shouldn't need these casts
178 const fn_proto = try self.createAttachFnProto(arena, &root_node.decls, token,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 try stack.append(State { .FnDef = fn_proto });221 try stack.append(State { .FnDef = fn_proto });
181 try stack.append(State { .FnProto = fn_proto });222 try stack.append(State { .FnProto = fn_proto });
182 continue;223 continue;
...@@ -228,13 +269,19 @@ pub const Parser = struct {...@@ -228,13 +269,19 @@ pub const Parser = struct {
228 const token = self.getNextToken();269 const token = self.getNextToken();
229 if (token.id == Token.Id.Equal) {270 if (token.id == Token.Id.Equal) {
230 var_decl.eq_token = token;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 try stack.append(State {278 try stack.append(State {
233 .Expression = DestPtr {.NullableField = &var_decl.init_node},279 .Expression = DestPtr {.NullableField = &var_decl.init_node},
234 });280 });
235 continue;281 continue;
236 }282 }
237 if (token.id == Token.Id.Semicolon) {283 if (token.id == Token.Id.Semicolon) {
284 var_decl.semicolon_token = token;
238 continue;285 continue;
239 }286 }
240 return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id));287 return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id));
...@@ -244,6 +291,11 @@ pub const Parser = struct {...@@ -244,6 +291,11 @@ pub const Parser = struct {
244 continue;291 continue;
245 },292 },
246293
294 State.ExpectTokenSave => |expect_token_save| {
295 *expect_token_save.ptr = try self.eatToken(expect_token_save.id);
296 continue;
297 },
298
247 State.Expression => |dest_ptr| {299 State.Expression => |dest_ptr| {
248 // save the dest_ptr for later300 // save the dest_ptr for later
249 stack.append(state) catch unreachable;301 stack.append(state) catch unreachable;
...@@ -261,6 +313,12 @@ pub const Parser = struct {...@@ -261,6 +313,12 @@ pub const Parser = struct {
261 try stack.append(State.ExpectOperand);313 try stack.append(State.ExpectOperand);
262 continue;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 Token.Id.Ampersand => {322 Token.Id.Ampersand => {
265 const prefix_op = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{323 const prefix_op = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{
266 .AddrOf = ast.NodePrefixOp.AddrOfInfo {324 .AddrOf = ast.NodePrefixOp.AddrOfInfo {
...@@ -297,6 +355,40 @@ pub const Parser = struct {...@@ -297,6 +355,40 @@ pub const Parser = struct {
297 try stack.append(State.AfterOperand);355 try stack.append(State.AfterOperand);
298 continue;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 else => return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)),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,6 +413,13 @@ pub const Parser = struct {
321 try stack.append(State.ExpectOperand);413 try stack.append(State.ExpectOperand);
322 continue;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 else => {423 else => {
325 // no postfix/infix operator after this operand.424 // no postfix/infix operator after this operand.
326 self.putBackToken(token);425 self.putBackToken(token);
...@@ -352,6 +451,29 @@ pub const Parser = struct {...@@ -352,6 +451,29 @@ pub const Parser = struct {
352 }451 }
353 },452 },
354453
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 State.AddrOfModifiers => |addr_of_info| {477 State.AddrOfModifiers => |addr_of_info| {
356 var token = self.getNextToken();478 var token = self.getNextToken();
357 switch (token.id) {479 switch (token.id) {
...@@ -414,11 +536,37 @@ pub const Parser = struct {...@@ -414,11 +536,37 @@ pub const Parser = struct {
414 }536 }
415 self.putBackToken(token);537 self.putBackToken(token);
416 stack.append(State {538 stack.append(State {
417 .TypeExpr = DestPtr {.Field = &fn_proto.return_type},539 .FnProtoReturnType = fn_proto,
418 }) catch unreachable;540 }) catch unreachable;
419 continue;541 continue;
420 },542 },
421543
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 State.ParamDecl => |fn_proto| {570 State.ParamDecl => |fn_proto| {
423 var token = self.getNextToken();571 var token = self.getNextToken();
424 if (token.id == Token.Id.RParen) {572 if (token.id == Token.Id.RParen) {
...@@ -539,17 +687,25 @@ pub const Parser = struct {...@@ -539,17 +687,25 @@ pub const Parser = struct {
539 State.PrefixOp => unreachable,687 State.PrefixOp => unreachable,
540 State.Operand => unreachable,688 State.Operand => unreachable,
541 }689 }
542 @import("std").debug.panic("{}", @tagName(state));
543 //unreachable;
544 }690 }
545 }691 }
546692
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 fn createRoot(self: &Parser, arena: &mem.Allocator) !&ast.NodeRoot {701 fn createRoot(self: &Parser, arena: &mem.Allocator) !&ast.NodeRoot {
548 const node = try arena.create(ast.NodeRoot);702 const node = try arena.create(ast.NodeRoot);
549703
550 *node = ast.NodeRoot {704 *node = ast.NodeRoot {
551 .base = ast.Node {.id = ast.Node.Id.Root},705 .base = self.initNode(ast.Node.Id.Root),
552 .decls = ArrayList(&ast.Node).init(arena),706 .decls = ArrayList(&ast.Node).init(arena),
707 // initialized when we get the eof token
708 .eof_token = undefined,
553 };709 };
554 return node;710 return node;
555 }711 }
...@@ -560,7 +716,7 @@ pub const Parser = struct {...@@ -560,7 +716,7 @@ pub const Parser = struct {
560 const node = try arena.create(ast.NodeVarDecl);716 const node = try arena.create(ast.NodeVarDecl);
561717
562 *node = ast.NodeVarDecl {718 *node = ast.NodeVarDecl {
563 .base = ast.Node {.id = ast.Node.Id.VarDecl},719 .base = self.initNode(ast.Node.Id.VarDecl),
564 .visib_token = *visib_token,720 .visib_token = *visib_token,
565 .mut_token = *mut_token,721 .mut_token = *mut_token,
566 .comptime_token = *comptime_token,722 .comptime_token = *comptime_token,
...@@ -572,6 +728,7 @@ pub const Parser = struct {...@@ -572,6 +728,7 @@ pub const Parser = struct {
572 // initialized later728 // initialized later
573 .name_token = undefined,729 .name_token = undefined,
574 .eq_token = undefined,730 .eq_token = undefined,
731 .semicolon_token = undefined,
575 };732 };
576 return node;733 return node;
577 }734 }
...@@ -582,7 +739,7 @@ pub const Parser = struct {...@@ -582,7 +739,7 @@ pub const Parser = struct {
582 const node = try arena.create(ast.NodeFnProto);739 const node = try arena.create(ast.NodeFnProto);
583740
584 *node = ast.NodeFnProto {741 *node = ast.NodeFnProto {
585 .base = ast.Node {.id = ast.Node.Id.FnProto},742 .base = self.initNode(ast.Node.Id.FnProto),
586 .visib_token = *visib_token,743 .visib_token = *visib_token,
587 .name_token = null,744 .name_token = null,
588 .fn_token = *fn_token,745 .fn_token = *fn_token,
...@@ -603,7 +760,7 @@ pub const Parser = struct {...@@ -603,7 +760,7 @@ pub const Parser = struct {
603 const node = try arena.create(ast.NodeParamDecl);760 const node = try arena.create(ast.NodeParamDecl);
604761
605 *node = ast.NodeParamDecl {762 *node = ast.NodeParamDecl {
606 .base = ast.Node {.id = ast.Node.Id.ParamDecl},763 .base = self.initNode(ast.Node.Id.ParamDecl),
607 .comptime_token = null,764 .comptime_token = null,
608 .noalias_token = null,765 .noalias_token = null,
609 .name_token = null,766 .name_token = null,
...@@ -617,7 +774,7 @@ pub const Parser = struct {...@@ -617,7 +774,7 @@ pub const Parser = struct {
617 const node = try arena.create(ast.NodeBlock);774 const node = try arena.create(ast.NodeBlock);
618775
619 *node = ast.NodeBlock {776 *node = ast.NodeBlock {
620 .base = ast.Node {.id = ast.Node.Id.Block},777 .base = self.initNode(ast.Node.Id.Block),
621 .begin_token = *begin_token,778 .begin_token = *begin_token,
622 .end_token = undefined,779 .end_token = undefined,
623 .statements = ArrayList(&ast.Node).init(arena),780 .statements = ArrayList(&ast.Node).init(arena),
...@@ -629,7 +786,7 @@ pub const Parser = struct {...@@ -629,7 +786,7 @@ pub const Parser = struct {
629 const node = try arena.create(ast.NodeInfixOp);786 const node = try arena.create(ast.NodeInfixOp);
630787
631 *node = ast.NodeInfixOp {788 *node = ast.NodeInfixOp {
632 .base = ast.Node {.id = ast.Node.Id.InfixOp},789 .base = self.initNode(ast.Node.Id.InfixOp),
633 .op_token = *op_token,790 .op_token = *op_token,
634 .lhs = undefined,791 .lhs = undefined,
635 .op = *op,792 .op = *op,
...@@ -642,7 +799,7 @@ pub const Parser = struct {...@@ -642,7 +799,7 @@ pub const Parser = struct {
642 const node = try arena.create(ast.NodePrefixOp);799 const node = try arena.create(ast.NodePrefixOp);
643800
644 *node = ast.NodePrefixOp {801 *node = ast.NodePrefixOp {
645 .base = ast.Node {.id = ast.Node.Id.PrefixOp},802 .base = self.initNode(ast.Node.Id.PrefixOp),
646 .op_token = *op_token,803 .op_token = *op_token,
647 .op = *op,804 .op = *op,
648 .rhs = undefined,805 .rhs = undefined,
...@@ -654,7 +811,7 @@ pub const Parser = struct {...@@ -654,7 +811,7 @@ pub const Parser = struct {
654 const node = try arena.create(ast.NodeIdentifier);811 const node = try arena.create(ast.NodeIdentifier);
655812
656 *node = ast.NodeIdentifier {813 *node = ast.NodeIdentifier {
657 .base = ast.Node {.id = ast.Node.Id.Identifier},814 .base = self.initNode(ast.Node.Id.Identifier),
658 .name_token = *name_token,815 .name_token = *name_token,
659 };816 };
660 return node;817 return node;
...@@ -664,7 +821,7 @@ pub const Parser = struct {...@@ -664,7 +821,7 @@ pub const Parser = struct {
664 const node = try arena.create(ast.NodeIntegerLiteral);821 const node = try arena.create(ast.NodeIntegerLiteral);
665822
666 *node = ast.NodeIntegerLiteral {823 *node = ast.NodeIntegerLiteral {
667 .base = ast.Node {.id = ast.Node.Id.IntegerLiteral},824 .base = self.initNode(ast.Node.Id.IntegerLiteral),
668 .token = *token,825 .token = *token,
669 };826 };
670 return node;827 return node;
...@@ -674,7 +831,7 @@ pub const Parser = struct {...@@ -674,7 +831,7 @@ pub const Parser = struct {
674 const node = try arena.create(ast.NodeFloatLiteral);831 const node = try arena.create(ast.NodeFloatLiteral);
675832
676 *node = ast.NodeFloatLiteral {833 *node = ast.NodeFloatLiteral {
677 .base = ast.Node {.id = ast.Node.Id.FloatLiteral},834 .base = self.initNode(ast.Node.Id.FloatLiteral),
678 .token = *token,835 .token = *token,
679 };836 };
680 return node;837 return node;
...@@ -712,11 +869,11 @@ pub const Parser = struct {...@@ -712,11 +869,11 @@ pub const Parser = struct {
712869
713 fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) (error{ParseError}) {870 fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) (error{ParseError}) {
714 const loc = self.tokenizer.getTokenLocation(token);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 warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]);873 warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]);
717 {874 {
718 var i: usize = 0;875 var i: usize = 0;
719 while (i < loc.column) : (i += 1) {876 while (i < token.column) : (i += 1) {
720 warn(" ");877 warn(" ");
721 }878 }
722 }879 }
...@@ -808,11 +965,26 @@ pub const Parser = struct {...@@ -808,11 +965,26 @@ pub const Parser = struct {
808 defer self.deinitUtilityArrayList(stack);965 defer self.deinitUtilityArrayList(stack);
809966
810 {967 {
968 try stack.append(RenderState { .Text = "\n"});
969
811 var i = root_node.decls.len;970 var i = root_node.decls.len;
812 while (i != 0) {971 while (i != 0) {
813 i -= 1;972 i -= 1;
814 const decl = root_node.decls.items[i];973 const decl = root_node.decls.items[i];
815 try stack.append(RenderState {.TopLevelDecl = decl});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 }
818990
...@@ -842,7 +1014,6 @@ pub const Parser = struct {...@@ -842,7 +1014,6 @@ pub const Parser = struct {
8421014
843 try stream.print("(");1015 try stream.print("(");
8441016
845 try stack.append(RenderState { .Text = "\n" });
846 if (fn_proto.body_node == null) {1017 if (fn_proto.body_node == null) {
847 try stack.append(RenderState { .Text = ";" });1018 try stack.append(RenderState { .Text = ";" });
848 }1019 }
...@@ -860,7 +1031,6 @@ pub const Parser = struct {...@@ -860,7 +1031,6 @@ pub const Parser = struct {
860 },1031 },
861 ast.Node.Id.VarDecl => {1032 ast.Node.Id.VarDecl => {
862 const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", decl);1033 const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", decl);
863 try stack.append(RenderState { .Text = "\n"});
864 try stack.append(RenderState { .VarDecl = var_decl});1034 try stack.append(RenderState { .VarDecl = var_decl});
8651035
866 },1036 },
...@@ -927,19 +1097,35 @@ pub const Parser = struct {...@@ -927,19 +1097,35 @@ pub const Parser = struct {
927 },1097 },
928 ast.Node.Id.Block => {1098 ast.Node.Id.Block => {
929 const block = @fieldParentPtr(ast.NodeBlock, "base", base);1099 const block = @fieldParentPtr(ast.NodeBlock, "base", base);
930 try stream.write("{");1100 if (block.statements.len == 0) {
931 try stack.append(RenderState { .Text = "}"});1101 try stream.write("{}");
932 try stack.append(RenderState.PrintIndent);1102 } else {
933 try stack.append(RenderState { .Indent = indent});1103 try stream.write("{");
934 try stack.append(RenderState { .Text = "\n"});1104 try stack.append(RenderState { .Text = "}"});
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});
940 try stack.append(RenderState.PrintIndent);1105 try stack.append(RenderState.PrintIndent);
941 try stack.append(RenderState { .Indent = indent + indent_delta});1106 try stack.append(RenderState { .Indent = indent});
942 try stack.append(RenderState { .Text = "\n" });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 ast.Node.Id.InfixOp => {1131 ast.Node.Id.InfixOp => {
...@@ -952,7 +1138,9 @@ pub const Parser = struct {...@@ -952,7 +1138,9 @@ pub const Parser = struct {
952 ast.NodeInfixOp.InfixOp.BangEqual => {1138 ast.NodeInfixOp.InfixOp.BangEqual => {
953 try stack.append(RenderState { .Text = " != "});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 try stack.append(RenderState { .Expression = prefix_op_node.lhs });1145 try stack.append(RenderState { .Expression = prefix_op_node.lhs });
958 },1146 },
...@@ -963,6 +1151,9 @@ pub const Parser = struct {...@@ -963,6 +1151,9 @@ pub const Parser = struct {
963 ast.NodePrefixOp.PrefixOp.Return => {1151 ast.NodePrefixOp.PrefixOp.Return => {
964 try stream.write("return ");1152 try stream.write("return ");
965 },1153 },
1154 ast.NodePrefixOp.PrefixOp.Try => {
1155 try stream.write("try ");
1156 },
966 ast.NodePrefixOp.PrefixOp.AddrOf => |addr_of_info| {1157 ast.NodePrefixOp.PrefixOp.AddrOf => |addr_of_info| {
967 try stream.write("&");1158 try stream.write("&");
968 if (addr_of_info.volatile_token != null) {1159 if (addr_of_info.volatile_token != null) {
...@@ -977,7 +1168,6 @@ pub const Parser = struct {...@@ -977,7 +1168,6 @@ pub const Parser = struct {
977 try stack.append(RenderState { .Expression = align_expr});1168 try stack.append(RenderState { .Expression = align_expr});
978 }1169 }
979 },1170 },
980 else => unreachable,
981 }1171 }
982 },1172 },
983 ast.Node.Id.IntegerLiteral => {1173 ast.Node.Id.IntegerLiteral => {
...@@ -988,7 +1178,30 @@ pub const Parser = struct {...@@ -988,7 +1178,30 @@ pub const Parser = struct {
988 const float_literal = @fieldParentPtr(ast.NodeFloatLiteral, "base", base);1178 const float_literal = @fieldParentPtr(ast.NodeFloatLiteral, "base", base);
989 try stream.print("{}", self.tokenizer.getTokenSlice(float_literal.token));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 RenderState.FnProtoRParen => |fn_proto| {1206 RenderState.FnProtoRParen => |fn_proto| {
994 try stream.print(")");1207 try stream.print(")");
...@@ -1000,9 +1213,26 @@ pub const Parser = struct {...@@ -1000,9 +1213,26 @@ pub const Parser = struct {
1000 try stack.append(RenderState { .Expression = body_node});1213 try stack.append(RenderState { .Expression = body_node});
1001 try stack.append(RenderState { .Text = " "});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 RenderState.Statement => |base| {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 switch (base.id) {1236 switch (base.id) {
1007 ast.Node.Id.VarDecl => {1237 ast.Node.Id.VarDecl => {
1008 const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base);1238 const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base);
...@@ -1040,10 +1270,7 @@ pub const Parser = struct {...@@ -1040,10 +1270,7 @@ pub const Parser = struct {
1040var fixed_buffer_mem: [100 * 1024]u8 = undefined;1270var fixed_buffer_mem: [100 * 1024]u8 = undefined;
10411271
1042fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {1272fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {
1043 var padded_source: [0x100]u8 = undefined;1273 var tokenizer = Tokenizer.init(source);
1044 std.mem.copy(u8, padded_source[0..source.len], source);
1045
1046 var tokenizer = Tokenizer.init(padded_source[0..source.len]);
1047 var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");1274 var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
1048 defer parser.deinit();1275 defer parser.deinit();
10491276
...@@ -1098,6 +1325,43 @@ fn testCanonical(source: []const u8) !void {...@@ -1098,6 +1325,43 @@ fn testCanonical(source: []const u8) !void {
1098}1325}
10991326
1100test "zig fmt" {1327test "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 try testCanonical(1365 try testCanonical(
1102 \\extern fn puts(s: &const u8) c_int;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,6 +5,8 @@ pub const Token = struct {
5 id: Id,5 id: Id,
6 start: usize,6 start: usize,
7 end: usize,7 end: usize,
8 line: usize,
9 column: usize,
810
9 const KeywordId = struct {11 const KeywordId = struct {
10 bytes: []const u8,12 bytes: []const u8,
...@@ -16,6 +18,7 @@ pub const Token = struct {...@@ -16,6 +18,7 @@ pub const Token = struct {
16 KeywordId{.bytes="and", .id = Id.Keyword_and},18 KeywordId{.bytes="and", .id = Id.Keyword_and},
17 KeywordId{.bytes="asm", .id = Id.Keyword_asm},19 KeywordId{.bytes="asm", .id = Id.Keyword_asm},
18 KeywordId{.bytes="break", .id = Id.Keyword_break},20 KeywordId{.bytes="break", .id = Id.Keyword_break},
21 KeywordId{.bytes="catch", .id = Id.Keyword_catch},
19 KeywordId{.bytes="comptime", .id = Id.Keyword_comptime},22 KeywordId{.bytes="comptime", .id = Id.Keyword_comptime},
20 KeywordId{.bytes="const", .id = Id.Keyword_const},23 KeywordId{.bytes="const", .id = Id.Keyword_const},
21 KeywordId{.bytes="continue", .id = Id.Keyword_continue},24 KeywordId{.bytes="continue", .id = Id.Keyword_continue},
...@@ -28,7 +31,6 @@ pub const Token = struct {...@@ -28,7 +31,6 @@ pub const Token = struct {
28 KeywordId{.bytes="false", .id = Id.Keyword_false},31 KeywordId{.bytes="false", .id = Id.Keyword_false},
29 KeywordId{.bytes="fn", .id = Id.Keyword_fn},32 KeywordId{.bytes="fn", .id = Id.Keyword_fn},
30 KeywordId{.bytes="for", .id = Id.Keyword_for},33 KeywordId{.bytes="for", .id = Id.Keyword_for},
31 KeywordId{.bytes="goto", .id = Id.Keyword_goto},
32 KeywordId{.bytes="if", .id = Id.Keyword_if},34 KeywordId{.bytes="if", .id = Id.Keyword_if},
33 KeywordId{.bytes="inline", .id = Id.Keyword_inline},35 KeywordId{.bytes="inline", .id = Id.Keyword_inline},
34 KeywordId{.bytes="nakedcc", .id = Id.Keyword_nakedcc},36 KeywordId{.bytes="nakedcc", .id = Id.Keyword_nakedcc},
...@@ -38,12 +40,14 @@ pub const Token = struct {...@@ -38,12 +40,14 @@ pub const Token = struct {
38 KeywordId{.bytes="packed", .id = Id.Keyword_packed},40 KeywordId{.bytes="packed", .id = Id.Keyword_packed},
39 KeywordId{.bytes="pub", .id = Id.Keyword_pub},41 KeywordId{.bytes="pub", .id = Id.Keyword_pub},
40 KeywordId{.bytes="return", .id = Id.Keyword_return},42 KeywordId{.bytes="return", .id = Id.Keyword_return},
43 KeywordId{.bytes="section", .id = Id.Keyword_section},
41 KeywordId{.bytes="stdcallcc", .id = Id.Keyword_stdcallcc},44 KeywordId{.bytes="stdcallcc", .id = Id.Keyword_stdcallcc},
42 KeywordId{.bytes="struct", .id = Id.Keyword_struct},45 KeywordId{.bytes="struct", .id = Id.Keyword_struct},
43 KeywordId{.bytes="switch", .id = Id.Keyword_switch},46 KeywordId{.bytes="switch", .id = Id.Keyword_switch},
44 KeywordId{.bytes="test", .id = Id.Keyword_test},47 KeywordId{.bytes="test", .id = Id.Keyword_test},
45 KeywordId{.bytes="this", .id = Id.Keyword_this},48 KeywordId{.bytes="this", .id = Id.Keyword_this},
46 KeywordId{.bytes="true", .id = Id.Keyword_true},49 KeywordId{.bytes="true", .id = Id.Keyword_true},
50 KeywordId{.bytes="try", .id = Id.Keyword_try},
47 KeywordId{.bytes="undefined", .id = Id.Keyword_undefined},51 KeywordId{.bytes="undefined", .id = Id.Keyword_undefined},
48 KeywordId{.bytes="union", .id = Id.Keyword_union},52 KeywordId{.bytes="union", .id = Id.Keyword_union},
49 KeywordId{.bytes="unreachable", .id = Id.Keyword_unreachable},53 KeywordId{.bytes="unreachable", .id = Id.Keyword_unreachable},
...@@ -95,10 +99,12 @@ pub const Token = struct {...@@ -95,10 +99,12 @@ pub const Token = struct {
95 AmpersandEqual,99 AmpersandEqual,
96 IntegerLiteral,100 IntegerLiteral,
97 FloatLiteral,101 FloatLiteral,
102 LineComment,
98 Keyword_align,103 Keyword_align,
99 Keyword_and,104 Keyword_and,
100 Keyword_asm,105 Keyword_asm,
101 Keyword_break,106 Keyword_break,
107 Keyword_catch,
102 Keyword_comptime,108 Keyword_comptime,
103 Keyword_const,109 Keyword_const,
104 Keyword_continue,110 Keyword_continue,
...@@ -111,7 +117,6 @@ pub const Token = struct {...@@ -111,7 +117,6 @@ pub const Token = struct {
111 Keyword_false,117 Keyword_false,
112 Keyword_fn,118 Keyword_fn,
113 Keyword_for,119 Keyword_for,
114 Keyword_goto,
115 Keyword_if,120 Keyword_if,
116 Keyword_inline,121 Keyword_inline,
117 Keyword_nakedcc,122 Keyword_nakedcc,
...@@ -121,12 +126,14 @@ pub const Token = struct {...@@ -121,12 +126,14 @@ pub const Token = struct {
121 Keyword_packed,126 Keyword_packed,
122 Keyword_pub,127 Keyword_pub,
123 Keyword_return,128 Keyword_return,
129 Keyword_section,
124 Keyword_stdcallcc,130 Keyword_stdcallcc,
125 Keyword_struct,131 Keyword_struct,
126 Keyword_switch,132 Keyword_switch,
127 Keyword_test,133 Keyword_test,
128 Keyword_this,134 Keyword_this,
129 Keyword_true,135 Keyword_true,
136 Keyword_try,
130 Keyword_undefined,137 Keyword_undefined,
131 Keyword_union,138 Keyword_union,
132 Keyword_unreachable,139 Keyword_unreachable,
...@@ -140,21 +147,19 @@ pub const Token = struct {...@@ -140,21 +147,19 @@ pub const Token = struct {
140pub const Tokenizer = struct {147pub const Tokenizer = struct {
141 buffer: []const u8,148 buffer: []const u8,
142 index: usize,149 index: usize,
150 line: usize,
151 column: usize,
143 pending_invalid_token: ?Token,152 pending_invalid_token: ?Token,
144153
145 pub const Location = struct {154 pub const LineLocation = struct {
146 line: usize,
147 column: usize,
148 line_start: usize,155 line_start: usize,
149 line_end: usize,156 line_end: usize,
150 };157 };
151158
152 pub fn getTokenLocation(self: &Tokenizer, token: &const Token) Location {159 pub fn getTokenLocation(self: &Tokenizer, token: &const Token) LineLocation {
153 var loc = Location {160 var loc = LineLocation {
154 .line = 0,
155 .column = 0,
156 .line_start = 0,161 .line_start = 0,
157 .line_end = 0,162 .line_end = self.buffer.len,
158 };163 };
159 for (self.buffer) |c, i| {164 for (self.buffer) |c, i| {
160 if (i == token.start) {165 if (i == token.start) {
...@@ -163,11 +168,7 @@ pub const Tokenizer = struct {...@@ -163,11 +168,7 @@ pub const Tokenizer = struct {
163 return loc;168 return loc;
164 }169 }
165 if (c == '\n') {170 if (c == '\n') {
166 loc.line += 1;
167 loc.column = 0;
168 loc.line_start = i + 1;171 loc.line_start = i + 1;
169 } else {
170 loc.column += 1;
171 }172 }
172 }173 }
173 return loc;174 return loc;
...@@ -182,6 +183,8 @@ pub const Tokenizer = struct {...@@ -182,6 +183,8 @@ pub const Tokenizer = struct {
182 return Tokenizer {183 return Tokenizer {
183 .buffer = buffer,184 .buffer = buffer,
184 .index = 0,185 .index = 0,
186 .line = 0,
187 .column = 0,
185 .pending_invalid_token = null,188 .pending_invalid_token = null,
186 };189 };
187 }190 }
...@@ -222,13 +225,21 @@ pub const Tokenizer = struct {...@@ -222,13 +225,21 @@ pub const Tokenizer = struct {
222 .id = Token.Id.Eof,225 .id = Token.Id.Eof,
223 .start = self.index,226 .start = self.index,
224 .end = undefined,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 const c = self.buffer[self.index];232 const c = self.buffer[self.index];
228 switch (state) {233 switch (state) {
229 State.Start => switch (c) {234 State.Start => switch (c) {
230 ' ', '\n' => {235 ' ' => {
236 result.start = self.index + 1;
237 result.column += 1;
238 },
239 '\n' => {
231 result.start = self.index + 1;240 result.start = self.index + 1;
241 result.line += 1;
242 result.column = 0;
232 },243 },
233 'c' => {244 'c' => {
234 state = State.C;245 state = State.C;
...@@ -460,7 +471,7 @@ pub const Tokenizer = struct {...@@ -460,7 +471,7 @@ pub const Tokenizer = struct {
460471
461 State.Slash => switch (c) {472 State.Slash => switch (c) {
462 '/' => {473 '/' => {
463 result.id = undefined;474 result.id = Token.Id.LineComment;
464 state = State.LineComment;475 state = State.LineComment;
465 },476 },
466 else => {477 else => {
...@@ -469,14 +480,7 @@ pub const Tokenizer = struct {...@@ -469,14 +480,7 @@ pub const Tokenizer = struct {
469 },480 },
470 },481 },
471 State.LineComment => switch (c) {482 State.LineComment => switch (c) {
472 '\n' => {483 '\n' => break,
473 state = State.Start;
474 result = Token {
475 .id = Token.Id.Eof,
476 .start = self.index + 1,
477 .end = undefined,
478 };
479 },
480 else => self.checkLiteralCharacter(),484 else => self.checkLiteralCharacter(),
481 },485 },
482 State.Zero => switch (c) {486 State.Zero => switch (c) {
...@@ -543,6 +547,14 @@ pub const Tokenizer = struct {...@@ -543,6 +547,14 @@ pub const Tokenizer = struct {
543 else => break,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 } else if (self.index == self.buffer.len) {558 } else if (self.index == self.buffer.len) {
547 switch (state) {559 switch (state) {
548 State.Start,560 State.Start,
...@@ -622,6 +634,8 @@ pub const Tokenizer = struct {...@@ -622,6 +634,8 @@ pub const Tokenizer = struct {
622 .id = Token.Id.Invalid,634 .id = Token.Id.Invalid,
623 .start = self.index,635 .start = self.index,
624 .end = self.index + invalid_length,636 .end = self.index + invalid_length,
637 .line = self.line,
638 .column = self.column,
625 };639 };
626 }640 }
627641
test/behavior.zig+1
...@@ -35,6 +35,7 @@ comptime {...@@ -35,6 +35,7 @@ comptime {
35 _ = @import("cases/slice.zig");35 _ = @import("cases/slice.zig");
36 _ = @import("cases/struct.zig");36 _ = @import("cases/struct.zig");
37 _ = @import("cases/struct_contains_slice_of_itself.zig");37 _ = @import("cases/struct_contains_slice_of_itself.zig");
38 _ = @import("cases/struct_contains_null_ptr_itself.zig");
38 _ = @import("cases/switch.zig");39 _ = @import("cases/switch.zig");
39 _ = @import("cases/switch_prong_err_enum.zig");40 _ = @import("cases/switch_prong_err_enum.zig");
40 _ = @import("cases/switch_prong_implicit_cast.zig");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,3 +388,10 @@ test "string literal used as comptime slice is memoized" {
388 comptime assert(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node);388 comptime assert(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node);
389 comptime assert(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node);389 comptime assert(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node);
390}390}
391
392test "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,4 +394,11 @@ fn test_f128() void {
394394
395fn should_not_be_zero(x: f128) void {395fn should_not_be_zero(x: f128) void {
396 assert(x != 0.0);396 assert(x != 0.0);
397}
\ No newline at end of file
397}
398
399test "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,12 +499,29 @@ test "@canImplicitCast" {
499}499}
500500
501test "@typeName" {501test "@typeName" {
502 const Struct = struct {
503 };
504 const Union = union {
505 unused: u8,
506 };
507 const Enum = enum {
508 Unused,
509 };
502 comptime {510 comptime {
503 assert(mem.eql(u8, @typeName(i64), "i64"));511 assert(mem.eql(u8, @typeName(i64), "i64"));
504 assert(mem.eql(u8, @typeName(&usize), "&usize"));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}
507520
521fn TypeFromFn(comptime T: type) type {
522 return struct {};
523}
524
508test "volatile load and store" {525test "volatile load and store" {
509 var number: i32 = 1234;526 var number: i32 = 1234;
510 const ptr = (&volatile i32)(&number);527 const ptr = (&volatile i32)(&number);
test/cases/struct_contains_null_ptr_itself.zig created+22
...@@ -0,0 +1,22 @@
1const std = @import("std");
2const assert = std.debug.assert;
3
4test "struct contains null pointer which contains original struct" {
5 var x: ?&NodeLineComment = null;
6 assert(x == null);
7}
8
9pub const Node = struct {
10 id: Id,
11 comment: ?&NodeLineComment,
12
13 pub const Id = enum {
14 Root,
15 LineComment,
16 };
17};
18
19pub const NodeLineComment = struct {
20 base: Node,
21};
22
test/compile_errors.zig+20
...@@ -1,6 +1,26 @@...@@ -1,6 +1,26 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: &tests.CompileErrorContext) void {3pub 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 cases.add("no else prong on switch on global error set",24 cases.add("no else prong on switch on global error set",
5 \\export fn entry() void {25 \\export fn entry() void {
6 \\ foo(error.A);26 \\ foo(error.A);