[Filter] Group set query suffix modes into a subrule

Address review feedback: keep the 'e'/'set' prefix in one place and move
the three suffix modes (exact, negated, release-date comparison) into a
dedicated choice-like rule so sv.choice() still dispatches on them.

Add coverage for the release-date comparison mode.
This commit is contained in:
Lukas Brübach 2026-09-21 13:54:18 +02:00
parent 6dec989e96
commit c8014f0eb3
2 changed files with 24 additions and 2 deletions

View file

@ -20,7 +20,8 @@ SomewhatComplexQueryPart <- [(] QueryPartList [)] / QueryPart
QueryPart <- NotQuery / SetQuery / RarityQuery / CMCQuery / FormatQuery / PowerQuery / ToughnessQuery / ColorQuery / TypeQuery / OracleQuery / FieldQuery / GenericQuery QueryPart <- NotQuery / SetQuery / RarityQuery / CMCQuery / FormatQuery / PowerQuery / ToughnessQuery / ColorQuery / TypeQuery / OracleQuery / FieldQuery / GenericQuery
NotQuery <- ('NOT' ws/'-') SomewhatComplexQueryPart NotQuery <- ('NOT' ws/'-') SomewhatComplexQueryPart
SetQuery <- ('e'/'set') ([=:] FlexStringValue) / ('e'/'set') (<[!][=]?> FlexStringValue) / ('e'/'set') SetExpression SetQuery <- ('e'/'set') SetQueryValue
SetQueryValue <- ([=:] FlexStringValue) / (<[!][=]?> FlexStringValue) / SetExpression
OracleQuery <- 'o' [:] MatcherString OracleQuery <- 'o' [:] MatcherString
@ -103,7 +104,7 @@ static void setupParserRules()
const auto matcher = std::any_cast<StringMatcher>(sv[0]); const auto matcher = std::any_cast<StringMatcher>(sv[0]);
return [=](const CardData &x) -> bool { return matcher(x->getCardType()); }; return [=](const CardData &x) -> bool { return matcher(x->getCardType()); };
}; };
search["SetQuery"] = [](const peg::SemanticValues &sv) -> Filter { search["SetQueryValue"] = [](const peg::SemanticValues &sv) -> Filter {
if (sv.choice() == 0) { if (sv.choice() == 0) {
auto matcher = std::any_cast<StringMatcher>(sv[0]); auto matcher = std::any_cast<StringMatcher>(sv[0]);
return [=](const CardData &x) -> bool { return [=](const CardData &x) -> bool {

View file

@ -3,6 +3,7 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include <libcockatrice/card/card_info.h> #include <libcockatrice/card/card_info.h>
#include <libcockatrice/card/database/card_database_manager.h>
#include <libcockatrice/card/printing/printing_info.h> #include <libcockatrice/card/printing/printing_info.h>
#include <libcockatrice/filters/filter_string.h> #include <libcockatrice/filters/filter_string.h>
#include <libcockatrice/interfaces/noop_card_preference_provider.h> #include <libcockatrice/interfaces/noop_card_preference_provider.h>
@ -95,9 +96,18 @@ protected:
CardSet::PriorityPrimary); CardSet::PriorityPrimary);
commanderSet = CardSet::newInstance(&controller, "EOC", "Edge of Eternities Commander", "commander", commanderSet = CardSet::newInstance(&controller, "EOC", "Edge of Eternities Commander", "commander",
sharedReleaseDate, CardSet::PrioritySecondary); sharedReleaseDate, CardSet::PrioritySecondary);
oldSet = CardSet::newInstance(&controller, "OLD", "Older Set", "expansion", QDate(2020, 1, 1),
CardSet::PriorityPrimary);
inBothSets = newCardWithPrintings({{"EOE", mainSet}, {"EOC", commanderSet}}); inBothSets = newCardWithPrintings({{"EOE", mainSet}, {"EOC", commanderSet}});
onlyInCommanderSet = newCardWithPrintings({{"EOC", commanderSet}}); onlyInCommanderSet = newCardWithPrintings({{"EOC", commanderSet}});
onlyInOldSet = newCardWithPrintings({{"OLD", oldSet}});
// SetExpression resolves set codes to release dates through the global set list.
auto *database = CardDatabaseManager::getInstance();
database->addSet(mainSet);
database->addSet(commanderSet);
database->addSet(oldSet);
} }
CardInfoPtr newCardWithPrintings(const QList<QPair<QString, CardSetPtr>> &printings) CardInfoPtr newCardWithPrintings(const QList<QPair<QString, CardSetPtr>> &printings)
@ -112,8 +122,10 @@ protected:
NoopCardSetPriorityController controller; NoopCardSetPriorityController controller;
CardSetPtr mainSet; CardSetPtr mainSet;
CardSetPtr commanderSet; CardSetPtr commanderSet;
CardSetPtr oldSet;
CardInfoPtr inBothSets; CardInfoPtr inBothSets;
CardInfoPtr onlyInCommanderSet; CardInfoPtr onlyInCommanderSet;
CardInfoPtr onlyInOldSet;
}; };
TEST_F(SetQuery, ExactMatchSeparatesSameDaySets) TEST_F(SetQuery, ExactMatchSeparatesSameDaySets)
@ -138,6 +150,15 @@ TEST_F(SetQuery, NotEqualsMatchesPrintingsOutsideTheSet)
EXPECT_TRUE(FilterString("set!EOE").check(onlyInCommanderSet)); EXPECT_TRUE(FilterString("set!EOE").check(onlyInCommanderSet));
} }
TEST_F(SetQuery, ComparisonMatchesByReleaseDate)
{
// OLD (2020) predates EOE/EOC (2026-03-13), so comparison operators still use release dates.
EXPECT_TRUE(FilterString("set<EOE").check(onlyInOldSet));
EXPECT_FALSE(FilterString("set>EOE").check(onlyInOldSet));
EXPECT_TRUE(FilterString("set>=EOE").check(inBothSets));
EXPECT_FALSE(FilterString("set<EOE").check(inBothSets));
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
::testing::InitGoogleTest(&argc, argv); ::testing::InitGoogleTest(&argc, argv);