diff --git a/src/AstGen.zig b/src/AstGen.zig index f40ddf3..32fd579 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -857,7 +857,7 @@ fn inlineIfStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir. if (data.rhs) |rhs| { // TODO: Revisit this. This isn't quite correct. switch (rhs.tag) { - .content => _ = try content(&then_block, scope, rhs, false, false), + .content => _ = try content(&then_block, scope, rhs, false), inline else => |tag| @panic("Unexpected node type: " ++ @tagName(tag)), } } @@ -1047,7 +1047,9 @@ fn switchStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.In var case_block = gi.makeSubBlock(); defer case_block.unstack(); - _ = try blockStmt(&case_block, scope, case_data.rhs.?); + if (case_data.rhs) |case_block_node| { + _ = try blockStmt(&case_block, scope, case_block_node); + } if (!case_block.endsWithNoReturn()) { _ = try case_block.addBreak(.@"break", case_stmt, switch_br, .void); } @@ -1105,9 +1107,9 @@ fn content( block: *GenIr, scope: *Scope, node: *const Ast.Node, - is_last: bool, ignore_divert: bool, -) InnerError!void { +) InnerError!bool { + var suppress: bool = false; const data = node.data.content; if (data.leading_glue) { _ = try block.addUnaryNode(.content_glue, .none); @@ -1122,33 +1124,43 @@ fn content( const result = try inlineLogicExpr(block, scope, child_node); _ = try block.addUnaryNode(.content_push, result); }, - .if_stmt => _ = try ifStmt(block, scope, child_node), - .multi_if_stmt => _ = try multiIfStmt(block, scope, child_node), - .switch_stmt => _ = try switchStmt(block, scope, child_node), + .if_stmt => _ = { + _ = try ifStmt(block, scope, child_node); + suppress = true; + }, + .multi_if_stmt => { + _ = try multiIfStmt(block, scope, child_node); + suppress = true; + }, + .switch_stmt => { + _ = try switchStmt(block, scope, child_node); + suppress = true; + }, .inline_if_stmt => _ = try inlineIfStmt(block, scope, child_node), .divert_expr => _ = try divertExpr(block, scope, child_node), else => unreachable, } } - if (is_last) { - _ = try block.addUnaryNode(.content_line, .none); - if (data.trailing_glue or data.trailing_divert != null) { + if (data.trailing_glue or data.trailing_divert != null) { + _ = try block.addUnaryNode(.content_glue, .none); + } + if (!ignore_divert) { + if (data.trailing_divert) |trailing| { + _ = try divertExpr(block, scope, trailing); _ = try block.addUnaryNode(.content_glue, .none); } - if (!ignore_divert) { - if (data.trailing_divert) |trailing| { - _ = try divertExpr(block, scope, trailing); - _ = try block.addUnaryNode(.content_glue, .none); - } - } } + return suppress; } fn contentStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) !void { - const items = node.data.list.items; - for (items, 0..) |n, i| { - const is_last = i == items.len - 1; - try content(gi, scope, n, is_last, false); + var supress: bool = false; + const data = node.data.list; + for (data.items) |item_node| { + supress = try content(gi, scope, item_node, false); + } + if (!supress) { + _ = try gi.addUnaryNode(.content_line, .void); } } @@ -1186,11 +1198,11 @@ fn choiceStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!void defer block_1.unstack(); if (branch_expr.start_expr) |lhs| { - for (lhs) |n| { - if (n.data.content.trailing_divert) |trailing| { + for (lhs) |lhs_node| { + if (lhs_node.data.content.trailing_divert) |trailing| { trailing_divert = trailing; } - _ = try content(&block_1, scope, n, false, true); + _ = try content(&block_1, scope, lhs_node, true); } } @@ -1198,9 +1210,9 @@ fn choiceStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!void defer block_2.unstack(); if (branch_expr.option_expr) |mhs| { - for (mhs) |n| { - assert(n.data.content.trailing_divert == null); - _ = try content(&block_2, scope, n, false, true); + for (mhs) |mhs_node| { + assert(mhs_node.data.content.trailing_divert == null); + _ = try content(&block_2, scope, mhs_node, true); } } @@ -1208,11 +1220,11 @@ fn choiceStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!void defer block_3.unstack(); if (branch_expr.inner_expr) |rhs| { - for (rhs) |n| { - if (n.data.content.trailing_divert) |trailing| { + for (rhs) |rhs_node| { + if (rhs_node.data.content.trailing_divert) |trailing| { trailing_divert = trailing; } - _ = try content(&block_3, scope, n, false, true); + _ = try content(&block_3, scope, rhs_node, true); } } diff --git a/src/Sema.zig b/src/Sema.zig index 7f74975..51aec9f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -154,6 +154,12 @@ pub fn deinit(sema: *Sema) void { sema.* = undefined; } +pub const Block = struct { + parent_block: ?*Block, + block_inst: Ir.Inst.Index, + exit_label: usize, +}; + pub const Builder = struct { sema: *Sema, namespace: *Module.Namespace, @@ -503,9 +509,12 @@ fn irBinaryOp( fn analyzeInlineBody( sema: *Sema, builder: *Builder, + block: *Block, body: []const Ir.Inst.Index, ) !ValueInfo { - if (sema.analyzeBodyInner(builder, body, true)) |_| {} else |err| switch (err) { + if (sema.analyzeBodyInner(builder, block, body, true)) |_| { + // TODO: Do something. + } else |err| switch (err) { error.ComptimeBreak => {}, else => |e| return e, } @@ -517,6 +526,7 @@ fn analyzeInlineBody( fn irLogicalOp( sema: *Sema, builder: *Builder, + parent_block: *Block, inst: Ir.Inst.Index, is_logical_or: bool, ) InnerError!ValueInfo { @@ -535,7 +545,14 @@ fn irLogicalOp( const value = ip.getOrPutBool(false); return .{ .value = value }; } - return try sema.analyzeInlineBody(builder, body); + + var block: Block = .{ + .parent_block = parent_block, + .exit_label = try builder.addLabel(), + .block_inst = inst, + }; + + return sema.analyzeInlineBody(builder, &block, body); } const else_label = try builder.addLabel(); @@ -543,7 +560,13 @@ fn irLogicalOp( try builder.addFixup(if (is_logical_or) .jmp_t else .jmp_f, else_label); try builder.addByteOp(.pop); - const rhs = try sema.analyzeInlineBody(builder, body); + var block: Block = .{ + .parent_block = parent_block, + .exit_label = try builder.addLabel(), + .block_inst = inst, + }; + + const rhs = try sema.analyzeInlineBody(builder, &block, body); try builder.ensureLoad(rhs); builder.setLabel(else_label); return .none; @@ -552,6 +575,7 @@ fn irLogicalOp( fn irDeclRef( sema: *Sema, builder: *Builder, + block: *Block, inst: Ir.Inst.Index, inline_block: bool, ) InnerError!ValueInfo { @@ -562,7 +586,7 @@ fn irDeclRef( if (inline_block) { switch (ident.tag) { .knot, .stitch, .function => unreachable, - .var_const => return sema.resolveGlobalDecl(builder, ip_index, src_loc), + .var_const => return sema.resolveGlobalDecl(builder, block, ip_index, src_loc), .var_mut => return sema.fail( src_loc, "global variable assignments cannot refer to other variables", @@ -621,7 +645,12 @@ fn irLoad(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!ValueI return .stack; } -fn irCondBr(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!ValueInfo { +fn irCondBr( + sema: *Sema, + builder: *Builder, + block: *Block, + inst: Ir.Inst.Index, +) InnerError!ValueInfo { const data = sema.ir.instructions[@intFromEnum(inst)].data.payload; const extra = sema.ir.extraData(Ir.Inst.CondBr, data.extra_index); const then_body = sema.ir.bodySlice(extra.end, extra.data.then_body_len); @@ -633,36 +662,44 @@ fn irCondBr(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!Valu try builder.addFixup(.jmp_f, else_label); try builder.addByteOp(.pop); - _ = try analyzeBodyInner(sema, builder, then_body, false); + + _ = try analyzeBodyInner(sema, builder, block, then_body, false); try builder.addFixup(.jmp, end_label); builder.setLabel(else_label); try builder.addByteOp(.pop); - _ = try analyzeBodyInner(sema, builder, else_body, false); + _ = try analyzeBodyInner(sema, builder, block, else_body, false); builder.setLabel(end_label); return .none; } -fn irBreak(sema: *Sema, inst: Ir.Inst.Index) InnerError!void { - _ = sema; - _ = inst; -} - -fn irBreakInline(sema: *Sema, inst: Ir.Inst.Index) InnerError!ValueInfo { - const data = sema.ir.instructions[@intFromEnum(inst)].data.bin; - const rvalue_inst = sema.resolveInst(data.rhs); - return rvalue_inst; -} - -fn irBlock(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!void { +fn irBlock( + sema: *Sema, + builder: *Builder, + parent_block: *Block, + inst: Ir.Inst.Index, +) InnerError!void { const data = sema.ir.instructions[@intFromEnum(inst)].data.payload; const extra = sema.ir.extraData(Ir.Inst.Block, data.extra_index); const body = sema.ir.bodySlice(extra.end, extra.data.body_len); - _ = try analyzeBodyInner(sema, builder, body, false); + + var block: Block = .{ + .parent_block = parent_block, + .exit_label = try builder.addLabel(), + .block_inst = inst, + }; + + _ = try analyzeBodyInner(sema, builder, &block, body, false); + builder.setLabel(block.exit_label); } -fn irSwitchBr(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!void { +fn irSwitchBlock( + sema: *Sema, + builder: *Builder, + parent_block: *Block, + inst: Ir.Inst.Index, +) InnerError!void { const gpa = sema.gpa; const data = sema.ir.instructions[@intFromEnum(inst)].data.payload; const extra = sema.ir.extraData(Ir.Inst.SwitchBr, data.extra_index); @@ -675,7 +712,12 @@ fn irSwitchBr(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!vo const condition = sema.resolveInst(extra.data.operand); if (condition != .none) try builder.ensureLoad(condition); - const exit_label = try builder.addLabel(); + var switch_block: Block = .{ + .parent_block = parent_block, + .exit_label = try builder.addLabel(), + .block_inst = inst, + }; + const cmp_var = builder.addStackSlot(); try builder.addConstOp(.store, cmp_var); @@ -701,8 +743,8 @@ fn irSwitchBr(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!vo builder.setLabel(label_index); try builder.addByteOp(.pop); - _ = try analyzeBodyInner(sema, builder, case_body, false); - try builder.addFixup(.jmp, exit_label); + _ = try analyzeBodyInner(sema, builder, &switch_block, case_body, false); + try builder.addFixup(.jmp, switch_block.exit_label); } const else_body = sema.ir.bodySlice( @@ -711,8 +753,34 @@ fn irSwitchBr(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!vo ); builder.setLabel(else_label); - _ = try analyzeBodyInner(sema, builder, else_body, false); - builder.setLabel(exit_label); + _ = try analyzeBodyInner(sema, builder, &switch_block, else_body, false); + builder.setLabel(switch_block.exit_label); +} + +fn irBreak( + sema: *Sema, + builder: *Builder, + parent_block: *Block, + inst: Ir.Inst.Index, +) InnerError!void { + const data = sema.ir.instructions[@intFromEnum(inst)].data.payload; + const extra = sema.ir.extraData(Ir.Inst.Break, data.extra_index).data; + const target = extra.block_inst; + + var current_block: ?*Block = parent_block; + while (current_block) |block| : (current_block = block.parent_block) { + if (block.block_inst == target) { + try builder.addFixup(.jmp, block.exit_label); + return; + } + } + return error.InvalidBreakTarget; +} + +fn irBreakInline(sema: *Sema, inst: Ir.Inst.Index) InnerError!ValueInfo { + const data = sema.ir.instructions[@intFromEnum(inst)].data.bin; + const rvalue_inst = sema.resolveInst(data.rhs); + return rvalue_inst; } fn irContentPush(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!void { @@ -723,7 +791,12 @@ fn irContentPush(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError try builder.addByteOp(.stream_push); } -fn irChoiceBr(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!void { +fn irChoiceBr( + sema: *Sema, + builder: *Builder, + block: *Block, + inst: Ir.Inst.Index, +) InnerError!void { const gpa = sema.gpa; const data = sema.ir.instructions[@intFromEnum(inst)].data.payload; const choice_extra = sema.ir.extraData(Ir.Inst.ChoiceBr, data.extra_index); @@ -748,8 +821,8 @@ fn irChoiceBr(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!vo branch_labels.appendAssumeCapacity(case_label); try builder.addByteOp(.stream_mark); - _ = try analyzeBodyInner(sema, builder, lhs_slice, false); - _ = try analyzeBodyInner(sema, builder, mhs_slice, false); + _ = try analyzeBodyInner(sema, builder, block, lhs_slice, false); + _ = try analyzeBodyInner(sema, builder, block, mhs_slice, false); try builder.addFixupAbsolute(.br_push, case_label); } @@ -779,10 +852,10 @@ fn irChoiceBr(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!vo builder.setLabel(label); - _ = try analyzeBodyInner(sema, builder, lhs_slice, false); - _ = try analyzeBodyInner(sema, builder, rhs_slice, false); + _ = try analyzeBodyInner(sema, builder, block, lhs_slice, false); + _ = try analyzeBodyInner(sema, builder, block, rhs_slice, false); try builder.addByteOp(.stream_line); - _ = try analyzeBodyInner(sema, builder, body_slice, false); + _ = try analyzeBodyInner(sema, builder, block, body_slice, false); } } @@ -804,6 +877,7 @@ fn irImplicitRet(_: *Sema, builder: *Builder, _: Ir.Inst.Index) InnerError!void }, .function => { try builder.addByteOp(.stream_glue); + try builder.addByteOp(.nil); try builder.addByteOp(.ret); }, } @@ -812,6 +886,7 @@ fn irImplicitRet(_: *Sema, builder: *Builder, _: Ir.Inst.Index) InnerError!void fn irCall( sema: *Sema, builder: *Builder, + block: *Block, inst: Ir.Inst.Index, comptime kind: enum { direct, field }, ) !ValueInfo { @@ -853,7 +928,7 @@ fn irCall( 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 sema.analyzeInlineBody(builder, @ptrCast(arg_body)); + const arg_value = try sema.analyzeInlineBody(builder, block, @ptrCast(arg_body)); if (arg_value != .none) try builder.ensureLoad(arg_value); } try builder.addConstOp(.call, @intCast(args_len)); @@ -863,6 +938,7 @@ fn irCall( fn irDivert( sema: *Sema, builder: *Builder, + block: *Block, inst: Ir.Inst.Index, comptime kind: enum { direct, field }, ) !void { @@ -874,6 +950,7 @@ fn irDivert( 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); @@ -904,7 +981,7 @@ fn irDivert( 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); + const arg_value = try analyzeBodyInner(sema, builder, block, @ptrCast(arg_body), false); if (arg_value != .none) try builder.ensureLoad(arg_value); } try builder.addConstOp(.divert, @intCast(args_len)); @@ -987,6 +1064,7 @@ fn analyzeDivertTarget( fn analyzeBodyInner( sema: *Sema, builder: *Builder, + block: *Block, body: []const Ir.Inst.Index, inline_block: bool, ) InnerError!ValueInfo { @@ -1016,15 +1094,15 @@ fn analyzeBodyInner( .mod => try irBinaryOp(sema, builder, inst, .mod), .neg => try irUnaryOp(sema, builder, inst, .neg), .not => try irUnaryOp(sema, builder, inst, .not), - .bool_br_and => try irLogicalOp(sema, builder, inst, false), - .bool_br_or => try irLogicalOp(sema, builder, inst, true), + .bool_br_and => try irLogicalOp(sema, builder, block, inst, false), + .bool_br_or => try irLogicalOp(sema, builder, block, inst, true), .cmp_eq => try irBinaryOp(sema, builder, inst, .cmp_eq), .cmp_neq => try irBinaryOp(sema, builder, inst, .cmp_neq), .cmp_lt => try irBinaryOp(sema, builder, inst, .cmp_lt), .cmp_lte => try irBinaryOp(sema, builder, inst, .cmp_lte), .cmp_gt => try irBinaryOp(sema, builder, inst, .cmp_gt), .cmp_gte => try irBinaryOp(sema, builder, inst, .cmp_gte), - .decl_ref => try irDeclRef(sema, builder, inst, inline_block), + .decl_ref => try irDeclRef(sema, builder, block, inst, inline_block), .ret => { try irRet(sema, builder, inst); continue; @@ -1033,9 +1111,9 @@ fn analyzeBodyInner( try irImplicitRet(sema, builder, inst); continue; }, - .condbr => try irCondBr(sema, builder, inst), + .condbr => try irCondBr(sema, builder, block, inst), .@"break" => { - try irBreak(sema, inst); + try irBreak(sema, builder, block, inst); continue; }, .break_inline => { @@ -1043,7 +1121,7 @@ fn analyzeBodyInner( return error.ComptimeBreak; }, .block => { - try irBlock(sema, builder, inst); + try irBlock(sema, builder, block, inst); continue; }, .content_push => { @@ -1059,21 +1137,21 @@ fn analyzeBodyInner( continue; }, .choice_br => { - try irChoiceBr(sema, builder, inst); + try irChoiceBr(sema, builder, block, inst); continue; }, .switch_br => { - try irSwitchBr(sema, builder, inst); + try irSwitchBlock(sema, builder, block, inst); continue; }, - .call => try irCall(sema, builder, inst, .direct), - .field_call => try irCall(sema, builder, inst, .field), + .call => try irCall(sema, builder, block, inst, .direct), + .field_call => try irCall(sema, builder, block, inst, .field), .divert => { - try irDivert(sema, builder, inst, .direct); + try irDivert(sema, builder, block, inst, .direct); continue; }, .field_divert => { - try irDivert(sema, builder, inst, .field); + try irDivert(sema, builder, block, inst, .field); continue; }, .field_ptr => try irFieldPtr(sema, builder, inst), @@ -1095,7 +1173,13 @@ pub fn analyzeStitch( const data = sema.ir.instructions[@intFromEnum(inst)].data.payload; const extra = sema.ir.extraData(Ir.Inst.Stitch, data.extra_index); const body = sema.ir.bodySlice(extra.end, extra.data.body_len); - _ = try analyzeBodyInner(sema, builder, body, false); + + var block: Block = .{ + .parent_block = null, + .exit_label = try builder.addLabel(), + .block_inst = inst, + }; + _ = try analyzeBodyInner(sema, builder, &block, body, false); } // TODO: No diverts allowed. @@ -1107,7 +1191,13 @@ pub fn analyzeFunction( 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); + + var block: Block = .{ + .parent_block = null, + .exit_label = try builder.addLabel(), + .block_inst = inst, + }; + _ = try analyzeBodyInner(sema, builder, &block, body, false); } // TODO: No return allowed. @@ -1119,7 +1209,13 @@ pub fn analyzeKnot( const data = sema.ir.instructions[@intFromEnum(inst)].data.payload; const extra = sema.ir.extraData(Ir.Inst.Knot, data.extra_index); const body = sema.ir.bodySlice(extra.end, extra.data.body_len); - _ = try analyzeBodyInner(sema, builder, body, false); + + var block: Block = .{ + .parent_block = null, + .exit_label = try builder.addLabel(), + .block_inst = inst, + }; + _ = try analyzeBodyInner(sema, builder, &block, body, false); } fn analyzeNestedDecl( @@ -1255,6 +1351,7 @@ fn scanTopLevelDecl( fn resolveGlobalDecl( sema: *Sema, builder: *Builder, + block: *Block, decl_name: InternPool.Index, src_loc: SrcLoc, ) InnerError!ValueInfo { @@ -1266,7 +1363,7 @@ fn resolveGlobalDecl( const body = sema.ir.bodySlice(extra.end, extra.data.body_len); entry.resolution = .in_progress; - const val = try sema.analyzeInlineBody(builder, body); + const val = try sema.analyzeInlineBody(builder, block, body); entry.resolution = .{ .resolved = val }; return val; }, @@ -1301,11 +1398,18 @@ pub fn scanTopLevelDecls( while (iter.next()) |entry| { const key = entry.key_ptr.*; const value = entry.value_ptr.*; + + var block: Block = .{ + .parent_block = null, + .block_inst = @enumFromInt(0), + .exit_label = 0, + }; + switch (value.tag) { .var_mut, .var_const => { // TODO: Set a proper source offset for this. const src_loc: SrcLoc = .{ .src_offset = 0 }; - const result = try sema.resolveGlobalDecl(&builder, key, src_loc); + const result = try sema.resolveGlobalDecl(&builder, &block, key, src_loc); const initial_value = sema.resolveValue(result).?; try sema.module.globals.append(gpa, .{ .key = key, diff --git a/src/Story.zig b/src/Story.zig index 9b6bfc4..a865996 100644 --- a/src/Story.zig +++ b/src/Story.zig @@ -43,6 +43,7 @@ pub const Opcode = enum(u8) { exit, done, ret, + nil, /// Pop a value off the stack, discarding it. pop, /// Push an object representing the boolean value of "true" to the stack. @@ -105,8 +106,6 @@ pub const CallFrame = struct { /// Instruction pointer. ip: usize, bp: usize, - /// Output stream base. - output_base: usize, }; pub const Value = union(enum) { @@ -450,10 +449,9 @@ fn call(vm: *Story, knot: *Object.Knot, args_count: u8) !void { .callee = knot, .ip = 0, .bp = vm.stack_top - args_count - 1, - .output_base = vm.output_buffer.items.len, }; vm.call_stack_top += 1; - vm.stack_top += knot.code.locals_count; + vm.stack_top += knot.code.stack_size; } fn divertValue(vm: *Story, value: Value, args_count: u8) !void { @@ -477,7 +475,6 @@ fn divert(vm: *Story, knot: *Object.Knot, args_count: u8) !void { .callee = knot, .ip = 0, .bp = vm.stack_top - args_count - 1, - .output_base = vm.output_buffer.items.len, }; vm.stack_top += knot.code.locals_count; @@ -510,32 +507,18 @@ fn step(vm: *Story) !StepSignal { switch (code[frame.ip]) { .exit => return .exit, .done => return .done, + .nil => { + try pushStack(vm, .nil); + frame.ip += 1; + }, .ret => { if (vm.call_stack_top == 0) return error.UnexpectedReturn; vm.call_stack_top -= 1; - const resolved_stream: ?Value = if (vm.output_buffer.items.len > frame.output_base) blk: { - const frame_output = vm.output_buffer.items[frame.output_base..]; - defer vm.output_buffer.shrinkRetainingCapacity(frame.output_base); - - const str_bytes = try resolveOutputStream(vm, arena, frame_output); - const str_object = try Object.String.create(vm, .{ .bytes = str_bytes }); - break :blk .{ .object = &str_object.base }; - } else blk: { - break :blk null; - }; - const return_value = if (resolved_stream) |stream| blk: { - if (frame.bp + frame.callee.code.stack_size + 1 < vm.stack_top) { - try vm.output_buffer.append(gpa, .{ .value = stream }); - break :blk popStack(vm).?; - } - break :blk stream; - } else if (frame.bp + frame.callee.code.stack_size + 1 < vm.stack_top) blk: { - break :blk popStack(vm).?; - } else .nil; + const value = popStack(vm).?; vm.stack_top = frame.bp; - vm.stack[vm.stack_top] = return_value; + vm.stack[vm.stack_top] = value; vm.stack_top += 1; frame = &vm.call_stack[vm.call_stack_top - 1]; }, @@ -726,7 +709,10 @@ fn step(vm: *Story) !StepSignal { .stream_push => { // TODO: Make this more strict. if (popStack(vm)) |value| { - try vm.output_buffer.append(gpa, .{ .value = value }); + switch (value) { + .nil => {}, + else => try vm.output_buffer.append(gpa, .{ .value = value }), + } } frame.ip += 1; }, @@ -780,8 +766,8 @@ fn resolveOutputStream( gpa: std.mem.Allocator, stream: []const OutputCommand, ) ![]const u8 { - var glue_active = false; var pending_newline = false; + var pending_glue = false; var result: std.ArrayListUnmanaged(u8) = .empty; defer result.deinit(gpa); @@ -792,16 +778,21 @@ fn resolveOutputStream( try result.append(gpa, '\n'); pending_newline = false; } - glue_active = false; - const str = try Object.String.fromValue(story, value); - try result.appendSlice(gpa, str.toSlice()); + pending_glue = false; + switch (value) { + .nil => {}, + else => { + const str = try Object.String.fromValue(story, value); + try result.appendSlice(gpa, str.toSlice()); + }, + } }, .line => { - if (!glue_active) pending_newline = true; + if (!pending_glue) pending_newline = true; }, .glue => { pending_newline = false; - glue_active = true; + pending_glue = true; }, } } diff --git a/src/Story/Dumper.zig b/src/Story/Dumper.zig index 5c95347..60ff771 100644 --- a/src/Story/Dumper.zig +++ b/src/Story/Dumper.zig @@ -126,6 +126,7 @@ pub fn dumpInst( .exit => return self.dumpSimpleInst(w, offset, op), .done => return self.dumpSimpleInst(w, offset, op), .ret => return self.dumpSimpleInst(w, offset, op), + .nil => return self.dumpSimpleInst(w, offset, op), .pop => return self.dumpSimpleInst(w, offset, op), .true => return self.dumpSimpleInst(w, offset, op), .false => return self.dumpSimpleInst(w, offset, op), @@ -359,10 +360,9 @@ pub fn trace(vm: *Story, writer: *std.Io.Writer, frame: *Story.CallFrame) !void } try writer.writeAll("]\n"); + try writer.print("\tOutput => values=[", .{}); - try writer.print("\tOutput => offset_base={d}, values=[", .{frame.output_base}); - - const output_window = vm.output_buffer.items[frame.output_base..]; + const output_window = vm.output_buffer.items[0..]; for (output_window, 0..) |slot, i| { if (i > 0) try writer.writeAll(", "); try writer.print("{any}", .{slot}); diff --git a/src/Story/runtime_tests.zig b/src/Story/runtime_tests.zig index 99af845..ee82555 100644 --- a/src/Story/runtime_tests.zig +++ b/src/Story/runtime_tests.zig @@ -119,10 +119,22 @@ test "fixture - I042 (Weave options)" { try testRuntimeFixture("I042"); } +test "fixture - I044 (#)" { + try testRuntimeFixture("I044"); +} + +test "fixture - I045 (#)" { + try testRuntimeFixture("I045"); +} + test "fixture - I046 (Left right glue matching)" { try testRuntimeFixture("I046"); } +test "fixture - I047 (#)" { + try testRuntimeFixture("I047"); +} + test "fixture - I048 (Simple glue)" { try testRuntimeFixture("I048"); } @@ -168,6 +180,10 @@ test "fixture - I087 (Non text in choice inner content)" { try testRuntimeFixture("I087"); } +test "fixture - I094 (# content)" { + try testRuntimeFixture("I094"); +} + test "fixture - I095 (Multiline logic with glue)" { try testRuntimeFixture("I095"); } @@ -176,6 +192,22 @@ test "fixture - I097 (Logic lines with newlines)" { try testRuntimeFixture("I097"); } +test "fixture - I112 (#)" { + try testRuntimeFixture("I112"); +} + +//test "fixture - I113 (#)" { +// try testRuntimeFixture("I113"); +//} + +test "fixture - I115 (#)" { + try testRuntimeFixture("I115"); +} + +test "fixture - I116 (#)" { + try testRuntimeFixture("I116"); +} + test "fixture - I117 (Factorial recursive)" { try testRuntimeFixture("I117"); } @@ -196,6 +228,10 @@ test "fixture - I124 (Evaluating ink functions from game 2)" { try testRuntimeFixture("I124"); } +test "fixture - I127 (#)" { + try testRuntimeFixture("I127"); +} + test "fixture - I133 (Float printing precision)" { try testRuntimeFixture("I133"); } diff --git a/src/compile.zig b/src/compile.zig index 9f623e8..46d76d9 100644 --- a/src/compile.zig +++ b/src/compile.zig @@ -355,6 +355,7 @@ pub const Module = struct { try tree.render(gpa, w, .{ .use_color = options.dump_use_color, }); + try w.flush(); } }