feat: function calls
This commit is contained in:
parent
11d99fba38
commit
d08e753664
99 changed files with 881 additions and 148 deletions
113
src/AstGen.zig
113
src/AstGen.zig
|
|
@ -280,6 +280,13 @@ const GenIr = struct {
|
||||||
self.instructions.items[self.instructions_top..];
|
self.instructions.items[self.instructions_top..];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn endsWithNoReturn(self: *GenIr) bool {
|
||||||
|
if (self.isEmpty()) return false;
|
||||||
|
const last_inst_index = self.instructions.items[self.instructions.items.len - 1];
|
||||||
|
const last_inst = self.astgen.instructions.items[@intFromEnum(last_inst_index)];
|
||||||
|
return last_inst.isNoReturn();
|
||||||
|
}
|
||||||
|
|
||||||
fn makeSubBlock(self: *GenIr) GenIr {
|
fn makeSubBlock(self: *GenIr) GenIr {
|
||||||
return .{
|
return .{
|
||||||
.astgen = self.astgen,
|
.astgen = self.astgen,
|
||||||
|
|
@ -730,7 +737,7 @@ fn expr(gi: *GenIr, scope: *Scope, optional_node: ?*const Ast.Node) InnerError!I
|
||||||
.logical_greater_or_equal_expr => return binaryOp(gi, scope, node, .cmp_gte),
|
.logical_greater_or_equal_expr => return binaryOp(gi, scope, node, .cmp_gte),
|
||||||
.logical_lesser_expr => return binaryOp(gi, scope, node, .cmp_lt),
|
.logical_lesser_expr => return binaryOp(gi, scope, node, .cmp_lt),
|
||||||
.logical_lesser_or_equal_expr => return binaryOp(gi, scope, node, .cmp_lte),
|
.logical_lesser_or_equal_expr => return binaryOp(gi, scope, node, .cmp_lte),
|
||||||
.call_expr => unreachable,
|
.call_expr => return callExpr(gi, scope, node, .call),
|
||||||
.choice_expr => unreachable,
|
.choice_expr => unreachable,
|
||||||
.choice_start_expr => unreachable,
|
.choice_start_expr => unreachable,
|
||||||
.choice_option_expr => unreachable,
|
.choice_option_expr => unreachable,
|
||||||
|
|
@ -1171,7 +1178,9 @@ fn callExpr(
|
||||||
const scratch_top = astgen.scratch.items.len;
|
const scratch_top = astgen.scratch.items.len;
|
||||||
defer astgen.scratch.shrinkRetainingCapacity(scratch_top);
|
defer astgen.scratch.shrinkRetainingCapacity(scratch_top);
|
||||||
|
|
||||||
const arguments: ?[]*Ast.Node = if (args_node) |n| n.data.list.items.? else null;
|
// FIXME: List nodes should not have optional slices.
|
||||||
|
// This hack is an abomination.
|
||||||
|
const arguments: ?[]*Ast.Node = if (args_node) |n| if (n.data.list.items) |items| items else null else null;
|
||||||
const args_count = if (arguments) |args| args.len else 0;
|
const args_count = if (arguments) |args| args.len else 0;
|
||||||
|
|
||||||
try astgen.scratch.resize(gpa, scratch_top + args_count);
|
try astgen.scratch.resize(gpa, scratch_top + args_count);
|
||||||
|
|
@ -1275,10 +1284,20 @@ fn divertExpr(gi: *GenIr, scope: *Scope, node: *const Ast.Node) !void {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn divertStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) !void {
|
fn divertStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) !void {
|
||||||
|
// TODO: Revisit this.
|
||||||
const data = node.data.bin;
|
const data = node.data.bin;
|
||||||
return divertExpr(gi, scope, data.lhs.?);
|
return divertExpr(gi, scope, data.lhs.?);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn returnStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) !void {
|
||||||
|
// TODO: Revisit this.
|
||||||
|
const ret_arg = if (node.data.bin.lhs) |lhs| blk: {
|
||||||
|
const arg_inst = try expr(gi, scope, lhs);
|
||||||
|
break :blk arg_inst;
|
||||||
|
} else .none;
|
||||||
|
_ = try gi.addUnaryNode(.ret, ret_arg);
|
||||||
|
}
|
||||||
|
|
||||||
fn tempDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
|
fn tempDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
|
||||||
const astgen = gi.astgen;
|
const astgen = gi.astgen;
|
||||||
const identifier_node = decl_node.data.bin.lhs.?;
|
const identifier_node = decl_node.data.bin.lhs.?;
|
||||||
|
|
@ -1327,20 +1346,23 @@ fn blockInner(gi: *GenIr, parent_scope: *Scope, stmt_list: []*Ast.Node) !void {
|
||||||
var child_scope = parent_scope.makeChild();
|
var child_scope = parent_scope.makeChild();
|
||||||
defer child_scope.deinit();
|
defer child_scope.deinit();
|
||||||
|
|
||||||
for (stmt_list) |inner_node| {
|
for (stmt_list) |node| {
|
||||||
_ = switch (inner_node.tag) {
|
_ = switch (node.tag) {
|
||||||
.var_decl => try varDecl(gi, &child_scope, inner_node),
|
.var_decl => try varDecl(gi, &child_scope, node),
|
||||||
.const_decl => try varDecl(gi, &child_scope, inner_node),
|
.const_decl => try varDecl(gi, &child_scope, node),
|
||||||
.temp_decl => try tempDecl(gi, &child_scope, inner_node),
|
.temp_decl => try tempDecl(gi, &child_scope, node),
|
||||||
.assign_stmt => try assignStmt(gi, &child_scope, inner_node),
|
.assign_stmt => try assignStmt(gi, &child_scope, node),
|
||||||
.content_stmt => try contentStmt(gi, &child_scope, inner_node),
|
.content_stmt => try contentStmt(gi, &child_scope, node),
|
||||||
.choice_stmt => try choiceStmt(gi, &child_scope, inner_node),
|
.choice_stmt => try choiceStmt(gi, &child_scope, node),
|
||||||
.expr_stmt => try exprStmt(gi, &child_scope, inner_node),
|
.expr_stmt => try exprStmt(gi, &child_scope, node),
|
||||||
.divert_stmt => try divertStmt(gi, &child_scope, inner_node),
|
.divert_stmt => try divertStmt(gi, &child_scope, node),
|
||||||
|
.return_stmt => try returnStmt(gi, &child_scope, node),
|
||||||
inline else => |e| @panic("Unexpected node: " ++ @tagName(e)),
|
inline else => |e| @panic("Unexpected node: " ++ @tagName(e)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
_ = try gi.addUnaryNode(.implicit_ret, .none);
|
if (!gi.endsWithNoReturn()) {
|
||||||
|
_ = try gi.addUnaryNode(.implicit_ret, .none);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn blockStmt(block: *GenIr, scope: *Scope, stmt_node: *const Ast.Node) InnerError!void {
|
fn blockStmt(block: *GenIr, scope: *Scope, stmt_node: *const Ast.Node) InnerError!void {
|
||||||
|
|
@ -1415,9 +1437,10 @@ fn stitchDeclInner(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (body_node) |body| {
|
if (body_node) |body| {
|
||||||
try blockStmt(&decl_block, scope, body);
|
const body_list = body.data.list.items.?;
|
||||||
|
try blockInner(&decl_block, scope, body_list);
|
||||||
} else {
|
} else {
|
||||||
_ = try decl_block.addUnaryNode(.implicit_ret, .none);
|
try blockInner(&decl_block, scope, &.{});
|
||||||
}
|
}
|
||||||
|
|
||||||
const decl_str = try astgen.strFromNode(identifier_node);
|
const decl_str = try astgen.strFromNode(identifier_node);
|
||||||
|
|
@ -1440,7 +1463,65 @@ fn stitchDecl(gi: *GenIr, parent_scope: *Scope, decl_node: *const Ast.Node) Inne
|
||||||
return stitchDeclInner(gi, &decl_scope, decl_node, prototype_node.?, body_node);
|
return stitchDeclInner(gi, &decl_scope, decl_node, prototype_node.?, body_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn functionDecl(_: *GenIr, _: *Scope, _: *const Ast.Node) InnerError!void {}
|
fn functionDeclInner(
|
||||||
|
gi: *GenIr,
|
||||||
|
scope: *Scope,
|
||||||
|
node: *const Ast.Node,
|
||||||
|
prototype_node: *const Ast.Node,
|
||||||
|
body_node: ?*const Ast.Node,
|
||||||
|
) InnerError!void {
|
||||||
|
const astgen = gi.astgen;
|
||||||
|
const prototype_data = prototype_node.data.bin;
|
||||||
|
const identifier_node = prototype_data.lhs.?;
|
||||||
|
const decl_inst = try gi.addAsIndex(.{
|
||||||
|
.tag = .declaration,
|
||||||
|
.data = .{ .payload = undefined },
|
||||||
|
});
|
||||||
|
|
||||||
|
var decl_block = gi.makeSubBlock();
|
||||||
|
defer decl_block.unstack();
|
||||||
|
|
||||||
|
const stitch_inst = try decl_block.makePayloadNode(.decl_function);
|
||||||
|
|
||||||
|
if (prototype_data.rhs) |args_node| {
|
||||||
|
const args_list = args_node.data.list.items.?;
|
||||||
|
for (args_list) |arg| {
|
||||||
|
assert(arg.tag == .parameter_decl);
|
||||||
|
const arg_str = try astgen.strFromNode(arg);
|
||||||
|
const arg_inst = try decl_block.addStrTok(.param, arg_str.index, arg.loc.start);
|
||||||
|
|
||||||
|
// TODO: Maybe make decl accept a ref?
|
||||||
|
try scope.insert(arg_str.index, .{
|
||||||
|
.decl_node = arg,
|
||||||
|
.inst_index = arg_inst.toIndex().?,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (body_node) |body| {
|
||||||
|
try blockStmt(&decl_block, scope, body);
|
||||||
|
} else {
|
||||||
|
_ = try decl_block.addUnaryNode(.implicit_ret, .none);
|
||||||
|
}
|
||||||
|
|
||||||
|
const decl_str = try astgen.strFromNode(identifier_node);
|
||||||
|
try setDeclStitchPayload(stitch_inst, &decl_block);
|
||||||
|
try setDeclaration(decl_inst, .{
|
||||||
|
.name = decl_str.index,
|
||||||
|
.value = stitch_inst,
|
||||||
|
.gi = gi,
|
||||||
|
.node = node,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn functionDecl(gi: *GenIr, parent_scope: *Scope, decl_node: *const Ast.Node) InnerError!void {
|
||||||
|
const knot_data = decl_node.data.bin;
|
||||||
|
const prototype_node = knot_data.lhs;
|
||||||
|
const body_node = knot_data.rhs;
|
||||||
|
var decl_scope = parent_scope.makeChild();
|
||||||
|
defer decl_scope.deinit();
|
||||||
|
|
||||||
|
return functionDeclInner(gi, &decl_scope, decl_node, prototype_node.?, body_node);
|
||||||
|
}
|
||||||
|
|
||||||
fn knotDecl(gi: *GenIr, parent_scope: *Scope, decl_node: *const Ast.Node) InnerError!void {
|
fn knotDecl(gi: *GenIr, parent_scope: *Scope, decl_node: *const Ast.Node) InnerError!void {
|
||||||
const astgen = gi.astgen;
|
const astgen = gi.astgen;
|
||||||
|
|
|
||||||
66
src/Ir.zig
66
src/Ir.zig
|
|
@ -147,10 +147,16 @@ pub const Inst = struct {
|
||||||
|
|
||||||
pub const Tag = enum {
|
pub const Tag = enum {
|
||||||
file,
|
file,
|
||||||
|
/// Uses the `payload` union field.
|
||||||
declaration,
|
declaration,
|
||||||
|
/// Uses the `payload` union field.
|
||||||
decl_var,
|
decl_var,
|
||||||
|
/// Uses the `payload` union field.
|
||||||
decl_knot,
|
decl_knot,
|
||||||
|
/// Uses the `payload` union field.
|
||||||
decl_stitch,
|
decl_stitch,
|
||||||
|
/// Uses the `payload` union field.
|
||||||
|
decl_function,
|
||||||
/// Uses the `str_tok` union field.
|
/// Uses the `str_tok` union field.
|
||||||
decl_ref,
|
decl_ref,
|
||||||
alloc,
|
alloc,
|
||||||
|
|
@ -199,6 +205,9 @@ pub const Inst = struct {
|
||||||
content_push,
|
content_push,
|
||||||
content_flush,
|
content_flush,
|
||||||
choice_br,
|
choice_br,
|
||||||
|
// Uses the `un` union field.
|
||||||
|
ret,
|
||||||
|
// Uses the `un` union field.
|
||||||
implicit_ret,
|
implicit_ret,
|
||||||
call,
|
call,
|
||||||
divert,
|
divert,
|
||||||
|
|
@ -264,6 +273,11 @@ pub const Inst = struct {
|
||||||
body_len: u32,
|
body_len: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Function = struct {
|
||||||
|
/// Number of instructions for the function's body block.
|
||||||
|
body_len: u32,
|
||||||
|
};
|
||||||
|
|
||||||
pub const Field = struct {
|
pub const Field = struct {
|
||||||
lhs: Ref,
|
lhs: Ref,
|
||||||
field_name_start: NullTerminatedString,
|
field_name_start: NullTerminatedString,
|
||||||
|
|
@ -324,4 +338,56 @@ pub const Inst = struct {
|
||||||
byte_offset: u32,
|
byte_offset: u32,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn isNoReturn(inst: Inst) bool {
|
||||||
|
return switch (inst.tag) {
|
||||||
|
.file,
|
||||||
|
.declaration,
|
||||||
|
.decl_var,
|
||||||
|
.decl_knot,
|
||||||
|
.decl_stitch,
|
||||||
|
.decl_function,
|
||||||
|
.decl_ref,
|
||||||
|
.alloc,
|
||||||
|
.load,
|
||||||
|
.store,
|
||||||
|
.add,
|
||||||
|
.sub,
|
||||||
|
.mul,
|
||||||
|
.div,
|
||||||
|
.mod,
|
||||||
|
.neg,
|
||||||
|
.not,
|
||||||
|
.bool_and,
|
||||||
|
.bool_or,
|
||||||
|
.cmp_eq,
|
||||||
|
.cmp_neq,
|
||||||
|
.cmp_gt,
|
||||||
|
.cmp_gte,
|
||||||
|
.cmp_lt,
|
||||||
|
.cmp_lte,
|
||||||
|
.int,
|
||||||
|
.float,
|
||||||
|
.str,
|
||||||
|
.block,
|
||||||
|
.condbr,
|
||||||
|
.switch_br,
|
||||||
|
.content_push,
|
||||||
|
.content_flush,
|
||||||
|
.choice_br,
|
||||||
|
.call,
|
||||||
|
.divert,
|
||||||
|
.field_ptr,
|
||||||
|
.field_call,
|
||||||
|
.field_divert,
|
||||||
|
.param,
|
||||||
|
=> false,
|
||||||
|
.ret,
|
||||||
|
.implicit_ret,
|
||||||
|
.@"break",
|
||||||
|
.done,
|
||||||
|
.exit,
|
||||||
|
=> true,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
140
src/Sema.zig
140
src/Sema.zig
|
|
@ -29,6 +29,7 @@ pub const ValueInfo = union(enum) {
|
||||||
variable: InternPool.Index,
|
variable: InternPool.Index,
|
||||||
knot: InternPool.Index,
|
knot: InternPool.Index,
|
||||||
stitch: InternPool.Index,
|
stitch: InternPool.Index,
|
||||||
|
function: InternPool.Index,
|
||||||
temp: u32,
|
temp: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -294,7 +295,11 @@ pub const Builder = struct {
|
||||||
const local_index = try self.getOrPutConstantIndex(index);
|
const local_index = try self.getOrPutConstantIndex(index);
|
||||||
try self.addConstOp(.load_const, @intCast(local_index));
|
try self.addConstOp(.load_const, @intCast(local_index));
|
||||||
},
|
},
|
||||||
.variable, .knot, .stitch => |index| {
|
.variable,
|
||||||
|
.knot,
|
||||||
|
.stitch,
|
||||||
|
.function,
|
||||||
|
=> |index| {
|
||||||
const local_index = try self.getOrPutConstantIndex(index);
|
const local_index = try self.getOrPutConstantIndex(index);
|
||||||
try self.addConstOp(.load_global, @intCast(local_index));
|
try self.addConstOp(.load_global, @intCast(local_index));
|
||||||
},
|
},
|
||||||
|
|
@ -536,7 +541,7 @@ fn irDeclRef(
|
||||||
const ident = try sema.lookupIdentifier(builder, ip_index, src_loc);
|
const ident = try sema.lookupIdentifier(builder, ip_index, src_loc);
|
||||||
if (inline_block) {
|
if (inline_block) {
|
||||||
switch (ident.tag) {
|
switch (ident.tag) {
|
||||||
.knot, .stitch => unreachable,
|
.knot, .stitch, .function => unreachable,
|
||||||
.var_const => return sema.resolveGlobalDecl(builder, ip_index, src_loc),
|
.var_const => return sema.resolveGlobalDecl(builder, ip_index, src_loc),
|
||||||
.var_mut => return sema.fail(
|
.var_mut => return sema.fail(
|
||||||
src_loc,
|
src_loc,
|
||||||
|
|
@ -548,6 +553,7 @@ fn irDeclRef(
|
||||||
switch (ident.tag) {
|
switch (ident.tag) {
|
||||||
.knot => return .{ .knot = ip_index },
|
.knot => return .{ .knot = ip_index },
|
||||||
.stitch => return .{ .stitch = ip_index },
|
.stitch => return .{ .stitch = ip_index },
|
||||||
|
.function => return .{ .function = ip_index },
|
||||||
.var_mut => return .{ .variable = ip_index },
|
.var_mut => return .{ .variable = ip_index },
|
||||||
.var_const => return .{ .variable = ip_index },
|
.var_const => return .{ .variable = ip_index },
|
||||||
}
|
}
|
||||||
|
|
@ -577,8 +583,9 @@ fn irStore(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!void
|
||||||
try builder.addConstOp(.store_global, @intCast(local_index));
|
try builder.addConstOp(.store_global, @intCast(local_index));
|
||||||
},
|
},
|
||||||
.temp => |temp| try builder.addConstOp(.store, @intCast(temp)),
|
.temp => |temp| try builder.addConstOp(.store, @intCast(temp)),
|
||||||
.stitch => |_| return sema.fail(src, "could not assign to stitch", .{}),
|
|
||||||
.knot => |_| return sema.fail(src, "could not assign to knot", .{}),
|
.knot => |_| return sema.fail(src, "could not assign to knot", .{}),
|
||||||
|
.stitch => |_| return sema.fail(src, "could not assign to stitch", .{}),
|
||||||
|
.function => |_| return sema.fail(src, "could not assign to function", .{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
try builder.addByteOp(.pop);
|
try builder.addByteOp(.pop);
|
||||||
|
|
@ -757,12 +764,66 @@ fn irChoiceBr(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!vo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn irRet(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!void {
|
||||||
|
const data = sema.ir.instructions[@intFromEnum(inst)].data.un;
|
||||||
|
const value = sema.resolveInst(data.lhs);
|
||||||
|
if (value != .none) try builder.ensureLoad(value);
|
||||||
|
try builder.addByteOp(.ret);
|
||||||
|
}
|
||||||
|
|
||||||
fn irImplicitRet(_: *Sema, builder: *Builder, _: Ir.Inst.Index) InnerError!void {
|
fn irImplicitRet(_: *Sema, builder: *Builder, _: Ir.Inst.Index) InnerError!void {
|
||||||
try builder.addByteOp(.exit);
|
try builder.addByteOp(.exit);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn irCall(_: *Sema, _: *Builder, _: Ir.Inst.Index) !ValueInfo {
|
fn irCall(
|
||||||
return .none;
|
sema: *Sema,
|
||||||
|
builder: *Builder,
|
||||||
|
inst: Ir.Inst.Index,
|
||||||
|
comptime kind: enum { direct, field },
|
||||||
|
) !ValueInfo {
|
||||||
|
const ExtraType = switch (kind) {
|
||||||
|
.direct => Ir.Inst.Call,
|
||||||
|
.field => Ir.Inst.FieldCall,
|
||||||
|
};
|
||||||
|
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
|
||||||
|
const extra = sema.ir.extraData(ExtraType, data.extra_index);
|
||||||
|
const body = sema.ir.extra[extra.end..];
|
||||||
|
const callee_src: SrcLoc = .{ .src_offset = data.src_offset };
|
||||||
|
switch (kind) {
|
||||||
|
.direct => {
|
||||||
|
const callee = sema.resolveInst(extra.data.callee);
|
||||||
|
_ = try analyzeCallTarget(sema, builder, callee_src, callee);
|
||||||
|
},
|
||||||
|
.field => {
|
||||||
|
const callee = sema.resolveInst(extra.data.obj_ptr);
|
||||||
|
const target = try analyzeCallTarget(sema, builder, callee_src, callee);
|
||||||
|
const ip_index = try sema.module.intern_pool.getOrPutStr(
|
||||||
|
sema.gpa,
|
||||||
|
extra.data.field_name_start,
|
||||||
|
);
|
||||||
|
const e = try sema.lookupInNamespace(target.namespace.?, ip_index, callee_src);
|
||||||
|
switch (e.tag) {
|
||||||
|
.function => {
|
||||||
|
const local_index = try builder.getOrPutConstantIndex(ip_index);
|
||||||
|
_ = try builder.addConstOp(.load_attr, @intCast(local_index));
|
||||||
|
},
|
||||||
|
else => return sema.fail(callee_src, "invalid call target", .{}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const args_len = extra.data.args_len;
|
||||||
|
var arg_start: u32 = args_len;
|
||||||
|
var i: u32 = 0;
|
||||||
|
while (i < args_len) : (i += 1) {
|
||||||
|
const arg_end = sema.ir.extra[extra.end + i];
|
||||||
|
defer arg_start = arg_end;
|
||||||
|
const arg_body = body[arg_start..arg_end];
|
||||||
|
const arg_value = try analyzeBodyInner(sema, builder, @ptrCast(arg_body), false);
|
||||||
|
if (arg_value != .none) try builder.ensureLoad(arg_value);
|
||||||
|
}
|
||||||
|
try builder.addConstOp(.call, @intCast(args_len));
|
||||||
|
return .stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn irDivert(
|
fn irDivert(
|
||||||
|
|
@ -809,13 +870,8 @@ fn irDivert(
|
||||||
const arg_end = sema.ir.extra[extra.end + i];
|
const arg_end = sema.ir.extra[extra.end + i];
|
||||||
defer arg_start = arg_end;
|
defer arg_start = arg_end;
|
||||||
const arg_body = body[arg_start..arg_end];
|
const arg_body = body[arg_start..arg_end];
|
||||||
_ = try analyzeBodyInner(sema, builder, @ptrCast(arg_body), false);
|
const arg_value = try analyzeBodyInner(sema, builder, @ptrCast(arg_body), false);
|
||||||
// FIXME: hack
|
if (arg_value != .none) try builder.ensureLoad(arg_value);
|
||||||
{
|
|
||||||
const last_inst: Ir.Inst.Index = @enumFromInt(arg_body[arg_body.len - 1]);
|
|
||||||
const val = sema.resolveInst(last_inst.toRef());
|
|
||||||
try builder.ensureLoad(val);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
try builder.addConstOp(.divert, @intCast(args_len));
|
try builder.addConstOp(.divert, @intCast(args_len));
|
||||||
}
|
}
|
||||||
|
|
@ -864,6 +920,21 @@ fn analyzeArithmeticArg(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn analyzeCallTarget(
|
||||||
|
sema: *Sema,
|
||||||
|
builder: *Builder,
|
||||||
|
src: SrcLoc,
|
||||||
|
callee: ValueInfo,
|
||||||
|
) !Module.Namespace.Decl {
|
||||||
|
switch (callee) {
|
||||||
|
.function => |ip_index| {
|
||||||
|
try builder.ensureLoad(callee);
|
||||||
|
return sema.lookupIdentifier(builder, ip_index, src);
|
||||||
|
},
|
||||||
|
else => return sema.fail(src, "invalid call target", .{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn analyzeDivertTarget(
|
fn analyzeDivertTarget(
|
||||||
sema: *Sema,
|
sema: *Sema,
|
||||||
builder: *Builder,
|
builder: *Builder,
|
||||||
|
|
@ -894,6 +965,7 @@ fn analyzeBodyInner(
|
||||||
.decl_var => unreachable, // never present inside block bodies
|
.decl_var => unreachable, // never present inside block bodies
|
||||||
.decl_knot => unreachable, // never present inside block bodies
|
.decl_knot => unreachable, // never present inside block bodies
|
||||||
.decl_stitch => unreachable, // never present inside block bodies
|
.decl_stitch => unreachable, // never present inside block bodies
|
||||||
|
.decl_function => unreachable, // never present inside block bodies
|
||||||
.alloc => try irAlloc(sema, builder, inst),
|
.alloc => try irAlloc(sema, builder, inst),
|
||||||
.store => {
|
.store => {
|
||||||
try irStore(sema, builder, inst);
|
try irStore(sema, builder, inst);
|
||||||
|
|
@ -919,6 +991,10 @@ fn analyzeBodyInner(
|
||||||
.cmp_gt => try irBinaryOp(sema, builder, inst, .cmp_gt),
|
.cmp_gt => try irBinaryOp(sema, builder, inst, .cmp_gt),
|
||||||
.cmp_gte => try irBinaryOp(sema, builder, inst, .cmp_gte),
|
.cmp_gte => try irBinaryOp(sema, builder, inst, .cmp_gte),
|
||||||
.decl_ref => try irDeclRef(sema, builder, inst, inline_block),
|
.decl_ref => try irDeclRef(sema, builder, inst, inline_block),
|
||||||
|
.ret => {
|
||||||
|
try irRet(sema, builder, inst);
|
||||||
|
continue;
|
||||||
|
},
|
||||||
.implicit_ret => {
|
.implicit_ret => {
|
||||||
try irImplicitRet(sema, builder, inst);
|
try irImplicitRet(sema, builder, inst);
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -948,12 +1024,12 @@ fn analyzeBodyInner(
|
||||||
try irSwitchBr(sema, builder, inst);
|
try irSwitchBr(sema, builder, inst);
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
.call => try irCall(sema, builder, inst),
|
.call => try irCall(sema, builder, inst, .direct),
|
||||||
|
.field_call => try irCall(sema, builder, inst, .field),
|
||||||
.divert => {
|
.divert => {
|
||||||
try irDivert(sema, builder, inst, .direct);
|
try irDivert(sema, builder, inst, .direct);
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
.field_call => try irCall(sema, builder, inst),
|
|
||||||
.field_divert => {
|
.field_divert => {
|
||||||
try irDivert(sema, builder, inst, .field);
|
try irDivert(sema, builder, inst, .field);
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -968,6 +1044,7 @@ fn analyzeBodyInner(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: No return allowed.
|
||||||
pub fn analyzeStitch(
|
pub fn analyzeStitch(
|
||||||
sema: *Sema,
|
sema: *Sema,
|
||||||
builder: *Builder,
|
builder: *Builder,
|
||||||
|
|
@ -979,6 +1056,19 @@ pub fn analyzeStitch(
|
||||||
_ = try analyzeBodyInner(sema, builder, body, false);
|
_ = try analyzeBodyInner(sema, builder, body, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: No diverts allowed.
|
||||||
|
pub fn analyzeFunction(
|
||||||
|
sema: *Sema,
|
||||||
|
builder: *Builder,
|
||||||
|
inst: Ir.Inst.Index,
|
||||||
|
) !void {
|
||||||
|
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
|
||||||
|
const extra = sema.ir.extraData(Ir.Inst.Function, data.extra_index);
|
||||||
|
const body = sema.ir.bodySlice(extra.end, extra.data.body_len);
|
||||||
|
_ = try analyzeBodyInner(sema, builder, body, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: No return allowed.
|
||||||
pub fn analyzeKnot(
|
pub fn analyzeKnot(
|
||||||
sema: *Sema,
|
sema: *Sema,
|
||||||
builder: *Builder,
|
builder: *Builder,
|
||||||
|
|
@ -1088,7 +1178,6 @@ fn scanTopLevelDecl(
|
||||||
.namespace = child_namespace,
|
.namespace = child_namespace,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
try sema.module.queueWorkItem(.{
|
try sema.module.queueWorkItem(.{
|
||||||
.tag = .stitch,
|
.tag = .stitch,
|
||||||
.decl_name = decl_name,
|
.decl_name = decl_name,
|
||||||
|
|
@ -1096,6 +1185,27 @@ fn scanTopLevelDecl(
|
||||||
.namespace = child_namespace,
|
.namespace = child_namespace,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
.decl_function => {
|
||||||
|
const child_namespace = try sema.module.createNamespace(namespace);
|
||||||
|
const gop = try namespace.decls.getOrPut(sema.arena, decl_name);
|
||||||
|
if (gop.found_existing) {
|
||||||
|
return sema.fail(src_loc, "duplicate identifier", .{});
|
||||||
|
} else {
|
||||||
|
gop.value_ptr.* = .{
|
||||||
|
.tag = .function,
|
||||||
|
.decl_inst = extra.value,
|
||||||
|
.args_count = 0,
|
||||||
|
.namespace = child_namespace,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
try sema.module.queueWorkItem(.{
|
||||||
|
.tag = .function,
|
||||||
|
.decl_name = decl_name,
|
||||||
|
.inst_index = extra.value,
|
||||||
|
.namespace = child_namespace,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
232
src/Story.zig
232
src/Story.zig
|
|
@ -14,13 +14,16 @@ dump_writer: ?*std.Io.Writer = null,
|
||||||
is_exited: bool = false,
|
is_exited: bool = false,
|
||||||
can_advance: bool = false,
|
can_advance: bool = false,
|
||||||
choice_index: usize = 0,
|
choice_index: usize = 0,
|
||||||
|
stack_top: usize = 0,
|
||||||
|
call_stack_top: usize = 0,
|
||||||
current_choices: std.ArrayListUnmanaged(Choice) = .empty,
|
current_choices: std.ArrayListUnmanaged(Choice) = .empty,
|
||||||
code_chunks: std.ArrayListUnmanaged(*Object.Code) = .empty,
|
code_chunks: std.ArrayListUnmanaged(*Object.Code) = .empty,
|
||||||
constants_pool: std.ArrayListUnmanaged(Value) = .empty,
|
|
||||||
globals: std.StringHashMapUnmanaged(?Value) = .empty,
|
globals: std.StringHashMapUnmanaged(?Value) = .empty,
|
||||||
stack: std.ArrayListUnmanaged(?Value) = .empty,
|
stack: []Value = &.{},
|
||||||
call_stack: std.ArrayListUnmanaged(CallFrame) = .empty,
|
call_stack: []CallFrame = &.{},
|
||||||
stack_max: usize = 128,
|
/// Global constants pool.
|
||||||
|
constants_pool: []const Value = &.{},
|
||||||
|
/// Linked list of all tracked runtime objects.
|
||||||
gc_objects: std.SinglyLinkedList = .{},
|
gc_objects: std.SinglyLinkedList = .{},
|
||||||
// FIXME: This was a hack to keep string bytes alive.
|
// FIXME: This was a hack to keep string bytes alive.
|
||||||
string_bytes: []const u8 = &.{},
|
string_bytes: []const u8 = &.{},
|
||||||
|
|
@ -28,6 +31,7 @@ string_bytes: []const u8 = &.{},
|
||||||
pub const default_knot_name: [:0]const u8 = "$__main__$";
|
pub const default_knot_name: [:0]const u8 = "$__main__$";
|
||||||
|
|
||||||
pub const Value = union(enum) {
|
pub const Value = union(enum) {
|
||||||
|
nil,
|
||||||
bool: bool,
|
bool: bool,
|
||||||
int: i64,
|
int: i64,
|
||||||
float: f64,
|
float: f64,
|
||||||
|
|
@ -35,6 +39,7 @@ pub const Value = union(enum) {
|
||||||
|
|
||||||
pub fn tagBytes(v: Value) []const u8 {
|
pub fn tagBytes(v: Value) []const u8 {
|
||||||
return switch (v) {
|
return switch (v) {
|
||||||
|
.nil => "Nil",
|
||||||
.bool => "Bool",
|
.bool => "Bool",
|
||||||
.int => "Int",
|
.int => "Int",
|
||||||
.float => "Float",
|
.float => "Float",
|
||||||
|
|
@ -57,7 +62,7 @@ pub const Value = union(enum) {
|
||||||
|
|
||||||
pub fn isTruthy(v: Value) bool {
|
pub fn isTruthy(v: Value) bool {
|
||||||
return switch (v) {
|
return switch (v) {
|
||||||
//.nil => false,
|
.nil => false,
|
||||||
.bool => |b| b,
|
.bool => |b| b,
|
||||||
.int => |i| i != 0,
|
.int => |i| i != 0,
|
||||||
.float => |f| f != 0.0,
|
.float => |f| f != 0.0,
|
||||||
|
|
@ -133,6 +138,7 @@ pub const Value = union(enum) {
|
||||||
|
|
||||||
pub fn eql(lhs: Value, rhs: Value) bool {
|
pub fn eql(lhs: Value, rhs: Value) bool {
|
||||||
return switch (lhs) {
|
return switch (lhs) {
|
||||||
|
.nil => rhs == .nil,
|
||||||
.bool => |l| rhs == .bool and l == rhs.bool,
|
.bool => |l| rhs == .bool and l == rhs.bool,
|
||||||
.int => |l| switch (rhs) {
|
.int => |l| switch (rhs) {
|
||||||
.int => |r| l == r,
|
.int => |r| l == r,
|
||||||
|
|
@ -183,15 +189,15 @@ pub const Value = union(enum) {
|
||||||
|
|
||||||
pub fn negate(lhs: Value) !Value {
|
pub fn negate(lhs: Value) !Value {
|
||||||
switch (lhs) {
|
switch (lhs) {
|
||||||
.bool => return error.TypeError,
|
.nil, .bool, .object => return error.TypeError,
|
||||||
.int => |int| return .{ .int = -int },
|
.int => |int| return .{ .int = -int },
|
||||||
.float => |float| return .{ .float = -float },
|
.float => |float| return .{ .float = -float },
|
||||||
.object => return error.TypeError,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format(value: Value, writer: *std.Io.Writer) error{WriteFailed}!void {
|
pub fn format(value: Value, writer: *std.Io.Writer) error{WriteFailed}!void {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
|
.nil => try writer.writeAll(value.tagBytes()),
|
||||||
.bool => |boolean| try writer.writeAll(if (boolean) "true" else "false"),
|
.bool => |boolean| try writer.writeAll(if (boolean) "true" else "false"),
|
||||||
.int => |int| try writer.print("{d}", .{int}),
|
.int => |int| try writer.print("{d}", .{int}),
|
||||||
.float => |float| {
|
.float => |float| {
|
||||||
|
|
@ -222,9 +228,10 @@ pub const Value = union(enum) {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const CallFrame = struct {
|
pub const CallFrame = struct {
|
||||||
|
callee: *Object.Knot,
|
||||||
|
caller_top: usize,
|
||||||
ip: usize,
|
ip: usize,
|
||||||
sp: usize,
|
sp: usize,
|
||||||
callee: *Object.Knot,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Choice = struct {
|
pub const Choice = struct {
|
||||||
|
|
@ -304,62 +311,52 @@ pub fn deinit(story: *Story) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
story.current_choices.deinit(gpa);
|
story.current_choices.deinit(gpa);
|
||||||
story.constants_pool.deinit(gpa);
|
|
||||||
story.globals.deinit(gpa);
|
story.globals.deinit(gpa);
|
||||||
story.stack.deinit(gpa);
|
|
||||||
story.call_stack.deinit(gpa);
|
|
||||||
gpa.free(story.string_bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn isCallStackEmpty(vm: *const Story) bool {
|
gpa.free(story.string_bytes);
|
||||||
return vm.call_stack.items.len == 0;
|
gpa.free(story.constants_pool);
|
||||||
|
gpa.free(story.stack);
|
||||||
|
gpa.free(story.call_stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn currentFrame(vm: *Story) *CallFrame {
|
fn currentFrame(vm: *Story) *CallFrame {
|
||||||
return &vm.call_stack.items[vm.call_stack.items.len - 1];
|
assert(vm.call_stack_top > 0);
|
||||||
|
return &vm.call_stack[vm.call_stack_top - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peekStack(vm: *Story, offset: usize) ?Value {
|
fn peekStack(vm: *Story, offset: usize) ?Value {
|
||||||
const stack_top = vm.stack.items.len;
|
if (vm.stack_top <= offset) return null;
|
||||||
if (stack_top <= offset) return null;
|
return vm.stack[vm.stack_top - offset - 1];
|
||||||
|
|
||||||
return vm.stack.items[stack_top - offset - 1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pushStack(vm: *Story, value: Value) !void {
|
fn pushStack(vm: *Story, value: Value) !void {
|
||||||
const gpa = vm.allocator;
|
if (vm.stack_top >= vm.stack.len) return error.StackOverflow;
|
||||||
const stack_top = vm.stack.items.len;
|
vm.stack[vm.stack_top] = value;
|
||||||
const max_stack_top = vm.stack_max;
|
vm.stack_top += 1;
|
||||||
if (stack_top >= max_stack_top) return error.StackOverflow;
|
|
||||||
|
|
||||||
return vm.stack.append(gpa, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn popStack(vm: *Story) ?Value {
|
fn popStack(vm: *Story) ?Value {
|
||||||
return vm.stack.pop() orelse unreachable;
|
if (vm.stack_top == 0) return null;
|
||||||
|
|
||||||
|
const stack_top = vm.stack_top;
|
||||||
|
vm.stack_top -= 1;
|
||||||
|
return vm.stack[stack_top];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getConstant(story: *Story, frame: *CallFrame, offset: u8) !Value {
|
fn getConstant(story: *Story, frame: *CallFrame, offset: u8) !Value {
|
||||||
if (offset >= frame.callee.code.constants.len) return error.InvalidArgument;
|
if (offset >= frame.callee.code.constants.len) return error.InvalidArgument;
|
||||||
|
|
||||||
const constant_index = frame.callee.code.constants[offset];
|
const constant_index = frame.callee.code.constants[offset];
|
||||||
return story.constants_pool.items[constant_index];
|
return story.constants_pool[constant_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getLocal(vm: *Story, frame: *CallFrame, offset: u8) ?Value {
|
fn getLocal(vm: *Story, frame: *CallFrame, offset: u8) ?Value {
|
||||||
const stack_top = vm.stack.items.len;
|
assert(vm.stack_top > frame.sp + offset);
|
||||||
const stack_offset = frame.sp + offset;
|
return vm.stack[frame.sp + offset];
|
||||||
assert(stack_top > stack_offset);
|
|
||||||
|
|
||||||
return vm.stack.items[stack_offset];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setLocal(vm: *Story, frame: *CallFrame, offset: u8, value: Value) void {
|
fn setLocal(vm: *Story, frame: *CallFrame, offset: u8, value: Value) void {
|
||||||
const stack_top = vm.stack.items.len;
|
assert(vm.stack_top > frame.sp + offset);
|
||||||
const stack_offset = frame.sp + offset;
|
vm.stack[frame.sp + offset] = value;
|
||||||
assert(stack_top > stack_offset);
|
|
||||||
|
|
||||||
vm.stack.items[stack_offset] = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This should probably check the constants table first.
|
// TODO: This should probably check the constants table first.
|
||||||
|
|
@ -396,15 +393,14 @@ fn setGlobal(vm: *Story, key: Value, value: Value) !void {
|
||||||
|
|
||||||
fn execute(vm: *Story) !std.ArrayListUnmanaged(u8) {
|
fn execute(vm: *Story) !std.ArrayListUnmanaged(u8) {
|
||||||
const gpa = vm.allocator;
|
const gpa = vm.allocator;
|
||||||
|
if (vm.call_stack_top == 0) return .empty;
|
||||||
errdefer vm.can_advance = false;
|
errdefer vm.can_advance = false;
|
||||||
|
|
||||||
if (vm.isCallStackEmpty()) return .empty;
|
|
||||||
|
|
||||||
var stream_writer = std.Io.Writer.Allocating.init(gpa);
|
var stream_writer = std.Io.Writer.Allocating.init(gpa);
|
||||||
defer stream_writer.deinit();
|
defer stream_writer.deinit();
|
||||||
|
|
||||||
|
var frame = vm.currentFrame();
|
||||||
while (true) {
|
while (true) {
|
||||||
const frame = vm.currentFrame();
|
|
||||||
const code = std.mem.bytesAsSlice(Opcode, frame.callee.code.bytecode);
|
const code = std.mem.bytesAsSlice(Opcode, frame.callee.code.bytecode);
|
||||||
if (vm.dump_writer) |w| {
|
if (vm.dump_writer) |w| {
|
||||||
Dumper.trace(vm, w, frame) catch {};
|
Dumper.trace(vm, w, frame) catch {};
|
||||||
|
|
@ -419,14 +415,27 @@ fn execute(vm: *Story) !std.ArrayListUnmanaged(u8) {
|
||||||
vm.can_advance = false;
|
vm.can_advance = false;
|
||||||
return .empty;
|
return .empty;
|
||||||
},
|
},
|
||||||
|
.ret => {
|
||||||
|
const return_value = vm.stack[vm.stack_top - 1];
|
||||||
|
|
||||||
|
vm.call_stack_top -= 1;
|
||||||
|
const completed_frame = vm.call_stack[vm.call_stack_top];
|
||||||
|
|
||||||
|
vm.stack_top = completed_frame.caller_top;
|
||||||
|
|
||||||
|
vm.stack[vm.stack_top] = return_value;
|
||||||
|
vm.stack_top += 1;
|
||||||
|
|
||||||
|
if (vm.call_stack_top == 0) return error.UnexpectedReturn;
|
||||||
|
|
||||||
|
frame = &vm.call_stack[vm.call_stack_top - 1];
|
||||||
|
},
|
||||||
.true => {
|
.true => {
|
||||||
const value: Value = .{ .bool = true };
|
try vm.pushStack(.{ .bool = true });
|
||||||
try vm.pushStack(value);
|
|
||||||
frame.ip += 1;
|
frame.ip += 1;
|
||||||
},
|
},
|
||||||
.false => {
|
.false => {
|
||||||
const value: Value = .{ .bool = false };
|
try vm.pushStack(.{ .bool = false });
|
||||||
try vm.pushStack(value);
|
|
||||||
frame.ip += 1;
|
frame.ip += 1;
|
||||||
},
|
},
|
||||||
.pop => {
|
.pop => {
|
||||||
|
|
@ -519,6 +528,43 @@ fn execute(vm: *Story) !std.ArrayListUnmanaged(u8) {
|
||||||
return error.InvalidArgument;
|
return error.InvalidArgument;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
.call => {
|
||||||
|
const arg_offset: u8 = @intFromEnum(code[frame.ip + 1]);
|
||||||
|
frame.ip += 2;
|
||||||
|
|
||||||
|
if (peekStack(vm, arg_offset)) |value| {
|
||||||
|
switch (value) {
|
||||||
|
.object => |object| switch (object.tag) {
|
||||||
|
.knot => try call(vm, @ptrCast(object)),
|
||||||
|
else => unreachable,
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return error.InvalidArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-fetch — we're now in the callee's frame
|
||||||
|
frame = &vm.call_stack[vm.call_stack_top - 1];
|
||||||
|
},
|
||||||
|
.divert => {
|
||||||
|
const arg_offset: u8 = @intFromEnum(code[frame.ip + 1]);
|
||||||
|
frame.ip += 2;
|
||||||
|
|
||||||
|
if (peekStack(vm, arg_offset)) |value| {
|
||||||
|
switch (value) {
|
||||||
|
.object => |object| switch (object.tag) {
|
||||||
|
.knot => try divert(vm, @ptrCast(object)),
|
||||||
|
else => unreachable,
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return error.InvalidArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame = &vm.call_stack[vm.call_stack_top - 1];
|
||||||
|
},
|
||||||
.load_const => {
|
.load_const => {
|
||||||
const index: u8 = @intFromEnum(code[frame.ip + 1]);
|
const index: u8 = @intFromEnum(code[frame.ip + 1]);
|
||||||
const value = try vm.getConstant(frame, index);
|
const value = try vm.getConstant(frame, index);
|
||||||
|
|
@ -611,16 +657,6 @@ fn execute(vm: *Story) !std.ArrayListUnmanaged(u8) {
|
||||||
|
|
||||||
frame.ip = branch_dispatch.dest_offset;
|
frame.ip = branch_dispatch.dest_offset;
|
||||||
},
|
},
|
||||||
.divert => {
|
|
||||||
const arg_offset: u8 = @intFromEnum(code[frame.ip + 1]);
|
|
||||||
frame.ip += 2;
|
|
||||||
|
|
||||||
if (peekStack(vm, arg_offset)) |value| {
|
|
||||||
try divertToValue(vm, value);
|
|
||||||
} else {
|
|
||||||
return error.InvalidArgument;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.load_attr => {
|
.load_attr => {
|
||||||
const arg_offset: u8 = @intFromEnum(code[frame.ip + 1]);
|
const arg_offset: u8 = @intFromEnum(code[frame.ip + 1]);
|
||||||
frame.ip += 2;
|
frame.ip += 2;
|
||||||
|
|
@ -667,38 +703,62 @@ pub fn getKnot(vm: *Story, name: []const u8) ?*Object.Knot {
|
||||||
return knot;
|
return knot;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(Brett): Add arguments?
|
fn call(vm: *Story, knot: *Object.Knot) !void {
|
||||||
fn divertToKnot(vm: *Story, knot: *Object.Knot) !void {
|
if (vm.call_stack_top >= vm.call_stack.len)
|
||||||
const gpa = vm.allocator;
|
return error.CallStackOverflow;
|
||||||
const stack_ptr = vm.stack.items.len - knot.code.args_count;
|
|
||||||
const stack_needed = knot.code.stack_size;
|
|
||||||
|
|
||||||
try vm.stack.ensureUnusedCapacity(gpa, stack_needed);
|
const locals_count = knot.code.locals_count;
|
||||||
try vm.call_stack.ensureUnusedCapacity(gpa, 1);
|
const args_count = knot.code.args_count;
|
||||||
|
const sp = vm.stack_top - args_count;
|
||||||
|
const caller_top = if (vm.call_stack_top == 0)
|
||||||
|
sp
|
||||||
|
else
|
||||||
|
sp - 1;
|
||||||
|
|
||||||
vm.call_stack.appendAssumeCapacity(.{
|
const frame_top = sp + args_count + locals_count;
|
||||||
|
if (frame_top > vm.stack.len) return error.StackOverflow;
|
||||||
|
for (vm.stack[sp + args_count .. frame_top]) |*slot| slot.* = .nil;
|
||||||
|
|
||||||
|
vm.stack_top = frame_top;
|
||||||
|
vm.call_stack[vm.call_stack_top] = .{
|
||||||
.callee = knot,
|
.callee = knot,
|
||||||
.ip = 0,
|
.ip = 0,
|
||||||
.sp = stack_ptr,
|
.sp = sp,
|
||||||
});
|
.caller_top = caller_top,
|
||||||
vm.stack.appendNTimesAssumeCapacity(null, stack_needed);
|
};
|
||||||
vm.can_advance = true;
|
vm.call_stack_top += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn divertToValue(vm: *Story, value: Value) !void {
|
// Diverts are essentially tail calls.
|
||||||
switch (value) {
|
fn divert(vm: *Story, knot: *Object.Knot) !void {
|
||||||
.object => |object| switch (object.tag) {
|
const args_count = knot.code.args_count;
|
||||||
.knot => try divertToKnot(vm, @ptrCast(object)),
|
const locals_count = knot.code.locals_count;
|
||||||
else => return error.TypeError,
|
if (vm.call_stack_top == 0) return vm.call(knot);
|
||||||
},
|
|
||||||
else => return error.TypeError,
|
const args_start = vm.stack_top - args_count;
|
||||||
|
const current_frame = &vm.call_stack[vm.call_stack_top - 1];
|
||||||
|
const sp = current_frame.sp;
|
||||||
|
const caller_top = current_frame.caller_top;
|
||||||
|
|
||||||
|
if (args_count > 0) {
|
||||||
|
std.mem.copyForwards(
|
||||||
|
Value,
|
||||||
|
vm.stack[sp .. sp + args_count],
|
||||||
|
vm.stack[args_start .. args_start + args_count],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn divert(vm: *Story, knot_name: []const u8) !void {
|
const frame_top = sp + args_count + locals_count;
|
||||||
return if (getKnot(vm, knot_name)) |knot| {
|
if (frame_top > vm.stack.len) return error.StackOverflow;
|
||||||
return divertToKnot(vm, knot);
|
for (vm.stack[sp + args_count .. frame_top]) |*slot| slot.* = .nil;
|
||||||
} else return error.InvalidPath;
|
vm.stack_top = frame_top;
|
||||||
|
|
||||||
|
current_frame.* = .{
|
||||||
|
.callee = knot,
|
||||||
|
.ip = 0,
|
||||||
|
.sp = sp,
|
||||||
|
.caller_top = caller_top,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const LoadOptions = struct {
|
pub const LoadOptions = struct {
|
||||||
|
|
@ -746,15 +806,25 @@ pub fn loadFromString(
|
||||||
return error.LoadFailed;
|
return error.LoadFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const stack_size = 128;
|
||||||
|
const eval_stack_ptr = try gpa.alloc(Value, stack_size);
|
||||||
|
errdefer gpa.free(eval_stack_ptr);
|
||||||
|
|
||||||
|
const call_stack_ptr = try gpa.alloc(CallFrame, stack_size);
|
||||||
|
errdefer gpa.free(call_stack_ptr);
|
||||||
|
|
||||||
var story: Story = .{
|
var story: Story = .{
|
||||||
.allocator = gpa,
|
.allocator = gpa,
|
||||||
.can_advance = false,
|
.can_advance = false,
|
||||||
.dump_writer = if (options.dump_trace) options.dump_writer else null,
|
.dump_writer = if (options.dump_trace) options.dump_writer else null,
|
||||||
|
.stack = eval_stack_ptr,
|
||||||
|
.call_stack = call_stack_ptr,
|
||||||
};
|
};
|
||||||
errdefer story.deinit();
|
errdefer story.deinit();
|
||||||
|
|
||||||
try comp.setupStoryRuntime(gpa, &story);
|
try comp.setupStoryRuntime(gpa, &story);
|
||||||
if (story.getKnot(Story.default_knot_name)) |knot| {
|
if (story.getKnot(Story.default_knot_name)) |knot| {
|
||||||
try story.divertToKnot(knot);
|
try story.divert(knot);
|
||||||
story.can_advance = true;
|
story.can_advance = true;
|
||||||
}
|
}
|
||||||
return story;
|
return story;
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ fn dumpByteInst(
|
||||||
op: Opcode,
|
op: Opcode,
|
||||||
) !usize {
|
) !usize {
|
||||||
const code = knot.code;
|
const code = knot.code;
|
||||||
const constants_pool = &self.story.constants_pool;
|
|
||||||
|
|
||||||
assert(code.bytecode.len > offset + 1);
|
assert(code.bytecode.len > offset + 1);
|
||||||
const arg = code.bytecode[offset + 1];
|
const arg = code.bytecode[offset + 1];
|
||||||
|
|
@ -31,9 +30,8 @@ fn dumpByteInst(
|
||||||
try w.writeAll(" (");
|
try w.writeAll(" (");
|
||||||
if (code.constants.len > arg) {
|
if (code.constants.len > arg) {
|
||||||
const constant_index = code.constants[arg];
|
const constant_index = code.constants[arg];
|
||||||
if (constants_pool.items.len > constant_index) {
|
if (self.story.constants_pool.len > constant_index) {
|
||||||
const global_constant = &constants_pool.items[constant_index];
|
try self.dumpValue(w, &self.story.constants_pool[constant_index]);
|
||||||
try self.dumpValue(w, global_constant);
|
|
||||||
} else {
|
} else {
|
||||||
try w.writeAll("invalid!");
|
try w.writeAll("invalid!");
|
||||||
}
|
}
|
||||||
|
|
@ -55,14 +53,11 @@ fn dumpGlobalInst(
|
||||||
op: Opcode,
|
op: Opcode,
|
||||||
) !usize {
|
) !usize {
|
||||||
const code = knot.code;
|
const code = knot.code;
|
||||||
const constants_pool = &self.story.constants_pool;
|
|
||||||
|
|
||||||
assert(code.bytecode.len > offset + 1);
|
assert(code.bytecode.len > offset + 1);
|
||||||
const arg = code.bytecode[offset + 1];
|
const arg = code.bytecode[offset + 1];
|
||||||
assert(code.constants.len > arg);
|
assert(code.constants.len > arg);
|
||||||
const constant_index = code.constants[arg];
|
const constant_index = code.constants[arg];
|
||||||
const global_constant = constants_pool.items[constant_index];
|
switch (self.story.constants_pool[constant_index]) {
|
||||||
switch (global_constant) {
|
|
||||||
.object => |object| switch (object.tag) {
|
.object => |object| switch (object.tag) {
|
||||||
.string => {
|
.string => {
|
||||||
const global_name: *Object.String = @ptrCast(object);
|
const global_name: *Object.String = @ptrCast(object);
|
||||||
|
|
@ -280,6 +275,10 @@ pub fn dumpKnot(self: *Dumper, w: *std.Io.Writer, knot: *const Object.Knot) !voi
|
||||||
|
|
||||||
pub fn dumpValue(_: Dumper, w: *std.Io.Writer, value: *const Value) !void {
|
pub fn dumpValue(_: Dumper, w: *std.Io.Writer, value: *const Value) !void {
|
||||||
switch (value.*) {
|
switch (value.*) {
|
||||||
|
.nil => try w.print(
|
||||||
|
"<{s}>",
|
||||||
|
.{value.tagBytes()},
|
||||||
|
),
|
||||||
.bool => |_| try w.print(
|
.bool => |_| try w.print(
|
||||||
"<{s} value={f}>",
|
"<{s} value={f}>",
|
||||||
.{ value.tagBytes(), value },
|
.{ value.tagBytes(), value },
|
||||||
|
|
@ -313,8 +312,8 @@ pub fn dump(story: *Story, writer: *std.Io.Writer) !void {
|
||||||
try writer.writeAll("=== Constants ===\n");
|
try writer.writeAll("=== Constants ===\n");
|
||||||
|
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (i < story.constants_pool.items.len) : (i += 1) {
|
while (i < story.constants_pool.len) : (i += 1) {
|
||||||
const global_constant = &story.constants_pool.items[i];
|
const global_constant = &story.constants_pool[i];
|
||||||
try story_dumper.dumpValue(writer, global_constant);
|
try story_dumper.dumpValue(writer, global_constant);
|
||||||
try writer.writeAll("\n");
|
try writer.writeAll("\n");
|
||||||
}
|
}
|
||||||
|
|
@ -350,31 +349,14 @@ pub fn dump(story: *Story, writer: *std.Io.Writer) !void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trace(story: *Story, writer: *std.Io.Writer, frame: *Story.CallFrame) !void {
|
pub fn trace(vm: *Story, writer: *std.Io.Writer, frame: *Story.CallFrame) !void {
|
||||||
var dumper: Dumper = .{ .story = story };
|
var dumper: Dumper = .{ .story = vm };
|
||||||
const stack = &story.stack;
|
|
||||||
const stack_top = story.stack.items.len;
|
|
||||||
|
|
||||||
try writer.print("\tStack => stack_pointer={d}, objects=[", .{frame.sp});
|
try writer.print("\tStack => stack_pointer={d}, objects=[", .{frame.sp});
|
||||||
|
|
||||||
if (stack_top > 0) {
|
const window = vm.stack[frame.sp..vm.stack_top];
|
||||||
// FIXME: There has to be a better way to do this.
|
for (window, 0..) |*slot, i| {
|
||||||
if (stack_top > 1) {
|
if (i > 0) try writer.writeAll(", ");
|
||||||
var i: usize = frame.sp;
|
try dumper.dumpValue(writer, slot);
|
||||||
while (i < stack.items.len - 1) : (i += 1) {
|
|
||||||
if (stack.items[i]) |*value| {
|
|
||||||
try dumper.dumpValue(writer, value);
|
|
||||||
} else {
|
|
||||||
try writer.writeAll("null");
|
|
||||||
}
|
|
||||||
try writer.writeAll(", ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (stack.items[stack.items.len - 1]) |*object| {
|
|
||||||
try dumper.dumpValue(writer, object);
|
|
||||||
} else {
|
|
||||||
try writer.writeAll("null");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try writer.writeAll("]\n");
|
try writer.writeAll("]\n");
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ pub const String = struct {
|
||||||
const print_buffer_len = 64;
|
const print_buffer_len = 64;
|
||||||
var print_buffer: [print_buffer_len]u8 = undefined;
|
var print_buffer: [print_buffer_len]u8 = undefined;
|
||||||
switch (value) {
|
switch (value) {
|
||||||
.bool, .int, .float => {
|
.nil, .bool, .int, .float => {
|
||||||
const bytes = try std.fmt.bufPrint(&print_buffer, "{f}", .{value});
|
const bytes = try std.fmt.bufPrint(&print_buffer, "{f}", .{value});
|
||||||
return .create(story, .{ .bytes = bytes });
|
return .create(story, .{ .bytes = bytes });
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,18 @@ test "fixture - I002 (Fogg comforts Passepartout)" {
|
||||||
try testRuntimeFixture("I002");
|
try testRuntimeFixture("I002");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "fixture - I004 (Print number as English)" {
|
||||||
|
try testRuntimeFixture("I004");
|
||||||
|
}
|
||||||
|
|
||||||
test "fixture - I005 (Const variable)" {
|
test "fixture - I005 (Const variable)" {
|
||||||
try testRuntimeFixture("I005");
|
try testRuntimeFixture("I005");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "fixture - I006 (Multiple constant references)" {
|
||||||
|
try testRuntimeFixture("I006");
|
||||||
|
}
|
||||||
|
|
||||||
test "fixture - I007 (Set non existant variable)" {
|
test "fixture - I007 (Set non existant variable)" {
|
||||||
try testRuntimeFixture("I007");
|
try testRuntimeFixture("I007");
|
||||||
}
|
}
|
||||||
|
|
@ -35,6 +43,10 @@ test "fixture - I011 (Temporaries at global scope)" {
|
||||||
// try testRuntimeFixture("I012");
|
// try testRuntimeFixture("I012");
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
test "fixture - I014 (Variable swap recurse)" {
|
||||||
|
try testRuntimeFixture("I014");
|
||||||
|
}
|
||||||
|
|
||||||
test "fixture - I016 (Empty)" {
|
test "fixture - I016 (Empty)" {
|
||||||
try testRuntimeFixture("I016");
|
try testRuntimeFixture("I016");
|
||||||
}
|
}
|
||||||
|
|
@ -59,10 +71,26 @@ test "fixture - I023 (Whitespace)" {
|
||||||
try testRuntimeFixture("I023");
|
try testRuntimeFixture("I023");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "fixture - I033 (Newline consistency, the first)" {
|
||||||
|
try testRuntimeFixture("I033");
|
||||||
|
}
|
||||||
|
|
||||||
|
test "fixture - I034 (Newline consistency, the second)" {
|
||||||
|
try testRuntimeFixture("I034");
|
||||||
|
}
|
||||||
|
|
||||||
test "fixture - I035 (Newline consistency, the third)" {
|
test "fixture - I035 (Newline consistency, the third)" {
|
||||||
try testRuntimeFixture("I035");
|
try testRuntimeFixture("I035");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "fixture - I036 (Newlines with string eval)" {
|
||||||
|
try testRuntimeFixture("I036");
|
||||||
|
}
|
||||||
|
|
||||||
|
test "fixture - I037 (Newline at start of multiline conditional)" {
|
||||||
|
try testRuntimeFixture("I037");
|
||||||
|
}
|
||||||
|
|
||||||
test "fixture - I042 (Weave options)" {
|
test "fixture - I042 (Weave options)" {
|
||||||
try testRuntimeFixture("I042");
|
try testRuntimeFixture("I042");
|
||||||
}
|
}
|
||||||
|
|
@ -75,18 +103,35 @@ test "fixture - I064 (Done stops thread)" {
|
||||||
try testRuntimeFixture("I064");
|
try testRuntimeFixture("I064");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This one is named oddly...
|
||||||
|
test "fixture - I075 (Clean callstack reset on path choice)" {
|
||||||
|
try testRuntimeFixture("I075");
|
||||||
|
}
|
||||||
|
|
||||||
test "fixture - I078 (Choice with brackets only)" {
|
test "fixture - I078 (Choice with brackets only)" {
|
||||||
try testRuntimeFixture("I078");
|
try testRuntimeFixture("I078");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "fixture - I117 (Factorial recursive)" {
|
||||||
|
try testRuntimeFixture("I117");
|
||||||
|
}
|
||||||
|
|
||||||
test "fixture - I118 (Literal unary)" {
|
test "fixture - I118 (Literal unary)" {
|
||||||
try testRuntimeFixture("I118");
|
try testRuntimeFixture("I118");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "fixture - I119 (Basic string literals)" {
|
||||||
|
try testRuntimeFixture("I119");
|
||||||
|
}
|
||||||
|
|
||||||
test "fixture - I121 (Arithmetic)" {
|
test "fixture - I121 (Arithmetic)" {
|
||||||
try testRuntimeFixture("I121");
|
try testRuntimeFixture("I121");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "fixture - I124 (Evaluating ink functions from game 2)" {
|
||||||
|
try testRuntimeFixture("I124");
|
||||||
|
}
|
||||||
|
|
||||||
test "fixture - I133 (Float printing precision)" {
|
test "fixture - I133 (Float printing precision)" {
|
||||||
try testRuntimeFixture("I133");
|
try testRuntimeFixture("I133");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
0
src/Story/testdata/I004/input.txt
vendored
Normal file
0
src/Story/testdata/I004/input.txt
vendored
Normal file
53
src/Story/testdata/I004/story.ink
vendored
Normal file
53
src/Story/testdata/I004/story.ink
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
You have {print_num(58)} coins.
|
||||||
|
|
||||||
|
=== function print_num(x)
|
||||||
|
{
|
||||||
|
- x >= 1000:
|
||||||
|
{print_num(x / 1000)} thousand { x mod 1000 > 0:{print_num(x mod 1000)}}
|
||||||
|
- x >= 100:
|
||||||
|
{print_num(x / 100)} hundred { x mod 100 > 0:and {print_num(x mod 100)}}
|
||||||
|
- x == 0:
|
||||||
|
zero
|
||||||
|
- else:
|
||||||
|
{ x >= 20:
|
||||||
|
{ x / 10:
|
||||||
|
- 2: twenty
|
||||||
|
- 3: thirty
|
||||||
|
- 4: forty
|
||||||
|
- 5: fifty
|
||||||
|
- 6: sixty
|
||||||
|
- 7: seventy
|
||||||
|
- 8: eighty
|
||||||
|
- 9: ninety
|
||||||
|
}
|
||||||
|
{ x mod 10 > 0:
|
||||||
|
<>-<>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{ x < 10 || x > 20:
|
||||||
|
{ x mod 10:
|
||||||
|
- 1: one
|
||||||
|
- 2: two
|
||||||
|
- 3: three
|
||||||
|
- 4: four
|
||||||
|
- 5: five
|
||||||
|
- 6: six
|
||||||
|
- 7: seven
|
||||||
|
- 8: eight
|
||||||
|
- 9: nine
|
||||||
|
}
|
||||||
|
- else:
|
||||||
|
{ x:
|
||||||
|
- 10: ten
|
||||||
|
- 11: eleven
|
||||||
|
- 12: twelve
|
||||||
|
- 13: thirteen
|
||||||
|
- 14: fourteen
|
||||||
|
- 15: fifteen
|
||||||
|
- 16: sixteen
|
||||||
|
- 17: seventeen
|
||||||
|
- 18: eighteen
|
||||||
|
- 19: nineteen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
src/Story/testdata/I004/transcript.txt
vendored
Normal file
1
src/Story/testdata/I004/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
You have fifty-eight coins.
|
||||||
0
src/Story/testdata/I006/input.txt
vendored
Normal file
0
src/Story/testdata/I006/input.txt
vendored
Normal file
3
src/Story/testdata/I006/story.ink
vendored
Normal file
3
src/Story/testdata/I006/story.ink
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
CONST CONST_STR = "ConstantString"
|
||||||
|
VAR varStr = CONST_STR
|
||||||
|
{varStr == CONST_STR:success}
|
||||||
1
src/Story/testdata/I006/transcript.txt
vendored
Normal file
1
src/Story/testdata/I006/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
success
|
||||||
0
src/Story/testdata/I014/input.txt
vendored
Normal file
0
src/Story/testdata/I014/input.txt
vendored
Normal file
9
src/Story/testdata/I014/story.ink
vendored
Normal file
9
src/Story/testdata/I014/story.ink
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
~ f(1, 1)
|
||||||
|
== function f(x, y) ==
|
||||||
|
{ x == 1 and y == 1:
|
||||||
|
~ x = 2
|
||||||
|
~ f(y, x)
|
||||||
|
- else:
|
||||||
|
{x} {y}
|
||||||
|
}
|
||||||
|
~ return
|
||||||
1
src/Story/testdata/I014/transcript.txt
vendored
Normal file
1
src/Story/testdata/I014/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
1 2
|
||||||
0
src/Story/testdata/I033/input.txt
vendored
Normal file
0
src/Story/testdata/I033/input.txt
vendored
Normal file
4
src/Story/testdata/I033/story.ink
vendored
Normal file
4
src/Story/testdata/I033/story.ink
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
hello -> world
|
||||||
|
== world
|
||||||
|
world
|
||||||
|
-> END
|
||||||
1
src/Story/testdata/I033/transcript.txt
vendored
Normal file
1
src/Story/testdata/I033/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
hello world
|
||||||
1
src/Story/testdata/I034/input.txt
vendored
Normal file
1
src/Story/testdata/I034/input.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
||||||
4
src/Story/testdata/I034/story.ink
vendored
Normal file
4
src/Story/testdata/I034/story.ink
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
* hello -> world
|
||||||
|
== world
|
||||||
|
world
|
||||||
|
-> END
|
||||||
2
src/Story/testdata/I034/transcript.txt
vendored
Normal file
2
src/Story/testdata/I034/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
1: hello
|
||||||
|
?> hello world
|
||||||
0
src/Story/testdata/I036/input.txt
vendored
Normal file
0
src/Story/testdata/I036/input.txt
vendored
Normal file
9
src/Story/testdata/I036/story.ink
vendored
Normal file
9
src/Story/testdata/I036/story.ink
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
A
|
||||||
|
~temp someTemp = string()
|
||||||
|
B
|
||||||
|
A
|
||||||
|
{string()}
|
||||||
|
B
|
||||||
|
=== function string()
|
||||||
|
~ return "{3}"
|
||||||
|
}
|
||||||
5
src/Story/testdata/I036/transcript.txt
vendored
Normal file
5
src/Story/testdata/I036/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
A
|
||||||
|
B
|
||||||
|
A
|
||||||
|
3
|
||||||
|
B
|
||||||
0
src/Story/testdata/I037/input.txt
vendored
Normal file
0
src/Story/testdata/I037/input.txt
vendored
Normal file
6
src/Story/testdata/I037/story.ink
vendored
Normal file
6
src/Story/testdata/I037/story.ink
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{isTrue():
|
||||||
|
x
|
||||||
|
}
|
||||||
|
=== function isTrue()
|
||||||
|
X
|
||||||
|
~ return true
|
||||||
2
src/Story/testdata/I037/transcript.txt
vendored
Normal file
2
src/Story/testdata/I037/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
X
|
||||||
|
x
|
||||||
0
src/Story/testdata/I044/input.txt
vendored
Normal file
0
src/Story/testdata/I044/input.txt
vendored
Normal file
7
src/Story/testdata/I044/story.ink
vendored
Normal file
7
src/Story/testdata/I044/story.ink
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
A
|
||||||
|
{f():X}
|
||||||
|
C
|
||||||
|
=== function f()
|
||||||
|
{ true:
|
||||||
|
~ return false
|
||||||
|
}
|
||||||
2
src/Story/testdata/I044/transcript.txt
vendored
Normal file
2
src/Story/testdata/I044/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
A
|
||||||
|
C
|
||||||
0
src/Story/testdata/I045/input.txt
vendored
Normal file
0
src/Story/testdata/I045/input.txt
vendored
Normal file
6
src/Story/testdata/I045/story.ink
vendored
Normal file
6
src/Story/testdata/I045/story.ink
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
A {f():B}
|
||||||
|
X
|
||||||
|
=== function f() ===
|
||||||
|
{true:
|
||||||
|
~ return false
|
||||||
|
}
|
||||||
2
src/Story/testdata/I045/transcript.txt
vendored
Normal file
2
src/Story/testdata/I045/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
A
|
||||||
|
X
|
||||||
0
src/Story/testdata/I046/input.txt
vendored
Normal file
0
src/Story/testdata/I046/input.txt
vendored
Normal file
7
src/Story/testdata/I046/story.ink
vendored
Normal file
7
src/Story/testdata/I046/story.ink
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
A line.
|
||||||
|
{ f():
|
||||||
|
Another line.
|
||||||
|
}
|
||||||
|
== function f ==
|
||||||
|
{false:nothing}
|
||||||
|
~ return true
|
||||||
2
src/Story/testdata/I046/transcript.txt
vendored
Normal file
2
src/Story/testdata/I046/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
A line.
|
||||||
|
Another line.
|
||||||
0
src/Story/testdata/I047/input.txt
vendored
Normal file
0
src/Story/testdata/I047/input.txt
vendored
Normal file
6
src/Story/testdata/I047/story.ink
vendored
Normal file
6
src/Story/testdata/I047/story.ink
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
I have {five()} eggs.
|
||||||
|
== function five ==
|
||||||
|
{false:
|
||||||
|
Don't print this
|
||||||
|
}
|
||||||
|
five
|
||||||
1
src/Story/testdata/I047/transcript.txt
vendored
Normal file
1
src/Story/testdata/I047/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
I have five eggs.
|
||||||
0
src/Story/testdata/I048/input.txt
vendored
Normal file
0
src/Story/testdata/I048/input.txt
vendored
Normal file
2
src/Story/testdata/I048/story.ink
vendored
Normal file
2
src/Story/testdata/I048/story.ink
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
Some <>
|
||||||
|
content<> with glue.
|
||||||
1
src/Story/testdata/I048/transcript.txt
vendored
Normal file
1
src/Story/testdata/I048/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Some content with glue.
|
||||||
0
src/Story/testdata/I055/input.txt
vendored
Normal file
0
src/Story/testdata/I055/input.txt
vendored
Normal file
6
src/Story/testdata/I055/story.ink
vendored
Normal file
6
src/Story/testdata/I055/story.ink
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
-> hurry_home
|
||||||
|
=== hurry_home ===
|
||||||
|
We hurried home to Savile Row -> as_fast_as_we_could
|
||||||
|
=== as_fast_as_we_could ===
|
||||||
|
as fast as we could.
|
||||||
|
-> DONE
|
||||||
1
src/Story/testdata/I055/transcript.txt
vendored
Normal file
1
src/Story/testdata/I055/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
We hurried home to Savile Row as fast as we could.
|
||||||
0
src/Story/testdata/I061/input.txt
vendored
Normal file
0
src/Story/testdata/I061/input.txt
vendored
Normal file
8
src/Story/testdata/I061/story.ink
vendored
Normal file
8
src/Story/testdata/I061/story.ink
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
=== intro
|
||||||
|
= top
|
||||||
|
{ main: -> done }
|
||||||
|
-> END
|
||||||
|
= main
|
||||||
|
-> top
|
||||||
|
= done
|
||||||
|
-> END
|
||||||
0
src/Story/testdata/I061/transcript.txt
vendored
Normal file
0
src/Story/testdata/I061/transcript.txt
vendored
Normal file
0
src/Story/testdata/I075/input.txt
vendored
Normal file
0
src/Story/testdata/I075/input.txt
vendored
Normal file
7
src/Story/testdata/I075/story.ink
vendored
Normal file
7
src/Story/testdata/I075/story.ink
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
{RunAThing()}
|
||||||
|
== function RunAThing ==
|
||||||
|
The first line.
|
||||||
|
The second line.
|
||||||
|
== SomewhereElse ==
|
||||||
|
{"somewhere else"}
|
||||||
|
->END
|
||||||
2
src/Story/testdata/I075/transcript.txt
vendored
Normal file
2
src/Story/testdata/I075/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
The first line.
|
||||||
|
The second line.
|
||||||
0
src/Story/testdata/I076/input.txt
vendored
Normal file
0
src/Story/testdata/I076/input.txt
vendored
Normal file
8
src/Story/testdata/I076/story.ink
vendored
Normal file
8
src/Story/testdata/I076/story.ink
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{ six() + two() }
|
||||||
|
-> END
|
||||||
|
=== function six
|
||||||
|
~ return four() + two()
|
||||||
|
=== function four
|
||||||
|
~ return two() + two()
|
||||||
|
=== function two
|
||||||
|
~ return 2
|
||||||
1
src/Story/testdata/I076/transcript.txt
vendored
Normal file
1
src/Story/testdata/I076/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
8
|
||||||
1
src/Story/testdata/I082/input.txt
vendored
Normal file
1
src/Story/testdata/I082/input.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
||||||
1
src/Story/testdata/I082/story.ink
vendored
Normal file
1
src/Story/testdata/I082/story.ink
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
* choice -> DONE
|
||||||
2
src/Story/testdata/I082/transcript.txt
vendored
Normal file
2
src/Story/testdata/I082/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
1: choice
|
||||||
|
?> choice
|
||||||
1
src/Story/testdata/I085/input.txt
vendored
Normal file
1
src/Story/testdata/I085/input.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
||||||
4
src/Story/testdata/I085/story.ink
vendored
Normal file
4
src/Story/testdata/I085/story.ink
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
* 'Hello {name()}[, your name is {name()}.'],' I said, knowing full well that his name was {name()}.
|
||||||
|
-> DONE
|
||||||
|
== function name ==
|
||||||
|
Joe
|
||||||
2
src/Story/testdata/I085/transcript.txt
vendored
Normal file
2
src/Story/testdata/I085/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
1: 'Hello Joe, your name is Joe.'
|
||||||
|
?> 'Hello Joe,' I said, knowing full well that his name was Joe.
|
||||||
1
src/Story/testdata/I087/input.txt
vendored
Normal file
1
src/Story/testdata/I087/input.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
||||||
7
src/Story/testdata/I087/story.ink
vendored
Normal file
7
src/Story/testdata/I087/story.ink
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
-> knot
|
||||||
|
== knot
|
||||||
|
* option text[]. {true: Conditional bit.} -> next
|
||||||
|
-> DONE
|
||||||
|
== next
|
||||||
|
Next.
|
||||||
|
-> DONE
|
||||||
2
src/Story/testdata/I087/transcript.txt
vendored
Normal file
2
src/Story/testdata/I087/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
1: option text
|
||||||
|
?> option text. Conditional bit. Next.
|
||||||
0
src/Story/testdata/I094/input.txt
vendored
Normal file
0
src/Story/testdata/I094/input.txt
vendored
Normal file
55
src/Story/testdata/I094/story.ink
vendored
Normal file
55
src/Story/testdata/I094/story.ink
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
. {print_num(4)} .
|
||||||
|
. {print_num(15)} .
|
||||||
|
. {print_num(37)} .
|
||||||
|
. {print_num(101)} .
|
||||||
|
. {print_num(222)} .
|
||||||
|
. {print_num(1234)} .
|
||||||
|
=== function print_num(x) ===
|
||||||
|
{
|
||||||
|
- x >= 1000:
|
||||||
|
{print_num(x / 1000)} thousand { x mod 1000 > 0:{print_num(x mod 1000)}}
|
||||||
|
- x >= 100:
|
||||||
|
{print_num(x / 100)} hundred { x mod 100 > 0:and {print_num(x mod 100)}}
|
||||||
|
- x == 0:
|
||||||
|
zero
|
||||||
|
- else:
|
||||||
|
{ x >= 20:
|
||||||
|
{ x / 10:
|
||||||
|
- 2: twenty
|
||||||
|
- 3: thirty
|
||||||
|
- 4: forty
|
||||||
|
- 5: fifty
|
||||||
|
- 6: sixty
|
||||||
|
- 7: seventy
|
||||||
|
- 8: eighty
|
||||||
|
- 9: ninety
|
||||||
|
}
|
||||||
|
{ x mod 10 > 0:<>-<>}
|
||||||
|
}
|
||||||
|
{ x < 10 || x > 20:
|
||||||
|
{ x mod 10:
|
||||||
|
- 1: one
|
||||||
|
- 2: two
|
||||||
|
- 3: three
|
||||||
|
- 4: four
|
||||||
|
- 5: five
|
||||||
|
- 6: six
|
||||||
|
- 7: seven
|
||||||
|
- 8: eight
|
||||||
|
- 9: nine
|
||||||
|
}
|
||||||
|
- else:
|
||||||
|
{ x:
|
||||||
|
- 10: ten
|
||||||
|
- 11: eleven
|
||||||
|
- 12: twelve
|
||||||
|
- 13: thirteen
|
||||||
|
- 14: fourteen
|
||||||
|
- 15: fifteen
|
||||||
|
- 16: sixteen
|
||||||
|
- 17: seventeen
|
||||||
|
- 18: eighteen
|
||||||
|
- 19: nineteen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/Story/testdata/I094/transcript.txt
vendored
Normal file
6
src/Story/testdata/I094/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
. four .
|
||||||
|
. fifteen .
|
||||||
|
. thirty-seven .
|
||||||
|
. one hundred and one .
|
||||||
|
. two hundred and twenty-two .
|
||||||
|
. one thousand two hundred and thirty-four .
|
||||||
0
src/Story/testdata/I095/input.txt
vendored
Normal file
0
src/Story/testdata/I095/input.txt
vendored
Normal file
8
src/Story/testdata/I095/story.ink
vendored
Normal file
8
src/Story/testdata/I095/story.ink
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{true:
|
||||||
|
a
|
||||||
|
} <> b
|
||||||
|
{true:
|
||||||
|
a
|
||||||
|
} <> { true:
|
||||||
|
b
|
||||||
|
}
|
||||||
2
src/Story/testdata/I095/transcript.txt
vendored
Normal file
2
src/Story/testdata/I095/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
a b
|
||||||
|
a b
|
||||||
0
src/Story/testdata/I097/input.txt
vendored
Normal file
0
src/Story/testdata/I097/input.txt
vendored
Normal file
7
src/Story/testdata/I097/story.ink
vendored
Normal file
7
src/Story/testdata/I097/story.ink
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
~ func ()
|
||||||
|
text 2
|
||||||
|
~ temp tempVar = func ()
|
||||||
|
text 2
|
||||||
|
== function func ()
|
||||||
|
text1
|
||||||
|
~ return true
|
||||||
4
src/Story/testdata/I097/transcript.txt
vendored
Normal file
4
src/Story/testdata/I097/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
text1
|
||||||
|
text 2
|
||||||
|
text1
|
||||||
|
text 2
|
||||||
0
src/Story/testdata/I112/input.txt
vendored
Normal file
0
src/Story/testdata/I112/input.txt
vendored
Normal file
4
src/Story/testdata/I112/story.ink
vendored
Normal file
4
src/Story/testdata/I112/story.ink
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{ 1:
|
||||||
|
- 2: x
|
||||||
|
- 3: y
|
||||||
|
}
|
||||||
0
src/Story/testdata/I112/transcript.txt
vendored
Normal file
0
src/Story/testdata/I112/transcript.txt
vendored
Normal file
0
src/Story/testdata/I113/input.txt
vendored
Normal file
0
src/Story/testdata/I113/input.txt
vendored
Normal file
20
src/Story/testdata/I113/story.ink
vendored
Normal file
20
src/Story/testdata/I113/story.ink
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
VAR x = 3
|
||||||
|
{
|
||||||
|
- x == 1: one
|
||||||
|
- x == 2: two
|
||||||
|
- else: other
|
||||||
|
}
|
||||||
|
{
|
||||||
|
- x == 1: one
|
||||||
|
- x == 2: two
|
||||||
|
- other
|
||||||
|
}
|
||||||
|
{ x == 4:
|
||||||
|
- The main clause
|
||||||
|
- else: other
|
||||||
|
}
|
||||||
|
{ x == 4:
|
||||||
|
The main clause
|
||||||
|
- else:
|
||||||
|
other
|
||||||
|
}
|
||||||
4
src/Story/testdata/I113/transcript.txt
vendored
Normal file
4
src/Story/testdata/I113/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
other
|
||||||
|
other
|
||||||
|
other
|
||||||
|
other
|
||||||
0
src/Story/testdata/I115/input.txt
vendored
Normal file
0
src/Story/testdata/I115/input.txt
vendored
Normal file
5
src/Story/testdata/I115/story.ink
vendored
Normal file
5
src/Story/testdata/I115/story.ink
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{ 3:
|
||||||
|
- 3:
|
||||||
|
- 4:
|
||||||
|
txt
|
||||||
|
}
|
||||||
0
src/Story/testdata/I115/transcript.txt
vendored
Normal file
0
src/Story/testdata/I115/transcript.txt
vendored
Normal file
0
src/Story/testdata/I116/input.txt
vendored
Normal file
0
src/Story/testdata/I116/input.txt
vendored
Normal file
4
src/Story/testdata/I116/story.ink
vendored
Normal file
4
src/Story/testdata/I116/story.ink
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
- false:
|
||||||
|
beep
|
||||||
|
}
|
||||||
0
src/Story/testdata/I116/transcript.txt
vendored
Normal file
0
src/Story/testdata/I116/transcript.txt
vendored
Normal file
0
src/Story/testdata/I117/input.txt
vendored
Normal file
0
src/Story/testdata/I117/input.txt
vendored
Normal file
7
src/Story/testdata/I117/story.ink
vendored
Normal file
7
src/Story/testdata/I117/story.ink
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
{ factorial(5) }
|
||||||
|
== function factorial(n) ==
|
||||||
|
{ n == 1:
|
||||||
|
~ return 1
|
||||||
|
- else:
|
||||||
|
~ return (n * factorial(n-1))
|
||||||
|
}
|
||||||
1
src/Story/testdata/I117/transcript.txt
vendored
Normal file
1
src/Story/testdata/I117/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
120
|
||||||
0
src/Story/testdata/I119/input.txt
vendored
Normal file
0
src/Story/testdata/I119/input.txt
vendored
Normal file
3
src/Story/testdata/I119/story.ink
vendored
Normal file
3
src/Story/testdata/I119/story.ink
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
VAR x = "Hello world 1"
|
||||||
|
{x}
|
||||||
|
Hello {"world"} 2.
|
||||||
2
src/Story/testdata/I119/transcript.txt
vendored
Normal file
2
src/Story/testdata/I119/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
Hello world 1
|
||||||
|
Hello world 2.
|
||||||
0
src/Story/testdata/I124/input.txt
vendored
Normal file
0
src/Story/testdata/I124/input.txt
vendored
Normal file
12
src/Story/testdata/I124/story.ink
vendored
Normal file
12
src/Story/testdata/I124/story.ink
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
One
|
||||||
|
Two
|
||||||
|
Three
|
||||||
|
== function func1 ==
|
||||||
|
This is a function
|
||||||
|
~ return 5
|
||||||
|
== function func2 ==
|
||||||
|
This is a function without a return value
|
||||||
|
~ return
|
||||||
|
== function add(x,y) ==
|
||||||
|
x = {x}, y = {y}
|
||||||
|
~ return x + y
|
||||||
3
src/Story/testdata/I124/transcript.txt
vendored
Normal file
3
src/Story/testdata/I124/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
One
|
||||||
|
Two
|
||||||
|
Three
|
||||||
1
src/Story/testdata/I127/input.txt
vendored
Normal file
1
src/Story/testdata/I127/input.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
||||||
10
src/Story/testdata/I127/story.ink
vendored
Normal file
10
src/Story/testdata/I127/story.ink
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
VAR testVar = 5
|
||||||
|
VAR testVar2 = 10
|
||||||
|
Hello world!
|
||||||
|
~ testVar = 15
|
||||||
|
~ testVar2 = 100
|
||||||
|
Hello world 2!
|
||||||
|
* choice
|
||||||
|
~ testVar = 25
|
||||||
|
~ testVar2 = 200
|
||||||
|
-> END
|
||||||
5
src/Story/testdata/I127/transcript.txt
vendored
Normal file
5
src/Story/testdata/I127/transcript.txt
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
Hello world!
|
||||||
|
Hello world 2!
|
||||||
|
|
||||||
|
1: choice
|
||||||
|
?> choice
|
||||||
|
|
@ -164,6 +164,7 @@ pub const InternPool = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: Revisit this. We might not need this at all.
|
||||||
pub const WorkItem = struct {
|
pub const WorkItem = struct {
|
||||||
tag: Tag,
|
tag: Tag,
|
||||||
next: ?*WorkItem = null,
|
next: ?*WorkItem = null,
|
||||||
|
|
@ -174,6 +175,7 @@ pub const WorkItem = struct {
|
||||||
pub const Tag = enum {
|
pub const Tag = enum {
|
||||||
knot,
|
knot,
|
||||||
stitch,
|
stitch,
|
||||||
|
function,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -235,6 +237,7 @@ pub const Module = struct {
|
||||||
pub const Tag = enum {
|
pub const Tag = enum {
|
||||||
knot,
|
knot,
|
||||||
stitch,
|
stitch,
|
||||||
|
function,
|
||||||
var_mut,
|
var_mut,
|
||||||
var_const,
|
var_const,
|
||||||
};
|
};
|
||||||
|
|
@ -315,6 +318,16 @@ pub const Module = struct {
|
||||||
.code_index = @enumFromInt(chunk_index),
|
.code_index = @enumFromInt(chunk_index),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
.function => {
|
||||||
|
try sema.analyzeFunction(&builder, work_unit.inst_index);
|
||||||
|
try builder.finalize();
|
||||||
|
|
||||||
|
try mod.stitches.append(gpa, .{
|
||||||
|
.knot_index = null,
|
||||||
|
.name_index = work_unit.decl_name,
|
||||||
|
.code_index = @enumFromInt(chunk_index),
|
||||||
|
});
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -412,11 +425,13 @@ pub const Module = struct {
|
||||||
pub fn setupStoryRuntime(mod: *Module, gpa: std.mem.Allocator, story: *Story) !void {
|
pub fn setupStoryRuntime(mod: *Module, gpa: std.mem.Allocator, story: *Story) !void {
|
||||||
assert(mod.errors.items.len == 0);
|
assert(mod.errors.items.len == 0);
|
||||||
const constants_len = mod.intern_pool.values.items.len;
|
const constants_len = mod.intern_pool.values.items.len;
|
||||||
|
var constants_pool: std.ArrayListUnmanaged(Value) = .empty;
|
||||||
|
try constants_pool.ensureUnusedCapacity(gpa, constants_len);
|
||||||
|
defer constants_pool.deinit(gpa);
|
||||||
|
|
||||||
try story.constants_pool.ensureUnusedCapacity(gpa, constants_len);
|
|
||||||
for (mod.intern_pool.values.items) |value| {
|
for (mod.intern_pool.values.items) |value| {
|
||||||
const obj = try mod.makeValueFromInterned(story, value);
|
const obj = try mod.makeValueFromInterned(story, value);
|
||||||
story.constants_pool.appendAssumeCapacity(obj);
|
constants_pool.appendAssumeCapacity(obj);
|
||||||
}
|
}
|
||||||
for (mod.globals.items) |global| {
|
for (mod.globals.items) |global| {
|
||||||
const key_bytes = mod.intern_pool.getStrBytes(mod.ir, global.key);
|
const key_bytes = mod.intern_pool.getStrBytes(mod.ir, global.key);
|
||||||
|
|
@ -459,8 +474,13 @@ pub const Module = struct {
|
||||||
const parent_knot_value = story.globals.get(parent_knot_name).?;
|
const parent_knot_value = story.globals.get(parent_knot_name).?;
|
||||||
const parent_knot_obj: *Object.Knot = @ptrCast(parent_knot_value.?.object);
|
const parent_knot_obj: *Object.Knot = @ptrCast(parent_knot_value.?.object);
|
||||||
try parent_knot_obj.members.put(gpa, name_bytes, &stitch_obj.base);
|
try parent_knot_obj.members.put(gpa, name_bytes, &stitch_obj.base);
|
||||||
|
} else {
|
||||||
|
const value: Value = .{ .object = &stitch_obj.base };
|
||||||
|
try story.globals.put(gpa, name_bytes, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
story.constants_pool = try constants_pool.toOwnedSlice(gpa);
|
||||||
story.string_bytes = mod.ir.string_bytes;
|
story.string_bytes = mod.ir.string_bytes;
|
||||||
mod.ir.string_bytes = &.{};
|
mod.ir.string_bytes = &.{};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -293,7 +293,10 @@ pub const Writer = struct {
|
||||||
.declaration => try self.writeDeclarationInst(w, inst),
|
.declaration => try self.writeDeclarationInst(w, inst),
|
||||||
.decl_var => try self.writeVarDeclInst(w, inst),
|
.decl_var => try self.writeVarDeclInst(w, inst),
|
||||||
.decl_knot => try self.writeKnotDeclInst(w, inst),
|
.decl_knot => try self.writeKnotDeclInst(w, inst),
|
||||||
|
// TODO: Revisit this.
|
||||||
.decl_stitch => try self.writeVarDeclInst(w, inst),
|
.decl_stitch => try self.writeVarDeclInst(w, inst),
|
||||||
|
// TODO: Revisit this.
|
||||||
|
.decl_function => try self.writeVarDeclInst(w, inst),
|
||||||
.decl_ref => try self.writeStrTokInst(w, inst),
|
.decl_ref => try self.writeStrTokInst(w, inst),
|
||||||
.condbr => try self.writeCondbrInst(w, inst),
|
.condbr => try self.writeCondbrInst(w, inst),
|
||||||
.@"break" => try self.writeBreakInst(w, inst),
|
.@"break" => try self.writeBreakInst(w, inst),
|
||||||
|
|
@ -323,6 +326,7 @@ pub const Writer = struct {
|
||||||
.content_push => try self.writeUnaryInst(w, inst),
|
.content_push => try self.writeUnaryInst(w, inst),
|
||||||
.content_flush => try self.writeUnaryInst(w, inst),
|
.content_flush => try self.writeUnaryInst(w, inst),
|
||||||
.choice_br => try self.writeChoiceBrInst(w, inst),
|
.choice_br => try self.writeChoiceBrInst(w, inst),
|
||||||
|
.ret => try self.writeUnaryInst(w, inst),
|
||||||
.implicit_ret => try self.writeUnaryInst(w, inst),
|
.implicit_ret => try self.writeUnaryInst(w, inst),
|
||||||
.call => try self.writeCallInst(w, inst, .direct),
|
.call => try self.writeCallInst(w, inst, .direct),
|
||||||
.divert => try self.writeCallInst(w, inst, .direct),
|
.divert => try self.writeCallInst(w, inst, .direct),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue