// astgen.cc see license.txt for copyright and terms of use // program to generate C++ code from an AST specification #include "agrampar.h" // readAbstractGrammar #include "test.h" // ARGS_MAIN #include "trace.h" // TRACE_ARGS #include "owner.h" // Owner #include "ckheap.h" // checkHeap #include "strutil.h" // replace, translate, localTimeString #include "sobjlist.h" // SObjList #include "stringset.h" // StringSet #include "srcloc.h" // SourceLocManager #include "strtokp.h" // StrtokParse #include "exc.h" // xfatal #include "strdict.h" // StringDict #include // strncmp #include // ofstream #include // isalnum // propertly a member of ListClass below, but I don't like nested // things enum ListKind { LK_NONE, LK_ASTList, LK_FakeList, }; // a product type of the information relevant to a list member of a // class; used to construct the traverse calls and visitors to the // fictional list classes struct ListClass { ListKind lkind; string classAndMemberName; string elementClassName; explicit ListClass(ListKind lkind0, rostring classAndMemberName0, rostring elementClassName0) : lkind(lkind0) , classAndMemberName(classAndMemberName0) , elementClassName(elementClassName0) {} char const * kindName() const; }; char const * ListClass::kindName() const { if (lkind == LK_ASTList) return "ASTList"; else if (lkind == LK_FakeList) return "FakeList"; else xfailure("illegal ListKind"); } // this is the name of the visitor interface class, or "" // if the user does not want a visitor string visitorName; inline bool wantVisitor() { return visitorName.length() != 0; } // this is the name of the delegator-visitor, or "" // if the user does not want a delegator-visitor string dvisitorName; inline bool wantDVisitor() { return dvisitorName.length() != 0; } // this is the name of the xml-visitor, or "" // if the user does not want a delegator-visitor string xmlVisitorName; inline bool wantXmlVisitor() { return xmlVisitorName.length() != 0; } // this is the prefix of the filenames rendered for xml lexing and // parsing string xmlParserName; inline bool wantXmlParser() { return xmlParserName.length() != 0; } // similar for the modification visitor ("mvisitor") string mvisitorName; inline bool wantMVisitor() { return mvisitorName.length() != 0; } // entire input ASTSpecFile *wholeAST = NULL; // list of all TF_classes in the input, useful for certain // applications which don't care about other forms SObjList allClasses; // list of all ASTList "list classes" StringSet listClassesSet; ASTList listClasses; // true if the user wants the xmlPrint stuff bool wantXMLPrint = false; // true if the user wants the gdb() functions bool wantGDB = false; // true if we should use a hack to work around the absence of // support for covariant return types in MSVC; see // http://support.microsoft.com/kb/240862/EN-US/ // The approach is to use // virtual Super *nocvr_clone() const; // Sub *clone() const { return static_cast(nocvr_clone()); } // in place of // virtual Sub *clone() const; bool nocvr = false; // ------------------ shared gen functions ---------------------- enum TreeNodeKind { TKN_NONE, TKN_SUPERCLASS, TKN_SUBCLASS }; class Gen { protected: // data string srcFname; // name of source file ObjList const &modules; // extension modules string destFname; // name of output file ofstream out; // output stream ASTSpecFile const &file; // AST specification public: // funcs Gen(rostring srcFname, ObjList const &modules, rostring destFname, ASTSpecFile const &file); ~Gen(); // shared output sequences void headerComments(); void doNotEdit(); void emitFiltered(ASTList const &decls, AccessCtl mode, rostring indent); }; Gen::Gen(rostring srcfn, ObjList const &mods, rostring destfn, ASTSpecFile const &f) : srcFname(srcfn), modules(mods), destFname(destfn), out(toCStr(destfn)), file(f) { if (!out) { throw_XOpen(destfn); } } Gen::~Gen() {} // type queries TreeNodeKind getTreeNodeKind(rostring type); TreeNodeKind getTreeNodePtrKind(rostring type); bool isTreeNode(rostring type) { return getTreeNodeKind(type) != TKN_NONE; } bool isTreeNodePtr(rostring type) { return getTreeNodePtrKind(type) != TKN_NONE; } bool isSuperclassTreeNode(rostring type) { return getTreeNodeKind(type) == TKN_SUPERCLASS; } bool isSuperclassTreeNodePtr(rostring type) { return getTreeNodePtrKind(type) == TKN_SUPERCLASS; } bool isSubclassTreeNode(rostring type) { return getTreeNodeKind(type) == TKN_SUBCLASS; } bool isSubclassTreeNodePtr(rostring type) { return getTreeNodePtrKind(type) == TKN_SUBCLASS; } string extractNodeType(rostring type); string getSuperTypeOf(rostring sub); bool isListType(rostring type); bool isFakeListType(rostring type); bool isTreeListType(rostring type); string extractListType(rostring type); // dsw: I just need to know if the thing is an object or not bool isPtrKind(rostring type) { return type[strlen(type)-1] == '*'; } TreeNodeKind getTreeNodePtrKind(rostring type) { if (isPtrKind(type)) { // is pointer type; get base type; dsw: FIX: I think this is // wrong; you might want to consider the same replacement in other // places where trimWhitespace() is used. // string base = trimWhitespace(substring(type, strlen(type)-1)); string base = firstAlphanumToken(substring(type, strlen(type)-1)); return getTreeNodeKind(base); } // not a pointer type return TKN_NONE; } TreeNodeKind getTreeNodeKind(rostring base) { // search among defined classes for this name SFOREACH_OBJLIST(TF_class, allClasses, iter) { TF_class const *c = iter.data(); if (c->super->name.equals(base)) { // found it in a superclass return TKN_SUPERCLASS; } // check the subclasses FOREACH_ASTLIST(ASTClass, c->ctors, ctor) { if (ctor.data()->name.equals(base)) { // found it in a subclass return TKN_SUBCLASS; } } } return TKN_NONE; } string getSuperTypeOf(rostring sub) { SFOREACH_OBJLIST(TF_class, allClasses, iter) { TF_class const *c = iter.data(); // look among the subclasses FOREACH_ASTLIST(ASTClass, c->ctors, ctor) { if (ctor.data()->name.equals(sub)) { // found it return c->super->name; } } } xfailure("getSuperTypeOf: invalid subclass name"); return ""; // silence warning } // get just the first alphanumeric word string extractNodeType(rostring type) { char const *end = toCStr(type); while (isalnum(*end) || *end=='_') { end++; } return substring(type, end-toCStr(type)); } // is this type a use of my ASTList template? bool isListType(rostring type) { // do a fairly coarse analysis.. (the space before "<" is // there because the type string is actually parsed by the // grammar, and as it assembles it back into a string it // inserts a space after every name-like token) return prefixEquals(type, "ASTList <"); } // similar for FakeList bool isFakeListType(rostring type) { return prefixEquals(type, "FakeList <"); } // is it a list type, with the elements being tree nodes? bool isTreeListType(rostring type) { return isListType(type) && isTreeNode(extractListType(type)); } // given a type for which 'is[Fake]ListType' returns true, extract // the type in the template argument angle brackets; this is used // to get the name of the type so we can pass it to the macros // which print list contents string extractListType(rostring type) { xassert(isListType(type) || isFakeListType(type)); char const *langle = strchr(toCStr(type), '<'); char const *rangle = strchr(toCStr(type), '>'); xassert(langle && rangle); return trimWhitespace(substring(langle+1, rangle-langle-1)); } // given a string that comes from a user's declaration of a field // of a class, extract the type and the name; this assumes that, // syntactically, they separate cleanly (without the name in the // middle of the type syntax) void parseFieldDecl(string &type, string &name, rostring decl) { // it's not trivial to extract the name of the field from // its declaration.. so let's use a simple heuristic: it's // probably the last sequence of non-whitespace alphanum chars StrtokParse tok(decl, " \t*()[]<>,"); // now, find the offset of the start of the last token int ofs = tok.offset(tok.tokc()-1); // extract the parts type = trimWhitespace(substring(decl, ofs)); name = trimWhitespace(toCStr(decl)+ofs); } string extractFieldType(rostring decl) { string t, n; parseFieldDecl(t, n, decl); return t; } string extractFieldName(rostring decl) { string t, n; parseFieldDecl(t, n, decl); return n; } // I scatter this throughout the generated code as pervasive // reminders that these files shouldn't be edited -- I often // accidentally edit them and then have to backtrack and reapply // the changes to where they were supposed to go .. void Gen::doNotEdit() { out << "// *** DO NOT EDIT ***\n"; } void Gen::headerComments() { out << "// " << sm_basename(destFname) << "\n"; doNotEdit(); out << "// generated automatically by astgen, from " << sm_basename(srcFname) << "\n"; if (modules.count()) { out << "// active extension modules:"; FOREACH_OBJLIST(string, modules, iter) { out << " " << *(iter.data()); } out << "\n"; } // the inclusion of the date introduces gratuitous changes when the // tool is re-run for whatever reason //out << "// on " << localTimeString() << "\n"; out << "\n"; } void Gen::emitFiltered(ASTList const &decls, AccessCtl mode, rostring indent) { FOREACH_ASTLIST(Annotation, decls, iter) { if (iter.data()->kind() == Annotation::USERDECL) { UserDecl const &decl = *( iter.data()->asUserDeclC() ); if (decl.access() == mode) { out << indent << decl.code << ";\n"; } } } } // ------------------ generation of the header ----------------------- // translation-wide state class HGen : public Gen { private: // funcs void emitVerbatim(TF_verbatim const &v); void emitTFClass(TF_class const &cls); void emitBaseClassDecls(ASTClass const &cls, int ct); static char const *virtualIfChildren(TF_class const &cls); void emitCtorFields(ASTList const &args, ASTList const &lastArgs); void innerEmitCtorFields(ASTList const &args); void emitCtorFormal(int &ct, CtorArg const *arg); void emitCtorFormals(int &ct, ASTList const &args); void emitCtorDefn(ASTClass const &cls, ASTClass const *parent); void passParentCtorArgs(int &ct, ASTList const &args); void initializeMyCtorArgs(int &ct, ASTList const &args); void emitCommonFuncs(rostring virt); void emitUserDecls(ASTList const &decls); void emitCtor(ASTClass const &ctor, ASTClass const &parent); void emitVisitorInterfacePrelude(rostring visitorName); void emitVisitorInterface(); void emitDVisitorInterface(); void emitXmlVisitorInterface(); void emitMVisitorInterface(); public: // funcs HGen(rostring srcFname, ObjList const &modules, rostring destFname, ASTSpecFile const &file) : Gen(srcFname, modules, destFname, file) {} void emitFile(); }; // emit header code for an entire AST spec file void HGen::emitFile() { string includeLatch = translate(sm_basename(destFname), "a-z.", "A-Z_"); headerComments(); out << "#ifndef " << includeLatch << "\n"; out << "#define " << includeLatch << "\n"; out << "\n"; out << "#include \"asthelp.h\" // helpers for generated code\n"; if (wantDVisitor()) { out << "#include \"sobjset.h\" // SObjSet\n"; } out << "\n"; // forward-declare all the classes out << "// fwd decls\n"; FOREACH_ASTLIST(ToplevelForm, file.forms, form) { TF_class const *c = form.data()->ifTF_classC(); if (c) { out << "class " << c->super->name << ";\n"; FOREACH_ASTLIST(ASTClass, c->ctors, ctor) { out << "class " << ctor.data()->name << ";\n"; } } } out << "\n\n"; if (wantVisitor()) { out << "// visitor interface class\n" << "class " << visitorName << ";\n\n"; } if (wantDVisitor()) { // dsw: this seems necessary and here seems as good a place as any // to do it if (!wantVisitor()) { xfatal("If you specify the 'dvisitor' option, you must also specify the 'visitor' option"); } out << "// delegator-visitor interface class\n" << "class " << dvisitorName << ";\n\n"; } if (wantXmlVisitor()) { // dsw: this seems necessary and here seems as good a place as any // to do it if (!wantVisitor()) { xfatal("If you specify the 'xmlVisitor' option, you must also specify the 'visitor' option"); } out << "// xml-visitor interface class\n" << "class " << xmlVisitorName << ";\n\n"; } if (wantMVisitor()) { out << "class " << mvisitorName << ";\n\n"; } // do all the enums first; this became necessary when I had an // enum in an extension, since the use of the enum ended up // before the enum itself, due to the use being in a class that // was defined in the base module { FOREACH_ASTLIST(ToplevelForm, file.forms, form) { if (form.data()->isTF_enum()) { TF_enum const *e = form.data()->asTF_enumC(); out << "enum " << e->name << " {\n"; FOREACH_ASTLIST(string, e->enumerators, iter) { out << " " << *(iter.data()) << ",\n"; } out << "};\n" << "\n" << "char const *toString(" << e->name << ");\n" << "\n" << "\n" ; } } } // process each (non-enum) directive FOREACH_ASTLIST(ToplevelForm, file.forms, form) { switch (form.data()->kind()) { case ToplevelForm::TF_VERBATIM: emitVerbatim(*( form.data()->asTF_verbatimC() )); break; case ToplevelForm::TF_IMPL_VERBATIM: // nop break; case ToplevelForm::TF_CLASS: emitTFClass(*( form.data()->asTF_classC() )); break; default: // ignore other toplevel forms (e.g. TF_option) break; } out << "\n"; } if (wantVisitor()) { emitVisitorInterface(); } if (wantDVisitor()) { emitDVisitorInterface(); } if (wantXmlVisitor()) { emitXmlVisitorInterface(); } if (wantMVisitor()) { emitMVisitorInterface(); } out << "#endif // " << includeLatch << "\n"; } // emit a verbatim section void HGen::emitVerbatim(TF_verbatim const &v) { doNotEdit(); out << v.code; } STATICDEF char const *HGen::virtualIfChildren(TF_class const &cls) { if (cls.hasChildren()) { // since this class has children, make certain functions virtual return "virtual "; } else { // no children, no need to introduce a vtable return ""; } } // emit declaration for a class ("phylum") void HGen::emitTFClass(TF_class const &cls) { doNotEdit(); out << "class " << cls.super->name; emitBaseClassDecls(*(cls.super), 0 /*ct*/); out << " {\n"; // the xml visitor needs to be able to see the internals of the // classes if (wantXmlVisitor()) { out << " friend class " << xmlVisitorName << ";\n"; out << " friend class ASTXmlReader;\n"; } emitCtorFields(cls.super->args, cls.super->lastArgs); emitCtorDefn(*(cls.super), NULL /*parent*/); // destructor char const *virt = virtualIfChildren(cls); out << " " << virt << "~" << cls.super->name << "();\n"; out << "\n"; // declare the child kind selector if (cls.hasChildren()) { out << " enum Kind { "; FOREACH_ASTLIST(ASTClass, cls.ctors, ctor) { out << ctor.data()->classKindName() << ", "; } out << "NUM_KINDS };\n"; out << " virtual Kind kind() const = 0;\n"; out << "\n"; out << " static char const * const kindNames[NUM_KINDS];\n"; out << " char const *kindName() const { return kindNames[kind()]; }\n"; out << "\n"; } else { // even if the phylum doesn't have children, it's nice to be able // to ask an AST node for its name (e.g. in templatized code that // deals with arbitrary AST node types), so define the // 'kindName()' method anyway out << " char const *kindName() const { return \"" << cls.super->name << "\"; }\n"; } // declare checked downcast functions { FOREACH_ASTLIST(ASTClass, cls.ctors, ctor) { // declare the const downcast ASTClass const &c = *(ctor.data()); out << " DECL_AST_DOWNCASTS(" << c.name << ", " << c.classKindName() << ")\n"; } out << "\n"; } // declare clone function if (cls.hasChildren()) { if (!nocvr) { // normal case out << " virtual " << cls.super->name << "* clone() const=0;\n"; } else { // msvc hack case out << " virtual " << cls.super->name << "* nocvr_clone() const=0;\n"; out << " " << cls.super->name << "* clone() const { return nocvr_clone(); }\n"; } } else { // not pure or virtual out << " " << cls.super->name << " *clone() const;\n"; } out << "\n"; emitCommonFuncs(virt); if (wantGDB) { out << " void gdb() const;\n\n"; } emitUserDecls(cls.super->decls); // close the declaration of the parent class out << "};\n"; out << "\n"; // print declarations for all child classes { FOREACH_ASTLIST(ASTClass, cls.ctors, ctor) { emitCtor(*(ctor.data()), *(cls.super)); } } out << "\n"; } void HGen::emitBaseClassDecls(ASTClass const &cls, int ct) { FOREACH_ASTLIST(BaseClass, cls.bases, iter) { out << ((ct++ > 0)? ", " : " : ") << toString(iter.data()->access) << " " << iter.data()->name ; } } // emit data fields implied by the constructor void HGen::emitCtorFields(ASTList const &args, ASTList const &lastArgs) { out << "public: // data\n"; innerEmitCtorFields(args); innerEmitCtorFields(lastArgs); out << "\n"; } void HGen::innerEmitCtorFields(ASTList const &args) { // go over the arguments in the ctor and declare fields for them { FOREACH_ASTLIST(CtorArg, args, arg) { char const *star = ""; if (isTreeNode(arg.data()->type)) { // make it a pointer in the concrete representation star = "*"; } out << " " << arg.data()->type << " " << star << arg.data()->name << ";\n"; } } } // emit the declaration of a formal argument to a constructor void HGen::emitCtorFormal(int &ct, CtorArg const *arg) { // put commas between formals if (ct++ > 0) { out << ", "; } string const &type = arg->type; out << type << " "; if (isListType(type) || isTreeNode(type) || type.equals("LocString")) { // lists and subtrees and LocStrings are constructed by passing pointers trace("putStar") << "putting star for " << type << endl; out << "*"; } else { trace("putStar") << "NOT putting star for " << type << endl; } out << "_" << arg->name; // prepend underscore to param's name // emit default value, if any if (arg->defaultValue.length() > 0) { out << " = " << arg->defaultValue; } } void HGen::emitCtorFormals(int &ct, ASTList const &args) { FOREACH_ASTLIST(CtorArg, args, arg) { emitCtorFormal(ct, arg.data()); } } // true if 'ud' seems to declare a function, as opposed to data bool isFuncDecl(UserDecl const *ud) { return ud->amod->hasMod("virtual") || ud->amod->hasMod("func"); } // emit the definition of the constructor itself void HGen::emitCtorDefn(ASTClass const &cls, ASTClass const *parent) { // declare the constructor { out << "public: // funcs\n"; out << " " << cls.name << "("; // list of formal parameters to the constructor { int ct = 0; if (parent) { emitCtorFormals(ct, parent->args); } emitCtorFormals(ct, cls.args); emitCtorFormals(ct, cls.lastArgs); if (parent) { emitCtorFormals(ct, parent->lastArgs); } } out << ")"; // pass args to superclass, and initialize fields { int ct = 0; if (parent) { out << " : " << parent->name << "("; passParentCtorArgs(ct, parent->args); passParentCtorArgs(ct, parent->lastArgs); ct++; // make sure we print a comma, below out << ")"; } initializeMyCtorArgs(ct, cls.args); initializeMyCtorArgs(ct, cls.lastArgs); // initialize fields that have initializers FOREACH_ASTLIST(Annotation, cls.decls, ann) { if (!ann.data()->isUserDecl()) continue; UserDecl const *ud = ann.data()->asUserDeclC(); if (ud->init.length() == 0) continue; if (isFuncDecl(ud)) continue; // don't do this for functions! if (ct++ > 0) { out << ", "; } else { out << " : "; } out << extractFieldName(ud->code) << "(" << ud->init << ")"; } } // insert user's ctor code out << " {\n"; emitFiltered(cls.decls, AC_CTOR, " "); out << " }\n"; } } void HGen::passParentCtorArgs(int &ct, ASTList const &args) { FOREACH_ASTLIST(CtorArg, args, parg) { if (ct++ > 0) { out << ", "; } // pass the formal arg to the parent ctor out << "_" << parg.data()->name; } } void HGen::initializeMyCtorArgs(int &ct, ASTList const &args) { FOREACH_ASTLIST(CtorArg, args, arg) { if (ct++ > 0) { out << ", "; } else { out << " : "; // only used when 'parent' is NULL } // initialize the field with the formal argument out << arg.data()->name << "(_" << arg.data()->name << ")"; } } // emit functions that are declared in every tree node void HGen::emitCommonFuncs(rostring virt) { // declare the functions they all have out << " " << virt << "void debugPrint(ostream &os, int indent, char const *subtreeName = \"tree\") const;\n"; if (wantXMLPrint) { out << " " << virt << "void xmlPrint(ostream &os, int indent) const;\n"; } if (wantVisitor()) { // visitor traversal entry point out << " " << virt << "void traverse(" << visitorName << " &vis);\n"; } out << "\n"; } // emit user-supplied declarations void HGen::emitUserDecls(ASTList const &decls) { FOREACH_ASTLIST(Annotation, decls, iter) { // in the header, we only look at userdecl annotations if (iter.data()->kind() == Annotation::USERDECL) { UserDecl const &decl = *( iter.data()->asUserDeclC() ); if (decl.access() == AC_PUBLIC || decl.access() == AC_PRIVATE || decl.access() == AC_PROTECTED) { out << " " << toString(decl.access()) << ": "; if (decl.amod->hasMod("virtual")) { out << "virtual "; } out << decl.code; if (isFuncDecl(&decl) && decl.init.length() > 0) { out << " = " << decl.init; // the "=0" of a pure virtual function } out << ";"; // emit field flags as comments, to help debug astgen if (decl.amod->mods.count()) { out << " //"; FOREACH_ASTLIST(string, decl.amod->mods, mod) { out << " " << *(mod.data()); } } out << "\n"; } if (decl.access() == AC_PUREVIRT) { // this is the parent class: declare it pure virtual out << " public: virtual " << decl.code << "=0;\n"; } } } } // emit declaration for a specific class instance constructor void HGen::emitCtor(ASTClass const &ctor, ASTClass const &parent) { out << "class " << ctor.name << " : public " << parent.name; emitBaseClassDecls(ctor, 1 /*ct*/); out << " {\n"; emitCtorFields(ctor.args, ctor.lastArgs); emitCtorDefn(ctor, &parent); // destructor out << " virtual ~" << ctor.name << "();\n"; out << "\n"; // type tag out << " virtual Kind kind() const { return " << ctor.classKindName() << "; }\n"; out << " enum { TYPE_TAG = " << ctor.classKindName() << " };\n"; out << "\n"; // common functions emitCommonFuncs("virtual "); // clone function (take advantage of covariant return types) if (!nocvr) { // normal case out << " virtual " << ctor.name << " *clone() const;\n"; } else { // msvc hack case out << " virtual " << parent.name << "* nocvr_clone() const;\n"; out << " " << ctor.name << "* clone() const\n" << " { return static_cast<" << ctor.name << "*>(nocvr_clone()); }\n"; } out << "\n"; emitUserDecls(ctor.decls); // emit implementation declarations for parent's pure virtuals FOREACH_ASTLIST(Annotation, parent.decls, iter) { UserDecl const *decl = iter.data()->ifUserDeclC(); if (!decl) continue; if (decl->access() == AC_PUREVIRT) { out << " public: virtual " << decl->code << ";\n"; } else if (decl->amod->hasMod("virtual")) { out << " " << toString(decl->access()) << ": virtual " << decl->code << ";\n"; } } // close the decl out << "};\n"; out << "\n"; } // --------------------- generation of C++ code file -------------------------- class CGen : public Gen { public: string hdrFname; // name of associated .h file public: CGen(rostring srcFname, ObjList const &modules, rostring destFname, ASTSpecFile const &file, rostring hdr) : Gen(srcFname, modules, destFname, file), hdrFname(hdr) {} void emitFile(); void emitTFClass(TF_class const &cls); void emitDestructor(ASTClass const &cls); void emitDestroyField(bool isOwner, rostring type, rostring name); void emitPrintCtorArgs(ASTList const &args); void emitPrintFields(ASTList const &decls); void emitPrintField(rostring print, bool isOwner, rostring type, rostring name); void emitXmlPrintCtorArgs(ASTList const &args); bool emitCustomCode(ASTList const &list, rostring tag); void emitCloneCtorArg(CtorArg const *arg, int &ct); void emitCloneCtorArgs(int &ct, ASTList const &args); void emitCloneCode(ASTClass const *super, ASTClass const *sub); void emitVisitorImplementation(); private: void emitDVisitorImplVisitedCheck(char const *name); public: void emitDVisitorImplementation(); private: void emitXmlCtorArgs(ASTList const &args, char const *baseName, string const &className); void emitXmlFields(ASTList const &decls, char const *baseName, string const &className); void emitXmlField(rostring type, rostring name, char const *baseName, string const &className, AccessMod *amod); void emitXmlVisitorImplVisitedCheck(char const *name); public: void emitXmlVisitorImplementation(); void emitTraverse(ASTClass const *c, ASTClass const * /*nullable*/ super, bool hasChildren); private: void emitOneTraverseCall(rostring className, string name, string type); public: void emitMVisitorImplementation(); void emitMTraverse(ASTClass const *c, rostring obj, rostring ident); void emitMTraverseCall(rostring i, rostring eltType, rostring argVar); }; // generation of xml parser class XmlParserGen { StringSet attributeNames; // names of attributes of AST nodes ofstream tokensOutH; ofstream tokensOutCC; ofstream lexerOut; ofstream parser0_decls; ofstream parser1_defs; ofstream parser2_ctorCalls; ofstream parser3_registerCalls; public: XmlParserGen(string &xmlParserName) : tokensOutH(stringc << xmlParserName << "_tokens1_mid.gen.h") , tokensOutCC(stringc << xmlParserName << "_lexer1_mid.gen.cc") , lexerOut(stringc << xmlParserName << "_lexer1_mid.gen.lex") , parser0_decls(stringc << xmlParserName << "_parse1_0decl.gen.cc") , parser1_defs (stringc << xmlParserName << "_parse1_1defn.gen.cc") , parser2_ctorCalls (stringc << xmlParserName << "_parse1_2ctrc.gen.cc") , parser3_registerCalls(stringc << xmlParserName << "_parse1_3regc.gen.cc") {} private: void collectXmlParserCtorArgs(ASTList const &args, char const *baseName); void collectXmlParserFields(ASTList const &decls, char const *baseName); void collectXmlParserField (rostring type, rostring name, char const *baseName, AccessMod *amod); void emitXmlCtorArgs_AttributeParseRule(ASTList const &args, string &baseName); void emitXmlFields_AttributeParseRule(ASTList const &decls, string &baseName); void emitXmlField_AttributeParseRule (rostring type, rostring name, string &baseName, AccessMod *amod); void emitXmlParser_objCtorArgs (ASTList const &args, bool &firstTime); void emitXmlParser_Node (ASTClass const *clazz, ASTList const *args, ASTList const *decls, ASTList const *lastArgs, ASTList const *childArgs = NULL, ASTList const *childDecls = NULL); void emitXmlParser_Node_registerAttr (ASTClass const *clazz, ASTList const *args, ASTList const *decls, ASTList const *lastArgs, ASTList const *childArgs = NULL, ASTList const *childDecls = NULL); void emitXmlParser_ASTList(ListClass const *type); // void emitXmlParser_FakeList(ListClass const *type); public: void emitXmlParserImplementation(); }; void CGen::emitFile() { headerComments(); out << "#include \"" << hdrFname << "\" // this module\n"; if (wantXmlVisitor()) { out << "#include \"strutil.h\" // quoted, parseQuotedString\n"; out << "#include \"xmlhelp.h\" // to/fromXml_bool/int\n"; } out << "\n"; out << "\n"; FOREACH_ASTLIST(ToplevelForm, file.forms, form) { ASTSWITCHC(ToplevelForm, form.data()) { //ASTCASEC(TF_verbatim, v) { // // nop //} ASTCASEC(TF_impl_verbatim, v) { doNotEdit(); out << v->code; } ASTNEXTC(TF_class, c) { emitTFClass(*c); } ASTNEXTC(TF_enum, e) { out << "char const *toString(" << e->name << " x)\n" << "{\n" << " static char const * const map[] = {\n"; FOREACH_ASTLIST(string, e->enumerators, iter) { out << " \"" << *(iter.data()) << "\",\n"; } out << " };\n" << " xassert((unsigned)x < TABLESIZE(map));\n" << " return map[x];\n" << "};\n" << "\n" << "\n" ; break; } ASTENDCASECD } } out << "\n\n"; if (wantVisitor()) { emitVisitorImplementation(); } if (wantDVisitor()) { emitDVisitorImplementation(); } if (wantXmlVisitor()) { emitXmlVisitorImplementation(); } if (wantMVisitor()) { emitMVisitorImplementation(); } } void CGen::emitTFClass(TF_class const &cls) { out << "// ------------------ " << cls.super->name << " -------------------\n"; doNotEdit(); // class destructor emitDestructor(*(cls.super)); // kind name map if (cls.hasChildren()) { out << "char const * const " << cls.super->name << "::kindNames[" << cls.super->name << "::NUM_KINDS] = {\n"; FOREACH_ASTLIST(ASTClass, cls.ctors, ctor) { out << " \"" << ctor.data()->name << "\",\n"; } out << "};\n"; out << "\n"; } // debugPrint out << "void " << cls.super->name << "::debugPrint(ostream &os, int indent, char const *subtreeName) const\n"; out << "{\n"; if (!cls.hasChildren()) { // childless superclasses get the preempt in the superclass; // otherwise it goes into the child classes emitCustomCode(cls.super->decls, "preemptDebugPrint"); // childless superclasses print headers; otherwise the subclass // prints the header out << " PRINT_HEADER(subtreeName, " << cls.super->name << ");\n"; out << "\n"; } // 10/31/01: decided I wanted custom debug print first, since it's // often much shorter (and more important) than the subtrees emitCustomCode(cls.super->decls, "debugPrint"); emitPrintCtorArgs(cls.super->args); if (cls.super->lastArgs.isNotEmpty()) { out << " // (lastArgs are printed by subclasses)\n"; } emitPrintFields(cls.super->decls); out << "}\n"; out << "\n"; // gdb() if (wantGDB) { out << "void " << cls.super->name << "::gdb() const\n" << " { debugPrint(cout, 0); }\n" << "\n" ; } // dsw: xmlPrint if (wantXMLPrint) { out << "void " << cls.super->name << "::xmlPrint(ostream &os, int indent) const\n"; out << "{\n"; if (!cls.hasChildren()) { // dsw: Haven't figured out what this subsection means yet. // // childless superclasses get the preempt in the superclass; // // otherwise it goes into the child classes // emitCustomCode(cls.super->decls, "preemptDebugPrint"); // // childless superclasses print headers; otherwise the subclass // // prints the header out << " XMLPRINT_HEADER(" << cls.super->name << ");\n"; out << "\n"; } // dsw: This must be in case the client .ast code overrides it. // 10/31/01: decided I wanted custom debug print first, since it's // often much shorter (and more important) than the subtrees // emitCustomCode(cls.super->decls, "xmlPrint"); emitXmlPrintCtorArgs(cls.super->args); if (!cls.hasChildren()) { out << " XMLPRINT_FOOTER(" << cls.super->name << ");\n"; } out << "}\n"; out << "\n"; } // clone for childless superclasses if (!cls.hasChildren()) { emitCloneCode(cls.super, NULL /*sub*/); } // constructors (class hierarchy children) FOREACH_ASTLIST(ASTClass, cls.ctors, ctoriter) { ASTClass const &ctor = *(ctoriter.data()); // downcast function out << "DEFN_AST_DOWNCASTS(" << cls.super->name << ", " << ctor.name << ", " << ctor.classKindName() << ")\n"; out << "\n"; // subclass destructor emitDestructor(ctor); // subclass debugPrint out << "void " << ctor.name << "::debugPrint(ostream &os, int indent, char const *subtreeName) const\n"; out << "{\n"; // the debug print preempter is declared in the outer "class", // but inserted into the print methods of the inner "constructors" emitCustomCode(cls.super->decls, "preemptDebugPrint"); // do same if it's declared only in the subclass emitCustomCode(ctor.decls, "preemptDebugPrint"); out << " PRINT_HEADER(subtreeName, " << ctor.name << ");\n"; out << "\n"; // call the superclass's fn to get its data members out << " " << cls.super->name << "::debugPrint(os, indent, subtreeName);\n"; out << "\n"; emitCustomCode(ctor.decls, "debugPrint"); emitPrintCtorArgs(ctor.args); emitPrintFields(ctor.decls); // superclass 'last' args come after all subclass things emitPrintCtorArgs(cls.super->lastArgs); out << "}\n"; out << "\n"; if (wantXMLPrint) { // subclass xmlPrint out << "void " << ctor.name << "::xmlPrint(ostream &os, int indent) const\n"; out << "{\n"; // the xml print preempter is declared in the outer "class", // but inserted into the print methods of the inner "constructors" emitCustomCode(cls.super->decls, "preemptXmlPrint"); out << " XMLPRINT_HEADER(" << ctor.name << ");\n"; out << "\n"; // call the superclass's fn to get its data members out << " " << cls.super->name << "::xmlPrint(os, indent);\n"; out << "\n"; // dsw: I assume this is not vital in the generic case, so I // leave it out for now. // emitCustomCode(ctor.decls, "xmlPrint"); emitXmlPrintCtorArgs(ctor.args); emitXmlPrintCtorArgs(cls.super->lastArgs); // dsw: probably don't need the name; take it out later if not out << " XMLPRINT_FOOTER(" << ctor.name << ");\n"; out << "\n"; out << "}\n"; out << "\n"; } // clone for subclasses emitCloneCode(cls.super, &ctor); } out << "\n"; } bool CGen::emitCustomCode(ASTList const &list, rostring tag) { bool emitted = false; FOREACH_ASTLIST(Annotation, list, iter) { CustomCode const *cc = iter.data()->ifCustomCodeC(); if (cc && cc->qualifier.equals(tag)) { out << " " << cc->code << ";\n"; emitted = true; // conceptually mutable.. const_cast(cc->used) = true; } } return emitted; } void CGen::emitDestructor(ASTClass const &cls) { out << cls.name << "::~" << cls.name << "()\n"; out << "{\n"; // user's code first emitFiltered(cls.decls, AC_DTOR, " "); // constructor arguments FOREACH_ASTLIST(CtorArg, cls.args, argiter) { CtorArg const &arg = *(argiter.data()); emitDestroyField(arg.isOwner, arg.type, arg.name); } // owner fields FOREACH_ASTLIST(Annotation, cls.decls, iter) { if (!iter.data()->isUserDecl()) continue; UserDecl const *ud = iter.data()->asUserDeclC(); if (!ud->amod->hasMod("owner")) continue; emitDestroyField(true /*isOwner*/, extractFieldType(ud->code), extractFieldName(ud->code)); } out << "}\n"; out << "\n"; } void CGen::emitDestroyField(bool isOwner, rostring type, rostring name) { if (isTreeListType(type)) { // explicitly destroy list elements, because it's easy to do, and // because if there is a problem, it's much easier to see its // role in a debugger backtrace out << " " << name << ".deleteAll();\n"; } else if (isListType(type)) { if (streq(extractListType(type), "LocString")) { // these are owned even though they aren't actually tree nodes out << " " << name << ".deleteAll();\n"; // TODO: this analysis is duplicated below, during cloning; // the astgen tool should do a better job of encapsulating // the relationships (particularly owning/non-owning) between // its parts, instead of doing ad-hoc type inspection in random // places during emission } else { // we don't own the list elements; it's *essential* to // explicitly remove the elements; this is a hack, since the // ideal solution is to make a variant of ASTList which is // explicitly serf pointers.. the real ASTList doesn't have // a removeAll method (since it's an owner list), and rather // than corrupting that interface I'll emit the code each time.. out << " while (" << name << ".isNotEmpty()) {\n" << " " << name << ".removeFirst();\n" << " }\n"; //out << " " << name << ".removeAll();\n"; } } else if (isOwner || isTreeNode(type)) { out << " delete " << name << ";\n"; } } void CGen::emitPrintCtorArgs(ASTList const &args) { FOREACH_ASTLIST(CtorArg, args, argiter) { CtorArg const &arg = *(argiter.data()); emitPrintField("PRINT", arg.isOwner, arg.type, arg.name); } } void CGen::emitPrintFields(ASTList const &decls) { FOREACH_ASTLIST(Annotation, decls, iter) { if (!iter.data()->isUserDecl()) continue; UserDecl const *ud = iter.data()->asUserDeclC(); if (!ud->amod->hasMod("field")) continue; emitPrintField("PRINT", ud->amod->hasMod("owner"), extractFieldType(ud->code), extractFieldName(ud->code)); } } void CGen::emitPrintField(rostring print, bool isOwner, rostring type, rostring name) { if (streq(type, "string")) { out << " " << print << "_STRING(" << name << ");\n"; } else if (streq(type, "StringRef")) { out << " " << print << "_CSTRING(" << name << ");\n"; } else if (streq(type, "bool")) { out << " " << print << "_BOOL(" << name << ");\n"; } else if (isListType(type)) { // for now, I'll continue to assume that any class that appears // in ASTList<> is compatible with the printing regime here out << " " << print << "_LIST(" << extractListType(type) << ", " << name << ");\n"; } else if (isFakeListType(type)) { // similar printing approach for FakeLists out << " " << print << "_FAKE_LIST(" << extractListType(type) << ", " << name << ");\n"; } else if (isTreeNode(type) || (isTreeNodePtr(type) && isOwner)) { // don't print subtrees that are possibly shared or circular out << " " << print << "_SUBTREE(" << name << ");\n"; } else { // catch-all .. out << " " << print << "_GENERIC(" << name << ");\n"; } } void CGen::emitXmlPrintCtorArgs(ASTList const &args) { FOREACH_ASTLIST(CtorArg, args, argiter) { CtorArg const &arg = *(argiter.data()); emitPrintField("XMLPRINT", arg.isOwner, arg.type, arg.name); } } void CGen::emitCloneCtorArg(CtorArg const *arg, int &ct) { if (ct++ > 0) { out << ","; } out << "\n "; string argName = arg->name; if (argName == "ret") { // avoid clash with local variable name argName = "this->ret"; // NOTE: I do not simply want to make the local variable name // something ugly like __astgen_ret because in the user-defined // clone() augmentation functions, the user is supposed to be // able to use 'ret' to refer to the tree constructed so far. } if (isTreeListType(arg->type)) { // clone an ASTList of tree nodes out << "cloneASTList(" << argName << ")"; } else if (isListType(arg->type)) { if (streq(extractListType(arg->type), "LocString")) { // these are owned, so clone deeply out << "cloneASTList(" << argName << ")"; } else { // clone an ASTList of non-tree nodes out << "shallowCloneASTList(" << argName << ")"; } } else if (isFakeListType(arg->type)) { // clone a FakeList (of tree nodes, we assume..) out << "cloneFakeList(" << argName << ")"; } else if (isTreeNode(arg->type)) { // clone a tree node out << argName << "? " << argName << "->clone() : NULL"; } else if (streq(arg->type, "LocString")) { // clone a LocString; we store objects, but pass pointers out << argName << ".clone()"; } else { // pass the non-tree node's value directly out << argName; } } void CGen::emitCloneCtorArgs(int &ct, ASTList const &args) { FOREACH_ASTLIST(CtorArg, args, iter) { emitCloneCtorArg(iter.data(), ct); } } void CGen::emitCloneCode(ASTClass const *super, ASTClass const *sub) { ASTClass const *myClass = sub? sub : super; string const &name = myClass->name; if (!nocvr || !sub) { // normal case, or childless superclass case out << name << " *" << name << "::clone() const\n"; } else { // msvc hack case out << super->name << " *" << name << "::nocvr_clone() const\n"; } out << "{\n"; if (!emitCustomCode(myClass->decls, "substituteClone")) { out << " " << name << " *ret = new " << name << "("; // clone each of the superclass ctor arguments int ct=0; emitCloneCtorArgs(ct, super->args); // and likewise for the subclass ctor arguments if (sub) { emitCloneCtorArgs(ct, sub->args); emitCloneCtorArgs(ct, sub->lastArgs); } emitCloneCtorArgs(ct, super->lastArgs); out << "\n" << " );\n"; // custom clone code emitCustomCode(super->decls, "clone"); if (sub) { emitCustomCode(sub->decls, "clone"); } out << " return ret;\n"; } out << "}\n" << "\n"; } // -------------------------- visitor --------------------------- void emitTF_custom(ofstream &out, rostring qualifierName, bool addNewline) { FOREACH_ASTLIST_NC(ToplevelForm, wholeAST->forms, iter) { if (iter.data()->isTF_custom()) { CustomCode *cc = iter.data()->asTF_customC()->cust; if (cc->qualifier == qualifierName) { out << cc->code; if (addNewline) { out << "\n"; } cc->used = true; } } } } void HGen::emitVisitorInterfacePrelude(rostring visitorName) { // custom additions to this visitor emitTF_custom(out, visitorName, true /*addNewline*/); out << "private: // disallowed, not implemented\n" << " " << visitorName << "(" << visitorName << "&);\n" << " void operator= (" << visitorName << "&);\n" << "\n" << "public: // funcs\n" << " " << visitorName << "() {" ; // custom additions to the visitor's constructor emitTF_custom(out, stringc << visitorName << "_ctor", false /*addNewline*/); out << "}\n" << " virtual ~" << visitorName << "(); // silence gcc warning...\n" << "\n" ; } void HGen::emitVisitorInterface() { out << "// the visitor interface class\n" << "class " << visitorName << " {\n"; emitVisitorInterfacePrelude(visitorName); SFOREACH_OBJLIST(TF_class, allClasses, iter) { TF_class const *c = iter.data(); out << " virtual bool visit" << c->super->name << "(" << c->super->name << " *obj);\n" << " virtual void postvisit" << c->super->name << "(" << c->super->name << " *obj);\n" ; } out << "\n // List 'classes'\n"; FOREACH_ASTLIST(ListClass, listClasses, iter) { ListClass const *cls = iter.data(); out << " virtual bool visitList_" << cls->classAndMemberName << "(" << cls->kindName() << "<" << cls->elementClassName << ">*);\n"; out << " virtual void postvisitList_" << cls->classAndMemberName << "(" << cls->kindName() << "<" << cls->elementClassName << ">*);\n"; } StringSet listItemClassesSet; // set of list item classes printed so far FOREACH_ASTLIST(ListClass, listClasses, iter) { ListClass const *cls = iter.data(); xassert(!listItemClassesSet.contains(cls->classAndMemberName)); // should not repeat listItemClassesSet.add(cls->classAndMemberName); out << " virtual bool visitListItem_" << cls->classAndMemberName << "(" << cls->elementClassName << "*);\n"; out << " virtual void postvisitListItem_" << cls->classAndMemberName << "(" << cls->elementClassName << "*);\n"; } out << "};\n\n"; } void CGen::emitVisitorImplementation() { out << "// ---------------------- " << visitorName << " ---------------------\n"; out << "// default no-op visitor\n"; out << visitorName << "::~" << visitorName << "() {}\n"; SFOREACH_OBJLIST(TF_class, allClasses, iter) { TF_class const *c = iter.data(); out << "bool " << visitorName << "::visit" << c->super->name << "(" << c->super->name << " *obj) { return true; }\n" << "void " << visitorName << "::postvisit" << c->super->name << "(" << c->super->name << " *obj) {}\n"; } out << "\n// List 'classes'\n"; FOREACH_ASTLIST(ListClass, listClasses, iter) { ListClass const *cls = iter.data(); out << "bool " << visitorName << "::visitList_" << cls->classAndMemberName << "(" << cls->kindName() << "<" << cls->elementClassName << ">*) { return true; }\n"; out << "void " << visitorName << "::postvisitList_" << cls->classAndMemberName << "(" << cls->kindName() << "<" << cls->elementClassName << ">*) {}\n"; } StringSet listItemClassesSet; // set of list item classes printed so far FOREACH_ASTLIST(ListClass, listClasses, iter) { ListClass const *cls = iter.data(); xassert(!listItemClassesSet.contains(cls->classAndMemberName)); // should not repeat listItemClassesSet.add(cls->classAndMemberName); out << "bool " << visitorName << "::visitListItem_" << cls->classAndMemberName << "(" << cls->elementClassName << "*) { return true; }\n"; out << "void " << visitorName << "::postvisitListItem_" << cls->classAndMemberName << "(" << cls->elementClassName << "*) {}\n"; } out << "\n\n"; // implementations of traversal functions SFOREACH_OBJLIST(TF_class, allClasses, iter) { TF_class const *c = iter.data(); // superclass traversal emitTraverse(c->super, NULL /*super*/, c->hasChildren()); // subclass traversal FOREACH_ASTLIST(ASTClass, c->ctors, iter) { ASTClass const *sub = iter.data(); emitTraverse(sub, c->super, false /*hasChildren*/); } } out << "\n"; } void CGen::emitOneTraverseCall(rostring className, string name, string type) { if (isTreeNode(type) || isTreeNodePtr(type)) { // traverse it directly // cout << "emitOneTraverseCall, name:" << name << ", type: " << type << endl; out << " if (" << name << ") { " << name << "->traverse(vis); }\n"; } else if ((isListType(type) || isFakeListType(type)) && isTreeNode(extractListType(type))) { // list of tree nodes: iterate and traverse string eltType = extractListType(type); // could add an assertion here that the elt type is one of the // list types extracted earlier during getListClasses() // compute list accessor names char const *iterMacroName = "FOREACH_ASTLIST_NC"; char const *iterElt = ".data()"; char const *argNamePrefix = "&"; if (isFakeListType(type)) { iterMacroName = "FAKELIST_FOREACH_NC"; iterElt = ""; argNamePrefix = ""; } // emit the pre-visit call to the fantasy "list class" out << " if (vis.visitList_" << className << "_" << name << "(" << argNamePrefix << name << ")) {\n"; out << " " << iterMacroName << "(" << eltType << ", " << name << ", iter) {\n"; out << " if (vis.visitListItem_" << className << "_" << name << "(iter" << iterElt << ")) {\n"; out << " iter" << iterElt << "->traverse(vis);\n"; out << " vis.postvisitListItem_" << className << "_" << name << "(iter" << iterElt << ");\n"; out << " }\n"; // end of if out << " }\n"; // end of iter out << " vis.postvisitList_" << className << "_" << name << "(" << argNamePrefix << name << ");\n"; out << " }\n"; // end of if } } void CGen::emitTraverse(ASTClass const *c, ASTClass const * /*nullable*/ super, bool hasChildren) { out << "void " << c->name << "::traverse(" << visitorName << " &vis)\n" << "{\n"; // do any initial traversal action specified by the user emitCustomCode(c->decls, "preemptTraverse"); // name of the 'visit' method that applies to this class; // these methods are always named according to the least-derived // class in the hierarchy string visitName = stringc << "visit" << (super? super : c)->name; // we only call 'visit' in the most-derived classes; this of course // assumes that classes with children are never themselves instantiated if (!hasChildren) { // visit this node; if the visitor doesn't want to traverse // the children, then bail out << " if (!vis." << visitName << "(this)) { return; }\n\n"; } else { out << " // no 'visit' because it's handled by subclasses\n\n"; } if (super) { // traverse superclass ctor args, if this class has one out << " " << super->name << "::traverse(vis);\n" << "\n"; } // traverse into the ctor arguments FOREACH_ASTLIST(CtorArg, c->args, iter) { CtorArg const *arg = iter.data(); emitOneTraverseCall(c->name, arg->name, arg->type); } // dsw: I need a way to make fields traversable FOREACH_ASTLIST(Annotation, c->decls, iter) { if (!iter.data()->isUserDecl()) continue; UserDecl const *ud = iter.data()->asUserDeclC(); if (!ud->amod->hasMod("traverse")) continue; emitOneTraverseCall(c->name, extractFieldName(ud->code), extractFieldType(ud->code)); } // do any final traversal action specified by the user emitCustomCode(c->decls, "traverse"); // traverse into the ctor last arguments FOREACH_ASTLIST(CtorArg, c->lastArgs, iter) { CtorArg const *arg = iter.data(); emitOneTraverseCall(c->name, arg->name, arg->type); } if (!hasChildren) { // if we did the preorder visit on the way in, do the // postorder visit now out << " vis.post" << visitName << "(this);\n"; } else { out << " // no 'postvisit' either\n"; } out << "}\n\n"; } // ------------------- delegation visitor -------------------- void HGen::emitDVisitorInterface() { out << "// the delegator-visitor interface class\n" << "class " << dvisitorName << " : public " << visitorName << " {\n"; out << "protected: // data\n"; out << " " << visitorName << " *client; // visitor to delegate to\n"; out << " bool ensureOneVisit; // check for visiting at most once?\n"; out << " SObjSet wasVisitedASTNodes; // set of visited nodes\n"; out << " SObjSet wasVisitedList_ASTListNodes; // set of visited ASTLists\n"; out << " SObjSet wasVisitedList_FakeListNodes; // set of visited FakeLists\n"; out << "\n"; out << "protected: // funcs\n"; out << " bool wasVisitedAST(void *ast);\n"; out << " bool wasVisitedList_ASTList(void *ast);\n"; out << " bool wasVisitedList_FakeList(void *ast);\n"; out << "\n"; // ctor out << "public: // funcs\n"; out << " explicit " << dvisitorName << "(" << visitorName << " *client0 = NULL, " << "bool ensureOneVisit0 = true" << ")\n"; out << " : client(client0)\n"; out << " , ensureOneVisit(ensureOneVisit0)\n"; out << " {}\n"; out << "\n"; // visitor methods SFOREACH_OBJLIST(TF_class, allClasses, iter) { TF_class const *c = iter.data(); out << " virtual bool visit" << c->super->name << "(" << c->super->name << " *obj);\n" << " virtual void postvisit" << c->super->name << "(" << c->super->name << " *obj);\n"; } out << "\n // List 'classes'\n"; FOREACH_ASTLIST(ListClass, listClasses, iter) { ListClass const *cls = iter.data(); out << " virtual bool visitList_" << cls->classAndMemberName << "(" << cls->kindName() << "<" << cls->elementClassName << ">*);\n"; out << " virtual void postvisitList_" << cls->classAndMemberName << "(" << cls->kindName() << "<" << cls->elementClassName << ">*);\n"; } StringSet listItemClassesSet; // set of list item classes printed so far FOREACH_ASTLIST(ListClass, listClasses, iter) { ListClass const *cls = iter.data(); xassert(!listItemClassesSet.contains(cls->classAndMemberName)); // should not repeat listItemClassesSet.add(cls->classAndMemberName); out << " virtual bool visitListItem_" << cls->classAndMemberName << "(" << cls->elementClassName << "*);\n"; out << " virtual void postvisitListItem_" << cls->classAndMemberName << "(" << cls->elementClassName << "*);\n"; } // we are done out << "};\n\n"; } void CGen::emitDVisitorImplVisitedCheck(char const *name) { out << "bool " << dvisitorName << "::" << name << "(void *ast)\n"; out << "{\n"; out << " // do not bother to check if the user does not want us to\n"; out << " if (!ensureOneVisit) {\n"; out << " return false;\n"; out << " }\n\n"; out << " if (!ast) {return false;} // avoid NULL; actually happens for FakeLists\n"; out << " if (" << name << "Nodes.contains(ast)) {\n"; out << " return true;\n"; out << " } else {\n"; out << " " << name << "Nodes.add(ast);\n"; out << " return false;\n"; out << " }\n"; out << "}\n\n"; } void CGen::emitDVisitorImplementation() { out << "// ---------------------- " << dvisitorName << " ---------------------\n"; emitDVisitorImplVisitedCheck("wasVisitedAST"); emitDVisitorImplVisitedCheck("wasVisitedList_ASTList"); emitDVisitorImplVisitedCheck("wasVisitedList_FakeList"); out << "// default no-op delegator-visitor\n"; SFOREACH_OBJLIST(TF_class, allClasses, iter) { TF_class const *c = iter.data(); out << "bool " << dvisitorName << "::visit" << c->super->name << "(" << c->super->name << " *obj) {\n" // dsw: I changed this back to xassert() from xassertdb() // because NDEBUG just gets turned on more often than I like. << " xassert(!wasVisitedAST(obj));\n" << " return client ? client->visit" << c->super->name << "(obj) : true;\n" << "}\n"; out << "void " << dvisitorName << "::postvisit" << c->super->name << "(" << c->super->name << " *obj) {\n" << " if (client) {\n" << " client->postvisit" << c->super->name << "(obj);\n" << " }\n" << "}\n"; } out << "\n// List 'classes'\n"; FOREACH_ASTLIST(ListClass, listClasses, iter) { ListClass const *cls = iter.data(); // visit out << "bool " << dvisitorName << "::visitList_" << cls->classAndMemberName << "(" << cls->kindName() << "<" << cls->elementClassName << ">* obj) {\n"; out << " xassert(!wasVisitedList_" << cls->kindName() << "(obj));\n"; out << " return client ? client->visitList_" << cls->classAndMemberName << "(obj) : true;\n"; out << "}\n"; // post visit out << "void " << dvisitorName << "::postvisitList_" << cls->classAndMemberName << "(" << cls->kindName() << "<" << cls->elementClassName << ">* obj) {\n"; out << " if (client) {\n"; out << " client->postvisitList_" << cls->classAndMemberName << "(obj);\n"; out << " }\n"; out << "}\n"; } StringSet listItemClassesSet; // set of list item classes printed so far FOREACH_ASTLIST(ListClass, listClasses, iter) { ListClass const *cls = iter.data(); xassert(!listItemClassesSet.contains(cls->classAndMemberName)); // should not repeat listItemClassesSet.add(cls->classAndMemberName); // visit item out << "bool " << dvisitorName << "::visitListItem_" << cls->classAndMemberName << "(" << cls->elementClassName << " *obj) {\n"; // NOTE: we do NOT check that it has been visited before; this is // the point of list items: that they are visited multiple times // even if the contained item itself is not out << " return client ? client->visitListItem_" << cls->classAndMemberName << "(obj) : true;\n"; out << "}\n"; // post visit item out << "void " << dvisitorName << "::postvisitListItem_" << cls->classAndMemberName << "(" << cls->elementClassName << " *obj) {\n"; out << " if (client) {\n"; out << " client->postvisitListItem_" << cls->classAndMemberName << "(obj);\n"; out << " }\n"; out << "}\n"; } out << "\n\n"; } // ------------------- xml visitor -------------------- void CGen::emitXmlCtorArgs(ASTList const &args, char const *baseName, string const &className) { FOREACH_ASTLIST(CtorArg, args, argiter) { CtorArg const &arg = *(argiter.data()); emitXmlField(arg.type, arg.name, baseName, className, NULL); } } void CGen::emitXmlFields(ASTList const &decls, char const *baseName, string const &className) { FOREACH_ASTLIST(Annotation, decls, iter) { if (!iter.data()->isUserDecl()) continue; UserDecl const *ud = iter.data()->asUserDeclC(); if (!ud->amod->hasModPrefix("xml")) continue; emitXmlField(extractFieldType(ud->code), extractFieldName(ud->code), baseName, className, ud->amod); } } void CGen::emitXmlField(rostring type, rostring name, char const *baseName, string const &className, AccessMod *amod) { if (streq(name, "arraySize")) { breaker(); } // FIX: there is a problem with coming up with a way to serialize a // NULL string: 1) the value must be quoted, 2) any string you use // to represent NULL is also a valid string value, 3) quoting twice // is expensive; For now, I simply omit mention of it: it will // default to NULL. if (streq(type, "string") || streq(type, "StringRef")) { out << " if (" << baseName << "->" << name << ") {\n"; out << " out << \"\\n\";\n"; out << " if (indent) printIndentation();\n"; out << " out << \"" << name << "\" << \"=\";\n"; out << " out << quoted(" << baseName << "->" << name << ");\n"; out << " } else {\n"; // print nothing; the default value is NULL // out << " out << \"\\\"0\\\"\";\n"; out << " }\n"; } else if (streq(type, "bool")) { out << " out << \"\\n\";\n"; out << " if (indent) printIndentation();\n"; out << " out << \"" << name << "\" << \"=\";\n"; out << " out << quoted(toXml_bool(" << baseName << "->" << name << "));\n"; } else if (streq(type, "int")) { out << " out << \"\\n\";\n"; out << " if (indent) printIndentation();\n"; out << " out << \"" << name << "\" << \"=\";\n"; out << " out << quoted(toXml_int(" << baseName << "->" << name << "));\n"; } else if (streq(type, "unsigned int")) { out << " out << \"\\n\";\n"; out << " if (indent) printIndentation();\n"; out << " out << \"" << name << "\" << \"=\";\n"; out << " out << quoted(toXml_unsigned_int(" << baseName << "->" << name << "));\n"; } else if (streq(type, "long")) { out << " out << \"\\n\";\n"; out << " if (indent) printIndentation();\n"; out << " out << \"" << name << "\" << \"=\";\n"; out << " out << quoted(toXml_long(" << baseName << "->" << name << "));\n"; } else if (streq(type, "unsigned long")) { out << " out << \"\\n\";\n"; out << " if (indent) printIndentation();\n"; out << " out << \"" << name << "\" << \"=\";\n"; out << " out << quoted(toXml_unsigned_long(" << baseName << "->" << name << "));\n"; } else if (streq(type, "double")) { out << " out << \"\\n\";\n"; out << " if (indent) printIndentation();\n"; out << " out << \"" << name << "\" << \"=\";\n"; out << " out << quoted(toXml_double(" << baseName << "->" << name << "));\n"; } else if (streq(type, "SourceLoc")) { out << " out << \"\\n\";\n"; out << " if (indent) printIndentation();\n"; out << " out << \"" << name << "\" << \"=\";\n"; out << " out << quoted(toXml_SourceLoc(" << baseName << "->" << name << "));\n"; } else if (isListType(type)) { out << " if (" << baseName << ") {\n"; out << " out << \"\\n\";\n"; out << " if (indent) printIndentation();\n"; out << " out << \"" << name << "\" << \"=\\\"\";\n"; out << " xmlPrintPointer(out, \"AL\", &(" << baseName << "->" << name << "));\n"; out << " out << \"\\\"\";\n"; out << " }\n"; } else if (isFakeListType(type)) { out << " if (" << baseName << " && " << baseName << "->" << name << ") {\n"; out << " out << \"\\n\";\n"; out << " if (indent) printIndentation();\n"; out << " out << \"" << name << "\" << \"=\\\"\";\n"; out << " xmlPrintPointer(out, \"FL\", " << baseName << "->" << name << ");\n"; out << " out << \"\\\"\";\n"; out << " }\n"; } else if (isTreeNode(type) || (isTreeNodePtr(type))) { out << " if (" << baseName << " && " << baseName << "->" << name << ") {\n"; out << " out << \"\\n\";\n"; out << " if (indent) printIndentation();\n"; out << " out << \"" << name << "\" << \"=\\\"\";\n"; out << " xmlPrintPointer(out, \"AST\", " << baseName << "->" << name << ");\n"; out << " out << \"\\\"\";\n"; out << " }\n"; } else if (amod && amod->hasModPrefix("xmlEmbed") && amod->hasModPrefix("xml_")) { rostring idPrefix = amod->getModSuffixFromPrefix("xml_"); out << " if (" << baseName << ") {\n"; out << " out << \"\\n\";\n"; out << " if (indent) printIndentation();\n"; out << " out << \"" << name << "\" << \"=\\\"\";\n"; out << " xmlPrintPointer(out, \"" << idPrefix << "\", "; out << "&"; out << baseName << "->" << name << ");\n"; out << " out << \"\\\"\";\n"; out << " }\n"; } // FIX: relace the below with this; see note at "crap" below. // else if (isPtrKind(type) && amod && amod->hasModPrefix("xml_")) { else if ( (!amod && isPtrKind(type)) || (amod && amod->hasModPrefix("xml_")) ) { // catch-all for xml objects string idPrefix; if (amod) { idPrefix = amod->getModSuffixFromPrefix("xml_"); } else { // FIX: get rid of this piece of crap when we get a way to put // an access specifier onto a ctor argument in the ast language idPrefix = stringc << "TY"; } out << " if (" << baseName << " && " << baseName << "->" << name << ") {\n"; out << " out << \"\\n\";\n"; out << " if (indent) printIndentation();\n"; out << " out << \"" << name << "\" << \"=\\\"\";\n"; out << " xmlPrintPointer(out, \"" << idPrefix << "\", "; out << baseName << "->" << name << ");\n"; out << " out << \"\\\"\";\n"; out << " }\n"; } else { // catch-all for non-objects out << " out << \"\\n\";\n"; out << " if (indent) printIndentation();\n"; out << " out << \"" << name << "\" << \"=\";\n"; out << " out << quoted(toXml(" << baseName << "->" << name << "));\n"; } } void HGen::emitXmlVisitorInterface() { out << "// the xml-visitor interface class\n" << "class " << xmlVisitorName << " : public " << visitorName << " {\n"; out << "protected: // data\n"; out << " ostream &out; // output stream to print to\n"; out << " int &depth; // current depth\n"; out << " bool indent; // should the xml be indented\n"; out << " bool ensureOneVisit; // check for visiting at most once?\n"; out << " SObjSet wasVisitedASTNodes; // set of visited nodes\n"; out << " SObjSet wasVisitedList_ASTListNodes; // set of visited ASTLists\n"; out << " SObjSet wasVisitedList_FakeListNodes; // set of visited FakeLists\n"; out << "\n"; out << "protected: // funcs\n"; out << " bool wasVisitedAST(void *ast);\n"; out << " bool wasVisitedList_ASTList(void *ast);\n"; out << " bool wasVisitedList_FakeList(void *ast);\n"; out << " void printIndentation();\n"; out << "\n"; // ctor out << "public: // funcs\n"; out << " explicit " << xmlVisitorName << "(" << "ostream &out0, " << "int &depth0, " << "bool indent0 = false, " << "bool ensureOneVisit0 = true" << ")\n"; out << " : out(out0)\n"; out << " , depth(depth0)\n"; out << " , indent(indent0)\n"; out << " , ensureOneVisit(ensureOneVisit0)\n"; out << " {}\n"; out << "\n"; // visitor methods SFOREACH_OBJLIST(TF_class, allClasses, iter) { TF_class const *c = iter.data(); out << " virtual bool visit" << c->super->name << "(" << c->super->name << " *obj);\n" << " virtual void postvisit" << c->super->name << "(" << c->super->name << " *obj);\n"; } out << "\n // List 'classes'\n"; FOREACH_ASTLIST(ListClass, listClasses, iter) { ListClass const *cls = iter.data(); out << " virtual bool visitList_" << cls->classAndMemberName << "(" << cls->kindName() << "<" << cls->elementClassName << ">*);\n"; out << " virtual void postvisitList_" << cls->classAndMemberName << "(" << cls->kindName() << "<" << cls->elementClassName << ">*);\n"; } StringSet listItemClassesSet; // set of list item classes printed so far FOREACH_ASTLIST(ListClass, listClasses, iter) { ListClass const *cls = iter.data(); xassert(!listItemClassesSet.contains(cls->classAndMemberName)); // should not repeat listItemClassesSet.add(cls->classAndMemberName); out << " virtual bool visitListItem_" << cls->classAndMemberName << "(" << cls->elementClassName << "*);\n"; out << " virtual void postvisitListItem_" << cls->classAndMemberName << "(" << cls->elementClassName << "*);\n"; } // we are done out << "};\n\n"; } void CGen::emitXmlVisitorImplVisitedCheck(char const *name) { out << "bool " << xmlVisitorName << "::" << name << "(void *ast)\n"; out << "{\n"; out << " // do not bother to check if the user does not want us to\n"; out << " if (!ensureOneVisit) {\n"; out << " return false;\n"; out << " }\n\n"; out << " if (!ast) {return false;} // avoid NULL; actually happens for FakeLists\n"; out << " if (" << name << "Nodes.contains(ast)) {\n"; out << " return true;\n"; out << " } else {\n"; out << " " << name << "Nodes.add(ast);\n"; out << " return false;\n"; out << " }\n"; out << "}\n\n"; } void CGen::emitXmlVisitorImplementation() { out << "// ---------------------- " << xmlVisitorName << " ---------------------\n"; emitXmlVisitorImplVisitedCheck("wasVisitedAST"); emitXmlVisitorImplVisitedCheck("wasVisitedList_ASTList"); emitXmlVisitorImplVisitedCheck("wasVisitedList_FakeList"); out << "void " << xmlVisitorName << "::printIndentation()\n"; out << "{\n"; out << " for(int i=0; isuper->name; out << "(" << c->super->name << " *obj) {\n"; out << " if(wasVisitedAST(obj)) return false;\n"; // for now everything is a container tag out << " out << \"\\n\";\n"; out << " if (indent) printIndentation();\n"; out << " out << \"<\" << obj->kindName();\n"; // print the attributes of the superclass out << " ++depth;\n"; // indent them one level // emit the id property // put it on the same line as the tag // out << " out << \"\\n\";\n"; // out << " if (indent) printIndentation();\n"; out << " out << \" _id=\\\"\";\n"; out << " xmlPrintPointer(out, \"AST\", obj);\n"; out << " out << \"\\\"\";\n"; // emit other properties emitXmlCtorArgs(c->super->args, "obj", c->super->name); emitXmlFields(c->super->decls, "obj", c->super->name); // print the attributes of the subclasses if (c->hasChildren()) { out << " switch(obj->kind()) {\n"; out << " default: xfailure(\"bad tag\"); break;\n"; FOREACH_ASTLIST(ASTClass, c->ctors, iter) { ASTClass const *clazz = iter.data(); out << " case " << c->super->name << "::" << clazz->classKindName() << ": {\n"; // prevent unused variable warning when compiling client code; // FIX: turn on when take away the '= NULL' code below. // if (clazz->args.isNotEmpty() || clazz->decls.isNotEmpty()) { out << " " << clazz->name << " *obj0 = obj->as" << clazz->name << "();\n"; // } emitXmlCtorArgs(clazz->args, "obj0", clazz->name); emitXmlFields(clazz->decls, "obj0", clazz->name); // prevent unused variable warning when compiling client code; // FIX: remove when all kinds of fields are printed out << " obj0 = NULL;\n"; out << " break;\n"; out << " }\n"; } out << " }\n"; } // print the 'last' attributes of the superclass, which come after // all subclass things emitXmlCtorArgs(c->super->lastArgs, "obj", c->super->name); out << " --depth;\n"; // outdent temporarily so close bracket lines up with open // commenting out these two lines puts the closing angle bracket // on the same line as the last attribute, or if none, on the same // line as the tag // out << " out << \"\\n\";\n"; // out << " if (indent) printIndentation();\n"; out << " out << \">\";\n"; out << " ++depth;\n"; // indent for nested children out << " return true;\n"; out << "}\n\n";; out << "void " << xmlVisitorName << "::postvisit" << c->super->name; out << "(" << c->super->name << " *obj) {\n"; out << " --depth;\n"; out << " out << \"\\n\";\n"; out << " if (indent) printIndentation();\n"; out << " out << \"kindName() << \">\";\n"; out << "}\n\n"; } out << "\n// List 'classes'\n"; FOREACH_ASTLIST(ListClass, listClasses, iter) { ListClass const *cls = iter.data(); // visit out << "bool " << xmlVisitorName << "::visitList_" << cls->classAndMemberName << "(" << cls->kindName() << "<" << cls->elementClassName << ">* obj) {\n"; out << " if (obj->isNotEmpty()) {\n"; out << " if(wasVisitedList_" << cls->kindName() << "(obj)) return false;\n"; out << " out << \"\\n\";\n"; out << " if (indent) printIndentation();\n"; out << " out << \"classAndMemberName << " _id=\\\"\";\n"; out << " xmlPrintPointer(out, \""; if (cls->lkind == LK_ASTList) { out << "AL"; } else if (cls->lkind == LK_FakeList) { out << "FL"; } else xfailure("illegal list kind"); out << "\", obj);\n"; out << " out << \"\\\">\";\n"; out << " ++depth;\n"; out << " };\n"; out << " return true;\n"; out << "}\n\n"; // post visit out << "void " << xmlVisitorName << "::postvisitList_" << cls->classAndMemberName << "(" << cls->kindName() << "<" << cls->elementClassName << ">* obj) {\n"; out << " if (obj->isNotEmpty()) {\n"; out << " --depth;\n"; out << " out << \"\\n\";\n"; out << " if (indent) printIndentation();\n"; out << " out << \"classAndMemberName << ">\";\n"; out << " }\n"; out << "}\n\n"; // visit item out << "bool " << xmlVisitorName << "::visitListItem_" << cls->classAndMemberName << "(" << cls->elementClassName << " *obj) {\n"; out << " xassert(obj);\n"; out << " out << \"\\n\";\n"; out << " if (indent) printIndentation();\n"; out << " out << \"<_List_Item\" << \" item=\\\"\";\n"; out << " xmlPrintPointer(out, \"AST\", obj);\n"; out << " out << \"\\\">\";\n"; out << " ++depth;\n"; out << " return true;\n"; out << "}\n\n"; // post visit item out << "void " << xmlVisitorName << "::postvisitListItem_" << cls->classAndMemberName << "(" << cls->elementClassName << " *obj) {\n"; out << " xassert(obj);\n"; out << " --depth;\n"; out << " out << \"\\n\";\n"; out << " if (indent) printIndentation();\n"; out << " out << \"\";\n"; out << "}\n\n"; } out << "\n\n"; } // ------------------- modification visitor -------------------- void HGen::emitMVisitorInterface() { out << "// the modification visitor interface class\n" << "class " << mvisitorName << " {\n"; emitVisitorInterfacePrelude(mvisitorName); SFOREACH_OBJLIST(TF_class, allClasses, iter) { TF_class const *c = iter.data(); out << " virtual bool visit" << c->super->name << "(" << c->super->name << " *&obj);\n" << " void mtraverse(" << c->super->name << " *&obj);\n" ; } out << "};\n\n"; } // The generated code does: // - any actions specified for the superclass in the .ast file // - invoke the visitor method, returning if it returns false // - recursively traverse superclass fields // - switch on subclass type, if there are any // - do actions specified for the subclass in the .ast file // - recursively visit the subclass fields // There is a slightly nonideal aspect to the above, in that the // subclass 'mtraverse' action is done *after* the superclass fields // are traversed, so the former can't skip the latter, but I'll leave // it for now (would require emitting two switch blocks to fix). void CGen::emitMVisitorImplementation() { out << "// ---------------------- " << mvisitorName << " ---------------------\n"; out << mvisitorName << "::~" << mvisitorName << "() {}\n\n"; SFOREACH_OBJLIST(TF_class, allClasses, iter) { TF_class const *c = iter.data(); out << "bool " << mvisitorName << "::visit" << c->super->name << "(" << c->super->name << " *&obj) { return true; }\n" << "\n" ; out << "void " << mvisitorName << "::mtraverse(" << c->super->name << " *&obj)\n" << "{\n" ; // invoke visitor function out << " if (!visit" << c->super->name << "(obj)) { return; }\n" << "\n" ; // superclass action specified in .ast file emitCustomCode(c->super->decls, "mtraverse"); // superclass field traversal emitMTraverse(c->super, "obj", " "); if (c->hasChildren()) { out << " switch (obj->kind()) {\n"; // subclass traversal FOREACH_ASTLIST(ASTClass, c->ctors, iter) { ASTClass const *sub = iter.data(); out << " case " << c->super->name << "::" << sub->classKindName() << ": {\n" << " " << sub->name << " *sub = (" << sub->name << "*)obj;\n" << " (void)sub;\n" // silence warning if unused ; // subclass action specified in .ast file emitCustomCode(sub->decls, "mtraverse"); // subclass field traversal emitMTraverse(sub, "sub", " "); out << " break;\n" << " }\n" ; } out << " default:;\n" // silence warning << " }\n"; } out << "}\n" << "\n" << "\n" ; } out << "\n"; } // emit mtraverse calls for the fields void CGen::emitMTraverse(ASTClass const *c, rostring obj, rostring i) { // traverse into the ctor arguments FOREACH_ASTLIST(CtorArg, c->args, iter) { CtorArg const *arg = iter.data(); string argVar = stringc << obj << "->" << arg->name; if (isTreeNode(arg->type) || isTreeNodePtr(arg->type)) { string eltType = extractNodeType(arg->type); out << i << "if (" << argVar << ") {\n"; emitMTraverseCall(stringc << i << " ", eltType, argVar); out << i << "}\n"; } else if (isListType(arg->type) && isTreeNode(extractListType(arg->type))) { string eltType = extractListType(arg->type); // list of tree nodes: iterate and traverse out << i << "FOREACH_ASTLIST_NC(" << eltType << ", " << argVar << ", iter) {\n"; emitMTraverseCall(stringc << i << " ", eltType, "iter.dataRef()"); out << i << "}\n"; } else if (isFakeListType(arg->type) && isTreeNode(extractListType(arg->type))) { string eltType = extractListType(arg->type); // fakelist mtraversal is a little complicated because I // need to break apart the 'FakeList' abstraction so I can // modify elements, iterate into the substituted part, etc. out << i << "// fakelist mtraversal: " << argVar << "\n" << i << "{\n" << i << " " << eltType << " **iter = " << "(" << eltType << "**)&(" << argVar << ");\n" << i << " while (*iter) {\n" ; emitMTraverseCall(stringc << i << " ", eltType, "*iter"); out << i << " iter = &( (*iter)->next );\n" << i << " }\n" << i << "}\n" ; } } } void CGen::emitMTraverseCall(rostring i, rostring eltType, rostring argVar) { if (isSuperclassTreeNode(eltType)) { // easy case out << i << "mtraverse(" << argVar << ");\n"; return; } // because the field is declared to be a specific subclass type, we // invoke mtraverse but then check that the result has that type xassert(isSubclassTreeNode(eltType)); string superType = getSuperTypeOf(eltType); out << i << superType << "* tmp = " << argVar << ";\n" << i << "mtraverse(tmp);\n" << i << "if (tmp != " << argVar << ") {\n" << i << " " << argVar << " = tmp->as" << eltType << "();\n" << i << "}\n" ; } // ------------------- xml parser -------------------- // FIX: an iterator desing pattern over the fields/ctor_args of an AST // node would be better than this repeated copies of the DFS void XmlParserGen::collectXmlParserCtorArgs(ASTList const &args, char const *baseName) { FOREACH_ASTLIST(CtorArg, args, argiter) { CtorArg const &arg = *(argiter.data()); collectXmlParserField(arg.type, arg.name, baseName, NULL); } } void XmlParserGen::collectXmlParserFields(ASTList const &decls, char const *baseName) { FOREACH_ASTLIST(Annotation, decls, iter) { if (!iter.data()->isUserDecl()) continue; UserDecl const *ud = iter.data()->asUserDeclC(); if (!ud->amod->hasModPrefix("xml")) continue; collectXmlParserField(extractFieldType(ud->code), extractFieldName(ud->code), baseName, ud->amod); } } // FIX: this is now very redundant, but we can still change it to // exclude some fields of some types, so lets keep it for now. void XmlParserGen::collectXmlParserField (rostring type, rostring name, char const *baseName, AccessMod*) { if (streq(type, "string")) { attributeNames.add(name); } else if (streq(type, "StringRef")) { attributeNames.add(name); } else if (streq(type, "bool")) { attributeNames.add(name); } else if (isListType(type)) { attributeNames.add(name); } else if (isFakeListType(type)) { attributeNames.add(name); } else if (isTreeNode(type) || (isTreeNodePtr(type))) { attributeNames.add(name); } // If you start being more selective, be sure to include the names // for things marked "xml" else { // catch-all .. // out << " " << print << "_GENERIC(" << name << ");\n"; attributeNames.add(name); } } void XmlParserGen::emitXmlCtorArgs_AttributeParseRule (ASTList const &args, string &baseName) { FOREACH_ASTLIST(CtorArg, args, argiter) { CtorArg const &arg = *(argiter.data()); emitXmlField_AttributeParseRule(arg.type, arg.name, baseName, NULL); } } void XmlParserGen::emitXmlFields_AttributeParseRule (ASTList const &decls, string &baseName) { FOREACH_ASTLIST(Annotation, decls, iter) { if (!iter.data()->isUserDecl()) continue; UserDecl const *ud = iter.data()->asUserDeclC(); if (!ud->amod->hasModPrefix("xml")) continue; emitXmlField_AttributeParseRule(extractFieldType(ud->code), extractFieldName(ud->code), baseName, ud->amod); } } void XmlParserGen::emitXmlField_AttributeParseRule (rostring type0, rostring name, string &baseName, AccessMod *amod) { string type = trimWhitespace(type0); // cout << "emitXmlField_AttributeParseRule() name:" << name << ", type:" << type << endl; if (streq(type, "string")) { parser1_defs << " case XTOK_" << name << ":\n"; parser1_defs << " obj->" << name << " = strdup(parseQuotedString(strValue));\n"; parser1_defs << " break;\n"; } else if (streq(type, "StringRef")) { parser1_defs << " case XTOK_" << name << ":\n"; parser1_defs << " obj->" << name << " = manager->strTable(parseQuotedString(strValue));\n"; parser1_defs << " break;\n"; } else if (streq(type, "bool")) { parser1_defs << " case XTOK_" << name << ":\n"; parser1_defs << " fromXml_bool(obj->" << name << ", parseQuotedString(strValue));\n"; parser1_defs << " break;\n"; } else if (streq(type, "int")) { parser1_defs << " case XTOK_" << name << ":\n"; parser1_defs << " fromXml_int(obj->" << name << ", parseQuotedString(strValue));\n"; parser1_defs << " break;\n"; } else if (streq(type, "unsigned int")) { parser1_defs << " case XTOK_" << name << ":\n"; parser1_defs << " fromXml_unsigned_int(obj->" << name << ", parseQuotedString(strValue));\n"; parser1_defs << " break;\n"; } else if (streq(type, "long")) { parser1_defs << " case XTOK_" << name << ":\n"; parser1_defs << " fromXml_long(obj->" << name << ", parseQuotedString(strValue));\n"; parser1_defs << " break;\n"; } else if (streq(type, "unsigned long")) { parser1_defs << " case XTOK_" << name << ":\n"; parser1_defs << " fromXml_unsigned_long(obj->" << name << ", parseQuotedString(strValue));\n"; parser1_defs << " break;\n"; } else if (streq(type, "double")) { parser1_defs << " case XTOK_" << name << ":\n"; parser1_defs << " fromXml_double(obj->" << name << ", parseQuotedString(strValue));\n"; parser1_defs << " break;\n"; } else if (streq(type, "SourceLoc")) { parser1_defs << " case XTOK_" << name << ":\n"; // FIX: we don't parse SourceLoc-s yet parser1_defs << " // fromXml_SourceLoc(obj->" << name << ", parseQuotedString(strValue));\n"; parser1_defs << " break;\n"; } else if (isListType(type)) { parser1_defs << " case XTOK_" << name << ":\n"; parser1_defs << " manager->unsatLinks_List.append(new UnsatLink(" << "(void*) &(obj->" << name << "), parseQuotedString(strValue), " << "XTOK_List_" << baseName << "_" << name << ", true));\n"; parser1_defs << " break;\n"; } else if (isFakeListType(type)) { parser1_defs << " case XTOK_" << name << ":\n"; parser1_defs << " manager->unsatLinks_List.append(new UnsatLink(" << "(void*) &(obj->" << name << "), parseQuotedString(strValue), " << "XTOK_List_" << baseName << "_" << name << ", true));\n"; parser1_defs << " break;\n"; } else if (isTreeNode(type) || (isTreeNodePtr(type))) { parser1_defs << " case XTOK_" << name << ":\n"; parser1_defs << " manager->unsatLinks.append(new UnsatLink(" << "(void*) &(obj->" << name << "), parseQuotedString(strValue)," << "XTOK_" << extractNodeType(type) << ", false));\n"; parser1_defs << " break;\n"; } else if (isPtrKind(type)) { // catch-all for objects parser1_defs << " case XTOK_" << name << ":\n"; parser1_defs << " manager->unsatLinks.append(new UnsatLink(" << "(void*) &(obj->" << name << "), parseQuotedString(strValue)," << "XTOK_" << extractNodeType(type) << ", false));\n"; parser1_defs << " break;\n"; } else if (amod && amod->hasModPrefix("xmlEmbed")) { // embedded thing rostring kind = amod->getModSuffixFromPrefix("xmlEmbed"); parser1_defs << " case XTOK_" << name << ":\n"; parser1_defs << " manager->unsatLinks" << kind << ".append(new UnsatLink(" << "(void*) &(obj->" << name << "), parseQuotedString(strValue)," << "XTOK" << kind << "_" << baseName << "_" << name << ", true));\n"; parser1_defs << " break;\n"; } else { // catch-all for non-objects parser1_defs << " case XTOK_" << name << ":\n"; parser1_defs << " fromXml(obj->" << name << ", parseQuotedString(strValue));\n"; parser1_defs << " break;\n"; } } void XmlParserGen::emitXmlParser_objCtorArgs (ASTList const &args, bool &firstTime) { FOREACH_ASTLIST(CtorArg, args, argiter) { CtorArg const &arg = *(argiter.data()); if (firstTime) { firstTime = false; } else { parser2_ctorCalls << ", "; } if (strlen(arg.defaultValue.c_str())>0) { parser2_ctorCalls << arg.defaultValue; } else { // dsw: this is Scott's idea of how to initialize a type that we // know nothing about with no information string type = arg.type; if (isListType(arg.type)) { // need to put a pointer onto the end of ASTList types type = stringc << type << "*"; } else if (isTreeNode(arg.type)) { // dsw: FIX: I should probably have something like this here // (isTreeNodePtr(type) && isOwner) // // also for AST node types type = stringc << type << "*"; } parser2_ctorCalls << "(" << type << ")0"; } } } void XmlParserGen::emitXmlParser_Node (ASTClass const *clazz, ASTList const *args, ASTList const *decls, ASTList const *lastArgs, // these two default to NULL, which is used in the case of a top // level class with no subclasses ASTList const *childArgs, ASTList const *childDecls) { string name = clazz->name; parser0_decls << "void registerAttr_" << name << "(" << name << " *obj, int attr, char const *strValue);\n"; parser3_registerCalls << " case XTOK_" << name << ":\n" << " registerAttr_" << name << "((" << name << "*)target, attr, yytext0);\n" << " break;\n"; // we need to supply however many NULL args here as there are ctor args. parser2_ctorCalls << " case XTOK_" << name << ":\n" << " return new " << name << "("; bool firstTime = true; if (args) {emitXmlParser_objCtorArgs(*args, firstTime);} if (childArgs) {emitXmlParser_objCtorArgs(*childArgs, firstTime);} if (lastArgs) {emitXmlParser_objCtorArgs(*lastArgs, firstTime);} parser2_ctorCalls << ");\n" << " break;\n"; } void XmlParserGen::emitXmlParser_Node_registerAttr (ASTClass const *clazz, ASTList const *args, ASTList const *decls, ASTList const *lastArgs, // these two default to NULL, which is used in the case of a top // level class with no subclasses ASTList const *childArgs, ASTList const *childDecls) { string name = clazz->name; parser1_defs << "\nvoid ASTXmlReader::registerAttr_" << name << "(" << name << " *obj, int attr, char const *strValue) {\n"; parser1_defs << " switch(attr) {\n"; parser1_defs << " default:\n"; parser1_defs << " userError(\"illegal attribute for a " << name << "\");\n"; parser1_defs << " break;\n"; // for each attribute, emit a rule to parse it as an attribute of this tag emitXmlCtorArgs_AttributeParseRule(*args, name); emitXmlFields_AttributeParseRule(*decls, name); emitXmlCtorArgs_AttributeParseRule(*lastArgs, name); if (childArgs) { xassert(childDecls); emitXmlCtorArgs_AttributeParseRule(*childArgs, name); emitXmlFields_AttributeParseRule(*childDecls, name); } parser1_defs << " }\n"; parser1_defs << "}\n"; } void XmlParserGen::emitXmlParser_ASTList(ListClass const *cls) { string name = stringc << "List_" << cls->classAndMemberName; // only one rule as lists are homogeneous xassert(isTreeNode(cls->elementClassName)); parser2_ctorCalls << " case XTOK_" << name << ":\n" // NOTE: yes, this should say 'new ASTList' even in the case of // FakeLists; ASTLists are also used as "generic lists". << " return new ASTList<" << cls->elementClassName << ">();\n" << " break;\n"; } void XmlParserGen::emitXmlParserImplementation() { tokensOutH << " // AST nodes\n"; tokensOutCC << " // AST nodes\n"; lexerOut << " /* AST nodes */\n"; parser1_defs << "bool ASTXmlReader::kind2kindCat(int kind, KindCategory *kindCat) {\n"; parser1_defs << " switch(kind) {\n"; parser1_defs << " default: return false; // don't know this kind\n"; SFOREACH_OBJLIST(TF_class, allClasses, iter) { TF_class const *c = iter.data(); tokensOutH << " XTOK_" << c->super->name << ", // \"" << c->super->name << "\"\n"; tokensOutCC << " \"XTOK_" << c->super->name << "\",\n"; lexerOut << "\"" << c->super->name << "\" return tok(XTOK_" << c->super->name << ");\n"; parser1_defs << " case XTOK_" << c->super->name << ": *kindCat = KC_Node; break;\n"; collectXmlParserCtorArgs(c->super->args, "obj"); collectXmlParserFields(c->super->decls, "obj"); collectXmlParserCtorArgs(c->super->lastArgs, "obj"); if (c->hasChildren()) { FOREACH_ASTLIST(ASTClass, c->ctors, iter) { ASTClass const *clazz = iter.data(); tokensOutH << " XTOK_" << clazz->name << ", // \"" << clazz->name << "\"\n"; tokensOutCC << " \"XTOK_" << clazz->name << "\",\n"; lexerOut << "\"" << clazz->name << "\" return tok(XTOK_" << clazz->name << ");\n"; parser1_defs << " case XTOK_" << clazz->name << ": *kindCat = KC_Node; break;\n"; collectXmlParserCtorArgs(clazz->args, "obj0"); collectXmlParserFields(clazz->decls, "obj0"); emitXmlParser_Node(clazz, &c->super->args, &c->super->decls, &c->super->lastArgs, &clazz->args, &clazz->decls); } } else { emitXmlParser_Node(c->super, &c->super->args, &c->super->decls, &c->super->lastArgs); } } tokensOutH << "\n // List 'classes'\n"; tokensOutCC << "\n // List 'classes'\n"; FOREACH_ASTLIST(ListClass, listClasses, iter) { ListClass const *cls = iter.data(); tokensOutH << " XTOK_List_" << cls->classAndMemberName << ", // \"List_" << cls->classAndMemberName << "\"\n"; tokensOutCC << " \"XTOK_List_" << cls->classAndMemberName << "\",\n"; lexerOut << "\"List_" << cls->classAndMemberName << "\" return tok(XTOK_List_" << cls->classAndMemberName << ");\n"; parser1_defs << " case XTOK_List_" << cls->classAndMemberName << ": *kindCat = "; if (cls->lkind == LK_FakeList) { parser1_defs << "KC_FakeList"; } else if (cls->lkind == LK_ASTList) { parser1_defs << "KC_ASTList"; } else xfailure("illegal list kind"); parser1_defs << "; break;\n"; emitXmlParser_ASTList(cls); } parser1_defs << " }\n"; parser1_defs << " return true;\n"; parser1_defs << "}\n"; SFOREACH_OBJLIST(TF_class, allClasses, iter) { TF_class const *c = iter.data(); if (c->hasChildren()) { FOREACH_ASTLIST(ASTClass, c->ctors, iter) { ASTClass const *clazz = iter.data(); emitXmlParser_Node_registerAttr (clazz, &c->super->args, &c->super->decls, &c->super->lastArgs, &clazz->args, &clazz->decls); } } else { emitXmlParser_Node_registerAttr (c->super, &c->super->args, &c->super->decls, &c->super->lastArgs); } } // generate the method that says we should not record the kind of // any tag that is parsed as there is no multiple inheritance going // on in the AST parser1_defs << "\nbool ASTXmlReader::recordKind(int kind, bool& answer) {\n"; parser1_defs << " switch(kind) {\n"; parser1_defs << " default: return false; break;\n"; SFOREACH_OBJLIST(TF_class, allClasses, iter) { TF_class const *c = iter.data(); if (c->hasChildren()) { FOREACH_ASTLIST(ASTClass, c->ctors, iter) { string name = iter.data()->name; parser1_defs << " case XTOK_" << name << ": answer = false; return true; break;\n"; } } else { string name = c->super->name; parser1_defs << " case XTOK_" << name << ": answer = false; return true; break;\n"; } } FOREACH_ASTLIST(ListClass, listClasses, iter) { ListClass const *cls = iter.data(); parser1_defs << " case XTOK_List_" << cls->classAndMemberName << ": answer = false; return true; break;\n"; } parser1_defs << " }\n"; parser1_defs << "}\n"; // generate the method that says we should not record the kind of // any tag that is parsed as there is no multiple inheritance going // on in the AST parser1_defs << "\nbool ASTXmlReader::upcastToWantedType"; parser1_defs << "(void *obj, int kind, void **target, int targetKind) {\n"; parser1_defs << " switch(kind) {\n"; parser1_defs << " default: return false; break;\n"; SFOREACH_OBJLIST(TF_class, allClasses, iter) { TF_class const *c = iter.data(); if (c->hasChildren()) { FOREACH_ASTLIST(ASTClass, c->ctors, iter) { parser1_defs << " case XTOK_" << iter.data()->name << ":\n"; } } else { parser1_defs << " case XTOK_" << c->super->name << ":\n"; } } FOREACH_ASTLIST(ListClass, listClasses, iter) { parser1_defs << " case XTOK_List_" << iter.data()->classAndMemberName << ":\n"; } parser1_defs << " xfailure(\"should never be called\"); return true; break;\n"; parser1_defs << " }\n"; parser1_defs << "}\n"; // generate the method that says we should not have to call // operator=() on any objects because no AST nodes embed into one // another parser1_defs << "\nbool ASTXmlReader::callOpAssignToEmbeddedObj"; parser1_defs << "(void *obj, int kind, void *target) {\n"; parser1_defs << " switch(kind) {\n"; parser1_defs << " default: return false; break;\n"; SFOREACH_OBJLIST(TF_class, allClasses, iter) { TF_class const *c = iter.data(); if (c->hasChildren()) { FOREACH_ASTLIST(ASTClass, c->ctors, iter) { parser1_defs << " case XTOK_" << iter.data()->name << ":\n"; } } else { parser1_defs << " case XTOK_" << c->super->name << ":\n"; } } FOREACH_ASTLIST(ListClass, listClasses, iter) { parser1_defs << " case XTOK_List_" << iter.data()->classAndMemberName << ":\n"; } parser1_defs << " xfailure(\"should never be called\"); return true; break;\n"; parser1_defs << " }\n"; parser1_defs << "}\n"; // generate generic FakeList reverse parser1_defs << "bool ASTXmlReader::convertList2FakeList"; parser1_defs << "(ASTList *list, int listKind, void **target) {\n"; parser1_defs << " switch(listKind) {\n"; parser1_defs << " default: return false; // we did not find a matching tag\n"; FOREACH_ASTLIST(ListClass, listClasses, iter) { ListClass const *cls = iter.data(); if (cls->lkind != LK_FakeList) continue; parser1_defs << " case XTOK_List_" << cls->classAndMemberName << ": {\n"; parser1_defs << " xassert(list);\n"; parser1_defs << " FakeList<" << cls->elementClassName << "> *ret = NULL;\n"; parser1_defs << " FakeList<" << cls->elementClassName << "> *prev = NULL;\n"; parser1_defs << " FOREACH_ASTLIST_NC(" << cls->elementClassName << ",\n"; parser1_defs << " reinterpret_castelementClassName << ">&>(*list),\n"; parser1_defs << " iter) {\n"; parser1_defs << " if (prev) {\n"; parser1_defs << " prev->first()->next = iter.data();\n"; parser1_defs << " prev = FakeList<" << cls->elementClassName << ">::makeList(prev->first()->next);\n"; parser1_defs << " } else {\n"; parser1_defs << " ret = FakeList<" << cls->elementClassName << ">::makeList(iter.data());\n"; parser1_defs << " prev = ret;\n"; parser1_defs << " }\n"; parser1_defs << " }\n"; parser1_defs << " *target = ret;"; parser1_defs << " break;\n"; parser1_defs << " }\n"; } parser1_defs << " }\n"; parser1_defs << " return true;\n"; parser1_defs << "}\n"; tokensOutH << "\n"; tokensOutH << " // metadata tags that do not occur in the AST itself\n"; tokensOutH << " XTOK__List_Item, // \"_List_Item\"\n"; tokensOutH << " // metadata attributes that do not occur in the AST itself\n"; tokensOutH << " XTOK_DOT_ID, // \"_id\"\n"; tokensOutH << " XTOK_item, // \"item\"\n"; tokensOutCC << "\n"; tokensOutCC << " // metadata tags that do not occur in the AST itself\n"; tokensOutCC << " \"XTOK__List_Item\", // \"_List_Item\"\n"; tokensOutCC << " // metadata attributes that do not occur in the AST itself\n"; tokensOutCC << " \"XTOK_item\", // \"item\"\n"; tokensOutCC << " \"XTOK_DOT_ID\", // \"_id\"\n"; lexerOut << "\n"; lexerOut << " /* metadata tags that do not occur in the AST itself */\n"; lexerOut << "\"_List_Item\" return tok(XTOK__List_Item);\n"; lexerOut << " /* metadata attributes that do not occur in the AST itself */\n"; lexerOut << "\"item\" return tok(XTOK_item);\n"; lexerOut << "\"_id\" return tok(XTOK_DOT_ID);\n"; tokensOutH << "\n"; tokensOutH << " // child attribute names\n"; tokensOutCC << "\n"; tokensOutCC << " // child attribute names\n"; lexerOut << "\n"; lexerOut << " /* child attribute names */\n"; FOREACH_STRINGSET(attributeNames, attrIter) { string const &attr = attrIter.data(); tokensOutH << " XTOK_" << attr << ", // \"" << attr << "\"\n"; tokensOutCC << " \"XTOK_" << attr << "\",\n"; lexerOut << "\"" << attr << "\" return tok(XTOK_" << attr << ");\n"; } } // -------------------- extension merging ------------------ // the 'allClasses' list is filled in after merging, so I // can't use it in this section #define allClasses NO! void mergeClass(ASTClass *base, ASTClass *ext) { xassert(base->name.equals(ext->name)); trace("merge") << "merging class: " << ext->name << endl; // move all ctor args to the base while (ext->args.isNotEmpty()) { base->args.append(ext->args.removeFirst()); } // and same for annotations while (ext->decls.isNotEmpty()) { base->decls.append(ext->decls.removeFirst()); } } void mergeEnum(TF_enum *base, TF_enum *ext) { xassert(base->name.equals(ext->name)); trace("merge") << "merging enum: " << ext->name << endl; while (ext->enumerators.isNotEmpty()) { base->enumerators.append(ext->enumerators.removeFirst()); } } ASTClass *findClass(TF_class *base, rostring name) { FOREACH_ASTLIST_NC(ASTClass, base->ctors, iter) { if (iter.data()->name.equals(name)) { return iter.data(); } } return NULL; // not found } void mergeSuperclass(TF_class *base, TF_class *ext) { // should only get here for same-named classes xassert(base->super->name.equals(ext->super->name)); trace("merge") << "merging superclass: " << ext->super->name << endl; // merge the superclass ctor args and annotations mergeClass(base->super, ext->super); // for each subclass, either add it or merge it while (ext->ctors.isNotEmpty()) { ASTClass * /*owner*/ c = ext->ctors.removeFirst(); ASTClass *prev = findClass(base, c->name); if (prev) { mergeClass(prev, c); delete c; } else { // add it wholesale trace("merge") << "adding subclass: " << c->name << endl; base->ctors.append(c); } } } TF_class *findSuperclass(ASTSpecFile *base, rostring name) { // I can *not* simply iterate over 'allClasses', because that // list is created *after* merging! FOREACH_ASTLIST_NC(ToplevelForm, base->forms, iter) { ToplevelForm *tf = iter.data(); if (tf->isTF_class() && tf->asTF_class()->super->name.equals(name)) { return tf->asTF_class(); } } return NULL; // not found } TF_enum *findEnum(ASTSpecFile *base, rostring name) { FOREACH_ASTLIST_NC(ToplevelForm, base->forms, iter) { ToplevelForm *tf = iter.data(); if (tf->isTF_enum() && tf->asTF_enum()->name.equals(name)) { return tf->asTF_enum(); } } return NULL; // not found } void mergeExtension(ASTSpecFile *base, ASTSpecFile *ext) { // for each toplevel form, either add it or merge it int ct = 0; while (ext->forms.isNotEmpty()) { ToplevelForm * /*owner*/ tf = ext->forms.removeFirst(); if (tf->isTF_class()) { TF_class *c = tf->asTF_class(); // is the superclass name something present in the // base specification? TF_class *prev = findSuperclass(base, c->super->name); if (prev) { mergeSuperclass(prev, c); delete c; } else { // add the whole class trace("merge") << "adding new superclass: " << c->super->name << endl; base->forms.append(c); } } else if (tf->isTF_enum()) { TF_enum *e = tf->asTF_enum(); // is the enum present in the base? TF_enum *prev = findEnum(base, e->name); if (prev) { mergeEnum(prev, e); delete e; } else { // add the whole enum trace("merge") << "adding new enum: " << e->name << endl; base->forms.append(e); } } else { // verbatim or option: just add it directly if (ct == 0) { // *first* verbatim: goes into a special place in the // base, before any classes but after any base verbatims int i; for (i=0; i < base->forms.count(); i++) { ToplevelForm *baseForm = base->forms.nth(i); if (baseForm->isTF_class()) { // ok, this is the first class, so stop here // and we'll insert at 'i', thus inserting // just before this class break; } } // insert the base so it becomes position 'i' trace("merge") << "inserting extension verbatim near top\n"; base->forms.insertAt(tf, i); } else { // normal processing: append everything trace("merge") << "appending extension verbatim/option section\n"; base->forms.append(tf); } } ct++; } } // re-enable allClasses #undef allClasses void recordListClass(ListKind lkind, rostring className, CtorArg const *arg) { rostring argName = arg->name; ListClass *cls = new ListClass (lkind, stringc << className << "_" << argName, extractListType(arg->type)); if (!listClassesSet.contains(cls->classAndMemberName)) { listClassesSet.add(cls->classAndMemberName); listClasses.append(cls); } else { delete cls; } } void getListClasses(rostring className, CtorArg const *arg) { // I would rather visit all the lists, but we don't seem to generate // visit() traversals for non-tree nodes, so there is nothing to put // inside the nesting if (isListType(arg->type) && isTreeNode(extractListType(arg->type))) { recordListClass(LK_ASTList, className, arg); } if (isFakeListType(arg->type) && isTreeNode(extractListType(arg->type))) { recordListClass(LK_FakeList, className, arg); } } void getListClasses(ASTClass const *c) { rostring className = c->name; FOREACH_ASTLIST(CtorArg, c->args, ctorIter) { getListClasses(className, ctorIter.data()); } FOREACH_ASTLIST(CtorArg, c->lastArgs, ctorIter) { getListClasses(className, ctorIter.data()); } } void getListClasses() { SFOREACH_OBJLIST(TF_class, allClasses, iter) { TF_class const *cls = iter.data(); getListClasses(cls->super); FOREACH_ASTLIST(ASTClass, cls->ctors, ctorIter) { getListClasses(ctorIter.data()); } } } // --------------------- toplevel control ---------------------- void checkUnusedCustoms(ASTClass const *c) { FOREACH_ASTLIST(Annotation, c->decls, iter) { Annotation const *a = iter.data(); if (a->isCustomCode()) { CustomCode const *cc = a->asCustomCodeC(); if (cc->used == false) { cout << "warning: unused custom code `" << cc->qualifier << "'\n"; } } } } void grabOptionName(rostring opname, string &oparg, TF_option const *op) { if (op->args.count() != 1) { xfatal("'" << opname << "' option requires one argument"); } if (oparg.length() > 0) { // It would be conceivable to allow multiple visitors, but // I don't see any advantage to doing so. If the extension // simply changes the name, then the resulting error messages // (compilation errors from parts of the system using the // old name) are not obvious to diagnose. xfatal("there is already " << a_or_an(opname) << " class, called " << oparg << ";\n" << "you should use (subclass) that one"); } // name of the visitor interface class oparg = *( op->args.firstC() ); } void entry(int argc, char **argv) { TRACE_ARGS(); checkHeap(); SourceLocManager mgr; if (argc < 2) { cout << "usage: " << argv[0] << " [options] file.ast [extension.ast [...]]\n" << " options:\n" << " -o output filenames are name.{h,cc}\n" << " (default is \"file\" for \"file.ast\")\n" << " -v verbose operation, particularly for merging\n" << " -nocvr do not use covariant return types in clone()\n" ; return; } char const *basename = NULL; // nothing set argv++; while (argv[0][0] == '-') { if (argv[0][1] == 'b' || // 'b' is for compatibility argv[0][1] == 'o') { if (argv[0][2]) { // name specified directly after the -o basename = argv[0]+2; } else if (argv[1]) { // name follows the -o option basename = argv[1]; argv++; } } else if (argv[0][1] == 'v') { traceAddSys("merge"); } else if (streq(argv[0], "-nocvr")) { nocvr = true; } else { xfatal("unknown option: " << argv[0]); } argv++; } if (!argv[0]) { xfatal("missing ast spec file name"); } char const *srcFname = argv[0]; argv++; // parse the grammar spec Owner ast; ast = readAbstractGrammar(srcFname); wholeAST = ast; // parse and merge extension modules ObjList modules; while (*argv) { char const *fname = *argv; argv++; modules.append(new string(fname)); Owner extension; extension = readAbstractGrammar(fname); mergeExtension(ast, extension); } // scan options, and fill 'allClasses' { FOREACH_ASTLIST_NC(ToplevelForm, ast->forms, iter) { if (iter.data()->isTF_option()) { TF_option const *op = iter.data()->asTF_optionC(); if (op->name.equals("visitor")) { grabOptionName("visitor", visitorName, op); } else if (op->name.equals("dvisitor")) { grabOptionName("dvisitor", dvisitorName, op); } else if (op->name.equals("xmlVisitor")) { grabOptionName("xmlVisitor", xmlVisitorName, op); } else if (op->name.equals("xmlParser")) { grabOptionName("xmlParser", xmlParserName, op); } else if (op->name.equals("mvisitor")) { grabOptionName("mvisitor", mvisitorName, op); } else if (op->name.equals("xmlPrint")) { wantXMLPrint = true; } else if (op->name.equals("gdb")) { wantGDB = true; } else { xfatal("unknown option: " << op->name); } } else if (iter.data()->isTF_class()) { allClasses.prepend(iter.data()->asTF_class()); } } } // I built it in reverse for O(n) performance allClasses.reverse(); // generate the header string base = replace(srcFname, ".ast", ""); if (basename) { base = basename; } // get all of the list classes getListClasses(); // dsw: I want a way to generate just the parser and nothing else; // this separation into two blocks suggests that parsing the ast // file and generating something from it should perhaps be separated // into two parts if (!tracingSys("no_ast.gen")) { string hdrFname = base & ".h"; HGen hg(srcFname, modules, hdrFname, *ast); cout << "writing " << hdrFname << "...\n"; hg.emitFile(); // generated the c++ code string codeFname = base & ".cc"; CGen cg(srcFname, modules, codeFname, *ast, hdrFname); cout << "writing " << codeFname << "...\n"; cg.emitFile(); // dsw: the xml parser stuff won't use the custom sections, so // this generates a lot of spurious warnings if I put it outside // of this block // // check for any unused 'custom' sections; a given section is only // used if one of the generation routines asks for it by name, so // a mistyped custom section name would not yet have been noticed { SFOREACH_OBJLIST(TF_class, allClasses, iter) { TF_class const *c = iter.data(); checkUnusedCustoms(c->super); FOREACH_ASTLIST(ASTClass, c->ctors, subIter) { checkUnusedCustoms(subIter.data()); } } FOREACH_ASTLIST(ToplevelForm, ast->forms, iter2) { if (iter2.data()->isTF_custom()) { CustomCode const *cc = iter2.data()->asTF_customC()->cust; if (cc->used == false) { cout << "warning: unused custom code `" << cc->qualifier << "'\n"; } } } } } // dsw: this is really completely independent of the other generated // files if (tracingSys("xmlParser") && wantXmlParser()) { XmlParserGen xgen(xmlParserName); xgen.emitXmlParserImplementation(); } } ARGS_MAIN