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:
@@ -42,7 +42,7 @@ local args=(
|
|||||||
'(-v --verbose)'{-v,--verbose}'[Enable verbose output]'
|
'(-v --verbose)'{-v,--verbose}'[Enable verbose output]'
|
||||||
-w'[Disable all warnings]'
|
-w'[Disable all warnings]'
|
||||||
|
|
||||||
'(-B --backtrace)'{-B,--backtrace}'+[Set backtrace depth or style]:depth:'
|
'(-B --backtrace)'{-B,--backtrace}'+[Set backtrace depth or style]:param:'
|
||||||
'(-b --binary-digits)'{-b,--binary-digits}'+[Change chars for binary constants]:digit spec:'
|
'(-b --binary-digits)'{-b,--binary-digits}'+[Change chars for binary constants]:digit spec:'
|
||||||
--color'[Whether to use color in output]:color:(auto always never)'
|
--color'[Whether to use color in output]:color:(auto always never)'
|
||||||
'*'{-D,--define}'+[Define a string symbol]:name + value (default 1):'
|
'*'{-D,--define}'+[Define a string symbol]:name + value (default 1):'
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ local args=(
|
|||||||
'(-w --wramx)'{-w,--wramx}'[Disable WRAM banking]'
|
'(-w --wramx)'{-w,--wramx}'[Disable WRAM banking]'
|
||||||
'(-x --nopad)'{-x,--nopad}'[Disable padding the end of the final file]'
|
'(-x --nopad)'{-x,--nopad}'[Disable padding the end of the final file]'
|
||||||
|
|
||||||
'(-B --backtrace)'{-B,--backtrace}'+[Set backtrace depth or style]:depth:'
|
'(-B --backtrace)'{-B,--backtrace}'+[Set backtrace depth or style]:param:'
|
||||||
--color'[Whether to use color in output]:color:(auto always never)'
|
--color'[Whether to use color in output]:color:(auto always never)'
|
||||||
'(-l --linkerscript)'{-l,--linkerscript}"+[Use a linker script]:linker script:_files -g '*.link'"
|
'(-l --linkerscript)'{-l,--linkerscript}"+[Use a linker script]:linker script:_files -g '*.link'"
|
||||||
'(-M --no-sym-in-map)'{-M,--no-sym-in-map}'[Do not output symbol names in map file]'
|
'(-M --no-sym-in-map)'{-M,--no-sym-in-map}'[Do not output symbol names in map file]'
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ struct FileStackNode {
|
|||||||
std::string // NODE_FILE, NODE_MACRO
|
std::string // NODE_FILE, NODE_MACRO
|
||||||
>
|
>
|
||||||
data;
|
data;
|
||||||
|
bool isQuiet; // Whether to omit this node from error reporting
|
||||||
|
|
||||||
std::shared_ptr<FileStackNode> parent; // Pointer to parent node, for error reporting
|
std::shared_ptr<FileStackNode> parent; // Pointer to parent node, for error reporting
|
||||||
// Line at which the parent context was exited
|
// Line at which the parent context was exited
|
||||||
@@ -40,8 +41,12 @@ struct FileStackNode {
|
|||||||
std::string &name() { return std::get<std::string>(data); }
|
std::string &name() { return std::get<std::string>(data); }
|
||||||
std::string const &name() const { return std::get<std::string>(data); }
|
std::string const &name() const { return std::get<std::string>(data); }
|
||||||
|
|
||||||
FileStackNode(FileStackNodeType type_, std::variant<std::vector<uint32_t>, std::string> data_)
|
FileStackNode(
|
||||||
: type(type_), data(data_) {}
|
FileStackNodeType type_,
|
||||||
|
std::variant<std::vector<uint32_t>, std::string> data_,
|
||||||
|
bool isQuiet_
|
||||||
|
)
|
||||||
|
: type(type_), data(data_), isQuiet(isQuiet_) {}
|
||||||
|
|
||||||
void printBacktrace(uint32_t curLineNo) const;
|
void printBacktrace(uint32_t curLineNo) const;
|
||||||
};
|
};
|
||||||
@@ -62,16 +67,19 @@ bool fstk_FileError(std::string const &path, char const *functionName);
|
|||||||
bool fstk_FailedOnMissingInclude();
|
bool fstk_FailedOnMissingInclude();
|
||||||
|
|
||||||
bool yywrap();
|
bool yywrap();
|
||||||
bool fstk_RunInclude(std::string const &path);
|
bool fstk_RunInclude(std::string const &path, bool isQuiet);
|
||||||
void fstk_RunMacro(std::string const ¯oName, std::shared_ptr<MacroArgs> macroArgs);
|
void fstk_RunMacro(
|
||||||
void fstk_RunRept(uint32_t count, int32_t reptLineNo, ContentSpan const &span);
|
std::string const ¯oName, std::shared_ptr<MacroArgs> macroArgs, bool isQuiet
|
||||||
|
);
|
||||||
|
void fstk_RunRept(uint32_t count, int32_t reptLineNo, ContentSpan const &span, bool isQuiet);
|
||||||
void fstk_RunFor(
|
void fstk_RunFor(
|
||||||
std::string const &symName,
|
std::string const &symName,
|
||||||
int32_t start,
|
int32_t start,
|
||||||
int32_t stop,
|
int32_t stop,
|
||||||
int32_t step,
|
int32_t step,
|
||||||
int32_t reptLineNo,
|
int32_t reptLineNo,
|
||||||
ContentSpan const &span
|
ContentSpan const &span,
|
||||||
|
bool isQuiet
|
||||||
);
|
);
|
||||||
bool fstk_Break();
|
bool fstk_Break();
|
||||||
|
|
||||||
|
|||||||
@@ -30,8 +30,9 @@ bool sym_IsPC(Symbol const *sym); // Forward declaration for `getSection`
|
|||||||
struct Symbol {
|
struct Symbol {
|
||||||
std::string name;
|
std::string name;
|
||||||
SymbolType type;
|
SymbolType type;
|
||||||
bool isExported; // Whether the symbol is to be exported
|
bool isBuiltin;
|
||||||
bool isBuiltin; // Whether the symbol is a built-in
|
bool isExported; // Not relevant for SYM_MACRO or SYM_EQUS
|
||||||
|
bool isQuiet; // Only relevant for SYM_MACRO
|
||||||
Section *section;
|
Section *section;
|
||||||
std::shared_ptr<FileStackNode> src; // Where the symbol was defined
|
std::shared_ptr<FileStackNode> src; // Where the symbol was defined
|
||||||
uint32_t fileLine; // Line where the symbol was defined
|
uint32_t fileLine; // Line where the symbol was defined
|
||||||
@@ -88,7 +89,9 @@ Symbol *sym_FindScopedSymbol(std::string const &symName);
|
|||||||
// Find a scoped symbol by name; do not return `@` or `_NARG` when they have no value
|
// Find a scoped symbol by name; do not return `@` or `_NARG` when they have no value
|
||||||
Symbol *sym_FindScopedValidSymbol(std::string const &symName);
|
Symbol *sym_FindScopedValidSymbol(std::string const &symName);
|
||||||
Symbol const *sym_GetPC();
|
Symbol const *sym_GetPC();
|
||||||
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_Ref(std::string const &symName);
|
Symbol *sym_Ref(std::string const &symName);
|
||||||
Symbol *sym_AddString(std::string const &symName, std::shared_ptr<std::string> value);
|
Symbol *sym_AddString(std::string const &symName, std::shared_ptr<std::string> value);
|
||||||
Symbol *sym_RedefString(std::string const &symName, std::shared_ptr<std::string> value);
|
Symbol *sym_RedefString(std::string const &symName, std::shared_ptr<std::string> value);
|
||||||
|
|||||||
@@ -11,58 +11,74 @@
|
|||||||
|
|
||||||
#include "style.hpp"
|
#include "style.hpp"
|
||||||
|
|
||||||
static constexpr uint64_t TRACE_COLLAPSE = UINT64_MAX;
|
struct Tracing {
|
||||||
|
uint64_t depth = 0;
|
||||||
|
bool collapse = false;
|
||||||
|
bool loud = false;
|
||||||
|
};
|
||||||
|
|
||||||
extern uint64_t traceDepth;
|
extern Tracing tracing;
|
||||||
|
|
||||||
bool trace_ParseTraceDepth(char const *arg);
|
bool trace_ParseTraceDepth(char const *arg);
|
||||||
|
|
||||||
template<typename T, typename M, typename N>
|
template<typename T, typename M, typename N>
|
||||||
void trace_PrintBacktrace(std::vector<T> const &stack, M getName, N getLineNo) {
|
void trace_PrintBacktrace(std::vector<T> const &stack, M getName, N getLineNo) {
|
||||||
auto printLocation = [&](T const &item) {
|
size_t n = stack.size();
|
||||||
|
if (n == 0) {
|
||||||
|
return; // LCOV_EXCL_LINE
|
||||||
|
}
|
||||||
|
|
||||||
|
auto printLocation = [&](size_t i) {
|
||||||
|
T const &item = stack[n - i - 1];
|
||||||
|
style_Reset(stderr);
|
||||||
|
if (!tracing.collapse) {
|
||||||
|
fputs(" ", stderr); // Just three spaces; the fourth will be printed next
|
||||||
|
}
|
||||||
|
fprintf(stderr, " %s ", i == 0 ? "at" : "<-");
|
||||||
style_Set(stderr, STYLE_CYAN, true);
|
style_Set(stderr, STYLE_CYAN, true);
|
||||||
fputs(getName(item), stderr);
|
fputs(getName(item), stderr);
|
||||||
style_Set(stderr, STYLE_CYAN, false);
|
style_Set(stderr, STYLE_CYAN, false);
|
||||||
fprintf(stderr, "(%" PRIu32 ")", getLineNo(item));
|
fprintf(stderr, "(%" PRIu32 ")", getLineNo(item));
|
||||||
|
if (!tracing.collapse) {
|
||||||
|
putc('\n', stderr);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t n = stack.size();
|
if (tracing.collapse) {
|
||||||
|
|
||||||
if (traceDepth == TRACE_COLLAPSE) {
|
|
||||||
fputs(" ", stderr); // Just three spaces; the fourth will be handled by the loop
|
fputs(" ", stderr); // Just three spaces; the fourth will be handled by the loop
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tracing.depth == 0 || static_cast<size_t>(tracing.depth) >= n) {
|
||||||
for (size_t i = 0; i < n; ++i) {
|
for (size_t i = 0; i < n; ++i) {
|
||||||
style_Reset(stderr);
|
printLocation(i);
|
||||||
fprintf(stderr, " %s ", i == 0 ? "at" : "<-");
|
|
||||||
printLocation(stack[n - i - 1]);
|
|
||||||
}
|
|
||||||
putc('\n', stderr);
|
|
||||||
} else if (traceDepth == 0 || static_cast<size_t>(traceDepth) >= n) {
|
|
||||||
for (size_t i = 0; i < n; ++i) {
|
|
||||||
style_Reset(stderr);
|
|
||||||
fprintf(stderr, " %s ", i == 0 ? "at" : "<-");
|
|
||||||
printLocation(stack[n - i - 1]);
|
|
||||||
putc('\n', stderr);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
size_t last = traceDepth / 2;
|
size_t last = tracing.depth / 2;
|
||||||
size_t first = traceDepth - last;
|
size_t first = tracing.depth - last;
|
||||||
size_t skipped = n - traceDepth;
|
size_t skipped = n - tracing.depth;
|
||||||
for (size_t i = 0; i < first; ++i) {
|
for (size_t i = 0; i < first; ++i) {
|
||||||
style_Reset(stderr);
|
printLocation(i);
|
||||||
fprintf(stderr, " %s ", i == 0 ? "at" : "<-");
|
|
||||||
printLocation(stack[n - i - 1]);
|
|
||||||
putc('\n', stderr);
|
|
||||||
}
|
}
|
||||||
style_Reset(stderr);
|
style_Reset(stderr);
|
||||||
fprintf(stderr, " ...%zu more%s\n", skipped, last ? "..." : "");
|
|
||||||
for (size_t i = n - last; i < n; ++i) {
|
if (tracing.collapse) {
|
||||||
style_Reset(stderr);
|
fputs(" <-", stderr);
|
||||||
fputs(" <- ", stderr);
|
} else {
|
||||||
printLocation(stack[n - i - 1]);
|
fputs(" ", stderr); // Just three spaces; the fourth will be printed next
|
||||||
|
}
|
||||||
|
fprintf(stderr, " ...%zu more%s", skipped, last ? "..." : "");
|
||||||
|
if (!tracing.collapse) {
|
||||||
putc('\n', stderr);
|
putc('\n', stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (size_t i = n - last; i < n; ++i) {
|
||||||
|
printLocation(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tracing.collapse) {
|
||||||
|
putc('\n', stderr);
|
||||||
|
}
|
||||||
style_Reset(stderr);
|
style_Reset(stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ struct FileStackNode {
|
|||||||
std::string // NODE_FILE, NODE_MACRO
|
std::string // NODE_FILE, NODE_MACRO
|
||||||
>
|
>
|
||||||
data;
|
data;
|
||||||
|
bool isQuiet; // Whether to omit this node from error reporting
|
||||||
|
|
||||||
FileStackNode *parent;
|
FileStackNode *parent;
|
||||||
// Line at which the parent context was exited; meaningless for the root level
|
// Line at which the parent context was exited; meaningless for the root level
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#include "helpers.hpp" // assume
|
#include "helpers.hpp" // assume
|
||||||
|
|
||||||
#define RGBDS_OBJECT_VERSION_STRING "RGB9"
|
#define RGBDS_OBJECT_VERSION_STRING "RGB9"
|
||||||
#define RGBDS_OBJECT_REV 12U
|
#define RGBDS_OBJECT_REV 13U
|
||||||
|
|
||||||
enum AssertionType { ASSERT_WARN, ASSERT_ERROR, ASSERT_FATAL };
|
enum AssertionType { ASSERT_WARN, ASSERT_ERROR, ASSERT_FATAL };
|
||||||
|
|
||||||
@@ -78,12 +78,18 @@ enum SectionType {
|
|||||||
SECTTYPE_INVALID
|
SECTTYPE_INVALID
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static constexpr uint8_t SECTTYPE_TYPE_MASK = 0b111;
|
||||||
|
static constexpr uint8_t SECTTYPE_UNION_BIT = 7;
|
||||||
|
static constexpr uint8_t SECTTYPE_FRAGMENT_BIT = 6;
|
||||||
|
|
||||||
enum FileStackNodeType {
|
enum FileStackNodeType {
|
||||||
NODE_REPT,
|
NODE_REPT,
|
||||||
NODE_FILE,
|
NODE_FILE,
|
||||||
NODE_MACRO,
|
NODE_MACRO,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static constexpr uint8_t FSTACKNODE_QUIET_BIT = 7;
|
||||||
|
|
||||||
// Nont-`const` members may be patched in RGBLINK depending on CLI flags
|
// Nont-`const` members may be patched in RGBLINK depending on CLI flags
|
||||||
extern struct SectionTypeInfo {
|
extern struct SectionTypeInfo {
|
||||||
std::string const name;
|
std::string const name;
|
||||||
|
|||||||
34
man/rgbasm.1
34
man/rgbasm.1
@@ -9,7 +9,7 @@
|
|||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm
|
.Nm
|
||||||
.Op Fl EhVvw
|
.Op Fl EhVvw
|
||||||
.Op Fl B Ar depth
|
.Op Fl B Ar param
|
||||||
.Op Fl b Ar chars
|
.Op Fl b Ar chars
|
||||||
.Op Fl \-color Ar when
|
.Op Fl \-color Ar when
|
||||||
.Op Fl D Ar name Ns Op = Ns Ar value
|
.Op Fl D Ar name Ns Op = Ns Ar value
|
||||||
@@ -53,15 +53,29 @@ is invalid because it could also be
|
|||||||
.Fl \-version .
|
.Fl \-version .
|
||||||
The arguments are as follows:
|
The arguments are as follows:
|
||||||
.Bl -tag -width Ds
|
.Bl -tag -width Ds
|
||||||
.It Fl B Ar depth , Fl \-backtrace Ar depth
|
.It Fl B Ar param , Fl \-backtrace Ar param
|
||||||
Specifies the maximum depth for which
|
Configures how location backtraces are printed if warnings or errors occur.
|
||||||
.Nm
|
This flag may be specified multiple times with different parameters that combine meaningfully.
|
||||||
will print location backtraces for warnings or errors.
|
If
|
||||||
Deeper backtraces than that will be abbreviated.
|
.Ar param
|
||||||
.Fl B Ar 0
|
is a positive number, it specifies the maximum backtrace depth, abbreviating deeper ones.
|
||||||
allows unlimited-depth backtraces.
|
Other valid parameter values are the following:
|
||||||
.Fl B Ar collapse
|
.Bl -tag -width Ds
|
||||||
will print the entire location trace on one line.
|
.It Cm 0
|
||||||
|
Do not limit the maximum backtrace depth; this is the default.
|
||||||
|
.It Cm all
|
||||||
|
Force all locations to be printed, even "quiet" ones (see
|
||||||
|
.Dq Excluding locations from backtraces
|
||||||
|
in
|
||||||
|
.Xr rgbasm 5
|
||||||
|
for details).
|
||||||
|
.It Cm no-all
|
||||||
|
Do not print "quieted" locations in backtraces; this is the default.
|
||||||
|
.It Cm collapse
|
||||||
|
Print all locations on one line.
|
||||||
|
.It Cm no-collapse
|
||||||
|
Print one location per line; this is the default.
|
||||||
|
.El
|
||||||
.It Fl b Ar chars , Fl \-binary-digits Ar chars
|
.It Fl b Ar chars , Fl \-binary-digits Ar chars
|
||||||
Allow two characters to be used for binary constants in addition to the default
|
Allow two characters to be used for binary constants in addition to the default
|
||||||
.Sq 0
|
.Sq 0
|
||||||
|
|||||||
65
man/rgbasm.5
65
man/rgbasm.5
@@ -1681,7 +1681,8 @@ $ rgbasm -o a.o a.asm
|
|||||||
$ rgbasm -o b.o b.asm
|
$ rgbasm -o b.o b.asm
|
||||||
$ rgbasm -o c.o c.asm
|
$ rgbasm -o c.o c.asm
|
||||||
$ rgblink a.o b.o c.o
|
$ rgblink a.o b.o c.o
|
||||||
error: c.asm(2): Unknown symbol "LabelA"
|
error: Undefined symbol "LabelA"
|
||||||
|
at c.asm(2)
|
||||||
Linking failed with 1 error
|
Linking failed with 1 error
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
@@ -2516,6 +2517,68 @@ PUSHO b.X, g.oOX
|
|||||||
DW `..ooOOXX
|
DW `..ooOOXX
|
||||||
POPO
|
POPO
|
||||||
.Ed
|
.Ed
|
||||||
|
.Ss Excluding locations from backtraces
|
||||||
|
Errors and warnings print
|
||||||
|
.Em backtraces
|
||||||
|
showing the location in the source file where the problem occurred, tracing the origin of the problem even through a chain of
|
||||||
|
.Ic REPT ,
|
||||||
|
.Ic FOR ,
|
||||||
|
.Ic MACRO ,
|
||||||
|
and
|
||||||
|
.Ic INCLUDE
|
||||||
|
locations.
|
||||||
|
Sometimes there are locations you would like to ignore; for example, a common utility macro when you only care about the line where the macro is used, or an
|
||||||
|
.Ic INCLUDE
|
||||||
|
file that only serves to include other files and is just filler in the backtrace.
|
||||||
|
.Pp
|
||||||
|
In those cases, you can
|
||||||
|
.Em silence
|
||||||
|
a location with a question mark
|
||||||
|
.Sq \&?
|
||||||
|
after the token: all of the locations created by a
|
||||||
|
.Sq REPT? ,
|
||||||
|
.Sq FOR? ,
|
||||||
|
or
|
||||||
|
.Sq MACRO?
|
||||||
|
will not be printed, and any location created by a
|
||||||
|
.Sq INCLUDE? ,
|
||||||
|
or a macro invocation whose name is immediately followed by a
|
||||||
|
.Sq \&? ,
|
||||||
|
will not be printed.
|
||||||
|
For example, if this were assembled as
|
||||||
|
.Ql example.asm :
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
MACRO lb
|
||||||
|
assert -128 <= (\e2) && (\e2) < 256, "\e2 is not a byte"
|
||||||
|
assert -128 <= (\e3) && (\e3) < 256, "\e3 is not a byte"
|
||||||
|
ld \e1, (LOW(\e2) << 8) | LOW(\e3)
|
||||||
|
ENDM
|
||||||
|
SECTION "Code", ROM0
|
||||||
|
lb hl, $123, $45
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
This would print an error backtrace:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
error: Assertion failed: $123 is not a byte
|
||||||
|
at example.asm::lb(2)
|
||||||
|
<- example.asm(7)
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
But if
|
||||||
|
.Ql MACRO
|
||||||
|
were changed to
|
||||||
|
.Ql MACRO? ,
|
||||||
|
or
|
||||||
|
.Ql lb hl
|
||||||
|
were changed to
|
||||||
|
.Ql lb? hl ,
|
||||||
|
then the error backtrace would not mention the location within the
|
||||||
|
.Ql lb
|
||||||
|
macro:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
error: Assertion failed: $123 is not a byte
|
||||||
|
at example.asm(7)
|
||||||
|
.Ed
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr rgbasm 1 ,
|
.Xr rgbasm 1 ,
|
||||||
.Xr rgblink 1 ,
|
.Xr rgblink 1 ,
|
||||||
|
|||||||
@@ -79,12 +79,16 @@ order, meaning the node with ID 0 is the last one in the list!
|
|||||||
.It Cm LONG Ar ParentLineNo
|
.It Cm LONG Ar ParentLineNo
|
||||||
Line at which the parent node's context was exited; meaningless for the root node.
|
Line at which the parent node's context was exited; meaningless for the root node.
|
||||||
.It Cm BYTE Ar Type
|
.It Cm BYTE Ar Type
|
||||||
|
Bits 0\(en6 indicate the node's type:
|
||||||
.Bl -column "Value" -compact
|
.Bl -column "Value" -compact
|
||||||
.It Sy Value Ta Sy Meaning
|
.It Sy Value Ta Sy Meaning
|
||||||
.It 0 Ta REPT node
|
.It 0 Ta REPT node
|
||||||
.It 1 Ta File node
|
.It 1 Ta File node
|
||||||
.It 2 Ta Macro node
|
.It 2 Ta Macro node
|
||||||
.El
|
.El
|
||||||
|
.Pp
|
||||||
|
Bit\ 7 being set means that the node is "quieted"
|
||||||
|
.Pq see Do Excluding locations from backtraces Dc in Xr rgbasm 5 .
|
||||||
.It Cm IF Ar Type No \(!= 0
|
.It Cm IF Ar Type No \(!= 0
|
||||||
If the node is not a REPT node...
|
If the node is not a REPT node...
|
||||||
.Pp
|
.Pp
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm
|
.Nm
|
||||||
.Op Fl dhMtVvwx
|
.Op Fl dhMtVvwx
|
||||||
.Op Fl B Ar depth
|
.Op Fl B Ar param
|
||||||
.Op Fl \-color Ar when
|
.Op Fl \-color Ar when
|
||||||
.Op Fl l Ar linker_script
|
.Op Fl l Ar linker_script
|
||||||
.Op Fl m Ar map_file
|
.Op Fl m Ar map_file
|
||||||
@@ -65,15 +65,29 @@ is invalid because it could also be
|
|||||||
.Fl \-version .
|
.Fl \-version .
|
||||||
The arguments are as follows:
|
The arguments are as follows:
|
||||||
.Bl -tag -width Ds
|
.Bl -tag -width Ds
|
||||||
.It Fl B Ar depth , Fl \-backtrace Ar depth
|
.It Fl B Ar param , Fl \-backtrace Ar param
|
||||||
Specifies the maximum depth for which
|
Configures how location backtraces are printed if warnings or errors occur.
|
||||||
.Nm
|
This flag may be specified multiple times with different parameters that combine meaningfully.
|
||||||
will print location backtraces for warnings or errors.
|
If
|
||||||
Deeper backtraces than that will be abbreviated.
|
.Ar param
|
||||||
.Fl B Ar 0
|
is a positive number, it specifies the maximum backtrace depth, abbreviating deeper ones.
|
||||||
allows unlimited-depth backtraces.
|
Other valid parameter values are the following:
|
||||||
.Fl B Ar collapse
|
.Bl -tag -width Ds
|
||||||
will print the entire location trace on one line.
|
.It Cm 0
|
||||||
|
Do not limit the maximum backtrace depth; this is the default.
|
||||||
|
.It Cm all
|
||||||
|
Force all locations to be printed, even "quiet" ones (see
|
||||||
|
.Dq Excluding locations from backtraces
|
||||||
|
in
|
||||||
|
.Xr rgbasm 5
|
||||||
|
for details).
|
||||||
|
.It Cm no-all
|
||||||
|
Do not print "quieted" locations in backtraces; this is the default.
|
||||||
|
.It Cm collapse
|
||||||
|
Print all locations on one line.
|
||||||
|
.It Cm no-collapse
|
||||||
|
Print one location per line; this is the default.
|
||||||
|
.El
|
||||||
.It Fl \-color Ar when
|
.It Fl \-color Ar when
|
||||||
Specify when to highlight warning and error messages with color:
|
Specify when to highlight warning and error messages with color:
|
||||||
.Ql always ,
|
.Ql always ,
|
||||||
|
|||||||
@@ -63,6 +63,16 @@ static std::string reptChain(FileStackNode const &node) {
|
|||||||
using TraceNode = std::pair<std::string, uint32_t>;
|
using TraceNode = std::pair<std::string, uint32_t>;
|
||||||
|
|
||||||
static std::vector<TraceNode> backtrace(FileStackNode const &node, uint32_t curLineNo) {
|
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) {
|
if (!node.parent) {
|
||||||
assume(node.type != NODE_REPT && std::holds_alternative<std::string>(node.data));
|
assume(node.type != NODE_REPT && std::holds_alternative<std::string>(node.data));
|
||||||
return {
|
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();
|
checkRecursionDepth();
|
||||||
|
|
||||||
std::shared_ptr<std::string> uniqueIDStr = nullptr;
|
std::shared_ptr<std::string> uniqueIDStr = nullptr;
|
||||||
std::shared_ptr<MacroArgs> macroArgs = nullptr;
|
std::shared_ptr<MacroArgs> macroArgs = nullptr;
|
||||||
|
|
||||||
auto fileInfo =
|
auto fileInfo =
|
||||||
std::make_shared<FileStackNode>(NODE_FILE, filePath == "-" ? "<stdin>" : filePath);
|
std::make_shared<FileStackNode>(NODE_FILE, filePath == "-" ? "<stdin>" : filePath, isQuiet);
|
||||||
if (!contextStack.empty()) {
|
if (!contextStack.empty()) {
|
||||||
Context &oldContext = contextStack.top();
|
Context &oldContext = contextStack.top();
|
||||||
fileInfo->parent = oldContext.fileInfo;
|
fileInfo->parent = oldContext.fileInfo;
|
||||||
@@ -269,7 +279,8 @@ static void newFileContext(std::string const &filePath, bool updateStateNow) {
|
|||||||
context.lexerState.setFileAsNextState(filePath, 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();
|
checkRecursionDepth();
|
||||||
|
|
||||||
Context &oldContext = contextStack.top();
|
Context &oldContext = contextStack.top();
|
||||||
@@ -287,7 +298,7 @@ static void newMacroContext(Symbol const ¯o, std::shared_ptr<MacroArgs> macr
|
|||||||
fileInfoName.append("::");
|
fileInfoName.append("::");
|
||||||
fileInfoName.append(macro.name);
|
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
|
assume(!contextStack.empty()); // The top level context cannot be a MACRO
|
||||||
fileInfo->parent = oldContext.fileInfo;
|
fileInfo->parent = oldContext.fileInfo;
|
||||||
fileInfo->lineNo = lexer_GetLineNo();
|
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);
|
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();
|
checkRecursionDepth();
|
||||||
|
|
||||||
Context &oldContext = contextStack.top();
|
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()));
|
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
|
assume(!contextStack.empty()); // The top level context cannot be a REPT
|
||||||
fileInfo->parent = oldContext.fileInfo;
|
fileInfo->parent = oldContext.fileInfo;
|
||||||
fileInfo->lineNo = reptLineNo;
|
fileInfo->lineNo = reptLineNo;
|
||||||
@@ -356,15 +368,17 @@ bool fstk_FailedOnMissingInclude() {
|
|||||||
return 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) {
|
if (std::optional<std::string> fullPath = fstk_FindFile(path); fullPath) {
|
||||||
newFileContext(*fullPath, false);
|
newFileContext(*fullPath, isQuiet, false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return fstk_FileError(path, "INCLUDE");
|
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);
|
Symbol *macro = sym_FindExactSymbol(macroName);
|
||||||
|
|
||||||
if (!macro) {
|
if (!macro) {
|
||||||
@@ -380,15 +394,15 @@ void fstk_RunMacro(std::string const ¯oName, std::shared_ptr<MacroArgs> macr
|
|||||||
return;
|
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) {
|
if (count == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
newReptContext(reptLineNo, span, count);
|
newReptContext(reptLineNo, span, count, isQuiet);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_RunFor(
|
void fstk_RunFor(
|
||||||
@@ -397,7 +411,8 @@ void fstk_RunFor(
|
|||||||
int32_t stop,
|
int32_t stop,
|
||||||
int32_t step,
|
int32_t step,
|
||||||
int32_t reptLineNo,
|
int32_t reptLineNo,
|
||||||
ContentSpan const &span
|
ContentSpan const &span,
|
||||||
|
bool isQuiet
|
||||||
) {
|
) {
|
||||||
if (Symbol *sym = sym_AddVar(symName, start); sym->type != SYM_VAR) {
|
if (Symbol *sym = sym_AddVar(symName, start); sym->type != SYM_VAR) {
|
||||||
return;
|
return;
|
||||||
@@ -422,7 +437,7 @@ void fstk_RunFor(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Context &context = newReptContext(reptLineNo, span, count);
|
Context &context = newReptContext(reptLineNo, span, count, isQuiet);
|
||||||
context.isForLoop = true;
|
context.isForLoop = true;
|
||||||
context.forValue = start;
|
context.forValue = start;
|
||||||
context.forStep = step;
|
context.forStep = step;
|
||||||
@@ -447,11 +462,11 @@ void fstk_NewRecursionDepth(size_t newDepth) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void fstk_Init(std::string const &mainPath) {
|
void fstk_Init(std::string const &mainPath) {
|
||||||
newFileContext(mainPath, true);
|
newFileContext(mainPath, false, true);
|
||||||
|
|
||||||
for (std::string const &name : preIncludeNames) {
|
for (std::string const &name : preIncludeNames) {
|
||||||
if (std::optional<std::string> fullPath = fstk_FindFile(name); fullPath) {
|
if (std::optional<std::string> fullPath = fstk_FindFile(name); fullPath) {
|
||||||
newFileContext(*fullPath, false);
|
newFileContext(*fullPath, false, false);
|
||||||
} else {
|
} else {
|
||||||
error("Error reading pre-included file \"%s\": %s", name.c_str(), strerror(errno));
|
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)) {
|
if (isWhitespace(c)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Printable characters which are nevertheless garbage: braces should have been interpolated,
|
// Printable characters which are nevertheless garbage: braces should have been interpolated
|
||||||
// and question mark is unused
|
if (c == '{' || c == '}') {
|
||||||
if (c == '{' || c == '}' || c == '?') {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// All other printable characters are not garbage (i.e. `yylex_NORMAL` handles them), and
|
// All other printable characters are not garbage (i.e. `yylex_NORMAL` handles them), and
|
||||||
@@ -1614,6 +1613,9 @@ static Token yylex_NORMAL() {
|
|||||||
case '~':
|
case '~':
|
||||||
return Token(T_(OP_NOT));
|
return Token(T_(OP_NOT));
|
||||||
|
|
||||||
|
case '?':
|
||||||
|
return Token(T_(QUESTIONMARK));
|
||||||
|
|
||||||
case '@': {
|
case '@': {
|
||||||
std::string symName("@");
|
std::string symName("@");
|
||||||
return Token(T_(SYMBOL), 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
|
// We need to distinguish between:
|
||||||
// macro invocations (which start with `SYMBOL`).
|
// - 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
|
// 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
|
// 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
|
// 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 ':'
|
// Instead, we have separate `SYMBOL`, `LABEL`, and `QMACRO` tokens, and decide which
|
||||||
// character *immediately* follows the identifier. Thus, "Label:" and "mac:" are treated
|
// one to lex depending on the character *immediately* following the identifier.
|
||||||
// as label definitions, but "Label :" and "mac :" are treated as macro invocations.
|
// 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.
|
||||||
// The alternative would be a "lexer hack" like C, where identifiers would lex as a
|
if (token.type == T_(SYMBOL)) {
|
||||||
// `SYMBOL` if they are already defined, otherwise as a `LABEL`.
|
c = peek();
|
||||||
if (token.type == T_(SYMBOL) && peek() == ':') {
|
token.type = c == ':' ? T_(LABEL) : c == '?' ? T_(QMACRO) : T_(SYMBOL);
|
||||||
token.type = T_(LABEL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
|
|||||||
@@ -83,10 +83,10 @@ static void writeSection(Section const §, FILE *file) {
|
|||||||
|
|
||||||
putLong(sect.size, file);
|
putLong(sect.size, file);
|
||||||
|
|
||||||
|
assume((sect.type & SECTTYPE_TYPE_MASK) == sect.type);
|
||||||
bool isUnion = sect.modifier == SECTION_UNION;
|
bool isUnion = sect.modifier == SECTION_UNION;
|
||||||
bool isFragment = sect.modifier == SECTION_FRAGMENT;
|
bool isFragment = sect.modifier == SECTION_FRAGMENT;
|
||||||
|
putc(sect.type | isUnion << SECTTYPE_UNION_BIT | isFragment << SECTTYPE_FRAGMENT_BIT, file);
|
||||||
putc(sect.type | isUnion << 7 | isFragment << 6, file);
|
|
||||||
|
|
||||||
putLong(sect.org, file);
|
putLong(sect.org, file);
|
||||||
putLong(sect.bank, 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) {
|
static void writeFileStackNode(FileStackNode const &node, FILE *file) {
|
||||||
putLong(node.parent ? node.parent->ID : UINT32_MAX, file);
|
putLong(node.parent ? node.parent->ID : UINT32_MAX, file);
|
||||||
putLong(node.lineNo, file);
|
putLong(node.lineNo, file);
|
||||||
putc(node.type, file);
|
|
||||||
|
putc(node.type | node.isQuiet << FSTACKNODE_QUIET_BIT, file);
|
||||||
|
|
||||||
if (node.type != NODE_REPT) {
|
if (node.type != NODE_REPT) {
|
||||||
putString(node.name(), file);
|
putString(node.name(), file);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -98,6 +98,7 @@
|
|||||||
%token LBRACK "[" RBRACK "]"
|
%token LBRACK "[" RBRACK "]"
|
||||||
%token LBRACKS "[[" RBRACKS "]]"
|
%token LBRACKS "[[" RBRACKS "]]"
|
||||||
%token LPAREN "(" RPAREN ")"
|
%token LPAREN "(" RPAREN ")"
|
||||||
|
%token QUESTIONMARK "?"
|
||||||
|
|
||||||
// Arithmetic operators
|
// Arithmetic operators
|
||||||
%token OP_ADD "+" OP_SUB "-"
|
%token OP_ADD "+" OP_SUB "-"
|
||||||
@@ -322,6 +323,7 @@
|
|||||||
%token <std::string> LABEL "label"
|
%token <std::string> LABEL "label"
|
||||||
%token <std::string> LOCAL "local label"
|
%token <std::string> LOCAL "local label"
|
||||||
%token <std::string> ANON "anonymous label"
|
%token <std::string> ANON "anonymous label"
|
||||||
|
%token <std::string> QMACRO "quiet macro"
|
||||||
|
|
||||||
/******************** Data types ********************/
|
/******************** Data types ********************/
|
||||||
|
|
||||||
@@ -398,6 +400,7 @@
|
|||||||
%type <SectionType> sect_type
|
%type <SectionType> sect_type
|
||||||
%type <StrFmtArgList> strfmt_args
|
%type <StrFmtArgList> strfmt_args
|
||||||
%type <StrFmtArgList> strfmt_va_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
|
// Parsing 'macro_args' will restore the lexer's normal mode
|
||||||
lexer_SetMode(LEXER_RAW);
|
lexer_SetMode(LEXER_RAW);
|
||||||
} macro_args {
|
} 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:
|
rept:
|
||||||
POP_REPT uconst NEWLINE capture_rept endofline {
|
POP_REPT maybe_quiet uconst NEWLINE capture_rept endofline {
|
||||||
if ($4.span.ptr) {
|
if ($5.span.ptr) {
|
||||||
fstk_RunRept($2, $4.lineNo, $4.span);
|
fstk_RunRept($3, $5.lineNo, $5.span, $2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@@ -791,11 +809,11 @@ rept:
|
|||||||
for:
|
for:
|
||||||
POP_FOR {
|
POP_FOR {
|
||||||
lexer_ToggleStringExpansion(false);
|
lexer_ToggleStringExpansion(false);
|
||||||
} SYMBOL {
|
} maybe_quiet SYMBOL {
|
||||||
lexer_ToggleStringExpansion(true);
|
lexer_ToggleStringExpansion(true);
|
||||||
} COMMA for_args NEWLINE capture_rept endofline {
|
} COMMA for_args NEWLINE capture_rept endofline {
|
||||||
if ($8.span.ptr) {
|
if ($9.span.ptr) {
|
||||||
fstk_RunFor($3, $6.start, $6.stop, $6.step, $8.lineNo, $8.span);
|
fstk_RunFor($4, $7.start, $7.stop, $7.step, $9.lineNo, $9.span, $3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@@ -835,11 +853,11 @@ break:
|
|||||||
def_macro:
|
def_macro:
|
||||||
POP_MACRO {
|
POP_MACRO {
|
||||||
lexer_ToggleStringExpansion(false);
|
lexer_ToggleStringExpansion(false);
|
||||||
} SYMBOL {
|
} maybe_quiet SYMBOL {
|
||||||
lexer_ToggleStringExpansion(true);
|
lexer_ToggleStringExpansion(true);
|
||||||
} NEWLINE capture_macro endofline {
|
} NEWLINE capture_macro endofline {
|
||||||
if ($6.span.ptr) {
|
if ($7.span.ptr) {
|
||||||
sym_AddMacro($3, $6.lineNo, $6.span);
|
sym_AddMacro($4, $7.lineNo, $7.span, $3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@@ -1002,8 +1020,8 @@ export_def:
|
|||||||
;
|
;
|
||||||
|
|
||||||
include:
|
include:
|
||||||
label POP_INCLUDE string endofline {
|
label POP_INCLUDE maybe_quiet string endofline {
|
||||||
if (fstk_RunInclude($3)) {
|
if (fstk_RunInclude($4, $3)) {
|
||||||
YYACCEPT;
|
YYACCEPT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -205,8 +205,9 @@ static Symbol &createSymbol(std::string const &symName) {
|
|||||||
Symbol &sym = symbols[symName];
|
Symbol &sym = symbols[symName];
|
||||||
|
|
||||||
sym.name = symName;
|
sym.name = symName;
|
||||||
sym.isExported = false;
|
|
||||||
sym.isBuiltin = false;
|
sym.isBuiltin = false;
|
||||||
|
sym.isExported = false;
|
||||||
|
sym.isQuiet = false;
|
||||||
sym.section = nullptr;
|
sym.section = nullptr;
|
||||||
sym.src = fstk_GetFileStack();
|
sym.src = fstk_GetFileStack();
|
||||||
sym.fileLine = sym.src ? lexer_GetLineNo() : 0;
|
sym.fileLine = sym.src ? lexer_GetLineNo() : 0;
|
||||||
@@ -616,7 +617,9 @@ void sym_Export(std::string const &symName) {
|
|||||||
sym->isExported = true;
|
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);
|
Symbol *sym = createNonrelocSymbol(symName, false);
|
||||||
|
|
||||||
if (!sym) {
|
if (!sym) {
|
||||||
@@ -625,6 +628,7 @@ Symbol *sym_AddMacro(std::string const &symName, int32_t defLineNo, ContentSpan
|
|||||||
|
|
||||||
sym->type = SYM_MACRO;
|
sym->type = SYM_MACRO;
|
||||||
sym->data = span;
|
sym->data = span;
|
||||||
|
sym->isQuiet = isQuiet;
|
||||||
|
|
||||||
sym->src = fstk_GetFileStack();
|
sym->src = fstk_GetFileStack();
|
||||||
// The symbol is created at the line after the `ENDM`,
|
// The symbol is created at the line after the `ENDM`,
|
||||||
|
|||||||
@@ -6,16 +6,24 @@
|
|||||||
|
|
||||||
#include "platform.hpp" // strcasecmp
|
#include "platform.hpp" // strcasecmp
|
||||||
|
|
||||||
uint64_t traceDepth = 0;
|
Tracing tracing;
|
||||||
|
|
||||||
bool trace_ParseTraceDepth(char const *arg) {
|
bool trace_ParseTraceDepth(char const *arg) {
|
||||||
if (!strcasecmp(arg, "collapse")) {
|
if (!strcasecmp(arg, "collapse")) {
|
||||||
traceDepth = TRACE_COLLAPSE;
|
tracing.collapse = true;
|
||||||
return 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>;
|
using TraceNode = std::pair<std::string, uint32_t>;
|
||||||
|
|
||||||
static std::vector<TraceNode> backtrace(FileStackNode const &node, uint32_t curLineNo) {
|
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) {
|
if (!node.parent) {
|
||||||
assume(node.type != NODE_REPT && std::holds_alternative<std::string>(node.data));
|
assume(node.type != NODE_REPT && std::holds_alternative<std::string>(node.data));
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -105,14 +105,12 @@ static void readFileStackNode(
|
|||||||
tryReadLong(
|
tryReadLong(
|
||||||
node.lineNo, file, "%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, nodeID
|
node.lineNo, file, "%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, nodeID
|
||||||
);
|
);
|
||||||
tryGetc(
|
|
||||||
FileStackNodeType,
|
uint8_t type;
|
||||||
node.type,
|
tryGetc(uint8_t, type, file, "%s: Cannot read node #%" PRIu32 "'s type: %s", fileName, nodeID);
|
||||||
file,
|
node.type = static_cast<FileStackNodeType>(type & ~(1 << FSTACKNODE_QUIET_BIT));
|
||||||
"%s: Cannot read node #%" PRIu32 "'s type: %s",
|
node.isQuiet = (type & (1 << FSTACKNODE_QUIET_BIT)) != 0;
|
||||||
fileName,
|
|
||||||
nodeID
|
|
||||||
);
|
|
||||||
switch (node.type) {
|
switch (node.type) {
|
||||||
case NODE_FILE:
|
case NODE_FILE:
|
||||||
case NODE_MACRO:
|
case NODE_MACRO:
|
||||||
@@ -318,14 +316,14 @@ static void readSection(
|
|||||||
tryGetc(
|
tryGetc(
|
||||||
uint8_t, byte, file, "%s: Cannot read \"%s\"'s type: %s", fileName, section.name.c_str()
|
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);
|
fatal("\"%s\" has unknown section type 0x%02x", section.name.c_str(), type);
|
||||||
} else {
|
} else {
|
||||||
section.type = SectionType(type);
|
section.type = SectionType(type);
|
||||||
}
|
}
|
||||||
if (byte >> 7) {
|
if (byte & (1 << SECTTYPE_UNION_BIT)) {
|
||||||
section.modifier = SECTION_UNION;
|
section.modifier = SECTION_UNION;
|
||||||
} else if (byte >> 6) {
|
} else if (byte & (1 << SECTTYPE_FRAGMENT_BIT)) {
|
||||||
section.modifier = SECTION_FRAGMENT;
|
section.modifier = SECTION_FRAGMENT;
|
||||||
} else {
|
} else {
|
||||||
section.modifier = SECTION_NORMAL;
|
section.modifier = SECTION_NORMAL;
|
||||||
@@ -458,6 +456,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID) {
|
|||||||
nodes[fileID].push_back({
|
nodes[fileID].push_back({
|
||||||
.type = NODE_FILE,
|
.type = NODE_FILE,
|
||||||
.data = std::variant<std::monostate, std::vector<uint32_t>, std::string>(fileName),
|
.data = std::variant<std::monostate, std::vector<uint32_t>, std::string>(fileName),
|
||||||
|
.isQuiet = false,
|
||||||
.parent = nullptr,
|
.parent = nullptr,
|
||||||
.lineNo = 0,
|
.lineNo = 0,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
-B 5
|
-B 5 -B no-collapse
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
-B 0
|
-B no-collapse
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
assert 1 +# 1 == 2
|
assert 1 +# 1 == 2
|
||||||
assert 2 ?<EFBFBD>* 2 == 4
|
assert 2 #<EFBFBD>* 2 == 4
|
||||||
assert 3 **?<EFBFBD>?##?? 3 == 27
|
assert 3 **#<EFBFBD>}<EFBFBD># 3 == 27
|
||||||
charmap "x", 4
|
charmap "x", 4
|
||||||
assert 4 <<#<EFBFBD>'x' == 64
|
assert 4 <<#<EFBFBD>'x' == 64
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
error: Invalid character '#'
|
error: Invalid character '#'
|
||||||
at garbage_sequence.asm(1)
|
at garbage_sequence.asm(1)
|
||||||
error: Invalid characters '?', 0xFF (is the file UTF-8?)
|
error: Invalid characters '#', 0xFF (is the file UTF-8?)
|
||||||
at garbage_sequence.asm(2)
|
at garbage_sequence.asm(2)
|
||||||
error: Invalid characters '?', 0xFF, '?' (is the file UTF-8?)
|
error: Invalid characters '#', 0xFF, '}', 0xFF (is the file UTF-8?)
|
||||||
at garbage_sequence.asm(3)
|
at garbage_sequence.asm(3)
|
||||||
error: Invalid character '#'
|
error: Invalid character '#'
|
||||||
at garbage_sequence.asm(3)
|
at garbage_sequence.asm(3)
|
||||||
error: Invalid characters '#', '?', '?'
|
|
||||||
at garbage_sequence.asm(3)
|
|
||||||
error: Invalid characters '#', 0xFF (is the file UTF-8?)
|
error: Invalid characters '#', 0xFF (is the file UTF-8?)
|
||||||
at garbage_sequence.asm(5)
|
at garbage_sequence.asm(5)
|
||||||
Assembly aborted with 6 errors!
|
Assembly aborted with 5 errors!
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
error: syntax error, unexpected label, expecting symbol
|
error: syntax error, unexpected label, expecting symbol
|
||||||
at lex-label.asm(1)
|
at lex-label.asm(1)
|
||||||
error: syntax error, unexpected label, expecting symbol
|
error: syntax error, unexpected label, expecting ? or symbol
|
||||||
at lex-label.asm(3)
|
at lex-label.asm(3)
|
||||||
error: syntax error, unexpected ENDM
|
error: syntax error, unexpected ENDM
|
||||||
at lex-label.asm(4)
|
at lex-label.asm(4)
|
||||||
error: syntax error, unexpected label, expecting symbol
|
error: syntax error, unexpected label, expecting ? or symbol
|
||||||
at lex-label.asm(6)
|
at lex-label.asm(6)
|
||||||
error: syntax error, unexpected ENDR
|
error: syntax error, unexpected ENDR
|
||||||
at lex-label.asm(7)
|
at lex-label.asm(7)
|
||||||
|
|||||||
20
test/asm/loud-backtrace.asm
Normal file
20
test/asm/loud-backtrace.asm
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
macro mac
|
||||||
|
warn "from macro"
|
||||||
|
endm
|
||||||
|
mac ; normal
|
||||||
|
|
||||||
|
macro? quiet
|
||||||
|
warn "from quiet macro"
|
||||||
|
endm
|
||||||
|
quiet
|
||||||
|
rept? 1
|
||||||
|
warn "from quiet rept"
|
||||||
|
endr
|
||||||
|
for? x, 1
|
||||||
|
warn "from quiet for (x={d:x})"
|
||||||
|
endr
|
||||||
|
include? "loud-backtrace.inc"
|
||||||
|
macro loud
|
||||||
|
warn "from loud macro"
|
||||||
|
endm
|
||||||
|
mac?
|
||||||
12
test/asm/loud-backtrace.err
Normal file
12
test/asm/loud-backtrace.err
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
warning: from macro [-Wuser]
|
||||||
|
at loud-backtrace.asm::mac(2) <- loud-backtrace.asm(4)
|
||||||
|
warning: from quiet macro [-Wuser]
|
||||||
|
at loud-backtrace.asm::quiet(7) <- loud-backtrace.asm(9)
|
||||||
|
warning: from quiet rept [-Wuser]
|
||||||
|
at loud-backtrace.asm::REPT~1(11) <- loud-backtrace.asm(10)
|
||||||
|
warning: from quiet for (x=0) [-Wuser]
|
||||||
|
at loud-backtrace.asm::REPT~1(14) <- loud-backtrace.asm(13)
|
||||||
|
warning: from quiet include [-Wuser]
|
||||||
|
at loud-backtrace.inc(1) <- loud-backtrace.asm(16)
|
||||||
|
warning: from macro [-Wuser]
|
||||||
|
at loud-backtrace.asm::mac(2) <- loud-backtrace.asm(20)
|
||||||
1
test/asm/loud-backtrace.flags
Normal file
1
test/asm/loud-backtrace.flags
Normal file
@@ -0,0 +1 @@
|
|||||||
|
-B all
|
||||||
1
test/asm/loud-backtrace.inc
Normal file
1
test/asm/loud-backtrace.inc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
warn "from quiet include"
|
||||||
20
test/asm/quiet-backtrace.asm
Normal file
20
test/asm/quiet-backtrace.asm
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
macro mac
|
||||||
|
warn "from macro"
|
||||||
|
endm
|
||||||
|
mac ; normal
|
||||||
|
|
||||||
|
macro? quiet
|
||||||
|
warn "from quiet macro"
|
||||||
|
endm
|
||||||
|
quiet
|
||||||
|
rept? 1
|
||||||
|
warn "from quiet rept"
|
||||||
|
endr
|
||||||
|
for? x, 1
|
||||||
|
warn "from quiet for (x={d:x})"
|
||||||
|
endr
|
||||||
|
include? "quiet-backtrace.inc"
|
||||||
|
macro loud
|
||||||
|
warn "from loud macro"
|
||||||
|
endm
|
||||||
|
mac?
|
||||||
12
test/asm/quiet-backtrace.err
Normal file
12
test/asm/quiet-backtrace.err
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
warning: from macro [-Wuser]
|
||||||
|
at quiet-backtrace.asm::mac(2) <- quiet-backtrace.asm(4)
|
||||||
|
warning: from quiet macro [-Wuser]
|
||||||
|
at quiet-backtrace.asm(9)
|
||||||
|
warning: from quiet rept [-Wuser]
|
||||||
|
at quiet-backtrace.asm(11)
|
||||||
|
warning: from quiet for (x=0) [-Wuser]
|
||||||
|
at quiet-backtrace.asm(14)
|
||||||
|
warning: from quiet include [-Wuser]
|
||||||
|
at quiet-backtrace.asm(16)
|
||||||
|
warning: from macro [-Wuser]
|
||||||
|
at quiet-backtrace.asm(20)
|
||||||
1
test/asm/quiet-backtrace.inc
Normal file
1
test/asm/quiet-backtrace.inc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
warn "from quiet include"
|
||||||
@@ -1 +1 @@
|
|||||||
-B 0
|
-B no-collapse
|
||||||
|
|||||||
Reference in New Issue
Block a user