mirror of
https://github.com/bdring/Grbl_Esp32.git
synced 2025-08-31 18:11:48 +02:00
Simplify parser and rename handle() to various
The configuration parser no longer needs to hold two tokens. It looks ahead by one token as necessary. When it finds a token at a lesser indent level, so that token must be handled higher up in the tree, it changes its state to "Held" so that the next call to Tokenize() will return the same token. The "moveNext()" logic in Parser is thus eliminated. The state variables for indent processing are local variables of single routine ParserHandler::enterSection(). That routine handles parsing of a single level of the YAML tree everywhere, including at the top level, so there is no duplication, and only one place where things can go wrong. The token has a state variable with these states: Bof, Matching, Matched, Held, Eof. Parser::is() only succeeds if the state is Matching, and then changes the state to Matched, thus preventing spurious matching of items when returning from a nested section into the middle of a list of items that are being handled at a higher level. The heavily-overloaded function name "handle()" has been split into several more-precise names. item() is for a leaf configuration item with a single value. item() is overloaded for different data types like int, float, bool, etc, but is not used for hierarchical objects. group() is for a list of items associated with an object or base class of an object. section() is for a named section of the YAML tree. group() and section() are related, but they are used differently. factory() is similar to group() but it is used for Motor and Spindle factories. The renaming makes it much easier, IMO, to figure out how the code works. Naming everything "handle()" hid the distinction between things with different behaviors so that everything merged together in your head, and searching revealed an unmanageable number of hits. Furthermore, there are a couple of other existing uses of handle() in the old code - namely for the input and web polling methods, and for ISR callback handlers - further adding to the search confusion.
This commit is contained in:
@@ -23,8 +23,8 @@
|
||||
#include <cstring>
|
||||
|
||||
namespace Configuration {
|
||||
void AfterParse::handleDetail(const char* name, Configurable* value) {
|
||||
void AfterParse::enterSection(const char* name, Configurable* value) {
|
||||
value->afterParse();
|
||||
value->handle(*this);
|
||||
value->group(*this);
|
||||
}
|
||||
}
|
||||
|
@@ -31,19 +31,19 @@ namespace Configuration {
|
||||
AfterParse& operator=(const AfterParse&) = delete;
|
||||
|
||||
protected:
|
||||
void handleDetail(const char* name, Configurable* value) override;
|
||||
void enterSection(const char* name, Configurable* value) override;
|
||||
bool matchesUninitialized(const char* name) override { return false; }
|
||||
HandlerType handlerType() override { return HandlerType::AfterParse; }
|
||||
|
||||
public:
|
||||
AfterParse() = default;
|
||||
|
||||
void handle(const char* name, bool& value) override {}
|
||||
void handle(const char* name, int32_t& value, int32_t minValue, int32_t maxValue) override {}
|
||||
void handle(const char* name, float& value, float minValue, float maxValue) override {}
|
||||
void handle(const char* name, StringRange& value, int minLength, int maxLength) override {}
|
||||
void handle(const char* name, Pin& value) override {}
|
||||
void handle(const char* name, IPAddress& value) override {}
|
||||
void handle(const char* name, int& value, EnumItem* e) override {}
|
||||
void item(const char* name, bool& value) override {}
|
||||
void item(const char* name, int32_t& value, int32_t minValue, int32_t maxValue) override {}
|
||||
void item(const char* name, float& value, float minValue, float maxValue) override {}
|
||||
void item(const char* name, StringRange& value, int minLength, int maxLength) override {}
|
||||
void item(const char* name, Pin& value) override {}
|
||||
void item(const char* name, IPAddress& value) override {}
|
||||
void item(const char* name, int& value, EnumItem* e) override {}
|
||||
};
|
||||
}
|
||||
|
@@ -34,8 +34,8 @@ namespace Configuration {
|
||||
public:
|
||||
Configurable() = default;
|
||||
|
||||
virtual void validate() const = 0;
|
||||
virtual void handle(HandlerBase& handler) = 0;
|
||||
virtual void validate() const {};
|
||||
virtual void group(HandlerBase& handler) = 0;
|
||||
virtual void afterParse() {}
|
||||
// virtual const char* name() const = 0;
|
||||
|
||||
|
@@ -49,10 +49,10 @@ public:
|
||||
// more validations?
|
||||
}
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) {
|
||||
handler.handle("bck", _bck);
|
||||
handler.handle("data", _data);
|
||||
handler.handle("ws", _ws);
|
||||
void group(Configuration::HandlerBase& handler) {
|
||||
handler.item("bck", _bck);
|
||||
handler.item("data", _data);
|
||||
handler.item("ws", _ws);
|
||||
}
|
||||
|
||||
~I2SOBus() = default;
|
||||
@@ -65,7 +65,7 @@ What normally happens, is that for all the configuration settings
|
||||
within the relevant section, the `handle` call of the Configurable
|
||||
is called. The handler has a callback with some convenience methods:
|
||||
|
||||
`handler.handle("bck", _bck);` -- this maps class field `_bck` on
|
||||
`handler.item("bck", _bck);` -- this maps class field `_bck` on
|
||||
the setting named "bck". Note that the default setting is normally
|
||||
defined in the constructor, or if that's not sufficient, in the
|
||||
`afterParse` method.
|
||||
|
@@ -37,7 +37,7 @@ namespace Configuration {
|
||||
|
||||
void Generator::add(Configuration::Configurable* configurable) {
|
||||
if (configurable != nullptr) {
|
||||
configurable->handle(*this);
|
||||
configurable->group(*this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,9 +50,9 @@ namespace Configuration {
|
||||
indent_--;
|
||||
}
|
||||
|
||||
void Generator::handleDetail(const char* name, Configurable* value) {
|
||||
void Generator::enterSection(const char* name, Configurable* value) {
|
||||
enter(name);
|
||||
value->handle(*this);
|
||||
value->group(*this);
|
||||
leave();
|
||||
}
|
||||
|
||||
|
@@ -48,37 +48,37 @@ namespace Configuration {
|
||||
void leave();
|
||||
|
||||
protected:
|
||||
void handleDetail(const char* name, Configurable* value) override;
|
||||
void enterSection(const char* name, Configurable* value) override;
|
||||
bool matchesUninitialized(const char* name) override { return false; }
|
||||
HandlerType handlerType() override { return HandlerType::Generator; }
|
||||
|
||||
public:
|
||||
Generator(SimpleOutputStream& dst);
|
||||
|
||||
void handle(const char* name, int& value, int32_t minValue, int32_t maxValue) override {
|
||||
void item(const char* name, int& value, int32_t minValue, int32_t maxValue) override {
|
||||
indent();
|
||||
dst_ << name << ": " << value << '\n';
|
||||
}
|
||||
|
||||
void handle(const char* name, float& value, float minValue, float maxValue) override {
|
||||
void item(const char* name, float& value, float minValue, float maxValue) override {
|
||||
indent();
|
||||
dst_ << name << ": " << value << '\n';
|
||||
}
|
||||
|
||||
void handle(const char* name, StringRange& value, int minLength, int maxLength) override {
|
||||
void item(const char* name, StringRange& value, int minLength, int maxLength) override {
|
||||
indent();
|
||||
dst_ << name << ": " << value << '\n';
|
||||
}
|
||||
|
||||
void handle(const char* name, Pin& value) override {
|
||||
void item(const char* name, Pin& value) override {
|
||||
indent();
|
||||
dst_ << name << ": " << value << '\n';
|
||||
}
|
||||
void handle(const char* name, IPAddress& value) override {
|
||||
void item(const char* name, IPAddress& value) override {
|
||||
indent();
|
||||
dst_ << name << ": " << value << '\n';
|
||||
}
|
||||
void handle(const char* name, int& value, EnumItem* e) override {
|
||||
void item(const char* name, int& value, EnumItem* e) override {
|
||||
indent();
|
||||
const char* str = "unknown";
|
||||
for (; e->name; ++e) {
|
||||
|
@@ -63,18 +63,18 @@ namespace Configuration {
|
||||
BaseType* create() const override { return new DerivedType(); }
|
||||
};
|
||||
|
||||
static void handle(Configuration::HandlerBase& handler, BaseType*& inst) {
|
||||
static void factory(Configuration::HandlerBase& handler, BaseType*& inst) {
|
||||
if (inst == nullptr) {
|
||||
for (auto it : instance().builders_) {
|
||||
if (handler.matchesUninitialized(it->name())) {
|
||||
inst = it->create();
|
||||
handler.handle(it->name(), *inst);
|
||||
handler.enterFactory(it->name(), *inst);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
handler.handleDetail(inst->name(), inst);
|
||||
handler.enterSection(inst->name(), inst);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -7,7 +7,7 @@ by name. The only thing you need to do is put the name in the header
|
||||
and put the registration in the cpp file
|
||||
`namespace { MotorFactory::InstanceBuilder<Nullmotor> registration("null_motor"); }`.
|
||||
In the yaml parser, the correct motor is then created in the handle
|
||||
method by calling `Motors::MotorFactory::handle(handler, _motor);`.
|
||||
method by calling `Motors::MotorFactory::factory(handler, _motor);`.
|
||||
|
||||
This means that there are literally no hard dependencies between motors.
|
||||
Not having dependencies is good, it can greatly help with compile-times
|
||||
|
@@ -31,42 +31,42 @@ namespace Configuration {
|
||||
|
||||
class HandlerBase {
|
||||
protected:
|
||||
virtual void handleDetail(const char* name, Configurable* value) = 0;
|
||||
virtual void enterSection(const char* name, Configurable* value) = 0;
|
||||
virtual bool matchesUninitialized(const char* name) = 0;
|
||||
|
||||
template <typename BaseType>
|
||||
friend class GenericFactory;
|
||||
|
||||
public:
|
||||
virtual void handle(const char* name, bool& value) = 0;
|
||||
virtual void handle(const char* name, int32_t& value, int32_t minValue = 0, int32_t maxValue = INT32_MAX) = 0;
|
||||
virtual void item(const char* name, bool& value) = 0;
|
||||
virtual void item(const char* name, int32_t& value, int32_t minValue = 0, int32_t maxValue = INT32_MAX) = 0;
|
||||
|
||||
/* We bound uint32_t to INT32_MAX because we use the same parser */
|
||||
void handle(const char* name, uint32_t& value, uint32_t minValue = 0, uint32_t maxValue = INT32_MAX) {
|
||||
void item(const char* name, uint32_t& value, uint32_t minValue = 0, uint32_t maxValue = INT32_MAX) {
|
||||
int32_t v = int32_t(value);
|
||||
handle(name, v, int32_t(minValue), int32_t(maxValue));
|
||||
item(name, v, int32_t(minValue), int32_t(maxValue));
|
||||
value = uint32_t(v);
|
||||
}
|
||||
|
||||
void handle(const char* name, uint8_t& value, uint8_t minValue = 0, uint8_t maxValue = UINT8_MAX) {
|
||||
void item(const char* name, uint8_t& value, uint8_t minValue = 0, uint8_t maxValue = UINT8_MAX) {
|
||||
int32_t v = int32_t(value);
|
||||
handle(name, v, int32_t(minValue), int32_t(maxValue));
|
||||
item(name, v, int32_t(minValue), int32_t(maxValue));
|
||||
value = uint8_t(v);
|
||||
}
|
||||
|
||||
virtual void handle(const char* name, float& value, float minValue = -3e38, float maxValue = 3e38) = 0;
|
||||
virtual void item(const char* name, float& value, float minValue = -3e38, float maxValue = 3e38) = 0;
|
||||
|
||||
virtual void handle(const char* name, StringRange& value, int minLength = 0, int maxLength = 255) = 0;
|
||||
virtual void handle(const char* name, Pin& value) = 0;
|
||||
virtual void handle(const char* name, IPAddress& value) = 0;
|
||||
virtual void item(const char* name, StringRange& value, int minLength = 0, int maxLength = 255) = 0;
|
||||
virtual void item(const char* name, Pin& value) = 0;
|
||||
virtual void item(const char* name, IPAddress& value) = 0;
|
||||
|
||||
virtual void handle(const char* name, int& value, EnumItem* e) = 0;
|
||||
virtual void item(const char* name, int& value, EnumItem* e) = 0;
|
||||
|
||||
virtual void handle(const char* name, String& value) {
|
||||
virtual void item(const char* name, String& value) {
|
||||
StringRange range(value);
|
||||
StringRange copy(value);
|
||||
|
||||
handle(name, range);
|
||||
item(name, range);
|
||||
|
||||
// Check for changes, and update if the string is changed.
|
||||
if (range.begin() != copy.begin() || range.end() != copy.end()) {
|
||||
@@ -77,22 +77,23 @@ namespace Configuration {
|
||||
virtual HandlerType handlerType() = 0;
|
||||
|
||||
template <typename T>
|
||||
void handle(const char* name, T*& value) {
|
||||
void section(const char* name, T*& value) {
|
||||
if (handlerType() == HandlerType::Parser) {
|
||||
// For Parser, matchesUninitialized(name) resolves to _parser.is(name)
|
||||
if (value == nullptr && matchesUninitialized(name)) {
|
||||
value = new T();
|
||||
handleDetail(name, value);
|
||||
enterSection(name, value);
|
||||
}
|
||||
} else {
|
||||
if (value != nullptr) {
|
||||
handleDetail(name, value);
|
||||
enterSection(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void handle(const char* name, T& value) {
|
||||
handleDetail(name, &value);
|
||||
void enterFactory(const char* name, T& value) {
|
||||
enterSection(name, &value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@@ -47,7 +47,7 @@ namespace Configuration {
|
||||
|
||||
void JsonGenerator::add(Configuration::Configurable* configurable) {
|
||||
if (configurable != nullptr) {
|
||||
configurable->handle(*this);
|
||||
configurable->group(*this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,13 +57,13 @@ namespace Configuration {
|
||||
*_paths[_depth] = '\0';
|
||||
}
|
||||
|
||||
void JsonGenerator::handleDetail(const char* name, Configurable* value) {
|
||||
void JsonGenerator::enterSection(const char* name, Configurable* value) {
|
||||
enter(name);
|
||||
value->handle(*this);
|
||||
value->group(*this);
|
||||
leave();
|
||||
}
|
||||
|
||||
void JsonGenerator::handle(const char* name, bool& value) {
|
||||
void JsonGenerator::item(const char* name, bool& value) {
|
||||
enter(name);
|
||||
const char* val = value ? "Yes" : "No";
|
||||
_encoder.begin_webui(name, _currentPath, "B", val);
|
||||
@@ -79,7 +79,7 @@ namespace Configuration {
|
||||
leave();
|
||||
}
|
||||
|
||||
void JsonGenerator::handle(const char* name, int& value, int32_t minValue, int32_t maxValue) {
|
||||
void JsonGenerator::item(const char* name, int& value, int32_t minValue, int32_t maxValue) {
|
||||
enter(name);
|
||||
char buf[32];
|
||||
itoa(value, buf, 10);
|
||||
@@ -88,12 +88,12 @@ namespace Configuration {
|
||||
leave();
|
||||
}
|
||||
|
||||
void JsonGenerator::handle(const char* name, float& value, float minValue, float maxValue) {
|
||||
void JsonGenerator::item(const char* name, float& value, float minValue, float maxValue) {
|
||||
int n = int(value * 1000);
|
||||
handle(name, n, int(minValue * 1000), int(maxValue * 1000));
|
||||
item(name, n, int(minValue * 1000), int(maxValue * 1000));
|
||||
}
|
||||
|
||||
void JsonGenerator::handle(const char* name, StringRange& value, int minLength, int maxLength) {
|
||||
void JsonGenerator::item(const char* name, StringRange& value, int minLength, int maxLength) {
|
||||
enter(name);
|
||||
auto sv = value.str();
|
||||
_encoder.begin_webui(name, _currentPath, "S", sv.c_str(), minLength, maxLength);
|
||||
@@ -101,7 +101,7 @@ namespace Configuration {
|
||||
leave();
|
||||
}
|
||||
|
||||
void JsonGenerator::handle(const char* name, Pin& value) {
|
||||
void JsonGenerator::item(const char* name, Pin& value) {
|
||||
// We commented this out, because pins are very confusing for users. The code is correct,
|
||||
// but it really gives more support than it's worth.
|
||||
/*
|
||||
@@ -113,7 +113,7 @@ namespace Configuration {
|
||||
*/
|
||||
}
|
||||
|
||||
void JsonGenerator::handle(const char* name, IPAddress& value) {
|
||||
void JsonGenerator::item(const char* name, IPAddress& value) {
|
||||
enter(name);
|
||||
#ifdef LATER
|
||||
// Encode IP address
|
||||
@@ -122,7 +122,7 @@ namespace Configuration {
|
||||
leave();
|
||||
}
|
||||
|
||||
void JsonGenerator::handle(const char* name, int& value, EnumItem* e) {
|
||||
void JsonGenerator::item(const char* name, int& value, EnumItem* e) {
|
||||
enter(name);
|
||||
const char* str = "unknown";
|
||||
for (; e->name; ++e) {
|
||||
|
@@ -44,19 +44,19 @@ namespace Configuration {
|
||||
void leave();
|
||||
|
||||
protected:
|
||||
void handleDetail(const char* name, Configurable* value) override;
|
||||
void enterSection(const char* name, Configurable* value) override;
|
||||
bool matchesUninitialized(const char* name) override { return false; }
|
||||
HandlerType handlerType() override { return HandlerType::Generator; }
|
||||
|
||||
public:
|
||||
JsonGenerator(WebUI::JSONencoder& encoder);
|
||||
|
||||
void handle(const char* name, bool& value) override;
|
||||
void handle(const char* name, int& value, int32_t minValue, int32_t maxValue) override;
|
||||
void handle(const char* name, float& value, float minValue, float maxValue) override;
|
||||
void handle(const char* name, StringRange& value, int minLength, int maxLength) override;
|
||||
void handle(const char* name, Pin& value) override;
|
||||
void handle(const char* name, IPAddress& value) override;
|
||||
void handle(const char* name, int& value, EnumItem* e) override;
|
||||
void item(const char* name, bool& value) override;
|
||||
void item(const char* name, int& value, int32_t minValue, int32_t maxValue) override;
|
||||
void item(const char* name, float& value, float minValue, float maxValue) override;
|
||||
void item(const char* name, StringRange& value, int minLength, int maxLength) override;
|
||||
void item(const char* name, Pin& value) override;
|
||||
void item(const char* name, IPAddress& value) override;
|
||||
void item(const char* name, int& value, EnumItem* e) override;
|
||||
};
|
||||
}
|
||||
|
@@ -30,8 +30,8 @@ namespace Configuration {
|
||||
LegacySettingHandler& operator=(const LegacySettingHandler&) = delete;
|
||||
LegacySettingHandler& operator=(LegacySettingHandler&&) = delete;
|
||||
|
||||
virtual int index() = 0;
|
||||
virtual void handle(const char* value) = 0;
|
||||
virtual int index() = 0;
|
||||
virtual void setValue(const char* value) = 0;
|
||||
|
||||
virtual ~LegacySettingHandler() {
|
||||
// Remove from factory? We shouldn't remove handlers...
|
||||
|
@@ -40,7 +40,7 @@ namespace Configuration {
|
||||
if (*str == '=') {
|
||||
++str;
|
||||
|
||||
handleLegacy(value, str);
|
||||
tryLegacy(value, str);
|
||||
} else {
|
||||
log_warn("Incorrect setting '" << start << "': cannot find '='.");
|
||||
}
|
||||
@@ -50,12 +50,13 @@ namespace Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
void LegacySettingRegistry::handleLegacy(int index, const char* value) {
|
||||
void LegacySettingRegistry::tryLegacy(int index, const char* value) {
|
||||
bool handled = false;
|
||||
for (auto it : instance().handlers_) {
|
||||
if (it->index() == index) {
|
||||
handled = true;
|
||||
it->handle(value);
|
||||
it->setValue(value);
|
||||
// ??? Show we break here, or are index duplications allowed?
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -38,7 +38,7 @@ namespace Configuration {
|
||||
std::vector<LegacySettingHandler*> handlers_;
|
||||
|
||||
static bool isLegacySetting(const char* str);
|
||||
static void handleLegacy(int index, const char* value);
|
||||
static void tryLegacy(int index, const char* value);
|
||||
|
||||
public:
|
||||
static void registerHandler(LegacySettingHandler* handler);
|
||||
|
@@ -26,106 +26,41 @@
|
||||
#include <climits>
|
||||
|
||||
namespace Configuration {
|
||||
Parser::Parser(const char* start, const char* end) : Tokenizer(start, end), current_() {
|
||||
Tokenize();
|
||||
current_ = token_;
|
||||
if (current_.kind_ != TokenKind::Eof) {
|
||||
Tokenize();
|
||||
}
|
||||
}
|
||||
Parser::Parser(const char* start, const char* end) : Tokenizer(start, end) {}
|
||||
|
||||
void Parser::parseError(const char* description) const {
|
||||
// Attempt to use the correct position in the parser:
|
||||
if (current_.keyEnd_) {
|
||||
throw ParseException(start_, current_.keyEnd_, description);
|
||||
if (token_.keyEnd_) {
|
||||
throw ParseException(token_.keyStart_, token_.keyEnd_, description);
|
||||
} else {
|
||||
Tokenizer::ParseError(description);
|
||||
}
|
||||
}
|
||||
|
||||
bool Parser::is(const char* expected) const {
|
||||
if (current_.keyStart_ == nullptr) {
|
||||
bool Parser::is(const char* expected) {
|
||||
if (token_.state != TokenState::Matching || token_.keyStart_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
auto len = strlen(expected);
|
||||
if (len != (current_.keyEnd_ - current_.keyStart_)) {
|
||||
if (len != (token_.keyEnd_ - token_.keyStart_)) {
|
||||
return false;
|
||||
}
|
||||
return !strncmp(expected, current_.keyStart_, len);
|
||||
bool result = !strncmp(expected, token_.keyStart_, len);
|
||||
if (result) {
|
||||
token_.state = TokenState::Matched;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MoveNext: moves to the next entry in the current section. By default we're in the
|
||||
/// root section.
|
||||
/// </summary>
|
||||
bool Parser::moveNext() {
|
||||
// While the indent of the token is > current indent, we have to skip it. This is a
|
||||
// sub-section, that we're apparently not interested in.
|
||||
while (token_.indent_ > current_.indent_) {
|
||||
Tokenize();
|
||||
}
|
||||
|
||||
// If the indent is the same, we're in the same section. Update current, move to next
|
||||
// token.
|
||||
if (token_.indent_ == current_.indent_) {
|
||||
current_ = token_;
|
||||
Tokenize();
|
||||
} else {
|
||||
// Apparently token_.indent < current_.indent_, which means we have no more items
|
||||
// in our tokenizer that are relevant.
|
||||
//
|
||||
// Note that we want to preserve current_.indent_!
|
||||
current_.kind_ = TokenKind::Eof;
|
||||
}
|
||||
|
||||
return current_.kind_ != TokenKind::Eof;
|
||||
}
|
||||
|
||||
void Parser::enter() {
|
||||
indentStack_.push(current_.indent_);
|
||||
|
||||
// If we can enter, token_.indent_ > current_.indent_:
|
||||
if (token_.indent_ > current_.indent_) {
|
||||
current_ = token_;
|
||||
Tokenize();
|
||||
} else {
|
||||
current_ = TokenData();
|
||||
current_.indent_ = INT_MAX;
|
||||
}
|
||||
indent_ = current_.indent_;
|
||||
}
|
||||
|
||||
void Parser::leave() {
|
||||
// While the indent of the tokenizer is >= current, we can ignore the contents:
|
||||
while (token_.indent_ >= current_.indent_ && token_.kind_ != TokenKind::Eof) {
|
||||
Tokenize();
|
||||
}
|
||||
|
||||
// At this point, we just know the indent is smaller. We don't know if we're in
|
||||
// the *right* section tho.
|
||||
auto last = indentStack_.top();
|
||||
indent_ = last;
|
||||
indentStack_.pop();
|
||||
|
||||
if (last == token_.indent_) {
|
||||
// Yes, the token continues where we left off:
|
||||
current_ = token_;
|
||||
// Tokenize();
|
||||
} else {
|
||||
current_ = TokenData();
|
||||
current_.indent_ = last;
|
||||
}
|
||||
}
|
||||
|
||||
StringRange Parser::stringValue() const { return StringRange(current_.sValueStart_, current_.sValueEnd_); }
|
||||
StringRange Parser::stringValue() const { return StringRange(token_.sValueStart_, token_.sValueEnd_); }
|
||||
|
||||
bool Parser::boolValue() const {
|
||||
auto str = StringRange(current_.sValueStart_, current_.sValueEnd_);
|
||||
auto str = StringRange(token_.sValueStart_, token_.sValueEnd_);
|
||||
return str.equals("true");
|
||||
}
|
||||
|
||||
int Parser::intValue() const {
|
||||
auto str = StringRange(current_.sValueStart_, current_.sValueEnd_);
|
||||
auto str = StringRange(token_.sValueStart_, token_.sValueEnd_);
|
||||
int32_t value;
|
||||
if (!str.isInteger(value)) {
|
||||
parseError("Expected an integer value like 123");
|
||||
@@ -134,7 +69,7 @@ namespace Configuration {
|
||||
}
|
||||
|
||||
float Parser::floatValue() const {
|
||||
auto str = StringRange(current_.sValueStart_, current_.sValueEnd_);
|
||||
auto str = StringRange(token_.sValueStart_, token_.sValueEnd_);
|
||||
float value;
|
||||
if (!str.isFloat(value)) {
|
||||
parseError("Expected a float value like 123.456");
|
||||
@@ -143,13 +78,13 @@ namespace Configuration {
|
||||
}
|
||||
|
||||
Pin Parser::pinValue() const {
|
||||
auto str = StringRange(current_.sValueStart_, current_.sValueEnd_);
|
||||
auto str = StringRange(token_.sValueStart_, token_.sValueEnd_);
|
||||
return Pin::create(str);
|
||||
}
|
||||
|
||||
IPAddress Parser::ipValue() const {
|
||||
IPAddress ip;
|
||||
auto str = StringRange(current_.sValueStart_, current_.sValueEnd_);
|
||||
auto str = StringRange(token_.sValueStart_, token_.sValueEnd_);
|
||||
if (!ip.fromString(str.str())) {
|
||||
parseError("Expected an IP address like 192.168.0.100");
|
||||
}
|
||||
@@ -157,7 +92,7 @@ namespace Configuration {
|
||||
}
|
||||
|
||||
int Parser::enumValue(EnumItem* e) const {
|
||||
auto str = StringRange(current_.sValueStart_, current_.sValueEnd_);
|
||||
auto str = StringRange(token_.sValueStart_, token_.sValueEnd_);
|
||||
for (; e->name; ++e) {
|
||||
if (str.equals(e->name)) {
|
||||
break;
|
||||
|
@@ -29,38 +29,12 @@
|
||||
|
||||
namespace Configuration {
|
||||
class Parser : public Tokenizer {
|
||||
// Parsing here might be a bit confusing, because the state of the tokenizer is one step
|
||||
// ahead of the parser. That way we always have 2 tokens at our disposal, so we know when
|
||||
// we're entering or exiting a section.
|
||||
|
||||
std::stack<int> indentStack_;
|
||||
TokenData current_;
|
||||
int indent_ = 0;
|
||||
|
||||
void parseError(const char* description) const;
|
||||
|
||||
public:
|
||||
Parser(const char* start, const char* end);
|
||||
|
||||
/// <summary>
|
||||
/// MoveNext: moves to the next entry in the current section. By default we're in the
|
||||
/// root section.
|
||||
/// </summary>
|
||||
bool moveNext();
|
||||
|
||||
inline bool isEndSection() { return current_.kind_ == TokenKind::Eof || current_.indent_ < indent_; }
|
||||
|
||||
// !!! Important !!! We cannot use a scoped variable for enter & leave, because 'leave' can throw,
|
||||
// and it could be called using stack unrolling. Destructors by definition have to be 'nothrow',
|
||||
// so forget it: it just Won't Work. In other words, if we leave the 'leave' call up to the
|
||||
// destructor, we end up what we in C++ call 'undefined behavior'.
|
||||
|
||||
void enter();
|
||||
void leave();
|
||||
|
||||
bool is(const char* expected) const;
|
||||
|
||||
inline StringRange key() const { return StringRange(current_.keyStart_, current_.keyEnd_); }
|
||||
bool is(const char* expected);
|
||||
|
||||
StringRange stringValue() const;
|
||||
bool boolValue() const;
|
||||
|
@@ -25,36 +25,72 @@
|
||||
|
||||
#include "../Logging.h"
|
||||
|
||||
//#define VERBOSE_PARSER
|
||||
// #define CHATTY_PARSER
|
||||
namespace Configuration {
|
||||
class ParserHandler : public Configuration::HandlerBase {
|
||||
private:
|
||||
Configuration::Parser& _parser;
|
||||
bool _previousIsLeave = false;
|
||||
|
||||
void checkPreviousLeave() {
|
||||
// When a section does a 'leave', the token becomes invalid, and we need to do a
|
||||
// moveNext. See the tests for the yaml parser for what the parser expects.
|
||||
// So, let's introduce that here:
|
||||
if (_previousIsLeave) {
|
||||
_parser.moveNext();
|
||||
}
|
||||
_previousIsLeave = false;
|
||||
}
|
||||
public:
|
||||
void enterSection(const char* name, Configuration::Configurable* section) override {
|
||||
// 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 CHATTY_PARSER
|
||||
log_debug("Entered section " << name << " at indent " << entryIndent);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void handleDetail(const char* name, Configuration::Configurable* value) override {
|
||||
if (value != nullptr && _parser.is(name)) {
|
||||
log_debug("Parsing configurable " << name);
|
||||
checkPreviousLeave();
|
||||
// The next token controls what we do next. If thisIndent is greater
|
||||
// than entryIndent, there are some subordinate tokens.
|
||||
_parser.Tokenize();
|
||||
int thisIndent = _parser.token_.indent_;
|
||||
#ifdef VERBOSE_PARSER
|
||||
log_debug("thisIndent " << _parser.key().str() << " " << thisIndent);
|
||||
#endif
|
||||
|
||||
_parser.enter();
|
||||
for (; !_parser.isEndSection(); _parser.moveNext()) {
|
||||
value->handle(*this);
|
||||
_previousIsLeave = false;
|
||||
// If thisIndent <= entryIndent, the section is empty - there are
|
||||
// no more-deeply-indented subordinate tokens.
|
||||
|
||||
if (thisIndent > entryIndent) {
|
||||
// If thisIndent > entryIndent, the new token is the first token within
|
||||
// this section so we process tokens at the same level as thisIndent.
|
||||
for (; _parser.token_.indent_ >= thisIndent; _parser.Tokenize()) {
|
||||
#ifdef VERBOSE_PARSER
|
||||
log_debug(" KEY " << _parser.key().str() << " state " << int(_parser.token_.state) << " indent "
|
||||
<< _parser.token_.indent_);
|
||||
#endif
|
||||
if (_parser.token_.indent_ > thisIndent) {
|
||||
log_info("Skipping key " << _parser.key().str() << " indent " << _parser.token_.indent_ << " thisIndent "
|
||||
<< thisIndent);
|
||||
} else {
|
||||
#ifdef VERBOSE_PARSER
|
||||
log_debug("Parsing key " << _parser.key().str());
|
||||
#endif
|
||||
section->group(*this);
|
||||
if (_parser.token_.state == TokenState::Matching) {
|
||||
log_error("Ignored key " << _parser.key().str());
|
||||
}
|
||||
#ifdef CHATTY_PARSER
|
||||
if (_parser.token_.state == Configuration::TokenState::Matched) {
|
||||
log_debug("Handled key " << _parser.key().str());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
_parser.leave();
|
||||
_previousIsLeave = true;
|
||||
}
|
||||
|
||||
// At this point we have the next token whose indent we
|
||||
// needed in order to decide what to do. When we return,
|
||||
// the caller will call Tokenize() to get a token, so we
|
||||
// "hold" the current token so that Tokenize() will
|
||||
// release that token instead of parsing ahead.
|
||||
// _parser.token_.held = true;
|
||||
|
||||
_parser.token_.state = TokenState::Held;
|
||||
#ifdef CHATTY_PARSER
|
||||
log_debug("Left section at indent " << entryIndent);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool matchesUninitialized(const char* name) override { return _parser.is(name); }
|
||||
@@ -62,61 +98,49 @@ namespace Configuration {
|
||||
public:
|
||||
ParserHandler(Configuration::Parser& parser) : _parser(parser) {}
|
||||
|
||||
void handle(const char* name, int32_t& value, int32_t minValue, int32_t maxValue) override {
|
||||
void item(const char* name, int32_t& value, int32_t minValue, int32_t maxValue) override {
|
||||
if (_parser.is(name)) {
|
||||
checkPreviousLeave();
|
||||
value = _parser.intValue();
|
||||
}
|
||||
}
|
||||
|
||||
void handle(const char* name, int& value, EnumItem* e) override {
|
||||
void item(const char* name, int& value, EnumItem* e) override {
|
||||
if (_parser.is(name)) {
|
||||
checkPreviousLeave();
|
||||
value = _parser.enumValue(e);
|
||||
}
|
||||
}
|
||||
|
||||
void handle(const char* name, bool& value) override {
|
||||
void item(const char* name, bool& value) override {
|
||||
if (_parser.is(name)) {
|
||||
checkPreviousLeave();
|
||||
value = _parser.boolValue();
|
||||
}
|
||||
}
|
||||
|
||||
void handle(const char* name, float& value, float minValue, float maxValue) override {
|
||||
void item(const char* name, float& value, float minValue, float maxValue) override {
|
||||
if (_parser.is(name)) {
|
||||
checkPreviousLeave();
|
||||
value = _parser.floatValue();
|
||||
}
|
||||
}
|
||||
|
||||
void handle(const char* name, StringRange& value, int minLength, int maxLength) override {
|
||||
void item(const char* name, StringRange& value, int minLength, int maxLength) override {
|
||||
if (_parser.is(name)) {
|
||||
checkPreviousLeave();
|
||||
value = _parser.stringValue();
|
||||
}
|
||||
}
|
||||
|
||||
void handle(const char* name, Pin& value) override {
|
||||
void item(const char* name, Pin& value) override {
|
||||
if (_parser.is(name)) {
|
||||
checkPreviousLeave();
|
||||
auto parsed = _parser.pinValue();
|
||||
value.swap(parsed);
|
||||
}
|
||||
}
|
||||
|
||||
void handle(const char* name, IPAddress& value) override {
|
||||
void item(const char* name, IPAddress& value) override {
|
||||
if (_parser.is(name)) {
|
||||
checkPreviousLeave();
|
||||
value = _parser.ipValue();
|
||||
}
|
||||
}
|
||||
|
||||
HandlerType handlerType() override { return HandlerType::Parser; }
|
||||
|
||||
void moveNext() {
|
||||
_previousIsLeave = false;
|
||||
_parser.moveNext();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@@ -27,7 +27,7 @@ namespace Configuration {
|
||||
std::atomic_thread_fence(std::memory_order::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
void RuntimeSetting::handleDetail(const char* name, Configuration::Configurable* value) {
|
||||
void RuntimeSetting::enterSection(const char* name, Configuration::Configurable* value) {
|
||||
if (is(name) && this->value() == nullptr) {
|
||||
auto previous = start_;
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Configuration {
|
||||
start_ = next;
|
||||
|
||||
// Handle child:
|
||||
value->handle(*this);
|
||||
value->group(*this);
|
||||
}
|
||||
|
||||
// Restore situation:
|
||||
@@ -49,26 +49,26 @@ namespace Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
void RuntimeSetting::handle(const char* name, int32_t& value, int32_t minValue, int32_t maxValue) {
|
||||
void RuntimeSetting::item(const char* name, int32_t& value, int32_t minValue, int32_t maxValue) {
|
||||
if (is(name) && this->value() != nullptr) {
|
||||
value = atoi(this->value());
|
||||
}
|
||||
}
|
||||
|
||||
void RuntimeSetting::handle(const char* name, float& value, float minValue, float maxValue) {
|
||||
void RuntimeSetting::item(const char* name, float& value, float minValue, float maxValue) {
|
||||
if (is(name) && this->value() != nullptr) {
|
||||
char* floatEnd;
|
||||
value = strtof(this->value(), &floatEnd);
|
||||
}
|
||||
}
|
||||
|
||||
void RuntimeSetting::handle(const char* name, StringRange& value, int minLength, int maxLength) {
|
||||
void RuntimeSetting::item(const char* name, StringRange& value, int minLength, int maxLength) {
|
||||
if (is(name) && this->value() != nullptr) {
|
||||
value = this->value();
|
||||
}
|
||||
}
|
||||
|
||||
void RuntimeSetting::handle(const char* name, Pin& value) {
|
||||
void RuntimeSetting::item(const char* name, Pin& value) {
|
||||
if (is(name) && this->value() != nullptr) {
|
||||
auto parsed = Pin::create(StringRange(this->value()));
|
||||
value.swap(parsed);
|
||||
|
@@ -48,18 +48,18 @@ namespace Configuration {
|
||||
}
|
||||
|
||||
protected:
|
||||
void handleDetail(const char* name, Configuration::Configurable* value) override;
|
||||
void enterSection(const char* name, Configuration::Configurable* value) override;
|
||||
|
||||
bool matchesUninitialized(const char* name) override { return false; }
|
||||
|
||||
public:
|
||||
RuntimeSetting(const char* runtimeSetting);
|
||||
|
||||
void handle(const char* name, int32_t& value, int32_t minValue, int32_t maxValue) override;
|
||||
void handle(const char* name, float& value, float minValue, float maxValue) override;
|
||||
void handle(const char* name, StringRange& value, int minLength, int maxLength) override;
|
||||
void handle(const char* name, Pin& value) override;
|
||||
void handle(const char* name, int& value, EnumItem* e) override {}
|
||||
void item(const char* name, int32_t& value, int32_t minValue, int32_t maxValue) override;
|
||||
void item(const char* name, float& value, float minValue, float maxValue) override;
|
||||
void item(const char* name, StringRange& value, int minLength, int maxLength) override;
|
||||
void item(const char* name, Pin& value) override;
|
||||
void item(const char* name, int& value, EnumItem* e) override {}
|
||||
|
||||
HandlerType handlerType() override { return HandlerType::Runtime; }
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Part of Grbl_ESP32
|
||||
2021 - Stefan de Bruijn
|
||||
2021 - Mitch Bradley
|
||||
|
||||
Grbl_ESP32 is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -19,10 +19,11 @@
|
||||
#pragma once
|
||||
|
||||
namespace Configuration {
|
||||
|
||||
enum struct TokenKind {
|
||||
Section,
|
||||
String,
|
||||
enum class TokenState {
|
||||
Bof,
|
||||
Matching,
|
||||
Matched,
|
||||
Held,
|
||||
Eof,
|
||||
};
|
||||
}
|
@@ -24,19 +24,22 @@
|
||||
|
||||
namespace Configuration {
|
||||
|
||||
void Tokenizer::skipToEol() {
|
||||
while (!IsEndLine()) {
|
||||
Inc();
|
||||
}
|
||||
if (Eof()) {
|
||||
ParseError("Missing end-of-line");
|
||||
}
|
||||
}
|
||||
|
||||
Tokenizer::Tokenizer(const char* start, const char* end) : current_(start), end_(end), start_(start), token_() {
|
||||
// If start is a yaml document start ('---' [newline]), skip that first.
|
||||
if (EqualsCaseInsensitive("---")) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
Inc();
|
||||
}
|
||||
while (IsWhiteSpace()) {
|
||||
Inc();
|
||||
}
|
||||
while (Current() == '\r' || Current() == '\n') {
|
||||
Inc();
|
||||
}
|
||||
|
||||
skipToEol();
|
||||
start_ = current_;
|
||||
}
|
||||
}
|
||||
@@ -44,6 +47,17 @@ namespace Configuration {
|
||||
void Tokenizer::ParseError(const char* description) const { throw ParseException(start_, current_, description); }
|
||||
|
||||
void Tokenizer::Tokenize() {
|
||||
// Release a held token
|
||||
if (token_.state == TokenState::Held) {
|
||||
token_.state = TokenState::Matching;
|
||||
#ifdef VERBOSE_TOKENIZER
|
||||
log_debug("Releasing " << key().str());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise find the next token
|
||||
token_.state = TokenState::Matching;
|
||||
// We parse 1 line at a time. Each time we get here, we can assume that the cursor
|
||||
// is at the start of the line.
|
||||
|
||||
@@ -54,121 +68,103 @@ namespace Configuration {
|
||||
Inc();
|
||||
++indent;
|
||||
}
|
||||
token_.indent_ = indent;
|
||||
|
||||
if (!Eof()) {
|
||||
switch (Current()) {
|
||||
case '\t':
|
||||
// TODO FIXME: We can do tabs or spaces, not both. However, we *could* let the user decide.
|
||||
ParseError("Indentation through tabs is not allowed. Convert all tabs to spaces please.");
|
||||
break;
|
||||
|
||||
case '#': // Comment till end of line
|
||||
Inc();
|
||||
while (!Eof() && !IsEndLine()) {
|
||||
Inc();
|
||||
}
|
||||
return;
|
||||
|
||||
case '\r':
|
||||
Inc();
|
||||
if (!Eof() && Current() == '\n') {
|
||||
Inc();
|
||||
} // \r\n
|
||||
goto parseAgain;
|
||||
case '\n':
|
||||
// \n without a preceding \r
|
||||
Inc();
|
||||
goto parseAgain;
|
||||
|
||||
default:
|
||||
if (!IsAlpha()) {
|
||||
ParseError("Expected identifier.");
|
||||
}
|
||||
|
||||
token_.keyStart_ = current_;
|
||||
Inc();
|
||||
while (!Eof() && (IsAlpha() || IsDigit() || Current() == '_')) {
|
||||
Inc();
|
||||
}
|
||||
token_.keyEnd_ = current_;
|
||||
|
||||
// Skip whitespaces:
|
||||
while (!Eof() && IsWhiteSpace()) {
|
||||
Inc();
|
||||
}
|
||||
|
||||
if (Current() != ':') {
|
||||
ParseError("After a key or section name, we expect a colon character ':'.");
|
||||
}
|
||||
Inc();
|
||||
|
||||
// Skip whitespaces after the colon:
|
||||
while (!Eof() && IsWhiteSpace()) {
|
||||
Inc();
|
||||
}
|
||||
|
||||
token_.indent_ = indent;
|
||||
if (IsEndLine()) {
|
||||
token_.kind_ = TokenKind::Section;
|
||||
#ifdef VERBOSE_TOKENIZER
|
||||
log_debug("Section " << StringRange(token_.keyStart_, token_.keyEnd_).str());
|
||||
#endif
|
||||
|
||||
Inc();
|
||||
} else {
|
||||
if (Current() == '"' || Current() == '\'') {
|
||||
auto delimiter = Current();
|
||||
|
||||
token_.kind_ = TokenKind::String;
|
||||
Inc();
|
||||
token_.sValueStart_ = current_;
|
||||
while (!Eof() && Current() != delimiter && !IsEndLine()) {
|
||||
Inc();
|
||||
}
|
||||
token_.sValueEnd_ = current_;
|
||||
if (Current() != delimiter) {
|
||||
ParseError("Could not find matching delimiter in string value.");
|
||||
}
|
||||
Inc();
|
||||
#ifdef VERBOSE_TOKENIZER
|
||||
log_debug("StringQ " << StringRange(token_.keyStart_, token_.keyEnd_).str() << " "
|
||||
<< StringRange(token_.sValueStart_, token_.sValueEnd_).str());
|
||||
#endif
|
||||
} else {
|
||||
token_.kind_ = TokenKind::String;
|
||||
token_.sValueStart_ = current_;
|
||||
while (!Eof() && !IsWhiteSpace() && !IsEndLine()) {
|
||||
Inc();
|
||||
}
|
||||
token_.sValueEnd_ = current_;
|
||||
#ifdef VERBOSE_TOKENIZER
|
||||
log_debug("String " << StringRange(token_.keyStart_, token_.keyEnd_).str() << " "
|
||||
<< StringRange(token_.sValueStart_, token_.sValueEnd_).str());
|
||||
#endif
|
||||
}
|
||||
// Skip more whitespaces
|
||||
while (!Eof() && IsWhiteSpace()) {
|
||||
Inc();
|
||||
}
|
||||
|
||||
// A comment after a key-value pair is allowed.
|
||||
if (Current() == '#') {
|
||||
Inc();
|
||||
while (!Eof() && !IsEndLine()) {
|
||||
Inc();
|
||||
}
|
||||
}
|
||||
|
||||
// Should be EOL or EOF at this point.
|
||||
if (!IsEndLine() && !Eof()) {
|
||||
ParseError("Expected line end after key/value pair.");
|
||||
}
|
||||
}
|
||||
break;
|
||||
} // switch
|
||||
} else { // Eof()
|
||||
token_.kind_ = TokenKind::Eof;
|
||||
token_.indent_ = 0;
|
||||
if (Eof()) {
|
||||
token_.state = TokenState::Eof;
|
||||
token_.indent_ = -1;
|
||||
return;
|
||||
}
|
||||
switch (Current()) {
|
||||
case '\t':
|
||||
// TODO FIXME: We can do tabs or spaces, not both. However, we *could* let the user decide.
|
||||
ParseError("Indentation through tabs is not allowed. Convert all tabs to spaces please.");
|
||||
break;
|
||||
|
||||
case '#': // Comment till end of line
|
||||
Inc();
|
||||
while (!Eof() && !IsEndLine()) {
|
||||
Inc();
|
||||
}
|
||||
return;
|
||||
|
||||
case '\r':
|
||||
Inc();
|
||||
if (!Eof() && Current() == '\n') {
|
||||
Inc();
|
||||
} // \r\n
|
||||
goto parseAgain;
|
||||
case '\n':
|
||||
// \n without a preceding \r
|
||||
Inc();
|
||||
goto parseAgain;
|
||||
|
||||
default:
|
||||
if (!IsAlpha()) {
|
||||
ParseError("Expected identifier.");
|
||||
}
|
||||
|
||||
token_.keyStart_ = current_;
|
||||
Inc();
|
||||
while (!Eof() && (IsAlpha() || IsDigit() || Current() == '_')) {
|
||||
Inc();
|
||||
}
|
||||
token_.keyEnd_ = current_;
|
||||
|
||||
// Skip whitespaces:
|
||||
while (IsWhiteSpace()) {
|
||||
Inc();
|
||||
}
|
||||
|
||||
if (Current() != ':') {
|
||||
ParseError("After a key or section name, we expect a colon character ':'.");
|
||||
}
|
||||
Inc();
|
||||
|
||||
// Skip whitespaces after the colon:
|
||||
while (IsWhiteSpace()) {
|
||||
Inc();
|
||||
}
|
||||
|
||||
// token_.indent_ = indent;
|
||||
if (IsEndLine()) {
|
||||
#ifdef VERBOSE_TOKENIZER
|
||||
log_debug("Section " << StringRange(token_.keyStart_, token_.keyEnd_).str());
|
||||
#endif
|
||||
|
||||
Inc();
|
||||
} else {
|
||||
if (Current() == '"' || Current() == '\'') {
|
||||
auto delimiter = Current();
|
||||
|
||||
Inc();
|
||||
token_.sValueStart_ = current_;
|
||||
while (!Eof() && Current() != delimiter && !IsEndLine()) {
|
||||
Inc();
|
||||
}
|
||||
token_.sValueEnd_ = current_;
|
||||
if (Current() != delimiter) {
|
||||
ParseError("Could not find matching delimiter in string value.");
|
||||
}
|
||||
Inc();
|
||||
#ifdef VERBOSE_TOKENIZER
|
||||
log_debug("StringQ " << StringRange(token_.keyStart_, token_.keyEnd_).str() << " "
|
||||
<< StringRange(token_.sValueStart_, token_.sValueEnd_).str());
|
||||
#endif
|
||||
} else {
|
||||
token_.sValueStart_ = current_;
|
||||
while (!IsWhiteSpace() && !IsEndLine()) {
|
||||
Inc();
|
||||
}
|
||||
token_.sValueEnd_ = current_;
|
||||
#ifdef VERBOSE_TOKENIZER
|
||||
log_debug("String " << StringRange(token_.keyStart_, token_.keyEnd_).str() << " "
|
||||
<< StringRange(token_.sValueStart_, token_.sValueEnd_).str());
|
||||
#endif
|
||||
}
|
||||
skipToEol();
|
||||
}
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TokenKind.h"
|
||||
#include "TokenState.h"
|
||||
#include "../Logging.h"
|
||||
|
||||
namespace Configuration {
|
||||
@@ -27,6 +27,8 @@ namespace Configuration {
|
||||
const char* current_;
|
||||
const char* end_;
|
||||
|
||||
void skipToEol();
|
||||
|
||||
inline void Inc() {
|
||||
if (current_ != end_) {
|
||||
++current_;
|
||||
@@ -42,18 +44,21 @@ namespace Configuration {
|
||||
inline bool IsSpace() { return Current() == ' '; }
|
||||
|
||||
inline bool IsWhiteSpace() {
|
||||
if (Eof()) {
|
||||
return false;
|
||||
}
|
||||
char c = Current();
|
||||
return c == ' ' || c == '\t' || c == '\f' || c == '\r';
|
||||
}
|
||||
|
||||
inline bool IsEndLine() { return Current() == '\n'; }
|
||||
inline bool IsEndLine() { return Eof() || Current() == '\n'; }
|
||||
|
||||
inline bool IsDigit() {
|
||||
char c = Current();
|
||||
return (c >= '0' && c <= '9');
|
||||
}
|
||||
|
||||
static inline char ToLower(char c) { return (c >= 'A' && c <= 'Z') ? (char)(c + 32) : c; }
|
||||
inline char ToLower(char c) { return (c >= 'A' && c <= 'Z') ? (char)(c + 32) : c; }
|
||||
|
||||
inline bool EqualsCaseInsensitive(const char* input) {
|
||||
const char* tmp = current_;
|
||||
@@ -67,32 +72,35 @@ namespace Configuration {
|
||||
return isSame;
|
||||
}
|
||||
|
||||
protected:
|
||||
public:
|
||||
const char* start_;
|
||||
|
||||
// Results:
|
||||
struct TokenData {
|
||||
// The initial value for indent is -1, so when ParserHandler::enterSection()
|
||||
// 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_(0), kind_(TokenKind::Eof), sValueStart_(nullptr), sValueEnd_(nullptr) {}
|
||||
keyStart_(nullptr), keyEnd_(nullptr), indent_(-1), state(TokenState::Bof), sValueStart_(nullptr), sValueEnd_(nullptr) {}
|
||||
|
||||
const char* keyStart_;
|
||||
const char* keyEnd_;
|
||||
int indent_;
|
||||
|
||||
TokenKind kind_;
|
||||
struct {
|
||||
const char* sValueStart_;
|
||||
const char* sValueEnd_;
|
||||
};
|
||||
TokenState state = TokenState::Bof;
|
||||
|
||||
const char* sValueStart_;
|
||||
const char* sValueEnd_;
|
||||
} token_;
|
||||
|
||||
void ParseError(const char* description) const;
|
||||
|
||||
inline bool Eof() const { return current_ == end_; }
|
||||
|
||||
void Tokenize();
|
||||
|
||||
public:
|
||||
Tokenizer(const char* start, const char* end);
|
||||
void Tokenize();
|
||||
|
||||
inline StringRange key() const { return StringRange(token_.keyStart_, token_.keyEnd_); }
|
||||
};
|
||||
}
|
||||
|
@@ -29,8 +29,8 @@ namespace Configuration {
|
||||
std::atomic_thread_fence(std::memory_order::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
void Validator::handleDetail(const char* name, Configurable* value) {
|
||||
void Validator::enterSection(const char* name, Configurable* value) {
|
||||
value->validate();
|
||||
value->handle(*this);
|
||||
value->group(*this);
|
||||
}
|
||||
}
|
||||
|
@@ -31,19 +31,19 @@ namespace Configuration {
|
||||
Validator& operator=(const Validator&) = delete;
|
||||
|
||||
protected:
|
||||
void handleDetail(const char* name, Configurable* value) override;
|
||||
void enterSection(const char* name, Configurable* value) override;
|
||||
bool matchesUninitialized(const char* name) override { return false; }
|
||||
HandlerType handlerType() override { return HandlerType::Validator; }
|
||||
|
||||
public:
|
||||
Validator();
|
||||
|
||||
void handle(const char* name, bool& value) override {}
|
||||
void handle(const char* name, int32_t& value, int32_t minValue, int32_t maxValue) override {}
|
||||
void handle(const char* name, float& value, float minValue, float maxValue) override {}
|
||||
void handle(const char* name, StringRange& value, int minLength, int maxLength) override {}
|
||||
void handle(const char* name, Pin& value) override {}
|
||||
void handle(const char* name, IPAddress& value) override {}
|
||||
void handle(const char* name, int& value, EnumItem* e) override {}
|
||||
void item(const char* name, bool& value) override {}
|
||||
void item(const char* name, int32_t& value, int32_t minValue, int32_t maxValue) override {}
|
||||
void item(const char* name, float& value, float minValue, float maxValue) override {}
|
||||
void item(const char* name, StringRange& value, int minLength, int maxLength) override {}
|
||||
void item(const char* name, Pin& value) override {}
|
||||
void item(const char* name, IPAddress& value) override {}
|
||||
void item(const char* name, int& value, EnumItem* e) override {}
|
||||
};
|
||||
}
|
||||
|
@@ -52,8 +52,8 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) {
|
||||
handler.handle("bck", _bck);
|
||||
void group(Configuration::HandlerBase& handler) {
|
||||
handler.item("bck", _bck);
|
||||
// ...
|
||||
}
|
||||
|
||||
|
@@ -41,17 +41,15 @@ void Control::init() {
|
||||
_macro3.init();
|
||||
}
|
||||
|
||||
void Control::validate() const {}
|
||||
|
||||
void Control::handle(Configuration::HandlerBase& handler) {
|
||||
handler.handle("safety_door", _safetyDoor._pin);
|
||||
handler.handle("reset", _reset._pin);
|
||||
handler.handle("feed_hold", _feedHold._pin);
|
||||
handler.handle("cycle_start", _cycleStart._pin);
|
||||
handler.handle("macro0", _macro0._pin);
|
||||
handler.handle("macro1", _macro1._pin);
|
||||
handler.handle("macro2", _macro2._pin);
|
||||
handler.handle("macro3", _macro3._pin);
|
||||
void Control::group(Configuration::HandlerBase& handler) {
|
||||
handler.item("safety_door", _safetyDoor._pin);
|
||||
handler.item("reset", _reset._pin);
|
||||
handler.item("feed_hold", _feedHold._pin);
|
||||
handler.item("cycle_start", _cycleStart._pin);
|
||||
handler.item("macro0", _macro0._pin);
|
||||
handler.item("macro1", _macro1._pin);
|
||||
handler.item("macro2", _macro2._pin);
|
||||
handler.item("macro3", _macro3._pin);
|
||||
}
|
||||
|
||||
void Control::report(char* status) {
|
||||
|
@@ -48,8 +48,7 @@ public:
|
||||
void init();
|
||||
|
||||
// Configuration handlers.
|
||||
void validate() const override;
|
||||
void handle(Configuration::HandlerBase& handler) override;
|
||||
void group(Configuration::HandlerBase& handler) override;
|
||||
|
||||
bool system_check_safety_door_ajar();
|
||||
void report(char* status);
|
||||
|
@@ -113,10 +113,8 @@ void CoolantControl::sync(CoolantState state) {
|
||||
set_state(state);
|
||||
}
|
||||
|
||||
void CoolantControl::validate() const {}
|
||||
|
||||
void CoolantControl::handle(Configuration::HandlerBase& handler) {
|
||||
handler.handle("flood", _flood);
|
||||
handler.handle("mist", _mist);
|
||||
handler.handle("delay", _delay);
|
||||
void CoolantControl::group(Configuration::HandlerBase& handler) {
|
||||
handler.item("flood", _flood);
|
||||
handler.item("mist", _mist);
|
||||
handler.item("delay", _delay);
|
||||
}
|
||||
|
@@ -58,8 +58,7 @@ public:
|
||||
void sync(CoolantState state);
|
||||
|
||||
// Configuration handlers.
|
||||
void validate() const override;
|
||||
void handle(Configuration::HandlerBase& handler) override;
|
||||
void group(Configuration::HandlerBase& handler) override;
|
||||
|
||||
~CoolantControl() = default;
|
||||
};
|
||||
|
@@ -26,6 +26,7 @@
|
||||
#include "Grbl.h"
|
||||
#include "Logging.h"
|
||||
|
||||
#include "Configuration/Parser.h"
|
||||
#include "Configuration/ParserHandler.h"
|
||||
#include "Configuration/Validator.h"
|
||||
#include "Configuration/AfterParse.h"
|
||||
@@ -50,17 +51,16 @@ void Endstops::validate() const {
|
||||
// }
|
||||
}
|
||||
|
||||
void Endstops::handle(Configuration::HandlerBase& handler) {
|
||||
// handler.handle("positive", _positive);
|
||||
// handler.handle("negative", _negative);
|
||||
handler.handle("dual", _dual);
|
||||
handler.handle("hard_limits", _hardLimits);
|
||||
void Endstops::group(Configuration::HandlerBase& handler) {
|
||||
// handler.item("positive", _positive);
|
||||
// handler.item("negative", _negative);
|
||||
handler.item("dual", _dual);
|
||||
handler.item("hard_limits", _hardLimits);
|
||||
}
|
||||
|
||||
void Gang::validate() const {}
|
||||
void Gang::handle(Configuration::HandlerBase& handler) {
|
||||
handler.handle("endstops", _endstops);
|
||||
Motors::MotorFactory::handle(handler, _motor);
|
||||
void Gang::group(Configuration::HandlerBase& handler) {
|
||||
handler.section("endstops", _endstops);
|
||||
Motors::MotorFactory::factory(handler, _motor);
|
||||
}
|
||||
void Gang::afterParse() {
|
||||
if (_motor == nullptr) {
|
||||
@@ -73,9 +73,7 @@ Gang::~Gang() {
|
||||
delete _endstops;
|
||||
}
|
||||
|
||||
void Axis::validate() const {}
|
||||
|
||||
void Axis::handle(Configuration::HandlerBase& handler) {
|
||||
void Axis::group(Configuration::HandlerBase& handler) {
|
||||
char tmp[6];
|
||||
tmp[0] = 0;
|
||||
strcat(tmp, "gang");
|
||||
@@ -84,7 +82,7 @@ void Axis::handle(Configuration::HandlerBase& handler) {
|
||||
tmp[4] = char(g + '0');
|
||||
tmp[5] = '\0';
|
||||
|
||||
handler.handle(tmp, _gangs[g]);
|
||||
handler.section(tmp, _gangs[g]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,11 +295,10 @@ size_t Axes::findAxisGanged(const Motors::Motor* const motor) const {
|
||||
}
|
||||
|
||||
// Configuration helpers:
|
||||
void Axes::validate() const {}
|
||||
|
||||
void Axes::handle(Configuration::HandlerBase& handler) {
|
||||
handler.handle("number_axis", _numberAxis);
|
||||
handler.handle("shared_stepper_disable", _sharedStepperDisable);
|
||||
void Axes::group(Configuration::HandlerBase& handler) {
|
||||
handler.item("number_axis", _numberAxis);
|
||||
handler.item("shared_stepper_disable", _sharedStepperDisable);
|
||||
|
||||
const char* allAxis = "xyzabc";
|
||||
|
||||
@@ -314,7 +311,7 @@ void Axes::handle(Configuration::HandlerBase& handler) {
|
||||
|
||||
if (handler.handlerType() == Configuration::HandlerType::Runtime || handler.handlerType() == Configuration::HandlerType::Parser ||
|
||||
handler.handlerType() == Configuration::HandlerType::AfterParse) {
|
||||
handler.handle(tmp, _axis[a]);
|
||||
handler.section(tmp, _axis[a]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -341,10 +338,10 @@ void I2SOBus::validate() const {
|
||||
}
|
||||
}
|
||||
|
||||
void I2SOBus::handle(Configuration::HandlerBase& handler) {
|
||||
handler.handle("bck", _bck);
|
||||
handler.handle("data", _data);
|
||||
handler.handle("ws", _ws);
|
||||
void I2SOBus::group(Configuration::HandlerBase& handler) {
|
||||
handler.item("bck", _bck);
|
||||
handler.item("data", _data);
|
||||
handler.item("ws", _ws);
|
||||
}
|
||||
|
||||
void SPIBus::validate() const {
|
||||
@@ -356,56 +353,52 @@ void SPIBus::validate() const {
|
||||
}
|
||||
}
|
||||
|
||||
void SPIBus::handle(Configuration::HandlerBase& handler) {
|
||||
handler.handle("ss", _ss);
|
||||
handler.handle("miso", _miso);
|
||||
handler.handle("mosi", _mosi);
|
||||
handler.handle("mosi", _sck);
|
||||
void SPIBus::group(Configuration::HandlerBase& handler) {
|
||||
handler.item("ss", _ss);
|
||||
handler.item("miso", _miso);
|
||||
handler.item("mosi", _mosi);
|
||||
handler.item("mosi", _sck);
|
||||
}
|
||||
|
||||
void UserOutputs::validate() const {}
|
||||
|
||||
void UserOutputs::handle(Configuration::HandlerBase& handler) {
|
||||
handler.handle("analog0", _analogOutput[0]);
|
||||
handler.handle("analog1", _analogOutput[1]);
|
||||
handler.handle("analog2", _analogOutput[2]);
|
||||
handler.handle("analog3", _analogOutput[3]);
|
||||
handler.handle("digital0", _digitalOutput[0]);
|
||||
handler.handle("digital1", _digitalOutput[1]);
|
||||
handler.handle("digital2", _digitalOutput[2]);
|
||||
handler.handle("digital3", _digitalOutput[3]);
|
||||
void UserOutputs::group(Configuration::HandlerBase& handler) {
|
||||
handler.item("analog0", _analogOutput[0]);
|
||||
handler.item("analog1", _analogOutput[1]);
|
||||
handler.item("analog2", _analogOutput[2]);
|
||||
handler.item("analog3", _analogOutput[3]);
|
||||
handler.item("digital0", _digitalOutput[0]);
|
||||
handler.item("digital1", _digitalOutput[1]);
|
||||
handler.item("digital2", _digitalOutput[2]);
|
||||
handler.item("digital3", _digitalOutput[3]);
|
||||
}
|
||||
|
||||
void MachineConfig::validate() const {}
|
||||
void MachineConfig::group(Configuration::HandlerBase& handler) {
|
||||
handler.item("board", _board);
|
||||
handler.item("name", _name);
|
||||
|
||||
void MachineConfig::handle(Configuration::HandlerBase& handler) {
|
||||
handler.handle("board", _board);
|
||||
handler.handle("name", _name);
|
||||
handler.section("axes", _axes);
|
||||
handler.section("i2so", _i2so);
|
||||
handler.section("spi", _spi);
|
||||
handler.section("control", _control);
|
||||
handler.section("coolant", _coolant);
|
||||
handler.section("probe", _probe);
|
||||
handler.section("comms", _comms);
|
||||
|
||||
handler.handle("axes", _axes);
|
||||
handler.handle("i2so", _i2so);
|
||||
handler.handle("spi", _spi);
|
||||
handler.handle("control", _control);
|
||||
handler.handle("coolant", _coolant);
|
||||
handler.handle("probe", _probe);
|
||||
handler.handle("comms", _comms);
|
||||
|
||||
handler.handle("pulse_microseconds", _pulseMicroSeconds);
|
||||
handler.handle("dir_delay_microseconds", _directionDelayMicroSeconds);
|
||||
handler.handle("disable_delay_us", _disableDelayMicroSeconds);
|
||||
handler.handle("idle_time", _idleTime);
|
||||
handler.handle("user_outputs", _userOutputs);
|
||||
handler.handle("sdcard", _sdCard);
|
||||
handler.handle("step_type", _stepType, stepTypes);
|
||||
handler.item("pulse_microseconds", _pulseMicroSeconds);
|
||||
handler.item("dir_delay_microseconds", _directionDelayMicroSeconds);
|
||||
handler.item("disable_delay_us", _disableDelayMicroSeconds);
|
||||
handler.item("idle_time", _idleTime);
|
||||
handler.section("user_outputs", _userOutputs);
|
||||
handler.section("sdcard", _sdCard);
|
||||
handler.item("step_type", _stepType, stepTypes);
|
||||
|
||||
// TODO: Consider putting these under a gcode: hierarchy level? Or motion control?
|
||||
handler.handle("laser_mode", _laserMode);
|
||||
handler.handle("arc_tolerance", _arcTolerance);
|
||||
handler.handle("junction_deviation", _junctionDeviation);
|
||||
handler.handle("verbose_errors", _verboseErrors);
|
||||
handler.handle("report_inches", _reportInches);
|
||||
handler.handle("homing_init_lock", _homingInitLock);
|
||||
Spindles::SpindleFactory::handle(handler, _spindle);
|
||||
handler.item("laser_mode", _laserMode);
|
||||
handler.item("arc_tolerance", _arcTolerance);
|
||||
handler.item("junction_deviation", _junctionDeviation);
|
||||
handler.item("verbose_errors", _verboseErrors);
|
||||
handler.item("report_inches", _reportInches);
|
||||
handler.item("homing_init_lock", _homingInitLock);
|
||||
Spindles::SpindleFactory::factory(handler, _spindle);
|
||||
}
|
||||
|
||||
void MachineConfig::afterParse() {
|
||||
@@ -506,17 +499,6 @@ bool MachineConfig::load(const char* filename) {
|
||||
|
||||
log_info("Heap size before load config is " << uint32_t(xPortGetFreeHeapSize()));
|
||||
|
||||
// instance() is by reference, so we can just get rid of an old instance and
|
||||
// create a new one here:
|
||||
{
|
||||
auto& machineConfig = instance();
|
||||
if (machineConfig != nullptr) {
|
||||
delete machineConfig;
|
||||
}
|
||||
machineConfig = new MachineConfig();
|
||||
}
|
||||
config = instance();
|
||||
|
||||
// If the system crashes we skip the config file and use the default
|
||||
// builtin config. This helps prevent reset loops on bad config files.
|
||||
size_t filesize = 0;
|
||||
@@ -544,10 +526,18 @@ bool MachineConfig::load(const char* filename) {
|
||||
Configuration::Parser parser(input->begin(), input->end());
|
||||
Configuration::ParserHandler handler(parser);
|
||||
|
||||
for (; !parser.isEndSection(); handler.moveNext()) {
|
||||
log_info("Parsing key " << parser.key().str());
|
||||
config->handle(handler);
|
||||
// instance() is by reference, so we can just get rid of an old instance and
|
||||
// create a new one here:
|
||||
{
|
||||
auto& machineConfig = instance();
|
||||
if (machineConfig != nullptr) {
|
||||
delete machineConfig;
|
||||
}
|
||||
machineConfig = new MachineConfig();
|
||||
}
|
||||
config = instance();
|
||||
|
||||
handler.enterSection("machine", config);
|
||||
|
||||
log_info("Parsed configuration. Running after-parse tasks");
|
||||
|
||||
@@ -556,7 +546,7 @@ bool MachineConfig::load(const char* filename) {
|
||||
try {
|
||||
Configuration::AfterParse afterParse;
|
||||
config->afterParse();
|
||||
config->handle(afterParse);
|
||||
config->group(afterParse);
|
||||
} catch (std::exception& ex) { log_info("Validation error: " << ex.what()); }
|
||||
|
||||
log_info("Validating configuration");
|
||||
@@ -566,7 +556,7 @@ bool MachineConfig::load(const char* filename) {
|
||||
try {
|
||||
Configuration::Validator validator;
|
||||
config->validate();
|
||||
config->handle(validator);
|
||||
config->group(validator);
|
||||
} catch (std::exception& ex) { log_info("Validation error: " << ex.what()); }
|
||||
|
||||
log_info("Validated configuration");
|
||||
|
@@ -49,7 +49,7 @@ public:
|
||||
|
||||
// Configuration system helpers:
|
||||
void validate() const override;
|
||||
void handle(Configuration::HandlerBase& handler) override;
|
||||
void group(Configuration::HandlerBase& handler) override;
|
||||
};
|
||||
|
||||
class Gang : public Configuration::Configurable {
|
||||
@@ -60,8 +60,7 @@ public:
|
||||
Endstops* _endstops = nullptr;
|
||||
|
||||
// Configuration system helpers:
|
||||
void validate() const override;
|
||||
void handle(Configuration::HandlerBase& handler) override;
|
||||
void group(Configuration::HandlerBase& handler) override;
|
||||
void afterParse() override;
|
||||
|
||||
~Gang();
|
||||
@@ -83,15 +82,15 @@ public:
|
||||
// Configuration system helpers:
|
||||
void validate() const override { Assert(_cycle >= 1, "Cycle has to be defined as >= 1 for homing sequence."); }
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) override {
|
||||
handler.handle("cycle", _cycle);
|
||||
handler.handle("positive_direction", _positiveDirection);
|
||||
handler.handle("mpos", _mpos);
|
||||
handler.handle("feed_rate", _feedRate);
|
||||
handler.handle("seek_rate", _seekRate);
|
||||
handler.handle("debounce", _debounce);
|
||||
handler.handle("pulloff", _pulloff);
|
||||
handler.handle("square", _square);
|
||||
void group(Configuration::HandlerBase& handler) override {
|
||||
handler.item("cycle", _cycle);
|
||||
handler.item("positive_direction", _positiveDirection);
|
||||
handler.item("mpos", _mpos);
|
||||
handler.item("feed_rate", _feedRate);
|
||||
handler.item("seek_rate", _seekRate);
|
||||
handler.item("debounce", _debounce);
|
||||
handler.item("pulloff", _pulloff);
|
||||
handler.item("square", _square);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -114,8 +113,7 @@ public:
|
||||
bool _softLimits = false;
|
||||
|
||||
// Configuration system helpers:
|
||||
void validate() const override;
|
||||
void handle(Configuration::HandlerBase& handler) override;
|
||||
void group(Configuration::HandlerBase& handler) override;
|
||||
void afterParse() override;
|
||||
|
||||
// Checks if a motor matches this axis:
|
||||
@@ -171,8 +169,7 @@ public:
|
||||
void unstep();
|
||||
|
||||
// Configuration helpers:
|
||||
void validate() const override;
|
||||
void handle(Configuration::HandlerBase& handler) override;
|
||||
void group(Configuration::HandlerBase& handler) override;
|
||||
void afterParse() override;
|
||||
|
||||
~Axes();
|
||||
@@ -187,7 +184,7 @@ public:
|
||||
Pin _ws;
|
||||
|
||||
void validate() const override;
|
||||
void handle(Configuration::HandlerBase& handler) override;
|
||||
void group(Configuration::HandlerBase& handler) override;
|
||||
|
||||
~I2SOBus() = default;
|
||||
};
|
||||
@@ -202,7 +199,7 @@ public:
|
||||
Pin _sck;
|
||||
|
||||
void validate() const override;
|
||||
void handle(Configuration::HandlerBase& handler) override;
|
||||
void group(Configuration::HandlerBase& handler) override;
|
||||
|
||||
~SPIBus() = default;
|
||||
};
|
||||
@@ -214,8 +211,7 @@ public:
|
||||
Pin _analogOutput[4];
|
||||
Pin _digitalOutput[4];
|
||||
|
||||
void validate() const override;
|
||||
void handle(Configuration::HandlerBase& handler) override;
|
||||
void group(Configuration::HandlerBase& handler) override;
|
||||
|
||||
~UserOutputs() = default;
|
||||
};
|
||||
@@ -235,17 +231,15 @@ public:
|
||||
|
||||
bool _dhcp = true;
|
||||
|
||||
void validate() const override {}
|
||||
void group(Configuration::HandlerBase& handler) override {
|
||||
handler.item("ssid", _ssid);
|
||||
// handler.item("password", _password);
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) override {
|
||||
handler.handle("ssid", _ssid);
|
||||
// handler.handle("password", _password);
|
||||
handler.item("ip_address", _ipAddress);
|
||||
handler.item("gateway", _gateway);
|
||||
handler.item("netmask", _netmask);
|
||||
|
||||
handler.handle("ip_address", _ipAddress);
|
||||
handler.handle("gateway", _gateway);
|
||||
handler.handle("netmask", _netmask);
|
||||
|
||||
handler.handle("dhcp", _dhcp);
|
||||
handler.item("dhcp", _dhcp);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -260,9 +254,9 @@ public:
|
||||
Assert(_channel >= 1 && _channel <= 16, "WIFI channel %d is out of bounds", _channel); // TODO: I guess?
|
||||
}
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) override {
|
||||
WifiConfig::handle(handler);
|
||||
handler.handle("channel", _channel);
|
||||
void group(Configuration::HandlerBase& handler) override {
|
||||
WifiConfig::group(handler);
|
||||
handler.item("channel", _channel);
|
||||
}
|
||||
|
||||
~WifiAPConfig() = default;
|
||||
@@ -274,7 +268,7 @@ public:
|
||||
|
||||
void validate() const override { WifiConfig::validate(); }
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) override { WifiConfig::handle(handler); }
|
||||
void group(Configuration::HandlerBase& handler) override { WifiConfig::group(handler); }
|
||||
|
||||
~WifiSTAConfig() = default;
|
||||
};
|
||||
@@ -300,23 +294,21 @@ public:
|
||||
WifiAPConfig* _apConfig = nullptr;
|
||||
WifiSTAConfig* _staConfig = nullptr;
|
||||
|
||||
void validate() const override {}
|
||||
void group(Configuration::HandlerBase& handler) override {
|
||||
// handler.item("user_password", _userPassword);
|
||||
// handler.item("admin_password", _adminPassword);
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) override {
|
||||
// handler.handle("user_password", _userPassword);
|
||||
// handler.handle("admin_password", _adminPassword);
|
||||
handler.item("telnet_enable", _telnetEnable);
|
||||
handler.item("telnet_port", _telnetPort);
|
||||
|
||||
handler.handle("telnet_enable", _telnetEnable);
|
||||
handler.handle("telnet_port", _telnetPort);
|
||||
handler.item("http_enable", _httpEnable);
|
||||
handler.item("http_port", _httpPort);
|
||||
|
||||
handler.handle("http_enable", _httpEnable);
|
||||
handler.handle("http_port", _httpPort);
|
||||
handler.item("hostname", _hostname);
|
||||
|
||||
handler.handle("hostname", _hostname);
|
||||
|
||||
handler.handle("bluetooth", _bluetoothConfig);
|
||||
handler.handle("wifi_ap", _apConfig);
|
||||
handler.handle("wifi_sta", _staConfig);
|
||||
handler.section("bluetooth", _bluetoothConfig);
|
||||
handler.section("wifi_ap", _apConfig);
|
||||
handler.section("wifi_sta", _staConfig);
|
||||
}
|
||||
|
||||
~Communications() {
|
||||
@@ -357,17 +349,18 @@ public:
|
||||
String _board = "None";
|
||||
String _name = "None";
|
||||
|
||||
#if 1
|
||||
static MachineConfig*& instance() {
|
||||
static MachineConfig* instance = nullptr;
|
||||
return instance;
|
||||
}
|
||||
#endif
|
||||
|
||||
void validate() const override;
|
||||
void afterParse() override;
|
||||
void handle(Configuration::HandlerBase& handler) override;
|
||||
void group(Configuration::HandlerBase& handler) override;
|
||||
|
||||
size_t readFile(const char* file, char*& buffer);
|
||||
bool load(const char* file = "/spiffs/config.yaml");
|
||||
static size_t readFile(const char* file, char*& buffer);
|
||||
static bool load(const char* file = "/spiffs/config.yaml");
|
||||
|
||||
~MachineConfig();
|
||||
};
|
||||
|
@@ -134,14 +134,14 @@ namespace Motors {
|
||||
Assert(_id != 255, "Dynamixel ID should be configured.");
|
||||
}
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) override {
|
||||
handler.handle("tx", _tx_pin);
|
||||
handler.handle("rx", _rx_pin);
|
||||
handler.handle("rts", _rts_pin);
|
||||
handler.handle("invert_direction", _invert_direction);
|
||||
void group(Configuration::HandlerBase& handler) override {
|
||||
handler.item("tx", _tx_pin);
|
||||
handler.item("rx", _rx_pin);
|
||||
handler.item("rts", _rts_pin);
|
||||
handler.item("invert_direction", _invert_direction);
|
||||
|
||||
int id = _id;
|
||||
handler.handle("id", id);
|
||||
handler.item("id", id);
|
||||
_id = id;
|
||||
}
|
||||
|
||||
|
@@ -10,8 +10,7 @@ namespace Motors {
|
||||
bool set_homing_mode(bool isHoming) { return false; }
|
||||
|
||||
// Configuration handlers:
|
||||
void validate() const override {}
|
||||
void handle(Configuration::HandlerBase& handler) override {}
|
||||
void group(Configuration::HandlerBase& handler) override {}
|
||||
|
||||
const char* name() const override { return "null_motor"; }
|
||||
};
|
||||
|
@@ -62,10 +62,10 @@ namespace Motors {
|
||||
// Configuration handlers:
|
||||
void validate() const override { Assert(!_pwm_pin.undefined(), "PWM pin should be configured."); }
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) override {
|
||||
handler.handle("pwm", _pwm_pin);
|
||||
handler.handle("cal_min", _cal_min);
|
||||
handler.handle("cal_max", _cal_max);
|
||||
void group(Configuration::HandlerBase& handler) override {
|
||||
handler.item("pwm", _pwm_pin);
|
||||
handler.item("cal_min", _cal_min);
|
||||
handler.item("cal_max", _cal_max);
|
||||
}
|
||||
|
||||
// Name of the configurable. Must match the name registered in the cpp file.
|
||||
|
@@ -35,10 +35,10 @@ namespace Motors {
|
||||
Assert(!_dir_pin.undefined(), "Direction pin should be configured.");
|
||||
}
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) override {
|
||||
handler.handle("step", _step_pin);
|
||||
handler.handle("direction", _dir_pin);
|
||||
handler.handle("disable", _disable_pin);
|
||||
void group(Configuration::HandlerBase& handler) override {
|
||||
handler.item("step", _step_pin);
|
||||
handler.item("direction", _dir_pin);
|
||||
handler.item("disable", _disable_pin);
|
||||
}
|
||||
|
||||
// Name of the configurable. Must match the name registered in the cpp file.
|
||||
|
@@ -13,13 +13,13 @@ namespace Motors {
|
||||
// Configuration handlers:
|
||||
void StepStick::validate() const { StandardStepper::validate(); }
|
||||
|
||||
void StepStick::handle(Configuration::HandlerBase& handler) {
|
||||
StandardStepper::handle(handler);
|
||||
void StepStick::group(Configuration::HandlerBase& handler) {
|
||||
StandardStepper::group(handler);
|
||||
|
||||
handler.handle("ms1", _MS1);
|
||||
handler.handle("ms2", _MS2);
|
||||
handler.handle("ms3", _MS3);
|
||||
handler.handle("reset", _Reset);
|
||||
handler.item("ms1", _MS1);
|
||||
handler.item("ms2", _MS2);
|
||||
handler.item("ms3", _MS3);
|
||||
handler.item("reset", _Reset);
|
||||
}
|
||||
|
||||
void StepStick::afterParse() {
|
||||
|
@@ -16,7 +16,7 @@ namespace Motors {
|
||||
|
||||
// Configuration handlers:
|
||||
void validate() const override;
|
||||
void handle(Configuration::HandlerBase& handler) override;
|
||||
void group(Configuration::HandlerBase& handler) override;
|
||||
|
||||
void afterParse() override;
|
||||
|
||||
|
@@ -129,16 +129,16 @@ namespace Motors {
|
||||
StandardStepper::validate();
|
||||
}
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) override {
|
||||
handler.handle("spi_cs", _cs_pin);
|
||||
handler.handle("r_sense", _r_sense);
|
||||
handler.handle("run_current", _run_current);
|
||||
handler.handle("hold_current", _hold_current);
|
||||
handler.handle("microsteps", _microsteps);
|
||||
handler.handle("stallguard", _stallguard);
|
||||
handler.handle("stallguardDebugMode", _stallguardDebugMode);
|
||||
void group(Configuration::HandlerBase& handler) override {
|
||||
handler.item("spi_cs", _cs_pin);
|
||||
handler.item("r_sense", _r_sense);
|
||||
handler.item("run_current", _run_current);
|
||||
handler.item("hold_current", _hold_current);
|
||||
handler.item("microsteps", _microsteps);
|
||||
handler.item("stallguard", _stallguard);
|
||||
handler.item("stallguardDebugMode", _stallguardDebugMode);
|
||||
|
||||
StandardStepper::handle(handler);
|
||||
StandardStepper::group(handler);
|
||||
}
|
||||
|
||||
// Name of the configurable. Must match the name registered in the cpp file.
|
||||
|
@@ -137,15 +137,15 @@ namespace Motors {
|
||||
// Configuration handlers:
|
||||
void validate() const override { StandardStepper::validate(); }
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) override {
|
||||
handler.handle("r_sense", _r_sense);
|
||||
handler.handle("run_current", _run_current);
|
||||
handler.handle("hold_current", _hold_current);
|
||||
handler.handle("microsteps", _microsteps);
|
||||
handler.handle("stallguard", _stallguard);
|
||||
handler.handle("stallguardDebugMode", _stallguardDebugMode);
|
||||
void group(Configuration::HandlerBase& handler) override {
|
||||
handler.item("r_sense", _r_sense);
|
||||
handler.item("run_current", _run_current);
|
||||
handler.item("hold_current", _hold_current);
|
||||
handler.item("microsteps", _microsteps);
|
||||
handler.item("stallguard", _stallguard);
|
||||
handler.item("stallguardDebugMode", _stallguardDebugMode);
|
||||
|
||||
StandardStepper::handle(handler);
|
||||
StandardStepper::group(handler);
|
||||
}
|
||||
|
||||
// Name of the configurable. Must match the name registered in the cpp file.
|
||||
|
@@ -22,12 +22,12 @@ namespace Motors {
|
||||
Assert(!_pin_phase3.undefined(), "Phase 3 pin should be configured.");
|
||||
}
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) override {
|
||||
handler.handle("phase0", _pin_phase0);
|
||||
handler.handle("phase1", _pin_phase1);
|
||||
handler.handle("phase2", _pin_phase2);
|
||||
handler.handle("phase3", _pin_phase3);
|
||||
handler.handle("half_step", _half_step);
|
||||
void group(Configuration::HandlerBase& handler) override {
|
||||
handler.item("phase0", _pin_phase0);
|
||||
handler.item("phase1", _pin_phase1);
|
||||
handler.item("phase2", _pin_phase2);
|
||||
handler.item("phase3", _pin_phase3);
|
||||
handler.item("half_step", _half_step);
|
||||
}
|
||||
|
||||
// Name of the configurable. Must match the name registered in the cpp file.
|
||||
|
@@ -57,6 +57,6 @@ bool IRAM_ATTR Probe::tripped() {
|
||||
|
||||
void Probe::validate() const {}
|
||||
|
||||
void Probe::handle(Configuration::HandlerBase& handler) {
|
||||
handler.handle("pin", _probePin);
|
||||
void Probe::group(Configuration::HandlerBase& handler) {
|
||||
handler.item("pin", _probePin);
|
||||
}
|
||||
|
@@ -58,7 +58,7 @@ public:
|
||||
|
||||
// Configuration handlers.
|
||||
void validate() const override;
|
||||
void handle(Configuration::HandlerBase& handler) override;
|
||||
void group(Configuration::HandlerBase& handler) override;
|
||||
|
||||
~Probe() = default;
|
||||
};
|
||||
|
@@ -78,9 +78,9 @@ public:
|
||||
// Configuration handlers.
|
||||
void validate() const override { Assert(!_cs_pin.undefined(), "spi_cs pin should be configured."); }
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) override {
|
||||
handler.handle("card_detect", _cardDetect);
|
||||
handler.handle("spi_cs", _cs_pin);
|
||||
void group(Configuration::HandlerBase& handler) override {
|
||||
handler.item("card_detect", _cardDetect);
|
||||
handler.item("spi_cs", _cs_pin);
|
||||
}
|
||||
|
||||
~SDCard();
|
||||
|
@@ -48,10 +48,10 @@ namespace Spindles {
|
||||
// Configuration handlers:
|
||||
void validate() const override { PWM::validate(); }
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) override {
|
||||
handler.handle("forward", _forward_pin);
|
||||
handler.handle("reverse", _reverse_pin);
|
||||
PWM::handle(handler);
|
||||
void group(Configuration::HandlerBase& handler) override {
|
||||
handler.item("forward", _forward_pin);
|
||||
handler.item("reverse", _reverse_pin);
|
||||
PWM::group(handler);
|
||||
}
|
||||
|
||||
// Name of the configurable. Must match the name registered in the cpp file.
|
||||
|
@@ -53,7 +53,7 @@ namespace Spindles {
|
||||
// Configuration handlers:
|
||||
void validate() const override { PWM::validate(); }
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) override { PWM::handle(handler); }
|
||||
void group(Configuration::HandlerBase& handler) override { PWM::group(handler); }
|
||||
|
||||
// Name of the configurable. Must match the name registered in the cpp file.
|
||||
const char* name() const override { return "BESC"; }
|
||||
|
@@ -44,7 +44,7 @@ namespace Spindles {
|
||||
// Configuration handlers:
|
||||
void validate() const override { PWM::validate(); }
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) override { PWM::handle(handler); }
|
||||
void group(Configuration::HandlerBase& handler) override { PWM::group(handler); }
|
||||
|
||||
// Name of the configurable. Must match the name registered in the cpp file.
|
||||
const char* name() const override { return "DAC"; }
|
||||
|
@@ -45,10 +45,10 @@ namespace Spindles {
|
||||
// Name of the configurable. Must match the name registered in the cpp file.
|
||||
const char* name() const override { return "Laser"; }
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) override {
|
||||
handler.handle("laser_full_power", _laser_full_power);
|
||||
void group(Configuration::HandlerBase& handler) override {
|
||||
handler.item("laser_full_power", _laser_full_power);
|
||||
|
||||
PWM::handle(handler);
|
||||
PWM::group(handler);
|
||||
}
|
||||
|
||||
virtual ~Laser() {}
|
||||
|
@@ -43,9 +43,7 @@ namespace Spindles {
|
||||
void config_message() override;
|
||||
|
||||
// Configuration handlers:
|
||||
void validate() const override {}
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) override {}
|
||||
void group(Configuration::HandlerBase& handler) override {}
|
||||
|
||||
// Name of the configurable. Must match the name registered in the cpp file.
|
||||
const char* name() const override { return "Null"; }
|
||||
|
@@ -49,21 +49,21 @@ namespace Spindles {
|
||||
// Configuration handlers:
|
||||
void validate() const override { Spindle::validate(); }
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) override {
|
||||
handler.handle("min_rpm", _min_rpm);
|
||||
handler.handle("max_rpm", _max_rpm);
|
||||
void group(Configuration::HandlerBase& handler) override {
|
||||
handler.item("min_rpm", _min_rpm);
|
||||
handler.item("max_rpm", _max_rpm);
|
||||
|
||||
handler.handle("pwm_freq", _pwm_freq);
|
||||
handler.handle("pwm_off", _pwm_off_setting);
|
||||
handler.handle("pwm_min", _pwm_min_setting);
|
||||
handler.handle("pwm_max", _pwm_max_setting);
|
||||
handler.handle("invert_pwm", _invert_pwm);
|
||||
handler.handle("output_pin", _output_pin);
|
||||
handler.handle("enable_pin", _enable_pin);
|
||||
handler.handle("direction_pin", _direction_pin);
|
||||
handler.handle("enable_off_with_zero_speed", _off_with_zero_speed);
|
||||
handler.item("pwm_freq", _pwm_freq);
|
||||
handler.item("pwm_off", _pwm_off_setting);
|
||||
handler.item("pwm_min", _pwm_min_setting);
|
||||
handler.item("pwm_max", _pwm_max_setting);
|
||||
handler.item("invert_pwm", _invert_pwm);
|
||||
handler.item("output_pin", _output_pin);
|
||||
handler.item("enable_pin", _enable_pin);
|
||||
handler.item("direction_pin", _direction_pin);
|
||||
handler.item("enable_off_with_zero_speed", _off_with_zero_speed);
|
||||
|
||||
Spindle::handle(handler);
|
||||
Spindle::group(handler);
|
||||
}
|
||||
|
||||
// Name of the configurable. Must match the name registered in the cpp file.
|
||||
|
@@ -78,9 +78,9 @@ namespace Spindles {
|
||||
// TODO: Validate spinup/spindown delay?
|
||||
}
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) override {
|
||||
handler.handle("spinup_delay_ms", _spinup_delay);
|
||||
handler.handle("spindown_delay_ms", _spindown_delay);
|
||||
void group(Configuration::HandlerBase& handler) override {
|
||||
handler.item("spinup_delay_ms", _spinup_delay);
|
||||
handler.item("spindown_delay_ms", _spindown_delay);
|
||||
}
|
||||
|
||||
// Virtual base classes require a virtual destructor.
|
||||
|
@@ -123,17 +123,17 @@ namespace Spindles {
|
||||
// Configuration handlers:
|
||||
void validate() const override { Spindle::validate(); }
|
||||
|
||||
void handle(Configuration::HandlerBase& handler) override {
|
||||
handler.handle("min_rpm", _min_rpm);
|
||||
handler.handle("max_rpm", _max_rpm);
|
||||
void group(Configuration::HandlerBase& handler) override {
|
||||
handler.item("min_rpm", _min_rpm);
|
||||
handler.item("max_rpm", _max_rpm);
|
||||
|
||||
handler.handle("txd_pin", _txd_pin);
|
||||
handler.handle("rxd_pin", _rxd_pin);
|
||||
handler.handle("rts_pin", _rts_pin);
|
||||
handler.item("txd_pin", _txd_pin);
|
||||
handler.item("rxd_pin", _rxd_pin);
|
||||
handler.item("rts_pin", _rts_pin);
|
||||
|
||||
// TODO FIXME: baud rate, etc
|
||||
|
||||
Spindle::handle(handler);
|
||||
Spindle::group(handler);
|
||||
}
|
||||
|
||||
virtual ~VFD() {}
|
||||
|
@@ -52,7 +52,7 @@ namespace WebUI {
|
||||
MIN_BTNAME_LENGTH,
|
||||
MAX_BTNAME_LENGTH);
|
||||
}
|
||||
void handle(Configuration::HandlerBase& handler) override { handler.handle("_name", _btname); }
|
||||
void group(Configuration::HandlerBase& handler) override { handler.item("_name", _btname); }
|
||||
|
||||
String info();
|
||||
bool isBTnameValid(const char* hostname);
|
||||
|
@@ -625,7 +625,7 @@ namespace WebUI {
|
||||
j.begin_array("EEPROM");
|
||||
|
||||
Configuration::JsonGenerator gen(j);
|
||||
config->handle(gen);
|
||||
config->group(gen);
|
||||
j.end_array();
|
||||
webPrint(j.end());
|
||||
return Error::Ok;
|
||||
|
@@ -15,10 +15,10 @@ namespace Configuration {
|
||||
String c;
|
||||
|
||||
void validate() const {}
|
||||
void handle(HandlerBase& handler) {
|
||||
handler.handle("a", a);
|
||||
handler.handle("b", b);
|
||||
handler.handle("c", c);
|
||||
void group(HandlerBase& handler) {
|
||||
handler.item("a", a);
|
||||
handler.item("b", b);
|
||||
handler.item("c", c);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -28,9 +28,9 @@ namespace Configuration {
|
||||
int banaan;
|
||||
|
||||
void validate() const {}
|
||||
void handle(HandlerBase& handler) {
|
||||
handler.handle("aap", aap);
|
||||
handler.handle("banaan", banaan);
|
||||
void group(HandlerBase& handler) {
|
||||
handler.item("aap", aap);
|
||||
handler.item("banaan", banaan);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -66,10 +66,10 @@ namespace Configuration {
|
||||
int foo = 0;
|
||||
|
||||
void validate() const {}
|
||||
void handle(HandlerBase& handler) {
|
||||
handler.handle("n1", n1);
|
||||
handler.handle("n2", n2);
|
||||
handler.handle("foo", foo);
|
||||
void group(HandlerBase& handler) {
|
||||
handler.item("n1", n1);
|
||||
handler.item("n2", n2);
|
||||
handler.item("foo", foo);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -79,9 +79,9 @@ namespace Configuration {
|
||||
Parser p(config, config + strlen(config));
|
||||
ParserHandler handler(p);
|
||||
|
||||
test.handle(handler);
|
||||
test.group(handler);
|
||||
for (; !p.isEndSection(); handler.moveNext()) {
|
||||
test.handle(handler);
|
||||
test.group(handler);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
Reference in New Issue
Block a user