diff --git a/Changes b/Changes index f7920164f..402987811 100644 --- a/Changes +++ b/Changes @@ -5,6 +5,10 @@ indicates the contributor was also the author of the fix; Thanks! * Verilator 3.8**** +*** Fix preprocessor preservation of newlines across macro substitutions. + +**** Fix preprocessor stringification of nested macros. + **** Fix some constant parameter functions causing crash, bug253. [Nick Bowler] **** Fix do {...} while() not requiring final semicolon. diff --git a/src/V3PreLex.h b/src/V3PreLex.h index 3276a64e7..d4dd591f7 100644 --- a/src/V3PreLex.h +++ b/src/V3PreLex.h @@ -61,10 +61,11 @@ class V3PreProcImp; #define VP_DEFARG 307 #define VP_ERROR 308 #define VP_DEFFORM 309 +#define VP_STRIFY 310 +#define VP_BACKQUOTE 311 #define VP_PSL 350 - //====================================================================== // Externs created by flex // We add a prefix so that other lexers/flexers in the same program won't collide. @@ -117,21 +118,39 @@ void yy_delete_buffer( YY_BUFFER_STATE b ); #define KEEPCMT_SUB 2 #define KEEPCMT_EXP 3 +//====================================================================== +// Entry for each file processed; a stack of entries included + +class VPreStream { +public: + FileLine* m_curFilelinep; // Current processing point (see also m_tokFilelinep) + deque m_buffers; // Buffer of characters to process + int m_ignNewlines; // Ignore multiline newlines + bool m_eof; // "EOF" buffer + bool m_file; // Buffer is start of new file + int m_termState; // Termination fsm + VPreStream(FileLine* fl) + : m_curFilelinep(fl), m_ignNewlines(0), + m_eof(false), m_file(false), m_termState(0) { + } + ~VPreStream() {} +}; + //====================================================================== // Class entry for each per-lexer state class V3PreLex { public: // Used only by V3PreLex.cpp and V3PreProc.cpp - FileLine* m_curFilelinep; // Current processing point - - // Parse state - stack m_bufferStack; // Stack of inserted text above current point - deque m_buffers; // Buffer of characters to process + V3PreProcImp* m_preimpp; // Preprocessor lexor belongs to + stack m_streampStack; // Stack of processing files + YY_BUFFER_STATE m_bufferState; // Flex state + FileLine* m_tokFilelinep; // Starting position of current token // State to lexer - static V3PreLex* s_currentLexp; // Current lexing point - int m_keepComments; // Emit comments in output text - bool m_pedantic; // Obey standard; don't Substitute `error + static V3PreLex* s_currentLexp; ///< Current lexing point + int m_keepComments; ///< Emit comments in output text + int m_keepWhitespace; ///< Emit all whitespace in output text + bool m_pedantic; ///< Obey standard; don't Substitute `error // State from lexer int m_formalLevel; // Parenthesis counting inside def formals @@ -143,42 +162,59 @@ class V3PreLex { int m_enterExit; // For VL_LINE, the enter/exit level // CONSTRUCTORS - V3PreLex() { + V3PreLex(V3PreProcImp* preimpp, FileLine* filelinep) { + m_preimpp = preimpp; m_keepComments = 0; + m_keepWhitespace = 1; m_pedantic = false; m_formalLevel = 0; m_parenLevel = 0; m_defCmtSlash = false; + m_tokFilelinep = filelinep; m_enterExit = 0; m_pslParenLevel = 0; m_pslMoreNeeded = false; - initFirstBuffer(); + initFirstBuffer(filelinep); } ~V3PreLex() { - while (!m_bufferStack.empty()) { yy_delete_buffer(m_bufferStack.top()); m_bufferStack.pop(); } + while (!m_streampStack.empty()) { delete m_streampStack.top(); m_streampStack.pop(); } + yy_delete_buffer(m_bufferState); m_bufferState=NULL; } - void initFirstBuffer(); // Called by V3PreLex.l from lexer - FileLine* curFilelinep() { return m_curFilelinep; } - void curFilelinep(FileLine* fl) { m_curFilelinep = fl; } - void appendDefValue(const char* text, size_t len); + VPreStream* curStreamp() { return m_streampStack.top(); } // Can't be empty, "EOF" is on top + FileLine* curFilelinep() { return curStreamp()->m_curFilelinep; } + void curFilelinep(FileLine* fl) { curStreamp()->m_curFilelinep = fl; } + void appendDefValue(const char* textp, size_t len) { m_defValue.append(textp,len); } void lineDirective(const char* textp); - void linenoInc() { curFilelinep()->linenoInc(); } + void linenoInc() { if (curStreamp()->m_ignNewlines) curStreamp()->m_ignNewlines--; + else curFilelinep()->linenoInc(); } // Called by V3PreProc.cpp to inform lexer void pushStateDefArg(int level); void pushStateDefForm(); void pushStateDefValue(); void pushStateIncFilename(); - void scanBytes(const char* strp, size_t len); + void scanNewFile(FileLine* filelinep); + void scanBytes(const string& str); void scanBytesBack(const string& str); size_t inputToLex(char* buf, size_t max_size); - /// Called by VPreproc.cpp to get data from lexer + /// Called by V3PreProc.cpp to get data from lexer YY_BUFFER_STATE currentBuffer(); + int lex(); int currentStartState(); - string currentUnreadChars(); void dumpSummary(); void dumpStack(); + void unused(); + /// Utility + static int debug(); + static void debug(int level); + static string cleanDbgStrg(const string& in); + +private: + string currentUnreadChars(); + string endOfStream(bool& againr); + void initFirstBuffer(FileLine* filelinep); + void scanSwitchStream(VPreStream* streamp); }; #endif // Guard diff --git a/src/V3PreLex.l b/src/V3PreLex.l index b29924975..b91bb3a39 100644 --- a/src/V3PreLex.l +++ b/src/V3PreLex.l @@ -78,8 +78,10 @@ ws [ \t\f\r] wsn [ \t\f] crnl [\r]*[\n] quote [\"] +tickquote [`][\"] backslash [\\] symb ([a-zA-Z_][a-zA-Z0-9_$]*|\\[^ \t\f\r\n]+) +symbdef ([a-zA-Z_][a-zA-Z0-9_$]*|\\[^ \t\f\r\n`]+) word [a-zA-Z0-9_]+ drop [\032] psl [p]sl @@ -124,6 +126,10 @@ psl [p]sl if (LEXP->m_parenLevel || LEXP->m_formalLevel) { appendDefValue(yytext,yyleng); yyleng=0; } else return (VP_STRING); } + /* Stringification */ +{tickquote} { return VP_STRIFY; } +"`\\`\"" { return VP_BACKQUOTE; } + /* Protected blocks */ "`protected" { yy_push_state(PRTMODE); yymore(); } <> { linenoInc(); yyerrorf("EOF in `protected"); yyleng=0; yyterminate(); } @@ -153,8 +159,10 @@ psl [p]sl {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 */ -{quote} { yy_push_state(STRMODE); yymore(); } +[\\]{crnl} { linenoInc(); appendDefValue((char*)"\\\n",2); } /* Include return so can maintain output line count */ +{quote} { yy_push_state(STRMODE); yymore(); } /* Legal only in default values */ +"`\\`\"" { appendDefValue(yytext,yyleng); } /* Maybe illegal, otherwise in default value */ +{tickquote} { appendDefValue(yytext,yyleng); } /* Maybe illegal, otherwise in default value */ [{\[] { LEXP->m_formalLevel++; appendDefValue(yytext,yyleng); } [}\]] { LEXP->m_formalLevel--; appendDefValue(yytext,yyleng); } [^\/\*\n\r\\(){}\[\]\"]+ | @@ -168,7 +176,7 @@ psl [p]sl {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); } /* Return, but not \ is part of define value */ +[\\]{crnl} { linenoInc(); appendDefValue((char*)"\\\n",2); } /* Return, AND \ is part of define value */ [^\/\*\n\r\\]+ | [\\][^\n\r] | . { appendDefValue(yytext,yyleng); } @@ -185,13 +193,15 @@ psl [p]sl . { yymore(); } <> { yyerrorf("EOF in '/* ... */' block comment\n"); yyleng=0; yyterminate(); } - /* Define arguments */ + /* Define arguments (use of a define) */ "/*" { yy_push_state(CMTMODE); yymore(); } "//"[^\n\r]* { return (VP_COMMENT);} {drop} { } <> { yyerrorf("EOF in define argument list\n"); yyleng = 0; yyterminate(); } {crnl} { linenoInc(); yytext=(char*)"\n"; yyleng=1; return(VP_WHITE); } {quote} { yy_push_state(STRMODE); yymore(); } +"`\\`\"" { appendDefValue(yytext,yyleng); } /* Literal text */ +{tickquote} { return(VP_STRIFY); } [{\[] { LEXP->m_parenLevel++; appendDefValue(yytext,yyleng); } [}\]] { LEXP->m_parenLevel--; appendDefValue(yytext,yyleng); } [(] { LEXP->m_parenLevel++; @@ -214,7 +224,7 @@ psl [p]sl } else { yy_pop_state(); return (VP_DEFARG); }} -"`"{symb} { return (VP_DEFREF); } /* defref in defref */ +"`"{symbdef} { appendDefValue(yytext,yyleng); } /* defref in defref - outer macro expands first */ [^\/\*\n\r\\(,){}\[\]\"`]+ | . { appendDefValue(yytext,yyleng); } @@ -257,7 +267,8 @@ psl [p]sl "//"[^\n\r]* { return (VP_COMMENT); } /* Comments inside block comments get literal inclusion (later removal) */ /* Define calls */ -"`"{symb} { return (VP_DEFREF); } + /* symbdef prevents normal lex rules from makeing `\`"foo a symbol {`"foo} instead of a BACKQUOTE */ +"`"{symbdef} { return (VP_DEFREF); } /* Generics */ {crnl} { linenoInc(); yytext=(char*)"\n"; yyleng=1; return(VP_WHITE); } @@ -296,59 +307,177 @@ void V3PreLex::pushStateIncFilename() { yymore(); } -void V3PreLex::initFirstBuffer() { - // Called from constructor to make first buffer - // yy_create_buffer also sets yy_fill_buffer=1 so reads from YY_INPUT - yy_switch_to_buffer(yy_create_buffer(NULL, YY_BUF_SIZE)); - m_bufferStack.push(currentBuffer()); - yyrestart(NULL); +void V3PreLex::debug(int level) { yy_flex_debug=level; } +int V3PreLex::debug() { return yy_flex_debug; } + +int V3PreLex::lex() { + V3PreLex::s_currentLexp = this; // Tell parser where to get/put data + m_tokFilelinep = curFilelinep(); // Remember token start location, may be updated by the lexer later + return yylex(); } size_t V3PreLex::inputToLex(char* buf, size_t max_size) { // We need a custom YY_INPUT because we can't use flex buffers. // Flex buffers are limited to 2GB, and we can't chop into 2G pieces // because buffers can't end in the middle of tokens. - // m_buffers only applies to the "base" buffer when there's no scanBytes outstanding - // It won't be called on scan_buffers as they don't have yy_fill_buffer set. + // Note if we switched streams here (which we don't) "buf" would be + // become a stale invalid pointer. // - //if (debug()) { cout<<"- pp:inputToLex ITL s="< max_size) { + yyerrorf("Output buffer too small for a `line"); + } else { + got = forceOut.length(); + strncpy(buf, forceOut.c_str(), got); + } + } else { + if (streamp->m_eof) { + if (yy_flex_debug) cout<<"- EOF\n"; + } + got = 0; // 0=EOF/EOS - although got was already 0. + if (again) goto again; + } + } + if (debug()>=10) { cout<<"- pp::inputToLex got="<m_eof) return ""; // Don't delete the final "EOF" stream + bool exited_file = curStreamp()->m_file; + if (!exited_file) { + // Midpoint of stream, just change buffers + delete curStreamp(); + m_streampStack.pop(); // Must work as size>1; EOF is entry 0 + againr = true; + return ""; + } + // Multiple steps because we need FLEX to see ending \n and EOS to end + // any illegal states, like an unterminated `protected region + else if (!curStreamp()->m_termState) { + // First shutdown phase for a file + // Terminate all files with a newline. This prevents problems if + // the user had a define without a terminating newline, + // otherwise the resumed file's next line would get tacked on. + // Also makes it likely the `line that changes files comes out + // immediately. + curStreamp()->m_termState = 1; + return "\n"; // Exit old file + } + else if (curStreamp()->m_termState == 1) { + // Now the EOF - can't be sent with other characters + curStreamp()->m_termState = 2; + return ""; // End of file + } + else if (curStreamp()->m_termState == 2) { + // Now ending `line + curStreamp()->m_termState = 3; + return curFilelinep()->lineDirectiveStrg(2); // Exit old file + } + else { + // Final shutdown phase for a stream, we can finally change the + // current fileline to the new stream + curStreamp()->m_termState = 0; + FileLine* filelinep = curFilelinep(); + delete curStreamp(); + m_streampStack.pop(); // Must work as size>1; EOF is entry 0 + if (curStreamp()->m_eof) { + // EOF doesn't have a "real" fileline, but a linenumber of 0 from init time + // Inherit whatever we last parsed so it's more obvious. + curFilelinep(filelinep); + } + // The caller parser remembered the start location for the text we are parsing, + // but we've discovered there was a file switch along the way, so update it. + m_tokFilelinep = curFilelinep(); + // + if (curStreamp()->m_eof) { + return ""; + } else { + return curFilelinep()->lineDirectiveStrg(0); // Reenter resumed file + } + } +} + +void V3PreLex::initFirstBuffer(FileLine* filelinep) { + // Called from constructor to make first buffer + // yy_create_buffer also sets yy_fill_buffer=1 so reads from YY_INPUT + VPreStream* streamp = new VPreStream(filelinep); + streamp->m_eof = true; + m_streampStack.push(streamp); + // + m_bufferState = yy_create_buffer(NULL, YY_BUF_SIZE); + yy_switch_to_buffer(m_bufferState); + yyrestart(NULL); +} + +void V3PreLex::scanNewFile(FileLine* filelinep) { + // Called on new open file. scanBytesBack will be called next. + VPreStream* streamp = new VPreStream(filelinep); + m_tokFilelinep = curFilelinep(); + streamp->m_file = true; + scanSwitchStream(streamp); +} + +void V3PreLex::scanBytes(const string& str) { // Note buffers also appended in ::scanBytesBack // Not "m_buffers.push_front(string(strp,len))" as we need a `define // to take effect immediately, in the middle of the current buffer - yy_scan_bytes(strp, len); - m_bufferStack.push(currentBuffer()); // yy_scan_bytes makes new buffer + // Also we don't use scan_bytes that would set yy_fill_buffer + // which would force Flex to bypass our YY_INPUT routine. + VPreStream* streamp = new VPreStream(curFilelinep()); + streamp->m_buffers.push_front(str); + scanSwitchStream(streamp); +} + +void V3PreLex::scanSwitchStream(VPreStream* streamp) { + curStreamp()->m_buffers.push_front(currentUnreadChars()); + m_streampStack.push(streamp); + yyrestart(NULL); } void V3PreLex::scanBytesBack(const string& str) { // Initial creation, that will pull from YY_INPUT==inputToLex // Note buffers also appended in ::scanBytes - m_buffers.push_back(str); + if (curStreamp()->m_eof) yyerrorf("scanBytesBack without being under scanNewFile"); + curStreamp()->m_buffers.push_back(str); } -void V3PreLex::appendDefValue(const char* textp, size_t len) { - // Append given text to current definition value being formed - m_defValue.append(textp,len); +string V3PreLex::currentUnreadChars() { + // WARNING - Peeking at internals + ssize_t left = (yy_n_chars - (yy_c_buf_p -currentBuffer()->yy_ch_buf)); + if (left > 0) { // left may be -1 at EOS + *(yy_c_buf_p) = (yy_hold_char); + return string(yy_c_buf_p, left); + } else { + return ""; + } } YY_BUFFER_STATE V3PreLex::currentBuffer() { @@ -366,23 +495,48 @@ void V3PreLex::lineDirective(const char* textp) { } void V3PreLex::dumpSummary() { - cout<<"- pp::dumpSummary curBuf="<<(void*)(currentBuffer()) - <<" nBuf="< tmpstack = LEXP->m_streampStack; while (!tmpstack.empty()) { - printf(" %p",tmpstack.top()); + VPreStream* streamp = tmpstack.top(); + cout<<"- bufferStack["<<(void*)(streamp)<<"]: " + <<" at="<m_curFilelinep + <<" nBuf="<m_buffers.size() + <<" size0="<<(streamp->m_buffers.empty() ? 0 : streamp->m_buffers.front().length()) + <<(streamp->m_eof?" [EOF]":"") + <<(streamp->m_file?" [FILE]":""); + cout< see V3PreShellImp::debug + // Defines list + DefinesMap m_defines; ///< Map of defines + // STATE - V3PreLex* m_lexp; // Current lexer state (NULL = closed) - stack m_includeStack; // Stack of includers above current m_lexp + V3PreProc* m_preprocp; ///< Object we're holding data for + 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_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. + ps_DEFNAME_UNDEF, ps_DEFNAME_DEFINE, + ps_DEFNAME_IFDEF, ps_DEFNAME_IFNDEF, ps_DEFNAME_ELSIF, + ps_DEFFORM, ps_DEFVALUE, ps_DEFPAREN, ps_DEFARG, + ps_INCNAME, ps_ERRORNAME, ps_STRIFY }; + const char* procStateName(ProcState s) { + static const char* states[] + = {"ps_TOP", + "ps_DEFNAME_UNDEF", "ps_DEFNAME_DEFINE", + "ps_DEFNAME_IFDEF", "ps_DEFNAME_IFNDEF", "ps_DEFNAME_ELSIF", + "ps_DEFFORM", "ps_DEFVALUE", "ps_DEFPAREN", "ps_DEFARG", + "ps_INCNAME", "ps_ERRORNAME", "ps_STRIFY"}; + return states[s]; + }; + + stack m_states; ///< Current state of parser + 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 + string m_lineCmt; ///< Line comment(s) to be returned bool m_lineCmtNl; ///< Newline needed before inserting lineCmt int m_lineAdd; ///< Empty lines to return to maintain line count bool m_rawAtBol; ///< Last rawToken left us at beginning of line + // For getFinalToken + bool m_finAhead; ///< Have read a token ahead + int m_finToken; ///< Last token read + string m_finBuf; ///< Last yytext read + bool m_finAtBol; ///< Last getFinalToken left us at beginning of line + FileLine* m_finFilelinep; ///< Location of last returned token (internal only) + + // For stringification + string m_strify; ///< Text to be stringified + // For defines stack m_defRefs; // Pending definine substitution stack m_ifdefStack; ///< Stack of true/false emitting evaluations - unsigned m_defDepth; // How many `defines deep - - // Defines list - DefinesMap m_defines; // Map of defines + unsigned m_defDepth; ///< How many `defines deep // For getline() - string m_lineChars; // Characters left for next line + string m_lineChars; ///< Characters left for next line void v3errorEnd(ostringstream& str) { fileline()->v3errorEnd(str); } const char* tokenName(int tok); - int getRawToken(); - int getToken(); void debugToken(int tok, const char* cmtp); void parseTop(); void parseUndef(); private: // Internal methods - void eof(); + void endOfOneFile(); string defineSubst(V3DefineRef* refp); - void addLineComment(int enter_exit_level); bool defExists(const string& name); string defValue(const string& name); @@ -169,21 +188,41 @@ private: bool commentTokenMatch(string& cmdr, const char* strg); string trimWhitespace(const string& strg, bool trailing); void unputString(const string& strg); + void unputDefrefString(const string& strg); void parsingOn() { m_off--; - if (m_off<0) fileline()->v3fatalSrc("Underflow of parsing cmds"); - if (!m_off) addLineComment(0); + if (m_off<0) fatalSrc("Underflow of parsing cmds"); + // addLineComment no longer needed; getFinalToken will correct. } void parsingOff() { m_off++; } + int getRawToken(); + int getStateToken(); + int getFinalToken(string& buf); + + void statePush(ProcState state) { + m_states.push(state); + } + void statePop() { + m_states.pop(); + if (m_states.empty()) { + error("InternalError: Pop of parser state with nothing on stack"); + m_states.push(ps_TOP); + } + } + void stateChange(ProcState state) { + statePop(); statePush(state); + } + public: // METHODS, called from upper level shell - virtual void openFile(FileLine* fl, V3InFilter* filterp, const string& filename); - virtual bool isEof() const { return (m_lexp==NULL); } - virtual string getline(); - virtual void insertUnreadback(const string& text) { m_lineCmt += text; } + void openFile(FileLine* fl, V3InFilter* filterp, const string& filename); + bool isEof() const { return m_lexp->curStreamp()->m_eof; } + string getline(); + void insertUnreadback(const string& text) { m_lineCmt += text; } void insertUnreadbackAtBol(const string& text); + void addLineComment(int enter_exit_level); // METHODS, callbacks virtual void comment(const string& cmt); // Comment detected (if keepComments==2) @@ -195,17 +234,30 @@ public: virtual string removeDefines(const string& text); // Remove defines in a text string // CONSTRUCTORS - V3PreProcImp(FileLine* fl) : V3PreProc(fl) { - m_lexp = NULL; // Closed. - m_state = ps_TOP; + V3PreProcImp() : V3PreProc() { + m_debug = 0; + m_states.push(ps_TOP); m_off = 0; m_lineChars = ""; m_lastSym = ""; m_lineAdd = 0; m_lineCmtNl = false; m_rawAtBol = true; + m_finAhead = false; + m_finAtBol = true; m_defDepth = 0; } + void configure(FileLine* filelinep) { + // configure() separate from constructor to avoid calling abstract functions + m_preprocp = this; // Silly, but to make code more similar to Verilog-Perl + m_finFilelinep = filelinep->create(1); + // Create lexer + m_lexp = new V3PreLex (this, filelinep); + m_lexp->m_keepComments = m_preprocp->keepComments(); + m_lexp->m_keepWhitespace = m_preprocp->keepWhitespace(); + m_lexp->m_pedantic = m_preprocp->pedantic(); + m_lexp->debug(debug()>=5 ? debug() : 0); // See also V3PreProc::debug() method + } ~V3PreProcImp() { if (m_lexp) { delete m_lexp; m_lexp = NULL; } } @@ -215,7 +267,9 @@ public: // Creation V3PreProc* V3PreProc::createPreProc(FileLine* fl) { - return new V3PreProcImp(fl); + V3PreProcImp* preprocp = new V3PreProcImp(); + preprocp->configure(fl); + return preprocp; } bool V3PreProc::optPsl() { @@ -386,11 +440,20 @@ void V3PreProcImp::comment(const string& text) { } } +//************************************************************************* +// VPreProc Methods. + +FileLine* V3PreProc::fileline() { + V3PreProcImp* idatap = static_cast(this); + return idatap->m_lexp->m_tokFilelinep; +} + //********************************************************************** // Parser Utilities const char* V3PreProcImp::tokenName(int tok) { switch (tok) { + case VP_BACKQUOTE : return("BACKQUOTE"); case VP_COMMENT : return("COMMENT"); case VP_DEFARG : return("DEFARG"); case VP_DEFFORM : return("DEFFORM"); @@ -407,6 +470,7 @@ const char* V3PreProcImp::tokenName(int tok) { case VP_INCLUDE : return("INCLUDE"); case VP_LINE : return("LINE"); case VP_PSL : return("PSL"); + case VP_STRIFY : return("STRIFY"); case VP_STRING : return("STRING"); case VP_SYMBOL : return("SYMBOL"); case VP_TEXT : return("TEXT"); @@ -423,13 +487,22 @@ void V3PreProcImp::unputString(const string& strg) { // However this can lead to "flex scanner push-back overflow" // so instead we scan from a temporary buffer, then on EOF return. // This is also faster than the old scheme, amazingly. - if (1) { - if (m_lexp->m_bufferStack.empty() || m_lexp->m_bufferStack.top()!=m_lexp->currentBuffer()) { - fileline()->v3fatalSrc("bufferStack missing current buffer; will return incorrectly"); - // Hard to debug lost text as won't know till much later - } + if (m_lexp->m_bufferState!=m_lexp->currentBuffer()) { + fatalSrc("bufferStack missing current buffer; will return incorrectly"); + // Hard to debug lost text as won't know till much later } - m_lexp->scanBytes(strg.c_str(), strg.length()); + m_lexp->scanBytes(strg); +} + +void V3PreProcImp::unputDefrefString(const string& strg) { + int multiline = 0; + for (size_t i=0; icurStreamp()->m_ignNewlines += multiline; // Must be after unput - applies to new stream } string V3PreProcImp::trimWhitespace(const string& strg, bool trailing) { @@ -444,13 +517,17 @@ string V3PreProcImp::trimWhitespace(const string& strg, bool trailing) { string::size_type trailspace = 0; while (out.length() > trailspace && isspace(out[out.length()-1-trailspace])) trailspace++; + // Don't remove \{space_or_newline} + if (trailspace && out.length() > trailspace && out[out.length()-1-trailspace]=='\\') + trailspace--; if (trailspace) out.erase(out.length()-trailspace,trailspace); } return out; } string V3PreProcImp::defineSubst(V3DefineRef* refp) { - // Substitute out defines in a argumented define reference. + // Substitute out defines in a define reference. + // (We also need to call here on non-param defines to handle `") // We could push the define text back into the lexer, but that's slow // and would make recursive definitions and parameter handling nasty. // @@ -463,7 +540,7 @@ string V3PreProcImp::defineSubst(V3DefineRef* refp) { } // Grab value string value = defValue(refp->name()); - UINFO(4,"defineValue '"< argValueByName; { // Parse argument list into map @@ -488,11 +565,11 @@ string V3PreProcImp::defineSubst(V3DefineRef* refp) { if (argName!="") { if (refp->args().size() > numArgs) { // A call `def( a ) must be equivelent to `def(a ), so trimWhitespace - // Note other sims don't trim trailing whitespace, so we don't either. - string arg = trimWhitespace(refp->args()[numArgs], false); + // At one point we didn't trim trailing whitespace, but this confuses `" + string arg = trimWhitespace(refp->args()[numArgs], true); if (arg != "") value = arg; } else if (!haveDefault) { - fileline()->v3error("Define missing argument '"+argName+"' for: "+refp->name()+"\n"); + error("Define missing argument '"+argName+"' for: "+refp->name()+"\n"); return " `"+refp->name()+" "; } numArgs++; @@ -527,7 +604,7 @@ string V3PreProcImp::defineSubst(V3DefineRef* refp) { if (refp->args().size() > numArgs // `define X() is ok to call with nothing && !(refp->args().size()==1 && numArgs==0 && trimWhitespace(refp->args()[0],false)=="")) { - fileline()->v3error("Define passed too many arguments: "+refp->name()+"\n"); + error("Define passed too many arguments: "+refp->name()+"\n"); return " `"+refp->name()+" "; } } @@ -568,15 +645,31 @@ string V3PreProcImp::defineSubst(V3DefineRef* refp) { continue; } else if (cp[0]=='`' && cp[1]=='"') { - out += '"'; // `" means to put out a " without enabling quote mode (sort of) + out += "`\""; // `" means to put out a " without enabling quote mode (sort of) + // however we must expand any macro calls inside it first. + // So keep it `", so we don't enter quote mode. cp++; continue; } + else if (cp[0]=='`' && cp[1]=='\\' && cp[2]=='`' && cp[3]=='"') { + out += "`\\`\""; // `\`" means to put out a backslash quote + // Leave it literal until we parse the VP_STRIFY string + cp+=3; + continue; + } else if (cp[0]=='`' && cp[1]=='\\') { out += '\\'; // `\ means to put out a backslash cp++; continue; } + else if (cp[0]=='\\' && cp[1]=='\n') { + // We kept the \\n when we lexed because we don't want whitespace + // trimming to mis-drop the final \\n + // At replacement time we need the standard newline. + out += "\n"; // \\n newline + cp++; + continue; + } } if (cp[0]=='\\' && cp[1]) { out += cp[0]; // \{any} Put out literal next character @@ -589,7 +682,7 @@ string V3PreProcImp::defineSubst(V3DefineRef* refp) { } } - UINFO(4,"defineSubstOut '"< with the whole file. StrList wholefile; bool ok = filterp->readWholefile(filename, wholefile/*ref*/); if (!ok) { - fileline()->v3error("File not found: "+filename+"\n"); + error("File not found: "+filename+"\n"); return; } - if (m_lexp) { + if (!m_preprocp->isEof()) { // IE not the first file. // We allow the same include file twice, because occasionally it pops // up, with guards preventing a real recursion. - if (m_includeStack.size()>V3PreProc::INCLUDE_DEPTH_MAX) { - fileline()->v3error("Recursive inclusion of file: "+filename); + if (m_lexp->m_streampStack.size()>V3PreProc::INCLUDE_DEPTH_MAX) { + error("Recursive inclusion of file: "+filename); return; } // There's already a file active. Push it to work on the new one. - m_includeStack.push(m_lexp); addLineComment(0); } - m_lexp = new V3PreLex(); - m_lexp->m_keepComments = keepComments(); - m_lexp->m_pedantic = pedantic(); - m_lexp->m_curFilelinep = new FileLine(filename, 1); - m_fileline = m_lexp->m_curFilelinep; // Remember token start location + // Create new stream structure + m_lexp->scanNewFile(m_preprocp->fileline()->create(filename, 1)); addLineComment(1); // Enter - yy_flex_debug = (debug()>4)?1:0; - // Filter all DOS CR's en-mass. This avoids bugs with lexing CRs in the wrong places. // This will also strip them from strings, but strings aren't supposed to be multi-line without a "\" for (StrList::iterator it=wholefile.begin(); it!=wholefile.end(); ++it) { @@ -679,31 +762,6 @@ void V3PreProcImp::addLineComment(int enter_exit_level) { } } -void V3PreProcImp::eof() { - // Perhaps we're completing unputString - if (m_lexp->m_bufferStack.size()>1) { - UINFO(4,fileline()<<"EOS\n"); - // Switch to file or next unputString, but not a eof so don't delete lexer - yy_delete_buffer(m_lexp->currentBuffer()); - m_lexp->m_bufferStack.pop(); // Must work as size>1 - yy_switch_to_buffer(m_lexp->m_bufferStack.top()); - } else { - // Remove current lexer - UINFO(4,fileline()<<"EOF!\n"); - addLineComment(2); // Exit - // Destructor will call yy_delete_buffer - delete m_lexp; m_lexp=NULL; - // Perhaps there's a parent file including us? - if (!m_includeStack.empty()) { - // Back to parent. - m_lexp = m_includeStack.top(); m_includeStack.pop(); - addLineComment(0); - if (m_lexp->m_bufferStack.empty()) fileline()->v3fatalSrc("No include buffer to return to"); - yy_switch_to_buffer(m_lexp->m_bufferStack.top()); // newest buffer in older lexer - } - } -} - int V3PreProcImp::getRawToken() { // Get a token from the file, whatever it may be. while (1) { @@ -726,7 +784,7 @@ int V3PreProcImp::getRawToken() { yyourtext(rtncmt.c_str(), rtncmt.length()); m_lineCmt = ""; if (yyourleng()) m_rawAtBol = (yyourtext()[yyourleng()-1]=='\n'); - if (m_state==ps_DEFVALUE) { + if (m_states.top()==ps_DEFVALUE) { V3PreLex::s_currentLexp->appendDefValue(yyourtext(),yyourleng()); goto next_tok; } else { @@ -737,16 +795,13 @@ int V3PreProcImp::getRawToken() { if (isEof()) return (VP_EOF); // Snarf next token from the file - m_fileline = m_lexp->curFilelinep(); // Remember token start location - V3PreLex::s_currentLexp = m_lexp; // Tell parser where to get/put data - int tok = yylex(); + int tok = m_lexp->lex(); if (debug()) debugToken(tok, "RAW"); - // On EOF, try to pop to upper level includes, as needed. + // A EOF on an include, so we can print `line and detect mis-matched "s if (tok==VP_EOF) { - eof(); - goto next_tok; // Parse parent, or find the EOF. + goto next_tok; // find the EOF, after adding needed lines } if (yyourleng()) m_rawAtBol = (yyourtext()[yyourleng()-1]=='\n'); @@ -760,8 +815,9 @@ void V3PreProcImp::debugToken(int tok, const char* cmtp) { 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: %s %s s%d dr%d: <%d>%-10s: %s\n", - fileline()->lineno(), cmtp, m_off?"of":"on", m_state, (int)m_defRefs.size(), + fprintf (stderr, "%d: %s %s %s(%d) dr%d: <%d>%-10s: %s\n", + m_lexp->m_tokFilelinep->lineno(), cmtp, m_off?"of":"on", + procStateName(m_states.top()), (int)m_states.size(), (int)m_defRefs.size(), m_lexp->currentStartState(), tokenName(tok), buf.c_str()); } } @@ -769,15 +825,17 @@ void V3PreProcImp::debugToken(int tok, const char* cmtp) { // Sorry, we're not using bison/yacc. It doesn't handle returning white space // in the middle of parsing other tokens. -int V3PreProcImp::getToken() { - // Return the next user-visible token in the input stream. - // Includes and such are handled here, and are never seen by the caller. +int V3PreProcImp::getStateToken() { + // Return the next state-determined token while (1) { next_tok: if (isEof()) return VP_EOF; int tok = getRawToken(); - // Always emit white space and comments between tokens. - if (tok==VP_WHITE) return (tok); + ProcState state = m_states.top(); + + // Most states emit white space and comments between tokens. (Unless collecting a string) + if (tok==VP_WHITE && state !=ps_STRIFY) return (tok); + if (tok==VP_BACKQUOTE && state !=ps_STRIFY) { tok = VP_TEXT; } if (tok==VP_COMMENT) { if (!m_off) { if (m_lexp->m_keepComments == KEEPCMT_SUB) { @@ -790,33 +848,39 @@ int V3PreProcImp::getToken() { // We're off or processed the comment specially. If there are newlines // in it, we also return the newlines as TEXT so that the linenumber // count is maintained for downstream tools - for (size_t len=0; lenm_enterExit); goto next_tok; } + // Deal with some special parser states - switch (m_state) { + switch (state) { case ps_TOP: { break; } - case ps_DEFNAME: { + case ps_DEFNAME_UNDEF: // FALLTHRU + case ps_DEFNAME_DEFINE: // FALLTHRU + case ps_DEFNAME_IFDEF: // FALLTHRU + case ps_DEFNAME_IFNDEF: // FALLTHRU + case ps_DEFNAME_ELSIF: { if (tok==VP_SYMBOL) { - m_state = ps_TOP; m_lastSym.assign(yyourtext(),yyourleng()); - if (m_stateFor==VP_IFDEF - || m_stateFor==VP_IFNDEF) { + if (state==ps_DEFNAME_IFDEF + || state==ps_DEFNAME_IFNDEF) { bool enable = defExists(m_lastSym); UINFO(4,"Ifdef "<v3error("`elsif with no matching `if\n"); + error("`elsif with no matching `if\n"); } else { // Handle `else portion VPreIfEntry lastIf = m_ifdefStack.top(); m_ifdefStack.pop(); @@ -827,19 +891,24 @@ int V3PreProcImp::getToken() { m_ifdefStack.push(VPreIfEntry(enable, lastIf.everOn())); if (!enable) parsingOff(); } + statePop(); + goto next_tok; } - else if (m_stateFor==VP_UNDEF) { + else if (state==ps_DEFNAME_UNDEF) { if (!m_off) { UINFO(4,"Undef "<pushStateDefForm(); + goto next_tok; } - else fileline()->v3fatalSrc("Bad case\n"); + else fatalSrc("Bad case\n"); goto next_tok; } else if (tok==VP_TEXT) { @@ -847,16 +916,20 @@ int V3PreProcImp::getToken() { if (!m_off) return tok; else goto next_tok; } + else if (tok==VP_DEFREF) { + // IE, `ifdef `MACRO(x): Substitue and come back here when state pops. + break; + } else { - fileline()->v3error("Expecting define name. Found: "<m_defValue; - m_state = ps_DEFVALUE; - if (debug()) cout<<"DefFormals='"<pushStateDefValue(); goto next_tok; } else if (tok==VP_TEXT) { @@ -864,7 +937,7 @@ int V3PreProcImp::getToken() { if (!m_off) return tok; else goto next_tok; } else { - fileline()->v3error("Expecting define formal arguments. Found: "<m_defValue; @@ -880,16 +954,14 @@ int V3PreProcImp::getToken() { // Not removing returns in values has two problems, // 1. we need to correct line numbers with `line after each substitution // 2. Substituting in " .... " with embedded returns requires \ escape. - // This is very difficult in the presence of `". + // This is very difficult in the presence of `", so we keep the \ before the newline. for (size_t i=0; iv3fatalSrc(msg); + fatalSrc(msg); } - m_state = ps_TOP; + statePop(); // DEFVALUE is terminated by a return, but lex can't return both tokens. // Thus, we emit a return here. yyourtext(newlines.c_str(), newlines.length()); @@ -912,36 +985,40 @@ int V3PreProcImp::getToken() { } case ps_DEFPAREN: { if (tok==VP_TEXT && yyourleng()==1 && yyourtext()[0]=='(') { - m_state = ps_DEFARG; + stateChange(ps_DEFARG); goto next_tok; } else { - m_state = ps_TOP; - if (m_defRefs.empty()) v3fatalSrc("Shouldn't be in DEFPAREN w/o active defref"); + if (m_defRefs.empty()) fatalSrc("Shouldn't be in DEFPAREN w/o active defref"); V3DefineRef* refp = &(m_defRefs.top()); - fileline()->v3error("Expecting ( to begin argument list for define reference `"<name()); + error((string)"Expecting ( to begin argument list for define reference `"+refp->name()+"\n"); + statePop(); goto next_tok; } } case ps_DEFARG: { - if (m_defRefs.empty()) v3fatalSrc("Shouldn't be in DEFARG w/o active defref"); + if (m_defRefs.empty()) fatalSrc("Shouldn't be in DEFARG w/o active defref"); V3DefineRef* refp = &(m_defRefs.top()); refp->nextarg(refp->nextarg()+m_lexp->m_defValue); m_lexp->m_defValue=""; UINFO(4,"defarg++ "<nextarg()<args().push_back(refp->nextarg()); - m_state = ps_DEFARG; + stateChange(ps_DEFARG); m_lexp->pushStateDefArg(1); refp->nextarg(""); goto next_tok; } else if (tok==VP_DEFARG && yyourleng()==1 && yyourtext()[0]==')') { - refp->args().push_back(refp->nextarg()); - string out = defineSubst(refp); // Substitute in and prepare for next action // Similar code in non-parenthesized define (Search for END_OF_DEFARG) - m_defRefs.pop(); + refp->args().push_back(refp->nextarg()); + string out; + if (!m_off) { + out = defineSubst(refp); + //NOP: out = m_preprocp->defSubstitute(out); + } + m_defRefs.pop(); refp=NULL; if (m_defRefs.empty()) { - unputString(out.c_str()); - m_state = ps_TOP; + statePop(); + if (!m_off) unputDefrefString(out); m_lexp->m_parenLevel = 0; } else { // Finished a defref inside a upper defref @@ -951,7 +1028,7 @@ int V3PreProcImp::getToken() { refp = &(m_defRefs.top()); // We popped, so new top refp->nextarg(refp->nextarg()+m_lexp->m_defValue+out); m_lexp->m_defValue=""; m_lexp->m_parenLevel = refp->parenLevel(); - m_state = ps_DEFARG; + statePop(); // Will go to ps_DEFARG, as we're under another define } goto next_tok; } else if (tok==VP_DEFREF) { @@ -964,14 +1041,14 @@ int V3PreProcImp::getToken() { refp->nextarg(refp->nextarg()+rtn); goto next_tok; } else { - fileline()->v3error("Expecting ) or , to end argument list for define reference. Found: "< - m_state = ps_INCNAME; // Still + stateChange(ps_INCNAME); // Still m_lexp->pushStateIncFilename(); goto next_tok; } - else if (tok==VP_DEFREF) { + else if (tok==VP_DEFREF + || tok==VP_STRIFY) { // Expand it, then state will come back here break; } else { - m_state = ps_TOP; - fileline()->v3error("Expecting include filename. Found: "<v3error(m_lastSym); + error(m_lastSym); } + statePop(); goto next_tok; } else { - m_state = ps_TOP; - fileline()->v3error("Expecting `error string. Found: "<v3fatalSrc("Bad case\n"); + case ps_STRIFY: { + if (tok==VP_STRIFY) { + // Quote what's in the middle of the stringification + // Note a `" MACRO_WITH(`") `" doesn't need to be handled (we don't need a stack) + // That behavior isn't specified, and other simulators vary widely + string out = m_strify; + m_strify = ""; + // Convert any newlines to spaces, so we don't get a multiline "..." without \ escapes + // The spec is silent about this either way; simulators vary + string::size_type pos; + while ((pos=out.find("\n")) != string::npos) { + out.replace(pos, 1, " "); + } + unputString((string)"\""+out+"\""); + statePop(); + goto next_tok; + } + else if (tok==VP_EOF) { + error("`\" not terminated at EOF\n"); + } + else if (tok==VP_BACKQUOTE) { + m_strify += "\\\""; + goto next_tok; + } + else if (tok==VP_DEFREF) { + // Spec says to expand macros inside `" + // Substitue it into the stream, then return here + break; + } + else { + // Append token to eventual string + m_strify.append(yyourtext(),yyourleng()); + goto next_tok; + } + } + default: fatalSrc("Bad case\n"); } // Default is to do top level expansion of some tokens switch (tok) { case VP_INCLUDE: if (!m_off) { - m_state = ps_INCNAME; m_stateFor = tok; + statePush(ps_INCNAME); } goto next_tok; case VP_UNDEF: + statePush(ps_DEFNAME_UNDEF); + goto next_tok; case VP_DEFINE: + // No m_off check here, as a `ifdef NEVER `define FOO(`endif) should work + statePush(ps_DEFNAME_DEFINE); + goto next_tok; case VP_IFDEF: + statePush(ps_DEFNAME_IFDEF); + goto next_tok; case VP_IFNDEF: + statePush(ps_DEFNAME_IFNDEF); + goto next_tok; case VP_ELSIF: - m_state = ps_DEFNAME; m_stateFor = tok; + statePush(ps_DEFNAME_ELSIF); goto next_tok; case VP_ELSE: if (m_ifdefStack.empty()) { - fileline()->v3error("`else with no matching `if\n"); + error("`else with no matching `if\n"); } else { VPreIfEntry lastIf = m_ifdefStack.top(); m_ifdefStack.pop(); bool enable = !lastIf.everOn(); @@ -1042,7 +1164,7 @@ int V3PreProcImp::getToken() { case VP_ENDIF: UINFO(4,"Endif "<v3error("`endif with no matching `if\n"); + error("`endif with no matching `if\n"); } else { VPreIfEntry lastIf = m_ifdefStack.top(); m_ifdefStack.pop(); if (!lastIf.on()) parsingOn(); @@ -1052,30 +1174,40 @@ int V3PreProcImp::getToken() { goto next_tok; case VP_DEFREF: { - if (!m_off) { - string name; name.append(yyourtext()+1,yyourleng()-1); - UINFO(4,"DefRef "< V3PreProc::DEFINE_RECURSION_LEVEL_MAX) { - fileline()->v3error("Recursive `define substitution: `"+name); + // m_off not right here, but inside substitution, to make this work: `ifdef NEVER `DEFUN(`endif) + string name; name.append(yyourtext()+1,yyourleng()-1); + UINFO(4,"DefRef "< V3PreProc::DEFINE_RECURSION_LEVEL_MAX) { + error("Recursive `define substitution: `"+name); + goto next_tok; + } + // substitute + if (!defExists(name)) { // Not found, return original string as-is + m_defDepth = 0; + UINFO(4,"Defref `"< not_defined"< not_defined"< '"<defSubstitute(out); if (m_defRefs.empty()) { // Just output the substitution - unputString(out.c_str()); + unputDefrefString(out); } else { - // Inside another define. Can't subst now, or + // Inside another define. + // Can't subst now, or // `define a x,y // foo(`a,`b) would break because a contains comma V3DefineRef* refp = &(m_defRefs.top()); @@ -1083,27 +1215,26 @@ int V3PreProcImp::getToken() { } goto next_tok; } - else { // Found, with parameters - UINFO(4,"Defref `"< parameterized"<m_parenLevel); - m_defRefs.push(V3DefineRef(name, params)); - m_state = ps_DEFPAREN; m_stateFor = tok; - m_lexp->pushStateDefArg(0); - goto next_tok; - } } - fileline()->v3fatalSrc("Bad case\n"); + else { // Found, with parameters + UINFO(4,"Defref `"< parameterized"<m_parenLevel); + m_defRefs.push(V3DefineRef(name, params)); + statePush(ps_DEFPAREN); + m_lexp->pushStateDefArg(0); + goto next_tok; + } } - else goto next_tok; + fatalSrc("Bad case\n"); } case VP_ERROR: { - m_state = ps_ERRORNAME; m_stateFor = tok; + statePush(ps_ERRORNAME); goto next_tok; } case VP_EOF: if (!m_ifdefStack.empty()) { - fileline()->v3error("`ifdef not terminated at EOF\n"); + error("`ifdef not terminated at EOF\n"); } return tok; case VP_UNDEFINEALL: @@ -1112,6 +1243,12 @@ int V3PreProcImp::getToken() { undefineall(); } goto next_tok; + case VP_STRIFY: + // We must expand macros in the body of the stringification + // Then, when done, form a final string to return + // (it could be used as a include filename, for example, so need the string token) + statePush(ps_STRIFY); + goto next_tok; case VP_SYMBOL: case VP_STRING: case VP_PSL: @@ -1122,34 +1259,86 @@ 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; + case VP_DEFFORM: // Handled by state=ps_DEFFORM; + case VP_DEFVALUE: // Handled by state=ps_DEFVALUE; default: - fileline()->v3fatalSrc("Internal error: Unexpected token.\n"); + fatalSrc((string)"Internal error: Unexpected token "+tokenName(tok)+"\n"); break; } return tok; } } +int V3PreProcImp::getFinalToken(string& buf) { + // Return the next user-visible token in the input stream. + // Includes and such are handled here, and are never seen by the caller. + if (!m_finAhead) { + m_finAhead = true; + m_finToken = getStateToken(); + m_finBuf = string (yyourtext(), yyourleng()); + } + int tok = m_finToken; + buf = m_finBuf; + if (0 && debug()>=5) fprintf (stderr,"%d: FIN: %-10s: %s\n", + m_lexp->m_tokFilelinep->lineno(), + tokenName(tok), V3PreLex::cleanDbgStrg(buf).c_str()); + // Track `line + const char* bufp = buf.c_str(); + while (*bufp == '\n') bufp++; + if ((tok == VP_TEXT || tok == VP_LINE) && 0==strncmp(bufp,"`line ",6)) { + int enter; + m_finFilelinep->lineDirective(bufp, enter/*ref*/); + } + else { + if (m_finAtBol && !(tok==VP_TEXT && buf=="\n") + && m_preprocp->lineDirectives()) { + if (int outBehind = m_lexp->m_tokFilelinep->lineno() - m_finFilelinep->lineno()) { + if (debug()>=5) fprintf(stderr,"%d: FIN: readjust, fin at %d request at %d\n", + m_lexp->m_tokFilelinep->lineno(), + m_finFilelinep->lineno(), m_lexp->m_tokFilelinep->lineno()); + m_finFilelinep = m_finFilelinep->create(m_lexp->m_tokFilelinep->lineno()); + if (outBehind > 0 && outBehind <= (int)V3PreProc::NEWLINES_VS_TICKLINE) { + // Output stream is behind, send newlines to get back in sync + // (Most likely because we're completing a disabled `endif) + if (m_preprocp->keepWhitespace()) { + buf = string(outBehind,'\n'); + return VP_TEXT; + } + } else { + // Need to backup, use `line + buf = m_finFilelinep->lineDirectiveStrg(0); + return VP_LINE; + } + } + } + // Track newlines in prep for next token + for (const char* cp = buf.c_str(); *cp; cp++) { + if (*cp == '\n') { + m_finAtBol = true; + m_finFilelinep->linenoIncInPlace(); // Increment in place to avoid new/delete calls. It's private data. + } else { + m_finAtBol = false; + } + } + } + m_finAhead = false; // Consumed the token + return tok; +} + string V3PreProcImp::getline() { // Get a single line from the parse stream. Buffer unreturned text until the newline. if (isEof()) return ""; const char* rtnp; bool gotEof = false; while (NULL==(rtnp=strchr(m_lineChars.c_str(),'\n')) && !gotEof) { - int tok = getToken(); + string buf; + int tok = getFinalToken(buf/*ref*/); if (debug()>4) { - string buf = string (yyourtext(), yyourleng()); - 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: GETFETC: %-10s: %s\n", - fileline()->lineno(), tokenName(tok), buf.c_str()); + m_lexp->m_tokFilelinep->lineno(), tokenName(tok), V3PreLex::cleanDbgStrg(buf).c_str()); } if (tok==VP_EOF) { // Add a final newline, if the user forgot the final \n. - // Note tok==VP_EOF isn't always seen by us, as isEof() may be set earlier if (m_lineChars != "" && m_lineChars[m_lineChars.length()-1] != '\n') { m_lineChars.append("\n"); } @@ -1159,7 +1348,7 @@ string V3PreProcImp::getline() { m_lineChars.append(" psl "); } else { - m_lineChars.append(yyourtext(),0,yyourleng()); + m_lineChars.append(buf); } } @@ -1167,6 +1356,12 @@ string V3PreProcImp::getline() { int len = rtnp-m_lineChars.c_str()+1; string theLine(m_lineChars, 0, len); m_lineChars = m_lineChars.erase(0,len); // Remove returned characters - UINFO(4, fileline()->lineno()<<": GETLINE: "<=4) fprintf (stderr,"%d: GETLINE: %s\n", + m_lexp->m_tokFilelinep->lineno(), + V3PreLex::cleanDbgStrg(theLine).c_str()); + if (debug()>=4) fprintf (stderr,"%d: GETLINE len %d FC %d\n", + m_lexp->m_tokFilelinep->lineno(), + (int)(theLine.length()), + (int)(theLine.length()>0?theLine[0]:-1)); return theLine; } diff --git a/src/V3PreProc.h b/src/V3PreProc.h index 2cb8c17c2..c9731a1b7 100644 --- a/src/V3PreProc.h +++ b/src/V3PreProc.h @@ -32,6 +32,9 @@ #include #include +// Compatibility with Verilog-Perl's preprocessor +#define fatalSrc(msg) v3fatalSrc(msg) + class V3InFilter; class V3PreProc { @@ -40,14 +43,14 @@ class V3PreProc { protected: // STATE - FileLine* m_fileline; // Last token's starting point int m_debug; // Debugging public: // CONSTANTS enum MiscConsts { DEFINE_RECURSION_LEVEL_MAX = 50, // How many `def substitutions before an error - INCLUDE_DEPTH_MAX = 500 // How many `includes deep before an error + INCLUDE_DEPTH_MAX = 500, // How many `includes deep before an error + NEWLINES_VS_TICKLINE = 20 // Use `line in place of this many newlines }; // ACCESSORS @@ -60,11 +63,12 @@ public: int debug() const { return m_debug; } void debug(int level) { m_debug = level; } - FileLine* fileline() { return m_fileline; } // File/Line number for last getline call + FileLine* fileline(); ///< File/Line number for last getline call // CONTROL METHODS // These options control how the parsing proceeds int keepComments() { return 2; } // Return comments, 0=no, 1=yes, 2=callback + bool keepWhitespace() { return false; } bool lineDirectives() { return true; } // Insert `line directives bool pedantic() { return false; } // Obey standard; Don't substitute `error static bool optPsl(); @@ -83,12 +87,16 @@ public: } virtual string removeDefines(const string& text)=0; // Remove defines in a text string + // UTILITIES + void error(string msg) { fileline()->v3error(msg); } ///< Report a error + void fatal(string msg) { fileline()->v3fatalSrc(msg); } ///< Report a fatal error + protected: // CONSTUCTORS - V3PreProc(FileLine* fl) { - m_fileline=fl; + V3PreProc() { m_debug=0; }; + void configure(FileLine* fl); public: static V3PreProc* createPreProc(FileLine* fileline); virtual ~V3PreProc() {} diff --git a/test_regress/t/t_pipe_filter.out b/test_regress/t/t_pipe_filter.out index 48254933e..09d412234 100644 --- a/test_regress/t/t_pipe_filter.out +++ b/test_regress/t/t_pipe_filter.out @@ -8,45 +8,56 @@ +`line 10 "t/t_pipe_filter.v" 0 example line 10; example line 11; +`line 13 "t/t_pipe_filter.v" 0 `line 13 "t/t_pipe_filter.v" 0 `line 1 "t/t_pipe_filter_inc.vh" 1 int lint_off_line_7 = 1; +`line 2 "t/t_pipe_filter.v" 0 int lint_off_line_8 = 1; +`line 8 "t/t_pipe_filter.v" 0 inc line 6; inc line 7; inc line 8; inc line 9; -`line 12 "t/t_pipe_filter_inc.vh" 2 + +`line 13 "t/t_pipe_filter_inc.vh" 2 `line 13 "t/t_pipe_filter.v" 0 +`line 15 "t/t_pipe_filter.v" 0 `line 15 "t/t_pipe_filter.v" 0 `line 1 "t/t_pipe_filter_inc.vh" 1 int lint_off_line_7 = 1; +`line 2 "t/t_pipe_filter.v" 0 int lint_off_line_8 = 1; +`line 8 "t/t_pipe_filter.v" 0 inc line 6; inc line 7; inc line 8; inc line 9; -`line 12 "t/t_pipe_filter_inc.vh" 2 + +`line 13 "t/t_pipe_filter_inc.vh" 2 `line 15 "t/t_pipe_filter.v" 0 +`line 17 "t/t_pipe_filter.v" 0 example line 15; example line 16; -`line 19 "t/t_pipe_filter.v" 2 + +`line 20 "t/t_pipe_filter.v" 2 diff --git a/test_regress/t/t_pp_display.pl b/test_regress/t/t_pp_display.pl index 69799687b..ffa68590b 100755 --- a/test_regress/t/t_pp_display.pl +++ b/test_regress/t/t_pp_display.pl @@ -13,21 +13,23 @@ compile ( execute ( check_finished=>1, expect=>quotemeta( -'pre thrupre thrumid thrupost post: "right side" +qq{pre thrupre thrumid thrupost post: "right side" left side: "right side" -left side : "right side " -left_side : "right_side " -na : "right_side " -prep ( midp1 left_side midp2 ( outp ) ) : "right_side " +left side: "right side" +left_side: "right_side" +na: "right_side" +prep ( midp1 left_side midp2 ( outp ) ): "right_side" na: "nana" -`ls `rs : "`ls `rs " +left_side right_side: "left_side right_side" +left side: "right side" : "" left side: "right side" -left side : "right side " +left side: "right side" +standalone twoline: "first second" -Line 38 File "t/t_pp_display.v" +Line 49 File "t/t_pp_display.v" *-* All Finished *-* -')); +})); ok(1); 1; diff --git a/test_regress/t/t_pp_display.v b/test_regress/t/t_pp_display.v index c4d9c939a..346a8d2ae 100644 --- a/test_regress/t/t_pp_display.v +++ b/test_regress/t/t_pp_display.v @@ -17,6 +17,7 @@ module t; `define thru(x) x `define thruthru `ls `rs // Doesn't expand `define msg(x,y) `"x: `\`"y`\`"`" +`define left(m,left) m // The 'left' as the variable name shouldn't match the "left" in the `" string initial begin //$display(`msg( \`, \`)); // Illegal $display(`msg(pre `thru(thrupre `thru(thrumid) thrupost) post,right side)); @@ -27,13 +28,23 @@ module t; $display(`msg( prep ( midp1 `ls midp2 ( outp ) ) , `rs )); $display(`msg(`noarg,`noarg`noarg)); $display(`msg( `thruthru , `thruthru )); // Results vary between simulators + $display(`left(`msg( left side , right side ), left_replaced)); + //$display(`msg( `"tickquoted_left`", `"tickquoted_right`" )); // Syntax error +`ifndef VCS // Sim bug - wrong number of arguments, but we're right $display(`msg(`thru(),)); // Empty +`endif $display(`msg(`thru(left side),`thru(right side))); $display(`msg( `thru( left side ) , `thru( right side ) )); +`ifndef NC + $display(`"standalone`"); +`endif +`ifdef VERILATOR + // Illegal on some simulators, as the "..." crosses two lines `define twoline first \ second $display(`msg(twoline, `twoline)); +`endif $display("Line %0d File \"%s\"",`__LINE__,`__FILE__); diff --git a/test_regress/t/t_preproc.out b/test_regress/t/t_preproc.out index d72c6dcbd..c305d2feb 100644 --- a/test_regress/t/t_preproc.out +++ b/test_regress/t/t_preproc.out @@ -5,14 +5,15 @@ - - +`line 7 "t/t_preproc.v" 0 -`line 9 "t/t_preproc.v" 0 +`line 7 "t/t_preproc.v" 0 `line 1 "t/t_preproc_inc2.vh" 1 +`line 3 "t/t_preproc.v" 0 +`line 4 "t/t_preproc.v" 0 At file "t/t_preproc_inc2.vh" line 4 @@ -23,124 +24,211 @@ At file "t/t_preproc_inc2.vh" line 4 +`line 6 "inc3_a_filename_from_line_directive" 0 At file "inc3_a_filename_from_line_directive" line 10 +`line 12 "inc3_a_filename_from_line_directive" 0 - -`line 13 "inc3_a_filename_from_line_directive" 0 +`line 15 "inc3_a_filename_from_line_directive" 0 -`line 17 "inc3_a_filename_from_line_directive" 0 -`line 18 "inc3_a_filename_from_line_directive" 2 +`line 19 "inc3_a_filename_from_line_directive" 2 `line 6 "t/t_preproc_inc2.vh" 0 -`line 7 "t/t_preproc_inc2.vh" 2 -`line 9 "t/t_preproc.v" 0 + +`line 8 "t/t_preproc_inc2.vh" 2 +`line 7 "t/t_preproc.v" 0 +`line 12 "t/t_preproc.v" 0 /*verilator pass_thru comment*/ +`line 14 "t/t_preproc.v" 0 /*verilator pass_thru_comment2*/ +`line 19 "t/t_preproc.v" 0 +`line 22 "t/t_preproc.v" 0 wire [3:0] q = { + 1'b1 , + 1'b0 , + 1'b1 , 1'b1 -`line 25 "t/t_preproc.v" 0 - , - -`line 26 "t/t_preproc.v" 0 - 1'b0 , - 1'b1 -`line 27 "t/t_preproc.v" 0 - , - 1'b1 -`line 28 "t/t_preproc.v" 0 - }; +`line 29 "t/t_preproc.v" 0 text. +`line 31 "t/t_preproc.v" 0 foo bar foobar2 +`line 36 "t/t_preproc.v" 0 +`line 40 "t/t_preproc.v" 0 -first part second part third part -Line_Preproc_Check 49 +`line 46 "t/t_preproc.v" 0 +first part +`line 46 "t/t_preproc.v" 0 + second part +`line 46 "t/t_preproc.v" 0 + third part +{ +`line 47 "t/t_preproc.v" 0 + a, +`line 47 "t/t_preproc.v" 0 + b, +`line 47 "t/t_preproc.v" 0 + c} +Line_Preproc_Check 48 +`line 52 "t/t_preproc.v" 0 +`line 54 "t/t_preproc.v" 0 deep deep +`line 58 "t/t_preproc.v" 0 "Inside: `nosubst" "`nosubst" +`line 63 "t/t_preproc.v" 0 x y LLZZ x y -p q LLZZ p q r s LLZZ r s LLZZ p q LLZZ p q r s LLZZ r s +p q LLZZ p q r s LLZZ r s LLZZ p q LLZZ p q r s LLZZ r s -firstline comma","line LLZZ firstline comma","line +`line 69 "t/t_preproc.v" 0 +firstline comma","line LLZZ firstline comma","line +`line 71 "t/t_preproc.v" 0 -x y LLZZ "a" y +x y LLZZ "a" y +`line 74 "t/t_preproc.v" 0 (a,b)(a,b) +`line 77 "t/t_preproc.v" 0 $display("left side: \"right side\"") +`line 80 "t/t_preproc.v" 0 bar_suffix more +`line 83 "t/t_preproc.v" 0 -$c("Zap(\"",bug1,"\");");; -$c("Zap(\"","bug2","\");");; + +`line 85 "t/t_preproc.v" 0 + $c("Zap(\"",bug1,"\");");; + +`line 86 "t/t_preproc.v" 0 + $c("Zap(\"","bug2","\");");; + + + + + +`line 94 "t/t_preproc.v" 0 + + + + + + + initial begin + + $display("pre thrupre thrumid thrupost post: \"right side\""); + $display("left side: \"right side\""); + $display("left side: \"right side\""); + $display("left_side: \"right_side\""); + $display("na: \"right_side\""); + $display("prep ( midp1 left_side midp2 ( outp ) ): \"right_side\""); + $display("na: \"nana\""); + $display("left_side right_side: \"left_side right_side\""); + $display(": \"\""); + $display("left side: \"right side\""); + $display("left side: \"right side\""); + $display("standalone"); + +`line 115 "t/t_preproc.v" 0 + + + + $display("twoline: \"first second\""); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule + + + + +`line 128 "t/t_preproc.v" 0 -wire tmp_d1 = d1; wire tmp_o1 = tmp_d1 + 1; assign o1 = tmp_o1 ; -wire tmp_d2 = d2 ; wire tmp_o2 = tmp_d2 + 1; assign o2 = tmp_o2 ; +`line 133 "t/t_preproc.v" 0 +module add1 ( input wire d1, output wire o1); + +`line 134 "t/t_preproc.v" 0 +wire tmp_d1 = d1; +`line 134 "t/t_preproc.v" 0 +wire tmp_o1 = tmp_d1 + 1; +`line 134 "t/t_preproc.v" 0 +assign o1 = tmp_o1 ; +endmodule +module add2 ( input wire d2, output wire o2); + +`line 137 "t/t_preproc.v" 0 +wire tmp_d2 = d2; +`line 137 "t/t_preproc.v" 0 +wire tmp_o2 = tmp_d2 + 1; +`line 137 "t/t_preproc.v" 0 +assign o2 = tmp_o2 ; +endmodule +`line 140 "t/t_preproc.v" 0 @@ -148,13 +236,37 @@ wire tmp_d2 = d2 ; wire tmp_o2 = tmp_d2 + 1; assign o2 = tmp_o2 ; +`line 147 "t/t_preproc.v" 0 - generate for (i=0; i<(3); i=i+1) begin psl cover { m5k.f .ctl._ctl_mvldx_m1.d[i] & ~m5k.f .ctl._ctl_mvldx_m1.q[i] & !m5k.f .ctl._ctl_mvldx_m1.cond & ((m5k.f .ctl.alive & m5k.f .ctl.alive_m1))} report "fondNoRise: m5kc_fcl._ctl_mvldx_m1"; psl cover { ~m5k.f .ctl._ctl_mvldx_m1.d[i] & m5k.f .ctl._ctl_mvldx_m1.q[i] & !m5k.f .ctl._ctl_mvldx_m1.cond & ((m5k.f .ctl.alive & m5k.f .ctl.alive_m1))} report "fondNoFall: m5kc_fcl._ctl_mvldx_m1"; end endgenerate +`line 151 "t/t_preproc.v" 0 + +`line 151 "t/t_preproc.v" 0 + generate for (i=0; i<(3); i=i+1) begin +`line 151 "t/t_preproc.v" 0 + psl cover { m5k.f .ctl._ctl_mvldx_m1.d[i] & ~m5k.f .ctl._ctl_mvldx_m1.q[i] & !m5k.f .ctl._ctl_mvldx_m1.cond & ((m5k.f .ctl.alive & m5k.f .ctl.alive_m1))} report "fondNoRise: m5kc_fcl._ctl_mvldx_m1"; +`line 151 "t/t_preproc.v" 0 + psl cover { ~m5k.f .ctl._ctl_mvldx_m1.d[i] & m5k.f .ctl._ctl_mvldx_m1.q[i] & !m5k.f .ctl._ctl_mvldx_m1.cond & ((m5k.f .ctl.alive & m5k.f .ctl.alive_m1))} report "fondNoFall: m5kc_fcl._ctl_mvldx_m1"; +`line 151 "t/t_preproc.v" 0 + end endgenerate + +`line 155 "t/t_preproc.v" 0 +module prot(); +`protected + I!#r#e6<_Q{{E2+]I3<[3s)1@D|'E''i!O?]jD>Jo_![Cl) + #nj1]p,3^1~,="E@QZB\T)eU\pC#C|7=\$J$##A[@-@{Qk] +`endprotected +`line 160 "t/t_preproc.v" 0 +endmodule + + + + +`line 165 "t/t_preproc.v" 0 @@ -163,24 +275,74 @@ wire tmp_d2 = d2 ; wire tmp_o2 = tmp_d2 + 1; assign o2 = tmp_o2 ; +`line 173 "t/t_preproc.v" 0 begin addr <= (({regs[6], regs[7]} + 1)); rd <= 1; end and begin addr <= (({regs[6], regs[7]})); wdata <= (rdata); wr <= 1; end begin addr <= ({regs[6], regs[7]} + 1); rd <= 1; end -begin addr <= ({regs[6], regs[7]}); wdata <= (rdata); wr <= 1; end +begin addr <= ({regs[6], regs[7]}); wdata <= (rdata); wr <= 1; end more + + + +`line 179 "t/t_preproc.v" 0 + + +`line 180 "t/t_preproc.v" 0 +`line 1 "t/t_preproc_inc4.vh" 1 + +`line 3 "t/t_preproc.v" 0 + + + +`line 5 "t/t_preproc.v" 0 + + +`line 7 "t/t_preproc_inc4.vh" 2 +`line 180 "t/t_preproc.v" 0 + +`line 181 "t/t_preproc.v" 0 + + + +`line 184 "t/t_preproc.v" 0 + + +`line 186 "t/t_preproc.v" 0 + + + +`line 193 "t/t_preproc.v" 0 + +$blah("ab,cd","e,f"); +$blah(this.logfile,vec); +$blah(this.logfile,vec[1,2,3]); +$blah(this.logfile,{blah.name(), " is not foo"}); + + + + +`line 202 "t/t_preproc.v" 0 +`pragma foo = 1 +`default_nettype none +`default_nettype uwire + + + + +`line 209 "t/t_preproc.v" 0 -`line 130 "t/t_preproc.v" 0 - -Line_Preproc_Check 131 +`line 213 "t/t_preproc.v" 0 +Line_Preproc_Check 213 +`line 218 "t/t_preproc.v" 0 @@ -188,11 +350,35 @@ Line_Preproc_Check 131 -(x,y ) -Line_Preproc_Check 144 +`line 225 "t/t_preproc.v" 0 +(x,y) +Line_Preproc_Check 226 + +`line 231 "t/t_preproc.v" 0 + + + + +beginend +beginend +"beginend" + + + +`line 241 "t/t_preproc.v" 0 + + + `\esc`def + +`line 245 "t/t_preproc.v" 0 +Not a \`define + + + +`line 249 "t/t_preproc.v" 0 @@ -201,25 +387,23 @@ x,y)--bee submacro has comma paren +`line 257 "t/t_preproc.v" 0 $display("bits %d %d", $bits(foo), `10); +`line 262 "t/t_preproc.v" 0 -`line 162 "t/t_preproc.v" 0 - +`line 265 "t/t_preproc.v" 0 -`line 164 "t/t_preproc.v" 0 - -`line 165 "t/t_preproc.v" 0 - +`line 270 "t/t_preproc.v" 0 @@ -232,12 +416,30 @@ $display("bits %d %d", $bits(foo), `10); - -`line 181 "t/t_preproc.v" 0 - -`line 181 "t/t_preproc.v" 0 - assign a3 = ~b3 ; +`line 282 "t/t_preproc.v" 0 +`line 282 "t/t_preproc.v" 0 + +`line 282 "t/t_preproc.v" 0 + +`line 282 "t/t_preproc.v" 0 + +`line 282 "t/t_preproc.v" 0 + +`line 282 "t/t_preproc.v" 0 + +`line 282 "t/t_preproc.v" 0 + +`line 282 "t/t_preproc.v" 0 + +`line 282 "t/t_preproc.v" 0 + +`line 282 "t/t_preproc.v" 0 + assign a3 = ~b3 ; +`line 282 "t/t_preproc.v" 0 + + +`line 284 "t/t_preproc.v" 0 \ @@ -247,32 +449,56 @@ $display("bits %d %d", $bits(foo), `10); - def foo +`line 293 "t/t_preproc.v" 0 + +`line 293 "t/t_preproc.v" 0 + +`line 293 "t/t_preproc.v" 0 + +`line 293 "t/t_preproc.v" 0 + def i + +`line 297 "t/t_preproc.v" 0 +`line 301 "t/t_preproc.v" 0 +`line 307 "t/t_preproc.v" 0 1 /*verilator NOT IN DEFINE*/ (nodef) 2 /*verilator PART OF DEFINE*/ (hasdef) -3 /*verilator NOT PART OF DEFINE*/ (nodef) -4 /*verilator PART OF DEFINE*/ (nodef) -5 also in also3 (nodef) +3 +`line 309 "t/t_preproc.v" 0 +/*verilator NOT PART + OF DEFINE*/ (nodef) +`line 310 "t/t_preproc.v" 0 +4 +`line 310 "t/t_preproc.v" 0 +/*verilator PART + OF DEFINE*/ (nodef) +`line 311 "t/t_preproc.v" 0 +5 also in +`line 311 "t/t_preproc.v" 0 + also3 (nodef) -HAS a NEW LINE +HAS a NEW +`line 314 "t/t_preproc.v" 0 +LINE +`line 318 "t/t_preproc.v" 0 @@ -286,40 +512,146 @@ HAS a NEW LINE +`line 331 "t/t_preproc.v" 0 +`line 334 "t/t_preproc.v" 0 EXP: clxx_scen clxx_scen EXP: clxx_scen "clxx_scen" EXP: do if (start("verilog/inc1.v", 25)) begin message({"Blah-", "clx_scen", " end"}); end while(0); -do -`line 239 "t/t_preproc.v" 0 - if (start("t/t_preproc.v", 239)) begin message({"Blah-", "clx_scen", " end"}); end while(0); + +`line 340 "t/t_preproc.v" 0 + do +`line 340 "t/t_preproc.v" 0 + +`line 340 "t/t_preproc.v" 0 + +`line 340 "t/t_preproc.v" 0 + +`line 340 "t/t_preproc.v" 0 + +`line 340 "t/t_preproc.v" 0 + if (start("t/t_preproc.v", 340)) begin +`line 340 "t/t_preproc.v" 0 + +`line 340 "t/t_preproc.v" 0 + message({"Blah-", "clx_scen", " end"}); +`line 340 "t/t_preproc.v" 0 + end +`line 340 "t/t_preproc.v" 0 + +`line 340 "t/t_preproc.v" 0 + while(0); +`line 344 "t/t_preproc.v" 0 - +`line 348 "t/t_preproc.v" 0 + +`line 348 "t/t_preproc.v" 0 + + +`line 349 "t/t_preproc.v" 0 -`line 249 "t/t_preproc.v" 0 - +`line 351 "t/t_preproc.v" 0 EXP: This is fooed -This is fooed - +This is fooed EXP: This is fooed_2 This is fooed_2 +`line 357 "t/t_preproc.v" 0 np np -`line 262 "t/t_preproc.v" 2 + +`line 362 "t/t_preproc.v" 0 + + + + + +`line 367 "t/t_preproc.v" 0 + + + + + +`line 372 "t/t_preproc.v" 0 + + + + + + +`line 378 "t/t_preproc.v" 0 + + + + +`line 382 "t/t_preproc.v" 0 +hello3hello3hello3 +hello4hello4hello4hello4 + + +`line 386 "t/t_preproc.v" 0 + + + +`line 388 "t/t_preproc.v" 0 +`line 1 "t/t_preproc_inc4.vh" 1 + +`line 3 "t/t_preproc.v" 0 + + + +`line 5 "t/t_preproc.v" 0 + + +`line 7 "t/t_preproc_inc4.vh" 2 +`line 388 "t/t_preproc.v" 0 + +`line 389 "t/t_preproc.v" 0 + + + + +`line 393 "t/t_preproc.v" 0 + + + + + +`line 397 "t/t_preproc.v" 0 + + + + +Line_Preproc_Check 401 + + +`line 404 "t/t_preproc.v" 0 + + + +Line_Preproc_Check 407 +"FOO \ + BAR " "arg_line1 \ + arg_line2" "FOO \ + BAR " +`line 410 "t/t_preproc.v" 0 +Line_Preproc_Check 410 + + +`line 413 "t/t_preproc.v" 2 diff --git a/test_regress/t/t_preproc.v b/test_regress/t/t_preproc.v index 6d1094d5b..cd5de413d 100644 --- a/test_regress/t/t_preproc.v +++ b/test_regress/t/t_preproc.v @@ -1,8 +1,6 @@ // DESCRIPTION: Verilator: Verilog Test module -// -// // This file ONLY is placed into the Public Domain, for any use, -// without warranty, 2004-2007 by Wilson Snyder. +// without warranty, 2000-2010 by Wilson Snyder. //=========================================================================== // Includes @@ -46,6 +44,7 @@ text. /*******COMMENT*****/ `MULTILINE +`MOREMULTILINE Line_Preproc_Check `__LINE__ //=========================================================================== @@ -86,14 +85,57 @@ $display(`msg(left side, right side)) `zap(bug1); `zap("bug2"); +/* Define inside comment: `DEEPER and `WITHTICK */ +// More commentary: `zap(bug1); `zap("bug2"); + +//====================================================================== +// display passthru + +`define ls left_side +`define rs right_side +`define noarg na +`define thru(x) x +`define thruthru `ls `rs // Doesn't expand +`define msg(x,y) `"x: `\`"y`\`"`" + initial begin + //$display(`msg( \`, \`)); // Illegal + $display(`msg(pre `thru(thrupre `thru(thrumid) thrupost) post,right side)); + $display(`msg(left side,right side)); + $display(`msg( left side , right side )); + $display(`msg( `ls , `rs )); + $display(`msg( `noarg , `rs )); + $display(`msg( prep ( midp1 `ls midp2 ( outp ) ) , `rs )); + $display(`msg(`noarg,`noarg`noarg)); + $display(`msg( `thruthru , `thruthru )); // Results vary between simulators + $display(`msg(`thru(),)); // Empty + $display(`msg(`thru(left side),`thru(right side))); + $display(`msg( `thru( left side ) , `thru( right side ) )); + $display(`"standalone`"); + + // Unspecified when the stringification has multiple lines +`define twoline first \ + second + $display(`msg(twoline, `twoline)); + //$display(`msg(left side, \ right side \ )); // Not sure \{space} is legal. + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule + +//====================================================================== // rt.cpan.org bug34429 + `define ADD_UP(a,c) \ wire tmp_``a = a; \ wire tmp_``c = tmp_``a + 1; \ assign c = tmp_``c ; -`ADD_UP(d1,o1) // expansion is OK -`ADD_UP( d2 , o2 ) // expansion is bad +module add1 ( input wire d1, output wire o1); + `ADD_UP(d1,o1) // expansion is OK +endmodule +module add2 ( input wire d2, output wire o2); + `ADD_UP( d2 , o2 ) // expansion is bad +endmodule `define check(mod, width, flopname, gate, path) \ generate for (i=0; i<(width); i=i+1) begin \ @@ -108,6 +150,17 @@ assign c = tmp_``c ; `check(m5kc_fcl, 3, _ctl_mvldx_m1, `CK_fr, `MF._ctl_mvldx_m1) // ignorecmt +//====================================================================== +// Quotes are legal in protected blocks. Grr. +module prot(); +`protected + I!#r#e6<_Q{{E2+]I3<[3s)1@D|'E''i!O?]jD>Jo_![Cl) + #nj1]p,3^1~,="E@QZB\T)eU\pC#C|7=\$J$##A[@-@{Qk] +`endprotected +endmodule +//" + +//====================================================================== // macro call with define that has comma `define REG_H 6 `define REG_L 7 @@ -119,9 +172,38 @@ assign c = tmp_``c ; `EX_READ((`_HL + 1)) and `EX_WRITE((`_HL), rdata) `EX_READ(`_HL + 1) -`EX_WRITE(`_HL, rdata) +`EX_WRITE(`_HL, rdata) more -//=========================================================================== +//====================================================================== +// include of parameterized file +`define INCNAME "t_preproc_inc4.vh" +`include `INCNAME +`ifndef T_PREPROC_INC4 + `error "No Inc4" +`endif +`undef T_PREPROC_INC4 + +`ifdef NOT_DEFINED_INC + `include NOT_DEFINED_INC +`endif + +//====================================================================== +// macro call with , in {} + +`define xxerror(logfile, msg) $blah(logfile,msg) +`xxerror("ab,cd","e,f"); +`xxerror(this.logfile, vec); +`xxerror(this.logfile, vec[1,2,3]); +`xxerror(this.logfile, {blah.name(), " is not foo"}); + +//====================================================================== +// pragma/default net type + +`pragma foo = 1 +`default_nettype none +`default_nettype uwire + +//====================================================================== // Ifdef `define EMPTY_TRUE @@ -143,6 +225,25 @@ Line_Preproc_Check `__LINE__ ) Line_Preproc_Check `__LINE__ +//====================================================================== +// defines split arguments + +`define BEGIN begin +`define END end +`define BEGINEND `BEGIN`END +`define quoteit(x) `"x`" +`BEGIN`END // 2001 spec doesn't require two tokens, so "beginend" ok +`BEGINEND // 2001 spec doesn't require two tokens, so "beginend" ok +`quoteit(`BEGIN`END) // No space "beginend" + +//====================================================================== +// bug106 +`define \esc`def got_escaped +`ifdef \esc`def + `\esc`def +`endif +Not a \`define + //====================================================================== // misparsed comma in submacro `define sb bee @@ -257,3 +358,54 @@ EXP: This is fooed_2 `NOPARAM() `NOPARAM( ) //====================================================================== +// It's unclear if the spec allows this; is text_macro_idenitfier before or after substitution? +`define NODS_DEFINED +`define NODS_INDIRECT(x) x +`ifndef `NODS_INDIRECT(NODS_DEFINED) + `error "Indirect failed" +`endif +`ifdef `NODS_INDIRECT(NODS_UNDEFINED) + `error "Indirect2 failed" +`endif +//====================================================================== +// Metaprogramming +`define REPEAT_0(d) +`define REPEAT_1(d) d +`define REPEAT_2(d) `REPEAT_1(d)d +`define REPEAT_3(d) `REPEAT_2(d)d +`define REPEAT_4(d) `REPEAT_3(d)d + +`define CONCAT(a, b) a``b +`define REPEATC(n, d) `CONCAT(`REPEAT_, n)(d) +`define REPEATT(n, d) `REPEAT_``n(d) + +`REPEATC(3, hello3 ) +`REPEATT(4, hello4 ) +//====================================================================== +// Include from stringification +`undef T_PREPROC_INC4 +`define NODS_CONC_VH(m) `"m.vh`" +`include `NODS_CONC_VH(t_preproc_inc4) +`ifndef T_PREPROC_INC4 `error_here `endif +//====================================================================== +// Defines doing defines +// Note the newline on the end - required to form the end of a define +`define DEFINEIT(d) d \ + +`define _DEFIF_Z_0 1 +`define DEFIF_NZ(d,n) `undef d `ifndef _DEFIF_Z_``n `DEFINEIT(`define d 1) `endif +`DEFIF_NZ(TEMP,1) +`ifndef TEMP `error "bad" `endif +`DEFIF_NZ(TEMP,0) +`ifdef TEMP `error "bad0" `endif +Line_Preproc_Check `__LINE__ +//====================================================================== +// Quoted multiline - track line numbers, and insure \\n gets propagated +`define MULQUOTE "FOO \ + BAR " +`define MULQUOTE2(mq) `MULQUOTE mq `MULQUOTE +Line_Preproc_Check `__LINE__ +`MULQUOTE2("arg_line1 \ + arg_line2") +Line_Preproc_Check `__LINE__ +//====================================================================== diff --git a/test_regress/t/t_preproc_def09.out b/test_regress/t/t_preproc_def09.out index 3d6ba1f2d..90131cae0 100644 --- a/test_regress/t/t_preproc_def09.out +++ b/test_regress/t/t_preproc_def09.out @@ -8,8 +8,9 @@ +`line 10 "t/t_preproc_def09.v" 0 -'initial $display("start", "msg1" , "msg2" , "end");' +'initial $display("start", "msg1" , "msg2", "end");' 'initial $display("start", "msg1" , "msg2" , "end");' 'initial $display("start", " msg1" , , "end");' 'initial $display("start", " msg1" , , "end");' @@ -24,15 +25,17 @@ +`line 26 "t/t_preproc_def09.v" 0 -'$display(5,,2,,3 );' '$display(5,,2,,3);' -'$display(1 ,,"B",,3 );' +'$display(5,,2,,3);' +'$display(1,,"B",,3);' '$display(1 ,,"B",,3 );' '$display(5,,2,,);' '$display(5,,2,,);' +`line 35 "t/t_preproc_def09.v" 0 '$display(1,,,,3);' '$display(5,,,,"C");' @@ -41,23 +44,27 @@ '$display(5,,2,,"C");' '$display(5,,2,,"C");' +`line 43 "t/t_preproc_def09.v" 0 -'$display(1 ,,0,,"C");' +'$display(1,,0,,"C");' '$display(1 ,,0,,"C");' '$display(5,,0,,"C");' '$display(5,,0,,"C");' +`line 50 "t/t_preproc_def09.v" 0 -'b + 1 + 42 + a ' +'b + 1 + 42 + a' 'b + 1 + 42 + a' +`line 55 "t/t_preproc_def09.v" 0 '"==)" "((((" () '; '"==)" "((((" () '; +`line 60 "t/t_preproc_def09.v" 0 @@ -68,8 +75,10 @@ +`line 70 "t/t_preproc_def09.v" 0 '(6) (eq=al) ZOT' HERE-71 - Line71 -`line 74 "t/t_preproc_def09.v" 2 + +`line 75 "t/t_preproc_def09.v" 2 diff --git a/test_regress/t/t_preproc_inc4.vh b/test_regress/t/t_preproc_inc4.vh new file mode 100644 index 000000000..da6d5c484 --- /dev/null +++ b/test_regress/t/t_preproc_inc4.vh @@ -0,0 +1,5 @@ +// DESCRIPTION: Verilog::Preproc: Example source code +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2000-2010 by Wilson Snyder. + +`define T_PREPROC_INC4 diff --git a/test_regress/t/t_preproc_psl_off.out b/test_regress/t/t_preproc_psl_off.out index c86a48ac0..dfb321c53 100644 --- a/test_regress/t/t_preproc_psl_off.out +++ b/test_regress/t/t_preproc_psl_off.out @@ -5,16 +5,20 @@ +`line 7 "t/t_preproc_psl.v" 0 /*verilator metacomment preserved*/ +`line 9 "t/t_preproc_psl.v" 0 /*verilator metacomment also_preserved*/ +`line 11 "t/t_preproc_psl.v" 0 Hello in t_preproc_psl.v +`line 17 "t/t_preproc_psl.v" 0 @@ -22,32 +26,47 @@ Hello in t_preproc_psl.v +`line 28 "t/t_preproc_psl.v" 0 + +`line 28 "t/t_preproc_psl.v" 0 + +`line 28 "t/t_preproc_psl.v" 0 + +`line 28 "t/t_preproc_psl.v" 0 - - - +`line 29 "t/t_preproc_psl.v" 0 29 +`line 31 "t/t_preproc_psl.v" 0 +`line 40 "t/t_preproc_psl.v" 0 + +`line 40 "t/t_preproc_psl.v" 0 + +`line 40 "t/t_preproc_psl.v" 0 + +`line 40 "t/t_preproc_psl.v" 0 + +`line 40 "t/t_preproc_psl.v" 0 - - - - +`line 41 "t/t_preproc_psl.v" 0 41 +`line 43 "t/t_preproc_psl.v" 0 +`line 45 "t/t_preproc_psl.v" 0 +`line 50 "t/t_preproc_psl.v" 0 @@ -55,16 +74,17 @@ Hello in t_preproc_psl.v +`line 57 "t/t_preproc_psl.v" 0 -`line 59 "t/t_preproc_psl.v" 0 - +`line 62 "t/t_preproc_psl.v" 0 +`line 65 "t/t_preproc_psl.v" 0 `psl psl assert always sig!=90; @@ -72,5 +92,7 @@ psl assert always sig!=90; +`line 72 "t/t_preproc_psl.v" 0 72 -`line 73 "t/t_preproc_psl.v" 2 + +`line 74 "t/t_preproc_psl.v" 2 diff --git a/test_regress/t/t_preproc_psl_on.out b/test_regress/t/t_preproc_psl_on.out index 0f2ad24a7..35550efc3 100644 --- a/test_regress/t/t_preproc_psl_on.out +++ b/test_regress/t/t_preproc_psl_on.out @@ -5,16 +5,20 @@ +`line 7 "t/t_preproc_psl.v" 0 /*verilator metacomment preserved*/ +`line 9 "t/t_preproc_psl.v" 0 /*verilator metacomment also_preserved*/ +`line 11 "t/t_preproc_psl.v" 0 Hello in t_preproc_psl.v +`line 17 "t/t_preproc_psl.v" 0 psl default clock = (posedge clk); psl fails1: cover {cyc==10}; psl assert always cyc!=10; @@ -29,10 +33,12 @@ Hello in t_preproc_psl.v 29 +`line 31 "t/t_preproc_psl.v" 0 psl +`line 35 "t/t_preproc_psl.v" 0 fails_ml: assert always cyc==3 -> mask==8'h21; @@ -41,13 +47,16 @@ Hello in t_preproc_psl.v 41 +`line 43 "t/t_preproc_psl.v" 0 psl assert never (cyc==1 && reset_l); +`line 45 "t/t_preproc_psl.v" 0 psl fails3: assert always psl cyc==3 -> mask==8'h21; +`line 50 "t/t_preproc_psl.v" 0 psl assert always psl {[*]; cyc==3; psl cyc==4; cyc==6}; @@ -55,16 +64,17 @@ Hello in t_preproc_psl.v +`line 57 "t/t_preproc_psl.v" 0 -`line 59 "t/t_preproc_psl.v" 0 - +`line 62 "t/t_preproc_psl.v" 0 psl assert always cyc!=10; +`line 65 "t/t_preproc_psl.v" 0 `psl psl assert always sig!=90; @@ -72,5 +82,7 @@ psl assert always sig!=90; +`line 72 "t/t_preproc_psl.v" 0 72 -`line 73 "t/t_preproc_psl.v" 2 + +`line 74 "t/t_preproc_psl.v" 2