From 91d24859404364e1a23969edddb5e342bf5fcbab Mon Sep 17 00:00:00 2001 From: Zach H Date: Fri, 20 Dec 2024 22:37:08 -0500 Subject: [PATCH] Update PegLib, Fix Database Searching CFG (#5244) * Support C++20 Standard * Update peglib.h * Fix lambdas * Move from for loops to std::any/all_of * Support fixed CFG * Fix Rarity Search to be more accurate --- CMakeLists.txt | 10 +- .../src/client/tabs/tab_deck_editor.cpp | 2 +- cockatrice/src/client/tabs/tab_game.cpp | 8 +- cockatrice/src/client/tearoff_menu.h | 10 +- .../printing_selector/printing_selector.cpp | 2 +- cockatrice/src/game/filters/filter_string.cpp | 315 +- .../src/game/zones/view_zone_widget.cpp | 2 +- cockatrice/src/server/user/user_info_box.cpp | 2 +- common/expression.cpp | 4 +- common/lib/peglib.h | 5688 +++++++++++------ 10 files changed, 3851 insertions(+), 2192 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b6af504c..93c001ddf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,9 +81,9 @@ if(NOT DEFINED GIT_TAG_RELEASENAME) set(GIT_TAG_RELEASENAME "Rings of the Wild") endif() -# Use c++17 for all targets +# Use c++20 for all targets set(CMAKE_CXX_STANDARD - 17 + 20 CACHE STRING "C++ ISO Standard" ) set(CMAKE_CXX_STANDARD_REQUIRED True) @@ -140,8 +140,8 @@ endif() # Define proper compilation flags if(MSVC) - # Visual Studio: Disable Warning C4251, C++17 compatibility, Multi-threaded Builds, Warn Detection, Unwind Semantics - set(CMAKE_CXX_FLAGS "/wd4251 /Zc:__cplusplus /std:c++17 /permissive- /W4 /MP /EHsc") + # Visual Studio: Disable Warning C4251, C++20 compatibility, Multi-threaded Builds, Warn Detection, Unwind Semantics + set(CMAKE_CXX_FLAGS "/wd4251 /Zc:__cplusplus /std:c++20 /permissive- /W4 /MP /EHsc") # Visual Studio: Maximum Optimization, Multi-threaded DLL set(CMAKE_CXX_FLAGS_RELEASE "/Ox /MD") # Visual Studio: No Optimization, Multi-threaded Debug DLL, Debug Symbols @@ -158,7 +158,7 @@ elseif(CMAKE_COMPILER_IS_GNUCXX) endif() if(APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++20") endif() set(ADDITIONAL_DEBUG_FLAGS diff --git a/cockatrice/src/client/tabs/tab_deck_editor.cpp b/cockatrice/src/client/tabs/tab_deck_editor.cpp index 999f22580..38599f5ee 100644 --- a/cockatrice/src/client/tabs/tab_deck_editor.cpp +++ b/cockatrice/src/client/tabs/tab_deck_editor.cpp @@ -1535,6 +1535,6 @@ void TabDeckEditor::showSearchSyntaxHelp() browser->document()->setDefaultStyleSheet(sheet); browser->setHtml(text); - connect(browser, &QTextBrowser::anchorClicked, [=](const QUrl &link) { searchEdit->setText(link.fragment()); }); + connect(browser, &QTextBrowser::anchorClicked, [this](const QUrl &link) { searchEdit->setText(link.fragment()); }); browser->show(); } diff --git a/cockatrice/src/client/tabs/tab_game.cpp b/cockatrice/src/client/tabs/tab_game.cpp index 76dd82ff5..d858a9362 100644 --- a/cockatrice/src/client/tabs/tab_game.cpp +++ b/cockatrice/src/client/tabs/tab_game.cpp @@ -1742,22 +1742,22 @@ void TabGame::createReplayDock() aReplaySkipForward = new QAction(timelineWidget); timelineWidget->addAction(aReplaySkipForward); connect(aReplaySkipForward, &QAction::triggered, this, - [=]() { timelineWidget->skipByAmount(ReplayTimelineWidget::SMALL_SKIP_MS); }); + [this] { timelineWidget->skipByAmount(ReplayTimelineWidget::SMALL_SKIP_MS); }); aReplaySkipBackward = new QAction(timelineWidget); timelineWidget->addAction(aReplaySkipBackward); connect(aReplaySkipBackward, &QAction::triggered, this, - [=]() { timelineWidget->skipByAmount(-ReplayTimelineWidget::SMALL_SKIP_MS); }); + [this] { timelineWidget->skipByAmount(-ReplayTimelineWidget::SMALL_SKIP_MS); }); aReplaySkipForwardBig = new QAction(timelineWidget); timelineWidget->addAction(aReplaySkipForwardBig); connect(aReplaySkipForwardBig, &QAction::triggered, this, - [=]() { timelineWidget->skipByAmount(ReplayTimelineWidget::BIG_SKIP_MS); }); + [this] { timelineWidget->skipByAmount(ReplayTimelineWidget::BIG_SKIP_MS); }); aReplaySkipBackwardBig = new QAction(timelineWidget); timelineWidget->addAction(aReplaySkipBackwardBig); connect(aReplaySkipBackwardBig, &QAction::triggered, this, - [=]() { timelineWidget->skipByAmount(-ReplayTimelineWidget::BIG_SKIP_MS); }); + [this] { timelineWidget->skipByAmount(-ReplayTimelineWidget::BIG_SKIP_MS); }); // buttons replayPlayButton = new QToolButton; diff --git a/cockatrice/src/client/tearoff_menu.h b/cockatrice/src/client/tearoff_menu.h index 20ee30e10..1c1258f5f 100644 --- a/cockatrice/src/client/tearoff_menu.h +++ b/cockatrice/src/client/tearoff_menu.h @@ -7,23 +7,23 @@ class TearOffMenu : public QMenu { public: - TearOffMenu(const QString &title, QWidget *parent = nullptr) : QMenu(title, parent) + explicit TearOffMenu(const QString &title, QWidget *parent = nullptr) : QMenu(title, parent) { connect(&SettingsCache::instance(), &SettingsCache::useTearOffMenusChanged, this, - [=](bool state) { setTearOffEnabled(state); }); + [this](const bool state) { setTearOffEnabled(state); }); setTearOffEnabled(SettingsCache::instance().getUseTearOffMenus()); } - TearOffMenu(QWidget *parent = nullptr) : QMenu(parent) + explicit TearOffMenu(QWidget *parent = nullptr) : QMenu(parent) { connect(&SettingsCache::instance(), &SettingsCache::useTearOffMenusChanged, this, - [=](bool state) { setTearOffEnabled(state); }); + [this](const bool state) { setTearOffEnabled(state); }); setTearOffEnabled(SettingsCache::instance().getUseTearOffMenus()); } TearOffMenu *addTearOffMenu(const QString &title) { - TearOffMenu *menu = new TearOffMenu(title, this); + auto *menu = new TearOffMenu(title, this); addMenu(menu); return menu; } diff --git a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.cpp b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.cpp index 5d3adccd2..64b11ccfd 100644 --- a/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.cpp +++ b/cockatrice/src/client/ui/widgets/printing_selector/printing_selector.cpp @@ -175,7 +175,7 @@ void PrintingSelector::getAllSetsForCurrentCard() // Defer widget creation currentIndex = 0; - connect(widgetLoadingBufferTimer, &QTimer::timeout, this, [=]() mutable { + connect(widgetLoadingBufferTimer, &QTimer::timeout, this, [=, this]() mutable { for (int i = 0; i < BATCH_SIZE && currentIndex < setsToUse.size(); ++i, ++currentIndex) { auto *cardDisplayWidget = new PrintingSelectorCardDisplayWidget(this, deckEditor, deckModel, deckView, cardSizeWidget->getSlider(), selectedCard, diff --git a/cockatrice/src/game/filters/filter_string.cpp b/cockatrice/src/game/filters/filter_string.cpp index 8427967f6..32a448977 100644 --- a/cockatrice/src/game/filters/filter_string.cpp +++ b/cockatrice/src/game/filters/filter_string.cpp @@ -26,7 +26,9 @@ OracleQuery <- 'o' [:] RegexString CMCQuery <- ('cmc'/'mv') ws? NumericExpression PowerQuery <- [Pp] 'ow' 'er'? ws? NumericExpression ToughnessQuery <- [Tt] 'ou' 'ghness'? ws? NumericExpression -RarityQuery <- [rR] ':' RegexString + +RarityQuery <- [rR] ':' Rarity +Rarity <- [Cc] 'ommon'? / [Uu] 'ncommon'? / [Rr] 'are'? / [Mm] 'ythic'? / [Ss] 'pecial'? / [a-zA-Z] [a-z]* FormatQuery <- 'f' ':' Format / Legality ':' Format Format <- [a-zA-Z] [a-z]* @@ -42,10 +44,11 @@ ColorQuery <- [cC] 'olor'? <[iI]?> <[:!]> ColorEx* FieldQuery <- String [:] RegexString / String ws? NumericExpression -NonDoubleQuoteUnlessEscaped <- !["]. / '\"'. -NonSingleQuoteUnlessEscaped <- ![']. / "\'". +NonDoubleQuoteUnlessEscaped <- '\\\"'. / !["]. +NonSingleQuoteUnlessEscaped <- "\\\'". / ![']. UnescapedStringListPart <- !['":<>=! ]. -String <- UnescapedStringListPart+ / ["] ["] / ['] ['] +SingleApostropheString <- (UnescapedStringListPart+ ws*)* ['] (UnescapedStringListPart+ ws*)* +String <- UnescapedStringListPart+ / ["] ["] / ['] ['] / SingleApostropheString StringValue <- String / [(] StringList [)] StringList <- StringListString (ws? [,] ws? StringListString)* StringListString <- UnescapedStringListPart+ @@ -58,78 +61,92 @@ CompactStringSet <- StringListString ([,+] StringListString)+ NumericExpression <- NumericOperator ws? NumericValue NumericOperator <- [=:] / <[> NumericValue <- [0-9]+ - )"); std::once_flag init; static void setupParserRules() { - auto passthru = [](const peg::SemanticValues &sv) -> Filter { return !sv.empty() ? sv[0].get() : nullptr; }; + auto passthru = [](const peg::SemanticValues &sv) -> Filter { + return !sv.empty() ? std::any_cast(sv[0]) : nullptr; + }; search["Start"] = passthru; search["QueryPartList"] = [](const peg::SemanticValues &sv) -> Filter { - return [=](CardData x) { - for (int i = 0; i < static_cast(sv.size()); ++i) { - if (!sv[i].get()(x)) - return false; - } - return true; + return [=](const CardData &x) { + auto matchesFilter = [&x](const std::any &query) { return std::any_cast(query)(x); }; + return std::all_of(sv.begin(), sv.end(), matchesFilter); }; }; search["ComplexQueryPart"] = [](const peg::SemanticValues &sv) -> Filter { - return [=](CardData x) { - for (int i = 0; i < static_cast(sv.size()); ++i) { - if (sv[i].get()(x)) - return true; - } - return false; + return [=](const CardData &x) { + auto matchesFilter = [&x](const std::any &query) { return std::any_cast(query)(x); }; + return std::any_of(sv.begin(), sv.end(), matchesFilter); }; }; search["SomewhatComplexQueryPart"] = passthru; search["QueryPart"] = passthru; search["NotQuery"] = [](const peg::SemanticValues &sv) -> Filter { - Filter dependent = sv[0].get(); - return [=](CardData x) -> bool { return !dependent(x); }; + const auto dependent = std::any_cast(sv[0]); + return [=](const CardData &x) -> bool { return !dependent(x); }; }; search["TypeQuery"] = [](const peg::SemanticValues &sv) -> Filter { - StringMatcher matcher = sv[0].get(); - return [=](CardData x) -> bool { return matcher(x->getCardType()); }; + const auto matcher = std::any_cast(sv[0]); + return [=](const CardData &x) -> bool { return matcher(x->getCardType()); }; }; search["SetQuery"] = [](const peg::SemanticValues &sv) -> Filter { - StringMatcher matcher = sv[0].get(); - return [=](CardData x) -> bool { - for (const auto &set : x->getSets().keys()) { - if (matcher(set)) - return true; - } - return false; - }; - }; - search["RarityQuery"] = [](const peg::SemanticValues &sv) -> Filter { - StringMatcher matcher = sv[0].get(); + auto matcher = std::any_cast(sv[0]); return [=](const CardData &x) -> bool { - for (const auto &cardInfoPerSetList : x->getSets().values()) { - for (const auto &set : cardInfoPerSetList) { - if (matcher(set.getProperty("rarity"))) - return true; - } - } - return false; + QList sets = x->getSets().keys(); + + auto matchesSet = [&matcher](const QString &set) { return matcher(set); }; + return std::any_of(sets.begin(), sets.end(), matchesSet); }; }; - search["FormatQuery"] = [](const peg::SemanticValues &sv) -> Filter { - if (sv.choice() == 0) { - QString format = sv[0].get(); - return [=](CardData x) -> bool { return x->getProperty(QString("format-%1").arg(format)) == "legal"; }; - } else { - QString format = sv[1].get(); - QString legality = sv[0].get(); - return [=](CardData x) -> bool { return x->getProperty(QString("format-%1").arg(format)) == legality; }; + search["Rarity"] = [](const peg::SemanticValues &sv) -> QString { + switch (tolower(std::string(sv.sv())[0])) { + case 'c': + return "common"; + case 'u': + return "uncommon"; + case 'r': + return "rare"; + case 'm': + return "mythic"; + case 's': + return "special"; + default: + return QString::fromStdString(std::string(sv.sv())); } }; + search["RarityQuery"] = [](const peg::SemanticValues &sv) -> Filter { + const auto rarity = std::any_cast(sv[0]); + return [=](const CardData &x) -> bool { + QList infos; + for (const auto &setsValue : x->getSets().values()) { + for (const auto &cardInfoPerSet : setsValue) { + infos.append(cardInfoPerSet); + } + } + + auto matchesRarity = [&rarity](const CardInfoPerSet &info) { return rarity == info.getProperty("rarity"); }; + return std::any_of(infos.begin(), infos.end(), matchesRarity); + }; + }; + + search["FormatQuery"] = [](const peg::SemanticValues &sv) -> Filter { + if (sv.choice() == 0) { + const auto format = std::any_cast(sv[0]); + return + [=](const CardData &x) -> bool { return x->getProperty(QString("format-%1").arg(format)) == "legal"; }; + } + + const auto format = std::any_cast(sv[1]); + const auto legality = std::any_cast(sv[0]); + return [=](const CardData &x) -> bool { return x->getProperty(QString("format-%1").arg(format)) == legality; }; + }; search["Legality"] = [](const peg::SemanticValues &sv) -> QString { - switch (tolower(sv.str()[0])) { + switch (tolower(std::string(sv.sv())[0])) { case 'l': return "legal"; case 'b': @@ -142,8 +159,8 @@ static void setupParserRules() }; search["Format"] = [](const peg::SemanticValues &sv) -> QString { - if (sv.length() == 1) { - switch (tolower(sv.str()[0])) { + if (sv.size() == 1) { + switch (tolower(std::string(sv.sv())[0])) { case 'm': return "modern"; case 's': @@ -159,193 +176,191 @@ static void setupParserRules() default: return ""; } - } else { - return QString::fromStdString(sv.str()).toLower(); } + + return QString::fromStdString(std::string(sv.sv())).toLower(); }; search["StringValue"] = [](const peg::SemanticValues &sv) -> StringMatcher { if (sv.choice() == 0) { - auto target = sv[0].get(); + const auto target = std::any_cast(sv[0]); return [=](const QString &s) { return s.split(" ").contains(target, Qt::CaseInsensitive); }; - } else { - auto target = sv[0].get(); - return [=](const QString &s) { - for (const QString &str : target) { - if (s.split(" ").contains(str, Qt::CaseInsensitive)) { - return true; - } - } - return false; - }; } + + const auto target = std::any_cast(sv[0]); + return [=](const QString &s) { + auto containsString = [&s](const QString &str) { return s.split(" ").contains(str, Qt::CaseInsensitive); }; + return std::any_of(target.begin(), target.end(), containsString); + }; }; search["String"] = [](const peg::SemanticValues &sv) -> QString { if (sv.choice() == 0) { - return QString::fromStdString(sv.str()); - } else { - return QString::fromStdString(sv.token(0)); + return QString::fromStdString(std::string(sv.sv())); } + + return QString::fromStdString(std::string(sv.token(0))); }; search["FlexStringValue"] = [](const peg::SemanticValues &sv) -> StringMatcher { if (sv.choice() != 1) { - auto target = sv[0].get(); + const auto target = std::any_cast(sv[0]); return [=](const QString &s) { - for (const QString &str : target) { - if (s.split(" ").contains(str, Qt::CaseInsensitive)) { - return true; - } - } - return false; + auto containsString = [&s](const QString &str) { + return s.split(" ").contains(str, Qt::CaseInsensitive); + }; + return std::any_of(target.begin(), target.end(), containsString); }; - } else { - auto target = sv[0].get(); - return [=](const QString &s) { return s.split(" ").contains(target, Qt::CaseInsensitive); }; } + + const auto target = std::any_cast(sv[0]); + return [=](const QString &s) { return s.split(" ").contains(target, Qt::CaseInsensitive); }; }; - search["CompactStringSet"] = search["StringList"] = [](const peg::SemanticValues &sv) -> QStringList { + search["CompactStringSet"] = [](const peg::SemanticValues &sv) -> QStringList { QStringList result; - for (int i = 0; i < static_cast(sv.size()); ++i) { - result.append(sv[i].get()); + for (const auto &i : sv) { + result.append(std::any_cast(i)); + } + return result; + }; + search["StringList"] = [](const peg::SemanticValues &sv) -> QStringList { + QStringList result; + for (const auto &i : sv) { + result.append(std::any_cast(i)); } return result; }; search["StringListString"] = [](const peg::SemanticValues &sv) -> QString { - return QString::fromStdString(sv.str()); + return QString::fromStdString(std::string(sv.sv())); }; search["NumericExpression"] = [](const peg::SemanticValues &sv) -> NumberMatcher { - auto arg = sv[1].get(); - auto op = sv[0].get(); + const auto arg = std::any_cast(sv[1]); + const auto op = std::any_cast(sv[0]); if (op == ">") - return [=](int s) { return s > arg; }; + return [=](const int s) { return s > arg; }; if (op == ">=") - return [=](int s) { return s >= arg; }; + return [=](const int s) { return s >= arg; }; if (op == "<") - return [=](int s) { return s < arg; }; + return [=](const int s) { return s < arg; }; if (op == "<=") - return [=](int s) { return s <= arg; }; + return [=](const int s) { return s <= arg; }; if (op == "=") - return [=](int s) { return s == arg; }; + return [=](const int s) { return s == arg; }; if (op == ":") - return [=](int s) { return s == arg; }; + return [=](const int s) { return s == arg; }; if (op == "!=") - return [=](int s) { return s != arg; }; + return [=](const int s) { return s != arg; }; return [](int) { return false; }; }; search["NumericValue"] = [](const peg::SemanticValues &sv) -> int { - return QString::fromStdString(sv.str()).toInt(); + return QString::fromStdString(std::string(sv.sv())).toInt(); }; search["NumericOperator"] = [](const peg::SemanticValues &sv) -> QString { - return QString::fromStdString(sv.str()); + return QString::fromStdString(std::string(sv.sv())); }; search["RegexString"] = [](const peg::SemanticValues &sv) -> StringMatcher { - auto target = sv[0].get(); + auto target = std::any_cast(sv[0]); return [=](const QString &s) { auto sanitizedTarget = QString(target); sanitizedTarget.replace("\\\"", "\""); sanitizedTarget.replace("\\'", "'"); - return s.QString::contains(sanitizedTarget, Qt::CaseInsensitive); + return s.contains(sanitizedTarget, Qt::CaseInsensitive); }; }; search["OracleQuery"] = [](const peg::SemanticValues &sv) -> Filter { - StringMatcher matcher = sv[0].get(); - return [=](CardData x) { return matcher(x->getText()); }; + const auto matcher = std::any_cast(sv[0]); + return [=](const CardData &x) { return matcher(x->getText()); }; }; search["ColorQuery"] = [](const peg::SemanticValues &sv) -> Filter { QString parts; - for (int i = 0; i < static_cast(sv.size()); ++i) { - parts += sv[i].get(); + for (const auto &i : sv) { + parts += std::any_cast(i); } - bool idenity = sv.tokens[0].first[0] != 'i'; - if (sv.tokens[1].first[0] == ':') { - return [=](CardData x) { - QString match = idenity ? x->getColors() : x->getProperty("coloridentity"); + const bool identity = sv.tokens[0][0] != 'i'; + if (sv.tokens[1][0] == ':') { + return [=](const CardData &x) { + QString match = identity ? x->getColors() : x->getProperty("coloridentity"); if (parts.contains("m") && match.length() < 2) { return false; - } else if (parts == "m") { + } + if (parts == "m") { return true; } if (parts.contains("c") && match.length() == 0) return true; - for (const auto &i : match) { - if (parts.contains(i)) - return true; - } - return false; - }; - } else { - return [=](CardData x) { - QString match = idenity ? x->getColors() : x->getProperty("colorIdentity"); - if (parts.contains("m") && match.length() < 2) - return false; - - if (parts.contains("c") && match.length() != 0) - return false; - - for (const auto &part : parts) { - if (!match.contains(part)) - return false; - } - - for (const auto &i : match) { - if (!parts.contains(i)) - return false; - } - return true; + auto containsColor = [&parts](const QString &s) { return parts.contains(s); }; + return std::any_of(match.begin(), match.end(), containsColor); }; } + + return [=](const CardData &x) { + QString match = identity ? x->getColors() : x->getProperty("colorIdentity"); + if (parts.contains("m") && match.length() < 2) { + return false; + } + + if (parts.contains("c") && match.length() != 0) { + return false; + } + + for (const auto &part : parts) { + if (!match.contains(part)) { + return false; + } + } + + auto containsColor = [&parts](const QString &s) { return parts.contains(s); }; + return std::all_of(match.begin(), match.end(), containsColor); + }; }; search["CMCQuery"] = [](const peg::SemanticValues &sv) -> Filter { - NumberMatcher matcher = sv[0].get(); - return [=](CardData x) -> bool { return matcher(x->getProperty("cmc").toInt()); }; + const auto matcher = std::any_cast(sv[0]); + return [=](const CardData &x) -> bool { return matcher(x->getProperty("cmc").toInt()); }; }; search["PowerQuery"] = [](const peg::SemanticValues &sv) -> Filter { - NumberMatcher matcher = sv[0].get(); - return [=](CardData x) -> bool { return matcher(x->getPowTough().split("/")[0].toInt()); }; + const auto matcher = std::any_cast(sv[0]); + return [=](const CardData &x) -> bool { return matcher(x->getPowTough().split("/")[0].toInt()); }; }; search["ToughnessQuery"] = [](const peg::SemanticValues &sv) -> Filter { - NumberMatcher matcher = sv[0].get(); - return [=](CardData x) -> bool { + const auto matcher = std::any_cast(sv[0]); + return [=](const CardData &x) -> bool { auto parts = x->getPowTough().split("/"); return matcher(parts.length() == 2 ? parts[1].toInt() : 0); }; }; search["FieldQuery"] = [](const peg::SemanticValues &sv) -> Filter { - QString field = sv[0].get(); + const auto field = std::any_cast(sv[0]); if (sv.choice() == 0) { - StringMatcher matcher = sv[1].get(); - return [=](CardData x) -> bool { return x->hasProperty(field) ? matcher(x->getProperty(field)) : false; }; - } else { - NumberMatcher matcher = sv[1].get(); - return [=](CardData x) -> bool { - return x->hasProperty(field) ? matcher(x->getProperty(field).toInt()) : false; - }; + const auto matcher = std::any_cast(sv[1]); + return [=](const CardData &x) -> bool { return x->hasProperty(field) && matcher(x->getProperty(field)); }; } + + const auto matcher = std::any_cast(sv[1]); + return + [=](const CardData &x) -> bool { return x->hasProperty(field) && matcher(x->getProperty(field).toInt()); }; }; search["GenericQuery"] = [](const peg::SemanticValues &sv) -> Filter { - StringMatcher matcher = sv[0].get(); - return [=](CardData x) { return matcher(x->getName()); }; + const auto matcher = std::any_cast(sv[0]); + return [=](const CardData &x) { return matcher(x->getName()); }; }; search["Color"] = [](const peg::SemanticValues &sv) -> char { return "WUBRGU"[sv.choice()]; }; search["ColorEx"] = [](const peg::SemanticValues &sv) -> char { - return sv.choice() == 0 ? sv[0].get() : *sv.c_str(); + return sv.choice() == 0 ? std::any_cast(sv[0]) : *std::string(sv.sv()).c_str(); }; } FilterString::FilterString() { - result = [](CardData) -> bool { return false; }; + result = [](const CardData &) -> bool { return false; }; _error = "Not initialized"; } @@ -358,16 +373,16 @@ FilterString::FilterString(const QString &expr) _error = QString(); if (ba.isEmpty()) { - result = [](CardData) -> bool { return true; }; + result = [](const CardData &) -> bool { return true; }; return; } - search.log = [&](size_t /*ln*/, size_t col, const std::string &msg) { + search.set_logger([&](size_t /*ln*/, size_t col, const std::string &msg) { _error = QString("Error at position %1: %2").arg(col).arg(QString::fromStdString(msg)); - }; + }); if (!search.parse(ba.data(), result)) { qDebug().nospace() << "FilterString error for " << expr << "; " << qPrintable(_error); - result = [](CardData) -> bool { return false; }; + result = [](const CardData &) -> bool { return false; }; } } diff --git a/cockatrice/src/game/zones/view_zone_widget.cpp b/cockatrice/src/game/zones/view_zone_widget.cpp index 59c45bf58..1489ab723 100644 --- a/cockatrice/src/game/zones/view_zone_widget.cpp +++ b/cockatrice/src/game/zones/view_zone_widget.cpp @@ -138,7 +138,7 @@ ZoneViewWidget::ZoneViewWidget(Player *_player, // QLabel sizes aren't taken into account until the widget is rendered. // Force refresh after 1ms to fix glitchy rendering with long QLabels. auto *lastResizeBeforeVisibleTimer = new QTimer(this); - connect(lastResizeBeforeVisibleTimer, &QTimer::timeout, this, [=] { + connect(lastResizeBeforeVisibleTimer, &QTimer::timeout, this, [=, this] { resizeToZoneContents(); disconnect(lastResizeBeforeVisibleTimer); lastResizeBeforeVisibleTimer->deleteLater(); diff --git a/cockatrice/src/server/user/user_info_box.cpp b/cockatrice/src/server/user/user_info_box.cpp index 364f38afd..a7857b170 100644 --- a/cockatrice/src/server/user/user_info_box.cpp +++ b/cockatrice/src/server/user/user_info_box.cpp @@ -253,7 +253,7 @@ void UserInfoBox::actPassword() connect(pend, // we need qoverload here in order to select the right version of this function QOverload::of(&PendingCommand::finished), - this, [=](const Response &response, const CommandContainer &, const QVariant &) { + this, [=, this](const Response &response, const CommandContainer &, const QVariant &) { if (response.response_code() == Response::RespOk) { changePassword(oldPassword, newPassword); } else { diff --git a/common/expression.cpp b/common/expression.cpp index cc3a393b9..9cb40ea3c 100644 --- a/common/expression.cpp +++ b/common/expression.cpp @@ -51,9 +51,9 @@ double Expression::eval(const peg::Ast &ast) { const auto &nodes = ast.nodes; if (ast.name == "NUMBER") { - return stod(ast.token); + return stod(std::string(ast.token)); } else if (ast.name == "FUNCTION") { - QString name = QString::fromStdString(nodes[0]->token); + QString name = QString::fromStdString(std::string(nodes[0]->token)); if (!fns.contains(name)) return 0; return fns[name](eval(*nodes[1])); diff --git a/common/lib/peglib.h b/common/lib/peglib.h index 5c5e4c011..459fa5a27 100644 --- a/common/lib/peglib.h +++ b/common/lib/peglib.h @@ -1,15 +1,27 @@ // // peglib.h // -// Copyright (c) 2015-18 Yuji Hirose. All rights reserved. +// Copyright (c) 2022 Yuji Hirose. All rights reserved. // MIT License // -#ifndef CPPPEGLIB_PEGLIB_H -#define CPPPEGLIB_PEGLIB_H +#pragma once + +/* + * Configuration + */ + +#ifndef CPPPEGLIB_HEURISTIC_ERROR_TOKEN_MAX_CHAR_COUNT +#define CPPPEGLIB_HEURISTIC_ERROR_TOKEN_MAX_CHAR_COUNT 32 +#endif #include +#include #include +#include +#if __has_include() +#include +#endif #include #include #include @@ -19,312 +31,210 @@ #include #include #include +#include #include #include +#include #include -// guard for older versions of VC++ -#ifdef _MSC_VER -// VS2013 has no constexpr -#if (_MSC_VER == 1800) -#define PEGLIB_NO_CONSTEXPR_SUPPORT -#elif (_MSC_VER >= 1800) -// good to go -#else (_MSC_VER < 1800) -#error "Requires C+11 support" +#if !defined(__cplusplus) || __cplusplus < 201703L +#error "Requires complete C++17 support" #endif -#endif - -// define if the compiler doesn't support unicode characters reliably in the -// source code -//#define PEGLIB_NO_UNICODE_CHARS namespace peg { -/*----------------------------------------------------------------------------- - * any - *---------------------------------------------------------------------------*/ - -class any -{ -public: - any() : content_(nullptr) {} - - any(const any& rhs) : content_(rhs.clone()) {} - - any(any&& rhs) : content_(rhs.content_) { - rhs.content_ = nullptr; - } - - template - any(const T& value) : content_(new holder(value)) {} - - any& operator=(const any& rhs) { - if (this != &rhs) { - if (content_) { - delete content_; - } - content_ = rhs.clone(); - } - return *this; - } - - any& operator=(any&& rhs) { - if (this != &rhs) { - if (content_) { - delete content_; - } - content_ = rhs.content_; - rhs.content_ = nullptr; - } - return *this; - } - - ~any() { - delete content_; - } - - bool is_undefined() const { - return content_ == nullptr; - } - - template < - typename T, - typename std::enable_if::value, std::nullptr_t>::type = nullptr - > - T& get() { - if (!content_) { - throw std::bad_cast(); - } - auto p = dynamic_cast*>(content_); - assert(p); - if (!p) { - throw std::bad_cast(); - } - return p->value_; - } - - template < - typename T, - typename std::enable_if::value, std::nullptr_t>::type = nullptr - > - T& get() { - return *this; - } - - template < - typename T, - typename std::enable_if::value, std::nullptr_t>::type = nullptr - > - const T& get() const { - assert(content_); - auto p = dynamic_cast*>(content_); - assert(p); - if (!p) { - throw std::bad_cast(); - } - return p->value_; - } - - template < - typename T, - typename std::enable_if::value, std::nullptr_t>::type = nullptr - > - const any& get() const { - return *this; - } - -private: - struct placeholder { - virtual ~placeholder() {} - virtual placeholder* clone() const = 0; - }; - - template - struct holder : placeholder { - holder(const T& value) : value_(value) {} - placeholder* clone() const override { - return new holder(value_); - } - T value_; - }; - - placeholder* clone() const { - return content_ ? content_->clone() : nullptr; - } - - placeholder* content_; -}; - /*----------------------------------------------------------------------------- * scope_exit *---------------------------------------------------------------------------*/ -// This is based on "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189". +// This is based on +// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189". -template -struct scope_exit -{ - explicit scope_exit(EF&& f) - : exit_function(std::move(f)) - , execute_on_destruction{true} {} +template struct scope_exit { + explicit scope_exit(EF &&f) + : exit_function(std::move(f)), execute_on_destruction{true} {} - scope_exit(scope_exit&& rhs) - : exit_function(std::move(rhs.exit_function)) - , execute_on_destruction{rhs.execute_on_destruction} { - rhs.release(); + scope_exit(scope_exit &&rhs) + : exit_function(std::move(rhs.exit_function)), + execute_on_destruction{rhs.execute_on_destruction} { + rhs.release(); } ~scope_exit() { - if (execute_on_destruction) { - this->exit_function(); - } + if (execute_on_destruction) { this->exit_function(); } } - void release() { - this->execute_on_destruction = false; - } + void release() { this->execute_on_destruction = false; } private: - scope_exit(const scope_exit&) = delete; - void operator=(const scope_exit&) = delete; - scope_exit& operator=(scope_exit&&) = delete; + scope_exit(const scope_exit &) = delete; + void operator=(const scope_exit &) = delete; + scope_exit &operator=(scope_exit &&) = delete; - EF exit_function; + EF exit_function; bool execute_on_destruction; }; -template -auto make_scope_exit(EF&& exit_function) -> scope_exit { - return scope_exit::type>(std::forward(exit_function)); -} - /*----------------------------------------------------------------------------- * UTF8 functions *---------------------------------------------------------------------------*/ inline size_t codepoint_length(const char *s8, size_t l) { - if (l) { - auto b = static_cast(s8[0]); - if ((b & 0x80) == 0) { - return 1; - } else if ((b & 0xE0) == 0xC0) { - return 2; - } else if ((b & 0xF0) == 0xE0) { - return 3; - } else if ((b & 0xF8) == 0xF0) { - return 4; + if (l) { + auto b = static_cast(s8[0]); + if ((b & 0x80) == 0) { + return 1; + } else if ((b & 0xE0) == 0xC0 && l >= 2) { + return 2; + } else if ((b & 0xF0) == 0xE0 && l >= 3) { + return 3; + } else if ((b & 0xF8) == 0xF0 && l >= 4) { + return 4; + } } - } - return 0; + return 0; +} + +inline size_t codepoint_count(const char *s8, size_t l) { + size_t count = 0; + for (size_t i = 0; i < l; i += codepoint_length(s8 + i, l - i)) { + count++; + } + return count; } inline size_t encode_codepoint(char32_t cp, char *buff) { - if (cp < 0x0080) { - buff[0] = static_cast(cp & 0x7F); - return 1; - } else if (cp < 0x0800) { - buff[0] = static_cast(0xC0 | ((cp >> 6) & 0x1F)); - buff[1] = static_cast(0x80 | (cp & 0x3F)); - return 2; - } else if (cp < 0xD800) { - buff[0] = static_cast(0xE0 | ((cp >> 12) & 0xF)); - buff[1] = static_cast(0x80 | ((cp >> 6) & 0x3F)); - buff[2] = static_cast(0x80 | (cp & 0x3F)); - return 3; - } else if (cp < 0xE000) { - // D800 - DFFF is invalid... + if (cp < 0x0080) { + buff[0] = static_cast(cp & 0x7F); + return 1; + } else if (cp < 0x0800) { + buff[0] = static_cast(0xC0 | ((cp >> 6) & 0x1F)); + buff[1] = static_cast(0x80 | (cp & 0x3F)); + return 2; + } else if (cp < 0xD800) { + buff[0] = static_cast(0xE0 | ((cp >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((cp >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (cp & 0x3F)); + return 3; + } else if (cp < 0xE000) { + // D800 - DFFF is invalid... + return 0; + } else if (cp < 0x10000) { + buff[0] = static_cast(0xE0 | ((cp >> 12) & 0xF)); + buff[1] = static_cast(0x80 | ((cp >> 6) & 0x3F)); + buff[2] = static_cast(0x80 | (cp & 0x3F)); + return 3; + } else if (cp < 0x110000) { + buff[0] = static_cast(0xF0 | ((cp >> 18) & 0x7)); + buff[1] = static_cast(0x80 | ((cp >> 12) & 0x3F)); + buff[2] = static_cast(0x80 | ((cp >> 6) & 0x3F)); + buff[3] = static_cast(0x80 | (cp & 0x3F)); + return 4; + } return 0; - } else if (cp < 0x10000) { - buff[0] = static_cast(0xE0 | ((cp >> 12) & 0xF)); - buff[1] = static_cast(0x80 | ((cp >> 6) & 0x3F)); - buff[2] = static_cast(0x80 | (cp & 0x3F)); - return 3; - } else if (cp < 0x110000) { - buff[0] = static_cast(0xF0 | ((cp >> 18) & 0x7)); - buff[1] = static_cast(0x80 | ((cp >> 12) & 0x3F)); - buff[2] = static_cast(0x80 | ((cp >> 6) & 0x3F)); - buff[3] = static_cast(0x80 | (cp & 0x3F)); - return 4; - } - return 0; } inline std::string encode_codepoint(char32_t cp) { - char buff[4]; - auto l = encode_codepoint(cp, buff); - return std::string(buff, l); + char buff[4]; + auto l = encode_codepoint(cp, buff); + return std::string(buff, l); } inline bool decode_codepoint(const char *s8, size_t l, size_t &bytes, char32_t &cp) { - if (l) { - auto b = static_cast(s8[0]); - if ((b & 0x80) == 0) { - bytes = 1; - cp = b; - return true; - } else if ((b & 0xE0) == 0xC0) { - if (l >= 2) { - bytes = 2; - cp = ((static_cast(s8[0] & 0x1F)) << 6) | - (static_cast(s8[1] & 0x3F)); - return true; - } - } else if ((b & 0xF0) == 0xE0) { - if (l >= 3) { - bytes = 3; - cp = ((static_cast(s8[0] & 0x0F)) << 12) | - ((static_cast(s8[1] & 0x3F)) << 6) | - (static_cast(s8[2] & 0x3F)); - return true; - } - } else if ((b & 0xF8) == 0xF0) { - if (l >= 4) { - bytes = 4; - cp = ((static_cast(s8[0] & 0x07)) << 18) | - ((static_cast(s8[1] & 0x3F)) << 12) | - ((static_cast(s8[2] & 0x3F)) << 6) | - (static_cast(s8[3] & 0x3F)); - return true; - } + if (l) { + auto b = static_cast(s8[0]); + if ((b & 0x80) == 0) { + bytes = 1; + cp = b; + return true; + } else if ((b & 0xE0) == 0xC0) { + if (l >= 2) { + bytes = 2; + cp = ((static_cast(s8[0] & 0x1F)) << 6) | + (static_cast(s8[1] & 0x3F)); + return true; + } + } else if ((b & 0xF0) == 0xE0) { + if (l >= 3) { + bytes = 3; + cp = ((static_cast(s8[0] & 0x0F)) << 12) | + ((static_cast(s8[1] & 0x3F)) << 6) | + (static_cast(s8[2] & 0x3F)); + return true; + } + } else if ((b & 0xF8) == 0xF0) { + if (l >= 4) { + bytes = 4; + cp = ((static_cast(s8[0] & 0x07)) << 18) | + ((static_cast(s8[1] & 0x3F)) << 12) | + ((static_cast(s8[2] & 0x3F)) << 6) | + (static_cast(s8[3] & 0x3F)); + return true; + } + } } - } - return false; + return false; } -inline size_t decode_codepoint(const char *s8, size_t l, char32_t &out) { - size_t bytes; - if (decode_codepoint(s8, l, bytes, out)) { - return bytes; - } - return 0; +inline size_t decode_codepoint(const char *s8, size_t l, char32_t &cp) { + size_t bytes; + if (decode_codepoint(s8, l, bytes, cp)) { return bytes; } + return 0; } inline char32_t decode_codepoint(const char *s8, size_t l) { - char32_t out = 0; - decode_codepoint(s8, l, out); - return out; + char32_t cp = 0; + decode_codepoint(s8, l, cp); + return cp; } inline std::u32string decode(const char *s8, size_t l) { - std::u32string out; - size_t i = 0; - while (i < l) { - auto beg = i++; - while (i < l && (s8[i] & 0xc0) == 0x80) { - i++; + std::u32string out; + size_t i = 0; + while (i < l) { + auto beg = i++; + while (i < l && (s8[i] & 0xc0) == 0x80) { + i++; + } + out += decode_codepoint(&s8[beg], (i - beg)); } - out += decode_codepoint(&s8[beg], (i - beg)); - } - return out; + return out; +} + +template const char *u8(const T *s) { + return reinterpret_cast(s); +} + +/*----------------------------------------------------------------------------- + * escape_characters + *---------------------------------------------------------------------------*/ + +inline std::string escape_characters(const char *s, size_t n) { + std::string str; + for (size_t i = 0; i < n; i++) { + auto c = s[i]; + switch (c) { + case '\f': str += "\\f"; break; + case '\n': str += "\\n"; break; + case '\r': str += "\\r"; break; + case '\t': str += "\\t"; break; + case '\v': str += "\\v"; break; + default: str += c; break; + } + } + return str; +} + +inline std::string escape_characters(std::string_view sv) { + return escape_characters(sv.data(), sv.size()); } /*----------------------------------------------------------------------------- * resolve_escape_sequence *---------------------------------------------------------------------------*/ -inline bool is_hex(char c, int& v) { +inline bool is_hex(char c, int &v) { if ('0' <= c && c <= '9') { v = c - '0'; return true; @@ -338,7 +248,7 @@ inline bool is_hex(char c, int& v) { return false; } -inline bool is_digit(char c, int& v) { +inline bool is_digit(char c, int &v) { if ('0' <= c && c <= '9') { v = c - '0'; return true; @@ -346,27 +256,29 @@ inline bool is_digit(char c, int& v) { return false; } -inline std::pair parse_hex_number(const char* s, size_t n, size_t i) { +inline std::pair parse_hex_number(const char *s, size_t n, + size_t i) { int ret = 0; int val; while (i < n && is_hex(s[i], val)) { ret = static_cast(ret * 16 + val); i++; } - return std::make_pair(ret, i); + return std::pair(ret, i); } -inline std::pair parse_octal_number(const char* s, size_t n, size_t i) { +inline std::pair parse_octal_number(const char *s, size_t n, + size_t i) { int ret = 0; int val; while (i < n && is_digit(s[i], val)) { ret = static_cast(ret * 8 + val); i++; } - return std::make_pair(ret, i); + return std::pair(ret, i); } -inline std::string resolve_escape_sequence(const char* s, size_t n) { +inline std::string resolve_escape_sequence(const char *s, size_t n) { std::string r; r.reserve(n); @@ -375,15 +287,48 @@ inline std::string resolve_escape_sequence(const char* s, size_t n) { auto ch = s[i]; if (ch == '\\') { i++; + if (i == n) { throw std::runtime_error("Invalid escape sequence..."); } switch (s[i]) { - case 'n': r += '\n'; i++; break; - case 'r': r += '\r'; i++; break; - case 't': r += '\t'; i++; break; - case '\'': r += '\''; i++; break; - case '"': r += '"'; i++; break; - case '[': r += '['; i++; break; - case ']': r += ']'; i++; break; - case '\\': r += '\\'; i++; break; + case 'f': + r += '\f'; + i++; + break; + case 'n': + r += '\n'; + i++; + break; + case 'r': + r += '\r'; + i++; + break; + case 't': + r += '\t'; + i++; + break; + case 'v': + r += '\v'; + i++; + break; + case '\'': + r += '\''; + i++; + break; + case '"': + r += '"'; + i++; + break; + case '[': + r += '['; + i++; + break; + case ']': + r += ']'; + i++; + break; + case '\\': + r += '\\'; + i++; + break; case 'x': case 'u': { char32_t cp; @@ -406,14 +351,111 @@ inline std::string resolve_escape_sequence(const char* s, size_t n) { return r; } +/*----------------------------------------------------------------------------- + * token_to_number_ - This function should be removed eventually + *---------------------------------------------------------------------------*/ + +template T token_to_number_(std::string_view sv) { + T n = 0; +#if __has_include() + if constexpr (!std::is_floating_point::value) { + std::from_chars(sv.data(), sv.data() + sv.size(), n); +#else + if constexpr (false) { +#endif + } else { + auto s = std::string(sv); + std::istringstream ss(s); + ss >> n; + } + return n; +} + +/*----------------------------------------------------------------------------- + * Trie + *---------------------------------------------------------------------------*/ + +class Trie { +public: + Trie(const std::vector &items, bool ignore_case) + : ignore_case_(ignore_case) { + size_t id = 0; + for (const auto &item : items) { + const auto &s = ignore_case ? to_lower(item) : item; + for (size_t len = 1; len <= item.size(); len++) { + auto last = len == item.size(); + std::string_view sv(s.data(), len); + auto it = dic_.find(sv); + if (it == dic_.end()) { + dic_.emplace(sv, Info{last, last, id}); + } else if (last) { + it->second.match = true; + } else { + it->second.done = false; + } + } + id++; + } + } + + size_t match(const char *text, size_t text_len, size_t &id) const { + std::string lower_text; + if (ignore_case_) { + lower_text = to_lower(text); + text = lower_text.data(); + } + + size_t match_len = 0; + auto done = false; + size_t len = 1; + while (!done && len <= text_len) { + std::string_view sv(text, len); + auto it = dic_.find(sv); + if (it == dic_.end()) { + done = true; + } else { + if (it->second.match) { + match_len = len; + id = it->second.id; + } + if (it->second.done) { done = true; } + } + len += 1; + } + return match_len; + } + + size_t size() const { return dic_.size(); } + +private: + std::string to_lower(std::string s) const { + for (char &c : s) { + c = std::tolower(c); + } + return s; + } + + struct Info { + bool done; + bool match; + size_t id; + }; + + // TODO: Use unordered_map when heterogeneous lookup is supported in C++20 + // std::unordered_map dic_; + std::map> dic_; + + bool ignore_case_; +}; + /*----------------------------------------------------------------------------- * PEG *---------------------------------------------------------------------------*/ /* -* Line information utility function -*/ -inline std::pair line_info(const char* start, const char* cur) { + * Line information utility function + */ +inline std::pair line_info(const char *start, const char *cur) { auto p = start; auto col_ptr = p; auto no = 1; @@ -426,398 +468,371 @@ inline std::pair line_info(const char* start, const char* cur) { p++; } - auto col = p - col_ptr + 1; + auto col = codepoint_count(col_ptr, p - col_ptr) + 1; - return std::make_pair(no, col); + return std::pair(no, col); } /* -* Semantic values -*/ -struct SemanticValues : protected std::vector -{ + * String tag + */ +inline constexpr unsigned int str2tag_core(const char *s, size_t l, + unsigned int h) { + return (l == 0) ? h + : str2tag_core(s + 1, l - 1, + (h * 33) ^ static_cast(*s)); +} + +inline constexpr unsigned int str2tag(std::string_view sv) { + return str2tag_core(sv.data(), sv.size(), 0); +} + +namespace udl { + +inline constexpr unsigned int operator"" _(const char *s, size_t l) { + return str2tag_core(s, l, 0); +} + +} // namespace udl + +/* + * Semantic values + */ +class Context; + +struct SemanticValues : protected std::vector { + SemanticValues() = default; + SemanticValues(Context *c) : c_(c) {} + // Input text - const char* path; - const char* ss; + const char *path = nullptr; + const char *ss = nullptr; // Matched string - const char* c_str() const { return s_; } - size_t length() const { return n_; } + std::string_view sv() const { return sv_; } - std::string str() const { - return std::string(s_, n_); - } + // Definition name + const std::string &name() const { return name_; } + + std::vector tags; // Line number and column at which the matched string is - std::pair line_info() const { - return peg::line_info(ss, s_); - } + std::pair line_info() const; // Choice count - size_t choice_count() const { return choice_count_; } + size_t choice_count() const { return choice_count_; } // Choice number (0 based index) - size_t choice() const { return choice_; } + size_t choice() const { return choice_; } // Tokens - std::vector> tokens; + std::vector tokens; - std::string token(size_t id = 0) const { - if (!tokens.empty()) { - assert(id < tokens.size()); - const auto& tok = tokens[id]; - return std::string(tok.first, tok.second); - } - return std::string(s_, n_); + std::string_view token(size_t id = 0) const { + if (tokens.empty()) { return sv_; } + assert(id < tokens.size()); + return tokens[id]; + } + + // Token conversion + std::string token_to_string(size_t id = 0) const { + return std::string(token(id)); + } + + template T token_to_number() const { + return token_to_number_(token()); } // Transform the semantic value vector to another vector template - auto transform(size_t beg = 0, size_t end = static_cast(-1)) const -> vector { - return this->transform(beg, end, [](const any& v) { return v.get(); }); + std::vector transform(size_t beg = 0, + size_t end = static_cast(-1)) const { + std::vector r; + end = (std::min)(end, size()); + for (size_t i = beg; i < end; i++) { + r.emplace_back(std::any_cast((*this)[i])); + } + return r; } - SemanticValues() : s_(nullptr), n_(0), choice_count_(0), choice_(0) {} + void append(SemanticValues &chvs) { + sv_ = chvs.sv_; + for (auto &v : chvs) { + emplace_back(std::move(v)); + } + for (auto &tag : chvs.tags) { + tags.emplace_back(std::move(tag)); + } + for (auto &tok : chvs.tokens) { + tokens.emplace_back(std::move(tok)); + } + } - using std::vector::iterator; - using std::vector::const_iterator; - using std::vector::size; - using std::vector::empty; - using std::vector::assign; - using std::vector::begin; - using std::vector::end; - using std::vector::rbegin; - using std::vector::rend; - using std::vector::operator[]; - using std::vector::at; - using std::vector::resize; - using std::vector::front; - using std::vector::back; - using std::vector::push_back; - using std::vector::pop_back; - using std::vector::insert; - using std::vector::erase; - using std::vector::clear; - using std::vector::swap; - using std::vector::emplace; - using std::vector::emplace_back; + using std::vector::iterator; + using std::vector::const_iterator; + using std::vector::size; + using std::vector::empty; + using std::vector::assign; + using std::vector::begin; + using std::vector::end; + using std::vector::rbegin; + using std::vector::rend; + using std::vector::operator[]; + using std::vector::at; + using std::vector::resize; + using std::vector::front; + using std::vector::back; + using std::vector::push_back; + using std::vector::pop_back; + using std::vector::insert; + using std::vector::erase; + using std::vector::clear; + using std::vector::swap; + using std::vector::emplace; + using std::vector::emplace_back; private: friend class Context; + friend class Dictionary; friend class Sequence; friend class PrioritizedChoice; + friend class Repetition; friend class Holder; + friend class PrecedenceClimbing; - const char* s_; - size_t n_; - size_t choice_count_; - size_t choice_; - - template - auto transform(F f) const -> vector::type> { - vector::type> r; - for (const auto& v: *this) { - r.emplace_back(f(v)); - } - return r; - } - - template - auto transform(size_t beg, size_t end, F f) const -> vector::type> { - vector::type> r; - end = (std::min)(end, size()); - for (size_t i = beg; i < end; i++) { - r.emplace_back(f((*this)[i])); - } - return r; - } - - void reset() { - path = nullptr; - ss = nullptr; - tokens.clear(); - - s_ = nullptr; - n_ = 0; - choice_count_ = 0; - choice_ = 0; - } + Context *c_ = nullptr; + std::string_view sv_; + size_t choice_count_ = 0; + size_t choice_ = 0; + std::string name_; }; /* * Semantic action */ -template < - typename R, typename F, - typename std::enable_if::value, std::nullptr_t>::type = nullptr, - typename... Args> -any call(F fn, Args&&... args) { - fn(std::forward(args)...); - return any(); +template std::any call(F fn, Args &&...args) { + using R = decltype(fn(std::forward(args)...)); + if constexpr (std::is_void::value) { + fn(std::forward(args)...); + return std::any(); + } else if constexpr (std::is_same::type, + std::any>::value) { + return fn(std::forward(args)...); + } else { + return std::any(fn(std::forward(args)...)); + } } -template < - typename R, typename F, - typename std::enable_if::type, any>::value, std::nullptr_t>::type = nullptr, - typename... Args> -any call(F fn, Args&&... args) { - return fn(std::forward(args)...); -} +template +struct argument_count : argument_count {}; +template +struct argument_count + : std::integral_constant {}; +template +struct argument_count + : std::integral_constant {}; +template +struct argument_count + : std::integral_constant {}; -template < - typename R, typename F, - typename std::enable_if< - !std::is_void::value && - !std::is_same::type, any>::value, std::nullptr_t>::type = nullptr, - typename... Args> -any call(F fn, Args&&... args) { - return any(fn(std::forward(args)...)); -} - -class Action -{ +class Action { public: Action() = default; + Action(Action &&rhs) = default; + template Action(F fn) : fn_(make_adaptor(fn)) {} + template void operator=(F fn) { fn_ = make_adaptor(fn); } + Action &operator=(const Action &rhs) = default; - Action(const Action& rhs) : fn_(rhs.fn_) {} + operator bool() const { return bool(fn_); } - template ::value && !std::is_same::value, std::nullptr_t>::type = nullptr> - Action(F fn) : fn_(make_adaptor(fn, &F::operator())) {} - - template ::value, std::nullptr_t>::type = nullptr> - Action(F fn) : fn_(make_adaptor(fn, fn)) {} - - template ::value, std::nullptr_t>::type = nullptr> - Action(F /*fn*/) {} - - template ::value && !std::is_same::value, std::nullptr_t>::type = nullptr> - void operator=(F fn) { - fn_ = make_adaptor(fn, &F::operator()); - } - - template ::value, std::nullptr_t>::type = nullptr> - void operator=(F fn) { - fn_ = make_adaptor(fn, fn); - } - - template ::value, std::nullptr_t>::type = nullptr> - void operator=(F /*fn*/) {} - - Action& operator=(const Action& rhs) = default; - - operator bool() const { - return bool(fn_); - } - - any operator()(SemanticValues& sv, any& dt) const { - return fn_(sv, dt); + std::any operator()(SemanticValues &vs, std::any &dt) const { + return fn_(vs, dt); } private: - template - struct TypeAdaptor_sv { - TypeAdaptor_sv(std::function fn) - : fn_(fn) {} - any operator()(SemanticValues& sv, any& /*dt*/) { - return call(fn_, sv); + using Fty = std::function; + + template Fty make_adaptor(F fn) { + if constexpr (argument_count::value == 1) { + return [fn](auto &vs, auto & /*dt*/) { return call(fn, vs); }; + } else { + return [fn](auto &vs, auto &dt) { return call(fn, vs, dt); }; } - std::function fn_; - }; - - template - struct TypeAdaptor_csv { - TypeAdaptor_csv(std::function fn) - : fn_(fn) {} - any operator()(SemanticValues& sv, any& /*dt*/) { - return call(fn_, sv); - } - std::function fn_; - }; - - template - struct TypeAdaptor_sv_dt { - TypeAdaptor_sv_dt(std::function fn) - : fn_(fn) {} - any operator()(SemanticValues& sv, any& dt) { - return call(fn_, sv, dt); - } - std::function fn_; - }; - - template - struct TypeAdaptor_csv_dt { - TypeAdaptor_csv_dt(std::function fn) - : fn_(fn) {} - any operator()(SemanticValues& sv, any& dt) { - return call(fn_, sv, dt); - } - std::function fn_; - }; - - typedef std::function Fty; - - template - Fty make_adaptor(F fn, R (F::* /*mf*/)(SemanticValues& sv) const) { - return TypeAdaptor_sv(fn); - } - - template - Fty make_adaptor(F fn, R (F::* /*mf*/)(const SemanticValues& sv) const) { - return TypeAdaptor_csv(fn); - } - - template - Fty make_adaptor(F fn, R (F::* /*mf*/)(SemanticValues& sv)) { - return TypeAdaptor_sv(fn); - } - - template - Fty make_adaptor(F fn, R (F::* /*mf*/)(const SemanticValues& sv)) { - return TypeAdaptor_csv(fn); - } - - template - Fty make_adaptor(F fn, R (* /*mf*/)(SemanticValues& sv)) { - return TypeAdaptor_sv(fn); - } - - template - Fty make_adaptor(F fn, R (* /*mf*/)(const SemanticValues& sv)) { - return TypeAdaptor_csv(fn); - } - - template - Fty make_adaptor(F fn, R (F::* /*mf*/)(SemanticValues& sv, any& dt) const) { - return TypeAdaptor_sv_dt(fn); - } - - template - Fty make_adaptor(F fn, R (F::* /*mf*/)(const SemanticValues& sv, any& dt) const) { - return TypeAdaptor_csv_dt(fn); - } - - template - Fty make_adaptor(F fn, R (F::* /*mf*/)(SemanticValues& sv, any& dt)) { - return TypeAdaptor_sv_dt(fn); - } - - template - Fty make_adaptor(F fn, R (F::* /*mf*/)(const SemanticValues& sv, any& dt)) { - return TypeAdaptor_csv_dt(fn); - } - - template - Fty make_adaptor(F fn, R(* /*mf*/)(SemanticValues& sv, any& dt)) { - return TypeAdaptor_sv_dt(fn); - } - - template - Fty make_adaptor(F fn, R(* /*mf*/)(const SemanticValues& sv, any& dt)) { - return TypeAdaptor_csv_dt(fn); } Fty fn_; }; /* - * Semantic predicate + * Parse result helper */ -// Note: 'parse_error' exception class should be be used in sematic action handlers to reject the rule. -struct parse_error { - parse_error() = default; - parse_error(const char* s) : s_(s) {} - const char* what() const { return s_.empty() ? nullptr : s_.c_str(); } -private: - std::string s_; -}; +inline bool success(size_t len) { return len != static_cast(-1); } + +inline bool fail(size_t len) { return len == static_cast(-1); } /* - * Result + * Log */ -inline bool success(size_t len) { - return len != static_cast(-1); -} +using Log = std::function; -inline bool fail(size_t len) { - return len == static_cast(-1); -} +/* + * ErrorInfo + */ +class Definition; + +struct ErrorInfo { + const char *error_pos = nullptr; + std::vector> expected_tokens; + const char *message_pos = nullptr; + std::string message; + std::string label; + const char *last_output_pos = nullptr; + bool keep_previous_token = false; + + void clear() { + error_pos = nullptr; + expected_tokens.clear(); + message_pos = nullptr; + message.clear(); + } + + void add(const char *error_literal, const Definition *error_rule) { + for (const auto &[t, r] : expected_tokens) { + if (t == error_literal && r == error_rule) { return; } + } + expected_tokens.emplace_back(error_literal, error_rule); + } + + void output_log(const Log &log, const char *s, size_t n); + +private: + int cast_char(char c) const { return static_cast(c); } + + std::string heuristic_error_token(const char *s, size_t n, + const char *pos) const { + auto len = n - std::distance(s, pos); + if (len) { + size_t i = 0; + auto c = cast_char(pos[i++]); + if (!std::ispunct(c) && !std::isspace(c)) { + while (i < len && !std::ispunct(cast_char(pos[i])) && + !std::isspace(cast_char(pos[i]))) { + i++; + } + } + + size_t count = CPPPEGLIB_HEURISTIC_ERROR_TOKEN_MAX_CHAR_COUNT; + size_t j = 0; + while (count > 0 && j < i) { + j += codepoint_length(&pos[j], i - j); + count--; + } + + return escape_characters(pos, j); + } + return std::string(); + } + + std::string replace_all(std::string str, const std::string &from, + const std::string &to) const { + size_t pos = 0; + while ((pos = str.find(from, pos)) != std::string::npos) { + str.replace(pos, from.length(), to); + pos += to.length(); + } + return str; + } +}; /* * Context */ -class Context; class Ope; -class Definition; -typedef std::function Tracer; +using TracerEnter = std::function; -class Context -{ +using TracerLeave = std::function; + +using TracerStartOrEnd = std::function; + +class Context { public: - const char* path; - const char* s; - const size_t l; + const char *path; + const char *s; + const size_t l; - const char* error_pos; - const char* message_pos; - std::string message; // TODO: should be `int`. + ErrorInfo error_info; + bool recovered = false; std::vector> value_stack; - size_t value_stack_size; + size_t value_stack_size = 0; + + std::vector rule_stack; std::vector>> args_stack; - size_t nest_level; + size_t in_token_boundary_count = 0; - bool in_token; + std::shared_ptr whitespaceOpe; + bool in_whitespace = false; - std::shared_ptr whitespaceOpe; - bool in_whitespace; + std::shared_ptr wordOpe; - std::shared_ptr wordOpe; + std::vector> capture_scope_stack; + size_t capture_scope_stack_size = 0; - std::vector> capture_scope_stack; + std::vector cut_stack; - const size_t def_count; - const bool enablePackratParsing; - std::vector cache_registered; - std::vector cache_success; + const size_t def_count; + const bool enablePackratParsing; + std::vector cache_registered; + std::vector cache_success; - std::map, std::tuple> cache_values; + std::map, std::tuple> + cache_values; - std::function tracer; + TracerEnter tracer_enter; + TracerLeave tracer_leave; + std::any trace_data; + const bool verbose_trace; - Context( - const char* a_path, - const char* a_s, - size_t a_l, - size_t a_def_count, - std::shared_ptr a_whitespaceOpe, - std::shared_ptr a_wordOpe, - bool a_enablePackratParsing, - Tracer a_tracer) - : path(a_path) - , s(a_s) - , l(a_l) - , error_pos(nullptr) - , message_pos(nullptr) - , value_stack_size(0) - , nest_level(0) - , in_token(false) - , whitespaceOpe(a_whitespaceOpe) - , in_whitespace(false) - , wordOpe(a_wordOpe) - , def_count(a_def_count) - , enablePackratParsing(a_enablePackratParsing) - , cache_registered(enablePackratParsing ? def_count * (l + 1) : 0) - , cache_success(enablePackratParsing ? def_count * (l + 1) : 0) - , tracer(a_tracer) - { - args_stack.resize(1); - capture_scope_stack.resize(1); + Log log; + + Context(const char *path, const char *s, size_t l, size_t def_count, + std::shared_ptr whitespaceOpe, std::shared_ptr wordOpe, + bool enablePackratParsing, TracerEnter tracer_enter, + TracerLeave tracer_leave, std::any trace_data, bool verbose_trace, + Log log) + : path(path), s(s), l(l), whitespaceOpe(whitespaceOpe), wordOpe(wordOpe), + def_count(def_count), enablePackratParsing(enablePackratParsing), + cache_registered(enablePackratParsing ? def_count * (l + 1) : 0), + cache_success(enablePackratParsing ? def_count * (l + 1) : 0), + tracer_enter(tracer_enter), tracer_leave(tracer_leave), + trace_data(trace_data), verbose_trace(verbose_trace), log(log) { + + push_args({}); + push_capture_scope(); } + ~Context() { + pop_capture_scope(); + + assert(!value_stack_size); + assert(!capture_scope_stack_size); + assert(cut_stack.empty()); + } + + Context(const Context &) = delete; + Context(Context &&) = delete; + Context operator=(const Context &) = delete; + template - void packrat(const char* a_s, size_t def_id, size_t& len, any& val, T fn) { + void packrat(const char *a_s, size_t def_id, size_t &len, std::any &val, + T fn) { if (!enablePackratParsing) { fn(val); return; @@ -828,7 +843,7 @@ public: if (cache_registered[idx]) { if (cache_success[idx]) { - auto key = std::make_pair(col, def_id); + auto key = std::pair(col, def_id); std::tie(len, val) = cache_values[key]; return; } else { @@ -840,469 +855,445 @@ public: cache_registered[idx] = true; cache_success[idx] = success(len); if (success(len)) { - auto key = std::make_pair(col, def_id); - cache_values[key] = std::make_pair(len, val); + auto key = std::pair(col, def_id); + cache_values[key] = std::pair(len, val); } return; } } - SemanticValues& push() { - assert(value_stack_size <= value_stack.size()); - if (value_stack_size == value_stack.size()) { - value_stack.emplace_back(std::make_shared()); - } - auto& sv = *value_stack[value_stack_size++]; - if (!sv.empty()) { - sv.clear(); - } - sv.reset(); - sv.path = path; - sv.ss = s; - return sv; + SemanticValues &push() { + push_capture_scope(); + return push_semantic_values_scope(); } void pop() { - value_stack_size--; + pop_capture_scope(); + pop_semantic_values_scope(); } - void push_args(const std::vector>& args) { - args_stack.push_back(args); + // Semantic values + SemanticValues &push_semantic_values_scope() { + assert(value_stack_size <= value_stack.size()); + if (value_stack_size == value_stack.size()) { + value_stack.emplace_back(std::make_shared(this)); + } else { + auto &vs = *value_stack[value_stack_size]; + if (!vs.empty()) { + vs.clear(); + if (!vs.tags.empty()) { vs.tags.clear(); } + } + vs.sv_ = std::string_view(); + vs.choice_count_ = 0; + vs.choice_ = 0; + if (!vs.tokens.empty()) { vs.tokens.clear(); } + } + + auto &vs = *value_stack[value_stack_size++]; + vs.path = path; + vs.ss = s; + return vs; } - void pop_args() { - args_stack.pop_back(); + void pop_semantic_values_scope() { value_stack_size--; } + + // Arguments + void push_args(std::vector> &&args) { + args_stack.emplace_back(args); } - const std::vector>& top_args() const { + void pop_args() { args_stack.pop_back(); } + + const std::vector> &top_args() const { return args_stack[args_stack.size() - 1]; } + // Capture scope void push_capture_scope() { - capture_scope_stack.resize(capture_scope_stack.size() + 1); + assert(capture_scope_stack_size <= capture_scope_stack.size()); + if (capture_scope_stack_size == capture_scope_stack.size()) { + capture_scope_stack.emplace_back( + std::map()); + } else { + auto &cs = capture_scope_stack[capture_scope_stack_size]; + if (!cs.empty()) { cs.clear(); } + } + capture_scope_stack_size++; } - void pop_capture_scope() { - capture_scope_stack.pop_back(); - } + void pop_capture_scope() { capture_scope_stack_size--; } void shift_capture_values() { - assert(capture_scope_stack.size() >= 2); - auto it = capture_scope_stack.rbegin(); - auto it_prev = it + 1; - for (const auto& kv: *it) { - (*it_prev)[kv.first] = kv.second; + assert(capture_scope_stack_size >= 2); + auto curr = &capture_scope_stack[capture_scope_stack_size - 1]; + auto prev = curr - 1; + for (const auto &[k, v] : *curr) { + (*prev)[k] = v; } } - void set_error_pos(const char* a_s) { - if (error_pos < a_s) error_pos = a_s; + // Error + void set_error_pos(const char *a_s, const char *literal = nullptr); + + // Trace + void trace_enter(const Ope &ope, const char *a_s, size_t n, + const SemanticValues &vs, std::any &dt); + void trace_leave(const Ope &ope, const char *a_s, size_t n, + const SemanticValues &vs, std::any &dt, size_t len); + bool is_traceable(const Ope &ope) const; + + // Line info + std::pair line_info(const char *cur) const { + std::call_once(source_line_index_init_, [this]() { + for (size_t pos = 0; pos < l; pos++) { + if (s[pos] == '\n') { source_line_index.push_back(pos); } + } + source_line_index.push_back(l); + }); + + auto pos = static_cast(std::distance(s, cur)); + + auto it = std::lower_bound( + source_line_index.begin(), source_line_index.end(), pos, + [](size_t element, size_t value) { return element < value; }); + + auto id = static_cast(std::distance(source_line_index.begin(), it)); + auto off = pos - (id == 0 ? 0 : source_line_index[id - 1] + 1); + return std::pair(id + 1, off + 1); } - void trace(const char* name, const char* a_s, size_t n, SemanticValues& sv, any& dt) const { - if (tracer) tracer(name, a_s, n, sv, *this, dt); - } + size_t next_trace_id = 0; + std::vector trace_ids; + bool ignore_trace_state = false; + mutable std::once_flag source_line_index_init_; + mutable std::vector source_line_index; }; /* * Parser operators */ -class Ope -{ +class Ope { public: struct Visitor; - virtual ~Ope() {} - virtual size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const = 0; - virtual void accept(Visitor& v) = 0; + virtual ~Ope() = default; + size_t parse(const char *s, size_t n, SemanticValues &vs, Context &c, + std::any &dt) const; + virtual size_t parse_core(const char *s, size_t n, SemanticValues &vs, + Context &c, std::any &dt) const = 0; + virtual void accept(Visitor &v) = 0; }; -class Sequence : public Ope -{ +class Sequence : public Ope { public: - Sequence(const Sequence& rhs) : opes_(rhs.opes_) {} - -#if defined(_MSC_VER) && _MSC_VER < 1900 // Less than Visual Studio 2015 - // NOTE: Compiler Error C2797 on Visual Studio 2013 - // "The C++ compiler in Visual Studio does not implement list - // initialization inside either a member initializer list or a non-static - // data member initializer. Before Visual Studio 2013 Update 3, this was - // silently converted to a function call, which could lead to bad code - // generation. Visual Studio 2013 Update 3 reports this as an error." template - Sequence(const Args& ...args) { - opes_ = std::vector>{ static_cast>(args)... }; - } -#else - template - Sequence(const Args& ...args) : opes_{ static_cast>(args)... } {} -#endif + Sequence(const Args &...args) + : opes_{static_cast>(args)...} {} + Sequence(const std::vector> &opes) : opes_(opes) {} + Sequence(std::vector> &&opes) : opes_(opes) {} - Sequence(const std::vector>& opes) : opes_(opes) {} - Sequence(std::vector>&& opes) : opes_(opes) {} - - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("Sequence", s, n, sv, dt); - auto& chldsv = c.push(); + size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, + std::any &dt) const override { + auto &chvs = c.push_semantic_values_scope(); + auto se = scope_exit([&]() { c.pop_semantic_values_scope(); }); size_t i = 0; - for (const auto& ope : opes_) { - c.nest_level++; - auto se = make_scope_exit([&]() { c.nest_level--; }); - const auto& rule = *ope; - auto len = rule.parse(s + i, n - i, chldsv, c, dt); - if (fail(len)) { - return static_cast(-1); - } + for (const auto &ope : opes_) { + auto len = ope->parse(s + i, n - i, chvs, c, dt); + if (fail(len)) { return len; } i += len; } - sv.insert(sv.end(), chldsv.begin(), chldsv.end()); - sv.s_ = chldsv.c_str(); - sv.n_ = chldsv.length(); - sv.tokens.insert(sv.tokens.end(), chldsv.tokens.begin(), chldsv.tokens.end()); + vs.append(chvs); return i; } - void accept(Visitor& v) override; + void accept(Visitor &v) override; std::vector> opes_; }; -class PrioritizedChoice : public Ope -{ +class PrioritizedChoice : public Ope { public: -#if defined(_MSC_VER) && _MSC_VER < 1900 // Less than Visual Studio 2015 - // NOTE: Compiler Error C2797 on Visual Studio 2013 - // "The C++ compiler in Visual Studio does not implement list - // initialization inside either a member initializer list or a non-static - // data member initializer. Before Visual Studio 2013 Update 3, this was - // silently converted to a function call, which could lead to bad code - // generation. Visual Studio 2013 Update 3 reports this as an error." template - PrioritizedChoice(const Args& ...args) { - opes_ = std::vector>{ static_cast>(args)... }; - } -#else - template - PrioritizedChoice(const Args& ...args) : opes_{ static_cast>(args)... } {} -#endif + PrioritizedChoice(bool for_label, const Args &...args) + : opes_{static_cast>(args)...}, + for_label_(for_label) {} + PrioritizedChoice(const std::vector> &opes) + : opes_(opes) {} + PrioritizedChoice(std::vector> &&opes) : opes_(opes) {} - PrioritizedChoice(const std::vector>& opes) : opes_(opes) {} - PrioritizedChoice(std::vector>&& opes) : opes_(opes) {} + size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, + std::any &dt) const override { + size_t len = static_cast(-1); + + if (!for_label_) { c.cut_stack.push_back(false); } + auto se1 = scope_exit([&]() { + if (!for_label_) { c.cut_stack.pop_back(); } + }); - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("PrioritizedChoice", s, n, sv, dt); size_t id = 0; - for (const auto& ope : opes_) { - c.nest_level++; - auto& chldsv = c.push(); - c.push_capture_scope(); - auto se = make_scope_exit([&]() { - c.nest_level--; - c.pop(); - c.pop_capture_scope(); - }); - const auto& rule = *ope; - auto len = rule.parse(s, n, chldsv, c, dt); - if (success(len)) { - sv.insert(sv.end(), chldsv.begin(), chldsv.end()); - sv.s_ = chldsv.c_str(); - sv.n_ = chldsv.length(); - sv.choice_count_ = opes_.size(); - sv.choice_ = id; - sv.tokens.insert(sv.tokens.end(), chldsv.tokens.begin(), chldsv.tokens.end()); + for (const auto &ope : opes_) { + if (!c.cut_stack.empty()) { c.cut_stack.back() = false; } + auto &chvs = c.push(); + c.error_info.keep_previous_token = id > 0; + auto se2 = scope_exit([&]() { + c.pop(); + c.error_info.keep_previous_token = false; + }); + + len = ope->parse(s, n, chvs, c, dt); + + if (success(len)) { + vs.append(chvs); + vs.choice_count_ = opes_.size(); + vs.choice_ = id; c.shift_capture_values(); - return len; + break; + } else if (!c.cut_stack.empty() && c.cut_stack.back()) { + break; } + id++; } - return static_cast(-1); + + return len; } - void accept(Visitor& v) override; + void accept(Visitor &v) override; - size_t size() const { return opes_.size(); } + size_t size() const { return opes_.size(); } std::vector> opes_; + bool for_label_ = false; }; -class ZeroOrMore : public Ope -{ +class Repetition : public Ope { public: - ZeroOrMore(const std::shared_ptr& ope) : ope_(ope) {} + Repetition(const std::shared_ptr &ope, size_t min, size_t max) + : ope_(ope), min_(min), max_(max) {} - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("ZeroOrMore", s, n, sv, dt); - auto save_error_pos = c.error_pos; + size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, + std::any &dt) const override { + size_t count = 0; size_t i = 0; - while (n - i > 0) { - c.nest_level++; - c.push_capture_scope(); - auto se = make_scope_exit([&]() { - c.nest_level--; - c.pop_capture_scope(); - }); - auto save_sv_size = sv.size(); - auto save_tok_size = sv.tokens.size(); - const auto& rule = *ope_; - auto len = rule.parse(s + i, n - i, sv, c, dt); + while (count < min_) { + auto &chvs = c.push(); + auto se = scope_exit([&]() { c.pop(); }); + + auto len = ope_->parse(s + i, n - i, chvs, c, dt); + if (success(len)) { + vs.append(chvs); + c.shift_capture_values(); + } else { + return len; + } + i += len; + count++; + } + + while (count < max_) { + auto &chvs = c.push(); + auto se = scope_exit([&]() { c.pop(); }); + + auto len = ope_->parse(s + i, n - i, chvs, c, dt); + + if (success(len)) { + vs.append(chvs); c.shift_capture_values(); } else { - if (sv.size() != save_sv_size) { - sv.erase(sv.begin() + static_cast(save_sv_size)); - } - if (sv.tokens.size() != save_tok_size) { - sv.tokens.erase(sv.tokens.begin() + static_cast(save_tok_size)); - } - c.error_pos = save_error_pos; break; } i += len; + count++; } return i; } - void accept(Visitor& v) override; + void accept(Visitor &v) override; - std::shared_ptr ope_; -}; - -class OneOrMore : public Ope -{ -public: - OneOrMore(const std::shared_ptr& ope) : ope_(ope) {} - - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("OneOrMore", s, n, sv, dt); - size_t len = 0; - { - c.nest_level++; - c.push_capture_scope(); - auto se = make_scope_exit([&]() { - c.nest_level--; - c.pop_capture_scope(); - }); - const auto& rule = *ope_; - len = rule.parse(s, n, sv, c, dt); - if (success(len)) { - c.shift_capture_values(); - } else { - return static_cast(-1); - } - } - auto save_error_pos = c.error_pos; - auto i = len; - while (n - i > 0) { - c.nest_level++; - c.push_capture_scope(); - auto se = make_scope_exit([&]() { - c.nest_level--; - c.pop_capture_scope(); - }); - auto save_sv_size = sv.size(); - auto save_tok_size = sv.tokens.size(); - const auto& rule = *ope_; - len = rule.parse(s + i, n - i, sv, c, dt); - if (success(len)) { - c.shift_capture_values(); - } else { - if (sv.size() != save_sv_size) { - sv.erase(sv.begin() + static_cast(save_sv_size)); - } - if (sv.tokens.size() != save_tok_size) { - sv.tokens.erase(sv.tokens.begin() + static_cast(save_tok_size)); - } - c.error_pos = save_error_pos; - break; - } - i += len; - } - return i; + bool is_zom() const { + return min_ == 0 && max_ == std::numeric_limits::max(); } - void accept(Visitor& v) override; + static std::shared_ptr zom(const std::shared_ptr &ope) { + return std::make_shared(ope, 0, + std::numeric_limits::max()); + } + + static std::shared_ptr oom(const std::shared_ptr &ope) { + return std::make_shared(ope, 1, + std::numeric_limits::max()); + } + + static std::shared_ptr opt(const std::shared_ptr &ope) { + return std::make_shared(ope, 0, 1); + } std::shared_ptr ope_; + size_t min_; + size_t max_; }; -class Option : public Ope -{ +class AndPredicate : public Ope { public: - Option(const std::shared_ptr& ope) : ope_(ope) {} + AndPredicate(const std::shared_ptr &ope) : ope_(ope) {} + + size_t parse_core(const char *s, size_t n, SemanticValues & /*vs*/, + Context &c, std::any &dt) const override { + auto &chvs = c.push(); + auto se = scope_exit([&]() { c.pop(); }); + + auto len = ope_->parse(s, n, chvs, c, dt); - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("Option", s, n, sv, dt); - auto save_error_pos = c.error_pos; - c.nest_level++; - auto save_sv_size = sv.size(); - auto save_tok_size = sv.tokens.size(); - c.push_capture_scope(); - auto se = make_scope_exit([&]() { - c.nest_level--; - c.pop_capture_scope(); - }); - const auto& rule = *ope_; - auto len = rule.parse(s, n, sv, c, dt); if (success(len)) { - c.shift_capture_values(); + return 0; + } else { return len; - } else { - if (sv.size() != save_sv_size) { - sv.erase(sv.begin() + static_cast(save_sv_size)); - } - if (sv.tokens.size() != save_tok_size) { - sv.tokens.erase(sv.tokens.begin() + static_cast(save_tok_size)); - } - c.error_pos = save_error_pos; - return 0; } } - void accept(Visitor& v) override; + void accept(Visitor &v) override; std::shared_ptr ope_; }; -class AndPredicate : public Ope -{ +class NotPredicate : public Ope { public: - AndPredicate(const std::shared_ptr& ope) : ope_(ope) {} + NotPredicate(const std::shared_ptr &ope) : ope_(ope) {} - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("AndPredicate", s, n, sv, dt); - c.nest_level++; - auto& chldsv = c.push(); - c.push_capture_scope(); - auto se = make_scope_exit([&]() { - c.nest_level--; - c.pop(); - c.pop_capture_scope(); - }); - const auto& rule = *ope_; - auto len = rule.parse(s, n, chldsv, c, dt); - if (success(len)) { - return 0; - } else { - return static_cast(-1); - } - } - - void accept(Visitor& v) override; - - std::shared_ptr ope_; -}; - -class NotPredicate : public Ope -{ -public: - NotPredicate(const std::shared_ptr& ope) : ope_(ope) {} - - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("NotPredicate", s, n, sv, dt); - auto save_error_pos = c.error_pos; - c.nest_level++; - auto& chldsv = c.push(); - c.push_capture_scope(); - auto se = make_scope_exit([&]() { - c.nest_level--; - c.pop(); - c.pop_capture_scope(); - }); - const auto& rule = *ope_; - auto len = rule.parse(s, n, chldsv, c, dt); + size_t parse_core(const char *s, size_t n, SemanticValues & /*vs*/, + Context &c, std::any &dt) const override { + auto &chvs = c.push(); + auto se = scope_exit([&]() { c.pop(); }); + auto len = ope_->parse(s, n, chvs, c, dt); if (success(len)) { c.set_error_pos(s); return static_cast(-1); } else { - c.error_pos = save_error_pos; return 0; } } - void accept(Visitor& v) override; + void accept(Visitor &v) override; std::shared_ptr ope_; }; -class LiteralString : public Ope - , public std::enable_shared_from_this -{ +class Dictionary : public Ope, public std::enable_shared_from_this { public: - LiteralString(const std::string& s) - : lit_(s) - , init_is_word_(false) - , is_word_(false) - {} + Dictionary(const std::vector &v, bool ignore_case) + : trie_(v, ignore_case) {} - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; + size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, + std::any &dt) const override; - void accept(Visitor& v) override; + void accept(Visitor &v) override; + + Trie trie_; +}; + +class LiteralString : public Ope, + public std::enable_shared_from_this { +public: + LiteralString(std::string &&s, bool ignore_case) + : lit_(s), ignore_case_(ignore_case), is_word_(false) {} + + LiteralString(const std::string &s, bool ignore_case) + : lit_(s), ignore_case_(ignore_case), is_word_(false) {} + + size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, + std::any &dt) const override; + + void accept(Visitor &v) override; std::string lit_; - mutable bool init_is_word_; + bool ignore_case_; + mutable std::once_flag init_is_word_; mutable bool is_word_; }; -class CharacterClass : public Ope - , public std::enable_shared_from_this -{ +class CharacterClass : public Ope, + public std::enable_shared_from_this { public: - CharacterClass(const std::string& s) { - auto chars = decode(s.c_str(), s.length()); + CharacterClass(const std::string &s, bool negated, bool ignore_case) + : negated_(negated), ignore_case_(ignore_case) { + auto chars = decode(s.data(), s.length()); auto i = 0u; while (i < chars.size()) { if (i + 2 < chars.size() && chars[i + 1] == '-') { auto cp1 = chars[i]; auto cp2 = chars[i + 2]; - ranges_.emplace_back(std::make_pair(cp1, cp2)); + ranges_.emplace_back(std::pair(cp1, cp2)); i += 3; } else { auto cp = chars[i]; - ranges_.emplace_back(std::make_pair(cp, cp)); + ranges_.emplace_back(std::pair(cp, cp)); i += 1; } } + assert(!ranges_.empty()); } - CharacterClass(const std::vector>& ranges) : ranges_(ranges) {} - - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("CharacterClass", s, n, sv, dt); + CharacterClass(const std::vector> &ranges, + bool negated, bool ignore_case) + : ranges_(ranges), negated_(negated), ignore_case_(ignore_case) { + assert(!ranges_.empty()); + } + size_t parse_core(const char *s, size_t n, SemanticValues & /*vs*/, + Context &c, std::any & /*dt*/) const override { if (n < 1) { c.set_error_pos(s); return static_cast(-1); } - char32_t cp; + char32_t cp = 0; auto len = decode_codepoint(s, n, cp); - if (!ranges_.empty()) { - for (const auto& range: ranges_) { - if (range.first <= cp && cp <= range.second) { + for (const auto &range : ranges_) { + if (in_range(range, cp)) { + if (negated_) { + c.set_error_pos(s); + return static_cast(-1); + } else { return len; } } } - c.set_error_pos(s); - return static_cast(-1); + if (negated_) { + return len; + } else { + c.set_error_pos(s); + return static_cast(-1); + } } - void accept(Visitor& v) override; + void accept(Visitor &v) override; + +private: + bool in_range(const std::pair &range, char32_t cp) const { + if (ignore_case_) { + auto cpl = std::tolower(cp); + return std::tolower(range.first) <= cpl && + cpl <= std::tolower(range.second); + } else { + return range.first <= cp && cp <= range.second; + } + } std::vector> ranges_; + bool negated_; + bool ignore_case_; }; -class Character : public Ope - , public std::enable_shared_from_this -{ +class Character : public Ope, public std::enable_shared_from_this { public: Character(char ch) : ch_(ch) {} - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("Character", s, n, sv, dt); + size_t parse_core(const char *s, size_t n, SemanticValues & /*vs*/, + Context &c, std::any & /*dt*/) const override { if (n < 1 || s[0] != ch_) { c.set_error_pos(s); return static_cast(-1); @@ -1310,17 +1301,16 @@ public: return 1; } - void accept(Visitor& v) override; + void accept(Visitor &v) override; char ch_; }; -class AnyCharacter : public Ope - , public std::enable_shared_from_this -{ +class AnyCharacter : public Ope, + public std::enable_shared_from_this { public: - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("AnyCharacter", s, n, sv, dt); + size_t parse_core(const char *s, size_t n, SemanticValues & /*vs*/, + Context &c, std::any & /*dt*/) const override { auto len = codepoint_length(s, n); if (len < 1) { c.set_error_pos(s); @@ -1329,393 +1319,553 @@ public: return len; } - void accept(Visitor& v) override; + void accept(Visitor &v) override; }; -class CaptureScope : public Ope -{ +class CaptureScope : public Ope { public: - CaptureScope(const std::shared_ptr& ope) - : ope_(ope) {} + CaptureScope(const std::shared_ptr &ope) : ope_(ope) {} - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, + std::any &dt) const override { c.push_capture_scope(); - auto se = make_scope_exit([&]() { - c.pop_capture_scope(); - }); - const auto& rule = *ope_; - auto len = rule.parse(s, n, sv, c, dt); - return len; + auto se = scope_exit([&]() { c.pop_capture_scope(); }); + return ope_->parse(s, n, vs, c, dt); } - void accept(Visitor& v) override; + void accept(Visitor &v) override; std::shared_ptr ope_; }; -class Capture : public Ope -{ +class Capture : public Ope { public: - typedef std::function MatchAction; + using MatchAction = std::function; - Capture(const std::shared_ptr& ope, MatchAction ma) + Capture(const std::shared_ptr &ope, MatchAction ma) : ope_(ope), match_action_(ma) {} - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - const auto& rule = *ope_; - auto len = rule.parse(s, n, sv, c, dt); - if (success(len) && match_action_) { - match_action_(s, len, c); - } + size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, + std::any &dt) const override { + auto len = ope_->parse(s, n, vs, c, dt); + if (success(len) && match_action_) { match_action_(s, len, c); } return len; } - void accept(Visitor& v) override; + void accept(Visitor &v) override; std::shared_ptr ope_; - MatchAction match_action_; + MatchAction match_action_; }; -class TokenBoundary : public Ope -{ +class TokenBoundary : public Ope { public: - TokenBoundary(const std::shared_ptr& ope) : ope_(ope) {} + TokenBoundary(const std::shared_ptr &ope) : ope_(ope) {} - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; + size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, + std::any &dt) const override; - void accept(Visitor& v) override; + void accept(Visitor &v) override; std::shared_ptr ope_; }; -class Ignore : public Ope -{ +class Ignore : public Ope { public: - Ignore(const std::shared_ptr& ope) : ope_(ope) {} + Ignore(const std::shared_ptr &ope) : ope_(ope) {} - size_t parse(const char* s, size_t n, SemanticValues& /*sv*/, Context& c, any& dt) const override { - const auto& rule = *ope_; - auto& chldsv = c.push(); - auto se = make_scope_exit([&]() { - c.pop(); - }); - return rule.parse(s, n, chldsv, c, dt); + size_t parse_core(const char *s, size_t n, SemanticValues & /*vs*/, + Context &c, std::any &dt) const override { + auto &chvs = c.push_semantic_values_scope(); + auto se = scope_exit([&]() { c.pop_semantic_values_scope(); }); + return ope_->parse(s, n, chvs, c, dt); } - void accept(Visitor& v) override; + void accept(Visitor &v) override; std::shared_ptr ope_; }; -typedef std::function Parser; +using Parser = std::function; -class User : public Ope -{ +class User : public Ope { public: User(Parser fn) : fn_(fn) {} - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("User", s, n, sv, dt); + size_t parse_core(const char *s, size_t n, SemanticValues &vs, + Context & /*c*/, std::any &dt) const override { assert(fn_); - return fn_(s, n, sv, dt); + return fn_(s, n, vs, dt); } - void accept(Visitor& v) override; - std::function fn_; + void accept(Visitor &v) override; + std::function + fn_; }; -class WeakHolder : public Ope -{ +class WeakHolder : public Ope { public: - WeakHolder(const std::shared_ptr& ope) : weak_(ope) {} + WeakHolder(const std::shared_ptr &ope) : weak_(ope) {} - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { + size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, + std::any &dt) const override { auto ope = weak_.lock(); assert(ope); - const auto& rule = *ope; - return rule.parse(s, n, sv, c, dt); + return ope->parse(s, n, vs, c, dt); } - void accept(Visitor& v) override; + void accept(Visitor &v) override; std::weak_ptr weak_; }; -class Holder : public Ope -{ +class Holder : public Ope { public: - Holder(Definition* outer) - : outer_(outer) {} + Holder(Definition *outer) : outer_(outer) {} - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; + size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, + std::any &dt) const override; - void accept(Visitor& v) override; + void accept(Visitor &v) override; - any reduce(SemanticValues& sv, any& dt) const; + std::any reduce(SemanticValues &vs, std::any &dt) const; + + const std::string &name() const; + const std::string &trace_name() const; std::shared_ptr ope_; - Definition* outer_; + Definition *outer_; + mutable std::once_flag trace_name_init_; + mutable std::string trace_name_; friend class Definition; }; -typedef std::unordered_map Grammar; +using Grammar = std::unordered_map; -class Reference : public Ope - , public std::enable_shared_from_this -{ +class Reference : public Ope, public std::enable_shared_from_this { public: - Reference( - const Grammar& grammar, - const std::string& name, - const char* s, - bool is_macro, - const std::vector>& args) - : grammar_(grammar) - , name_(name) - , s_(s) - , is_macro_(is_macro) - , args_(args) - , rule_(nullptr) - , iarg_(0) - {} + Reference(const Grammar &grammar, const std::string &name, const char *s, + bool is_macro, const std::vector> &args) + : grammar_(grammar), name_(name), s_(s), is_macro_(is_macro), args_(args), + rule_(nullptr), iarg_(0) {} - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; + size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, + std::any &dt) const override; - void accept(Visitor& v) override; + void accept(Visitor &v) override; std::shared_ptr get_core_operator() const; - const Grammar& grammar_; + const Grammar &grammar_; const std::string name_; - const char* s_; + const char *s_; const bool is_macro_; const std::vector> args_; - Definition* rule_; + Definition *rule_; size_t iarg_; }; -class Whitespace : public Ope -{ +class Whitespace : public Ope { public: - Whitespace(const std::shared_ptr& ope) : ope_(ope) {} + Whitespace(const std::shared_ptr &ope) : ope_(ope) {} - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - if (c.in_whitespace) { - return 0; - } + size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, + std::any &dt) const override { + if (c.in_whitespace) { return 0; } c.in_whitespace = true; - auto se = make_scope_exit([&]() { c.in_whitespace = false; }); - const auto& rule = *ope_; - return rule.parse(s, n, sv, c, dt); + auto se = scope_exit([&]() { c.in_whitespace = false; }); + return ope_->parse(s, n, vs, c, dt); } - void accept(Visitor& v) override; + void accept(Visitor &v) override; std::shared_ptr ope_; }; -class BackReference : public Ope -{ +class BackReference : public Ope { public: - BackReference(const std::string& name) : name_(name) {} + BackReference(std::string &&name) : name_(name) {} - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; + BackReference(const std::string &name) : name_(name) {} - void accept(Visitor& v) override; + size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, + std::any &dt) const override; + + void accept(Visitor &v) override; std::string name_; }; +class PrecedenceClimbing : public Ope { +public: + using BinOpeInfo = std::map>; + + PrecedenceClimbing(const std::shared_ptr &atom, + const std::shared_ptr &binop, const BinOpeInfo &info, + const Definition &rule) + : atom_(atom), binop_(binop), info_(info), rule_(rule) {} + + size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, + std::any &dt) const override { + return parse_expression(s, n, vs, c, dt, 0); + } + + void accept(Visitor &v) override; + + std::shared_ptr atom_; + std::shared_ptr binop_; + BinOpeInfo info_; + const Definition &rule_; + +private: + size_t parse_expression(const char *s, size_t n, SemanticValues &vs, + Context &c, std::any &dt, size_t min_prec) const; + + Definition &get_reference_for_binop(Context &c) const; +}; + +class Recovery : public Ope { +public: + Recovery(const std::shared_ptr &ope) : ope_(ope) {} + + size_t parse_core(const char *s, size_t n, SemanticValues &vs, Context &c, + std::any &dt) const override; + + void accept(Visitor &v) override; + + std::shared_ptr ope_; +}; + +class Cut : public Ope, public std::enable_shared_from_this { +public: + size_t parse_core(const char * /*s*/, size_t /*n*/, SemanticValues & /*vs*/, + Context &c, std::any & /*dt*/) const override { + if (!c.cut_stack.empty()) { c.cut_stack.back() = true; } + return 0; + } + + void accept(Visitor &v) override; +}; + /* * Factories */ -template -std::shared_ptr seq(Args&& ...args) { +template std::shared_ptr seq(Args &&...args) { return std::make_shared(static_cast>(args)...); } -template -std::shared_ptr cho(Args&& ...args) { - return std::make_shared(static_cast>(args)...); +template std::shared_ptr cho(Args &&...args) { + return std::make_shared( + false, static_cast>(args)...); } -inline std::shared_ptr zom(const std::shared_ptr& ope) { - return std::make_shared(ope); +template std::shared_ptr cho4label_(Args &&...args) { + return std::make_shared( + true, static_cast>(args)...); } -inline std::shared_ptr oom(const std::shared_ptr& ope) { - return std::make_shared(ope); +inline std::shared_ptr zom(const std::shared_ptr &ope) { + return Repetition::zom(ope); } -inline std::shared_ptr opt(const std::shared_ptr& ope) { - return std::make_shared