feat: ink variable semantics, global ref table for astgen
This commit is contained in:
parent
197a37ebe7
commit
9658c8a308
7 changed files with 298 additions and 141 deletions
215
src/Sema.zig
215
src/Sema.zig
|
|
@ -2,6 +2,7 @@ const std = @import("std");
|
|||
const Ir = @import("Ir.zig");
|
||||
const Story = @import("Story.zig");
|
||||
const Object = Story.Object;
|
||||
const assert = std.debug.assert;
|
||||
const Sema = @This();
|
||||
|
||||
gpa: std.mem.Allocator,
|
||||
|
|
@ -23,6 +24,10 @@ fn deinit(sema: *Sema) void {
|
|||
sema.* = undefined;
|
||||
}
|
||||
|
||||
fn fail(_: *Sema, message: []const u8) InnerError {
|
||||
@panic(message);
|
||||
}
|
||||
|
||||
fn resolveIndex(sema: *Sema, index: Ir.Inst.Index) Ir.Inst {
|
||||
return sema.ir.instructions[@intFromEnum(index)];
|
||||
}
|
||||
|
|
@ -36,12 +41,12 @@ fn emitByteOp(sema: *Sema, op: Story.Opcode) !void {
|
|||
return emitByte(sema, @intFromEnum(op));
|
||||
}
|
||||
|
||||
fn emitConstOp(sema: *Sema, op: Story.Opcode, arg: usize) !void {
|
||||
fn emitConstOp(sema: *Sema, op: Story.Opcode, arg: u8) !void {
|
||||
const gpa = sema.gpa;
|
||||
if (arg >= std.math.maxInt(u8)) return error.TooManyConstants;
|
||||
try sema.bytecode.ensureUnusedCapacity(gpa, 2);
|
||||
sema.bytecode.appendAssumeCapacity(@intFromEnum(op));
|
||||
sema.bytecode.appendAssumeCapacity(@intCast(arg));
|
||||
sema.bytecode.appendAssumeCapacity(arg);
|
||||
}
|
||||
|
||||
fn emitJumpOp(sema: *Sema, op: Story.Opcode) error{OutOfMemory}!usize {
|
||||
|
|
@ -60,6 +65,27 @@ fn makeConstant(sema: *Sema, data: CompiledStory.Constant) !usize {
|
|||
return const_index;
|
||||
}
|
||||
|
||||
const Chunk = struct {
|
||||
name: Ir.NullTerminatedString,
|
||||
arity: u32,
|
||||
stack_size: u32,
|
||||
stack_map: std.AutoHashMapUnmanaged(Ir.Inst.Index, u32),
|
||||
};
|
||||
|
||||
fn integerInst(sema: *Sema, inst: Ir.Inst) InnerError!void {
|
||||
const int_const = try sema.makeConstant(.{
|
||||
.integer = inst.data.integer.value,
|
||||
});
|
||||
return emitConstOp(sema, .load_const, @intCast(int_const));
|
||||
}
|
||||
|
||||
fn stringInst(sema: *Sema, inst: Ir.Inst) InnerError!void {
|
||||
const str_const = try sema.makeConstant(.{
|
||||
.string = inst.data.string.start,
|
||||
});
|
||||
return emitConstOp(sema, .load_const, @intCast(str_const));
|
||||
}
|
||||
|
||||
fn unaryInst(sema: *Sema, _: Ir.Inst, op: Story.Opcode) InnerError!void {
|
||||
return emitByteOp(sema, op);
|
||||
}
|
||||
|
|
@ -72,34 +98,129 @@ fn contentInst(sema: *Sema, _: Ir.Inst) InnerError!void {
|
|||
return emitByteOp(sema, .stream_flush);
|
||||
}
|
||||
|
||||
fn blockInst(sema: *Sema, block_inst: Ir.Inst) InnerError!void {
|
||||
fn allocLocal(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!void {
|
||||
const gpa = sema.gpa;
|
||||
// TODO: Add constraints on how many temporaries we can have.
|
||||
// max(u8) or max(u16) are most likey appropriate.
|
||||
try chunk.stack_map.put(gpa, inst, chunk.stack_size);
|
||||
chunk.stack_size += 1;
|
||||
}
|
||||
|
||||
fn storeLocal(sema: *Sema, chunk: *Chunk, inst: Ir.Inst) InnerError!void {
|
||||
const temp_inst = inst.data.bin.lhs;
|
||||
const stack_offset = chunk.stack_map.get(temp_inst).?;
|
||||
try emitConstOp(sema, .store, @intCast(stack_offset));
|
||||
try emitByteOp(sema, .pop);
|
||||
}
|
||||
|
||||
fn loadLocal(sema: *Sema, chunk: *Chunk, inst: Ir.Inst) InnerError!void {
|
||||
const temp_inst = inst.data.un.lhs;
|
||||
const stack_offset = chunk.stack_map.get(temp_inst).?;
|
||||
try emitConstOp(sema, .load, @intCast(stack_offset));
|
||||
}
|
||||
|
||||
fn declRef(sema: *Sema, inst: Ir.Inst) InnerError!void {
|
||||
const ir = sema.ir;
|
||||
const str = inst.data.string.start;
|
||||
for (ir.globals) |global| {
|
||||
if (str == global.name) {
|
||||
const constant_index = try sema.makeConstant(.{ .string = str });
|
||||
return emitConstOp(sema, .load_global, @intCast(constant_index));
|
||||
}
|
||||
}
|
||||
return sema.fail("unknown global variable");
|
||||
}
|
||||
|
||||
fn declVar(
|
||||
sema: *Sema,
|
||||
chunk: *Chunk,
|
||||
name: Ir.NullTerminatedString,
|
||||
inst: Ir.Inst,
|
||||
) InnerError!void {
|
||||
const ir = sema.ir;
|
||||
const payload_index = inst.data.payload.payload_index;
|
||||
const extra = ir.extraData(Ir.Inst.Block, payload_index);
|
||||
const body = ir.bodySlice(extra.end, extra.data.body_len);
|
||||
for (body) |body_index| try compileInst(sema, chunk, body_index);
|
||||
for (ir.globals) |global| {
|
||||
if (name == global.name) {
|
||||
const constant_index = try sema.makeConstant(.{ .string = name });
|
||||
return emitConstOp(sema, .store_global, @intCast(constant_index));
|
||||
}
|
||||
}
|
||||
|
||||
return sema.fail("unknown global variable");
|
||||
}
|
||||
|
||||
fn declKnot(sema: *Sema, name_ref: Ir.NullTerminatedString, inst: Ir.Inst) InnerError!void {
|
||||
const gpa = sema.gpa;
|
||||
const ir = sema.ir;
|
||||
const extra = ir.extraData(Ir.Inst.Knot, inst.data.payload.payload_index);
|
||||
const bytecode_start = sema.bytecode.items.len;
|
||||
const const_start = sema.constants.items.len;
|
||||
|
||||
var chunk: Chunk = .{
|
||||
.name = name_ref,
|
||||
.arity = 0,
|
||||
.stack_size = 0,
|
||||
.stack_map = .empty,
|
||||
};
|
||||
defer chunk.stack_map.deinit(gpa);
|
||||
|
||||
const body_slice = ir.bodySlice(extra.end, extra.data.body_len);
|
||||
for (body_slice) |body_inst| try compileInst(sema, &chunk, body_inst);
|
||||
|
||||
try emitByteOp(sema, .exit);
|
||||
|
||||
try sema.knots.append(gpa, .{
|
||||
.name_ref = name_ref,
|
||||
.arity = chunk.arity,
|
||||
.stack_size = chunk.stack_size,
|
||||
.bytecode = .{
|
||||
.start = @intCast(bytecode_start),
|
||||
.len = @intCast(sema.bytecode.items.len - bytecode_start),
|
||||
},
|
||||
.constants = .{
|
||||
.start = @intCast(const_start),
|
||||
.len = @intCast(sema.constants.items.len - const_start),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
fn blockInst(sema: *Sema, chunk: *Chunk, block_inst: Ir.Inst) InnerError!void {
|
||||
const ir = sema.ir;
|
||||
const extra = ir.extraData(Ir.Inst.Block, block_inst.data.payload.payload_index);
|
||||
const body = ir.bodySlice(extra.end, extra.data.body_len);
|
||||
for (body) |body_index| try compileInst(sema, chunk, body_index);
|
||||
}
|
||||
|
||||
for (body) |inst_index| {
|
||||
const body_inst = resolveIndex(sema, inst_index);
|
||||
try compileInst(sema, body_inst);
|
||||
fn declaration(sema: *Sema, parent_chunk: ?*Chunk, inst: Ir.Inst) !void {
|
||||
const ir = sema.ir;
|
||||
const extra = ir.extraData(Ir.Inst.Declaration, inst.data.payload.payload_index);
|
||||
const value_inst = sema.resolveIndex(extra.data.value);
|
||||
const str = extra.data.name;
|
||||
|
||||
switch (value_inst.tag) {
|
||||
.decl_var => try declVar(sema, parent_chunk.?, str, value_inst),
|
||||
.decl_knot => try declKnot(sema, str, value_inst),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn integerInst(sema: *Sema, inst: Ir.Inst) InnerError!void {
|
||||
const int_const = try sema.makeConstant(.{
|
||||
.integer = inst.data.integer.value,
|
||||
});
|
||||
return emitConstOp(sema, .load_const, int_const);
|
||||
}
|
||||
|
||||
fn stringInst(sema: *Sema, inst: Ir.Inst) InnerError!void {
|
||||
const str_const = try sema.makeConstant(.{
|
||||
.string = inst.data.string.start,
|
||||
});
|
||||
return emitConstOp(sema, .load_const, str_const);
|
||||
}
|
||||
|
||||
fn compileInst(sema: *Sema, inst: Ir.Inst) InnerError!void {
|
||||
fn compileInst(sema: *Sema, chunk: *Chunk, index: Ir.Inst.Index) InnerError!void {
|
||||
const inst = sema.resolveIndex(index);
|
||||
switch (inst.tag) {
|
||||
.block => try blockInst(sema, inst),
|
||||
.file => unreachable,
|
||||
.declaration => try declaration(sema, chunk, inst),
|
||||
.decl_var => unreachable, // handled in declaration()
|
||||
.decl_knot => unreachable, // handled in declaration()
|
||||
.decl_ref => try declRef(sema, inst),
|
||||
.block => try blockInst(sema, chunk, inst),
|
||||
.alloc_local => try allocLocal(sema, chunk, index),
|
||||
.store_local => try storeLocal(sema, chunk, inst),
|
||||
.load_local => try loadLocal(sema, chunk, inst),
|
||||
.true_literal => unreachable, // TODO
|
||||
.false_literal => unreachable, // TODO
|
||||
.add => try binaryInst(sema, inst, .add),
|
||||
.sub => try binaryInst(sema, inst, .sub),
|
||||
.mul => try binaryInst(sema, inst, .mul),
|
||||
|
|
@ -109,58 +230,16 @@ fn compileInst(sema: *Sema, inst: Ir.Inst) InnerError!void {
|
|||
.content => try contentInst(sema, inst),
|
||||
.string => try stringInst(sema, inst),
|
||||
.integer => try integerInst(sema, inst),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn innerDecl(sema: *Sema, inst: Ir.Inst) !void {
|
||||
const ir = sema.ir;
|
||||
const extra = ir.extraData(Ir.Inst.Knot, inst.data.payload.payload_index);
|
||||
const body_slice = ir.bodySlice(extra.end, extra.data.body_len);
|
||||
for (body_slice) |inst_index| {
|
||||
const body_inst = resolveIndex(sema, inst_index);
|
||||
try compileInst(sema, body_inst);
|
||||
}
|
||||
}
|
||||
|
||||
fn declaration(sema: *Sema, inst: Ir.Inst) !void {
|
||||
const gpa = sema.gpa;
|
||||
const ir = sema.ir;
|
||||
const byte_index = sema.bytecode.items.len;
|
||||
const const_index = sema.constants.items.len;
|
||||
const extra = ir.extraData(Ir.Inst.Declaration, inst.data.payload.payload_index);
|
||||
const value_inst = sema.resolveIndex(extra.data.value);
|
||||
switch (value_inst.tag) {
|
||||
.decl_var => try innerDecl(sema, value_inst),
|
||||
.decl_knot => try innerDecl(sema, value_inst),
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
const name_ref = extra.data.name;
|
||||
try sema.knots.append(gpa, .{
|
||||
.name_ref = name_ref,
|
||||
.arity = 0,
|
||||
.stack_size = 0,
|
||||
.bytecode = .{
|
||||
.start = @intCast(byte_index),
|
||||
.len = @intCast(sema.bytecode.items.len - byte_index),
|
||||
},
|
||||
.constants = .{
|
||||
.start = @intCast(const_index),
|
||||
.len = @intCast(sema.constants.items.len - const_index),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
fn file(sema: *Sema, inst: Ir.Inst) !void {
|
||||
const extra = sema.ir.extraData(Ir.Inst.Block, inst.data.payload.payload_index);
|
||||
const body = sema.ir.bodySlice(extra.end, extra.data.body_len);
|
||||
for (body) |inst_index| {
|
||||
const body_inst = sema.resolveIndex(inst_index);
|
||||
switch (body_inst.tag) {
|
||||
.declaration => try declaration(sema, body_inst),
|
||||
else => unreachable,
|
||||
}
|
||||
for (body) |body_index| {
|
||||
const body_inst = sema.resolveIndex(body_index);
|
||||
assert(body_inst.tag == .declaration);
|
||||
try declaration(sema, null, body_inst);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue