Skip to content

Commit

Permalink
feat(script): add peekInt and use it in tests (#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
tdelabro authored Sep 18, 2024
1 parent 8e63031 commit 4899c4f
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 88 deletions.
135 changes: 67 additions & 68 deletions src/script/engine.zig
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ test "Script execution - OP_1 OP_1 OP_EQUAL" {
}

// Ensure the stack is empty after popping the result
try std.testing.expectEqual(@as(usize, 0), engine.stack.len());
try std.testing.expectEqual(0, engine.stack.len());
}

test "Script execution - OP_RETURN" {
Expand All @@ -513,7 +513,7 @@ test "Script execution - OP_RETURN" {
try std.testing.expectError(error.EarlyReturn, engine.execute());

// Check if the stack has one item (OP_1 should have been executed)
try std.testing.expectEqual(@as(usize, 1), engine.stack.len());
try std.testing.expectEqual(1, engine.stack.len());

// Check the item on the stack (should be 1)
{
Expand All @@ -536,7 +536,7 @@ test "Script execution - OP_1 OP_1 OP_1 OP_2Drop" {
try engine.execute();

// Ensure the stack is empty after popping the result
try std.testing.expectEqual(@as(usize, 1), engine.stack.len());
try std.testing.expectEqual(1, engine.stack.len());
}

test "Script execution - OP_1 OP_2 OP_2Dup" {
Expand All @@ -550,17 +550,17 @@ test "Script execution - OP_1 OP_2 OP_2Dup" {
defer engine.deinit();

try engine.execute();
const element0 = try engine.stack.peek(0);
const element1 = try engine.stack.peek(1);
const element2 = try engine.stack.peek(2);
const element3 = try engine.stack.peek(3);
const element0 = try engine.stack.peekInt(0);
const element1 = try engine.stack.peekInt(1);
const element2 = try engine.stack.peekInt(2);
const element3 = try engine.stack.peekInt(3);

// Ensure the stack is empty after popping the result
try std.testing.expectEqual(@as(usize, 4), engine.stack.len());
try std.testing.expectEqualSlices(u8, &[_]u8{2}, element0);
try std.testing.expectEqualSlices(u8, &[_]u8{1}, element1);
try std.testing.expectEqualSlices(u8, &[_]u8{2}, element2);
try std.testing.expectEqualSlices(u8, &[_]u8{1}, element3);
try std.testing.expectEqual(4, engine.stack.len());
try std.testing.expectEqual(2, element0);
try std.testing.expectEqual(1, element1);
try std.testing.expectEqual(2, element2);
try std.testing.expectEqual(1, element3);
}

test "Script execution - OP_1 OP_2 OP_3 OP_4 OP_3Dup" {
Expand All @@ -574,23 +574,23 @@ test "Script execution - OP_1 OP_2 OP_3 OP_4 OP_3Dup" {
defer engine.deinit();

try engine.execute();
const element0 = try engine.stack.peek(0);
const element1 = try engine.stack.peek(1);
const element2 = try engine.stack.peek(2);
const element3 = try engine.stack.peek(3);
const element4 = try engine.stack.peek(4);
const element5 = try engine.stack.peek(5);
const element6 = try engine.stack.peek(6);
const element0 = try engine.stack.peekInt(0);
const element1 = try engine.stack.peekInt(1);
const element2 = try engine.stack.peekInt(2);
const element3 = try engine.stack.peekInt(3);
const element4 = try engine.stack.peekInt(4);
const element5 = try engine.stack.peekInt(5);
const element6 = try engine.stack.peekInt(6);

// Ensure the stack is empty after popping the result
try std.testing.expectEqual(@as(usize, 7), engine.stack.len());
try std.testing.expectEqualSlices(u8, &[_]u8{4}, element0);
try std.testing.expectEqualSlices(u8, &[_]u8{3}, element1);
try std.testing.expectEqualSlices(u8, &[_]u8{2}, element2);
try std.testing.expectEqualSlices(u8, &[_]u8{4}, element3);
try std.testing.expectEqualSlices(u8, &[_]u8{3}, element4);
try std.testing.expectEqualSlices(u8, &[_]u8{2}, element5);
try std.testing.expectEqualSlices(u8, &[_]u8{1}, element6);
try std.testing.expectEqual(7, engine.stack.len());
try std.testing.expectEqual(4, element0);
try std.testing.expectEqual(3, element1);
try std.testing.expectEqual(2, element2);
try std.testing.expectEqual(4, element3);
try std.testing.expectEqual(3, element4);
try std.testing.expectEqual(2, element5);
try std.testing.expectEqual(1, element6);
}

test "Script execution - OP_1 OP_2 OP_IFDUP" {
Expand All @@ -604,12 +604,12 @@ test "Script execution - OP_1 OP_2 OP_IFDUP" {
defer engine.deinit();

try engine.execute();
const element0 = try engine.stack.peek(0);
const element1 = try engine.stack.peek(1);
const element0 = try engine.stack.peekInt(0);
const element1 = try engine.stack.peekInt(1);

try std.testing.expectEqual(@as(usize, 3), engine.stack.len());
try std.testing.expectEqualSlices(u8, &[_]u8{2}, element0);
try std.testing.expectEqualSlices(u8, &[_]u8{2}, element1);
try std.testing.expectEqual(3, engine.stack.len());
try std.testing.expectEqual(2, element0);
try std.testing.expectEqual(2, element1);
}

test "Script execution - OP_1 OP_2 OP_DEPTH" {
Expand All @@ -623,12 +623,12 @@ test "Script execution - OP_1 OP_2 OP_DEPTH" {
defer engine.deinit();

try engine.execute();
const element0 = try engine.stack.peek(0);
const element1 = try engine.stack.peek(1);
const element0 = try engine.stack.peekInt(0);
const element1 = try engine.stack.peekInt(1);

try std.testing.expectEqual(@as(usize, 3), engine.stack.len());
try std.testing.expectEqualSlices(u8, &[_]u8{2}, element0);
try std.testing.expectEqualSlices(u8, &[_]u8{2}, element1);
try std.testing.expectEqual(3, engine.stack.len());
try std.testing.expectEqual(2, element0);
try std.testing.expectEqual(2, element1);
}

test "Script execution - OP_1 OP_2 OP_DROP" {
Expand All @@ -642,11 +642,11 @@ test "Script execution - OP_1 OP_2 OP_DROP" {
defer engine.deinit();

try engine.execute();
try std.testing.expectEqual(@as(usize, 1), engine.stack.len());
try std.testing.expectEqual(1, engine.stack.len());

const element0 = try engine.stack.peek(0);
const element0 = try engine.stack.peekInt(0);

try std.testing.expectEqualSlices(u8, &[_]u8{1}, element0);
try std.testing.expectEqual(1, element0);
}

test "Script execution - OP_DISABLED" {
Expand Down Expand Up @@ -688,14 +688,14 @@ test "Script execution OP_1 OP_2 OP_3 OP_NIP" {
defer engine.deinit();

try engine.execute();
try std.testing.expectEqual(@as(usize, 2), engine.stack.len());
try std.testing.expectEqual(2, engine.stack.len());

const element0 = try engine.stack.peek(0);
const element1 = try engine.stack.peek(1);
const element0 = try engine.stack.peekInt(0);
const element1 = try engine.stack.peekInt(1);

// Ensure the stack is empty after popping the result
try std.testing.expectEqualSlices(u8, &[_]u8{3}, element0);
try std.testing.expectEqualSlices(u8, &[_]u8{1}, element1);
try std.testing.expectEqual(3, element0);
try std.testing.expectEqual(1, element1);
}

test "Script execution OP_1 OP_2 OP_3 OP_OVER" {
Expand All @@ -709,13 +709,13 @@ test "Script execution OP_1 OP_2 OP_3 OP_OVER" {
defer engine.deinit();

try engine.execute();
try std.testing.expectEqual(@as(usize, 4), engine.stack.len());
try std.testing.expectEqual(4, engine.stack.len());

const element0 = try engine.stack.peek(0);
const element1 = try engine.stack.peek(1);
const element0 = try engine.stack.peekInt(0);
const element1 = try engine.stack.peekInt(1);

try std.testing.expectEqualSlices(u8, &[_]u8{2}, element0);
try std.testing.expectEqualSlices(u8, &[_]u8{3}, element1);
try std.testing.expectEqual(2, element0);
try std.testing.expectEqual(3, element1);
}

test "Script execution OP_1 OP_2 OP_3 OP_SWAP" {
Expand All @@ -729,13 +729,13 @@ test "Script execution OP_1 OP_2 OP_3 OP_SWAP" {
defer engine.deinit();

try engine.execute();
try std.testing.expectEqual(@as(usize, 3), engine.stack.len());
try std.testing.expectEqual(3, engine.stack.len());

const element0 = try engine.stack.peek(0);
const element1 = try engine.stack.peek(1);
const element0 = try engine.stack.peekInt(0);
const element1 = try engine.stack.peekInt(1);

try std.testing.expectEqualSlices(u8, &[_]u8{2}, element0);
try std.testing.expectEqualSlices(u8, &[_]u8{3}, element1);
try std.testing.expectEqual(2, element0);
try std.testing.expectEqual(3, element1);
}

test "Script execution OP_1 OP_2 OP_3 OP_TUCK" {
Expand All @@ -749,15 +749,15 @@ test "Script execution OP_1 OP_2 OP_3 OP_TUCK" {
defer engine.deinit();

try engine.execute();
try std.testing.expectEqual(@as(usize, 4), engine.stack.len());
try std.testing.expectEqual(4, engine.stack.len());

const element0 = try engine.stack.peek(0);
const element1 = try engine.stack.peek(1);
const element2 = try engine.stack.peek(2);
const element0 = try engine.stack.peekInt(0);
const element1 = try engine.stack.peekInt(1);
const element2 = try engine.stack.peekInt(2);

try std.testing.expectEqualSlices(u8, &[_]u8{2}, element0);
try std.testing.expectEqualSlices(u8, &[_]u8{3}, element1);
try std.testing.expectEqualSlices(u8, &[_]u8{2}, element2);
try std.testing.expectEqual(2, element0);
try std.testing.expectEqual(3, element1);
try std.testing.expectEqual(2, element2);
}

test "Script execution OP_1 OP_2 OP_3 OP_SIZE" {
Expand All @@ -771,12 +771,11 @@ test "Script execution OP_1 OP_2 OP_3 OP_SIZE" {
defer engine.deinit();

try engine.execute();
try std.testing.expectEqual(@as(usize, 4), engine.stack.len());
try std.testing.expectEqual(4, engine.stack.len());

const element0 = &[_]i64{try engine.stack.popInt()};
const element1 = try engine.stack.peek(0);
const checker = &[_]i64{1};
const element0 = try engine.stack.popInt();
const element1 = try engine.stack.peekInt(0);

try std.testing.expectEqualSlices(i64, checker, element0);
try std.testing.expectEqualSlices(u8, &[_]u8{3}, element1);
try std.testing.expectEqual(1, element0);
try std.testing.expectEqual(3, element1);
}
47 changes: 27 additions & 20 deletions src/script/stack.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const testing = std.testing;
const native_endian = @import("builtin").target.cpu.arch.endian();

/// Errors that can occur during stack operations
pub const StackError = error{
Expand Down Expand Up @@ -70,10 +71,7 @@ pub const Stack = struct {
/// # Returns
/// - `StackError` if out of memory
pub fn pushInt(self: *Stack, value: i64) StackError!void {
var buffer: [8]u8 = undefined;
const bytes = std.mem.asBytes(&value);
@memcpy(&buffer, bytes);
try self.pushByteArray(&buffer);
try self.pushByteArray(std.mem.asBytes(&value));
}

/// Push an item onto the stack(does not create copy of item)
Expand Down Expand Up @@ -101,9 +99,7 @@ pub const Stack = struct {

if (value.len > 8) return StackError.InvalidValue;

var buffer: [8]u8 = undefined;
std.mem.copyBackwards(u8, buffer[0..value.len], value);
return std.mem.bytesToValue(i64, &buffer);
return std.mem.readVarInt(i64, value, native_endian);
}

/// Pop a boolean value from the stack
Expand Down Expand Up @@ -150,6 +146,17 @@ pub const Stack = struct {
return self.items.items[self.items.items.len - 1 - index];
}

pub fn peekInt(self: *Stack, index: usize) StackError!i64 {
if (index >= self.items.items.len) {
return StackError.StackUnderflow;
}

const elem = self.items.items[self.items.items.len - 1 - index];
if (elem.len > 8) return StackError.InvalidValue;

return std.mem.readVarInt(i64, elem, native_endian);
}

/// Get the number of items in the stack
///
/// # Returns
Expand All @@ -166,10 +173,10 @@ test "Stack basic operations" {

// Test push and len
try stack.pushByteArray(&[_]u8{1});
try testing.expectEqual(@as(usize, 1), stack.len());
try testing.expectEqual(1, stack.len());

try stack.pushByteArray(&[_]u8{ 2, 3 });
try testing.expectEqual(@as(usize, 2), stack.len());
try testing.expectEqual(2, stack.len());

// Test peek
const top = try stack.peek(0);
Expand All @@ -181,7 +188,7 @@ test "Stack basic operations" {
defer allocator.free(popped);
try testing.expectEqualSlices(u8, &[_]u8{ 2, 3 }, popped);
}
try testing.expectEqual(@as(usize, 1), stack.len());
try testing.expectEqual(1, stack.len());

// Test underflow
try testing.expectError(StackError.StackUnderflow, stack.peek(1));
Expand Down Expand Up @@ -216,7 +223,7 @@ test "Stack memory management" {
}

// Stack should be empty now
try testing.expectEqual(@as(usize, 0), stack.len());
try testing.expectEqual(0, stack.len());
}
// The stack should be fully deallocated here
}
Expand All @@ -232,12 +239,12 @@ test "Stack push and peek multiple items" {
try stack.pushByteArray(&[_]u8{3});

// Peek at different indices
try testing.expectEqualSlices(u8, &[_]u8{3}, try stack.peek(0));
try testing.expectEqualSlices(u8, &[_]u8{2}, try stack.peek(1));
try testing.expectEqualSlices(u8, &[_]u8{1}, try stack.peek(2));
try testing.expectEqual(3, try stack.peekInt(0));
try testing.expectEqual(2, try stack.peekInt(1));
try testing.expectEqual(1, try stack.peekInt(2));

// Verify length
try testing.expectEqual(@as(usize, 3), stack.len());
try testing.expectEqual(3, stack.len());

// Attempt to peek beyond stack size
try testing.expectError(StackError.StackUnderflow, stack.peek(3));
Expand All @@ -252,7 +259,7 @@ test "Stack push empty slice" {
try stack.pushByteArray(&[_]u8{});

// Verify it was pushed correctly
try testing.expectEqual(@as(usize, 1), stack.len());
try testing.expectEqual(1, stack.len());
try testing.expectEqualSlices(u8, &[_]u8{}, try stack.peek(0));

// Pop and verify
Expand All @@ -273,7 +280,7 @@ test "Stack out of memory simulation" {
try testing.expectError(StackError.OutOfMemory, stack.pushByteArray(&[_]u8{1}));

// Verify the stack is still empty
try testing.expectEqual(@as(usize, 0), stack.len());
try testing.expectEqual(0, stack.len());
}

test "Stack pushInt and popInt" {
Expand All @@ -283,15 +290,15 @@ test "Stack pushInt and popInt" {

// Test pushing and popping positive integers
try stack.pushInt(42);
try testing.expectEqual(@as(i64, 42), try stack.popInt());
try testing.expectEqual(42, try stack.popInt());

// Test pushing and popping negative integers
try stack.pushInt(-123);
try testing.expectEqual(@as(i64, -123), try stack.popInt());
try testing.expectEqual(-123, try stack.popInt());

// Test pushing and popping zero
try stack.pushInt(0);
try testing.expectEqual(@as(i64, 0), try stack.popInt());
try testing.expectEqual(0, try stack.popInt());

// Test pushing and popping large integers
try stack.pushInt(std.math.maxInt(i64));
Expand Down

0 comments on commit 4899c4f

Please sign in to comment.