Completed integration of list.drop_at into interpreter, working on record arg handling to finish list.sublist

This commit is contained in:
Edwin Santos 2025-11-29 16:52:35 -05:00
parent 4555f2060c
commit c3b87ff8cf
7 changed files with 231 additions and 0 deletions

View file

@ -166,6 +166,12 @@ fn replaceStrIsEmptyWithLowLevel(env: *ModuleEnv) !std.ArrayList(CIR.Def.Idx) {
if (env.common.findIdent("list_get_unsafe")) |list_get_unsafe_ident| {
try low_level_map.put(list_get_unsafe_ident, .list_get_unsafe);
}
if (env.common.findIdent("Builtin.List.drop_at")) |list_drop_at_ident| {
try low_level_map.put(list_drop_at_ident, .list_drop_at);
}
if (env.common.findIdent("Builtin.List.sublist")) |list_sublist_ident| {
try low_level_map.put(list_sublist_ident, .list_sublist);
}
if (env.common.findIdent("Builtin.Bool.is_eq")) |bool_is_eq_ident| {
try low_level_map.put(bool_is_eq_ident, .bool_is_eq);
}

View file

@ -132,6 +132,9 @@ Builtin :: [].{
True
}
drop_at : List(a), U64 -> List(a)
sublist : List(a), {start : U64, len : U64} -> List(a)
}
Bool := [False, True].{

View file

@ -475,6 +475,8 @@ pub const Expr = union(enum) {
list_concat,
list_with_capacity,
list_sort_with,
list_drop_at,
list_sublist,
// Bool operations
bool_is_eq,

View file

@ -163,6 +163,9 @@ pub const CommonIdents = extern struct {
// from_utf8 error payload fields (BadUtf8 record)
problem: Ident.Idx,
index: Ident.Idx,
// sublist argument payload fields
// sublist_start: Ident.Idx,
// sublist_len: Ident.Idx,
/// Insert all well-known identifiers into a CommonEnv.
/// Use this when creating a fresh ModuleEnv from scratch.
@ -228,6 +231,9 @@ pub const CommonIdents = extern struct {
// from_utf8 error payload fields (BadUtf8 record)
.problem = try common.insertIdent(gpa, Ident.for_text("problem")),
.index = try common.insertIdent(gpa, Ident.for_text("index")),
// sublist argument payload fields
// .sublist_start = try common.insertIdent(gpa, Ident.for_text("start")),
// .sublist_len = try common.insertIdent(gpa, Ident.for_text("len")),
};
}
@ -296,6 +302,9 @@ pub const CommonIdents = extern struct {
// from_utf8 error payload fields (BadUtf8 record)
.problem = common.findIdent("problem") orelse unreachable,
.index = common.findIdent("index") orelse unreachable,
// sublist argument payload fields
// .sublist_start = common.findIdent("start") orelse unreachable,
// .sublist_len = common.findIdent("len") orelse unreachable,
};
}
};

View file

