Support associative array pattern assignments and defaults.
This commit is contained in:
parent
046c0a7aa1
commit
95d1272269
2
Changes
2
Changes
|
|
@ -6,6 +6,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
|
||||||
|
|
||||||
**** Support queue slicing (#2326).
|
**** Support queue slicing (#2326).
|
||||||
|
|
||||||
|
**** Support associative array pattern assignments and defaults.
|
||||||
|
|
||||||
**** Fix WIDTH warnings on comparisons with nullptr (#2602). [Rupert Swarbrick]
|
**** Fix WIDTH warnings on comparisons with nullptr (#2602). [Rupert Swarbrick]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -196,6 +196,16 @@ public:
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Setting as a chained operation
|
||||||
|
VlAssocArray& set(const T_Key& index, const T_Value& value) {
|
||||||
|
at(index) = value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
VlAssocArray& setDefault(const T_Value& value) {
|
||||||
|
atDefault() = value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
// For save/restore
|
// For save/restore
|
||||||
const_iterator begin() const { return m_map.begin(); }
|
const_iterator begin() const { return m_map.begin(); }
|
||||||
const_iterator end() const { return m_map.end(); }
|
const_iterator end() const { return m_map.end(); }
|
||||||
|
|
|
||||||
|
|
@ -4599,6 +4599,49 @@ public:
|
||||||
virtual bool same(const AstNode* samep) const override { return true; }
|
virtual bool same(const AstNode* samep) const override { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AstConsAssoc : public AstNodeMath {
|
||||||
|
// Construct an assoc array and return object, '{}
|
||||||
|
// Parents: math
|
||||||
|
// Children: expression (elements or other queues)
|
||||||
|
public:
|
||||||
|
AstConsAssoc(FileLine* fl, AstNode* defaultp)
|
||||||
|
: ASTGEN_SUPER(fl) {
|
||||||
|
setNOp1p(defaultp);
|
||||||
|
}
|
||||||
|
ASTNODE_NODE_FUNCS(ConsAssoc)
|
||||||
|
virtual string emitVerilog() override { return "'{}"; }
|
||||||
|
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||||
|
virtual string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||||
|
virtual bool cleanOut() const override { return true; }
|
||||||
|
virtual int instrCount() const override { return widthInstrs(); }
|
||||||
|
AstNode* defaultp() const { return op1p(); }
|
||||||
|
virtual V3Hash sameHash() const override { return V3Hash(); }
|
||||||
|
virtual bool same(const AstNode* samep) const override { return true; }
|
||||||
|
};
|
||||||
|
class AstSetAssoc : public AstNodeMath {
|
||||||
|
// Set an assoc array element and return object, '{}
|
||||||
|
// Parents: math
|
||||||
|
// Children: expression (elements or other queues)
|
||||||
|
public:
|
||||||
|
AstSetAssoc(FileLine* fl, AstNode* lhsp, AstNode* keyp, AstNode* valuep)
|
||||||
|
: ASTGEN_SUPER(fl) {
|
||||||
|
setOp1p(lhsp);
|
||||||
|
setNOp2p(keyp);
|
||||||
|
setOp3p(valuep);
|
||||||
|
}
|
||||||
|
ASTNODE_NODE_FUNCS(SetAssoc)
|
||||||
|
virtual string emitVerilog() override { return "'{}"; }
|
||||||
|
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||||
|
virtual string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||||
|
virtual bool cleanOut() const override { return true; }
|
||||||
|
virtual int instrCount() const override { return widthInstrs(); }
|
||||||
|
AstNode* lhsp() const { return op1p(); }
|
||||||
|
AstNode* keyp() const { return op2p(); }
|
||||||
|
AstNode* valuep() const { return op3p(); }
|
||||||
|
virtual V3Hash sameHash() const override { return V3Hash(); }
|
||||||
|
virtual bool same(const AstNode* samep) const override { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
class AstConsQueue : public AstNodeMath {
|
class AstConsQueue : public AstNodeMath {
|
||||||
// Construct a queue and return object, '{}. '{lhs}, '{lhs. rhs}
|
// Construct a queue and return object, '{}. '{lhs}, '{lhs. rhs}
|
||||||
// Parents: math
|
// Parents: math
|
||||||
|
|
|
||||||
|
|
@ -1575,6 +1575,24 @@ class EmitCImp : EmitCStmts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void visit(AstConsAssoc* nodep) override {
|
||||||
|
putbs(nodep->dtypep()->cType("", false, false));
|
||||||
|
puts("()");
|
||||||
|
if (nodep->defaultp()) {
|
||||||
|
putbs(".setDefault(");
|
||||||
|
iterateAndNextNull(nodep->defaultp());
|
||||||
|
puts(")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual void visit(AstSetAssoc* nodep) override {
|
||||||
|
iterateAndNextNull(nodep->lhsp());
|
||||||
|
putbs(".set(");
|
||||||
|
iterateAndNextNull(nodep->keyp());
|
||||||
|
puts(", ");
|
||||||
|
putbs("");
|
||||||
|
iterateAndNextNull(nodep->valuep());
|
||||||
|
puts(")");
|
||||||
|
}
|
||||||
virtual void visit(AstConsQueue* nodep) override {
|
virtual void visit(AstConsQueue* nodep) override {
|
||||||
putbs(nodep->dtypep()->cType("", false, false));
|
putbs(nodep->dtypep()->cType("", false, false));
|
||||||
if (!nodep->lhsp()) {
|
if (!nodep->lhsp()) {
|
||||||
|
|
|
||||||
|
|
@ -1960,7 +1960,32 @@ private:
|
||||||
}
|
}
|
||||||
nodep->dtypeFrom(nodep->itemp());
|
nodep->dtypeFrom(nodep->itemp());
|
||||||
}
|
}
|
||||||
virtual void visit(AstConsQueue* nodep) override { //
|
virtual void visit(AstConsAssoc* nodep) override {
|
||||||
|
// Type computed when constructed here
|
||||||
|
auto* vdtypep = VN_CAST(m_vup->dtypep(), AssocArrayDType);
|
||||||
|
UASSERT_OBJ(vdtypep, nodep, "ConsAssoc requires assoc upper parent data type");
|
||||||
|
if (m_vup->prelim()) {
|
||||||
|
nodep->dtypeFrom(vdtypep);
|
||||||
|
if (nodep->defaultp()) {
|
||||||
|
iterateCheck(nodep, "default", nodep->defaultp(), CONTEXT, FINAL, vdtypep->subDTypep(),
|
||||||
|
EXTEND_EXP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual void visit(AstSetAssoc* nodep) override {
|
||||||
|
// Type computed when constructed here
|
||||||
|
auto* vdtypep = VN_CAST(m_vup->dtypep(), AssocArrayDType);
|
||||||
|
UASSERT_OBJ(vdtypep, nodep, "SetsAssoc requires assoc upper parent data type");
|
||||||
|
if (m_vup->prelim()) {
|
||||||
|
nodep->dtypeFrom(vdtypep);
|
||||||
|
userIterateAndNext(nodep->lhsp(), WidthVP(vdtypep, BOTH).p());
|
||||||
|
iterateCheck(nodep, "key", nodep->keyp(), CONTEXT, FINAL, vdtypep->keyDTypep(),
|
||||||
|
EXTEND_EXP);
|
||||||
|
iterateCheck(nodep, "value", nodep->valuep(), CONTEXT, FINAL, vdtypep->subDTypep(),
|
||||||
|
EXTEND_EXP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual void visit(AstConsQueue* nodep) override {
|
||||||
// Type computed when constructed here
|
// Type computed when constructed here
|
||||||
AstQueueDType* vdtypep = VN_CAST(m_vup->dtypep(), QueueDType);
|
AstQueueDType* vdtypep = VN_CAST(m_vup->dtypep(), QueueDType);
|
||||||
UASSERT_OBJ(vdtypep, nodep, "ConsQueue requires queue upper parent data type");
|
UASSERT_OBJ(vdtypep, nodep, "ConsQueue requires queue upper parent data type");
|
||||||
|
|
@ -2937,6 +2962,8 @@ private:
|
||||||
VL_DO_DANGLING(patternUOrStruct(nodep, vdtypep, defaultp), nodep);
|
VL_DO_DANGLING(patternUOrStruct(nodep, vdtypep, defaultp), nodep);
|
||||||
} else if (auto* vdtypep = VN_CAST(dtypep, NodeArrayDType)) {
|
} else if (auto* vdtypep = VN_CAST(dtypep, NodeArrayDType)) {
|
||||||
VL_DO_DANGLING(patternArray(nodep, vdtypep, defaultp), nodep);
|
VL_DO_DANGLING(patternArray(nodep, vdtypep, defaultp), nodep);
|
||||||
|
} else if (auto* vdtypep = VN_CAST(dtypep, AssocArrayDType)) {
|
||||||
|
VL_DO_DANGLING(patternAssoc(nodep, vdtypep, defaultp), nodep);
|
||||||
} else if (auto* vdtypep = VN_CAST(dtypep, QueueDType)) {
|
} else if (auto* vdtypep = VN_CAST(dtypep, QueueDType)) {
|
||||||
VL_DO_DANGLING(patternQueue(nodep, vdtypep, defaultp), nodep);
|
VL_DO_DANGLING(patternQueue(nodep, vdtypep, defaultp), nodep);
|
||||||
} else if (VN_IS(dtypep, BasicDType) && VN_CAST(dtypep, BasicDType)->isRanged()) {
|
} else if (VN_IS(dtypep, BasicDType) && VN_CAST(dtypep, BasicDType)->isRanged()) {
|
||||||
|
|
@ -3044,9 +3071,8 @@ private:
|
||||||
}
|
}
|
||||||
VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present
|
VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present
|
||||||
}
|
}
|
||||||
void patternArray(AstPattern* nodep, AstNodeArrayDType* vdtypep, AstPatMember* defaultp) {
|
void patternArray(AstPattern* nodep, AstNodeArrayDType* arrayDtp, AstPatMember* defaultp) {
|
||||||
AstNodeArrayDType* arrayp = VN_CAST(vdtypep, NodeArrayDType);
|
VNumRange range = arrayDtp->declRange();
|
||||||
VNumRange range = arrayp->declRange();
|
|
||||||
PatVecMap patmap = patVectorMap(nodep, range);
|
PatVecMap patmap = patVectorMap(nodep, range);
|
||||||
UINFO(9, "ent " << range.hi() << " to " << range.lo() << endl);
|
UINFO(9, "ent " << range.hi() << " to " << range.lo() << endl);
|
||||||
AstNode* newp = nullptr;
|
AstNode* newp = nullptr;
|
||||||
|
|
@ -3068,11 +3094,11 @@ private:
|
||||||
|
|
||||||
if (patp) {
|
if (patp) {
|
||||||
// Don't want the RHS an array
|
// Don't want the RHS an array
|
||||||
patp->dtypep(arrayp->subDTypep());
|
patp->dtypep(arrayDtp->subDTypep());
|
||||||
AstNode* valuep = patternMemberValueIterate(patp);
|
AstNode* valuep = patternMemberValueIterate(patp);
|
||||||
if (VN_IS(arrayp, UnpackArrayDType)) {
|
if (VN_IS(arrayDtp, UnpackArrayDType)) {
|
||||||
if (!newp) {
|
if (!newp) {
|
||||||
AstInitArray* newap = new AstInitArray(nodep->fileline(), arrayp, nullptr);
|
AstInitArray* newap = new AstInitArray(nodep->fileline(), arrayDtp, nullptr);
|
||||||
newp = newap;
|
newp = newap;
|
||||||
}
|
}
|
||||||
VN_CAST(newp, InitArray)->addIndexValuep(ent - range.lo(), valuep);
|
VN_CAST(newp, InitArray)->addIndexValuep(ent - range.lo(), valuep);
|
||||||
|
|
@ -3099,6 +3125,24 @@ private:
|
||||||
// if (debug() >= 9) newp->dumpTree("-apat-out: ");
|
// if (debug() >= 9) newp->dumpTree("-apat-out: ");
|
||||||
VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present
|
VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present
|
||||||
}
|
}
|
||||||
|
void patternAssoc(AstPattern* nodep, AstAssocArrayDType* arrayDtp, AstPatMember* defaultp) {
|
||||||
|
AstNode* defaultValuep = nullptr;
|
||||||
|
if (defaultp) defaultValuep = defaultp->lhssp()->unlinkFrBack();
|
||||||
|
AstNode* newp = new AstConsAssoc(nodep->fileline(), defaultValuep);
|
||||||
|
newp->dtypeFrom(arrayDtp);
|
||||||
|
for (AstPatMember* patp = VN_CAST(nodep->itemsp(), PatMember); patp;
|
||||||
|
patp = VN_CAST(patp->nextp(), PatMember)) {
|
||||||
|
patp->dtypep(arrayDtp->subDTypep());
|
||||||
|
AstNode* valuep = patternMemberValueIterate(patp);
|
||||||
|
AstNode* keyp = patp->keyp();
|
||||||
|
auto* newap = new AstSetAssoc(nodep->fileline(), newp, keyp->unlinkFrBack(), valuep);
|
||||||
|
newap->dtypeFrom(arrayDtp);
|
||||||
|
newp = newap;
|
||||||
|
}
|
||||||
|
nodep->replaceWith(newp);
|
||||||
|
// if (debug() >= 9) newp->dumpTree("-apat-out: ");
|
||||||
|
VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present
|
||||||
|
}
|
||||||
void patternQueue(AstPattern* nodep, AstQueueDType* arrayp, AstPatMember* defaultp) {
|
void patternQueue(AstPattern* nodep, AstQueueDType* arrayp, AstPatMember* defaultp) {
|
||||||
AstNode* newp = new AstConsQueue(nodep->fileline());
|
AstNode* newp = new AstConsQueue(nodep->fileline());
|
||||||
newp->dtypeFrom(arrayp);
|
newp->dtypeFrom(arrayp);
|
||||||
|
|
|
||||||
|
|
@ -82,13 +82,14 @@ module t (/*AUTOARG*/
|
||||||
i = a.last(k); `checkh(i, 0);
|
i = a.last(k); `checkh(i, 0);
|
||||||
|
|
||||||
// Patterns & default
|
// Patterns & default
|
||||||
`ifndef VERILATOR // Unsupported: Pattern assignment
|
|
||||||
a = '{ "f": "fooed", "b": "bared", default: "defaulted" };
|
a = '{ "f": "fooed", "b": "bared", default: "defaulted" };
|
||||||
i = a.size(); `checkh(i, 2); // Default doesn't count
|
i = a.size(); `checkh(i, 2); // Default doesn't count
|
||||||
v = a["f"]; `checks(v, "fooed");
|
v = a["f"]; `checks(v, "fooed");
|
||||||
v = a["b"]; `checks(v, "bared");
|
v = a["b"]; `checks(v, "bared");
|
||||||
v = a["NEXISTS"]; `checks(v, "defaulted");
|
v = a["NEXISTS"]; `checks(v, "defaulted");
|
||||||
`endif
|
|
||||||
|
a = '{};
|
||||||
|
i = a.size(); `checkh(i, 0);
|
||||||
end
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
%Error-UNSUPPORTED: t/t_assoc_pattern_unsup.v:19:11: Unsupported: Assignment pattern applies against non struct/union data type: 'string[string]'
|
|
||||||
: ... In instance t
|
|
||||||
19 | a = '{ "f": "fooed", "b": "bared", default: "defaulted" };
|
|
||||||
| ^~
|
|
||||||
%Error: Exiting due to
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
#!/usr/bin/env perl
|
|
||||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
|
||||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
|
||||||
#
|
|
||||||
# Copyright 2019 by Wilson Snyder. This program is free software; you
|
|
||||||
# can redistribute it and/or modify it under the terms of either the GNU
|
|
||||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
|
||||||
# Version 2.0.
|
|
||||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
|
||||||
|
|
||||||
scenarios(vlt => 1);
|
|
||||||
|
|
||||||
lint(
|
|
||||||
fails => 1,
|
|
||||||
expect_filename => $Self->{golden_filename},
|
|
||||||
);
|
|
||||||
|
|
||||||
ok(1);
|
|
||||||
1;
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
// DESCRIPTION: Verilator: Verilog Test module
|
|
||||||
//
|
|
||||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
|
||||||
// any use, without warranty, 2019 by Wilson Snyder.
|
|
||||||
// SPDX-License-Identifier: CC0-1.0
|
|
||||||
|
|
||||||
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
|
||||||
`define checks(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
|
||||||
`define checkg(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%g' exp='%g'\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
|
||||||
|
|
||||||
module t (/*AUTOARG*/);
|
|
||||||
|
|
||||||
initial begin
|
|
||||||
int i;
|
|
||||||
string a [string];
|
|
||||||
string k;
|
|
||||||
string v;
|
|
||||||
|
|
||||||
a = '{ "f": "fooed", "b": "bared", default: "defaulted" };
|
|
||||||
i = a.size(); `checkh(i, 2); // Default doesn't count
|
|
||||||
v = a["f"]; `checks(v, "fooed");
|
|
||||||
v = a["b"]; `checks(v, "bared");
|
|
||||||
v = a["NEXISTS"]; `checks(v, "defaulted");
|
|
||||||
|
|
||||||
$write("*-* All Finished *-*\n");
|
|
||||||
$finish;
|
|
||||||
end
|
|
||||||
endmodule
|
|
||||||
Loading…
Reference in New Issue