//============================================================================// // // // 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