Fix '' with multiple format strings
This commit is contained in:
parent
b99b3d7b9c
commit
9dfc050fb5
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 *-*
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue