Refactor tail call resolution logic

The tail call resolution logic was duplicated in `Environment::call` and
`VM::run`. This commit extracts the tail call resolution logic into a
single method `VM::resolve_tail_calls` and uses it in both places.

Additionally, this commit adds support for series indexing as a form of
tail call, allowing for direct access to series elements through the
`series(index)` syntax. This is useful for back-referencing in
time-series data.

A new example `err.myc` is added to demonstrate basic series usage and
error handling.
This commit is contained in:
Michael Schimmel
2026-03-06 12:35:00 +01:00
parent b9d88f019e
commit 13dc6beb52
4 changed files with 52 additions and 41 deletions
+8 -19
View File
@@ -501,7 +501,10 @@ impl Environment {
Ok(v) => v,
Err(e) => panic!("Myc Runtime Error: {}", e),
};
vm.resolve_tail_calls(res)
match vm.resolve_tail_calls(&mut crate::ast::vm::NoOpObserver, Ok(res)) {
Ok(v) => v,
Err(e) => panic!("Myc Runtime Error: {}", e),
}
}),
});
}
@@ -528,7 +531,10 @@ impl Environment {
Err(e) => panic!("Myc Runtime Error (Closure): {}", e),
};
}
vm.resolve_tail_calls(final_res)
match vm.resolve_tail_calls(&mut crate::ast::vm::NoOpObserver, Ok(final_res)) {
Ok(v) => v,
Err(e) => panic!("Myc Runtime Error: {}", e),
}
}),
})
}
@@ -645,23 +651,6 @@ impl Environment {
result = vm.run_with_observer(&mut observer, &closure.exec_node);
}
while let Ok(Value::TailCallRequest(payload)) = result {
let (next_obj, next_args) = *payload;
if next_obj
.as_any()
.downcast_ref::<crate::ast::vm::Closure>()
.is_some()
{
result = vm.run_with_args_observed(&mut observer, next_obj, &next_args);
} else {
result = Err(format!(
"Tail call target is not a closure: {}",
next_obj.type_name()
));
break;
}
}
self.run_pipeline();
Ok((result, observer.logs))