From 093e2d23fd01b13876724baeb332d05fccc69a0f Mon Sep 17 00:00:00 2001 From: IRBorisov <8611739+IRBorisov@users.noreply.github.com> Date: Fri, 14 Jun 2024 20:42:20 +0300 Subject: [PATCH] Fix typechecking issues and update pyconcept --- .../include/ccl/rslang/ASTInterpreter.h | 11 +++ ccl/rslang/include/ccl/rslang/TypeAuditor.h | 2 + ccl/rslang/src/ASTInterpreter.cpp | 48 +++++++++++- ccl/rslang/src/TypeAuditor.cpp | 70 +++++++++++++----- ccl/rslang/test/src/testASTInterpreter.cpp | 1 + ccl/rslang/test/src/testTypeAuditor.cpp | 9 ++- pyconcept/CHANGELOG.md | 11 +++ pyconcept/VERSION | 2 +- pyconcept/requirements-build.txt | Bin 92 -> 89 bytes pyconcept/requirements-dev.txt | Bin 140 -> 61 bytes 10 files changed, 129 insertions(+), 25 deletions(-) diff --git a/ccl/rslang/include/ccl/rslang/ASTInterpreter.h b/ccl/rslang/include/ccl/rslang/ASTInterpreter.h index ed0ea4f..81fe54f 100644 --- a/ccl/rslang/include/ccl/rslang/ASTInterpreter.h +++ b/ccl/rslang/include/ccl/rslang/ASTInterpreter.h @@ -112,6 +112,17 @@ private: [[nodiscard]] std::optional ExtractDomain(Cursor iter); [[nodiscard]] bool TryEvaluateFromFirstArg(TokenID operation, bool firstArgValue) noexcept; + + [[nodiscard]] bool EvaluateFilterComplex( + Cursor iter, + const std::vector& indicies, + const object::StructuredData& argument + ); + [[nodiscard]] bool EvaluateFilterTuple( + Cursor iter, + const std::vector& indicies, + const object::StructuredData& argument + ); }; } // namespace ccl::rslang \ No newline at end of file diff --git a/ccl/rslang/include/ccl/rslang/TypeAuditor.h b/ccl/rslang/include/ccl/rslang/TypeAuditor.h index 5db661f..c9428c3 100644 --- a/ccl/rslang/include/ccl/rslang/TypeAuditor.h +++ b/ccl/rslang/include/ccl/rslang/TypeAuditor.h @@ -38,6 +38,8 @@ class TypeAuditor final : public ASTVisitor { friend class SyntaxTree::Cursor; friend class ASTVisitor; + static constexpr auto typeDeductionDepth = 5; + struct LocalData { TypedID arg; int32_t level{}; diff --git a/ccl/rslang/src/ASTInterpreter.cpp b/ccl/rslang/src/ASTInterpreter.cpp index d85264d..fc3072e 100644 --- a/ccl/rslang/src/ASTInterpreter.cpp +++ b/ccl/rslang/src/ASTInterpreter.cpp @@ -492,7 +492,7 @@ bool ASTInterpreter::ViBoolean(Cursor iter) { if (!childValue.has_value()) { return false; } - const auto value = std::get(childValue.value()); + const auto& value = std::get(childValue.value()); if ( (iter.IsRoot() || (iter.Parent().id != TokenID::IN && iter.Parent().id != TokenID::NT_DECLARATIVE_EXPR)) && value.B().Cardinality() >= StructuredData::BOOL_INFINITY @@ -600,14 +600,56 @@ bool ASTInterpreter::ViFilter(Cursor iter) { } const auto& indicies = iter->data.ToTuple(); + const auto tupleParam = ssize(indicies) == iter.ChildrenCount() - 1; + if (tupleParam) { + return EvaluateFilterTuple(iter, indicies, argument); + } else { + return EvaluateFilterComplex(iter, indicies, argument); + } +} + +bool ASTInterpreter::EvaluateFilterComplex( + Cursor iter, + const std::vector& indicies, + const object::StructuredData& argument +) { + const auto param = EvaluateChild(iter, 0); + if (!param.has_value()) { + return false; + } + const auto& paramValue = std::get(param.value()); + if (paramValue.B().IsEmpty()) { + return SetCurrent(Factory::EmptySet()); + } + + auto result = Factory::EmptySet(); + for (const auto& element : argument.B()) { + std::vector components{}; + for (auto i = 0U; i < size(indicies); ++i) { + components.emplace_back(element.T().Component(indicies[i])); + } + const auto tuple = Factory::Tuple(components); + if (paramValue.B().Contains(tuple)) { + result.ModifyB().AddElement(element); + } + } + return SetCurrent(std::move(result)); +} + +bool ASTInterpreter::EvaluateFilterTuple( + Cursor iter, + const std::vector& indicies, + const object::StructuredData& argument +) { std::vector params{}; params.reserve(size(indicies)); for (Index child = 0; child < iter.ChildrenCount() - 1; ++child) { const auto param = EvaluateChild(iter, child); if (!param.has_value()) { return false; - } - if (const auto val = std::get(param.value()); val.B().IsEmpty()) { + } + const auto& val = std::get(param.value()); + if (val.B().IsEmpty()) { return SetCurrent(Factory::EmptySet()); } else { params.emplace_back(val); diff --git a/ccl/rslang/src/TypeAuditor.cpp b/ccl/rslang/src/TypeAuditor.cpp index 95d16ee..50e0184 100644 --- a/ccl/rslang/src/TypeAuditor.cpp +++ b/ccl/rslang/src/TypeAuditor.cpp @@ -724,15 +724,15 @@ bool TypeAuditor::ViRecursion(Cursor iter) { const bool isFull = iter->id == TokenID::NT_RECURSIVE_FULL; const auto iterationIndex = static_cast(isFull ? 3 : 2); - const auto iterationType = ChildType(iter, iterationIndex); - if (!iterationType.has_value()) { + auto iterationValue = ChildType(iter, iterationIndex); + if (!iterationValue.has_value()) { return false; } - if (!env.AreCompatible(iterationType.value(), initType.value())) { + if (!env.AreCompatible(iterationValue.value(), initType.value())) { OnError( SemanticEID::typesNotEqual, iter(iterationIndex).pos.start, - iterationType.value(), + iterationValue.value(), initType.value() ); return false; @@ -740,12 +740,19 @@ bool TypeAuditor::ViRecursion(Cursor iter) { { const auto guard = noWarnings.CreateGuard(); - ClearLocalVariables(); - if (!VisitChildDeclaration(iter, 0, std::get(iterationType.value()))) { - return false; - } - if (!VisitChild(iter, iterationIndex)) { - return false; + for (auto retries = typeDeductionDepth; retries > 0; --retries) { + ClearLocalVariables(); + if (!VisitChildDeclaration(iter, 0, std::get(iterationValue.value()))) { + return false; + } + auto newIteration = ChildType(iter, iterationIndex); + if (!newIteration.has_value()) { + return false; + } + if (std::get(newIteration.value()) == std::get(iterationValue.value())) { + break; + } + iterationValue = newIteration; } } @@ -756,7 +763,7 @@ bool TypeAuditor::ViRecursion(Cursor iter) { } EndScope(iter->pos.start); - return SetCurrent(iterationType.value()); + return SetCurrent(iterationValue.value()); } bool TypeAuditor::ViDecart(Cursor iter) { @@ -927,7 +934,8 @@ bool TypeAuditor::ViProjectTuple(Cursor iter) { bool TypeAuditor::ViFilter(Cursor iter) { const auto& indicies = iter->data.ToTuple(); - if (ssize(indicies) + 1 != iter.ChildrenCount()) { + const auto tupleParam = ssize(indicies) == iter.ChildrenCount() - 1; + if (!tupleParam && iter.ChildrenCount() > 2) { OnError(SemanticEID::invalidFilterArity, iter->pos.start); return false; } @@ -949,7 +957,7 @@ bool TypeAuditor::ViFilter(Cursor iter) { return false; } - Index child{ 0 }; + std::vector bases{}; for (const auto index : indicies) { if (!argument.B().Base().T().TestIndex(index)) { OnError( @@ -959,21 +967,43 @@ bool TypeAuditor::ViFilter(Cursor iter) { ); return false; } - const auto param = ChildType(iter, child); - if(!param.has_value()) { + bases.push_back(argument.B().Base().T().Component(index)); + } + + if (tupleParam) { + for (Index child = 0; child + 1 < iter.ChildrenCount(); ++child) { + const auto param = ChildType(iter, child); + if (!param.has_value()) { + return false; + } + const auto& paramType = std::get(param.value()); + if (!paramType.IsCollection() || + !env.AreCompatible(bases.at(child), paramType.B().Base())) { + OnError( + SemanticEID::typesNotEqual, + iter(child).pos.start, + bases.at(child).Bool(), paramType + ); + return false; + } + ++child; + } + } else { + const auto param = ChildType(iter, 0); + if (!param.has_value()) { return false; } const auto& paramType = std::get(param.value()); - if (!paramType.IsCollection() || - !env.AreCompatible(argument.B().Base().T().Component(index), paramType.B().Base())) { + const auto expected = Typification::Tuple(std::move(bases)).ApplyBool(); + if (!paramType.IsCollection() || + !env.AreCompatible(expected, paramType)) { OnError( SemanticEID::typesNotEqual, - iter(child).pos.start, - argument.B().Base().T().Component(index).Bool(), paramType + iter(0).pos.start, + expected, paramType ); return false; } - ++child; } return SetCurrent(maybeArgument.value()); } diff --git a/ccl/rslang/test/src/testASTInterpreter.cpp b/ccl/rslang/test/src/testASTInterpreter.cpp index 686f393..10773b6 100644 --- a/ccl/rslang/test/src/testASTInterpreter.cpp +++ b/ccl/rslang/test/src/testASTInterpreter.cpp @@ -285,6 +285,7 @@ TEST_F(UTASTInterpreter, TypedExpressions) { ExpectValue(R"(Pr1,2(S3 \setminus S3))", Factory::EmptySet()); ExpectValue(R"(Fi1[X1](S3))", data.at("S3")); ExpectValue(R"(Fi1,2[X1,X2](S3))", data.at("S3")); + ExpectValue(R"(Fi1,2[X1*X2](S3))", data.at("S3")); ExpectValue(R"(Fi2,1[X2,X1](S3))", data.at("S3")); ExpectValue(R"(Fi1[X1 \setminus X1](S3))", Factory::EmptySet()); ExpectValue(R"(Fi1[X1](S3 \setminus S3))", Factory::EmptySet()); diff --git a/ccl/rslang/test/src/testTypeAuditor.cpp b/ccl/rslang/test/src/testTypeAuditor.cpp index 81b709d..a5c7457 100644 --- a/ccl/rslang/test/src/testTypeAuditor.cpp +++ b/ccl/rslang/test/src/testTypeAuditor.cpp @@ -251,6 +251,10 @@ TEST_F(UTTypeAuditor, ConstructorsCorrect) { ExpectTypification(R"(R{a \assign {} | Pr1(a) \eq Pr2(a) | a \union (X1*X1)})", "B(X1*X1)"_t); ExpectTypification(R"(R{a \assign {} | a \union D{x \in X1 | x \eq x}})", "B(X1)"_t); ExpectTypification(R"(R{a \assign {} | red(a) \eq {} | a \union {X1}})", "BB(X1)"_t); + ExpectTypification( + R"(R{(a, b) \assign (I{(x, {}) | x \from X1}, {}) | (X1 * B(X1*X1), b \union Fi1[X1](a))})", + "B(X1*B(X1*X1))*B(X1*B(X1*X1))"_t + ); ExpectTypification(R"(I{(a, b) | a \from X1; b \assign a})", "B(X1*X1)"_t); ExpectTypification(R"(I{(a, b) | a \from X1; b \assign a; 1 \eq 1})", "B(X1*X1)"_t); @@ -340,6 +344,8 @@ TEST_F(UTTypeAuditor, TypedOperationsCorrect) { ExpectTypification(R"(Pr1(S1))", "B(X1)"_t); ExpectTypification(R"(Pr1({}))", "B(R0)"_t); ExpectTypification(R"(Fi1[X1](S1))", "B(X1*X1)"_t); + ExpectTypification(R"(Fi1,2[X1, X1](S1))", "B(X1*X1)"_t); + ExpectTypification(R"(Fi1,2[X1 * X1](S1))", "B(X1*X1)"_t); ExpectTypification(R"(Fi1[{1,2,3}](Z*X1))", "B(Z*X1)"_t); ExpectTypification(R"(Fi1[{1,2,3}](C1*X1))", "B(C1*X1)"_t); ExpectTypification(R"(Pr1,2(S1))", "B(X1*X1)"_t); @@ -364,7 +370,8 @@ TEST_F(UTTypeAuditor, TypedOperationsErrors) { ExpectError(R"(Fi1[Z](B(X1)))", SemanticEID::invalidFilterArgumentType, 7); ExpectError(R"(Fi3[X1](X1*X1))", SemanticEID::invalidFilterArgumentType, 8); ExpectError(R"(Fi1[X1](B(X1)*X1))", SemanticEID::typesNotEqual, 4); - ExpectError(R"(Fi1,2[X1](X1*X1))", SemanticEID::invalidFilterArity, 0); + ExpectError(R"(Fi1,2[X1*{X1}](B(X1)*X1))", SemanticEID::typesNotEqual, 6); + ExpectError(R"(Fi1,2,1[X1, X1](X1*X1))", SemanticEID::invalidFilterArity, 0); ExpectError(R"(\A a \in X1 Fi1[a](B(X1)*X1) \eq X1)", SemanticEID::typesNotEqual, 16); ExpectError(R"(\A a \in X1*X1 Fi1[a](B(X1)*X1) \eq X1)", SemanticEID::typesNotEqual, 19); ExpectError(R"(red(X1))", SemanticEID::invalidReduce, 5); diff --git a/pyconcept/CHANGELOG.md b/pyconcept/CHANGELOG.md index 588bb3b..de4a33d 100644 --- a/pyconcept/CHANGELOG.md +++ b/pyconcept/CHANGELOG.md @@ -19,6 +19,17 @@ Here we write upgrading notes. Make them as straightforward as possible. ### Fixed +## [0.1.6] - 2024-06-14 + +### Fixed + +- Fix emptyset type deduction in recursive structures + +### Added + +- Allow single argument for multi-Filter +- Add fixed dependencies versions to build requirements + ## [0.1.5] - 2024-05-10 ### Added diff --git a/pyconcept/VERSION b/pyconcept/VERSION index def9a01..a192233 100644 --- a/pyconcept/VERSION +++ b/pyconcept/VERSION @@ -1 +1 @@ -0.1.5 \ No newline at end of file +0.1.6 \ No newline at end of file diff --git a/pyconcept/requirements-build.txt b/pyconcept/requirements-build.txt index 55fc3550760d647535ff208f369d4bb9f2fb2415..068f732cb3ac497bc05a6f12c670349467d3ee6e 100644 GIT binary patch literal 89 zcmWlPQ3`+{5C#7|%%W=re!Rn&30bxVDP6w}=4Y6BavyhZtvgkGS20U>fHmOMzfOTc_0i;40p#T5? literal 92 zcmW-ZTMB?M48Za&cu1WCpH-OR?bS>QC8443Q^=fL9PZs&DYP`)j9#){t+$xu;kS^3 RneMm5CM~wLO#TQx_yCIM5f}gf diff --git a/pyconcept/requirements-dev.txt b/pyconcept/requirements-dev.txt index 05d2db382f9f7558433aed09263ad8ba2fa6f9d3..3d23fd1f6e9dd5d022f9f30f890c6e7b4c7dd31e 100644 GIT binary patch literal 61 zcmWN`K@Na02n4|UzR2%j#Ym+s)7Yu1I&C_#S4(xkN9RSSPeWFQd7U0Z}TJ L7!`$E|2VJ#y;v2t literal 140 zcmXAhSqgwK5JU4VcptB$wc^&X(296@H5n