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.
I moved work of preparing set_rpm and set_mode commands
out into the task so the ISR routines are as short as
possible. This makes it much easier to verify that
everything they use has IRAM_ATTR.
It is unclear whether or not the ESP-IDF functions
like timer_set_alarm_value() are interrupt safe.
The HAL timer functions all have IRAM_ATTR so
they are probably safe.