Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions apyds/chain_t.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,20 @@ def execute(self, callback: typing.Callable[[Rule], bool]) -> int:
The number of rules processed.
"""
return self._chain.execute(lambda candidate: callback(Rule(candidate.clone())))

def __iter__(self) -> typing.Iterator[Rule]:
"""Iterate over inferred rules.

Returns:
An iterator over Rule objects.

Example:
>>> for rule in chain:
... print(rule)
"""
iterator = self._chain.iter()
while True:
candidate = iterator.next()
if candidate is None:
break
yield Rule(candidate.clone())
39 changes: 39 additions & 0 deletions apyds/ds.cc
Original file line number Diff line number Diff line change
@@ -1,11 +1,37 @@
#include <ds/chain.hh>
#include <ds/ds.hh>
#include <ds/generator.hh>
#include <ds/search.hh>
#include <pybind11/functional.h>
#include <pybind11/pybind11.h>

namespace py = pybind11;

class Iterator {
public:
explicit Iterator(ds::generator<ds::rule_t*> _generator) : generator(std::move(_generator)), initialized(false), iterator(nullptr) { }

ds::rule_t* next() {
if (initialized) {
++*iterator;
} else {
iterator = std::make_unique<iterator_t>(generator.begin());
initialized = true;
}
if (*iterator == nullptr) {
return nullptr;
}
ds::rule_t* result = **iterator;
return result;
}
Comment on lines +14 to +26

private:
ds::generator<ds::rule_t*> generator;
bool initialized;
using iterator_t = decltype(generator.begin());
std::unique_ptr<iterator_t> iterator;
};

template<typename T>
auto from_string(const std::string_view& string, int buffer_size) -> std::unique_ptr<T> {
auto result = reinterpret_cast<T*>(operator new(buffer_size));
Expand Down Expand Up @@ -169,6 +195,11 @@ PYBIND11_MODULE(_ds, m, py::mod_gil_not_used()) {
search_t.def("reset", &ds::search_t::reset);
search_t.def("add", &ds::search_t::add);
search_t.def("execute", &ds::search_t::execute);
search_t.def(
"iter",
[](ds::search_t& self) { return Iterator(std::move(self.iterator())); },
py::keep_alive<0, 1>()
);

auto chain_t = py::class_<ds::chain_t>(m, "Chain");
chain_t.def(py::init<ds::length_t, ds::length_t>());
Expand All @@ -177,4 +208,12 @@ PYBIND11_MODULE(_ds, m, py::mod_gil_not_used()) {
chain_t.def("reset", &ds::chain_t::reset);
chain_t.def("add", &ds::chain_t::add);
chain_t.def("execute", &ds::chain_t::execute);
chain_t.def(
"iter",
[](ds::chain_t& self) { return Iterator(std::move(self.iterator())); },
py::keep_alive<0, 1>()
);
Comment on lines 198 to +215

auto iterator_t = py::class_<Iterator>(m, "Iterator");
iterator_t.def("next", &Iterator::next, py::return_value_policy::reference_internal);
}
17 changes: 17 additions & 0 deletions apyds/search_t.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,20 @@ def execute(self, callback: typing.Callable[[Rule], bool]) -> int:
The number of rules processed.
"""
return self._search.execute(lambda candidate: callback(Rule(candidate.clone())))

def __iter__(self) -> typing.Iterator[Rule]:
"""Iterate over inferred rules.

Returns:
An iterator over Rule objects.

Example:
>>> for rule in search:
... print(rule)
"""
iterator = self._search.iter()
while True:
candidate = iterator.next()
if candidate is None:
break
yield Rule(candidate.clone())
39 changes: 39 additions & 0 deletions atsds/ds.cc
Original file line number Diff line number Diff line change
@@ -1,10 +1,36 @@
#include <ds/chain.hh>
#include <ds/ds.hh>
#include <ds/generator.hh>
#include <ds/search.hh>
#include <emscripten/bind.h>

namespace em = emscripten;

class Iterator {
public:
explicit Iterator(ds::generator<ds::rule_t*> _generator) : generator(std::move(_generator)), initialized(false), iterator(nullptr) { }

ds::rule_t* next() {
if (initialized) {
++*iterator;
} else {
iterator = std::make_unique<iterator_t>(generator.begin());
initialized = true;
}
if (*iterator == nullptr) {
return nullptr;
}
ds::rule_t* result = **iterator;
return result;
}

private:
ds::generator<ds::rule_t*> generator;
bool initialized;
using iterator_t = decltype(generator.begin());
std::unique_ptr<iterator_t> iterator;
};

// 由于embind的限制,这里无法使用string_view。
// 为了保持一致性,一律使用复制。
template<typename T>
Expand Down Expand Up @@ -134,6 +160,10 @@ auto search_execute(ds::search_t* search, const em::val& callback) -> ds::length
return search->execute([&callback](ds::rule_t* candidate) -> bool { return callback(candidate, em::allow_raw_pointers()).as<bool>(); });
}

auto search_iter(ds::search_t* search) -> std::unique_ptr<Iterator> {
return std::make_unique<Iterator>(std::move(search->iterator()));
}

auto chain_add(ds::chain_t* chain, const std::string& text) -> bool {
return chain->add(text);
}
Expand All @@ -142,6 +172,10 @@ auto chain_execute(ds::chain_t* chain, const em::val& callback) -> ds::length_t
return chain->execute([&callback](ds::rule_t* candidate) -> bool { return callback(candidate, em::allow_raw_pointers()).as<bool>(); });
}

auto chain_iter(ds::chain_t* chain) -> std::unique_ptr<Iterator> {
return std::make_unique<Iterator>(std::move(chain->iterator()));
}

