From bac1600558fee52cd0c9cf6543ca668b89ff71dd Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Sat, 20 Dec 2025 21:54:09 +0800 Subject: [PATCH] feat: write some brigadier code --- src/yycc/carton/brigadier/constraint.cpp | 14 ++++ src/yycc/carton/brigadier/constraint.hpp | 40 ++++++++++++ src/yycc/carton/brigadier/types.cpp | 73 +++++++++++++++++++++ src/yycc/carton/brigadier/types.hpp | 82 ++++++++++++++++++++++++ 4 files changed, 209 insertions(+) diff --git a/src/yycc/carton/brigadier/constraint.cpp b/src/yycc/carton/brigadier/constraint.cpp index e69de29..5351ad1 100644 --- a/src/yycc/carton/brigadier/constraint.cpp +++ b/src/yycc/carton/brigadier/constraint.cpp @@ -0,0 +1,14 @@ +#include "constraint.hpp" + +namespace yycc::carton::brigadier::constraint { + +#pragma region Validator + + IValidator::IValidator() {} + + IValidator::~IValidator() {} + +#pragma endregion + + +} diff --git a/src/yycc/carton/brigadier/constraint.hpp b/src/yycc/carton/brigadier/constraint.hpp index e69de29..74971ba 100644 --- a/src/yycc/carton/brigadier/constraint.hpp +++ b/src/yycc/carton/brigadier/constraint.hpp @@ -0,0 +1,40 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace yycc::carton::brigadier::constraint { + + class IValidator { + public: + IValidator(); + virtual ~IValidator(); + + public: + virtual bool validate(const std::u8string_view& sv) const = 0; + }; + + template + concept Converter = requires(const T& t, const std::u8string_view& sv) { + // Check whether there is T::ReturnType type + typename T::ReturnType; + // Check whether there is "convert" member function and it has correct signature. + { t.convert(sv) } -> std::same_as; + }; + + //template + //concept Constraint = requires(const T& t) { + // { t.get_validator() } -> std::same_as>; + // { t.get_converter() } -> std::same_as; + //}; + + template + requires std::is_base_of_v + struct Constraint { + std::unique_ptr validator() const { throw std::logic_error("not implemented function"); } + TConverter converter() const { throw std::logic_error("not implemented function"); } + }; + +} // namespace yycc::carton::brigadier::constraint diff --git a/src/yycc/carton/brigadier/types.cpp b/src/yycc/carton/brigadier/types.cpp index e69de29..faf1ef2 100644 --- a/src/yycc/carton/brigadier/types.cpp +++ b/src/yycc/carton/brigadier/types.cpp @@ -0,0 +1,73 @@ +#include "types.hpp" +#include "../../patch/format.hpp" +#include +#include + +#define FORMAT ::yycc::patch::format + +namespace yycc::carton::brigadier::types { + +#pragma region ArgumentStack + + ArgumentStack::ArgumentStack(std::vector &&args) : stack(std::move(args)), cursor(0) {} + + ArgumentStack::~ArgumentStack() {} + + void ArgumentStack::reset() { + this->cursor = 0; + } + + bool ArgumentStack::empty() const { + return cursor >= this->stack.size(); + } + + std::u8string_view ArgumentStack::peek() const { + return std::u8string_view(this->stack.at(this->cursor)); + } + + void ArgumentStack::push() { + if (cursor == 0) throw std::logic_error("no arguments can be pushed"); + else --cursor; + } + + void ArgumentStack::push() { + if (cursor >= this->stack.size()) throw std::logic_error("no arguments can be poped"); + else ++cursor; + } + +#pragma endregion + +#pragma region ConflictSet + + ConflictSet::ConflictSet() : inner() {} + + ConflictSet::~ConflictSet() {} + + void ConflictSet::add_literal(const std::u8string_view &value) { + if (value.empty()) throw std::invalid_argument("try to insert empty item to conflict set."); + auto entry = FORMAT::format(u8"literal:{}", value); + + auto result = this->inner.emplace(std::move(entry)); + if (!result.second) throw std::runtime_error("try to insert duplicated item in conflict set."); + } + + void ConflictSet::add_argument(const std::u8string_view &value) { + if (value.empty()) throw std::invalid_argument("try to insert empty item to conflict set."); + auto entry = FORMAT::format(u8"argument:{}", value); + + auto result = this->inner.emplace(std::move(entry)); + if (!result.second) throw std::runtime_error("try to insert duplicated item in conflict set."); + } + + bool ConflictSet::is_conflict_with(const ConflictSet &rhs) const { + // create a cache to store computed intersection + std::vector intersection; + // compute intersection + std::set_intersection(this->inner.begin(), this->inner.end(), rhs.inner.begin(), rhs.inner.begin(), std::back_inserter(intersection)); + // check whether it is empty intersection + return !intersection.empty(); + } + +#pragma endregion + +} // namespace yycc::carton::brigadier::types diff --git a/src/yycc/carton/brigadier/types.hpp b/src/yycc/carton/brigadier/types.hpp index 139597f..1577808 100644 --- a/src/yycc/carton/brigadier/types.hpp +++ b/src/yycc/carton/brigadier/types.hpp @@ -1,2 +1,84 @@ +#pragma once +#include "../../macro/class_copy_move.hpp" +#include +#include +#include +#include +namespace yycc::carton::brigadier::types { + + /** + * @private + * @brief A wrapper for argument visiting in brigadier. + * @details + * When using this stack, the top of stack is the first argument, + * and the bottom of stack is the last argument for the convenient calling from brigadier. + * + * Actually, in all usage, all poped items will be finally pushed with FILO order again. + * So I simply use a vector and cursor to represent a fake stack. + */ + class ArgumentStack { + public: + ArgumentStack(std::vector&& args); + ~ArgumentStack(); + YYCC_DEFAULT_COPY_MOVE(ArgumentStack) + + public: + void reset(); + bool empty() const; + std::u8string_view peek() const; + void pop(); + void push(); + + private: + std::vector stack; + size_t cursor; + }; + + /// @private + /// @brief Internal node used conflict set for detecting potential conflict. + class ConflictSet { + public: + ConflictSet(); + ~ConflictSet(); + YYCC_DEFAULT_COPY_MOVE(ConflictSet) + + public: + /** + * @brief Add literal item into conflict set. + * @param[in] value Literal item. + * @remarks + * \li Literal item is the string input in command line. + * \li The word in Literal, and the vocabulary in Choice should be put by this function. + * \li Added item will add \c literal: prefix to make it in literal scope, + * so that it will not be compared with argument name items. + * Because we allow 2 literal item and argument name item have same name. + */ + void add_literal(const std::u8string_view& value); + /** + * @brief Add argument name item into conflict + * @param[in] value Argument name item. + * @remarks + * \li Argument name item is the key name put in ArgumentsMap. + * \li The argument name in Choice and Argument should be put by this function. + * \li Added item will add \c argument: prefix to make it in argument name scope, + * so that it will not be compared with literal items. + * Because we allow 2 literal item and argument name item have same name. + */ + void add_argument(const std::u8string_view& value); + /** + * @brief Check whether this set is conflict with another. + * @param[in] rhs The set to be compared. + * @return True if they have conflict. + * @remarks + * This function simply compute the intersection of two set. + * If the result is not empty, it means that there is a conflict. + */ + bool is_conflict_with(const ConflictSet& rhs) const; + + private: + std::set inner; + }; + +}