File indexing completed on 2024-05-12 04:38:08
0001 /* 0002 SPDX-FileCopyrightText: 2010 Milian Wolff <mail@milianw.de> 0003 SPDX-FileCopyrightText: 2014 Kevin Funk <kfunk@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-only 0006 */ 0007 0008 #ifndef KDEVPLATFORM_DEBUGLANGUAGEPARSERHELPER_H 0009 #define KDEVPLATFORM_DEBUGLANGUAGEPARSERHELPER_H 0010 0011 #include <cstdlib> 0012 #include <cstdio> 0013 0014 #ifndef Q_OS_WIN 0015 #include <unistd.h> // for isatty 0016 #endif 0017 0018 #include <tests/autotestshell.h> 0019 #include <language/duchain/duchain.h> 0020 #include <language/duchain/problem.h> 0021 #include <language/codegen/coderepresentation.h> 0022 #include <tests/testcore.h> 0023 0024 #include <KAboutData> 0025 #include <KLocalizedString> 0026 0027 #include <QCoreApplication> 0028 #include <QCommandLineParser> 0029 #include <QCommandLineOption> 0030 #include <QTextStream> 0031 0032 namespace KDevelopUtils { 0033 QTextStream qout(stdout); 0034 QTextStream qerr(stderr); 0035 QTextStream qin(stdin); 0036 0037 using TokenTextFunc = QString (*)(int); 0038 /** 0039 * This class is a pure helper to use for binaries that you can 0040 * run on short snippets of test code or whole files and let 0041 * it print the generated tokens or AST. 0042 * 0043 * It should work fine for any KDevelop-PG-Qt based parser. 0044 * 0045 * 0046 * @tparam SessionT the parse session for your language. 0047 * @tparam TokenStreamT the token stream for your language, based on KDevPG::TokenStreamBase. 0048 * @tparam TokenT the token class for your language, based on KDevPG::Token. 0049 * @tparam LexerT the Lexer for your language. 0050 * @tparam StartAstT the AST node that is returned from @c SessionT::parse(). 0051 * @tparam DebugVisitorT the debug visitor for your language. 0052 * @tparam TokenTextT function pointer to the function that returns a string representation for an integral token. 0053 */ 0054 template <class SessionT, class TokenStreamT, class TokenT, class LexerT, 0055 class StartAstT, class DebugVisitorT, TokenTextFunc TokenTextT> 0056 class DebugLanguageParserHelper 0057 { 0058 public: 0059 DebugLanguageParserHelper(const bool printAst, const bool printTokens) 0060 : m_printAst(printAst) 0061 , m_printTokens(printTokens) 0062 { 0063 m_session.setDebug(printAst); 0064 } 0065 0066 /// parse contents of a file 0067 void parseFile(const QString& fileName) 0068 { 0069 if (!m_session.readFile(fileName, "utf-8")) { 0070 qerr << "Can't open file " << fileName << Qt::endl; 0071 std::exit(255); 0072 } else { 0073 qout << "Parsing file " << fileName << Qt::endl; 0074 } 0075 runSession(); 0076 } 0077 0078 /// parse code directly 0079 void parseCode(const QString& code) 0080 { 0081 m_session.setContents(code); 0082 0083 qout << "Parsing input" << Qt::endl; 0084 runSession(); 0085 } 0086 0087 private: 0088 /** 0089 * actually run the parse session 0090 */ 0091 void runSession() 0092 { 0093 if (m_printTokens) { 0094 TokenStreamT tokenStream; 0095 LexerT lexer(&tokenStream, m_session.contents()); 0096 int token; 0097 while ((token = lexer.nextTokenKind())) { 0098 TokenT& t = tokenStream.push(); 0099 t.begin = lexer.tokenBegin(); 0100 t.end = lexer.tokenEnd(); 0101 t.kind = token; 0102 printToken(token, lexer); 0103 } 0104 printToken(token, lexer); 0105 if (tokenStream.size() > 0) { 0106 qint64 line; 0107 qint64 column; 0108 tokenStream.endPosition(tokenStream.size() - 1, &line, &column); 0109 qDebug() << "last token endPosition: line" << line << "column" << column; 0110 } else { 0111 qDebug() << "empty token stream"; 0112 } 0113 } 0114 0115 StartAstT* ast = 0; 0116 if (!m_session.parse(&ast)) { 0117 qerr << "no AST tree could be generated" << Qt::endl; 0118 } else { 0119 qout << "AST tree successfully generated" << Qt::endl; 0120 if (m_printAst) { 0121 DebugVisitorT debugVisitor(m_session.tokenStream(), m_session.contents()); 0122 debugVisitor.visitStart(ast); 0123 } 0124 } 0125 const auto problems = m_session.problems(); 0126 if (!problems.isEmpty()) { 0127 qout << "\nproblems encountered during parsing:" << Qt::endl; 0128 for (auto& p : problems) { 0129 qout << p->description() << Qt::endl; 0130 } 0131 } else { 0132 qout << "no problems encountered during parsing" << Qt::endl; 0133 } 0134 0135 if (!ast) { 0136 exit(255); 0137 } 0138 } 0139 0140 void printToken(int token, const LexerT& lexer) const 0141 { 0142 int begin = lexer.tokenBegin(); 0143 int end = lexer.tokenEnd(); 0144 qout << m_session.contents().mid(begin, end - begin + 1).replace('\n', "\\n") << ' ' << TokenTextT(token) 0145 << Qt::endl; 0146 } 0147 0148 SessionT m_session; 0149 const bool m_printAst; 0150 const bool m_printTokens; 0151 }; 0152 0153 template <class ParserT> 0154 void setupCustomArgs(QCommandLineParser* parser) 0155 { 0156 Q_UNUSED(parser); 0157 } 0158 0159 template <class ParserT> 0160 void setCustomArgs(ParserT* parser, QCommandLineParser* commandLineParser) 0161 { 0162 Q_UNUSED(parser); 0163 Q_UNUSED(commandLineParser); 0164 } 0165 0166 /// call this after setting up @p aboutData in your @c main() function. 0167 template <class ParserT> 0168 int initAndRunParser(KAboutData& aboutData, int argc, char* argv[]) 0169 { 0170 qout.setCodec("UTF-8"); 0171 qerr.setCodec("UTF-8"); 0172 qin.setCodec("UTF-8"); 0173 0174 QCoreApplication app(argc, argv); 0175 0176 KAboutData::setApplicationData(aboutData); 0177 0178 QCommandLineParser parser; 0179 aboutData.setupCommandLine(&parser); 0180 0181 parser.addPositionalArgument("files", 0182 i18n( 0183 "files or - to read from STDIN, the latter is the default if nothing is provided"), 0184 "[FILE...]"); 0185 0186 parser.addOption(QCommandLineOption{QStringList{"a", "print-ast"}, i18n("print generated AST tree")}); 0187 parser.addOption(QCommandLineOption{QStringList{"t", "print-tokens"}, i18n("print generated token stream")}); 0188 parser.addOption(QCommandLineOption{QStringList{"c", "code"}, i18n("code to parse"), "code"}); 0189 setupCustomArgs<ParserT>(&parser); 0190 0191 parser.process(app); 0192 aboutData.processCommandLine(&parser); 0193 0194 QStringList files = parser.positionalArguments(); 0195 bool printAst = parser.isSet("print-ast"); 0196 bool printTokens = parser.isSet("print-tokens"); 0197 0198 KDevelop::AutoTestShell::init(); 0199 KDevelop::TestCore::initialize(KDevelop::Core::NoUi); 0200 0201 KDevelop::DUChain::self()->disablePersistentStorage(); 0202 KDevelop::CodeRepresentation::setDiskChangesForbidden(true); 0203 0204 ParserT parserT(printAst, printTokens); 0205 setCustomArgs(&parserT, &parser); 0206 0207 if (parser.isSet("code")) { 0208 parserT.parseCode(parser.value("code")); 0209 } else if (files.isEmpty()) { 0210 files << "-"; 0211 } 0212 0213 for (const QString& fileName : qAsConst(files)) { 0214 if (fileName == "-") { 0215 #ifndef Q_OS_WIN 0216 if (isatty(STDIN_FILENO)) { 0217 qerr << "no STDIN given" << Qt::endl; 0218 return 255; 0219 } 0220 #endif 0221 parserT.parseCode(qin.readAll().toUtf8()); 0222 } else { 0223 parserT.parseFile(QFileInfo(fileName).absoluteFilePath()); 0224 } 0225 } 0226 0227 KDevelop::TestCore::shutdown(); 0228 0229 return 0; 0230 } 0231 } 0232 0233 #endif // KDEVPLATFORM_DEBUGLANGUAGEPARSERHELPER_H