refactor: boilerplate for semantic ir
This commit is contained in:
parent
98f5215629
commit
f16162b5bb
2 changed files with 604 additions and 482 deletions
822
src/AstGen.zig
822
src/AstGen.zig
File diff suppressed because it is too large
Load diff
264
src/Ir.zig
Normal file
264
src/Ir.zig
Normal file
|
|
@ -0,0 +1,264 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const Ast = @import("Ast.zig");
|
||||||
|
const Ir = @This();
|
||||||
|
|
||||||
|
string_bytes: []u8,
|
||||||
|
instructions: []Inst,
|
||||||
|
extra: []u32,
|
||||||
|
errors: []Ast.Error,
|
||||||
|
|
||||||
|
pub const Inst = struct {
|
||||||
|
tag: Tag,
|
||||||
|
data: Data,
|
||||||
|
|
||||||
|
pub const Index = enum(u32) {
|
||||||
|
file_inst = 0,
|
||||||
|
_,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Tag = enum {
|
||||||
|
file,
|
||||||
|
decl_knot,
|
||||||
|
block,
|
||||||
|
add,
|
||||||
|
sub,
|
||||||
|
mul,
|
||||||
|
div,
|
||||||
|
mod,
|
||||||
|
neg,
|
||||||
|
true_literal,
|
||||||
|
false_literal,
|
||||||
|
integer,
|
||||||
|
string,
|
||||||
|
content,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Data = union {
|
||||||
|
payload: struct {
|
||||||
|
payload_index: u32,
|
||||||
|
},
|
||||||
|
un: struct {
|
||||||
|
lhs: Inst.Index,
|
||||||
|
},
|
||||||
|
bin: struct {
|
||||||
|
lhs: Inst.Index,
|
||||||
|
rhs: Inst.Index,
|
||||||
|
},
|
||||||
|
integer: struct {
|
||||||
|
value: u64,
|
||||||
|
},
|
||||||
|
string: struct {
|
||||||
|
/// Offset into `string_bytes`.
|
||||||
|
start: NullTerminatedString,
|
||||||
|
|
||||||
|
pub fn get(self: @This(), ir: Ir) []const u8 {
|
||||||
|
return nullTerminatedString(ir, self.start);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Knot = struct {
|
||||||
|
name_ref: NullTerminatedString,
|
||||||
|
body_len: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Block = struct {
|
||||||
|
body_len: u32,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const NullTerminatedString = enum(u32) {
|
||||||
|
empty,
|
||||||
|
_,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const IndexSlice = struct {
|
||||||
|
index: u32,
|
||||||
|
len: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn deinit(ir: *Ir, gpa: std.mem.Allocator) void {
|
||||||
|
gpa.free(ir.string_bytes);
|
||||||
|
gpa.free(ir.instructions);
|
||||||
|
gpa.free(ir.extra);
|
||||||
|
gpa.free(ir.errors);
|
||||||
|
ir.* = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Prefix = struct {
|
||||||
|
buf: std.ArrayListUnmanaged(u8) = .empty,
|
||||||
|
|
||||||
|
pub fn deinit(self: *Prefix, gpa: std.mem.Allocator) void {
|
||||||
|
self.buf.deinit(gpa);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writeIndent(self: *const Prefix, writer: *std.Io.Writer) !void {
|
||||||
|
try writer.writeAll(self.buf.items);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pushChildPrefix(self: *Prefix, gpa: std.mem.Allocator) !usize {
|
||||||
|
const old_len = self.buf.items.len;
|
||||||
|
const seg: []const u8 = " ";
|
||||||
|
try self.buf.appendSlice(gpa, seg);
|
||||||
|
return old_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restore(self: *Prefix, new_len: usize) void {
|
||||||
|
self.buf.shrinkRetainingCapacity(new_len);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Render = struct {
|
||||||
|
gpa: std.mem.Allocator,
|
||||||
|
prefix: Prefix,
|
||||||
|
writer: *std.Io.Writer,
|
||||||
|
|
||||||
|
pub const Error = error{
|
||||||
|
OutOfMemory,
|
||||||
|
WriteFailed,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn renderUnaryInst(r: *Render, inst: Inst) Error!void {
|
||||||
|
const io_w = r.writer;
|
||||||
|
return io_w.print("{s}(%{d})", .{ @tagName(inst.tag), inst.data.un.lhs });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderBinaryInst(r: *Render, inst: Inst) Error!void {
|
||||||
|
const io_w = r.writer;
|
||||||
|
return io_w.print("{s}(%{d}, %{d})", .{ @tagName(inst.tag), inst.data.bin.lhs, inst.data.bin.rhs });
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderBodyInner(r: *Render, ir: Ir, body_list: []const Inst.Index) Error!void {
|
||||||
|
const io_w = r.writer;
|
||||||
|
try io_w.writeAll("{\n");
|
||||||
|
{
|
||||||
|
const old_len = try r.prefix.pushChildPrefix(r.gpa);
|
||||||
|
defer r.prefix.restore(old_len);
|
||||||
|
|
||||||
|
for (body_list) |inst| try r.renderInst(ir, inst);
|
||||||
|
}
|
||||||
|
try r.prefix.writeIndent(io_w);
|
||||||
|
try io_w.writeAll("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderBlockInst(r: *Render, ir: Ir, inst: Inst) Error!void {
|
||||||
|
const io_w = r.writer;
|
||||||
|
const extra = ir.extraData(Inst.Block, inst.data.payload.payload_index);
|
||||||
|
const body_slice = ir.bodySlice(extra.end, extra.data.body_len);
|
||||||
|
|
||||||
|
try io_w.print("{s}(", .{@tagName(inst.tag)});
|
||||||
|
try renderBodyInner(r, ir, body_slice);
|
||||||
|
try io_w.writeAll(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderKnotInst(r: *Render, ir: Ir, inst: Inst) Error!void {
|
||||||
|
const io_w = r.writer;
|
||||||
|
const extra = ir.extraData(Inst.Knot, inst.data.payload.payload_index);
|
||||||
|
const body_slice = ir.bodySlice(extra.end, extra.data.body_len);
|
||||||
|
const knot_ident_str = ir.nullTerminatedString(extra.data.name_ref);
|
||||||
|
try io_w.print("{s}(name=\"{s}\",body=", .{ @tagName(inst.tag), knot_ident_str });
|
||||||
|
try renderBodyInner(r, ir, body_slice);
|
||||||
|
try io_w.writeAll(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderFileInst(r: *Render, ir: Ir, inst: Inst) Error!void {
|
||||||
|
const io_w = r.writer;
|
||||||
|
const extra = ir.extraData(Inst.Block, inst.data.payload.payload_index);
|
||||||
|
const body_slice = ir.bodySlice(extra.end, extra.data.body_len);
|
||||||
|
|
||||||
|
try io_w.print("{s}(", .{@tagName(inst.tag)});
|
||||||
|
try renderBodyInner(r, ir, body_slice);
|
||||||
|
return io_w.writeAll(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderInst(r: *Render, ir: Ir, index: Inst.Index) Error!void {
|
||||||
|
const io_w = r.writer;
|
||||||
|
const inst_index: u32 = @intFromEnum(index);
|
||||||
|
const inst = ir.instructions[inst_index];
|
||||||
|
|
||||||
|
try r.prefix.writeIndent(io_w);
|
||||||
|
try io_w.print("%{d} = ", .{inst_index});
|
||||||
|
switch (inst.tag) {
|
||||||
|
.file => try r.renderFileInst(ir, inst),
|
||||||
|
.decl_knot => try r.renderKnotInst(ir, inst),
|
||||||
|
.block => try r.renderBlockInst(ir, inst),
|
||||||
|
.add => try r.renderBinaryInst(inst),
|
||||||
|
.sub => try r.renderBinaryInst(inst),
|
||||||
|
.mul => try r.renderBinaryInst(inst),
|
||||||
|
.div => try r.renderBinaryInst(inst),
|
||||||
|
.mod => try r.renderBinaryInst(inst),
|
||||||
|
.neg => try r.renderUnaryInst(inst),
|
||||||
|
.true_literal => {
|
||||||
|
try io_w.print("{s}", .{@tagName(inst.tag)});
|
||||||
|
},
|
||||||
|
.false_literal => {
|
||||||
|
try io_w.print("{s}", .{@tagName(inst.tag)});
|
||||||
|
},
|
||||||
|
.integer => {
|
||||||
|
const value = inst.data.integer.value;
|
||||||
|
try io_w.print("{s}({d})", .{ @tagName(inst.tag), value });
|
||||||
|
},
|
||||||
|
.string => {
|
||||||
|
const str_bytes = inst.data.string.get(ir);
|
||||||
|
try io_w.print("{s}(\"{s}\")", .{ @tagName(inst.tag), str_bytes });
|
||||||
|
},
|
||||||
|
.content => try r.renderUnaryInst(inst),
|
||||||
|
}
|
||||||
|
try io_w.writeAll("\n");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn render(ir: Ir, gpa: std.mem.Allocator, writer: *std.Io.Writer) !void {
|
||||||
|
std.debug.assert(ir.instructions.len > 0);
|
||||||
|
var r = Render{ .gpa = gpa, .writer = writer, .prefix = .{} };
|
||||||
|
defer r.prefix.deinit(gpa);
|
||||||
|
|
||||||
|
try r.renderInst(ir, .file_inst);
|
||||||
|
return writer.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dumpStringsWithHex(cu: *const Ir) void {
|
||||||
|
const bytes = cu.string_bytes;
|
||||||
|
var start: usize = 0;
|
||||||
|
while (start < bytes.len) {
|
||||||
|
const end = std.mem.indexOfScalarPos(u8, bytes, start, 0) orelse break;
|
||||||
|
const s = bytes[start..end];
|
||||||
|
|
||||||
|
std.debug.print("[{d:04}] ", .{start});
|
||||||
|
for (s) |b| std.debug.print("{x:02} ", .{b});
|
||||||
|
std.debug.print("00: {s}\n", .{s});
|
||||||
|
start = end + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ExtraData(comptime T: type) type {
|
||||||
|
return struct {
|
||||||
|
data: T,
|
||||||
|
end: usize,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extraData(ir: Ir, comptime T: type, index: usize) ExtraData(T) {
|
||||||
|
const fields = @typeInfo(T).@"struct".fields;
|
||||||
|
var i: usize = index;
|
||||||
|
var result: T = undefined;
|
||||||
|
inline for (fields) |field| {
|
||||||
|
@field(result, field.name) = switch (field.type) {
|
||||||
|
u32 => ir.extra[i],
|
||||||
|
Inst.Index => @enumFromInt(ir.extra[i]),
|
||||||
|
NullTerminatedString => @enumFromInt(ir.extra[i]),
|
||||||
|
else => @compileError("bad field type"),
|
||||||
|
};
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
return .{ .data = result, .end = i };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nullTerminatedString(ir: Ir, index: NullTerminatedString) [:0]const u8 {
|
||||||
|
const slice = ir.string_bytes[@intFromEnum(index)..];
|
||||||
|
return slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bodySlice(ir: Ir, start: usize, len: usize) []Inst.Index {
|
||||||
|
return @ptrCast(ir.extra[start..][0..len]);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue