Skip to main content

mech_interpreter/
structures.rs

1use crate::*;
2use std::collections::HashMap;
3
4// Structures
5// ----------------------------------------------------------------------------
6
7pub fn structure(strct: &Structure, env: Option<&Environment>, p: &Interpreter) -> MResult<Value> {
8  match strct {
9    Structure::Empty => Ok(Value::Empty),
10    #[cfg(feature = "record")]
11    Structure::Record(x) => record(&x, env, p),
12    #[cfg(feature = "matrix")]
13    Structure::Matrix(x) => matrix(&x, env, p),
14    #[cfg(feature = "table")]
15    Structure::Table(x) => table(&x, env, p),
16    #[cfg(feature = "tuple")]
17    Structure::Tuple(x) => tuple(&x, env, p),
18    #[cfg(all(feature = "tuple", feature = "atom"))]
19    Structure::TupleStruct(x) => tuple_struct(&x, env, p),
20    #[cfg(feature = "set")]
21    Structure::Set(x) => set(&x, env, p),
22    #[cfg(feature = "map")]
23    Structure::Map(x) => map(&x, env, p),
24    x => Err(MechError::new(FeatureNotEnabledError, Some(format!("Feature not enabled for `{:?}`", stringify!(x)))).with_compiler_loc()),
25  }
26}
27
28#[cfg(feature = "tuple")]
29pub fn tuple(tpl: &Tuple, env: Option<&Environment>, p: &Interpreter) -> MResult<Value> {
30  let mut elements = vec![];
31  for el in &tpl.elements {
32    let result = expression(el, env, p)?;
33    elements.push(Box::new(result));
34  }
35  let mech_tuple = Ref::new(MechTuple{elements});
36  Ok(Value::Tuple(mech_tuple))
37}
38
39#[cfg(all(feature = "tuple", feature = "atom"))]
40pub fn tuple_struct(tpl: &TupleStruct, env: Option<&Environment>, p: &Interpreter) -> MResult<Value> {
41  let mut elements = vec![];
42  let atom_value = atom(&Atom { name: tpl.name.clone() }, p);
43  elements.push(Box::new(atom_value));
44  let payload = expression(&tpl.value, env, p)?;
45  elements.push(Box::new(payload));
46  Ok(Value::Tuple(Ref::new(MechTuple { elements })))
47}
48
49#[cfg(feature = "map")]
50pub fn map(mp: &Map, env: Option<&Environment>, p: &Interpreter) -> MResult<Value> {
51  let mut m = IndexMap::new();
52  for b in &mp.elements {
53    let key = expression(&b.key, env, p)?;
54    let val = expression(&b.value, env, p)?;
55    m.insert(key,val);
56  }
57  
58  let key_kind = m.keys().next().unwrap().kind();
59  // verify that all the keys are the same kind:
60  for k in m.keys() {
61    if k.kind() != key_kind {
62      return Err(MechError::new(
63        MapKeyKindMismatchError{expected_kind: key_kind.clone(), actual_kind: k.kind().clone()},
64        None
65      ).with_compiler_loc());
66    }
67  }
68  
69  let value_kind = m.values().next().unwrap().kind();
70  // verify that all the values are the same kind:
71  for v in m.values() {
72    if v.kind() != value_kind {
73      return Err(MechError::new(
74        MapValueKindMismatchError{expected_kind: value_kind.clone(), actual_kind: v.kind().clone()},
75        None
76      ).with_compiler_loc());
77    }
78  }
79  Ok(Value::Map(Ref::new(MechMap{
80    num_elements: m.len(),
81    key_kind,
82    value_kind,
83    map: m
84  })))
85}
86
87#[cfg(feature = "record")]
88pub fn record(rcrd: &Record, env: Option<&Environment>, p: &Interpreter) -> MResult<Value> {
89  let mut data: IndexMap<u64,Value> = IndexMap::new();
90  let cols: usize = rcrd.bindings.len();
91  let mut kinds: Vec<ValueKind> = Vec::new();
92  let mut field_names: HashMap<u64,String> = HashMap::new();
93  for b in &rcrd.bindings {
94    let name_hash = b.name.hash();
95    let name_str = b.name.to_string();
96    let val = expression(&b.value, env, p)?;
97    let knd: ValueKind = match &b.kind {
98      Some(k) => kind_annotation(&k.kind, p)?.to_value_kind(&p.state.borrow().kinds)?,
99      None => val.kind(),
100    };
101    // If the kinds are different, do a conversion.
102    kinds.push(knd.clone());
103    #[cfg(feature = "convert")]
104    if knd != val.kind() {
105      let fxn = ConvertKind{}.compile(&vec![val.clone(), Value::Kind(knd.clone())]);
106      match fxn {
107        Ok(convert_fxn) => {
108          convert_fxn.solve();
109          let converted_result = convert_fxn.out();
110          p.state.borrow_mut().add_plan_step(convert_fxn);
111          data.insert(name_hash, converted_result);
112        },
113        Err(e) => {
114          return Err(MechError::new(
115            TableColumnKindMismatchError {
116              column_id: name_hash,
117              expected_kind: knd.clone(),
118              actual_kind: val.kind().clone(),
119            },
120            None
121          ).with_compiler_loc());
122        }
123      }
124    } else {
125      data.insert(name_hash, val);
126    }
127    #[cfg(not(feature = "convert"))]
128    if knd != val.kind() {
129      return Err(MechError{file: file!().to_string(), tokens: vec![], msg: "".to_string(), id: line!(), kind: MechErrorKind::KindMismatch(val.kind(),knd)});
130    } else {
131      data.insert(name_hash, val);
132    }
133    field_names.insert(name_hash, name_str);
134  }
135  Ok(Value::Record(Ref::new(MechRecord{
136    cols,
137    kinds,
138    data,
139    field_names,
140  })))
141}
142
143// Set
144// ----------------------------------------------------------------------------
145
146// Define a MechFunction that creaates a Set from a list of Values
147#[cfg(feature = "set")]
148#[derive(Debug)]
149pub struct ValueSet {
150  pub out: Ref<MechSet>,
151}
152#[cfg(feature = "set")]
153#[cfg(feature = "functions")]
154impl MechFunctionImpl for ValueSet {
155  fn solve(&self) {}
156  fn out(&self) -> Value { Value::Set(self.out.clone()) }
157  fn to_string(&self) -> String { format!("{:#?}", self) }
158}
159#[cfg(feature = "set")]
160#[cfg(feature = "functions")]
161impl MechFunctionFactory for ValueSet {
162  fn new(args: FunctionArgs) -> MResult<Box<dyn MechFunction>> {
163    match args {
164      FunctionArgs::Nullary(out) => {
165        let out: Ref<MechSet> = unsafe{ out.as_unchecked().clone() };
166        Ok(Box::new(ValueSet {out}))
167      },
168      _ => Err(MechError::new(
169          IncorrectNumberOfArguments { expected: 0, found: args.len() },
170          None
171        ).with_compiler_loc()
172      ),
173    }
174  }
175}
176#[cfg(feature = "set")]
177#[cfg(feature = "compiler")]
178impl MechFunctionCompiler for ValueSet {
179  fn compile(&self, ctx: &mut CompileCtx) -> MResult<Register> {
180    compile_nullop!("set/define", self.out, ctx, FeatureFlag::Builtin(FeatureKind::Set));
181  }
182}
183#[cfg(feature = "set")]
184#[cfg(feature = "functions")]
185register_descriptor!{
186  FunctionDescriptor {
187    name: "set/define",
188    ptr: ValueSet::new,
189  }
190}
191
192#[cfg(feature = "set")]
193pub struct SetDefine {}
194#[cfg(feature = "set")]
195#[cfg(feature = "functions")]
196impl NativeFunctionCompiler for SetDefine {
197  fn compile(&self, arguments: &Vec<Value>) -> MResult<Box<dyn MechFunction>> {
198    Ok(Box::new(ValueSet {
199      out: Ref::new(MechSet::from_vec(arguments.clone())),
200    }))
201  }
202}
203#[cfg(feature = "set")]
204#[cfg(feature = "functions")]
205register_descriptor!{
206  FunctionCompilerDescriptor {
207    name: "set/define",
208    ptr: &SetDefine{},
209  }
210}
211
212#[cfg(feature = "set")]
213pub fn set(m: &Set, env: Option<&Environment>, p: &Interpreter) -> MResult<Value> { 
214  let mut elements = Vec::new();
215  for el in &m.elements {
216    let result = expression(el, env, p)?;
217    elements.push(result.clone());
218  }
219  let element_kind = if elements.len() > 0 {
220    elements[0].kind()
221  } else {
222    ValueKind::Empty
223  };
224  // Make sure all elements have the same kind
225  for el in &elements {
226    if el.kind() != element_kind {
227      return Err(MechError::new(
228        SetKindMismatchError{expected_kind: element_kind.clone(), actual_kind: el.kind().clone()},
229        None
230      ).with_compiler_loc());
231    }
232  }
233  #[cfg(feature = "functions")]
234  {
235    let new_fxn = SetDefine {}.compile(&elements)?;
236    new_fxn.solve();
237    let out = new_fxn.out();
238    let plan = p.plan();
239    let mut plan_brrw = plan.borrow_mut();
240    plan_brrw.push(new_fxn);
241    Ok(out)
242  }
243  #[cfg(not(feature = "functions"))]
244  {
245    Ok(Value::Set(Ref::new(MechSet::from_vec(elements))))
246  }
247}
248
249// Table
250// ----------------------------------------------------------------------------
251
252macro_rules! handle_value_kind {
253  ($value_kind:ident, $val:expr, $field_label:expr, $data_map:expr, $converter:ident) => {{
254    let mut vals = Vec::new();
255    let id = $field_label; // <- FIXED: it's already a u64
256    for x in $val.as_vec().iter() {
257      match x.$converter() {
258        Ok(u) => vals.push(u.to_value()),
259        Err(_) => {
260          return Err(MechError::new(
261            TableColumnKindMismatchError { 
262              column_id: id, 
263              expected_kind: $value_kind.clone(), 
264              actual_kind: x.kind() 
265            },
266            None
267          ).with_compiler_loc());
268        }
269      }
270    }
271    $data_map.insert(id, ($value_kind.clone(), Value::to_matrixd(vals.clone(), vals.len(), 1)));
272  }};
273}
274
275
276#[cfg(feature = "table")]
277fn handle_column_kind(
278    kind: ValueKind,
279    id: u64,
280    val: Matrix<Value>,
281    data_map: &mut IndexMap<u64,(ValueKind,Matrix<Value>)>
282) -> MResult<()> 
283{
284  match kind {
285    #[cfg(feature = "i8")]
286    ValueKind::I8   => handle_value_kind!(kind, val, id, data_map, as_i8),
287    #[cfg(feature = "i16")]
288    ValueKind::I16  => handle_value_kind!(kind, val, id, data_map, as_i16),
289    #[cfg(feature = "i32")]
290    ValueKind::I32  => handle_value_kind!(kind, val, id, data_map, as_i32),
291    #[cfg(feature = "i64")]
292    ValueKind::I64  => handle_value_kind!(kind, val, id, data_map, as_i64),
293    #[cfg(feature = "i128")]
294    ValueKind::I128 => handle_value_kind!(kind, val, id, data_map, as_i128),
295
296    #[cfg(feature = "u8")]
297    ValueKind::U8   => handle_value_kind!(kind, val, id, data_map, as_u8),
298    #[cfg(feature = "u16")]
299    ValueKind::U16  => handle_value_kind!(kind, val, id, data_map, as_u16),
300    #[cfg(feature = "u32")]
301    ValueKind::U32  => handle_value_kind!(kind, val, id, data_map, as_u32),
302    #[cfg(feature = "u64")]
303    ValueKind::U64  => handle_value_kind!(kind, val, id, data_map, as_u64),
304    #[cfg(feature = "u128")]
305    ValueKind::U128 => handle_value_kind!(kind, val, id, data_map, as_u128),
306
307    #[cfg(feature = "f32")]
308    ValueKind::F32  => handle_value_kind!(kind, val, id, data_map, as_f32),
309    #[cfg(feature = "f64")]
310    ValueKind::F64  => handle_value_kind!(kind, val, id, data_map, as_f64),
311
312    #[cfg(feature = "string")]
313    ValueKind::String => handle_value_kind!(kind, val, id, data_map, as_string),
314
315    #[cfg(feature = "complex")]
316    ValueKind::C64 => handle_value_kind!(kind, val, id, data_map, as_c64),
317
318    #[cfg(feature = "rational")]
319    ValueKind::R64 => handle_value_kind!(kind, val, id, data_map, as_r64),
320
321    #[cfg(feature = "bool")]
322    ValueKind::Bool => {
323      let vals: Vec<Value> = val.as_vec()
324          .iter()
325          .map(|x| x.as_bool().unwrap().to_value())
326          .collect();
327      data_map.insert(id, (ValueKind::Bool, Value::to_matrix(vals.clone(), vals.len(), 1)));
328    }
329    ValueKind::Any => {
330      let vals: Vec<Value> = val.as_vec()
331          .iter()
332          .map(|x| x.clone())
333          .collect();
334      data_map.insert(id, (ValueKind::Any, Value::to_matrix(vals.clone(), vals.len(), 1)));
335    }
336
337    x => {
338      println!("Unsupported kind in table column: {:?}", x);
339      todo!()
340    }
341  }
342
343  Ok(())
344}
345
346#[cfg(feature = "table")]
347pub fn table(t: &Table, env: Option<&Environment>, p: &Interpreter) -> MResult<Value> { 
348  let mut rows = vec![];
349  let headings = table_header(&t.header, env, p)?;
350  let mut cols = 0;
351
352  // Interpret rows
353  for row in &t.rows {
354    let result = table_row(row, env, p)?;
355    cols = result.len();
356    rows.push(result);
357  }
358
359  // Allocate columns
360  let mut data = vec![Vec::<Value>::new(); cols];
361
362  // Populate columns
363  for row in rows {
364    for (ix, el) in row.into_iter().enumerate() {
365      data[ix].push(el);
366    }
367  }
368
369  // Build table
370  let mut data_map: IndexMap<u64,(ValueKind,Matrix<Value>)> = IndexMap::new();
371
372  for ((id, knd, _name), column) in headings.iter().zip(data.iter()) {
373    let id_u64 = id.as_u64().unwrap().borrow().clone();
374
375    // Infer kind if None
376    let actual_kind = match knd {
377      ValueKind::None => {
378        match column.first() {
379          Some(v) => v.kind(),
380          None => ValueKind::String, // default for empty column
381        }
382      }
383      _ => knd.clone(),
384    };
385
386    // Convert column to matrix
387    let val = Value::to_matrix(column.clone(), column.len(), 1);
388
389    // Dispatch conversion
390    handle_column_kind(actual_kind, id_u64, val, &mut data_map)?;
391  }
392
393  // Assign names
394  let names: HashMap<u64, String> = headings.iter()
395      .map(|(id, _, name)| (id.as_u64().unwrap().borrow().clone(), name.to_string()))
396      .collect();
397
398  let tbl = MechTable::new(t.rows.len(), cols, data_map.clone(), names);
399  Ok(Value::Table(Ref::new(tbl)))
400}
401
402#[cfg(feature = "kind_annotation")]
403pub fn table_header(fields: &TableHeader, env: Option<&Environment>, p: &Interpreter) -> MResult<Vec<(Value,ValueKind,Identifier)>> {
404  let mut headings: Vec<(Value,ValueKind,Identifier)> = Vec::new();
405  for f in &fields.0 {
406    let id = f.name.hash();
407    let kind = match &f.kind {
408      Some(k) => kind_annotation(&k.kind, p)?,
409      None => Kind::None,
410    };
411    headings.push((Value::Id(id),kind.to_value_kind(&p.state.borrow().kinds)?,f.name.clone()));
412  }
413  Ok(headings)
414}
415
416pub fn table_row(r: &TableRow, env: Option<&Environment>, p: &Interpreter) -> MResult<Vec<Value>> {
417  let mut row: Vec<Value> = Vec::new();
418  for col in &r.columns {
419    let result = table_column(col, env, p)?;
420    row.push(result);
421  }
422  Ok(row)
423}
424
425pub fn table_column(r: &TableColumn, env: Option<&Environment>, p: &Interpreter) -> MResult<Value> { 
426  expression(&r.element, env, p)
427}
428
429// Matrix
430// ----------------------------------------------------------------------------
431
432#[cfg(feature = "matrix")]
433pub fn matrix(m: &Mat, env: Option<&Environment>, p: &Interpreter) -> MResult<Value> {
434  let plan = p.plan();
435  let mut shape = vec![0, 0];
436  let mut col: Vec<Value> = Vec::new();
437  let mut kind = ValueKind::Empty;
438  #[cfg(feature = "matrix_horzcat")]
439  {
440    for row in &m.rows {
441      let result = matrix_row(row, env, p)?;
442      if shape == vec![0,0] {
443        shape = result.shape();
444        kind = result.kind();
445        col.push(result);
446      } else if shape[1] == result.shape()[1] {
447        col.push(result);
448      } else {
449        return Err(MechError::new(
450            DimensionMismatch { dims: vec![shape[1], result.shape()[1]] },
451            None
452          ).with_compiler_loc()
453        );
454      }
455    }
456    if col.is_empty() {
457      return Ok(Value::MatrixValue(Matrix::from_vec(vec![], 0, 0)));
458    } else if col.len() == 1 {
459      return Ok(col[0].clone());
460    }
461  }
462  #[cfg(feature = "matrix_vertcat")]
463  {
464    let new_fxn = MatrixVertCat{}.compile(&col)?;
465    new_fxn.solve();
466    let out = new_fxn.out();
467    let mut plan_brrw = plan.borrow_mut();
468    plan_brrw.push(new_fxn);
469    return Ok(out);
470  }
471  return Err(MechError::new(
472    FeatureNotEnabledError,
473    Some("matrix/vertcat feature not enabled".to_string())).with_compiler_loc()
474  );
475}
476
477#[cfg(feature = "matrix_horzcat")]
478pub fn matrix_row(r: &MatrixRow, env: Option<&Environment>, p: &Interpreter) -> MResult<Value> {
479  let plan = p.plan();
480  let mut row: Vec<Value> = Vec::new();
481  let mut shape = vec![0, 0];
482  let mut kind = ValueKind::Empty;
483  for col in &r.columns {
484    let result = matrix_column(col, env, p)?;
485    if shape == vec![0,0] {
486      shape = result.shape();
487      kind = result.kind();
488      row.push(result);
489    } else if shape[0] == result.shape()[0] {
490      row.push(result);
491    } else {
492      return Err(MechError::new(
493          DimensionMismatch { dims: vec![shape[0], result.shape()[0]] },
494          None
495        ).with_compiler_loc()
496      );
497    }
498  }
499  let new_fxn = MatrixHorzCat{}.compile(&row)?;
500  new_fxn.solve();
501  let out = new_fxn.out();
502  let mut plan_brrw = plan.borrow_mut();
503  plan_brrw.push(new_fxn);
504  Ok(out)
505}
506
507pub fn matrix_column(r: &MatrixColumn, env: Option<&Environment>, p: &Interpreter) -> MResult<Value> { 
508  expression(&r.element, env, p)
509}