// $Id: CxxFile.cc,v 1.29 2004/05/07 06:53:14 christof Exp $ /* glade--: C++ frontend for glade (Gtk+ User Interface Builder) * Copyright (C) 1998 Christof Petig * Copyright (C) 1999-2000 Adolf Petig GmbH & Co. KG, written by Christof Petig * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ //#define DEBUG_STATE_MACHINE //#define DEBUG(x) std::cout << x << '\n' #include "CxxFile.hh" #include #include #include #include CxxFile &CxxFile::GlobalContext(Global_Context new_gc,bool stay_inside) // insert blank line if necessary { if (!stay_inside) ToOutside(); #ifdef DEBUG_STATE_MACHINE std::cout << "global " << state.global << "->" << new_gc << '\n'; #endif if (new_gc!=state.global) { if (state.global!=gc_start) VSpace(); } else if (new_gc==gc_function || new_gc==gc_class) VSpace(); state.global=new_gc; return *this; } void CxxFile::close(void) { EndLine(); // necessary?? ToOutside(); SystemFile::close(); elements.clear(); } CxxFile &CxxFile::ToOutside() { switch(state.context) { case ctx_Outside: case ctx_ClassPrivate: case ctx_ClassPublic: case ctx_ClassProtected: break; case ctx_Declaration: case ctx_Statement: case ctx_BlockStatement: case ctx_FunctionName: // no function args case ctx_MidFunctionArgs: // end function case ctx_RValueWritten: case ctx_CppIf: case ctx_ShortComment: case ctx_Namespace: case ctx_ClassName: EndLine(); break; default: InvalidState("ToOutside"); break; } return *this; } CxxFile &CxxFile::EndLine(bool endBlock) { int iterations=10; reiterate: #ifdef DEBUG_STATE_MACHINE std::cout << "EndLine(" << endBlock << ") at state " << state.context << " | " << state.global << '\n'; #endif if (!--iterations) { std::cerr << "endless loop in EndLine(), state '" << state.context << "'\n"; return *this; } switch(state.context) { case ctx_Outside: case ctx_ClassPrivate: case ctx_ClassProtected: case ctx_ClassPublic: break; case ctx_MidCtorArgs: Output() << ')'; new_context(ctx_MidConstructor); goto reiterate; case ctx_DefineBody: case ctx_DefineName: case ctx_MidConstructor: case ctx_ShortComment: case ctx_CppIf: NewLine(false); pop(); goto reiterate; case ctx_Derivation: assert(state.global==gc_class); NewLine(false); break; case ctx_ClassName: if (state.global==gc_declaration) { pop(); goto reiterate; } else NewLine(false); break; case ctx_Definition: if (!endBlock) // NewLine before Block { NewLine(false); } else // Block ended { switch (state.global) { case gc_class: Output() << ';'; // second newline after class definition // better via global_context break; case gc_function: break; case gc_declaration: // should be an enum Output() << ';'; break; default: std::cerr << "illegal state.global " << state.global << '\n'; assert(0); break; } NewLine(false); Global_Context help=state.global; pop(); // End Of Definition ! state.global=help; // promote global up, since we didn't know then } break; case ctx_RValue: pop(); goto reiterate; case ctx_RValueWritten: Output() << ';'; NewLine(false); // never second ';' needed ?? do { pop(); } while (state.context==ctx_Statement || state.context==ctx_Declaration); goto reiterate; case ctx_Statement: Output() << ';'; // fall through case ctx_BlockStatement: // e.g. else { } NewLine(false); pop(); state.global=gc_statement; goto reiterate; case ctx_Namespace: NewLine(false); pop(); state.global=gc_declaration; goto reiterate; case ctx_Declaration: Output() << ';'; NewLine(false); pop(); state.global=gc_declaration; goto reiterate; case ctx_FunctionName: // no function args Output() << "()"; pop(); goto reiterate; case ctx_MidFunctionArgs: // end function Output() << ')'; pop(); goto reiterate; default: InvalidState("EndLine"); break; } return *this; } CxxFile &CxxFile::Include(const std::string &name,bool local) { if (name.empty()) return *this; // suchen ob schon ? const UniqueValue::value_t type=local?v_IncludeQuote:v_IncludeBracket; if (ElementAlreadyThere(element_t(type,name))) return *this; // ToOutside(); GlobalContext(gc_include); // insert blank lines if necessary Output() << "#include " << (local?'"':'<') << name << (local?'"':'>'); NewLine(false); // FIXME: cpp indentation AddElement(element_t(type,name)); return *this; } CxxFile &CxxFile::FunctionArg(void) { switch(state.context) { case ctx_MidFunctionArgs: Output() << ", "; break; case ctx_FunctionName: Output() << '('; break; case ctx_MidConstructor: Output() << '('; new_context(ctx_MidCtorArgs); return *this; case ctx_MidCtorArgs: Output() << ", "; return *this; default: InvalidState("FunctionArg"); break; } new_context(ctx_MidFunctionArgs); return *this; } CxxFile &CxxFile::Constructor(void) { switch(state.context) { case ctx_MidConstructor: Output() << ", "; break; case ctx_FunctionName: Output() << "() : "; break; case ctx_MidFunctionArgs: Output() << ") : "; break; case ctx_MidCtorArgs: Output() << "), "; break; default: InvalidState("Constructor"); break; } new_context(ctx_MidConstructor); return *this; } CxxFile &CxxFile::Derivation(void) { switch(state.context) { case ctx_Derivation: Output() << ", "; break; case ctx_ClassName: Output() << " : "; break; default: InvalidState("Derivation"); break; } new_context(ctx_Derivation); return *this; } CxxFile &CxxFile::Funct_ReturnType(void) // similar to Class() { switch (state.context) { case ctx_Declaration: GlobalContext(gc_declaration,true); break; case ctx_Definition: GlobalContext(gc_function,true); break; case ctx_Outside: std::cerr << "Funct_ReturnType(): please specify either Definition()" " or Declaration().\n" "Assuming Definition()\n"; GlobalContext(gc_function,true); break; case ctx_ReturnType: // since Storage is an alias to ReturnType this is legal Output() << ' '; return *this; default: InvalidState("ReturnType"); break; } push(ctx_ReturnType); return *this; } CxxFile &CxxFile::FunctionName(void) { switch(state.context) { case ctx_ReturnType: Output() << ' '; break; case ctx_RValue: // assignment = statement case ctx_RValueWritten: break; case ctx_Outside: // function call GlobalContext(gc_statement); break; case ctx_Declaration: case ctx_Definition: // constructor/destructor have no return type GlobalContext(gc_function,true); push(ctx_FunctionName); break; case ctx_MidFunctionArgs: push(ctx_FunctionName); break; default: InvalidState("FunctionName"); break; } new_context(ctx_FunctionName); return *this; } CxxFile &CxxFile::StartBlock(void) { int right=3; if (state.context!=ctx_RValue) EndLine(); else NewLine(false); // this is meant for "int b[]=\n{ 1,2 };" Context oldcontext=state.context; Output() << '{'; switch(oldcontext) { case ctx_Definition: push(ctx_Outside,right=3); break; case ctx_ClassName: // switch state, do not push it ... THINK AGAIN case ctx_Derivation: state.indentation+=8; state.context=ctx_ClassPrivate; // push(ctx_ClassPrivate,right=8); break; case ctx_Outside: push(ctx_Outside,right=3); break; case ctx_RValue: push(ctx_Outside,right=8); break; default: InvalidState("StartBlock"); break; } Indent(right-1); indent_pending=false; return *this; } CxxFile &CxxFile::EndBlock(void) { EndLine(); pop(); NewLine(false) << '}'; EndLine(true); NewLine(); return *this; } CxxFile &CxxFile::EndType(void) { return EndBlock(); } CxxFile &CxxFile::Private(void) { ToOutside(); switch(state.context) { case ctx_ClassPrivate: break; case ctx_ClassProtected: case ctx_ClassPublic: NewLine().NoIndent() << "private:"; NewLine(); break; default: InvalidState("Private"); break; } new_context(ctx_ClassPrivate); return *this; } CxxFile &CxxFile::Protected(void) { ToOutside(); switch(state.context) { case ctx_ClassProtected: break; case ctx_ClassPrivate: case ctx_ClassPublic: NewLine().NoIndent() << "protected:"; NewLine(); break; default: InvalidState("Protected"); break; } new_context(ctx_ClassProtected); return *this; } CxxFile &CxxFile::Public(void) { ToOutside(); switch(state.context) { case ctx_ClassPublic: break; case ctx_ClassPrivate: case ctx_ClassProtected: NewLine().NoIndent() << "public:"; NewLine(); break; default: InvalidState("Public"); break; } new_context(ctx_ClassPublic); return *this; } CxxFile &CxxFile::NewLine(bool end_this_line) { if (end_this_line) EndLine(); if (!empty_line) Output() << '\n'; indent_pending=true; empty_line=true; return *this; } CxxFile &CxxFile::Statement(void) { ToOutside(); push(ctx_Statement); return *this; } CxxFile &CxxFile::BlockStatement(void) { ToOutside(); push(ctx_BlockStatement); return *this; } CxxFile &CxxFile::Definition(void) { ToOutside(); // global must change later when we know // whether this is a class or a function push(ctx_Definition); return *this; } CxxFile &CxxFile::Declaration(void) { // ToOutside(); GlobalContext(gc_declaration); push(ctx_Declaration); return *this; } CxxFile &CxxFile::Class(void) // similar to Funct_ReturnType() { // ToOutside(); switch (state.context) { case ctx_Declaration: GlobalContext(gc_declaration,true); break; case ctx_Definition: GlobalContext(gc_class,true); break; case ctx_Outside: std::cerr << "Class(): please specify either Definition()" " or Declaration().\n" "Assuming Definition()\n"; GlobalContext(gc_class,true); break; default: InvalidState("Class"); break; } Output() << "class "; push(ctx_ClassName); return *this; } std::ostream &operator<<(std::ostream &o,const CxxFile_Context &ctx) { switch (ctx) #define Entry(x) case ctx_##x: return o << #x { Entry(Outside); Entry(ClassPrivate); Entry(ClassPublic); Entry(ClassProtected); Entry(ReturnType); Entry(FunctionName); Entry(MidFunctionArgs); Entry(MidConstructor); Entry(MidConstruction); Entry(DefineName); Entry(DefineBody); Entry(Declaration); Entry(Definition); Entry(Statement); Entry(BlockStatement); Entry(ShortComment); Entry(Comment); Entry(ClassName); Entry(RValue); Entry(RValueWritten); Entry(Derivation); Entry(CppIf); Entry(CppIfDef); default: return o << "Unknown state"; #undef Entry } } std::ostream &operator<<(std::ostream &o,const CxxFile_Global_Context &ctx) { switch (ctx) #define Entry(x) case gc_##x: return o << #x { Entry(start); Entry(include); // includes Entry(declaration); // declaration Entry(statement); // statement Entry(class); // class definition Entry(function); // function definition default: return o << "Unknown state"; #undef Entry } } void CxxFile::new_context(CxxFile_Context c) { if (state.context!=c) { #ifdef DEBUG_STATE_MACHINE std::cout << state.context << "->" << c << " | " << state.global << '\n'; #endif state.context=c; } } void CxxFile::push(CxxFile_Context c,int indent_delta) { pushed.push_back(state); #ifdef DEBUG_STATE_MACHINE std::cout << '+' << state.context << "->" << c << " | " << state.global << '\n'; #endif state.context=c; state.indentation+=indent_delta; } void CxxFile::pop() { if (!pushed.size()) { std::cerr << "context stack underflow\n"; return; } #ifdef DEBUG_STATE_MACHINE std::cout << '-' << pushed.back().context << "<-" << state.context << " | " << pushed.back().global << "<-" << state.global << '\n'; #endif state=pushed.back(); pushed.pop_back(); } void CxxFile::dump_context_stack() const { std::cerr << "context stack contents: top->"; for (t_statestack::const_reverse_iterator i=pushed.rbegin(); i!=pushed.rend();i++) { std::cerr << i->context << " | " << i->global << ", "; } std::cerr << '\n'; } CxxFile &CxxFile::Assignment() { switch (state.context) { case ctx_Declaration: case ctx_Statement: break; case ctx_Definition: // ok, let's call it an declaration, it's both new_context(ctx_Declaration); break; default: InvalidState("Assignment"); break; } push(ctx_RValue); Output() << " = "; return *this; } void CxxFile::InvalidState(const std::string &s) { std::cerr << s << ": Invalid preceding state '" << state.context << "'\n"; dump_context_stack(); abort(); } CxxFile &CxxFile::DefineName(void) { EndLine(); // XXX: indentation, multiline, vertikal space above, below? switch (state.context) { // I don't know which states are legal case ctx_Outside: break; default: std::cerr << "CxxFile::DefineName add "<< state.context << " to the valid states?\n"; break; } Output() << "#define "; push(ctx_DefineName); return *this; } CxxFile &CxxFile::DefineBody(void) { if (state.context!=ctx_DefineName) InvalidState("DefineBody"); Output() << ' '; new_context(ctx_DefineBody); return *this; } // XXX: these functions are definitely suboptimal CxxFile &CxxFile::CppIf(void) { NewLine().NoIndent() << "#if "; push(ctx_CppIf); return *this; } CxxFile &CxxFile::CppElif(void) { NewLine().NoIndent() << "#elif "; push(ctx_CppIf); return *this; } CxxFile &CxxFile::EndIf(void) { NewLine().NoIndent() << "#endif //"; push(ctx_ShortComment); return *this; } CxxFile &CxxFile::CppElse(void) { NewLine().NoIndent() << "#else //"; push(ctx_ShortComment); return *this; } CxxFile &CxxFile::ShortComment(void) { EndLine(); Output() << "// "; push(ctx_ShortComment); return *this; } CxxFile_Context CxxFile::Visibility() { ToOutside(); switch(state.context) { case ctx_ClassPrivate: case ctx_ClassProtected: case ctx_ClassPublic: return state.context; default: assert(0); return ctx_Outside; // to make gcc happy } } CxxFile &CxxFile::Visibility(CxxFile_Context ctx) { switch(ctx) { case ctx_ClassPublic: Public(); break; case ctx_ClassProtected: Protected(); break; case ctx_ClassPrivate: Private(); break; default: assert(0); } return *this; } CxxFile &CxxFile::Namespace() { ToOutside(); push(ctx_Namespace); return *this; } void CxxFile::SomethingShiftingLeft() { if (state.context==ctx_RValue) state.context=ctx_RValueWritten; } SystemFile &CxxFile::Output() { if (indent_pending) { Indent(state.indentation); #ifdef DEBUG_STATE_MACHINE std::cout << "pending indent(" << state.indentation << ")\n"; #endif } indent_pending=false; empty_line=false; return *this; } CxxFile &CxxFile::FunctionEndArgs() { switch (state.context) { case ctx_FunctionName: // no function args Output() << "()"; pop(); break; case ctx_MidFunctionArgs: // end function Output() << ')'; pop(); break; default: InvalidState("FunctionEndArgs"); break; } return *this; } UniqueValue CxxFile::element_types; const UniqueValue::value_t CxxFile::v_IncludeBracket=element_types.get(); const UniqueValue::value_t CxxFile::v_IncludeQuote=element_types.get();