Fix define formal arguments that contain newlines, bug84.

This commit is contained in:
Wilson Snyder 2009-05-11 11:57:43 -04:00
parent 96d150e92d
commit 4e522ab7f5
6 changed files with 130 additions and 39 deletions

View File

@ -14,6 +14,8 @@ indicates the contributor was also the author of the fix; Thanks!
**** Fix escaped identifiers with '.' causing conflicts, bug83. [J Baxter]
**** Fix define formal arguments that contain newlines, bug84. [David A]
* Verilator 3.703 2009/05/02
*** Fix $clog2 calculation error with powers-of-2, bug81. [Patricio Kaplan]

View File

@ -51,7 +51,9 @@
#define VP_DEFREF 306
#define VP_DEFARG 307
#define VP_ERROR 308
#define VP_PSL 309
#define VP_DEFFORM 309
#define VP_PSL 350
//======================================================================
@ -134,9 +136,12 @@ class V3PreLex {
void incLineno() { m_curFilelinep->incLineno(); }
// Called by V3PreProc.cpp to inform lexer
void pushStateDefArg(int level);
void pushStateDefForm();
void pushStateDefValue();
void pushStateIncFilename();
void unputString(const char* textp);
/// Called by VPreproc.cpp to get data from lexer
int currentStartState();
};
#endif // Guard

View File

