1
// This file is part of PUMA.
2
// Copyright (C) 1999-2003 The PUMA developer team.
4
// This program is free software; you can redistribute it and/or
5
// modify it under the terms of the GNU General Public License as
6
// published by the Free Software Foundation; either version 2 of
7
// the License, or (at your option) any later version.
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
// GNU General Public License for more details.
14
// You should have received a copy of the GNU General Public
15
// License along with this program; if not, write to the Free
16
// Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
19
#include <Puma/CParser.h>
20
#include <Puma/CCParser.h>
21
#include <Puma/ErrorStream.h>
22
#include <Puma/CSemVisitor.h>
23
#include <Puma/CCSemVisitor.h>
24
#include <Puma/CPrintVisitor.h>
25
#include <Puma/PrePrintVisitor.h>
26
#include <Puma/CTranslationUnit.h>
27
#include <Puma/RegComp.h>
28
#include <Puma/PreTree.h>
29
#include <Puma/CTree.h>
30
#include <Puma/version.h>
40
#include <sys/timeb.h>
47
extern int TRACE_TYPE; // trace created types
48
extern int TRACE_UNDO; // trace discarded subtrees
49
extern int TRACE_OBJS; // trace created and discarded semantic objects
50
extern int TRACE_SCOPES; // trace entered/leaved scopes
51
extern int TRACE_INSTANCE_CODE; // trace instance units
52
extern int TRACE_PARSE_INSTANCE; // trace parse instance units
56
static bool verify_tree = false;
57
static bool lang_c = false;
58
static bool lang_cc = false;
59
static std::string input_file = "";
60
static bool verbose = false;
61
static bool dump_cpp = false;
62
static std::string dump_cpp_file;
63
static int dump_db = 0;
64
static std::string dump_db_file;
65
static bool dump_unit = false;
66
static std::string dump_unit_file;
67
static bool dump_tree = false;
68
static std::string dump_tree_file;
69
static bool dump_cpp_tree = false;
70
static std::string dump_cpp_tree_file;
71
static bool trace_parse = false;
72
static std::string trace_parse_file;
75
void log(const std::string& message) {
79
struct tm *today = localtime(<ime);
81
strftime(buffer,127,"%b %d %H:%M:%S",today);
82
std::cout << "-- " << buffer << ".";
86
std::cout << btime.millitm;
90
std::cout << tv.tv_usec/1000;
92
std::cout << ": " << message << std::endl;
97
void printError(Puma::ErrorStream& err, Puma::CTree* node, const std::string& message) {
99
Puma::Token* t = node->token();
101
err << t->location();
104
err << Puma::sev_error << message.c_str() << Puma::endMessage;
108
void printError(Puma::ErrorStream& err, const std::string& message) {
109
printError(err, 0, message);
113
std::ostream* openStream(const std::string& file) {
114
return file.empty() ? &std::cout : new std::ofstream(file.c_str());
118
void closeStream(const std::string& file, std::ostream* os) {
119
if (! file.empty()) {
125
void addCompilerConfig(Puma::ErrorStream& err, Puma::Config& config) {
126
// always set when Puma parses something, can be used
127
// to hide code from Puma's view
128
config.Add("-D", "__puma");
129
// signal that Puma prefers standard C code
130
config.Add("-D", "__STDC__");
132
// command to get the configuration options from the compiler
133
std::string tmpfile = ".tmp_config";
136
cmd = "gcc -E -dM -v -x c /dev/null > "+tmpfile+" 2>&1";
139
cmd = "g++ -E -dM -v -x c++ /dev/null > "+tmpfile+" 2>&1";
141
// execute command to get the compiler config
144
// parse the compiler config
145
std::ifstream in(tmpfile.c_str());
147
printError(err, "unable to open file "+tmpfile);
148
printError(err, "command '"+cmd+"' failed");
152
// regular expressions for matching includes and macros
153
Puma::RegComp regincl("#include [<\"].*:.*");
154
Puma::RegComp reginlist("^ .*");
155
Puma::RegComp regdef("#define .*");
157
// read file line by line
158
bool in_include_list = false;
159
std::list<std::string> includes;
161
while (std::getline(in, line)) {
162
// start of list of include paths
163
if (regincl.match(line.c_str())) {
164
in_include_list = true;
165
// within a list of include paths
166
} else if (in_include_list && reginlist.match(line.c_str())) {
167
// strip leading white space
168
unsigned int pos = line.find_first_not_of(" ");
169
line = line.substr(pos);
171
// convert '//*' to '/'
173
for (std::string::iterator c = line.begin(); c != line.end(); ++c) {
174
if (lastchar == *c && *c == '/') {
181
// add the include path
182
includes.push_back(line);
183
// end of list of include paths
184
} else if (in_include_list) {
185
in_include_list = false;
186
// match a '#define ...'
187
} else if (regdef.match(line.c_str())) {
188
// remove '#define ' (8 characters)
189
line = line.substr(8);
191
// split name and argument of macro
193
// find name, split at first whitespace
194
std::string::size_type pos = line.find(" ");
195
std::string name = line.substr(0, pos);
197
// set '1' as default argument if no argument is defined
198
std::string value = "1";
200
// find value, i.e. the string behind the macro name
201
if ((pos != string::npos) && (line.find_first_not_of(" ", pos) != std::string::npos)) {
202
value = line.substr(pos+1);
205
// add the macro definition to the configuration
206
config.Add("-D", name.c_str(), value.c_str());
208
// check for gcc support
209
if (name == "__GNUC__") {
210
// get the compiler version
212
std::istringstream convert(value);
213
convert >> gcc_version;
215
// enable correct gcc support
216
if (gcc_version > 2) {
219
config.Add("--gnu-2.95");
221
// check for the size_t
222
} else if (name == "__SIZE_TYPE__") {
223
config.Add("--size-type", value.c_str());
224
// check for the ptrdiff_t
225
} else if (name == "__PTRDIFF_TYPE__") {
226
config.Add("--ptrdiff-type", value.c_str());
232
// now add the include directories in the correct order
233
std::list<std::string>::reverse_iterator i = includes.rbegin();
234
for (; i != includes.rend(); ++i)
235
config.Add("-I", i->c_str());
237
unlink(tmpfile.c_str());
241
void readOptionArg(int argc, char** argv, int& i, std::string& file) {
242
if (argc > (i+2) && argv[i+1][0] != '-') {
248
void printUsage(Puma::CProject& project) {
249
std::cout << "PUMA C/C++ Parser, v" << Puma::puma_version() << std::endl;
250
std::cout << "usage: parser [options] <FILE>" << std::endl;
251
std::cout << "options:" << std::endl;
252
project.config().PrintOptions(std::cout);
253
std::cout << " --dump-db [FILE] Dump the semantic database to file or stdout" << std::endl;
254
std::cout << " --dump-db-all [FILE] Dump the semantic database incl. builtins to file or stdout" << std::endl;
255
std::cout << " --dump-tree [FILE] Dump the syntax tree to file or stdout" << std::endl;
256
std::cout << " --dump-unit [FILE] Dump the primary token unit to file or stdout" << std::endl;
257
std::cout << " --dump-cpp [FILE] Dump the C preprocessor output to file or stdout" << std::endl;
258
std::cout << " --dump-cpp-tree [FILE] Dump the C preprocessor syntax tree to file or stdout" << std::endl;
259
std::cout << " --dump-instances Dump the created template instances to stdout [debug]" << std::endl;
260
std::cout << " --trace-parse [FILE] Trace the parse process and write it to file or stdout [debug]" << std::endl;
261
std::cout << " --trace-instances Trace parsing template instances and write it to stdout [debug]" << std::endl;
262
std::cout << " --trace-types Trace the created types and write it to stdout [debug]" << std::endl;
263
std::cout << " --trace-scopes Trace the entered scopes and write it to stdout [debug]" << std::endl;
264
std::cout << " --trace-objects Trace the created semantic opjects and write it to stdout [debug]" << std::endl;
265
std::cout << " --trace-undo-tree Trace the backtracking and write it to stdout [debug]" << std::endl;
266
std::cout << " --verify-tree Verify the syntax tree" << std::endl;
267
std::cout << " --default-path Add the path to the input file as source path" << std::endl;
268
std::cout << " --no-default-config Don't add the default configuration of the target compiler" << std::endl;
269
std::cout << " --help Show the usage information and exit" << std::endl;
270
std::cout << " -v Enable verbose logging" << std::endl;
275
void evalOptions(Puma::CProject& project, int argc, char **argv) {
276
// need usage information?
281
// read additional command line options
282
bool default_path = false;
283
bool default_config = true;
284
for (int i = 1; i < argc; i++) {
285
std::string o = argv[i];
288
} else if (o == "-v") {
290
} else if (o == "--verify-tree") {
292
} else if (o == "--default-path") {
294
} else if (o == "--no-default-config") {
295
default_config = false;
296
} else if (o == "--dump-db") {
298
readOptionArg(argc, argv, i, dump_db_file);
299
} else if (o == "--dump-db-all") {
301
readOptionArg(argc, argv, i, dump_db_file);
302
} else if (o == "--dump-tree") {
304
readOptionArg(argc, argv, i, dump_tree_file);
305
} else if (o == "--dump-unit") {
307
readOptionArg(argc, argv, i, dump_unit_file);
308
} else if (o == "--dump-cpp") {
310
readOptionArg(argc, argv, i, dump_cpp_file);
311
} else if (o == "--dump-cpp-tree") {
312
dump_cpp_tree = true;
313
readOptionArg(argc, argv, i, dump_cpp_tree_file);
314
} else if (o == "--trace-parse") {
316
readOptionArg(argc, argv, i, trace_parse_file);
317
} else if (o == "--trace-types") {
318
Puma::TRACE_TYPE = 1;
319
} else if (o == "--trace-scopes") {
320
Puma::TRACE_SCOPES = 1;
321
} else if (o == "--dump-instances") {
322
Puma::TRACE_INSTANCE_CODE = 1;
323
} else if (o == "--trace-instances") {
324
Puma::TRACE_PARSE_INSTANCE = 1;
325
} else if (o == "--trace-objects") {
326
Puma::TRACE_OBJS = 1;
327
} else if (o == "--trace-undo-tree") {
328
Puma::TRACE_UNDO = 1;
332
// add compiler configuration
333
if (default_config) {
334
addCompilerConfig(project.err(),project.config());
337
// add command line options
338
project.config().Read(argc,argv);
341
lang_c = project.config().Option("--lang-c");
342
lang_cc = project.config().Option("--lang-c++") ||
343
project.config().Option("--lang-ac++");
344
input_file = argv[argc-1];
346
// get input language
347
if (! lang_c && ! lang_cc) {
348
std::string::size_type pos = input_file.rfind('.');
349
if (pos != input_file.npos) {
350
std::string suffix = input_file.substr(pos+1);
351
if (suffix == "cc" || suffix == "cpp") {
354
else if (suffix == "c") {
359
// set input language
361
project.config().Add("--lang-c");
365
project.config().Add("--lang-c++");
370
// add the directory containing the current input file
371
// as source directory
372
std::string path = ".";
373
std::string::size_type pos = input_file.rfind("/");
374
if (pos != std::string::npos) {
377
path = input_file.substr(0, pos);
380
project.config().Add("-p", path.c_str());
383
// propagate the configuration
384
project.configure(project.config());
388
Puma::CTranslationUnit* parseCFile(Puma::ErrorStream& err, Puma::CProject& project) {
390
log("lexical analysis");
391
Puma::Unit *unit = project.scanFile(input_file.c_str());
393
printError(err, "lexical analysis of input file failed");
399
std::ostream* unit_os = openStream(dump_unit_file);
400
unit->print(*unit_os);
401
closeStream(dump_unit_file, unit_os);
404
// syntactic analysis
405
log("syntactic analysis");
406
Puma::CParser parser;
407
std::ostream* trace_os = openStream(trace_parse_file);
408
std::ostream* cpp_os = openStream(dump_cpp_file);
410
#ifdef __PUMA_TRACING__
411
parser.trace(*trace_os, verbose);
414
Puma::CTranslationUnit* file = parser.parse(*unit, project, dump_cpp, *cpp_os);
415
closeStream(dump_cpp_file, cpp_os);
416
closeStream(trace_parse_file, trace_os);
419
if (file && file->tree()) {
420
log("semantic analysis");
421
Puma::CSemVisitor semantics(err);
422
semantics.run(file->tree());
429
Puma::CTranslationUnit* parseCCFile(Puma::ErrorStream& err, Puma::CProject& project) {
431
log("lexical analysis");
432
Puma::Unit *unit = project.scanFile(input_file.c_str());
439
std::ostream* unit_os = openStream(dump_unit_file);
440
unit->print(*unit_os);
441
closeStream(dump_unit_file, unit_os);
444
// syntactic analysis
445
log("syntactic analysis");
446
Puma::CCParser parser;
447
std::ostream* trace_os = openStream(trace_parse_file);
448
std::ostream* cpp_os = openStream(dump_cpp_file);
450
#ifdef __PUMA_TRACING__
451
parser.trace(*trace_os, verbose);
454
Puma::CTranslationUnit* file = parser.parse(*unit, project, dump_cpp, *cpp_os);
455
closeStream(dump_cpp_file, cpp_os);
456
closeStream(trace_parse_file, trace_os);
459
if (file && file->tree()) {
460
log("semantic analysis");
461
Puma::CCSemVisitor semantics(err);
462
semantics.run(file->tree());
469
bool isDependent(Puma::CTree* node) {
470
// check if any sub expression of a given expression
471
// depends on a template parameter
472
if (node->Type() && node->Type()->isDependent()) {
475
// check the child nodes
476
unsigned sons = node->Sons();
477
for (unsigned i = 0; i < sons; ++i) {
478
if (isDependent(node->Son(i))) {
486
std::string printTree(Puma::CTree* node) {
487
// print the tokens of a sub-tree
488
std::ostringstream s;
489
Puma::Token* token = node->token();
490
Puma::Token* end = node->end_token();
492
Puma::Unit* file = token->unit();
498
token = file->next(token);
505
void walk(Puma::ErrorStream& err, Puma::CTree* node) {
509
// node indicating an error in the input file
510
if (! node->Parent() && node->NodeName() != Puma::CT_Program::NodeId()) {
511
printError(err, node, node->NodeName()+std::string(" node has no parent"));
513
// node indicating an error in the input file
514
if (node->NodeName() == Puma::CT_Error::NodeId()) {
515
printError(err, node, "error node found in tree");
517
// all expressions shall have a type
518
else if (node->IsExpression() && ! node->Type()) {
519
if (! isDependent(node)) {
520
printError(err, node, "expression without type");
523
// all expressions shall have a defined type
524
else if (node->IsExpression() && node->Type()->is_undefined()) {
525
if (! isDependent(node)) {
526
printError(err, node, "expression with undefined type");
529
// all symbols shall be resolved
530
else if (node->IsSimpleName() && node->SemObject() && ! node->SemObject()->Object()) {
531
if (! isDependent(node)) {
532
printError(err, node, std::string("unresolved symbol "+printTree(node)));
535
// walk over the child nodes
537
unsigned int sons = node->Sons();
538
for (unsigned i = 0; i < sons; ++i) {
539
walk(err, node->Son(i));
545
int main(int argc, char **argv) {
546
Puma::ErrorStream err;
547
Puma::CProject project(err);
549
evalOptions(project,argc,argv);
552
Puma::CTranslationUnit* file = 0;
554
file = parseCFile(err,project);
557
file = parseCCFile(err,project);
560
// dump the syntax tree
561
if (dump_tree && file && file->tree()) {
562
log("dump syntax tree");
563
std::ostream* tree_os = openStream(dump_tree_file);
564
Puma::CPrintVisitor printer;
565
printer.print(file->tree(), *tree_os);
566
closeStream(dump_tree_file, tree_os);
569
// dump the preprocessor syntax tree
570
if (dump_cpp_tree && file && file->cpp_tree()) {
571
log("dump preprocessor syntax tree");
572
std::ostream* tree_os = openStream(dump_cpp_tree_file);
573
Puma::PrePrintVisitor printer(*tree_os);
574
file->cpp_tree()->accept(printer);
575
closeStream(dump_cpp_tree_file, tree_os);
578
// dump the semantic database
579
if (dump_db && file) {
580
log("dump semantic information database");
581
std::ostream* db_os = openStream(dump_db_file);
582
file->db().Dump(*db_os, 10, dump_db == 2);
583
closeStream(dump_db_file, db_os);
586
// walk over the tree
587
if (verify_tree && file && file->tree()) {
588
log("verify syntax tree");
589
walk(err, file->tree());
596
return err.severity() > Puma::sev_warning;