1use crate::*;
2use crate::tracing::{
3 format_trace,
4 format_trace_args,
5 summarize_function_pattern,
6 summarize_function_value,
7 summarize_values_with_kinds,
8};
9#[cfg(all(feature = "kind_annotation", feature = "enum"))]
10use std::collections::HashSet;
11use crate::*;
12
13#[derive(Clone, PartialEq, Eq, Debug)]
21pub enum FrameState {
22 Running,
23 Suspended,
24 Completed,
25}
26
27#[derive(Clone)]
30pub struct Frame {
31 plan: Plan,
32 ip: usize, locals: SymbolTableRef, out: Option<Value>, state: FrameState, }
37
38#[derive(Clone)]
40pub struct Stack {
41 frames: Vec<Frame>,
42}
43
44pub fn function_define(fxn_def: &FunctionDefine, p: &Interpreter) -> MResult<FunctionDefinition> {
47 let fxn_name_id = fxn_def.name.hash();
48 let mut new_fxn = FunctionDefinition::new(fxn_name_id, fxn_def.name.to_string(), fxn_def.clone());
49
50 for input_arg in &fxn_def.input {
52 new_fxn
53 .input
54 .insert(input_arg.name.hash(), input_arg.kind.clone());
55 }
56
57 for output_arg in &fxn_def.output {
59 new_fxn
60 .output
61 .insert(output_arg.name.hash(), output_arg.kind.clone());
62 }
63
64 let functions = p.functions();
67 let mut functions_brrw = functions.borrow_mut();
68 functions_brrw
69 .user_functions
70 .insert(fxn_name_id, new_fxn.clone());
71 functions_brrw
72 .dictionary
73 .borrow_mut()
74 .insert(fxn_name_id, fxn_def.name.to_string());
75 p.state
76 .borrow()
77 .dictionary
78 .borrow_mut()
79 .insert(fxn_name_id, fxn_def.name.to_string());
80
81 Ok(new_fxn)
82}
83
84pub fn function_call(fxn_call: &FunctionCall, env: Option<&Environment>, p: &Interpreter) -> MResult<Value> {
91 let functions = p.functions();
92 let fxn_name_id = fxn_call.name.hash();
93
94 if let Some(user_fxn) = { functions.borrow().user_functions.get(&fxn_name_id).cloned() } {
96 let mut input_arg_values = vec![];
97 for (_, arg_expr) in fxn_call.args.iter() {
98 input_arg_values.push(expression(arg_expr, env, p)?);
99 }
100 return execute_user_function(&user_fxn, &input_arg_values, p);
101 }
102
103 if { functions.borrow().functions.contains_key(&fxn_name_id) } {
105 todo!();
106 }
107
108 let fxn_compiler = {
111 functions
112 .borrow()
113 .function_compilers
114 .get(&fxn_name_id)
115 .copied()
116 };
117 match fxn_compiler {
118 Some(fxn_compiler) => {
119 let mut input_arg_values = vec![];
120 for (_, arg_expr) in fxn_call.args.iter() {
121 input_arg_values.push(expression(arg_expr, env, p)?);
122 }
123 trace_println!(
124 p,
125 "{}",
126 format_trace(
127 "fn",
128 format!(
129 "native {}({})",
130 fxn_call.name.to_string(),
131 format_trace_args(&input_arg_values)
132 ),
133 )
134 );
135 execute_native_function_compiler(fxn_compiler, &input_arg_values, p)
136 }
137 None => Err(MechError::new(
139 MissingFunctionError {
140 function_id: fxn_name_id,
141 },
142 None,
143 )
144 .with_compiler_loc()
145 .with_tokens(fxn_call.name.tokens())),
146 }
147}
148
149pub fn execute_native_function_compiler(
153 fxn_compiler: &'static dyn NativeFunctionCompiler,
154 input_arg_values: &Vec<Value>,
155 p: &Interpreter,
156) -> MResult<Value> {
157 let plan = p.plan();
158 match fxn_compiler.compile(input_arg_values) {
159 Ok(mut new_fxn) => {
160 trace_println!(
161 p,
162 "{}",
163 format_trace(
164 "arm",
165 format!(
166 "selected {} args=[{}]",
167 new_fxn
168 .to_string()
169 .lines()
170 .next()
171 .unwrap_or("<unknown-arm>"),
172 format_trace_args(input_arg_values)
173 ),
174 )
175 );
176 let mut plan_brrw = plan.borrow_mut();
177 new_fxn.solve(); let result = new_fxn.out();
179 trace_println!(
180 p,
181 "{}",
182 format_trace("arm", format!("result {}", summarize_function_value(&result)))
183 );
184 plan_brrw.push(new_fxn); Ok(result)
186 }
187 Err(err) => Err(err),
188 }
189}
190
191fn execute_user_function(
195 fxn_def: &FunctionDefinition,
196 input_arg_values: &Vec<Value>,
197 p: &Interpreter,
198) -> MResult<Value> {
199 if input_arg_values.len() != fxn_def.input.len() {
201 return Err(MechError::new(
202 IncorrectNumberOfArguments {
203 expected: fxn_def.input.len(),
204 found: input_arg_values.len(),
205 },
206 None,
207 )
208 .with_compiler_loc()
209 .with_tokens(fxn_def.code.name.tokens()));
210 }
211
212 #[cfg(feature = "matrix")]
215 if let Some(result) = try_broadcast_user_function(fxn_def, input_arg_values, p)? {
216 return Ok(result);
217 }
218
219 trace_println!(
220 p,
221 "{}",
222 format_trace(
223 "fn",
224 format!(
225 "enter {}({})",
226 fxn_def.name,
227 format_trace_args(input_arg_values)
228 ),
229 )
230 );
231
232 let output = if !fxn_def.code.match_arms.is_empty() {
234 let mut current_args: Vec<Value> = input_arg_values.clone();
238 loop {
239 let scope = FunctionScope::enter(p);
240 bind_function_inputs(fxn_def, ¤t_args, p)?;
241 let step: FunctionCallStep = execute_function_match_arms(fxn_def, ¤t_args, p)?;
242 drop(scope);
243 match step {
244 FunctionCallStep::Return(value) => break Ok(value),
245 FunctionCallStep::TailCall(next_args) => {
248 current_args = next_args;
249 }
250 }
251 }
252 } else {
253 let scope = FunctionScope::enter(p);
255 bind_function_inputs(fxn_def, input_arg_values, p)?;
256 for statement_node in &fxn_def.code.statements {
257 statement(statement_node, None, p)?;
258 }
259 let result = collect_function_output(p, fxn_def);
260 drop(scope);
261 result
262 };
263
264 match output {
265 Ok(value) => {
266 trace_println!(
267 p,
268 "{}",
269 format_trace(
270 "fn",
271 format!("exit {} => {}", fxn_def.name, summarize_function_value(&value))
272 )
273 );
274 Ok(value)
275 }
276 Err(err) => {
277 trace_println!(
278 p,
279 "{}",
280 format_trace("fn", format!("fail {} => {:?}", fxn_def.name, err))
281 );
282 Err(err)
283 }
284 }
285}
286
287enum FunctionCallStep {
290 Return(Value),
291 TailCall(Vec<Value>),
292}
293
294#[cfg(feature = "matrix")]
300fn try_broadcast_user_function(
301 fxn_def: &FunctionDefinition,
302 input_arg_values: &Vec<Value>,
303 p: &Interpreter,
304) -> MResult<Option<Value>> {
305 if input_arg_values.len() != 1
306 || fxn_def.code.output.len() != 1
307 || fxn_def.code.input.len() != 1
308 {
309 return Ok(None);
310 }
311
312 let source = detach_value(&input_arg_values[0]);
313 if !source.is_matrix() {
314 return Ok(None);
315 }
316
317 #[cfg(feature = "kind_annotation")]
320 let (input_kind, output_kind) = {
321 let input_kind = kind_annotation(&fxn_def.code.input[0].kind.kind, p)?
322 .to_value_kind(&p.state.borrow().kinds)?;
323 let output_kind = kind_annotation(&fxn_def.code.output[0].kind.kind, p)?
324 .to_value_kind(&p.state.borrow().kinds)?;
325 (input_kind, output_kind)
326 };
327
328 #[cfg(not(feature = "kind_annotation"))]
329 let (input_kind, output_kind) = {
330 return Ok(None);
331 };
332
333 if input_kind != output_kind || matches!(input_kind, ValueKind::Matrix(_, _)) {
336 return Ok(None);
337 }
338
339 let Some(elements) = crate::patterns::matrix_like_values(&source) else {
340 return Ok(None);
341 };
342
343 let mut outputs = Vec::with_capacity(elements.len());
345 for element in elements {
346 outputs.push(execute_user_function(fxn_def, &vec![element], p)?);
347 }
348
349 let shape = source.shape();
350 Ok(Some(build_typed_matrix_from_values(
351 &output_kind,
352 outputs,
353 shape[0],
354 shape[1],
355 )))
356}
357
358#[cfg(feature = "matrix")]
361fn build_typed_matrix_from_values(
362 output_kind: &ValueKind,
363 outputs: Vec<Value>,
364 rows: usize,
365 cols: usize,
366) -> Value {
367 match output_kind {
368 #[cfg(feature = "f64")]
369 ValueKind::F64 => Value::MatrixF64(f64::to_matrix(
370 outputs
371 .into_iter()
372 .map(|value| {
373 value
374 .as_f64()
375 .expect("Expected f64 output")
376 .borrow()
377 .clone()
378 })
379 .collect::<Vec<f64>>(),
380 rows,
381 cols,
382 )),
383 _ => Value::MatrixValue(Value::to_matrix(outputs, rows, cols)),
384 }
385}
386
387fn execute_function_match_arms(
393 fxn_def: &FunctionDefinition,
394 input_arg_values: &Vec<Value>,
395 p: &Interpreter,
396) -> MResult<FunctionCallStep> {
397
398 #[cfg(all(feature = "kind_annotation", feature = "enum"))]
402 {
403 let has_wildcard = fxn_def
404 .code
405 .match_arms
406 .iter()
407 .any(|arm| matches!(arm.pattern, Pattern::Wildcard));
408 if !has_wildcard && fxn_def.input.len() == 1 {
409 if let Some((_, kind_annotation_node)) = fxn_def.input.iter().next() {
410 let input_kind = kind_annotation(&kind_annotation_node.kind, p)?
411 .to_value_kind(&p.state.borrow().kinds)?;
412 if let ValueKind::Enum(enum_id, _) = input_kind {
413 let state_brrw = p.state.borrow();
414 if let Some(enum_def) = state_brrw.enums.get(&enum_id) {
415 let mut covered_variants: HashSet<u64> = HashSet::new();
417 for arm in &fxn_def.code.match_arms {
418 match &arm.pattern {
419 #[cfg(feature = "atom")]
420 Pattern::TupleStruct(tuple_struct) => {
421 covered_variants.insert(tuple_struct.name.hash());
422 }
423 Pattern::Expression(expr) => {
424 if let Expression::Literal(Literal::Atom(atom)) = expr {
425 covered_variants.insert(atom.name.hash());
426 }
427 }
428 _ => {}
429 }
430 }
431 let all_covered = enum_def
432 .variants
433 .iter()
434 .all(|(variant_id, _)| covered_variants.contains(variant_id));
435 if !all_covered {
436 let missing_patterns = enum_def
438 .variants
439 .iter()
440 .filter(|(variant_id, _)| !covered_variants.contains(variant_id))
441 .map(|(variant_id, payload_kind)| {
442 let variant_name = enum_def
443 .names
444 .borrow()
445 .get(variant_id)
446 .cloned()
447 .unwrap_or_else(|| variant_id.to_string());
448 if payload_kind.is_some() {
449 format!(":{}(...)", variant_name)
450 } else {
451 format!(":{}", variant_name)
452 }
453 })
454 .collect::<Vec<String>>();
455 return Err(MechError::new(
456 FunctionMatchNonExhaustiveError {
457 function_name: fxn_def.name.clone(),
458 missing_patterns,
459 },
460 None,
461 )
462 .with_compiler_loc()
463 .with_tokens(fxn_def.code.name.tokens()));
464 }
465 }
466 }
467 }
468 }
469 }
470
471 for (arm_idx, arm) in fxn_def.code.match_arms.iter().enumerate() {
473 let mut env = Environment::new();
474 let matched = crate::patterns::pattern_matches_arguments(
475 &arm.pattern,
476 input_arg_values,
477 &mut env,
478 p,
479 )?;
480 trace_println!(p, "{}", {
481 let args_summary = summarize_values_with_kinds(input_arg_values);
482 let pattern_summary = summarize_function_pattern(&arm.pattern);
483 let marker = if matched { "✓" } else { "X" };
484 format_trace(
485 "match",
486 format!(
487 "arm[{arm_idx}] test pattern={pattern_summary} args=[{args_summary}] {marker}"
488 ),
489 )
490 });
491 if matched {
492 if let Expression::FunctionCall(fxn_call) = &arm.expression {
495 if fxn_call.name.hash() == fxn_def.code.name.hash() {
496 let mut tail_args = Vec::with_capacity(fxn_call.args.len());
497 for (_, arg_expr) in fxn_call.args.iter() {
498 tail_args.push(expression(arg_expr, Some(&env), p)?);
499 }
500 if tail_args.len() == fxn_def.input.len() {
501 trace_println!(
502 p,
503 "{}",
504 format_trace(
505 "match",
506 format!("arm[{arm_idx}] tail-call {}", fxn_def.name)
507 )
508 );
509 return Ok(FunctionCallStep::TailCall(tail_args));
510 }
511 }
512 }
513 let out = expression(&arm.expression, Some(&env), p)?;
515 let coerced = coerce_function_output_kind(detach_value(&out), fxn_def, p)?;
516 trace_println!(
517 p,
518 "{}",
519 format_trace(
520 "match",
521 format!(
522 "arm[{arm_idx}] out value={} kind={}",
523 summarize_function_value(&coerced),
524 coerced.kind().to_string()
525 )
526 )
527 );
528 return Ok(FunctionCallStep::Return(coerced));
529 }
530 }
531 Err(MechError::new(
533 FunctionOutputUndefinedError {
534 output_id: fxn_def.id,
535 },
536 None,
537 )
538 .with_compiler_loc()
539 .with_tokens(fxn_def.code.name.tokens()))
540}
541
542#[cfg(feature = "kind_annotation")]
545fn coerce_function_output_kind(
546 value: Value,
547 fxn_def: &FunctionDefinition,
548 p: &Interpreter,
549) -> MResult<Value> {
550 if fxn_def.output.is_empty() {
551 return Ok(value);
552 }
553 let Some((_, output_kind_annotation)) = fxn_def.output.get_index(0) else {
554 return Ok(value);
555 };
556 let target_kind =
557 kind_annotation(&output_kind_annotation.kind, p)?.to_value_kind(&p.state.borrow().kinds)?;
558 return Ok(value.convert_to(&target_kind).unwrap_or(value));
559}
560
561struct FunctionScope {
565 state: Ref<ProgramState>,
566 previous_symbols: SymbolTableRef,
567 previous_plan: Plan,
568 previous_environment: Option<SymbolTableRef>,
569}
570
571impl FunctionScope {
572 fn enter(p: &Interpreter) -> Self {
573 let state = p.state.clone();
574 let mut state_brrw = state.borrow_mut();
575 let mut local_symbols = SymbolTable::new();
578 local_symbols.dictionary = state_brrw.dictionary.clone();
579 let local_symbols = Ref::new(local_symbols);
580 let previous_symbols = std::mem::replace(&mut state_brrw.symbol_table, local_symbols);
581 let previous_plan = std::mem::replace(&mut state_brrw.plan, Plan::new());
582 let previous_environment = state_brrw.environment.take();
583 drop(state_brrw);
584
585 Self {
586 state,
587 previous_symbols,
588 previous_plan,
589 previous_environment,
590 }
591 }
592}
593
594impl Drop for FunctionScope {
596 fn drop(&mut self) {
597 let mut state_brrw = self.state.borrow_mut();
598 state_brrw.symbol_table = self.previous_symbols.clone();
599 state_brrw.plan = self.previous_plan.clone();
600 state_brrw.environment = self.previous_environment.clone();
601 }
602}
603
604fn bind_function_inputs(
611 fxn_def: &FunctionDefinition,
612 input_arg_values: &Vec<Value>,
613 p: &Interpreter,
614) -> MResult<()> {
615 let scoped_state = p.state.borrow();
616 for ((arg_id, input_kind_annotation), input_value) in
617 fxn_def.input.iter().zip(input_arg_values.iter())
618 {
619 let arg_name = fxn_def
621 .code
622 .input
623 .iter()
624 .find(|arg| arg.name.hash() == *arg_id)
625 .map(|arg| arg.name.to_string())
626 .unwrap_or_else(|| arg_id.to_string());
627
628 let bound_value = {
629 #[cfg(feature = "kind_annotation")]
630 {
631 let target_kind = kind_annotation(&input_kind_annotation.kind, p)?
632 .to_value_kind(&p.state.borrow().kinds)?;
633 let detached_input = detach_value(input_value);
634
635 #[cfg(all(feature = "enum", feature = "atom"))]
638 if let ValueKind::Enum(enum_id, _) = &target_kind {
639 let state_brrw = p.state.borrow();
640 if enum_value_matches(detached_input.clone(), *enum_id, &state_brrw) {
641 detached_input.clone()
642 } else {
643 return Err(MechError::new(
644 FunctionInputTypeMismatchError {
645 function_name: fxn_def.name.clone(),
646 argument_name: arg_name.clone(),
647 expected: target_kind.clone(),
648 found: detached_input.kind(),
649 },
650 None,
651 )
652 .with_compiler_loc()
653 .with_tokens(input_kind_annotation.tokens()));
654 }
655 } else {
656 detached_input
658 .clone()
659 .convert_to(&target_kind)
660 .ok_or_else(|| {
661 MechError::new(
662 FunctionInputTypeMismatchError {
663 function_name: fxn_def.name.clone(),
664 argument_name: arg_name.clone(),
665 expected: target_kind.clone(),
666 found: detached_input.kind(),
667 },
668 None,
669 )
670 .with_compiler_loc()
671 .with_tokens(input_kind_annotation.tokens())
672 })?
673 }
674 #[cfg(not(all(feature = "enum", feature = "atom")))]
675 detached_input
676 .clone()
677 .convert_to(&target_kind)
678 .ok_or_else(|| {
679 MechError::new(
680 FunctionInputTypeMismatchError {
681 function_name: fxn_def.name.clone(),
682 argument_name: arg_name.clone(),
683 expected: target_kind.clone(),
684 found: detached_input.kind(),
685 },
686 None,
687 )
688 .with_compiler_loc()
689 .with_tokens(input_kind_annotation.tokens())
690 })?
691 }
692 #[cfg(not(feature = "kind_annotation"))]
694 {
695 detach_value(input_value)
696 }
697 };
698 scoped_state.save_symbol(*arg_id, arg_name, bound_value, false);
699 }
700 Ok(())
701}
702
703#[cfg(all(feature = "enum", feature = "atom"))]
706fn enum_value_matches(value: Value, enum_id: u64, state: &ProgramState) -> bool {
707 let enum_def = match state.enums.get(&enum_id) {
708 Some(enm) => enm,
709 None => return false,
710 };
711 match value {
712 Value::Atom(atom) => {
714 let variant_id = atom.borrow().id();
715 enum_def
716 .variants
717 .iter()
718 .any(|(known_variant, payload_kind)| {
719 *known_variant == variant_id && payload_kind.is_none()
720 })
721 }
722 #[cfg(feature = "tuple")]
726 Value::Tuple(tuple_val) => {
727 let tuple_brrw = tuple_val.borrow();
728 if tuple_brrw.elements.len() != 2 {
729 return false;
730 }
731 let tag = match tuple_brrw.elements[0].as_ref() {
732 Value::Atom(atom) => atom.borrow().id(),
733 _ => return false,
734 };
735 let payload = tuple_brrw.elements[1].as_ref().clone();
736 let (_, declared_payload_kind) = match enum_def
737 .variants
738 .iter()
739 .find(|(known_variant, _)| *known_variant == tag)
740 {
741 Some(entry) => entry,
742 None => return false,
743 };
744 match declared_payload_kind {
745 Some(Value::Kind(expected_kind)) => match expected_kind {
746 ValueKind::Enum(inner_enum_id, _) => {
748 enum_value_matches(payload, *inner_enum_id, state)
749 }
750 _ => {
752 payload.kind() == expected_kind.clone()
753 || payload.convert_to(expected_kind).is_some()
754 }
755 },
756 _ => false,
757 }
758 }
759 _ => false,
760 }
761}
762
763fn collect_function_output(p: &Interpreter, fxn_def: &FunctionDefinition) -> MResult<Value> {
767 let symbols = p.symbols();
768 let symbols_brrw = symbols.borrow();
769 let mut outputs = vec![];
770
771 for output_arg in &fxn_def.code.output {
772 let output_id = output_arg.name.hash();
773 match symbols_brrw.get(output_id) {
774 Some(cell) => outputs.push(detach_value(&cell.borrow())),
775 None => {
776 return Err(
777 MechError::new(FunctionOutputUndefinedError { output_id }, None)
778 .with_compiler_loc()
779 .with_tokens(output_arg.tokens()),
780 );
781 }
782 }
783 }
784
785 Ok(match outputs.len() {
786 0 => Value::Empty,
787 1 => outputs.remove(0),
788 _ => Value::Tuple(Ref::new(MechTuple::from_vec(outputs))),
789 })
790}
791
792pub(crate) fn detach_value(value: &Value) -> Value {
796 match value {
797 Value::MutableReference(reference) => detach_value(&reference.borrow()),
798 _ => value.clone(),
799 }
800}
801
802#[derive(Debug, Clone)]
807pub struct MissingFunctionError {
808 pub function_id: u64,
809}
810
811impl MechErrorKind for MissingFunctionError {
812 fn name(&self) -> &str {
813 "MissingFunction"
814 }
815 fn message(&self) -> String {
816 format!("Function with id {} not found", self.function_id)
817 }
818}
819
820#[derive(Debug, Clone)]
822pub struct FunctionOutputUndefinedError {
823 pub output_id: u64,
824}
825
826impl MechErrorKind for FunctionOutputUndefinedError {
827 fn name(&self) -> &str {
828 "FunctionOutputUndefined"
829 }
830 fn message(&self) -> String {
831 format!(
832 "Function output {} was declared but never defined",
833 self.output_id
834 )
835 }
836}
837
838#[derive(Debug, Clone)]
840pub struct FunctionMatchNonExhaustiveError {
841 pub function_name: String,
842 pub missing_patterns: Vec<String>,
843}
844
845impl MechErrorKind for FunctionMatchNonExhaustiveError {
846 fn name(&self) -> &str {
847 "FunctionMatchNonExhaustive"
848 }
849
850 fn message(&self) -> String {
851 format!(
852 "Function '{}' has non-exhaustive match arms. Missing patterns: {}. Add the missing patterns or add a wildcard (`*`) arm.",
853 self.function_name,
854 self.missing_patterns.join(", ")
855 )
856 }
857}
858
859#[derive(Debug, Clone)]
862pub struct FunctionInputTypeMismatchError {
863 pub function_name: String,
864 pub argument_name: String,
865 pub expected: ValueKind,
866 pub found: ValueKind,
867}
868
869impl MechErrorKind for FunctionInputTypeMismatchError {
870 fn name(&self) -> &str {
871 "FunctionInputTypeMismatch"
872 }
873
874 fn message(&self) -> String {
875 format!(
876 "Function '{}' argument '{}' expected {}, found {}",
877 self.function_name, self.argument_name, self.expected, self.found
878 )
879 }
880}