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(variant_id), Value::Kind(ValueKind::Enum(enum_id))) => {
169          let variants = vec![(variant_id.borrow().0,None)];
170          let enm = MechEnum{id: enum_id, variants};
171          let val = Ref::new(enm.clone());
172          Ok(Box::new(ConvertSEnum{out: val}))
173        }
174        x => Err(MechError2::new(
175            UnsupportedConversionError{from: x.0.kind(), to: x.1.kind()},
176            None,
177          ).with_compiler_loc()
178        ),
179      }
180    }
181  }
182}
183
184#[derive(Debug)]
185pub struct ConvertScalarToScalar<F, T> {
186  pub arg: Ref<F>,
187  pub out: Ref<T>,
188}
189
190impl<F, T> MechFunctionImpl for ConvertScalarToScalar<F, T>
191where
192  Ref<T>: ToValue,
193  F: LosslessInto<T> + Debug + Clone,
194  T: Debug,
195{
196  fn solve(&self) {
197    let arg_ptr = self.arg.as_ptr();
198    let out_ptr = self.out.as_mut_ptr();
199    unsafe {
200      let out_ref: &mut T = &mut *out_ptr;
201      let arg_ref: &F = &*arg_ptr;
202      *out_ref = arg_ref.clone().lossless_into();
203    }
204  }
205  fn out(&self) -> Value { self.out.to_value() }
206  fn to_string(&self) -> String { format!("{:#?}", self) }
207}
208#[cfg(feature = "compiler")]
209impl<F, T> MechFunctionCompiler for ConvertScalarToScalar<F, T> 
210where
211  F: ConstElem + CompileConst + AsValueKind,
212  T: ConstElem + CompileConst + AsValueKind,
213{
214  fn compile(&self, ctx: &mut CompileCtx) -> MResult<Register> {
215    let name = format!("ConvertScalarToScalar<{},{}>", F::as_value_kind(), T::as_value_kind());
216    compile_unop!(name, self.out, self.arg, ctx, FeatureFlag::Builtin(FeatureKind::Convert));
217  }
218}
219
220#[derive(Debug)]
221pub struct ConvertScalarToScalarBasic<F, T> {
222  pub arg: Ref<F>,
223  pub out: Ref<T>,
224}
225
226impl<F, T> MechFunctionImpl for ConvertScalarToScalarBasic<F, T>
227where
228  Ref<T>: ToValue,
229  F: Debug + Clone,
230  T: Debug + LossyFrom<F>,
231{
232  fn solve(&self) {
233    let arg_ptr = self.arg.as_ptr();
234    let out_ptr = self.out.as_mut_ptr();
235    unsafe {
236      let out_ref: &mut T = &mut *out_ptr;
237      let arg_ref: &F = &*arg_ptr;
238      *out_ref = T::lossy_from(arg_ref.clone());
239    }
240  }
241  fn out(&self) -> Value { self.out.to_value() }
242  fn to_string(&self) -> String { format!("{:#?}", self) }
243}
244#[cfg(feature = "compiler")]
245impl<F,T> MechFunctionCompiler for ConvertScalarToScalarBasic<F, T> 
246where
247  F: ConstElem + CompileConst + AsValueKind,
248  T: ConstElem + CompileConst + AsValueKind,
249{
250  fn compile(&self, ctx: &mut CompileCtx) -> MResult<Register> {
251    let name = format!("ConvertScalarToScalarBasic<{},{}>", F::as_value_kind(), T::as_value_kind());
252    compile_unop!(name, self.out, self.arg, ctx, FeatureFlag::Builtin(FeatureKind::Convert));
253  }
254}
255
256fn impl_conversion_fxn(source_value: Value, target_kind: Value) -> MResult<Box<dyn MechFunction>>  {
257  match (&source_value, &target_kind) {
258    #[cfg(all(feature = "rational", feature = "f64"))]
259    (Value::R64(r), Value::Kind(ValueKind::F64)) => {return Ok(Box::new(ConvertScalarToScalar{arg: r.clone(),out: Ref::new(f64::default()),}));}
260    #[cfg(all(feature = "matrix", feature = "table", feature = "string"))]
261    (Value::MatrixString(ref mat), Value::Kind(ValueKind::Table(tbl, sze))) => {
262      let in_shape = mat.shape();
263      // Verify the table has the correct number of columns
264      if in_shape[1] != tbl.len() {
265        return Err(MechError2::new(
266          ConvertIncorrectNumberOfColumnsError{from: in_shape[1], to: tbl.len()},
267          None,
268        ).with_compiler_loc());
269      }
270      // Create a blank table, with as many rows as the matrix has
271      let out = MechTable::from_kind(ValueKind::Table(tbl.clone(), in_shape[0]))?;
272      return Ok(Box::new(ConvertMat2Table::<String>{arg: mat.clone(), out: Ref::new(out)}));
273    }
274    #[cfg(all(feature = "matrix", feature = "table", feature = "bool"))]
275    (Value::MatrixBool(ref mat), Value::Kind(ValueKind::Table(tbl, sze))) => {
276      let in_shape = mat.shape();
277      // Verify the table has the correct number of columns
278      if in_shape[1] != tbl.len() {
279        return Err(MechError2::new(
280          ConvertIncorrectNumberOfColumnsError{from: in_shape[1], to: tbl.len()},
281          None,
282        ).with_compiler_loc());
283      }
284      // Create a blank table, with as many rows as the matrix has
285      let out = MechTable::from_kind(ValueKind::Table(tbl.clone(), in_shape[0]))?;
286      return Ok(Box::new(ConvertMat2Table::<bool>{arg: mat.clone(), out: Ref::new(out)}));
287    }
288    _ =>(),
289  }
290  impl_conversion_match_arms!(
291    (source_value, target_kind),
292    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";
293    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";
294    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";
295    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";
296    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";
297    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";
298    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";
299    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";
300    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";
301    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";
302    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";
303    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";
304    R64, "rational" => String, "string", f64, "f64";
305    String, "string" => String, "string";
306    bool, "bool" => String, "string", bool, "bool";
307  )
308}
309
310pub struct ConvertKind {}
311
312impl NativeFunctionCompiler for ConvertKind {
313  fn compile(&self, arguments: &Vec<Value>) -> MResult<Box<dyn MechFunction>> {
314    if arguments.len() != 2 {
315      return Err(MechError2::new(IncorrectNumberOfArguments { expected: 1, found: arguments.len() }, None).with_compiler_loc());
316    }
317    let source_value = arguments[0].clone();
318    let target_kind = arguments[1].clone();
319    match impl_conversion_fxn(source_value.clone(), target_kind.clone()) {
320      Ok(fxn) => Ok(fxn),
321      Err(_) => {
322        match source_value {
323          Value::MutableReference(rhs) => impl_conversion_fxn(rhs.borrow().clone(), target_kind.clone()),
324          #[cfg(feature = "atom")]
325          Value::Atom(ref atom_id) => impl_conversion_fxn(source_value, target_kind.clone()),
326          #[cfg(all(feature = "matrix", feature = "u8"))]
327          Value::MatrixU8(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
328          #[cfg(all(feature = "matrix", feature = "u16"))]
329          Value::MatrixU16(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
330          #[cfg(all(feature = "matrix", feature = "u32"))]
331          Value::MatrixU32(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
332          #[cfg(all(feature = "matrix", feature = "u64"))]
333          Value::MatrixU64(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
334          #[cfg(all(feature = "matrix", feature = "u128"))]
335          Value::MatrixU128(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
336          #[cfg(all(feature = "matrix", feature = "i8"))]
337          Value::MatrixI8(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
338          #[cfg(all(feature = "matrix", feature = "i16"))]
339          Value::MatrixI16(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
340          #[cfg(all(feature = "matrix", feature = "i32"))]
341          Value::MatrixI32(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
342          #[cfg(all(feature = "matrix", feature = "i64"))]
343          Value::MatrixI64(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
344          #[cfg(all(feature = "matrix", feature = "i128"))]
345          Value::MatrixI128(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
346          #[cfg(all(feature = "matrix", feature = "f32"))]
347          Value::MatrixF32(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
348          #[cfg(all(feature = "matrix", feature = "f64"))]
349          Value::MatrixF64(ref mat) => impl_conversion_fxn(source_value, target_kind.clone()),
350          x => Err(MechError2::new(
351              UnhandledFunctionArgumentKind2 { arg: (arguments[0].kind(), arguments[1].kind()), fxn_name: "convert/scalar".to_string() },
352              None,
353            ).with_compiler_loc()
354          ),
355        }
356      }
357    }
358  }
359}
360
361#[derive(Debug, Clone)]
362pub struct ColumnConvertKindMismatchError {
363  pub from: ValueKind,
364  pub to: ValueKind,
365}
366
367impl MechErrorKind2 for ColumnConvertKindMismatchError {
368  fn name(&self) -> &str { "ColumnTypeMismatch" }
369  fn message(&self) -> String {
370    format!(
371      "Matrix column kind {:?} does not match table column kind {:?}. Conversion requires the element types to be compatible.",
372      self.from, self.to
373    )
374  }
375}
376
377#[derive(Debug, Clone)]
378pub struct ConvertIncorrectNumberOfColumnsError {
379  pub from: usize,
380  pub to: usize,
381}
382impl MechErrorKind2 for ConvertIncorrectNumberOfColumnsError {
383  fn name(&self) -> &str { "IncorrectNumberOfColumns" }
384  fn message(&self) -> String {
385    format!(
386      "Matrix has {} columns, but table expects {}. Column count must match for assignment.",
387      self.from, self.to
388    )
389  }
390}