Support associative array pattern assignments and defaults.

This commit is contained in:
Wilson Snyder 2020-10-25 21:05:22 -04:00
parent 046c0a7aa1
commit 95d1272269
9 changed files with 127 additions and 61 deletions

View File

@ -6,6 +6,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
**** Support queue slicing (#2326).
**** Support associative array pattern assignments and defaults.
**** Fix WIDTH warnings on comparisons with nullptr (#2602). [Rupert Swarbrick]

View File

@ -196,6 +196,16 @@ public:
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
const_iterator begin() const { return m_map.begin(); }
const_iterator end() const { return m_map.end(); }

View File

@ -4599,6 +4599,49 @@ public:
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 {
// Construct a queue and return object, '{}. '{lhs}, '{lhs. rhs}
// Parents: math

View File

@ -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 {
putbs(nodep->dtypep()->cType("", false, false));
if (!nodep->lhsp()) {

View File

@ -1960,7 +1960,32 @@ private:
}
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
AstQueueDType* vdtypep = VN_CAST(m_vup->dtypep(), QueueDType);
UASSERT_OBJ(vdtypep, nodep, "ConsQueue requires queue upper parent data type");
@ -2937,6 +2962,8 @@ private:
VL_DO_DANGLING(patternUOrStruct(nodep, vdtypep, defaultp), nodep);
} else if (auto* vdtypep = VN_CAST(dtypep, NodeArrayDType)) {
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)) {
VL_DO_DANGLING(patternQueue(nodep, vdtypep, defaultp), nodep);
} 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
}
void patternArray(AstPattern* nodep, AstNodeArrayDType* vdtypep, AstPatMember* defaultp) {
AstNodeArrayDType* arrayp = VN_CAST(vdtypep, NodeArrayDType);
VNumRange range = arrayp->declRange();
void patternArray(AstPattern* nodep, AstNodeArrayDType* arrayDtp, AstPatMember* defaultp) {
VNumRange range = arrayDtp->declRange();
PatVecMap patmap = patVectorMap(nodep, range);
UINFO(9, "ent " << range.hi() << " to " << range.lo() << endl);
AstNode* newp = nullptr;
@ -3068,11 +3094,11 @@ private:
if (patp) {
// Don't want the RHS an array
patp->dtypep(arrayp->subDTypep());
patp->dtypep(arrayDtp->subDTypep());
AstNode* valuep = patternMemberValueIterate(patp);
if (VN_IS(arrayp, UnpackArrayDType)) {
if (VN_IS(arrayDtp, UnpackArrayDType)) {
if (!newp) {
AstInitArray* newap = new AstInitArray(nodep->fileline(), arrayp, nullptr);
AstInitArray* newap = new AstInitArray(nodep->fileline(), arrayDtp, nullptr);
newp = newap;
}
VN_CAST(newp, InitArray)->addIndexValuep(ent - range.lo(), valuep);
@ -3099,6 +3125,24 @@ private:
// if (debug() >= 9) newp->dumpTree("-apat-out: ");
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) {
AstNode* newp = new AstConsQueue(nodep->fileline());
newp->dtypeFrom(arrayp);

View File

@ -82,13 +82,14 @@ module t (/*AUTOARG*/
i = a.last(k); `checkh(i, 0);
// Patterns & default
`ifndef VERILATOR // Unsupported: Pattern assignment
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");
`endif
a = '{};
i = a.size(); `checkh(i, 0);
end
begin

View File

@ -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

View File

@ -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;

View File

@ -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