Synopsis - Cross-Reference
File:
src/Synopsis/Parser.cc 1
2
3
4
5
6
7
8#include <Synopsis/PTree.hh>
9#include <Synopsis/PTree/Display.hh>
10#include <Synopsis/SymbolLookup.hh>
11#include <Synopsis/TypeAnalysis.hh>
12#include "Synopsis/Parser.hh"
13#include "Synopsis/Lexer.hh"
14#include <Synopsis/Trace.hh>
15#include <iostream>
16
17using namespace Synopsis;
18
19class SyntaxError : public Parser::Error
20{
21public:
22 SyntaxError(const std::string &f, unsigned long l, const std::string &c)
23 : my_filename(f), my_line(l), my_context(c) {}
24 virtual void write(std::ostream &) const
25 {
26 std::cerr << "Syntax error : " << my_filename << ':' << my_line
27 << ": Error before '" << my_context << '\'' << std::endl;
28 }
29private:
30 std::string my_filename;
31 unsigned long my_line;
32 std::string my_context;
33};
34
35class UndefinedSymbol : public Parser::Error
36{
37public:
38 UndefinedSymbol(PTree::Encoding const &name,
39 std::string const &f, unsigned long l)
40 : my_name(name), my_filename(f), my_line(l) {}
41 virtual void write(std::ostream &os) const
42 {
43 os << "Undefined symbol : " << my_name.unmangled();
44 if (!my_filename.empty()) os << " at " << my_filename << ':' << my_line;
45 os << std::endl;
46 }
47private:
48 PTree::Encoding my_name;
49 std::string my_filename;
50 unsigned long my_line;
51};
52
53class SymbolAlreadyDefined : public Parser::Error
54{
55public:
56 SymbolAlreadyDefined(PTree::Encoding const &name,
57 std::string const & f1, unsigned long l1,
58 std::string const & f2, unsigned long l2)
59 : my_name(name), my_file1(f1), my_line1(l1), my_file2(f2), my_line2(l2) {}
60 virtual void write(std::ostream &os) const
61 {
62 os << "Symbol already defined : definition of " << my_name.unmangled()
63 << " at " << my_file1 << ':' << my_line1 << '\n'
64 << "previously defined at " << my_file2 << ':' << my_line2 << std::endl;
65 }
66private:
67 PTree::Encoding my_name;
68 std::string my_file1;
69 unsigned long my_line1;
70 std::string my_file2;
71 unsigned long my_line2;
72};
73
74class SymbolTypeMismatch : public Parser::Error
75{
76public:
77 SymbolTypeMismatch(PTree::Encoding const &name, PTree::Encoding const &type)
78 : my_name(name), my_type(type) {}
79 virtual void write(std::ostream &os) const
80 {
81 os << "Symbol type mismatch : " << my_name.unmangled()
82 << " has unexpected type " << my_type.unmangled() << std::endl;
83 }
84private:
85 PTree::Encoding my_name;
86 PTree::Encoding my_type;
87};
88
89namespace
90{
91
92template <typename T>
93struct PGuard
94{
95 PGuard(Parser &p, T Parser::*m) : parser(p), member(m), saved(p.*m) {}
96 ~PGuard() { parser.*member = saved;}
97 Parser & parser;
98 T Parser::* member;
99 T saved;
100};
101
102const unsigned int max_errors = 10;
103
104PTree::Node *wrap_comments(const Lexer::Comments &c)
105{
106 PTree::Node *head = 0;
107 for (Lexer::Comments::const_iterator i = c.begin(); i != c.end(); ++i)
108 head = PTree::snoc(head, new PTree::Atom(*i));
109 return head;
110}
111
112PTree::Node *nth_declarator(PTree::Node *decl, size_t n)
113{
114 decl = PTree::third(decl);
115 if(!decl || decl->is_atom()) return 0;
116
117 if(PTree::is_a(decl, Token::ntDeclarator))
118 { // if it is a function
119 if(n-- == 0) return decl;
120 }
121 else
122 while(decl && !decl->is_atom())
123 {
124 if(n-- == 0) return decl->car();
125 if((decl = decl->cdr())) decl = decl->cdr();
126 }
127 return 0;
128}
129
130void set_declarator_comments(PTree::Declaration *decl, PTree::Node *comments)
131{
132 if (!decl) return;
133
134 PTree::Node *declarator;
135 size_t n = 0;
136 while (true)
137 {
138 size_t i = n++;
139 declarator = nth_declarator(decl, i);
140 if (!declarator) break;
141 else if (PTree::is_a(declarator, Token::ntDeclarator))
142 ((PTree::Declarator*)declarator)->set_comments(comments);
143 }
144}
145
146
147PTree::Node *leftmost_leaf(PTree::Node *node, PTree::Node *& parent)
148{
149 if (!node || node->is_atom()) return node;
150 // Non-leaf node. So find first leafy child
151 PTree::Node *leaf;
152 while (node)
153 {
154 if (node->car())
155 {
156 // There is a child here..
157 if (node->car()->is_atom())
158 {
159 // And this child is a leaf! return it and set parent
160 parent = node;
161 return node->car();
162 }
163 if ((leaf = leftmost_leaf(node->car(), parent)))
164
165 return leaf;
166 }
167 // No leaves from car of this node, so try next cdr
168 node = node->cdr();
169 }
170 return 0;
171}
172
173
174
175void set_leaf_comments(PTree::Node *node, PTree::Node *comments)
176{
177 PTree::Node *parent, *leaf;
178 PTree::CommentedAtom* cleaf;
179
180 // Find leaf
181 leaf = leftmost_leaf(node, parent);
182
183 // Sanity
184 if (!leaf)
185 {
186 std::cerr << "Warning: Failed to find leaf when trying to add comments." << std::endl;
187 PTree::display(parent, std::cerr, false);
188 return;
189 }
190
191 if (!(cleaf = dynamic_cast<PTree::CommentedAtom *>(leaf)))
192 {
193 // Must change first child of parent to be a commented leaf
194 Token tk(leaf->position(), leaf->length(), Token::Comment);
195 cleaf = new (PTree::GC) PTree::CommentedAtom(tk, comments);
196 parent->set_car(cleaf);
197 }
198 else
199 {
200 // Already is a commented leaf, so add the comments to it
201 comments = PTree::snoc(cleaf->get_comments(), comments);
202 cleaf->set_comments(comments);
203 }
204}
205
206}
207
208struct Parser::ScopeGuard
209{
210 template <typename T>
211 ScopeGuard(Parser &p, T const *s)
212 : parser(p), noop(s == 0), scope_was_valid(p.my_scope_is_valid)
213 {
214 // If s contains a qualified name this may fail, as the name
215 // has to be declared before. We record the error but continune
216 // processing.
217 try { if (!noop) parser.my_symbols.enter_scope(s);}
218 catch (SymbolLookup::Undefined const &e)
219 {
220 std::string filename;
221 unsigned long line = 0;
222 if (e.ptree)
223 line = parser.my_lexer.origin(e.ptree->begin(), filename);
224
225 parser.my_errors.push_back(new UndefinedSymbol(e.name, filename, line));
226 parser.my_scope_is_valid = false;
227 noop = true;
228 }
229 }
230 ~ScopeGuard()
231 {
232 if (!noop) parser.my_symbols.leave_scope();
233 parser.my_scope_is_valid = scope_was_valid;
234 }
235 Parser & parser;
236 bool noop;
237 bool scope_was_valid;
238};
239
240Parser::StatusGuard::StatusGuard(Parser &p)
241 : my_lexer(p.my_lexer),
242 my_token_mark(my_lexer.save()),
243 my_errors(p.my_errors),
244 my_error_mark(my_errors.size()),
245 my_committed(false)
246{
247}
248
249Parser::StatusGuard::~StatusGuard()
250{
251 if (!my_committed)
252 {
253 my_lexer.restore(my_token_mark);
254 my_errors.resize(my_error_mark);
255 }
256}
257
258Parser::Parser(Lexer &lexer, SymbolFactory &symbols, int ruleset)
259 : my_lexer(lexer),
260 my_ruleset(ruleset),
261 my_symbols(symbols),
262 my_scope_is_valid(true),
263 my_comments(0),
264 my_gt_is_operator(true),
265 my_in_template_decl(false)
266{
267}
268
269Parser::~Parser()
270{
271}
272
273bool Parser::mark_error()
274{
275 Trace trace("Parser::mark_error", Trace::PARSING);
276 Token t1, t2;
277 my_lexer.look_ahead(0, t1);
278 my_lexer.look_ahead(1, t2);
279
280 std::string filename;
281 unsigned long line = my_lexer.origin(t1.ptr, filename);
282
283 const char *end = t1.ptr;
284 if(t2.type != '\0') end = t2.ptr + t2.length;
285 else if(t1.type != '\0') end = t1.ptr + t1.length;
286 my_errors.push_back(new SyntaxError(filename, line, std::string(t1.ptr, end - t1.ptr)));
287 return my_errors.size() < max_errors;
288}
289
290template <typename T>
291bool Parser::declare(T *t)
292{
293 // If the scope isn't valid, do nothing.
294 if (!my_scope_is_valid) return true;
295 try
296 {
297 my_symbols.declare(t);
298 }
299 catch (SymbolLookup::Undefined const &e)
300 {
301 std::string filename;
302 unsigned long line = 0;
303 if (e.ptree)
304 line = my_lexer.origin(e.ptree->begin(), filename);
305
306 my_errors.push_back(new UndefinedSymbol(e.name, filename, line));
307 }
308 catch (SymbolLookup::MultiplyDefined const &e)
309 {
310 std::string file1;
311 unsigned long line1 = my_lexer.origin(e.declaration->begin(), file1);
312 std::string file2;
313 unsigned long line2 = my_lexer.origin(e.original->begin(), file2);
314 my_errors.push_back(new SymbolAlreadyDefined(e.name,
315 file1, line1,
316 file2, line2));
317 }
318 catch (SymbolLookup::TypeError const &e)
319 {
320 my_errors.push_back(new SymbolTypeMismatch(e.name, e.type));
321 }
322 return my_errors.size() < max_errors;
323}
324
325unsigned long Parser::origin(const char *ptr,
326 std::string &filename) const
327{
328 return my_lexer.origin(ptr, filename);
329}
330
331void Parser::show_message_head(const char *pos)
332{
333 std::string filename;
334 unsigned long line = origin(pos, filename);
335 std::cerr << filename << ':' << line << ": ";
336}
337
338PTree::Node *Parser::parse()
339{
340 Trace trace("Parser::parse", Trace::PARSING);
341 PTree::Node *statements = 0;
342 while(my_lexer.look_ahead(0) != '\0')
343 {
344 PTree::Node *def;
345 if(definition(def))
346 {
347 statements = PTree::nconc(statements, PTree::list(def));
348 }
349 else
350 {
351 if(!mark_error()) return 0; // too many errors
352 skip_to(';');
353 Token tk;
354 my_lexer.get_token(tk); // ignore ';'
355 }
356 }
357 // Retrieve trailing comments
358 PTree::Node *c = wrap_comments(my_lexer.get_comments());
359 if (c)
360 {
361 // Use zero-length CommentedAtom as special marker.
362 // Should we define a 'PTree::Comment' atom for those comments that
363 // don't clash with the grammar ? At least that seems less hackish than this:
364 c = new PTree::CommentedAtom(c->begin(), 0, c);
365 statements = PTree::nconc(statements, PTree::list(c));
366 }
367 return statements;
368}
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383bool Parser::definition(PTree::Node *&p)
384{
385 Trace trace("Parser::definition", Trace::PARSING);
386 bool res;
387 int t = my_lexer.look_ahead(0);
388 if(t == ';')
389 res = null_declaration(p);
390 else if(t == Token::TYPEDEF)
391 {
392 PTree::Typedef *td;
393 res = typedef_(td);
394 p = td;
395 }
396 else if(t == Token::TEMPLATE)
397 res = template_decl(p);
398 else if(t == Token::METACLASS)
399 res = metaclass_decl(p);
400 else if(t == Token::EXTERN && my_lexer.look_ahead(1) == Token::StringL)
401 res = linkage_spec(p);
402 else if(t == Token::EXTERN && my_lexer.look_ahead(1) == Token::TEMPLATE &&
403 my_ruleset & GCC)
404 res = extern_template_decl(p);
405 else if(t == Token::NAMESPACE && my_lexer.look_ahead(2) == '=')
406 {
407 PTree::NamespaceAlias *alias;
408 res = namespace_alias(alias);
409 p = alias;
410 }
411 else if(t == Token::NAMESPACE)
412 {
413 PTree::NamespaceSpec *spec;
414 res = namespace_spec(spec);
415 p = spec;
416 }
417 else if(t == Token::USING)
418 {
419 if (my_lexer.look_ahead(1) == Token::NAMESPACE)
420 {
421 PTree::UsingDirective *udir;
422 res = using_directive(udir);
423 if (res)
424 {
425 declare(udir);
426 p = udir;
427 }
428 }
429 else
430 {
431 PTree::UsingDeclaration *udecl;
432 res = using_declaration(udecl);
433 if (res)
434 {
435 declare(udecl);
436 p = udecl;
437 }
438 }
439 }
440 else
441 {
442 PTree::Declaration *decl;
443 if (!declaration(decl)) return false;
444 PTree::Node *c = wrap_comments(my_lexer.get_comments());
445 if (c) set_declarator_comments(decl, c);
446 p = decl;
447 declare(decl);
448 return true;
449 }
450 my_lexer.get_comments();
451 return res;
452}
453
454bool Parser::null_declaration(PTree::Node *&decl)
455{
456 Trace trace("Parser::null_declaration", Trace::PARSING);
457 Token tk;
458
459 if(my_lexer.get_token(tk) != ';') return false;
460 decl = new PTree::Declaration(0, PTree::list(0, new PTree::Atom(tk)));
461 return true;
462}
463
464
465
466
467
468bool Parser::typedef_(PTree::Typedef *&def)
469{
470 Trace trace("Parser::typedef_", Trace::PARSING);
471 Token tk;
472 PTree::Node *type_name, *decl;
473 PTree::Encoding type_encode;
474
475 if(my_lexer.get_token(tk) != Token::TYPEDEF) return false;
476
477 def = new PTree::Typedef(new PTree::Kwd::Typedef(tk));
478 if(!type_specifier(type_name, false, type_encode)) return false;
479
480 def = PTree::snoc(def, type_name);
481 if(!init_declarator_list(decl, type_encode, true)) return false;
482 if(my_lexer.get_token(tk) != ';') return false;
483
484 def = PTree::nconc(def, PTree::list(decl, new PTree::Atom(tk)));
485 declare(def);
486 return true;
487}
488
489
490
491
492
493bool Parser::type_specifier(PTree::Node *&tspec, bool check, PTree::Encoding &encode)
494{
495 Trace trace("Parser::type_specifier", Trace::PARSING);
496 PTree::Node *cv_q, *cv_q2;
497
498 // FIXME: Need to rewrite this to correctly reflect the grammar, in particular
499 // 'typename' ...
500 // Do we need a new node type ('Typename') ?
501 if(!opt_cv_qualifier(cv_q) || !opt_integral_type_or_class_spec(tspec, encode))
502 return false;
503
504 if(!tspec)
505 {
506 if(check)
507 {
508 Token tk;
509 my_lexer.look_ahead(0, tk);
510 if(!maybe_typename_or_class_template(tk))
511 return false;
512 }
513
514 if(!name(tspec, encode))
515 return false;
516 }
517
518 if(!opt_cv_qualifier(cv_q2))
519 return false;
520
521 if(cv_q)
522 {
523 tspec = PTree::snoc(cv_q, tspec);
524 if(cv_q2)
525 tspec = PTree::nconc(tspec, cv_q2);
526 }
527 else if(cv_q2)
528 tspec = PTree::cons(tspec, cv_q2);
529
530 encode.cv_qualify(cv_q, cv_q2);
531 return true;
532}
533
534
535bool Parser::is_type_specifier()
536{
537 int t = my_lexer.look_ahead(0);
538 if(t == Token::TYPENAME || t == Token::Identifier || t == Token::Scope
539 || t == Token::CONST || t == Token::VOLATILE
540 || t == Token::CHAR || t == Token::WCHAR
541 || t == Token::INT || t == Token::SHORT || t == Token::LONG
542 || t == Token::SIGNED || t == Token::UNSIGNED || t == Token::FLOAT || t == Token::DOUBLE
543 || t == Token::VOID || t == Token::BOOLEAN
544 || t == Token::CLASS || t == Token::STRUCT || t == Token::UNION || t == Token::ENUM)
545 return true;
546 else if (my_ruleset & MSVC && t == Token::INT64)
547 return true;
548 else
549 return false;
550}
551
552
553
554
555
556
557
558
559
560
561
562bool Parser::metaclass_decl(PTree::Node *&decl)
563{
564 int t;
565 Token tk1, tk2, tk3, tk4;
566 PTree::Node *metaclass_name;
567
568 if(my_lexer.get_token(tk1) != Token::METACLASS)
569 return false;
570
571 if(my_lexer.get_token(tk2) != Token::Identifier)
572 return false;
573
574 t = my_lexer.get_token(tk3);
575 if(t == Token::Identifier)
576 {
577 metaclass_name = new PTree::Identifier(tk2);
578 decl = new PTree::MetaclassDecl(new PTree::UserKeyword(tk1),
579 PTree::list(metaclass_name,
580 new PTree::Identifier(tk3)));
581 }
582 else if(t == ':')
583 {
584 if(my_lexer.get_token(tk4) != Token::Identifier)
585 return false;
586
587 metaclass_name = new PTree::Identifier(tk4);
588 decl = new PTree::MetaclassDecl(new PTree::UserKeyword(tk1),
589 PTree::list(metaclass_name,
590 new PTree::Identifier(tk2)));
591 }
592 else if(t == ';')
593 {
594 metaclass_name = new PTree::Identifier(tk2);
595 decl = new PTree::MetaclassDecl(new PTree::UserKeyword(tk1),
596 PTree::list(metaclass_name, 0,
597 new PTree::Atom(tk3)));
598 return true;
599 }
600 else
601 return false;
602
603 t = my_lexer.get_token(tk1);
604 if(t == '(')
605 {
606 PTree::Node *args;
607 if(!meta_arguments(args))
608 return false;
609
610 if(my_lexer.get_token(tk2) != ')')
611 return false;
612
613 decl = PTree::nconc(decl, PTree::list(new PTree::Atom(tk1), args,
614 new PTree::Atom(tk2)));
615 t = my_lexer.get_token(tk1);
616 }
617
618 if(t == ';')
619 {
620 decl = PTree::snoc(decl, new PTree::Atom(tk1));
621 return true;
622 }
623 else
624 return false;
625}
626
627
628
629
630bool Parser::meta_arguments(PTree::Node *&args)
631{
632 int t;
633 Token tk;
634
635 int n = 1;
636 args = 0;
637 while(true)
638 {
639 t = my_lexer.look_ahead(0);
640 if(t == '\0')
641 return false;
642 else if(t == '(')
643 ++n;
644 else if(t == ')')
645 if(--n <= 0)
646 return true;
647
648 my_lexer.get_token(tk);
649 args = PTree::snoc(args, new PTree::Atom(tk));
650 }
651}
652
653
654
655
656
657
658bool Parser::linkage_spec(PTree::Node *&spec)
659{
660 Trace trace("Parser::linkage_spec", Trace::PARSING);
661 Token tk1, tk2;
662 PTree::Node *body;
663
664 if(my_lexer.get_token(tk1) != Token::EXTERN) return false;
665 if(my_lexer.get_token(tk2) != Token::StringL) return false;
666
667 spec = new PTree::LinkageSpec(new PTree::Kwd::Extern(tk1),
668 PTree::list(new PTree::Atom(tk2)));
669 if(my_lexer.look_ahead(0) == '{')
670 {
671 if(!linkage_body(body)) return false;
672 }
673 else
674 if(!definition(body)) return false;
675
676 spec = PTree::snoc(spec, body);
677 return true;
678}
679
680
681
682
683
684
685bool Parser::namespace_spec(PTree::NamespaceSpec *&spec)
686{
687 Trace trace("Parser::namespace_spec", Trace::PARSING);
688
689 Token tk;
690 if(my_lexer.get_token(tk) != Token::NAMESPACE) return false;
691 PTree::Kwd::Namespace *namespace_ = new PTree::Kwd::Namespace(tk);
692
693 PTree::Node *comments = wrap_comments(my_lexer.get_comments());
694
695 PTree::Node *name;
696 if(my_lexer.look_ahead(0) == '{') name = 0;
697 else
698 if(my_lexer.get_token(tk) == Token::Identifier)
699 name = new PTree::Identifier(tk);
700 else return false;
701
702 spec = new PTree::NamespaceSpec(namespace_, PTree::list(name, 0));
703 spec->set_comments(comments);
704
705 PTree::Node *body;
706 if(my_lexer.look_ahead(0) == '{')
707 {
708 declare(spec);
709 ScopeGuard guard(*this, spec);
710 if(!linkage_body(body)) return false;
711 }
712 else if(!definition(body)) return false;
713
714 PTree::tail(spec, 2)->set_car(body);
715
716 return true;
717}
718
719
720
721
722bool Parser::namespace_alias(PTree::NamespaceAlias *&exp)
723{
724 Trace trace("Parser::namespace_alias", Trace::PARSING);
725 Token tk;
726
727 if(my_lexer.get_token(tk) != Token::NAMESPACE) return false;
728 PTree::Node *ns = new PTree::Kwd::Namespace(tk);
729
730 if (my_lexer.get_token(tk) != Token::Identifier) return false;
731 PTree::Node *alias = new PTree::Identifier(tk);
732
733 if (my_lexer.get_token(tk) != '=') return false;
734 PTree::Node *eq = new PTree::Atom(tk);
735
736 PTree::Node *name;
737 PTree::Encoding encode;
738 int length = 0;
739 if(my_lexer.look_ahead(0) == Token::Scope)
740 {
741 my_lexer.get_token(tk);
742 name = PTree::list(new PTree::Atom(tk));
743 encode.global_scope();
744 ++length;
745 }
746 else name = 0;
747
748 while (true)
749 {
750 if (my_lexer.get_token(tk) != Token::Identifier) return false;
751 PTree::Node *n = new PTree::Identifier(tk);
752 encode.simple_name(n);
753 ++length;
754
755 if(my_lexer.look_ahead(0) == Token::Scope)
756 {
757 my_lexer.get_token(tk);
758 name = PTree::nconc(name, PTree::list(n, new PTree::Atom(tk)));
759 }
760 else
761 {
762 if(name == 0) name = n;
763 else name = PTree::snoc(name, n);
764
765 if(length > 1) encode.qualified(length);
766
767 break;
768 }
769 }
770
771 if (my_lexer.get_token(tk) != ';') return false;
772
773 exp = new PTree::NamespaceAlias(ns, PTree::list(alias, eq,
774 name, new PTree::Atom(tk)));
775 return true;
776}
777
778
779
780
781
782bool Parser::using_directive(PTree::UsingDirecti