Skip site navigation (1)Skip section navigation (2)
Date:      18 Apr 2002 05:53:51 -0700
From:      Max Okumoto <okumoto@ucsd.edu>
To:        freebsd-libh@FreeBSD.ORG
Subject:   *.cd.cc parser
Message-ID:  <hf662p417k.fsf@multivac.sdsc.edu>

next in thread | raw e-mail | index | archive | help

Here is my *cd.cc parser can people check if it parses and generates
the same kind of output as the perl version?  Currently the only difference
that I can find is the sort order of the header includes.  The ordering
is for perl hashs vs C++ map<>.  Which should not cause any problems...

I don't really plan to use it in the build process, since that would
complicate matters.  I am looking into merging it with HSystem.

*.cd.cc -> build_systems_*.cc -> tcl_interface_gen_* -> LibTclInterface_*.cc 
would become 
 
*.cd.cc -> LibTclInterface_*.cc 

		Max Okumoto

---------------------%<----------------%<--------------
/**
 * @file
 */

#include <fstream>
#include <map>
#include <set>
#include <string>
#include <vector>

extern "C" {
    #include <unistd.h>		/* getopt() */
    #include <string.h>		/* strlen() */
}

class ClassDescParser {
    typedef string			FilePath;
    typedef string			DirPath;
    typedef string			SysName;
    typedef string			ClassDesc;

    typedef set<FilePath>		FileSet;
    typedef vector<ClassDesc>		ClassDescV;
    typedef map<FilePath, ClassDescV>	MapV;
    typedef map<DirPath, SysName>	Map;

    FileSet	files;
    MapV	classes;
    Map		systems;

public:
    /**
     * Process the contents of the ClassDescription file (*.cd.cc).
     *
     * @param filename	name of file to process.
     */
    void
    process_file(const string &filename)
    {
	ifstream	file(filename.c_str());

	char		buff[4096];
	while (file.getline(buff, sizeof(buff))) {
	    string	line(buff);

	    /* remove C++ style comments from line */
	    string::size_type pos = line.find("//");
	    if (pos != string::npos) {
		line.erase(pos);
	    }


	   /*
	    * LanguageInterface::Object::ClassDescription ([^\s]+)::sClassDescription[^A-Za-z0-9]/
	    */
	    string prefix("LanguageInterface::Object::ClassDescription ");
	    string suffix("::sClassDescription");

	    /* Find lines which contain prefix */
	    pos = line.find(prefix);
	    if (pos == string::npos) {
		continue;	/* line does not contain prefix */
	    }
	    string	cd = line.substr(pos + prefix.length());

	    pos = cd.find(suffix);

	    /* class description must also contain the suffix */
	    assert(pos != string::npos);

	    /* char following suffix should not be alpha or number */
	    assert(!isalnum(cd[pos + suffix.length()]));

	    cd.erase(pos);

	    assert(cd.find_first_of(" \t") == string::npos);

	    classes[filename].push_back(cd);
	}

	file.close();

	files.insert(filename);
    }

    /**
     * Output the build_systems_*.cc file.
     *
     * @param prog		Name of generator program.
     * @param the_only_system	(Don't know yet.)
     */
    void
    gen_file(const string &prog, const string *the_only_system)
    {
	cout << "/*\n"
		" * This file is automatically generated by " << prog << ",\n"
		" * do not edit!\n"
		" */\n"
		"\n"
		"#ifndef HSystem_hh\n"
		"#include \"HSystem.hh\"\n"
		"#endif\n"
		"\n"
		"#define H_BUILD_SYSTEMS 1\n"
		"\n";

	for (MapV::iterator i = classes.begin(); i != classes.end(); ++i) {
	    FilePath		filename = i->first;
	    string::size_type	pos = filename.rfind('/');

	    assert(pos != string::npos); /* contains at least 1 slash */
	    string		system = filename.substr(0, pos);
	    string		file   = filename.substr(pos + 1);
	
	    if (the_only_system == NULL) {
		pos = system.rfind('/');
		assert(pos != string::npos);	/* contains 1 slash */

		systems[system] = system.substr(pos + 1);
	    } else {
		systems[*the_only_system] = *the_only_system;
	    }

	    pos = system.rfind('/');
	    pos = system.rfind('/', pos);
	    string	systemdir = system.substr(pos + 1);

	    /* remove ".cd.cc" suffix */
	    pos = file.find(".cd.cc");
	    assert(pos != string::npos); /* contains ".cd.cc" */
	    assert(file.length() == pos + strlen(".cd.cc"));	/* at end */
	    file.erase(pos);

	    if (systemdir == "common") {
		cout << "#ifndef " << file << "_hh\n"
			"#include \"" << file << ".hh\"\n"
			"#endif\n"
			"\n";
	    } else {
		cout << "#ifndef " << file << "_hh\n"
			"#include \"" << systemdir << "/" << file << ".hh\"\n"
			"#endif\n"
			"\n";
	    }
	}

	cout << "void\n"
		"build_systems(Systems& systems)\n"
		"{\n";

	for (Map::iterator i = systems.begin(); i != systems.end(); ++i) {
	    DirPath	system	= i->first;
	    SysName	sysname	= i->second;

	    cout <<
		"\tpair<Systems::iterator,bool> s = systems.insert(HSystem(\"" << sysname << "\"));\n"
		"\tif (!s.second)\n"
		"\t	throw runtime_error(string(\"System \") + s.first->name + \" already exists\");\n"
		"\tHSystem& system = const_cast<HSystem&>(*s.first);\n";

	    for (FileSet::iterator j = files.begin(); j != files.end(); ++j) {
		FilePath filename = *j;

		if ((the_only_system != NULL) || (filename.substr(0, system.length() + 1) == (system + '/'))) {
		    ClassDescV cl = classes[filename];
		    for (ClassDescV::iterator k = cl.begin(); k != cl.end(); ++k) {
			cout <<
			    "\tsystem.add_class(" << *k << "::thisClassDescription(),\n"
			    "\t\t\t" << *k << "::fileToInclude());\n";
		    }
		}
	    }
	}

	cout << "} /* build systems */ \n"
		"\n";
    }
};

/**
 * Outputs usage information.
 *
 * @param prog		Name of binary
 */
static void
usage(const string &prog)
{
    cout << "Usage: " << prog << " -s <system> <file-to-scan> ...\n";
}

/**
 * The main program.
 *
 * It parses the command line arguments, and checks for input files.
 */
int
main(int argc, char *argv[])
{
    /*************************************************************************
     * Parse command line args
     *************************************************************************/
    const string	prog(argv[0]);

    string		*the_only_system = NULL;

    int c;
    while ((c = getopt(argc, argv, "s:")) != -1) {
	switch (c) {
	case 's':
	    the_only_system = new string(optarg);
	    break;
	case '?':
	default:
	    usage(prog);
	    return(1);	/* bad cli options */
	}  
    }
    argc -= optind;
    argv += optind;

    if (argc < 1) {
	usage(prog);
	return(1);	/* missing input files */
    }

    /*************************************************************************
     * process files
     *************************************************************************/

    ClassDescParser	parser;

    for (int i = 0; i < argc; ++i) {
	parser.process_file(argv[i]);
    }

    parser.gen_file(prog, the_only_system);

    delete the_only_system;
    return(0);
}


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-libh" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?hf662p417k.fsf>