1#[macro_use]
2use crate::stdlib::*;
3
4#[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 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 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 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 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 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 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 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}