| ... | @@ -33,6 +33,14 @@ pub const Inst = struct { | ... | @@ -33,6 +33,14 @@ pub const Inst = struct { |
| 33 | }; | 33 | }; |
| 34 | } | 34 | } |
| 35 | | 35 | |
| | 36 | pub fn cast(base: *Inst, comptime T: type) ?*T { |
| | 37 | const expected_tag = std.meta.fieldInfo(T, "base").default_value.?.tag; |
| | 38 | if (base.tag != expected_tag) |
| | 39 | return null; |
| | 40 | |
| | 41 | return @fieldParentPtr(T, "base", base); |
| | 42 | } |
| | 43 | |
| 36 | /// This struct owns the `Value` memory. When the struct is deallocated, | 44 | /// This struct owns the `Value` memory. When the struct is deallocated, |
| 37 | /// so is the `Value`. The value of a constant must be copied into | 45 | /// so is the `Value`. The value of a constant must be copied into |
| 38 | /// a memory location for the value to survive after a const instruction. | 46 | /// a memory location for the value to survive after a const instruction. |
| ... | @@ -130,6 +138,92 @@ pub const ErrorMsg = struct { | ... | @@ -130,6 +138,92 @@ pub const ErrorMsg = struct { |
| 130 | pub const Tree = struct { | 138 | pub const Tree = struct { |
| 131 | decls: []*Inst, | 139 | decls: []*Inst, |
| 132 | errors: []ErrorMsg, | 140 | errors: []ErrorMsg, |
| | 141 | |
| | 142 | pub fn deinit(self: *Tree) void { |
| | 143 | // TODO resource deallocation |
| | 144 | self.* = undefined; |
| | 145 | } |
| | 146 | |
| | 147 | /// This is a debugging utility for rendering the tree to stderr. |
| | 148 | pub fn dump(self: Tree) void { |
| | 149 | self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {}; |
| | 150 | } |
| | 151 | |
| | 152 | const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Inst.Fn.Body }); |
| | 153 | |
| | 154 | pub fn writeToStream(self: Tree, allocator: *Allocator, stream: var) !void { |
| | 155 | // First, build a map of *Inst to @ or % indexes |
| | 156 | var inst_table = InstPtrTable.init(allocator); |
| | 157 | defer inst_table.deinit(); |
| | 158 | |
| | 159 | try inst_table.ensureCapacity(self.decls.len); |
| | 160 | |
| | 161 | for (self.decls) |decl, decl_i| { |
| | 162 | try inst_table.putNoClobber(decl, .{ .index = decl_i, .fn_body = null }); |
| | 163 | |
| | 164 | if (decl.cast(Inst.Fn)) |fn_inst| { |
| | 165 | for (fn_inst.positionals.body.instructions) |inst, inst_i| { |
| | 166 | try inst_table.putNoClobber(inst, .{ .index = inst_i, .fn_body = &fn_inst.positionals.body }); |
| | 167 | } |
| | 168 | } |
| | 169 | } |
| | 170 | |
| | 171 | for (self.decls) |decl, i| { |
| | 172 | try stream.print("@{} = ", .{i}); |
| | 173 | try self.writeInstToStream(stream, decl, &inst_table); |
| | 174 | } |
| | 175 | } |
| | 176 | |
| | 177 | fn writeInstToStream(self: Tree, stream: var, decl: *Inst, inst_table: *const InstPtrTable) !void { |
| | 178 | // TODO I tried implementing this with an inline for loop and hit a compiler bug |
| | 179 | switch (decl.tag) { |
| | 180 | .constant => return self.writeInstToStreamGeneric(stream, .constant, decl, inst_table), |
| | 181 | .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table), |
| | 182 | .fieldptr => return self.writeInstToStreamGeneric(stream, .fieldptr, decl, inst_table), |
| | 183 | .deref => return self.writeInstToStreamGeneric(stream, .deref, decl, inst_table), |
| | 184 | .@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", decl, inst_table), |
| | 185 | .@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", decl, inst_table), |
| | 186 | .@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", decl, inst_table), |
| | 187 | .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table), |
| | 188 | } |
| | 189 | } |
| | 190 | |
| | 191 | fn writeInstToStreamGeneric( |
| | 192 | self: Tree, |
| | 193 | stream: var, |
| | 194 | comptime inst_tag: Inst.Tag, |
| | 195 | base: *Inst, |
| | 196 | inst_table: *const InstPtrTable, |
| | 197 | ) !void { |
| | 198 | const SpecificInst = Inst.TagToType(inst_tag); |
| | 199 | const inst = @fieldParentPtr(SpecificInst, "base", base); |
| | 200 | const Positionals = @TypeOf(inst.positionals); |
| | 201 | try stream.writeAll(@tagName(inst_tag) ++ "("); |
| | 202 | inline for (@typeInfo(Positionals).Struct.fields) |arg_field, i| { |
| | 203 | if (i != 0) { |
| | 204 | try stream.writeAll(", "); |
| | 205 | } |
| | 206 | try self.writeParamToStream(stream, @field(inst.positionals, arg_field.name), inst_table); |
| | 207 | } |
| | 208 | try stream.writeAll(")\n"); |
| | 209 | } |
| | 210 | |
| | 211 | pub fn writeParamToStream(self: Tree, stream: var, param: var, inst_table: *const InstPtrTable) !void { |
| | 212 | switch (@TypeOf(param)) { |
| | 213 | Value => { |
| | 214 | try stream.print("{}", .{param}); |
| | 215 | }, |
| | 216 | *Inst => { |
| | 217 | const info = inst_table.getValue(param).?; |
| | 218 | const prefix = if (info.fn_body == null) "@" else "%"; |
| | 219 | try stream.print("{}{}", .{ prefix, info.index }); |
| | 220 | }, |
| | 221 | Inst.Fn.Body => { |
| | 222 | try stream.print("(fn body)", .{}); |
| | 223 | }, |
| | 224 | else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)), |
| | 225 | } |
| | 226 | } |
| 133 | }; | 227 | }; |
| 134 | | 228 | |
| 135 | const ParseContext = struct { | 229 | const ParseContext = struct { |
| ... | @@ -278,6 +372,7 @@ fn parseInstructionGeneric( | ... | @@ -278,6 +372,7 @@ fn parseInstructionGeneric( |
| 278 | body_ctx: ?*BodyContext, | 372 | body_ctx: ?*BodyContext, |
| 279 | ) !*Inst { | 373 | ) !*Inst { |
| 280 | const inst_specific = try ctx.allocator.create(InstType); | 374 | const inst_specific = try ctx.allocator.create(InstType); |
| | 375 | inst_specific.base = std.meta.fieldInfo(InstType, "base").default_value.?; |
| 281 | | 376 | |
| 282 | if (@hasField(InstType, "ty")) { | 377 | if (@hasField(InstType, "ty")) { |
| 283 | inst_specific.ty = opt_type orelse { | 378 | inst_specific.ty = opt_type orelse { |
| ... | @@ -286,7 +381,7 @@ fn parseInstructionGeneric( | ... | @@ -286,7 +381,7 @@ fn parseInstructionGeneric( |
| 286 | } | 381 | } |
| 287 | | 382 | |
| 288 | const Positionals = @TypeOf(inst_specific.positionals); | 383 | const Positionals = @TypeOf(inst_specific.positionals); |
| 289 | inline for (@typeInfo(Positionals).Struct.fields) |arg_field, i| { | 384 | inline for (@typeInfo(Positionals).Struct.fields) |arg_field| { |
| 290 | if (ctx.source[ctx.i] == ',') { | 385 | if (ctx.source[ctx.i] == ',') { |
| 291 | ctx.i += 1; | 386 | ctx.i += 1; |
| 292 | skipSpace(ctx); | 387 | skipSpace(ctx); |
| ... | @@ -379,7 +474,11 @@ fn parseParameterInst(ctx: *ParseContext, body_ctx: ?*BodyContext) !*Inst { | ... | @@ -379,7 +474,11 @@ fn parseParameterInst(ctx: *ParseContext, body_ctx: ?*BodyContext) !*Inst { |
| 379 | const local_ref = switch (ctx.source[ctx.i]) { | 474 | const local_ref = switch (ctx.source[ctx.i]) { |
| 380 | '@' => false, | 475 | '@' => false, |
| 381 | '%' => true, | 476 | '%' => true, |
| 382 | '"' => return parseStringLiteralConst(ctx, null), | 477 | '"' => { |
| | 478 | const str_lit_inst = try parseStringLiteralConst(ctx, null); |
| | 479 | try ctx.decls.append(str_lit_inst); |
| | 480 | return str_lit_inst; |
| | 481 | }, |
| 383 | else => |byte| return parseError(ctx, "unexpected byte: '{c}'", .{byte}), | 482 | else => |byte| return parseError(ctx, "unexpected byte: '{c}'", .{byte}), |
| 384 | }; | 483 | }; |
| 385 | const map = if (local_ref) | 484 | const map = if (local_ref) |
| ... | @@ -538,7 +637,9 @@ pub fn main() anyerror!void { | ... | @@ -538,7 +637,9 @@ pub fn main() anyerror!void { |
| 538 | | 637 | |
| 539 | const source = try std.fs.cwd().readFileAlloc(allocator, src_path, std.math.maxInt(u32)); | 638 | const source = try std.fs.cwd().readFileAlloc(allocator, src_path, std.math.maxInt(u32)); |
| 540 | | 639 | |
| 541 | const tree = try parse(allocator, source); | 640 | var tree = try parse(allocator, source); |
| | 641 | defer tree.deinit(); |
| | 642 | |
| 542 | if (tree.errors.len != 0) { | 643 | if (tree.errors.len != 0) { |
| 543 | for (tree.errors) |err_msg| { | 644 | for (tree.errors) |err_msg| { |
| 544 | const loc = findLineColumn(source, err_msg.byte_offset); | 645 | const loc = findLineColumn(source, err_msg.byte_offset); |
| ... | @@ -547,6 +648,11 @@ pub fn main() anyerror!void { | ... | @@ -547,6 +648,11 @@ pub fn main() anyerror!void { |
| 547 | if (debug_error_trace) return error.ParseFailure; | 648 | if (debug_error_trace) return error.ParseFailure; |
| 548 | std.process.exit(1); | 649 | std.process.exit(1); |
| 549 | } | 650 | } |
| | 651 | |
| | 652 | tree.dump(); |
| | 653 | |
| | 654 | //const new_tree = try semanticallyAnalyze(tree); |
| | 655 | //defer new_tree.deinit(); |
| 550 | } | 656 | } |
| 551 | | 657 | |
| 552 | fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } { | 658 | fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } { |