Commit 4da344c0 authored by Nikolai Kosjar's avatar Nikolai Kosjar

C++: ast2png: Try parsing harder

Try to parse a declarator, if that fails an expression, if that fails,
...at last a TranslationUnit is tried. It is also possible to specify
which AST should be parsed.

This simplifies the code snippets we can pass to this tool.

Change-Id: Idbc1a8a6f1c5cf7e20d899f7a2e4263c7f9d33a6
Reviewed-by: default avatarErik Verbruggen <erik.verbruggen@digia.com>
parent 1a0bed27
......@@ -363,8 +363,7 @@ private:
Overview o;
};
void createImageFromDot(const QString &inputFile, const QString &outputFile, bool verbose)
static void createImageFromDot(const QString &inputFile, const QString &outputFile, bool verbose)
{
const QString command = CplusplusToolsUtils::portableExecutableName(QLatin1String("dot"));
const QStringList arguments = QStringList()
......@@ -372,9 +371,9 @@ void createImageFromDot(const QString &inputFile, const QString &outputFile, boo
CplusplusToolsUtils::executeCommand(command, arguments, QString(), verbose);
}
const char PATH_STDIN_FILE[] = "_stdincontents.cpp";
static const char PATH_STDIN_FILE[] = "_stdincontents.cpp";
QString example()
static QString example()
{
return
#if defined(Q_OS_WIN)
......@@ -387,16 +386,120 @@ QString example()
.arg(QFileInfo(qApp->arguments().at(0)).fileName(), QLatin1String(PATH_STDIN_FILE));
}
void printUsage()
static QString parseModeToString(Document::ParseMode parseMode)
{
switch (parseMode) {
case Document::ParseTranlationUnit:
return QLatin1String("TranlationUnit");
case Document::ParseDeclaration:
return QLatin1String("Declaration");
case Document::ParseExpression:
return QLatin1String("Expression");
case Document::ParseDeclarator:
return QLatin1String("Declarator");
case Document::ParseStatement:
return QLatin1String("Statement");
default:
return QLatin1String("UnknownParseMode");
}
}
/// Counts errors and appends error messages containing the parse mode to an error string
class ErrorHandler: public DiagnosticClient {
public:
int m_errorCount;
QByteArray *m_errorString;
Document::ParseMode m_parseMode;
ErrorHandler(Document::ParseMode parseMode, QByteArray *errorStringOutput)
: m_errorCount(0)
, m_errorString(errorStringOutput)
, m_parseMode(parseMode) {}
void report(int level,
const StringLiteral *fileName,
unsigned line, unsigned column,
const char *format, va_list ap)
{
++m_errorCount;
if (! m_errorString)
return;
static const char *const pretty[] = { "warning", "error", "fatal" };
QString str;
str.sprintf("%s:%d:%d: When parsing as %s: %s: ", fileName->chars(), line, column,
parseModeToString(m_parseMode).toUtf8().constData(), pretty[level]);
m_errorString->append(str.toUtf8());
str.vsprintf(format, ap);
m_errorString->append(str.toUtf8());
m_errorString->append('\n');
}
};
/// Try to parse with given parseModes. Returns a document pointer if it was possible to
/// successfully parse with one of the given parseModes (one parse mode after the other
/// is tried), otherwise a null pointer.
static Document::Ptr parse(const QString &fileName, const QByteArray &source,
QList<Document::ParseMode> parseModes, QByteArray *errors,
bool verbose = false)
{
foreach (const Document::ParseMode parseMode, parseModes) {
ErrorHandler *errorHandler = new ErrorHandler(parseMode, errors); // Deleted by ~Document.
if (verbose)
std::cout << "Parsing as " << qPrintable(parseModeToString(parseMode)) << "...";
Document::Ptr doc = Document::create(fileName);
doc->control()->setDiagnosticClient(errorHandler);
doc->setUtf8Source(source);
const bool parsed = doc->parse(parseMode);
if (parsed && errorHandler->m_errorCount == 0) {
if (verbose)
std::cout << "succeeded." << std::endl;
return doc;
}
if (verbose)
std::cout << "failed." << std::endl;
}
return Document::Ptr();
}
/// Convenience function
static Document::Ptr parse(const QString &fileName, const QByteArray &source,
Document::ParseMode parseMode, QByteArray *errors,
bool verbose = false)
{
QList<Document::ParseMode> parseModes = QList<Document::ParseMode>() << parseMode;
return parse(fileName, source, parseModes, errors, verbose);
}
static void printUsage()
{
std::cout << "Usage: " << qPrintable(QFileInfo(qApp->arguments().at(0)).fileName())
<< " [-v] <file1> <file2> ...\n\n";
<< " [-v] [-p ast] <file1> <file2> ...\n\n";
std::cout
<< "Visualize AST and symbol hierarchy of given C++ files by generating png image files\n"
<< "in the same directory as the input files. Print paths to generated image files.\n"
<< "\n"
<< "Options:\n"
<< " -v Run with increased verbosity.\n"
<< " -p <ast> Parse each file as <ast>. <ast> is one of:\n"
<< " - 'declarator' or 'dr'\n"
<< " - 'expression' or 'ex'\n"
<< " - 'declaration' or 'dn'\n"
<< " - 'statement' or 'st'\n"
<< " - 'translationunit' or 'tr'\n"
<< " If this option is not provided, each file is tried to be parsed as\n"
<< " declarator, expression, etc. using the stated order.\n"
<< "\n";
std::cout << QString::fromLatin1(
"Visualize AST and symbol hierarchy of given C++ files by generating png image files\n"
"in the same directory as the input files. Print paths to generated image files.\n"
"\n"
"Standard input is also read. The resulting files starts with \"%1\"\n"
"Standard input is also read. The resulting files start with \"%1\"\n"
"and are created in the current working directory. To show the AST for simple snippets\n"
"you might want to execute:\n"
"\n"
......@@ -415,9 +518,12 @@ int main(int argc, char *argv[])
args.removeFirst();
bool optionVerbose = false;
int optionParseMode = -1;
// Data from stdin?
if (!tty_for_stdin()) {
// Test only for stdin if not input files are specified.
const bool doTestForStdIn = args.isEmpty()
|| (args.count() == 1 && args.contains(QLatin1String("-v")));
if (doTestForStdIn && !tty_for_stdin()) {
QFile file((QLatin1String(PATH_STDIN_FILE)));
if (! file.open(QFile::WriteOnly)) {
std::cerr << "Error: Cannot open file for writing\"" << qPrintable(file.fileName())
......@@ -430,15 +536,46 @@ int main(int argc, char *argv[])
}
// Process options & arguments
const bool helpRequested = args.contains(QLatin1String("-h"))
|| args.contains(QLatin1String("-help"));
if (helpRequested) {
printUsage();
return helpRequested ? EXIT_SUCCESS : EXIT_FAILURE;
}
if (args.contains(QLatin1String("-v"))) {
optionVerbose = true;
args.removeOne(QLatin1String("-v"));
}
const bool helpRequested = args.contains(QLatin1String("-h"))
|| args.contains(QLatin1String("-help"));
if (args.isEmpty() || helpRequested) {
if (args.contains(QLatin1String("-p"))) {
args.removeOne(QLatin1String("-p"));
if (args.isEmpty()) {
std::cerr << "Error: Expected ast after option \"-p\"." << std::endl;
printUsage();
exit(EXIT_FAILURE);
}
const QString parseAs = args.first();
if (parseAs == QLatin1String("declarator") || parseAs == QLatin1String("dr"))
optionParseMode = Document::ParseDeclarator;
else if (parseAs == QLatin1String("expression") || parseAs == QLatin1String("ex"))
optionParseMode = Document::ParseExpression;
else if (parseAs == QLatin1String("declaration") || parseAs == QLatin1String("dn"))
optionParseMode = Document::ParseDeclaration;
else if (parseAs == QLatin1String("statement") || parseAs == QLatin1String("st"))
optionParseMode = Document::ParseStatement;
else if (parseAs == QLatin1String("translationunit") || parseAs == QLatin1String("tr"))
optionParseMode = Document::ParseTranlationUnit;
else {
std::cerr << "Error: Invalid ast for option \"-p\"." << std::endl;
printUsage();
exit(EXIT_FAILURE);
}
args.removeOne(parseAs);
}
if (args.isEmpty()) {
printUsage();
return helpRequested ? EXIT_SUCCESS : EXIT_FAILURE;
return EXIT_SUCCESS;
}
// Process files
......@@ -466,12 +603,32 @@ int main(int argc, char *argv[])
const QByteArray source = file.readAll();
file.close();
Document::Ptr doc = Document::create(fileName);
doc->control()->setDiagnosticClient(0);
doc->setUtf8Source(source);
doc->parse();
// Parse Document
QByteArray errors;
Document::Ptr doc;
if (optionParseMode == -1) {
QList<Document::ParseMode> parseModes;
parseModes
<< Document::ParseDeclarator
<< Document::ParseExpression
<< Document::ParseDeclaration
<< Document::ParseStatement
<< Document::ParseTranlationUnit;
doc = parse(fileName, source, parseModes, &errors, optionVerbose);
} else {
doc = parse(fileName, source, static_cast<Document::ParseMode>(optionParseMode),
&errors, optionVerbose);
}
if (!doc) {
std::cerr << "Error: Could not parse file \"" << qPrintable(fileName) << "\".\n";
std::cerr << errors.constData();
exit(EXIT_FAILURE);
}
doc->check();
// Run AST dumper
ASTDump dump(doc->translationUnit());
dump(doc->translationUnit()->ast());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment