//============================================================================//
// //
// artichoke programming language //
// //
// Copyright (C) 2025 Erick Saul Guzman Ramos, whoami.artichoke.dev //
// //
// //
// This program is free software: you can redistribute it and/or modify //
// it under the terms of the GNU Affero General Public License as published //
// by the Free Software Foundation, either version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU Affero General Public License for more details. //
// //
// You should have received a copy of the GNU Affero General Public License //
// along with this program. If not, see . //
// //
//============================================================================//
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
namespace arti::lang {
template
requires (not std::is_void_v)
struct Generator;
template
requires (not std::is_void_v)
struct GeneratorPromise {
using value_type = std::remove_cvref_t;
using reference_type = value_type &;
using pointer_type = value_type *;
using ValueType = value_type;
using ReferenceType = reference_type;
using PointerType = pointer_type;
GeneratorPromise() = default;
Generator get_return_object();
std::suspend_always initial_suspend() const noexcept {
return {};
}
std::suspend_always final_suspend() const noexcept {
return {};
}
void unhandled_exception() {
exception = std::current_exception();
}
void rethrow_if_exception() {
if (exception) {
std::rethrow_exception(exception);
}
}
void return_value(const ValueType &val) noexcept {
value = val;
}
void return_value(ValueType &&val) noexcept {
value = std::forward(val);
}
std::suspend_always yield_value(const ValueType &val) noexcept {
value = val;
return {};
}
std::suspend_always yield_value(ValueType &&val) noexcept {
value = std::forward(val);
return {};
}
decltype(auto) await_transform(auto &&awaitable) = delete;
std::exception_ptr exception;
std::variant value;
};
struct GeneratorSentinel { };
template
requires (not std::is_void_v)
struct GeneratorIterator {
using iterator_category = std::input_iterator_tag;
using difference_type = std::ptrdiff_t;
using promise_type = GeneratorPromise;
using value_type = typename promise_type::value_type;
using reference_type = value_type &;
using pointer_type = value_type *;
using handle_type = std::coroutine_handle;
using IteratorCategory = iterator_category;
using DifferenceType = difference_type;
using PromiseType = promise_type;
using ValueType = value_type;
using ReferenceType = reference_type;
using PointerType = pointer_type;
using HandleType = handle_type;
GeneratorIterator() noexcept = default;
~GeneratorIterator() noexcept = default;
GeneratorIterator(HandleType handle) noexcept
: handle(handle) { }
GeneratorIterator(GeneratorIterator &&rhs) noexcept
: handle(std::exchange(rhs.handle, nullptr)) { }
GeneratorIterator &operator=(GeneratorIterator &&rhs) noexcept {
handle = std::exchange(rhs.handle, nullptr);
return *this;
}
GeneratorIterator(const GeneratorIterator &) noexcept = delete;
GeneratorIterator &operator=(const GeneratorIterator &) noexcept = delete;
GeneratorIterator &operator++() {
handle.resume();
if (handle.done()) {
handle.promise().rethrow_if_exception();
}
return *this;
}
void operator++(int) {
(void) this->operator++();
}
ReferenceType operator*() const noexcept {
auto &ref = handle.promise().value;
if (std::holds_alternative(ref)) {
return *std::get(ref);
}
else {
return std::get(ref);
}
}
PointerType operator->() const noexcept {
auto &ref = handle.promise().value;
if (std::holds_alternative(ref)) {
return std::get(ref);
}
else {
return &std::get(ref);
}
}
friend bool operator==(const GeneratorIterator &it, GeneratorSentinel) {
return (! it.handle || it.handle.done());
}
friend bool operator!=(const GeneratorIterator &it, GeneratorSentinel s) {
return ! (it == s);
}
friend bool operator==(GeneratorSentinel s, const GeneratorIterator &it) {
return it == s;
}
friend bool operator!=(GeneratorSentinel s, const GeneratorIterator &it) {
return it != s;
}
HandleType handle;
};
template
requires (not std::is_void_v)
struct [[nodiscard]] Generator {
using promise_type = GeneratorPromise;
using handle_type = std::coroutine_handle;
using iterator_type = GeneratorIterator;
using sentinel_type = GeneratorSentinel;
using PromiseType = promise_type;
using HandleType = handle_type;
using IteratorType = iterator_type;
using SentinelType = sentinel_type;
Generator() noexcept = default;
~Generator() noexcept {
if (handle) {
handle.destroy();
}
}
explicit Generator(HandleType handle) noexcept
: handle(handle) { }
Generator(Generator &&rhs) noexcept
: handle(std::exchange(rhs.handle, nullptr)) { }
Generator &operator=(Generator &&rhs) noexcept {
this->~Generator();
handle = std::exchange(rhs.handle, nullptr);
return *this;
}
Generator(const Generator &) noexcept = delete;
Generator &operator=(const Generator &) noexcept = delete;
bool finished() const noexcept {
return (not handle || (handle.done() and !handle.promise().exception));
}
void next() {
if (not finished()) {
handle.resume();
if (handle.done()) {
handle.promise().rethrow_if_exception();
}
}
}
PromiseType::ReferenceType get() {
using PointerType = PromiseType::PointerType;
using ValueType = PromiseType::ValueType;
auto &ref = handle.promise().value;
if (std::holds_alternative(ref)) {
return *std::get(ref);
}
else {
return std::get(ref);
}
}
IteratorType begin() {
if (handle) {
handle.resume();
if (handle.done()) {
handle.promise().rethrow_if_exception();
}
}
return { handle };
}
SentinelType end() const noexcept {
return {};
}
void swap(Generator &other) noexcept {
std::swap(handle, other.handle);
}
private:
HandleType handle;
};
template
requires (not std::is_void_v)
Generator GeneratorPromise::get_return_object() {
using GeneratorType = Generator;
using HandleType = typename GeneratorType::HandleType;
return GeneratorType{ HandleType::from_promise(*this) };
}
} // namespace arti::lang