#include "GeneratorInterface.hpp"
#include "PyAsyncGenerator.hpp"
#include "PyCode.hpp"
#include "PyCoroutine.hpp"
#include "PyFrame.hpp"
#include "PyGenerator.hpp"
#include "PyNone.hpp"
#include "PyTraceback.hpp"
#include "PyType.hpp"
#include "RuntimeError.hpp"
#include "StopIteration.hpp"
#include "ValueError.hpp"
#include "interpreter/Interpreter.hpp"
#include "vm/VM.hpp"
namespace py {
template GeneratorInterface::GeneratorInterface(PyType *type) : PyBaseObject(type) {}
template
GeneratorInterface::GeneratorInterface(TypePrototype &type,
PyFrame *frame,
std::unique_ptr &&stack_frame,
bool is_running,
PyObject *code,
PyString *name,
PyString *qualname)
: PyBaseObject(type), m_frame(frame), m_stack_frame(std::move(stack_frame)),
m_is_running(is_running), m_code(code), m_name(name), m_qualname(qualname)
{}
template std::string GeneratorInterface::to_string() const
{
// FIXME: use qualname
return fmt::format("<{} object {} at {}>",
T::GeneratorTypeName,
m_name->value(),
static_cast(this));
}
template PyResult GeneratorInterface::__repr__() const
{
return PyString::create(to_string());
}
template PyResult GeneratorInterface::__iter__() const
{
return Ok(static_cast(const_cast *>(this)));
}
template PyResult GeneratorInterface::__next__()
{
return send(py_none());
}
template PyResult GeneratorInterface::send(PyObject *value)
{
if (m_is_running) { return Err(value_error("generator already executing")); }
if (m_frame == nullptr) {
// exhausted generator, or raised an exception
return Err(stop_iteration(py_none()));
}
// send the value to the coroutine
m_last_sent_value = value;
m_is_running = true;
ASSERT(m_code);
ASSERT(as(m_code));
m_frame->m_f_back = VirtualMachine::the().interpreter().execution_frame();
auto result = VirtualMachine::the().interpreter().call(
as(m_code)->function(), m_frame, *m_stack_frame);
m_is_running = false;
m_frame->m_f_back = nullptr;
m_last_sent_value = nullptr;
if (m_invalid_return && result.is_ok()) {
spdlog::debug("generator returned value {}", result.unwrap()->to_string());
} else if (!m_invalid_return && result.is_err()
&& result.unwrap_err()->type()->issubclass(stop_iteration()->type())) {
result = Err(runtime_error("generator raised StopIteration"));
} else if (result.is_ok()) {
spdlog::debug("generator yielded {}", result.unwrap()->to_string());
}
if (result.is_err()) { m_frame = nullptr; }
m_invalid_return = false;
return result;
}
template PyResult GeneratorInterface::close()
{
// TODO: implement generator close logic
return Ok(py_none());
}
template void GeneratorInterface::visit_graph(Visitor &visitor)
{
PyObject::visit_graph(visitor);
if (m_frame) visitor.visit(*m_frame);
if (m_code) visitor.visit(*m_code);
if (m_name) visitor.visit(*m_name);
if (m_qualname) visitor.visit(*m_qualname);
if (m_exception_stack) {
for (const auto &exc : *m_exception_stack) {
if (exc.exception) visitor.visit(*exc.exception);
if (exc.exception_type) visitor.visit(*exc.exception_type);
if (exc.traceback) visitor.visit(*exc.traceback);
}
}
if (m_stack_frame) {
for (const auto &el : m_stack_frame->registers) {
if (std::holds_alternative(el)) {
auto *obj = std::get(el);
if (obj) { visitor.visit(*obj); }
}
}
for (const auto &el : m_stack_frame->locals_storage) {
if (std::holds_alternative(el)) {
auto *obj = std::get(el);
if (obj) { visitor.visit(*obj); }
}
}
}
if (m_last_sent_value) { visitor.visit(*m_last_sent_value); }
}
template class GeneratorInterface;
template class GeneratorInterface;
template class GeneratorInterface;
}// namespace py