Fix '' with multiple format strings

This commit is contained in:
Wilson Snyder 2025-10-13 19:47:08 -04:00
parent b99b3d7b9c
commit 9dfc050fb5
3 changed files with 81 additions and 92 deletions

View File

@ -308,106 +308,75 @@ class LinkResolveVisitor final : public VNVisitor {
bool inPct = false; bool inPct = false;
bool inIgnore = false; bool inIgnore = false;
string fmt; string fmt;
for (const char ch : format) { string parseFormat = format;
if (!inPct && ch == '%') { while (!parseFormat.empty() || argp) {
inPct = true; for (const char ch : parseFormat) {
inIgnore = false; if (!inPct && ch == '%') {
fmt = ch;
} else if (inPct && (std::isdigit(ch) || ch == '.' || ch == '-')) {
fmt += ch;
} else if (inPct) {
inPct = false;
fmt += ch;
switch (std::tolower(ch)) {
case '%': // %% - just output a %
break;
case '*':
inPct = true; inPct = true;
inIgnore = true; inIgnore = false;
break; fmt = ch;
case 'm': // %m - auto insert "name" } else if (inPct && (std::isdigit(ch) || ch == '.' || ch == '-')) {
if (isScan) { fmt += ch;
nodep->v3warn(E_UNSUPPORTED, "Unsupported: %m in $fscanf"); } else if (inPct) {
fmt = ""; inPct = false;
} fmt += ch;
break; switch (std::tolower(ch)) {
case 'l': // %l - auto insert "library" case '%': // %% - just output a %
if (isScan) { break;
nodep->v3warn(E_UNSUPPORTED, "Unsupported: %l in $fscanf"); case '*':
fmt = ""; inPct = true;
} inIgnore = true;
if (m_modp) break;
fmt = AstNode::prettyName(m_modp->libname()) + "." + m_modp->prettyName(); case 'm': // %m - auto insert "name"
break; if (isScan) {
default: // Most operators, just move to next argument nodep->v3warn(E_UNSUPPORTED, "Unsupported: %m in $fscanf");
if (!V3Number::displayedFmtLegal(ch, isScan)) { fmt = "";
nodep->v3error("Unknown $display-like format code: '%" << ch << "'");
} else if (!inIgnore) {
if (!argp) {
nodep->v3error("Missing arguments for $display-like format");
} else {
argp = argp->nextp();
} }
} break;
break; case 'l': // %l - auto insert "library"
} // switch if (isScan) {
newFormat += fmt; nodep->v3warn(E_UNSUPPORTED, "Unsupported: %l in $fscanf");
} else { fmt = "";
newFormat += ch; }
} if (m_modp)
} fmt = AstNode::prettyName(m_modp->libname()) + "."
+ m_modp->prettyName();
if (argp && !isScan) { break;
int skipCount = 0; // number of args consume by any additional format strings default: // Most operators, just move to next argument
while (argp) { if (!V3Number::displayedFmtLegal(ch, isScan)) {
if (skipCount) { nodep->v3error("Unknown $display-like format code: '%" << ch << "'");
argp = argp->nextp(); } else if (!inIgnore) {
--skipCount; if (!argp) {
continue; nodep->v3error("Missing arguments for $display-like format");
} } else {
const AstConst* const constp = VN_CAST(argp, Const); argp = argp->nextp();
const bool isFromString = (constp) ? constp->num().isFromString() : false;
if (isFromString) {
const int numchars = argp->dtypep()->width() / 8;
if (!constp->num().toString().empty()) {
string str(numchars, ' ');
// now scan for % operators
bool inpercent = false;
for (int i = 0; i < numchars; i++) {
const int ii = numchars - i - 1;
const char c = constp->num().dataByte(ii);
str[i] = c;
if (!inpercent && c == '%') {
inpercent = true;
} else if (inpercent) {
inpercent = false;
switch (c) {
case '0': // FALLTHRU
case '1': // FALLTHRU
case '2': // FALLTHRU
case '3': // FALLTHRU
case '4': // FALLTHRU
case '5': // FALLTHRU
case '6': // FALLTHRU
case '7': // FALLTHRU
case '8': // FALLTHRU
case '9': // FALLTHRU
case '.': inpercent = true; break;
case '%': break;
default:
if (V3Number::displayedFmtLegal(c, isScan)) ++skipCount;
}
} }
} }
newFormat.append(str); break;
} } // switch
newFormat += fmt;
} else {
newFormat += ch;
}
}
// Find additional arguments (without format) or additional format strings
parseFormat = "";
if (isScan) break;
while (argp) {
const AstConst* const constp = VN_CAST(argp, Const);
const bool isFromString = (constp) ? constp->num().isFromString() : false;
if (!isFromString) {
newFormat.append("%?"); // V3Width to figure it out
argp = argp->nextp();
} else { // New format string
parseFormat += constp->num().toString();
AstNode* const nextp = argp->nextp(); AstNode* const nextp = argp->nextp();
argp->unlinkFrBack(); argp->unlinkFrBack();
VL_DO_DANGLING(pushDeletep(argp), argp); VL_DO_DANGLING(pushDeletep(argp), argp);
argp = nextp; argp = nextp;
} else { break; // And continue at top of parsing the new parseFormat
newFormat.append("%?"); // V3Width to figure it out
argp = argp->nextp();
} }
} }
} }

View File

@ -101,4 +101,9 @@ ZzX
ZzXx ZzXx
XXXx XXXx
10 10
[50] FIFTY 50
[0] FIFTY 50
[0] not-fmt %-d 60
[0] fmt-as-string-not-%0x 70
s=[0] fmt-string-not-%s
*-* All Finished *-* *-* All Finished *-*

View File

@ -21,6 +21,9 @@ module t;
reg [5:0] assoc_c[int]; reg [5:0] assoc_c[int];
string s;
string not_fmt;
sub sub (); sub sub ();
sub2 sub2 (); sub2 sub2 ();
sub3 sub3 (); sub3 sub3 ();
@ -215,6 +218,18 @@ multiline", $time);
$display(,, 10); // Strange but legal $display(,, 10); // Strange but legal
// $sformat allows only single format while $display/write allow multiple
$display("[50] FIFTY 50");
$display("[%0t]", $time, " FIFTY %-d", 50);
// This prints as %s, the %-d is not a format, as not_fmt is not literal
not_fmt = " not-fmt %-d";
$display("[%0t]", $time, not_fmt, 60);
// This prints as %s as forces the literal to a string
$display("[%0t] %s", $time, " fmt-as-string-not-%0x", 70);
// Sformat takes only single format per IEEE
s = $sformatf("[%0t] %s", $time, " fmt-string-not-%s");
$display("s=%s", s);
$write("*-* All Finished *-*\n"); $write("*-* All Finished *-*\n");
$finish; $finish;
end end