[cig-commits] r6911 - cs/babel/trunk
leif at geodynamics.org
leif at geodynamics.org
Wed May 16 18:40:31 PDT 2007
Author: leif
Date: 2007-05-16 18:40:31 -0700 (Wed, 16 May 2007)
New Revision: 6911
Added:
cs/babel/trunk/babel.cc
Log:
Wrote an ASTVisitor that automatically generates Python bindings for C
code.
Added: cs/babel/trunk/babel.cc
===================================================================
--- cs/babel/trunk/babel.cc 2007-05-17 00:30:57 UTC (rev 6910)
+++ cs/babel/trunk/babel.cc 2007-05-17 01:40:31 UTC (rev 6911)
@@ -0,0 +1,555 @@
+
+#include <iostream.h> // cout
+#include <stdlib.h> // exit, atoi
+
+#include "parssppt.h" // ParseTreeAndTokens
+#include "srcloc.h" // SourceLocManager
+#include "cc_env.h" // Env
+#include "cc_ast.h" // C++ AST (r)
+#include "cc_lang.h" // CCLang
+#include "parsetables.h" // ParseTables
+#include "cc.gr.gen.h" // CCParse
+#include "strtokp.h" // StrtokParse
+
+#include "cc_elaborate.h" // ElabVisitor
+
+#include "cc_print.h" // PrintEnv
+
+
+// ---------------------- PyExtModuleVisitor ------------------------
+
+
+class PyExtModuleGenerator : public ASTVisitor {
+private: // data
+ // name of module
+ std::string name;
+ // C/C++ source filename
+ std::string source;
+
+ enum State {
+ internDecls,
+ methods,
+ internTable,
+ methodTable,
+ };
+ State state;
+
+public: // funcs
+ PyExtModuleGenerator(std::string n, std::string s);
+ virtual ~PyExtModuleGenerator() {}
+
+ void generate(TranslationUnit *unit);
+ void generatePreamble();
+ void generatePostamble();
+ void generateModInitFunc();
+
+ virtual bool visitExpression(Expression *obj);
+ virtual bool visitDeclarator(Declarator *obj);
+ virtual bool visitFunction(Function *f);
+
+ bool visitFunctionVariable(Variable *var);
+ void writePyMethod(Variable *var);
+ void scanFunctionParams(Variable *var,
+ std::string &format,
+ std::string &argnames,
+ std::string &varargs,
+ std::string &args);
+ const char *returnValueConversionFunction(Variable *var);
+ void xxx(Variable *var);
+
+ virtual bool visitMember(Member *obj);
+
+ virtual bool visitTypeSpecifier(TypeSpecifier *ts);
+
+};
+
+
+PyExtModuleGenerator::PyExtModuleGenerator(std::string n, std::string s) :
+ name(n),
+ source(s)
+{
+}
+
+
+void PyExtModuleGenerator::generate(TranslationUnit *unit)
+{
+ cout << "/* Generated by Babel */" << endl
+ << endl;
+
+ cout << "#include \"" << source << "\"" << endl
+ << endl;
+
+ cout << "#include \"Python.h\"" << endl
+ << endl;
+
+ this->generatePreamble();
+
+ state = internDecls;
+ unit->traverse(*this);
+ cout << endl;
+
+ state = methods;
+ unit->traverse(*this);
+ cout << endl;
+
+ state = internTable;
+ cout << "static __Babel_InternTabEntry __babel_intern_tab[] = {" << endl;
+ unit->traverse(*this);
+ cout << " {0, 0}" << endl
+ << "};" << endl
+ << endl;
+
+ state = methodTable;
+ cout << "static struct PyMethodDef __babel_methods[] = {" << endl;
+ unit->traverse(*this);
+ cout << " {0, 0, 0, 0}" << endl
+ << "};" << endl
+ << endl;
+
+ this->generateModInitFunc();
+
+ this->generatePostamble();
+
+}
+
+
+void PyExtModuleGenerator::generatePreamble()
+{
+ cout << "typedef struct {PyObject **p; char *s;} __Babel_InternTabEntry;" << endl
+ << endl
+ << "static PyObject *__babel_m;" << endl
+ << endl
+ << "static int __Babel_InternStrings(__Babel_InternTabEntry *t);" << endl
+ << endl;
+}
+
+
+void PyExtModuleGenerator::generatePostamble()
+{
+ cout << "static int __Babel_InternStrings(__Babel_InternTabEntry *t) {" << endl
+ << " while (t->p) {" << endl
+ << " *t->p = PyString_InternFromString(t->s);" << endl
+ << " if (!*t->p)" << endl
+ << " return -1;" << endl
+ << " ++t;" << endl
+ << " }" << endl
+ << " return 0;" << endl
+ << "}" << endl
+ << endl;
+}
+
+
+void PyExtModuleGenerator::generateModInitFunc()
+{
+ cout << "PyMODINIT_FUNC init" << name <<"(void) {" << endl
+ << " __babel_m = Py_InitModule4(\"" << name << "\", __babel_methods, 0, 0, PYTHON_API_VERSION);" << endl
+ << " if (!__babel_m) return;" << endl
+ << " if (__Babel_InternStrings(__babel_intern_tab) < 0) return;" << endl
+ << " return;" << endl
+ << "}" << endl
+ << endl;
+}
+
+
+bool PyExtModuleGenerator::visitExpression(Expression *obj)
+{
+#if 0
+ ASTSWITCH(Expression, obj) {
+ ASTCASE(E_variable, e) {
+ //tryHit(e->var, e->name->loc, "use as variable");
+ }
+ ASTNEXT(E_fieldAcc, e) {
+ //tryHit(e->field, e->fieldName->loc, "use as field");
+ }
+ ASTENDCASED
+ }
+#endif
+ return true;
+}
+
+
+bool PyExtModuleGenerator::visitDeclarator(Declarator *obj)
+{
+ Variable *var = obj->var;
+ if (var->type->isFunctionType()) {
+ if (false) {
+ // This would generate methods for function
+ // prototypes... currently, we have no way of
+ // filtering-out system headers, etc.
+ return this->visitFunctionVariable(obj->var);
+ }
+ }
+ return true;
+}
+
+
+bool PyExtModuleGenerator::visitFunction(Function *f)
+{
+ Variable *var = f->nameAndParams->var;
+ this->visitFunctionVariable(var);
+ if (false) {
+ cout << "function " << *(f->nameAndParams->getDeclaratorId()) << " (" << var->name << ")" << endl;
+ }
+ return true;
+}
+
+
+bool PyExtModuleGenerator::visitFunctionVariable(Variable *var)
+{
+ switch (state) {
+ case internDecls:
+ cout << "static PyObject *__babel_n_" << var->name << ";" << endl;
+ break;
+ case methods:
+ this->writePyMethod(var);
+ cout << endl;
+ break;
+ case internTable:
+ cout << " {&__babel_n_" << var->name << ", \"" << var->name << "\"}," << endl;
+ break;
+ case methodTable:
+ cout << " {\"" << var->name << "\", (PyCFunction)__babel_m_" << var->name << ", METH_VARARGS|METH_KEYWORDS, 0}," << endl;
+ break;
+ }
+ if (false) {
+ cout << " " << var->toCString() << endl;
+ cout << " " << var->toQualifiedString() << endl;
+ }
+ return true;
+}
+
+
+void PyExtModuleGenerator::writePyMethod(Variable *var)
+{
+ cout << "static PyObject *__babel_m_" << var->name << "(PyObject *self, PyObject *args, PyObject *kwds) {" << endl;
+
+ std::string format, argnames, varargs, args;
+ this->scanFunctionParams(var, format, argnames, varargs, args);
+ cout << " PyObject *r;" << endl;
+
+ cout << " static char *argnames[] = {" << argnames << "0};" << endl
+ << " if (!PyArg_ParseTupleAndKeywords(args, kwds, \"" << format << "\", argnames" << varargs << ")) return 0;" << endl;
+
+ const char *pyXXX_FromXXX = this->returnValueConversionFunction(var);
+
+ if (pyXXX_FromXXX) {
+ cout << " r = " << pyXXX_FromXXX << "(" << var->name << "(" << args << "));" << endl;
+ } else {
+ cout << " " << var->name << "(" << args << ");" << endl;
+ cout << " r = Py_None; Py_INCREF(Py_None);" << endl;
+ }
+
+ cout << " return r;" << endl
+ << "}" << endl;
+}
+
+
+void PyExtModuleGenerator::scanFunctionParams(Variable *var,
+ std::string &format,
+ std::string &argnames,
+ std::string &varargs,
+ std::string &args)
+{
+ int ct = 0;
+ SFOREACH_OBJLIST(Variable, var->type->asFunctionType()->params, iter) {
+ ct++;
+ if (var->type->isMethod() && ct==1) {
+ // "this"
+ continue;
+ }
+
+ const Variable *param = iter.data();
+
+ cout << " " << param->toCString() << ";" << endl;
+
+ char c = '?';
+ if (param->type->isSimpleType()) {
+ SimpleTypeId id = param->type->asSimpleTypeC()->type;
+ switch (id) {
+ case ST_CHAR: c = 'b'; break;
+ case ST_UNSIGNED_CHAR: c = 'B'; break;
+ case ST_SIGNED_CHAR: c = 'b'; break; // Is 'char' signed?
+ case ST_BOOL: break; // ???
+ case ST_INT: c = 'i'; break;
+ case ST_UNSIGNED_INT: c = 'I'; break;
+ case ST_LONG_INT: c = 'l'; break;
+ case ST_UNSIGNED_LONG_INT: c = 'k'; break;
+ case ST_LONG_LONG: c = 'L'; break;
+ case ST_UNSIGNED_LONG_LONG: c = 'K'; break;
+ case ST_SHORT_INT: c = 'h'; break;
+ case ST_UNSIGNED_SHORT_INT: c = 'H'; break;
+ case ST_WCHAR_T: break; // ???
+ case ST_FLOAT: c = 'f'; break;
+ case ST_DOUBLE: c = 'd'; break;
+ case ST_LONG_DOUBLE:
+ case ST_FLOAT_COMPLEX:
+ case ST_DOUBLE_COMPLEX:
+ case ST_LONG_DOUBLE_COMPLEX:
+ case ST_FLOAT_IMAGINARY:
+ case ST_DOUBLE_IMAGINARY:
+ case ST_LONG_DOUBLE_IMAGINARY:
+ case ST_VOID:
+ // ???
+ // ERROR
+ break;
+ default:
+ // ERROR
+ break;
+ }
+ } else if (param->type->isPointerType()) {
+ PointerType *pt = param->type->asPointerType();
+ if (pt->atType->isSimpleType()) {
+ SimpleTypeId id = pt->atType->asSimpleTypeC()->type;
+ switch (id) {
+ case ST_CHAR:
+ case ST_UNSIGNED_CHAR:
+ case ST_SIGNED_CHAR:
+ c = 's';
+ break;
+ case ST_WCHAR_T:
+ break; // ???
+ default:
+ // ERROR
+ break;
+ }
+ } else {
+ // ERROR
+ }
+ } else {
+ // ERROR
+ }
+ format += c;
+
+ argnames += std::string("\"") + param->name + "\",";
+ varargs += std::string(", &") + param->name;
+
+ if (ct >= 3 || (!var->type->isMethod() && ct>=2)) {
+ args += ", ";
+ }
+ args += param->name;
+ }
+ return;
+}
+
+
+const char *PyExtModuleGenerator::returnValueConversionFunction(Variable *var)
+{
+ Type *rt = var->type->asFunctionType()->retType;
+ const char *pyXXX_FromXXX = "<error>";
+ if (rt->isSimpleType()) {
+ SimpleTypeId id = rt->asSimpleTypeC()->type;
+ switch (id) {
+ case ST_CHAR: pyXXX_FromXXX = "PyInt_FromLong"; break;
+ case ST_UNSIGNED_CHAR: pyXXX_FromXXX = "PyLong_FromUnsignedLong"; break;
+ case ST_SIGNED_CHAR: pyXXX_FromXXX = "PyInt_FromLong"; break;
+ case ST_BOOL: pyXXX_FromXXX = "PyInt_FromLong"; break;
+ case ST_INT: pyXXX_FromXXX = "PyInt_FromLong"; break;
+ case ST_UNSIGNED_INT: pyXXX_FromXXX = "PyLong_FromUnsignedLong"; break;
+ case ST_LONG_INT: pyXXX_FromXXX = "PyInt_FromLong"; break;
+ case ST_UNSIGNED_LONG_INT: pyXXX_FromXXX = "PyLong_FromUnsignedLong"; break;
+ case ST_LONG_LONG: pyXXX_FromXXX = "PyLong_FromLongLong"; break;
+ case ST_UNSIGNED_LONG_LONG: pyXXX_FromXXX = "PyLong_FromUnsignedLongLong"; break;
+ case ST_SHORT_INT: pyXXX_FromXXX = "PyInt_FromLong"; break;
+ case ST_UNSIGNED_SHORT_INT: pyXXX_FromXXX = "PyLong_FromUnsignedLong"; break;
+ case ST_WCHAR_T: break; // ???
+ case ST_FLOAT: pyXXX_FromXXX = "PyFloat_FromDouble"; break;
+ case ST_DOUBLE: pyXXX_FromXXX = "PyFloat_FromDouble"; break;
+ case ST_LONG_DOUBLE:
+ case ST_FLOAT_COMPLEX:
+ case ST_DOUBLE_COMPLEX:
+ case ST_LONG_DOUBLE_COMPLEX:
+ case ST_FLOAT_IMAGINARY:
+ case ST_DOUBLE_IMAGINARY:
+ case ST_LONG_DOUBLE_IMAGINARY:
+ // ???
+ // ERROR
+ break;
+ case ST_VOID:
+ pyXXX_FromXXX = 0; // no conversion
+ break;
+ default:
+ // ERROR
+ break;
+ }
+ } else if (rt->isPointerType()) {
+ PointerType *pt = rt->asPointerType();
+ if (pt->atType->isSimpleType()) {
+ SimpleTypeId id = pt->atType->asSimpleTypeC()->type;
+ switch (id) {
+ case ST_CHAR:
+ case ST_UNSIGNED_CHAR:
+ case ST_SIGNED_CHAR:
+ pyXXX_FromXXX = "PyString_FromString";
+ break;
+ case ST_WCHAR_T:
+ break; // ???
+ default:
+ // ERROR
+ break;
+ }
+ } else {
+ // ERROR
+ }
+ } else {
+ // ERROR
+ }
+ return pyXXX_FromXXX;
+}
+
+
+void PyExtModuleGenerator::xxx(Variable *var)
+{
+ int ct = 0;
+ SFOREACH_OBJLIST(Variable, var->type->asFunctionType()->params, iter) {
+ ct++;
+ if (var->type->isMethod() && ct==1) {
+ // don't actually print the first parameter;
+ // the 'm' stands for nonstatic member function
+ cout << "/""*m: " << iter.data()->type->toCString() << " *""/ ";
+ continue;
+ }
+ if (ct >= 3 || (!var->type->isMethod() && ct>=2)) {
+ cout << ", ";
+ }
+ cout << iter.data()->toCStringAsParameter();
+ }
+}
+
+
+bool PyExtModuleGenerator::visitMember(Member *obj)
+{
+ return true;
+ if (obj->isMR_func()) {
+ Function *f = obj->asMR_func()->f;
+ Variable *var = f->nameAndParams->var;
+ cout << "member function " << *(f->nameAndParams->getDeclaratorId()) << " (" << var->name << ")" << endl;
+ cout << " " << var->toCString() << endl;
+ cout << " " << var->toQualifiedString() << endl;
+ } else if (obj->isMR_access()) {
+ cout << "access" << endl;
+ } else if (obj->isMR_decl()) {
+ cout << "member decl" << endl;
+ Declaration *d = obj->asMR_decl()->d;
+ if (false) {
+ d->debugPrint(cout, 0);
+ }
+ } else {
+ cout << "visitMember ???" << endl;
+ }
+ return true;
+}
+
+
+bool PyExtModuleGenerator::visitTypeSpecifier(TypeSpecifier *ts)
+{
+ return true;
+}
+
+
+// ---------------------- main -------------------------
+
+void doit(int argc, char **argv)
+{
+ xBase::logExceptions = false;
+
+ SourceLocManager mgr;
+ StringTable strTable;
+
+ // parsing language options
+ CCLang lang;
+ lang.GNU_Cplusplus();
+
+ // process command-line arguments
+ if (argc != 3) {
+ cout << "usage: " << argv[0] << " input.cc modulename\n";
+ return;
+ }
+
+ PyExtModuleGenerator visitor(argv[2], argv[1]);
+ string inputFname = argv[1];
+
+ // parse+tcheck (TODO: make this more convenient)
+ TranslationUnit *unit;
+ {
+ SemanticValue treeTop;
+ ParseTreeAndTokens tree(lang, treeTop, strTable, inputFname.c_str());
+
+ // grab the lexer so we can check it for errors (damn this
+ // 'tree' thing is stupid..)
+ Lexer *lexer = dynamic_cast<Lexer*>(tree.lexer);
+ xassert(lexer);
+
+ CCParse *parseContext = new CCParse(strTable, lang);
+ tree.userAct = parseContext;
+
+ ParseTables *tables = parseContext->makeTables();
+ tree.tables = tables;
+
+ // parse
+ if (!toplevelParse(tree, inputFname.c_str())) {
+ exit(2); // parse error
+ }
+
+ // check for parse errors
+ if (parseContext->errors || lexer->errors) {
+ exit(2);
+ }
+
+ // treeTop is a TranslationUnit pointer
+ unit = (TranslationUnit*)treeTop;
+
+ delete parseContext;
+ delete tables;
+
+ // tcheck
+ BasicTypeFactory tfac;
+ Env env(strTable, lang, tfac, unit);
+ unit->tcheck(env);
+
+ int numErrors = env.errors.numErrors();
+ if (numErrors) {
+ env.errors.print(cerr);
+ cerr << numErrors << " errors\n";
+ exit(4);
+ }
+
+ // do elaboration
+ ElabVisitor vis(strTable, tfac, unit);
+ unit->traverse(vis.loweredVisitor);
+ }
+
+ visitor.generate(unit);
+
+ if (false) {
+ // pretty printing
+ OStreamOutStream out0(cout);
+ CodeOutStream codeOut(out0);
+ TypePrinterC typePrinter;
+ PrintEnv env(typePrinter, &codeOut);
+ cout << "---- START ----" << endl;
+ cout << "// -*-c++-*-" << endl;
+ unit->print(env);
+ codeOut.finish();
+ cout << "---- STOP ----" << endl;
+ }
+
+}
+
+
+int main(int argc, char **argv)
+{
+ try {
+ doit(argc, argv);
+ }
+ catch (xBase &x) {
+ HANDLER();
+ cout << x << endl;
+ abort();
+ }
+
+ return 0;
+}
+
+
+// end of file
+
More information about the cig-commits
mailing list