@ -37,7 +37,7 @@ static bool optPsl() { return V3PreProc::optPsl(); }
static bool pedantic() { return V3PreLex::s_currentLexp->m_pedantic; }
static void yyerror(char* msg) { V3PreLex::s_currentLexp->m_curFilelinep->v3error(msg); }
static void yyerrorf(const char* msg) { V3PreLex::s_currentLexp->m_curFilelinep->v3error(msg); }
static void appendDefValue(char* t,int l) { V3PreLex::s_currentLexp->appendDefValue(t,l); }
static void appendDefValue(const char* t,int l) { V3PreLex::s_currentLexp->appendDefValue(t,l); }
static int pslParenLevel() { return V3PreLex::s_currentLexp->m_pslParenLevel; }
static void pslParenLevelInc() { V3PreLex::s_currentLexp->m_pslParenLevel++; }
static void pslParenLevelDec() { if (pslParenLevel()) V3PreLex::s_currentLexp->m_pslParenLevel--; }
@ -55,7 +55,9 @@ static void pslMoreNeeded(bool flag) { V3PreLex::s_currentLexp->m_pslMoreNeeded
%x CMTBEGM
%x CMTMODE
%x STRMODE
%x DEFMODE
%x DEFFPAR
%x DEFFORM
%x DEFVAL
%x ARGMODE
%x INCMODE
%x PRTMODE
@ -123,16 +125,35 @@ psl [p]sl
<INCMODE>{backslash}. { yymore(); }
<INCMODE>[\>] { yy_pop_state(); return (VP_STRING); }
/* Reading definition */
<DEFMODE>"/*" { yy_push_state(CMTMODE); yymore(); }
<DEFMODE>"//"[^\n\r]* { return (VP_COMMENT);}
<DEFMODE>{drop} { }
<DEFMODE><<EOF>> { linenoInc(); yy_pop_state(); yytext=(char*)"\n"; yyleng=1; return (VP_DEFVALUE); } /* Technically illegal, but people complained */
<DEFMODE>{crnl} { linenoInc(); yy_pop_state(); yytext=(char*)"\n"; yyleng=1; return (VP_DEFVALUE); }
<DEFMODE>[\\]{crnl} { linenoInc(); appendDefValue((char*)"\n",1); } /* Include return so can maintain output line count */
<DEFMODE>[^\/\*\n\r\\]+ |
<DEFMODE>[\\][^\n\r] |
<DEFMODE>. { appendDefValue(yytext,yyleng); }
/* Reading definition formal parenthesis (or not) to begin formal arguments */
/* Note '(' must IMMEDIATELY follow definition name */
<DEFFPAR>[(] { appendDefValue("(",1); BEGIN(DEFFORM); }
<DEFFPAR>{crnl} { yy_pop_state(); unput('\n'); yyleng=0; return VP_DEFFORM; } /* DEFVAL will later grab the return */
<DEFFPAR>. { yy_pop_state(); unput(yytext[yyleng-1]); yyleng=0; return VP_DEFFORM; } /* empty formals */
/* Reading definition formals */
<DEFFORM>[(] { yy_pop_state(); yyerrorf("Extra ( in define formal arguments.\n"); yyleng=0; return VP_DEFFORM; }
<DEFFORM>[)] { yy_pop_state(); appendDefValue(yytext,yyleng); yyleng=0; return VP_DEFFORM; }
<DEFFORM>"/*" { yy_push_state(CMTMODE); yymore(); }
<DEFFORM>"//"[^\n\r]* { return (VP_COMMENT);}
<DEFFORM>{drop} { }
<DEFFORM><<EOF>> { linenoInc(); yy_pop_state(); yyerrorf("Unterminated ( in define formal arguments."); yyleng=0; return VP_DEFFORM; }
<DEFFORM>{crnl} { linenoInc(); appendDefValue((char*)"\n",1); } /* Include return so can maintain output line count */
<DEFFORM>[\\]{crnl} { linenoInc(); appendDefValue((char*)"\n",1); } /* Include return so can maintain output line count */
<DEFFORM>[^\/\*\n\r\\()]+ |
<DEFFORM>[\\][^\n\r] |
<DEFFORM>. { appendDefValue(yytext,yyleng); }
/* Reading definition value */
<DEFVAL>"/*" { yy_push_state(CMTMODE); yymore(); }
<DEFVAL>"//"[^\n\r]* { return (VP_COMMENT);}
<DEFVAL>{drop} { }
<DEFVAL><<EOF>> { linenoInc(); yy_pop_state(); yytext=(char*)"\n"; yyleng=1; return (VP_DEFVALUE); } /* Technically illegal, but people complained */
<DEFVAL>{crnl} { linenoInc(); yy_pop_state(); yytext=(char*)"\n"; yyleng=1; return (VP_DEFVALUE); }
<DEFVAL>[\\]{crnl} { linenoInc(); appendDefValue((char*)"\n",1); } /* Include return so can maintain output line count */
<DEFVAL>[^\/\*\n\r\\]+ |
<DEFVAL>[\\][^\n\r] |
<DEFVAL>. { appendDefValue(yytext,yyleng); }
/* Define arguments */
<ARGMODE>"/*" { yy_push_state(CMTMODE); yymore(); }
@ -225,9 +246,16 @@ void V3PreLex::pushStateDefArg(int level) {
m_defValue = "";
}
void V3PreLex::pushStateDefForm() {
// Enter define formal arguments state
yy_push_state(DEFFPAR); // First is an optional ( to begin args
m_parenLevel = 0;
m_defValue = "";
}
void V3PreLex::pushStateDefValue() {
// Enter define value state
yy_push_state(DEFMODE);
yy_push_state(DEFVAL);
m_parenLevel = 0;
m_defValue = "";
}
@ -252,6 +280,10 @@ void V3PreLex::appendDefValue(const char* textp, int len) {
m_defValue.append(textp,len);
}
int V3PreLex::currentStartState() {
return YY_START;
}
void V3PreLex::lineDirective(const char* textp) {
m_curFilelinep->lineDirective(textp);
// Make sure we have a dependency on whatever file was specified

View File

@ -111,12 +111,14 @@ struct V3PreProcImp : public V3PreProc {
V3PreLex* m_lexp; // Current lexer state (NULL = closed)
stack<V3PreLex*> m_includeStack; // Stack of includers above current m_lexp
enum ProcState { ps_TOP, ps_DEFNAME, ps_DEFVALUE, ps_DEFPAREN, ps_DEFARG,
enum ProcState { ps_TOP,
ps_DEFNAME, ps_DEFFORM, ps_DEFVALUE, ps_DEFPAREN, ps_DEFARG,
ps_INCNAME, ps_ERRORNAME };
ProcState m_state; // Current state of parser
int m_stateFor; // Token state is parsing for
int m_off; // If non-zero, ifdef level is turned off, don't dump text
string m_lastSym; // Last symbol name found.
string m_formals; ///< Last formals found
// For getRawToken/ `line insertion
string m_lineCmt; // Line comment(s) to be returned
@ -371,6 +373,7 @@ const char* V3PreProcImp::tokenName(int tok) {
case VP_LINE : return("LINE");
case VP_SYMBOL : return("SYMBOL");
case VP_STRING : return("STRING");
case VP_DEFFORM : return("DEFFORM");
case VP_DEFVALUE : return("DEFVALUE");
case VP_COMMENT : return("COMMENT");
case VP_TEXT : return("TEXT");
@ -581,7 +584,7 @@ int V3PreProcImp::getRawToken() {
return (VP_TEXT);
}
if (m_lineCmt!="") {
// We have some `line directive to return to the user. Do it.
// We have some `line directive or other processed data to return to the user.
static string rtncmt; // Keep the c string till next call
rtncmt = m_lineCmt;
if (m_lineCmtNl) {
@ -591,10 +594,11 @@ int V3PreProcImp::getRawToken() {
yytext=(char*)rtncmt.c_str(); yyleng=rtncmt.length();
m_lineCmt = "";
if (yyleng) m_rawAtBol = (yytext[yyleng-1]=='\n');
if (m_state!=ps_DEFVALUE) return (VP_TEXT);
else {
if (m_state==ps_DEFVALUE) {
V3PreLex::s_currentLexp->appendDefValue(yytext,yyleng);
goto next_tok;
} else {
return (VP_TEXT);
}
}
if (isEof()) return (VP_EOF);
@ -609,9 +613,9 @@ int V3PreProcImp::getRawToken() {
string::size_type pos;
while ((pos=buf.find("\n")) != string::npos) { buf.replace(pos, 1, "\\n"); }
while ((pos=buf.find("\r")) != string::npos) { buf.replace(pos, 1, "\\r"); }
fprintf (stderr, "%d: RAW %s s%d dr%d: %-10s: %s\n",
fprintf (stderr, "%d: RAW %s s%d dr%d: <%d>%-10s: %s\n",
fileline()->lineno(), m_off?"of":"on", m_state, (int)m_defRefs.size(),
tokenName(tok), buf.c_str());
m_lexp->currentStartState(), tokenName(tok), buf.c_str());
}
// On EOF, try to pop to upper level includes, as needed.
@ -695,8 +699,8 @@ int V3PreProcImp::getToken() {
}
else if (m_stateFor==VP_DEFINE) {
// m_lastSym already set.
m_state = ps_DEFVALUE;
m_lexp->pushStateDefValue();
m_state = ps_DEFFORM;
m_lexp->pushStateDefForm();
}
else fileline()->v3fatalSrc("Bad case\n");
goto next_tok;
@ -706,45 +710,64 @@ int V3PreProcImp::getToken() {
goto next_tok;
}
}
case ps_DEFFORM: {
if (tok==VP_DEFFORM) {
m_formals = m_lexp->m_defValue;
m_state = ps_DEFVALUE;
if (debug()) cout<<"DefFormals='"<<m_formals<<"'\n";
m_lexp->pushStateDefValue();
goto next_tok;
} else if (tok==VP_TEXT) {
// IE, something like comment in formals
if (!m_off) return tok;
else goto next_tok;
} else {
fileline()->v3error("Expecting define formal arguments. Found: "<<tokenName(tok));
goto next_tok;
}
}
case ps_DEFVALUE: {
static string newlines;
newlines = "\n"; // Always start with trailing return
if (tok == VP_DEFVALUE) {
if (debug()) cout<<"DefValue='"<<m_lexp->m_defValue<<"' formals='"<<m_formals<<"'\n";
// Add any formals
string formAndValue = m_formals + m_lexp->m_defValue;
// Remove returns
for (unsigned i=0; i<m_lexp->m_defValue.length(); i++) {
if (m_lexp->m_defValue[i] == '\n') {
m_lexp->m_defValue[i] = ' ';
for (unsigned i=0; i<formAndValue.length(); i++) {
if (formAndValue[i] == '\n') {
formAndValue[i] = ' ';
newlines += "\n";
}
}
if (!m_off) {
string params;
if (m_lexp->m_defValue=="" || isspace(m_lexp->m_defValue[0])) {
if (formAndValue=="" || isspace(formAndValue[0])) {
// Define without parameters
} else if (m_lexp->m_defValue[0] == '(') {
string::size_type paren = m_lexp->m_defValue.find(")");
} else if (formAndValue[0] == '(') {
string::size_type paren = formAndValue.find(")");
if (paren == string::npos) {
fileline()->v3error("Missing ) to end define arguments.");
} else {
params = m_lexp->m_defValue.substr(0, paren+1);
m_lexp->m_defValue.replace(0, paren+1, "");
params = formAndValue.substr(0, paren+1);
formAndValue.replace(0, paren+1, "");
}
} else {
fileline()->v3error("Missing space or paren to start define value.");
}
// Remove leading whitespace
unsigned leadspace = 0;
while (m_lexp->m_defValue.length() > leadspace
&& isspace(m_lexp->m_defValue[leadspace])) leadspace++;
if (leadspace) m_lexp->m_defValue.erase(0,leadspace);
while (formAndValue.length() > leadspace
&& isspace(formAndValue[leadspace])) leadspace++;
if (leadspace) formAndValue.erase(0,leadspace);
// Remove trailing whitespace
unsigned trailspace = 0;
while (m_lexp->m_defValue.length() > trailspace
&& isspace(m_lexp->m_defValue[m_lexp->m_defValue.length()-1-trailspace])) trailspace++;
if (trailspace) m_lexp->m_defValue.erase(m_lexp->m_defValue.length()-trailspace,trailspace);
while (formAndValue.length() > trailspace
&& isspace(formAndValue[formAndValue.length()-1-trailspace])) trailspace++;
if (trailspace) formAndValue.erase(formAndValue.length()-trailspace,trailspace);
// Define it
UINFO(4,"Define "<<m_lastSym<<" = '"<<m_lexp->m_defValue<<"'"<<endl);
define(fileline(), m_lastSym, m_lexp->m_defValue, params);
UINFO(4,"Define "<<m_lastSym<<" = '"<<formAndValue<<"'"<<endl);
define(fileline(), m_lastSym, formAndValue, params);
}
} else {
fileline()->v3fatalSrc("Bad define text\n");
@ -954,6 +977,7 @@ int V3PreProcImp::getToken() {
}
case VP_WHITE: // Handled at top of loop
case VP_COMMENT: // Handled at top of loop
case VP_DEFFORM: // Handled by m_state=ps_DEFFORM;
case VP_DEFVALUE: // Handled by m_state=ps_DEFVALUE;
default:
fileline()->v3fatalSrc("Internal error: Unexpected token.\n");

View File

@ -177,4 +177,18 @@ begin addr <= ({regs[6], regs[7]}); wdata <= (rdata); wr <= 1; end
`line 130 "t/t_preproc.v" 0
Line_Preproc_Check 131
`line 132 "t/t_preproc.v" 2
(p,q)
(x,y )
Line_Preproc_Check 144
`line 146 "t/t_preproc.v" 2

View File

@ -129,3 +129,17 @@ assign c = tmp_``c ;
`error "Empty is still true"
`endif
Line_Preproc_Check `__LINE__
//======================================================================
// bug84
`define ARGPAR(a, // Hello, comments MIGHT not be legal
/*more,,)cmts*/ b // But newlines ARE legal... who speced THAT?
) (a,b)
`ARGPAR(p,q)
`ARGPAR( //Here
x,
y //Too
)
Line_Preproc_Check `__LINE__