From 43e5da510b8becef1efc1aed5db30e8e32bd9f96 Mon Sep 17 00:00:00 2001 From: Johannes Wolf Date: Mon, 30 Mar 2026 11:29:01 +0200 Subject: [PATCH 1/3] async: Minimal coroutine wrapper --- CMakeLists.txt | 3 ++- cmake/deps.cmake | 4 ++++ include/simfil/expression.h | 25 +++++++++++++++++++++++++ src/simfil.cpp | 10 +++++----- 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f0db16a0..07394e79 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,7 +118,8 @@ target_link_libraries(simfil sfl::sfl fmt::fmt tl::expected - Bitsery::bitsery) + Bitsery::bitsery + asyncpp) if (SIMFIL_FPIC OR NOT SIMFIL_SHARED) set_property(TARGET simfil PROPERTY diff --git a/cmake/deps.cmake b/cmake/deps.cmake index e7c88f37..93a5ac7b 100644 --- a/cmake/deps.cmake +++ b/cmake/deps.cmake @@ -20,6 +20,10 @@ CPMAddPackage( "EXPECTED_BUILD_TESTS OFF" "EXPECTED_BUILD_PACKAGE_DEB OFF") +# asyncpp +CPMAddPackage( + URI "gh:asyncpp/asyncpp@0.0.1#c1d3831") + # nlohmann/json if (SIMFIL_WITH_MODEL_JSON) CPMAddPackage("gh:nlohmann/json@3.11.3") diff --git a/include/simfil/expression.h b/include/simfil/expression.h index 39692f9d..7b67d79b 100644 --- a/include/simfil/expression.h +++ b/include/simfil/expression.h @@ -7,7 +7,12 @@ #include "simfil/environment.h" #include "simfil/result.h" +#include "asyncpp/generator.h" + +#include #include +#include +#include namespace simfil { @@ -59,6 +64,26 @@ class Expr /* Debug */ virtual auto toString() const -> std::string = 0; + auto eval(Context ctx, Value val) const -> asyncpp::generator> + { + if (ctx.canceled()) + co_return; + + std::vector results; + auto result = eval(ctx, val, LambdaResultFn([&results](Context, const Value& value) -> tl::expected { + results.push_back(value); + return Result::Continue; + })); + + if (!result) { + co_yield tl::unexpected(std::move(result.error())); + co_return; + } + + for (auto value : results) + co_yield value; + } + auto eval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected { if (ctx.canceled()) diff --git a/src/simfil.cpp b/src/simfil.cpp index ae6f6738..585388a7 100644 --- a/src/simfil.cpp +++ b/src/simfil.cpp @@ -937,11 +937,11 @@ auto eval(Environment& env, const AST& ast, const ModelNode& node, Diagnostics* Context ctx(&env, &localDiag); std::vector values; - auto res = ast.expr().eval(ctx, Value::field(node), LambdaResultFn([&values](const Context&, Value&& value) { - values.push_back(std::move(value)); - return Result::Continue; - })); - TRY_EXPECTED(res); + auto stream = ast.expr().eval(ctx, Value::field(node)); + for (auto&& value : stream) { + TRY_EXPECTED(value); + values.push_back(*value); + } // Merge diagnostics if (diag) From adb5503137be20ac84f6d16feefc6f93aaab1980 Mon Sep 17 00:00:00 2001 From: Johannes Wolf Date: Mon, 30 Mar 2026 18:38:01 +0200 Subject: [PATCH 2/3] async: Minimal implementation --- include/simfil/environment.h | 5 + include/simfil/expression.h | 29 +- include/simfil/function.h | 38 +- include/simfil/model/arena.h | 77 ++++ include/simfil/model/nodes.h | 20 +- include/simfil/model/nodes.impl.h | 15 + include/simfil/typed-meta-type.h | 16 +- include/simfil/types.h | 3 +- src/completion.cpp | 91 ++-- src/completion.h | 10 +- src/expressions.cpp | 667 +++++++++++++++--------------- src/expressions.h | 109 ++--- src/function.cpp | 420 ++++++++++--------- src/model/nodes.cpp | 9 + src/parser.cpp | 4 +- src/simfil.cpp | 26 +- src/types.cpp | 6 +- test/common.cpp | 16 +- test/common.hpp | 23 +- test/simfil.cpp | 40 +- 20 files changed, 910 insertions(+), 714 deletions(-) diff --git a/include/simfil/environment.h b/include/simfil/environment.h index 03838728..098a62b0 100644 --- a/include/simfil/environment.h +++ b/include/simfil/environment.h @@ -162,6 +162,11 @@ struct Context return *timeout < std::chrono::steady_clock::now(); return false; } + + auto compiling() const -> bool + { + return phase == Compilation; + } }; /** diff --git a/include/simfil/expression.h b/include/simfil/expression.h index 7b67d79b..05ecb487 100644 --- a/include/simfil/expression.h +++ b/include/simfil/expression.h @@ -5,18 +5,18 @@ #include "simfil/token.h" #include "simfil/value.h" #include "simfil/environment.h" -#include "simfil/result.h" #include "asyncpp/generator.h" #include #include #include -#include namespace simfil { +using EvalStream = asyncpp::generator>; + class ExprVisitor; class Expr @@ -64,26 +64,16 @@ class Expr /* Debug */ virtual auto toString() const -> std::string = 0; - auto eval(Context ctx, Value val) const -> asyncpp::generator> + auto eval(Context ctx, Value val) const -> EvalStream { if (ctx.canceled()) co_return; - std::vector results; - auto result = eval(ctx, val, LambdaResultFn([&results](Context, const Value& value) -> tl::expected { - results.push_back(value); - return Result::Continue; - })); - - if (!result) { - co_yield tl::unexpected(std::move(result.error())); - co_return; - } - - for (auto value : results) + for (auto value : ieval(ctx, val)) co_yield value; } + /* auto eval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected { if (ctx.canceled()) @@ -114,6 +104,7 @@ class Expr return ieval(ctx, std::move(val), res); } + */ /* Accept expression visitor */ virtual auto accept(ExprVisitor& v) const -> void = 0; @@ -127,13 +118,7 @@ class Expr private: /* Abstract evaluation implementation */ - virtual auto ieval(Context ctx, const Value& value, const ResultFn& result) const -> tl::expected = 0; - - /* Move-optimized evaluation implementation */ - virtual auto ieval(Context ctx, Value&& value, const ResultFn& result) const -> tl::expected - { - return ieval(ctx, value, result); - } + virtual auto ieval(Context ctx, Value value) const -> EvalStream = 0; ExprId id_; SourceLocation sourceLocation_; diff --git a/include/simfil/function.h b/include/simfil/function.h index 19265b1d..b53b2db3 100644 --- a/include/simfil/function.h +++ b/include/simfil/function.h @@ -33,7 +33,7 @@ class Function virtual ~Function() = default; virtual auto ident() const -> const FnInfo& = 0; - virtual auto eval(Context, const Value&, const std::vector&, const ResultFn&) const -> tl::expected = 0; + virtual auto eval(Context, Value, const std::vector&) const -> EvalStream = 0; }; class CountFn : public Function @@ -44,7 +44,7 @@ class CountFn : public Function CountFn(); auto ident() const -> const FnInfo& override; - auto eval(Context, const Value&, const std::vector&, const ResultFn&) const -> tl::expected override; + auto eval(Context, Value, const std::vector&) const -> EvalStream override; }; class TraceFn : public Function @@ -55,7 +55,7 @@ class TraceFn : public Function TraceFn(); auto ident() const -> const FnInfo& override; - auto eval(Context, const Value&, const std::vector&, const ResultFn&) const -> tl::expected override; + auto eval(Context, Value, const std::vector&) const -> EvalStream override; }; class RangeFn : public Function @@ -66,7 +66,7 @@ class RangeFn : public Function RangeFn(); auto ident() const -> const FnInfo& override; - auto eval(Context, const Value&, const std::vector&, const ResultFn&) const -> tl::expected override; + auto eval(Context, Value, const std::vector&) const -> EvalStream override; }; class ReFn : public Function @@ -77,7 +77,7 @@ class ReFn : public Function ReFn(); auto ident() const -> const FnInfo& override; - auto eval(Context, const Value&, const std::vector&, const ResultFn&) const -> tl::expected override; + auto eval(Context, Value, const std::vector&) const -> EvalStream override; }; class ArrFn : public Function @@ -88,7 +88,7 @@ class ArrFn : public Function ArrFn(); auto ident() const -> const FnInfo& override; - auto eval(Context, const Value&, const std::vector&, const ResultFn&) const -> tl::expected override; + auto eval(Context, Value, const std::vector&) const -> EvalStream override; }; class SplitFn : public Function @@ -99,7 +99,7 @@ class SplitFn : public Function SplitFn(); auto ident() const -> const FnInfo& override; - auto eval(Context, const Value&, const std::vector&, const ResultFn&) const -> tl::expected override; + auto eval(Context, Value, const std::vector&) const -> EvalStream override; }; class SelectFn : public Function @@ -110,7 +110,7 @@ class SelectFn : public Function SelectFn(); auto ident() const -> const FnInfo& override; - auto eval(Context, const Value&, const std::vector&, const ResultFn&) const -> tl::expected override; + auto eval(Context, Value, const std::vector&) const -> EvalStream override; }; class SumFn : public Function @@ -121,7 +121,7 @@ class SumFn : public Function SumFn(); auto ident() const -> const FnInfo& override; - auto eval(Context, const Value&, const std::vector&, const ResultFn&) const -> tl::expected override; + auto eval(Context, Value, const std::vector&) const -> EvalStream override; }; class KeysFn : public Function @@ -132,7 +132,7 @@ class KeysFn : public Function KeysFn(); auto ident() const -> const FnInfo& override; - auto eval(Context, const Value&, const std::vector&, const ResultFn&) const -> tl::expected override; + auto eval(Context, Value, const std::vector&) const -> EvalStream override; }; /** Utility functions for working with arguments*/ @@ -143,15 +143,15 @@ inline auto evalArg1Any(Context ctx, const Value& val, const ExprPtr& expr) -> s { if (!expr) return {false, Value::undef()}; - auto n = 0; - auto out = Value::undef(); - (void)expr->eval(ctx, val, LambdaResultFn([&n, &out](auto, Value v) { - ++n; - out = std::move(v); - return Result::Continue; - })); - - return {n == 1, std::move(out)}; + + auto n = 0u; + std::optional out ; + for (auto value : expr->eval(ctx, val)) { + n++; + out.emplace(*value); + } + + return {n == 1, std::move(out).value_or(Value::undef())}; } template diff --git a/include/simfil/model/arena.h b/include/simfil/model/arena.h index 36af661c..ef412f2c 100644 --- a/include/simfil/model/arena.h +++ b/include/simfil/model/arena.h @@ -3,6 +3,7 @@ #pragma once #include +#include #include #include #include @@ -628,6 +629,22 @@ class ArrayArena iterate_chunked(a, std::forward(lambda)); } + auto iterate(ArrayIndex idx) -> asyncpp::generator + { + if (is_singleton_handle(idx)) { + for (auto value : iterate_singleton(idx)) + co_yield value; + } + else if (heads_.empty() && compactHeads_) { + for (auto value : iterate_compact(idx)) + co_yield value; + } + else { + for (auto value : iterate_chunked(idx)) + co_yield value; + } + } + private: // Represents a chunk of an array in the arena. struct Chunk @@ -682,6 +699,16 @@ class ArrayArena } } + template + static ElementType materialize_iter_value(Value&& value) + { + if constexpr (requires { std::forward(value).to_value(); }) { + return std::forward(value).to_value(); + } else { + return std::forward(value); + } + } + template void iterate_singleton(ArrayIndex a, Func&& lambda) { @@ -698,6 +725,22 @@ class ArrayArena invoke_iter_callback(lambda, value, 0); } + auto iterate_singleton(ArrayIndex idx) -> asyncpp::generator // TODO: should be an expected with an error! + { + auto singletonIndex = singleton_payload(idx); + if (singletonIndex >= singletonValues_.size() || + singletonIndex >= singletonOccupied_.size()) { + raise("ArrayArena singleton handle index out of range."); + co_return; + } + else if (singletonOccupied_.at(singletonIndex) == 0) { + co_return; + } + + auto value = materialize_iter_value(singletonValues_.at(singletonIndex)); + co_yield value; + } + template void iterate_compact(ArrayIndex a, Func&& lambda) { @@ -715,6 +758,20 @@ class ArrayArena } } + auto iterate_compact(ArrayIndex idx) -> asyncpp::generator + { + if (idx >= compactHeads_->size()) { + raise("ArrayArena head index out of range."); + co_return; + } + + auto const& compact = (*compactHeads_)[idx]; + for (size_t i = 0; i < static_cast(compact.size); ++i) { + auto value = materialize_iter_value(data_[static_cast(compact.offset) + i]); + co_yield value; + } + } + template void iterate_chunked(ArrayIndex a, Func&& lambda) { @@ -740,6 +797,26 @@ class ArrayArena } } + auto iterate_chunked(ArrayIndex idx) -> asyncpp::generator + { + if (idx >= heads_.size()) { + raise("ArrayArena head index out of range."); + co_return; + } + + Chunk const* current = &heads_[idx]; + while (current != nullptr) { + for (size_t i = 0; i < current->size && i < current->capacity; ++i) { + auto value = materialize_iter_value(data_[current->offset + i]); + co_yield value; + } + + current = (current->next != InvalidArrayIndex) + ? &continuations_[current->next] + : nullptr; + } + } + void ensure_regular_head_pool() { if (!heads_.empty()) { diff --git a/include/simfil/model/nodes.h b/include/simfil/model/nodes.h index d25da4bb..f7746ce6 100644 --- a/include/simfil/model/nodes.h +++ b/include/simfil/model/nodes.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -303,6 +304,8 @@ struct ModelNode }; virtual bool iterate(IterCallback const& cb) const; // NOLINT (allow discard) + virtual auto iterate() const -> asyncpp::generator; + /// Iterator Support /// * `fieldNames()`: Returns range object which supports `for(StringId const& f: node.fieldNames())` /// * `fields()`: Returns range object which supports `for(auto const& [fieldId, value] : node.fields())` @@ -430,6 +433,7 @@ struct ModelNodeBase : public ModelNode [[nodiscard]] StringId keyAt(int64_t) const override; [[nodiscard]] uint32_t size() const override; bool iterate(IterCallback const&) const override {return true;} // NOLINT (allow discard) + auto iterate() const -> asyncpp::generator override {co_return;} public: explicit ModelNodeBase(detail::mp_key key) : ModelNode(key) {} @@ -548,6 +552,7 @@ struct BaseArray : public MandatoryDerivedModelNodeBase [[nodiscard]] ModelNode::Ptr at(int64_t) const override; [[nodiscard]] uint32_t size() const override; bool iterate(ModelNode::IterCallback const& cb) const override; // NOLINT (allow discard) + auto iterate() const -> asyncpp::generator override; protected: BaseArray& appendInternal(ModelNode::Ptr const& value={}); @@ -612,7 +617,8 @@ struct BaseObject : public MandatoryDerivedModelNodeBase [[nodiscard]] uint32_t size() const override; [[nodiscard]] ModelNode::Ptr get(const StringId &) const override; [[nodiscard]] StringId keyAt(int64_t) const override; - bool iterate(ModelNode::IterCallback const& cb) const override; // NOLINT (allow discard) + bool iterate(ModelNode::IterCallback const& cb) const override; // NOLINT (allow discard) + auto iterate() const -> asyncpp::generator override; protected: /** @@ -710,6 +716,18 @@ class ProceduralObject : public Object return true; } + auto iterate() const -> asyncpp::generator override + { + for (auto const& [k, v] : fields_) { + auto vv = v(static_cast(*this)); + co_yield vv; + } + + if (members_ != InvalidArrayIndex) + for (auto member : Object::iterate()) + co_yield member; + } + inline ModelPoolDerivedModel& model() const {return *modelPtr();} // NOLINT public: diff --git a/include/simfil/model/nodes.impl.h b/include/simfil/model/nodes.impl.h index d8a08e7e..0048a095 100644 --- a/include/simfil/model/nodes.impl.h +++ b/include/simfil/model/nodes.impl.h @@ -1,6 +1,7 @@ #pragma once #include "nodes.h" #include "tl/expected.hpp" +#include #include namespace simfil @@ -52,6 +53,13 @@ bool BaseArray::iterate(const ModelNode::IterCallback& return cont; } +template +auto BaseArray::iterate() const -> asyncpp::generator +{ + for (auto member : storage_->iterate(members_)) + co_yield ModelNode::Ptr::make(model_, member); +} + template bool BaseArray::forEach( std::function const& callback) const @@ -154,6 +162,13 @@ bool BaseObject::iterate(const ModelNode::IterCallback return cont; } +template +auto BaseObject::iterate() const -> asyncpp::generator +{ + for (auto member : storage_->iterate(members_)) + co_yield ModelNode::Ptr::make(model_, detail::objectFieldNode(member)); +} + template tl::expected>, Error> BaseObject::addFieldInternal( std::string_view const& name, diff --git a/include/simfil/typed-meta-type.h b/include/simfil/typed-meta-type.h index 3506e094..c04f8c8a 100644 --- a/include/simfil/typed-meta-type.h +++ b/include/simfil/typed-meta-type.h @@ -2,10 +2,10 @@ #pragma once +#include "src/expected.h" #include "value.h" -#include "operator.h" -#include "exception-handler.h" #include "transient.h" +#include "expression.h" namespace simfil { @@ -68,16 +68,22 @@ struct TypedMetaType : MetaType auto unpack(const TransientObject& obj, std::function fn) const -> tl::expected override { - return unpack(*(const Type*)obj.data, fn); + for (auto value : unpack(*(const Type*)obj.data)) { + TRY_EXPECTED(value); + if (!fn(std::move(*value))) + return {}; + } + + return {}; } virtual auto unaryOp(std::string_view op, const Type&) const -> tl::expected = 0; virtual auto binaryOp(std::string_view op, const Type&, const Value&) const -> tl::expected = 0; virtual auto binaryOp(std::string_view op, const Value&, const Type&) const -> tl::expected = 0; - virtual auto unpack(const Type&, std::function fn) const -> tl::expected + virtual auto unpack(const Type&) const -> EvalStream { - return tl::unexpected(Error::Unimplemented, "Type has no unpack operator"); + co_yield tl::unexpected(Error::Unimplemented, "Type has no unpack operator"); } }; diff --git a/include/simfil/types.h b/include/simfil/types.h index 683d668e..2df5557d 100644 --- a/include/simfil/types.h +++ b/include/simfil/types.h @@ -2,6 +2,7 @@ #pragma once +#include "simfil/expression.h" #include "value.h" #include "typed-meta-type.h" @@ -40,7 +41,7 @@ class IRangeType : public TypedMetaType auto binaryOp(std::string_view op, const IRange& l, const Value& r) const -> tl::expected override; auto binaryOp(std::string_view op, const Value& l, const IRange& r) const -> tl::expected override; - auto unpack(const IRange& , std::function res) const -> tl::expected override; + auto unpack(const IRange&) const -> EvalStream override; }; struct Re diff --git a/src/completion.cpp b/src/completion.cpp index 83f3be14..e8f21181 100644 --- a/src/completion.cpp +++ b/src/completion.cpp @@ -137,24 +137,30 @@ auto CompletionFieldOrWordExpr::type() const -> Type return Type::FIELD; } -auto CompletionFieldOrWordExpr::ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected +auto CompletionFieldOrWordExpr::ieval(Context ctx, Value val) const -> EvalStream { - if (ctx.phase == Context::Phase::Compilation) - return res(ctx, Value::undef()); + if (ctx.phase == Context::Phase::Compilation) { + co_yield Value::undef(); + co_return; + } - if (val.isa(ValueType::Undef)) - return res(ctx, val); + if (val.isa(ValueType::Undef)) { + co_yield val; + co_return; + } const auto node = val.node(); - if (!node) - return res(ctx, val); + if (!node) { + co_yield val; + co_return; + } const auto caseSensitive = comp_->options.smartCase && containsUppercaseCharacter(prefix_); // First we try to complete fields for (StringId id : node->fieldNames()) { if (comp_->size() >= comp_->limit) { - return Result::Stop; + co_return; } if (id == StringPool::Empty) @@ -163,8 +169,8 @@ auto CompletionFieldOrWordExpr::ieval(Context ctx, const Value& val, const Resul auto keyPtr = ctx.env->strings()->resolve(id); if (!keyPtr || keyPtr->empty()) continue; - const auto& key = *keyPtr; + const auto& key = *keyPtr; if (startsWith(key, prefix_, caseSensitive)) { if (needsEscaping(key)) { comp_->add(escapeKey(key), sourceLocation(), CompletionCandidate::Type::FIELD); @@ -177,13 +183,13 @@ auto CompletionFieldOrWordExpr::ieval(Context ctx, const Value& val, const Resul // If not in a path, we try to complete words and functions if (!inPath_) { if (auto r = completeWords(ctx, prefix_, *comp_, sourceLocation()); r != Result::Continue) - return r; + co_return; if (auto r = completeFunctions(ctx, prefix_, *comp_, sourceLocation()); r != Result::Continue) - return r; + co_return; } - return res(ctx, Value::null()); + co_yield Value::undef(); } auto CompletionFieldOrWordExpr::toString() const -> std::string @@ -250,19 +256,17 @@ auto CompletionAndExpr::type() const -> Type return Type::VALUE; } -auto CompletionAndExpr::ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected +auto CompletionAndExpr::ieval(Context ctx, Value val) const -> EvalStream { if (left_) - (void)left_->eval(ctx, val, LambdaResultFn([](const Context&, const Value&) { - return Result::Continue; - })); + for (auto left : left_->eval(ctx, val)) { + CO_TRY_EXPECTED(left); + } if (right_) - (void)right_->eval(ctx, val, LambdaResultFn([](const Context&, const Value&) { - return Result::Continue; - })); - - return Result::Continue; + for (auto right : right_->eval(ctx, val)) { + CO_TRY_EXPECTED(right); + } } void CompletionAndExpr::accept(ExprVisitor& v) const @@ -306,19 +310,17 @@ auto CompletionOrExpr::type() const -> Type return Type::VALUE; } -auto CompletionOrExpr::ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected +auto CompletionOrExpr::ieval(Context ctx, Value val) const -> EvalStream { if (left_) - (void)left_->eval(ctx, val, LambdaResultFn([](const Context&, const Value&) { - return Result::Continue; - })); + for (auto left : left_->eval(ctx, val)) { + CO_TRY_EXPECTED(left); + } if (right_) - (void)right_->eval(ctx, val, LambdaResultFn([](const Context&, const Value&) { - return Result::Continue; - })); - - return Result::Continue; + for (auto right : right_->eval(ctx, val)) { + CO_TRY_EXPECTED(right); + } } void CompletionOrExpr::accept(ExprVisitor& v) const @@ -353,15 +355,19 @@ auto CompletionWordExpr::constant() const -> bool return true; } -auto CompletionWordExpr::ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected +auto CompletionWordExpr::ieval(Context ctx, Value val) const -> EvalStream { - if (ctx.phase == Context::Phase::Compilation) - return res(ctx, Value::undef()); - - if (auto r = completeWords(ctx, prefix_, *comp_, sourceLocation()); r != Result::Continue) - return r; + if (ctx.phase == Context::Phase::Compilation) { + co_yield Value::undef(); + co_return; + } - return res(ctx, Value::undef()); + else if (auto r = completeWords(ctx, prefix_, *comp_, sourceLocation()); r != Result::Continue) { + co_yield val; + } + else { + co_yield Value::undef(); + } } auto CompletionWordExpr::toString() const -> std::string @@ -379,11 +385,14 @@ auto CompletionConstExpr::constant() const -> bool return false; } -auto CompletionConstExpr::ieval(Context ctx, const Value&, const ResultFn& res) const -> tl::expected +auto CompletionConstExpr::ieval(Context ctx, Value) const -> EvalStream { - if (ctx.phase == Context::Compilation) - return res(ctx, Value::undef()); - return res(ctx, value_); + if (ctx.phase == Context::Compilation) { + co_yield Value::undef(); + } + else { + co_yield value_; + } } } diff --git a/src/completion.h b/src/completion.h index 8f9c51ba..20fddb43 100644 --- a/src/completion.h +++ b/src/completion.h @@ -48,7 +48,7 @@ class CompletionFieldOrWordExpr : public Expr CompletionFieldOrWordExpr(ExprId id, std::string prefix, Completion* comp, const Token& token, bool inPath); auto type() const -> Type override; - auto ieval(Context ctx, const Value& value, const ResultFn& result) const -> tl::expected override; + auto ieval(Context ctx, Value value) const -> EvalStream override; auto accept(ExprVisitor& v) const -> void override; auto toString() const -> std::string override; @@ -63,7 +63,7 @@ class CompletionAndExpr : public Expr CompletionAndExpr(ExprId id, ExprPtr left, ExprPtr right, const Completion* comp); auto type() const -> Type override; - auto ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected override; + auto ieval(Context ctx, Value value) const -> EvalStream override; void accept(ExprVisitor& v) const override; auto toString() const -> std::string override; @@ -76,7 +76,7 @@ class CompletionOrExpr : public Expr CompletionOrExpr(ExprId id, ExprPtr left, ExprPtr right, const Completion* comp); auto type() const -> Type override; - auto ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected override; + auto ieval(Context ctx, Value value) const -> EvalStream override; void accept(ExprVisitor& v) const override; auto toString() const -> std::string override; @@ -90,7 +90,7 @@ class CompletionWordExpr : public Expr auto type() const -> Type override; auto constant() const -> bool override; - auto ieval(Context ctx, const Value& value, const ResultFn& result) const -> tl::expected override; + auto ieval(Context ctx, Value value) const -> EvalStream override; auto accept(ExprVisitor& v) const -> void override; auto toString() const -> std::string override; @@ -108,7 +108,7 @@ class CompletionConstExpr : public ConstExpr using ConstExpr::ConstExpr; auto constant() const -> bool override; - auto ieval(Context ctx, const Value&, const ResultFn& res) const -> tl::expected override; + auto ieval(Context ctx, Value value) const -> EvalStream override; }; } diff --git a/src/expressions.cpp b/src/expressions.cpp index 82d77d2b..2e2cd2d1 100644 --- a/src/expressions.cpp +++ b/src/expressions.cpp @@ -2,6 +2,8 @@ #include "fmt/format.h" #include "simfil/environment.h" +#include "simfil/expression.h" +#include "simfil/model/nodes.h" #include "simfil/result.h" #include "simfil/value.h" #include "simfil/function.h" @@ -18,60 +20,13 @@ namespace simfil namespace { -/** - * Helper for calling the result function if it has never been executed - * at the time of destruction. - */ -template -struct CountedResultFn : ResultFn -{ - mutable std::size_t calls = 0; - bool finished = false; - InnerFn fn; - Context nonctx; - - CountedResultFn(InnerFn fn, Context ctx) - : fn(fn) - , nonctx(ctx) - {} - - CountedResultFn(const CountedResultFn&) = delete; - CountedResultFn(CountedResultFn&&) = delete; - - auto operator()(Context ctx, const Value& vv) const noexcept -> tl::expected override - { - assert(!finished); - ++calls; - return fn(ctx, vv); - } - - auto operator()(Context ctx, Value&& vv) const noexcept -> tl::expected override - { - assert(!finished); - ++calls; - return fn(ctx, std::move(vv)); - } - - /* NOTE: You _must_ call finish before destruction! */ - auto ensureCall() - { - assert(!finished); - if (calls == 0 && !finished) { - finished = true; - if (nonctx.phase == Context::Phase::Compilation) - fn(nonctx, Value::undef()); - else - fn(nonctx, Value::null()); - } - } -}; - auto boolify(const Value& v) -> bool { /* Needed because DispatchOperator* returns * Undef if any argument is Undef. */ if (v.isa(ValueType::Undef)) return false; + return UnaryOperatorDispatcher::dispatch(v).value_or(Value::f()).as(); } @@ -86,51 +41,36 @@ auto WildcardExpr::type() const -> Type return Type::PATH; } -auto WildcardExpr::ieval(Context ctx, const Value& val, const ResultFn& ores) const -> tl::expected +auto WildcardExpr::ieval(Context ctx, Value val) const -> EvalStream { - if (ctx.phase == Context::Phase::Compilation) - return ores(ctx, Value::undef()); - - CountedResultFn res(ores, ctx); + if (ctx.compiling()) { + co_yield Value::undef(); + co_return; + } - struct Iterate + struct Iterator { - Context& ctx; - ResultFn& res; - - [[nodiscard]] auto iterate(ModelNode const& val) noexcept -> tl::expected - { - if (val.type() == ValueType::Null) [[unlikely]] - return Result::Continue; - - auto result = res(ctx, Value::field(val)); - TRY_EXPECTED(result); - if (*result == Result::Stop) [[unlikely]] - return *result; - - tl::expected finalResult = Result::Continue; - val.iterate(ModelNode::IterLambda([&, this](const auto& subNode) { - auto subResult = iterate(subNode); - if (!subResult) { - finalResult = std::move(subResult); - return false; - } - - if (*subResult == Result::Stop) { - finalResult = Result::Stop; - return false; - } - - return true; - })); + auto operator()(const ModelNode::Ptr& node) const -> asyncpp::generator { + if (node->type() != ValueType::Null) + co_yield Value::field(node); - return finalResult; + for (const auto& ptr : node->iterate()) + for (auto sub : (*this)(ptr)) + co_yield sub; } }; - auto r = val.nodePtr() ? Iterate{ctx, res}.iterate(**val.nodePtr()) : tl::expected(Result::Continue); - res.ensureCall(); - return r; + if (val.nodePtr()) { + co_yield Value::field(*val.nodePtr()); + + for (const auto& field : val.node()->iterate()) { + Iterator iter; + for (auto value : iter(field)) + co_yield value; + } + } else { + co_yield Value::null(); + } } void WildcardExpr::accept(ExprVisitor& v) const @@ -152,28 +92,20 @@ auto AnyChildExpr::type() const -> Type return Type::PATH; } -auto AnyChildExpr::ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected +auto AnyChildExpr::ieval(Context ctx, Value val) const -> EvalStream { - if (ctx.phase == Context::Phase::Compilation) - return res(ctx, Value::undef()); + if (ctx.compiling()) { + co_yield Value::undef(); + co_return; + } - if (!val.node() || !val.node()->size()) - return res(ctx, Value::null()); + if (!val.node() || !val.node()->size()) { + co_yield Value::null(); + co_return; + } - std::optional error; - val.node()->iterate(ModelNode::IterLambda([&error, &ctx, &res](auto subNode) -> bool { - auto result = res(ctx, Value::field(std::move(subNode))); - if (!result) { - error = std::move(result.error()); - return false; - } - if (*result == Result::Stop) - return false; - return true; - })); - if (error) - return tl::unexpected(std::move(*error)); - return Result::Continue; + for (const auto& ptr : val.node()->iterate()) + co_yield Value::field(ptr); } void AnyChildExpr::accept(ExprVisitor& v) const @@ -201,12 +133,7 @@ auto FieldExpr::type() const -> Type return Type::FIELD; } -auto FieldExpr::ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected -{ - return ieval(ctx, Value{val}, res); -} - -auto FieldExpr::ieval(Context ctx, Value&& val, const ResultFn& res) const -> tl::expected +auto FieldExpr::ieval(Context ctx, Value val) const -> EvalStream { Diagnostics::FieldExprData* diag = nullptr; if (ctx.diag) @@ -219,37 +146,43 @@ auto FieldExpr::ieval(Context ctx, Value&& val, const ResultFn& res) const -> tl diag->name = name_; } - if (val.isa(ValueType::Undef)) - return res(ctx, std::move(val)); + if (val.isa(ValueType::Undef)) { + co_yield std::move(val); + co_return; + } /* Special case: _ points to the current node */ if (name_ == "_") { if (diag) diag->hits++; - return res(ctx, std::move(val)); + co_yield std::move(val); + co_return; } - if (!val.node()) - return res(ctx, Value::null()); + if (!val.node()) { + co_yield Value::null(); + co_return; + } if (!nameId_) [[unlikely]] { nameId_ = ctx.env->strings()->get(name_); - if (!nameId_) + if (!nameId_) { /* If the field name is not in the string cache, then there is no field with that name. */ - return res(ctx, Value::null()); + co_yield Value::null(); + co_return; + } } /* Enter sub-node */ if (auto sub = val.node()->get(nameId_)) { if (diag) diag->hits++; - return res(ctx, Value::field(*sub)); + co_yield Value::field(*sub); + co_return; } - if (ctx.phase == Context::Phase::Compilation) - return res(ctx, Value::undef()); - return res(ctx, Value::null()); + co_yield ctx.compiling() ? Value::undef() : Value::null(); } void FieldExpr::accept(ExprVisitor& v) const @@ -282,16 +215,10 @@ auto MultiConstExpr::constant() const -> bool return true; } -auto MultiConstExpr::ieval(Context ctx, const Value&, const ResultFn& res) const -> tl::expected +auto MultiConstExpr::ieval(Context ctx, Value) const -> EvalStream { - for (const auto& v : values_) { - auto r = res(ctx, v); - TRY_EXPECTED(r); - if (*r == Result::Stop) - return Result::Stop; - } - - return Result::Continue; + for (auto value : values_) + co_yield value; } void MultiConstExpr::accept(ExprVisitor& v) const @@ -323,9 +250,9 @@ auto ConstExpr::constant() const -> bool return true; } -auto ConstExpr::ieval(Context ctx, const Value&, const ResultFn& res) const -> tl::expected +auto ConstExpr::ieval(Context ctx, Value) const -> EvalStream { - return res(ctx, value_); + co_yield value_; } void ConstExpr::accept(ExprVisitor& v) const @@ -356,43 +283,58 @@ auto SubscriptExpr::type() const -> Type return Type::SUBSCRIPT; } -auto SubscriptExpr::ieval(Context ctx, const Value& val, const ResultFn& ores) const -> tl::expected +auto SubscriptExpr::ieval(Context ctx, Value val) const -> EvalStream { - auto res = CountedResultFn(ores, ctx); - auto r = left_->eval(ctx, val, LambdaResultFn([this, &val, &res](Context ctx, const Value& lval) { - return index_->eval(ctx, val, LambdaResultFn([this, &res, &lval](Context ctx, const Value& ival) -> tl::expected { + auto empty = true; + for (auto left : left_->eval(ctx, val)) { + if (!left) { + co_yield tl::unexpected(std::move(left.error())); + co_return; + } + + for (auto index : index_->eval(ctx, val)) { + if (!index) { + co_yield tl::unexpected(std::move(index.error())); + co_return; + } + /* Field subscript */ - if (lval.node()) { + if (left->node()) { ModelNode::Ptr node; /* Array subscript */ - if (ival.isa(ValueType::Int)) { - auto index = ival.as(); - node = lval.node()->at(index); + if (index->isa(ValueType::Int)) { + auto idx = index->as(); + node = left->node()->at(idx); } /* String subscript */ - else if (ival.isa(ValueType::String)) { - auto key = ival.as(); + else if (index->isa(ValueType::String)) { + auto key = index->as(); if (auto keyStrId = ctx.env->strings()->get(key)) - node = lval.node()->get(keyStrId); + node = left->node()->get(keyStrId); } - if (node) - return res(ctx, Value::field(*node)); - else - ctx.env->warn("Invalid subscript index type "s + valueType2String(ival.type), this->toString()); + if (node) { + empty = false; + co_yield Value::field(*node); + } else { + ctx.env->warn("Invalid subscript index type "s + valueType2String(index->type), this->toString()); + } } else { - auto v = BinaryOperatorDispatcher::dispatch(lval, ival); - TRY_EXPECTED(v); - return res(ctx, std::move(v.value())); + auto v = BinaryOperatorDispatcher::dispatch(*left, *index); + if (!v) { + co_yield tl::unexpected(std::move(v.error())); + co_return; + } + + empty = false; + co_yield std::move(v.value()); } + } + } - return Result::Continue; - })); - })); - TRY_EXPECTED(r); - res.ensureCall(); - return r; + if (empty) + co_yield ctx.compiling() ? Value::undef() : Value::null(); } void SubscriptExpr::accept(ExprVisitor& v) const @@ -416,31 +358,37 @@ auto SubExpr::type() const -> Type return Type::SUBEXPR; } -auto SubExpr::ieval(Context ctx, const Value& val, const ResultFn& ores) const -> tl::expected +auto SubExpr::ieval(Context ctx, Value val) const -> EvalStream { - return ieval(ctx, Value{val}, ores); -} + auto empty = true; + for (auto left : left_->eval(ctx, val)) { + CO_TRY_EXPECTED(left); -auto SubExpr::ieval(Context ctx, Value&& val, const ResultFn& ores) const -> tl::expected -{ - /* Do not return null unless we have _no_ matching value. */ - auto res = CountedResultFn(ores, ctx); + if (left->isa(ValueType::Undef)) { + co_yield Value::undef(); + co_return; + } + + for (auto sub : sub_->eval(ctx, *left)) { + CO_TRY_EXPECTED(sub); - auto r = left_->eval(ctx, val, LambdaResultFn([this, &res](Context ctx, const Value& lv) -> tl::expected { - return sub_->eval(ctx, lv, LambdaResultFn([&res, &lv](const Context& ctx, const Value& vv) -> tl::expected { - auto bv = UnaryOperatorDispatcher::dispatch(vv); - TRY_EXPECTED(bv); - if (bv->isa(ValueType::Undef)) - return Result::Continue; + if (sub->isa(ValueType::Undef)) { + co_yield Value::undef(); + co_return; + } - if (bv->isa(ValueType::Bool) && bv->template as()) - return res(ctx, lv); + auto bv = UnaryOperatorDispatcher::dispatch(*sub); + CO_TRY_EXPECTED(bv); - return Result::Continue; - })); - })); - res.ensureCall(); - return r; + if (bv->isa(ValueType::Bool) && bv->template as()) { + empty = false; + co_yield *left; + } + } + } + + if (empty) + co_yield ctx.compiling() ? Value::undef() : Value::null(); } auto SubExpr::toString() const -> std::string @@ -463,33 +411,32 @@ auto AnyExpr::type() const -> Type return Type::VALUE; } -auto AnyExpr::ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected +auto AnyExpr::ieval(Context ctx, Value val) const -> EvalStream { - auto subctx = ctx; - auto result = false; /* At least one value is true */ - auto undef = false; /* At least one value is undef */ + auto any = false; /* At least one value is true */ for (const auto& arg : args_) { - auto res = arg->eval(ctx, val, LambdaResultFn([&](Context, const Value& vv) { - if (ctx.phase == Context::Phase::Compilation) { - if (vv.isa(ValueType::Undef)) { - undef = true; - return Result::Stop; - } + for (auto result : arg->eval(ctx, val)) { + if (!result) { + co_yield tl::unexpected(std::move(result.error())); + co_return; + } + + if (result->isa(ValueType::Undef)) { + co_yield Value::undef(); + co_return; } - result = result || boolify(vv); - return result ? Result::Stop : Result::Continue; - })); - TRY_EXPECTED(res); - if (result || undef) + any = any || boolify(*result); + if (any) + break; + } + + if (any) break; } - if (undef) - return res(subctx, Value::undef()); - - return res(subctx, Value::make(result)); + co_yield Value::make(any); } auto AnyExpr::accept(ExprVisitor& v) const -> void @@ -519,32 +466,38 @@ auto EachExpr::type() const -> Type return Type::VALUE; } -auto EachExpr::ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected +auto EachExpr::ieval(Context ctx, Value val) const -> EvalStream { - auto subctx = ctx; - auto result = true; /* All values are true */ + auto each = true; /* All values are true */ auto undef = false; /* At least one value is undef */ for (const auto& arg : args_) { - auto argRes = arg->eval(ctx, val, LambdaResultFn([&](Context, const Value& vv) { - if (ctx.phase == Context::Phase::Compilation) { - if (vv.isa(ValueType::Undef)) { + for (auto result : arg->eval(ctx, val)) { + if (!result) { + co_yield tl::unexpected(std::move(result.error())); + co_return; + } + + if (ctx.compiling()) { + if (result->isa(ValueType::Undef)) { undef = true; - return Result::Stop; + break; } } - result = result && boolify(vv); - return result ? Result::Continue : Result::Stop; - })); - TRY_EXPECTED(argRes); - if (!result || undef) + + each = each && boolify(*result); + if (!each || undef) + break; + } + + if (!each || undef) break; } if (undef) - return res(subctx, Value::undef()); - - return res(subctx, Value::make(result)); + co_yield Value::undef(); + else + co_yield Value::make(each); } auto EachExpr::accept(ExprVisitor& v) const -> void @@ -575,30 +528,33 @@ auto CallExpression::type() const -> Type return Type::VALUE; } -auto CallExpression::ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected -{ - return ieval(ctx, Value{val}, res); -} - -auto CallExpression::ieval(Context ctx, Value&& val, const ResultFn& res) const -> tl::expected +auto CallExpression::ieval(Context ctx, Value val) const -> EvalStream { if (!fn_) [[unlikely]] { fn_ = ctx.env->findFunction(name_); - if (!fn_) - return tl::unexpected(Error::UnknownFunction, fmt::format("Unknown function '{}'", name_)); + if (!fn_) { + co_yield tl::unexpected(Error::UnknownFunction, fmt::format("Unknown function '{}'", name_)); + co_return; + } } auto anyval = false; - auto result = fn_->eval(ctx, std::move(val), args_, LambdaResultFn([&res, &anyval](const Context& ctx, Value&& vv) { + for (auto result : fn_->eval(ctx, val, args_)) { + CO_TRY_EXPECTED(result); + + if (!result) { + co_yield tl::unexpected(std::move(result.error())); + co_return; + } + anyval = true; - return res(ctx, std::move(vv)); - })); - if (!result) - return result; - if (!anyval) - return tl::unexpected(Error::InternalError, "Function did not call result callback"); + co_yield *result; + } - return result; + if (!anyval) { + co_yield tl::unexpected(Error::InternalError, "Function did not call result callback"); + co_return; + } } void CallExpression::accept(ExprVisitor& v) const @@ -632,34 +588,42 @@ auto PathExpr::type() const -> Type return Type::PATH; } -auto PathExpr::ieval(Context ctx, const Value& val, const ResultFn& ores) const -> tl::expected +auto PathExpr::ieval(Context ctx, Value val) const -> EvalStream { - return ieval(ctx, Value{val}, ores); -} + auto empty = true; + for (auto left : left_->eval(ctx, std::move(val))) { + if (!left) { + co_yield tl::unexpected(std::move(left.error())); + co_return; + } -auto PathExpr::ieval(Context ctx, Value&& val, const ResultFn& ores) const -> tl::expected -{ - auto res = CountedResultFn(ores, ctx); + if (left->isa(ValueType::Undef)) { + co_yield Value::undef(); + co_return; + } - auto r = left_->eval(ctx, std::move(val), LambdaResultFn([this, &res](Context ctx, Value&& v) -> tl::expected { - if (v.isa(ValueType::Undef)) - return Result::Continue; + if (left->isa(ValueType::Null) && !left->node()) + continue; - if (v.isa(ValueType::Null) && !v.node()) - return Result::Continue; + for (auto right : right_->eval(ctx, std::move(*left))) { + if (!right) { + co_yield tl::unexpected(std::move(right.error())); + co_return; + } - return right_->eval(ctx, std::move(v), LambdaResultFn([this, &res](Context ctx, Value&& vv) -> tl::expected { - if (vv.isa(ValueType::Undef)) - return Result::Continue; + if (right->isa(ValueType::Undef)) + continue; - if (vv.isa(ValueType::Null) && !vv.node()) - return Result::Continue; + if (right->isa(ValueType::Null) && !right->node()) + continue; - return res(ctx, std::move(vv)); - })); - })); - res.ensureCall(); - return r; + empty = false; + co_yield right; + } + } + + if (empty) + co_yield ctx.compiling() ? Value::undef() : Value::null(); }; void PathExpr::accept(ExprVisitor& v) const @@ -682,34 +646,37 @@ auto UnpackExpr::type() const -> Type return Type::VALUE; } -auto UnpackExpr::ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected +auto UnpackExpr::ieval(Context ctx, Value val) const -> EvalStream { - auto anyval = false; - auto r = sub_->eval(ctx, val, LambdaResultFn([&res, &anyval](Context ctx, Value&& v) -> tl::expected { - if (v.isa(ValueType::TransientObject)) { - const auto& obj = v.as(); - auto r = Result::Continue; - obj.meta->unpack(obj, [&](Value vv) { - anyval = true; - return res(ctx, std::move(vv)) == Result::Continue; + auto empty = true; + for (auto result : sub_->eval(ctx, std::move(val))) { + CO_TRY_EXPECTED(result); + + if (result->isa(ValueType::TransientObject)) { + const auto& obj = result->as(); + + std::vector values; + std::optional error; + + auto unpackResult = obj.meta->unpack(obj, [&values, &error](Value value) -> bool { + values.push_back(std::move(value)); + return true; }); + CO_TRY_EXPECTED(unpackResult); - if (r == Result::Stop) - return Result::Stop; - } else { - anyval = true; - auto r = res(ctx, std::move(v)); - TRY_EXPECTED(r); - if (*r == Result::Stop) - return Result::Stop; + for (auto value : values) { + empty = false; + co_yield value; + } } - return Result::Continue; - })); - TRY_EXPECTED(r); + else { + empty = false; + co_yield result; + } + } - if (!anyval) - r = res(ctx, Value::null()); - return r; + if (empty) + co_yield ctx.compiling() ? Value::undef() : Value::null(); } void UnpackExpr::accept(ExprVisitor& v) const @@ -733,22 +700,33 @@ auto UnaryWordOpExpr::type() const -> Type return Type::VALUE; } -auto UnaryWordOpExpr::ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected +auto UnaryWordOpExpr::ieval(Context ctx, Value val) const -> EvalStream { - return left_->eval(ctx, val, LambdaResultFn([this, &res](const Context& ctx, Value&& val) -> tl::expected { - if (val.isa(ValueType::Undef)) - return res(ctx, std::move(val)); + auto empty = true; + for (auto left : left_->eval(ctx, val)) { + CO_TRY_EXPECTED(left); - if (val.isa(ValueType::TransientObject)) { - const auto& obj = val.as(); - auto v = obj.meta->unaryOp(ident_, obj); - TRY_EXPECTED(v); - return res(ctx, std::move(v.value())); + if (left->isa(ValueType::Undef)) { + empty = false; + co_yield left; } + else if (left->isa(ValueType::TransientObject)) { + const auto& obj = left->as(); + auto resolved = obj.meta->unaryOp(ident_, obj); + CO_TRY_EXPECTED(resolved); - return tl::unexpected(Error::InvalidOperator, - fmt::format("Invalid operator '{}' for value of type {}", ident_, valueType2String(val.type))); - })); + empty = false; + co_yield std::move(resolved.value()); + } + else { + co_yield tl::unexpected(Error::InvalidOperator, + fmt::format("Invalid operator '{}' for value of type {}", ident_, valueType2String(left->type))); + co_return; + } + } + + if (empty) + co_yield ctx.compiling() ? Value::undef() : Value::null(); } void UnaryWordOpExpr::accept(ExprVisitor& v) const @@ -773,32 +751,44 @@ auto BinaryWordOpExpr::type() const -> Type return Type::VALUE; } -auto BinaryWordOpExpr::ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected +auto BinaryWordOpExpr::ieval(Context ctx, Value val) const -> EvalStream { - return left_->eval(ctx, val, LambdaResultFn([this, &res, &val](const Context& ctx, const Value& lval) { - return right_->eval(ctx, val, LambdaResultFn([this, &res, &lval](const Context& ctx, const Value& rval) -> tl::expected { - if (lval.isa(ValueType::Undef) || rval.isa(ValueType::Undef)) - return res(ctx, Value::undef()); + auto empty = false; + for (auto left : left_->eval(ctx, val)) { + CO_TRY_EXPECTED(left); - if (lval.isa(ValueType::TransientObject)) { - const auto& obj = lval.as(); - auto v = obj.meta->binaryOp(ident_, obj, rval); - TRY_EXPECTED(v); - return res(ctx, std::move(v.value())); + for (auto right : right_->eval(ctx, val)) { + CO_TRY_EXPECTED(right); + + if (left->isa(ValueType::Undef) || right->isa(ValueType::Undef)) { + co_yield Value::undef(); } + else if (left->isa(ValueType::TransientObject)) { + const auto& obj = left->as(); + auto v = obj.meta->binaryOp(ident_, obj, *right); + CO_TRY_EXPECTED(v); - if (rval.isa(ValueType::TransientObject)) { - const auto& obj = rval.as(); - auto v = obj.meta->binaryOp(ident_, lval, obj); - TRY_EXPECTED(v); - return res(ctx, std::move(v.value())); + co_yield std::move(v.value()); } + else if (right->isa(ValueType::TransientObject)) { + const auto& obj = right->as(); + auto v = obj.meta->binaryOp(ident_, *left, obj); + CO_TRY_EXPECTED(v); + + co_yield std::move(v.value()); + } + else { + co_yield tl::unexpected(Error::InvalidOperator, + fmt::format("Invalid operator '{}' for values of type {} and {}", + ident_, valueType2String(left->type), valueType2String(right->type))); + } + + empty = false; + } + } - return tl::unexpected(Error::InvalidOperator, - fmt::format("Invalid operator '{}' for values of type {} and {}", - ident_, valueType2String(lval.type), valueType2String(rval.type))); - })); - })); + if (empty) + co_yield ctx.compiling() ? Value::undef() : Value::null(); } void BinaryWordOpExpr::accept(ExprVisitor& v) const @@ -825,24 +815,33 @@ auto AndExpr::type() const -> Type return Type::VALUE; } -auto AndExpr::ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected +auto AndExpr::ieval(Context ctx, Value val) const -> EvalStream { /* Operator and behaves like in lua: * 'a and b' returns a if 'not a?' else b is returned */ - return left_->eval(ctx, val, LambdaResultFn([this, &res, &val](const Context& ctx, Value&& lval) -> tl::expected { - if (lval.isa(ValueType::Undef)) - return res(ctx, lval); + for (auto left : left_->eval(ctx, val)) { + CO_TRY_EXPECTED(left); - auto v = UnaryOperatorDispatcher::dispatch(lval); - TRY_EXPECTED(v); - if (v->isa(ValueType::Bool)) - if (!v->template as()) - return res(ctx, std::move(lval)); + if (left->isa(ValueType::Undef)) { + co_yield left; + } + else { + auto boolean = UnaryOperatorDispatcher::dispatch(*left); + CO_TRY_EXPECTED(boolean); + + if (boolean->isa(ValueType::Bool)) { + if (!boolean->template as()) { + co_yield std::move(*left); + co_return; // Short circuit + } + } - return right_->eval(ctx, val, LambdaResultFn([&res](const Context& ctx, Value&& rval) { - return res(ctx, std::move(rval)); - })); - })); + for (auto right : right_->eval(ctx, val)) { + CO_TRY_EXPECTED(right); + co_yield *right; + } + } + } } void AndExpr::accept(ExprVisitor& v) const @@ -869,25 +868,33 @@ auto OrExpr::type() const -> Type return Type::VALUE; } -auto OrExpr::ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected +auto OrExpr::ieval(Context ctx, Value val) const -> EvalStream { /* Operator or behaves like in lua: * 'a or b' returns a if 'a?' else b is returned */ - return left_->eval(ctx, val, LambdaResultFn([this, &res, &val](Context ctx, Value&& lval) -> tl::expected { - if (lval.isa(ValueType::Undef)) - return res(ctx, lval); + for (auto left : left_->eval(ctx, val)) { + CO_TRY_EXPECTED(left); - auto v = UnaryOperatorDispatcher::dispatch(lval); - TRY_EXPECTED(v); - if (v->isa(ValueType::Bool)) - if (v->template as()) - return res(ctx, std::move(lval)); - - return right_->eval(ctx, val, LambdaResultFn([&](Context ctx, Value&& rval) { - return res(ctx, std::move(rval)); - })); - })); + if (left->isa(ValueType::Undef)) { + co_yield left; + } + else { + auto boolean = UnaryOperatorDispatcher::dispatch(*left); + CO_TRY_EXPECTED(boolean); + + if (boolean->isa(ValueType::Bool)) { + if (boolean->template as()) { + co_yield std::move(*left); + co_return; // Short circuit + } + } + for (auto right : right_->eval(ctx, val)) { + CO_TRY_EXPECTED(right); + co_yield *right; + } + } + } } void OrExpr::accept(ExprVisitor& v) const diff --git a/src/expressions.h b/src/expressions.h index c98257ec..97976068 100644 --- a/src/expressions.h +++ b/src/expressions.h @@ -9,16 +9,22 @@ #include #include +#define CO_TRY_EXPECTED(value) \ + do { if (!(value)) { co_yield tl::unexpected(std::move((value).error())); co_return; } } while (false) + namespace simfil { +/** + * Returns every child recursive. + */ class WildcardExpr : public Expr { public: explicit WildcardExpr(ExprId); auto type() const -> Type override; - auto ieval(Context ctx, const Value& val, const ResultFn& ores) const -> tl::expected override; + auto ieval(Context ctx, Value val) const -> EvalStream override; void accept(ExprVisitor& v) const override; auto toString() const -> std::string override; }; @@ -32,7 +38,7 @@ class AnyChildExpr : public Expr explicit AnyChildExpr(ExprId); auto type() const -> Type override; - auto ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected override; + auto ieval(Context ctx, Value val) const -> EvalStream override; void accept(ExprVisitor& v) const override; auto toString() const -> std::string override; }; @@ -44,8 +50,7 @@ class FieldExpr : public Expr FieldExpr(ExprId id, std::string name, const Token& token); auto type() const -> Type override; - auto ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected override; - auto ieval(Context ctx, Value&& val, const ResultFn& res) const -> tl::expected override; + auto ieval(Context ctx, Value val) const -> EvalStream override; void accept(ExprVisitor& v) const override; auto toString() const -> std::string override; @@ -64,7 +69,7 @@ class MultiConstExpr : public Expr auto type() const -> Type override; auto constant() const -> bool override; - auto ieval(Context ctx, const Value&, const ResultFn& res) const -> tl::expected override; + auto ieval(Context ctx, Value val) const -> EvalStream override; void accept(ExprVisitor& v) const override; auto toString() const -> std::string override; @@ -84,7 +89,7 @@ class ConstExpr : public Expr auto type() const -> Type override; auto constant() const -> bool override; - auto ieval(Context ctx, const Value&, const ResultFn& res) const -> tl::expected override; + auto ieval(Context ctx, Value val) const -> EvalStream override; void accept(ExprVisitor& v) const override; auto toString() const -> std::string override; @@ -100,7 +105,7 @@ class SubscriptExpr : public Expr SubscriptExpr(ExprId id, ExprPtr left, ExprPtr index); auto type() const -> Type override; - auto ieval(Context ctx, const Value& val, const ResultFn& ores) const -> tl::expected override; + auto ieval(Context ctx, Value val) const -> EvalStream override; void accept(ExprVisitor& v) const override; auto toString() const -> std::string override; @@ -114,8 +119,7 @@ class SubExpr : public Expr SubExpr(ExprId id, ExprPtr left, ExprPtr sub); auto type() const -> Type override; - auto ieval(Context ctx, const Value& val, const ResultFn& ores) const -> tl::expected override; - auto ieval(Context ctx, Value&& val, const ResultFn& ores) const -> tl::expected override; + auto ieval(Context ctx, Value val) const -> EvalStream override; void accept(ExprVisitor& v) const override; auto toString() const -> std::string override; @@ -128,7 +132,7 @@ class AnyExpr : public Expr AnyExpr(ExprId id, std::vector args); auto type() const -> Type override; - auto ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected override; + auto ieval(Context ctx, Value val) const -> EvalStream override; void accept(ExprVisitor& v) const override; auto toString() const -> std::string override; @@ -141,7 +145,7 @@ class EachExpr : public Expr EachExpr(ExprId id, std::vector args); auto type() const -> Type override; - auto ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected override; + auto ieval(Context ctx, Value val) const -> EvalStream override; void accept(ExprVisitor& v) const override; auto toString() const -> std::string override; @@ -154,8 +158,7 @@ class CallExpression : public Expr CallExpression(ExprId id, std::string name, std::vector args); auto type() const -> Type override; - auto ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected override; - auto ieval(Context ctx, Value&& val, const ResultFn& res) const -> tl::expected override; + auto ieval(Context ctx, Value val) const -> EvalStream override; void accept(ExprVisitor& v) const override; auto toString() const -> std::string override; @@ -170,8 +173,7 @@ class PathExpr : public Expr PathExpr(ExprId id, ExprPtr left, ExprPtr right); auto type() const -> Type override; - auto ieval(Context ctx, const Value& val, const ResultFn& ores) const -> tl::expected override; - auto ieval(Context ctx, Value&& val, const ResultFn& ores) const -> tl::expected override; + auto ieval(Context ctx, Value val) const -> EvalStream override; void accept(ExprVisitor& v) const override; auto toString() const -> std::string override; @@ -189,7 +191,7 @@ class UnpackExpr : public Expr UnpackExpr(ExprId id, ExprPtr sub); auto type() const -> Type override; - auto ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected override; + auto ieval(Context ctx, Value val) const -> EvalStream override; void accept(ExprVisitor& v) const override; auto toString() const -> std::string override; @@ -213,14 +215,16 @@ class UnaryExpr : public Expr return Type::VALUE; } - auto ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected override + auto ieval(Context ctx, Value val) const -> EvalStream override { - return sub_->eval(ctx, val, LambdaResultFn([&](Context ctx, Value vv) -> tl::expected { - auto v = UnaryOperatorDispatcher::dispatch(std::move(vv)); - if (!v) - return tl::unexpected(std::move(v.error())); - return res(ctx, std::move(v.value())); - })); + for (auto value : sub_->eval(ctx, val)) { + CO_TRY_EXPECTED(value); + + auto resolved = UnaryOperatorDispatcher::dispatch(*value); + CO_TRY_EXPECTED(resolved); + + co_yield resolved; + } } void accept(ExprVisitor& v) const override @@ -261,16 +265,20 @@ class BinaryExpr : public Expr return Type::VALUE; } - auto ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected override + auto ieval(Context ctx, Value val) const -> EvalStream override { - return left_->eval(ctx, val, LambdaResultFn([this, &res, &val](Context ctx, Value lv) { - return right_->eval(ctx, val, LambdaResultFn([this, &res, &lv](Context ctx, Value rv) -> tl::expected { - auto v = BinaryOperatorDispatcher::dispatch(std::move(lv), std::move(rv)); - if (!v) - return tl::unexpected(std::move(v.error())); - return res(ctx, std::move(v.value())); - })); - })); + for (auto left : left_->eval(ctx, val)) { + CO_TRY_EXPECTED(left); + + for (auto right : right_->eval(ctx, val)) { + CO_TRY_EXPECTED(right); + + auto resolved = BinaryOperatorDispatcher::dispatch(*left, *right); + CO_TRY_EXPECTED(resolved); + + co_yield resolved; + } + } } void accept(ExprVisitor& v) const override @@ -318,7 +326,7 @@ class ComparisonExpr : public ComparisonExprBase public: using ComparisonExprBase::ComparisonExprBase; - auto ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected override + auto ieval(Context ctx, Value val) const -> EvalStream override { Diagnostics::ComparisonExprData* diag = nullptr; if (ctx.diag) @@ -328,28 +336,31 @@ class ComparisonExpr : public ComparisonExprBase diag->evaluations++; } - return left_->eval(ctx, val, LambdaResultFn([this, &res, &val, &diag](Context ctx, Value lv) { + for (auto left : left_->eval(ctx, val)) { + CO_TRY_EXPECTED(left); + if (diag) - diag->leftTypes.set(lv.type); + diag->leftTypes.set(left->type); + + for (auto right : right_->eval(ctx, val)) { + CO_TRY_EXPECTED(right); - return right_->eval(ctx, val, LambdaResultFn([this, &res, &lv, &diag](Context ctx, Value rv) -> tl::expected { if (diag) - diag->rightTypes.set(rv.type); + diag->rightTypes.set(right->type); - auto operatorResult = BinaryOperatorDispatcher::dispatch(std::move(lv), std::move(rv)); - if (!operatorResult) - return tl::unexpected(std::move(operatorResult.error())); + auto resolved = BinaryOperatorDispatcher::dispatch(*left, *right); + CO_TRY_EXPECTED(resolved); - if (diag && operatorResult->isa(ValueType::Bool)) { - if (operatorResult->template as()) + if (diag && resolved->isa(ValueType::Bool)) { + if (resolved->template as()) diag->trueResults++; else diag->falseResults++; } - return res(ctx, std::move(operatorResult.value())); - })); - })); + co_yield resolved; + } + } } void accept(ExprVisitor& v) const override @@ -407,7 +418,7 @@ class UnaryWordOpExpr : public Expr UnaryWordOpExpr(ExprId id, std::string ident, ExprPtr left); auto type() const -> Type override; - auto ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected override; + auto ieval(Context ctx, Value val) const -> EvalStream override; void accept(ExprVisitor& v) const override; auto toString() const -> std::string override; @@ -421,7 +432,7 @@ class BinaryWordOpExpr : public Expr BinaryWordOpExpr(ExprId id, std::string ident, ExprPtr left, ExprPtr right); auto type() const -> Type override; - auto ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected override; + auto ieval(Context ctx, Value val) const -> EvalStream override; void accept(ExprVisitor& v) const override; auto toString() const -> std::string override; @@ -435,7 +446,7 @@ class AndExpr : public Expr AndExpr(ExprId id, ExprPtr left, ExprPtr right); auto type() const -> Type override; - auto ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected override; + auto ieval(Context ctx, Value val) const -> EvalStream override; void accept(ExprVisitor& v) const override; auto toString() const -> std::string override; @@ -448,7 +459,7 @@ class OrExpr : public Expr OrExpr(ExprId id, ExprPtr left, ExprPtr right); auto type() const -> Type override; - auto ieval(Context ctx, const Value& val, const ResultFn& res) const -> tl::expected override; + auto ieval(Context ctx, Value val) const -> EvalStream override; void accept(ExprVisitor& v) const override; auto toString() const -> std::string override; diff --git a/src/function.cpp b/src/function.cpp index 9a57fd41..84edaa4a 100644 --- a/src/function.cpp +++ b/src/function.cpp @@ -1,5 +1,6 @@ #include "simfil/function.h" +#include "simfil/expression.h" #include "simfil/model/nodes.h" #include "simfil/result.h" #include "simfil/operator.h" @@ -8,6 +9,7 @@ #include "simfil/types.h" #include "simfil/overlay.h" #include "fmt/core.h" +#include "src/expressions.h" #include "tl/expected.hpp" #include "expected.h" @@ -43,35 +45,37 @@ struct ArgParser auto arg(const char* name, ValueType type, Value& outValue) -> ArgParser& { if (args.size() <= idx) { - error = Error(Error::InvalidArguments, fmt::format("missing argument {} for function {}", name, this->functionName)); + error.emplace(Error::InvalidArguments, fmt::format("missing argument {} for function {}", name, this->functionName)); return *this; } auto subctx = ctx; - auto res = args[idx]->eval(subctx, value, LambdaResultFn([&, n = 0](Context, Value&& vv) mutable -> tl::expected { + auto n = 0u; + for (auto value : args[idx]->eval(subctx, value)) { + if (!value) { + error.emplace(std::move(value.error())); + outValue = std::move(*value); + break; + } + if (++n > 1) [[unlikely]] { - return tl::unexpected(Error::ExpectedSingleValue, - fmt::format("expeted single argument value for argument {} for function {}", name, functionName)); + error.emplace(Error::ExpectedSingleValue, + fmt::format("expected single argument value for argument {} for function {}", name, functionName)); + break; } - if (vv.isa(ValueType::Undef)) { + if (value->isa(ValueType::Undef)) { anyUndef = true; - outValue = std::move(vv); - return Result::Continue; + outValue = std::move(*value); } - - if (!vv.isa(type)) [[unlikely]] { - return tl::unexpected(Error::TypeMissmatch, - fmt::format("invalid type for argument {} for function {}", name, functionName)); + else if (!value->isa(type)) [[unlikely]] { + error.emplace(Error::TypeMissmatch, + fmt::format("invalid type for argument {} for function {}", name, functionName)); + break; } - outValue = std::move(vv); - - return Result::Continue; - })); - - if (!res) [[unlikely]] - error = std::move(res.error()); + outValue = std::move(*value); + } ++idx; return *this; @@ -84,28 +88,28 @@ struct ArgParser return *this; } + auto n = 0u; auto subctx = ctx; - auto res = args[idx]->eval(subctx, value, LambdaResultFn([&, n = 0](Context, Value&& vv) mutable -> tl::expected { + for (auto value : args[idx]->eval(subctx, value)) { if (++n > 1) { - return tl::unexpected(Error::ExpectedSingleValue, - fmt::format("{}: argument {} must return a single value", functionName, name)); + error.emplace(Error::ExpectedSingleValue, + fmt::format("{}: argument {} must return a single value", functionName, name)); + break; } - if (vv.isa(ValueType::Undef)) { + if (value->isa(ValueType::Undef)) { anyUndef = true; - outValue = std::move(vv); - return Result::Continue; + outValue = std::move(*value); } - if (!vv.isa(type)) - return tl::unexpected(Error::TypeMissmatch, - fmt::format("{}: invalid value type for argument", functionName, name)); + if (!value->isa(type)) { + error.emplace(Error::TypeMissmatch, + fmt::format("{}: invalid value type for argument", functionName, name)); + break; + } - outValue = std::move(vv); - return Result::Continue; - })); - if (!res) [[unlikely]] - error = std::move(res.error()); + outValue = std::move(*value); + }; ++idx; return *this; @@ -143,36 +147,31 @@ auto CountFn::ident() const -> const FnInfo& return info; } -auto CountFn::eval(Context ctx, const Value& val, const std::vector& args, const ResultFn& res) const -> tl::expected +auto CountFn::eval(Context ctx, Value val, const std::vector& args) const -> EvalStream { - if (args.empty()) - return tl::unexpected(Error::InvalidArguments, - fmt::format("function 'count' expects one argument, got {}", args.size())); + if (args.empty()) { + co_yield tl::unexpected(Error::InvalidArguments, + fmt::format("function 'count' expects one argument, got {}", args.size())); + co_return; + } - auto subctx = ctx; - auto undef = false; /* At least one value is undef */ int64_t count = 0; - for (const auto& arg : args) { - auto evalRes = arg->eval(ctx, val, LambdaResultFn([&](Context, const Value& vv) { - if (ctx.phase == Context::Phase::Compilation) { - if (vv.isa(ValueType::Undef)) { - undef = true; - return Result::Stop; + for (const auto& value : arg->eval(ctx, val)) { + CO_TRY_EXPECTED(value); + + if (ctx.compiling()) { + if (value->isa(ValueType::Undef)) { + co_yield Value::undef(); + co_return; } } - count += boolify(vv) ? 1 : 0; - return Result::Continue; - })); - TRY_EXPECTED(evalRes); - if (undef) - break; + count += boolify(*value) ? 1 : 0; + } } - if (undef) - return res(subctx, Value::undef()); - return res(subctx, Value::make(count)); + co_yield Value::make(count); } TraceFn TraceFn::Fn; @@ -188,8 +187,14 @@ auto TraceFn::ident() const -> const FnInfo& return info; } -auto TraceFn::eval(Context ctx, const Value& val, const std::vector& args, const ResultFn& res) const -> tl::expected +auto TraceFn::eval(Context ctx, Value val, const std::vector& args) const -> EvalStream { + /* Never run in compilation phase */ + if (ctx.compiling()) { + co_yield Value::undef(); + co_return; + } + Value name = Value::undef(); Value limit = Value::undef(); @@ -198,27 +203,27 @@ auto TraceFn::eval(Context ctx, const Value& val, const std::vector& ar .opt("limit", ValueType::Int, limit, Value::make(static_cast(-1))) .opt("name", ValueType::String, name, Value::make(args[0]->toString())) .ok(); - TRY_EXPECTED(ok); - - /* Never run in compilation phase */ - if (ctx.phase == Context::Phase::Compilation) - return res(ctx, Value::undef()); + CO_TRY_EXPECTED(ok); auto sname = name.as(); auto ilimit = limit.as(); auto values = std::vector(); + auto n = 0; auto start = std::chrono::steady_clock::now(); - auto result = args[0]->eval(ctx, val, LambdaResultFn([&, n = 0](Context ctx, Value vv) mutable { - if (ilimit < 0 || n++ <= ilimit) { + for (auto value : args[0]->eval(ctx, val)) { + CO_TRY_EXPECTED(value); + + n++; + if (ilimit < 0 || n <= ilimit) { // Do not allow string view to leak into the trace result. - auto copy = vv; - if (auto sv = vv.stringViewValue()) - copy = Value::make(std::string(*sv)); - values.emplace_back(std::move(copy)); + if (auto sv = value->stringViewValue()) + value = Value::make(std::string(*sv)); + values.emplace_back(*value); } - return res(ctx, std::move(vv)); - })); + + co_yield std::move(*value); + } auto duration = std::chrono::steady_clock::now() - start; ctx.env->trace(sname, [&](auto& t) { @@ -232,7 +237,8 @@ auto TraceFn::eval(Context ctx, const Value& val, const std::vector& ar t.values.resize(ilimit, Value::undef()); }); - return result; + if (n == 0) + co_yield Value::undef(); } @@ -249,11 +255,13 @@ auto RangeFn::ident() const -> const FnInfo& return info; } -auto RangeFn::eval(Context ctx, const Value& val, const std::vector& args, const ResultFn& res) const -> tl::expected +auto RangeFn::eval(Context ctx, Value val, const std::vector& args) const -> EvalStream { - if (args.size() != 2) - return tl::unexpected(Error::InvalidArguments, - fmt::format("function 'range' expects 2 arguments, got {}", args.size())); + if (args.size() != 2) { + co_yield tl::unexpected(Error::InvalidArguments, + fmt::format("function 'range' expects 2 arguments, got {}", args.size())); + co_return; + } Value begin = Value::undef(); Value end = Value::undef(); @@ -262,13 +270,16 @@ auto RangeFn::eval(Context ctx, const Value& val, const std::vector& ar .arg("begin", ValueType::Int, begin) .arg("end", ValueType::Int, end) .ok(); - TRY_EXPECTED(ok); - if (!ok.value()) [[unlikely]] - return res(ctx, Value::undef()); + CO_TRY_EXPECTED(ok); + + if (!ok.value()) [[unlikely]] { + co_yield Value::undef(); + co_return; + } auto ibegin = begin.as(); auto iend = end.as(); - return res(ctx, IRangeType::Type.make(ibegin, iend)); + co_yield IRangeType::Type.make(ibegin, iend); } ReFn ReFn::Fn; @@ -284,28 +295,42 @@ auto ReFn::ident() const -> const FnInfo& return info; } -auto ReFn::eval(Context ctx, const Value& val, const std::vector& args, const ResultFn& res) const -> tl::expected +auto ReFn::eval(Context ctx, Value val, const std::vector& args) const -> EvalStream { - if (args.size() != 1) - return tl::unexpected(Error::InvalidArguments, - fmt::format("'re' expects 1 argument, got {}", args.size())); + if (args.size() != 0) { + co_yield tl::unexpected(Error::InvalidArguments, + fmt::format("'re' expects 1 argument, got {}", args.size())); + co_return; + } + + for (auto value : args[0]->eval(ctx, val)) { + CO_TRY_EXPECTED(value); - auto subctx = ctx; - return args[0]->eval(subctx, val, LambdaResultFn([&](Context, Value&& vv) -> tl::expected { - if (vv.isa(ValueType::Undef)) - return res(ctx, Value::undef()); + if (value->isa(ValueType::Undef)) { + co_yield Value::undef(); + co_return; + } - if (vv.isa(ValueType::String)) - return res(ctx, ReType:: Type.make(vv.as())); + if (value->isa(ValueType::String)) { + co_yield ReType::Type.make(value->as()); + co_return; + } // Passing another object is a no-op - if (vv.isa(ValueType::TransientObject)) - if (const auto obj = vv.as(); obj.meta == &ReType::Type) - return res(ctx, std::move(vv)); + if (value->isa(ValueType::TransientObject)) { + if (const auto obj = value->as(); obj.meta == &ReType::Type) { + co_yield std::move(value); + co_return; + } + } - return tl::unexpected(Error::TypeMissmatch, - fmt::format("invalid type for argument 'expr' for function 're'")); - })); + co_yield tl::unexpected(Error::TypeMissmatch, + fmt::format("invalid type for argument 'expr' for function 're'")); + co_return; + } + + assert(0 && "unreachable"); + co_yield Value::null(); } ArrFn ArrFn::Fn; @@ -322,21 +347,24 @@ auto ArrFn::ident() const -> const FnInfo& } -auto ArrFn::eval(Context ctx, const Value& val, const std::vector& args, const ResultFn& res) const -> tl::expected +auto ArrFn::eval(Context ctx, Value val, const std::vector& args) const -> EvalStream { - if (args.empty()) - return res(ctx, Value::null()); + if (args.empty()) { + co_yield Value::null(); + co_return; + } + auto empty = true; for (const auto& arg : args) { - auto r = arg->eval(ctx, val, LambdaResultFn([&res](Context ctx, Value &&vv) { - return res(ctx, std::move(vv)); - })); - TRY_EXPECTED(r); - if (*r == Result::Stop) - return Result::Stop; + for (auto value : arg->eval(ctx, val)) { + CO_TRY_EXPECTED(value); + empty = false; + co_yield std::move(*value); + } } - return Result::Continue; + if (empty) + co_yield ctx.compiling() ? Value::undef() : Value::null(); } SplitFn SplitFn::Fn; @@ -355,8 +383,8 @@ auto SplitFn::ident() const -> const FnInfo& namespace { template > ContainerType split(std::string_view what, - std::string_view at, - bool removeEmpty = true) + std::string_view at, + bool removeEmpty = true) { using ResultType = typename ContainerType::value_type; @@ -397,7 +425,7 @@ ContainerType split(std::string_view what, } } -auto SplitFn::eval(Context ctx, const Value& val, const std::vector& args, const ResultFn& res) const -> tl::expected +auto SplitFn::eval(Context ctx, Value val, const std::vector& args) const -> EvalStream { Value str = Value::undef(); Value sep = Value::undef(); @@ -408,22 +436,22 @@ auto SplitFn::eval(Context ctx, const Value& val, const std::vector& ar .arg("separator", ValueType::String, sep) .opt("keepEmpty", ValueType::Bool, keepEmpty, Value::t()) .ok(); - TRY_EXPECTED(ok); + CO_TRY_EXPECTED(ok); - auto subctx = ctx; - if (!ok.value()) [[unlikely]] - return res(subctx, Value::undef()); + if (!ok.value()) [[unlikely]] { + co_yield Value::undef(); + co_return; + } auto items = split(str.as(), sep.as(), !keepEmpty.as()); - for (auto&& item : items) { - auto r = res(subctx, Value::make(std::move(item))); - TRY_EXPECTED(r); - if (*r == Result::Stop) - break; + if (!items.empty()) { + for (auto&& item : items) + co_yield Value::make(std::move(item)); } - - return Result::Continue; -}; + else { + co_yield std::move(str); + } +} SelectFn SelectFn::Fn; SelectFn::SelectFn() = default; @@ -438,7 +466,7 @@ auto SelectFn::ident() const -> const FnInfo& return info; } -auto SelectFn::eval(Context ctx, const Value& val, const std::vector& args, const ResultFn& res) const -> tl::expected +auto SelectFn::eval(Context ctx, Value val, const std::vector& args) const -> EvalStream { Value idx = Value::undef(); Value cnt = Value::undef(); @@ -448,29 +476,42 @@ auto SelectFn::eval(Context ctx, const Value& val, const std::vector& a .arg("index", ValueType::Int, idx) .opt("limit", ValueType::Int, cnt, Value::make(static_cast(1))) .ok(); - TRY_EXPECTED(ok); + CO_TRY_EXPECTED(ok); - if (!ok.value()) [[unlikely]] - return res(ctx, Value::undef()); + if (!ok.value()) [[unlikely]] { + co_yield Value::undef(); + co_return; + } auto iidx = idx.as(); auto icnt = cnt.as(); if (icnt <= 0) icnt = std::numeric_limits::max(); - auto result = args[0]->eval(ctx, val, LambdaResultFn([&, n = -1](Context ctx, Value&& vv) mutable -> tl::expected { - ++n; - if (ctx.phase == Context::Phase::Compilation) - if (vv.isa(ValueType::Undef)) - return res(ctx, std::move(vv)); + auto empty = true; + auto n = -1; + for (auto value : args[0]->eval(ctx, val)) { + CO_TRY_EXPECTED(value); + + n++; + // if (ctx.compiling()) { + // if (value->isa(ValueType::Undef)) { + // co_yield Value::undef(); + // co_return; + // } + // } + if (n >= iidx + icnt) - return Result::Stop; - if (n >= iidx) - return res(ctx, std::move(vv)); - return Result::Continue; - })); + co_return; - return result; + if (n >= iidx) { + empty = false; + co_yield std::move(value); + } + } + + if (empty) + co_yield ctx.compiling() ? Value::undef() : Value::null(); } SumFn SumFn::Fn; @@ -486,56 +527,59 @@ auto SumFn::ident() const -> const FnInfo& return info; } -auto SumFn::eval(Context ctx, const Value& val, const std::vector& args, const ResultFn& res) const -> tl::expected +auto SumFn::eval(Context ctx, Value val, const std::vector& args) const -> EvalStream { - if (args.empty() || args.size() > 3) - return tl::unexpected(Error::InvalidArguments, - fmt::format("'sum' expects at least 1 argument, got {}", args.size())); + if (args.empty() || args.size() > 3) { + co_yield tl::unexpected(Error::InvalidArguments, + fmt::format("'sum' expects at least 1 argument, got {}", args.size())); + co_return; + } Value sum = Value::make(static_cast(0)); Expr* subexpr = args.size() >= 2 ? args[1].get() : nullptr; Expr* initval = args.size() == 3 ? args[2].get() : nullptr; if (initval) { - auto initRes = initval->eval(ctx, val, LambdaResultFn([&sum](Context, Value&& vv) { - sum = std::move(vv); - return Result::Continue; - })); - - TRY_EXPECTED(initRes); - if (sum.isa(ValueType::Undef)) - return res(ctx, sum); + for (auto value : initval->eval(ctx, val)) { + CO_TRY_EXPECTED(value); + sum = std::move(*value); + } + + if (sum.isa(ValueType::Undef)) { + co_yield Value::undef(); + co_return; + } } - auto argRes = args[0]->eval(ctx, val, LambdaResultFn([&, n = 0](Context ctx, Value&& vv) mutable -> tl::expected { + auto n = 0; + for (auto value : args[0]->eval(ctx, val)) { if (subexpr) { - auto ov = model_ptr::make(vv); + auto ov = model_ptr::make(*value); ov->set(StringPool::OverlaySum, sum); - ov->set(StringPool::OverlayValue, vv); + ov->set(StringPool::OverlayValue, *value); ov->set(StringPool::OverlayIndex, Value::make(static_cast(n))); - n += 1; - - auto subRes = subexpr->eval(ctx, Value::field(ov), LambdaResultFn([&ov, &sum](auto ctx, Value&& vv) { - ov->set(StringPool::OverlaySum, vv); - sum = vv; - return Result::Continue; - })); - TRY_EXPECTED(subRes); - } else { + + n++; + for (auto subValue : subexpr->eval(ctx, Value::field(ov))) { + CO_TRY_EXPECTED(subValue); + + ov->set(StringPool::OverlaySum, *subValue); + sum = *subValue; + } + } + else { if (sum.isa(ValueType::Null)) { - sum = std::move(vv); - } else { - auto newSum = BinaryOperatorDispatcher::dispatch(sum, vv); - TRY_EXPECTED(newSum); - sum = std::move(newSum.value()); + sum = std::move(*value); + } + else { + auto subSum = BinaryOperatorDispatcher::dispatch(sum, *value); + CO_TRY_EXPECTED(subSum); + sum = std::move(*subSum); } } + } - return Result::Continue; - })); - TRY_EXPECTED(argRes); - - return res(ctx, sum); + co_yield std::move(sum); } KeysFn KeysFn::Fn; @@ -551,30 +595,30 @@ auto KeysFn::ident() const -> const FnInfo& return info; } -auto KeysFn::eval(Context ctx, const Value& val, const std::vector& args, const ResultFn& res) const -> tl::expected +auto KeysFn::eval(Context ctx, Value val, const std::vector& args) const -> EvalStream { - if (args.size() != 1) - return tl::unexpected(Error::InvalidArguments, - fmt::format("'keys' expects 1 argument got {}", args.size())); - - auto result = args[0]->eval(ctx, val, LambdaResultFn([&res](Context ctx, const Value& vv) -> tl::expected { - if (ctx.phase == Context::Phase::Compilation) - if (vv.isa(ValueType::Undef)) - return res(ctx, vv); - - if (vv.nodePtr()) - for (auto&& fieldName : vv.node()->fieldNames()) { - if (auto key = ctx.env->stringPool->resolve(fieldName)) { - auto r = res(ctx, Value::strref(*key)); - TRY_EXPECTED(r); - if (*r == Result::Stop) - return Result::Stop; - } - } - return Result::Continue; - })); + if (args.size() != 1) { + co_yield tl::unexpected(Error::InvalidArguments, + fmt::format("'keys' expects 1 argument got {}", args.size())); + co_return; + } + + for (auto value : args[0]->eval(ctx, val)) { + CO_TRY_EXPECTED(value); + + if (ctx.compiling() && value->isa(ValueType::Undef)) { + co_yield Value::undef(); + co_return; + } - return result; + if (value->nodePtr()) { + for (const auto& name : value->node()->fieldNames()) { + if (auto key = ctx.env->stringPool->resolve(name)) { + co_yield Value::strref(*key); + } + } + } + } } } diff --git a/src/model/nodes.cpp b/src/model/nodes.cpp index 9274ea62..e46e1de8 100644 --- a/src/model/nodes.cpp +++ b/src/model/nodes.cpp @@ -79,6 +79,15 @@ bool ModelNode::iterate(const IterCallback& cb) const { return result; } +auto ModelNode::iterate() const -> asyncpp::generator +{ + const auto childCount = static_cast(size()); + for (auto i = 0; i < childCount; ++i) { + if (auto child = at(i); child) + co_yield child; + } +} + #if defined(SIMFIL_WITH_MODEL_JSON) nlohmann::json ModelNode::toJson() const { diff --git a/src/parser.cpp b/src/parser.cpp index ca7be97d..3bdf88f7 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -26,9 +26,9 @@ class NOOPExpr : public Expr return Type::FIELD; } - auto ieval(Context ctx, const Value& val, const ResultFn& ores) const -> tl::expected override + auto ieval(Context ctx, Value val) const -> EvalStream override { - return Result::Stop; + co_return; } void accept(ExprVisitor& v) const override diff --git a/src/simfil.cpp b/src/simfil.cpp index 585388a7..c05f2ba1 100644 --- a/src/simfil.cpp +++ b/src/simfil.cpp @@ -125,17 +125,19 @@ static auto simplifyOrForward(Environment* env, expected expr) - std::deque values; auto stub = Context(env, nullptr, Context::Phase::Compilation); - auto res = (*expr)->eval(stub, Value::undef(), LambdaResultFn([&, n = 0](Context ctx, Value&& vv) mutable { - n += 1; - if ((n <= MultiConstExpr::Limit) && (!vv.isa(ValueType::Undef) || vv.nodePtr())) { - values.push_back(std::move(vv)); - return Result::Continue; - } - values.clear(); - return Result::Stop; - })); - TRY_EXPECTED(res); + auto n = 0u; + for (auto value : (*expr)->eval(stub, Value::undef())) { + TRY_EXPECTED(value); + + n++; + if ((n <= MultiConstExpr::Limit) && (!value->isa(ValueType::Undef) || value->nodePtr())) { + values.push_back(std::move(*value)); + } + else { + values.clear(); + } + } /* Warn about constant results */ if (!values.empty() && std::ranges::all_of(values.begin(), values.end(), [](const Value& v) { @@ -892,9 +894,7 @@ auto complete(Environment& env, std::string_view query, size_t point, const Mode if (options.timeoutMs > 0) ctx.timeout = std::chrono::steady_clock::now() + std::chrono::milliseconds(options.timeoutMs); - ast->eval(ctx, Value::field(node), LambdaResultFn([](Context, const Value&) { - return Result::Continue; - })); + for (auto& _ : ast->eval(ctx, Value::field(node))) { ; } // TODO: Is there a function to wait for all values? auto candidates = std::vector(comp.candidates.begin(), comp.candidates.end()); if (options.sorted) diff --git a/src/types.cpp b/src/types.cpp index ca217100..f1773ad6 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -73,7 +73,7 @@ auto IRangeType::binaryOp(std::string_view op, const Value& l, const IRange& r) return tl::unexpected(Error::InvalidOperands, fmt::format("Invalid operands for operator '{}'", op)); } -auto IRangeType::unpack(const IRange& self, std::function res) const -> tl::expected +auto IRangeType::unpack(const IRange& self) const -> EvalStream { auto begin = self.begin; auto end = self.end; @@ -84,10 +84,8 @@ auto IRangeType::unpack(const IRange& self, std::function res) cons auto i = begin - step; do { i += step; - if (!res(Value(ValueType::Int, static_cast(i)))) - return {}; + co_yield Value::make(i); } while (i != end); - return {}; } ReType ReType::Type; diff --git a/test/common.cpp b/test/common.cpp index ad45f200..71b418c7 100644 --- a/test/common.cpp +++ b/test/common.cpp @@ -3,15 +3,18 @@ #include "simfil/environment.h" #include "src/completion.h" -static const PanicFn panicFn{}; +static const PanicFn cPanicFn{true}; +static const PanicFn ePanicFn{false}; auto CompileError(std::string_view query, bool autoWildcard) -> Error { Environment env(Environment::WithNewStringCache); env.constants.try_emplace("a_number", simfil::Value::make((int64_t)123)); - env.functions["panic"] = &panicFn; + env.functions["cpanic"] = &cPanicFn; + env.functions["epanic"] = &ePanicFn; auto ast = compile(env, query, false, autoWildcard); + INFO("AST: " << query); REQUIRE(!ast.has_value()); return std::move(ast.error()); @@ -21,7 +24,8 @@ auto Compile(std::string_view query, bool autoWildcard) -> ASTPtr { Environment env(Environment::WithNewStringCache); env.constants.try_emplace("a_number", simfil::Value::make((int64_t)123)); - env.functions["panic"] = &panicFn; + env.functions["cpanic"] = &cPanicFn; + env.functions["epanic"] = &ePanicFn; auto ast = compile(env, query, false, autoWildcard); if (!ast) @@ -37,7 +41,8 @@ auto JoinedResult(std::string_view query, std::optional json) -> st REQUIRE(model); Environment env(model.value()->strings()); - env.functions["panic"] = &panicFn; + env.functions["cpanic"] = &cPanicFn; + env.functions["epanic"] = &ePanicFn; auto ast = compile(env, query, false); if (!ast) { @@ -87,7 +92,8 @@ auto GetDiagnosticMessages(std::string_view query) -> std::vectorstrings()); - env.functions["panic"] = &panicFn; + env.functions["cpanic"] = &cPanicFn; + env.functions["epanic"] = &ePanicFn; auto ast = compile(env, query, false); if (!ast) diff --git a/test/common.hpp b/test/common.hpp index 3291a10f..4e21cb58 100644 --- a/test/common.hpp +++ b/test/common.hpp @@ -1,9 +1,9 @@ #pragma once #include "simfil/diagnostics.h" +#include "simfil/expression.h" #include "simfil/simfil.h" #include "simfil/environment.h" -#include "simfil/exception-handler.h" #include "simfil/function.h" #include "simfil/model/json.h" #include "simfil/result.h" @@ -13,7 +13,6 @@ #include #include -#include using namespace simfil; @@ -59,23 +58,29 @@ static const char* const TestModel = R"json( class PanicFn : public simfil::Function { public: + const bool comptime = false; + + explicit PanicFn(bool comptime) + : comptime(comptime) + {} + auto ident() const -> const FnInfo& override { static const FnInfo info{ - "panic", + comptime ? "cpanic" : "epanic", "Raise an error", - "panic()" + comptime ? "cpanic()" : "epanic()" }; return info; } - auto eval(Context ctx, const Value&, const std::vector&, const ResultFn& res) const -> tl::expected override + auto eval(Context ctx, Value, const std::vector&) const -> EvalStream override { - if (ctx.phase != Context::Phase::Compilation) - return tl::unexpected(Error::RuntimeError, "Panic!"); - - return res(ctx, Value::undef()); + if (!ctx.compiling() || comptime) + co_yield tl::unexpected(Error::RuntimeError, "Panic!"); + else + co_yield Value::undef(); } }; diff --git a/test/simfil.cpp b/test/simfil.cpp index 7ec6511c..5c3febe3 100644 --- a/test/simfil.cpp +++ b/test/simfil.cpp @@ -77,8 +77,8 @@ TEST_CASE("OperatorConst", "[ast.operator]") { REQUIRE_AST("a+2", "(+ a 2)"); REQUIRE_AST("2+a", "(+ 2 a)"); REQUIRE_AST("a+b", "(+ a b)"); - REQUIRE_PANIC("1+panic()"); - REQUIRE_PANIC("panic()+1"); + REQUIRE_PANIC("1+cpanic()"); + REQUIRE_PANIC("cpanic()+1"); auto GetError = [&](std::string_view query) -> std::string { Environment env(Environment::WithNewStringCache); @@ -300,7 +300,7 @@ TEST_CASE("CompareIncompatibleTypesFields", "[ast.compare-incompatible-types-fie TEST_CASE("OperatorNegate", "[ast.operator-negate]") { REQUIRE_ERROR("-('abc')"); REQUIRE_ERROR("-(true)"); - REQUIRE_PANIC("-panic()"); + REQUIRE_PANIC("-cpanic()"); REQUIRE_AST("-(1)", "-1"); REQUIRE_AST("-(1.1)", "-1.100000"); REQUIRE_AST("-(null)", "null"); @@ -371,8 +371,8 @@ TEST_CASE("ModeSetter", "[ast.mode-setter]") { } TEST_CASE("UtilityFns", "[ast.functions]") { - REQUIRE_PANIC("range(panic(), 5)"); - REQUIRE_PANIC("range(1, panic())"); + REQUIRE_ERROR("range(cpanic(), 5)"); + REQUIRE_ERROR("range(1, cpanic())"); REQUIRE_AST("range(a,b)", "(range a b)"); /* Ca not optimize */ REQUIRE_AST("range(1,5)", "1..5"); REQUIRE_AST("range(1,5)==0", "false"); @@ -396,15 +396,15 @@ TEST_CASE("UtilityFns", "[ast.functions]") { } TEST_CASE("PanicFunction", "[eval.panic-function]") { - REQUIRE_RESULT("panic()", "ERROR: Panic!"); + REQUIRE_RESULT("epanic()", "ERROR: Panic!"); } TEST_CASE("OperatorOrShortCircuit", "[eval.operator-or-short-circuit]") { - REQUIRE_RESULT("true or panic()", "true"); + REQUIRE_RESULT("true or epanic()", "true"); } TEST_CASE("OperatorAndShortCircuit", "[eval.operator-and-short-circuit]") { - REQUIRE_RESULT("false and panic()", "false"); + REQUIRE_RESULT("false and epanic()", "false"); } TEST_CASE("OperatorOr", "[eval.operator-or]") { @@ -489,14 +489,14 @@ TEST_CASE("Model Functions", "[yaml.model-functions]") { SECTION("Test arr(...)") { REQUIRE_RESULT("arr(2,3,5,7,'ok')", "2|3|5|7|ok"); - REQUIRE_PANIC("arr(0,panic(),2)"); + REQUIRE_PANIC("arr(0,cpanic(),2)"); } SECTION("Test split(...)") { REQUIRE_RESULT("split('hello.this.is.a.test.', '.')", "hello|this|is|a|test|"); REQUIRE_RESULT("split('hello.this.is.a.test.', '.', false)", "hello|this|is|a|test"); - REQUIRE_PANIC("split(panic(), '.')"); - REQUIRE_PANIC("split('a.b.c', panic())"); + REQUIRE_PANIC("split(cpanic(), '.')"); + REQUIRE_PANIC("split('a.b.c', cpanic())"); } SECTION("Test select(...)") { REQUIRE_RESULT("select(split('a.b.c.d', '.'), a)", "b"); @@ -504,8 +504,8 @@ TEST_CASE("Model Functions", "[yaml.model-functions]") { REQUIRE_RESULT("select(split('a.b.c.d', '.'), 1, 2)", "b|c"); REQUIRE_RESULT("select(split('a.b.c.d', '.'), 1, 0)", "b|c|d"); - REQUIRE_PANIC("select(panic(), 0)"); - REQUIRE_PANIC("select(0, panic())"); + REQUIRE_PANIC("select(cpanic(), 0)"); + REQUIRE_PANIC("select(0, cpanic())"); } SECTION("Test sum(...)") { REQUIRE_RESULT("sum(range(1, 10)...)", "55"); @@ -513,9 +513,9 @@ TEST_CASE("Model Functions", "[yaml.model-functions]") { REQUIRE_RESULT("sum(range(1, 10)..., $sum + $val, 10)", "65"); REQUIRE_RESULT("sum(range(1, 10)..., $sum * $val, 1)", "3628800"); - REQUIRE_PANIC("sum(panic())"); - REQUIRE_PANIC("sum(range(1, 10)..., panic())"); - REQUIRE_PANIC("sum(range(1, 10)..., 0, panic())"); + REQUIRE_PANIC("sum(cpanic())"); + REQUIRE_PANIC("sum(range(1, 10)..., cpanic())"); + REQUIRE_PANIC("sum(range(1, 10)..., 0, cpanic())"); } SECTION("Count non-false values of arr(...)") { REQUIRE_RESULT("count(arr(null, null))", "0"); @@ -523,14 +523,14 @@ TEST_CASE("Model Functions", "[yaml.model-functions]") { REQUIRE_RESULT("count(arr(null, true))", "1"); REQUIRE_RESULT("count(arr(true, true))", "2"); - REQUIRE_PANIC("count(panic())"); + REQUIRE_PANIC("count(cpanic())"); } SECTION("Count model keys") { REQUIRE_RESULT("count(keys(**))", "50"); - REQUIRE_PANIC("keys(panic())"); - REQUIRE_PANIC("keys(**.{panic()})"); - REQUIRE_PANIC("panic(keys(**))"); + REQUIRE_PANIC("keys(epanic())"); + REQUIRE_PANIC("keys(**.{epanic()})"); + REQUIRE_PANIC("epanic(keys(**))"); } } From d60fc43bc92503aaf1c5f5e0fbbae27f28064987 Mon Sep 17 00:00:00 2001 From: Johannes Wolf Date: Wed, 8 Apr 2026 19:15:43 +0200 Subject: [PATCH 3/3] async: Forward For-Each Values --- include/simfil/expression.h | 5 ++-- include/simfil/function.h | 2 +- include/simfil/model/arena.h | 17 ++++--------- include/simfil/model/nodes.impl.h | 4 +-- include/simfil/typed-meta-type.h | 2 +- src/completion.cpp | 8 +++--- src/expressions.cpp | 42 +++++++++++++++---------------- src/expressions.h | 10 ++++---- src/function.cpp | 20 +++++++-------- src/simfil.cpp | 2 +- 10 files changed, 52 insertions(+), 60 deletions(-) diff --git a/include/simfil/expression.h b/include/simfil/expression.h index 05ecb487..e446e4ef 100644 --- a/include/simfil/expression.h +++ b/include/simfil/expression.h @@ -67,10 +67,9 @@ class Expr auto eval(Context ctx, Value val) const -> EvalStream { if (ctx.canceled()) - co_return; + return {}; - for (auto value : ieval(ctx, val)) - co_yield value; + return ieval(ctx, std::move(val)); } /* diff --git a/include/simfil/function.h b/include/simfil/function.h index b53b2db3..29daea42 100644 --- a/include/simfil/function.h +++ b/include/simfil/function.h @@ -146,7 +146,7 @@ inline auto evalArg1Any(Context ctx, const Value& val, const ExprPtr& expr) -> s auto n = 0u; std::optional out ; - for (auto value : expr->eval(ctx, val)) { + for (auto&& value : expr->eval(ctx, val)) { n++; out.emplace(*value); } diff --git a/include/simfil/model/arena.h b/include/simfil/model/arena.h index ef412f2c..3d1d5c90 100644 --- a/include/simfil/model/arena.h +++ b/include/simfil/model/arena.h @@ -631,18 +631,11 @@ class ArrayArena auto iterate(ArrayIndex idx) -> asyncpp::generator { - if (is_singleton_handle(idx)) { - for (auto value : iterate_singleton(idx)) - co_yield value; - } - else if (heads_.empty() && compactHeads_) { - for (auto value : iterate_compact(idx)) - co_yield value; - } - else { - for (auto value : iterate_chunked(idx)) - co_yield value; - } + if (is_singleton_handle(idx)) + return iterate_singleton(idx); + if (heads_.empty() && compactHeads_) + return iterate_compact(idx); + return iterate_chunked(idx); } private: diff --git a/include/simfil/model/nodes.impl.h b/include/simfil/model/nodes.impl.h index 0048a095..210eb419 100644 --- a/include/simfil/model/nodes.impl.h +++ b/include/simfil/model/nodes.impl.h @@ -56,7 +56,7 @@ bool BaseArray::iterate(const ModelNode::IterCallback& template auto BaseArray::iterate() const -> asyncpp::generator { - for (auto member : storage_->iterate(members_)) + for (auto&& member : storage_->iterate(members_)) co_yield ModelNode::Ptr::make(model_, member); } @@ -165,7 +165,7 @@ bool BaseObject::iterate(const ModelNode::IterCallback template auto BaseObject::iterate() const -> asyncpp::generator { - for (auto member : storage_->iterate(members_)) + for (auto&& member : storage_->iterate(members_)) co_yield ModelNode::Ptr::make(model_, detail::objectFieldNode(member)); } diff --git a/include/simfil/typed-meta-type.h b/include/simfil/typed-meta-type.h index c04f8c8a..a79dd904 100644 --- a/include/simfil/typed-meta-type.h +++ b/include/simfil/typed-meta-type.h @@ -68,7 +68,7 @@ struct TypedMetaType : MetaType auto unpack(const TransientObject& obj, std::function fn) const -> tl::expected override { - for (auto value : unpack(*(const Type*)obj.data)) { + for (auto&& value : unpack(*(const Type*)obj.data)) { TRY_EXPECTED(value); if (!fn(std::move(*value))) return {}; diff --git a/src/completion.cpp b/src/completion.cpp index e8f21181..120c73b6 100644 --- a/src/completion.cpp +++ b/src/completion.cpp @@ -259,12 +259,12 @@ auto CompletionAndExpr::type() const -> Type auto CompletionAndExpr::ieval(Context ctx, Value val) const -> EvalStream { if (left_) - for (auto left : left_->eval(ctx, val)) { + for (auto&& left : left_->eval(ctx, val)) { CO_TRY_EXPECTED(left); } if (right_) - for (auto right : right_->eval(ctx, val)) { + for (auto&& right : right_->eval(ctx, val)) { CO_TRY_EXPECTED(right); } } @@ -313,12 +313,12 @@ auto CompletionOrExpr::type() const -> Type auto CompletionOrExpr::ieval(Context ctx, Value val) const -> EvalStream { if (left_) - for (auto left : left_->eval(ctx, val)) { + for (auto&& left : left_->eval(ctx, val)) { CO_TRY_EXPECTED(left); } if (right_) - for (auto right : right_->eval(ctx, val)) { + for (auto&& right : right_->eval(ctx, val)) { CO_TRY_EXPECTED(right); } } diff --git a/src/expressions.cpp b/src/expressions.cpp index 2e2cd2d1..d73cc534 100644 --- a/src/expressions.cpp +++ b/src/expressions.cpp @@ -55,7 +55,7 @@ auto WildcardExpr::ieval(Context ctx, Value val) const -> EvalStream co_yield Value::field(node); for (const auto& ptr : node->iterate()) - for (auto sub : (*this)(ptr)) + for (auto&& sub : (*this)(ptr)) co_yield sub; } }; @@ -65,7 +65,7 @@ auto WildcardExpr::ieval(Context ctx, Value val) const -> EvalStream for (const auto& field : val.node()->iterate()) { Iterator iter; - for (auto value : iter(field)) + for (auto&& value : iter(field)) co_yield value; } } else { @@ -217,7 +217,7 @@ auto MultiConstExpr::constant() const -> bool auto MultiConstExpr::ieval(Context ctx, Value) const -> EvalStream { - for (auto value : values_) + for (auto&& value : values_) co_yield value; } @@ -286,13 +286,13 @@ auto SubscriptExpr::type() const -> Type auto SubscriptExpr::ieval(Context ctx, Value val) const -> EvalStream { auto empty = true; - for (auto left : left_->eval(ctx, val)) { + for (auto&& left : left_->eval(ctx, val)) { if (!left) { co_yield tl::unexpected(std::move(left.error())); co_return; } - for (auto index : index_->eval(ctx, val)) { + for (auto&& index : index_->eval(ctx, val)) { if (!index) { co_yield tl::unexpected(std::move(index.error())); co_return; @@ -361,7 +361,7 @@ auto SubExpr::type() const -> Type auto SubExpr::ieval(Context ctx, Value val) const -> EvalStream { auto empty = true; - for (auto left : left_->eval(ctx, val)) { + for (auto&& left : left_->eval(ctx, val)) { CO_TRY_EXPECTED(left); if (left->isa(ValueType::Undef)) { @@ -369,7 +369,7 @@ auto SubExpr::ieval(Context ctx, Value val) const -> EvalStream co_return; } - for (auto sub : sub_->eval(ctx, *left)) { + for (auto&& sub : sub_->eval(ctx, *left)) { CO_TRY_EXPECTED(sub); if (sub->isa(ValueType::Undef)) { @@ -416,7 +416,7 @@ auto AnyExpr::ieval(Context ctx, Value val) const -> EvalStream auto any = false; /* At least one value is true */ for (const auto& arg : args_) { - for (auto result : arg->eval(ctx, val)) { + for (auto&& result : arg->eval(ctx, val)) { if (!result) { co_yield tl::unexpected(std::move(result.error())); co_return; @@ -472,7 +472,7 @@ auto EachExpr::ieval(Context ctx, Value val) const -> EvalStream auto undef = false; /* At least one value is undef */ for (const auto& arg : args_) { - for (auto result : arg->eval(ctx, val)) { + for (auto&& result : arg->eval(ctx, val)) { if (!result) { co_yield tl::unexpected(std::move(result.error())); co_return; @@ -539,7 +539,7 @@ auto CallExpression::ieval(Context ctx, Value val) const -> EvalStream } auto anyval = false; - for (auto result : fn_->eval(ctx, val, args_)) { + for (auto&& result : fn_->eval(ctx, val, args_)) { CO_TRY_EXPECTED(result); if (!result) { @@ -591,7 +591,7 @@ auto PathExpr::type() const -> Type auto PathExpr::ieval(Context ctx, Value val) const -> EvalStream { auto empty = true; - for (auto left : left_->eval(ctx, std::move(val))) { + for (auto&& left : left_->eval(ctx, std::move(val))) { if (!left) { co_yield tl::unexpected(std::move(left.error())); co_return; @@ -605,7 +605,7 @@ auto PathExpr::ieval(Context ctx, Value val) const -> EvalStream if (left->isa(ValueType::Null) && !left->node()) continue; - for (auto right : right_->eval(ctx, std::move(*left))) { + for (auto&& right : right_->eval(ctx, std::move(*left))) { if (!right) { co_yield tl::unexpected(std::move(right.error())); co_return; @@ -649,7 +649,7 @@ auto UnpackExpr::type() const -> Type auto UnpackExpr::ieval(Context ctx, Value val) const -> EvalStream { auto empty = true; - for (auto result : sub_->eval(ctx, std::move(val))) { + for (auto&& result : sub_->eval(ctx, std::move(val))) { CO_TRY_EXPECTED(result); if (result->isa(ValueType::TransientObject)) { @@ -664,7 +664,7 @@ auto UnpackExpr::ieval(Context ctx, Value val) const -> EvalStream }); CO_TRY_EXPECTED(unpackResult); - for (auto value : values) { + for (auto&& value : values) { empty = false; co_yield value; } @@ -703,7 +703,7 @@ auto UnaryWordOpExpr::type() const -> Type auto UnaryWordOpExpr::ieval(Context ctx, Value val) const -> EvalStream { auto empty = true; - for (auto left : left_->eval(ctx, val)) { + for (auto&& left : left_->eval(ctx, val)) { CO_TRY_EXPECTED(left); if (left->isa(ValueType::Undef)) { @@ -754,10 +754,10 @@ auto BinaryWordOpExpr::type() const -> Type auto BinaryWordOpExpr::ieval(Context ctx, Value val) const -> EvalStream { auto empty = false; - for (auto left : left_->eval(ctx, val)) { + for (auto&& left : left_->eval(ctx, val)) { CO_TRY_EXPECTED(left); - for (auto right : right_->eval(ctx, val)) { + for (auto&& right : right_->eval(ctx, val)) { CO_TRY_EXPECTED(right); if (left->isa(ValueType::Undef) || right->isa(ValueType::Undef)) { @@ -819,7 +819,7 @@ auto AndExpr::ieval(Context ctx, Value val) const -> EvalStream { /* Operator and behaves like in lua: * 'a and b' returns a if 'not a?' else b is returned */ - for (auto left : left_->eval(ctx, val)) { + for (auto&& left : left_->eval(ctx, val)) { CO_TRY_EXPECTED(left); if (left->isa(ValueType::Undef)) { @@ -836,7 +836,7 @@ auto AndExpr::ieval(Context ctx, Value val) const -> EvalStream } } - for (auto right : right_->eval(ctx, val)) { + for (auto&& right : right_->eval(ctx, val)) { CO_TRY_EXPECTED(right); co_yield *right; } @@ -872,7 +872,7 @@ auto OrExpr::ieval(Context ctx, Value val) const -> EvalStream { /* Operator or behaves like in lua: * 'a or b' returns a if 'a?' else b is returned */ - for (auto left : left_->eval(ctx, val)) { + for (auto&& left : left_->eval(ctx, val)) { CO_TRY_EXPECTED(left); if (left->isa(ValueType::Undef)) { @@ -889,7 +889,7 @@ auto OrExpr::ieval(Context ctx, Value val) const -> EvalStream } } - for (auto right : right_->eval(ctx, val)) { + for (auto&& right : right_->eval(ctx, val)) { CO_TRY_EXPECTED(right); co_yield *right; } diff --git a/src/expressions.h b/src/expressions.h index 97976068..8569154b 100644 --- a/src/expressions.h +++ b/src/expressions.h @@ -217,7 +217,7 @@ class UnaryExpr : public Expr auto ieval(Context ctx, Value val) const -> EvalStream override { - for (auto value : sub_->eval(ctx, val)) { + for (auto&& value : sub_->eval(ctx, val)) { CO_TRY_EXPECTED(value); auto resolved = UnaryOperatorDispatcher::dispatch(*value); @@ -267,10 +267,10 @@ class BinaryExpr : public Expr auto ieval(Context ctx, Value val) const -> EvalStream override { - for (auto left : left_->eval(ctx, val)) { + for (auto&& left : left_->eval(ctx, val)) { CO_TRY_EXPECTED(left); - for (auto right : right_->eval(ctx, val)) { + for (auto&& right : right_->eval(ctx, val)) { CO_TRY_EXPECTED(right); auto resolved = BinaryOperatorDispatcher::dispatch(*left, *right); @@ -336,13 +336,13 @@ class ComparisonExpr : public ComparisonExprBase diag->evaluations++; } - for (auto left : left_->eval(ctx, val)) { + for (auto&& left : left_->eval(ctx, val)) { CO_TRY_EXPECTED(left); if (diag) diag->leftTypes.set(left->type); - for (auto right : right_->eval(ctx, val)) { + for (auto&& right : right_->eval(ctx, val)) { CO_TRY_EXPECTED(right); if (diag) diff --git a/src/function.cpp b/src/function.cpp index 84edaa4a..1bb18484 100644 --- a/src/function.cpp +++ b/src/function.cpp @@ -51,7 +51,7 @@ struct ArgParser auto subctx = ctx; auto n = 0u; - for (auto value : args[idx]->eval(subctx, value)) { + for (auto&& value : args[idx]->eval(subctx, value)) { if (!value) { error.emplace(std::move(value.error())); outValue = std::move(*value); @@ -90,7 +90,7 @@ struct ArgParser auto n = 0u; auto subctx = ctx; - for (auto value : args[idx]->eval(subctx, value)) { + for (auto&& value : args[idx]->eval(subctx, value)) { if (++n > 1) { error.emplace(Error::ExpectedSingleValue, fmt::format("{}: argument {} must return a single value", functionName, name)); @@ -211,7 +211,7 @@ auto TraceFn::eval(Context ctx, Value val, const std::vector& args) con auto n = 0; auto start = std::chrono::steady_clock::now(); - for (auto value : args[0]->eval(ctx, val)) { + for (auto&& value : args[0]->eval(ctx, val)) { CO_TRY_EXPECTED(value); n++; @@ -303,7 +303,7 @@ auto ReFn::eval(Context ctx, Value val, const std::vector& args) const co_return; } - for (auto value : args[0]->eval(ctx, val)) { + for (auto&& value : args[0]->eval(ctx, val)) { CO_TRY_EXPECTED(value); if (value->isa(ValueType::Undef)) { @@ -356,7 +356,7 @@ auto ArrFn::eval(Context ctx, Value val, const std::vector& args) const auto empty = true; for (const auto& arg : args) { - for (auto value : arg->eval(ctx, val)) { + for (auto&& value : arg->eval(ctx, val)) { CO_TRY_EXPECTED(value); empty = false; co_yield std::move(*value); @@ -490,7 +490,7 @@ auto SelectFn::eval(Context ctx, Value val, const std::vector& args) co auto empty = true; auto n = -1; - for (auto value : args[0]->eval(ctx, val)) { + for (auto&& value : args[0]->eval(ctx, val)) { CO_TRY_EXPECTED(value); n++; @@ -540,7 +540,7 @@ auto SumFn::eval(Context ctx, Value val, const std::vector& args) const Expr* subexpr = args.size() >= 2 ? args[1].get() : nullptr; Expr* initval = args.size() == 3 ? args[2].get() : nullptr; if (initval) { - for (auto value : initval->eval(ctx, val)) { + for (auto&& value : initval->eval(ctx, val)) { CO_TRY_EXPECTED(value); sum = std::move(*value); } @@ -552,7 +552,7 @@ auto SumFn::eval(Context ctx, Value val, const std::vector& args) const } auto n = 0; - for (auto value : args[0]->eval(ctx, val)) { + for (auto&& value : args[0]->eval(ctx, val)) { if (subexpr) { auto ov = model_ptr::make(*value); ov->set(StringPool::OverlaySum, sum); @@ -560,7 +560,7 @@ auto SumFn::eval(Context ctx, Value val, const std::vector& args) const ov->set(StringPool::OverlayIndex, Value::make(static_cast(n))); n++; - for (auto subValue : subexpr->eval(ctx, Value::field(ov))) { + for (auto&& subValue : subexpr->eval(ctx, Value::field(ov))) { CO_TRY_EXPECTED(subValue); ov->set(StringPool::OverlaySum, *subValue); @@ -603,7 +603,7 @@ auto KeysFn::eval(Context ctx, Value val, const std::vector& args) cons co_return; } - for (auto value : args[0]->eval(ctx, val)) { + for (auto&& value : args[0]->eval(ctx, val)) { CO_TRY_EXPECTED(value); if (ctx.compiling() && value->isa(ValueType::Undef)) { diff --git a/src/simfil.cpp b/src/simfil.cpp index c05f2ba1..3225edc1 100644 --- a/src/simfil.cpp +++ b/src/simfil.cpp @@ -127,7 +127,7 @@ static auto simplifyOrForward(Environment* env, expected expr) - auto stub = Context(env, nullptr, Context::Phase::Compilation); auto n = 0u; - for (auto value : (*expr)->eval(stub, Value::undef())) { + for (auto&& value : (*expr)->eval(stub, Value::undef())) { TRY_EXPECTED(value); n++;