1 // $Id: CxxFile.cc,v 1.29 2004/05/07 06:53:14 christof Exp $
2 /* glade--: C++ frontend for glade (Gtk+ User Interface Builder)
3 * Copyright (C) 1998 Christof Petig
4 * Copyright (C) 1999-2000 Adolf Petig GmbH & Co. KG, written by Christof Petig
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21 //#define DEBUG_STATE_MACHINE
22 //#define DEBUG(x) std::cout << x << '\n'
23
24 #include "CxxFile.hh"
25 #include <cstdio>
26 #include <algorithm>
27 #include <cassert>
28 #include <iostream>
29
GlobalContext(Global_Context new_gc,bool stay_inside)30 CxxFile &CxxFile::GlobalContext(Global_Context new_gc,bool stay_inside)
31 // insert blank line if necessary
32 { if (!stay_inside) ToOutside();
33 #ifdef DEBUG_STATE_MACHINE
34 std::cout << "global " << state.global << "->" << new_gc << '\n';
35 #endif
36 if (new_gc!=state.global)
37 { if (state.global!=gc_start) VSpace();
38 }
39 else if (new_gc==gc_function || new_gc==gc_class) VSpace();
40 state.global=new_gc;
41 return *this;
42 }
43
close(void)44 void CxxFile::close(void)
45 { EndLine(); // necessary??
46 ToOutside();
47 SystemFile::close();
48 elements.clear();
49 }
50
ToOutside()51 CxxFile &CxxFile::ToOutside()
52 { switch(state.context)
53 { case ctx_Outside:
54 case ctx_ClassPrivate:
55 case ctx_ClassPublic:
56 case ctx_ClassProtected:
57 break;
58 case ctx_Declaration:
59 case ctx_Statement:
60 case ctx_BlockStatement:
61 case ctx_FunctionName: // no function args
62 case ctx_MidFunctionArgs: // end function
63 case ctx_RValueWritten:
64 case ctx_CppIf:
65 case ctx_ShortComment:
66 case ctx_Namespace:
67 case ctx_ClassName:
68 EndLine();
69 break;
70 default: InvalidState("ToOutside");
71 break;
72 }
73 return *this;
74 }
75
EndLine(bool endBlock)76 CxxFile &CxxFile::EndLine(bool endBlock)
77 { int iterations=10;
78 reiterate:
79 #ifdef DEBUG_STATE_MACHINE
80 std::cout << "EndLine(" << endBlock << ") at state " << state.context <<
81 " | " << state.global << '\n';
82 #endif
83 if (!--iterations)
84 { std::cerr << "endless loop in EndLine(), state '" << state.context << "'\n";
85 return *this;
86 }
87 switch(state.context)
88 { case ctx_Outside:
89 case ctx_ClassPrivate:
90 case ctx_ClassProtected:
91 case ctx_ClassPublic:
92 break;
93
94 case ctx_MidCtorArgs:
95 Output() << ')';
96 new_context(ctx_MidConstructor);
97 goto reiterate;
98
99 case ctx_DefineBody:
100 case ctx_DefineName:
101 case ctx_MidConstructor:
102 case ctx_ShortComment:
103 case ctx_CppIf:
104 NewLine(false);
105 pop();
106 goto reiterate;
107
108 case ctx_Derivation:
109 assert(state.global==gc_class);
110 NewLine(false);
111 break;
112
113 case ctx_ClassName:
114 if (state.global==gc_declaration)
115 { pop();
116 goto reiterate;
117 }
118 else NewLine(false);
119 break;
120
121 case ctx_Definition:
122 if (!endBlock) // NewLine before Block
123 { NewLine(false);
124 }
125 else // Block ended
126 { switch (state.global)
127 { case gc_class: Output() << ';';
128 // second newline after class definition
129 // better via global_context
130 break;
131 case gc_function:
132 break;
133 case gc_declaration: // should be an enum
134 Output() << ';';
135 break;
136 default: std::cerr << "illegal state.global " << state.global << '\n';
137 assert(0);
138 break;
139 }
140 NewLine(false);
141 Global_Context help=state.global;
142 pop(); // End Of Definition !
143 state.global=help; // promote global up, since we didn't know then
144 }
145 break;
146
147 case ctx_RValue:
148 pop();
149 goto reiterate;
150
151 case ctx_RValueWritten:
152 Output() << ';';
153 NewLine(false);
154 // never second ';' needed ??
155 do
156 { pop();
157 } while (state.context==ctx_Statement || state.context==ctx_Declaration);
158 goto reiterate;
159
160 case ctx_Statement:
161 Output() << ';';
162 // fall through
163 case ctx_BlockStatement: // e.g. else { }
164 NewLine(false);
165 pop();
166 state.global=gc_statement;
167 goto reiterate;
168
169 case ctx_Namespace:
170 NewLine(false);
171 pop();
172 state.global=gc_declaration;
173 goto reiterate;
174
175 case ctx_Declaration:
176 Output() << ';';
177 NewLine(false);
178 pop();
179 state.global=gc_declaration;
180 goto reiterate;
181
182 case ctx_FunctionName: // no function args
183 Output() << "()";
184 pop();
185 goto reiterate;
186
187 case ctx_MidFunctionArgs: // end function
188 Output() << ')';
189 pop();
190 goto reiterate;
191
192 default: InvalidState("EndLine"); break;
193 }
194 return *this;
195 }
196
Include(const std::string & name,bool local)197 CxxFile &CxxFile::Include(const std::string &name,bool local)
198 { if (name.empty()) return *this; // suchen ob schon ?
199
200 const UniqueValue::value_t type=local?v_IncludeQuote:v_IncludeBracket;
201 if (ElementAlreadyThere(element_t(type,name))) return *this;
202
203 // ToOutside();
204 GlobalContext(gc_include); // insert blank lines if necessary
205 Output() << "#include " << (local?'"':'<') << name << (local?'"':'>');
206 NewLine(false);
207 // FIXME: cpp indentation
208 AddElement(element_t(type,name));
209 return *this;
210 }
211
FunctionArg(void)212 CxxFile &CxxFile::FunctionArg(void)
213 { switch(state.context)
214 { case ctx_MidFunctionArgs: Output() << ", ";
215 break;
216 case ctx_FunctionName: Output() << '(';
217 break;
218 case ctx_MidConstructor: Output() << '(';
219 new_context(ctx_MidCtorArgs);
220 return *this;
221 case ctx_MidCtorArgs: Output() << ", ";
222 return *this;
223 default: InvalidState("FunctionArg");
224 break;
225 }
226 new_context(ctx_MidFunctionArgs);
227 return *this;
228 }
229
Constructor(void)230 CxxFile &CxxFile::Constructor(void)
231 { switch(state.context)
232 { case ctx_MidConstructor: Output() << ", ";
233 break;
234 case ctx_FunctionName: Output() << "() : ";
235 break;
236 case ctx_MidFunctionArgs: Output() << ") : ";
237 break;
238 case ctx_MidCtorArgs: Output() << "), ";
239 break;
240 default: InvalidState("Constructor");
241 break;
242 }
243 new_context(ctx_MidConstructor);
244 return *this;
245 }
246
Derivation(void)247 CxxFile &CxxFile::Derivation(void)
248 { switch(state.context)
249 { case ctx_Derivation: Output() << ", ";
250 break;
251 case ctx_ClassName: Output() << " : ";
252 break;
253 default: InvalidState("Derivation");
254 break;
255 }
256 new_context(ctx_Derivation);
257 return *this;
258 }
259
Funct_ReturnType(void)260 CxxFile &CxxFile::Funct_ReturnType(void) // similar to Class()
261 { switch (state.context)
262 { case ctx_Declaration: GlobalContext(gc_declaration,true);
263 break;
264 case ctx_Definition: GlobalContext(gc_function,true);
265 break;
266 case ctx_Outside:
267 std::cerr << "Funct_ReturnType(): please specify either Definition()"
268 " or Declaration().\n"
269 "Assuming Definition()\n";
270 GlobalContext(gc_function,true);
271 break;
272 case ctx_ReturnType:
273 // since Storage is an alias to ReturnType this is legal
274 Output() << ' ';
275 return *this;
276 default: InvalidState("ReturnType");
277 break;
278 }
279 push(ctx_ReturnType);
280 return *this;
281 }
282
FunctionName(void)283 CxxFile &CxxFile::FunctionName(void)
284 { switch(state.context)
285 { case ctx_ReturnType: Output() << ' ';
286 break;
287 case ctx_RValue: // assignment = statement
288 case ctx_RValueWritten:
289 break;
290 case ctx_Outside: // function call
291 GlobalContext(gc_statement);
292 break;
293 case ctx_Declaration:
294 case ctx_Definition: // constructor/destructor have no return type
295 GlobalContext(gc_function,true);
296 push(ctx_FunctionName);
297 break;
298 case ctx_MidFunctionArgs:
299 push(ctx_FunctionName);
300 break;
301 default: InvalidState("FunctionName"); break;
302 }
303 new_context(ctx_FunctionName);
304 return *this;
305 }
306
StartBlock(void)307 CxxFile &CxxFile::StartBlock(void)
308 { int right=3;
309 if (state.context!=ctx_RValue) EndLine();
310 else NewLine(false); // this is meant for "int b[]=\n{ 1,2 };"
311 Context oldcontext=state.context;
312 Output() << '{';
313 switch(oldcontext)
314 { case ctx_Definition:
315 push(ctx_Outside,right=3);
316 break;
317 case ctx_ClassName: // switch state, do not push it ... THINK AGAIN
318 case ctx_Derivation:
319 state.indentation+=8;
320 state.context=ctx_ClassPrivate;
321 // push(ctx_ClassPrivate,right=8);
322 break;
323 case ctx_Outside:
324 push(ctx_Outside,right=3);
325 break;
326 case ctx_RValue:
327 push(ctx_Outside,right=8);
328 break;
329 default: InvalidState("StartBlock"); break;
330 }
331 Indent(right-1); indent_pending=false;
332 return *this;
333 }
334
EndBlock(void)335 CxxFile &CxxFile::EndBlock(void)
336 { EndLine();
337 pop();
338 NewLine(false) << '}';
339 EndLine(true);
340 NewLine();
341 return *this;
342 }
343
EndType(void)344 CxxFile &CxxFile::EndType(void)
345 { return EndBlock();
346 }
347
Private(void)348 CxxFile &CxxFile::Private(void)
349 { ToOutside();
350 switch(state.context)
351 { case ctx_ClassPrivate: break;
352 case ctx_ClassProtected:
353 case ctx_ClassPublic:
354 NewLine().NoIndent() << "private:";
355 NewLine();
356 break;
357 default: InvalidState("Private");
358 break;
359 }
360 new_context(ctx_ClassPrivate);
361 return *this;
362 }
363
Protected(void)364 CxxFile &CxxFile::Protected(void)
365 { ToOutside();
366 switch(state.context)
367 { case ctx_ClassProtected: break;
368 case ctx_ClassPrivate:
369 case ctx_ClassPublic:
370 NewLine().NoIndent() << "protected:";
371 NewLine();
372 break;
373 default: InvalidState("Protected");
374 break;
375 }
376 new_context(ctx_ClassProtected);
377 return *this;
378 }
379
Public(void)380 CxxFile &CxxFile::Public(void)
381 { ToOutside();
382 switch(state.context)
383 { case ctx_ClassPublic: break;
384 case ctx_ClassPrivate:
385 case ctx_ClassProtected:
386 NewLine().NoIndent() << "public:";
387 NewLine();
388 break;
389 default: InvalidState("Public");
390 break;
391 }
392 new_context(ctx_ClassPublic);
393 return *this;
394 }
395
NewLine(bool end_this_line)396 CxxFile &CxxFile::NewLine(bool end_this_line)
397 { if (end_this_line) EndLine();
398 if (!empty_line) Output() << '\n';
399 indent_pending=true;
400 empty_line=true;
401 return *this;
402 }
403
Statement(void)404 CxxFile &CxxFile::Statement(void)
405 { ToOutside();
406 push(ctx_Statement);
407 return *this;
408 }
409
BlockStatement(void)410 CxxFile &CxxFile::BlockStatement(void)
411 { ToOutside();
412 push(ctx_BlockStatement);
413 return *this;
414 }
415
Definition(void)416 CxxFile &CxxFile::Definition(void)
417 { ToOutside();
418 // global must change later when we know
419 // whether this is a class or a function
420 push(ctx_Definition);
421 return *this;
422 }
423
Declaration(void)424 CxxFile &CxxFile::Declaration(void)
425 { // ToOutside();
426 GlobalContext(gc_declaration);
427 push(ctx_Declaration);
428 return *this;
429 }
430
Class(void)431 CxxFile &CxxFile::Class(void) // similar to Funct_ReturnType()
432 { // ToOutside();
433 switch (state.context)
434 { case ctx_Declaration: GlobalContext(gc_declaration,true);
435 break;
436 case ctx_Definition: GlobalContext(gc_class,true);
437 break;
438 case ctx_Outside:
439 std::cerr << "Class(): please specify either Definition()"
440 " or Declaration().\n"
441 "Assuming Definition()\n";
442 GlobalContext(gc_class,true);
443 break;
444 default: InvalidState("Class");
445 break;
446 }
447 Output() << "class ";
448 push(ctx_ClassName);
449 return *this;
450 }
451
operator <<(std::ostream & o,const CxxFile_Context & ctx)452 std::ostream &operator<<(std::ostream &o,const CxxFile_Context &ctx)
453 { switch (ctx)
454 #define Entry(x) case ctx_##x: return o << #x
455 { Entry(Outside);
456 Entry(ClassPrivate);
457 Entry(ClassPublic);
458 Entry(ClassProtected);
459 Entry(ReturnType);
460 Entry(FunctionName);
461 Entry(MidFunctionArgs);
462 Entry(MidConstructor);
463 Entry(MidConstruction);
464 Entry(DefineName);
465 Entry(DefineBody);
466 Entry(Declaration);
467 Entry(Definition);
468 Entry(Statement);
469 Entry(BlockStatement);
470 Entry(ShortComment);
471 Entry(Comment);
472 Entry(ClassName);
473 Entry(RValue);
474 Entry(RValueWritten);
475 Entry(Derivation);
476 Entry(CppIf);
477 Entry(CppIfDef);
478 default: return o << "Unknown state";
479 #undef Entry
480 }
481 }
482
operator <<(std::ostream & o,const CxxFile_Global_Context & ctx)483 std::ostream &operator<<(std::ostream &o,const CxxFile_Global_Context &ctx)
484 { switch (ctx)
485 #define Entry(x) case gc_##x: return o << #x
486 { Entry(start);
487 Entry(include); // includes
488 Entry(declaration); // declaration
489 Entry(statement); // statement
490 Entry(class); // class definition
491 Entry(function); // function definition
492 default: return o << "Unknown state";
493 #undef Entry
494 }
495 }
496
new_context(CxxFile_Context c)497 void CxxFile::new_context(CxxFile_Context c)
498 { if (state.context!=c)
499 {
500 #ifdef DEBUG_STATE_MACHINE
501 std::cout << state.context << "->" << c << " | " << state.global << '\n';
502 #endif
503 state.context=c;
504 }
505 }
506
push(CxxFile_Context c,int indent_delta)507 void CxxFile::push(CxxFile_Context c,int indent_delta)
508 { pushed.push_back(state);
509 #ifdef DEBUG_STATE_MACHINE
510 std::cout << '+' << state.context << "->" << c << " | " << state.global << '\n';
511 #endif
512 state.context=c;
513 state.indentation+=indent_delta;
514 }
515
pop()516 void CxxFile::pop()
517 { if (!pushed.size())
518 { std::cerr << "context stack underflow\n"; return; }
519 #ifdef DEBUG_STATE_MACHINE
520 std::cout << '-' << pushed.back().context << "<-" << state.context
521 << " | " << pushed.back().global << "<-" << state.global << '\n';
522 #endif
523 state=pushed.back();
524 pushed.pop_back();
525 }
526
dump_context_stack() const527 void CxxFile::dump_context_stack() const
528 { std::cerr << "context stack contents: top->";
529 for (t_statestack::const_reverse_iterator i=pushed.rbegin();
530 i!=pushed.rend();i++)
531 { std::cerr << i->context << " | " << i->global << ", ";
532 }
533 std::cerr << '\n';
534 }
535
Assignment()536 CxxFile &CxxFile::Assignment()
537 { switch (state.context)
538 { case ctx_Declaration:
539 case ctx_Statement:
540 break;
541
542 case ctx_Definition:
543 // ok, let's call it an declaration, it's both
544 new_context(ctx_Declaration);
545 break;
546
547 default: InvalidState("Assignment");
548 break;
549 }
550 push(ctx_RValue);
551 Output() << " = ";
552 return *this;
553 }
554
InvalidState(const std::string & s)555 void CxxFile::InvalidState(const std::string &s)
556 { std::cerr << s << ": Invalid preceding state '" << state.context << "'\n";
557 dump_context_stack();
558 abort();
559 }
560
DefineName(void)561 CxxFile &CxxFile::DefineName(void)
562 { EndLine();
563 // XXX: indentation, multiline, vertikal space above, below?
564 switch (state.context)
565 { // I don't know which states are legal
566 case ctx_Outside:
567 break;
568 default: std::cerr << "CxxFile::DefineName add "<< state.context << " to the valid states?\n";
569 break;
570 }
571 Output() << "#define ";
572 push(ctx_DefineName);
573 return *this;
574 }
575
DefineBody(void)576 CxxFile &CxxFile::DefineBody(void)
577 { if (state.context!=ctx_DefineName)
578 InvalidState("DefineBody");
579 Output() << ' ';
580 new_context(ctx_DefineBody);
581 return *this;
582 }
583
584 // XXX: these functions are definitely suboptimal
585
CppIf(void)586 CxxFile &CxxFile::CppIf(void)
587 { NewLine().NoIndent() << "#if ";
588 push(ctx_CppIf);
589 return *this;
590 }
591
CppElif(void)592 CxxFile &CxxFile::CppElif(void)
593 { NewLine().NoIndent() << "#elif ";
594 push(ctx_CppIf);
595 return *this;
596 }
597
EndIf(void)598 CxxFile &CxxFile::EndIf(void)
599 { NewLine().NoIndent() << "#endif //";
600 push(ctx_ShortComment);
601 return *this;
602 }
603
CppElse(void)604 CxxFile &CxxFile::CppElse(void)
605 { NewLine().NoIndent() << "#else //";
606 push(ctx_ShortComment);
607 return *this;
608 }
609
ShortComment(void)610 CxxFile &CxxFile::ShortComment(void)
611 { EndLine();
612 Output() << "// ";
613 push(ctx_ShortComment);
614 return *this;
615 }
616
Visibility()617 CxxFile_Context CxxFile::Visibility()
618 { ToOutside();
619 switch(state.context)
620 { case ctx_ClassPrivate:
621 case ctx_ClassProtected:
622 case ctx_ClassPublic:
623 return state.context;
624 default: assert(0);
625 return ctx_Outside; // to make gcc happy
626 }
627 }
628
Visibility(CxxFile_Context ctx)629 CxxFile &CxxFile::Visibility(CxxFile_Context ctx)
630 { switch(ctx)
631 { case ctx_ClassPublic: Public(); break;
632 case ctx_ClassProtected: Protected(); break;
633 case ctx_ClassPrivate: Private(); break;
634 default: assert(0);
635 }
636 return *this;
637 }
638
Namespace()639 CxxFile &CxxFile::Namespace()
640 { ToOutside();
641 push(ctx_Namespace);
642 return *this;
643 }
644
SomethingShiftingLeft()645 void CxxFile::SomethingShiftingLeft()
646 { if (state.context==ctx_RValue) state.context=ctx_RValueWritten;
647 }
648
Output()649 SystemFile &CxxFile::Output()
650 { if (indent_pending)
651 { Indent(state.indentation);
652 #ifdef DEBUG_STATE_MACHINE
653 std::cout << "pending indent(" << state.indentation << ")\n";
654 #endif
655 }
656 indent_pending=false;
657 empty_line=false;
658 return *this;
659 }
660
FunctionEndArgs()661 CxxFile &CxxFile::FunctionEndArgs()
662 { switch (state.context)
663 { case ctx_FunctionName: // no function args
664 Output() << "()";
665 pop();
666 break;
667 case ctx_MidFunctionArgs: // end function
668 Output() << ')';
669 pop();
670 break;
671 default: InvalidState("FunctionEndArgs"); break;
672 }
673 return *this;
674 }
675
676 UniqueValue CxxFile::element_types;
677 const UniqueValue::value_t CxxFile::v_IncludeBracket=element_types.get();
678 const UniqueValue::value_t CxxFile::v_IncludeQuote=element_types.get();
679
680