summaryrefslogtreecommitdiff
path: root/matrix-lang/src/value/index.rs
diff options
context:
space:
mode:
Diffstat (limited to 'matrix-lang/src/value/index.rs')
-rw-r--r--matrix-lang/src/value/index.rs230
1 files changed, 230 insertions, 0 deletions
diff --git a/matrix-lang/src/value/index.rs b/matrix-lang/src/value/index.rs
new file mode 100644
index 0000000..a5725e8
--- /dev/null
+++ b/matrix-lang/src/value/index.rs
@@ -0,0 +1,230 @@
+use std::cmp::Ordering;
+
+use crate::prelude::*;
+
+macro_rules! error {
+ ($($arg:tt)*) => {
+ Err(exception!(VALUE_EXCEPTION, $($arg)*))
+ };
+}
+
+impl Value {
+ pub fn val_cmp(&self, other: &Self) -> Result<Ordering> {
+ self.partial_cmp(other)
+ .map_or_else(|| error!("cannot compare: {self:?} and {other:?}"), Ok)
+ }
+
+ fn index_single(&self, index: &Value) -> Result<Self> {
+ use Value as V;
+ match (self, index) {
+ (V::Table(t), index) => {
+ Ok(t.get(index)?.unwrap_or(&Value::Nil).clone())
+ },
+ (V::List(l), V::Int(i)) => {
+ if *i < 0 || *i as usize >= l.len() {
+ return error!("{i} out of bounds for {self:?}")
+ }
+ Ok(l[*i as usize].clone())
+ },
+ (V::Matrix(m), V::Int(i)) => {
+ if *i < 0 || *i as usize >= m.values.len() {
+ return error!("{i} out of bounds for {self:?}")
+ }
+ Ok(m.values[*i as usize].clone())
+ },
+ _ => return error!("{index:?} cant index {self:?}")
+ }
+ }
+
+ fn index_multiple(&self, indexes: &Vec<Value>) -> Result<Self> {
+ use Value as V;
+ match self {
+ V::List(..) => {
+ let mut ret = Vec::new();
+ for index in indexes {
+ let res = self.index_single(index)?;
+ ret.push(res);
+ }
+ Ok(V::List(ret.into()))
+ }
+ V::Table(..) => {
+ let mut ret = ValueMap::new();
+ for index in indexes {
+ let res = self.index_single(index)?;
+ ret.insert(index.clone(), res)?;
+ }
+ Ok(V::Table(ret.into()))
+ }
+ V::Matrix(m) => {
+ let err = || error!("{self:?} can be index by [Int] or [Int;Int]");
+ if indexes.len() != 2 {
+ return err()
+ }
+ let lhs = indexes[0].clone();
+ let rhs = indexes[1].clone();
+ match (lhs, rhs) {
+ (V::Nil, V::Nil) => {
+ Ok(V::Matrix(m.shallow_clone()))
+ },
+ (V::Int(row), V::Nil) => {
+ let Some((_, row)) = m.rows().enumerate().filter(|(idx, _)| *idx as i64 == row).next() else {
+ return err();
+ };
+ let row: Vec<Value> = row.into_iter().map(|e| e.clone()).collect();
+ Ok(V::Matrix(Matrix::new(row.len(), 1, row).into()))
+ },
+ (V::Nil, V::Int(col)) => {
+ let Some((_, col)) = m.cols().enumerate().filter(|(idx, _)| *idx as i64 == col).next() else {
+ return err();
+ };
+ let col: Vec<Value> = col.into_iter().map(|e| e.clone()).collect();
+ Ok(V::Matrix(Matrix::new(1, col.len(), col).into()))
+ },
+ (V::Int(row), V::Int(col)) => {
+ if row < 0 || col < 0 {
+ return err();
+ }
+ m.get(row as usize, col as usize)
+ }
+ _ => return err()
+ }
+ }
+ _ => return error!("cannot index {self:?}")
+ }
+ }
+
+ pub fn index(&self, index: &Vec<Value>) -> Result<Self> {
+ if index.len() == 0 {
+ Ok(self.shallow_clone())
+ } else if index.len() == 1 {
+ self.index_single(&index[0])
+ } else {
+ self.index_multiple(index)
+ }
+ }
+
+ fn store_index_single(&mut self, index: &Value, store: Value) -> Result<()> {
+ use Value as V;
+ let err = format!("{self:?}");
+ match (self, index) {
+ (V::Table(t), index) => {
+ t.insert(index.clone(), store)
+ },
+ (V::List(l), V::Int(i)) => {
+ if *i < 0 || *i as usize >= l.len() {
+ return error!("{i} out of bounds for {err}")
+ }
+ l[*i as usize] = store;
+ Ok(())
+ },
+ (V::Matrix(m), V::Int(i)) => {
+ if *i < 0 || *i as usize >= m.values.len() {
+ return error!("{i} out of bounds for {err}")
+ }
+ m.values[*i as usize] = store;
+ Ok(())
+ },
+ _ => return error!("{index:?} cant index {err}")
+ }
+ }
+
+ fn store_index_multiple(&mut self, indexes: &Vec<Value>, store: Value) -> Result<()> {
+ use Value as V;
+ match self {
+ V::List(..) => {
+ for index in indexes {
+ self.store_index_single(index, store.clone())?;
+ }
+ Ok(())
+ }
+ V::Table(..) => {
+ for index in indexes {
+ self.store_index_single(index, store.clone())?;
+ }
+ Ok(())
+ }
+ _ => return error!("cannot index {self:?}")
+ }
+ }
+
+ pub fn store_index(&mut self, index: &Vec<Value>, store: Value) -> Result<()> {
+ if index.len() == 0 {
+ Ok(())
+ } else if index.len() == 1 {
+ self.store_index_single(&index[0], store)
+ } else {
+ self.store_index_multiple(index, store)
+ }
+ }
+
+ pub fn store_field_access(&mut self, ident: &str, val: Value) -> Result<()> {
+ use Value as V;
+ match self {
+ V::Table(t) => {
+ let key = V::String(Rc::from(ident));
+ Ok(t.insert(key, val)?)
+ },
+ _ => return error!("cannot field access assign {self:?}")
+ }
+ }
+
+ pub fn field_access(&self, ident: &str) -> Result<Self> {
+ use Value as V;
+ match self {
+ V::Table(t) => {
+ let key = V::String(Rc::from(ident));
+ Ok(t.get(&key)?.unwrap_or(&V::Nil).clone())
+ },
+ _ => return error!("cannot field access {self:?}")
+ }
+ }
+
+}
+
+impl Value {
+
+ pub fn into_iter_fn(self) -> Result<Rc<Function>> {
+ let Value::Iter(iter) = self.into_iter()? else {
+ return error!("bypassed iter check")
+ };
+ Ok(iter)
+ }
+
+ pub fn into_iter(self) -> Result<Self> {
+ use Value as V;
+ Ok(match self {
+ V::Iter(..) => self,
+ V::List(l) => {
+ let iter = RefCell::new(l.into_inner().into_iter());
+ iter!(move |_,_| {
+ match iter.borrow_mut().next() {
+ Some(v) => Ok(v),
+ None => Ok(V::Nil),
+ }
+ })
+ },
+ V::Range(r) => {
+ let r = (*r).clone();
+ let lhs = RefCell::new(r.0);
+ let rhs = r.1;
+ iter!(move |_,_| {
+ let val = *lhs.borrow();
+ let next = *lhs.borrow() + 1;
+ if (!r.2 && *lhs.borrow() < rhs) || (r.2 && *lhs.borrow() <= rhs) {
+ *lhs.borrow_mut() = next;
+ return Ok(Value::Int(val))
+ }
+ Ok(Value::Nil)
+ })
+ },
+ V::Function(f) => {
+ if f.arity > 0 || f.variadic {
+ return error!("iterator functions cannot be varadic or take arguments")
+ }
+ V::Iter(f)
+ },
+ val => return error!("cannot turn {val:?} into an iterator")
+ })
+ }
+}
+