1pub use inventory;
2use runmat_gc_api::GcPtr;
3use std::collections::HashMap;
4use std::convert::TryFrom;
5use std::fmt;
6
7use indexmap::IndexMap;
8
9#[derive(Debug, Clone, PartialEq)]
10pub enum Value {
11 Int(IntValue),
12 Num(f64),
13 Complex(f64, f64),
15 Bool(bool),
16 LogicalArray(LogicalArray),
18 String(String),
19 StringArray(StringArray),
21 CharArray(CharArray),
23 Tensor(Tensor),
24 ComplexTensor(ComplexTensor),
26 Cell(CellArray),
27 Struct(StructValue),
30 GpuTensor(runmat_accelerate_api::GpuTensorHandle),
32 Object(ObjectInstance),
34 HandleObject(HandleRef),
36 Listener(Listener),
38 FunctionHandle(String),
40 Closure(Closure),
41 ClassRef(String),
42 MException(MException),
43}
44#[derive(Debug, Clone, PartialEq, Eq)]
45pub enum IntValue {
46 I8(i8),
47 I16(i16),
48 I32(i32),
49 I64(i64),
50 U8(u8),
51 U16(u16),
52 U32(u32),
53 U64(u64),
54}
55
56impl IntValue {
57 pub fn to_i64(&self) -> i64 {
58 match self {
59 IntValue::I8(v) => *v as i64,
60 IntValue::I16(v) => *v as i64,
61 IntValue::I32(v) => *v as i64,
62 IntValue::I64(v) => *v,
63 IntValue::U8(v) => *v as i64,
64 IntValue::U16(v) => *v as i64,
65 IntValue::U32(v) => *v as i64,
66 IntValue::U64(v) => {
67 if *v > i64::MAX as u64 {
68 i64::MAX
69 } else {
70 *v as i64
71 }
72 }
73 }
74 }
75 pub fn to_f64(&self) -> f64 {
76 self.to_i64() as f64
77 }
78 pub fn is_zero(&self) -> bool {
79 self.to_i64() == 0
80 }
81 pub fn class_name(&self) -> &'static str {
82 match self {
83 IntValue::I8(_) => "int8",
84 IntValue::I16(_) => "int16",
85 IntValue::I32(_) => "int32",
86 IntValue::I64(_) => "int64",
87 IntValue::U8(_) => "uint8",
88 IntValue::U16(_) => "uint16",
89 IntValue::U32(_) => "uint32",
90 IntValue::U64(_) => "uint64",
91 }
92 }
93}
94
95#[derive(Debug, Clone, PartialEq)]
96pub struct StructValue {
97 pub fields: IndexMap<String, Value>,
98}
99
100impl StructValue {
101 pub fn new() -> Self {
102 Self {
103 fields: IndexMap::new(),
104 }
105 }
106
107 pub fn insert(&mut self, name: impl Into<String>, value: Value) -> Option<Value> {
109 self.fields.insert(name.into(), value)
110 }
111
112 pub fn remove(&mut self, name: &str) -> Option<Value> {
114 self.fields.shift_remove(name)
115 }
116
117 pub fn field_names(&self) -> impl Iterator<Item = &String> {
119 self.fields.keys()
120 }
121}
122
123impl Default for StructValue {
124 fn default() -> Self {
125 Self::new()
126 }
127}
128
129#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
130pub enum NumericDType {
131 F64,
132 F32,
133}
134
135#[derive(Debug, Clone, PartialEq)]
136pub struct Tensor {
137 pub data: Vec<f64>,
138 pub shape: Vec<usize>, pub rows: usize, pub cols: usize, pub dtype: NumericDType,
143}
144
145#[derive(Debug, Clone, PartialEq)]
146pub struct ComplexTensor {
147 pub data: Vec<(f64, f64)>,
148 pub shape: Vec<usize>,
149 pub rows: usize,
150 pub cols: usize,
151}
152
153#[derive(Debug, Clone, PartialEq)]
154pub struct StringArray {
155 pub data: Vec<String>,
156 pub shape: Vec<usize>,
157 pub rows: usize,
158 pub cols: usize,
159}
160
161#[derive(Debug, Clone, PartialEq)]
162pub struct LogicalArray {
163 pub data: Vec<u8>, pub shape: Vec<usize>,
165}
166
167impl LogicalArray {
168 pub fn new(data: Vec<u8>, shape: Vec<usize>) -> Result<Self, String> {
169 let expected: usize = shape.iter().product();
170 if data.len() != expected {
171 return Err(format!(
172 "LogicalArray data length {} doesn't match shape {:?} ({} elements)",
173 data.len(),
174 shape,
175 expected
176 ));
177 }
178 let mut d = data;
180 for v in &mut d {
181 *v = if *v != 0 { 1 } else { 0 };
182 }
183 Ok(LogicalArray { data: d, shape })
184 }
185 pub fn zeros(shape: Vec<usize>) -> Self {
186 let expected: usize = shape.iter().product();
187 LogicalArray {
188 data: vec![0u8; expected],
189 shape,
190 }
191 }
192 pub fn len(&self) -> usize {
193 self.data.len()
194 }
195 pub fn is_empty(&self) -> bool {
196 self.data.is_empty()
197 }
198}
199
200#[derive(Debug, Clone, PartialEq)]
201pub struct CharArray {
202 pub data: Vec<char>,
203 pub rows: usize,
204 pub cols: usize,
205}
206
207impl CharArray {
208 pub fn new_row(s: &str) -> Self {
209 CharArray {
210 data: s.chars().collect(),
211 rows: 1,
212 cols: s.chars().count(),
213 }
214 }
215 pub fn new(data: Vec<char>, rows: usize, cols: usize) -> Result<Self, String> {
216 if rows * cols != data.len() {
217 return Err(format!(
218 "Char data length {} doesn't match dimensions {}x{}",
219 data.len(),
220 rows,
221 cols
222 ));
223 }
224 Ok(CharArray { data, rows, cols })
225 }
226}
227
228impl StringArray {
229 pub fn new(data: Vec<String>, shape: Vec<usize>) -> Result<Self, String> {
230 let expected: usize = shape.iter().product();
231 if data.len() != expected {
232 return Err(format!(
233 "StringArray data length {} doesn't match shape {:?} ({} elements)",
234 data.len(),
235 shape,
236 expected
237 ));
238 }
239 let (rows, cols) = if shape.len() >= 2 {
240 (shape[0], shape[1])
241 } else if shape.len() == 1 {
242 (1, shape[0])
243 } else {
244 (0, 0)
245 };
246 Ok(StringArray {
247 data,
248 shape,
249 rows,
250 cols,
251 })
252 }
253 pub fn new_2d(data: Vec<String>, rows: usize, cols: usize) -> Result<Self, String> {
254 Self::new(data, vec![rows, cols])
255 }
256 pub fn rows(&self) -> usize {
257 self.shape.first().copied().unwrap_or(1)
258 }
259 pub fn cols(&self) -> usize {
260 self.shape.get(1).copied().unwrap_or(1)
261 }
262}
263
264impl Tensor {
267 pub fn new(data: Vec<f64>, shape: Vec<usize>) -> Result<Self, String> {
268 let expected: usize = shape.iter().product();
269 if data.len() != expected {
270 return Err(format!(
271 "Tensor data length {} doesn't match shape {:?} ({} elements)",
272 data.len(),
273 shape,
274 expected
275 ));
276 }
277 let (rows, cols) = if shape.len() >= 2 {
278 (shape[0], shape[1])
279 } else if shape.len() == 1 {
280 (1, shape[0])
281 } else {
282 (0, 0)
283 };
284 Ok(Tensor {
285 data,
286 shape,
287 rows,
288 cols,
289 dtype: NumericDType::F64,
290 })
291 }
292
293 pub fn new_2d(data: Vec<f64>, rows: usize, cols: usize) -> Result<Self, String> {
294 Self::new(data, vec![rows, cols])
295 }
296
297 pub fn from_f32(data: Vec<f32>, shape: Vec<usize>) -> Result<Self, String> {
298 let converted: Vec<f64> = data.into_iter().map(|v| v as f64).collect();
299 Self::new_with_dtype(converted, shape, NumericDType::F32)
300 }
301
302 pub fn from_f32_slice(data: &[f32], shape: &[usize]) -> Result<Self, String> {
303 let converted: Vec<f64> = data.iter().map(|&v| v as f64).collect();
304 Self::new_with_dtype(converted, shape.to_vec(), NumericDType::F32)
305 }
306
307 pub fn new_with_dtype(
308 data: Vec<f64>,
309 shape: Vec<usize>,
310 dtype: NumericDType,
311 ) -> Result<Self, String> {
312 let mut t = Self::new(data, shape)?;
313 t.dtype = dtype;
314 Ok(t)
315 }
316
317 pub fn zeros(shape: Vec<usize>) -> Self {
318 let size: usize = shape.iter().product();
319 let (rows, cols) = if shape.len() >= 2 {
320 (shape[0], shape[1])
321 } else if shape.len() == 1 {
322 (1, shape[0])
323 } else {
324 (0, 0)
325 };
326 Tensor {
327 data: vec![0.0; size],
328 shape,
329 rows,
330 cols,
331 dtype: NumericDType::F64,
332 }
333 }
334
335 pub fn ones(shape: Vec<usize>) -> Self {
336 let size: usize = shape.iter().product();
337 let (rows, cols) = if shape.len() >= 2 {
338 (shape[0], shape[1])
339 } else if shape.len() == 1 {
340 (1, shape[0])
341 } else {
342 (0, 0)
343 };
344 Tensor {
345 data: vec![1.0; size],
346 shape,
347 rows,
348 cols,
349 dtype: NumericDType::F64,
350 }
351 }
352
353 pub fn zeros2(rows: usize, cols: usize) -> Self {
355 Self::zeros(vec![rows, cols])
356 }
357 pub fn ones2(rows: usize, cols: usize) -> Self {
358 Self::ones(vec![rows, cols])
359 }
360
361 pub fn rows(&self) -> usize {
362 self.shape.first().copied().unwrap_or(1)
363 }
364 pub fn cols(&self) -> usize {
365 self.shape.get(1).copied().unwrap_or(1)
366 }
367
368 pub fn get2(&self, row: usize, col: usize) -> Result<f64, String> {
369 let rows = self.rows();
370 let cols = self.cols();
371 if row >= rows || col >= cols {
372 return Err(format!(
373 "Index ({row}, {col}) out of bounds for {rows}x{cols} tensor"
374 ));
375 }
376 Ok(self.data[row + col * rows])
378 }
379
380 pub fn set2(&mut self, row: usize, col: usize, value: f64) -> Result<(), String> {
381 let rows = self.rows();
382 let cols = self.cols();
383 if row >= rows || col >= cols {
384 return Err(format!(
385 "Index ({row}, {col}) out of bounds for {rows}x{cols} tensor"
386 ));
387 }
388 self.data[row + col * rows] = value;
390 Ok(())
391 }
392
393 pub fn scalar_to_tensor2(scalar: f64, rows: usize, cols: usize) -> Tensor {
394 Tensor {
395 data: vec![scalar; rows * cols],
396 shape: vec![rows, cols],
397 rows,
398 cols,
399 dtype: NumericDType::F64,
400 }
401 }
402 }
404
405impl ComplexTensor {
406 pub fn new(data: Vec<(f64, f64)>, shape: Vec<usize>) -> Result<Self, String> {
407 let expected: usize = shape.iter().product();
408 if data.len() != expected {
409 return Err(format!(
410 "ComplexTensor data length {} doesn't match shape {:?} ({} elements)",
411 data.len(),
412 shape,
413 expected
414 ));
415 }
416 let (rows, cols) = if shape.len() >= 2 {
417 (shape[0], shape[1])
418 } else if shape.len() == 1 {
419 (1, shape[0])
420 } else {
421 (0, 0)
422 };
423 Ok(ComplexTensor {
424 data,
425 shape,
426 rows,
427 cols,
428 })
429 }
430 pub fn new_2d(data: Vec<(f64, f64)>, rows: usize, cols: usize) -> Result<Self, String> {
431 Self::new(data, vec![rows, cols])
432 }
433 pub fn zeros(shape: Vec<usize>) -> Self {
434 let size: usize = shape.iter().product();
435 let (rows, cols) = if shape.len() >= 2 {
436 (shape[0], shape[1])
437 } else if shape.len() == 1 {
438 (1, shape[0])
439 } else {
440 (0, 0)
441 };
442 ComplexTensor {
443 data: vec![(0.0, 0.0); size],
444 shape,
445 rows,
446 cols,
447 }
448 }
449}
450
451impl fmt::Display for Tensor {
452 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
453 match self.shape.len() {
454 0 | 1 => {
455 write!(f, "[")?;
457 for (i, v) in self.data.iter().enumerate() {
458 if i > 0 {
459 write!(f, " ")?;
460 }
461 write!(f, "{}", format_number_short_g(*v))?;
462 }
463 write!(f, "]")
464 }
465 2 => {
466 let rows = self.rows();
467 let cols = self.cols();
468 for r in 0..rows {
470 writeln!(f)?;
471 write!(f, " ")?; for c in 0..cols {
473 if c > 0 {
474 write!(f, " ")?;
475 }
476 let v = self.data[r + c * rows];
477 write!(f, "{}", format_number_short_g(v))?;
478 }
479 }
480 Ok(())
481 }
482 _ => write!(f, "Tensor(shape={:?})", self.shape),
483 }
484 }
485}
486
487impl fmt::Display for StringArray {
488 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
489 match self.shape.len() {
490 0 | 1 => {
491 write!(f, "[")?;
492 for (i, v) in self.data.iter().enumerate() {
493 if i > 0 {
494 write!(f, " ")?;
495 }
496 let escaped = v.replace('"', "\\\"");
497 write!(f, "\"{escaped}\"")?;
498 }
499 write!(f, "]")
500 }
501 2 => {
502 let rows = self.rows();
503 let cols = self.cols();
504 for r in 0..rows {
506 writeln!(f)?;
507 write!(f, " ")?; for c in 0..cols {
509 if c > 0 {
510 write!(f, " ")?;
511 }
512 let v = &self.data[r + c * rows];
513 let escaped = v.replace('"', "\\\"");
514 write!(f, "\"{escaped}\"")?;
515 }
516 }
517 Ok(())
518 }
519 _ => write!(f, "StringArray(shape={:?})", self.shape),
520 }
521 }
522}
523
524impl fmt::Display for LogicalArray {
525 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
526 match self.shape.len() {
527 0 => write!(f, "[]"),
528 1 => {
529 write!(f, "[")?;
530 for (i, v) in self.data.iter().enumerate() {
531 if i > 0 {
532 write!(f, " ")?;
533 }
534 write!(f, "{}", if *v != 0 { 1 } else { 0 })?;
535 }
536 write!(f, "]")
537 }
538 2 => {
539 let rows = self.shape[0];
540 let cols = self.shape[1];
541 for r in 0..rows {
543 writeln!(f)?;
544 write!(f, " ")?; for c in 0..cols {
546 if c > 0 {
547 write!(f, " ")?;
548 }
549 let idx = r + c * rows;
550 write!(f, "{}", if self.data[idx] != 0 { 1 } else { 0 })?;
551 }
552 }
553 Ok(())
554 }
555 _ => write!(f, "LogicalArray(shape={:?})", self.shape),
556 }
557 }
558}
559
560impl fmt::Display for CharArray {
561 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
562 for r in 0..self.rows {
563 writeln!(f)?;
564 write!(f, " ")?; for c in 0..self.cols {
566 let ch = self.data[r * self.cols + c];
567 write!(f, "{ch}")?;
568 }
569 }
570 Ok(())
571 }
572}
573
574impl From<i32> for Value {
576 fn from(i: i32) -> Self {
577 Value::Int(IntValue::I32(i))
578 }
579}
580impl From<i64> for Value {
581 fn from(i: i64) -> Self {
582 Value::Int(IntValue::I64(i))
583 }
584}
585impl From<u32> for Value {
586 fn from(i: u32) -> Self {
587 Value::Int(IntValue::U32(i))
588 }
589}
590impl From<u64> for Value {
591 fn from(i: u64) -> Self {
592 Value::Int(IntValue::U64(i))
593 }
594}
595impl From<i16> for Value {
596 fn from(i: i16) -> Self {
597 Value::Int(IntValue::I16(i))
598 }
599}
600impl From<i8> for Value {
601 fn from(i: i8) -> Self {
602 Value::Int(IntValue::I8(i))
603 }
604}
605impl From<u16> for Value {
606 fn from(i: u16) -> Self {
607 Value::Int(IntValue::U16(i))
608 }
609}
610impl From<u8> for Value {
611 fn from(i: u8) -> Self {
612 Value::Int(IntValue::U8(i))
613 }
614}
615
616impl From<f64> for Value {
617 fn from(f: f64) -> Self {
618 Value::Num(f)
619 }
620}
621
622impl From<bool> for Value {
623 fn from(b: bool) -> Self {
624 Value::Bool(b)
625 }
626}
627
628impl From<String> for Value {
629 fn from(s: String) -> Self {
630 Value::String(s)
631 }
632}
633
634impl From<&str> for Value {
635 fn from(s: &str) -> Self {
636 Value::String(s.to_string())
637 }
638}
639
640impl From<Tensor> for Value {
641 fn from(m: Tensor) -> Self {
642 Value::Tensor(m)
643 }
644}
645
646impl TryFrom<&Value> for i32 {
650 type Error = String;
651 fn try_from(v: &Value) -> Result<Self, Self::Error> {
652 match v {
653 Value::Int(i) => Ok(i.to_i64() as i32),
654 Value::Num(n) => Ok(*n as i32),
655 _ => Err(format!("cannot convert {v:?} to i32")),
656 }
657 }
658}
659
660impl TryFrom<&Value> for f64 {
661 type Error = String;
662 fn try_from(v: &Value) -> Result<Self, Self::Error> {
663 match v {
664 Value::Num(n) => Ok(*n),
665 Value::Int(i) => Ok(i.to_f64()),
666 _ => Err(format!("cannot convert {v:?} to f64")),
667 }
668 }
669}
670
671impl TryFrom<&Value> for bool {
672 type Error = String;
673 fn try_from(v: &Value) -> Result<Self, Self::Error> {
674 match v {
675 Value::Bool(b) => Ok(*b),
676 Value::Int(i) => Ok(!i.is_zero()),
677 Value::Num(n) => Ok(*n != 0.0),
678 _ => Err(format!("cannot convert {v:?} to bool")),
679 }
680 }
681}
682
683impl TryFrom<&Value> for String {
684 type Error = String;
685 fn try_from(v: &Value) -> Result<Self, Self::Error> {
686 match v {
687 Value::String(s) => Ok(s.clone()),
688 Value::StringArray(sa) => {
689 if sa.data.len() == 1 {
690 Ok(sa.data[0].clone())
691 } else {
692 Err("cannot convert string array to scalar string".to_string())
693 }
694 }
695 Value::CharArray(ca) => {
696 if ca.rows == 1 {
698 Ok(ca.data.iter().collect())
699 } else {
700 Err("cannot convert multi-row char array to scalar string".to_string())
701 }
702 }
703 Value::Int(i) => Ok(i.to_i64().to_string()),
704 Value::Num(n) => Ok(n.to_string()),
705 Value::Bool(b) => Ok(b.to_string()),
706 _ => Err(format!("cannot convert {v:?} to String")),
707 }
708 }
709}
710
711impl TryFrom<&Value> for Tensor {
712 type Error = String;
713 fn try_from(v: &Value) -> Result<Self, Self::Error> {
714 match v {
715 Value::Tensor(m) => Ok(m.clone()),
716 _ => Err(format!("cannot convert {v:?} to Tensor")),
717 }
718 }
719}
720
721impl TryFrom<&Value> for Value {
722 type Error = String;
723 fn try_from(v: &Value) -> Result<Self, Self::Error> {
724 Ok(v.clone())
725 }
726}
727
728impl TryFrom<&Value> for Vec<Value> {
729 type Error = String;
730 fn try_from(v: &Value) -> Result<Self, Self::Error> {
731 match v {
732 Value::Cell(c) => Ok(c.data.iter().map(|p| (**p).clone()).collect()),
733 _ => Err(format!("cannot convert {v:?} to Vec<Value>")),
734 }
735 }
736}
737
738use serde::{Deserialize, Serialize};
739
740#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
743pub enum Type {
744 Int,
746 Num,
748 Bool,
750 Logical,
752 String,
754 Tensor {
756 shape: Option<Vec<Option<usize>>>,
758 },
759 Cell {
761 element_type: Option<Box<Type>>,
763 length: Option<usize>,
765 },
766 Function {
768 params: Vec<Type>,
770 returns: Box<Type>,
772 },
773 Void,
775 Unknown,
777 Union(Vec<Type>),
779 Struct {
781 known_fields: Option<Vec<String>>, },
784}
785
786impl Type {
787 pub fn tensor() -> Self {
789 Type::Tensor { shape: None }
790 }
791
792 pub fn tensor_with_shape(shape: Vec<usize>) -> Self {
794 Type::Tensor {
795 shape: Some(shape.into_iter().map(Some).collect()),
796 }
797 }
798
799 pub fn cell() -> Self {
801 Type::Cell {
802 element_type: None,
803 length: None,
804 }
805 }
806
807 pub fn cell_of(element_type: Type) -> Self {
809 Type::Cell {
810 element_type: Some(Box::new(element_type)),
811 length: None,
812 }
813 }
814
815 pub fn is_compatible_with(&self, other: &Type) -> bool {
817 match (self, other) {
818 (Type::Unknown, _) | (_, Type::Unknown) => true,
819 (Type::Int, Type::Num) | (Type::Num, Type::Int) => true, (Type::Tensor { .. }, Type::Tensor { .. }) => true, (a, b) => a == b,
822 }
823 }
824
825 pub fn unify(&self, other: &Type) -> Type {
827 match (self, other) {
828 (Type::Unknown, t) | (t, Type::Unknown) => t.clone(),
829 (Type::Int, Type::Num) | (Type::Num, Type::Int) => Type::Num,
830 (Type::Tensor { .. }, Type::Tensor { .. }) => Type::tensor(), (Type::Struct { known_fields: a }, Type::Struct { known_fields: b }) => match (a, b) {
832 (None, None) => Type::Struct { known_fields: None },
833 (Some(ka), None) | (None, Some(ka)) => Type::Struct {
834 known_fields: Some(ka.clone()),
835 },
836 (Some(ka), Some(kb)) => {
837 let mut set: std::collections::BTreeSet<String> = ka.iter().cloned().collect();
838 set.extend(kb.iter().cloned());
839 Type::Struct {
840 known_fields: Some(set.into_iter().collect()),
841 }
842 }
843 },
844 (a, b) if a == b => a.clone(),
845 _ => Type::Union(vec![self.clone(), other.clone()]),
846 }
847 }
848
849 pub fn from_value(value: &Value) -> Type {
851 match value {
852 Value::Int(_) => Type::Int,
853 Value::Num(_) => Type::Num,
854 Value::Complex(_, _) => Type::Num, Value::Bool(_) => Type::Bool,
856 Value::LogicalArray(_) => Type::Logical,
857 Value::String(_) => Type::String,
858 Value::StringArray(_sa) => {
859 Type::cell_of(Type::String)
861 }
862 Value::Tensor(t) => Type::Tensor {
863 shape: Some(t.shape.iter().map(|&d| Some(d)).collect()),
864 },
865 Value::ComplexTensor(t) => Type::Tensor {
866 shape: Some(t.shape.iter().map(|&d| Some(d)).collect()),
867 },
868 Value::Cell(cells) => {
869 if cells.data.is_empty() {
870 Type::cell()
871 } else {
872 let element_type = Type::from_value(&cells.data[0]);
874 Type::Cell {
875 element_type: Some(Box::new(element_type)),
876 length: Some(cells.data.len()),
877 }
878 }
879 }
880 Value::GpuTensor(h) => Type::Tensor {
881 shape: Some(h.shape.iter().map(|&d| Some(d)).collect()),
882 },
883 Value::Object(_) => Type::Unknown,
884 Value::HandleObject(_) => Type::Unknown,
885 Value::Listener(_) => Type::Unknown,
886 Value::Struct(_) => Type::Struct { known_fields: None },
887 Value::FunctionHandle(_) => Type::Function {
888 params: vec![Type::Unknown],
889 returns: Box::new(Type::Unknown),
890 },
891 Value::Closure(_) => Type::Function {
892 params: vec![Type::Unknown],
893 returns: Box::new(Type::Unknown),
894 },
895 Value::ClassRef(_) => Type::Unknown,
896 Value::MException(_) => Type::Unknown,
897 Value::CharArray(ca) => {
898 Type::Cell {
900 element_type: Some(Box::new(Type::String)),
901 length: Some(ca.rows * ca.cols),
902 }
903 }
904 }
905 }
906}
907
908#[derive(Debug, Clone, PartialEq)]
909pub struct Closure {
910 pub function_name: String,
911 pub captures: Vec<Value>,
912}
913
914#[derive(Debug, Clone, Copy, PartialEq, Eq)]
916pub enum AccelTag {
917 Unary,
918 Elementwise,
919 Reduction,
920 MatMul,
921 Transpose,
922 ArrayConstruct,
923}
924
925#[derive(Debug, Clone)]
927pub struct BuiltinFunction {
928 pub name: &'static str,
929 pub description: &'static str,
930 pub category: &'static str,
931 pub doc: &'static str,
932 pub examples: &'static str,
933 pub param_types: Vec<Type>,
934 pub return_type: Type,
935 pub implementation: fn(&[Value]) -> Result<Value, String>,
936 pub accel_tags: &'static [AccelTag],
937 pub is_sink: bool,
938}
939
940impl BuiltinFunction {
941 #[allow(clippy::too_many_arguments)]
942 pub fn new(
943 name: &'static str,
944 description: &'static str,
945 category: &'static str,
946 doc: &'static str,
947 examples: &'static str,
948 param_types: Vec<Type>,
949 return_type: Type,
950 implementation: fn(&[Value]) -> Result<Value, String>,
951 accel_tags: &'static [AccelTag],
952 is_sink: bool,
953 ) -> Self {
954 Self {
955 name,
956 description,
957 category,
958 doc,
959 examples,
960 param_types,
961 return_type,
962 implementation,
963 accel_tags,
964 is_sink,
965 }
966 }
967}
968
969#[derive(Clone)]
971pub struct Constant {
972 pub name: &'static str,
973 pub value: Value,
974}
975
976impl std::fmt::Debug for Constant {
977 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
978 write!(
979 f,
980 "Constant {{ name: {:?}, value: {:?} }}",
981 self.name, self.value
982 )
983 }
984}
985
986inventory::collect!(BuiltinFunction);
987inventory::collect!(Constant);
988
989pub fn builtin_functions() -> Vec<&'static BuiltinFunction> {
990 inventory::iter::<BuiltinFunction>().collect()
991}
992
993pub fn constants() -> Vec<&'static Constant> {
994 inventory::iter::<Constant>().collect()
995}
996
997#[derive(Debug)]
1002pub struct BuiltinDoc {
1003 pub name: &'static str,
1004 pub category: Option<&'static str>,
1005 pub summary: Option<&'static str>,
1006 pub keywords: Option<&'static str>,
1007 pub errors: Option<&'static str>,
1008 pub related: Option<&'static str>,
1009 pub introduced: Option<&'static str>,
1010 pub status: Option<&'static str>,
1011 pub examples: Option<&'static str>,
1012}
1013
1014inventory::collect!(BuiltinDoc);
1015
1016pub fn builtin_docs() -> Vec<&'static BuiltinDoc> {
1017 inventory::iter::<BuiltinDoc>().collect()
1018}
1019
1020fn format_number_short_g(value: f64) -> String {
1025 if value.is_nan() {
1026 return "NaN".to_string();
1027 }
1028 if value.is_infinite() {
1029 return if value.is_sign_negative() {
1030 "-Inf"
1031 } else {
1032 "Inf"
1033 }
1034 .to_string();
1035 }
1036 let mut v = value;
1038 if v == 0.0 {
1039 v = 0.0;
1040 }
1041
1042 let abs = v.abs();
1043 if abs == 0.0 {
1044 return "0".to_string();
1045 }
1046
1047 let use_scientific = !(1e-4..1e6).contains(&abs);
1049
1050 if use_scientific {
1051 let s = format!("{v:.5e}");
1053 if let Some(idx) = s.find('e') {
1055 let (mut mantissa, exp) = s.split_at(idx);
1056 if let Some(dot_idx) = mantissa.find('.') {
1058 let mut end = mantissa.len();
1060 while end > dot_idx + 1 && mantissa.as_bytes()[end - 1] == b'0' {
1061 end -= 1;
1062 }
1063 if end > 0 && mantissa.as_bytes()[end - 1] == b'.' {
1064 end -= 1;
1065 }
1066 mantissa = &mantissa[..end];
1067 }
1068 return format!("{mantissa}{exp}");
1069 }
1070 return s;
1071 }
1072
1073 let exp10 = abs.log10().floor() as i32; let sig_digits: i32 = 12;
1077 let decimals = (sig_digits - 1 - exp10).clamp(0, 12) as usize;
1078 let pow = 10f64.powi(decimals as i32);
1080 let rounded = (v * pow).round() / pow;
1081 let mut s = format!("{rounded:.decimals$}");
1082 if let Some(dot) = s.find('.') {
1083 let mut end = s.len();
1085 while end > dot + 1 && s.as_bytes()[end - 1] == b'0' {
1086 end -= 1;
1087 }
1088 if end > 0 && s.as_bytes()[end - 1] == b'.' {
1089 end -= 1;
1090 }
1091 s.truncate(end);
1092 }
1093 if s.is_empty() || s == "-0" {
1094 s = "0".to_string();
1095 }
1096 s
1097}
1098
1099#[derive(Debug, Clone, PartialEq)]
1101pub struct MException {
1102 pub identifier: String,
1103 pub message: String,
1104 pub stack: Vec<String>,
1105}
1106
1107impl MException {
1108 pub fn new(identifier: String, message: String) -> Self {
1109 Self {
1110 identifier,
1111 message,
1112 stack: Vec::new(),
1113 }
1114 }
1115}
1116
1117#[derive(Debug, Clone)]
1119pub struct HandleRef {
1120 pub class_name: String,
1121 pub target: GcPtr<Value>,
1122 pub valid: bool,
1123}
1124
1125impl PartialEq for HandleRef {
1126 fn eq(&self, other: &Self) -> bool {
1127 let a = unsafe { self.target.as_raw() } as usize;
1128 let b = unsafe { other.target.as_raw() } as usize;
1129 a == b
1130 }
1131}
1132
1133#[derive(Debug, Clone, PartialEq)]
1135pub struct Listener {
1136 pub id: u64,
1137 pub target: GcPtr<Value>,
1138 pub event_name: String,
1139 pub callback: GcPtr<Value>,
1140 pub enabled: bool,
1141 pub valid: bool,
1142}
1143
1144impl Listener {
1145 pub fn class_name(&self) -> String {
1146 match unsafe { &*self.target.as_raw() } {
1147 Value::Object(o) => o.class_name.clone(),
1148 Value::HandleObject(h) => h.class_name.clone(),
1149 _ => String::new(),
1150 }
1151 }
1152}
1153
1154impl fmt::Display for Value {
1155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1156 match self {
1157 Value::Int(i) => write!(f, "{}", i.to_i64()),
1158 Value::Num(n) => write!(f, "{}", format_number_short_g(*n)),
1159 Value::Complex(re, im) => {
1160 if *im == 0.0 {
1161 write!(f, "{}", format_number_short_g(*re))
1162 } else if *re == 0.0 {
1163 write!(f, "{}i", format_number_short_g(*im))
1164 } else if *im < 0.0 {
1165 write!(
1166 f,
1167 "{}-{}i",
1168 format_number_short_g(*re),
1169 format_number_short_g(im.abs())
1170 )
1171 } else {
1172 write!(
1173 f,
1174 "{}+{}i",
1175 format_number_short_g(*re),
1176 format_number_short_g(*im)
1177 )
1178 }
1179 }
1180 Value::Bool(b) => write!(f, "{}", if *b { 1 } else { 0 }),
1181 Value::LogicalArray(la) => write!(f, "{la}"),
1182 Value::String(s) => write!(f, "'{s}'"),
1183 Value::StringArray(sa) => write!(f, "{sa}"),
1184 Value::CharArray(ca) => write!(f, "{ca}"),
1185 Value::Tensor(m) => write!(f, "{m}"),
1186 Value::ComplexTensor(m) => write!(f, "{m}"),
1187 Value::Cell(ca) => ca.fmt(f),
1188
1189 Value::GpuTensor(h) => write!(
1190 f,
1191 "GpuTensor(shape={:?}, device={}, buffer={})",
1192 h.shape, h.device_id, h.buffer_id
1193 ),
1194 Value::Object(obj) => write!(f, "{}(props={})", obj.class_name, obj.properties.len()),
1195 Value::HandleObject(h) => {
1196 let ptr = unsafe { h.target.as_raw() } as usize;
1197 write!(
1198 f,
1199 "<handle {} @0x{:x} valid={}>",
1200 h.class_name, ptr, h.valid
1201 )
1202 }
1203 Value::Listener(l) => {
1204 let ptr = unsafe { l.target.as_raw() } as usize;
1205 write!(
1206 f,
1207 "<listener id={} {}@0x{:x} '{}' enabled={} valid={}>",
1208 l.id,
1209 l.class_name(),
1210 ptr,
1211 l.event_name,
1212 l.enabled,
1213 l.valid
1214 )
1215 }
1216 Value::Struct(st) => {
1217 write!(f, "struct {{")?;
1218 for (i, (key, val)) in st.fields.iter().enumerate() {
1219 if i > 0 {
1220 write!(f, ", ")?;
1221 }
1222 write!(f, "{}: {}", key, val)?;
1223 }
1224 write!(f, "}}")
1225 }
1226 Value::FunctionHandle(name) => write!(f, "@{name}"),
1227 Value::Closure(c) => write!(
1228 f,
1229 "<closure {} captures={}>",
1230 c.function_name,
1231 c.captures.len()
1232 ),
1233 Value::ClassRef(name) => write!(f, "<class {name}>"),
1234 Value::MException(e) => write!(
1235 f,
1236 "MException(identifier='{}', message='{}')",
1237 e.identifier, e.message
1238 ),
1239 }
1240 }
1241}
1242
1243impl fmt::Display for ComplexTensor {
1244 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1245 match self.shape.len() {
1246 0 | 1 => {
1247 write!(f, "[")?;
1248 for (i, (re, im)) in self.data.iter().enumerate() {
1249 if i > 0 {
1250 write!(f, " ")?;
1251 }
1252 let s = Value::Complex(*re, *im).to_string();
1253 write!(f, "{s}")?;
1254 }
1255 write!(f, "]")
1256 }
1257 2 => {
1258 let rows = self.rows;
1259 let cols = self.cols;
1260 write!(f, "[")?;
1261 for r in 0..rows {
1262 for c in 0..cols {
1263 if c > 0 {
1264 write!(f, " ")?;
1265 }
1266 let (re, im) = self.data[r + c * rows];
1267 let s = Value::Complex(re, im).to_string();
1268 write!(f, "{s}")?;
1269 }
1270 if r + 1 < rows {
1271 write!(f, "; ")?;
1272 }
1273 }
1274 write!(f, "]")
1275 }
1276 _ => write!(f, "ComplexTensor(shape={:?})", self.shape),
1277 }
1278 }
1279}
1280
1281#[derive(Debug, Clone, PartialEq)]
1282pub struct CellArray {
1283 pub data: Vec<GcPtr<Value>>,
1284 pub shape: Vec<usize>,
1286 pub rows: usize,
1288 pub cols: usize,
1290}
1291
1292impl CellArray {
1293 pub fn new_handles(
1294 handles: Vec<GcPtr<Value>>,
1295 rows: usize,
1296 cols: usize,
1297 ) -> Result<Self, String> {
1298 Self::new_handles_with_shape(handles, vec![rows, cols])
1299 }
1300
1301 pub fn new_handles_with_shape(
1302 handles: Vec<GcPtr<Value>>,
1303 shape: Vec<usize>,
1304 ) -> Result<Self, String> {
1305 let expected = total_len(&shape)
1306 .ok_or_else(|| "Cell data shape exceeds platform limits".to_string())?;
1307 if expected != handles.len() {
1308 return Err(format!(
1309 "Cell data length {} doesn't match shape {:?} ({} elements)",
1310 handles.len(),
1311 shape,
1312 expected
1313 ));
1314 }
1315 let (rows, cols) = shape_rows_cols(&shape);
1316 Ok(CellArray {
1317 data: handles,
1318 shape,
1319 rows,
1320 cols,
1321 })
1322 }
1323
1324 pub fn new(data: Vec<Value>, rows: usize, cols: usize) -> Result<Self, String> {
1325 Self::new_with_shape(data, vec![rows, cols])
1326 }
1327
1328 pub fn new_with_shape(data: Vec<Value>, shape: Vec<usize>) -> Result<Self, String> {
1329 let expected = total_len(&shape)
1330 .ok_or_else(|| "Cell data shape exceeds platform limits".to_string())?;
1331 if expected != data.len() {
1332 return Err(format!(
1333 "Cell data length {} doesn't match shape {:?} ({} elements)",
1334 data.len(),
1335 shape,
1336 expected
1337 ));
1338 }
1339 let handles: Vec<GcPtr<Value>> = data
1341 .into_iter()
1342 .map(|v| unsafe { GcPtr::from_raw(Box::into_raw(Box::new(v))) })
1343 .collect();
1344 Self::new_handles_with_shape(handles, shape)
1345 }
1346
1347 pub fn get(&self, row: usize, col: usize) -> Result<Value, String> {
1348 if row >= self.rows || col >= self.cols {
1349 return Err(format!(
1350 "Cell index ({row}, {col}) out of bounds for {}x{} cell array",
1351 self.rows, self.cols
1352 ));
1353 }
1354 Ok((*self.data[row * self.cols + col]).clone())
1355 }
1356}
1357
1358fn total_len(shape: &[usize]) -> Option<usize> {
1359 if shape.is_empty() {
1360 return Some(0);
1361 }
1362 shape
1363 .iter()
1364 .try_fold(1usize, |acc, &dim| acc.checked_mul(dim))
1365}
1366
1367fn shape_rows_cols(shape: &[usize]) -> (usize, usize) {
1368 if shape.is_empty() {
1369 return (0, 0);
1370 }
1371 let rows = shape[0];
1372 let cols = if shape.len() >= 2 { shape[1] } else { 1 };
1373 (rows, cols)
1374}
1375
1376impl fmt::Display for CellArray {
1377 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1378 if self.shape.len() > 2 {
1379 return write!(f, "CellArray(shape={:?})", self.shape);
1380 }
1381 write!(f, "{{")?;
1382 for r in 0..self.rows {
1383 for c in 0..self.cols {
1384 if c > 0 {
1385 write!(f, ", ")?;
1386 }
1387 let v = &self.data[r * self.cols + c];
1388 write!(f, "{}", **v)?;
1389 }
1390 if r + 1 < self.rows {
1391 write!(f, "; ")?;
1392 }
1393 }
1394 write!(f, "}}")
1395 }
1396}
1397
1398#[derive(Debug, Clone, PartialEq)]
1399pub struct ObjectInstance {
1400 pub class_name: String,
1401 pub properties: HashMap<String, Value>,
1402}
1403
1404impl ObjectInstance {
1405 pub fn new(class_name: String) -> Self {
1406 Self {
1407 class_name,
1408 properties: HashMap::new(),
1409 }
1410 }
1411}
1412
1413#[derive(Debug, Clone, PartialEq, Eq)]
1415pub enum Access {
1416 Public,
1417 Private,
1418}
1419
1420#[derive(Debug, Clone)]
1421pub struct PropertyDef {
1422 pub name: String,
1423 pub is_static: bool,
1424 pub is_dependent: bool,
1425 pub get_access: Access,
1426 pub set_access: Access,
1427 pub default_value: Option<Value>,
1428}
1429
1430#[derive(Debug, Clone)]
1431pub struct MethodDef {
1432 pub name: String,
1433 pub is_static: bool,
1434 pub access: Access,
1435 pub function_name: String, }
1437
1438#[derive(Debug, Clone)]
1439pub struct ClassDef {
1440 pub name: String, pub parent: Option<String>,
1442 pub properties: HashMap<String, PropertyDef>,
1443 pub methods: HashMap<String, MethodDef>,
1444}
1445
1446use std::sync::{Mutex, OnceLock};
1447
1448static CLASS_REGISTRY: OnceLock<Mutex<HashMap<String, ClassDef>>> = OnceLock::new();
1449static STATIC_VALUES: OnceLock<Mutex<HashMap<(String, String), Value>>> = OnceLock::new();
1450
1451fn registry() -> &'static Mutex<HashMap<String, ClassDef>> {
1452 CLASS_REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
1453}
1454
1455pub fn register_class(def: ClassDef) {
1456 let mut m = registry().lock().unwrap();
1457 m.insert(def.name.clone(), def);
1458}
1459
1460pub fn get_class(name: &str) -> Option<ClassDef> {
1461 registry().lock().unwrap().get(name).cloned()
1462}
1463
1464pub fn lookup_property(class_name: &str, prop: &str) -> Option<(PropertyDef, String)> {
1467 let reg = registry().lock().unwrap();
1468 let mut current = Some(class_name.to_string());
1469 let guard: Option<std::sync::MutexGuard<'_, std::collections::HashMap<String, ClassDef>>> =
1470 None;
1471 drop(guard);
1472 while let Some(name) = current {
1473 if let Some(cls) = reg.get(&name) {
1474 if let Some(p) = cls.properties.get(prop) {
1475 return Some((p.clone(), name));
1476 }
1477 current = cls.parent.clone();
1478 } else {
1479 break;
1480 }
1481 }
1482 None
1483}
1484
1485pub fn lookup_method(class_name: &str, method: &str) -> Option<(MethodDef, String)> {
1488 let reg = registry().lock().unwrap();
1489 let mut current = Some(class_name.to_string());
1490 while let Some(name) = current {
1491 if let Some(cls) = reg.get(&name) {
1492 if let Some(m) = cls.methods.get(method) {
1493 return Some((m.clone(), name));
1494 }
1495 current = cls.parent.clone();
1496 } else {
1497 break;
1498 }
1499 }
1500 None
1501}
1502
1503fn static_values() -> &'static Mutex<HashMap<(String, String), Value>> {
1504 STATIC_VALUES.get_or_init(|| Mutex::new(HashMap::new()))
1505}
1506
1507pub fn get_static_property_value(class_name: &str, prop: &str) -> Option<Value> {
1508 static_values()
1509 .lock()
1510 .unwrap()
1511 .get(&(class_name.to_string(), prop.to_string()))
1512 .cloned()
1513}
1514
1515pub fn set_static_property_value(class_name: &str, prop: &str, value: Value) {
1516 static_values()
1517 .lock()
1518 .unwrap()
1519 .insert((class_name.to_string(), prop.to_string()), value);
1520}
1521
1522pub fn set_static_property_value_in_owner(
1524 class_name: &str,
1525 prop: &str,
1526 value: Value,
1527) -> Result<(), String> {
1528 if let Some((_p, owner)) = lookup_property(class_name, prop) {
1529 set_static_property_value(&owner, prop, value);
1530 Ok(())
1531 } else {
1532 Err(format!("Unknown static property '{class_name}.{prop}'"))
1533 }
1534}