mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +00:00
Implement ? suffix to "quiet" a context and exclude it from backtraces (#1800)
This commit is contained in:
@@ -63,6 +63,16 @@ static std::string reptChain(FileStackNode const &node) {
|
||||
using TraceNode = std::pair<std::string, uint32_t>;
|
||||
|
||||
static std::vector<TraceNode> backtrace(FileStackNode const &node, uint32_t curLineNo) {
|
||||
if (node.isQuiet && !tracing.loud) {
|
||||
if (node.parent) {
|
||||
// Quiet REPT nodes will pass their interior line number up to their parent,
|
||||
// which is more precise than the parent's own line number (since that will be
|
||||
// the line number of the "REPT?" or "FOR?" itself).
|
||||
return backtrace(*node.parent, node.type == NODE_REPT ? curLineNo : node.lineNo);
|
||||
}
|
||||
return {}; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
if (!node.parent) {
|
||||
assume(node.type != NODE_REPT && std::holds_alternative<std::string>(node.data));
|
||||
return {
|
||||
@@ -244,14 +254,14 @@ static void checkRecursionDepth() {
|
||||
}
|
||||
}
|
||||
|
||||
static void newFileContext(std::string const &filePath, bool updateStateNow) {
|
||||
static void newFileContext(std::string const &filePath, bool isQuiet, bool updateStateNow) {
|
||||
checkRecursionDepth();
|
||||
|
||||
std::shared_ptr<std::string> uniqueIDStr = nullptr;
|
||||
std::shared_ptr<MacroArgs> macroArgs = nullptr;
|
||||
|
||||
auto fileInfo =
|
||||
std::make_shared<FileStackNode>(NODE_FILE, filePath == "-" ? "<stdin>" : filePath);
|
||||
std::make_shared<FileStackNode>(NODE_FILE, filePath == "-" ? "<stdin>" : filePath, isQuiet);
|
||||
if (!contextStack.empty()) {
|
||||
Context &oldContext = contextStack.top();
|
||||
fileInfo->parent = oldContext.fileInfo;
|
||||
@@ -269,7 +279,8 @@ static void newFileContext(std::string const &filePath, bool updateStateNow) {
|
||||
context.lexerState.setFileAsNextState(filePath, updateStateNow);
|
||||
}
|
||||
|
||||
static void newMacroContext(Symbol const ¯o, std::shared_ptr<MacroArgs> macroArgs) {
|
||||
static void
|
||||
newMacroContext(Symbol const ¯o, std::shared_ptr<MacroArgs> macroArgs, bool isQuiet) {
|
||||
checkRecursionDepth();
|
||||
|
||||
Context &oldContext = contextStack.top();
|
||||
@@ -287,7 +298,7 @@ static void newMacroContext(Symbol const ¯o, std::shared_ptr<MacroArgs> macr
|
||||
fileInfoName.append("::");
|
||||
fileInfoName.append(macro.name);
|
||||
|
||||
auto fileInfo = std::make_shared<FileStackNode>(NODE_MACRO, fileInfoName);
|
||||
auto fileInfo = std::make_shared<FileStackNode>(NODE_MACRO, fileInfoName, isQuiet);
|
||||
assume(!contextStack.empty()); // The top level context cannot be a MACRO
|
||||
fileInfo->parent = oldContext.fileInfo;
|
||||
fileInfo->lineNo = lexer_GetLineNo();
|
||||
@@ -301,7 +312,8 @@ static void newMacroContext(Symbol const ¯o, std::shared_ptr<MacroArgs> macr
|
||||
context.lexerState.setViewAsNextState("MACRO", macro.getMacro(), macro.fileLine);
|
||||
}
|
||||
|
||||
static Context &newReptContext(int32_t reptLineNo, ContentSpan const &span, uint32_t count) {
|
||||
static Context &
|
||||
newReptContext(int32_t reptLineNo, ContentSpan const &span, uint32_t count, bool isQuiet) {
|
||||
checkRecursionDepth();
|
||||
|
||||
Context &oldContext = contextStack.top();
|
||||
@@ -312,7 +324,7 @@ static Context &newReptContext(int32_t reptLineNo, ContentSpan const &span, uint
|
||||
fileInfoIters.insert(fileInfoIters.end(), RANGE(oldContext.fileInfo->iters()));
|
||||
}
|
||||
|
||||
auto fileInfo = std::make_shared<FileStackNode>(NODE_REPT, fileInfoIters);
|
||||
auto fileInfo = std::make_shared<FileStackNode>(NODE_REPT, fileInfoIters, isQuiet);
|
||||
assume(!contextStack.empty()); // The top level context cannot be a REPT
|
||||
fileInfo->parent = oldContext.fileInfo;
|
||||
fileInfo->lineNo = reptLineNo;
|
||||
@@ -356,15 +368,17 @@ bool fstk_FailedOnMissingInclude() {
|
||||
return failedOnMissingInclude;
|
||||
}
|
||||
|
||||
bool fstk_RunInclude(std::string const &path) {
|
||||
bool fstk_RunInclude(std::string const &path, bool isQuiet) {
|
||||
if (std::optional<std::string> fullPath = fstk_FindFile(path); fullPath) {
|
||||
newFileContext(*fullPath, false);
|
||||
newFileContext(*fullPath, isQuiet, false);
|
||||
return false;
|
||||
}
|
||||
return fstk_FileError(path, "INCLUDE");
|
||||
}
|
||||
|
||||
void fstk_RunMacro(std::string const ¯oName, std::shared_ptr<MacroArgs> macroArgs) {
|
||||
void fstk_RunMacro(
|
||||
std::string const ¯oName, std::shared_ptr<MacroArgs> macroArgs, bool isQuiet
|
||||
) {
|
||||
Symbol *macro = sym_FindExactSymbol(macroName);
|
||||
|
||||
if (!macro) {
|
||||
@@ -380,15 +394,15 @@ void fstk_RunMacro(std::string const ¯oName, std::shared_ptr<MacroArgs> macr
|
||||
return;
|
||||
}
|
||||
|
||||
newMacroContext(*macro, macroArgs);
|
||||
newMacroContext(*macro, macroArgs, isQuiet || macro->isQuiet);
|
||||
}
|
||||
|
||||
void fstk_RunRept(uint32_t count, int32_t reptLineNo, ContentSpan const &span) {
|
||||
void fstk_RunRept(uint32_t count, int32_t reptLineNo, ContentSpan const &span, bool isQuiet) {
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
newReptContext(reptLineNo, span, count);
|
||||
newReptContext(reptLineNo, span, count, isQuiet);
|
||||
}
|
||||
|
||||
void fstk_RunFor(
|
||||
@@ -397,7 +411,8 @@ void fstk_RunFor(
|
||||
int32_t stop,
|
||||
int32_t step,
|
||||
int32_t reptLineNo,
|
||||
ContentSpan const &span
|
||||
ContentSpan const &span,
|
||||
bool isQuiet
|
||||
) {
|
||||
if (Symbol *sym = sym_AddVar(symName, start); sym->type != SYM_VAR) {
|
||||
return;
|
||||
@@ -422,7 +437,7 @@ void fstk_RunFor(
|
||||
return;
|
||||
}
|
||||
|
||||
Context &context = newReptContext(reptLineNo, span, count);
|
||||
Context &context = newReptContext(reptLineNo, span, count, isQuiet);
|
||||
context.isForLoop = true;
|
||||
context.forValue = start;
|
||||
context.forStep = step;
|
||||
@@ -447,11 +462,11 @@ void fstk_NewRecursionDepth(size_t newDepth) {
|
||||
}
|
||||
|
||||
void fstk_Init(std::string const &mainPath) {
|
||||
newFileContext(mainPath, true);
|
||||
newFileContext(mainPath, false, true);
|
||||
|
||||
for (std::string const &name : preIncludeNames) {
|
||||
if (std::optional<std::string> fullPath = fstk_FindFile(name); fullPath) {
|
||||
newFileContext(*fullPath, false);
|
||||
newFileContext(*fullPath, false, false);
|
||||
} else {
|
||||
error("Error reading pre-included file \"%s\": %s", name.c_str(), strerror(errno));
|
||||
}
|
||||
|
||||
@@ -1561,9 +1561,8 @@ static bool isGarbageCharacter(int c) {
|
||||
if (isWhitespace(c)) {
|
||||
return false;
|
||||
}
|
||||
// Printable characters which are nevertheless garbage: braces should have been interpolated,
|
||||
// and question mark is unused
|
||||
if (c == '{' || c == '}' || c == '?') {
|
||||
// Printable characters which are nevertheless garbage: braces should have been interpolated
|
||||
if (c == '{' || c == '}') {
|
||||
return true;
|
||||
}
|
||||
// All other printable characters are not garbage (i.e. `yylex_NORMAL` handles them), and
|
||||
@@ -1614,6 +1613,9 @@ static Token yylex_NORMAL() {
|
||||
case '~':
|
||||
return Token(T_(OP_NOT));
|
||||
|
||||
case '?':
|
||||
return Token(T_(QUESTIONMARK));
|
||||
|
||||
case '@': {
|
||||
std::string symName("@");
|
||||
return Token(T_(SYMBOL), symName);
|
||||
@@ -1926,22 +1928,23 @@ static Token yylex_NORMAL() {
|
||||
}
|
||||
}
|
||||
|
||||
// We need to distinguish between label definitions (which start with `LABEL`) and
|
||||
// macro invocations (which start with `SYMBOL`).
|
||||
// We need to distinguish between:
|
||||
// - label definitions (which are followed by a ':' and use the token `LABEL`)
|
||||
// - quiet macro invocations (which are followed by a '?' and use the token `QMACRO`)
|
||||
// - regular macro invocations (which use the token `SYMBOL`)
|
||||
//
|
||||
// If we had one `IDENTIFIER` token, the parser would need to perform "lookahead" to
|
||||
// determine which rule applies. But since macros need to enter "raw" mode to parse
|
||||
// their arguments, which may not even be valid tokens in "normal" mode, we cannot use
|
||||
// lookahead to check for the presence of a `COLON`.
|
||||
// lookahead to check for the presence of a `COLON` or `QUESTIONMARK`.
|
||||
//
|
||||
// Instead, we have separate `SYMBOL` and `LABEL` tokens, lexing as a `LABEL` if a ':'
|
||||
// character *immediately* follows the identifier. Thus, "Label:" and "mac:" are treated
|
||||
// as label definitions, but "Label :" and "mac :" are treated as macro invocations.
|
||||
//
|
||||
// The alternative would be a "lexer hack" like C, where identifiers would lex as a
|
||||
// `SYMBOL` if they are already defined, otherwise as a `LABEL`.
|
||||
if (token.type == T_(SYMBOL) && peek() == ':') {
|
||||
token.type = T_(LABEL);
|
||||
// Instead, we have separate `SYMBOL`, `LABEL`, and `QMACRO` tokens, and decide which
|
||||
// one to lex depending on the character *immediately* following the identifier.
|
||||
// Thus "name:" is a label definition, and "name?" is a quiet macro invocation, but
|
||||
// "name :" and "name ?" and just "name" are all regular macro invocations.
|
||||
if (token.type == T_(SYMBOL)) {
|
||||
c = peek();
|
||||
token.type = c == ':' ? T_(LABEL) : c == '?' ? T_(QMACRO) : T_(SYMBOL);
|
||||
}
|
||||
|
||||
return token;
|
||||
|
||||
@@ -83,10 +83,10 @@ static void writeSection(Section const §, FILE *file) {
|
||||
|
||||
putLong(sect.size, file);
|
||||
|
||||
assume((sect.type & SECTTYPE_TYPE_MASK) == sect.type);
|
||||
bool isUnion = sect.modifier == SECTION_UNION;
|
||||
bool isFragment = sect.modifier == SECTION_FRAGMENT;
|
||||
|
||||
putc(sect.type | isUnion << 7 | isFragment << 6, file);
|
||||
putc(sect.type | isUnion << SECTTYPE_UNION_BIT | isFragment << SECTTYPE_FRAGMENT_BIT, file);
|
||||
|
||||
putLong(sect.org, file);
|
||||
putLong(sect.bank, file);
|
||||
@@ -268,7 +268,9 @@ static void writeAssert(Assertion const &assert, FILE *file) {
|
||||
static void writeFileStackNode(FileStackNode const &node, FILE *file) {
|
||||
putLong(node.parent ? node.parent->ID : UINT32_MAX, file);
|
||||
putLong(node.lineNo, file);
|
||||
putc(node.type, file);
|
||||
|
||||
putc(node.type | node.isQuiet << FSTACKNODE_QUIET_BIT, file);
|
||||
|
||||
if (node.type != NODE_REPT) {
|
||||
putString(node.name(), file);
|
||||
} else {
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
%token LBRACK "[" RBRACK "]"
|
||||
%token LBRACKS "[[" RBRACKS "]]"
|
||||
%token LPAREN "(" RPAREN ")"
|
||||
%token QUESTIONMARK "?"
|
||||
|
||||
// Arithmetic operators
|
||||
%token OP_ADD "+" OP_SUB "-"
|
||||
@@ -322,6 +323,7 @@
|
||||
%token <std::string> LABEL "label"
|
||||
%token <std::string> LOCAL "local label"
|
||||
%token <std::string> ANON "anonymous label"
|
||||
%token <std::string> QMACRO "quiet macro"
|
||||
|
||||
/******************** Data types ********************/
|
||||
|
||||
@@ -398,6 +400,7 @@
|
||||
%type <SectionType> sect_type
|
||||
%type <StrFmtArgList> strfmt_args
|
||||
%type <StrFmtArgList> strfmt_va_args
|
||||
%type <bool> maybe_quiet
|
||||
|
||||
%%
|
||||
|
||||
@@ -544,7 +547,13 @@ macro:
|
||||
// Parsing 'macro_args' will restore the lexer's normal mode
|
||||
lexer_SetMode(LEXER_RAW);
|
||||
} macro_args {
|
||||
fstk_RunMacro($1, $3);
|
||||
fstk_RunMacro($1, $3, false);
|
||||
}
|
||||
| QMACRO {
|
||||
// Parsing 'macro_args' will restore the lexer's normal mode
|
||||
lexer_SetMode(LEXER_RAW);
|
||||
} macro_args {
|
||||
fstk_RunMacro($1, $3, true);
|
||||
}
|
||||
;
|
||||
|
||||
@@ -780,10 +789,19 @@ load:
|
||||
}
|
||||
;
|
||||
|
||||
maybe_quiet:
|
||||
%empty {
|
||||
$$ = false;
|
||||
}
|
||||
| QUESTIONMARK {
|
||||
$$ = true;
|
||||
}
|
||||
;
|
||||
|
||||
rept:
|
||||
POP_REPT uconst NEWLINE capture_rept endofline {
|
||||
if ($4.span.ptr) {
|
||||
fstk_RunRept($2, $4.lineNo, $4.span);
|
||||
POP_REPT maybe_quiet uconst NEWLINE capture_rept endofline {
|
||||
if ($5.span.ptr) {
|
||||
fstk_RunRept($3, $5.lineNo, $5.span, $2);
|
||||
}
|
||||
}
|
||||
;
|
||||
@@ -791,11 +809,11 @@ rept:
|
||||
for:
|
||||
POP_FOR {
|
||||
lexer_ToggleStringExpansion(false);
|
||||
} SYMBOL {
|
||||
} maybe_quiet SYMBOL {
|
||||
lexer_ToggleStringExpansion(true);
|
||||
} COMMA for_args NEWLINE capture_rept endofline {
|
||||
if ($8.span.ptr) {
|
||||
fstk_RunFor($3, $6.start, $6.stop, $6.step, $8.lineNo, $8.span);
|
||||
if ($9.span.ptr) {
|
||||
fstk_RunFor($4, $7.start, $7.stop, $7.step, $9.lineNo, $9.span, $3);
|
||||
}
|
||||
}
|
||||
;
|
||||
@@ -835,11 +853,11 @@ break:
|
||||
def_macro:
|
||||
POP_MACRO {
|
||||
lexer_ToggleStringExpansion(false);
|
||||
} SYMBOL {
|
||||
} maybe_quiet SYMBOL {
|
||||
lexer_ToggleStringExpansion(true);
|
||||
} NEWLINE capture_macro endofline {
|
||||
if ($6.span.ptr) {
|
||||
sym_AddMacro($3, $6.lineNo, $6.span);
|
||||
if ($7.span.ptr) {
|
||||
sym_AddMacro($4, $7.lineNo, $7.span, $3);
|
||||
}
|
||||
}
|
||||
;
|
||||
@@ -1002,8 +1020,8 @@ export_def:
|
||||
;
|
||||
|
||||
include:
|
||||
label POP_INCLUDE string endofline {
|
||||
if (fstk_RunInclude($3)) {
|
||||
label POP_INCLUDE maybe_quiet string endofline {
|
||||
if (fstk_RunInclude($4, $3)) {
|
||||
YYACCEPT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,8 +205,9 @@ static Symbol &createSymbol(std::string const &symName) {
|
||||
Symbol &sym = symbols[symName];
|
||||
|
||||
sym.name = symName;
|
||||
sym.isExported = false;
|
||||
sym.isBuiltin = false;
|
||||
sym.isExported = false;
|
||||
sym.isQuiet = false;
|
||||
sym.section = nullptr;
|
||||
sym.src = fstk_GetFileStack();
|
||||
sym.fileLine = sym.src ? lexer_GetLineNo() : 0;
|
||||
@@ -616,7 +617,9 @@ void sym_Export(std::string const &symName) {
|
||||
sym->isExported = true;
|
||||
}
|
||||
|
||||
Symbol *sym_AddMacro(std::string const &symName, int32_t defLineNo, ContentSpan const &span) {
|
||||
Symbol *sym_AddMacro(
|
||||
std::string const &symName, int32_t defLineNo, ContentSpan const &span, bool isQuiet
|
||||
) {
|
||||
Symbol *sym = createNonrelocSymbol(symName, false);
|
||||
|
||||
if (!sym) {
|
||||
@@ -625,6 +628,7 @@ Symbol *sym_AddMacro(std::string const &symName, int32_t defLineNo, ContentSpan
|
||||
|
||||
sym->type = SYM_MACRO;
|
||||
sym->data = span;
|
||||
sym->isQuiet = isQuiet;
|
||||
|
||||
sym->src = fstk_GetFileStack();
|
||||
// The symbol is created at the line after the `ENDM`,
|
||||
|
||||
@@ -6,16 +6,24 @@
|
||||
|
||||
#include "platform.hpp" // strcasecmp
|
||||
|
||||
uint64_t traceDepth = 0;
|
||||
Tracing tracing;
|
||||
|
||||
bool trace_ParseTraceDepth(char const *arg) {
|
||||
if (!strcasecmp(arg, "collapse")) {
|
||||
traceDepth = TRACE_COLLAPSE;
|
||||
tracing.collapse = true;
|
||||
return true;
|
||||
} else if (!strcasecmp(arg, "no-collapse")) {
|
||||
tracing.collapse = false;
|
||||
return true;
|
||||
} else if (!strcasecmp(arg, "all")) {
|
||||
tracing.loud = true;
|
||||
return true;
|
||||
} else if (!strcasecmp(arg, "no-all")) {
|
||||
tracing.loud = false;
|
||||
return true;
|
||||
} else {
|
||||
char *endptr;
|
||||
tracing.depth = strtoul(arg, &endptr, 0);
|
||||
return arg[0] != '\0' && *endptr == '\0';
|
||||
}
|
||||
|
||||
char *endptr;
|
||||
traceDepth = strtoul(arg, &endptr, 0);
|
||||
|
||||
return arg[0] != '\0' && *endptr == '\0' && traceDepth != TRACE_COLLAPSE;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,16 @@
|
||||
using TraceNode = std::pair<std::string, uint32_t>;
|
||||
|
||||
static std::vector<TraceNode> backtrace(FileStackNode const &node, uint32_t curLineNo) {
|
||||
if (node.isQuiet && !tracing.loud) {
|
||||
if (node.parent) {
|
||||
// Quiet REPT nodes will pass their interior line number up to their parent,
|
||||
// which is more precise than the parent's own line number (since that will be
|
||||
// the line number of the "REPT?" or "FOR?" itself).
|
||||
return backtrace(*node.parent, node.type == NODE_REPT ? curLineNo : node.lineNo);
|
||||
}
|
||||
return {}; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
if (!node.parent) {
|
||||
assume(node.type != NODE_REPT && std::holds_alternative<std::string>(node.data));
|
||||
return {
|
||||
|
||||
@@ -105,14 +105,12 @@ static void readFileStackNode(
|
||||
tryReadLong(
|
||||
node.lineNo, file, "%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, nodeID
|
||||
);
|
||||
tryGetc(
|
||||
FileStackNodeType,
|
||||
node.type,
|
||||
file,
|
||||
"%s: Cannot read node #%" PRIu32 "'s type: %s",
|
||||
fileName,
|
||||
nodeID
|
||||
);
|
||||
|
||||
uint8_t type;
|
||||
tryGetc(uint8_t, type, file, "%s: Cannot read node #%" PRIu32 "'s type: %s", fileName, nodeID);
|
||||
node.type = static_cast<FileStackNodeType>(type & ~(1 << FSTACKNODE_QUIET_BIT));
|
||||
node.isQuiet = (type & (1 << FSTACKNODE_QUIET_BIT)) != 0;
|
||||
|
||||
switch (node.type) {
|
||||
case NODE_FILE:
|
||||
case NODE_MACRO:
|
||||
@@ -318,14 +316,14 @@ static void readSection(
|
||||
tryGetc(
|
||||
uint8_t, byte, file, "%s: Cannot read \"%s\"'s type: %s", fileName, section.name.c_str()
|
||||
);
|
||||
if (uint8_t type = byte & 0x3F; type >= SECTTYPE_INVALID) {
|
||||
if (uint8_t type = byte & SECTTYPE_TYPE_MASK; type >= SECTTYPE_INVALID) {
|
||||
fatal("\"%s\" has unknown section type 0x%02x", section.name.c_str(), type);
|
||||
} else {
|
||||
section.type = SectionType(type);
|
||||
}
|
||||
if (byte >> 7) {
|
||||
if (byte & (1 << SECTTYPE_UNION_BIT)) {
|
||||
section.modifier = SECTION_UNION;
|
||||
} else if (byte >> 6) {
|
||||
} else if (byte & (1 << SECTTYPE_FRAGMENT_BIT)) {
|
||||
section.modifier = SECTION_FRAGMENT;
|
||||
} else {
|
||||
section.modifier = SECTION_NORMAL;
|
||||
@@ -458,6 +456,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
|
||||
nodes[fileID].push_back({
|
||||
.type = NODE_FILE,
|
||||
.data = std::variant<std::monostate, std::vector<uint32_t>, std::string>(fileName),
|
||||
.isQuiet = false,
|
||||
.parent = nullptr,
|
||||
.lineNo = 0,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user