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 inIgnore = false;
string fmt;
for (const char ch : format) {
if (!inPct && ch == '%') {
inPct = true;
inIgnore = false;
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 '*':
string parseFormat = format;
while (!parseFormat.empty() || argp) {
for (const char ch : parseFormat) {
if (!inPct && ch == '%') {
inPct = true;
inIgnore = true;
break;
case 'm': // %m - auto insert "name"
if (isScan) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: %m in $fscanf");
fmt = "";
}
break;
case 'l': // %l - auto insert "library"
if (isScan) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: %l in $fscanf");
fmt = "";
}
if (m_modp)
fmt = AstNode::prettyName(m_modp->libname()) + "." + m_modp->prettyName();
break;
default: // Most operators, just move to next argument
if (!V3Number::displayedFmtLegal(ch, isScan)) {
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();
inIgnore = false;
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;
inIgnore = true;
break;
case 'm': // %m - auto insert "name"
if (isScan) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: %m in $fscanf");
fmt = "";
}
}
break;
} // switch
newFormat += fmt;
} else {
newFormat += ch;
}
}
if (argp && !isScan) {
int skipCount = 0; // number of args consume by any additional format strings
while (argp) {
if (skipCount) {
argp = argp->nextp();
--skipCount;
continue;
}
const AstConst* const constp = VN_CAST(argp, Const);
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;
}
break;
case 'l': // %l - auto insert "library"
if (isScan) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: %l in $fscanf");
fmt = "";
}
if (m_modp)
fmt = AstNode::prettyName(m_modp->libname()) + "."
+ m_modp->prettyName();
break;
default: // Most operators, just move to next argument
if (!V3Number::displayedFmtLegal(ch, isScan)) {
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();
}
}
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();
argp->unlinkFrBack();
VL_DO_DANGLING(pushDeletep(argp), argp);
argp = nextp;
} else {
newFormat.append("%?"); // V3Width to figure it out
argp = argp->nextp();
break; // And continue at top of parsing the new parseFormat
}
}
}

View File

@ -101,4 +101,9 @@ ZzX
ZzXx
XXXx
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 *-*

View File

@ -21,6 +21,9 @@ module t;
reg [5:0] assoc_c[int];
string s;
string not_fmt;
sub sub ();
sub2 sub2 ();
sub3 sub3 ();
@ -215,6 +218,18 @@ multiline", $time);
$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");
$finish;
end