EMSCRIPTEN_BINDINGS(ds) {
em::register_vector<std::uint8_t>("Buffer");

Expand Down Expand Up @@ -195,6 +229,7 @@ EMSCRIPTEN_BINDINGS(ds) {
// 因为embind的限制,这里无法使用string_view和function。
search_t.function("add", &search_add, em::allow_raw_pointers());
search_t.function("execute", &search_execute, em::allow_raw_pointers());
search_t.function("iter", &search_iter, em::return_value_policy::take_ownership());

auto chain_t = em::class_<ds::chain_t>("Chain");
chain_t.constructor<ds::length_t, ds::length_t>();
Expand All @@ -204,4 +239,8 @@ EMSCRIPTEN_BINDINGS(ds) {
// 因为 embind 的限制,这里无法使用 string_view 和 function。
chain_t.function("add", &chain_add, em::allow_raw_pointers());
chain_t.function("execute", &chain_execute, em::allow_raw_pointers());
chain_t.function("iter", &chain_iter, em::return_value_policy::take_ownership());

auto iterator_t = em::class_<Iterator>("Iterator");
iterator_t.function("next", &Iterator::next, em::return_value_policy::reference());
}
46 changes: 46 additions & 0 deletions atsds/index.mts
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,29 @@ export class Search {
return callback(new Rule(candidate).copy());
});
}

/**
* Iterate over inferred rules.
*
* @returns An iterator over Rule objects.
*
* @example
* ```typescript
* for (const rule of search) {
* console.log(rule.toString());
* }
* ```
*/
*[Symbol.iterator](): Iterator<Rule> {
const iterator = this._search.iter();
while (true) {
const candidate = iterator.next();
if (candidate === null) {
break;
}
yield new Rule(candidate);
}
}
}

/**
Expand Down Expand Up @@ -697,4 +720,27 @@ export class Chain {
return callback(new Rule(candidate).copy());
});
}

/**
* Iterate over inferred rules.
*
* @returns An iterator over Rule objects.
*
* @example
* ```typescript
* for (const rule of chain) {
* console.log(rule.toString());
* }
* ```
*/
*[Symbol.iterator](): Iterator<Rule> {
const iterator = this._chain.iter();
while (true) {
const candidate = iterator.next();
if (candidate === null) {
break;
}
yield new Rule(candidate);
}
}
}
5 changes: 5 additions & 0 deletions include/ds/chain.hh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <memory>
#include <string_view>

#include <ds/generator.hh>
#include <ds/rule.hh>

namespace ds {
Expand Down Expand Up @@ -70,6 +71,10 @@ namespace ds {
/// @return 搜索到新的结果的数量。
/// @note 如果回调函数返回false,则继续搜索;如果回调函数返回true,则停止搜索。
length_t execute(const std::function<bool(rule_t*)>& callback);

/// @brief 执行一轮搜索操作,以生成器方式迭代所有匹配的规则。
/// @return 生成器,每次迭代返回一个匹配的规则指针。
generator<rule_t*> iterator();
};
} // namespace ds

Expand Down
124 changes: 124 additions & 0 deletions include/ds/generator.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#ifndef DS_GENERATOR_HH
#define DS_GENERATOR_HH

#include <coroutine>
#include <exception>
#include <utility>

namespace ds {
template<typename T>
class _generator_promise;

template<typename T>
class _generator {
using promise_type = _generator_promise<T>;
using handle_type = std::coroutine_handle<promise_type>;
handle_type handle_;

public:
struct iterator {
handle_type h_;

T& operator*() const noexcept {
return h_.promise().value_;
}
iterator& operator++() {
h_.resume();
if (h_.done()) {
h_ = nullptr;
}
return *this;
}
bool operator==(std::nullptr_t) const noexcept {
return h_ == nullptr;
}
bool operator!=(std::nullptr_t) const noexcept {
return h_ != nullptr;
}
};

_generator(_generator&& other) noexcept : handle_(other.handle_) {
other.handle_ = nullptr;
}
_generator& operator=(_generator&& other) noexcept {
if (this != &other) {
if (handle_) {
handle_.destroy();
}
handle_ = other.handle_;
other.handle_ = nullptr;
}
return *this;
}

~_generator() {
if (handle_) {
handle_.destroy();
}
}

iterator begin() {
if (!handle_) {
return {nullptr};
}
handle_.resume();
if (handle_.done()) {
return {nullptr};
}
return {handle_};
}
std::nullptr_t end() {
return nullptr;
}

private:
_generator() noexcept : handle_(nullptr) { }
explicit _generator(handle_type h) noexcept : handle_(h) { }
friend class _generator_promise<T>;
};

template<typename T>
class _generator_promise {
public:
T value_;

_generator<T> get_return_object() noexcept {
return _generator<T>(std::coroutine_handle<_generator_promise>::from_promise(*this));
}
std::suspend_always initial_suspend() noexcept {
return {};
}
std::suspend_always final_suspend() noexcept {
return {};
}
std::suspend_always yield_value(T& value) noexcept {
value_ = value;
return {};
}
std::suspend_always yield_value(T&& value) noexcept {
value_ = std::move(value);
return {};
}
void return_void() noexcept { }
void unhandled_exception() {
std::terminate();
}
};

#if defined(__cpp_lib_generator) && 0
template<typename T>
using generator = std::generator<T>;
#else
template<typename T>
using generator = _generator<T>;
#endif
} // namespace ds

namespace std {
template<typename T, typename... Args>
struct coroutine_traits<ds::_generator<T>, Args...> {
using promise_type = ds::_generator_promise<T>;
};
} // namespace std

#endif
Loading
Loading