///|
fn list_append(
list : Ref[Array[@value.Value]],
pos : Array[@value.Value],
_kw : Map[String, @value.Value],
span : @ast.Span,
) -> @value.Value raise StarlarkError {
guard pos.length() == 1 else {
raise StarlarkError::TypeError(
message="append() takes exactly one argument (\{pos.length()} given)",
span~,
)
}
list.val.push(pos[0])
@value.Value::None
}
///|
fn list_clear(
list : Ref[Array[@value.Value]],
pos : Array[@value.Value],
_kw : Map[String, @value.Value],
span : @ast.Span,
) -> @value.Value raise StarlarkError {
guard pos.length() == 0 else {
raise StarlarkError::TypeError(
message="clear() takes no arguments (\{pos.length()} given)",
span~,
)
}
list.val.clear()
@value.Value::None
}
///|
fn list_extend(
list : Ref[Array[@value.Value]],
pos : Array[@value.Value],
_kw : Map[String, @value.Value],
span : @ast.Span,
) -> @value.Value raise StarlarkError {
guard pos.length() == 1 else {
raise StarlarkError::TypeError(
message="extend() takes exactly one argument (\{pos.length()} given)",
span~,
)
}
let items = iterable_to_array(pos[0], span)
for item in items {
list.val.push(item)
}
@value.Value::None
}
///|
fn list_index(
list : Ref[Array[@value.Value]],
pos : Array[@value.Value],
_kw : Map[String, @value.Value],
span : @ast.Span,
) -> @value.Value raise StarlarkError {
guard pos.length() >= 1 && pos.length() <= 3 else {
raise StarlarkError::TypeError(
message="index() takes at least 1 and at most 3 arguments (\{pos.length()} given)",
span~,
)
}
let x = pos[0]
let start = if pos.length() >= 2 {
clamp_list_index(expect_int(pos[1], "index", span), list.val.length())
} else {
0
}
let end = if pos.length() >= 3 {
clamp_list_index(expect_int(pos[2], "index", span), list.val.length())
} else {
list.val.length()
}
for i = start; i < end; i = i + 1 {
if values_equal(list.val[i], x) {
return @value.Value::Int(i.to_int64())
}
}
raise StarlarkError::ValueError(
message="\{value_to_repr(x)} is not in list",
span~,
)
}
///|
fn list_insert(
list : Ref[Array[@value.Value]],
pos : Array[@value.Value],
_kw : Map[String, @value.Value],
span : @ast.Span,
) -> @value.Value raise StarlarkError {
guard pos.length() == 2 else {
raise StarlarkError::TypeError(
message="insert() takes exactly 2 arguments (\{pos.length()} given)",
span~,
)
}
let idx_raw = expect_int(pos[0], "insert", span)
let x = pos[1]
let len = list.val.length()
let idx = if idx_raw < 0 {
let i = idx_raw + len
if i < 0 {
0
} else {
i
}
} else if idx_raw > len {
len
} else {
idx_raw
}
// Insert by shifting elements
list.val.push(@value.Value::None) // extend by 1
for i = list.val.length() - 1; i > idx; i = i - 1 {
list.val[i] = list.val[i - 1]
}
list.val[idx] = x
@value.Value::None
}
///|
fn list_pop(
list : Ref[Array[@value.Value]],
pos : Array[@value.Value],
_kw : Map[String, @value.Value],
span : @ast.Span,
) -> @value.Value raise StarlarkError {
guard pos.length() <= 1 else {
raise StarlarkError::TypeError(
message="pop() takes at most 1 argument (\{pos.length()} given)",
span~,
)
}
if list.val.length() == 0 {
raise StarlarkError::IndexError(message="pop from empty list", span~)
}
let idx_raw = if pos.length() == 1 {
expect_int(pos[0], "pop", span)
} else {
-1
}
let len = list.val.length()
let idx = if idx_raw < 0 { idx_raw + len } else { idx_raw }
if idx < 0 || idx >= len {
raise StarlarkError::IndexError(message="pop index out of range", span~)
}
let val = list.val[idx]
// Shift elements left
for i = idx; i < len - 1; i = i + 1 {
list.val[i] = list.val[i + 1]
}
let _ = list.val.pop()
val
}
///|
fn list_remove(
list : Ref[Array[@value.Value]],
pos : Array[@value.Value],
_kw : Map[String, @value.Value],
span : @ast.Span,
) -> @value.Value raise StarlarkError {
guard pos.length() == 1 else {
raise StarlarkError::TypeError(
message="remove() takes exactly 1 argument (\{pos.length()} given)",
span~,
)
}
let x = pos[0]
let mut found = -1
for i = 0; i < list.val.length(); i = i + 1 {
if values_equal(list.val[i], x) {
found = i
break
}
}
if found < 0 {
raise StarlarkError::ValueError(
message="list.remove(x): x not in list",
span~,
)
}
// Shift elements left
for i = found; i < list.val.length() - 1; i = i + 1 {
list.val[i] = list.val[i + 1]
}
let _ = list.val.pop()
@value.Value::None
}
///|
fn list_methods() -> Map[
String,
(
Ref[Array[@value.Value]],
Array[@value.Value],
Map[String, @value.Value],
@ast.Span,
) -> @value.Value raise StarlarkError,
] {
let m : Map[
String,
(
Ref[Array[@value.Value]],
Array[@value.Value],
Map[String, @value.Value],
@ast.Span,
) -> @value.Value raise StarlarkError,
] = {}
m["append"] = list_append
m["clear"] = list_clear
m["extend"] = list_extend
m["index"] = list_index
m["insert"] = list_insert
m["pop"] = list_pop
m["remove"] = list_remove
m
}
///|
fn clamp_list_index(idx : Int, len : Int) -> Int {
if idx < 0 {
let result = idx + len
if result < 0 {
0
} else {
result
}
} else if idx > len {
len
} else {
idx
}
}