Support string putc, getc, substr, bug1606.

Signed-off-by: Wilson Snyder <wsnyder@wsnyder.org>
This commit is contained in:
Yutetsu TAKATSUKASA 2019-12-15 08:09:52 -05:00 committed by Wilson Snyder
parent 460e0541f3
commit 8cdc0c4e00
11 changed files with 268 additions and 25 deletions

View File

@ -6,7 +6,7 @@ The contributors that suggested a given feature are shown in []. Thanks!
*** Support bounded queues.
*** Support string compare, icompare, ato* methods, bug1606. [Yutetsu TAKATSUKASA]
*** Support string compare, ato*, etc methods, bug1606. [Yutetsu TAKATSUKASA]
**** Ignore `uselib to end-of-line, bug1634. [Frederic Antonin]

View File

@ -1826,8 +1826,33 @@ std::string VL_CVT_PACK_STR_NW(int lwords, WDataInP lwp) VL_MT_SAFE {
return std::string(destout, len);
}
std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE {
std::string lstring = lhs;
const vlsint32_t rhs_s = rhs; // To signed value
// 6.16.2:str.putc(i, c) does not change the value when i < 0 || i >= str.len() || c == 0
if (0 <= rhs_s && rhs < lhs.length() && ths != 0) lstring[rhs] = ths;
return lstring;
}
CData VL_GETC_N(const std::string& lhs, IData rhs) VL_PURE {
CData v = 0;
const vlsint32_t rhs_s = rhs; // To signed value
// 6.16.3:str.getc(i) returns 0 if i < 0 || i >= str.len()
if (0 <= rhs_s && rhs < lhs.length()) v = lhs[rhs];
return v;
}
std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_PURE {
const vlsint32_t rhs_s = rhs; // To signed value
const vlsint32_t ths_s = ths; // To signed value
// 6.16.8:str.substr(i, j) returns an empty string when i < 0 || j < i || j >= str.len()
if (rhs_s < 0 || ths_s < rhs_s || ths >= lhs.length()) return "";
// Second parameter of std::string::substr(i, n) is length, not position as in SystemVerilog
return lhs.substr(rhs, ths - rhs + 1);
}
IData VL_ATOI_N(const std::string& str, int base) VL_PURE {
std::string str_mod = str; // create a new instance to modify later.
std::string str_mod = str;
// IEEE 1800-2017 6.16.9 says '_' may exist.
str_mod.erase(std::remove(str_mod.begin(), str_mod.end(), '_'), str_mod.end());

View File

@ -359,8 +359,12 @@ extern IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr)
//======================================================================
// Strings
extern std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE;
extern CData VL_GETC_N(const std::string& lhs, IData rhs) VL_PURE;
extern std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_PURE;
inline IData VL_CMP_NN(const std::string& lhs, const std::string& rhs, bool ignoreCase) VL_PURE {
// SystemVerilog Language Standard does not allow a string variable to contain '\0'.
// SystemVerilog does not allow a string variable to contain '\0'.
// So C functions such as strcmp() can correctly compare strings.
int result;
if (ignoreCase) {

View File

@ -5995,6 +5995,78 @@ public:
virtual string emitC() { return "hypot(%li,%ri)"; }
};
class AstPutcN : public AstNodeTriop {
// Verilog string.putc()
public:
AstPutcN(FileLine* fl, AstNode* lhsp, AstNode* rhsp, AstNode* ths) : AstNodeTriop(fl, lhsp, rhsp, ths) {
dtypeSetString();
}
ASTNODE_NODE_FUNCS(PutcN)
virtual void numberOperate(V3Number& out, const V3Number& lhs,
const V3Number& rhs, const V3Number& ths) {
out.opPutcN(lhs, rhs, ths);
}
virtual string name() const { return "putc"; }
virtual string emitVerilog() { return "%k(%l.putc(%r,%t))"; }
virtual string emitC() { return "VL_PUTC_N(%li,%ri,%ti)"; }
virtual string emitSimpleOperator() { return ""; }
virtual bool cleanOut() const { return true; }
virtual bool cleanLhs() const { return true; }
virtual bool cleanRhs() const { return true; }
virtual bool cleanThs() const { return true; }
virtual bool sizeMattersLhs() const { return false; }
virtual bool sizeMattersRhs() const { return false; }
virtual bool sizeMattersThs() const { return false; }
};
class AstGetcN : public AstNodeBiop {
// Verilog string.getc()
public:
AstGetcN(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {
dtypeSetBitSized(8, AstNumeric::UNSIGNED);
}
ASTNODE_NODE_FUNCS(GetcN)
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) {
return new AstGetcN(this->fileline(), lhsp, rhsp);
}
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) {
out.opGetcN(lhs, rhs);
}
virtual string name() const { return "getc"; }
virtual string emitVerilog() { return "%k(%l.getc(%r))"; }
virtual string emitC() { return "VL_GETC_N(%li,%ri)"; }
virtual string emitSimpleOperator() { return ""; }
virtual bool cleanOut() const { return true; }
virtual bool cleanLhs() const { return true; }
virtual bool cleanRhs() const { return true; }
virtual bool sizeMattersLhs() const { return false; }
virtual bool sizeMattersRhs() const { return false; }
};
class AstSubstrN : public AstNodeTriop {
// Verilog string.substr()
public:
AstSubstrN(FileLine* fl, AstNode* lhsp, AstNode* rhsp, AstNode* ths) : AstNodeTriop(fl, lhsp, rhsp, ths) {
dtypeSetString();
}
ASTNODE_NODE_FUNCS(SubstrN)
virtual void numberOperate(V3Number& out, const V3Number& lhs,
const V3Number& rhs, const V3Number& ths) {
out.opSubstrN(lhs, rhs, ths);
}
virtual string name() const { return "substr"; }
virtual string emitVerilog() { return "%k(%l.substr(%r,%t))"; }
virtual string emitC() { return "VL_SUBSTR_N(%li,%ri,%ti)"; }
virtual string emitSimpleOperator() { return ""; }
virtual bool cleanOut() const { return true; }
virtual bool cleanLhs() const { return true; }
virtual bool cleanRhs() const { return true; }
virtual bool cleanThs() const { return true; }
virtual bool sizeMattersLhs() const { return false; }
virtual bool sizeMattersRhs() const { return false; }
virtual bool sizeMattersThs() const { return false; }
};
class AstCompareNN : public AstNodeBiop {
// Verilog str.compare() and str.icompare()
private:

View File

@ -2526,6 +2526,8 @@ private:
TREEOPV("AstLogIf{$lhsp, $rhsp}", "AstLogOr{AstLogNot{$lhsp},$rhsp}");
TREEOPV("AstLogEq{$lhsp, $rhsp}", "replaceLogEq(nodep)");
// Strings
TREEOPC("AstPutcN{$lhsp.castConst, $rhsp.castConst, $thsp.castConst}", "replaceConst(nodep)");
TREEOPC("AstSubstrN{$lhsp.castConst, $rhsp.castConst, $thsp.castConst}", "replaceConst(nodep)");
TREEOPC("AstCvtPackString{$lhsp.castConst}", "replaceConstString(nodep, VN_CAST(nodep->lhsp(), Const)->num().toString())");

View File

@ -643,6 +643,10 @@ public:
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), NULL);
}
}
virtual void visit(AstNodeTriop* nodep) {
UASSERT_OBJ(!emitSimpleOk(nodep), nodep, "Triop cannot be described in a simple way");
emitOpName(nodep, nodep->emitC(), nodep->lhsp(), nodep->rhsp(), nodep->thsp());
}
virtual void visit(AstRedXor* nodep) {
if (nodep->lhsp()->isWide()) {
visit(VN_CAST(nodep, NodeUniop));

View File

@ -62,6 +62,18 @@ class EmitCInlines : EmitCBaseVisitor {
v3Global.needHeavy(true);
iterateChildren(nodep);
}
virtual void visit(AstPutcN* nodep) {
v3Global.needHeavy(true);
iterateChildren(nodep);
}
virtual void visit(AstGetcN* nodep) {
v3Global.needHeavy(true);
iterateChildren(nodep);
}
virtual void visit(AstSubstrN* nodep) {
v3Global.needHeavy(true);
iterateChildren(nodep);
}
virtual void visit(AstCompareNN* nodep) {
v3Global.needHeavy(true);
iterateChildren(nodep);

View File

@ -1262,6 +1262,44 @@ V3Number& V3Number::opAtoN(const V3Number& lhs, int base) {
return setLongS(static_cast<vlsint32_t>(v));
}
V3Number& V3Number::opPutcN(const V3Number& lhs, const V3Number& rhs, const V3Number& ths) {
NUM_ASSERT_OP_ARGS3(lhs, rhs, ths);
NUM_ASSERT_STRING_ARGS1(lhs);
string lstring = lhs.toString();
const vlsint32_t i = rhs.toSInt();
const vlsint32_t c = ths.toSInt() & 0xFF;
// 6.16.2:str.putc(i, c) does not change the value when i < 0 || i >= str.len() || c == 0
// when evaluating the second condition, i must be positive.
if (0 <= i && static_cast<uint32_t>(i) < lstring.length() && c != 0) lstring[i] = c;
return setString(lstring);
}
V3Number& V3Number::opGetcN(const V3Number& lhs, const V3Number& rhs) {
NUM_ASSERT_OP_ARGS2(lhs, rhs);
NUM_ASSERT_STRING_ARGS1(lhs);
const string lstring = lhs.toString();
const vlsint32_t i = rhs.toSInt();
vlsint32_t v = 0;
// 6.16.3:str.getc(i) returns 0 if i < 0 || i >= str.len()
// when evaluating the second condition, i must be positive.
if (0 <= i && static_cast<uint32_t>(i) < lstring.length()) v = lstring[i];
return setLong(v);
}
V3Number& V3Number::opSubstrN(const V3Number& lhs, const V3Number& rhs, const V3Number& ths) {
NUM_ASSERT_OP_ARGS3(lhs, rhs, ths);
NUM_ASSERT_STRING_ARGS1(lhs);
const string lstring = lhs.toString();
const vlsint32_t i = rhs.toSInt();
const vlsint32_t j = ths.toSInt();
// 6.16.8:str.substr(i, j) returns an empty string when i < 0 || j < i || j >= str.len()
// when evaluating the third condition, j must be positive because 0 <= i <= j is guaranteed by
// the former two conditions.
if (i < 0 || j < i || static_cast<uint32_t>(j) >= lstring.length()) return setString("");
// The second parameter of std::string::substr(i, n) is length, not position as SystemVerilog.
return setString(lstring.substr(i, j - i + 1));
}
V3Number& V3Number::opCompareNN(const V3Number& lhs, const V3Number& rhs, bool ignoreCase) {
NUM_ASSERT_OP_ARGS2(lhs, rhs);
NUM_ASSERT_STRING_ARGS2(lhs, rhs);

View File

@ -361,6 +361,9 @@ public:
// "N" - string operations
V3Number& opAtoN (const V3Number& lhs, int base);
V3Number& opPutcN (const V3Number& lhs, const V3Number& rhs, const V3Number& ths);
V3Number& opGetcN (const V3Number& lhs, const V3Number& rhs);
V3Number& opSubstrN (const V3Number& lhs, const V3Number& rhs, const V3Number& ths);
V3Number& opCompareNN(const V3Number& lhs,const V3Number& rhs, bool ignoreCase);
V3Number& opConcatN (const V3Number& lhs, const V3Number& rhs);
V3Number& opReplN (const V3Number& lhs, const V3Number& rhs);

View File

@ -333,6 +333,38 @@ private:
// Output integer, input string
virtual void visit(AstLenN* nodep) { visit_Os32_string(nodep); }
virtual void visit(AstPutcN* nodep) {
// CALLER: str.putc()
UASSERT_OBJ(nodep->rhsp() && nodep->thsp(), nodep, "For ternary ops only!");
if (m_vup && m_vup->prelim()) {
// See similar handling in visit_cmp_eq_gt where created
iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
iterateCheckSigned32(nodep, "THS", nodep->thsp(), BOTH);
nodep->dtypeSetString(); //AstPutcN returns the new string to be assigned by AstAssign
}
}
virtual void visit(AstGetcN* nodep) {
// CALLER: str.getc()
UASSERT_OBJ(nodep->rhsp(), nodep, "For binary ops only!");
if (m_vup && m_vup->prelim()) {
// See similar handling in visit_cmp_eq_gt where created
iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
nodep->dtypeSetBitSized(8, AstNumeric::UNSIGNED);
}
}
virtual void visit(AstSubstrN* nodep) {
// CALLER: str.substr()
UASSERT_OBJ(nodep->rhsp() && nodep->thsp(), nodep, "For ternary ops only!");
if (m_vup && m_vup->prelim()) {
// See similar handling in visit_cmp_eq_gt where created
iterateCheckString(nodep, "LHS", nodep->lhsp(), BOTH);
iterateCheckSigned32(nodep, "RHS", nodep->rhsp(), BOTH);
iterateCheckSigned32(nodep, "THS", nodep->thsp(), BOTH);
nodep->dtypeSetString();
}
}
virtual void visit(AstCompareNN* nodep) {
// CALLER: str.compare(), str.icompare()
// Widths: 32 bit out
@ -2115,6 +2147,34 @@ private:
AstNode* rhs = argp->exprp()->unlinkFrBack();
AstNode* newp = new AstCompareNN(nodep->fileline(), lhs, rhs, ignoreCase);
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
} else if (nodep->name() == "putc" ) {
methodOkArguments(nodep, 2, 2);
AstArg* arg0p = VN_CAST(nodep->pinsp(), Arg);
AstArg* arg1p = VN_CAST(arg0p->nextp(), Arg);
AstNodeVarRef* fromp = VN_CAST(nodep->fromp()->unlinkFrBack(), VarRef);
AstNode* rhsp = arg0p->exprp()->unlinkFrBack();
AstNode* thsp = arg1p->exprp()->unlinkFrBack();
AstVarRef* varrefp = new AstVarRef(nodep->fileline(), fromp->varp(), false);
AstNode* newp = new AstAssign(nodep->fileline(), fromp,
new AstPutcN(nodep->fileline(), varrefp, rhsp, thsp));
fromp->lvalue(true);
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
} else if (nodep->name() == "getc") {
methodOkArguments(nodep, 1, 1);
AstArg* arg0p = VN_CAST(nodep->pinsp(), Arg);
AstNode* lhsp = nodep->fromp()->unlinkFrBack();
AstNode* rhsp = arg0p->exprp()->unlinkFrBack();
AstNode* newp = new AstGetcN(nodep->fileline(), lhsp, rhsp);
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
} else if (nodep->name() == "substr") {
methodOkArguments(nodep, 2, 2);
AstArg* arg0p = VN_CAST(nodep->pinsp(), Arg);
AstArg* arg1p = VN_CAST(arg0p->nextp(), Arg);
AstNode* lhsp = nodep->fromp()->unlinkFrBack();
AstNode* rhsp = arg0p->exprp()->unlinkFrBack();
AstNode* thsp = arg1p->exprp()->unlinkFrBack();
AstNode* newp = new AstSubstrN(nodep->fileline(), lhsp, rhsp, thsp);
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
} else if (nodep->name() == "atobin"
|| nodep->name() == "atohex"
|| nodep->name() == "atoi"
@ -2130,9 +2190,6 @@ private:
methodOkArguments(nodep, 0, 0);
AstNode* newp = new AstAtoN(nodep->fileline(), nodep->fromp()->unlinkFrBack(), fmt);
nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep);
} else if (nodep->name() == "getc"
|| nodep->name() == "putc") {
nodep->v3error("Unsupported: built-in string method "<<nodep->prettyNameQ());
} else {
nodep->v3error("Unknown built-in string method "<<nodep->prettyNameQ());
}

View File

@ -22,10 +22,13 @@ module t (/*AUTOARG*/
s="1234"; `checkh(s.len(),4);
s="ab7CD"; `checks(s.toupper(), "AB7CD");
s="ab7CD"; `checks(s.tolower(), "ab7cd");
`ifndef VERILATOR
s="1234"; s.putc(-1, "z"); `checks(s, "1234");
s="1234"; s.putc(4, "z"); `checks(s, "1234");
s="1234"; s.putc(2, 0); `checks(s, "1234");
s="1234"; s.putc(2, "z"); `checks(s, "12z4");
s="1234"; `checkh(s.getc(-1), 0);
s="1234"; `checkh(s.getc(4), 0);
s="1234"; `checkh(s.getc(2), "3");
`endif
s="b"; if (s.compare("a") <= 0) $stop;
s="b"; if (s.compare("b") != 0) $stop;
s="b"; if (s.compare("c") >= 0) $stop;
@ -38,6 +41,10 @@ module t (/*AUTOARG*/
s="b"; if (s.icompare("A") < 0) $stop;
s="b"; if (s.icompare("B") != 0) $stop;
s="b"; if (s.icompare("C") >= 0) $stop;
s="abcd"; `checks(s.substr(-1,1), "");
s="abcd"; `checks(s.substr(1,0), "");
s="abcd"; `checks(s.substr(1,4), "");
s="abcd"; `checks(s.substr(2,3), "cd");
s="101"; `checkh(s.atoi(), 'd101);
s="101"; `checkh(s.atohex(), 'h101);
s="101"; `checkh(s.atooct(), 'o101);
@ -64,23 +71,35 @@ module t (/*AUTOARG*/
`checkh(s.len(),4);
end
else if (cyc==2) begin
`ifndef VERILATOR
s.putc(2, "z");
`endif
s.putc(-1, "z");
end
else if (cyc==3) begin
`ifndef VERILATOR
`checks(s, "12z4");
`checkh(s.getc(2), "z");
`endif
s="ab3CD";
`checks(s, "1234");
s.putc(4, "z");
end
else if (cyc==4) begin
`checks(s, "1234");
s.putc(2, 0);
end
else if (cyc==5) begin
`checks(s, "1234");
s.putc(2, "z");
end
else if (cyc==6) begin
`checks(s, "12z4");
end
else if (cyc==7) begin
`checkh(s.getc(-1), 0);
`checkh(s.getc(4), 0);
`checkh(s.getc(2), "z");
s="ab3CD";
end
else if (cyc==8) begin
`checks(s.toupper(), "AB3CD");
`checks(s.tolower(), "ab3cd");
s="b";
end
else if (cyc==5) begin
else if (cyc==9) begin
if (s.compare("a") <= 0) $stop;
if (s.compare("b") != 0) $stop;
if (s.compare("c") >= 0) $stop;
@ -90,38 +109,45 @@ module t (/*AUTOARG*/
if (s.icompare("A") < 0) $stop;
if (s.icompare("B") != 0) $stop;
if (s.icompare("C") >= 0) $stop;
s="abcd";
end
else if (cyc==10) begin
`checks(s.substr(-1,1), "");
`checks(s.substr(1,0), "");
`checks(s.substr(1,4), "");
`checks(s.substr(2,3), "cd");
s="101";
end
else if (cyc==7) begin
else if (cyc==11) begin
`checkh(s.atoi(), 'd101);
`checkh(s.atohex(), 'h101);
`checkh(s.atooct(), 'o101);
`checkh(s.atobin(), 'b101);
s="1.23";
end
else if (cyc==8) begin
else if (cyc==12) begin
`checkg(s.atoreal(), 1.23);
end
else if (cyc==9) begin
else if (cyc==13) begin
s.itoa(123);
end
else if (cyc==10) begin
else if (cyc==14) begin
`checks(s, "123");
s.hextoa(123);
end
else if (cyc==11) begin
else if (cyc==15) begin
`checks(s, "7b");
s.octtoa(123);
end
else if (cyc==12) begin
else if (cyc==16) begin
`checks(s, "173");
s.bintoa(123);
end
else if (cyc==13) begin
else if (cyc==17) begin
`checks(s, "1111011");
s.realtoa(1.23);
end
else if (cyc==14) begin
else if (cyc==18) begin
`checks(s, "1.23");
end
else if (cyc==99) begin