haku: add len and index functions for reading lists

This commit is contained in:
liquidex 2024-10-25 23:21:28 +02:00
parent dd955b0649
commit e2f9538156
3 changed files with 63 additions and 8 deletions

View file

@ -247,9 +247,8 @@ pub mod fns {
0x88 Nary "rgbaB" => rgba_b,
0x89 Nary "rgbaA" => rgba_a,
// NOTE: Not used right now, has been replaced with Opcode::List.
// Keeping it around to reserve a slot for data structure operations.
0x90 Nary "list (unused)" => list,
0x90 Nary "len" => len,
0x91 Nary "index" => index,
0xc0 Nary "toShape" => to_shape_f,
0xc1 Nary "line" => line,
@ -552,11 +551,33 @@ pub mod fns {
Ok(Value::Number(rgba.r))
}
pub fn list(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
let elements: Vec<_> = (0..args.num()).map(|i| args.get(vm, i)).collect();
vm.track_array(&elements)?;
let id = vm.create_ref(Ref::List(List { elements }))?;
Ok(Value::Ref(id))
pub fn len(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
if args.num() != 1 {
return Err(vm.create_exception("`len` expects a single argument (len list)"));
}
let list = args.get_list(vm, 0, "argument to (len list) must be a list")?;
Ok(Value::Number(list.elements.len() as f32))
}
pub fn index(vm: &mut Vm, args: FnArgs) -> Result<Value, Exception> {
if args.num() != 2 {
return Err(vm.create_exception("`index` expects two arguments (index list i)"));
}
let list = args.get_list(vm, 0, "1st argument to (index list i) must be a list")?;
let i = args.get_number(vm, 1, "2nd argument to (index list i) must be a number")?;
if i >= 0.0 {
let i = i as usize;
if i < list.elements.len() {
return Ok(list.elements[i]);
}
}
Err(vm.create_exception(format!(
"list index out of bounds (length of list is {}, index is {})",
list.elements.len(),
i
)))
}
fn to_shape(value: Value, vm: &Vm) -> Option<Shape> {

View file

@ -622,6 +622,23 @@ impl FnArgs {
};
Ok(closure)
}
#[inline(never)]
pub fn get_list<'vm>(
&self,
vm: &'vm Vm,
index: usize,
message: &'static str,
) -> Result<&'vm List, Exception> {
let value = self.get(vm, index);
let (_, any_ref) = vm
.get_ref_value(value)
.ok_or_else(|| vm.create_exception(message))?;
let Ref::List(list) = any_ref else {
return Err(vm.create_exception(message));
};
Ok(list)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]

View file

@ -292,3 +292,20 @@ fn with_dotter_identity() {
"#;
assert_eq!(eval(code).unwrap(), Value::Ref(RefId::from_u32(2)))
}
#[test]
fn system_len() {
expect_number("len []", 0.0, 0.0001);
expect_number("len [1, 2]", 2.0, 0.0001);
}
#[test]
fn system_index() {
expect_number("index [1] 0", 1.0, 0.0001);
expect_number("index [1, 2] 0", 1.0, 0.0001);
expect_number("index [1, 2] 1", 2.0, 0.0001);
expect_number("index [1] 0.5", 1.0, 0.0001);
expect_number("index [1, 2] 0.5", 1.0, 0.0001);
assert!(eval("index [1] (-1)").is_err());
assert!(eval("index [1] 1").is_err());
}