[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