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(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 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 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 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 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}