verilator/src/V3ParseGrammar.cpp

295 lines
12 KiB
C++
Raw Normal View History

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Parse syntax tree
//
2019-11-08 04:33:59 +01:00
// Code available from: https://verilator.org
//
//*************************************************************************
//
2025-01-01 14:30:25 +01:00
// Copyright 2003-2025 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
//
//*************************************************************************
#define YYDEBUG 1 // Nicer errors
#define VL_MT_DISABLED_CODE_UNIT 1
#include "V3Ast.h" // This must be before V3ParseBison.cpp, as we don't want #defines to conflict
VL_DEFINE_DEBUG_FUNCTIONS;
//======================================================================
// The guts come from bison output
#include "V3ParseBison.c"
//======================================================================
// V3ParseImp functions requiring bison state
int V3ParseImp::bisonParse() {
// Use --debugi-bison 9 to enable this
if (PARSEP->debugBison() >= 9) yydebug = 1;
return yyparse();
}
const char* V3ParseImp::tokenName(int token) {
#if YYDEBUG || YYERROR_VERBOSE
static const char** nameTablep = nullptr;
if (!nameTablep) {
int size;
for (size = 0; yytname[size]; ++size) {}
nameTablep = new const char*[size];
// Workaround bug in bison's which have '!' in yytname but not token values
int iout = 0;
for (int i = 0; yytname[i]; ++i) {
if (yytname[i][0] == '\'') continue;
nameTablep[iout++] = yytname[i];
}
}
if (token >= 255) {
return nameTablep[token - 255];
} else {
static char ch[2];
ch[0] = token;
ch[1] = '\0';
return ch;
}
#else
return "";
#endif
}
void V3ParseImp::candidatePli(VSpellCheck* spellerp) {
#if !YYERROR_VERBOSE
#error "Need lex token names"
#endif
for (int i = 0; yytname[i]; ++i) {
if (yytname[i][0] != '\"') continue;
if (yytname[i][1] != '$') continue;
spellerp->pushCandidate(string{yytname[i]}.substr(1, strlen(yytname[i]) - 2));
}
}
void V3ParseImp::parserClear() {
// Clear up any dynamic memory V3Parser required
VARDTYPE(nullptr);
2025-09-10 23:42:45 +02:00
GRAMMARP->setNetDelay(nullptr);
2025-09-11 13:01:36 +02:00
GRAMMARP->setScopedSigAttr(nullptr);
}
//======================================================================
// V3ParseGrammar functions requiring bison state
AstArg* V3ParseGrammar::argWrapList(AstNodeExpr* nodep) {
// Convert list of expressions to list of arguments
AstArg* outp = nullptr;
while (nodep) {
AstNodeExpr* const nextp = VN_AS(nodep->nextp(), NodeExpr);
if (nextp) nextp->unlinkFrBackWithNext();
outp = AstNode::addNext(outp, new AstArg{nodep->fileline(), "", nodep});
nodep = nextp;
}
return outp;
}
2019-10-06 14:20:02 +02:00
AstNode* V3ParseGrammar::createSupplyExpr(FileLine* fileline, const string& name, int value) {
AstAssignW* assignp = new AstAssignW{fileline, new AstParseRef{fileline, name},
value ? new AstConst{fileline, AstConst::All1{}}
: new AstConst{fileline, AstConst::All0{}}};
AstStrengthSpec* strengthSpecp
= new AstStrengthSpec{fileline, VStrength::SUPPLY, VStrength::SUPPLY};
assignp->strengthSpecp(strengthSpecp);
return assignp;
}
AstRange* V3ParseGrammar::scrubRange(AstNodeRange* nrangep) {
// Remove any UnsizedRange's from list
for (AstNodeRange *nodep = nrangep, *nextp; nodep; nodep = nextp) {
nextp = VN_AS(nodep->nextp(), NodeRange);
if (!VN_IS(nodep, Range)) {
nodep->v3error(
"Unsupported or syntax error: Unsized range in instance or other declaration");
nodep->unlinkFrBack();
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
}
if (nrangep && nrangep->nextp()) {
// Not supported by at least 2 of big 3
nrangep->nextp()->v3warn(E_UNSUPPORTED,
"Unsupported: Multidimensional instances/interfaces.");
nrangep->nextp()->unlinkFrBackWithNext()->deleteTree();
}
return VN_CAST(nrangep, Range);
}
AstNodePreSel* V3ParseGrammar::scrubSel(AstNodeExpr* fromp, AstNodePreSel* selp) VL_MT_DISABLED {
// SEL(PARSELVALUE, ...) -> SEL(fromp, ...)
AstNodePreSel* subSelp = selp;
while (true) {
if (VN_IS(subSelp->fromp(), ParseHolder)) break;
if (AstNodePreSel* const lowerSelp = VN_CAST(subSelp->fromp(), NodePreSel)) {
subSelp = lowerSelp;
continue;
}
subSelp->v3fatalSrc("Couldn't find where to insert expression into select");
}
AstNode* subSelFromp = subSelp->fromp();
subSelFromp->replaceWith(fromp);
VL_DO_DANGLING(subSelFromp->deleteTree(), subSelFromp);
return selp;
}
AstNodeDType* V3ParseGrammar::createArray(AstNodeDType* basep, AstNodeRange* nrangep,
bool isPacked) {
// Split RANGE0-RANGE1-RANGE2
// into ARRAYDTYPE0(ARRAYDTYPE1(ARRAYDTYPE2(BASICTYPE3), RANGE), RANGE)
AstNodeDType* arrayp = basep;
if (nrangep) { // Maybe no range - return unmodified base type
while (nrangep->nextp()) nrangep = VN_AS(nrangep->nextp(), NodeRange);
while (nrangep) {
AstNodeRange* const prevp = VN_AS(nrangep->backp(), NodeRange);
if (prevp) nrangep->unlinkFrBack();
AstRange* const rangep = VN_CAST(nrangep, Range);
if (rangep && isPacked) {
arrayp
2022-11-20 21:06:49 +01:00
= new AstPackArrayDType{rangep->fileline(), VFlagChildDType{}, arrayp, rangep};
} else if (rangep
&& (VN_IS(rangep->leftp(), Unbounded)
|| VN_IS(rangep->rightp(), Unbounded))) {
2022-11-20 21:06:49 +01:00
arrayp = new AstQueueDType{nrangep->fileline(), VFlagChildDType{}, arrayp,
2022-11-19 20:45:33 +01:00
rangep->rightp()->cloneTree(true)};
2025-09-10 23:42:45 +02:00
VL_DO_DANGLING(nrangep->deleteTree(), nrangep);
} else if (rangep) {
2022-11-19 20:45:33 +01:00
arrayp = new AstUnpackArrayDType{rangep->fileline(), VFlagChildDType{}, arrayp,
rangep};
} else if (VN_IS(nrangep, UnsizedRange)) {
arrayp = new AstDynArrayDType{nrangep->fileline(), VFlagChildDType{}, arrayp};
2022-12-29 19:59:24 +01:00
VL_DO_DANGLING(nrangep->deleteTree(), nrangep);
} else if (VN_IS(nrangep, BracketRange)) {
const AstBracketRange* const arangep = VN_AS(nrangep, BracketRange);
AstNode* const keyp = arangep->elementsp()->unlinkFrBack();
2022-11-19 20:45:33 +01:00
arrayp = new AstBracketArrayDType{nrangep->fileline(), VFlagChildDType{}, arrayp,
keyp};
2022-12-29 19:59:24 +01:00
VL_DO_DANGLING(nrangep->deleteTree(), nrangep);
} else if (VN_IS(nrangep, WildcardRange)) {
arrayp = new AstWildcardArrayDType{nrangep->fileline(), VFlagChildDType{}, arrayp};
2022-12-29 19:59:24 +01:00
VL_DO_DANGLING(nrangep->deleteTree(), nrangep);
} else {
UASSERT_OBJ(0, nrangep, "Expected range or unsized range");
}
nrangep = prevp;
}
}
return arrayp;
}
2019-10-06 14:20:02 +02:00
AstVar* V3ParseGrammar::createVariable(FileLine* fileline, const string& name,
AstNodeRange* arrayp, AstNode* attrsp) {
2025-09-10 23:42:45 +02:00
UINFO(5, " creVar " << name << " decl=" << GRAMMARP->m_varDecl << " io="
<< GRAMMARP->m_varIO << " dt=" << (GRAMMARP->m_varDTypep ? "set" : ""));
if (GRAMMARP->m_varIO == VDirection::NONE // In non-ANSI port list
&& GRAMMARP->m_varDecl == VVarType::PORT) {
// Just a port list with variable name (not v2k format); AstPort already created
2024-03-23 23:12:43 +01:00
if (arrayp) VL_DO_DANGLING(arrayp->deleteTree(), arrayp);
if (attrsp) {
// TODO: Merge attributes across list? Or warn attribute is ignored
VL_DO_DANGLING(attrsp->deleteTree(), attrsp);
}
return nullptr;
}
2025-09-10 23:42:45 +02:00
AstNodeDType* const dtypep = [&]() -> AstNodeDType* {
if (GRAMMARP->m_varDecl == VVarType::WREAL) {
// dtypep might not be null, might be implicit LOGIC before we knew better
return new AstBasicDType{fileline, VBasicDTypeKwd::DOUBLE};
}
if (GRAMMARP->m_varDTypep) {
// May make new variables with same type, so clone
return GRAMMARP->m_varDTypep->cloneTree(false);
}
// Created implicitly
2022-11-01 23:53:47 +01:00
if (m_insideProperty) {
if (m_typedPropertyPort) {
fileline->v3warn(E_UNSUPPORTED, "Untyped property port following a typed port");
}
2025-09-10 23:42:45 +02:00
return new AstBasicDType{fileline, VBasicDTypeKwd::UNTYPED};
2022-11-01 23:53:47 +01:00
}
2025-09-10 23:42:45 +02:00
return new AstBasicDType{fileline, LOGIC_IMPLICIT};
}();
// UINFO(0,"CREVAR "<<fileline->ascii()<<" decl="<<GRAMMARP->m_varDecl.ascii()<<"
// io="<<GRAMMARP->m_varIO.ascii()<<endl);
VVarType type = GRAMMARP->m_varDecl;
if (type == VVarType::UNKNOWN) { // e.g. "output" non-ANSI standalone direction (vs "reg")
if (GRAMMARP->m_varIO.isAny()) {
type = VVarType::PORT;
} else {
fileline->v3fatalSrc("Unknown signal type declared: " << type.ascii());
}
}
if (type == VVarType::GENVAR) {
// Should be impossible as the grammar blocks this, but...
if (arrayp) fileline->v3error("Genvars may not be arrayed: " << name); // LCOV_EXCL_LINE
}
// Split RANGE0-RANGE1-RANGE2 into
// ARRAYDTYPE0(ARRAYDTYPE1(ARRAYDTYPE2(BASICTYPE3), RANGE), RANGE)
AstNodeDType* const arrayDTypep = createArray(dtypep, arrayp, false);
2022-11-19 20:45:33 +01:00
AstVar* const nodep = new AstVar{fileline, type, name, VFlagChildDType(), arrayDTypep};
nodep->addAttrsp(attrsp);
nodep->ansi(m_pinAnsi);
nodep->declTyped(m_varDeclTyped);
nodep->lifetime(m_varLifetime);
2025-09-10 23:42:45 +02:00
if (m_netDelayp) nodep->delayp(m_netDelayp->cloneTree(false));
if (GRAMMARP->m_varDecl != VVarType::UNKNOWN) nodep->combineType(GRAMMARP->m_varDecl);
if (GRAMMARP->m_varIO != VDirection::NONE) {
nodep->declDirection(GRAMMARP->m_varIO);
nodep->direction(GRAMMARP->m_varIO);
}
if (GRAMMARP->m_varDecl == VVarType::SUPPLY0) {
AstNode::addNext<AstNode, AstNode>(
nodep, V3ParseGrammar::createSupplyExpr(fileline, nodep->name(), 0));
}
if (GRAMMARP->m_varDecl == VVarType::SUPPLY1) {
AstNode::addNext<AstNode, AstNode>(
nodep, V3ParseGrammar::createSupplyExpr(fileline, nodep->name(), 1));
}
if (VN_IS(dtypep, ParseTypeDType)) {
// Parser needs to know what is a type
AstNode* const newp = new AstTypedefFwd{fileline, name, VFwdType::NONE};
AstNode::addNext<AstNode, AstNode>(nodep, newp);
}
// Don't set dtypep in the ranging;
// We need to autosize parameters and integers separately
//
// Propagate from current module tracing state
if (nodep->isGenVar()) {
nodep->trace(false);
} else if (nodep->isParam() && !v3Global.opt.traceParams()) {
nodep->trace(false);
} else {
nodep->trace(allTracingOn(nodep->fileline()));
}
if (nodep->varType().isVPIAccessible()) nodep->addAttrsp(GRAMMARP->cloneScopedSigAttr());
// Remember the last variable created, so we can attach attributes to it in later parsing
GRAMMARP->m_varAttrp = nodep;
PARSEP->tagNodep(GRAMMARP->m_varAttrp);
return nodep;
}
string V3ParseGrammar::unquoteString(FileLine* fileline, const std::string& text) {
string errMsg;
string res = VString::unquoteSVString(text, errMsg);
if (!errMsg.empty()) fileline->v3error(errMsg.c_str());
return res;
}
int V3ParseGrammar::s_typeImpNum = 0;