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