fix(tokenizer): harden move semantics and operator parsing
Signed-off-by: erick-alcachofa <erick@artichoke.dev> This commit addresses subtle state-management issues uncovered while exercising tokenizer move semantics and operator parsing. Moved-from instances were retaining stale iterators and metadata that could manifest as incorrect token offsets when the objects were reused. Additionally, token location bookkeeping during comment skipping and operator backtracking drifted from the iterator, producing inaccurate column reports. - Refines the move constructor by exchanging `line`, `column`, `iter`, `tokensGenerator`, `tokensBuffer`, and `source`, ensuring moved-from tokenizers reset to neutral defaults without dangling references. - Aligns the move assignment operator with the constructor by using `std::exchange` across all transferred members, preventing stale state when a tokenizer is reassigned. - Adjusts column advancement for single-line comments so the cursor increments by one when skipping the comments sequence, matching the iterator progression. - Promotes the operator trie to a function-local static cache, avoiding repeated construction each time an operator token is read. - Restores accurate token metadata by assigning the original line and column (`cLine`, `cColumn`) when emitting operator tokens and rolling back the column alongside the iterator during longest-valid backtracking.
This commit is contained in:
parent
d92f39538b
commit
146a48aef9
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <artichoke/Tokenizer/Tokenizer.hpp>
|
#include <artichoke/Tokenizer/Tokenizer.hpp>
|
||||||
|
|
||||||
|
#include <print>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include <artichoke/Tokenizer/TokenizerRange.hpp>
|
#include <artichoke/Tokenizer/TokenizerRange.hpp>
|
||||||
@ -41,20 +42,20 @@ namespace arti::lang {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Tokenizer::Tokenizer(Tokenizer &&rhs) noexcept
|
Tokenizer::Tokenizer(Tokenizer &&rhs) noexcept
|
||||||
: line(rhs.line)
|
: line(std::exchange(rhs.line, 0))
|
||||||
, column(rhs.column)
|
, column(std::exchange(rhs.column, 0))
|
||||||
, iter(rhs.iter)
|
, iter(std::exchange(rhs.iter, {}))
|
||||||
, tokensGenerator(std::exchange(rhs.tokensGenerator, {}))
|
, tokensGenerator(std::exchange(rhs.tokensGenerator, {}))
|
||||||
, tokensBuffer(std::exchange(rhs.tokensBuffer, {}))
|
, tokensBuffer(std::exchange(rhs.tokensBuffer, {}))
|
||||||
, source(std::exchange(rhs.source, "")) { }
|
, source(std::exchange(rhs.source, {})) { }
|
||||||
|
|
||||||
Tokenizer &Tokenizer::operator=(Tokenizer &&rhs) noexcept {
|
Tokenizer &Tokenizer::operator=(Tokenizer &&rhs) noexcept {
|
||||||
line = rhs.line;
|
line = std::exchange(rhs.line, 0);
|
||||||
column = rhs.column;
|
column = std::exchange(rhs.column, 0);
|
||||||
iter = std::move(rhs.iter);
|
iter = std::exchange(rhs.iter, {});
|
||||||
source = std::move(rhs.source);
|
source = std::exchange(rhs.source, {});
|
||||||
tokensBuffer = std::move(rhs.tokensBuffer);
|
tokensBuffer = std::exchange(rhs.tokensBuffer, {});
|
||||||
tokensGenerator = std::move(rhs.tokensGenerator);
|
tokensGenerator = std::exchange(rhs.tokensGenerator, {});
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,7 +283,7 @@ namespace arti::lang {
|
|||||||
}
|
}
|
||||||
else if (*(iter + 1) == '/') {
|
else if (*(iter + 1) == '/') {
|
||||||
iter += 2;
|
iter += 2;
|
||||||
column += 2;
|
column += 1;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -744,6 +745,8 @@ namespace arti::lang {
|
|||||||
TrieMap<TokenV> buildOperatorsTrieMap();
|
TrieMap<TokenV> buildOperatorsTrieMap();
|
||||||
|
|
||||||
Expected<Token> Tokenizer::readOperator() {
|
Expected<Token> Tokenizer::readOperator() {
|
||||||
|
static auto tm = buildOperatorsTrieMap();
|
||||||
|
|
||||||
auto stIter = iter;
|
auto stIter = iter;
|
||||||
|
|
||||||
auto cLine = line;
|
auto cLine = line;
|
||||||
@ -754,8 +757,6 @@ namespace arti::lang {
|
|||||||
++column;
|
++column;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto tm = buildOperatorsTrieMap();
|
|
||||||
|
|
||||||
if (not tm.root.childs.contains(*iter)) {
|
if (not tm.root.childs.contains(*iter)) {
|
||||||
return langException<ExceptCode::ecInvalidCharacter>(line, column, *iter);
|
return langException<ExceptCode::ecInvalidCharacter>(line, column, *iter);
|
||||||
}
|
}
|
||||||
@ -781,8 +782,8 @@ namespace arti::lang {
|
|||||||
|
|
||||||
Token tok{
|
Token tok{
|
||||||
TokenV::opStar,
|
TokenV::opStar,
|
||||||
line = cLine,
|
cLine,
|
||||||
column = cColumn,
|
cColumn,
|
||||||
{ stIter, iter }
|
{ stIter, iter }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -791,6 +792,7 @@ namespace arti::lang {
|
|||||||
}
|
}
|
||||||
else if (lvNode != nullptr) {
|
else if (lvNode != nullptr) {
|
||||||
iter -= lvNodeDiff;
|
iter -= lvNodeDiff;
|
||||||
|
column -= static_cast<size_t>(lvNodeDiff);
|
||||||
tok.value = *lvNode->value;
|
tok.value = *lvNode->value;
|
||||||
tok.strValue = std::string_view{ stIter, iter };
|
tok.strValue = std::string_view{ stIter, iter };
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user