Skip to main content

mech_interpreter/stdlib/convert/
scalar.rs

1#[macro_use]
2use crate::stdlib::*;
3
4// Convert --------------------------------------------------------------------
5
6#[cfg(feature = "enum")]
7#[derive(Debug)]
8struct ConvertSEnum {
9  out: Ref<MechEnum>,
10}
11#[cfg(feature = "enum")]
12impl MechFunctionFactory for ConvertSEnum {
13  fn new(args: FunctionArgs) -> MResult<Box<dyn MechFunction>> {
14    match args {
15      FunctionArgs::Unary(out, _) => {
16        let out: Ref<MechEnum> = unsafe { out.as_unchecked() }.clone();
17        Ok(Box::new(Self {out}))
18      },
19      _ => Err(MechError::new(
20          IncorrectNumberOfArguments { expected: 1, found: args.len() },
21          None
22        ).with_compiler_loc()
23      ),
24    }
25  }
26}
27#[cfg(feature = "enum")]
28impl MechFunctionImpl for ConvertSEnum
29{
30  fn solve(&self) { }
31  fn out(&self) -> Value { Value::Enum(self.out.clone()) }
32  fn to_string(&self) -> String { format!("{:#?}", self) }
33}
34#[cfg(all(feature = "compiler", feature = "enum"))]
35impl MechFunctionCompiler for ConvertSEnum {
36  fn compile(&self, ctx: &mut CompileCtx) -> MResult<Register> {
37    let name = format!("ConvertSEnum<enum>");
38    compile_nullop!(name, self.out, ctx, FeatureFlag::Builtin(FeatureKind::Convert));
39  }
40}
41#[cfg(feature = "enum")]
42register_descriptor! {
43  FunctionDescriptor {
44    name: "ConvertSEnum<enum>",
45    ptr: ConvertSEnum::new,
46  }
47}
48
49#[derive(Debug)]
50struct ConvertSEmpty {
51  out: Ref<Value>,
52}
53
54impl MechFunctionImpl for ConvertSEmpty {
55  fn solve(&self) { }
56  fn out(&self) -> Value { self.out.borrow().clone() }
57  fn to_string(&self) -> String { format!("{:#?}", self) }
58}
59#[cfg(feature = "compiler")]
60impl MechFunctionCompiler for ConvertSEmpty {
61  fn compile(&self, ctx: &mut CompileCtx) -> MResult<Register> {
62    let name = format!("ConvertSEmpty<empty>");
63    compile_nullop!(name, self.out, ctx, FeatureFlag::Builtin(FeatureKind::Convert));
64  }
65}
66register_descriptor! {
67  FunctionDescriptor {
68    name: "ConvertSEmpty<empty>",
69    ptr: |args: FunctionArgs| -> MResult<Box<dyn MechFunction>> {
70      match args {
71        FunctionArgs::Nullary(out) => {
72          let out: Ref<Value> = unsafe { out.as_unchecked() }.clone();
73          Ok(Box::new(ConvertSEmpty { out }))
74        },
75        _ => Err(MechError::new(
76            IncorrectNumberOfArguments { expected: 0, found: args.len() },
77            None
78          ).with_compiler_loc()
79        ),
80      }
81    },
82  }
83}
84
85#[cfg(all(feature = "matrix", feature = "table"))]
86#[derive(Debug)]
87struct ConvertMat2Table<T> {
88  arg: Matrix<T>,
89  out: Ref<MechTable>,
90}
91
92#[cfg(all(feature = "matrix", feature = "table"))]
93impl<T> MechFunctionImpl for ConvertMat2Table<T>
94where T: Debug + Clone + PartialEq + Into<Value> + 'static,
95{
96  fn solve(&self) {
97    let arg = &self.arg;
98    let mut out_table = self.out.borrow_mut();
99    let rows = arg.rows().min(out_table.rows);
100
101    for (col_ix, (ix, (col_kind, out_col))) in out_table.data.iter_mut().enumerate() {
102      for row_ix in 0..rows {
103        let value = arg.index2d(row_ix + 1, col_ix + 1).clone().into();
104        let converted_value = value.convert_to(col_kind).unwrap();
105        out_col.set_index1d(row_ix, converted_value);
106      }
107    }
108  }
109  fn out(&self) -> Value { Value::Table(self.out.clone()) }
110  fn to_string(&self) -> String { format!("{:#?}", self) }
111}
112#[cfg(all(feature = "compiler", feature = "matrix", feature = "table"))]
113impl<T> MechFunctionCompiler for ConvertMat2Table<T> 
114where
115  T: ConstElem + CompileConst + AsValueKind,
116{
117  fn compile(&self, ctx: &mut CompileCtx) -> MResult<Register> {
118    let mut registers = [0,0];
119
120    registers[0] = compile_register_brrw!(self.out, ctx);
121    registers[1] = compile_register!(self.arg, ctx);
122
123    ctx.features.insert(FeatureFlag::Builtin(FeatureKind::Convert));
124
125    ctx.emit_unop(
126      hash_str("ConvertMat2Table"),
127      registers[0],
128      registers[1],
129    );
130
131    return Ok(registers[0]);
132  }
133}
134
135#[cfg(all(feature = "matrix", feature = "table"))]
136fn resolve_table_column_kinds(tbl: &Vec<(String, ValueKind)>, mat_knd: ValueKind) -> MResult<Vec<(String, ValueKind)>> {
137  tbl
138    .iter()
139    .map(|(col_name, col_kind)| {
140      if *col_kind == ValueKind::Any {
141        Ok((col_name.clone(), mat_knd.clone()))
142      } else if *col_kind == mat_knd || mat_knd.is_convertible_to(col_kind) {
143        Ok((col_name.clone(), col_kind.clone()))
144      } else {
145        Err(
146          MechError::new(
147            ColumnConvertKindMismatchError { from: mat_knd.clone(), to: col_kind.clone() },
148            None,
149          ).with_compiler_loc()
150        )
151      }
152    })
153    .collect()
154}
155
156#[cfg(all(feature = "matrix", feature = "set"))]
157fn matrix_to_values(value: &Value) -> Option<Vec<Value>> {
158  match value {
159    Value::MutableReference(reference) => matrix_to_values(&reference.borrow()),
160    Value::MatrixIndex(matrix) => Some(matrix.as_vec().into_iter().map(|value| Value::Index(Ref::new(value))).collect()),
161    #[cfg(feature = "bool")]
162    Value::MatrixBool(matrix) => Some(matrix.as_vec().into_iter().map(Value::from).collect()),
163    #[cfg(feature = "u8")]
164    Value::MatrixU8(matrix) => Some(matrix.as_vec().into_iter().map(Value::from).collect()),
165    #[cfg(feature = "u16")]
166    Value::MatrixU16(matrix) => Some(matrix.as_vec().into_iter().map(Value::from).collect()),
167    #[cfg(feature = "u32")]
168    Value::MatrixU32(matrix) => Some(matrix.as_vec().into_iter().map(Value::from).collect()),
169    #[cfg(feature = "u64")]
170    Value::MatrixU64(matrix) => Some(matrix.as_vec().into_iter().map(Value::from).collect()),
171    #[cfg(feature = "u128")]
172    Value::MatrixU128(matrix) => Some(matrix.as_vec().into_iter().map(Value::from).collect()),
173    #[cfg(feature = "i8")]
174    Value::MatrixI8(matrix) => Some(matrix.as_vec().into_iter().map(Value::from).collect()),
175    #[cfg(feature = "i16")]
176    Value::MatrixI16(matrix) => Some(matrix.as_vec().into_iter().map(Value::from).collect()),
177    #[cfg(feature = "i32")]
178    Value::MatrixI32(matrix) => Some(matrix.as_vec().into_iter().map(Value::from).collect()),
179    #[cfg(feature = "i64")]
180    Value::MatrixI64(matrix) => Some(matrix.as_vec().into_iter().map(Value::from).collect()),
181    #[cfg(feature = "i128")]
182    Value::MatrixI128(matrix) => Some(matrix.as_vec().into_iter().map(Value::from).collect()),
183    #[cfg(feature = "f32")]
184    Value::MatrixF32(matrix) => Some(matrix.as_vec().into_iter().map(Value::from).collect()),
185    #[cfg(feature = "f64")]
186    Value::MatrixF64(matrix) => Some(matrix.as_vec().into_iter().map(Value::from).collect()),
187    #[cfg(feature = "string")]
188    Value::MatrixString(matrix) => Some(matrix.as_vec().into_iter().map(Value::from).collect()),
189    #[cfg(feature = "rational")]
190    Value::MatrixR64(matrix) => Some(matrix.as_vec().into_iter().map(|value| value.to_value()).collect()),
191    #[cfg(feature = "complex")]
192    Value::MatrixC64(matrix) => Some(matrix.as_vec().into_iter().map(|value| value.to_value()).collect()),
193    Value::MatrixValue(matrix) => Some(matrix.as_vec()),
194    _ => None,
195  }
196}
197
198#[cfg(all(feature = "matrix", feature = "set"))]
199#[derive(Debug)]
200struct ConvertMatToSet {
201  arg: Value,
202  target_kind: ValueKind,
203  out: Ref<MechSet>,
204}
205
206#[cfg(all(feature = "matrix", feature = "set"))]
207impl MechFunctionImpl for ConvertMatToSet {
208  fn solve(&self) {
209    let values = matrix_to_values(&self.arg).unwrap_or_default();
210    let converted_values = values
211      .into_iter()
212      .map(|value| {
213        value
214          .convert_to(&self.target_kind)
215          .unwrap_or_else(|| panic!("Unable to convert matrix element to target set kind {}", self.target_kind))
216      })
217      .collect::<Vec<_>>();
218    *self.out.borrow_mut() = MechSet::from_vec(converted_values);
219  }
220  fn out(&self) -> Value { Value::Set(self.out.clone()) }
221  fn to_string(&self) -> String { format!("{:#?}", self) }
222}
223#[cfg(all(feature = "compiler", feature = "matrix", feature = "set"))]
224impl MechFunctionCompiler for ConvertMatToSet {
225  fn compile(&self, ctx: &mut CompileCtx) -> MResult<Register> {
226    let name = format!("ConvertMatToSet");
227    compile_nullop!(name, self.out, ctx, FeatureFlag::Builtin(FeatureKind::Convert));
228  }
229}
230
231#[cfg(all(feature = "rational", feature = "f64"))]
232#[derive(Debug)]
233struct ConvertSRationalToF64 {
234  arg: Ref<R64>,
235  out: Ref<f64>,
236}
237
238#[cfg(all(feature = "rational", feature = "f64"))]
239impl MechFunctionImpl for ConvertSRationalToF64 {
240  fn solve(&self) {
241    let arg_ptr = self.arg.as_ptr();
242    let out_ptr = self.out.as_mut_ptr();
243    unsafe{ *out_ptr = (*arg_ptr).into(); }
244  }
245  fn out(&self) -> Value { Value::F64(self.out.clone()) }
246  fn to_string(&self) -> String { format!("{:#?}", self) }
247}
248#[cfg(all(feature = "compiler", feature = "rational", feature = "f64"))]
249impl MechFunctionCompiler for ConvertSRationalToF64 {
250  fn compile(&self, ctx: &mut CompileCtx) -> MResult<Register> {
251    let name = format!("ConvertSRationalToF64<f64>");
252    compile_unop!(name, self.out, self.arg, ctx, FeatureFlag::Builtin(FeatureKind::Convert));
253  }
254}
255
256macro_rules! impl_conversion_match_arms {
257  ($arg:expr, $($input_type:ident, $input_type_string:tt => $($target_type:ident, $target_type_string:tt),+);+ $(;)?) => {
258    paste!{
259      match $arg {
260        $(
261          #[cfg(all(feature = "matrix", feature = "table", feature = $input_type_string))]
262          (Value::[<Matrix $input_type:camel>](mat), Value::Kind(ValueKind::Table(tbl, sze))) => {
263            let in_shape = mat.shape();
264            let tbl_cols = tbl.len();
265            let mat_knd = ValueKind::[<$input_type:camel>];
266            // Verify the table has the correct number of columns
267            if in_shape[1] != tbl_cols {
268              return Err(MechError::new(
269                ConvertIncorrectNumberOfColumnsError{from: in_shape[1], to: tbl_cols},
270                None,
271              ).with_compiler_loc());
272            }
273            let resolved_tbl = resolve_table_column_kinds(&tbl, mat_knd)?;
274            // Create a blank table matching the requested size, or source rows if unspecified.
275            let out_rows = if sze == 0 { in_shape[0] } else { sze };
276            let out = MechTable::from_kind(ValueKind::Table(resolved_tbl, out_rows))?;
277            Ok(Box::new(ConvertMat2Table::<$input_type>{arg: mat.clone(), out: Ref::new(out)}))
278          }
279          $(
280            #[cfg(all(feature = $input_type_string, feature = $target_type_string))]
281            (Value::[<$input_type:camel>](arg), Value::Kind(ValueKind::[<$target_type:camel>])) => {Ok(Box::new(ConvertScalarToScalarBasic{arg: arg.clone(), out: Ref::new($target_type::default())}))},
282          )+
283        )+
284        #[cfg(feature = "rational")]
285        (Value::R64(ref rat), Value::Kind(ValueKind::F64)) => {
286          Ok(Box::new(ConvertSRationalToF64{arg: rat.clone(), out: Ref::new(f64::default())}))
287        }
288        #[cfg(all(feature = "atom", feature = "enum"))]
289        (Value::Atom(atom), Value::Kind(ValueKind::Enum(enum_id, enum_variant_name))) => {
290          let atom_brrw = atom.borrow();
291          let variant_id = atom_brrw.id();
292          let atom_name = atom_brrw.name();
293          let variants = vec![(variant_id,None)];
294          let dictionary = (*atom_brrw).dictionary();
295          let enm = MechEnum{id: enum_id, variants, names: dictionary};
296          let val = Ref::new(enm.clone());
297          todo!("This isn't finished yet");
298          Ok(Box::new(ConvertSEnum{out: val}))
299        }
300        (Value::Empty, Value::Kind(ValueKind::Empty)) => {
301          Ok(Box::new(ConvertSEmpty { out: Ref::new(Value::Empty) }))
302        }
303        (value, Value::Kind(ValueKind::Option(inner_kind))) => {
304          let converted = if value == Value::Empty {
305            Value::Empty
306          } else {
307            value
308              .convert_to(inner_kind.as_ref())
309              .ok_or_else(|| MechError::new(
310                UnsupportedConversionError { from: value.kind(), to: ValueKind::Option(inner_kind.clone()) },
311                None,
312              ).with_compiler_loc())?
313          };
314          Ok(Box::new(ConvertSEmpty { out: Ref::new(converted) }))
315        }
316        x => Err(MechError::new(
317            UnsupportedConversionError{from: x.0.kind(), to: x.1.kind()},
318            None,
319          ).with_compiler_loc()
320        ),
321      }
322    }
323  }
324}
325
326#[derive(Debug)]
327pub struct ConvertScalarToScalar<F, T> {
328  pub arg: Ref<F>,
329  pub out: Ref<T>,
330}
331
332impl<F, T> MechFunctionImpl for ConvertScalarToScalar<F, T>
333where
334  Ref<T>: ToValue,
335  F: LosslessInto<T> + Debug + Clone,
336  T: Debug,
337{
338  fn solve(&self) {
339    let arg_ptr = self.arg.as_ptr();
340    let out_ptr = self.out.as_mut_ptr();
341    unsafe {
342      let out_ref: &mut T = &mut *out_ptr;
343      let arg_ref: &F = &*arg_ptr;
344      *out_ref = arg_ref.clone().lossless_into();
345    }
346  }
347  fn out(&self) -> Value { self.out.to_value() }
348  fn to_string(&self) -> String { format!("{:#?}", self) }
349}
350#[cfg(feature = "compiler")]
351impl<F, T> MechFunctionCompiler for ConvertScalarToScalar<F, T> 
352where
353  F: ConstElem + CompileConst + AsValueKind,
354  T: ConstElem + CompileConst + AsValueKind,
355{
356  fn compile(&self, ctx: &mut CompileCtx) -> MResult<Register> {
357    let name = format!("ConvertScalarToScalar<{},{}>", F::as_value_kind(), T::as_value_kind());
358    compile_unop!(name, self.out, self.arg, ctx, FeatureFlag::Builtin(FeatureKind::Convert));
359  }
360}
361
362#[derive(Debug)]
363pub struct ConvertScalarToScalarBasic<F, T> {
364  pub arg: Ref<F>,
365  pub out: Ref<T>,
366}
367
368impl<F, T> MechFunctionImpl for ConvertScalarToScalarBasic<F, T>
369where
370  Ref<T>: ToValue,
371  F: Debug + Clone,
372  T: Debug + LossyFrom<F>,
373{
374  fn solve(&self) {
375    let arg_ptr = self.arg.as_ptr();
376    let out_ptr = self.out.as_mut_ptr();
377    unsafe {
378      let out_ref: &mut T = &mut *out_ptr;
379      let arg_ref: &F = &*arg_ptr;
380      *out_ref = T::lossy_from(arg_ref.clone());
381    }
382  }
383  fn out(&self) -> Value { self.out.to_value() }
384  fn to_string(&self) -> String { format!("{:#?}", self) }
385}
386#[cfg(feature = "compiler")]
387impl<F,T> MechFunctionCompiler for ConvertScalarToScalarBasic<F, T> 
388where
389  F: ConstElem + CompileConst + AsValueKind,
390  T: ConstElem + CompileConst + AsValueKind,
391{
392  fn compile(&self, ctx: &mut CompileCtx) -> MResult<Register> {
393    let name = format!("ConvertScalarToScalarBasic<{},{}>", F::as_value_kind(), T::as_value_kind());
394    compile_unop!(name, self.out, self.arg, ctx, FeatureFlag::Builtin(FeatureKind::Convert));
395  }
396}
397
398fn impl_conversion_fxn(source_value: Value, target_kind: Value) -> MResult<Box<dyn MechFunction>>  {
399  if let (Value::Typed(inner, declared_kind), Value::Kind(target_vk)) = (&source_value, &target_kind) {
400    if declared_kind == target_vk {
401      return Ok(Box::new(ConvertSEmpty {
402        out: Ref::new(Value::Typed(inner.clone(), declared_kind.clone())),
403      }));
404    }
405    return impl_conversion_fxn((**inner).clone(), target_kind);
406  }
407
408  match (&source_value, &target_kind) {
409    #[cfg(all(feature = "matrix", feature = "set"))]
410    (source, Value::Kind(ValueKind::Set(target_kind, _))) => {
411      if let Some(values) = matrix_to_values(source) {
412        let converted_values = values
413          .into_iter()
414          .map(|value| value.convert_to(target_kind))
415          .collect::<Option<Vec<_>>>();
416        if let Some(converted_values) = converted_values {
417          return Ok(Box::new(ConvertMatToSet {
418            arg: source_value.clone(),
419            target_kind: target_kind.as_ref().clone(),
420            out: Ref::new(MechSet::from_vec(converted_values)),
421          }));
422        }
423      }
424    }
425    #[cfg(all(feature = "rational", feature = "f64"))]
426    (Value::R64(r), Value::Kind(ValueKind::F64)) => {return Ok(Box::new(ConvertScalarToScalar{arg: r.clone(),out: Ref::new(f64::default()),}));}
427    #[cfg(all(feature = "matrix", feature = "table", feature = "string"))]
428    (Value::MatrixString(mat), Value::Kind(ValueKind::Table(tbl, sze))) => {
429      let in_shape = mat.shape();
430      // Verify the table has the correct number of columns
431      if in_shape[1] != tbl.len() {
432        return Err(MechError::new(
433          ConvertIncorrectNumberOfColumnsError{from: in_shape[1], to: tbl.len()},
434          None,
435        ).with_compiler_loc());
436      }
437      let resolved_tbl = resolve_table_column_kinds(tbl, ValueKind::String)?;
438      // Create a blank table matching the requested size, or source rows if unspecified.
439      let out_rows = if *sze == 0 { in_shape[0] } else { *sze };
440      let out = MechTable::from_kind(ValueKind::Table(resolved_tbl, out_rows))?;
441      return Ok(Box::new(ConvertMat2Table::<String>{arg: mat.clone(), out: Ref::new(out)}));
442    }
443    #[cfg(all(feature = "matrix", feature = "table", feature = "bool"))]
444    (Value::MatrixBool(mat), Value::Kind(ValueKind::Table(tbl, sze))) => {
445      let in_shape = mat.shape();
446      // Verify the table has the correct number of columns
447      if in_shape[1] != tbl.len() {
448        return Err(MechError::new(
449          ConvertIncorrectNumberOfColumnsError{from: in_shape[1], to: tbl.len()},
450          None,
451        ).with_compiler_loc());
452      }
453      let resolved_tbl = resolve_table_column_kinds(tbl, ValueKind::Bool)?;
454      // Create a blank table matching the requested size, or source rows if unspecified.
455      let out_rows = if *sze == 0 { in_shape[0] } else { *sze };
456      let out = MechTable::from_kind(ValueKind::Table(resolved_tbl, out_rows))?;
457      return Ok(Box::new(ConvertMat2Table::<bool>{arg: mat.clone(), out: Ref::new(out)}));
458    }
459    #[cfg(all(feature = "matrix", feature = "table"))]
460    (Value::Table(table), Value::Kind(ValueKind::Matrix(target_kind, dims))) => {
461      let table = table.borrow();
462      let rows = table.rows();
463      let cols = table.cols();
464
465      if !dims.is_empty() {
466        let dim_count = dims.iter().product::<usize>();
467        if dim_count != rows * cols {
468          return Err(MechError::new(
469            ConvertIncorrectNumberOfColumnsError { from: rows * cols, to: dim_count },
470            None,
471          ).with_compiler_loc());
472        }
473      }
474
475      let mut elements = Vec::with_capacity(rows * cols);
476      for (_column_id, (_kind, column_values)) in table.data.iter() {
477        elements.extend(column_values.as_vec());
478      }
479
480      if target_kind.as_ref() != &ValueKind::Any {
481        return Err(MechError::new(
482          UnhandledFunctionArgumentKind2 {
483            arg: (
484              source_value.kind(),
485              ValueKind::Matrix(target_kind.clone(), dims.clone()),
486            ),
487            fxn_name: "convert/scalar".to_string(),
488          },
489          None,
490        ).with_compiler_loc());
491      }
492
493      let matrix = Value::MatrixValue(Matrix::from_vec(elements, rows, cols));
494      return Ok(Box::new(ConvertSEmpty { out: Ref::new(matrix) }));
495    }
496    _ =>(),
497  }
498  impl_conversion_match_arms!(
499    (source_value, target_kind),
500    i8, "i8" => String, "string", i8, "i8", i16, "i16", i32, "i32", i64, "i64", i128, "i128", u8, "u8", u16, "u16", u32, "u32", u64, "u64", u128, "u128", f32, "f32", f64, "f64";
501    i16, "i16" => String, "string", i8, "i8", i16, "i16", i32, "i32", i64, "i64", i128, "i128", u8, "u8", u16, "u16", u32, "u32", u64, "u64", u128, "u128", f32, "f32", f64, "f64";
502    i32, "i32" => String, "string", i8, "i8", i16, "i16", i32, "i32", i64, "i64", i128, "i128", u8, "u8", u16, "u16", u32, "u32", u64, "u64", u128, "u128", f32, "f32", f64, "f64";
503    i64, "i64" => String, "string", i8, "i8", i16, "i16", i32, "i32", i64, "i64", i128, "i128", u8, "u8", u16, "u16", u32, "u32", u64, "u64", u128, "u128", f32, "f32", f64, "f64";
504    i128, "i128" => String, "string", i8, "i8", i16, "i16", i32, "i32", i64, "i64", i128, "i128", u8, "u8", u16, "u16", u32, "u32", u64, "u64", u128, "u128", f32, "f32", f64, "f64";
505    u8, "u8" => String, "string", i8, "i8", i16, "i16", i32, "i32", i64, "i64", i128, "i128", u8, "u8", u16, "u16", u32, "u32", u64, "u64", u128, "u128", f32, "f32", f64, "f64";
506    u16, "u16" => String, "string", i8, "i8", i16, "i16", i32, "i32", i64, "i64", i128, "i128", u8, "u8", u16, "u16", u32, "u32", u64, "u64", u128, "u128", f32, "f32", f64, "f64";
507    u32, "u32" => String, "string", i8, "i8", i16, "i16", i32, "i32", i64, "i64", i128, "i128", u8, "u8", u16, "u16", u32, "u32", u64, "u64", u128, "u128", f32, "f32", f64, "f64";
508    u64, "u64" => String, "string", i8, "i8", i16, "i16", i32, "i32", i64, "i64", i128, "i128", u8, "u8", u16, "u16", u32, "u32", u64, "u64", u128, "u128", f32, "f32", f64, "f64";
509    u128, "u128" => String, "string", i8, "i8", i16, "i16", i32, "i32", i64, "i64", i128, "i128", u8, "u8", u16, "u16", u32, "u32", u64, "u64", u128, "u128", f32, "f32", f64, "f64";
510    f32, "f32" => String, "string", i8, "i8", i16, "i16", i32, "i32", i64, "i64", i128, "i128", u8, "u8", u16, "u16", u32, "u32", u64, "u64", u128, "u128", f32, "f32", f64, "f64";
511    f64, "f64" => String, "string", i8, "i8", i16, "i16", i32, "i32", i64, "i64", i128, "i128", u8, "u8", u16, "u16", u32, "u32", u64, "u64", u128, "u128", f32, "f32", f64, "f64", R64, "rational";
512    R64, "rational" => String, "string", f64, "f64";
513    String, "string" => String, "string";
514    bool, "bool" => String, "string", bool, "bool";
515  )
516}
517
518pub struct ConvertKind {}
519
520impl NativeFunctionCompiler for ConvertKind {
521  fn compile(&self, arguments: &Vec<Value>) -> MResult<Box<dyn MechFunction>> {
522    if arguments.len() != 2 {
523      return Err(MechError::new(IncorrectNumberOfArguments { expected: 1, found: arguments.len() }, None).with_compiler_loc());
524    }
525    let source_value = arguments[0].clone();
526    let target_kind = arguments[1].clone();
527    match impl_conversion_fxn(source_value.clone(), target_kind.clone()) {
528      Ok(fxn) => Ok(fxn),
529      Err(_) => {
530        match source_value {
531          Value::MutableReference(rhs) => impl_conversion_fxn(rhs.borrow().clone(), target_kind.clone()),
532          #[cfg(feature = "atom")]
533          Value::Atom(ref atom_id) => impl_conversion_fxn(source_value, target_kind.clone()),
534          #[cfg(all(feature = "matrix", feature = "u8"))]
535          Value::MatrixU8(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
536          #[cfg(all(feature = "matrix", feature = "u16"))]
537          Value::MatrixU16(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
538          #[cfg(all(feature = "matrix", feature = "u32"))]
539          Value::MatrixU32(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
540          #[cfg(all(feature = "matrix", feature = "u64"))]
541          Value::MatrixU64(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
542          #[cfg(all(feature = "matrix", feature = "u128"))]
543          Value::MatrixU128(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
544          #[cfg(all(feature = "matrix", feature = "i8"))]
545          Value::MatrixI8(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
546          #[cfg(all(feature = "matrix", feature = "i16"))]
547          Value::MatrixI16(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
548          #[cfg(all(feature = "matrix", feature = "i32"))]
549          Value::MatrixI32(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
550          #[cfg(all(feature = "matrix", feature = "i64"))]
551          Value::MatrixI64(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
552          #[cfg(all(feature = "matrix", feature = "i128"))]
553          Value::MatrixI128(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
554          #[cfg(all(feature = "matrix", feature = "f32"))]
555          Value::MatrixF32(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
556          #[cfg(all(feature = "matrix", feature = "f64"))]
557          Value::MatrixF64(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
558          x => Err(MechError::new(
559              UnhandledFunctionArgumentKind2 { arg: (arguments[0].kind(), arguments[1].kind()), fxn_name: "convert/scalar".to_string() },
560              None,
561            ).with_compiler_loc()
562          ),
563        }
564      }
565    }
566  }
567}
568
569#[derive(Debug, Clone)]
570pub struct ColumnConvertKindMismatchError {
571  pub from: ValueKind,
572  pub to: ValueKind,
573}
574
575impl MechErrorKind for ColumnConvertKindMismatchError {
576  fn name(&self) -> &str { "ColumnTypeMismatch" }
577  fn message(&self) -> String {
578    format!(
579      "Matrix column kind {:?} does not match table column kind {:?}. Conversion requires the element types to be compatible.",
580      self.from, self.to
581    )
582  }
583}
584
585#[derive(Debug, Clone)]
586pub struct ConvertIncorrectNumberOfColumnsError {
587  pub from: usize,
588  pub to: usize,
589}
590impl MechErrorKind for ConvertIncorrectNumberOfColumnsError {
591  fn name(&self) -> &str { "IncorrectNumberOfColumns" }
592  fn message(&self) -> String {
593    format!(
594      "Matrix has {} columns, but table expects {}. Column count must match for assignment.",
595      self.from, self.to
596    )
597  }
598}