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