From 5f5d719285b48fd6304ce91915d6bc1e74d694f5 Mon Sep 17 00:00:00 2001 From: Nils Schnabel Date: Thu, 17 Mar 2022 22:00:48 +0000 Subject: [PATCH] basic support for lists in config.yaml --- FluidNC/src/Configuration/GenericFactory.h | 47 ++++++++++++---------- FluidNC/src/Configuration/HandlerBase.h | 15 ++++++- FluidNC/src/Configuration/ParserHandler.h | 30 +++++++++++++- FluidNC/src/Configuration/Tokenizer.cpp | 11 +++++ FluidNC/src/Configuration/Tokenizer.h | 6 ++- 5 files changed, 83 insertions(+), 26 deletions(-) diff --git a/FluidNC/src/Configuration/GenericFactory.h b/FluidNC/src/Configuration/GenericFactory.h index 5ebaad006..6b1587a7b 100644 --- a/FluidNC/src/Configuration/GenericFactory.h +++ b/FluidNC/src/Configuration/GenericFactory.h @@ -8,6 +8,22 @@ #include "HandlerBase.h" namespace Configuration { + template + class BuilderBase { + const char* name_; + + public: + BuilderBase(const char* name) : name_(name) {} + + BuilderBase(const BuilderBase& o) = delete; + BuilderBase& operator=(const BuilderBase& o) = delete; + + virtual BaseType* create() const = 0; + const char* name() const { return name_; } + + virtual ~BuilderBase() = default; + }; + template class GenericFactory { static GenericFactory& instance() { @@ -20,30 +36,15 @@ namespace Configuration { GenericFactory(const GenericFactory&) = delete; GenericFactory& operator=(const GenericFactory&) = delete; - class BuilderBase { - const char* name_; - - public: - BuilderBase(const char* name) : name_(name) {} - - BuilderBase(const BuilderBase& o) = delete; - BuilderBase& operator=(const BuilderBase& o) = delete; - - virtual BaseType* create() const = 0; - const char* name() const { return name_; } - - virtual ~BuilderBase() = default; - }; - - std::vector builders_; + std::vector*> builders_; - inline static void registerBuilder(BuilderBase* builder) { instance().builders_.push_back(builder); } + inline static void registerBuilder(BuilderBase* builder) { instance().builders_.push_back(builder); } public: template - class InstanceBuilder : public BuilderBase { + class InstanceBuilder : public BuilderBase { public: - InstanceBuilder(const char* name) : BuilderBase(name) { instance().registerBuilder(this); } + InstanceBuilder(const char* name) : BuilderBase(name) { instance().registerBuilder(this); } BaseType* create() const override { return new DerivedType(); } }; @@ -66,9 +67,11 @@ namespace Configuration { if (handler.handlerType() == HandlerType::Parser) { for (auto it : instance().builders_) { if (handler.matchesUninitialized(it->name())) { - auto product = it->create(); - inst.push_back(product); - handler.enterFactory(it->name(), *product); + std::vector instlocal; + handler.enterFactoryList(it->name(), it, instlocal); + for (const auto& value : instlocal) { + inst.push_back((BaseType*)value); + } return; } diff --git a/FluidNC/src/Configuration/HandlerBase.h b/FluidNC/src/Configuration/HandlerBase.h index 82a18b53c..e064299e6 100644 --- a/FluidNC/src/Configuration/HandlerBase.h +++ b/FluidNC/src/Configuration/HandlerBase.h @@ -24,10 +24,16 @@ namespace Configuration { template class GenericFactory; + template + class BuilderBase; + class HandlerBase { protected: - virtual void enterSection(const char* name, Configurable* value) = 0; - virtual bool matchesUninitialized(const char* name) = 0; + virtual void enterSectionList(const char* name, BuilderBase* builder, std::vector& inst) { + throw "WIP: Won't work"; + }; + virtual void enterSection(const char* name, Configuration::Configurable* value) = 0; + virtual bool matchesUninitialized(const char* name) = 0; template friend class GenericFactory; @@ -77,6 +83,11 @@ namespace Configuration { } } + template + void enterFactoryList(const char* name, BuilderBase* value, std::vector& inst) { + enterSectionList(name, (BuilderBase*)value, inst); + } + template void enterFactory(const char* name, T& value) { enterSection(name, &value); diff --git a/FluidNC/src/Configuration/ParserHandler.h b/FluidNC/src/Configuration/ParserHandler.h index a49bfd0e0..c2726ad41 100644 --- a/FluidNC/src/Configuration/ParserHandler.h +++ b/FluidNC/src/Configuration/ParserHandler.h @@ -21,12 +21,40 @@ namespace Configuration { std::vector _path; public: + void enterSectionList(const char* name, BuilderBase* builder, std::vector& inst) override { + int entryIndent = _parser.token_.indent_; + // check if list + _parser.Tokenize(); + _parser.token_.state = TokenState::Held; + if (!_parser.token_.is_list_) { +#ifdef DEBUG_CHATTY_YAML_PARSER + log_debug("----------- Entered single item under " << name << " at indent " << _parser.token_.indent_); +#endif + inst.push_back(builder->create()); + this->enterSectionItem(name, inst.back(), entryIndent); + } + + while (_parser.token_.is_list_) { + _parser.token_.is_list_ = false; + entryIndent = _parser.token_.indent_; + _parser.token_.indent_ += 2; +#ifdef DEBUG_CHATTY_YAML_PARSER + log_debug("----------- Entered list item under " << name << " at indent " << _parser.token_.indent_); +#endif + inst.push_back(builder->create()); + this->enterSectionItem(name, inst.back(), entryIndent); + } + } + void enterSection(const char* name, Configuration::Configurable* section) override { + this->enterSectionItem(name, section, _parser.token_.indent_); + } + + void enterSectionItem(const char* name, Configuration::Configurable* section, int entryIndent) { _path.push_back(name); // For error handling // On entry, the token is for the section that invoked us. // We will handle following nodes with indents greater than entryIndent - int entryIndent = _parser.token_.indent_; #ifdef DEBUG_CHATTY_YAML_PARSER log_debug("Entered section " << name << " at indent " << entryIndent); #endif diff --git a/FluidNC/src/Configuration/Tokenizer.cpp b/FluidNC/src/Configuration/Tokenizer.cpp index 0d7475888..886ede90d 100644 --- a/FluidNC/src/Configuration/Tokenizer.cpp +++ b/FluidNC/src/Configuration/Tokenizer.cpp @@ -82,6 +82,17 @@ namespace Configuration { goto parseAgain; default: + if (IsListItem()) { + token_.is_list_ = true; + Inc(); + // skip expected whitespace. TODO: validate that it's indeed a whitespace + Inc(); + +#ifdef DEBUG_VERBOSE_YAML_TOKENIZER + log_debug("List token"); +#endif + } + if (!IsIdentifierChar()) { ParseError("Expected identifier."); } diff --git a/FluidNC/src/Configuration/Tokenizer.h b/FluidNC/src/Configuration/Tokenizer.h index 1be54b215..aaf4cb1a5 100644 --- a/FluidNC/src/Configuration/Tokenizer.h +++ b/FluidNC/src/Configuration/Tokenizer.h @@ -36,6 +36,8 @@ namespace Configuration { return c == ' ' || c == '\t' || c == '\f' || c == '\r'; } + inline bool IsListItem() { return Current() == '-'; } + inline bool IsIdentifierChar() { return IsAlpha() || IsDigit() || Current() == '_'; } inline bool IsEndLine() { return Eof() || Current() == '\n'; } @@ -68,11 +70,13 @@ namespace Configuration { // is called to handle the top level of the YAML config file, tokens at // indent 0 will be processed. TokenData() : - keyStart_(nullptr), keyEnd_(nullptr), indent_(-1), state(TokenState::Bof), sValueStart_(nullptr), sValueEnd_(nullptr) {} + keyStart_(nullptr), keyEnd_(nullptr), indent_(-1), is_list_(false), state(TokenState::Bof), sValueStart_(nullptr), + sValueEnd_(nullptr) {} const char* keyStart_; const char* keyEnd_; int indent_; + bool is_list_; TokenState state = TokenState::Bof;