mirror of
https://github.com/verilator/verilator.git
synced 2025-01-08 23:57:35 +00:00
789 lines
22 KiB
C++
789 lines
22 KiB
C++
//*************************************************************************
|
||
// DESCRIPTION: Verilator: File stream wrapper that understands indentation
|
||
//
|
||
// Code available from: http://www.veripool.org/verilator
|
||
//
|
||
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
|
||
//
|
||
//*************************************************************************
|
||
//
|
||
// Copyright 2003-2010 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.
|
||
//
|
||
//*************************************************************************
|
||
|
||
#include "config_build.h"
|
||
#include "verilatedos.h"
|
||
#include <cstdarg>
|
||
#include <sys/stat.h>
|
||
#include <sys/types.h>
|
||
#include <unistd.h>
|
||
#include <cerrno>
|
||
#include <fcntl.h>
|
||
#include <iomanip>
|
||
#include <memory>
|
||
#include <map>
|
||
|
||
#if defined(__unix__)
|
||
# define INFILTER_PIPE // Allow pipe filtering. Needs fork()
|
||
#endif
|
||
|
||
#ifdef INFILTER_PIPE
|
||
# include <sys/wait.h>
|
||
#endif
|
||
|
||
#include "V3Global.h"
|
||
#include "V3File.h"
|
||
#include "V3PreShell.h"
|
||
#include "V3Ast.h"
|
||
|
||
#define INFILTER_IPC_BUFSIZ 64*1024 // For debug, try this as a small number
|
||
#define INFILTER_CACHE_MAX 64*1024 // Maximum bytes to cache if same file read twice
|
||
|
||
//######################################################################
|
||
// V3File Internal state
|
||
|
||
class V3FileDependImp {
|
||
// TYPES
|
||
class DependFile {
|
||
// A single file
|
||
bool m_target; // True if write, else read
|
||
string m_filename; // Filename
|
||
struct stat m_stat; // Stat information
|
||
public:
|
||
DependFile(const string& filename, bool target)
|
||
: m_target(target), m_filename(filename) {
|
||
m_stat.st_mtime = 0;
|
||
}
|
||
~DependFile() {}
|
||
const string& filename() const { return m_filename; }
|
||
bool target() const { return m_target; }
|
||
off_t size() const { return m_stat.st_size; }
|
||
time_t mtime() const { return m_stat.st_mtime; }
|
||
void loadStats() {
|
||
if (!m_stat.st_mtime) {
|
||
int err = stat(filename().c_str(), &m_stat);
|
||
if (err!=0) {
|
||
m_stat.st_mtime = 1;
|
||
// Not a error... This can occur due to `line directives in the .vpp files
|
||
UINFO(1,"-Info: File not statable: "<<filename()<<endl);
|
||
}
|
||
}
|
||
}
|
||
bool operator<(const DependFile& rhs) const { return filename()<rhs.filename(); }
|
||
};
|
||
|
||
// MEMBERS
|
||
set<string> m_filenameSet; // Files generated (elim duplicates)
|
||
set<DependFile> m_filenameList; // Files sourced/generated
|
||
|
||
static string stripQuotes(const string& in) {
|
||
string pretty = in;
|
||
string::size_type pos;
|
||
while ((pos=pretty.find("\"")) != string::npos) pretty.replace(pos, 1, "_");
|
||
while ((pos=pretty.find("\n")) != string::npos) pretty.replace(pos, 1, "_");
|
||
return pretty;
|
||
}
|
||
public:
|
||
// ACCESSOR METHODS
|
||
void addSrcDepend(const string& filename) {
|
||
if (m_filenameSet.find(filename) == m_filenameSet.end()) {
|
||
m_filenameSet.insert(filename);
|
||
DependFile df (filename, false);
|
||
df.loadStats(); // Get size now, in case changes during the run
|
||
m_filenameList.insert(df);
|
||
}
|
||
}
|
||
void addTgtDepend(const string& filename) {
|
||
if (m_filenameSet.find(filename) == m_filenameSet.end()) {
|
||
m_filenameSet.insert(filename);
|
||
m_filenameList.insert(DependFile (filename, true));
|
||
}
|
||
}
|
||
void writeDepend(const string& filename);
|
||
void writeTimes(const string& filename, const string& cmdline);
|
||
bool checkTimes(const string& filename, const string& cmdline);
|
||
};
|
||
|
||
V3FileDependImp dependImp; // Depend implementation class
|
||
|
||
//######################################################################
|
||
// V3FileDependImp
|
||
|
||
inline void V3FileDependImp::writeDepend(const string& filename) {
|
||
const auto_ptr<ofstream> ofp (V3File::new_ofstream(filename));
|
||
if (ofp->fail()) v3fatalSrc("Can't write "<<filename);
|
||
|
||
for (set<DependFile>::iterator iter=m_filenameList.begin();
|
||
iter!=m_filenameList.end(); ++iter) {
|
||
if (iter->target()) {
|
||
*ofp<<iter->filename()<<" ";
|
||
}
|
||
}
|
||
*ofp<<" : ";
|
||
*ofp<<v3Global.opt.bin();
|
||
*ofp<<" ";
|
||
*ofp<<V3PreShell::dependFiles();
|
||
*ofp<<" ";
|
||
|
||
for (set<DependFile>::iterator iter=m_filenameList.begin();
|
||
iter!=m_filenameList.end(); ++iter) {
|
||
if (!iter->target()) {
|
||
*ofp<<iter->filename()<<" ";
|
||
}
|
||
}
|
||
|
||
*ofp<<endl;
|
||
|
||
if (v3Global.opt.makePhony()) {
|
||
*ofp<<endl;
|
||
for (set<DependFile>::iterator iter=m_filenameList.begin();
|
||
iter!=m_filenameList.end(); ++iter) {
|
||
if (!iter->target()) {
|
||
*ofp<<iter->filename()<<":"<<endl;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
inline void V3FileDependImp::writeTimes(const string& filename, const string& cmdlineIn) {
|
||
const auto_ptr<ofstream> ofp (V3File::new_ofstream(filename));
|
||
if (ofp->fail()) v3fatalSrc("Can't write "<<filename);
|
||
|
||
string cmdline = stripQuotes(cmdlineIn);
|
||
*ofp<<"# DESCR"<<"IPTION: Verilator output: Timestamp data for --skip-identical. Delete at will."<<endl;
|
||
*ofp<<"C \""<<cmdline<<"\""<<endl;
|
||
|
||
#ifndef _WIN32
|
||
sync(); // Push files so sizes look correct
|
||
#endif
|
||
for (set<DependFile>::iterator iter=m_filenameList.begin();
|
||
iter!=m_filenameList.end(); ++iter) {
|
||
// Read stats of files we create after we're done making them (execpt for this file, of course)
|
||
DependFile* dfp = (DependFile*)&(*iter);
|
||
dfp->loadStats();
|
||
off_t showSize = iter->size();
|
||
if (dfp->filename() == filename) showSize=0; // We're writing it, so need to ignore it
|
||
|
||
*ofp<<(iter->target()?"T":"S")<<" ";
|
||
*ofp<<" "<<setw(8)<<showSize;
|
||
*ofp<<" "<<setw(11)<<iter->mtime();
|
||
*ofp<<" \""<<iter->filename()<<"\"";
|
||
*ofp<<endl;
|
||
}
|
||
}
|
||
|
||
inline bool V3FileDependImp::checkTimes(const string& filename, const string& cmdlineIn) {
|
||
const auto_ptr<ifstream> ifp (V3File::new_ifstream_nodepend(filename));
|
||
if (ifp->fail()) {
|
||
UINFO(2," --check-times failed: no input "<<filename<<endl);
|
||
return false;
|
||
}
|
||
{
|
||
string ignore; getline(*ifp, ignore);
|
||
}
|
||
{
|
||
char chkDir; *ifp>>chkDir;
|
||
char quote; *ifp>>quote;
|
||
string chkCmdline; getline(*ifp, chkCmdline, '"');
|
||
string cmdline = stripQuotes(cmdlineIn);
|
||
if (cmdline != chkCmdline) {
|
||
UINFO(2," --check-times failed: different command line\n");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
while (!ifp->eof()) {
|
||
char chkDir; *ifp>>chkDir;
|
||
off_t chkSize; *ifp>>chkSize;
|
||
if (ifp->eof()) break; // Needed to read final whitespace before found eof
|
||
time_t chkMtime; *ifp>>chkMtime;
|
||
char quote; *ifp>>quote;
|
||
string chkFilename; getline(*ifp, chkFilename, '"');
|
||
//UINFO(9," got d="<<chkDir<<" s="<<chkSize<<" mt="<<chkMtime<<" fn = "<<chkFilename<<endl);
|
||
|
||
struct stat chkStat;
|
||
int err = stat(chkFilename.c_str(), &chkStat);
|
||
if (err!=0) {
|
||
UINFO(2," --check-times failed: missing "<<chkFilename<<endl);
|
||
return false;
|
||
}
|
||
if (filename != chkFilename) { // See above; we were writing it at the time...
|
||
// We'd like this rule:
|
||
//if (!(chkStat.st_size == chkSize
|
||
// && chkStat.st_mtime == chkMtime) {
|
||
// However NFS messes us up, as there might be some data outstanding when
|
||
// we determined the original size. For safety, we know the creation time
|
||
// must be within a few second window... call it 20 sec.
|
||
if (!(chkStat.st_size >= chkSize
|
||
&& chkStat.st_mtime >= chkMtime
|
||
&& chkStat.st_mtime <= (chkMtime + 20))) {
|
||
UINFO(2," --check-times failed: out-of-date "<<chkFilename
|
||
<<"; "<<chkStat.st_size<<"=?"<<chkSize
|
||
<<" "<<chkStat.st_mtime<<"=?"<<chkMtime<<endl);
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
//######################################################################
|
||
// V3File
|
||
|
||
void V3File::addSrcDepend(const string& filename) {
|
||
dependImp.addSrcDepend(filename);
|
||
}
|
||
void V3File::addTgtDepend(const string& filename) {
|
||
dependImp.addTgtDepend(filename);
|
||
}
|
||
void V3File::writeDepend(const string& filename) {
|
||
dependImp.writeDepend(filename);
|
||
}
|
||
void V3File::writeTimes(const string& filename, const string& cmdline) {
|
||
dependImp.writeTimes(filename, cmdline);
|
||
}
|
||
bool V3File::checkTimes(const string& filename, const string& cmdline) {
|
||
return dependImp.checkTimes(filename, cmdline);
|
||
}
|
||
|
||
void V3File::createMakeDir() {
|
||
static bool created = false;
|
||
if (!created) {
|
||
created = true;
|
||
#ifndef _WIN32
|
||
mkdir(v3Global.opt.makeDir().c_str(), 0777);
|
||
#else
|
||
mkdir(v3Global.opt.makeDir().c_str());
|
||
#endif
|
||
}
|
||
}
|
||
|
||
//######################################################################
|
||
// V3InFilterImp
|
||
|
||
class V3InFilterImp {
|
||
typedef map<string,string> FileContentsMap;
|
||
|
||
FileContentsMap m_contentsMap; // Cache of file contents
|
||
bool m_readEof; // Received EOF on read
|
||
#ifdef INFILTER_PIPE
|
||
pid_t m_pid; // fork() process id
|
||
#else
|
||
int m_pid; // fork() process id - always zero as disabled
|
||
#endif
|
||
bool m_pidExited;
|
||
int m_pidStatus;
|
||
int m_writeFd; // File descriptor TO filter
|
||
int m_readFd; // File descriptor FROM filter
|
||
|
||
private:
|
||
// METHODS
|
||
static int debug() {
|
||
static int level = -1;
|
||
if (VL_UNLIKELY(level < 0)) level = v3Global.opt.debugSrcLevel(__FILE__);
|
||
return level;
|
||
}
|
||
|
||
bool readContents(const string& filename, string& out) {
|
||
if (m_pid) return readContentsFilter(filename,out);
|
||
else return readContentsFile(filename,out);
|
||
}
|
||
bool readContentsFile(const string& filename, string& out) {
|
||
int fd = open (filename.c_str(), O_RDONLY);
|
||
if (!fd) return false;
|
||
m_readEof = false;
|
||
out = readBlocks(fd, -1);
|
||
close(fd);
|
||
return true;
|
||
}
|
||
bool readContentsFilter(const string& filename, string& out) {
|
||
if (filename!="" || out!="") {} // Prevent unused
|
||
#ifdef INFILTER_PIPE
|
||
writeFilter("read \""+filename+"\"\n");
|
||
string line = readFilterLine();
|
||
if (line.find("Content-Length") != string::npos) {
|
||
int len = 0;
|
||
sscanf(line.c_str(), "Content-Length: %d\n", &len);
|
||
out = readBlocks(m_readFd, len);
|
||
return true;
|
||
} else {
|
||
if (line!="") v3error("--pipe-filter protocol error, unexpected: "<<line);
|
||
return false;
|
||
}
|
||
#else
|
||
v3fatalSrc("--pipe-filter not implemented on this platform");
|
||
return false;
|
||
#endif
|
||
}
|
||
|
||
void checkFilter(bool hang) {
|
||
#ifdef INFILTER_PIPE
|
||
if (!m_pidExited && waitpid(m_pid, &m_pidStatus, hang?0:WNOHANG)) {
|
||
UINFO(1,"--pipe-filter: Exited, status "<<m_pidStatus<<" exit="<<WEXITSTATUS(m_pidStatus)<<" err"<<strerror(errno)<<endl);
|
||
m_readEof = true;
|
||
m_pidExited = true;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
string readBlocks(int fd, int size=-1) {
|
||
string out;
|
||
char buf[INFILTER_IPC_BUFSIZ];
|
||
while (!m_readEof && (size<0 || size>(int)out.length())) {
|
||
int todo = INFILTER_IPC_BUFSIZ;
|
||
if (size>0 && size<INFILTER_IPC_BUFSIZ) todo = size;
|
||
int got = read (fd, buf, todo);
|
||
//UINFO(9,"RD GOT g "<< got<<" e "<<errno<<" "<<strerror(errno)<<endl); usleep(50*1000);
|
||
if (got>0) out.append(buf, got);
|
||
else if (errno == EINTR || errno == EAGAIN
|
||
#ifdef EWOULDBLOCK
|
||
|| errno == EWOULDBLOCK
|
||
#endif
|
||
) {
|
||
checkFilter(false); usleep(1000); continue;
|
||
} else { m_readEof = true; break; }
|
||
}
|
||
return out;
|
||
}
|
||
string readFilterLine() {
|
||
// Slow, but we don't need it much
|
||
UINFO(9,"readFilterLine\n");
|
||
string line;
|
||
while (!m_readEof) {
|
||
string c = readBlocks(m_readFd, 1);
|
||
line += c;
|
||
if (c == "\n") {
|
||
if (line == "\n") { line=""; continue; }
|
||
else break;
|
||
}
|
||
}
|
||
UINFO(6,"filter-line-in: "<<line);
|
||
return line;
|
||
}
|
||
void writeFilter(const string& out) {
|
||
if (debug()>=6) { UINFO(6,"filter-out: "<<out); if (out[out.length()-1]!='\n') cout<<endl; }
|
||
if (!m_pid) { v3error("--pipe-filter: write to closed file\n"); m_readEof = true; stop(); }
|
||
unsigned offset = 0;
|
||
while (!m_readEof && out.length()>offset) {
|
||
int got = write (m_writeFd, (out.c_str())+offset, out.length()-offset);
|
||
//UINFO(9,"WR GOT g "<< got<<" e "<<errno<<" "<<strerror(errno)<<endl); usleep(50*1000);
|
||
if (got>0) offset += got;
|
||
else if (errno == EINTR || errno == EAGAIN
|
||
#ifdef EWOULDBLOCK
|
||
|| errno == EWOULDBLOCK
|
||
#endif
|
||
) {
|
||
checkFilter(false); usleep(1000); continue;
|
||
}
|
||
else break;
|
||
}
|
||
}
|
||
|
||
// Start the filter
|
||
void start(const string& command) {
|
||
if (command=="") {
|
||
m_pid = 0; // Disabled
|
||
} else {
|
||
startFilter(command);
|
||
}
|
||
}
|
||
void startFilter(const string& command) {
|
||
if (command=="") {} // Prevent Unused
|
||
#ifdef INFILTER_PIPE
|
||
int fd_stdin[2], fd_stdout[2];
|
||
static const int P_RD = 0;
|
||
static const int P_WR = 1;
|
||
|
||
if (pipe(fd_stdin) != 0 || pipe(fd_stdout) != 0) {
|
||
v3fatal("--pipe-filter: Can't pipe: "<<strerror(errno));
|
||
}
|
||
if (fd_stdin[P_RD]<=2 || fd_stdin[P_WR]<=2
|
||
|| fd_stdout[P_RD]<=2 || fd_stdout[P_WR]<=2) {
|
||
// We'd have to rearrange all of the FD usages in this case.
|
||
// Too unlikely; verilator isn't a daemon.
|
||
v3fatal("--pipe-filter: stdin/stdout closed before pipe opened\n");
|
||
}
|
||
|
||
UINFO(1,"--pipe-filter: /bin/sh -c "<<command<<endl);
|
||
|
||
pid_t pid = fork();
|
||
if (pid < 0) v3fatal("--pipe-filter: fork failed: "<<strerror(errno));
|
||
if (pid == 0) { // Child
|
||
UINFO(6,"In child\n");
|
||
close(fd_stdin[P_WR]);
|
||
dup2(fd_stdin[P_RD], 0);
|
||
close(fd_stdout[P_RD]);
|
||
dup2(fd_stdout[P_WR], 1);
|
||
// And stderr comes from parent
|
||
|
||
execl("/bin/sh", "sh", "-c", command.c_str(), NULL);
|
||
// Don't use v3fatal, we don't share the common structures any more
|
||
fprintf(stderr,"--pipe-filter: exec failed: %s\n",strerror(errno));
|
||
_exit(10);
|
||
}
|
||
else { // Parent
|
||
UINFO(6,"In parent, child pid "<<pid
|
||
<<" stdin "<<fd_stdin[P_WR]<<"->"<<fd_stdin[P_RD]
|
||
<<" stdout "<<fd_stdout[P_WR]<<"->"<<fd_stdout[P_RD]<<endl);
|
||
m_pid = pid;
|
||
m_pidExited = false;
|
||
m_pidStatus = 0;
|
||
m_writeFd = fd_stdin[P_WR];
|
||
m_readFd = fd_stdout[P_RD];
|
||
m_readEof = false;
|
||
|
||
close(fd_stdin[P_RD]);
|
||
close(fd_stdout[P_WR]);
|
||
|
||
int flags = fcntl(m_readFd,F_GETFL,0);
|
||
fcntl(m_readFd, F_SETFL, flags | O_NONBLOCK);
|
||
|
||
flags = fcntl(m_writeFd,F_GETFL,0);
|
||
fcntl(m_writeFd, F_SETFL, flags | O_NONBLOCK);
|
||
}
|
||
UINFO(6,"startFilter complete\n");
|
||
#else
|
||
v3fatalSrc("--pipe-filter not implemented on this platform");
|
||
#endif
|
||
}
|
||
|
||
void stop() {
|
||
if (m_pid) stopFilter();
|
||
}
|
||
void stopFilter() {
|
||
UINFO(6,"Stopping filter process\n");
|
||
#ifdef INFILTER_PIPE
|
||
close(m_writeFd);
|
||
checkFilter(true);
|
||
if (!WIFEXITED(m_pidStatus) || WEXITSTATUS(m_pidStatus)!=0) {
|
||
v3fatal("--pipe-filter returned bad status");
|
||
}
|
||
m_pid = 0;
|
||
close(m_readFd);
|
||
UINFO(6,"Closed\n");
|
||
#else
|
||
v3fatalSrc("--pipe-filter not implemented on this platform");
|
||
#endif
|
||
}
|
||
|
||
protected:
|
||
friend class V3InFilter;
|
||
// Read file contents and return it
|
||
bool readWholefile(const string& filename, string& out) {
|
||
FileContentsMap::iterator it = m_contentsMap.find(filename);
|
||
if (it != m_contentsMap.end()) {
|
||
out = it->second;
|
||
return true;
|
||
}
|
||
if (!readContents(filename, out)) return false;
|
||
if (out.length() < INFILTER_CACHE_MAX) {
|
||
// Cache small files (only to save space)
|
||
// It's quite common to `include "timescale" thousands of times
|
||
// This isn't so important if it's just a open(), but filtering can be slow
|
||
m_contentsMap.insert(make_pair(filename,out));
|
||
}
|
||
return true;
|
||
}
|
||
// CONSTRUCTORS
|
||
V3InFilterImp(const string& command) {
|
||
m_readEof = false;
|
||
m_pid = 0;
|
||
m_pidExited = false;
|
||
m_pidStatus = 0;
|
||
m_writeFd = 0;
|
||
m_readFd = 0;
|
||
start(command);
|
||
}
|
||
~V3InFilterImp() { stop(); }
|
||
};
|
||
|
||
//######################################################################
|
||
// V3InFilter
|
||
// Just dispatch to the implementation
|
||
|
||
V3InFilter::V3InFilter(const string& command) { m_impp = new V3InFilterImp(command); }
|
||
V3InFilter::~V3InFilter() { if (m_impp) delete m_impp; m_impp=NULL; }
|
||
|
||
bool V3InFilter::readWholefile(const string& filename, string& out) {
|
||
if (!m_impp) v3fatalSrc("readWholefile on invalid filter");
|
||
return m_impp->readWholefile(filename, out);
|
||
}
|
||
|
||
//######################################################################
|
||
// V3OutFormatter: A class for printing to a file, with automatic indentation of C++ code.
|
||
|
||
V3OutFormatter::V3OutFormatter(const string& filename, bool verilog)
|
||
: m_filename(filename), m_verilog(verilog)
|
||
, m_lineno(1), m_column(0)
|
||
, m_nobreak(false), m_prependIndent(true), m_indentLevel(0)
|
||
, m_declSAlign(0), m_declNSAlign(0), m_declPadNum(0) {
|
||
}
|
||
|
||
//----------------------------------------------------------------------
|
||
|
||
const char* V3OutFormatter::indentStr(int num) {
|
||
// Indent the specified number of spaces. Use tabs as possible.
|
||
static char str[MAXSPACE+20];
|
||
char* cp = str;
|
||
if (num>MAXSPACE) num=MAXSPACE;
|
||
if (!m_verilog) { // verilogPrefixedTree doesn't want tabs
|
||
while (num>=8) {
|
||
*cp++ = '\t';
|
||
num -= 8;
|
||
}
|
||
}
|
||
while (num>0) {
|
||
*cp++ = ' ';
|
||
num --;
|
||
}
|
||
*cp++ = '\0';
|
||
return (str);
|
||
}
|
||
|
||
const string V3OutFormatter::indentSpaces(int num) {
|
||
// Indent the specified number of spaces. Use spaces.
|
||
static char str[MAXSPACE+20];
|
||
char* cp = str;
|
||
if (num>MAXSPACE) num=MAXSPACE;
|
||
while (num>0) {
|
||
*cp++ = ' ';
|
||
num --;
|
||
}
|
||
*cp++ = '\0';
|
||
string st (str);
|
||
return (st);
|
||
}
|
||
|
||
bool V3OutFormatter::tokenStart(const char* cp, const char* cmp) {
|
||
while (*cmp == *cp) { cp++; cmp++; }
|
||
if (*cmp) return false;
|
||
if (*cp && !isspace(*cp)) return false;
|
||
return true;
|
||
}
|
||
|
||
bool V3OutFormatter::tokenEnd(const char* cp) {
|
||
return (tokenStart(cp,"end")
|
||
|| tokenStart(cp,"endcase")
|
||
|| tokenStart(cp,"endmodule"));
|
||
}
|
||
|
||
int V3OutFormatter::endLevels (const char *strg) {
|
||
int levels=m_indentLevel;
|
||
const char* cp=strg;
|
||
while (isspace(*cp)) cp++;
|
||
switch (*cp) {
|
||
case '\n': // Newlines.. No need for whitespace before it
|
||
return (0);
|
||
case '#': // Preproc directive
|
||
return (0);
|
||
}
|
||
{
|
||
// label/public/private: Deindent by 2 spaces
|
||
const char* mp=cp;
|
||
for (; isalnum(*mp); mp++) ;
|
||
if (mp[0]==':' && mp[1]!=':') return (levels-INDBLK/2);
|
||
}
|
||
|
||
// We want "} else {" to be one level to the left of normal
|
||
for (const char* cp=strg; *cp; cp++) {
|
||
switch (*cp) {
|
||
case '}':
|
||
case ')':
|
||
levels-=INDBLK;
|
||
break;
|
||
case 'e':
|
||
if (m_verilog && tokenEnd(cp)) {
|
||
levels-=INDBLK;
|
||
}
|
||
break;
|
||
case '\t':
|
||
case ' ':
|
||
break; // Continue
|
||
default:
|
||
return (levels); // Letter
|
||
}
|
||
}
|
||
return (levels);
|
||
}
|
||
|
||
void V3OutFormatter::puts (const char *strg) {
|
||
if (m_prependIndent) {
|
||
putsNoTracking(indentStr(endLevels(strg)));
|
||
m_prependIndent = false;
|
||
}
|
||
bool wordstart = true;
|
||
for (const char* cp=strg; *cp; cp++) {
|
||
putcNoTracking (*cp);
|
||
switch (*cp) {
|
||
case '\n':
|
||
m_lineno++;
|
||
wordstart = true;
|
||
if (cp[1]=='\0') {
|
||
m_prependIndent = true; // Add the indent later, may be a indentInc/indentDec called between now and then
|
||
} else {
|
||
m_prependIndent = false;
|
||
putsNoTracking(indentStr(endLevels(cp+1)));
|
||
}
|
||
break;
|
||
case ' ':
|
||
wordstart = true;
|
||
break;
|
||
case '\t':
|
||
wordstart = true;
|
||
break;
|
||
case '{':
|
||
indentInc();
|
||
break;
|
||
case '(':
|
||
indentInc();
|
||
m_parenVec.push(m_column);
|
||
break;
|
||
case '}':
|
||
indentDec();
|
||
break;
|
||
case ')':
|
||
if (!m_parenVec.empty()) m_parenVec.pop();
|
||
indentDec();
|
||
break;
|
||
case 'b':
|
||
if (wordstart && m_verilog && tokenStart(cp,"begin")) {
|
||
indentInc();
|
||
}
|
||
wordstart = false;
|
||
break;
|
||
case 'c':
|
||
if (wordstart && m_verilog && (tokenStart(cp,"case")
|
||
|| tokenStart(cp,"casex")
|
||
|| tokenStart(cp,"casez"))) {
|
||
indentInc();
|
||
}
|
||
wordstart = false;
|
||
break;
|
||
case 'e':
|
||
if (wordstart && m_verilog && tokenEnd(cp)) {
|
||
indentDec();
|
||
}
|
||
wordstart = false;
|
||
break;
|
||
case 'm':
|
||
if (wordstart && m_verilog && tokenStart(cp,"module")) {
|
||
indentInc();
|
||
}
|
||
wordstart = false;
|
||
break;
|
||
default:
|
||
wordstart = false;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
void V3OutFormatter::putBreakExpr () {
|
||
if (!m_parenVec.empty()) putBreak();
|
||
}
|
||
|
||
// Add a line break if too wide
|
||
void V3OutFormatter::putBreak () {
|
||
if (!m_nobreak) {
|
||
//char s[1000]; sprintf(s,"{%d,%d}",m_column,m_parenVec.top()); putsNoTracking(s);
|
||
if (exceededWidth()) {
|
||
putcNoTracking('\n');
|
||
if (!m_parenVec.empty()) putsNoTracking(indentStr(m_parenVec.top()));
|
||
}
|
||
}
|
||
}
|
||
|
||
void V3OutFormatter::putsQuoted(const char* strg) {
|
||
// Quote \ and " for use inside C programs
|
||
// Don't use to quote a filename for #include - #include doesn't \ escape.
|
||
putcNoTracking('"');
|
||
string quoted = AstNode::quoteName(strg);
|
||
for (const char* cp=quoted.c_str(); *cp; cp++) {
|
||
putcNoTracking (*cp);
|
||
}
|
||
putcNoTracking('"');
|
||
}
|
||
void V3OutFormatter::putsNoTracking (const char *strg) {
|
||
// Don't track {}'s, probably because it's a $display format string
|
||
for (const char* cp=strg; *cp; cp++) {
|
||
putcNoTracking (*cp);
|
||
}
|
||
}
|
||
|
||
void V3OutFormatter::putcNoTracking (char chr) {
|
||
switch (chr) {
|
||
case '\n':
|
||
m_lineno++;
|
||
m_column=0;
|
||
m_nobreak=true;
|
||
break;
|
||
case '\t':
|
||
m_column = ((m_column + 9)/8)*8;
|
||
break;
|
||
case ' ':
|
||
case '(':
|
||
case '|':
|
||
case '&':
|
||
m_column++;
|
||
break;
|
||
default:
|
||
m_column++;
|
||
m_nobreak=false;
|
||
break;
|
||
}
|
||
putcOutput (chr);
|
||
}
|
||
|
||
void V3OutFormatter::putAlign (bool/*AlignClass*/ isStatic, int align, int size, const char* prefix) {
|
||
if (size==0) size=align;
|
||
int alignSize = size; if (alignSize>8) alignSize=8;
|
||
int& alignr = isStatic ? m_declSAlign : m_declNSAlign;
|
||
int padsize = alignSize - (alignr % alignSize);
|
||
if (padsize && padsize!=alignSize) {
|
||
// Modern versions of GCC no longer need this, they'll pad for us, so
|
||
// we'll save the work and danger of getting it wrong.
|
||
puts("//char\t");
|
||
puts(prefix);
|
||
puts("__VpadToAlign"+cvtToStr(alignr)
|
||
+"["+cvtToStr(padsize)+"];\n");
|
||
alignr += padsize;
|
||
m_declPadNum++;
|
||
}
|
||
alignr += size;
|
||
}
|
||
|
||
//----------------------------------------------------------------------
|
||
// Simple wrappers
|
||
|
||
void V3OutFormatter::printf (const char *fmt...) {
|
||
char sbuff[5000];
|
||
va_list ap;
|
||
va_start(ap,fmt);
|
||
vsprintf(sbuff,fmt,ap);
|
||
va_end(ap);
|
||
this->puts(sbuff);
|
||
}
|
||
|
||
//######################################################################
|
||
// V3OutFormatter: A class for printing to a file, with automatic indentation of C++ code.
|
||
|
||
V3OutFile::V3OutFile(const string& filename)
|
||
: V3OutFormatter(filename, false) {
|
||
if ((m_fp = V3File::new_fopen_w(filename.c_str())) == NULL) {
|
||
v3fatal("Cannot write "<<filename);
|
||
}
|
||
}
|
||
|
||
V3OutFile::~V3OutFile() {
|
||
if (m_fp) fclose(m_fp);
|
||
m_fp = NULL;
|
||
}
|