Simplify toggling of enabling/disabling expansions

This commit is contained in:
Rangi42
2025-07-31 06:36:22 -04:00
parent 32b5ef5095
commit 6d53bc4121
2 changed files with 28 additions and 36 deletions

View File

@@ -92,9 +92,8 @@ struct LexerState {
size_t captureSize; // Amount of text captured size_t captureSize; // Amount of text captured
std::shared_ptr<std::vector<char>> captureBuf; // Buffer to send the captured text to if set std::shared_ptr<std::vector<char>> captureBuf; // Buffer to send the captured text to if set
bool disableMacroArgs; bool disableExpansions;
bool disableInterpolation; size_t expansionScanDistance; // Max distance already scanned for expansions
size_t macroArgScanDistance; // Max distance already scanned for macro args
bool expandStrings; bool expandStrings;
std::deque<Expansion> expansions; // Front is the innermost current expansion std::deque<Expansion> expansions; // Front is the innermost current expansion

View File

@@ -272,9 +272,8 @@ void LexerState::clear(uint32_t lineNo_) {
capturing = false; capturing = false;
captureBuf = nullptr; captureBuf = nullptr;
disableMacroArgs = false; disableExpansions = false;
disableInterpolation = false; expansionScanDistance = 0;
macroArgScanDistance = 0;
expandStrings = true; expandStrings = true;
expansions.clear(); expansions.clear();
@@ -517,13 +516,10 @@ static int nextChar();
static uint32_t readDecimalNumber(int initial); static uint32_t readDecimalNumber(int initial);
static uint32_t readBracketedMacroArgNum() { static uint32_t readBracketedMacroArgNum() {
bool disableMacroArgs = lexerState->disableMacroArgs; bool disableExpansions = lexerState->disableExpansions;
bool disableInterpolation = lexerState->disableInterpolation; lexerState->disableExpansions = false;
lexerState->disableMacroArgs = false;
lexerState->disableInterpolation = false;
Defer restoreExpansions{[&] { Defer restoreExpansions{[&] {
lexerState->disableMacroArgs = disableMacroArgs; lexerState->disableExpansions = disableExpansions;
lexerState->disableInterpolation = disableInterpolation;
}}; }};
int32_t num = 0; int32_t num = 0;
@@ -713,15 +709,17 @@ static std::shared_ptr<std::string> readInterpolation(size_t depth);
static int peek() { static int peek() {
int c = lexerState->peekChar(); int c = lexerState->peekChar();
if (lexerState->macroArgScanDistance > 0) { if (lexerState->expansionScanDistance > 0) {
return c; return c;
} }
++lexerState->macroArgScanDistance; // Do not consider again ++lexerState->expansionScanDistance; // Do not consider again
if (c == '\\' && !lexerState->disableMacroArgs) { if (lexerState->disableExpansions) {
return c;
} else if (c == '\\') {
// If character is a backslash, check for a macro arg // If character is a backslash, check for a macro arg
++lexerState->macroArgScanDistance; ++lexerState->expansionScanDistance;
if (!isMacroChar(lexerState->peekCharAhead())) { if (!isMacroChar(lexerState->peekCharAhead())) {
return c; return c;
} }
@@ -734,11 +732,11 @@ static int peek() {
// Mark the entire macro arg expansion as "painted blue" // Mark the entire macro arg expansion as "painted blue"
// so that macro args can't be recursive // so that macro args can't be recursive
// https://en.wikipedia.org/wiki/Painted_blue // https://en.wikipedia.org/wiki/Painted_blue
lexerState->macroArgScanDistance += str->length(); lexerState->expansionScanDistance += str->length();
} }
return peek(); // Tail recursion return peek(); // Tail recursion
} else if (c == '{' && !lexerState->disableInterpolation) { } else if (c == '{') {
// If character is an open brace, do symbol interpolation // If character is an open brace, do symbol interpolation
shiftChar(); shiftChar();
if (std::shared_ptr<std::string> str = readInterpolation(0); str) { if (std::shared_ptr<std::string> str = readInterpolation(0); str) {
@@ -759,7 +757,7 @@ static void shiftChar() {
++lexerState->captureSize; ++lexerState->captureSize;
} }
--lexerState->macroArgScanDistance; --lexerState->expansionScanDistance;
for (;;) { for (;;) {
if (!lexerState->expansions.empty()) { if (!lexerState->expansions.empty()) {
@@ -811,11 +809,9 @@ static void handleCRLF(int c) {
} }
static auto scopedDisableExpansions() { static auto scopedDisableExpansions() {
lexerState->disableMacroArgs = true; lexerState->disableExpansions = true;
lexerState->disableInterpolation = true;
return Defer{[&] { return Defer{[&] {
lexerState->disableMacroArgs = false; lexerState->disableExpansions = false;
lexerState->disableInterpolation = false;
}}; }};
} }
@@ -1217,21 +1213,19 @@ static std::shared_ptr<std::string> readInterpolation(size_t depth) {
std::string fmtBuf; std::string fmtBuf;
FormatSpec fmt{}; FormatSpec fmt{};
// In a context where `lexerState->disableInterpolation` is true, `peek` will expand for (;;) {
// nested interpolations itself, which can lead to stack overflow. This lets // If `lexerState->disableExpansions` is false, `peek()` will expand nested interpolations
// `readInterpolation` handle its own nested expansions, increasing `depth` each time. // and recursively call `readInterpolation()`, which can cause stack overflow.
bool disableInterpolation = lexerState->disableInterpolation; // `lexerState->peekChar()` lets `readInterpolation()` handle its own nested expansions,
lexerState->disableInterpolation = true; // increasing `depth` each time.
if (lexerState->peekChar() == '{') {
// Reset `lexerState->disableInterpolation` when exiting this loop ++lexerState->expansionScanDistance; // Prevent `shiftChar()` from calling `peek()`
for (Defer reset{[&] { lexerState->disableInterpolation = disableInterpolation; }};;) {
if (int c = peek(); c == '{') { // Nested interpolation
shiftChar(); shiftChar();
if (std::shared_ptr<std::string> str = readInterpolation(depth + 1); str) { if (std::shared_ptr<std::string> str = readInterpolation(depth + 1); str) {
beginExpansion(str, *str); beginExpansion(str, *str);
} }
continue; // Restart, reading from the new buffer continue; // Restart, reading from the new buffer
} else if (c == EOF || c == '\r' || c == '\n' || c == '"') { } else if (int c = peek(); c == EOF || c == '\r' || c == '\n' || c == '"') {
error("Missing }"); error("Missing }");
break; break;
} else if (c == '}') { } else if (c == '}') {
@@ -1329,12 +1323,11 @@ static void appendCharInLiteral(std::string &str, int c) {
// Symbol interpolation // Symbol interpolation
if (c == '{') { if (c == '{') {
// We'll be exiting the string/character scope, so re-enable expansions // We'll be exiting the string/character scope, so re-enable expansions
// (Not interpolations, since they're handled by the function itself...) lexerState->disableExpansions = false;
lexerState->disableMacroArgs = false;
if (std::shared_ptr<std::string> interpolation = readInterpolation(0); interpolation) { if (std::shared_ptr<std::string> interpolation = readInterpolation(0); interpolation) {
appendExpandedString(str, *interpolation); appendExpandedString(str, *interpolation);
} }
lexerState->disableMacroArgs = true; lexerState->disableExpansions = true;
return; return;
} }