diff --git a/Changes b/Changes index 704c39765..a2f904fb3 100644 --- a/Changes +++ b/Changes @@ -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] diff --git a/src/V3PreLex.h b/src/V3PreLex.h index 0a2fec643..3faae2a23 100644 --- a/src/V3PreLex.h +++ b/src/V3PreLex.h @@ -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 diff --git a/src/V3PreLex.l b/src/V3PreLex.l index 6b2b326e8..e97f2452a 100644 --- a/src/V3PreLex.l +++ b/src/V3PreLex.l @@ -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 {backslash}. { yymore(); } [\>] { yy_pop_state(); return (VP_STRING); } - /* Reading definition */ -"/*" { yy_push_state(CMTMODE); yymore(); } -"//"[^\n\r]* { return (VP_COMMENT);} -{drop} { } -<> { linenoInc(); yy_pop_state(); yytext=(char*)"\n"; yyleng=1; return (VP_DEFVALUE); } /* Technically illegal, but people complained */ -{crnl} { linenoInc(); yy_pop_state(); yytext=(char*)"\n"; yyleng=1; return (VP_DEFVALUE); } -[\\]{crnl} { linenoInc(); appendDefValue((char*)"\n",1); } /* Include return so can maintain output line count */ -[^\/\*\n\r\\]+ | -[\\][^\n\r] | -. { appendDefValue(yytext,yyleng); } + /* Reading definition formal parenthesis (or not) to begin formal arguments */ + /* Note '(' must IMMEDIATELY follow definition name */ +[(] { appendDefValue("(",1); BEGIN(DEFFORM); } +{crnl} { yy_pop_state(); unput('\n'); yyleng=0; return VP_DEFFORM; } /* DEFVAL will later grab the return */ +. { yy_pop_state(); unput(yytext[yyleng-1]); yyleng=0; return VP_DEFFORM; } /* empty formals */ + + /* Reading definition formals */ +[(] { yy_pop_state(); yyerrorf("Extra ( in define formal arguments.\n"); yyleng=0; return VP_DEFFORM; } +[)] { yy_pop_state(); appendDefValue(yytext,yyleng); yyleng=0; return VP_DEFFORM; } +"/*" { yy_push_state(CMTMODE); yymore(); } +"//"[^\n\r]* { return (VP_COMMENT);} +{drop} { } +<> { linenoInc(); yy_pop_state(); yyerrorf("Unterminated ( in define formal arguments."); yyleng=0; return VP_DEFFORM; } +{crnl} { linenoInc(); appendDefValue((char*)"\n",1); } /* Include return so can maintain output line count */ +[\\]{crnl} { linenoInc(); appendDefValue((char*)"\n",1); } /* Include return so can maintain output line count */ +[^\/\*\n\r\\()]+ | +[\\][^\n\r] | +. { appendDefValue(yytext,yyleng); } + + /* Reading definition value */ +"/*" { yy_push_state(CMTMODE); yymore(); } +"//"[^\n\r]* { return (VP_COMMENT);} +{drop} { } +<> { linenoInc(); yy_pop_state(); yytext=(char*)"\n"; yyleng=1; return (VP_DEFVALUE); } /* Technically illegal, but people complained */ +{crnl} { linenoInc(); yy_pop_state(); yytext=(char*)"\n"; yyleng=1; return (VP_DEFVALUE); } +[\\]{crnl} { linenoInc(); appendDefValue((char*)"\n",1); } /* Include return so can maintain output line count */ +[^\/\*\n\r\\]+ | +[\\][^\n\r] | +. { appendDefValue(yytext,yyleng); } /* Define arguments */ "/*" { 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 diff --git a/src/V3PreProc.cpp b/src/V3PreProc.cpp index c9698b6f4..84856dca4 100644 --- a/src/V3PreProc.cpp +++ b/src/V3PreProc.cpp @@ -111,12 +111,14 @@ struct V3PreProcImp : public V3PreProc { V3PreLex* m_lexp; // Current lexer state (NULL = closed) stack 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='"<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: "<m_defValue; // Remove returns - for (unsigned i=0; im_defValue.length(); i++) { - if (m_lexp->m_defValue[i] == '\n') { - m_lexp->m_defValue[i] = ' '; + for (unsigned i=0; im_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_defValue<<"'"<m_defValue, params); + UINFO(4,"Define "<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"); diff --git a/test_regress/t/t_preproc.out b/test_regress/t/t_preproc.out index 383982996..5a1a60ff7 100644 --- a/test_regress/t/t_preproc.out +++ b/test_regress/t/t_preproc.out @@ -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 diff --git a/test_regress/t/t_preproc.v b/test_regress/t/t_preproc.v index afc6ab0d8..a9f1c9630 100644 --- a/test_regress/t/t_preproc.v +++ b/test_regress/t/t_preproc.v @@ -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__ +