#include "single_keyword.h" #include "table_opaque.h" #include "debug.h" #include "flags.h" #include #include #include "cereal/types/set.hpp" using namespace std; USE_DEBUG_FLAG(D_KEYWORD); class StateopKeyword : public SingleKeyword { public: explicit StateopKeyword(const vector &attr, VariablesMapping &vars); MatchStatus isMatch(const I_KeywordRuntimeState* prev) const override; private: enum class Operation { ISSET, SET, UNSET, COUNT }; using OpFlags = Flags; void setState(const KeywordAttr &attr, const VariablesMapping &) { auto &var_name_param = attr.getParams(); if (var_name_param.size() != 2) { throw KeywordError("More than one element in the state name in the 'stateop' keyword"); } var_name = var_name_param[1]; } void setTesting(const KeywordAttr &, const VariablesMapping &) { if (!mode.empty()) throw KeywordError("Redefining 'stateop' keyword operation"); mode.setFlag(Operation::ISSET); } void setSetting(const KeywordAttr &, const VariablesMapping &) { if (!mode.empty()) throw KeywordError("Redefining 'stateop' keyword operation"); mode.setFlag(Operation::SET); } void setUnsetting(const KeywordAttr &, const VariablesMapping &) { if (!mode.empty()) throw KeywordError("Redefining 'stateop' keyword operation"); mode.setFlag(Operation::UNSET); } string var_name; OpFlags mode; static const map setops; }; const map StateopKeyword::setops = { { "isset", &StateopKeyword::setTesting }, { "set", &StateopKeyword::setSetting }, { "unset", &StateopKeyword::setUnsetting }, { "state", &StateopKeyword::setState } }; StateopKeyword::StateopKeyword(const vector &attrs, VariablesMapping &vars) { if (attrs.size() != 2) throw KeywordError("Invalid number of attributes in the 'stateop' keyword"); for (uint i = 0; i < attrs.size(); i++) { auto curr = setops.find(attrs[i].getAttrName()); if (curr == setops.end()) { throw KeywordError("Unknown attribute '" + attrs[i].getAttrName() + "' in the 'stateop' keyword"); } auto set_func = curr->second; (this->*set_func)(attrs[i], vars); } if (var_name == "" || mode.empty()) { throw KeywordError("Bad 'stateop' attribute configuration"); } } class KeywordStateop : public TableOpaqueSerialize { public: KeywordStateop() : TableOpaqueSerialize(this) {} bool hasVariable(const string &state) { return states.find(state) != states.end(); } void addVariable(const string &state) { states.insert(state); } void removeVariable(const string &state) { states.erase(state); } // LCOV_EXCL_START - sync functions, can only be tested once the sync module exists template void serialize(T &ar, uint32_t) { ar(states); } static std::string name() { return "KeywordStateop"; } static std::unique_ptr prototype() { return std::make_unique(); } static uint currVer() { return 0; } static uint minVer() { return 0; } // LCOV_EXCL_STOP private: set states; }; MatchStatus StateopKeyword::isMatch(const I_KeywordRuntimeState *prev) const { auto table = Singleton::Consume::by(); if (mode.isSet(Operation::ISSET)) { if (!table->hasState()) return MatchStatus::NoMatchFinal; auto &state = table->getState(); if (state.hasVariable(var_name)) return runNext(prev); else return MatchStatus::NoMatchFinal; } else if (mode.isSet(Operation::SET)) { if (!table->hasState()) table->createState(); table->getState().addVariable(var_name); return runNext(prev); } else if (mode.isSet(Operation::UNSET)) { if (table->hasState()) table->getState().removeVariable(var_name); return runNext(prev); } else { dbgAssert(false) << "Impossible 'stateop' keyword without operation"; } // If there was no matches and the keyword is effected by other keywords, then we know that the rule won't match return MatchStatus::NoMatchFinal; } unique_ptr genStateopKeyword(const vector &attr, VariablesMapping &known_vars) { return make_unique(attr, known_vars); }