Internals: Simplify AstForeach header handling (#7126)

Rename AstSelLoopVars to AstForeachHeader, and make it a non-NodeExpr.
Tweak parser to always create an AstForeachHeader, so no need to fix it
up later.
This commit is contained in:
Geza Lore 2026-02-22 18:57:12 +00:00 committed by GitHub
parent e238a2ca5e
commit da51021b0a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 110 additions and 158 deletions

View File

@ -52,8 +52,8 @@ public:
// METHODS
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
// TODO: The only AstNodeExpr without dtype is AstArg. Otherwise this could be final.
bool hasDType() const override VL_MT_SAFE { return true; }
// Every expression must have a data type after V3Width
bool hasDType() const override final VL_MT_SAFE { return true; }
virtual string emitVerilog() = 0; /// Format string for verilog writing; see V3EmitV
// For documentation on emitC format see EmitCFunc::emitOpName
virtual string emitC() = 0;
@ -746,7 +746,6 @@ public:
this->rhsp(rhsp);
}
ASTGEN_MEMBERS_AstCastSize;
// No hasDType because widthing removes this node before the hasDType check
string emitVerilog() override { return "((%r)'(%l))"; }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
@ -2273,27 +2272,6 @@ public:
// Name for __Vscopep variable including children
string scopePrettyDpiName() const { return scopePrettyNameFormatter(m_scopeEntr); }
};
class AstSelLoopVars final : public AstNodeExpr {
// Parser only concept "[id, id, id]" for a foreach statement
// Unlike normal selects elements is a list
// TODO: Should not be an AstNodeExpr, model foreach better
// @astgen op1 := fromp : AstNodeExpr
// @astgen op2 := elementsp : List[AstNode]
public:
AstSelLoopVars(FileLine* fl, AstNodeExpr* fromp, AstNode* elementsp)
: ASTGEN_SUPER_SelLoopVars(fl) {
this->fromp(fromp);
addElementsp(elementsp);
}
ASTGEN_MEMBERS_AstSelLoopVars;
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return false; }
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
string emitC() override { V3ERROR_NA_RETURN(""); }
bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
bool hasDType() const override VL_MT_SAFE { return false; }
};
class AstSetAssoc final : public AstNodeExpr {
// Set an assoc array element and return object, '{}
// @astgen op1 := lhsp : AstNode

View File

@ -126,13 +126,13 @@ public:
}
};
class AstNodeForeach VL_NOT_FINAL : public AstNodeStmt {
// @astgen op1 := arrayp : AstNode
// @astgen op2 := stmtsp : List[AstNode]
// @astgen op1 := headerp : AstForeachHeader
// @astgen op2 := bodyp : List[AstNode]
public:
AstNodeForeach(VNType t, FileLine* fl, AstNode* arrayp, AstNode* stmtsp)
AstNodeForeach(VNType t, FileLine* fl, AstForeachHeader* headerp, AstNode* bodyp)
: AstNodeStmt(t, fl) {
this->arrayp(arrayp);
addStmtsp(stmtsp);
this->headerp(headerp);
addBodyp(bodyp);
}
ASTGEN_MEMBERS_AstNodeForeach;
bool isGateOptimizable() const override { return false; }
@ -213,6 +213,23 @@ public:
bool isDefault() const { return condsp() == nullptr; }
};
class AstForeachHeader final : public AstNode {
// Variable reference + index enumeration "ref [id, id, id]" for a foreach statement
// @astgen op1 := fromp : AstNodeExpr
// @astgen op2 := elementsp : List[AstNode<AstNodeExpr|AstVar|AstEmpty>]
// AstNodeExpr/AstEmpty during parsing (only AstParseRef/AstEmpty is well formed)
// then AstVar/AstEmpty after LinkDot
public:
AstForeachHeader(FileLine* fl, AstNodeExpr* fromp, AstNode* elementsp)
: ASTGEN_SUPER_ForeachHeader(fl) {
this->fromp(fromp);
addElementsp(elementsp);
}
ASTGEN_MEMBERS_AstForeachHeader;
bool sameNode(const AstNode* /*samep*/) const override { return true; }
bool maybePointedTo() const override VL_MT_SAFE { return false; }
};
// === AstNodeStmt ===
class AstAssertCtl final : public AstNodeStmt {
// @astgen op1 := controlTypep : AstNodeExpr
@ -1485,14 +1502,14 @@ public:
class AstConstraintForeach final : public AstNodeForeach {
// Constraint foreach statement
public:
AstConstraintForeach(FileLine* fl, AstNodeExpr* exprp, AstNode* bodysp)
: ASTGEN_SUPER_ConstraintForeach(fl, exprp, bodysp) {}
AstConstraintForeach(FileLine* fl, AstForeachHeader* headerp, AstNode* bodyp)
: ASTGEN_SUPER_ConstraintForeach(fl, headerp, bodyp) {}
ASTGEN_MEMBERS_AstConstraintForeach;
};
class AstForeach final : public AstNodeForeach {
public:
AstForeach(FileLine* fl, AstNode* arrayp, AstNode* stmtsp)
: ASTGEN_SUPER_Foreach(fl, arrayp, stmtsp) {}
AstForeach(FileLine* fl, AstForeachHeader* headerp, AstNode* stmtsp)
: ASTGEN_SUPER_Foreach(fl, headerp, stmtsp) {}
ASTGEN_MEMBERS_AstForeach;
};

View File

@ -471,9 +471,8 @@ static AstNode* createForeachLoopRanged(AstNodeForeach* nodep, AstNode* bodysp,
}
AstNode* V3Begin::convertToWhile(AstForeach* nodep) {
// UINFOTREE(1, nodep, "", "foreach-old");
const AstSelLoopVars* const loopsp = VN_CAST(nodep->arrayp(), SelLoopVars);
UASSERT_OBJ(loopsp, nodep, "No loop variables under foreach");
AstNodeExpr* const fromp = loopsp->fromp();
const AstForeachHeader* const headerp = nodep->headerp();
AstNodeExpr* const fromp = headerp->fromp();
UASSERT_OBJ(fromp->dtypep(), fromp, "Missing data type");
AstNodeDType* fromDtp = fromp->dtypep()->skipRefp();
// Split into for loop
@ -486,7 +485,7 @@ AstNode* V3Begin::convertToWhile(AstForeach* nodep) {
// dyn-arr and associative-arr)
AstNodeExpr* subfromp = fromp->cloneTreePure(false);
// Major dimension first
for (AstNode *argsp = loopsp->elementsp(), *next_argsp; argsp; argsp = next_argsp) {
for (AstNode *argsp = headerp->elementsp(), *next_argsp; argsp; argsp = next_argsp) {
next_argsp = argsp->nextp();
const bool empty = VN_IS(argsp, Empty);
AstVar* const varp = VN_CAST(argsp, Var);
@ -578,7 +577,7 @@ AstNode* V3Begin::convertToWhile(AstForeach* nodep) {
}
VL_DO_DANGLING(subfromp->deleteTree(), subfromp);
// The parser validates we don't have "foreach (array[,,,])"
AstNode* const bodyp = nodep->stmtsp();
AstNode* const bodyp = nodep->bodyp();
if (!newp) {
nodep->v3warn(NOEFFECT, "foreach with no loop variable has no effect");
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);

View File

@ -193,6 +193,8 @@ private:
UASSERT_OBJ(nodep->dtypep(), nodep,
"No dtype on node with hasDType(): " << nodep->prettyTypeName());
} else {
UASSERT_OBJ(!VN_IS(nodep, NodeExpr), nodep,
"All AstNodeExpr must have a dtype post V3WidthCommit");
UASSERT_OBJ(!nodep->dtypep(), nodep,
"DType on node without hasDType(): " << nodep->prettyTypeName());
}

View File

@ -81,7 +81,7 @@ class DeadVisitor final : public VNVisitor {
bool m_inAssign = false; // Currently in an assign
AstNodeDType* m_curDTypep = nullptr; // Current NodeDType
AstNodeModule* m_modp = nullptr; // Current module
AstSelLoopVars* m_selloopvarsp = nullptr; // Current loop vars
AstForeachHeader* m_foreachHeaderp = nullptr; // Current foreach header
// STATE - Statistic tracking
VDouble0 m_statFTasksDeadified;
@ -268,10 +268,10 @@ class DeadVisitor final : public VNVisitor {
}
checkAll(nodep);
}
void visit(AstSelLoopVars* nodep) override {
// Var under a SelLoopVars means we haven't called V3Width to remove them yet
VL_RESTORER(m_selloopvarsp);
m_selloopvarsp = nodep;
void visit(AstForeachHeader* nodep) override {
// Var under a ForeachHeader means we haven't called V3Width to remove them yet
VL_RESTORER(m_foreachHeaderp);
m_foreachHeaderp = nodep;
iterateChildren(nodep);
checkAll(nodep);
}
@ -292,7 +292,7 @@ class DeadVisitor final : public VNVisitor {
void visit(AstVar* nodep) override {
iterateChildren(nodep);
checkAll(nodep);
if (m_selloopvarsp) nodep->user1Inc();
if (m_foreachHeaderp) nodep->user1Inc();
if (mightElimVar(nodep)) {
m_varsp.push_back(nodep);
} else {

View File

@ -347,7 +347,7 @@ class HasherVisitor final : public VNVisitorConst {
void visit(AstClassExtends* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {});
}
void visit(AstSelLoopVars* nodep) override {
void visit(AstForeachHeader* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, []() {});
}
void visit(AstDefParam* nodep) override {

View File

@ -1910,29 +1910,7 @@ class LinkDotFindVisitor final : public VNVisitor {
m_curSymp = m_statep->insertBlock(m_curSymp, "__Vforeach" + cvtToStr(m_modWithNum),
nodep, m_classOrPackagep);
m_curSymp->fallbackp(VL_RESTORER_PREV(m_curSymp));
// DOT(x, SELLOOPVARS(var, loops)) -> SELLOOPVARS(DOT(x, var), loops)
if (AstDot* const dotp = VN_CAST(nodep->arrayp(), Dot)) {
AstDot* dotAbovep = dotp;
while (AstDot* const dotNextp = VN_CAST(dotAbovep->rhsp(), Dot)) {
dotAbovep = dotNextp;
}
if (AstSelLoopVars* const loopvarsp = VN_CAST(dotAbovep->rhsp(), SelLoopVars)) {
AstNodeExpr* const fromp = loopvarsp->fromp()->unlinkFrBack();
loopvarsp->unlinkFrBack();
dotp->replaceWith(loopvarsp);
dotAbovep->rhsp(fromp);
loopvarsp->fromp(dotp);
}
}
const auto loopvarsp = VN_CAST(nodep->arrayp(), SelLoopVars);
if (!loopvarsp) {
AstNode* const warnp = nodep->arrayp() ? nodep->arrayp() : nodep;
warnp->v3warn(E_UNSUPPORTED,
"Unsupported (or syntax error): Foreach on this array's construct");
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
return;
}
for (AstNode *nextp, *argp = loopvarsp->elementsp(); argp; argp = nextp) {
for (AstNode *nextp, *argp = nodep->headerp()->elementsp(); argp; argp = nextp) {
nextp = argp->nextp();
AstVar* argrefp = nullptr;
if (AstParseRef* const parserefp = VN_CAST(argp, ParseRef)) {

View File

@ -89,7 +89,7 @@ class LinkJumpVisitor final : public VNVisitor {
underp = fTaskp->stmtsp();
} else if (AstForeach* const foreachp = VN_CAST(nodep, Foreach)) {
if (endOfIter) {
underp = foreachp->stmtsp();
underp = foreachp->bodyp();
// Keep a LoopTest **at the front** outside the jump block
if (VN_IS(underp, LoopTest)) underp = underp->nextp();
} else {
@ -351,7 +351,7 @@ class LinkJumpVisitor final : public VNVisitor {
void visit(AstNodeForeach* nodep) override {
VL_RESTORER(m_loopp);
m_loopp = nodep;
iterateAndNextNull(nodep->stmtsp());
iterateAndNextNull(nodep->bodyp());
}
void visit(AstReturn* nodep) override {
iterateChildren(nodep);

View File

@ -594,18 +594,8 @@ class LinkParseVisitor final : public VNVisitor {
// 4. DOT(DOT(first, second), ASTSELBIT(third, var0))
VL_RESTORER(m_insideLoop);
m_insideLoop = true;
AstNode* bracketp = nodep->arrayp();
while (AstDot* dotp = VN_CAST(bracketp, Dot)) bracketp = dotp->rhsp();
if (AstSelBit* const selp = VN_CAST(bracketp, SelBit)) {
// Convert to AstSelLoopVars so V3LinkDot knows what's being defined
AstNode* const newp
= new AstSelLoopVars{selp->fileline(), selp->fromp()->unlinkFrBack(),
selp->bitp()->unlinkFrBackWithNext()};
selp->replaceWith(newp);
VL_DO_DANGLING2(selp->deleteTree(), selp, bracketp);
} else if (VN_IS(bracketp, SelLoopVars)) {
// Ok
} else {
AstForeachHeader* const headerp = nodep->headerp();
if (!headerp->elementsp()) {
nodep->v3error("Foreach missing bracketed loop variable is no-operation"
" (IEEE 1800-2023 12.7.3)");
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);

View File

@ -1738,10 +1738,10 @@ class ConstraintExprVisitor final : public VNVisitor {
// Convert to plain foreach
FileLine* const fl = nodep->fileline();
if (!nodep->stmtsp()) {
if (!nodep->bodyp()) {
nodep->unlinkFrBack();
} else if (m_wantSingle) {
AstNodeExpr* const itemp = editSingle(fl, nodep->stmtsp());
AstNodeExpr* const itemp = editSingle(fl, nodep->bodyp());
AstCStmt* const cstmtp = new AstCStmt{fl};
cstmtp->add("ret += \" \";\n");
cstmtp->add("ret += ");
@ -1751,16 +1751,15 @@ class ConstraintExprVisitor final : public VNVisitor {
cexprp->dtypeSetString();
cexprp->add("([&]{\nstd::string ret;\n");
cexprp->add(new AstBegin{
fl, "", new AstForeach{fl, nodep->arrayp()->unlinkFrBack(), cstmtp}, true});
fl, "", new AstForeach{fl, nodep->headerp()->unlinkFrBack(), cstmtp}, true});
cexprp->add("return ret.empty() ? \"#b1\" : \"(bvand\" + ret + \")\";\n})()");
nodep->replaceWith(new AstSFormatF{fl, "%@", false, cexprp});
} else {
iterateAndNextNull(nodep->stmtsp());
nodep->replaceWith(
new AstBegin{fl, "",
new AstForeach{fl, nodep->arrayp()->unlinkFrBack(),
nodep->stmtsp()->unlinkFrBackWithNext()},
true});
iterateAndNextNull(nodep->bodyp());
nodep->replaceWith(new AstBegin{fl, "",
new AstForeach{fl, nodep->headerp()->unlinkFrBack(),
nodep->bodyp()->unlinkFrBackWithNext()},
true});
}
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
@ -1962,8 +1961,8 @@ class ConstraintExprVisitor final : public VNVisitor {
AstVar* const newVarp
= new AstVar{fl, VVarType::BLOCKTEMP, "__Vinside", nodep->findSigned32DType()};
AstNodeExpr* const idxRefp = new AstVarRef{nodep->fileline(), newVarp, VAccess::READ};
AstSelLoopVars* const arrayp
= new AstSelLoopVars{fl, nodep->fromp()->cloneTreePure(false), newVarp};
AstForeachHeader* const headerp
= new AstForeachHeader{fl, nodep->fromp()->cloneTreePure(false), newVarp};
AstNodeExpr* const selp = newSel(nodep->fileline(), nodep->fromp(), idxRefp);
selp->user1(randArr);
AstNode* const itemp = new AstEq{fl, selp, nodep->pinsp()->unlinkFrBack()};
@ -1977,7 +1976,7 @@ class ConstraintExprVisitor final : public VNVisitor {
AstCExpr* const cexprp = new AstCExpr{fl};
cexprp->dtypeSetString();
cexprp->add("([&]{\nstd::string ret;\n");
cexprp->add(new AstBegin{fl, "", new AstForeach{fl, arrayp, cstmtp}, true});
cexprp->add(new AstBegin{fl, "", new AstForeach{fl, headerp, cstmtp}, true});
cexprp->add("return ret.empty() ? \"#b0\" : \"(bvor\" + ret + \")\";\n})()");
nodep->replaceWith(new AstSFormatF{fl, "%@", false, cexprp});
VL_DO_DANGLING(nodep->deleteTree(), nodep);
@ -2022,8 +2021,8 @@ class ConstraintExprVisitor final : public VNVisitor {
AstVar* const newVarp
= new AstVar{fl, VVarType::BLOCKTEMP, "__Vreduce", nodep->findSigned32DType()};
AstSelLoopVars* const arrayp
= new AstSelLoopVars{fl, nodep->fromp()->cloneTreePure(false), newVarp};
AstForeachHeader* const headerp
= new AstForeachHeader{fl, nodep->fromp()->cloneTreePure(false), newVarp};
// Foreach body: register element as scalar solver var + append name
AstCStmt* const cstmtp = new AstCStmt{fl};
@ -2045,7 +2044,7 @@ class ConstraintExprVisitor final : public VNVisitor {
AstCExpr* const cexprp = new AstCExpr{fl};
cexprp->dtypeSetString();
cexprp->add("([&]{\nstd::string ret;\n");
cexprp->add(new AstBegin{fl, "", new AstForeach{fl, arrayp, cstmtp}, true});
cexprp->add(new AstBegin{fl, "", new AstForeach{fl, headerp, cstmtp}, true});
const char* smtOp = nullptr;
std::string identity;
@ -2243,7 +2242,7 @@ class CaptureVisitor final : public VNVisitor {
const bool varIsFieldOfCaller = AstClass::isClassExtendedFrom(callerClassp, varClassp);
const bool varIsParam = varRefp->varp()->isParam();
const bool varIsConstraintIterator
= VN_IS(varRefp->varp()->firstAbovep(), SelLoopVars)
= VN_IS(varRefp->varp()->firstAbovep(), ForeachHeader)
&& VN_IS(varRefp->varp()->firstAbovep()->firstAbovep(), ConstraintForeach);
if (refIsXref) return CaptureMode::CAP_VALUE | CaptureMode::CAP_F_XREF;
if (varIsConstraintIterator) return CaptureMode::CAP_NO;
@ -2939,12 +2938,12 @@ class RandomizeVisitor final : public VNVisitor {
tempDTypep = tempDTypep->virtRefDTypep();
}
AstSelLoopVars* const randLoopVarp
= new AstSelLoopVars{fl, exprp->cloneTree(false), randLoopIndxp};
AstForeachHeader* const headerp
= new AstForeachHeader{fl, exprp->cloneTree(false), randLoopIndxp};
AstNodeStmt* const randStmtsp = newRandStmtsp(fl, tempElementp, nullptr, outputVarp);
// TODO: we should just not clone in 'newRandStmtsp' if not necessary
if (!tempElementp->backp()) VL_DO_DANGLING(pushDeletep(tempElementp), tempElementp);
return new AstForeach{fl, randLoopVarp, randStmtsp};
return new AstForeach{fl, headerp, randStmtsp};
}
AstNodeStmt* newRandStmtsp(FileLine* fl, AstNodeExpr* exprp, AstVar* randcVarp,
AstVar* const outputVarp, int offset = 0,

View File

@ -5658,15 +5658,14 @@ class WidthVisitor final : public VNVisitor {
void visit(AstNodeForeach* nodep) override {
if (nodep->didWidth()) return;
nodep->didWidth(true);
const AstSelLoopVars* const loopsp = VN_CAST(nodep->arrayp(), SelLoopVars);
UASSERT_OBJ(loopsp, nodep, "No loop variables under foreach");
const AstForeachHeader* const headerp = nodep->headerp();
// UINFOTREE(1, nodep, "", "foreach-old");
userIterateAndNext(loopsp->fromp(), WidthVP{SELF, BOTH}.p());
AstNodeExpr* const fromp = loopsp->fromp();
userIterateAndNext(headerp->fromp(), WidthVP{SELF, BOTH}.p());
AstNodeExpr* const fromp = headerp->fromp();
UASSERT_OBJ(fromp->dtypep(), fromp, "Missing data type");
AstNodeDType* fromDtp = fromp->dtypep()->skipRefp();
// Major dimension first
for (AstNode *argsp = loopsp->elementsp(), *next_argsp; argsp; argsp = next_argsp) {
for (AstNode *argsp = headerp->elementsp(), *next_argsp; argsp; argsp = next_argsp) {
next_argsp = argsp->nextp();
const bool empty = VN_IS(argsp, Empty);
AstVar* const varp = VN_CAST(argsp, Var);
@ -5704,7 +5703,7 @@ class WidthVisitor final : public VNVisitor {
fromDtp = fromDtp->subDTypep();
}
// The parser validates we don't have "foreach (array[,,,])"
AstNode* const bodyp = nodep->stmtsp();
AstNode* const bodyp = nodep->bodyp();
userIterateAndNext(bodyp, nullptr);
if (AstForeach* const loopp = VN_CAST(nodep, Foreach)) {
VL_DO_DANGLING2(V3Begin::convertToWhile(loopp), loopp, nodep);

View File

@ -2927,48 +2927,48 @@ c_loop_generate_construct<nodep>: // IEEE: loop_generate_construct (for checker
;
genvar_initialization<nodep>: // ==IEEE: genvar_initialization
varRefBase '=' expr { $$ = new AstAssign{$2, $1, $3}; }
parseRefBase '=' expr { $$ = new AstAssign{$2, $1, $3}; }
| yGENVAR genvar_identifierDecl '=' constExpr
{ $$ = $2; AstNode::addNext<AstNode, AstNode>($$,
new AstAssign{$3, new AstVarRef{$2->fileline(), $2, VAccess::WRITE}, $4}); }
;
genvar_iteration<nodep>: // ==IEEE: genvar_iteration
varRefBase '=' expr
parseRefBase '=' expr
{ $$ = new AstAssign{$2, $1, $3}; }
| varRefBase yP_PLUSEQ expr
| parseRefBase yP_PLUSEQ expr
{ $$ = new AstAssign{$2, $1, new AstAdd{$2, $1->cloneTreePure(true), $3}}; }
| varRefBase yP_MINUSEQ expr
| parseRefBase yP_MINUSEQ expr
{ $$ = new AstAssign{$2, $1, new AstSub{$2, $1->cloneTreePure(true), $3}}; }
| varRefBase yP_TIMESEQ expr
| parseRefBase yP_TIMESEQ expr
{ $$ = new AstAssign{$2, $1, new AstMul{$2, $1->cloneTreePure(true), $3}}; }
| varRefBase yP_DIVEQ expr
| parseRefBase yP_DIVEQ expr
{ $$ = new AstAssign{$2, $1, new AstDiv{$2, $1->cloneTreePure(true), $3}}; }
| varRefBase yP_MODEQ expr
| parseRefBase yP_MODEQ expr
{ $$ = new AstAssign{$2, $1, new AstModDiv{$2, $1->cloneTreePure(true), $3}}; }
| varRefBase yP_ANDEQ expr
| parseRefBase yP_ANDEQ expr
{ $$ = new AstAssign{$2, $1, new AstAnd{$2, $1->cloneTreePure(true), $3}}; }
| varRefBase yP_OREQ expr
| parseRefBase yP_OREQ expr
{ $$ = new AstAssign{$2, $1, new AstOr{$2, $1->cloneTreePure(true), $3}}; }
| varRefBase yP_XOREQ expr
| parseRefBase yP_XOREQ expr
{ $$ = new AstAssign{$2, $1, new AstXor{$2, $1->cloneTreePure(true), $3}}; }
| varRefBase yP_SLEFTEQ expr
| parseRefBase yP_SLEFTEQ expr
{ $$ = new AstAssign{$2, $1, new AstShiftL{$2, $1->cloneTreePure(true), $3}}; }
| varRefBase yP_SRIGHTEQ expr
| parseRefBase yP_SRIGHTEQ expr
{ $$ = new AstAssign{$2, $1, new AstShiftR{$2, $1->cloneTreePure(true), $3}}; }
| varRefBase yP_SSRIGHTEQ expr
| parseRefBase yP_SSRIGHTEQ expr
{ $$ = new AstAssign{$2, $1, new AstShiftRS{$2, $1->cloneTreePure(true), $3}}; }
// // inc_or_dec_operator
| yP_PLUSPLUS varRefBase
| yP_PLUSPLUS parseRefBase
{ $$ = new AstAssign{$1, $2, new AstAdd{$1, $2->cloneTreePure(true),
new AstConst{$1, AstConst::StringToParse{}, "'b1"}}}; }
| yP_MINUSMINUS varRefBase
| yP_MINUSMINUS parseRefBase
{ $$ = new AstAssign{$1, $2, new AstSub{$1, $2->cloneTreePure(true),
new AstConst{$1, AstConst::StringToParse{}, "'b1"}}}; }
| varRefBase yP_PLUSPLUS
| parseRefBase yP_PLUSPLUS
{ $$ = new AstAssign{$2, $1, new AstAdd{$2, $1->cloneTreePure(true),
new AstConst{$2, AstConst::StringToParse{}, "'b1"}}}; }
| varRefBase yP_MINUSMINUS
| parseRefBase yP_MINUSMINUS
{ $$ = new AstAssign{$2, $1, new AstSub{$2, $1->cloneTreePure(true),
new AstConst{$2, AstConst::StringToParse{}, "'b1"}}}; }
;
@ -6125,17 +6125,18 @@ idClassSel<nodeExprp>: // Misc Ref to dotted, and/or arrayed, and/or bi
| packageClassScope idDottedSel { $$ = new AstDot{$<fl>2, true, $1, $2}; }
;
idClassSelForeach<nodeExprp>:
idClassSelForeach<foreachHeaderp>:
idDottedForeach { $$ = $1; }
// // IEEE: [ implicit_class_handle . | package_scope ] hierarchical_variable_identifier select
| yTHIS '.' idDottedForeach
{ $$ = new AstDot{$2, false, new AstParseRef{$<fl>1, "this"}, $3}; }
{ $3->fromp(new AstDot{$2, false, new AstParseRef{$<fl>1, "this"}, $3->fromp()->unlinkFrBack()}); $$ = $3; }
| ySUPER '.' idDottedForeach
{ $$ = new AstDot{$2, false, new AstParseRef{$<fl>1, "super"}, $3}; }
{ $3->fromp(new AstDot{$2, false, new AstParseRef{$<fl>1, "super"}, $3->fromp()->unlinkFrBack()}); $$ = $3; }
| yTHIS '.' ySUPER '.' idDottedForeach
{ $$ = new AstDot{$4, false, new AstParseRef{$<fl>3, "super"}, $5}; }
{ $5->fromp(new AstDot{$4, false, new AstParseRef{$<fl>3, "super"}, $5->fromp()->unlinkFrBack()}); $$ = $5; }
// // Expanded: package_scope idForeach
| packageClassScope idDottedForeach { $$ = new AstDot{$<fl>2, true, $1, $2}; }
| packageClassScope idDottedForeach
{ $2->fromp(new AstDot{$<fl>2, true, $1, $2->fromp()->unlinkFrBack()}); $$ = $2; }
;
@ -6160,15 +6161,15 @@ idDottedSel<nodeExprp>:
| idDottedSelMore { $$ = $1; }
;
idDottedForeach<nodeExprp>:
idDottedForeach<foreachHeaderp>:
yD_ROOT '.' idDottedMoreForeach
{ $$ = new AstDot{$2, false, new AstParseRef{$<fl>1, "$root"}, $3}; }
{ $3->fromp(new AstDot{$2, false, new AstParseRef{$<fl>1, "$root"}, $3->fromp()->unlinkFrBack()}); $$ = $3; }
| idDottedMoreForeach { $$ = $1; }
;
idDottedMore<nodeExprp>:
varRefBase { $$ = $1; }
| idDottedMore '.' varRefBase { $$ = new AstDot{$2, false, $1, $3}; }
parseRefBase { $$ = $1; }
| idDottedMore '.' parseRefBase { $$ = new AstDot{$2, false, $1, $3}; }
;
idDottedSelMore<nodeExprp>:
@ -6176,9 +6177,10 @@ idDottedSelMore<nodeExprp>:
| idDottedSelMore '.' idArrayed { $$ = new AstDot{$2, false, $1, $3}; }
;
idDottedMoreForeach<nodeExprp>:
idDottedMoreForeach<foreachHeaderp>:
idArrayedForeach { $$ = $1; }
| idDottedMoreForeach '.' idArrayedForeach { $$ = new AstDot{$2, false, $1, $3}; }
| idDottedSelMore '.' idArrayedForeach
{ $3->fromp(new AstDot{$2, false, $1, $3->fromp()->unlinkFrBack()}); $$ = $3; }
;
// Single component of dotted path, maybe [#].
@ -6197,36 +6199,24 @@ idArrayed<nodeExprp>: // IEEE: id + select
| idArrayed '[' expr yP_MINUSCOLON constExpr ']' { $$ = new AstSelMinus{$2, $1, $3, $5}; }
;
idArrayedForeach<nodeExprp>: // IEEE: id + select (under foreach expression)
id
{ $$ = new AstParseRef{$<fl>1, *$1, nullptr, nullptr}; }
// // IEEE: id + part_select_range/constant_part_select_range
| idArrayed '[' expr ']' { $$ = new AstSelBit{$2, $1, $3}; } // Or AstArraySel, don't know yet.
| idArrayed '[' constExpr ':' constExpr ']' { $$ = new AstSelExtract{$2, $1, $3, $5}; }
// // IEEE: id + indexed_range/constant_indexed_range
| idArrayed '[' expr yP_PLUSCOLON constExpr ']' { $$ = new AstSelPlus{$2, $1, $3, $5}; }
| idArrayed '[' expr yP_MINUSCOLON constExpr ']' { $$ = new AstSelMinus{$2, $1, $3, $5}; }
// // IEEE: loop_variables (under foreach expression)
// // To avoid conflicts we allow expr as first element, must post-check
idArrayedForeach<foreachHeaderp>: // IEEE: id + select (under foreach expression)
parseRefBase // Malformed, but accept for better error reporting
{ $$ = new AstForeachHeader{$<fl>1, $1, nullptr}; }
| idArrayed '[' expr ']'
{ $$ = new AstForeachHeader{$2, $1, $3}; }
| idArrayed '[' ']'
{ $$ = new AstSelLoopVars{$2, $1, new AstEmpty{$3}}; }
| idArrayed '[' expr ',' loop_variables ']'
{ $$ = new AstSelLoopVars{$2, $1, addNextNull(static_cast<AstNode*>($3), $5)}; }
{ $$ = new AstForeachHeader{$2, $1, new AstEmpty{$3}}; }
| idArrayed '[' parseRefBase ',' loop_variables ']'
{ $$ = new AstForeachHeader{$2, $1, addNextNull(static_cast<AstNode*>($3), $5)}; }
| idArrayed '[' ',' loop_variables ']'
{ $$ = new AstSelLoopVars{$2, $1, addNextNull(static_cast<AstNode*>(new AstEmpty{$3}), $4)}; }
{ $$ = new AstForeachHeader{$2, $1, addNextNull(static_cast<AstNode*>(new AstEmpty{$3}), $4)}; }
;
// VarRef without any dots or vectorizaion
varRefBase<parseRefp>:
// ParseRef without any dots or vectorizaion
parseRefBase<parseRefp>:
id { $$ = new AstParseRef{$<fl>1, *$1}; }
;
// ParseRef
parseRefBase<nodep>:
id
{ $$ = new AstParseRef{$<fl>1, *$1, nullptr, nullptr}; }
;
// yaSTRING shouldn't be used directly, instead via an abstraction below
str<strp>: // yaSTRING but with \{escapes} need decoded
yaSTRING { $$ = PARSEP->newString(GRAMMARP->unquoteString($<fl>1, *$1)); }