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