feat: semantics for if statements and multi-prong if statements
This commit is contained in:
parent
9658c8a308
commit
ce5385ebac
6 changed files with 553 additions and 317 deletions
133
src/Sema.zig
133
src/Sema.zig
|
|
@ -14,6 +14,7 @@ knots: std.ArrayListUnmanaged(CompiledStory.Knot) = .empty,
|
|||
const InnerError = error{
|
||||
OutOfMemory,
|
||||
TooManyConstants,
|
||||
InvalidJump,
|
||||
};
|
||||
|
||||
fn deinit(sema: *Sema) void {
|
||||
|
|
@ -66,10 +67,74 @@ fn makeConstant(sema: *Sema, data: CompiledStory.Constant) !usize {
|
|||
}
|
||||
|
||||
const Chunk = struct {
|
||||
sema: *Sema,
|
||||
name: Ir.NullTerminatedString,
|
||||
arity: u32,
|
||||
stack_size: u32,
|
||||
stack_map: std.AutoHashMapUnmanaged(Ir.Inst.Index, u32),
|
||||
labels: std.ArrayListUnmanaged(Label),
|
||||
fixups: std.ArrayListUnmanaged(Fixup),
|
||||
|
||||
const dummy_address = 0xffffffff;
|
||||
|
||||
const Label = struct {
|
||||
code_offset: usize,
|
||||
};
|
||||
|
||||
const Fixup = struct {
|
||||
mode: enum {
|
||||
relative,
|
||||
absolute,
|
||||
},
|
||||
label_index: usize,
|
||||
code_offset: usize,
|
||||
};
|
||||
|
||||
fn addFixup(chunk: *Chunk, op: Story.Opcode, label: usize) !void {
|
||||
return chunk.fixups.append(chunk.sema.gpa, .{
|
||||
.mode = .relative,
|
||||
.label_index = label,
|
||||
.code_offset = try chunk.sema.emitJumpOp(op),
|
||||
});
|
||||
}
|
||||
|
||||
fn addLabel(chunk: *Chunk) error{OutOfMemory}!usize {
|
||||
const label_index = chunk.labels.items.len;
|
||||
try chunk.labels.append(chunk.sema.gpa, .{
|
||||
.code_offset = dummy_address,
|
||||
});
|
||||
return label_index;
|
||||
}
|
||||
|
||||
fn setLabel(chunk: *Chunk, label_index: usize) void {
|
||||
const code_offset = chunk.sema.bytecode.items.len;
|
||||
assert(label_index <= chunk.labels.items.len);
|
||||
|
||||
const label_data = &chunk.labels.items[label_index];
|
||||
label_data.code_offset = code_offset;
|
||||
}
|
||||
|
||||
fn resolveLabels(chunk: *Chunk) !void {
|
||||
const start_index = 0;
|
||||
const end_index = chunk.fixups.items.len;
|
||||
const bytecode = &chunk.sema.bytecode;
|
||||
|
||||
for (chunk.fixups.items[start_index..end_index]) |fixup| {
|
||||
const label = chunk.labels.items[fixup.label_index];
|
||||
assert(label.code_offset != dummy_address);
|
||||
const target_offset: usize = switch (fixup.mode) {
|
||||
.relative => label.code_offset - fixup.code_offset - 2,
|
||||
.absolute => label.code_offset,
|
||||
};
|
||||
if (target_offset >= std.math.maxInt(u16)) {
|
||||
std.debug.print("Too much code to jump over!\n", .{});
|
||||
return error.InvalidJump;
|
||||
}
|
||||
assert(bytecode.capacity >= label.code_offset + 2);
|
||||
bytecode.items[fixup.code_offset] = @intCast((target_offset >> 8) & 0xff);
|
||||
bytecode.items[fixup.code_offset + 1] = @intCast(target_offset & 0xff);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn integerInst(sema: *Sema, inst: Ir.Inst) InnerError!void {
|
||||
|
|
@ -94,7 +159,11 @@ fn binaryInst(sema: *Sema, _: Ir.Inst, op: Story.Opcode) InnerError!void {
|
|||
return emitByteOp(sema, op);
|
||||
}
|
||||
|
||||
fn contentInst(sema: *Sema, _: Ir.Inst) InnerError!void {
|
||||
fn irContentPush(sema: *Sema, _: Ir.Inst) InnerError!void {
|
||||
return emitByteOp(sema, .stream_push);
|
||||
}
|
||||
|
||||
fn irContentFlush(sema: *Sema, _: Ir.Inst) InnerError!void {
|
||||
return emitByteOp(sema, .stream_flush);
|
||||
}
|
||||
|
||||
|
|
@ -119,6 +188,38 @@ fn loadLocal(sema: *Sema, chunk: *Chunk, inst: Ir.Inst) InnerError!void {
|
|||
try emitConstOp(sema, .load, @intCast(stack_offset));
|
||||
}
|
||||
|
||||
fn irCondBr(sema: *Sema, chunk: *Chunk, inst: Ir.Inst) InnerError!void {
|
||||
const payload_node = inst.data.payload;
|
||||
const extra = sema.ir.extraData(Ir.Inst.CondBr, payload_node.payload_index);
|
||||
const then_body = sema.ir.bodySlice(extra.end, extra.data.then_body_len);
|
||||
const else_body = sema.ir.bodySlice(extra.end + then_body.len, extra.data.else_body_len);
|
||||
const else_label = try chunk.addLabel();
|
||||
const end_label = try chunk.addLabel();
|
||||
|
||||
try chunk.addFixup(.jmp_f, else_label);
|
||||
try emitByteOp(sema, .pop);
|
||||
for (then_body) |body_index| try compileInst(sema, chunk, body_index);
|
||||
|
||||
try chunk.addFixup(.jmp, end_label);
|
||||
chunk.setLabel(else_label);
|
||||
try emitByteOp(sema, .pop);
|
||||
|
||||
for (else_body) |body_index| try compileInst(sema, chunk, body_index);
|
||||
chunk.setLabel(end_label);
|
||||
}
|
||||
|
||||
fn irBreak(sema: *Sema, inst: Ir.Inst) InnerError!void {
|
||||
_ = sema;
|
||||
_ = inst;
|
||||
}
|
||||
|
||||
fn irBlock(sema: *Sema, chunk: *Chunk, block_inst: Ir.Inst) InnerError!void {
|
||||
const payload_node = block_inst.data.payload;
|
||||
const extra = sema.ir.extraData(Ir.Inst.Block, payload_node.payload_index);
|
||||
const body = sema.ir.bodySlice(extra.end, extra.data.body_len);
|
||||
for (body) |body_index| try compileInst(sema, chunk, body_index);
|
||||
}
|
||||
|
||||
fn declRef(sema: *Sema, inst: Ir.Inst) InnerError!void {
|
||||
const ir = sema.ir;
|
||||
const str = inst.data.string.start;
|
||||
|
|
@ -160,17 +261,23 @@ fn declKnot(sema: *Sema, name_ref: Ir.NullTerminatedString, inst: Ir.Inst) Inner
|
|||
const const_start = sema.constants.items.len;
|
||||
|
||||
var chunk: Chunk = .{
|
||||
.sema = sema,
|
||||
.name = name_ref,
|
||||
.arity = 0,
|
||||
.stack_size = 0,
|
||||
.stack_map = .empty,
|
||||
.fixups = .empty,
|
||||
.labels = .empty,
|
||||
};
|
||||
defer chunk.stack_map.deinit(gpa);
|
||||
defer chunk.fixups.deinit(gpa);
|
||||
defer chunk.labels.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 chunk.resolveLabels();
|
||||
|
||||
try sema.knots.append(gpa, .{
|
||||
.name_ref = name_ref,
|
||||
|
|
@ -187,13 +294,6 @@ fn declKnot(sema: *Sema, name_ref: Ir.NullTerminatedString, inst: Ir.Inst) Inner
|
|||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
@ -215,7 +315,9 @@ fn compileInst(sema: *Sema, chunk: *Chunk, index: Ir.Inst.Index) InnerError!void
|
|||
.decl_var => unreachable, // handled in declaration()
|
||||
.decl_knot => unreachable, // handled in declaration()
|
||||
.decl_ref => try declRef(sema, inst),
|
||||
.block => try blockInst(sema, chunk, inst),
|
||||
.condbr => try irCondBr(sema, chunk, inst),
|
||||
.@"break" => try irBreak(sema, inst),
|
||||
.block => try irBlock(sema, chunk, inst),
|
||||
.alloc_local => try allocLocal(sema, chunk, index),
|
||||
.store_local => try storeLocal(sema, chunk, inst),
|
||||
.load_local => try loadLocal(sema, chunk, inst),
|
||||
|
|
@ -227,7 +329,18 @@ fn compileInst(sema: *Sema, chunk: *Chunk, index: Ir.Inst.Index) InnerError!void
|
|||
.div => try binaryInst(sema, inst, .div),
|
||||
.mod => try binaryInst(sema, inst, .mod),
|
||||
.neg => try unaryInst(sema, inst, .neg),
|
||||
.content => try contentInst(sema, inst),
|
||||
.not => try unaryInst(sema, inst, .not),
|
||||
.cmp_eq => try unaryInst(sema, inst, .cmp_eq),
|
||||
.cmp_neq => {
|
||||
try unaryInst(sema, inst, .cmp_eq);
|
||||
try emitByteOp(sema, .not);
|
||||
},
|
||||
.cmp_lt => try unaryInst(sema, inst, .cmp_lt),
|
||||
.cmp_lte => try unaryInst(sema, inst, .cmp_lte),
|
||||
.cmp_gt => try unaryInst(sema, inst, .cmp_gt),
|
||||
.cmp_gte => try unaryInst(sema, inst, .cmp_gte),
|
||||
.content_push => try irContentPush(sema, inst),
|
||||
.content_flush => try irContentFlush(sema, inst),
|
||||
.string => try stringInst(sema, inst),
|
||||
.integer => try integerInst(sema, inst),
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue