mirror of
https://github.com/ziglang/zig.git
synced 2024-11-29 00:22:33 +00:00
Merge remote-tracking branch 'origin/master' into llvm6
This commit is contained in:
commit
9cfd7dea19
@ -429,6 +429,7 @@ set(ZIG_STD_FILES
|
||||
"index.zig"
|
||||
"io.zig"
|
||||
"linked_list.zig"
|
||||
"macho.zig"
|
||||
"math/acos.zig"
|
||||
"math/acosh.zig"
|
||||
"math/asin.zig"
|
||||
|
@ -2278,17 +2278,16 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
|
||||
return;
|
||||
|
||||
if (struct_type->data.structure.zero_bits_loop_flag) {
|
||||
// If we get here it's due to recursion. From this we conclude that the struct is
|
||||
// not zero bits, and if abi_alignment == 0 we further conclude that the first field
|
||||
// is a pointer to this very struct, or a function pointer with parameters that
|
||||
// reference such a type.
|
||||
// If we get here it's due to recursion. This is a design flaw in the compiler,
|
||||
// we should be able to still figure out alignment, but here we give up and say that
|
||||
// the alignment is pointer width, then assert that the first field is within that
|
||||
// alignment
|
||||
struct_type->data.structure.zero_bits_known = true;
|
||||
if (struct_type->data.structure.abi_alignment == 0) {
|
||||
if (struct_type->data.structure.layout == ContainerLayoutPacked) {
|
||||
struct_type->data.structure.abi_alignment = 1;
|
||||
} else {
|
||||
struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref,
|
||||
LLVMPointerType(LLVMInt8Type(), 0));
|
||||
struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, LLVMPointerType(LLVMInt8Type(), 0));
|
||||
}
|
||||
}
|
||||
return;
|
||||
@ -2352,11 +2351,17 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
|
||||
if (gen_field_index == 0) {
|
||||
if (struct_type->data.structure.layout == ContainerLayoutPacked) {
|
||||
struct_type->data.structure.abi_alignment = 1;
|
||||
} else {
|
||||
} else if (struct_type->data.structure.abi_alignment == 0) {
|
||||
// Alignment of structs is the alignment of the first field, for now.
|
||||
// TODO change this when we re-order struct fields (issue #168)
|
||||
struct_type->data.structure.abi_alignment = get_abi_alignment(g, field_type);
|
||||
assert(struct_type->data.structure.abi_alignment != 0);
|
||||
} else {
|
||||
// due to a design flaw in the compiler we assumed that alignment was
|
||||
// pointer width, so we assert that this wasn't violated.
|
||||
if (get_abi_alignment(g, field_type) > struct_type->data.structure.abi_alignment) {
|
||||
zig_panic("compiler design flaw: incorrect alignment assumption");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4201,6 +4201,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
|
||||
continue;
|
||||
}
|
||||
ConstExprValue *field_val = &const_val->data.x_struct.fields[i];
|
||||
assert(field_val->type != nullptr);
|
||||
LLVMValueRef val = gen_const_val(g, field_val, "");
|
||||
fields[type_struct_field->gen_index] = val;
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
case TypeTableEntryIdErrorUnion:
|
||||
{
|
||||
TypeTableEntry *payload_type = type_entry->data.error_union.payload_type;
|
||||
|
85
src/ir.cpp
85
src/ir.cpp
@ -4172,7 +4172,13 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod
|
||||
buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));
|
||||
}
|
||||
|
||||
// Temporarily set the name of the IrExecutable to the VariableDeclaration
|
||||
// so that the struct or enum from the init expression inherits the name.
|
||||
Buf *old_exec_name = irb->exec->name;
|
||||
irb->exec->name = variable_declaration->symbol;
|
||||
IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope);
|
||||
irb->exec->name = old_exec_name;
|
||||
|
||||
if (init_value == irb->codegen->invalid_instruction)
|
||||
return init_value;
|
||||
|
||||
@ -6727,8 +6733,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
|
||||
result.id = ConstCastResultIdFnReturnType;
|
||||
result.data.return_type = allocate_nonzero<ConstCastOnly>(1);
|
||||
*result.data.return_type = child;
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) {
|
||||
result.id = ConstCastResultIdFnArgCount;
|
||||
@ -8183,7 +8189,7 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi
|
||||
}
|
||||
|
||||
if (instr_is_comptime(value)) {
|
||||
ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
|
||||
ConstExprValue *val = ir_resolve_const(ira, value, UndefOk);
|
||||
if (!val)
|
||||
return ira->codegen->invalid_instruction;
|
||||
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
|
||||
ok = bigint_cmp(&rem_result, &mod_result) == CmpEQ;
|
||||
}
|
||||
} else {
|
||||
if (float_cmp_zero(&op2->value) == CmpEQ) {
|
||||
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, resolved_type);
|
||||
if (casted_op2 == ira->codegen->invalid_instruction)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
if (float_cmp_zero(&casted_op2->value) == CmpEQ) {
|
||||
// the division by zero error will be caught later, but we don't
|
||||
// have a remainder function ambiguity problem
|
||||
ok = true;
|
||||
} else {
|
||||
ConstExprValue rem_result;
|
||||
ConstExprValue mod_result;
|
||||
float_rem(&rem_result, &op1->value, &op2->value);
|
||||
float_mod(&mod_result, &op1->value, &op2->value);
|
||||
float_rem(&rem_result, &op1->value, &casted_op2->value);
|
||||
float_mod(&mod_result, &op1->value, &casted_op2->value);
|
||||
ok = float_cmp(&rem_result, &mod_result) == CmpEQ;
|
||||
}
|
||||
}
|
||||
@ -14928,6 +14937,7 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
|
||||
ConstExprValue *parent_ptr;
|
||||
size_t abs_offset;
|
||||
size_t rel_end;
|
||||
bool ptr_is_undef = false;
|
||||
if (array_type->id == TypeTableEntryIdArray) {
|
||||
array_val = const_ptr_pointee(ira->codegen, &ptr_ptr->value);
|
||||
abs_offset = 0;
|
||||
@ -14935,7 +14945,12 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
|
||||
parent_ptr = nullptr;
|
||||
} else if (array_type->id == TypeTableEntryIdPointer) {
|
||||
parent_ptr = const_ptr_pointee(ira->codegen, &ptr_ptr->value);
|
||||
switch (parent_ptr->data.x_ptr.special) {
|
||||
if (parent_ptr->special == ConstValSpecialUndef) {
|
||||
array_val = nullptr;
|
||||
abs_offset = 0;
|
||||
rel_end = SIZE_MAX;
|
||||
ptr_is_undef = true;
|
||||
} else switch (parent_ptr->data.x_ptr.special) {
|
||||
case ConstPtrSpecialInvalid:
|
||||
case ConstPtrSpecialDiscard:
|
||||
zig_unreachable();
|
||||
@ -14989,7 +15004,7 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
|
||||
}
|
||||
|
||||
uint64_t start_scalar = bigint_as_unsigned(&casted_start->value.data.x_bigint);
|
||||
if (start_scalar > rel_end) {
|
||||
if (!ptr_is_undef && start_scalar > rel_end) {
|
||||
ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
@ -15000,12 +15015,18 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
|
||||
} else {
|
||||
end_scalar = rel_end;
|
||||
}
|
||||
if (end_scalar > rel_end) {
|
||||
ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
if (!ptr_is_undef) {
|
||||
if (end_scalar > rel_end) {
|
||||
ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
if (start_scalar > end_scalar) {
|
||||
ir_add_error(ira, &instruction->base, buf_sprintf("slice start is greater than end"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
}
|
||||
if (start_scalar > end_scalar) {
|
||||
ir_add_error(ira, &instruction->base, buf_sprintf("slice start is greater than end"));
|
||||
if (ptr_is_undef && start_scalar != end_scalar) {
|
||||
ir_add_error(ira, &instruction->base, buf_sprintf("non-zero length slice of undefined pointer"));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
@ -15021,25 +15042,27 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
|
||||
if (array_type->id == TypeTableEntryIdArray) {
|
||||
ptr_val->data.x_ptr.mut = ptr_ptr->value.data.x_ptr.mut;
|
||||
}
|
||||
} else {
|
||||
switch (parent_ptr->data.x_ptr.special) {
|
||||
case ConstPtrSpecialInvalid:
|
||||
case ConstPtrSpecialDiscard:
|
||||
zig_unreachable();
|
||||
case ConstPtrSpecialRef:
|
||||
init_const_ptr_ref(ira->codegen, ptr_val,
|
||||
parent_ptr->data.x_ptr.data.ref.pointee, slice_is_const(return_type));
|
||||
break;
|
||||
case ConstPtrSpecialBaseArray:
|
||||
zig_unreachable();
|
||||
case ConstPtrSpecialBaseStruct:
|
||||
zig_panic("TODO");
|
||||
case ConstPtrSpecialHardCodedAddr:
|
||||
init_const_ptr_hard_coded_addr(ira->codegen, ptr_val,
|
||||
parent_ptr->type->data.pointer.child_type,
|
||||
parent_ptr->data.x_ptr.data.hard_coded_addr.addr + start_scalar,
|
||||
slice_is_const(return_type));
|
||||
}
|
||||
} else if (ptr_is_undef) {
|
||||
ptr_val->type = get_pointer_to_type(ira->codegen, parent_ptr->type->data.pointer.child_type,
|
||||
slice_is_const(return_type));
|
||||
ptr_val->special = ConstValSpecialUndef;
|
||||
} else switch (parent_ptr->data.x_ptr.special) {
|
||||
case ConstPtrSpecialInvalid:
|
||||
case ConstPtrSpecialDiscard:
|
||||
zig_unreachable();
|
||||
case ConstPtrSpecialRef:
|
||||
init_const_ptr_ref(ira->codegen, ptr_val,
|
||||
parent_ptr->data.x_ptr.data.ref.pointee, slice_is_const(return_type));
|
||||
break;
|
||||
case ConstPtrSpecialBaseArray:
|
||||
zig_unreachable();
|
||||
case ConstPtrSpecialBaseStruct:
|
||||
zig_panic("TODO");
|
||||
case ConstPtrSpecialHardCodedAddr:
|
||||
init_const_ptr_hard_coded_addr(ira->codegen, ptr_val,
|
||||
parent_ptr->type->data.pointer.child_type,
|
||||
parent_ptr->data.x_ptr.data.hard_coded_addr.addr + start_scalar,
|
||||
slice_is_const(return_type));
|
||||
}
|
||||
|
||||
ConstExprValue *len_val = &out_val->data.x_struct.fields[slice_len_index];
|
||||
|
@ -125,7 +125,6 @@ static const struct ZigKeyword zig_keywords[] = {
|
||||
{"false", TokenIdKeywordFalse},
|
||||
{"fn", TokenIdKeywordFn},
|
||||
{"for", TokenIdKeywordFor},
|
||||
{"goto", TokenIdKeywordGoto},
|
||||
{"if", TokenIdKeywordIf},
|
||||
{"inline", TokenIdKeywordInline},
|
||||
{"nakedcc", TokenIdKeywordNakedCC},
|
||||
@ -1542,7 +1541,6 @@ const char * token_name(TokenId id) {
|
||||
case TokenIdKeywordFalse: return "false";
|
||||
case TokenIdKeywordFn: return "fn";
|
||||
case TokenIdKeywordFor: return "for";
|
||||
case TokenIdKeywordGoto: return "goto";
|
||||
case TokenIdKeywordIf: return "if";
|
||||
case TokenIdKeywordInline: return "inline";
|
||||
case TokenIdKeywordNakedCC: return "nakedcc";
|
||||
|
@ -66,7 +66,6 @@ enum TokenId {
|
||||
TokenIdKeywordFalse,
|
||||
TokenIdKeywordFn,
|
||||
TokenIdKeywordFor,
|
||||
TokenIdKeywordGoto,
|
||||
TokenIdKeywordIf,
|
||||
TokenIdKeywordInline,
|
||||
TokenIdKeywordNakedCC,
|
||||
|
@ -5,6 +5,7 @@ const io = std.io;
|
||||
const os = std.os;
|
||||
const elf = std.elf;
|
||||
const DW = std.dwarf;
|
||||
const macho = std.macho;
|
||||
const ArrayList = std.ArrayList;
|
||||
const builtin = @import("builtin");
|
||||
|
||||
@ -47,7 +48,7 @@ pub fn getSelfDebugInfo() !&ElfStackTrace {
|
||||
pub fn dumpCurrentStackTrace() void {
|
||||
const stderr = getStderrStream() catch return;
|
||||
const debug_info = getSelfDebugInfo() catch |err| {
|
||||
stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return;
|
||||
stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
|
||||
return;
|
||||
};
|
||||
defer debug_info.close();
|
||||
@ -61,7 +62,7 @@ pub fn dumpCurrentStackTrace() void {
|
||||
pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) void {
|
||||
const stderr = getStderrStream() catch return;
|
||||
const debug_info = getSelfDebugInfo() catch |err| {
|
||||
stderr.print("Unable to open debug info: {}\n", @errorName(err)) catch return;
|
||||
stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
|
||||
return;
|
||||
};
|
||||
defer debug_info.close();
|
||||
@ -180,43 +181,57 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator,
|
||||
}
|
||||
|
||||
fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: usize) !void {
|
||||
if (builtin.os == builtin.Os.windows) {
|
||||
return error.UnsupportedDebugInfo;
|
||||
}
|
||||
// TODO we really should be able to convert @sizeOf(usize) * 2 to a string literal
|
||||
// at compile time. I'll call it issue #313
|
||||
const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
|
||||
|
||||
const compile_unit = findCompileUnit(debug_info, address) catch {
|
||||
try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
|
||||
address);
|
||||
return;
|
||||
};
|
||||
const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
|
||||
if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| {
|
||||
defer line_info.deinit();
|
||||
try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
|
||||
DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
|
||||
line_info.file_name, line_info.line, line_info.column,
|
||||
address, compile_unit_name);
|
||||
if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {
|
||||
if (line_info.column == 0) {
|
||||
try out_stream.write("\n");
|
||||
} else {
|
||||
{var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
|
||||
try out_stream.writeByte(' ');
|
||||
}}
|
||||
try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
|
||||
}
|
||||
} else |err| switch (err) {
|
||||
error.EndOfFile => {},
|
||||
else => return err,
|
||||
}
|
||||
} else |err| switch (err) {
|
||||
error.MissingDebugInfo, error.InvalidDebugInfo => {
|
||||
try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name);
|
||||
switch (builtin.os) {
|
||||
builtin.Os.windows => return error.UnsupportedDebugInfo,
|
||||
builtin.Os.macosx => {
|
||||
// TODO(bnoordhuis) It's theoretically possible to obtain the
|
||||
// compilation unit from the symbtab but it's not that useful
|
||||
// in practice because the compiler dumps everything in a single
|
||||
// object file. Future improvement: use external dSYM data when
|
||||
// available.
|
||||
const unknown = macho.Symbol { .name = "???", .address = address };
|
||||
const symbol = debug_info.symbol_table.search(address) ?? &unknown;
|
||||
try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++
|
||||
DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n",
|
||||
symbol.name, address);
|
||||
},
|
||||
else => {
|
||||
const compile_unit = findCompileUnit(debug_info, address) catch {
|
||||
try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
|
||||
address);
|
||||
return;
|
||||
};
|
||||
const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
|
||||
if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| {
|
||||
defer line_info.deinit();
|
||||
try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++
|
||||
DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n",
|
||||
line_info.file_name, line_info.line, line_info.column,
|
||||
address, compile_unit_name);
|
||||
if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {
|
||||
if (line_info.column == 0) {
|
||||
try out_stream.write("\n");
|
||||
} else {
|
||||
{var col_i: usize = 1; while (col_i < line_info.column) : (col_i += 1) {
|
||||
try out_stream.writeByte(' ');
|
||||
}}
|
||||
try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
|
||||
}
|
||||
} else |err| switch (err) {
|
||||
error.EndOfFile => {},
|
||||
else => return err,
|
||||
}
|
||||
} else |err| switch (err) {
|
||||
error.MissingDebugInfo, error.InvalidDebugInfo => {
|
||||
try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name);
|
||||
},
|
||||
else => return err,
|
||||
}
|
||||
},
|
||||
else => return err,
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,6 +239,7 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace {
|
||||
switch (builtin.object_format) {
|
||||
builtin.ObjectFormat.elf => {
|
||||
const st = try allocator.create(ElfStackTrace);
|
||||
errdefer allocator.destroy(st);
|
||||
*st = ElfStackTrace {
|
||||
.self_exe_file = undefined,
|
||||
.elf = undefined,
|
||||
@ -249,12 +265,22 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace {
|
||||
try scanAllCompileUnits(st);
|
||||
return st;
|
||||
},
|
||||
builtin.ObjectFormat.macho => {
|
||||
var exe_file = try os.openSelfExe();
|
||||
defer exe_file.close();
|
||||
|
||||
const st = try allocator.create(ElfStackTrace);
|
||||
errdefer allocator.destroy(st);
|
||||
|
||||
*st = ElfStackTrace {
|
||||
.symbol_table = try macho.loadSymbols(allocator, &io.FileInStream.init(&exe_file)),
|
||||
};
|
||||
|
||||
return st;
|
||||
},
|
||||
builtin.ObjectFormat.coff => {
|
||||
return error.TodoSupportCoffDebugInfo;
|
||||
},
|
||||
builtin.ObjectFormat.macho => {
|
||||
return error.TodoSupportMachoDebugInfo;
|
||||
},
|
||||
builtin.ObjectFormat.wasm => {
|
||||
return error.TodoSupportCOFFDebugInfo;
|
||||
},
|
||||
@ -297,31 +323,40 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &con
|
||||
}
|
||||
}
|
||||
|
||||
pub const ElfStackTrace = struct {
|
||||
self_exe_file: os.File,
|
||||
elf: elf.Elf,
|
||||
debug_info: &elf.SectionHeader,
|
||||
debug_abbrev: &elf.SectionHeader,
|
||||
debug_str: &elf.SectionHeader,
|
||||
debug_line: &elf.SectionHeader,
|
||||
debug_ranges: ?&elf.SectionHeader,
|
||||
abbrev_table_list: ArrayList(AbbrevTableHeader),
|
||||
compile_unit_list: ArrayList(CompileUnit),
|
||||
pub const ElfStackTrace = switch (builtin.os) {
|
||||
builtin.Os.macosx => struct {
|
||||
symbol_table: macho.SymbolTable,
|
||||
|
||||
pub fn allocator(self: &const ElfStackTrace) &mem.Allocator {
|
||||
return self.abbrev_table_list.allocator;
|
||||
}
|
||||
pub fn close(self: &ElfStackTrace) void {
|
||||
self.symbol_table.deinit();
|
||||
}
|
||||
},
|
||||
else => struct {
|
||||
self_exe_file: os.File,
|
||||
elf: elf.Elf,
|
||||
debug_info: &elf.SectionHeader,
|
||||
debug_abbrev: &elf.SectionHeader,
|
||||
debug_str: &elf.SectionHeader,
|
||||
debug_line: &elf.SectionHeader,
|
||||
debug_ranges: ?&elf.SectionHeader,
|
||||
abbrev_table_list: ArrayList(AbbrevTableHeader),
|
||||
compile_unit_list: ArrayList(CompileUnit),
|
||||
|
||||
pub fn readString(self: &ElfStackTrace) ![]u8 {
|
||||
var in_file_stream = io.FileInStream.init(&self.self_exe_file);
|
||||
const in_stream = &in_file_stream.stream;
|
||||
return readStringRaw(self.allocator(), in_stream);
|
||||
}
|
||||
pub fn allocator(self: &const ElfStackTrace) &mem.Allocator {
|
||||
return self.abbrev_table_list.allocator;
|
||||
}
|
||||
|
||||
pub fn close(self: &ElfStackTrace) void {
|
||||
self.self_exe_file.close();
|
||||
self.elf.close();
|
||||
}
|
||||
pub fn readString(self: &ElfStackTrace) ![]u8 {
|
||||
var in_file_stream = io.FileInStream.init(&self.self_exe_file);
|
||||
const in_stream = &in_file_stream.stream;
|
||||
return readStringRaw(self.allocator(), in_stream);
|
||||
}
|
||||
|
||||
pub fn close(self: &ElfStackTrace) void {
|
||||
self.self_exe_file.close();
|
||||
self.elf.close();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const PcRange = struct {
|
||||
|
@ -550,12 +550,6 @@ test "parse unsigned comptime" {
|
||||
}
|
||||
}
|
||||
|
||||
// Dummy field because of https://github.com/zig-lang/zig/issues/557.
|
||||
// At top level because of https://github.com/zig-lang/zig/issues/675.
|
||||
const Struct = struct {
|
||||
unused: u8,
|
||||
};
|
||||
|
||||
test "fmt.format" {
|
||||
{
|
||||
var buf1: [32]u8 = undefined;
|
||||
@ -588,6 +582,10 @@ test "fmt.format" {
|
||||
assert(mem.eql(u8, result, "u3: 5\n"));
|
||||
}
|
||||
{
|
||||
// Dummy field because of https://github.com/zig-lang/zig/issues/557.
|
||||
const Struct = struct {
|
||||
unused: u8,
|
||||
};
|
||||
var buf1: [32]u8 = undefined;
|
||||
const value = Struct {
|
||||
.unused = 42,
|
||||
|
@ -21,6 +21,7 @@ pub const endian = @import("endian.zig");
|
||||
pub const fmt = @import("fmt/index.zig");
|
||||
pub const heap = @import("heap.zig");
|
||||
pub const io = @import("io.zig");
|
||||
pub const macho = @import("macho.zig");
|
||||
pub const math = @import("math/index.zig");
|
||||
pub const mem = @import("mem.zig");
|
||||
pub const net = @import("net.zig");
|
||||
@ -51,6 +52,7 @@ test "std" {
|
||||
_ = @import("endian.zig");
|
||||
_ = @import("fmt/index.zig");
|
||||
_ = @import("io.zig");
|
||||
_ = @import("macho.zig");
|
||||
_ = @import("math/index.zig");
|
||||
_ = @import("mem.zig");
|
||||
_ = @import("heap.zig");
|
||||
|
170
std/macho.zig
Normal file
170
std/macho.zig
Normal file
@ -0,0 +1,170 @@
|
||||
const builtin = @import("builtin");
|
||||
const std = @import("index.zig");
|
||||
const io = std.io;
|
||||
const mem = std.mem;
|
||||
|
||||
const MH_MAGIC_64 = 0xFEEDFACF;
|
||||
const MH_PIE = 0x200000;
|
||||
const LC_SYMTAB = 2;
|
||||
|
||||
const MachHeader64 = packed struct {
|
||||
magic: u32,
|
||||
cputype: u32,
|
||||
cpusubtype: u32,
|
||||
filetype: u32,
|
||||
ncmds: u32,
|
||||
sizeofcmds: u32,
|
||||
flags: u32,
|
||||
reserved: u32,
|
||||
};
|
||||
|
||||
const LoadCommand = packed struct {
|
||||
cmd: u32,
|
||||
cmdsize: u32,
|
||||
};
|
||||
|
||||
const SymtabCommand = packed struct {
|
||||
symoff: u32,
|
||||
nsyms: u32,
|
||||
stroff: u32,
|
||||
strsize: u32,
|
||||
};
|
||||
|
||||
const Nlist64 = packed struct {
|
||||
n_strx: u32,
|
||||
n_type: u8,
|
||||
n_sect: u8,
|
||||
n_desc: u16,
|
||||
n_value: u64,
|
||||
};
|
||||
|
||||
pub const Symbol = struct {
|
||||
name: []const u8,
|
||||
address: u64,
|
||||
|
||||
fn addressLessThan(lhs: &const Symbol, rhs: &const Symbol) bool {
|
||||
return lhs.address < rhs.address;
|
||||
}
|
||||
};
|
||||
|
||||
pub const SymbolTable = struct {
|
||||
allocator: &mem.Allocator,
|
||||
symbols: []const Symbol,
|
||||
strings: []const u8,
|
||||
|
||||
// Doubles as an eyecatcher to calculate the PIE slide, see loadSymbols().
|
||||
// Ideally we'd use _mh_execute_header because it's always at 0x100000000
|
||||
// in the image but as it's located in a different section than executable
|
||||
// code, its displacement is different.
|
||||
pub fn deinit(self: &SymbolTable) void {
|
||||
self.allocator.free(self.symbols);
|
||||
self.symbols = []const Symbol {};
|
||||
|
||||
self.allocator.free(self.strings);
|
||||
self.strings = []const u8 {};
|
||||
}
|
||||
|
||||
pub fn search(self: &const SymbolTable, address: usize) ?&const Symbol {
|
||||
var min: usize = 0;
|
||||
var max: usize = self.symbols.len - 1; // Exclude sentinel.
|
||||
while (min < max) {
|
||||
const mid = min + (max - min) / 2;
|
||||
const curr = &self.symbols[mid];
|
||||
const next = &self.symbols[mid + 1];
|
||||
if (address >= next.address) {
|
||||
min = mid + 1;
|
||||
} else if (address < curr.address) {
|
||||
max = mid;
|
||||
} else {
|
||||
return curr;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn loadSymbols(allocator: &mem.Allocator, in: &io.FileInStream) !SymbolTable {
|
||||
var file = in.file;
|
||||
try file.seekTo(0);
|
||||
|
||||
var hdr: MachHeader64 = undefined;
|
||||
try readOneNoEof(in, MachHeader64, &hdr);
|
||||
if (hdr.magic != MH_MAGIC_64) return error.MissingDebugInfo;
|
||||
const is_pie = MH_PIE == (hdr.flags & MH_PIE);
|
||||
|
||||
var pos: usize = @sizeOf(@typeOf(hdr));
|
||||
var ncmd: u32 = hdr.ncmds;
|
||||
while (ncmd != 0) : (ncmd -= 1) {
|
||||
try file.seekTo(pos);
|
||||
var lc: LoadCommand = undefined;
|
||||
try readOneNoEof(in, LoadCommand, &lc);
|
||||
if (lc.cmd == LC_SYMTAB) break;
|
||||
pos += lc.cmdsize;
|
||||
} else {
|
||||
return error.MissingDebugInfo;
|
||||
}
|
||||
|
||||
var cmd: SymtabCommand = undefined;
|
||||
try readOneNoEof(in, SymtabCommand, &cmd);
|
||||
|
||||
try file.seekTo(cmd.symoff);
|
||||
var syms = try allocator.alloc(Nlist64, cmd.nsyms);
|
||||
defer allocator.free(syms);
|
||||
try readNoEof(in, Nlist64, syms);
|
||||
|
||||
try file.seekTo(cmd.stroff);
|
||||
var strings = try allocator.alloc(u8, cmd.strsize);
|
||||
errdefer allocator.free(strings);
|
||||
try in.stream.readNoEof(strings);
|
||||
|
||||
var nsyms: usize = 0;
|
||||
for (syms) |sym| if (isSymbol(sym)) nsyms += 1;
|
||||
if (nsyms == 0) return error.MissingDebugInfo;
|
||||
|
||||
var symbols = try allocator.alloc(Symbol, nsyms + 1); // Room for sentinel.
|
||||
errdefer allocator.free(symbols);
|
||||
|
||||
var pie_slide: usize = 0;
|
||||
var nsym: usize = 0;
|
||||
for (syms) |sym| {
|
||||
if (!isSymbol(sym)) continue;
|
||||
const start = sym.n_strx;
|
||||
const end = ??mem.indexOfScalarPos(u8, strings, start, 0);
|
||||
const name = strings[start..end];
|
||||
const address = sym.n_value;
|
||||
symbols[nsym] = Symbol { .name = name, .address = address };
|
||||
nsym += 1;
|
||||
if (is_pie and mem.eql(u8, name, "_SymbolTable_deinit")) {
|
||||
pie_slide = @ptrToInt(SymbolTable.deinit) - address;
|
||||
}
|
||||
}
|
||||
|
||||
// Effectively a no-op, lld emits symbols in ascending order.
|
||||
std.sort.insertionSort(Symbol, symbols[0..nsyms], Symbol.addressLessThan);
|
||||
|
||||
// Insert the sentinel. Since we don't know where the last function ends,
|
||||
// we arbitrarily limit it to the start address + 4 KB.
|
||||
const top = symbols[nsyms - 1].address + 4096;
|
||||
symbols[nsyms] = Symbol { .name = "", .address = top };
|
||||
|
||||
if (pie_slide != 0) {
|
||||
for (symbols) |*symbol| symbol.address += pie_slide;
|
||||
}
|
||||
|
||||
return SymbolTable {
|
||||
.allocator = allocator,
|
||||
.symbols = symbols,
|
||||
.strings = strings,
|
||||
};
|
||||
}
|
||||
|
||||
fn readNoEof(in: &io.FileInStream, comptime T: type, result: []T) !void {
|
||||
return in.stream.readNoEof(([]u8)(result));
|
||||
}
|
||||
fn readOneNoEof(in: &io.FileInStream, comptime T: type, result: &T) !void {
|
||||
return readNoEof(in, T, result[0..1]);
|
||||
}
|
||||
|
||||
fn isSymbol(sym: &const Nlist64) bool {
|
||||
return sym.n_value != 0 and sym.n_desc == 0;
|
||||
}
|
@ -42,6 +42,9 @@ pub const Allocator = struct {
|
||||
fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29,
|
||||
n: usize) ![]align(alignment) T
|
||||
{
|
||||
if (n == 0) {
|
||||
return (&align(alignment) T)(undefined)[0..0];
|
||||
}
|
||||
const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
|
||||
const byte_slice = try self.allocFn(self, byte_count, alignment);
|
||||
assert(byte_slice.len == byte_count);
|
||||
@ -62,6 +65,10 @@ pub const Allocator = struct {
|
||||
if (old_mem.len == 0) {
|
||||
return self.alloc(T, n);
|
||||
}
|
||||
if (n == 0) {
|
||||
self.free(old_mem);
|
||||
return (&align(alignment) T)(undefined)[0..0];
|
||||
}
|
||||
|
||||
const old_byte_slice = ([]u8)(old_mem);
|
||||
const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
|
||||
|
@ -329,6 +329,8 @@ pub const TIOCGPKT = 0x80045438;
|
||||
pub const TIOCGPTLCK = 0x80045439;
|
||||
pub const TIOCGEXCL = 0x80045440;
|
||||
|
||||
pub const EPOLL_CLOEXEC = O_CLOEXEC;
|
||||
|
||||
pub const EPOLL_CTL_ADD = 1;
|
||||
pub const EPOLL_CTL_DEL = 2;
|
||||
pub const EPOLL_CTL_MOD = 3;
|
||||
@ -751,22 +753,31 @@ pub fn fstat(fd: i32, stat_buf: &Stat) usize {
|
||||
return arch.syscall2(arch.SYS_fstat, usize(fd), @ptrToInt(stat_buf));
|
||||
}
|
||||
|
||||
pub const epoll_data = u64;
|
||||
pub const epoll_data = extern union {
|
||||
ptr: usize,
|
||||
fd: i32,
|
||||
@"u32": u32,
|
||||
@"u64": u64,
|
||||
};
|
||||
|
||||
pub const epoll_event = extern struct {
|
||||
events: u32,
|
||||
data: epoll_data
|
||||
data: epoll_data,
|
||||
};
|
||||
|
||||
pub fn epoll_create() usize {
|
||||
return arch.syscall1(arch.SYS_epoll_create, usize(1));
|
||||
return epoll_create1(0);
|
||||
}
|
||||
|
||||
pub fn epoll_create1(flags: usize) usize {
|
||||
return arch.syscall1(arch.SYS_epoll_create1, flags);
|
||||
}
|
||||
|
||||
pub fn epoll_ctl(epoll_fd: i32, op: i32, fd: i32, ev: &epoll_event) usize {
|
||||
return arch.syscall4(arch.SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev));
|
||||
}
|
||||
|
||||
pub fn epoll_wait(epoll_fd: i32, events: &epoll_event, maxevents: i32, timeout: i32) usize {
|
||||
pub fn epoll_wait(epoll_fd: i32, events: &epoll_event, maxevents: u32, timeout: i32) usize {
|
||||
return arch.syscall4(arch.SYS_epoll_wait, usize(epoll_fd), @ptrToInt(events), usize(maxevents), usize(timeout));
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ test "timer" {
|
||||
|
||||
var event = linux.epoll_event {
|
||||
.events = linux.EPOLLIN | linux.EPOLLOUT | linux.EPOLLET,
|
||||
.data = 0
|
||||
.data = linux.epoll_data { .ptr = 0 },
|
||||
};
|
||||
|
||||
err = linux.epoll_ctl(i32(epoll_fd), linux.EPOLL_CTL_ADD, i32(timer_fd), &event);
|
||||
|
217
std/zig/ast.zig
217
std/zig/ast.zig
@ -6,6 +6,7 @@ const mem = std.mem;
|
||||
|
||||
pub const Node = struct {
|
||||
id: Id,
|
||||
comment: ?&NodeLineComment,
|
||||
|
||||
pub const Id = enum {
|
||||
Root,
|
||||
@ -18,7 +19,9 @@ pub const Node = struct {
|
||||
PrefixOp,
|
||||
IntegerLiteral,
|
||||
FloatLiteral,
|
||||
StringLiteral,
|
||||
BuiltinCall,
|
||||
LineComment,
|
||||
};
|
||||
|
||||
pub fn iterate(base: &Node, index: usize) ?&Node {
|
||||
@ -33,7 +36,45 @@ pub const Node = struct {
|
||||
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index),
|
||||
Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).iterate(index),
|
||||
Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).iterate(index),
|
||||
Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).iterate(index),
|
||||
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).iterate(index),
|
||||
Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).iterate(index),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn firstToken(base: &Node) Token {
|
||||
return switch (base.id) {
|
||||
Id.Root => @fieldParentPtr(NodeRoot, "base", base).firstToken(),
|
||||
Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).firstToken(),
|
||||
Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).firstToken(),
|
||||
Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).firstToken(),
|
||||
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).firstToken(),
|
||||
Id.Block => @fieldParentPtr(NodeBlock, "base", base).firstToken(),
|
||||
Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).firstToken(),
|
||||
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).firstToken(),
|
||||
Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).firstToken(),
|
||||
Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).firstToken(),
|
||||
Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).firstToken(),
|
||||
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).firstToken(),
|
||||
Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).firstToken(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn lastToken(base: &Node) Token {
|
||||
return switch (base.id) {
|
||||
Id.Root => @fieldParentPtr(NodeRoot, "base", base).lastToken(),
|
||||
Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).lastToken(),
|
||||
Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).lastToken(),
|
||||
Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).lastToken(),
|
||||
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).lastToken(),
|
||||
Id.Block => @fieldParentPtr(NodeBlock, "base", base).lastToken(),
|
||||
Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).lastToken(),
|
||||
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).lastToken(),
|
||||
Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).lastToken(),
|
||||
Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).lastToken(),
|
||||
Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).lastToken(),
|
||||
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).lastToken(),
|
||||
Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).lastToken(),
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -41,6 +82,7 @@ pub const Node = struct {
|
||||
pub const NodeRoot = struct {
|
||||
base: Node,
|
||||
decls: ArrayList(&Node),
|
||||
eof_token: Token,
|
||||
|
||||
pub fn iterate(self: &NodeRoot, index: usize) ?&Node {
|
||||
if (index < self.decls.len) {
|
||||
@ -48,6 +90,14 @@ pub const NodeRoot = struct {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn firstToken(self: &NodeRoot) Token {
|
||||
return if (self.decls.len == 0) self.eof_token else self.decls.at(0).firstToken();
|
||||
}
|
||||
|
||||
pub fn lastToken(self: &NodeRoot) Token {
|
||||
return if (self.decls.len == 0) self.eof_token else self.decls.at(self.decls.len - 1).lastToken();
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodeVarDecl = struct {
|
||||
@ -62,6 +112,7 @@ pub const NodeVarDecl = struct {
|
||||
type_node: ?&Node,
|
||||
align_node: ?&Node,
|
||||
init_node: ?&Node,
|
||||
semicolon_token: Token,
|
||||
|
||||
pub fn iterate(self: &NodeVarDecl, index: usize) ?&Node {
|
||||
var i = index;
|
||||
@ -83,6 +134,18 @@ pub const NodeVarDecl = struct {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn firstToken(self: &NodeVarDecl) Token {
|
||||
if (self.visib_token) |visib_token| return visib_token;
|
||||
if (self.comptime_token) |comptime_token| return comptime_token;
|
||||
if (self.extern_token) |extern_token| return extern_token;
|
||||
assert(self.lib_name == null);
|
||||
return self.mut_token;
|
||||
}
|
||||
|
||||
pub fn lastToken(self: &NodeVarDecl) Token {
|
||||
return self.semicolon_token;
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodeIdentifier = struct {
|
||||
@ -92,6 +155,14 @@ pub const NodeIdentifier = struct {
|
||||
pub fn iterate(self: &NodeIdentifier, index: usize) ?&Node {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn firstToken(self: &NodeIdentifier) Token {
|
||||
return self.name_token;
|
||||
}
|
||||
|
||||
pub fn lastToken(self: &NodeIdentifier) Token {
|
||||
return self.name_token;
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodeFnProto = struct {
|
||||
@ -100,7 +171,7 @@ pub const NodeFnProto = struct {
|
||||
fn_token: Token,
|
||||
name_token: ?Token,
|
||||
params: ArrayList(&Node),
|
||||
return_type: &Node,
|
||||
return_type: ReturnType,
|
||||
var_args_token: ?Token,
|
||||
extern_token: ?Token,
|
||||
inline_token: ?Token,
|
||||
@ -109,6 +180,12 @@ pub const NodeFnProto = struct {
|
||||
lib_name: ?&Node, // populated if this is an extern declaration
|
||||
align_expr: ?&Node, // populated if align(A) is present
|
||||
|
||||
pub const ReturnType = union(enum) {
|
||||
Explicit: &Node,
|
||||
Infer: Token,
|
||||
InferErrorSet: &Node,
|
||||
};
|
||||
|
||||
pub fn iterate(self: &NodeFnProto, index: usize) ?&Node {
|
||||
var i = index;
|
||||
|
||||
@ -117,8 +194,18 @@ pub const NodeFnProto = struct {
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
if (i < 1) return self.return_type;
|
||||
i -= 1;
|
||||
switch (self.return_type) {
|
||||
// TODO allow this and next prong to share bodies since the types are the same
|
||||
ReturnType.Explicit => |node| {
|
||||
if (i < 1) return node;
|
||||
i -= 1;
|
||||
},
|
||||
ReturnType.InferErrorSet => |node| {
|
||||
if (i < 1) return node;
|
||||
i -= 1;
|
||||
},
|
||||
ReturnType.Infer => {},
|
||||
}
|
||||
|
||||
if (self.align_expr) |align_expr| {
|
||||
if (i < 1) return align_expr;
|
||||
@ -135,6 +222,25 @@ pub const NodeFnProto = struct {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn firstToken(self: &NodeFnProto) Token {
|
||||
if (self.visib_token) |visib_token| return visib_token;
|
||||
if (self.extern_token) |extern_token| return extern_token;
|
||||
assert(self.lib_name == null);
|
||||
if (self.inline_token) |inline_token| return inline_token;
|
||||
if (self.cc_token) |cc_token| return cc_token;
|
||||
return self.fn_token;
|
||||
}
|
||||
|
||||
pub fn lastToken(self: &NodeFnProto) Token {
|
||||
if (self.body_node) |body_node| return body_node.lastToken();
|
||||
switch (self.return_type) {
|
||||
// TODO allow this and next prong to share bodies since the types are the same
|
||||
ReturnType.Explicit => |node| return node.lastToken(),
|
||||
ReturnType.InferErrorSet => |node| return node.lastToken(),
|
||||
ReturnType.Infer => |token| return token,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodeParamDecl = struct {
|
||||
@ -153,6 +259,18 @@ pub const NodeParamDecl = struct {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn firstToken(self: &NodeParamDecl) Token {
|
||||
if (self.comptime_token) |comptime_token| return comptime_token;
|
||||
if (self.noalias_token) |noalias_token| return noalias_token;
|
||||
if (self.name_token) |name_token| return name_token;
|
||||
return self.type_node.firstToken();
|
||||
}
|
||||
|
||||
pub fn lastToken(self: &NodeParamDecl) Token {
|
||||
if (self.var_args_token) |var_args_token| return var_args_token;
|
||||
return self.type_node.lastToken();
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodeBlock = struct {
|
||||
@ -169,6 +287,14 @@ pub const NodeBlock = struct {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn firstToken(self: &NodeBlock) Token {
|
||||
return self.begin_token;
|
||||
}
|
||||
|
||||
pub fn lastToken(self: &NodeBlock) Token {
|
||||
return self.end_token;
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodeInfixOp = struct {
|
||||
@ -181,6 +307,7 @@ pub const NodeInfixOp = struct {
|
||||
const InfixOp = enum {
|
||||
EqualEqual,
|
||||
BangEqual,
|
||||
Period,
|
||||
};
|
||||
|
||||
pub fn iterate(self: &NodeInfixOp, index: usize) ?&Node {
|
||||
@ -190,8 +317,9 @@ pub const NodeInfixOp = struct {
|
||||
i -= 1;
|
||||
|
||||
switch (self.op) {
|
||||
InfixOp.EqualEqual => {},
|
||||
InfixOp.BangEqual => {},
|
||||
InfixOp.EqualEqual,
|
||||
InfixOp.BangEqual,
|
||||
InfixOp.Period => {},
|
||||
}
|
||||
|
||||
if (i < 1) return self.rhs;
|
||||
@ -199,6 +327,14 @@ pub const NodeInfixOp = struct {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn firstToken(self: &NodeInfixOp) Token {
|
||||
return self.lhs.firstToken();
|
||||
}
|
||||
|
||||
pub fn lastToken(self: &NodeInfixOp) Token {
|
||||
return self.rhs.lastToken();
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodePrefixOp = struct {
|
||||
@ -209,6 +345,7 @@ pub const NodePrefixOp = struct {
|
||||
|
||||
const PrefixOp = union(enum) {
|
||||
Return,
|
||||
Try,
|
||||
AddrOf: AddrOfInfo,
|
||||
};
|
||||
const AddrOfInfo = struct {
|
||||
@ -223,7 +360,8 @@ pub const NodePrefixOp = struct {
|
||||
var i = index;
|
||||
|
||||
switch (self.op) {
|
||||
PrefixOp.Return => {},
|
||||
PrefixOp.Return,
|
||||
PrefixOp.Try => {},
|
||||
PrefixOp.AddrOf => |addr_of_info| {
|
||||
if (addr_of_info.align_expr) |align_expr| {
|
||||
if (i < 1) return align_expr;
|
||||
@ -237,6 +375,14 @@ pub const NodePrefixOp = struct {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn firstToken(self: &NodePrefixOp) Token {
|
||||
return self.op_token;
|
||||
}
|
||||
|
||||
pub fn lastToken(self: &NodePrefixOp) Token {
|
||||
return self.rhs.lastToken();
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodeIntegerLiteral = struct {
|
||||
@ -246,6 +392,14 @@ pub const NodeIntegerLiteral = struct {
|
||||
pub fn iterate(self: &NodeIntegerLiteral, index: usize) ?&Node {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn firstToken(self: &NodeIntegerLiteral) Token {
|
||||
return self.token;
|
||||
}
|
||||
|
||||
pub fn lastToken(self: &NodeIntegerLiteral) Token {
|
||||
return self.token;
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodeFloatLiteral = struct {
|
||||
@ -255,12 +409,21 @@ pub const NodeFloatLiteral = struct {
|
||||
pub fn iterate(self: &NodeFloatLiteral, index: usize) ?&Node {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn firstToken(self: &NodeFloatLiteral) Token {
|
||||
return self.token;
|
||||
}
|
||||
|
||||
pub fn lastToken(self: &NodeFloatLiteral) Token {
|
||||
return self.token;
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodeBuiltinCall = struct {
|
||||
base: Node,
|
||||
builtin_token: Token,
|
||||
params: ArrayList(&Node),
|
||||
rparen_token: Token,
|
||||
|
||||
pub fn iterate(self: &NodeBuiltinCall, index: usize) ?&Node {
|
||||
var i = index;
|
||||
@ -270,4 +433,46 @@ pub const NodeBuiltinCall = struct {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn firstToken(self: &NodeBuiltinCall) Token {
|
||||
return self.builtin_token;
|
||||
}
|
||||
|
||||
pub fn lastToken(self: &NodeBuiltinCall) Token {
|
||||
return self.rparen_token;
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodeStringLiteral = struct {
|
||||
base: Node,
|
||||
token: Token,
|
||||
|
||||
pub fn iterate(self: &NodeStringLiteral, index: usize) ?&Node {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn firstToken(self: &NodeStringLiteral) Token {
|
||||
return self.token;
|
||||
}
|
||||
|
||||
pub fn lastToken(self: &NodeStringLiteral) Token {
|
||||
return self.token;
|
||||
}
|
||||
};
|
||||
|
||||
pub const NodeLineComment = struct {
|
||||
base: Node,
|
||||
lines: ArrayList(Token),
|
||||
|
||||
pub fn iterate(self: &NodeLineComment, index: usize) ?&Node {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn firstToken(self: &NodeLineComment) Token {
|
||||
return self.lines.at(0);
|
||||
}
|
||||
|
||||
pub fn lastToken(self: &NodeLineComment) Token {
|
||||
return self.lines.at(self.lines.len - 1);
|
||||
}
|
||||
};
|
||||
|
@ -18,6 +18,7 @@ pub const Parser = struct {
|
||||
put_back_tokens: [2]Token,
|
||||
put_back_count: usize,
|
||||
source_file_name: []const u8,
|
||||
pending_line_comment_node: ?&ast.NodeLineComment,
|
||||
|
||||
pub const Tree = struct {
|
||||
root_node: &ast.NodeRoot,
|
||||
@ -43,6 +44,7 @@ pub const Parser = struct {
|
||||
.put_back_count = 0,
|
||||
.source_file_name = source_file_name,
|
||||
.utility_bytes = []align(utility_bytes_align) u8{},
|
||||
.pending_line_comment_node = null,
|
||||
};
|
||||
}
|
||||
|
||||
@ -69,6 +71,11 @@ pub const Parser = struct {
|
||||
}
|
||||
};
|
||||
|
||||
const ExpectTokenSave = struct {
|
||||
id: Token.Id,
|
||||
ptr: &Token,
|
||||
};
|
||||
|
||||
const State = union(enum) {
|
||||
TopLevel,
|
||||
TopLevelExtern: ?Token,
|
||||
@ -85,13 +92,17 @@ pub const Parser = struct {
|
||||
VarDeclAlign: &ast.NodeVarDecl,
|
||||
VarDeclEq: &ast.NodeVarDecl,
|
||||
ExpectToken: @TagType(Token.Id),
|
||||
ExpectTokenSave: ExpectTokenSave,
|
||||
FnProto: &ast.NodeFnProto,
|
||||
FnProtoAlign: &ast.NodeFnProto,
|
||||
FnProtoReturnType: &ast.NodeFnProto,
|
||||
ParamDecl: &ast.NodeFnProto,
|
||||
ParamDeclComma,
|
||||
FnDef: &ast.NodeFnProto,
|
||||
Block: &ast.NodeBlock,
|
||||
Statement: &ast.NodeBlock,
|
||||
ExprListItemOrEnd: &ArrayList(&ast.Node),
|
||||
ExprListCommaOrEnd: &ArrayList(&ast.Node),
|
||||
};
|
||||
|
||||
/// Returns an AST tree, allocated with the parser's allocator.
|
||||
@ -122,6 +133,33 @@ pub const Parser = struct {
|
||||
// warn("\n");
|
||||
//}
|
||||
|
||||
// look for line comments
|
||||
while (true) {
|
||||
const token = self.getNextToken();
|
||||
if (token.id == Token.Id.LineComment) {
|
||||
const node = blk: {
|
||||
if (self.pending_line_comment_node) |comment_node| {
|
||||
break :blk comment_node;
|
||||
} else {
|
||||
const comment_node = try arena.create(ast.NodeLineComment);
|
||||
*comment_node = ast.NodeLineComment {
|
||||
.base = ast.Node {
|
||||
.id = ast.Node.Id.LineComment,
|
||||
.comment = null,
|
||||
},
|
||||
.lines = ArrayList(Token).init(arena),
|
||||
};
|
||||
self.pending_line_comment_node = comment_node;
|
||||
break :blk comment_node;
|
||||
}
|
||||
};
|
||||
try node.lines.append(token);
|
||||
continue;
|
||||
}
|
||||
self.putBackToken(token);
|
||||
break;
|
||||
}
|
||||
|
||||
// This gives us 1 free append that can't fail
|
||||
const state = stack.pop();
|
||||
|
||||
@ -133,7 +171,10 @@ pub const Parser = struct {
|
||||
stack.append(State { .TopLevelExtern = token }) catch unreachable;
|
||||
continue;
|
||||
},
|
||||
Token.Id.Eof => return Tree {.root_node = root_node, .arena_allocator = arena_allocator},
|
||||
Token.Id.Eof => {
|
||||
root_node.eof_token = token;
|
||||
return Tree {.root_node = root_node, .arena_allocator = arena_allocator};
|
||||
},
|
||||
else => {
|
||||
self.putBackToken(token);
|
||||
stack.append(State { .TopLevelExtern = null }) catch unreachable;
|
||||
@ -176,7 +217,7 @@ pub const Parser = struct {
|
||||
stack.append(State.TopLevel) catch unreachable;
|
||||
// TODO shouldn't need these casts
|
||||
const fn_proto = try self.createAttachFnProto(arena, &root_node.decls, token,
|
||||
ctx.extern_token, (?Token)(null), (?Token)(null), (?Token)(null));
|
||||
ctx.extern_token, (?Token)(null), ctx.visib_token, (?Token)(null));
|
||||
try stack.append(State { .FnDef = fn_proto });
|
||||
try stack.append(State { .FnProto = fn_proto });
|
||||
continue;
|
||||
@ -228,13 +269,19 @@ pub const Parser = struct {
|
||||
const token = self.getNextToken();
|
||||
if (token.id == Token.Id.Equal) {
|
||||
var_decl.eq_token = token;
|
||||
stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
|
||||
stack.append(State {
|
||||
.ExpectTokenSave = ExpectTokenSave {
|
||||
.id = Token.Id.Semicolon,
|
||||
.ptr = &var_decl.semicolon_token,
|
||||
},
|
||||
}) catch unreachable;
|
||||
try stack.append(State {
|
||||
.Expression = DestPtr {.NullableField = &var_decl.init_node},
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if (token.id == Token.Id.Semicolon) {
|
||||
var_decl.semicolon_token = token;
|
||||
continue;
|
||||
}
|
||||
return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id));
|
||||
@ -244,6 +291,11 @@ pub const Parser = struct {
|
||||
continue;
|
||||
},
|
||||
|
||||
State.ExpectTokenSave => |expect_token_save| {
|
||||
*expect_token_save.ptr = try self.eatToken(expect_token_save.id);
|
||||
continue;
|
||||
},
|
||||
|
||||
State.Expression => |dest_ptr| {
|
||||
// save the dest_ptr for later
|
||||
stack.append(state) catch unreachable;
|
||||
@ -261,6 +313,12 @@ pub const Parser = struct {
|
||||
try stack.append(State.ExpectOperand);
|
||||
continue;
|
||||
},
|
||||
Token.Id.Keyword_try => {
|
||||
try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token,
|
||||
ast.NodePrefixOp.PrefixOp.Try) });
|
||||
try stack.append(State.ExpectOperand);
|
||||
continue;
|
||||
},
|
||||
Token.Id.Ampersand => {
|
||||
const prefix_op = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{
|
||||
.AddrOf = ast.NodePrefixOp.AddrOfInfo {
|
||||
@ -297,6 +355,40 @@ pub const Parser = struct {
|
||||
try stack.append(State.AfterOperand);
|
||||
continue;
|
||||
},
|
||||
Token.Id.Builtin => {
|
||||
const node = try arena.create(ast.NodeBuiltinCall);
|
||||
*node = ast.NodeBuiltinCall {
|
||||
.base = self.initNode(ast.Node.Id.BuiltinCall),
|
||||
.builtin_token = token,
|
||||
.params = ArrayList(&ast.Node).init(arena),
|
||||
.rparen_token = undefined,
|
||||
};
|
||||
try stack.append(State {
|
||||
.Operand = &node.base
|
||||
});
|
||||
try stack.append(State.AfterOperand);
|
||||
try stack.append(State {.ExprListItemOrEnd = &node.params });
|
||||
try stack.append(State {
|
||||
.ExpectTokenSave = ExpectTokenSave {
|
||||
.id = Token.Id.LParen,
|
||||
.ptr = &node.rparen_token,
|
||||
},
|
||||
});
|
||||
continue;
|
||||
},
|
||||
Token.Id.StringLiteral => {
|
||||
const node = try arena.create(ast.NodeStringLiteral);
|
||||
*node = ast.NodeStringLiteral {
|
||||
.base = self.initNode(ast.Node.Id.StringLiteral),
|
||||
.token = token,
|
||||
};
|
||||
try stack.append(State {
|
||||
.Operand = &node.base
|
||||
});
|
||||
try stack.append(State.AfterOperand);
|
||||
continue;
|
||||
},
|
||||
|
||||
else => return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)),
|
||||
}
|
||||
},
|
||||
@ -321,6 +413,13 @@ pub const Parser = struct {
|
||||
try stack.append(State.ExpectOperand);
|
||||
continue;
|
||||
},
|
||||
Token.Id.Period => {
|
||||
try stack.append(State {
|
||||
.InfixOp = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.Period)
|
||||
});
|
||||
try stack.append(State.ExpectOperand);
|
||||
continue;
|
||||
},
|
||||
else => {
|
||||
// no postfix/infix operator after this operand.
|
||||
self.putBackToken(token);
|
||||
@ -352,6 +451,29 @@ pub const Parser = struct {
|
||||
}
|
||||
},
|
||||
|
||||
State.ExprListItemOrEnd => |params| {
|
||||
var token = self.getNextToken();
|
||||
switch (token.id) {
|
||||
Token.Id.RParen => continue,
|
||||
else => {
|
||||
self.putBackToken(token);
|
||||
stack.append(State { .ExprListCommaOrEnd = params }) catch unreachable;
|
||||
try stack.append(State { .Expression = DestPtr{.List = params} });
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
State.ExprListCommaOrEnd => |params| {
|
||||
var token = self.getNextToken();
|
||||
switch (token.id) {
|
||||
Token.Id.Comma => {
|
||||
stack.append(State { .ExprListItemOrEnd = params }) catch unreachable;
|
||||
},
|
||||
Token.Id.RParen => continue,
|
||||
else => return self.parseError(token, "expected ',' or ')', found {}", @tagName(token.id)),
|
||||
}
|
||||
},
|
||||
|
||||
State.AddrOfModifiers => |addr_of_info| {
|
||||
var token = self.getNextToken();
|
||||
switch (token.id) {
|
||||
@ -414,11 +536,37 @@ pub const Parser = struct {
|
||||
}
|
||||
self.putBackToken(token);
|
||||
stack.append(State {
|
||||
.TypeExpr = DestPtr {.Field = &fn_proto.return_type},
|
||||
.FnProtoReturnType = fn_proto,
|
||||
}) catch unreachable;
|
||||
continue;
|
||||
},
|
||||
|
||||
State.FnProtoReturnType => |fn_proto| {
|
||||
const token = self.getNextToken();
|
||||
switch (token.id) {
|
||||
Token.Id.Keyword_var => {
|
||||
fn_proto.return_type = ast.NodeFnProto.ReturnType { .Infer = token };
|
||||
},
|
||||
Token.Id.Bang => {
|
||||
fn_proto.return_type = ast.NodeFnProto.ReturnType { .InferErrorSet = undefined };
|
||||
stack.append(State {
|
||||
.TypeExpr = DestPtr {.Field = &fn_proto.return_type.InferErrorSet},
|
||||
}) catch unreachable;
|
||||
},
|
||||
else => {
|
||||
self.putBackToken(token);
|
||||
fn_proto.return_type = ast.NodeFnProto.ReturnType { .Explicit = undefined };
|
||||
stack.append(State {
|
||||
.TypeExpr = DestPtr {.Field = &fn_proto.return_type.Explicit},
|
||||
}) catch unreachable;
|
||||
},
|
||||
}
|
||||
if (token.id == Token.Id.Keyword_align) {
|
||||
@panic("TODO fn proto align");
|
||||
}
|
||||
continue;
|
||||
},
|
||||
|
||||
State.ParamDecl => |fn_proto| {
|
||||
var token = self.getNextToken();
|
||||
if (token.id == Token.Id.RParen) {
|
||||
@ -539,17 +687,25 @@ pub const Parser = struct {
|
||||
State.PrefixOp => unreachable,
|
||||
State.Operand => unreachable,
|
||||
}
|
||||
@import("std").debug.panic("{}", @tagName(state));
|
||||
//unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
fn initNode(self: &Parser, id: ast.Node.Id) ast.Node {
|
||||
if (self.pending_line_comment_node) |comment_node| {
|
||||
self.pending_line_comment_node = null;
|
||||
return ast.Node {.id = id, .comment = comment_node};
|
||||
}
|
||||
return ast.Node {.id = id, .comment = null };
|
||||
}
|
||||
|
||||
fn createRoot(self: &Parser, arena: &mem.Allocator) !&ast.NodeRoot {
|
||||
const node = try arena.create(ast.NodeRoot);
|
||||
|
||||
*node = ast.NodeRoot {
|
||||
.base = ast.Node {.id = ast.Node.Id.Root},
|
||||
.base = self.initNode(ast.Node.Id.Root),
|
||||
.decls = ArrayList(&ast.Node).init(arena),
|
||||
// initialized when we get the eof token
|
||||
.eof_token = undefined,
|
||||
};
|
||||
return node;
|
||||
}
|
||||
@ -560,7 +716,7 @@ pub const Parser = struct {
|
||||
const node = try arena.create(ast.NodeVarDecl);
|
||||
|
||||
*node = ast.NodeVarDecl {
|
||||
.base = ast.Node {.id = ast.Node.Id.VarDecl},
|
||||
.base = self.initNode(ast.Node.Id.VarDecl),
|
||||
.visib_token = *visib_token,
|
||||
.mut_token = *mut_token,
|
||||
.comptime_token = *comptime_token,
|
||||
@ -572,6 +728,7 @@ pub const Parser = struct {
|
||||
// initialized later
|
||||
.name_token = undefined,
|
||||
.eq_token = undefined,
|
||||
.semicolon_token = undefined,
|
||||
};
|
||||
return node;
|
||||
}
|
||||
@ -582,7 +739,7 @@ pub const Parser = struct {
|
||||
const node = try arena.create(ast.NodeFnProto);
|
||||
|
||||
*node = ast.NodeFnProto {
|
||||
.base = ast.Node {.id = ast.Node.Id.FnProto},
|
||||
.base = self.initNode(ast.Node.Id.FnProto),
|
||||
.visib_token = *visib_token,
|
||||
.name_token = null,
|
||||
.fn_token = *fn_token,
|
||||
@ -603,7 +760,7 @@ pub const Parser = struct {
|
||||
const node = try arena.create(ast.NodeParamDecl);
|
||||
|
||||
*node = ast.NodeParamDecl {
|
||||
.base = ast.Node {.id = ast.Node.Id.ParamDecl},
|
||||
.base = self.initNode(ast.Node.Id.ParamDecl),
|
||||
.comptime_token = null,
|
||||
.noalias_token = null,
|
||||
.name_token = null,
|
||||
@ -617,7 +774,7 @@ pub const Parser = struct {
|
||||
const node = try arena.create(ast.NodeBlock);
|
||||
|
||||
*node = ast.NodeBlock {
|
||||
.base = ast.Node {.id = ast.Node.Id.Block},
|
||||
.base = self.initNode(ast.Node.Id.Block),
|
||||
.begin_token = *begin_token,
|
||||
.end_token = undefined,
|
||||
.statements = ArrayList(&ast.Node).init(arena),
|
||||
@ -629,7 +786,7 @@ pub const Parser = struct {
|
||||
const node = try arena.create(ast.NodeInfixOp);
|
||||
|
||||
*node = ast.NodeInfixOp {
|
||||
.base = ast.Node {.id = ast.Node.Id.InfixOp},
|
||||
.base = self.initNode(ast.Node.Id.InfixOp),
|
||||
.op_token = *op_token,
|
||||
.lhs = undefined,
|
||||
.op = *op,
|
||||
@ -642,7 +799,7 @@ pub const Parser = struct {
|
||||
const node = try arena.create(ast.NodePrefixOp);
|
||||
|
||||
*node = ast.NodePrefixOp {
|
||||
.base = ast.Node {.id = ast.Node.Id.PrefixOp},
|
||||
.base = self.initNode(ast.Node.Id.PrefixOp),
|
||||
.op_token = *op_token,
|
||||
.op = *op,
|
||||
.rhs = undefined,
|
||||
@ -654,7 +811,7 @@ pub const Parser = struct {
|
||||
const node = try arena.create(ast.NodeIdentifier);
|
||||
|
||||
*node = ast.NodeIdentifier {
|
||||
.base = ast.Node {.id = ast.Node.Id.Identifier},
|
||||
.base = self.initNode(ast.Node.Id.Identifier),
|
||||
.name_token = *name_token,
|
||||
};
|
||||
return node;
|
||||
@ -664,7 +821,7 @@ pub const Parser = struct {
|
||||
const node = try arena.create(ast.NodeIntegerLiteral);
|
||||
|
||||
*node = ast.NodeIntegerLiteral {
|
||||
.base = ast.Node {.id = ast.Node.Id.IntegerLiteral},
|
||||
.base = self.initNode(ast.Node.Id.IntegerLiteral),
|
||||
.token = *token,
|
||||
};
|
||||
return node;
|
||||
@ -674,7 +831,7 @@ pub const Parser = struct {
|
||||
const node = try arena.create(ast.NodeFloatLiteral);
|
||||
|
||||
*node = ast.NodeFloatLiteral {
|
||||
.base = ast.Node {.id = ast.Node.Id.FloatLiteral},
|
||||
.base = self.initNode(ast.Node.Id.FloatLiteral),
|
||||
.token = *token,
|
||||
};
|
||||
return node;
|
||||
@ -712,11 +869,11 @@ pub const Parser = struct {
|
||||
|
||||
fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) (error{ParseError}) {
|
||||
const loc = self.tokenizer.getTokenLocation(token);
|
||||
warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args);
|
||||
warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, token.line + 1, token.column + 1, args);
|
||||
warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]);
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < loc.column) : (i += 1) {
|
||||
while (i < token.column) : (i += 1) {
|
||||
warn(" ");
|
||||
}
|
||||
}
|
||||
@ -808,11 +965,26 @@ pub const Parser = struct {
|
||||
defer self.deinitUtilityArrayList(stack);
|
||||
|
||||
{
|
||||
try stack.append(RenderState { .Text = "\n"});
|
||||
|
||||
var i = root_node.decls.len;
|
||||
while (i != 0) {
|
||||
i -= 1;
|
||||
const decl = root_node.decls.items[i];
|
||||
try stack.append(RenderState {.TopLevelDecl = decl});
|
||||
if (i != 0) {
|
||||
try stack.append(RenderState {
|
||||
.Text = blk: {
|
||||
const prev_node = root_node.decls.at(i - 1);
|
||||
const prev_line_index = prev_node.lastToken().line;
|
||||
const this_line_index = decl.firstToken().line;
|
||||
if (this_line_index - prev_line_index >= 2) {
|
||||
break :blk "\n\n";
|
||||
}
|
||||
break :blk "\n";
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -842,7 +1014,6 @@ pub const Parser = struct {
|
||||
|
||||
try stream.print("(");
|
||||
|
||||
try stack.append(RenderState { .Text = "\n" });
|
||||
if (fn_proto.body_node == null) {
|
||||
try stack.append(RenderState { .Text = ";" });
|
||||
}
|
||||
@ -860,7 +1031,6 @@ pub const Parser = struct {
|
||||
},
|
||||
ast.Node.Id.VarDecl => {
|
||||
const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", decl);
|
||||
try stack.append(RenderState { .Text = "\n"});
|
||||
try stack.append(RenderState { .VarDecl = var_decl});
|
||||
|
||||
},
|
||||
@ -927,19 +1097,35 @@ pub const Parser = struct {
|
||||
},
|
||||
ast.Node.Id.Block => {
|
||||
const block = @fieldParentPtr(ast.NodeBlock, "base", base);
|
||||
try stream.write("{");
|
||||
try stack.append(RenderState { .Text = "}"});
|
||||
try stack.append(RenderState.PrintIndent);
|
||||
try stack.append(RenderState { .Indent = indent});
|
||||
try stack.append(RenderState { .Text = "\n"});
|
||||
var i = block.statements.len;
|
||||
while (i != 0) {
|
||||
i -= 1;
|
||||
const statement_node = block.statements.items[i];
|
||||
try stack.append(RenderState { .Statement = statement_node});
|
||||
if (block.statements.len == 0) {
|
||||
try stream.write("{}");
|
||||
} else {
|
||||
try stream.write("{");
|
||||
try stack.append(RenderState { .Text = "}"});
|
||||
try stack.append(RenderState.PrintIndent);
|
||||
try stack.append(RenderState { .Indent = indent + indent_delta});
|
||||
try stack.append(RenderState { .Text = "\n" });
|
||||
try stack.append(RenderState { .Indent = indent});
|
||||
try stack.append(RenderState { .Text = "\n"});
|
||||
var i = block.statements.len;
|
||||
while (i != 0) {
|
||||
i -= 1;
|
||||
const statement_node = block.statements.items[i];
|
||||
try stack.append(RenderState { .Statement = statement_node});
|
||||
try stack.append(RenderState.PrintIndent);
|
||||
try stack.append(RenderState { .Indent = indent + indent_delta});
|
||||
try stack.append(RenderState {
|
||||
.Text = blk: {
|
||||
if (i != 0) {
|
||||
const prev_statement_node = block.statements.items[i - 1];
|
||||
const prev_line_index = prev_statement_node.lastToken().line;
|
||||
const this_line_index = statement_node.firstToken().line;
|
||||
if (this_line_index - prev_line_index >= 2) {
|
||||
break :blk "\n\n";
|
||||
}
|
||||
}
|
||||
break :blk "\n";
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
ast.Node.Id.InfixOp => {
|
||||
@ -952,7 +1138,9 @@ pub const Parser = struct {
|
||||
ast.NodeInfixOp.InfixOp.BangEqual => {
|
||||
try stack.append(RenderState { .Text = " != "});
|
||||
},
|
||||
else => unreachable,
|
||||
ast.NodeInfixOp.InfixOp.Period => {
|
||||
try stack.append(RenderState { .Text = "."});
|
||||
},
|
||||
}
|
||||
try stack.append(RenderState { .Expression = prefix_op_node.lhs });
|
||||
},
|
||||
@ -963,6 +1151,9 @@ pub const Parser = struct {
|
||||
ast.NodePrefixOp.PrefixOp.Return => {
|
||||
try stream.write("return ");
|
||||
},
|
||||
ast.NodePrefixOp.PrefixOp.Try => {
|
||||
try stream.write("try ");
|
||||
},
|
||||
ast.NodePrefixOp.PrefixOp.AddrOf => |addr_of_info| {
|
||||
try stream.write("&");
|
||||
if (addr_of_info.volatile_token != null) {
|
||||
@ -977,7 +1168,6 @@ pub const Parser = struct {
|
||||
try stack.append(RenderState { .Expression = align_expr});
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
ast.Node.Id.IntegerLiteral => {
|
||||
@ -988,7 +1178,30 @@ pub const Parser = struct {
|
||||
const float_literal = @fieldParentPtr(ast.NodeFloatLiteral, "base", base);
|
||||
try stream.print("{}", self.tokenizer.getTokenSlice(float_literal.token));
|
||||
},
|
||||
else => unreachable,
|
||||
ast.Node.Id.StringLiteral => {
|
||||
const string_literal = @fieldParentPtr(ast.NodeStringLiteral, "base", base);
|
||||
try stream.print("{}", self.tokenizer.getTokenSlice(string_literal.token));
|
||||
},
|
||||
ast.Node.Id.BuiltinCall => {
|
||||
const builtin_call = @fieldParentPtr(ast.NodeBuiltinCall, "base", base);
|
||||
try stream.print("{}(", self.tokenizer.getTokenSlice(builtin_call.builtin_token));
|
||||
try stack.append(RenderState { .Text = ")"});
|
||||
var i = builtin_call.params.len;
|
||||
while (i != 0) {
|
||||
i -= 1;
|
||||
const param_node = builtin_call.params.at(i);
|
||||
try stack.append(RenderState { .Expression = param_node});
|
||||
if (i != 0) {
|
||||
try stack.append(RenderState { .Text = ", " });
|
||||
}
|
||||
}
|
||||
},
|
||||
ast.Node.Id.FnProto => @panic("TODO fn proto in an expression"),
|
||||
ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"),
|
||||
|
||||
ast.Node.Id.Root,
|
||||
ast.Node.Id.VarDecl,
|
||||
ast.Node.Id.ParamDecl => unreachable,
|
||||
},
|
||||
RenderState.FnProtoRParen => |fn_proto| {
|
||||
try stream.print(")");
|
||||
@ -1000,9 +1213,26 @@ pub const Parser = struct {
|
||||
try stack.append(RenderState { .Expression = body_node});
|
||||
try stack.append(RenderState { .Text = " "});
|
||||
}
|
||||
try stack.append(RenderState { .Expression = fn_proto.return_type});
|
||||
switch (fn_proto.return_type) {
|
||||
ast.NodeFnProto.ReturnType.Explicit => |node| {
|
||||
try stack.append(RenderState { .Expression = node});
|
||||
},
|
||||
ast.NodeFnProto.ReturnType.Infer => {
|
||||
try stream.print("var");
|
||||
},
|
||||
ast.NodeFnProto.ReturnType.InferErrorSet => |node| {
|
||||
try stream.print("!");
|
||||
try stack.append(RenderState { .Expression = node});
|
||||
},
|
||||
}
|
||||
},
|
||||
RenderState.Statement => |base| {
|
||||
if (base.comment) |comment| {
|
||||
for (comment.lines.toSliceConst()) |line_token| {
|
||||
try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token));
|
||||
try stream.writeByteNTimes(' ', indent);
|
||||
}
|
||||
}
|
||||
switch (base.id) {
|
||||
ast.Node.Id.VarDecl => {
|
||||
const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base);
|
||||
@ -1040,10 +1270,7 @@ pub const Parser = struct {
|
||||
var fixed_buffer_mem: [100 * 1024]u8 = undefined;
|
||||
|
||||
fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {
|
||||
var padded_source: [0x100]u8 = undefined;
|
||||
std.mem.copy(u8, padded_source[0..source.len], source);
|
||||
|
||||
var tokenizer = Tokenizer.init(padded_source[0..source.len]);
|
||||
var tokenizer = Tokenizer.init(source);
|
||||
var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
|
||||
defer parser.deinit();
|
||||
|
||||
@ -1098,6 +1325,43 @@ fn testCanonical(source: []const u8) !void {
|
||||
}
|
||||
|
||||
test "zig fmt" {
|
||||
try testCanonical(
|
||||
\\const std = @import("std");
|
||||
\\
|
||||
\\pub fn main() !void {
|
||||
\\ // If this program is run without stdout attached, exit with an error.
|
||||
\\ // another comment
|
||||
\\ var stdout_file = try std.io.getStdOut;
|
||||
\\}
|
||||
\\
|
||||
);
|
||||
|
||||
try testCanonical(
|
||||
\\const std = @import("std");
|
||||
\\
|
||||
\\pub fn main() !void {
|
||||
\\ var stdout_file = try std.io.getStdOut;
|
||||
\\ var stdout_file = try std.io.getStdOut;
|
||||
\\
|
||||
\\ var stdout_file = try std.io.getStdOut;
|
||||
\\ var stdout_file = try std.io.getStdOut;
|
||||
\\}
|
||||
\\
|
||||
);
|
||||
|
||||
try testCanonical(
|
||||
\\pub fn main() !void {}
|
||||
\\pub fn main() var {}
|
||||
\\pub fn main() i32 {}
|
||||
\\
|
||||
);
|
||||
|
||||
try testCanonical(
|
||||
\\const std = @import("std");
|
||||
\\const std = @import();
|
||||
\\
|
||||
);
|
||||
|
||||
try testCanonical(
|
||||
\\extern fn puts(s: &const u8) c_int;
|
||||
\\
|
||||
|
@ -5,6 +5,8 @@ pub const Token = struct {
|
||||
id: Id,
|
||||
start: usize,
|
||||
end: usize,
|
||||
line: usize,
|
||||
column: usize,
|
||||
|
||||
const KeywordId = struct {
|
||||
bytes: []const u8,
|
||||
@ -16,6 +18,7 @@ pub const Token = struct {
|
||||
KeywordId{.bytes="and", .id = Id.Keyword_and},
|
||||
KeywordId{.bytes="asm", .id = Id.Keyword_asm},
|
||||
KeywordId{.bytes="break", .id = Id.Keyword_break},
|
||||
KeywordId{.bytes="catch", .id = Id.Keyword_catch},
|
||||
KeywordId{.bytes="comptime", .id = Id.Keyword_comptime},
|
||||
KeywordId{.bytes="const", .id = Id.Keyword_const},
|
||||
KeywordId{.bytes="continue", .id = Id.Keyword_continue},
|
||||
@ -28,7 +31,6 @@ pub const Token = struct {
|
||||
KeywordId{.bytes="false", .id = Id.Keyword_false},
|
||||
KeywordId{.bytes="fn", .id = Id.Keyword_fn},
|
||||
KeywordId{.bytes="for", .id = Id.Keyword_for},
|
||||
KeywordId{.bytes="goto", .id = Id.Keyword_goto},
|
||||
KeywordId{.bytes="if", .id = Id.Keyword_if},
|
||||
KeywordId{.bytes="inline", .id = Id.Keyword_inline},
|
||||
KeywordId{.bytes="nakedcc", .id = Id.Keyword_nakedcc},
|
||||
@ -38,12 +40,14 @@ pub const Token = struct {
|
||||
KeywordId{.bytes="packed", .id = Id.Keyword_packed},
|
||||
KeywordId{.bytes="pub", .id = Id.Keyword_pub},
|
||||
KeywordId{.bytes="return", .id = Id.Keyword_return},
|
||||
KeywordId{.bytes="section", .id = Id.Keyword_section},
|
||||
KeywordId{.bytes="stdcallcc", .id = Id.Keyword_stdcallcc},
|
||||
KeywordId{.bytes="struct", .id = Id.Keyword_struct},
|
||||
KeywordId{.bytes="switch", .id = Id.Keyword_switch},
|
||||
KeywordId{.bytes="test", .id = Id.Keyword_test},
|
||||
KeywordId{.bytes="this", .id = Id.Keyword_this},
|
||||
KeywordId{.bytes="true", .id = Id.Keyword_true},
|
||||
KeywordId{.bytes="try", .id = Id.Keyword_try},
|
||||
KeywordId{.bytes="undefined", .id = Id.Keyword_undefined},
|
||||
KeywordId{.bytes="union", .id = Id.Keyword_union},
|
||||
KeywordId{.bytes="unreachable", .id = Id.Keyword_unreachable},
|
||||
@ -95,10 +99,12 @@ pub const Token = struct {
|
||||
AmpersandEqual,
|
||||
IntegerLiteral,
|
||||
FloatLiteral,
|
||||
LineComment,
|
||||
Keyword_align,
|
||||
Keyword_and,
|
||||
Keyword_asm,
|
||||
Keyword_break,
|
||||
Keyword_catch,
|
||||
Keyword_comptime,
|
||||
Keyword_const,
|
||||
Keyword_continue,
|
||||
@ -111,7 +117,6 @@ pub const Token = struct {
|
||||
Keyword_false,
|
||||
Keyword_fn,
|
||||
Keyword_for,
|
||||
Keyword_goto,
|
||||
Keyword_if,
|
||||
Keyword_inline,
|
||||
Keyword_nakedcc,
|
||||
@ -121,12 +126,14 @@ pub const Token = struct {
|
||||
Keyword_packed,
|
||||
Keyword_pub,
|
||||
Keyword_return,
|
||||
Keyword_section,
|
||||
Keyword_stdcallcc,
|
||||
Keyword_struct,
|
||||
Keyword_switch,
|
||||
Keyword_test,
|
||||
Keyword_this,
|
||||
Keyword_true,
|
||||
Keyword_try,
|
||||
Keyword_undefined,
|
||||
Keyword_union,
|
||||
Keyword_unreachable,
|
||||
@ -140,21 +147,19 @@ pub const Token = struct {
|
||||
pub const Tokenizer = struct {
|
||||
buffer: []const u8,
|
||||
index: usize,
|
||||
line: usize,
|
||||
column: usize,
|
||||
pending_invalid_token: ?Token,
|
||||
|
||||
pub const Location = struct {
|
||||
line: usize,
|
||||
column: usize,
|
||||
pub const LineLocation = struct {
|
||||
line_start: usize,
|
||||
line_end: usize,
|
||||
};
|
||||
|
||||
pub fn getTokenLocation(self: &Tokenizer, token: &const Token) Location {
|
||||
var loc = Location {
|
||||
.line = 0,
|
||||
.column = 0,
|
||||
pub fn getTokenLocation(self: &Tokenizer, token: &const Token) LineLocation {
|
||||
var loc = LineLocation {
|
||||
.line_start = 0,
|
||||
.line_end = 0,
|
||||
.line_end = self.buffer.len,
|
||||
};
|
||||
for (self.buffer) |c, i| {
|
||||
if (i == token.start) {
|
||||
@ -163,11 +168,7 @@ pub const Tokenizer = struct {
|
||||
return loc;
|
||||
}
|
||||
if (c == '\n') {
|
||||
loc.line += 1;
|
||||
loc.column = 0;
|
||||
loc.line_start = i + 1;
|
||||
} else {
|
||||
loc.column += 1;
|
||||
}
|
||||
}
|
||||
return loc;
|
||||
@ -182,6 +183,8 @@ pub const Tokenizer = struct {
|
||||
return Tokenizer {
|
||||
.buffer = buffer,
|
||||
.index = 0,
|
||||
.line = 0,
|
||||
.column = 0,
|
||||
.pending_invalid_token = null,
|
||||
};
|
||||
}
|
||||
@ -222,13 +225,21 @@ pub const Tokenizer = struct {
|
||||
.id = Token.Id.Eof,
|
||||
.start = self.index,
|
||||
.end = undefined,
|
||||
.line = self.line,
|
||||
.column = self.column,
|
||||
};
|
||||
while (self.index < self.buffer.len) : (self.index += 1) {
|
||||
while (self.index < self.buffer.len) {
|
||||
const c = self.buffer[self.index];
|
||||
switch (state) {
|
||||
State.Start => switch (c) {
|
||||
' ', '\n' => {
|
||||
' ' => {
|
||||
result.start = self.index + 1;
|
||||
result.column += 1;
|
||||
},
|
||||
'\n' => {
|
||||
result.start = self.index + 1;
|
||||
result.line += 1;
|
||||
result.column = 0;
|
||||
},
|
||||
'c' => {
|
||||
state = State.C;
|
||||
@ -460,7 +471,7 @@ pub const Tokenizer = struct {
|
||||
|
||||
State.Slash => switch (c) {
|
||||
'/' => {
|
||||
result.id = undefined;
|
||||
result.id = Token.Id.LineComment;
|
||||
state = State.LineComment;
|
||||
},
|
||||
else => {
|
||||
@ -469,14 +480,7 @@ pub const Tokenizer = struct {
|
||||
},
|
||||
},
|
||||
State.LineComment => switch (c) {
|
||||
'\n' => {
|
||||
state = State.Start;
|
||||
result = Token {
|
||||
.id = Token.Id.Eof,
|
||||
.start = self.index + 1,
|
||||
.end = undefined,
|
||||
};
|
||||
},
|
||||
'\n' => break,
|
||||
else => self.checkLiteralCharacter(),
|
||||
},
|
||||
State.Zero => switch (c) {
|
||||
@ -543,6 +547,14 @@ pub const Tokenizer = struct {
|
||||
else => break,
|
||||
},
|
||||
}
|
||||
|
||||
self.index += 1;
|
||||
if (c == '\n') {
|
||||
self.line += 1;
|
||||
self.column = 0;
|
||||
} else {
|
||||
self.column += 1;
|
||||
}
|
||||
} else if (self.index == self.buffer.len) {
|
||||
switch (state) {
|
||||
State.Start,
|
||||
@ -622,6 +634,8 @@ pub const Tokenizer = struct {
|
||||
.id = Token.Id.Invalid,
|
||||
.start = self.index,
|
||||
.end = self.index + invalid_length,
|
||||
.line = self.line,
|
||||
.column = self.column,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@ comptime {
|
||||
_ = @import("cases/slice.zig");
|
||||
_ = @import("cases/struct.zig");
|
||||
_ = @import("cases/struct_contains_slice_of_itself.zig");
|
||||
_ = @import("cases/struct_contains_null_ptr_itself.zig");
|
||||
_ = @import("cases/switch.zig");
|
||||
_ = @import("cases/switch_prong_err_enum.zig");
|
||||
_ = @import("cases/switch_prong_implicit_cast.zig");
|
||||
|
@ -388,3 +388,10 @@ test "string literal used as comptime slice is memoized" {
|
||||
comptime assert(TypeWithCompTimeSlice(a).Node == TypeWithCompTimeSlice(b).Node);
|
||||
comptime assert(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node);
|
||||
}
|
||||
|
||||
test "comptime slice of undefined pointer of length 0" {
|
||||
const slice1 = (&i32)(undefined)[0..0];
|
||||
assert(slice1.len == 0);
|
||||
const slice2 = (&i32)(undefined)[100..100];
|
||||
assert(slice2.len == 0);
|
||||
}
|
||||
|
@ -394,4 +394,11 @@ fn test_f128() void {
|
||||
|
||||
fn should_not_be_zero(x: f128) void {
|
||||
assert(x != 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
test "comptime float rem int" {
|
||||
comptime {
|
||||
var x = f32(1) % 2;
|
||||
assert(x == 1.0);
|
||||
}
|
||||
}
|
||||
|
@ -499,12 +499,29 @@ test "@canImplicitCast" {
|
||||
}
|
||||
|
||||
test "@typeName" {
|
||||
const Struct = struct {
|
||||
};
|
||||
const Union = union {
|
||||
unused: u8,
|
||||
};
|
||||
const Enum = enum {
|
||||
Unused,
|
||||
};
|
||||
comptime {
|
||||
assert(mem.eql(u8, @typeName(i64), "i64"));
|
||||
assert(mem.eql(u8, @typeName(&usize), "&usize"));
|
||||
// https://github.com/zig-lang/zig/issues/675
|
||||
assert(mem.eql(u8, @typeName(TypeFromFn(u8)), "TypeFromFn(u8)"));
|
||||
assert(mem.eql(u8, @typeName(Struct), "Struct"));
|
||||
assert(mem.eql(u8, @typeName(Union), "Union"));
|
||||
assert(mem.eql(u8, @typeName(Enum), "Enum"));
|
||||
}
|
||||
}
|
||||
|
||||
fn TypeFromFn(comptime T: type) type {
|
||||
return struct {};
|
||||
}
|
||||
|
||||
test "volatile load and store" {
|
||||
var number: i32 = 1234;
|
||||
const ptr = (&volatile i32)(&number);
|
||||
|
22
test/cases/struct_contains_null_ptr_itself.zig
Normal file
22
test/cases/struct_contains_null_ptr_itself.zig
Normal file
@ -0,0 +1,22 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
test "struct contains null pointer which contains original struct" {
|
||||
var x: ?&NodeLineComment = null;
|
||||
assert(x == null);
|
||||
}
|
||||
|
||||
pub const Node = struct {
|
||||
id: Id,
|
||||
comment: ?&NodeLineComment,
|
||||
|
||||
pub const Id = enum {
|
||||
Root,
|
||||
LineComment,
|
||||
};
|
||||
};
|
||||
|
||||
pub const NodeLineComment = struct {
|
||||
base: Node,
|
||||
};
|
||||
|
@ -1,6 +1,26 @@
|
||||
const tests = @import("tests.zig");
|
||||
|
||||
pub fn addCases(cases: &tests.CompileErrorContext) void {
|
||||
cases.add("comptime slice of undefined pointer non-zero len",
|
||||
\\export fn entry() void {
|
||||
\\ const slice = (&i32)(undefined)[0..1];
|
||||
\\}
|
||||
,
|
||||
".tmp_source.zig:2:36: error: non-zero length slice of undefined pointer");
|
||||
|
||||
cases.add("type checking function pointers",
|
||||
\\fn a(b: fn (&const u8) void) void {
|
||||
\\ b('a');
|
||||
\\}
|
||||
\\fn c(d: u8) void {
|
||||
\\ @import("std").debug.warn("{c}\n", d);
|
||||
\\}
|
||||
\\export fn entry() void {
|
||||
\\ a(c);
|
||||
\\}
|
||||
,
|
||||
".tmp_source.zig:8:7: error: expected type 'fn(&const u8) void', found 'fn(u8) void'");
|
||||
|
||||
cases.add("no else prong on switch on global error set",
|
||||
\\export fn entry() void {
|
||||
\\ foo(error.A);
|
||||
|
Loading…
Reference in New Issue
Block a user