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 write!(f, "[")?;
469 for r in 0..rows {
470 for c in 0..cols {
471 if c > 0 {
472 write!(f, " ")?;
473 }
474 let v = self.data[r + c * rows];
475 write!(f, "{}", format_number_short_g(v))?;
476 }
477 if r + 1 < rows {
478 write!(f, "; ")?;
479 }
480 }
481 write!(f, "]")
482 }
483 _ => write!(f, "Tensor(shape={:?})", self.shape),
484 }
485 }
486}
487
488impl fmt::Display for StringArray {
489 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
490 match self.shape.len() {
491 0 | 1 => {
492 write!(f, "[")?;
493 for (i, v) in self.data.iter().enumerate() {
494 if i > 0 {
495 write!(f, " ")?;
496 }
497 let escaped = v.replace('"', "\\\"");
498 write!(f, "\"{escaped}\"")?;
499 }
500 write!(f, "]")
501 }
502 2 => {
503 let rows = self.rows();
504 let cols = self.cols();
505 write!(f, "[")?;
506 for r in 0..rows {
507 for c in 0..cols {
508 if c > 0 {
509 write!(f, " ")?;
510 }
511 let v = &self.data[r + c * rows];
512 let escaped = v.replace('"', "\\\"");
513 write!(f, "\"{escaped}\"")?;
514 }
515 if r + 1 < rows {
516 write!(f, "; ")?;
517 }
518 }
519 write!(f, "]")
520 }
521 _ => write!(f, "StringArray(shape={:?})", self.shape),
522 }
523 }
524}
525
526impl fmt::Display for LogicalArray {
527 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
528 match self.shape.len() {
529 0 => write!(f, "[]"),
530 1 => {
531 write!(f, "[")?;
532 for (i, v) in self.data.iter().enumerate() {
533 if i > 0 {
534 write!(f, " ")?;
535 }
536 write!(f, "{}", if *v != 0 { 1 } else { 0 })?;
537 }
538 write!(f, "]")
539 }
540 2 => {
541 let rows = self.shape[0];
542 let cols = self.shape[1];
543 write!(f, "[")?;
544 for r in 0..rows {
545 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 if r + 1 < rows {
553 write!(f, "; ")?;
554 }
555 }
556 write!(f, "]")
557 }
558 _ => write!(f, "LogicalArray(shape={:?})", self.shape),
559 }
560 }
561}
562
563impl fmt::Display for CharArray {
564 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
565 write!(f, "[")?;
567 for r in 0..self.rows {
568 if r > 0 {
569 write!(f, "; ")?;
570 }
571 write!(f, "'")?;
572 for c in 0..self.cols {
573 let ch = self.data[r * self.cols + c];
574 if ch == '\'' {
575 write!(f, "''")?;
576 } else {
577 write!(f, "{ch}")?;
578 }
579 }
580 write!(f, "'")?;
581 }
582 write!(f, "]")
583 }
584}
585
586impl From<i32> for Value {
588 fn from(i: i32) -> Self {
589 Value::Int(IntValue::I32(i))
590 }
591}
592impl From<i64> for Value {
593 fn from(i: i64) -> Self {
594 Value::Int(IntValue::I64(i))
595 }
596}
597impl From<u32> for Value {
598 fn from(i: u32) -> Self {
599 Value::Int(IntValue::U32(i))
600 }
601}
602impl From<u64> for Value {
603 fn from(i: u64) -> Self {
604 Value::Int(IntValue::U64(i))
605 }
606}
607impl From<i16> for Value {
608 fn from(i: i16) -> Self {
609 Value::Int(IntValue::I16(i))
610 }
611}
612impl From<i8> for Value {
613 fn from(i: i8) -> Self {
614 Value::Int(IntValue::I8(i))
615 }
616}
617impl From<u16> for Value {
618 fn from(i: u16) -> Self {
619 Value::Int(IntValue::U16(i))
620 }
621}
622impl From<u8> for Value {
623 fn from(i: u8) -> Self {
624 Value::Int(IntValue::U8(i))
625 }
626}
627
628impl From<f64> for Value {
629 fn from(f: f64) -> Self {
630 Value::Num(f)
631 }
632}
633
634impl From<bool> for Value {
635 fn from(b: bool) -> Self {
636 Value::Bool(b)
637 }
638}
639
640impl From<String> for Value {
641 fn from(s: String) -> Self {
642 Value::String(s)
643 }
644}
645
646impl From<&str> for Value {
647 fn from(s: &str) -> Self {
648 Value::String(s.to_string())
649 }
650}
651
652impl From<Tensor> for Value {
653 fn from(m: Tensor) -> Self {
654 Value::Tensor(m)
655 }
656}
657
658impl TryFrom<&Value> for i32 {
662 type Error = String;
663 fn try_from(v: &Value) -> Result<Self, Self::Error> {
664 match v {
665 Value::Int(i) => Ok(i.to_i64() as i32),
666 Value::Num(n) => Ok(*n as i32),
667 _ => Err(format!("cannot convert {v:?} to i32")),
668 }
669 }
670}
671
672impl TryFrom<&Value> for f64 {
673 type Error = String;
674 fn try_from(v: &Value) -> Result<Self, Self::Error> {
675 match v {
676 Value::Num(n) => Ok(*n),
677 Value::Int(i) => Ok(i.to_f64()),
678 _ => Err(format!("cannot convert {v:?} to f64")),
679 }
680 }
681}
682
683impl TryFrom<&Value> for bool {
684 type Error = String;
685 fn try_from(v: &Value) -> Result<Self, Self::Error> {
686 match v {
687 Value::Bool(b) => Ok(*b),
688 Value::Int(i) => Ok(!i.is_zero()),
689 Value::Num(n) => Ok(*n != 0.0),
690 _ => Err(format!("cannot convert {v:?} to bool")),
691 }
692 }
693}
694
695impl TryFrom<&Value> for String {
696 type Error = String;
697 fn try_from(v: &Value) -> Result<Self, Self::Error> {
698 match v {
699 Value::String(s) => Ok(s.clone()),
700 Value::StringArray(sa) => {
701 if sa.data.len() == 1 {
702 Ok(sa.data[0].clone())
703 } else {
704 Err("cannot convert string array to scalar string".to_string())
705 }
706 }
707 Value::CharArray(ca) => {
708 if ca.rows == 1 {
710 Ok(ca.data.iter().collect())
711 } else {
712 Err("cannot convert multi-row char array to scalar string".to_string())
713 }
714 }
715 Value::Int(i) => Ok(i.to_i64().to_string()),
716 Value::Num(n) => Ok(n.to_string()),
717 Value::Bool(b) => Ok(b.to_string()),
718 _ => Err(format!("cannot convert {v:?} to String")),
719 }
720 }
721}
722
723impl TryFrom<&Value> for Tensor {
724 type Error = String;
725 fn try_from(v: &Value) -> Result<Self, Self::Error> {
726 match v {
727 Value::Tensor(m) => Ok(m.clone()),
728 _ => Err(format!("cannot convert {v:?} to Tensor")),
729 }
730 }
731}
732
733impl TryFrom<&Value> for Value {
734 type Error = String;
735 fn try_from(v: &Value) -> Result<Self, Self::Error> {
736 Ok(v.clone())
737 }
738}
739
740impl TryFrom<&Value> for Vec<Value> {
741 type Error = String;
742 fn try_from(v: &Value) -> Result<Self, Self::Error> {
743 match v {
744 Value::Cell(c) => Ok(c.data.iter().map(|p| (**p).clone()).collect()),
745 _ => Err(format!("cannot convert {v:?} to Vec<Value>")),
746 }
747 }
748}
749
750use serde::{Deserialize, Serialize};
751
752#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
755pub enum Type {
756 Int,
758 Num,
760 Bool,
762 Logical,
764 String,
766 Tensor {
768 shape: Option<Vec<Option<usize>>>,
770 },
771 Cell {
773 element_type: Option<Box<Type>>,
775 length: Option<usize>,
777 },
778 Function {
780 params: Vec<Type>,
782 returns: Box<Type>,
784 },
785 Void,
787 Unknown,
789 Union(Vec<Type>),
791 Struct {
793 known_fields: Option<Vec<String>>, },
796}
797
798impl Type {
799 pub fn tensor() -> Self {
801 Type::Tensor { shape: None }
802 }
803
804 pub fn tensor_with_shape(shape: Vec<usize>) -> Self {
806 Type::Tensor {
807 shape: Some(shape.into_iter().map(Some).collect()),
808 }
809 }
810
811 pub fn cell() -> Self {
813 Type::Cell {
814 element_type: None,
815 length: None,
816 }
817 }
818
819 pub fn cell_of(element_type: Type) -> Self {
821 Type::Cell {
822 element_type: Some(Box::new(element_type)),
823 length: None,
824 }
825 }
826
827 pub fn is_compatible_with(&self, other: &Type) -> bool {
829 match (self, other) {
830 (Type::Unknown, _) | (_, Type::Unknown) => true,
831 (Type::Int, Type::Num) | (Type::Num, Type::Int) => true, (Type::Tensor { .. }, Type::Tensor { .. }) => true, (a, b) => a == b,
834 }
835 }
836
837 pub fn unify(&self, other: &Type) -> Type {
839 match (self, other) {
840 (Type::Unknown, t) | (t, Type::Unknown) => t.clone(),
841 (Type::Int, Type::Num) | (Type::Num, Type::Int) => Type::Num,
842 (Type::Tensor { .. }, Type::Tensor { .. }) => Type::tensor(), (Type::Struct { known_fields: a }, Type::Struct { known_fields: b }) => match (a, b) {
844 (None, None) => Type::Struct { known_fields: None },
845 (Some(ka), None) | (None, Some(ka)) => Type::Struct {
846 known_fields: Some(ka.clone()),
847 },
848 (Some(ka), Some(kb)) => {
849 let mut set: std::collections::BTreeSet<String> = ka.iter().cloned().collect();
850 set.extend(kb.iter().cloned());
851 Type::Struct {
852 known_fields: Some(set.into_iter().collect()),
853 }
854 }
855 },
856 (a, b) if a == b => a.clone(),
857 _ => Type::Union(vec![self.clone(), other.clone()]),
858 }
859 }
860
861 pub fn from_value(value: &Value) -> Type {
863 match value {
864 Value::Int(_) => Type::Int,
865 Value::Num(_) => Type::Num,
866 Value::Complex(_, _) => Type::Num, Value::Bool(_) => Type::Bool,
868 Value::LogicalArray(_) => Type::Logical,
869 Value::String(_) => Type::String,
870 Value::StringArray(_sa) => {
871 Type::cell_of(Type::String)
873 }
874 Value::Tensor(t) => Type::Tensor {
875 shape: Some(t.shape.iter().map(|&d| Some(d)).collect()),
876 },
877 Value::ComplexTensor(t) => Type::Tensor {
878 shape: Some(t.shape.iter().map(|&d| Some(d)).collect()),
879 },
880 Value::Cell(cells) => {
881 if cells.data.is_empty() {
882 Type::cell()
883 } else {
884 let element_type = Type::from_value(&cells.data[0]);
886 Type::Cell {
887 element_type: Some(Box::new(element_type)),
888 length: Some(cells.data.len()),
889 }
890 }
891 }
892 Value::GpuTensor(h) => Type::Tensor {
893 shape: Some(h.shape.iter().map(|&d| Some(d)).collect()),
894 },
895 Value::Object(_) => Type::Unknown,
896 Value::HandleObject(_) => Type::Unknown,
897 Value::Listener(_) => Type::Unknown,
898 Value::Struct(_) => Type::Struct { known_fields: None },
899 Value::FunctionHandle(_) => Type::Function {
900 params: vec![Type::Unknown],
901 returns: Box::new(Type::Unknown),
902 },
903 Value::Closure(_) => Type::Function {
904 params: vec![Type::Unknown],
905 returns: Box::new(Type::Unknown),
906 },
907 Value::ClassRef(_) => Type::Unknown,
908 Value::MException(_) => Type::Unknown,
909 Value::CharArray(ca) => {
910 Type::Cell {
912 element_type: Some(Box::new(Type::String)),
913 length: Some(ca.rows * ca.cols),
914 }
915 }
916 }
917 }
918}
919
920#[derive(Debug, Clone, PartialEq)]
921pub struct Closure {
922 pub function_name: String,
923 pub captures: Vec<Value>,
924}
925
926#[derive(Debug, Clone, Copy, PartialEq, Eq)]
928pub enum AccelTag {
929 Unary,
930 Elementwise,
931 Reduction,
932 MatMul,
933 Transpose,
934 ArrayConstruct,
935}
936
937#[derive(Debug, Clone)]
939pub struct BuiltinFunction {
940 pub name: &'static str,
941 pub description: &'static str,
942 pub category: &'static str,
943 pub doc: &'static str,
944 pub examples: &'static str,
945 pub param_types: Vec<Type>,
946 pub return_type: Type,
947 pub implementation: fn(&[Value]) -> Result<Value, String>,
948 pub accel_tags: &'static [AccelTag],
949 pub is_sink: bool,
950}
951
952impl BuiltinFunction {
953 #[allow(clippy::too_many_arguments)]
954 pub fn new(
955 name: &'static str,
956 description: &'static str,
957 category: &'static str,
958 doc: &'static str,
959 examples: &'static str,
960 param_types: Vec<Type>,
961 return_type: Type,
962 implementation: fn(&[Value]) -> Result<Value, String>,
963 accel_tags: &'static [AccelTag],
964 is_sink: bool,
965 ) -> Self {
966 Self {
967 name,
968 description,
969 category,
970 doc,
971 examples,
972 param_types,
973 return_type,
974 implementation,
975 accel_tags,
976 is_sink,
977 }
978 }
979}
980
981#[derive(Clone)]
983pub struct Constant {
984 pub name: &'static str,
985 pub value: Value,
986}
987
988impl std::fmt::Debug for Constant {
989 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
990 write!(
991 f,
992 "Constant {{ name: {:?}, value: {:?} }}",
993 self.name, self.value
994 )
995 }
996}
997
998inventory::collect!(BuiltinFunction);
999inventory::collect!(Constant);
1000
1001pub fn builtin_functions() -> Vec<&'static BuiltinFunction> {
1002 inventory::iter::<BuiltinFunction>().collect()
1003}
1004
1005pub fn constants() -> Vec<&'static Constant> {
1006 inventory::iter::<Constant>().collect()
1007}
1008
1009#[derive(Debug)]
1014pub struct BuiltinDoc {
1015 pub name: &'static str,
1016 pub category: Option<&'static str>,
1017 pub summary: Option<&'static str>,
1018 pub keywords: Option<&'static str>,
1019 pub errors: Option<&'static str>,
1020 pub related: Option<&'static str>,
1021 pub introduced: Option<&'static str>,
1022 pub status: Option<&'static str>,
1023 pub examples: Option<&'static str>,
1024}
1025
1026inventory::collect!(BuiltinDoc);
1027
1028pub fn builtin_docs() -> Vec<&'static BuiltinDoc> {
1029 inventory::iter::<BuiltinDoc>().collect()
1030}
1031
1032fn format_number_short_g(value: f64) -> String {
1037 if value.is_nan() {
1038 return "NaN".to_string();
1039 }
1040 if value.is_infinite() {
1041 return if value.is_sign_negative() {
1042 "-Inf"
1043 } else {
1044 "Inf"
1045 }
1046 .to_string();
1047 }
1048 let mut v = value;
1050 if v == 0.0 {
1051 v = 0.0;
1052 }
1053
1054 let abs = v.abs();
1055 if abs == 0.0 {
1056 return "0".to_string();
1057 }
1058
1059 let use_scientific = !(1e-4..1e6).contains(&abs);
1061
1062 if use_scientific {
1063 let s = format!("{v:.5e}");
1065 if let Some(idx) = s.find('e') {
1067 let (mut mantissa, exp) = s.split_at(idx);
1068 if let Some(dot_idx) = mantissa.find('.') {
1070 let mut end = mantissa.len();
1072 while end > dot_idx + 1 && mantissa.as_bytes()[end - 1] == b'0' {
1073 end -= 1;
1074 }
1075 if end > 0 && mantissa.as_bytes()[end - 1] == b'.' {
1076 end -= 1;
1077 }
1078 mantissa = &mantissa[..end];
1079 }
1080 return format!("{mantissa}{exp}");
1081 }
1082 return s;
1083 }
1084
1085 let exp10 = abs.log10().floor() as i32; let sig_digits: i32 = 12;
1089 let decimals = (sig_digits - 1 - exp10).clamp(0, 12) as usize;
1090 let pow = 10f64.powi(decimals as i32);
1092 let rounded = (v * pow).round() / pow;
1093 let mut s = format!("{rounded:.decimals$}");
1094 if let Some(dot) = s.find('.') {
1095 let mut end = s.len();
1097 while end > dot + 1 && s.as_bytes()[end - 1] == b'0' {
1098 end -= 1;
1099 }
1100 if end > 0 && s.as_bytes()[end - 1] == b'.' {
1101 end -= 1;
1102 }
1103 s.truncate(end);
1104 }
1105 if s.is_empty() || s == "-0" {
1106 s = "0".to_string();
1107 }
1108 s
1109}
1110
1111#[derive(Debug, Clone, PartialEq)]
1113pub struct MException {
1114 pub identifier: String,
1115 pub message: String,
1116 pub stack: Vec<String>,
1117}
1118
1119impl MException {
1120 pub fn new(identifier: String, message: String) -> Self {
1121 Self {
1122 identifier,
1123 message,
1124 stack: Vec::new(),
1125 }
1126 }
1127}
1128
1129#[derive(Debug, Clone)]
1131pub struct HandleRef {
1132 pub class_name: String,
1133 pub target: GcPtr<Value>,
1134 pub valid: bool,
1135}
1136
1137impl PartialEq for HandleRef {
1138 fn eq(&self, other: &Self) -> bool {
1139 let a = unsafe { self.target.as_raw() } as usize;
1140 let b = unsafe { other.target.as_raw() } as usize;
1141 a == b
1142 }
1143}
1144
1145#[derive(Debug, Clone, PartialEq)]
1147pub struct Listener {
1148 pub id: u64,
1149 pub target: GcPtr<Value>,
1150 pub event_name: String,
1151 pub callback: GcPtr<Value>,
1152 pub enabled: bool,
1153 pub valid: bool,
1154}
1155
1156impl Listener {
1157 pub fn class_name(&self) -> String {
1158 match unsafe { &*self.target.as_raw() } {
1159 Value::Object(o) => o.class_name.clone(),
1160 Value::HandleObject(h) => h.class_name.clone(),
1161 _ => String::new(),
1162 }
1163 }
1164}
1165
1166impl fmt::Display for Value {
1167 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1168 match self {
1169 Value::Int(i) => write!(f, "{}", i.to_i64()),
1170 Value::Num(n) => write!(f, "{}", format_number_short_g(*n)),
1171 Value::Complex(re, im) => {
1172 if *im == 0.0 {
1173 write!(f, "{}", format_number_short_g(*re))
1174 } else if *re == 0.0 {
1175 write!(f, "{}i", format_number_short_g(*im))
1176 } else if *im < 0.0 {
1177 write!(
1178 f,
1179 "{}-{}i",
1180 format_number_short_g(*re),
1181 format_number_short_g(im.abs())
1182 )
1183 } else {
1184 write!(
1185 f,
1186 "{}+{}i",
1187 format_number_short_g(*re),
1188 format_number_short_g(*im)
1189 )
1190 }
1191 }
1192 Value::Bool(b) => write!(f, "{}", if *b { 1 } else { 0 }),
1193 Value::LogicalArray(la) => write!(f, "{la}"),
1194 Value::String(s) => write!(f, "'{s}'"),
1195 Value::StringArray(sa) => write!(f, "{sa}"),
1196 Value::CharArray(ca) => write!(f, "{ca}"),
1197 Value::Tensor(m) => write!(f, "{m}"),
1198 Value::ComplexTensor(m) => write!(f, "{m}"),
1199 Value::Cell(ca) => ca.fmt(f),
1200
1201 Value::GpuTensor(h) => write!(
1202 f,
1203 "GpuTensor(shape={:?}, device={}, buffer={})",
1204 h.shape, h.device_id, h.buffer_id
1205 ),
1206 Value::Object(obj) => write!(f, "{}(props={})", obj.class_name, obj.properties.len()),
1207 Value::HandleObject(h) => {
1208 let ptr = unsafe { h.target.as_raw() } as usize;
1209 write!(
1210 f,
1211 "<handle {} @0x{:x} valid={}>",
1212 h.class_name, ptr, h.valid
1213 )
1214 }
1215 Value::Listener(l) => {
1216 let ptr = unsafe { l.target.as_raw() } as usize;
1217 write!(
1218 f,
1219 "<listener id={} {}@0x{:x} '{}' enabled={} valid={}>",
1220 l.id,
1221 l.class_name(),
1222 ptr,
1223 l.event_name,
1224 l.enabled,
1225 l.valid
1226 )
1227 }
1228 Value::Struct(st) => write!(f, "struct(fields={})", st.fields.len()),
1229 Value::FunctionHandle(name) => write!(f, "@{name}"),
1230 Value::Closure(c) => write!(
1231 f,
1232 "<closure {} captures={}>",
1233 c.function_name,
1234 c.captures.len()
1235 ),
1236 Value::ClassRef(name) => write!(f, "<class {name}>"),
1237 Value::MException(e) => write!(
1238 f,
1239 "MException(identifier='{}', message='{}')",
1240 e.identifier, e.message
1241 ),
1242 }
1243 }
1244}
1245
1246impl fmt::Display for ComplexTensor {
1247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1248 match self.shape.len() {
1249 0 | 1 => {
1250 write!(f, "[")?;
1251 for (i, (re, im)) in self.data.iter().enumerate() {
1252 if i > 0 {
1253 write!(f, " ")?;
1254 }
1255 let s = Value::Complex(*re, *im).to_string();
1256 write!(f, "{s}")?;
1257 }
1258 write!(f, "]")
1259 }
1260 2 => {
1261 let rows = self.rows;
1262 let cols = self.cols;
1263 write!(f, "[")?;
1264 for r in 0..rows {
1265 for c in 0..cols {
1266 if c > 0 {
1267 write!(f, " ")?;
1268 }
1269 let (re, im) = self.data[r + c * rows];
1270 let s = Value::Complex(re, im).to_string();
1271 write!(f, "{s}")?;
1272 }
1273 if r + 1 < rows {
1274 write!(f, "; ")?;
1275 }
1276 }
1277 write!(f, "]")
1278 }
1279 _ => write!(f, "ComplexTensor(shape={:?})", self.shape),
1280 }
1281 }
1282}
1283
1284#[derive(Debug, Clone, PartialEq)]
1285pub struct CellArray {
1286 pub data: Vec<GcPtr<Value>>,
1287 pub shape: Vec<usize>,
1289 pub rows: usize,
1291 pub cols: usize,
1293}
1294
1295impl CellArray {
1296 pub fn new_handles(
1297 handles: Vec<GcPtr<Value>>,
1298 rows: usize,
1299 cols: usize,
1300 ) -> Result<Self, String> {
1301 Self::new_handles_with_shape(handles, vec![rows, cols])
1302 }
1303
1304 pub fn new_handles_with_shape(
1305 handles: Vec<GcPtr<Value>>,
1306 shape: Vec<usize>,
1307 ) -> Result<Self, String> {
1308 let expected = total_len(&shape)
1309 .ok_or_else(|| "Cell data shape exceeds platform limits".to_string())?;
1310 if expected != handles.len() {
1311 return Err(format!(
1312 "Cell data length {} doesn't match shape {:?} ({} elements)",
1313 handles.len(),
1314 shape,
1315 expected
1316 ));
1317 }
1318 let (rows, cols) = shape_rows_cols(&shape);
1319 Ok(CellArray {
1320 data: handles,
1321 shape,
1322 rows,
1323 cols,
1324 })
1325 }
1326
1327 pub fn new(data: Vec<Value>, rows: usize, cols: usize) -> Result<Self, String> {
1328 Self::new_with_shape(data, vec![rows, cols])
1329 }
1330
1331 pub fn new_with_shape(data: Vec<Value>, shape: Vec<usize>) -> Result<Self, String> {
1332 let expected = total_len(&shape)
1333 .ok_or_else(|| "Cell data shape exceeds platform limits".to_string())?;
1334 if expected != data.len() {
1335 return Err(format!(
1336 "Cell data length {} doesn't match shape {:?} ({} elements)",
1337 data.len(),
1338 shape,
1339 expected
1340 ));
1341 }
1342 let handles: Vec<GcPtr<Value>> = data
1344 .into_iter()
1345 .map(|v| unsafe { GcPtr::from_raw(Box::into_raw(Box::new(v))) })
1346 .collect();
1347 Self::new_handles_with_shape(handles, shape)
1348 }
1349
1350 pub fn get(&self, row: usize, col: usize) -> Result<Value, String> {
1351 if row >= self.rows || col >= self.cols {
1352 return Err(format!(
1353 "Cell index ({row}, {col}) out of bounds for {}x{} cell array",
1354 self.rows, self.cols
1355 ));
1356 }
1357 Ok((*self.data[row * self.cols + col]).clone())
1358 }
1359}
1360
1361fn total_len(shape: &[usize]) -> Option<usize> {
1362 if shape.is_empty() {
1363 return Some(0);
1364 }
1365 shape
1366 .iter()
1367 .try_fold(1usize, |acc, &dim| acc.checked_mul(dim))
1368}
1369
1370fn shape_rows_cols(shape: &[usize]) -> (usize, usize) {
1371 if shape.is_empty() {
1372 return (0, 0);
1373 }
1374 let rows = shape[0];
1375 let cols = if shape.len() >= 2 { shape[1] } else { 1 };
1376 (rows, cols)
1377}
1378
1379impl fmt::Display for CellArray {
1380 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1381 if self.shape.len() > 2 {
1382 return write!(f, "CellArray(shape={:?})", self.shape);
1383 }
1384 write!(f, "{{")?;
1385 for r in 0..self.rows {
1386 for c in 0..self.cols {
1387 if c > 0 {
1388 write!(f, ", ")?;
1389 }
1390 let v = &self.data[r * self.cols + c];
1391 write!(f, "{}", **v)?;
1392 }
1393 if r + 1 < self.rows {
1394 write!(f, "; ")?;
1395 }
1396 }
1397 write!(f, "}}")
1398 }
1399}
1400
1401#[derive(Debug, Clone, PartialEq)]
1402pub struct ObjectInstance {
1403 pub class_name: String,
1404 pub properties: HashMap<String, Value>,
1405}
1406
1407impl ObjectInstance {
1408 pub fn new(class_name: String) -> Self {
1409 Self {
1410 class_name,
1411 properties: HashMap::new(),
1412 }
1413 }
1414}
1415
1416#[derive(Debug, Clone, PartialEq, Eq)]
1418pub enum Access {
1419 Public,
1420 Private,
1421}
1422
1423#[derive(Debug, Clone)]
1424pub struct PropertyDef {
1425 pub name: String,
1426 pub is_static: bool,
1427 pub is_dependent: bool,
1428 pub get_access: Access,
1429 pub set_access: Access,
1430 pub default_value: Option<Value>,
1431}
1432
1433#[derive(Debug, Clone)]
1434pub struct MethodDef {
1435 pub name: String,
1436 pub is_static: bool,
1437 pub access: Access,
1438 pub function_name: String, }
1440
1441#[derive(Debug, Clone)]
1442pub struct ClassDef {
1443 pub name: String, pub parent: Option<String>,
1445 pub properties: HashMap<String, PropertyDef>,
1446 pub methods: HashMap<String, MethodDef>,
1447}
1448
1449use std::sync::{Mutex, OnceLock};
1450
1451static CLASS_REGISTRY: OnceLock<Mutex<HashMap<String, ClassDef>>> = OnceLock::new();
1452static STATIC_VALUES: OnceLock<Mutex<HashMap<(String, String), Value>>> = OnceLock::new();
1453
1454fn registry() -> &'static Mutex<HashMap<String, ClassDef>> {
1455 CLASS_REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
1456}
1457
1458pub fn register_class(def: ClassDef) {
1459 let mut m = registry().lock().unwrap();
1460 m.insert(def.name.clone(), def);
1461}
1462
1463pub fn get_class(name: &str) -> Option<ClassDef> {
1464 registry().lock().unwrap().get(name).cloned()
1465}
1466
1467pub fn lookup_property(class_name: &str, prop: &str) -> Option<(PropertyDef, String)> {
1470 let reg = registry().lock().unwrap();
1471 let mut current = Some(class_name.to_string());
1472 let guard: Option<std::sync::MutexGuard<'_, std::collections::HashMap<String, ClassDef>>> =
1473 None;
1474 drop(guard);
1475 while let Some(name) = current {
1476 if let Some(cls) = reg.get(&name) {
1477 if let Some(p) = cls.properties.get(prop) {
1478 return Some((p.clone(), name));
1479 }
1480 current = cls.parent.clone();
1481 } else {
1482 break;
1483 }
1484 }
1485 None
1486}
1487
1488pub fn lookup_method(class_name: &str, method: &str) -> Option<(MethodDef, String)> {
1491 let reg = registry().lock().unwrap();
1492 let mut current = Some(class_name.to_string());
1493 while let Some(name) = current {
1494 if let Some(cls) = reg.get(&name) {
1495 if let Some(m) = cls.methods.get(method) {
1496 return Some((m.clone(), name));
1497 }
1498 current = cls.parent.clone();
1499 } else {
1500 break;
1501 }
1502 }
1503 None
1504}
1505
1506fn static_values() -> &'static Mutex<HashMap<(String, String), Value>> {
1507 STATIC_VALUES.get_or_init(|| Mutex::new(HashMap::new()))
1508}
1509
1510pub fn get_static_property_value(class_name: &str, prop: &str) -> Option<Value> {
1511 static_values()
1512 .lock()
1513 .unwrap()
1514 .get(&(class_name.to_string(), prop.to_string()))
1515 .cloned()
1516}
1517
1518pub fn set_static_property_value(class_name: &str, prop: &str, value: Value) {
1519 static_values()
1520 .lock()
1521 .unwrap()
1522 .insert((class_name.to_string(), prop.to_string()), value);
1523}
1524
1525pub fn set_static_property_value_in_owner(
1527 class_name: &str,
1528 prop: &str,
1529 value: Value,
1530) -> Result<(), String> {
1531 if let Some((_p, owner)) = lookup_property(class_name, prop) {
1532 set_static_property_value(&owner, prop, value);
1533 Ok(())
1534 } else {
1535 Err(format!("Unknown static property '{class_name}.{prop}'"))
1536 }
1537}