use crate::{
error::{EvalResult, InterpreterError},
eval::{control_flow::iterate_value, modules::need_arg},
value::Value,
};
fn is_valid_typecode(c: char) -> bool {
matches!(c, 'b' | 'B' | 'h' | 'H' | 'i' | 'I' | 'l' | 'L' | 'q' | 'Q' | 'f' | 'd' | 'u')
}
pub(crate) fn itemsize(typecode: char) -> usize {
match typecode {
'b' | 'B' => 1,
'h' | 'H' => 2,
'i' | 'I' | 'f' => 4,
'u' => 4,
_ => 8, }
}
pub(crate) fn coerce_element(typecode: char, value: &Value) -> EvalResult {
let is_float = matches!(typecode, 'f' | 'd');
if is_float {
match value {
Value::Float(_) => Ok(value.clone()),
Value::Int(i) => Ok(Value::Float(*i as f64)),
Value::Bool(b) => Ok(Value::Float(f64::from(*b))),
_ => Err(InterpreterError::TypeError("array item must be a float".into()).into()),
}
} else if typecode == 'u' {
match value {
Value::String(_) => Ok(value.clone()),
_ => {
Err(InterpreterError::TypeError("array item must be a unicode character".into())
.into())
}
}
} else {
match value {
Value::Int(_) | Value::BigInt(_) => Ok(value.clone()),
Value::Bool(b) => Ok(Value::Int(i64::from(*b))),
_ => Err(InterpreterError::TypeError("array item must be an integer".into()).into()),
}
}
}
pub struct ArrayModule;
#[async_trait::async_trait]
impl crate::eval::modules::Module for ArrayModule {
fn name(&self) -> &'static str {
"array"
}
fn constant(&self, name: &str) -> Option<Value> {
(name == "typecodes").then(|| Value::String("bBhHiIlLqQfdu".into()))
}
fn has_function(&self, name: &str) -> bool {
name == "array" || name == "ArrayType"
}
async fn call(
&self,
_state: &mut crate::state::InterpreterState,
func: &str,
args: &[Value],
_kwargs: &indexmap::IndexMap<String, Value>,
_tools: &crate::tools::Tools,
) -> EvalResult {
match func {
"array" | "ArrayType" => construct_array(args),
_ => Err(InterpreterError::AttributeError(format!(
"module 'array' has no attribute '{func}'"
))
.into()),
}
}
}
pub(crate) fn construct_array(args: &[Value]) -> EvalResult {
let Value::String(tc) = need_arg("array", args, 0)? else {
return Err(InterpreterError::TypeError(
"array() argument 1 must be a unicode character, not ...".into(),
)
.into());
};
let mut chars = tc.chars();
let (Some(typecode), None) = (chars.next(), chars.next()) else {
return Err(InterpreterError::TypeError(
"array() argument 1 must be a unicode character".into(),
)
.into());
};
if !is_valid_typecode(typecode) {
return Err(InterpreterError::ValueError(
"bad typecode (must be b, B, u, h, H, i, I, l, L, q, Q, f or d)".into(),
)
.into());
}
let mut items = Vec::new();
if let Some(init) = args.get(1) {
for elem in iterate_value(init)? {
items.push(coerce_element(typecode, &elem)?);
}
}
Ok(Value::Array { typecode, items: crate::value::shared_list(items) })
}