File indexing completed on 2024-04-28 04:36:16
0001 /* This file is part of kdev-pg-qt 0002 Copyright (C) 2005 Roberto Raggi <roberto@kdevelop.org> 0003 Copyright (C) 2006 Jakob Petsovits <jpetso@gmx.at> 0004 0005 This library is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU Library General Public 0007 License as published by the Free Software Foundation; either 0008 version 2 of the License, or (at your option) any later version. 0009 0010 This library is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 Library General Public License for more details. 0014 0015 You should have received a copy of the GNU Library General Public License 0016 along with this library; see the file COPYING.LIB. If not, write to 0017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0018 Boston, MA 02110-1301, USA. 0019 */ 0020 0021 #include "kdev-pg.h" 0022 #include "kdev-pg-ast.h" 0023 #include "kdev-pg-visitor.h" 0024 #include "kdev-pg-default-visitor.h" 0025 #include "kdev-pg-pretty-printer.h" 0026 #include "kdev-pg-environment.h" 0027 #include "kdev-pg-first.h" 0028 #include "kdev-pg-follow.h" 0029 #include "kdev-pg-checker.h" 0030 #include "kdev-pg-inline-checker.h" 0031 #include "kdev-pg-generate.h" 0032 #include "kdev-pg-regexp.h" 0033 #include <../kdevelop-pg-qt_version.h> 0034 0035 #include <QFile> 0036 #include <QStringList> 0037 #include <QCoreApplication> 0038 #include <QRegularExpression> 0039 #include <QFileInfo> 0040 0041 namespace KDevPG 0042 { 0043 extern QTextStream checkOut; 0044 } 0045 0046 int yyparse(); 0047 0048 void usage() 0049 { 0050 KDevPG::checkOut << "Usage: kdev-pg-qt [further-options] --output=<name> [further-options] <file.g>" << Qt::endl; 0051 } 0052 0053 void help() 0054 { 0055 usage(); 0056 KDevPG::checkOut 0057 << "Options:" << Qt::endl 0058 << "\t--output=<name> - Specify a prefix for all generated files" << Qt::endl 0059 << "\t--namespace=<NameSpaceName> - Specify the namespace for all generated classes (default: the prefix)" << Qt::endl 0060 << "\t--no-output - Do not actually generate files" << Qt::endl 0061 << "Components:" << Qt::endl 0062 << "\t--debug-visitor - Generate a visitor to dump the parse-tree" << Qt::endl 0063 << "\t--serialize-visitor - Generate a visitor to store the parse-tree in a QTextStream" << Qt::endl 0064 << "\t--no-ast - Do not generate any AST-files" << Qt::endl 0065 << "\t--with-ast - Generate AST (default)" << Qt::endl 0066 << "\t--no-parser - Do not create the parser, built-in-visitors etc." << Qt::endl 0067 << "\t--with-parser - The default, a parser will be generated" << Qt::endl 0068 << "\t--no-lexer - Do not generate the lexer" << Qt::endl 0069 << "\t--with-lexer - Ensure lexer generation" << Qt::endl 0070 << "\t--token-text - Generate a function converting the number of a token into its name" << Qt::endl 0071 << "Informative output:" << Qt::endl 0072 << "\t--terminals - Save a list of all terminals in a file named \"kdev-pg-terminals\"" << Qt::endl 0073 << "\t--symbols - Save a list of all non-terminals in a file named \"kdev-pg-symbols\"" << Qt::endl 0074 << "\t--rules - Save debugging-information for all rules in a file named \"kdev-pg-rules\"" << Qt::endl 0075 << "\t--automata - Save DFAs for all used named regexps in \"kdev-pg-lexer-name-<name>.dot\" and for all states in \"kdev-pg-lexer-state-<name>.dot\"" << Qt::endl 0076 << "Error-handling:" << Qt::endl 0077 << "\t--permissive-conflicts - The default, conflicts are shown, but kdev-pg-qt will continue (default)" << Qt::endl 0078 << "\t--strict-conflicts - Quit after having detected conflicts" << Qt::endl 0079 << "\t--ignore-conflicts - Do not perform conflict-checking" << Qt::endl 0080 << "Visitor generation:" << Qt::endl 0081 << "\t--new-visitor=VisitorName - Create a new empty visitor" << Qt::endl 0082 << "\t--inherit-default-visitor - Use the DefaultVisitor to visit sub-nodes" << Qt::endl 0083 << "\t--inherit-abstract-visitor - Reimplement the functionality of the DefaultVisitor" << Qt::endl 0084 << "Output format:" << Qt::endl 0085 << "\t--error-aware-code - Line-numbers in parser.cpp related compiler-messages will correspond to line-numbers in the grammar-file (default)" << Qt::endl 0086 << "\t--beautiful-code - Line-numbers in compiler-messages will be arbitrary, but the code will look more beautiful and it is probably more compiler-independent" << Qt::endl 0087 << "\t--compatible-error-aware-code - Like --error-aware-code, but using #line instead of GCC-specific directives, thus not providing included-from-info to display the line number in the generated file" << Qt::endl 0088 << "\t--visitor-table - Visit::visitNode will be implemented by using a lookup-array (default)" << Qt::endl 0089 << "\t--visitor-switch - Visitor::visitNode will use a switch-statement" << Qt::endl 0090 << "About:" << Qt::endl 0091 << "\t--help - Show this messages" << Qt::endl 0092 << "\t--usage - Show usage" << Qt::endl 0093 << "\t--version - Show version" << Qt::endl 0094 << "\t--author - Show authors" << Qt::endl << Qt::endl 0095 << "See http://techbase.kde.org/Development/KDevelop-PG-Qt_Introduction for further aid." << Qt::endl; 0096 0097 exit(EXIT_SUCCESS); 0098 } 0099 0100 void version() 0101 { 0102 KDevPG::checkOut << "KDevelop-PG-Qt: " << KDEVELOP_PG_QT_VERSION_STRING << Qt::endl; 0103 0104 exit(EXIT_SUCCESS); 0105 } 0106 0107 void author() 0108 { 0109 KDevPG::checkOut << QString::fromUtf8("KDevelop-PG-Qt: Copyright © 2005-2012 by the KDevelop-PG-Qt developers:") << Qt::endl 0110 << QString::fromUtf8("\tRoberto Raggi\n" 0111 "\tJakob Petsovits\n" 0112 "\tAndreas Pakulat\n" 0113 "\tJonathan Schmidt-Dominé\n" 0114 "\t...and others") << Qt::endl; 0115 0116 exit(EXIT_SUCCESS); 0117 } 0118 0119 class DebugRule 0120 { 0121 public: 0122 QTextStream& out; 0123 0124 DebugRule(QTextStream& o): out(o) 0125 {} 0126 0127 void operator()(KDevPG::Model::Node *node) 0128 { 0129 KDevPG::Model::EvolveItem *e = KDevPG::nodeCast<KDevPG::Model::EvolveItem*>(node); 0130 0131 out << Qt::endl; 0132 KDevPG::PrettyPrinter p(out); 0133 p(e); 0134 0135 bool initial = true; 0136 KDevPG::World::NodeSet::iterator it; 0137 0138 out << Qt::endl; 0139 out << " FIRST:["; 0140 for (it = KDevPG::globalSystem.first(e).begin(); it != KDevPG::globalSystem.first(e).end(); ++it) 0141 { 0142 if (!initial) 0143 out << ","; 0144 0145 p(*it); 0146 initial = false; 0147 } 0148 out << "]"; 0149 0150 initial = true; 0151 0152 out << Qt::endl; 0153 out << " FOLLOW:["; 0154 for (it = KDevPG::globalSystem.follow(e->mSymbol).begin(); 0155 it != KDevPG::globalSystem.follow(e->mSymbol).end(); ++it) 0156 { 0157 if (!initial) 0158 out << ","; 0159 0160 p(*it); 0161 initial = false; 0162 } 0163 out << "]"; 0164 out << Qt::endl; 0165 } 0166 }; 0167 0168 namespace KDevPG 0169 { 0170 QFileInfo fileInfo; 0171 } 0172 0173 int main(int argc, char **argv) 0174 { 0175 bool dump_terminals = false; 0176 bool dump_symbols = false; 0177 bool generate_parser = true; 0178 bool generate_lexer = true; 0179 bool ensure_generate_lexer_or_not = false; 0180 bool ensure_generate_parser_or_not = false; 0181 bool debug_rules = false; 0182 bool debug_automata = false; 0183 bool inherit_default = false; 0184 bool output = true; 0185 QString new_visitor; 0186 0187 QCoreApplication app(argc, argv); 0188 0189 QStringList args = QCoreApplication::arguments(); 0190 args.pop_front(); 0191 foreach(const QString& arg, args) 0192 { 0193 #define SW(str) arg.startsWith(QLatin1String(#str)) 0194 if (SW(--output=)) 0195 { 0196 KDevPG::globalSystem.language = arg.mid( 9 ); 0197 output = true; 0198 } 0199 else if (SW(--namespace=)) 0200 { 0201 KDevPG::globalSystem.ns = arg.mid( 12 ); 0202 } 0203 else if (arg == "--no-output") 0204 { 0205 output = false; 0206 } 0207 else if (arg == "--no-ast") 0208 { 0209 KDevPG::globalSystem.generateAst = false; 0210 } 0211 else if (arg == "--with-ast") 0212 { 0213 KDevPG::globalSystem.generateAst = true; 0214 } 0215 else if (arg == "--serialize-visitor") 0216 { 0217 KDevPG::globalSystem.generateSerializeVisitor = true; 0218 } 0219 else if (arg == "--ignore-conflicts") 0220 { 0221 KDevPG::globalSystem.conflictHandling = KDevPG::World::Ignore; 0222 } 0223 else if (arg == "--strict-conflicts") 0224 { 0225 KDevPG::globalSystem.conflictHandling = KDevPG::World::Strict; 0226 } 0227 else if (arg == "--permissive-conflicts") 0228 { 0229 KDevPG::globalSystem.conflictHandling = KDevPG::World::Permissive; 0230 } 0231 else if (arg == "--debug-visitor") 0232 { 0233 KDevPG::globalSystem.generateDebugVisitor = true; 0234 } 0235 else if (arg == "--token-text") 0236 { 0237 KDevPG::globalSystem.generateTokenText = true; 0238 } 0239 else if (arg == "--no-parser") 0240 { 0241 generate_parser = false; 0242 ensure_generate_parser_or_not = true; 0243 } 0244 else if (arg == "--with-parser") 0245 { 0246 generate_parser = true; 0247 ensure_generate_parser_or_not = true; 0248 } 0249 else if (arg == "--no-lexer") 0250 { 0251 generate_lexer = false; 0252 ensure_generate_lexer_or_not = true; 0253 } 0254 else if (arg == "--with-lexer") 0255 { 0256 generate_lexer = true; 0257 ensure_generate_lexer_or_not = true; 0258 } 0259 else if (arg == "--beautiful-code") 0260 { 0261 KDevPG::globalSystem.lineNumberPolicy = KDevPG::World::BeautifulCode; 0262 } 0263 else if (arg == "--error-aware-code") 0264 { 0265 KDevPG::globalSystem.lineNumberPolicy = KDevPG::World::FullLineNumbers; 0266 } 0267 else if (arg == "--compatible-error-aware-code") 0268 { 0269 KDevPG::globalSystem.lineNumberPolicy = KDevPG::World::CompatibilityLineNumbers; 0270 } 0271 else if (arg == "--visitor-table") 0272 { 0273 KDevPG::globalSystem.visitorTable = true; 0274 } 0275 else if (arg == "--visitor-switch") 0276 { 0277 KDevPG::globalSystem.visitorTable = false; 0278 } 0279 else if (SW(--new-visitor=)) 0280 { 0281 new_visitor = arg.mid( 14 ); 0282 KDevPG::globalSystem.conflictHandling = KDevPG::World::Ignore; 0283 output = false; 0284 } 0285 else if (arg == "--inherit-default-visitor") 0286 { 0287 inherit_default = true; 0288 } 0289 else if (arg == "--inherit-abstract-visitor") 0290 { 0291 inherit_default = false; 0292 } 0293 else if (arg == "--help") 0294 { 0295 help(); 0296 } 0297 else if (arg == "--version") 0298 { 0299 version(); 0300 } 0301 else if (arg == "--author") 0302 { 0303 author(); 0304 } 0305 else if (arg == "--usage") 0306 { 0307 usage(); 0308 exit(EXIT_SUCCESS); 0309 } 0310 else if (arg == "--terminals") 0311 { 0312 dump_terminals = true; 0313 } 0314 else if (arg == "--symbols") 0315 { 0316 dump_symbols = true; 0317 } 0318 else if (arg == "--rules") 0319 { 0320 debug_rules = true; 0321 } 0322 else if (arg == "--automata") 0323 { 0324 debug_automata = true; 0325 } 0326 else if (KDevPG::file.fileName().isEmpty()) 0327 { 0328 KDevPG::file.setFileName(arg); 0329 0330 if (!KDevPG::file.open(QIODevice::ReadOnly|QIODevice::Text)) 0331 { 0332 KDevPG::checkOut << "kdev-pg-qt: file ``" << arg 0333 << "'' not found!" << Qt::endl; 0334 KDevPG::file.setFileName(""); 0335 } 0336 else 0337 KDevPG::fileInfo.setFile(KDevPG::file); 0338 } 0339 else 0340 { 0341 KDevPG::checkOut << "kdev-pg-qt: unknown option ``" << arg << "''" 0342 << Qt::endl; 0343 } 0344 } 0345 0346 if( KDevPG::globalSystem.ns.isEmpty() ) 0347 KDevPG::globalSystem.ns = KDevPG::globalSystem.language; 0348 0349 if( !KDevPG::file.isOpen() ) 0350 { 0351 exit(1); 0352 } 0353 0354 yyparse(); 0355 0356 KDevPG::file.close(); 0357 0358 KDevPG::globalSystem.finishedParsing(); 0359 0360 if(!ensure_generate_parser_or_not) 0361 generate_parser = !KDevPG::globalSystem.rules.empty(); 0362 0363 if(generate_parser) 0364 { 0365 if(KDevPG::globalSystem.rules.empty()) 0366 { 0367 KDevPG::checkOut << "** ERROR no parser rules" << Qt::endl; 0368 KDevPG::ProblemSummaryPrinter::reportError(); 0369 } 0370 else 0371 { 0372 0373 if(KDevPG::globalSystem.start.empty()) 0374 KDevPG::checkOut << "** WARNING could not detect a start-symbol, every symbol gets reused, you have to care about EOFs yourself!" << Qt::endl; 0375 0376 for(QList<KDevPG::Model::EvolveItem*>::iterator it = KDevPG::globalSystem.rules.begin(); it != KDevPG::globalSystem.rules.end(); ++it) 0377 { 0378 KDevPG::InlineChecker check; 0379 check(*it); 0380 } 0381 0382 for(QList<KDevPG::Model::EvolveItem*>::iterator it = KDevPG::globalSystem.rules.begin(); it != KDevPG::globalSystem.rules.end(); ++it) 0383 { 0384 KDevPG::InitializeEnvironment initenv; 0385 initenv(*it); 0386 } 0387 0388 for(QList<KDevPG::Model::EvolveItem*>::iterator it = KDevPG::globalSystem.rules.begin(); it != KDevPG::globalSystem.rules.end(); ++it) 0389 { 0390 KDevPG::EmptyOperatorChecker check; 0391 check(*it); 0392 } 0393 0394 KDevPG::computeFirst(); 0395 KDevPG::computeFollow(); 0396 0397 if(KDevPG::globalSystem.conflictHandling != KDevPG::World::Ignore) 0398 { 0399 for(QList<KDevPG::Model::EvolveItem*>::iterator it = KDevPG::globalSystem.rules.begin(); it != KDevPG::globalSystem.rules.end(); ++it) 0400 { 0401 KDevPG::FirstFollowConflictChecker check; 0402 check(*it); 0403 } 0404 0405 for(QList<KDevPG::Model::EvolveItem*>::iterator it = KDevPG::globalSystem.rules.begin(); it != KDevPG::globalSystem.rules.end(); ++it) 0406 { 0407 KDevPG::FirstFirstConflictChecker check; 0408 check(*it); 0409 } 0410 } 0411 0412 for(QList<KDevPG::Model::EvolveItem*>::iterator it = KDevPG::globalSystem.rules.begin(); it != KDevPG::globalSystem.rules.end(); ++it) 0413 { 0414 KDevPG::EmptyFirstChecker check; 0415 check(*it); 0416 } 0417 0418 for(QList<KDevPG::Model::EvolveItem*>::iterator it = KDevPG::globalSystem.rules.begin(); it != KDevPG::globalSystem.rules.end(); ++it) 0419 { 0420 KDevPG::EmptyOperatorChecker check; 0421 check(*it); 0422 } 0423 0424 for(QList<KDevPG::Model::EvolveItem*>::iterator it = KDevPG::globalSystem.rules.begin(); it != KDevPG::globalSystem.rules.end(); ++it) 0425 { 0426 KDevPG::UndefinedSymbolChecker check; 0427 check(*it); 0428 } 0429 0430 for(QList<KDevPG::Model::EvolveItem*>::iterator it = KDevPG::globalSystem.rules.begin(); it != KDevPG::globalSystem.rules.end(); ++it) 0431 { 0432 KDevPG::UndefinedTokenChecker check; 0433 check(*it); 0434 } 0435 } 0436 } 0437 0438 if (ensure_generate_lexer_or_not && generate_lexer && !KDevPG::globalSystem.hasLexer) 0439 { 0440 KDevPG::checkOut << "** ERROR no lexer definiton" << Qt::endl; 0441 KDevPG::ProblemSummaryPrinter::reportError(); 0442 generate_lexer = false; 0443 } 0444 0445 if (!ensure_generate_lexer_or_not) 0446 generate_lexer = KDevPG::globalSystem.hasLexer; 0447 0448 if (generate_lexer) 0449 { 0450 if(!KDevPG::globalSystem.lexerEnvs.contains("start")) 0451 { 0452 KDevPG::checkOut << "** ERROR missing start-state in the lexer" << Qt::endl; 0453 KDevPG::ProblemSummaryPrinter::reportError(); 0454 } 0455 if(!QRegularExpression(QStringLiteral("\\A[a-zA-Z_][a-zA-Z_0-9]*\\z")).match(KDevPG::globalSystem.tokenStream).hasMatch()) 0456 { // primarily to exclude KDevPG::TokenStream (the default value) 0457 KDevPG::checkOut << "** ERROR You have to specify a valid name for your lexer (%token_stream)" << Qt::endl; 0458 KDevPG::ProblemSummaryPrinter::reportError(); 0459 } 0460 foreach(QString state, KDevPG::globalSystem.lexerEnvs.keys()) 0461 { 0462 KDevPG::GNFA &alltogether = **KDevPG::globalSystem.lexerEnvResults.insert(state, new KDevPG::GNFA(KDevPG::globalSystem.lexerEnvs[state])); 0463 KDevPG::GDFA &deterministic = **KDevPG::globalSystem.dfaForNfa.insert(&alltogether, new KDevPG::GDFA(alltogether.dfa())); 0464 deterministic.minimize(); 0465 deterministic.setActions(KDevPG::globalSystem.lexerActions[state]); 0466 } 0467 } 0468 0469 if(dump_terminals) 0470 { 0471 QFile ft("kdev-pg-terminals"); 0472 ft.open( QIODevice::WriteOnly | QIODevice::Truncate ); 0473 QTextStream strm(&ft); 0474 for (KDevPG::World::TerminalSet::iterator it = KDevPG::globalSystem.terminals.begin(); 0475 it != KDevPG::globalSystem.terminals.end(); ++it) 0476 { 0477 strm << it.key() << Qt::endl; 0478 } 0479 } 0480 if (dump_symbols) 0481 { 0482 QFile ft("kdev-pg-symbols"); 0483 ft.open( QIODevice::WriteOnly | QIODevice::Truncate ); 0484 QTextStream strm(&ft); 0485 for (KDevPG::World::SymbolSet::iterator it = KDevPG::globalSystem.symbols.begin(); 0486 it != KDevPG::globalSystem.symbols.end(); ++it) 0487 { 0488 strm << it.key() << Qt::endl; 0489 } 0490 } 0491 if (debug_rules) 0492 { 0493 QFile ft("kdev-pg-rules"); 0494 ft.open( QIODevice::WriteOnly | QIODevice::Truncate ); 0495 QTextStream strm(&ft); 0496 for(QList<KDevPG::Model::EvolveItem*>::iterator it = KDevPG::globalSystem.rules.begin(); it != KDevPG::globalSystem.rules.end(); ++it) 0497 { 0498 DebugRule dr(strm); 0499 dr(*it); 0500 } 0501 } 0502 if (debug_automata) 0503 { 0504 foreach(QString state, KDevPG::globalSystem.lexerEnvs.keys()) 0505 { 0506 QFile ft(QString("kdev-pg-lexer-state-") + state + ".dot"); 0507 ft.open(QIODevice::WriteOnly); 0508 QTextStream str(&ft); 0509 KDevPG::globalSystem.dfaForNfa[KDevPG::globalSystem.lexerEnvResults[state]]->dotOutput(str, state); 0510 ft.close(); 0511 } 0512 foreach(QString name, KDevPG::globalSystem.regexpById.keys()) 0513 { 0514 if(KDevPG::globalSystem.dfaForNfa.contains(KDevPG::globalSystem.regexpById[name])) 0515 { 0516 QFile ft(QString("kdev-pg-lexer-name-") + name + ".dot"); 0517 ft.open(QIODevice::WriteOnly); 0518 QTextStream str(&ft); 0519 KDevPG::globalSystem.dfaForNfa[KDevPG::globalSystem.regexpById[name]]->dotOutput(str, name); 0520 ft.close(); 0521 } 0522 } 0523 } 0524 0525 KDevPG::ProblemSummaryPrinter()(); 0526 0527 if (!(dump_terminals || dump_symbols || debug_rules) && output && KDevPG::globalSystem.language.isEmpty()) 0528 { 0529 usage(); 0530 exit(EXIT_FAILURE); 0531 } 0532 0533 if (output && generate_parser) 0534 KDevPG::generateOutput(); 0535 0536 if (output && generate_lexer) 0537 KDevPG::generateLexer(); 0538 0539 if (!new_visitor.isEmpty()) 0540 KDevPG::generateVisitor(new_visitor, inherit_default); 0541 0542 return EXIT_SUCCESS; 0543 } 0544