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(MechError2::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#[cfg(all(feature = "matrix", feature = "table"))]
50#[derive(Debug)]
51struct ConvertMat2Table<T> {
52  arg: Matrix<T>,
53  out: Ref<MechTable>,
54}
55
56#[cfg(all(feature = "matrix", feature = "table"))]
57impl<T> MechFunctionImpl for ConvertMat2Table<T>
58where T: Debug + Clone + PartialEq + Into<Value> + 'static,
59{
60  fn solve(&self) {
61    let arg = &self.arg;
62    let mut out_table = self.out.borrow_mut();
63    let (rows, cols) = (arg.rows(), arg.cols());
64
65    for (col_ix, (ix, (col_kind, out_col))) in out_table.data.iter_mut().enumerate() {
66      for row_ix in 0..rows {
67        let value = arg.index2d(row_ix + 1, col_ix + 1).clone().into();
68        let converted_value = value.convert_to(col_kind).unwrap();
69        out_col.set_index1d(row_ix, converted_value);
70      }
71    }
72  }
73  fn out(&self) -> Value { Value::Table(self.out.clone()) }
74  fn to_string(&self) -> String { format!("{:#?}", self) }
75}
76#[cfg(all(feature = "compiler", feature = "matrix", feature = "table"))]
77impl<T> MechFunctionCompiler for ConvertMat2Table<T> 
78where
79  T: ConstElem + CompileConst + AsValueKind,
80{
81  fn compile(&self, ctx: &mut CompileCtx) -> MResult<Register> {
82    let mut registers = [0,0];
83
84    registers[0] = compile_register_brrw!(self.out, ctx);
85    registers[1] = compile_register!(self.arg, ctx);
86
87    ctx.features.insert(FeatureFlag::Builtin(FeatureKind::Convert));
88
89    ctx.emit_unop(
90      hash_str("ConvertMat2Table"),
91      registers[0],
92      registers[1],
93    );
94
95    return Ok(registers[0]);
96  }
97}
98
99#[cfg(all(feature = "rational", feature = "f64"))]
100#[derive(Debug)]
101struct ConvertSRationalToF64 {
102  arg: Ref<R64>,
103  out: Ref<f64>,
104}
105
106#[cfg(all(feature = "rational", feature = "f64"))]
107impl MechFunctionImpl for ConvertSRationalToF64 {
108  fn solve(&self) {
109    let arg_ptr = self.arg.as_ptr();
110    let out_ptr = self.out.as_mut_ptr();
111    unsafe{ *out_ptr = (*arg_ptr).into(); }
112  }
113  fn out(&self) -> Value { Value::F64(self.out.clone()) }
114  fn to_string(&self) -> String { format!("{:#?}", self) }
115}
116#[cfg(all(feature = "compiler", feature = "rational", feature = "f64"))]
117impl MechFunctionCompiler for ConvertSRationalToF64 {
118  fn compile(&self, ctx: &mut CompileCtx) -> MResult<Register> {
119    let name = format!("ConvertSRationalToF64<f64>");
120    compile_unop!(name, self.out, self.arg, ctx, FeatureFlag::Builtin(FeatureKind::Convert));
121  }
122}
123
124macro_rules! impl_conversion_match_arms {
125  ($arg:expr, $($input_type:ident, $input_type_string:tt => $($target_type:ident, $target_type_string:tt),+);+ $(;)?) => {
126    paste!{
127      match $arg {
128        $(
129          #[cfg(all(feature = "matrix", feature = "table", feature = $input_type_string))]
130          (Value::[<Matrix $input_type:camel>](mat), Value::Kind(ValueKind::Table(tbl, sze))) => {
131            let in_shape = mat.shape();
132            let tbl_cols = tbl.len();
133            let mat_knd = ValueKind::[<$input_type:camel>];
134            // Verify the table has the correct number of columns
135            if in_shape[1] != tbl_cols {
136              return Err(MechError2::new(
137                ConvertIncorrectNumberOfColumnsError{from: in_shape[1], to: tbl_cols},
138                None,
139              ).with_compiler_loc());
140            }
141            // Verify each column of the matrix can be converted to the target type of the table
142            for (_, knd) in &tbl {
143              if *knd == mat_knd {
144                continue;
145              } else if mat_knd.is_convertible_to(knd) {
146                continue;
147              } else {
148                return Err(MechError2::new(
149                  ColumnConvertKindMismatchError{from: mat_knd, to: knd.clone()},
150                  None,
151                ).with_compiler_loc());
152              }
153            }
154            // Create a blank table, with as many rows as the matrix has
155            let out = MechTable::from_kind(ValueKind::Table(tbl.clone(), in_shape[0]))?;
156            Ok(Box::new(ConvertMat2Table::<$input_type>{arg: mat.clone(), out: Ref::new(out)}))
157          }
158          $(
159            #[cfg(all(feature = $input_type_string, feature = $target_type_string))]
160            (Value::[<$input_type:camel>](arg), Value::Kind(ValueKind::[<$target_type:camel>])) => {Ok(Box::new(ConvertScalarToScalarBasic{arg: arg.clone(), out: Ref::new($target_type::default())}))},
161          )+
162        )+
163        #[cfg(feature = "rational")]
164        (Value::R64(ref rat), Value::Kind(ValueKind::F64)) => {
165          Ok(Box::new(ConvertSRationalToF64{arg: rat.clone(), out: Ref::new(f64::default())}))
166        }
167        #[cfg(all(feature = "atom", feature = "enum"))]
168        (Value::Atom(atom), Value::Kind(ValueKind::Enum(enum_id, enum_variant_name))) => {
169          let atom_brrw = atom.borrow();
170          let variant_id = atom_brrw.id();
171          let atom_name = atom_brrw.name();
172          let variants = vec![(variant_id,None)];
173          let dictionary = (*atom_brrw).dictionary();
174          let enm = MechEnum{id: enum_id, variants, names: dictionary};
175          let val = Ref::new(enm.clone());
176          todo!("This isn't finished yet");
177          Ok(Box::new(ConvertSEnum{out: val}))
178        }
179        x => Err(MechError2::new(
180            UnsupportedConversionError{from: x.0.kind(), to: x.1.kind()},
181            None,
182          ).with_compiler_loc()
183        ),
184      }
185    }
186  }
187}
188
189#[derive(Debug)]
190pub struct ConvertScalarToScalar<F, T> {
191  pub arg: Ref<F>,
192  pub out: Ref<T>,
193}
194
195impl<F, T> MechFunctionImpl for ConvertScalarToScalar<F, T>
196where
197  Ref<T>: ToValue,
198  F: LosslessInto<T> + Debug + Clone,
199  T: Debug,
200{
201  fn solve(&self) {
202    let arg_ptr = self.arg.as_ptr();
203    let out_ptr = self.out.as_mut_ptr();
204    unsafe {
205      let out_ref: &mut T = &mut *out_ptr;
206      let arg_ref: &F = &*arg_ptr;
207      *out_ref = arg_ref.clone().lossless_into();
208    }
209  }
210  fn out(&self) -> Value { self.out.to_value() }
211  fn to_string(&self) -> String { format!("{:#?}", self) }
212}
213#[cfg(feature = "compiler")]
214impl<F, T> MechFunctionCompiler for ConvertScalarToScalar<F, T> 
215where
216  F: ConstElem + CompileConst + AsValueKind,
217  T: ConstElem + CompileConst + AsValueKind,
218{
219  fn compile(&self, ctx: &mut CompileCtx) -> MResult<Register> {
220    let name = format!("ConvertScalarToScalar<{},{}>", F::as_value_kind(), T::as_value_kind());
221    compile_unop!(name, self.out, self.arg, ctx, FeatureFlag::Builtin(FeatureKind::Convert));
222  }
223}
224
225#[derive(Debug)]
226pub struct ConvertScalarToScalarBasic<F, T> {
227  pub arg: Ref<F>,
228  pub out: Ref<T>,
229}
230
231impl<F, T> MechFunctionImpl for ConvertScalarToScalarBasic<F, T>
232where
233  Ref<T>: ToValue,
234  F: Debug + Clone,
235  T: Debug + LossyFrom<F>,
236{
237  fn solve(&self) {
238    let arg_ptr = self.arg.as_ptr();
239    let out_ptr = self.out.as_mut_ptr();
240    unsafe {
241      let out_ref: &mut T = &mut *out_ptr;
242      let arg_ref: &F = &*arg_ptr;
243      *out_ref = T::lossy_from(arg_ref.clone());
244    }
245  }
246  fn out(&self) -> Value { self.out.to_value() }
247  fn to_string(&self) -> String { format!("{:#?}", self) }
248}
249#[cfg(feature = "compiler")]
250impl<F,T> MechFunctionCompiler for ConvertScalarToScalarBasic<F, T> 
251where
252  F: ConstElem + CompileConst + AsValueKind,
253  T: ConstElem + CompileConst + AsValueKind,
254{
255  fn compile(&self, ctx: &mut CompileCtx) -> MResult<Register> {
256    let name = format!("ConvertScalarToScalarBasic<{},{}>", F::as_value_kind(), T::as_value_kind());
257    compile_unop!(name, self.out, self.arg, ctx, FeatureFlag::Builtin(FeatureKind::Convert));
258  }
259}
260
261fn impl_conversion_fxn(source_value: Value, target_kind: Value) -> MResult<Box<dyn MechFunction>>  {
262  match (&source_value, &target_kind) {
263    #[cfg(all(feature = "rational", feature = "f64"))]
264    (Value::R64(r), Value::Kind(ValueKind::F64)) => {return Ok(Box::new(ConvertScalarToScalar{arg: r.clone(),out: Ref::new(f64::default()),}));}
265    #[cfg(all(feature = "matrix", feature = "table", feature = "string"))]
266    (Value::MatrixString(ref mat), Value::Kind(ValueKind::Table(tbl, sze))) => {
267      let in_shape = mat.shape();
268      // Verify the table has the correct number of columns
269      if in_shape[1] != tbl.len() {
270        return Err(MechError2::new(
271          ConvertIncorrectNumberOfColumnsError{from: in_shape[1], to: tbl.len()},
272          None,
273        ).with_compiler_loc());
274      }
275      // Create a blank table, with as many rows as the matrix has
276      let out = MechTable::from_kind(ValueKind::Table(tbl.clone(), in_shape[0]))?;
277      return Ok(Box::new(ConvertMat2Table::<String>{arg: mat.clone(), out: Ref::new(out)}));
278    }
279    #[cfg(all(feature = "matrix", feature = "table", feature = "bool"))]
280    (Value::MatrixBool(ref mat), Value::Kind(ValueKind::Table(tbl, sze))) => {
281      let in_shape = mat.shape();
282      // Verify the table has the correct number of columns
283      if in_shape[1] != tbl.len() {
284        return Err(MechError2::new(
285          ConvertIncorrectNumberOfColumnsError{from: in_shape[1], to: tbl.len()},
286          None,
287        ).with_compiler_loc());
288      }
289      // Create a blank table, with as many rows as the matrix has
290      let out = MechTable::from_kind(ValueKind::Table(tbl.clone(), in_shape[0]))?;
291      return Ok(Box::new(ConvertMat2Table::<bool>{arg: mat.clone(), out: Ref::new(out)}));
292    }
293    _ =>(),
294  }
295  impl_conversion_match_arms!(
296    (source_value, target_kind),
297    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";
298    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";
299    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";
300    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";
301    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";
302    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";
303    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";
304    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";
305    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";
306    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";
307    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";
308    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";
309    R64, "rational" => String, "string", f64, "f64";
310    String, "string" => String, "string";
311    bool, "bool" => String, "string", bool, "bool";
312  )
313}
314
315pub struct ConvertKind {}
316
317impl NativeFunctionCompiler for ConvertKind {
318  fn compile(&self, arguments: &Vec<Value>) -> MResult<Box<dyn MechFunction>> {
319    if arguments.len() != 2 {
320      return Err(MechError2::new(IncorrectNumberOfArguments { expected: 1, found: arguments.len() }, None).with_compiler_loc());
321    }
322    let source_value = arguments[0].clone();
323    let target_kind = arguments[1].clone();
324    match impl_conversion_fxn(source_value.clone(), target_kind.clone()) {
325      Ok(fxn) => Ok(fxn),
326      Err(_) => {
327        match source_value {
328          Value::MutableReference(rhs) => impl_conversion_fxn(rhs.borrow().clone(), target_kind.clone()),
329          #[cfg(feature = "atom")]
330          Value::Atom(ref atom_id) => impl_conversion_fxn(source_value, target_kind.clone()),
331          #[cfg(all(feature = "matrix", feature = "u8"))]
332          Value::MatrixU8(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
333          #[cfg(all(feature = "matrix", feature = "u16"))]
334          Value::MatrixU16(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
335          #[cfg(all(feature = "matrix", feature = "u32"))]
336          Value::MatrixU32(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
337          #[cfg(all(feature = "matrix", feature = "u64"))]
338          Value::MatrixU64(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
339          #[cfg(all(feature = "matrix", feature = "u128"))]
340          Value::MatrixU128(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
341          #[cfg(all(feature = "matrix", feature = "i8"))]
342          Value::MatrixI8(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
343          #[cfg(all(feature = "matrix", feature = "i16"))]
344          Value::MatrixI16(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
345          #[cfg(all(feature = "matrix", feature = "i32"))]
346          Value::MatrixI32(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
347          #[cfg(all(feature = "matrix", feature = "i64"))]
348          Value::MatrixI64(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
349          #[cfg(all(feature = "matrix", feature = "i128"))]
350          Value::MatrixI128(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
351          #[cfg(all(feature = "matrix", feature = "f32"))]
352          Value::MatrixF32(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
353          #[cfg(all(feature = "matrix", feature = "f64"))]
354          Value::MatrixF64(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
355          x => Err(MechError2::new(
356              UnhandledFunctionArgumentKind2 { arg: (arguments[0].kind(), arguments[1].kind()), fxn_name: "convert/scalar".to_string() },
357              None,
358            ).with_compiler_loc()
359          ),
360        }
361      }
362    }
363  }
364}
365
366#[derive(Debug, Clone)]
367pub struct ColumnConvertKindMismatchError {
368  pub from: ValueKind,
369  pub to: ValueKind,
370}
371
372impl MechErrorKind2 for ColumnConvertKindMismatchError {
373  fn name(&self) -> &str { "ColumnTypeMismatch" }
374  fn message(&self) -> String {
375    format!(
376      "Matrix column kind {:?} does not match table column kind {:?}. Conversion requires the element types to be compatible.",
377      self.from, self.to
378    )
379  }
380}
381
382#[derive(Debug, Clone)]
383pub struct ConvertIncorrectNumberOfColumnsError {
384  pub from: usize,
385  pub to: usize,
386}
387impl MechErrorKind2 for ConvertIncorrectNumberOfColumnsError {
388  fn name(&self) -> &str { "IncorrectNumberOfColumns" }
389  fn message(&self) -> String {
390    format!(
391      "Matrix has {} columns, but table expects {}. Column count must match for assignment.",
392      self.from, self.to
393    )
394  }
395}