%option noyywrap align interactive %option stack %option noc++ %option prefix="V3PreLex" %{ /************************************************************************** * DESCRIPTION: Verilator: Flex verilog preprocessor * * Code available from: http://www.veripool.org/verilator * ************************************************************************** * * Copyright 2003-2012 by Wilson Snyder. This program is free software; * you can redistribute it and/or modify it under the terms of either the * GNU Lesser General Public License Version 3 or the Perl Artistic License * Version 2.0. * * Verilator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * ************************************************************************** * Do not use Flex in C++ mode. It has bugs with yyunput() which result in * lost characters. **************************************************************************/ #include "V3PreProc.h" #include "V3PreLex.h" V3PreLex* V3PreLex::s_currentLexp = NULL; // Current lexing point #define LEXP V3PreLex::s_currentLexp #define YY_INPUT(buf,result,max_size) \ result = LEXP->inputToLex(buf,max_size); // Accessors, because flex keeps changing the type of yyleng char* yyourtext() { return yytext; } size_t yyourleng() { return yyleng; } void yyourtext(const char* textp, size_t size) { yytext=(char*)textp; yyleng=size; } // Prevent conflicts from perl version static void linenoInc() {LEXP->linenoInc();} static bool optPsl() { return V3PreProc::optPsl(); } static bool pedantic() { return LEXP->m_pedantic; } static void yyerror(char* msg) { LEXP->curFilelinep()->v3error(msg); } static void yyerrorf(const char* msg) { LEXP->curFilelinep()->v3error(msg); } static void appendDefValue(const char* t, size_t l) { LEXP->appendDefValue(t,l); } static int pslParenLevel() { return LEXP->m_pslParenLevel; } static void pslParenLevelInc() { LEXP->m_pslParenLevel++; } static void pslParenLevelDec() { if (pslParenLevel()) LEXP->m_pslParenLevel--; } static bool pslMoreNeeded() { return LEXP->m_pslMoreNeeded; } static void pslMoreNeeded(bool flag) { LEXP->m_pslMoreNeeded = flag; } /**********************************************************************/ %} %x PSLONEM %x PSLONEE %x PSLMULM %x PSLMUL1 %x CMTONEM %x CMTBEGM %x CMTMODE %x STRMODE %x DEFFPAR %x DEFFORM %x DEFVAL %x DEFCMT %x ARGMODE %x INCMODE %x PRTMODE /* drop: Drop Ctrl-Z - can't pass thru or may EOF the output too soon */ ws [ \t\f\r] wsn [ \t\f] crnl [\r]*[\n] quote [\"] tickquote [`][\"] backslash [\\] /* Where we use symb/symbdef, we must also look for a `` join */ /* Note in the preprocessor \ESCaped is *not* always special; mantis1537/bug441 */ 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 /**************************************************************/ %% ^{ws}*"`line"{ws}+.*{crnl} { LEXP->lineDirective(yytext); return(VP_LINE); } /* Special directives we recognize */ "`define" { return(VP_DEFINE); } "`else" { return(VP_ELSE); } "`elsif" { return(VP_ELSIF); } "`endif" { return(VP_ENDIF); } "`ifdef" { return(VP_IFDEF); } "`ifndef" { return(VP_IFNDEF); } "`include" { return(VP_INCLUDE); } "`undef" { return(VP_UNDEF); } "`undefineall" { return(VP_UNDEFINEALL); } /* Optional directives we recognize */ "`__FILE__" { static string rtnfile; rtnfile = '"'; rtnfile += LEXP->curFilelinep()->filename().c_str(); rtnfile += '"'; yytext=(char*)rtnfile.c_str(); yyleng = rtnfile.length(); return (VP_STRING); } "`__LINE__" { static char buf[10]; sprintf(buf, "%d",LEXP->curFilelinep()->lineno()); yytext = buf; yyleng = strlen(yytext); return (VP_TEXT); } "`error" { if (!pedantic()) return (VP_ERROR); else return(VP_DEFREF); } /* Pass-through strings */ {quote} { yy_push_state(STRMODE); yymore(); } <> { linenoInc(); yyerrorf("EOF in unterminated string"); yyleng=0; yyterminate(); } {crnl} { linenoInc(); yyerrorf("Unterminated string"); BEGIN(INITIAL); } {word} { yymore(); } [^\"\\] { yymore(); } {backslash}{crnl} { linenoInc(); yymore(); } {backslash}. { yymore(); } {quote} { yy_pop_state(); 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(); } {crnl} { linenoInc(); yymore(); } . { yymore(); } "`endprotected" { yy_pop_state(); return (VP_TEXT); } /* Pass-through include <> filenames */ <> { linenoInc(); yyerrorf("EOF in unterminated include filename"); yyleng=0; yyterminate(); } {crnl} { linenoInc(); yyerrorf("Unterminated include filename"); BEGIN(INITIAL); } [^\>\\] { yymore(); } {backslash}. { yymore(); } [\>] { yy_pop_state(); return (VP_STRING); } /* Reading definition formal parenthesis (or not) to begin formal arguments */ /* Note '(' must IMMEDIATELY follow definition name */ [(] { appendDefValue("(",1); LEXP->m_formalLevel=1; BEGIN(DEFFORM); } {crnl} { yy_pop_state(); unput('\n'); yyleng=0; return VP_DEFFORM; } /* DEFVAL will later grab the return */ <> { yy_pop_state(); return VP_DEFFORM; } /* empty formals */ . { yy_pop_state(); unput(yytext[yyleng-1]); yyleng=0; return VP_DEFFORM; } /* empty formals */ /* Reading definition formals (declaration of a define) */ [(] { appendDefValue(yytext,yyleng); yyleng=0; ++LEXP->m_formalLevel; } [)] { appendDefValue(yytext,yyleng); yyleng=0; if ((--LEXP->m_formalLevel)==0) { yy_pop_state(); 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",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\\(){}\[\]\"]+ | [\\][^\n\r] | . { appendDefValue(yytext,yyleng); } /* Reading definition value (declaration of a define's text) */ "/*" { LEXP->m_defCmtSlash=false; yy_push_state(DEFCMT); yymore(); } /* Special comment parser */ "//"[^\n\r]*[\\]{crnl} { linenoInc(); appendDefValue((char*)"\n",1); } /* Spec says // not part of define value */ "//"[^\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",2); } /* Return, AND \ is part of define value */ [^\/\*\n\r\\]+ | [\\][^\n\r] | . { appendDefValue(yytext,yyleng); } /* Comments inside define values - if embedded get added to define value per spec */ /* - if no \{crnl} ending then the comment belongs to the next line, as a non-embedded comment */ /* - if all but (say) 3rd line is missing \ then it's indeterminate */ "*/" { yy_pop_state(); appendDefValue(yytext,yyleng); } [\\]{crnl} { linenoInc(); LEXP->m_defCmtSlash=true; appendDefValue(yytext,yyleng-2); appendDefValue((char*)"\n",1); } /* Return but not \ */ {crnl} { linenoInc(); yymore(); if (LEXP->m_defCmtSlash) yyerrorf("One line of /* ... */ is missing \\ before newline"); BEGIN(CMTMODE); } {word} { yymore(); } . { yymore(); } <> { yyerrorf("EOF in '/* ... */' block comment\n"); yyleng=0; yyterminate(); } /* 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++; // Note paren level 0 means before "(" of starting args // Level 1 means "," between arguments // Level 2+ means one argument's internal () if (LEXP->m_parenLevel>1) { appendDefValue(yytext,yyleng); } else { return (VP_TEXT); }} [)] { LEXP->m_parenLevel--; if (LEXP->m_parenLevel>0) { appendDefValue(yytext,yyleng); } else { yy_pop_state(); return (VP_DEFARG); }} [,] { if (LEXP->m_parenLevel>1) { appendDefValue(yytext,yyleng); } else { yy_pop_state(); return (VP_DEFARG); }} "`"{symbdef} { appendDefValue(yytext,yyleng); } /* defref in defref - outer macro expands first */ "`"{symbdef}`` { appendDefValue(yytext,yyleng); } /* defref in defref - outer macro expands first */ [^\/\*\n\r\\(,){}\[\]\"`]+ | . { appendDefValue(yytext,yyleng); } /* One line comments. */ "//"{ws}*{psl} { if (optPsl()) { pslMoreNeeded(true); yy_push_state(PSLONEM); return(VP_PSL); } else { yy_push_state(CMTONEM); yymore(); } } "//"{ws}*{crnl} { linenoInc(); yytext=(char*)"\n"; yyleng=1; return (VP_WHITE); } "//" { if (pslMoreNeeded()) { pslMoreNeeded(true); yy_push_state(PSLONEM); return(VP_PSL); } else { yy_push_state(CMTONEM); yymore(); } } [^\n\r]* { yy_pop_state(); return (VP_COMMENT); } /* Psl oneline comments */ [{(] { pslParenLevelInc(); return (VP_TEXT); } [})] { pslParenLevelDec(); return (VP_TEXT); } [;] { if (!pslParenLevel()) {BEGIN PSLONEE; pslMoreNeeded(false);} return (VP_TEXT); } {crnl} { linenoInc(); yy_pop_state(); yytext=(char*)"\n"; yyleng=1; return(VP_WHITE); } /* Completed psl oneline comments */ {crnl} { linenoInc(); yy_pop_state(); yytext=(char*)"\n"; yyleng=1; return(VP_WHITE); } {ws}+ { yymore(); } . { yyerrorf("Unexpected text following psl assertion\n"); } /* C-style comments. */ /**** See also DEFCMT */ /* We distinguish between the start of a comment, and later, so we may find a "psl" prefix */ "/*" { yy_push_state(optPsl() ? CMTBEGM : CMTMODE); yymore(); } {psl} { yyleng -= 3; BEGIN PSLMUL1; return (VP_COMMENT); } {ws}+ { yymore(); } "*/" { yy_pop_state(); return(VP_COMMENT); } {crnl} { linenoInc(); yymore(); } <> { yyerrorf("EOF in '/* ... */' block comment\n"); yyleng=0; yyterminate(); } {word} { yymore(); } . { BEGIN CMTMODE; yymore(); } /* Non 'psl' beginning in comment */ . { yymore(); } /* Psl C-style comments. */ /* EOFs are normal because / * `foo(..) * / hits a unputString EOF */ .|{crnl} { yyless(0); BEGIN PSLMULM; return(VP_PSL); } "*/" { yy_pop_state(); return(VP_COMMENT); } "//"[^\n\r]* { return (VP_COMMENT); } /* Comments inside block comments get literal inclusion (later removal) */ /* Define calls */ /* symbdef prevents normal lex rules from making `\`"foo a symbol {`"foo} instead of a BACKQUOTE */ "`"{symbdef} { return (VP_DEFREF); } "`"{symbdef}`` { yyleng-=2; return (VP_DEFREF_JOIN); } /* Generics */ {crnl} { linenoInc(); yytext=(char*)"\n"; yyleng=1; return(VP_WHITE); } <> { yyterminate(); } /* A "normal" EOF */ {symb} { return (VP_SYMBOL); } {symb}`` { yyleng-=2; return (VP_SYMBOL_JOIN); } {wsn}+ { return (VP_WHITE); } {drop} { } [\r] { } . { return (VP_TEXT); } %% void V3PreLex::pushStateDefArg(int level) { // Enter define substitution argument state yy_push_state(ARGMODE); m_parenLevel = 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(DEFVAL); m_parenLevel = 0; m_defValue = ""; } void V3PreLex::pushStateIncFilename() { // Enter include <> filename state yy_push_state(INCMODE); yymore(); } 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. // Note if we switched streams here (which we don't) "buf" would be // become a stale invalid pointer. // VPreStream* streamp = curStreamp(); if (debug()>=10) { 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, this); 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. if (streamDepth() > V3PreProc::DEFINE_RECURSION_LEVEL_MAX) { // The recursive `include in VPreProcImp should trigger first yyerrorf("Recursive `define or other nested inclusion"); curStreamp()->m_eof = true; // Fake it to stop recursion } else { VPreStream* streamp = new VPreStream(filelinep, this); 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 // Also we don't use scan_bytes that would set yy_fill_buffer // which would force Flex to bypass our YY_INPUT routine. if (streamDepth() > V3PreProc::DEFINE_RECURSION_LEVEL_MAX) { // More streams if recursive `define with complex insertion // More buffers mostly if something internal goes funky yyerrorf("Recursive `define or other nested inclusion"); curStreamp()->m_eof = true; // Fake it to stop recursion } else { VPreStream* streamp = new VPreStream(curFilelinep(), this); 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 if (curStreamp()->m_eof) yyerrorf("scanBytesBack without being under scanNewFile"); curStreamp()->m_buffers.push_back(str); } 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() { return YY_CURRENT_BUFFER; } int V3PreLex::currentStartState() const { return YY_START; } void V3PreLex::lineDirective(const char* textp) { curFilelinep()->lineDirective(textp, m_enterExit/*ref*/); // Make sure we have a dependency on whatever file was specified V3File::addSrcDepend(curFilelinep()->filename()); } void V3PreLex::dumpSummary() { cout<<"- pp::dumpSummary curBuf="<<(void*)(currentBuffer()); #ifdef FLEX_DEBUG // Else peeking at internals may cause portability issues ssize_t left = (yy_n_chars - (yy_c_buf_p -currentBuffer()->yy_ch_buf)); cout<<" left="< tmpstack = LEXP->m_streampStack; while (!tmpstack.empty()) { 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<