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