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