@ -1729,6 +1729,122 @@ pub const Interpreter = struct {
out.is_initialized = true;
return out;
},
.list_drop_at => {
// List.drop_at : List(a), U64 -> List(a)
std.debug.assert(args.len == 2); // low-level .list_drop_at expects 2 argument
const list_arg = args[0];
const drop_index_arg = args[1];
const drop_index: u64 = @intCast(drop_index_arg.asI128());
std.debug.assert(list_arg.layout.tag == .list or list_arg.layout.tag == .list_of_zst);
const roc_list: *const builtins.list.RocList = @ptrCast(@alignCast(list_arg.ptr.?));
// Get element layout from the list layout
const elem_layout_idx = list_arg.layout.data.list;
const elem_layout = self.runtime_layout_store.getLayout(elem_layout_idx);
const elem_size = self.runtime_layout_store.layoutSize(elem_layout);
const elem_alignment = elem_layout.alignment(self.runtime_layout_store.targetUsize()).toByteUnits();
const elem_alignment_u32: u32 = @intCast(elem_alignment);
// Determine if elements are refcounted
const elements_refcounted = elem_layout.isRefcounted();
// Set up context for refcount callbacks
var refcount_context = RefcountContext{
.layout_store = &self.runtime_layout_store,
.elem_layout = elem_layout,
.roc_ops = roc_ops,
};
// Return list with element at index dropped
const result_list = builtins.list.listDropAt(
roc_list.*,
elem_alignment_u32,
elem_size,
elements_refcounted,
drop_index,
if (elements_refcounted) @ptrCast(&refcount_context) else null,
if (elements_refcounted) &listElementInc else &builtins.list.rcNone,
if (elements_refcounted) @ptrCast(&refcount_context) else null,
if (elements_refcounted) &listElementDec else &builtins.list.rcNone,
roc_ops,
);
// Allocate space for the result list
const result_layout = list_arg.layout;
var out = try self.pushRaw(result_layout, 0);
out.is_initialized = false;
// Copy the result list structure to the output
const result_ptr: *builtins.list.RocList = @ptrCast(@alignCast(out.ptr.?));
result_ptr.* = result_list;
out.is_initialized = true;
return out;
},
.list_sublist => {
// List.sublist : List(a), {start : U64, len : U64} -> List(a)
std.debug.assert(args.len == 2); // low-level .list_sublist expects 2 argument
// Check and extract first element as a typed RocList
const list_arg = args[0];
std.debug.assert(list_arg.layout.tag == .list or list_arg.layout.tag == .list_of_zst);
const roc_list: *const builtins.list.RocList = @ptrCast(@alignCast(list_arg.ptr.?));
// Access second argument as a record and extract its specific fields
const sublist_config = args[1].asRecord(&self.runtime_layout_store) catch unreachable;
const sublist_start_index = 0; // sublist_config.findFieldIndex(self.env.idents.sublist_start).?;
const sublist_start_stack = sublist_config.getFieldByIndex(sublist_start_index) catch unreachable;
const sublist_len_index = 1; //sublist_config.findFieldIndex(self.env.idents.sublist_len).?;
const sublist_len_stack = sublist_config.getFieldByIndex(sublist_len_index) catch unreachable;
const sublist_start: u64 = @intCast(sublist_start_stack.asI128());
const sublist_len: u64 = @intCast(sublist_len_stack.asI128());
std.debug.print("\nConfig Record: {{start: {d}, len: {d} }}\n", .{ sublist_start, sublist_len });
// Get element layout from the list layout
const elem_layout_idx = list_arg.layout.data.list;
const elem_layout = self.runtime_layout_store.getLayout(elem_layout_idx);
const elem_size = self.runtime_layout_store.layoutSize(elem_layout);
const elem_alignment = elem_layout.alignment(self.runtime_layout_store.targetUsize()).toByteUnits();
const elem_alignment_u32: u32 = @intCast(elem_alignment);
// Determine if elements are refcounted
const elements_refcounted = elem_layout.isRefcounted();
// Set up context for refcount callbacks
var refcount_context = RefcountContext{
.layout_store = &self.runtime_layout_store,
.elem_layout = elem_layout,
.roc_ops = roc_ops,
};
// Return list with element at index dropped
const result_list = builtins.list.listSublist(
roc_list.*,
elem_alignment_u32,
elem_size,
elements_refcounted,
sublist_start,
sublist_len,
if (elements_refcounted) @ptrCast(&refcount_context) else null,
if (elements_refcounted) &listElementDec else &builtins.list.rcNone,
roc_ops,
);
// Allocate space for the result list
const result_layout = list_arg.layout;
var out = try self.pushRaw(result_layout, 0);
out.is_initialized = false;
// Copy the result list structure to the output
const result_ptr: *builtins.list.RocList = @ptrCast(@alignCast(out.ptr.?));
result_ptr.* = result_list;
out.is_initialized = true;
return out;
},
// Bool operations
.bool_is_eq => {
// Bool.is_eq : Bool, Bool -> Bool

View file

@ -52,6 +52,13 @@ test "list refcount builtins - phase 12 limitation documented" {
// - "e_low_level_lambda - List.with_capacity without capacity, of str (refcounted elements) can concat"
// - "e_low_level_lambda - List.with_capacity of zero-sized type creates empty list"
//
// - "e_low_level_lambda - List.drop_at on an empty list at index 0"
// - "e_low_level_lambda - List.drop_at on an empty list at index >0"
// - "e_low_level_lambda - List.drop_at on non-empty list"
// - "e_low_level_lambda - List.drop_at out of bounds on non-empty list"
// - "e_low_level_lambda - List.drop_at on refcounted List(Str)"
// - "e_low_level_lambda - List.drop_at on refcounted List(List(Str))"
//
// interpreter_style_test.zig:
// - "interpreter: match list pattern destructures"
// - "interpreter: match list rest binds slice"

View file

@ -755,6 +755,94 @@ test "e_low_level_lambda - List.with_capacity of zero-sized type creates empty l
try testing.expectEqual(@as(i128, 0), len_value);
}
test "e_low_level_lambda - List.drop_at on an empty list at index 0" {
const src =
\\x = List.drop_at([], 0)
\\len = List.len(x)
;
const len_value = try evalModuleAndGetInt(src, 1);
try testing.expectEqual(@as(i128, 0), len_value);
}
test "e_low_level_lambda - List.drop_at on an empty list at index >0" {
const src =
\\x = List.drop_at([], 10)
\\len = List.len(x)
;
const len_value = try evalModuleAndGetInt(src, 1);
try testing.expectEqual(@as(i128, 0), len_value);
}
test "e_low_level_lambda - List.drop_at on non-empty list" {
const src =
\\x = List.drop_at([1, 2, 3], 0)
\\len = List.len(x)
\\first = List.get(x, 0)
;
const len_value = try evalModuleAndGetInt(src, 1);
try testing.expectEqual(@as(i128, 2), len_value);
const value = try evalModuleAndGetString(src, 2, test_allocator);
defer test_allocator.free(value);
try testing.expectEqualStrings("Ok(2)", value);
}
test "e_low_level_lambda - List.drop_at out of bounds on non-empty list" {
const src =
\\x = List.drop_at([1, 2, 3, 4, 5], 10)
\\len = List.len(x)
;
const len_value = try evalModuleAndGetInt(src, 1);
try testing.expectEqual(@as(i128, 5), len_value);
}
test "e_low_level_lambda - List.drop_at on refcounted List(Str)" {
const src =
\\x = List.drop_at(["cat", "chases", "rat"], 1)
\\len = List.len(x)
\\second = List.get(x, 1)
;
const len_value = try evalModuleAndGetInt(src, 1);
try testing.expectEqual(@as(i128, 2), len_value);
const value = try evalModuleAndGetString(src, 2, test_allocator);
defer test_allocator.free(value);
try testing.expectEqualStrings("Ok(\"rat\")", value);
}
test "e_low_level_lambda - List.drop_at on refcounted List(List(Str))" {
const src =
\\x = List.drop_at([["two", "words"], [], ["a", "four", "word", "list"]], 1)
\\len = List.len(x)
\\second = Try.ok_or(List.get(x, 1), [])
\\elt_len = List.len(second)
;
const len_value = try evalModuleAndGetInt(src, 1);
try testing.expectEqual(@as(i128, 2), len_value);
const elt_len_value = try evalModuleAndGetInt(src, 3);
try testing.expectEqual(@as(i128, 4), elt_len_value);
}
test "e_low_level_lambda - List.sublist on non-empty list" {
const src =
\\x = List.sublist([0, 1, 2, 3, 4], {len: 10, start: 3})
\\len = List.len(x)
;
const len_value = try evalModuleAndGetInt(src, 1);
try testing.expectEqual(@as(i128, 2), len_value);
// const elt_len_value = try evalModuleAndGetInt(src, 3);
// try testing.expectEqual(@as(i128, 4), elt_len_value);
}
test "e_low_level_lambda - Dec.to_str returns string representation of decimal" {
const src =
\\a : Dec