verilator/src/V3PreProc.cpp

1028 lines
32 KiB
C++

// -*- C++ -*-
//*************************************************************************
// DESCRIPTION: Verilog::Preproc: Internal implementation of default preprocessor
//
// Code available from: http://www.veripool.org/verilator
//
// Authors: Wilson Snyder
//
//*************************************************************************
//
// Copyright 2000-2009 by Wilson Snyder. This program is free software;
// you can redistribute it and/or modify it under the terms of either the GNU
// General Public License or the Perl Artistic License.
//
// This program 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.
//
//*************************************************************************
#include "config_build.h"
#include "verilatedos.h"
#include <cstdio>
#include <cstdarg>
#include <unistd.h>
#include <fstream>
#include <stack>
#include <vector>
#include <map>
#include "V3Error.h"
#include "V3Global.h"
#include "V3File.h"
#include "V3PreLex.h"
#include "V3PreProc.h"
#include "V3PreShell.h"
//======================================================================
// Build in LEX script
#define yyFlexLexer V3Lexer
#include "V3PreLex.yy.cpp"
#undef yyFlexLexer
//YYSTYPE yylval;
//*************************************************************************
class V3Define {
// Define class. One for each define.
//string m_name; // Name of the define (list is keyed by this)
FileLine* m_fileline; // Where it was declared
string m_value; // Value of define
string m_params; // Parameters
public:
V3Define(FileLine* fl, const string& value, const string& params)
: m_fileline(fl), m_value(value), m_params(params) {}
FileLine* fileline() const { return m_fileline; }
string value() const { return m_value; }
string params() const { return m_params; }
};
//*************************************************************************
class V3DefineRef {
// One for each pending define substitution
string m_name; // Define last name being defined
string m_params; // Define parameter list for next expansion
string m_nextarg; // String being built for next argument
int m_parenLevel; // Parenthesis counting inside def args
vector<string> m_args; // List of define arguments
public:
string name() const { return m_name; }
string params() const { return m_params; }
string nextarg() const { return m_nextarg; }
void nextarg(const string& value) { m_nextarg = value; }
int parenLevel() const { return m_parenLevel; }
vector<string>& args() { return m_args; }
V3DefineRef(const string& name, const string& params, int pl)
: m_name(name), m_params(params), m_parenLevel(pl) {}
~V3DefineRef() {}
};
//*************************************************************************
/// Data for parsing on/off
class VPreIfEntry {
// One for each pending ifdef/ifndef
bool m_on; // Current parse for this ifdef level is "on"
bool m_everOn; // Some if term in elsif tree has been on
public:
bool on() const { return m_on; }
bool everOn() const { return m_everOn; }
VPreIfEntry(bool on, bool everOn)
: m_on(on), m_everOn(everOn || on) {} // Note everOn includes new state
~VPreIfEntry() {}
};
//*************************************************************************
// Data for a preprocessor instantiation.
struct V3PreProcImp : public V3PreProc {
// TYPES
typedef std::map<string,V3Define> DefinesMap;
// debug() -> see V3PreShellImp::debug
// STATE
V3PreLex* m_lexp; // Current lexer state (NULL = closed)
stack<V3PreLex*> m_includeStack; // Stack of includers above current m_lexp
enum ProcState { ps_TOP,
ps_DEFNAME, ps_DEFFORM, ps_DEFVALUE, ps_DEFPAREN, ps_DEFARG,
ps_INCNAME, ps_ERRORNAME };
ProcState m_state; // Current state of parser
int m_stateFor; // Token state is parsing for
int m_off; // If non-zero, ifdef level is turned off, don't dump text
string m_lastSym; // Last symbol name found.
string m_formals; ///< Last formals found
// For getRawToken/ `line insertion
string m_lineCmt; // Line comment(s) to be returned
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 defines
stack<V3DefineRef> m_defRefs; // Pending definine substitution
stack<VPreIfEntry> m_ifdefStack; ///< Stack of true/false emitting evaluations
unsigned m_defDepth; // How many `defines deep
// Defines list
DefinesMap m_defines; // Map of defines
// For getline()
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 parseTop();
void parseUndef();
private:
// Internal methods
void eof();
string defineSubst(V3DefineRef* refp);
void addLineComment(int enter_exit_level);
bool defExists(const string& name);
string defValue(const string& name);
string defParams(const string& name);
FileLine* defFileline(const string& name);
bool commentTokenMatch(string& cmdr, const char* strg);
string trimWhitespace(const string& strg);
void parsingOn() {
m_off--;
if (m_off<0) fileline()->v3fatalSrc("Underflow of parsing cmds");
if (!m_off) addLineComment(0);
}
void parsingOff() { m_off++; }
public:
// METHODS, called from upper level shell
virtual void openFile(FileLine* fl, const string& filename);
virtual bool isEof() const { return (m_lexp==NULL); }
virtual string getline();
virtual void insertUnreadback(const string& text) { m_lineCmt += text; }
void insertUnreadbackAtBol(const string& text);
// METHODS, callbacks
virtual void comment(const string& cmt); // Comment detected (if keepComments==2)
virtual void include(const string& filename); // Request a include file be processed
virtual void undef (const string& name);
virtual void define (FileLine* fl, const string& name, const string& value, const string& params);
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;
m_off = 0;
m_lineChars = "";
m_lastSym = "";
m_lineAdd = 0;
m_lineCmtNl = false;
m_rawAtBol = true;
m_defDepth = 0;
}
};
//*************************************************************************
// Creation
V3PreProc* V3PreProc::createPreProc(FileLine* fl) {
return new V3PreProcImp(fl);
}
bool V3PreProc::optPsl() {
return v3Global.opt.psl();
}
//*************************************************************************
// Defines
void V3PreProcImp::undef(const string& name) {
m_defines.erase(name);
}
bool V3PreProcImp::defExists(const string& name) {
DefinesMap::iterator iter = m_defines.find(name);
if (iter == m_defines.end()) return false;
return true;
}
string V3PreProcImp::defValue(const string& name) {
DefinesMap::iterator iter = m_defines.find(name);
if (iter == m_defines.end()) {
fileline()->v3error("Define or directive not defined: `"+name);
return "";
}
return iter->second.value();
}
string V3PreProcImp::defParams(const string& name) {
DefinesMap::iterator iter = m_defines.find(name);
if (iter == m_defines.end()) {
fileline()->v3error("Define or directive not defined: `"+name);
return "";
}
return iter->second.params();
}
FileLine* V3PreProcImp::defFileline(const string& name) {
DefinesMap::iterator iter = m_defines.find(name);
if (iter == m_defines.end()) return false;
return iter->second.fileline();
}
void V3PreProcImp::define(FileLine* fl, const string& name, const string& value, const string& params) {
UINFO(4,"DEFINE '"<<name<<"' as '"<<value<<"' params '"<<params<<"'"<<endl);
if (defExists(name)) {
if (!(defValue(name)==value && defParams(name)==params)) { // Duplicate defs are OK
fl->v3warn(REDEFMACRO,"Redefining existing define: "<<name<<", with different value: "<<value<<" "<<params);
defFileline(name)->v3warn(REDEFMACRO,"Previous definition is here, with value: "<<defValue(name)<<" "<<defParams(name));
}
undef(name);
}
m_defines.insert(make_pair(name, V3Define(fl, value, params)));
}
string V3PreProcImp::removeDefines(const string& sym) {
string val = "0_never_match";
string rtnsym = sym;
for (int loopprevent=0; loopprevent<100; loopprevent++) {
string xsym = rtnsym;
if (xsym.substr(0,1)=="`") xsym.replace(0,1,"");
if (defExists(xsym)) {
val = defValue(xsym);
if (val != rtnsym) rtnsym=val; // Prevent infinite loop if have `define X X
else break;
} else break;
}
return rtnsym; // NA
}
//**********************************************************************
// Comments
void V3PreProcImp::include(const string& filename) {
// Include seen. Ask the preprocessor shell to call back around to us
V3PreShell::preprocInclude (fileline(), filename);
}
bool V3PreProcImp::commentTokenMatch(string& cmdr, const char* strg) {
int len = strlen(strg);
if (0==strncmp(cmdr.c_str(), strg, len)
&& (cmdr[len]=='\0' || isspace(cmdr[len]))) {
if (isspace(cmdr[len])) len++;
cmdr = cmdr.substr(len);
return true;
} else {
return false;
}
}
void V3PreProcImp::comment(const string& text) {
// Comment detected. Only keep relevant data.
// if (text =~ m!^\/[\*\/]\s*[vV]erilator\s*(.*$)!) {
// string cmd = $1;
// cmd =~ s!\s*(\*\/)\s*$!!;
// cmd =~ s!\s+! !g;
// cmd =~ s!\s+$!!g;
const char* cp = text.c_str();
if (cp[0]=='/' && (cp[1]=='/' || cp[1]=='*')) {
cp+=2;
} else return;
while (isspace(*cp)) cp++;
bool synth = false;
if ((cp[0]=='v' || cp[0]=='V')
&& 0==(strncmp(cp+1,"erilator",8))) {
cp+=strlen("verilator");
} else if (0==(strncmp(cp,"synopsys",strlen("synopsys")))) {
cp+=strlen("synopsys");
synth = true;
} else if (0==(strncmp(cp,"cadence",strlen("cadence")))) {
cp+=strlen("cadence");
synth = true;
} else if (0==(strncmp(cp,"ambit synthesis",strlen("ambit synthesis")))) {
cp+=strlen("ambit synthesis");
synth = true;
} else {
return;
}
if (*cp && !isspace(*cp)) return;
while (isspace(*cp)) cp++;
const char* ep = cp+strlen(cp);
if (ep>cp && (ep[-1]=='/' || cp[-2]=='*')) ep-=2;
while (ep>cp && (isspace(ep[-1]))) ep--;
string cmd (cp, ep-cp);
string::size_type pos;
while ((pos=cmd.find("\"")) != string::npos)
cmd.replace(pos, 1, " ");
while ((pos=cmd.find("\t")) != string::npos)
cmd.replace(pos, 1, " ");
while ((pos=cmd.find(" ")) != string::npos)
cmd.replace(pos, 2, " ");
if (synth) {
if (v3Global.opt.assertOn()) {
// one_hot, one_cold, (full_case, parallel_case)
if (commentTokenMatch(cmd/*ref*/, "full_case")) {
insertUnreadback ("/*verilator full_case*/");
}
if (commentTokenMatch(cmd/*ref*/, "parallel_case")) {
insertUnreadback ("/*verilator parallel_case*/");
}
//if (commentTokenMatch(cmd/*ref*/, "one_hot")) {
// insertUnreadback ("/*verilator one_hot*/ "+cmd+";");
//}
//if (commentTokenMatch(cmd/*ref*/, "one_cold")) {
// insertUnreadback ("/*verilator one_cold*/ "+cmd+";");
//}
// else ignore the comment we don't recognize
} // else no assertions
} else {
insertUnreadback ("/*verilator "+cmd+"*/");
}
}
//**********************************************************************
// Parser Utilities
const char* V3PreProcImp::tokenName(int tok) {
switch (tok) {
case VP_EOF : return("EOF");
case VP_INCLUDE : return("INCLUDE");
case VP_IFDEF : return("IFDEF");
case VP_IFNDEF : return("IFNDEF");
case VP_ENDIF : return("ENDIF");
case VP_UNDEF : return("UNDEF");
case VP_DEFINE : return("DEFINE");
case VP_ELSE : return("ELSE");
case VP_ELSIF : return("ELSIF");
case VP_LINE : return("LINE");
case VP_SYMBOL : return("SYMBOL");
case VP_STRING : return("STRING");
case VP_DEFFORM : return("DEFFORM");
case VP_DEFVALUE : return("DEFVALUE");
case VP_COMMENT : return("COMMENT");
case VP_TEXT : return("TEXT");
case VP_WHITE : return("WHITE");
case VP_DEFREF : return("DEFREF");
case VP_DEFARG : return("DEFARG");
case VP_ERROR : return("ERROR");
case VP_PSL : return("PSL");
default: return("?");
}
}
string V3PreProcImp::trimWhitespace(const string& strg) {
string out = strg;
while (out.length()>0 && isspace(out[0])) {
out.erase(0,1);
}
return out;
}
string V3PreProcImp::defineSubst(V3DefineRef* refp) {
// Substitute out defines in a argumented define reference.
// We could push the define text back into the lexer, but that's slow
// and would make recursive definitions and parameter handling nasty.
//
// Note we parse the definition parameters and value here. If a
// parametrized define is used many, many times, we could cache the
// parsed result.
UINFO(4,"defineSubstIn `"<<refp->name()<<" "<<refp->params()<<endl);
for (unsigned i=0; i<refp->args().size(); i++) {
UINFO(4,"defineArg["<<i<<"] = "<<refp->args()[i]<<endl);
}
// Grab value
string value = defValue(refp->name());
UINFO(4,"defineValue '"<<value<<"'"<<endl);
map<string,string> argValueByName;
{ // Parse argument list into map
unsigned numArgs=0;
string argName;
for (const char* cp=refp->params().c_str(); *cp; cp++) {
if (*cp=='(') {
} else if (argName=="" && isspace(*cp)) {
} else if (isspace(*cp) || *cp==')' || *cp==',') {
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.
argValueByName[argName] = trimWhitespace(refp->args()[numArgs]);
}
numArgs++;
//cout << " arg "<<argName<<endl;
}
argName = "";
} else if ( isalpha(*cp) || *cp=='_'
|| (argName!="" && (isdigit(*cp) || *cp=='$'))) {
argName += *cp;
}
}
if (refp->args().size() != numArgs) {
fileline()->v3error("Define passed wrong number of arguments: "+refp->name()+"\n");
return " `"+refp->name()+" ";
}
}
string out = "";
{ // Parse substitution define using arguments
string argName;
string prev;
bool quote = false;
// Note we go through the loop once more at the NULL end-of-string
for (const char* cp=value.c_str(); (*cp) || argName!=""; cp=(*cp?cp+1:cp)) {
//cout << "CH "<<*cp<<" an "<<argName<<"\n";
if (!quote) {
if ( isalpha(*cp) || *cp=='_'
|| (argName!="" && (isdigit(*cp) || *cp=='$'))) {
argName += *cp;
continue;
}
}
if (argName != "") {
// Found a possible variable substitution
map<string,string>::iterator iter = argValueByName.find(argName);
if (iter != argValueByName.end()) {
// Substitute
string subst = iter->second;
out += subst;
} else {
out += argName;
}
argName = "";
}
if (!quote) {
// Check for `` only after we've detected end-of-argname
if (cp[0]=='`' && cp[1]=='`') {
//out += ""; // `` means to suppress the ``
cp++;
continue;
}
else if (cp[0]=='`' && cp[1]=='"') {
out += '"'; // `" means to put out a " without enabling quote mode (sort of)
cp++;
continue;
}
else if (cp[0]=='`' && cp[1]=='\\') {
out += '\\'; // `\ means to put out a backslash
cp++;
continue;
}
}
if (cp[0]=='\\' && cp[1]) {
out += cp[0]; // \{any} Put out literal next character
out += cp[1];
cp++;
continue;
}
if (*cp=='"') quote=!quote;
if (*cp) out += *cp;
}
}
UINFO(4,"defineSubstOut '"<<out<<"'"<<endl);
return out;
}
//**********************************************************************
// Parser routines
void V3PreProcImp::openFile(FileLine* fl, const string& filename) {
// Open a new file, possibly overriding the current one which is active.
if (fl) {
m_fileline = new FileLine(fl);
}
V3File::addSrcDepend(filename);
FILE* fp = fopen (filename.c_str(), "r");
if (!fp) {
fileline()->v3error("File not found: "+filename+"\n");
return;
}
if (m_lexp) {
// 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);
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 (fp);
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
addLineComment(1); // Enter
yy_flex_debug = (debug()>4)?1:0;
yy_switch_to_buffer(m_lexp->m_yyState);
}
void V3PreProcImp::insertUnreadbackAtBol(const string& text) {
// Insert insuring we're at the beginning of line, for `line
// We don't always add a leading newline, as it may result in extra unreadback(newlines).
if (m_lineCmt == "") { m_lineCmtNl = true; }
else if (m_lineCmt[m_lineCmt.length()-1]!='\n') {
insertUnreadback("\n");
}
insertUnreadback(text);
}
void V3PreProcImp::addLineComment(int enter_exit_level) {
if (lineDirectives()) {
char numbuf[20]; sprintf(numbuf, "%d", m_lexp->m_curFilelinep->lineno());
char levelbuf[20]; sprintf(levelbuf, "%d", enter_exit_level);
string cmt = ((string)"`line "+numbuf
+" \""+m_lexp->m_curFilelinep->filename()+"\" "
+levelbuf+"\n");
insertUnreadbackAtBol(cmt);
}
}
void V3PreProcImp::eof() {
// Remove current lexer
UINFO(4,fileline()<<"EOF!\n");
addLineComment(2); // Exit
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);
yy_switch_to_buffer(m_lexp->m_yyState);
}
}
int V3PreProcImp::getRawToken() {
// Get a token from the file, whatever it may be.
while (1) {
next_tok:
if (m_lineAdd) {
m_lineAdd--;
m_rawAtBol = true;
yytext=(char*)"\n"; yyleng=1;
return (VP_TEXT);
}
if (m_lineCmt!="") {
// We have some `line directive or other processed data to return to the user.
static string rtncmt; // Keep the c string till next call
rtncmt = m_lineCmt;
if (m_lineCmtNl) {
if (!m_rawAtBol) rtncmt = "\n"+rtncmt;
m_lineCmtNl = false;
}
yytext=(char*)rtncmt.c_str(); yyleng=rtncmt.length();
m_lineCmt = "";
if (yyleng) m_rawAtBol = (yytext[yyleng-1]=='\n');
if (m_state==ps_DEFVALUE) {
V3PreLex::s_currentLexp->appendDefValue(yytext,yyleng);
goto next_tok;
} else {
return (VP_TEXT);
}
}
if (isEof()) return (VP_EOF);
// Snarf next token from the file
m_fileline = m_lexp->m_curFilelinep; // Remember token start location
V3PreLex::s_currentLexp = m_lexp; // Tell parser where to get/put data
int tok = yylex();
if (debug()>4) {
string buf = string (yytext, yyleng);
string::size_type pos;
while ((pos=buf.find("\n")) != string::npos) { buf.replace(pos, 1, "\\n"); }
while ((pos=buf.find("\r")) != string::npos) { buf.replace(pos, 1, "\\r"); }
fprintf (stderr, "%d: RAW %s s%d dr%d: <%d>%-10s: %s\n",
fileline()->lineno(), m_off?"of":"on", m_state, (int)m_defRefs.size(),
m_lexp->currentStartState(), tokenName(tok), buf.c_str());
}
// On EOF, try to pop to upper level includes, as needed.
if (tok==VP_EOF) {
eof();
goto next_tok; // Parse parent, or find the EOF.
}
if (yyleng) m_rawAtBol = (yytext[yyleng-1]=='\n');
return tok;
}
}
// 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.
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);
if (tok==VP_COMMENT) {
if (!m_off) {
if (m_lexp->m_keepComments == KEEPCMT_SUB) {
string rtn; rtn.assign(yytext,yyleng);
comment(rtn);
} else {
return (tok);
}
}
// 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 (int len=0; len<yyleng; len++) { if (yytext[len]=='\n') m_lineAdd++; }
goto next_tok;
}
if (tok==VP_LINE) {
addLineComment(0);
goto next_tok;
}
// Deal with some special parser states
switch (m_state) {
case ps_TOP: {
break;
}
case ps_DEFNAME: {
if (tok==VP_SYMBOL) {
m_state = ps_TOP;
m_lastSym.assign(yytext,yyleng);
if (m_stateFor==VP_IFDEF
|| m_stateFor==VP_IFNDEF) {
bool enable = defExists(m_lastSym);
UINFO(4,"Ifdef "<<m_lastSym<<(enable?" ON":" OFF")<<endl);
if (m_stateFor==VP_IFNDEF) enable = !enable;
m_ifdefStack.push(VPreIfEntry(enable,false));
if (!enable) parsingOff();
}
else if (m_stateFor==VP_ELSIF) {
if (m_ifdefStack.empty()) {
fileline()->v3error("`elsif with no matching `if\n");
} else {
// Handle `else portion
VPreIfEntry lastIf = m_ifdefStack.top(); m_ifdefStack.pop();
if (!lastIf.on()) parsingOn();
// Handle `if portion
bool enable = !lastIf.everOn() && defExists(m_lastSym);
UINFO(4,"Elsif "<<m_lastSym<<(enable?" ON":" OFF")<<endl);
m_ifdefStack.push(VPreIfEntry(enable, lastIf.everOn()));
if (!enable) parsingOff();
}
}
else if (m_stateFor==VP_UNDEF) {
if (!m_off) {
UINFO(4,"Undef "<<m_lastSym<<endl);
undef(m_lastSym);
}
}
else if (m_stateFor==VP_DEFINE) {
// m_lastSym already set.
m_state = ps_DEFFORM;
m_lexp->pushStateDefForm();
}
else fileline()->v3fatalSrc("Bad case\n");
goto next_tok;
}
else {
fileline()->v3error("Expecting define name. Found: "<<tokenName(tok));
goto next_tok;
}
}
case ps_DEFFORM: {
if (tok==VP_DEFFORM) {
m_formals = m_lexp->m_defValue;
m_state = ps_DEFVALUE;
if (debug()) cout<<"DefFormals='"<<m_formals<<"'\n";
m_lexp->pushStateDefValue();
goto next_tok;
} else if (tok==VP_TEXT) {
// IE, something like comment in formals
if (!m_off) return tok;
else goto next_tok;
} else {
fileline()->v3error("Expecting define formal arguments. Found: "<<tokenName(tok));
goto next_tok;
}
}
case ps_DEFVALUE: {
static string newlines;
newlines = "\n"; // Always start with trailing return
if (tok == VP_DEFVALUE) {
if (debug()) cout<<"DefValue='"<<m_lexp->m_defValue<<"' formals='"<<m_formals<<"'\n";
// Add any formals
string formAndValue = m_formals + m_lexp->m_defValue;
// Remove returns
for (unsigned i=0; i<formAndValue.length(); i++) {
if (formAndValue[i] == '\n') {
formAndValue[i] = ' ';
newlines += "\n";
}
}
if (!m_off) {
string params;
if (formAndValue=="" || isspace(formAndValue[0])) {
// Define without parameters
} else if (formAndValue[0] == '(') {
string::size_type paren = formAndValue.find(")");
if (paren == string::npos) {
fileline()->v3error("Missing ) to end define arguments.");
} else {
params = formAndValue.substr(0, paren+1);
formAndValue.replace(0, paren+1, "");
}
} else {
fileline()->v3error("Missing space or paren to start define value.");
}
// Remove leading whitespace
unsigned leadspace = 0;
while (formAndValue.length() > leadspace
&& isspace(formAndValue[leadspace])) leadspace++;
if (leadspace) formAndValue.erase(0,leadspace);
// Remove trailing whitespace
unsigned trailspace = 0;
while (formAndValue.length() > trailspace
&& isspace(formAndValue[formAndValue.length()-1-trailspace])) trailspace++;
if (trailspace) formAndValue.erase(formAndValue.length()-trailspace,trailspace);
// Define it
UINFO(4,"Define "<<m_lastSym<<" = '"<<formAndValue<<"'"<<endl);
define(fileline(), m_lastSym, formAndValue, params);
}
} else {
fileline()->v3fatalSrc("Bad define text\n");
}
m_state = ps_TOP;
// DEFVALUE is terminated by a return, but lex can't return both tokens.
// Thus, we emit a return here.
yytext=(char*)(newlines.c_str()); yyleng=newlines.length();
return(VP_WHITE);
}
case ps_DEFPAREN: {
if (tok==VP_TEXT && yyleng==1 && yytext[0]=='(') {
m_state = ps_DEFARG;
goto next_tok;
} else {
m_state = ps_TOP;
if (m_defRefs.empty()) v3fatalSrc("Shouldn't be in DEFPAREN w/o active defref");
V3DefineRef* refp = &(m_defRefs.top());
fileline()->v3error("Expecting ( to begin argument list for define reference `"<<refp->name());
goto next_tok;
}
}
case ps_DEFARG: {
if (m_defRefs.empty()) v3fatalSrc("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="";
if (tok==VP_DEFARG && yyleng==1 && yytext[0]==',') {
refp->args().push_back(refp->nextarg());
m_state = ps_DEFARG;
m_lexp->pushStateDefArg(1);
refp->nextarg("");
goto next_tok;
} else if (tok==VP_DEFARG && yyleng==1 && yytext[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();
m_lexp->unputString(out.c_str());
if (m_defRefs.empty()) {
m_state = ps_TOP;
m_lexp->m_parenLevel = 0;
}
else { // Finished a defref inside a upper defref
refp = &(m_defRefs.top()); // We popped, so new top
m_lexp->m_parenLevel = refp->parenLevel();
m_state = ps_DEFARG;
}
goto next_tok;
} else if (tok==VP_DEFREF) {
// Expand it, then state will come back here
// Value of building argument is data before the lower defref
// we'll append it when we push the argument.
break;
} else if (tok==VP_SYMBOL || tok==VP_STRING || VP_TEXT || VP_WHITE || VP_PSL) {
string rtn; rtn.assign(yytext,yyleng);
refp->nextarg(refp->nextarg()+rtn);
goto next_tok;
} else {
fileline()->v3error("Expecting ) or , to end argument list for define reference. Found: "<<tokenName(tok));
m_state = ps_TOP;
goto next_tok;
}
}
case ps_INCNAME: {
if (tok==VP_STRING) {
m_state = ps_TOP;
m_lastSym.assign(yytext,yyleng);
UINFO(4,"Include "<<m_lastSym<<endl);
// Drop leading and trailing quotes.
m_lastSym.erase(0,1);
m_lastSym.erase(m_lastSym.length()-1,1);
include(m_lastSym);
goto next_tok;
}
else if (tok==VP_TEXT && yyleng==1 && yytext[0]=='<') {
// include <filename>
m_state = ps_INCNAME; // Still
m_lexp->pushStateIncFilename();
goto next_tok;
}
else if (tok==VP_DEFREF) {
// Expand it, then state will come back here
break;
}
else {
m_state = ps_TOP;
fileline()->v3error("Expecting include filename. Found: "<<tokenName(tok));
goto next_tok;
}
}
case ps_ERRORNAME: {
if (tok==VP_STRING) {
m_state = ps_TOP;
if (!m_off) {
m_lastSym.assign(yytext,yyleng);
fileline()->v3error(m_lastSym);
}
goto next_tok;
}
else {
m_state = ps_TOP;
fileline()->v3error("Expecting `error string. Found: "<<tokenName(tok));
goto next_tok;
}
}
default: fileline()->v3fatalSrc("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;
}
goto next_tok;
case VP_UNDEF:
case VP_DEFINE:
case VP_IFDEF:
case VP_IFNDEF:
case VP_ELSIF:
m_state = ps_DEFNAME; m_stateFor = tok;
goto next_tok;
case VP_ELSE:
if (m_ifdefStack.empty()) {
fileline()->v3error("`else with no matching `if\n");
} else {
VPreIfEntry lastIf = m_ifdefStack.top(); m_ifdefStack.pop();
bool enable = !lastIf.everOn();
UINFO(4,"Else "<<(enable?" ON":" OFF")<<endl);
m_ifdefStack.push(VPreIfEntry(enable, lastIf.everOn()));
if (!lastIf.on()) parsingOn();
if (!enable) parsingOff();
}
goto next_tok;
case VP_ENDIF:
UINFO(4,"Endif "<<endl);
if (m_ifdefStack.empty()) {
fileline()->v3error("`endif with no matching `if\n");
} else {
VPreIfEntry lastIf = m_ifdefStack.top(); m_ifdefStack.pop();
if (!lastIf.on()) parsingOn();
// parsingOn() really only enables parsing if
// all ifdef's above this want it on
}
goto next_tok;
case VP_DEFREF: {
if (!m_off) {
string name; name.append(yytext+1,yyleng-1);
UINFO(4,"DefRef "<<name<<endl);
if (m_defDepth++ > V3PreProc::DEFINE_RECURSION_LEVEL_MAX) {
fileline()->v3error("Recursive `define substitution: `"+name);
goto next_tok;
}
// Substitute
if (!defExists(name)) { // Not found, return original string as-is
m_defDepth = 0;
UINFO(4,"Defref `"<<name<<" => not_defined"<<endl);
return (VP_TEXT);
}
else {
string params = defParams(name);
if (params=="0" || params=="") { // Found, as simple substitution
string out = defValue(name);
UINFO(4,"Defref `"<<name<<" => '"<<out<<"'"<<endl);
// Similar code in parenthesized define (Search for END_OF_DEFARG)
if (m_defRefs.empty()) {
// Just output the substitution
m_lexp->unputString(out.c_str());
} else {
// 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());
refp->nextarg(refp->nextarg()+m_lexp->m_defValue+out); m_lexp->m_defValue="";
}
goto next_tok;
}
else { // Found, with parameters
UINFO(4,"Defref `"<<name<<" => parameterized"<<endl);
m_defRefs.push(V3DefineRef(name, params, m_lexp->m_parenLevel));
m_state = ps_DEFPAREN; m_stateFor = tok;
m_lexp->pushStateDefArg(0);
goto next_tok;
}
}
fileline()->v3fatalSrc("Bad case\n");
}
else goto next_tok;
}
case VP_ERROR: {
m_state = ps_ERRORNAME; m_stateFor = tok;
goto next_tok;
}
case VP_EOF:
if (!m_ifdefStack.empty()) {
fileline()->v3error("`ifdef not terminated at EOF\n");
}
return tok;
case VP_SYMBOL:
case VP_STRING:
case VP_PSL:
case VP_TEXT: {
m_defDepth = 0;
if (!m_off) return tok;
else goto next_tok;
}
case VP_WHITE: // Handled at top of loop
case VP_COMMENT: // Handled at top of loop
case VP_DEFFORM: // Handled by m_state=ps_DEFFORM;
case VP_DEFVALUE: // Handled by m_state=ps_DEFVALUE;
default:
fileline()->v3fatalSrc("Internal error: Unexpected token.\n");
break;
}
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();
if (debug()>4) {
string buf = string (yytext, yyleng);
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());
}
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");
}
gotEof = true;
}
else if (tok==VP_PSL) {
m_lineChars.append(" psl ");
}
else {
m_lineChars.append(yytext,0,yyleng);
}
}
// Make new string with data up to the newline.
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: "<<theLine.c_str()<<endl);
return theLine;
}