talc/talc-lang/src/value/index.rs

160 lines
4.9 KiB
Rust

use crate::{symbol::{SYM_END_ITERATION, SYM_INDEX_ERROR, SYM_TYPE_ERROR}, value::function::NativeFunc, vmcalliter, Vm, exception::{Result, throw}};
use super::{Value, range::RangeType};
impl Value {
pub fn index(&self, idx: Self) -> Result<Self> {
use Value as V;
match (self, idx) {
(V::List(l), V::Int(i)) => {
let l = l.borrow();
if i >= 0 && (i as usize) < l.len() {
Ok(l[i as usize].clone())
} else {
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for list of length {}", l.len())
}
},
(V::Range(r), V::Int(i)) => {
if i >= 0 && (
r.ty == RangeType::Endless
|| i < r.stop
|| (r.ty == RangeType::Closed && i == r.stop)
) {
Ok((r.start + i).into())
} else {
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for range {self}")
}
},
(V::Table(t), i) if i.hashable() => {
let t = t.borrow();
let i = i.try_into()?;
Ok(t.get(&i).cloned().unwrap_or(Value::Nil))
},
(V::String(s), V::Range(r)) => {
let slen = s.len();
match r.ty {
RangeType::Open => {
if r.start < 0 || r.start > slen as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}", r.stop, slen)
}
if r.stop < 0 || r.stop > slen as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}", r.stop, slen)
}
Ok(s[r.start as usize..r.stop as usize].into())
},
RangeType::Closed => {
if r.start < 0 || r.start > slen as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}", r.stop, slen)
}
if r.stop < 0 || r.stop >= slen as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}", r.stop, slen)
}
Ok(s[r.start as usize..=r.stop as usize].into())
},
RangeType::Endless => {
if r.start < 0 || r.start > slen as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for string of length {}", r.stop, slen)
}
Ok(s[r.start as usize..].into())
},
}
},
(col, idx) => if let Ok(ii) = idx.clone().to_iter_function() {
let col = col.clone();
let func = move |vm: &mut Vm, _| {
match vmcalliter!(vm; ii.clone())? {
Some(i) => col.index(i),
None => Ok(Value::from(*SYM_END_ITERATION)),
}
};
Ok(NativeFunc::new(Box::new(func), 0).into())
} else {
throw!(*SYM_TYPE_ERROR, "cannot index {col:#} with {idx:#}")
}
}
}
pub fn store_index(&self, vm: &mut Vm, idx: Self, val: Self) -> Result<()> {
use Value as V;
match (self, idx) {
(V::List(l), V::Int(i)) => {
let mut l = l.borrow_mut();
if i >= 0 && (i as usize) < l.len() {
l[i as usize] = val;
Ok(())
} else {
throw!(*SYM_INDEX_ERROR, "index {i} out of bounds for list of length {}", l.len())
}
},
(V::Table(t), i) if i.hashable() => {
let mut t = t.borrow_mut();
let i = i.try_into()?;
t.insert(i, val);
Ok(())
},
(V::List(t), V::Range(r)) => {
let iter = val.to_iter_function()?;
let mut vals = Vec::new();
while let Some(v) = vmcalliter!(vm; iter.clone())? {
vals.push(v);
}
let mut tm = t.borrow_mut();
match r.ty {
RangeType::Open => {
if r.start < 0 || r.start > tm.len() as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}", r.stop, tm.len())
}
if r.stop < 0 || r.stop > tm.len() as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}", r.stop, tm.len())
}
let end = tm.split_off(r.stop as usize);
tm.truncate(r.start as usize);
tm.extend(vals.into_iter().chain(end));
},
RangeType::Closed => {
if r.start < 0 || r.start > tm.len() as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}", r.stop, tm.len())
}
if r.stop < 0 || r.stop >= tm.len() as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}", r.stop, tm.len())
}
let end = tm.split_off(r.stop as usize + 1);
tm.truncate(r.start as usize);
tm.extend(vals.into_iter().chain(end));
},
RangeType::Endless => {
if r.start < 0 || r.start > tm.len() as i64 {
throw!(*SYM_INDEX_ERROR,
"index {} out of bounds for list of length {}", r.stop, tm.len())
}
tm.truncate(r.start as usize);
tm.extend(vals);
},
}
Ok(())
},
(col, idx) => if let Ok(ii) = idx.clone().to_iter_function() {
let val = val.to_iter_function()?;
while let Some(i) = vmcalliter!(vm; ii.clone())? {
let Some(v) = vmcalliter!(vm; val.clone())? else {
throw!(*SYM_INDEX_ERROR, "not enough values provided for store index")
};
col.store_index(vm, i, v)?;
}
Ok(())
} else {
throw!(*SYM_TYPE_ERROR, "cannot index {self:#} with {idx:#}")
},
}
}
}