From 20c52def19220464dbf6cc48144051c375ad2842 Mon Sep 17 00:00:00 2001 From: Bruno da Silva de Oliveira Date: Tue, 11 Mar 2003 03:29:22 +0000 Subject: [PATCH] - first version [SVN r17805] --- ...orting_all_declarations_from_a_header.html | 76 ++ pyste/doc/introduction.html | 74 ++ pyste/doc/policies.html | 79 ++ pyste/doc/pyste.txt | 370 ++++++++++ pyste/doc/renaming_and_excluding.html | 68 ++ pyste/doc/running_pyste.html | 110 +++ pyste/doc/templates.html | 103 +++ pyste/doc/the_interface_files.html | 81 +++ pyste/doc/theme/alert.gif | Bin 0 -> 577 bytes pyste/doc/theme/arrow.gif | Bin 0 -> 70 bytes pyste/doc/theme/bkd.gif | Bin 0 -> 1317 bytes pyste/doc/theme/bkd2.gif | Bin 0 -> 2543 bytes pyste/doc/theme/bulb.gif | Bin 0 -> 944 bytes pyste/doc/theme/bullet.gif | Bin 0 -> 152 bytes pyste/doc/theme/c++boost.gif | Bin 0 -> 8819 bytes pyste/doc/theme/l_arr.gif | Bin 0 -> 147 bytes pyste/doc/theme/l_arr_disabled.gif | Bin 0 -> 91 bytes pyste/doc/theme/note.gif | Bin 0 -> 151 bytes pyste/doc/theme/r_arr.gif | Bin 0 -> 147 bytes pyste/doc/theme/r_arr_disabled.gif | Bin 0 -> 91 bytes pyste/doc/theme/smiley.gif | Bin 0 -> 879 bytes pyste/doc/theme/style.css | 170 +++++ pyste/doc/theme/u_arr.gif | Bin 0 -> 170 bytes pyste/doc/wrappers.html | 108 +++ pyste/index.html | 71 ++ pyste/src/.cvsignore | 1 + pyste/src/ClassExporter.py | 688 ++++++++++++++++++ pyste/src/CodeUnit.py | 78 ++ pyste/src/CppParser.py | 94 +++ pyste/src/EnumExporter.py | 30 + pyste/src/Exporter.py | 69 ++ pyste/src/FunctionExporter.py | 81 +++ pyste/src/GCCXMLParser.py | 395 ++++++++++ pyste/src/HeaderExporter.py | 67 ++ pyste/src/IncludeExporter.py | 19 + pyste/src/declarations.py | 452 ++++++++++++ pyste/src/enumerate.py | 7 + pyste/src/exporters.py | 3 + pyste/src/exporterutils.py | 26 + pyste/src/infos.py | 185 +++++ pyste/src/policies.py | 75 ++ pyste/src/pyste-profile.py | 17 + pyste/src/pyste.py | 154 ++++ pyste/src/settings.py | 12 + pyste/tests/GCCXMLParserUT.py | 338 +++++++++ pyste/tests/infosUT.py | 50 ++ pyste/tests/policiesUT.py | 59 ++ pyste/tests/runtests.py | 14 + 48 files changed, 4224 insertions(+) create mode 100644 pyste/doc/exporting_all_declarations_from_a_header.html create mode 100644 pyste/doc/introduction.html create mode 100644 pyste/doc/policies.html create mode 100644 pyste/doc/pyste.txt create mode 100644 pyste/doc/renaming_and_excluding.html create mode 100644 pyste/doc/running_pyste.html create mode 100644 pyste/doc/templates.html create mode 100644 pyste/doc/the_interface_files.html create mode 100644 pyste/doc/theme/alert.gif create mode 100644 pyste/doc/theme/arrow.gif create mode 100644 pyste/doc/theme/bkd.gif create mode 100644 pyste/doc/theme/bkd2.gif create mode 100644 pyste/doc/theme/bulb.gif create mode 100644 pyste/doc/theme/bullet.gif create mode 100644 pyste/doc/theme/c++boost.gif create mode 100644 pyste/doc/theme/l_arr.gif create mode 100644 pyste/doc/theme/l_arr_disabled.gif create mode 100644 pyste/doc/theme/note.gif create mode 100644 pyste/doc/theme/r_arr.gif create mode 100644 pyste/doc/theme/r_arr_disabled.gif create mode 100644 pyste/doc/theme/smiley.gif create mode 100644 pyste/doc/theme/style.css create mode 100644 pyste/doc/theme/u_arr.gif create mode 100644 pyste/doc/wrappers.html create mode 100644 pyste/index.html create mode 100644 pyste/src/.cvsignore create mode 100644 pyste/src/ClassExporter.py create mode 100644 pyste/src/CodeUnit.py create mode 100644 pyste/src/CppParser.py create mode 100644 pyste/src/EnumExporter.py create mode 100644 pyste/src/Exporter.py create mode 100644 pyste/src/FunctionExporter.py create mode 100644 pyste/src/GCCXMLParser.py create mode 100644 pyste/src/HeaderExporter.py create mode 100644 pyste/src/IncludeExporter.py create mode 100644 pyste/src/declarations.py create mode 100644 pyste/src/enumerate.py create mode 100644 pyste/src/exporters.py create mode 100644 pyste/src/exporterutils.py create mode 100644 pyste/src/infos.py create mode 100644 pyste/src/policies.py create mode 100644 pyste/src/pyste-profile.py create mode 100644 pyste/src/pyste.py create mode 100644 pyste/src/settings.py create mode 100644 pyste/tests/GCCXMLParserUT.py create mode 100644 pyste/tests/infosUT.py create mode 100644 pyste/tests/policiesUT.py create mode 100644 pyste/tests/runtests.py diff --git a/pyste/doc/exporting_all_declarations_from_a_header.html b/pyste/doc/exporting_all_declarations_from_a_header.html new file mode 100644 index 00000000..4f2418f5 --- /dev/null +++ b/pyste/doc/exporting_all_declarations_from_a_header.html @@ -0,0 +1,76 @@ + + + +Exporting All Declarations from a Header + + + + + + + + + +
+ + Exporting All Declarations from a Header +
+
+ + + + + + +
+

+Pyste also supports a mechanism to export all declarations found in a header +file. Suppose again our file, hello.h:

+
+    struct World
+    {
+        World(std::string msg): msg(msg) {} 
+        void set(std::string msg) { this->msg = msg; }
+        std::string greet() { return msg; }
+        std::string msg;
+    };
+
+    enum choice { red, blue };
+    
+    void show(choice c) { std::cout << "value: " << (int)c << std::endl; } 
+
+

+You can just use the AllFromHeader construct:

+
+    hello = AllFromHeader("hello.h")
+
+

+this will export all the declarations found in hello.h, which is equivalent +to write:

+
+    Class("World", "hello.h")
+    Enum("choice", "hello.h")
+    Function("show", "hello.h")
+
+

+Note that you can still use the functions rename, set_policy, exclude, etc. Just access +the members of the header object like this:

+
+    rename(hello.World.greet, "Greet")
+    exclude(hello.World.set, "Set")
+
+ + + + + + +
+
+
+ + diff --git a/pyste/doc/introduction.html b/pyste/doc/introduction.html new file mode 100644 index 00000000..ffb50e7e --- /dev/null +++ b/pyste/doc/introduction.html @@ -0,0 +1,74 @@ + + + +Introduction + + + + + + + + + +
+ + Introduction +
+
+ + + + + + +
+

What is Pyste?

+Pyste is a +Boost.Python code generator. The user specifies the classes and +functions to be exported using a simple interface file, which following the + +Boost.Python's philosophy, is simple Python code. Pyste then uses +GCCXML to +parse all the headers and extract the necessary information to automatically +generate C++ code.

+

Example

+Let's borrow the class World from the +tutorial:

+
+    struct World
+    {
+        void set(std::string msg) { this->msg = msg; }
+        std::string greet() { return msg; }
+        std::string msg;
+    };
+
+

+Here's the interface file for it, named world.pyste:

+
+    Class("World", "world.h")
+
+

+and that's it!

+

+The next step is invoke pyste in the command-line:

+
python pyste.py --module=hello world.pyste

+this will create a file "hello.cpp" in the directory where the command was +run.

+

+Pyste supports the following features:

+ + + + + + +
+
+
+ + diff --git a/pyste/doc/policies.html b/pyste/doc/policies.html new file mode 100644 index 00000000..2869889f --- /dev/null +++ b/pyste/doc/policies.html @@ -0,0 +1,79 @@ + + + +Policies + + + + + + + + + + +
+ + Policies +
+
+ + + + + + +
+

+Even thought Pyste can identify various elements in the C++ code, like virtual +methods, attributes, and so on, one thing that it can't do is to guess the +semantics of functions that return pointers or references. In this case, the +user must manually specify the policy. Policies are explained in the + +tutorial.

+

+The policies in Pyste are named exactly as in +Boost.Python, only the syntax is +slightly different. For instance, this policy:

+
+    return_internal_reference<1, with_custodian_and_ward<1, 2> >()
+
+

+becomes in Pyste:

+
+    return_internal_reference(1, with_custodian_and_ward(1, 2))
+
+

+The user can specify policies for functions and methods with the set_policy +function:

+
+    set_policy(f, return_internal_reference())
+    set_policy(C.foo, return_value_policy(manage_new_object))
+
+ + + + +
+ + What if a function or method needs a policy and the user +doesn't set one?

+If a function/method needs a policy and one was not set, Pyste will issue a error. +The user should then go in the interface file and set the policy for it, +otherwise the generated cpp won't compile. +
+ + + + + + +
+
+
+ + diff --git a/pyste/doc/pyste.txt b/pyste/doc/pyste.txt new file mode 100644 index 00000000..65c2cec9 --- /dev/null +++ b/pyste/doc/pyste.txt @@ -0,0 +1,370 @@ +[doc Pyste Documentation] + +[def GCCXML [@http://www.gccxml.org GCCXML]] +[def Boost.Python [@../../index.html Boost.Python]] + +[page Introduction] + +[h2 What is Pyste?] + +Pyste is a Boost.Python code generator. The user specifies the classes and +functions to be exported using a simple ['interface file], which following the +Boost.Python's philosophy, is simple Python code. Pyste then uses GCCXML to +parse all the headers and extract the necessary information to automatically +generate C++ code. + +[h2 Example] + +Let's borrow the class [^World] from the [@../../doc/tutorial/doc/exposing_classes.html tutorial]: + + struct World + { + void set(std::string msg) { this->msg = msg; } + std::string greet() { return msg; } + std::string msg; + }; + +Here's the interface file for it, named [^world.pyste]: + + Class("World", "world.h") + +and that's it! + +The next step is invoke pyste in the command-line: + +[pre python pyste.py --module=hello world.pyste] + +this will create a file "[^hello.cpp]" in the directory where the command was +run. + +Pyste supports the following features: + +* Functions +* Classes +* Class Templates +* Virtual Methods +* Overloading +* Attributes +* Enums (both "free" enums and class enums) +* Nested Classes + +[page Running Pyste] + +To run pyste, you will need: + +* Python 2.2, avaiable at [@http://www.python.org python's website]. +* The great [@http://effbot.org elementtree] library, from Fredrik Lundh. +* The excellent GCCXML, from Brad King. + +Installation for the tools is avaiable in their respective webpages. + +[blurb +[$theme/note.gif] GCCXML must be accessible in the PATH environment variable, so +that pyste can call it. How to do this varies from platform to platform. +] + +[h2 Ok, now what?] + +Well, now let's fire it up: + +[pre +''' +>python pyste.py + +Usage: + pyste [options] --module= interface-files + +where options are: + -I add an include path + -D define symbol + --no-using do not declare "using namespace boost"; + use explicit declarations instead + --pyste-ns= set the namespace where new types will be declared; + default is "pyste" +''' +] + +Options explained: + +The [^-I] and [^-D] are preprocessor flags, which are needed by gccxml to parse the header files correctly and by pyste to find the header files declared in the +interface files. + +[^--no-using] tells pyste to don't declare "[^using namespace boost;]" in the +generated cpp, using the namespace boost::python explicitly in all declarations. +Use only if you're having a name conflict in one of the files. + +Use [^--pyste-ns] to change the namespace where new types are declared (for +instance, the virtual wrappers). Use only if one of your header files declare a +namespace named "pyste" and this is causing conflicts. + +So, the usage is simple enough: + +[pre >python pyste.py --module=mymodule file.pyste file2.pyste ...] + +will generate a file [^mymodule.cpp] in the same dir where the command was +executed. Now you can compile the file using the same instructions of the +[@../../doc/tutorial/doc/building_hello_world.html tutorial]. + +[h2 Wait... how do I set those I and D flags?] + +Don't worry: normally GCCXML is already configured correctly for your plataform, +so the search path to the standard libraries and the standard defines should +already be set. You only have to set the paths to other libraries that your code +needs, like Boost, for example. + +Plus, Pyste automatically uses the contents of the environment variable +[^INCLUDE] if it exists. Windows users should run the [^Vcvars32.bat] file, +normally located at: + + C:\Program Files\Microsoft Visual Studio\VC98\bin\Vcvars32.bat + +with that, you should have little trouble setting up the flags. + +[page The Interface Files] + +The interface files are the heart of Pyste. The user creates one or more +interface files declaring the classes and functions he wants to export, and then +invokes pyste passing the interface files to it. Pyste then generates a single +cpp file with Boost.Python code, with all the classes and functions exported. + +Besides declaring the classes and functions, the user has a number of other +options, like renaming classes and methods, excluding methods and attributes, +and so on. + +[h2 Basics] + +Suppose we have a class and some functions that we want to expose to Python +declared in the header [^hello.h]: + + struct World + { + World(std::string msg): msg(msg) {} + void set(std::string msg) { this->msg = msg; } + std::string greet() { return msg; } + std::string msg; + }; + + enum choice { red, blue }; + + namespace test { + + void show(choice c) { std::cout << "value: " << (int)c << std::endl; } + + } + +We create a file named [^hello.pyste] and create instances of the classes +[^Function], [^Class] and [^Enum]: + + Function("test::show", "hello.h") + Class("World", "hello.h") + Enum("choice", "hello.h") + +That will expose the class, the free function and the enum found in [^hello.h]. + +[page:1 Renaming and Excluding] + +You can easily rename functions, classes, methods, attributes, etc. Just use the +function [^rename], like this: + + World = Class("World", "hello.h") + rename(World, "IWorld") + show = Function("choice", "hello.h") + rename(show, "Show") + +You can rename methods and attributes using this syntax: + + rename(World.greet, "Greet") + rename(World.set, "Set") + choice = Enum("choice", "hello.h") + rename(choice.red, "Red") + rename(choice.blue, "Blue") + +You can exclude functions, classes, methods, attributes, etc, in the same way, +with the function [^exclude]: + + exclude(World.greet) + exclude(World.msg) + +Easy, huh? [$theme/smiley.gif] + +[page:1 Policies] + +Even thought Pyste can identify various elements in the C++ code, like virtual +methods, attributes, and so on, one thing that it can't do is to guess the +semantics of functions that return pointers or references. In this case, the +user must manually specify the policy. Policies are explained in the +[@../../doc/tutorial/doc/call_policies.html tutorial]. + +The policies in Pyste are named exactly as in Boost.Python, only the syntax is +slightly different. For instance, this policy: + + return_internal_reference<1, with_custodian_and_ward<1, 2> >() + +becomes in Pyste: + + return_internal_reference(1, with_custodian_and_ward(1, 2)) + +The user can specify policies for functions and methods with the [^set_policy] +function: + + set_policy(f, return_internal_reference()) + set_policy(C.foo, return_value_policy(manage_new_object)) + +[blurb +[$theme/note.gif] [*What if a function or method needs a policy and the user +doesn't set one?][br][br] +If a function/method needs a policy and one was not set, Pyste will issue a error. +The user should then go in the interface file and set the policy for it, +otherwise the generated cpp won't compile. +] + +[page:1 Templates] + +Template Classes can easily exported too, but you can't export the "Template" +itself... you have to export instantiations of it! So, if you want to export a +[^std::vector], you will have to export vectors of int, doubles, etc. + +Suppose we have this code: + + template + struct Point + { + T x; + T y; + }; + +And we want to export [^Point]s of int and double: + + Point = Template("Point", "point.h") + Point("int") + Point("double") + +Pyste will assign default names for each instantiation. In this example, those +would be "[^Point_int]" and "[^Point_double]", but most of the time users will want to +rename the instantiations: + + Point("int", "IPoint") // renames the instantiation + double_inst = Point("double") // another way to do the same + rename(double_inst, "DPoint") + +Note that you can rename, exclude, set policies, etc, in the [^Template] class +like you would do with a [^Function] or a [^Class]. This changes affect all +[*future] instantiations: + + Point = Template("Point", "point.h") + Point("float", "FPoint") // will have x and y as data members + rename(Point.x, "X") + rename(Point.y, "Y") + Point("int", "IPoint") // will have X and Y as data members + Point("double", "DPoint") // also will have X and Y as data member + +If you want to change a option of a particular instantiation, you can do so: + + Point = Template("Point", "point.h") + Point("int", "IPoint") + d_inst = Point("double", "DPoint") + rename(d_inst.x, "X") // only DPoint is affect by this renames, + rename(d_inst.y, "Y") // IPoint stays intact + +[blurb [$theme/note.gif] [*What if my template accepts more than one type?] +[br][br] +When you want to instantiate a Template with more than one type, you can pass +either a string with the types separated by whitespace, or a list of strings +'''("int double" or ["int", "double"]''' would both work). +] + +[page:1 Wrappers] + +Suppose you have this function: + + std::vector names(); + +But you don't want to export a vector, you want this function to return +a python list of strings. Boost.Python has an excellent support for that: + + list names_wrapper() + { + list result; + vector v = names(); + // put each string in the vector in the list + return result; + } + + BOOST_PYTHON_MODULE(test) + { + def("names", &names_wrapper); + } + +Nice heh? +Pyste supports this mechanism too. You declare the [^names_wrapper] function in a +header, like "[^test_wrappers.h]", and in the interface file: + + Include("test_wrappers.h") + names = Function("names", "test.h") + set_wrapper(names, "names_wrapper") + +You can optionally declare the function in the interface file itself: + + names_wrapper = Wrapper("names_wrapper", + """ + list names_wrapper() + { + // call name() and convert the vector to a list... + } + """) + names = Function("names", "test.h") + set_wrapper(names, names_wrapper) + +The same mechanism can be done with methods too. Just remember that the first +parameter of wrappers for methods is a pointer to the class, like in +Boost.Python: + + struct C + { + std::vector names(); + } + + list names_wrapper(C* c) + { + // same as before, calling c->names() and converting result to a list + } + +And then in the interface file: + + C = Class("C", "test.h") + set_wrapper(C.names, "names_wrapper") + +[page:1 Exporting All Declarations from a Header] + +Pyste also supports a mechanism to export all declarations found in a header +file. Suppose again our file, [^hello.h]: + + struct World + { + World(std::string msg): msg(msg) {} + void set(std::string msg) { this->msg = msg; } + std::string greet() { return msg; } + std::string msg; + }; + + enum choice { red, blue }; + + void show(choice c) { std::cout << "value: " << (int)c << std::endl; } + +You can just use the [^AllFromHeader] construct: + + hello = AllFromHeader("hello.h") + +this will export all the declarations found in [^hello.h], which is equivalent +to write: + + Class("World", "hello.h") + Enum("choice", "hello.h") + Function("show", "hello.h") + +Note that you can still use the functions [^rename], [^set_policy], [^exclude], etc. Just access +the members of the header object like this: + + rename(hello.World.greet, "Greet") + exclude(hello.World.set, "Set") + diff --git a/pyste/doc/renaming_and_excluding.html b/pyste/doc/renaming_and_excluding.html new file mode 100644 index 00000000..29a8001b --- /dev/null +++ b/pyste/doc/renaming_and_excluding.html @@ -0,0 +1,68 @@ + + + +Renaming and Excluding + + + + + + + + + + +
+ + Renaming and Excluding +
+
+ + + + + + +
+

+You can easily rename functions, classes, methods, attributes, etc. Just use the +function rename, like this:

+
+    World = Class("World", "hello.h")
+    rename(World, "IWorld")
+    show = Function("choice", "hello.h")
+    rename(show, "Show")
+
+

+You can rename methods and attributes using this syntax:

+
+    rename(World.greet, "Greet")
+    rename(World.set, "Set")
+    choice = Enum("choice", "hello.h")
+    rename(choice.red, "Red")
+    rename(choice.blue, "Blue")
+
+

+You can exclude functions, classes, methods, attributes, etc, in the same way, +with the function exclude:

+
+    exclude(World.greet)
+    exclude(World.msg)
+
+

+Easy, huh?

+ + + + + + +
+
+
+ + diff --git a/pyste/doc/running_pyste.html b/pyste/doc/running_pyste.html new file mode 100644 index 00000000..a67d5812 --- /dev/null +++ b/pyste/doc/running_pyste.html @@ -0,0 +1,110 @@ + + + +Running Pyste + + + + + + + + + + +
+ + Running Pyste +
+
+ + + + + + +
+

+To run pyste, you will need:

+

+Installation for the tools is avaiable in their respective webpages.

+ + + + +
+ + +GCCXML must be accessible in the PATH environment variable, so +that pyste can call it. How to do this varies from platform to platform. +
+

Ok, now what?

+Well, now let's fire it up:

+
+
+>python pyste.py
+
+Usage:
+    pyste [options] --module=<name> interface-files
+
+where options are:
+    -I <path>           add an include path
+    -D <symbol>         define symbol
+    --no-using          do not declare "using namespace boost";
+                        use explicit declarations instead
+    --pyste-ns=<name>   set the namespace where new types will be declared;
+                        default is "pyste"
+                        
+

+Options explained:

+

+The -I and -D are preprocessor flags, which are needed by gccxml to parse the header files correctly and by pyste to find the header files declared in the +interface files.

+

+--no-using tells pyste to don't declare "using namespace boost;" in the +generated cpp, using the namespace boost::python explicitly in all declarations. +Use only if you're having a name conflict in one of the files.

+

+Use --pyste-ns to change the namespace where new types are declared (for +instance, the virtual wrappers). Use only if one of your header files declare a +namespace named "pyste" and this is causing conflicts.

+

+So, the usage is simple enough:

+
>python pyste.py --module=mymodule file.pyste file2.pyste ...

+will generate a file mymodule.cpp in the same dir where the command was +executed. Now you can compile the file using the same instructions of the + +tutorial.

+

Wait... how do I set those I and D flags?

+Don't worry: normally +GCCXML is already configured correctly for your plataform, +so the search path to the standard libraries and the standard defines should +already be set. You only have to set the paths to other libraries that your code +needs, like Boost, for example.

+

+Plus, Pyste automatically uses the contents of the environment variable +INCLUDE if it exists. Windows users should run the Vcvars32.bat file, +normally located at:

+
+    C:\Program Files\Microsoft Visual Studio\VC98\bin\Vcvars32.bat
+
+

+with that, you should have little trouble setting up the flags.

+ + + + + + +
+
+
+ + diff --git a/pyste/doc/templates.html b/pyste/doc/templates.html new file mode 100644 index 00000000..58548c72 --- /dev/null +++ b/pyste/doc/templates.html @@ -0,0 +1,103 @@ + + + +Templates + + + + + + + + + + +
+ + Templates +
+
+ + + + + + +
+

+Template Classes can easily exported too, but you can't export the "Template" +itself... you have to export instantiations of it! So, if you want to export a +std::vector, you will have to export vectors of int, doubles, etc.

+

+Suppose we have this code:

+
+    template <class T>
+    struct Point
+    {
+        T x;
+        T y;
+    };
+
+

+And we want to export Points of int and double:

+
+    Point = Template("Point", "point.h")
+    Point("int")
+    Point("double")
+
+

+Pyste will assign default names for each instantiation. In this example, those +would be "Point_int" and "Point_double", but most of the time users will want to +rename the instantiations:

+
+    Point("int", "IPoint")         // renames the instantiation
+    double_inst = Point("double")  // another way to do the same
+    rename(double_inst, "DPoint")
+
+

+Note that you can rename, exclude, set policies, etc, in the Template class +like you would do with a Function or a Class. This changes affect all +future instantiations:

+
+    Point = Template("Point", "point.h")
+    Point("float", "FPoint")        // will have x and y as data members
+    rename(Point.x, "X")
+    rename(Point.y, "Y")
+    Point("int", "IPoint")          // will have X and Y as data members
+    Point("double", "DPoint")       // also will have X and Y as data member
+
+

+If you want to change a option of a particular instantiation, you can do so:

+
+    Point = Template("Point", "point.h")
+    Point("int", "IPoint")          
+    d_inst = Point("double", "DPoint")       
+    rename(d_inst.x, "X")           // only DPoint is affect by this renames,
+    rename(d_inst.y, "Y")           // IPoint stays intact
+
+ + + + +
+ What if my template accepts more than one type? +

+When you want to instantiate a Template with more than one type, you can pass +either a string with the types separated by whitespace, or a list of strings +("int double" or ["int", "double"] would both work). +
+ + + + + + +
+
+
+ + diff --git a/pyste/doc/the_interface_files.html b/pyste/doc/the_interface_files.html new file mode 100644 index 00000000..77246af7 --- /dev/null +++ b/pyste/doc/the_interface_files.html @@ -0,0 +1,81 @@ + + + +The Interface Files + + + + + + + + + + +
+ + The Interface Files +
+
+ + + + + + +
+

+The interface files are the heart of Pyste. The user creates one or more +interface files declaring the classes and functions he wants to export, and then +invokes pyste passing the interface files to it. Pyste then generates a single +cpp file with +Boost.Python code, with all the classes and functions exported.

+

+Besides declaring the classes and functions, the user has a number of other +options, like renaming classes and methods, excluding methods and attributes, +and so on.

+

Basics

+Suppose we have a class and some functions that we want to expose to Python +declared in the header hello.h:

+
+    struct World
+    {
+        World(std::string msg): msg(msg) {} 
+        void set(std::string msg) { this->msg = msg; }
+        std::string greet() { return msg; }
+        std::string msg;
+    };
+
+    enum choice { red, blue };
+    
+    namespace test {
+    
+    void show(choice c) { std::cout << "value: " << (int)c << std::endl; }
+    
+    }
+
+

+We create a file named hello.pyste and create instances of the classes +Function, Class and Enum:

+
+    Function("test::show", "hello.h")
+    Class("World", "hello.h")
+    Enum("choice", "hello.h")
+
+

+That will expose the class, the free function and the enum found in hello.h.

+ + + + + + +
+
+
+ + diff --git a/pyste/doc/theme/alert.gif b/pyste/doc/theme/alert.gif new file mode 100644 index 0000000000000000000000000000000000000000..270764cc58716d36f8545c07ffbccb76a3dbc7f4 GIT binary patch literal 577 zcmZ?wbhEHb6krfwc*el+^5x4HFJ3%)^yuNkhj;GW`TvCB_N{CGA2IyD%W(C|x&OBr z{@-G_a_Q{m__~8F~vDYEl>qqZpDSjq@WJa>5z1Lm6Vd z86rG+L!23`^cb|27{rAb*jO1D7#RNl{|}<5jTL{gu!=AwGw6T}2E_>j`@DwarsiZ7 zvzCqy8~4s$Mnes@-VRGi5tqr$%yz7-+I%yUbrd6_%=Konm?$SD`KoeGb{17nO!DDy z>t(PKkWWZ*<cQAmnAE=fsD%$UE$ROUW+NUZ1+QQKJ-!ms2) z!wk-f=<_sLpE#kg){9r_k3+JcmI}v|3yP=3Bn5<=JQ5$B644M)RP)^A(&gG6!^Fs7 F4FJH#(p~@n literal 0 HcmV?d00001 diff --git a/pyste/doc/theme/arrow.gif b/pyste/doc/theme/arrow.gif new file mode 100644 index 0000000000000000000000000000000000000000..e33db0fb4dd85e0fe527343d95a3f28a0ebb74f9 GIT binary patch literal 70 zcmZ?wbhEHb6lLIGn8?8J9}E~6W->4^DE?&OC-XFJFH7^5yH-udiOcdjJ0Y=g*&CzkdDw`}cS6-hKG+ zVdct|pFVy1_U+rhfB)XRdGq$|+aEuEeEj(F|NsB1R;~K`_wVY}tN;A@vu4ej6)RT! z{{8#cuU~7|u3fio-TL+GfByWrapT4f8#Zj(v}yC^&D*wZ+q!k@mMvR$@7{gjz=7@C zx9{A!bMM~0yLRo`vuDr#{rh+9*s*WlzC(u&9X)#V)TvV^Po6w- zyLay%Jb3Wz*|X1|Kfizf{+Tmpu3fwK^y$-^H*a3Qe*M|AXXnnHJAeNCg$oysA3uKe z>eWk^E+4<0lBLU*@T^?5dd(`K^&2*BTFEI`2zd3>o;z)v)s9R@BSUeM~|O8Wn_5q^405CHt*hl`1r~B%hzw;zdQW;{pasr zF9v2V8Hs|fgUuYmej5*MSa`TSY=y@hkBv&AY7)j-cRDt)dE zU9$D{^$pSTGkte&%f01a^!nae>+L=F4>WVj`|WA_`1r(RZF9M$J3l|aF#GrnzrDM@ zzP^$C;>NkXyT8BJIMglgzi&_FC%sFnlY$n!TsEid z)yw4z+N54FEt!_}YUPS$t6r^IvuM`A)fY$|rssS*xogRqPp5XWJpOdrfW7(58I$WZ zJ;oN#*L*&AD&X$RBj zw_mR(wCjGmkup8^+s%ySYroyf+5Yz1?SkXF-|v)M&;5S4;`!R|_iFabaxho}0O~&E Au>b%7 literal 0 HcmV?d00001 diff --git a/pyste/doc/theme/bkd2.gif b/pyste/doc/theme/bkd2.gif new file mode 100644 index 0000000000000000000000000000000000000000..b03d9ba97ca7a4c4de297b5413fd3d3df8e0fb49 GIT binary patch literal 2543 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 literal 0 HcmV?d00001 diff --git a/pyste/doc/theme/bulb.gif b/pyste/doc/theme/bulb.gif new file mode 100644 index 0000000000000000000000000000000000000000..74f3baac42f12ac0551040a00f78cb573360a0ad GIT binary patch literal 944 zcmZ?wbhEHbU2JX=yWO8qb`WHgo38Gifu=q@6jF_W#UGhBGsb&&*6a zGjrydnP<+-{D0;Q!i=IrIPje+GvC495Q% z(*85d1W7Xd|8LCj-`M!SaoT_5nIN^s|No~k{7*CfpO*GNZ6-)-+W-GE8UD{S{y#JA z|IC>nlV<+^e}>`z8RP$8cYq8A8~y)3Nb~>yAnE^L0P?^n9t{C$ zm!W3VRz=HX0SO5P1_s5SEQ|~c3=BFT0%V2*i>ts1&-4h1-QU}6JQPJ#9Az#qP{_IL z>m8xcQ#>ccK&B>uvC-M7@I$1?63NFG4AfGLn>kr6)<~>7uC&oZb>E%VNA7Gc3=Gx) DZ>u*X literal 0 HcmV?d00001 diff --git a/pyste/doc/theme/c++boost.gif b/pyste/doc/theme/c++boost.gif new file mode 100644 index 0000000000000000000000000000000000000000..58be431a3fafbd5adfa01e829ce8d80a03a83ed0 GIT binary patch literal 8819 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 literal 0 HcmV?d00001 diff --git a/pyste/doc/theme/l_arr.gif b/pyste/doc/theme/l_arr.gif new file mode 100644 index 0000000000000000000000000000000000000000..5b3cb1cbf07e316c3655ac84c9ba72dfbe2e25a1 GIT binary patch literal 147 zcmZ?wbhEHb6k!l%*vtR|#>U2JX=yWO&OCGG%>OfK|If_)4`TfP|DWOif8+oE)BgXT z`Tzf!|NsBLfB#U2JX=yW!8D`F$dFITS|7mIeXEOYsnf8C?%>QSM|DT!p z|3Ab3|7ZS#f#OdVMg|6c1|5)2kQodtE+0;Mrh1v)&NktlbFtDU2JX=yWO&OCGG%>OfK|If_)4`TfP|DWOif8+oE)BgXT z`Tzf!|NsBLfB#&gTE>G6muSe93zS-%fd>Q)&EUbx3v}%NcRk;>%V0Cvz}Z0{|MoM2G+Y literal 0 HcmV?d00001 diff --git a/pyste/doc/theme/r_arr_disabled.gif b/pyste/doc/theme/r_arr_disabled.gif new file mode 100644 index 0000000000000000000000000000000000000000..2100f78bf35a47e09ca76ffcc6e80c5a2b11e359 GIT binary patch literal 91 zcmZ?wbhEHb6k!l%n8?5|bLPzZ_wWDz|DS<@LGdRGs|W)VgAM}&0|Q8&fk|gd|H|rv v@(cz3o}Svfg4O3$GVLgQHgm3)*?|1&XIvGFi0itr1TiFoeP&p1C|3gi5M|Dy8)Dq>pVFYM_y+=U^T ztHBMdj+tQ(A2Y*wF$RYE_4D-@jtU##9xMTW{_9v6c`wK7#J8B1sE6<80-`n z7><}aTg5mA1O&%;g!uX}*f26MGK54pa~u~3ne&L_xcGT71qKC%`|QWX?G$<#K=P%G z=f$!jvLf=G=8H2hFfyd8*(n?_D`m7(uv4gs=yaGbo~{-bxlJUa@tDY4zP5<9g6(Q8 z@$VV#2iy??g*_vVXy`OS*Qv? literal 0 HcmV?d00001 diff --git a/pyste/doc/theme/style.css b/pyste/doc/theme/style.css new file mode 100644 index 00000000..53a6205e --- /dev/null +++ b/pyste/doc/theme/style.css @@ -0,0 +1,170 @@ +body +{ + background-image: url(bkd.gif); + background-color: #FFFFFF; + margin: 1em 2em 1em 2em; +} + +h1 { font-family: Verdana, Arial, Helvetica, sans-serif; font-weight: bold; text-align: left; } +h2 { font: 140% sans-serif; font-weight: bold; text-align: left; } +h3 { font: 120% sans-serif; font-weight: bold; text-align: left; } +h4 { font: bold 100% sans-serif; font-weight: bold; text-align: left; } +h5 { font: italic 100% sans-serif; font-weight: bold; text-align: left; } +h6 { font: small-caps 100% sans-serif; font-weight: bold; text-align: left; } + +pre +{ + border-top: gray 1pt solid; + border-right: gray 1pt solid; + border-left: gray 1pt solid; + border-bottom: gray 1pt solid; + + padding-top: 2pt; + padding-right: 2pt; + padding-left: 2pt; + padding-bottom: 2pt; + + display: block; + font-family: "courier new", courier, mono; + background-color: #eeeeee; font-size: small +} + +code +{ + font-family: "Courier New", Courier, mono; + font-size: small +} + +tt +{ + display: inline; + font-family: "Courier New", Courier, mono; + color: #000099; + font-size: small +} + +p +{ + text-align: justify; + font-family: Georgia, "Times New Roman", Times, serif +} + +ul +{ + list-style-image: url(bullet.gif); + font-family: Georgia, "Times New Roman", Times, serif +} + +ol +{ + font-family: Georgia, "Times New Roman", Times, serif +} + +a +{ + font-weight: bold; + color: #003366; + text-decoration: none; +} + +a:hover { color: #8080FF; } + +.literal { color: #666666; font-style: italic} +.keyword { color: #000099} +.identifier {} +.comment { font-style: italic; color: #990000} +.special { color: #800040} +.preprocessor { color: #FF0000} +.string { font-style: italic; color: #666666} +.copyright { color: #666666; font-size: small} +.white_bkd { background-color: #FFFFFF} +.dk_grey_bkd { background-color: #999999} +.quotes { color: #666666; font-style: italic; font-weight: bold} + +.note_box +{ + display: block; + + border-top: gray 1pt solid; + border-right: gray 1pt solid; + border-left: gray 1pt solid; + border-bottom: gray 1pt solid; + + padding-right: 12pt; + padding-left: 12pt; + padding-bottom: 12pt; + padding-top: 12pt; + + font-family: Arial, Helvetica, sans-serif; + background-color: #E2E9EF; + font-size: small; text-align: justify +} + +.table_title +{ + background-color: #648CCA; + + font-family: Verdana, Arial, Helvetica, sans-serif; color: #FFFFFF; + font-weight: bold +; padding-top: 4px; padding-right: 4px; padding-bottom: 4px; padding-left: 4px +} + +.table_cells +{ + background-color: #E2E9EF; + + font-family: Geneva, Arial, Helvetica, san-serif; + font-size: small +; padding-top: 4px; padding-right: 4px; padding-bottom: 4px; padding-left: 4px +} + +.toc +{ + DISPLAY: block; + background-color: #E2E9EF + font-family: Arial, Helvetica, sans-serif; + + border-top: gray 1pt solid; + border-left: gray 1pt solid; + border-bottom: gray 1pt solid; + border-right: gray 1pt solid; + + padding-top: 24pt; + padding-right: 24pt; + padding-left: 24pt; + padding-bottom: 24pt; +} + +.toc_title +{ + background-color: #648CCA; + padding-top: 4px; + padding-right: 4px; + padding-bottom: 4px; + padding-left: 4px; + font-family: Geneva, Arial, Helvetica, san-serif; + color: #FFFFFF; + font-weight: bold +} + +.toc_cells +{ + background-color: #E2E9EF; + padding-top: 4px; + padding-right: 4px; + padding-bottom: 4px; + padding-left: 4px; + font-family: Geneva, Arial, Helvetica, san-serif; + font-size: small +} + +div.logo +{ + float: right; +} + +.toc_cells_L0 { background-color: #E2E9EF; padding-top: 4px; padding-right: 4px; padding-bottom: 4px; padding-left: 4px; font-family: Geneva, Arial, Helvetica, san-serif; font-size: small } +.toc_cells_L1 { background-color: #E2E9EF; padding-top: 4px; padding-right: 4px; padding-bottom: 4px; padding-left: 44px; font-family: Geneva, Arial, Helvetica, san-serif; font-size: small } +.toc_cells_L2 { background-color: #E2E9EF; padding-top: 4px; padding-right: 4px; padding-bottom: 4px; padding-left: 88px; font-family: Geneva, Arial, Helvetica, san-serif; font-size: small } +.toc_cells_L3 { background-color: #E2E9EF; padding-top: 4px; padding-right: 4px; padding-bottom: 4px; padding-left: 122px; font-family: Geneva, Arial, Helvetica, san-serif; font-size: small } +.toc_cells_L4 { background-color: #E2E9EF; padding-top: 4px; padding-right: 4px; padding-bottom: 4px; padding-left: 166px; font-family: Geneva, Arial, Helvetica, san-serif; font-size: small } diff --git a/pyste/doc/theme/u_arr.gif b/pyste/doc/theme/u_arr.gif new file mode 100644 index 0000000000000000000000000000000000000000..ada3d6e043d2e4314a20d6783f2800f2f21d89c9 GIT binary patch literal 170 zcmZ?wbhEHb6k!l%*vtR|#>U2JX=yWO&OCGG%>OfK|If_)4`TfP|DWOif8+oE)BgXT z`Tzf!|NsBLfB#pCE@bTiY5O-A{7vV42g%c7%l^E8u0x + + +Wrappers + + + + + + + + + + +
+ + Wrappers +
+
+ + + + + + +
+

+Suppose you have this function:

+
+    std::vector<std::string> names();
+
+

+But you don't want to export a vector<string>, you want this function to return +a python list of strings. +Boost.Python has an excellent support for that:

+
+    list names_wrapper()
+    {
+        list result;
+        vector<string> v = names();
+        // put each string in the vector in the list
+        return result;
+    }
+    
+    BOOST_PYTHON_MODULE(test)
+    {
+        def("names", &names_wrapper);
+    }
+
+

+Nice heh? +Pyste supports this mechanism too. You declare the names_wrapper function in a +header, like "test_wrappers.h", and in the interface file:

+
+    Include("test_wrappers.h")
+    names = Function("names", "test.h")
+    set_wrapper(names, "names_wrapper")
+
+

+You can optionally declare the function in the interface file itself:

+
+    names_wrapper = Wrapper("names_wrapper",
+    """
+    list names_wrapper()
+    {
+        // call name() and convert the vector to a list...
+    }
+    """)
+    names = Function("names", "test.h")
+    set_wrapper(names, names_wrapper)
+
+

+The same mechanism can be done with methods too. Just remember that the first +parameter of wrappers for methods is a pointer to the class, like in + +Boost.Python:

+
+    struct C
+    {
+        std::vector<std::string> names();
+    }
+
+    list names_wrapper(C* c)
+    {
+        // same as before, calling c->names() and converting result to a list
+    }
+
+

+And then in the interface file:

+
+    C = Class("C", "test.h")
+    set_wrapper(C.names, "names_wrapper")
+
+ + + + + + +
+
+
+ + diff --git a/pyste/index.html b/pyste/index.html new file mode 100644 index 00000000..ff153b50 --- /dev/null +++ b/pyste/index.html @@ -0,0 +1,71 @@ + + + +Pyste Documentation + + + + + + + + + +
+ + Pyste Documentation +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table of contents
+ Introduction +
+ Running Pyste +
+ The Interface Files +
+ Renaming and Excluding +
+ Policies +
+ Templates +
+ Wrappers +
+ Exporting All Declarations from a Header +
+
+
+ + diff --git a/pyste/src/.cvsignore b/pyste/src/.cvsignore new file mode 100644 index 00000000..0d20b648 --- /dev/null +++ b/pyste/src/.cvsignore @@ -0,0 +1 @@ +*.pyc diff --git a/pyste/src/ClassExporter.py b/pyste/src/ClassExporter.py new file mode 100644 index 00000000..d72ff9db --- /dev/null +++ b/pyste/src/ClassExporter.py @@ -0,0 +1,688 @@ +import exporters +from Exporter import Exporter +from declarations import * +from enumerate import enumerate +from settings import * +from CodeUnit import CodeUnit +from EnumExporter import EnumExporter + + +#============================================================================== +# ClassExporter +#============================================================================== +class ClassExporter(Exporter): + 'Generates boost.python code to export a class declaration' + + + def __init__(self, info, parser_tail=None): + Exporter.__init__(self, info, parser_tail) + # sections of code + self.sections = {} + # template: each item in the list is an item into the class_<...> + # section. + self.sections['template'] = [] + # constructor: each item in the list is a parameter to the class_ + # constructor, like class_(...) + self.sections['constructor'] = [] + # inside: everything within the class_<> statement + self.sections['inside'] = [] + # scope: items outside the class statement but within its scope. + # scope* s = new scope(class<>()); + # ... + # delete s; + self.sections['scope'] = [] + # declarations: outside the BOOST_PYTHON_MODULE macro + self.sections['declaration'] = [] + self.sections['include'] = [] + # a list of Method instances + self.methods = [] + # a list of Constructor instances + self.constructors = [] + # a dict of methodname => _WrapperVirtualMethod instances + self.virtual_wrappers = {} + # a list of code units, generated by nested declarations + self.nested_codeunits = [] + + + def ScopeName(self): + return _ID(self.class_.FullName()) + '_scope' + + + def Name(self): + return self.class_.FullName() + + + def SetDeclarations(self, declarations): + Exporter.SetDeclarations(self, declarations) + decl = self.GetDeclaration(self.info.name) + if isinstance(decl, Typedef): + self.class_ = decl.type + if not self.info.rename: + self.info.rename = decl.name + else: + self.class_ = decl + self.public_members = \ + [x for x in self.class_.members if x.visibility == Scope.public] + + + def Order(self): + '''Return the TOTAL number of bases that this class has, including the + bases' bases. Do this because base classes must be instantialized + before the derived classes in the module definition. + ''' + + def BasesCount(classname): + decl = self.GetDeclaration(classname) + bases = [x.name for x in decl.bases] + total = 0 + for base in bases: + total += BasesCount(base) + return len(bases) + total + + return BasesCount(self.class_.FullName()) + + + def Export(self, codeunit, exported_names): + self.GetMethods() + self.ExportBasics() + self.ExportBases(exported_names) + self.ExportConstructors() + self.ExportVariables() + self.ExportMethods() + self.GenerateVirtualWrapper() + self.ExportOperators() + self.ExportNestedClasses(exported_names) + self.ExportNestedEnums() + self.Write(codeunit) + + + def Write(self, codeunit): + indent = self.INDENT + boost_ns = namespaces.python + pyste_ns = namespaces.pyste + code = '' + # begin a scope for this class if needed + nested_codeunits = self.nested_codeunits + needs_scope = self.sections['scope'] or nested_codeunits + if needs_scope: + scope_name = self.ScopeName() + code += indent + boost_ns + 'scope* %s = new %sscope(\n' %\ + (scope_name, boost_ns) + # export the template section + template_params = ', '.join(self.sections['template']) + code += indent + boost_ns + 'class_< %s >' % template_params + # export the constructor section + constructor_params = ', '.join(self.sections['constructor']) + code += '(%s)\n' % constructor_params + # export the inside section + in_indent = indent*2 + for line in self.sections['inside']: + code += in_indent + line + '\n' + # write the scope section and end it + if not needs_scope: + code += indent + ';\n' + else: + code += indent + ');\n' + for line in self.sections['scope']: + code += indent + line + '\n' + # write the contents of the nested classes + for nested_unit in nested_codeunits: + code += '\n' + nested_unit.Section('module') + # close the scope + code += indent + 'delete %s;\n' % scope_name + + # write the code to the module section in the codeunit + codeunit.Write('module', code + '\n') + + # write the declarations to the codeunit + declarations = '\n'.join(self.sections['declaration']) + for nested_unit in nested_codeunits: + declarations += nested_unit.Section('declaration') + if declarations: + codeunit.Write('declaration', declarations + '\n') + + # write the includes to the codeunit + includes = '\n'.join(self.sections['include']) + for nested_unit in nested_codeunits: + includes += nested_unit.Section('include') + if includes: + codeunit.Write('include', includes) + + + def Add(self, section, item): + 'Add the item into the corresponding section' + self.sections[section].append(item.strip()) + + + def ExportBasics(self): + 'Export the name of the class and its class_ statement' + self.Add('template', self.class_.FullName()) + name = self.info.rename or self.class_.name + self.Add('constructor', '"%s"' % name) + + + def ExportBases(self, exported_names): + 'Expose the bases of the class into the template section' + bases = self.class_.bases + bases_list = [] + for base in bases: + if base.visibility == Scope.public and base.name in exported_names: + bases_list.append(base.name) + if bases_list: + code = namespaces.python + 'bases< %s > ' % \ + (', '.join(bases_list)) + self.Add('template', code) + + + def ExportConstructors(self): + '''Exports all the public contructors of the class, plus indicates if the + class is noncopyable. + ''' + py_ns = namespaces.python + indent = self.INDENT + + def init_code(cons): + 'return the init<>() code for the given contructor' + param_list = [p.FullName() for p in cons.parameters] + min_params_list = param_list[:cons.minArgs] + max_params_list = param_list[cons.minArgs:] + min_params = ', '.join(min_params_list) + max_params = ', '.join(max_params_list) + init = py_ns + 'init< ' + init += min_params + if max_params: + if min_params: + init += ', ' + init += py_ns + ('optional< %s >' % max_params) + init += ' >()' + return init + + constructors = [x for x in self.public_members if isinstance(x, Constructor)] + self.constructors = constructors[:] + if not constructors: + # declare no_init + self.Add('constructor', py_ns + 'no_init') + else: + # write one of the constructors to the class_ constructor + self.Add('constructor', init_code(constructors.pop(0))) + # write the rest to the inside section, using def() + for cons in constructors: + code = '.def(%s)' % init_code(cons) + self.Add('inside', code) + # check if the class is copyable + if not self.class_.HasCopyConstructor() or self.class_.abstract: + self.Add('template', namespaces.boost + 'noncopyable') + + + def ExportVariables(self): + 'Export the variables of the class, both static and simple variables' + vars = [x for x in self.public_members if isinstance(x, Variable)] + for var in vars: + if self.info[var.name].exclude: + continue + name = self.info[var.name].rename or var.name + fullname = var.FullName() + if var.static: + code = '%s->attr("%s") = %s;' % (self.ScopeName(), name, fullname) + self.Add('scope', code) + else: + if var.type.const: + def_ = '.def_readonly' + else: + def_ = '.def_readwrite' + code = '%s("%s", &%s)' % (def_, name, fullname) + self.Add('inside', code) + + + def GetMethods(self): + 'fill self.methods with a list of Method instances' + # get a list of all methods + def IsValid(m): + 'Returns true if the given method is exportable by this routine' + ignore = (Constructor, ClassOperator, Destructor) + return isinstance(m, Method) and not isinstance(m, ignore) + + self.methods = [x for x in self.public_members if IsValid(x)] + + + + printed_policy_warnings = {} + + def CheckPolicy(self, m): + 'Warns the user if this method needs a policy' + def IsString(type): + return type.const and type.name == 'char' and isinstance(type, PointerType) + needs_policy = isinstance(m.result, (ReferenceType, PointerType)) + if IsString(m.result): + needs_policy = False + has_policy = self.info[m.name].policy is not None + if needs_policy and not has_policy: + warning = '---> Error: Method "%s" needs a policy.' % m.FullName() + if warning not in self.printed_policy_warnings: + print warning + print + self.printed_policy_warnings[warning] = 1 + + + def ExportMethods(self): + 'Export all the methods of the class' + + def OverloadName(m): + 'Returns the name of the overloads struct for the given method' + + return _ID(m.FullName()) + ('_overloads_%i_%i' % (m.minArgs, m.maxArgs)) + + declared = {} + def DeclareOverloads(m): + 'Declares the macro for the generation of the overloads' + if m.virtual: + func = self.virtual_wrappers[m.PointerDeclaration()].DefaultName() + else: + func = m.name + code = 'BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(%s, %s, %i, %i)\n' + code = code % (OverloadName(m), func, m.minArgs, m.maxArgs) + if code not in declared: + declared[code] = True + self.Add('declaration', code) + + + def Pointer(m): + 'returns the correct pointer declaration for the method m' + # check if this method has a wrapper set for him + wrapper = self.info[method.name].wrapper + if wrapper: + return '&' + wrapper.FullName() + # if this method is virtual, return the pointers to the class and its wrapper + if m.virtual: + return self.virtual_wrappers[m.PointerDeclaration()].Pointer() + # return normal pointers to the methods of the class + is_unique = self.class_.IsUnique(m.name) + if is_unique: + return '&' + method.FullName() + else: + return method.PointerDeclaration() + + + for method in self.methods: + if self.info[method.name].exclude: + continue # skip this method + + name = self.info[method.name].rename or method.name + # check if this method needs to be wrapped as a virtual method + if method.virtual: + wrapper = _WrapperVirtualMethod(self.class_, method, name) + self.virtual_wrappers[method.PointerDeclaration()] = wrapper + # abstract methods don't need to be exported + if method.abstract: + continue # skip .def declaration for abstract methods + # warn the user if this method needs a policy and doesn't have one + self.CheckPolicy(method) + + # check for policies + policy = self.info[method.name].policy or '' + if policy: + policy = ', %s%s()' % (namespaces.python, policy.Code()) + # check for overloads + overload = '' + if method.minArgs != method.maxArgs: + # add the overloads for this method + overload_name = OverloadName(method) + DeclareOverloads(method) + if not method.virtual: + overload = ', %s%s()' % (namespaces.pyste, overload_name) + else: + pyste_ns = namespaces.pyste + pointer = self.virtual_wrappers[method.PointerDeclaration()].DefaultPointer() + defcode = '.def("%s", %s, %s%s())' % \ + (name, pointer, pyste_ns, overload_name) + self.Add('inside', defcode) + # build the string to export the method + pointer = Pointer(method) + code = '.def("%s", %s' % (name, pointer) + code += policy + code += overload + code += ')' + self.Add('inside', code) + # static method + if method.static: + code = '.staticmethod("%s")' % name + self.Add('inside', code) + # add wrapper code if this method has one + wrapper = self.info[method.name].wrapper + if wrapper and wrapper.code: + self.Add('declaration', wrapper.code) + + + def GenerateVirtualWrapper(self): + 'Generate the wrapper to dispatch virtual methods' + # check if this class needs a wrapper first + for m in self.methods: + if m.virtual: + break + else: + return + # add the wrapper name to the template section + wrapper_name = _WrapperName(self.class_) + self.Add('template', namespaces.pyste + wrapper_name) + indent = self.INDENT + method_codes = [x.Code(indent) for x in self.virtual_wrappers.values()] + body = '\n'.join(method_codes) + # generate the class code + class_name = self.class_.FullName() + code = 'struct %s: %s\n' % (wrapper_name, class_name) + code += '{\n' + # generate constructors + for cons in self.constructors: + params, param_names, param_types = _ParamsInfo(cons) + if params: + params = ', ' + params + cons_code = indent + '%s(PyObject* self_%s):\n' % (wrapper_name, params) + cons_code += indent*2 + '%s(%s), self(self_) {}\n\n' % \ + (class_name, ', '.join(param_names)) + code += cons_code + code += body + '\n' + code += indent + 'PyObject* self;\n' + code += '};\n' + self.Add('declaration', code) + + + # operators natively supported by boost + BOOST_SUPPORTED_OPERATORS = '+ - * / % ^ & ! ~ | < > == != <= >= << >> && || += -='\ + '*= /= %= ^= &= |= <<= >>='.split() + # create a map for faster lookup + BOOST_SUPPORTED_OPERATORS = dict(zip(BOOST_SUPPORTED_OPERATORS, range(len(BOOST_SUPPORTED_OPERATORS)))) + + # a dict of operators that are not directly supported by boost, but can be exposed + # simply as a function with a special signature + BOOST_RENAME_OPERATORS = { + '()' : '__call__', + } + + # converters which has a special name in python + SPECIAL_CONVETERS = { + 'double' : '__float__', + 'float' : '__float__', + 'int' : '__int__', + } + + + def ExportOperators(self): + 'Export all member operators and free operators related to this class' + + def GetFreeOperators(): + 'Get all the free (global) operators related to this class' + operators = [] + for decl in self.declarations: + if isinstance(decl, Operator): + # check if one of the params is this class + for param in decl.parameters: + if param.name == self.class_.FullName(): + operators.append(decl) + break + return operators + + def GetOperand(param): + 'Returns the operand of this parameter (either "self", or "other")' + if param.name == self.class_.FullName(): + return namespaces.python + 'self' + else: + return namespaces.python + ('other< %s >()' % param.name) + + + def HandleSpecialOperator(operator): + # gatter information about the operator and its parameters + result_name = operator.result.name + param1_name = '' + if operator.parameters: + param1_name = operator.parameters[0].name + + # check for str + ostream = 'basic_ostream' + is_str = result_name.find(ostream) != -1 and param1_name.find(ostream) != -1 + if is_str: + namespace = namespaces.python + 'self_ns::' + self_ = namespaces.python + 'self' + return '.def(%sstr(%s))' % (namespace, self_) + + # is not a special operator + return None + + + + frees = GetFreeOperators() + members = [x for x in self.public_members if type(x) == ClassOperator] + all_operators = frees + members + operators = [x for x in all_operators if not self.info['operator'][x.name].exclude] + + for operator in operators: + # gatter information about the operator, for use later + wrapper = self.info['operator'][operator.name].wrapper + if wrapper: + pointer = '&' + wrapper.FullName() + if wrapper.code: + self.Add('declaration', wrapper.code) + elif isinstance(operator, ClassOperator) and self.class_.IsUnique(operator.name): + pointer = '&' + operator.FullName() + else: + pointer = operator.PointerDeclaration() + rename = self.info['operator'][operator.name].rename + + # check if this operator will be exported as a method + export_as_method = wrapper or rename or operator.name in self.BOOST_RENAME_OPERATORS + + # check if this operator has a special representation in boost + special_code = HandleSpecialOperator(operator) + has_special_representation = special_code is not None + + if export_as_method: + # export this operator as a normal method, renaming or using the given wrapper + if not rename: + if wrapper: + rename = wrapper.name + else: + rename = self.BOOST_RENAME_OPERATORS[operator.name] + policy = '' + policy_obj = self.info['operator'][operator.name].policy + if policy_obj: + policy = ', %s()' % policy_obj.Code() + self.Add('inside', '.def("%s", %s%s)' % (rename, pointer, policy)) + + elif has_special_representation: + self.Add('inside', special_code) + + elif operator.name in self.BOOST_SUPPORTED_OPERATORS: + # export this operator using boost's facilities + op = operator + is_unary = isinstance(op, Operator) and len(op.parameters) == 1 or\ + isinstance(op, ClassOperator) and len(op.parameters) == 0 + if is_unary: + self.Add('inside', '.def( %s%sself )' % \ + (operator.name, namespaces.python)) + else: + # binary operator + if len(operator.parameters) == 2: + left_operand = GetOperand(operator.parameters[0]) + right_operand = GetOperand(operator.parameters[1]) + else: + left_operand = namespaces.python + 'self' + right_operand = GetOperand(operator.parameters[0]) + self.Add('inside', '.def( %s %s %s )' % \ + (left_operand, operator.name, right_operand)) + + # export the converters. + # export them as simple functions with a pre-determined name + + converters = [x for x in self.public_members if type(x) == ConverterOperator] + + def ConverterMethodName(converter): + result_fullname = converter.result.name + # extract the last name from the full name + result_name = _ID(result_fullname.split('::')[-1]) + return 'to_' + result_name + + for converter in converters: + info = self.info['operator'][converter.result.name] + # check if this operator should be excluded + if info.exclude: + continue + + special_code = HandleSpecialOperator(converter) + if info.rename or not special_code: + # export as method + name = info.rename or ConverterMethodName(converter) + if self.class_.IsUnique(converter.name): + pointer = '&' + converter.FullName() + else: + pointer = converter.PointerDeclaration() + policy_code = '' + if info.policy: + policy_code = ', %s()' % info.policy.Code() + self.Add('inside', '.def("%s", %s%s)' % (name, pointer, policy_code)) + + elif special_code: + self.Add('inside', special_code) + + + + def ExportNestedClasses(self, exported_names): + nested_classes = [x for x in self.public_members if isinstance(x, NestedClass)] + for nested_class in nested_classes: + nested_info = self.info[nested_class.name] + nested_info.include = self.info.include + nested_info.name = nested_class.FullName() + exporter = ClassExporter(nested_info) + exporter.SetDeclarations(self.declarations + [nested_class]) + codeunit = CodeUnit(None) + exporter.Export(codeunit, exported_names) + self.nested_codeunits.append(codeunit) + + + def ExportNestedEnums(self): + nested_enums = [x for x in self.public_members if isinstance(x, ClassEnumeration)] + for enum in nested_enums: + enum_info = self.info[enum.name] + enum_info.include = self.info.include + enum_info.name = enum.FullName() + exporter = EnumExporter(enum_info) + exporter.SetDeclarations(self.declarations + [enum]) + codeunit = CodeUnit(None) + exporter.Export(codeunit, None) + self.nested_codeunits.append(codeunit) + + + + +def _ID(name): + 'Returns the name as a valid identifier' + for invalidchar in ('::', '<', '>', ' ', ','): + name = name.replace(invalidchar, '_') + # avoid duplications of '_' chars + names = [x for x in name.split('_') if x] + return '_'.join(names) + + +#============================================================================== +# Virtual Wrapper utils +#============================================================================== + +def _WrapperName(class_): + return _ID(class_.FullName()) + '_Wrapper' + + +def _ParamsInfo(m): + param_names = ['p%i' % i for i in range(len(m.parameters))] + param_types = [x.FullName() for x in m.parameters] + params = ['%s %s' % (t, n) for t, n in zip(param_types, param_names)] + for i, p in enumerate(m.parameters): + if p.default is not None: + #params[i] += '=%s' % p.default + params[i] += '=%s' % (p.name + '()') + params = ', '.join(params) + return params, param_names, param_types + + +class _WrapperVirtualMethod(object): + 'Holds information about a virtual method that will be wrapped' + + def __init__(self, class_, method, rename): + self.method = method + if rename is None: + rename = method.name + self.rename = rename + self.class_ = class_ + + + def DefaultName(self): + return 'default_' + self.method.name + + + def DefaultPointer(self): + ns = namespaces.pyste + wrapper_name = _WrapperName(self.class_) + default_name = self.DefaultName() + fullname = '%s%s::%s' % (ns, wrapper_name, default_name) + if self.class_.IsUnique(self.method.name): + return '&%s' % fullname + else: + # the method is not unique, so we must specify the entire signature with it + param_list = [x.FullName() for x in self.method.parameters] + params = ', '.join(param_list) + result = self.method.result.FullName() + signature = '%s (%s%s::*)(%s)' % (result, ns, wrapper_name, params) + return '(%s)%s' % (signature, fullname) + + + def Pointer(self): + '''Returns the "pointer" declaration for this method, ie, the contents + of the .def after the method name (.def("name", ))''' + ns = namespaces.pyste + default_name = self.DefaultName() + name = self.method.name + class_name = self.class_.FullName() + wrapper = ns + _WrapperName(self.class_) + if self.class_.IsUnique(self.method.name): + return '&%s::%s, &%s::%s' % (class_name, name, wrapper, default_name) + else: + # the method is not unique, so we must specify the entire signature with it + param_list = [x.FullName() for x in self.method.parameters] + params = ', '.join(param_list) + result = self.method.result.FullName() + default_sig = '%s (%s::*)(%s)' % (result, wrapper, params) + normal_sig = '%s (%s::*)(%s)' % (result, class_name, params) + return '(%s)%s::%s, (%s)%s::%s' % \ + (normal_sig, class_name, name, default_sig, wrapper, default_name) + + + def Code(self, indent): + params, param_names, param_types = _ParamsInfo(self.method) + result = self.method.result.FullName() + return_ = 'return ' + if result == 'void': + return_ = '' + param_names = ', '.join(param_names) + class_name = self.class_.FullName() + method_name = self.method.name + default_name = self.DefaultName() + # constantness + const = '' + if self.method.const: + const = 'const ' + code = '' + # create default_method if this method has a default implementation + if not self.method.abstract: + default_sig = '%s %s(%s) %s' % (result, default_name, params, const) + body = '{ %s%s::%s(%s); } ' % \ + (return_, class_name, method_name, param_names) + code += indent + default_sig + body + '\n' + # create normal method + normal_sig = '%s %s(%s) %s' % (result, method_name, params, const) + if param_names: + param_names = ', ' + param_names + body = '{ %s%scall_method< %s >(self, "%s"%s); }' % \ + (return_, namespaces.python, result, self.rename, param_names) + code += indent + normal_sig + body + '\n' + + return code + + + diff --git a/pyste/src/CodeUnit.py b/pyste/src/CodeUnit.py new file mode 100644 index 00000000..ac123f99 --- /dev/null +++ b/pyste/src/CodeUnit.py @@ -0,0 +1,78 @@ +from settings import * + +#============================================================================== +# RemoveDuplicatedLines +#============================================================================== +def RemoveDuplicatedLines(text): + includes = text.splitlines() + d = dict([(include, 0) for include in includes]) + return '\n'.join(d.keys()) + + +#============================================================================== +# CodeUnit +#============================================================================== +class CodeUnit: + ''' + Represents a cpp file, where other objects can write in one of the + predefined sections. + The avaiable sections are: + include - The include area of the cpp file + declaration - The part before the module definition + module - Inside the BOOST_PYTHON_MODULE macro + ''' + + USING_BOOST_NS = True + + def __init__(self, modulename): + self.modulename = modulename + # define the avaiable sections + self.code = {} + self.code['include'] = '' + self.code['declaration'] = '' + self.code['module'] = '' + + + def Write(self, section, code): + 'write the given code in the section of the code unit' + if section not in self.code: + raise RuntimeError, 'Invalid CodeUnit section: %s' % section + self.code[section] += code + + + def Section(self, section): + return self.code[section] + + + def Save(self, filename): + 'Writes this code unit to the filename' + space = '\n\n' + fout = file(filename, 'w') + # includes + includes = RemoveDuplicatedLines(self.code['include']) + fout.write('\n' + self._leftEquals('Includes')) + fout.write('#include \n') + fout.write(includes) + fout.write(space) + # using + if self.USING_BOOST_NS: + fout.write(self._leftEquals('Using')) + fout.write('using namespace boost::python;\n\n') + # declarations + if self.code['declaration']: + pyste_namespace = namespaces.pyste[:-2] + fout.write(self._leftEquals('Declarations')) + fout.write('namespace %s {\n\n\n' % pyste_namespace) + fout.write(self.code['declaration']) + fout.write('\n\n}// namespace %s\n' % pyste_namespace) + fout.write(space) + # module + fout.write(self._leftEquals('Module')) + fout.write('BOOST_PYTHON_MODULE(%s)\n{\n' % self.modulename) + fout.write(self.code['module']) + fout.write('}\n') + + + def _leftEquals(self, s): + s = '// %s ' % s + return s + ('='*(80-len(s))) + '\n' diff --git a/pyste/src/CppParser.py b/pyste/src/CppParser.py new file mode 100644 index 00000000..2dd9c9ff --- /dev/null +++ b/pyste/src/CppParser.py @@ -0,0 +1,94 @@ +from GCCXMLParser import ParseDeclarations +import tempfile +import shutil +import os +import os.path +import settings + +class CppParserError(Exception): pass + + +class CppParser: + 'Parses a header file and returns a list of declarations' + + def __init__(self, includes=None, defines=None): + 'includes and defines ar the directives given to gcc' + if includes is None: + includes = [] + if defines is None: + defines = [] + self.includes = includes + self.defines = defines + + + def _includeparams(self, filename): + includes = self.includes[:] + filedir = os.path.dirname(filename) + if not filedir: + filedir = '.' + includes.insert(0, filedir) + includes = ['-I "%s"' % x for x in includes] + return ' '.join(includes) + + + def _defineparams(self): + defines = ['-D "%s"' % x for x in self.defines] + return ' '.join(defines) + + + def FindFileName(self, include): + if os.path.isfile(include): + return include + for path in self.includes: + filename = os.path.join(path, include) + if os.path.isfile(filename): + return filename + name = os.path.basename(include) + raise RuntimeError, 'Header file "%s" not found!' % name + + + def parse(self, include, symbols=None, tail=None): + '''Parses the given filename, and returns (declaration, header). The + header returned is normally the same as the given to this method, + except if tail is not None: in this case, the header is copied to a temp + filename and the tail code is appended to it before being passed on to gcc. + This temp filename is then returned. + ''' + filename = self.FindFileName(include) + # copy file to temp folder, if needed + if tail: + tempfilename = tempfile.mktemp('.h') + infilename = tempfilename + shutil.copy(filename, infilename) + f = file(infilename, 'a') + f.write('\n\n'+tail) + f.close() + else: + infilename = filename + xmlfile = tempfile.mktemp('.xml') + try: + # get the params + includes = self._includeparams(filename) + defines = self._defineparams() + # call gccxml + cmd = 'gccxml %s %s %s -fxml=%s' \ + % (includes, defines, infilename, xmlfile) + if symbols: + cmd += ' -fxml-start=' + ','.join(symbols) + status = os.system(cmd) + if status != 0 or not os.path.isfile(xmlfile): + raise CppParserError, 'Error executing gccxml' + # parse the resulting xml + declarations = ParseDeclarations(xmlfile) + # return the declarations + return declarations, infilename + finally: + if settings.DEBUG and os.path.isfile(xmlfile): + filename = os.path.basename(include) + shutil.copy(xmlfile, os.path.splitext(filename)[0] + '.xml') + # delete the temporary files + try: + os.remove(xmlfile) + if tail: + os.remove(tempfilename) + except OSError: pass diff --git a/pyste/src/EnumExporter.py b/pyste/src/EnumExporter.py new file mode 100644 index 00000000..fda4d721 --- /dev/null +++ b/pyste/src/EnumExporter.py @@ -0,0 +1,30 @@ +from Exporter import Exporter +from settings import * + +#============================================================================== +# EnumExporter +#============================================================================== +class EnumExporter(Exporter): + 'Exports enumerators' + + def __init__(self, info): + Exporter.__init__(self, info) + + + def SetDeclarations(self, declarations): + Exporter.SetDeclarations(self, declarations) + self.enum = self.GetDeclaration(self.info.name) + + + def Export(self, codeunit, expoted_names): + indent = self.INDENT + in_indent = self.INDENT*2 + rename = self.info.rename or self.enum.name + full_name = self.enum.FullName() + code = indent + namespaces.python + 'enum_< %s >("%s")\n' % (full_name, rename) + for name in self.enum.values: + rename = self.info[name].rename or name + value_fullname = self.enum.ValueFullName(name) + code += in_indent + '.value("%s", %s)\n' % (rename, value_fullname) + code += indent + ';\n\n' + codeunit.Write('module', code) diff --git a/pyste/src/Exporter.py b/pyste/src/Exporter.py new file mode 100644 index 00000000..02259582 --- /dev/null +++ b/pyste/src/Exporter.py @@ -0,0 +1,69 @@ +import os.path + +#============================================================================== +# Exporter +#============================================================================== +class Exporter: + 'Base class for objects capable to generate boost.python code.' + + INDENT = ' ' * 4 + + def __init__(self, info, parser_tail=None): + self.info = info + self.parser_tail = parser_tail + + + def Parse(self, parser): + self.parser = parser + header = self.info.include + tail = self.parser_tail + declarations, parser_header = parser.parse(header, tail=tail) + self.parser_header = parser_header + self.SetDeclarations(declarations) + + + def SetDeclarations(self, declarations): + self.declarations = declarations + + + def GenerateCode(self, codeunit, exported_names): + self.WriteInclude(codeunit) + self.Export(codeunit, exported_names) + + + def WriteInclude(self, codeunit): + codeunit.Write('include', '#include <%s>\n' % self.info.include) + + + def Export(self, codeunit, exported_names): + 'subclasses must override this to do the real work' + pass + + + def Name(self): + '''Returns the name of this Exporter. The name will be added to the + list of names exported, which may have a use for other exporters. + ''' + return None + + + def GetDeclarations(self, fullname): + decls = [x for x in self.declarations if x.FullName() == fullname] + if not decls: + raise RuntimeError, 'no %s declaration found!' % fullname + return decls + + + def GetDeclaration(self, fullname): + decls = self.GetDeclarations(fullname) + assert len(decls) == 1 + return decls[0] + + + def Order(self): + '''Returns a number that indicates to which order this exporter + belongs. The exporters will be called from the lowest order to the + highest order. + This function will only be called after Parse has been called. + ''' + return None # don't care diff --git a/pyste/src/FunctionExporter.py b/pyste/src/FunctionExporter.py new file mode 100644 index 00000000..2638a776 --- /dev/null +++ b/pyste/src/FunctionExporter.py @@ -0,0 +1,81 @@ +from Exporter import Exporter +from policies import * +from declarations import * +from settings import * + + +class FunctionExporter(Exporter): + 'Generates boost.python code to export the given function.' + + def __init__(self, info, tail=None): + Exporter.__init__(self, info, tail) + + + def Export(self, codeunit, exported_names): + decls = self.GetDeclarations(self.info.name) + for decl in decls: + self.CheckPolicy(decl) + self.ExportDeclaration(decl, len(decls) == 1, codeunit) + self.GenerateOverloads(decls, codeunit) + + + def Name(self): + return self.info.name + + + def CheckPolicy(self, func): + 'Warns the user if this function needs a policy' + needs_policy = isinstance(func.result, (ReferenceType, PointerType)) + if needs_policy and self.info.policy is None: + print '---> Error: Function "%s" needs a policy.' % func.FullName() + print + + def ExportDeclaration(self, decl, unique, codeunit): + name = self.info.rename or decl.name + defs = namespaces.python + 'def("%s", ' % name + wrapper = self.info.wrapper + if wrapper: + pointer = '&' + wrapper.FullName() + elif not unique: + pointer = decl.PointerDeclaration() + else: + pointer = '&' + decl.FullName() + defs += pointer + defs += self.PolicyCode() + overload = self.OverloadName(decl) + if overload: + defs += ', %s()' % (namespaces.pyste + overload) + defs += ');' + codeunit.Write('module', self.INDENT + defs + '\n') + # add the code of the wrapper + if wrapper and wrapper.code: + codeunit.Write('declaration', code + '\n') + + + def OverloadName(self, decl): + if decl.minArgs != decl.maxArgs: + return '%s_overloads_%i_%i' % \ + (decl.name, decl.minArgs, decl.maxArgs) + else: + return '' + + + def GenerateOverloads(self, declarations, codeunit): + codes = {} + for decl in declarations: + overload = self.OverloadName(decl) + if overload and overload not in codes: + code = 'BOOST_PYTHON_FUNCTION_OVERLOADS(%s, %s, %i, %i)' %\ + (overload, decl.FullName(), decl.minArgs, decl.maxArgs) + codeunit.Write('declaration', code + '\n') + codes[overload] = None + + + def PolicyCode(self): + policy = self.info.policy + if policy is not None: + assert isinstance(policy, Policy) + return ', %s()' % policy.Code() + else: + return '' + diff --git a/pyste/src/GCCXMLParser.py b/pyste/src/GCCXMLParser.py new file mode 100644 index 00000000..937db92f --- /dev/null +++ b/pyste/src/GCCXMLParser.py @@ -0,0 +1,395 @@ +from declarations import * +from elementtree.ElementTree import ElementTree +from xml.parsers.expat import ExpatError +from copy import deepcopy + + +class InvalidXMLError(Exception): pass + +class ParserError(Exception): pass + +class InvalidContextError(ParserError): pass + + +class GCCXMLParser(object): + 'Parse a GCC_XML file and extract the top-level declarations.' + + interested_tags = {'Class':0, 'Function':0, 'Variable':0, 'Enumeration':0} + + def Parse(self, filename): + self.elements = self.GetElementsFromXML(filename) + # high level declarations + self.declarations = [] + # parse the elements + for id in self.elements: + element, decl = self.elements[id] + if decl is None: + try: + self.ParseElement(id, element) + except InvalidContextError: + pass # ignore those nodes with invalid context + # (workaround gccxml bug) + + + def Declarations(self): + return self.declarations + + + def AddDecl(self, decl): + self.declarations.append(decl) + + + def ParseElement(self, id, element): + method = 'Parse' + element.tag + if hasattr(self, method): + func = getattr(self, method) + func(id, element) + + + def GetElementsFromXML(self,filename): + 'Extracts a dictionary of elements from the gcc_xml file.' + + tree = ElementTree() + try: + tree.parse(filename) + except ExpatError: + raise InvalidXMLError, 'Not a XML file: %s' % filename + + root = tree.getroot() + if root.tag != 'GCC_XML': + raise InvalidXMLError, 'Not a valid GCC_XML file' + + # build a dictionary of id -> element, None + elementlist = root.getchildren() + elements = {} + for element in elementlist: + id = element.get('id') + if id: + elements[id] = element, None + return elements + + + def GetDecl(self, id): + if id not in self.elements: + if id == '_0': + raise InvalidContextError, 'Invalid context found in the xml file.' + else: + msg = 'ID not found in elements: %s' % id + raise ParserError, msg + + elem, decl = self.elements[id] + if decl is None: + self.ParseElement(id, elem) + elem, decl = self.elements[id] + if decl is None: + raise ParserError, 'Could not parse element: %s' % elem.tag + return decl + + + def GetType(self, id): + const = False + volatile = False + if id[-1] == 'v': + volatile = True + id = id[:-1] + if id[-1] == 'c': + const = True + id = id[:-1] + decl = self.GetDecl(id) + if isinstance(decl, Type): + res = deepcopy(decl) + if const: + res.const = const + if volatile: + res.volatile = volatile + else: + res = Type(decl.FullName(), const) + res.volatile = volatile + return res + + + def GetLocation(self, location): + file, line = location.split(':') + file = self.GetDecl(file) + return file, int(line) + + + def Update(self, id, decl): + element, _ = self.elements[id] + self.elements[id] = element, decl + + + def ParseNamespace(self, id, element): + namespace = element.get('name') + context = element.get('context') + if context: + outerns = self.GetDecl(context) + if not outerns.endswith('::'): + outerns += '::' + namespace = outerns + namespace + if namespace.startswith('::'): + namespace = namespace[2:] + self.Update(id, namespace) + + + def ParseFile(self, id, element): + filename = element.get('name') + self.Update(id, filename) + + + def ParseVariable(self, id, element): + # in gcc_xml, a static Field is declared as a Variable, so we check + # this and call the Field parser if apply. + context = self.GetDecl(element.get('context')) + if isinstance(context, Class): + self.ParseField(id, element) + elem, decl = self.elements[id] + decl.static = True + else: + namespace = context + name = element.get('name') + type_ = self.GetType(element.get('type')) + location = self.GetLocation(element.get('location')) + variable = Variable(type_, name, namespace) + variable.location = location + self.AddDecl(variable) + self.Update(id, variable) + + + def GetArguments(self, element): + args = [] + for child in element: + if child.tag == 'Argument': + type_ = self.GetType(child.get('type')) + type_.default = child.get('default') + args.append(type_) + return args + + + def ParseFunction(self, id, element, functionType=Function): + '''functionType is used because a Operator is identical to a normal + function, only the type of the function changes.''' + name = element.get('name') + returns = self.GetType(element.get('returns')) + namespace = self.GetDecl(element.get('context')) + location = self.GetLocation(element.get('location')) + params = self.GetArguments(element) + function = functionType(name, namespace, returns, params) + function.location = location + self.AddDecl(function) + self.Update(id, function) + + + def ParseOperatorFunction(self, id, element): + self.ParseFunction(id, element, Operator) + + + def GetBases(self, bases): + 'Parses the string "bases" from the xml into a list of Base instances.' + + if bases is None: + return [] + bases = bases.split() + baseobjs = [] + for base in bases: + # get the visibility + split = base.split(':') + if len(split) == 2: + visib = split[0] + base = split[1] + else: + visib = Scope.public + decl = self.GetDecl(base) + baseobj = Base(decl.FullName(), visib) + baseobjs.append(baseobj) + return baseobjs + + + def GetMembers(self, members): + # members must be a string with the ids of the members + if members is None: + return [] + memberobjs = [] + for member in members.split(): + memberobjs.append(self.GetDecl(member)) + return memberobjs + + + def ParseClass(self, id, element): + name = element.get('name') + abstract = bool(int(element.get('abstract', '0'))) + bases = self.GetBases(element.get('bases')) + location = self.GetLocation(element.get('location')) + context = self.GetDecl(element.get('context')) + if isinstance(context, Class): # a nested class + visib = element.get('access', Scope.public) + class_ = NestedClass( + name, context.FullName(), visib, [], abstract, bases) + else: + assert isinstance(context, str) + class_ = Class(name, context, [], abstract, bases) + self.AddDecl(class_) + # we have to add the declaration of the class before trying + # to parse its members, to avoid recursion. + class_.location = location + self.Update(id, class_) + # now we can get the members + class_.members = self.GetMembers(element.get('members')) + + + def ParseStruct(self, id, element): + self.ParseClass(id, element) + + + def ParseFundamentalType(self, id, element): + name = element.get('name') + type_ = FundamentalType(name) + self.Update(id, type_) + + + def ParseArrayType(self, id, element): + type_ = self.GetType(element.get('type')) + min = element.get('min') + max = element.get('max') + if min: + min = int(min) + if max: + max = int(max) + array = ArrayType(type_.name, min, max, type_.const) + self.Update(id, array) + + + def ParseReferenceType(self, id, element): + type_ = self.GetType(element.get('type')) + expand = not isinstance(type_, FunctionType) + ref = ReferenceType(type_.name, type_.const, None, expand) + self.Update(id, ref) + + + def ParsePointerType(self, id, element): + type_ = self.GetType(element.get('type')) + expand = not isinstance(type_, FunctionType) + ref = PointerType(type_.name, type_.const, None, expand) + self.Update(id, ref) + + + def ParseFunctionType(self, id, element): + result = self.GetType(element.get('returns')) + args = self.GetArguments(element) + func = FunctionType(result, args) + self.Update(id, func) + + + def ParseMethodType(self, id, element): + class_ = self.GetDecl(element.get('basetype')).FullName() + result = self.GetType(element.get('returns')) + args = self.GetArguments(element) + method = MethodType(result, args, class_) + self.Update(id, method) + + + def ParseField(self, id, element): + name = element.get('name') + visib = element.get('access', Scope.public) + classname = self.GetDecl(element.get('context')).FullName() + type_ = self.GetType(element.get('type')) + static = bool(int(element.get('extern', '0'))) + location = self.GetLocation(element.get('location')) + var = ClassVariable(type_, name, classname, visib, static) + var.location = location + self.Update(id, var) + + + def ParseMethod(self, id, element, methodType=Method): + name = element.get('name') + result = self.GetType(element.get('returns')) + classname = self.GetDecl(element.get('context')).FullName() + visib = element.get('access', Scope.public) + static = bool(int(element.get('static', '0'))) + virtual = bool(int(element.get('virtual', '0'))) + abstract = bool(int(element.get('pure_virtual', '0'))) + const = bool(int(element.get('const', '0'))) + location = self.GetLocation(element.get('location')) + params = self.GetArguments(element) + method = methodType( + name, classname, result, params, visib, virtual, abstract, static, const) + method.location = location + self.Update(id, method) + + + def ParseOperatorMethod(self, id, element): + self.ParseMethod(id, element, ClassOperator) + + + def ParseConstructor(self, id, element): + name = element.get('name') + visib = element.get('access', Scope.public) + classname = self.GetDecl(element.get('context')).FullName() + location = self.GetLocation(element.get('location')) + params = self.GetArguments(element) + ctor = Constructor(name, classname, params, visib) + ctor.location = location + self.Update(id, ctor) + + + def ParseDestructor(self, id, element): + name = element.get('name') + visib = element.get('access', Scope.public) + classname = self.GetDecl(element.get('context')).FullName() + virtual = bool(int(element.get('virtual', '0'))) + location = self.GetLocation(element.get('location')) + des = Destructor(name, classname, visib, virtual) + des.location = location + self.Update(id, des) + + + def ParseConverter(self, id, element): + self.ParseMethod(id, element, ConverterOperator) + + + def ParseTypedef(self, id, element): + name = element.get('name') + type = self.GetDecl(element.get('type')) + context = self.GetDecl(element.get('context')) + if isinstance(context, Class): + context = context.FullName() + typedef = Typedef(type, name, context) + self.Update(id, typedef) + self.AddDecl(typedef) + + + def ParseEnumeration(self, id, element): + name = element.get('name') + location = self.GetLocation(element.get('location')) + context = self.GetDecl(element.get('context')) + if isinstance(context, Class): + visib = element.get('access', Scope.public) + enum = ClassEnumeration(name, context.FullName(), visib) + else: + enum = Enumeration(name, context) + self.AddDecl(enum) # in this case, is a top level decl + enum.location = location + for child in element: + if child.tag == 'EnumValue': + name = child.get('name') + value = int(child.get('init')) + enum.values[name] = value + self.Update(id, enum) + + + def ParseUnimplemented(self, id, element): + 'No idea of what this is' + self.Update(id, Declaration('', '')) + + + def ParseUnion(self, id, element): + self.Update(id, Declaration(element.get('name'), '')) + + + +def ParseDeclarations(filename): + 'Returns a list of the top declarations found in the gcc_xml file.' + + parser = GCCXMLParser() + parser.Parse(filename) + return parser.Declarations() diff --git a/pyste/src/HeaderExporter.py b/pyste/src/HeaderExporter.py new file mode 100644 index 00000000..9234e8af --- /dev/null +++ b/pyste/src/HeaderExporter.py @@ -0,0 +1,67 @@ +from Exporter import Exporter +from ClassExporter import ClassExporter +from FunctionExporter import FunctionExporter +from EnumExporter import EnumExporter +from infos import * +from declarations import * +import os.path +import exporters + +#============================================================================== +# HeaderExporter +#============================================================================== +class HeaderExporter(Exporter): + 'Exports all declarations found in the given header' + + def __init__(self, info, parser_tail=None): + Exporter.__init__(self, info, parser_tail) + + + def WriteInclude(self, codeunit): + pass + + + def SetDeclarations(self, declarations): + def IsInternalName(name): + '''Returns true if the given name looks like a internal compiler + structure''' + return name.startswith('__') + + Exporter.SetDeclarations(self, declarations) + header = os.path.normpath(self.parser_header) + for decl in declarations: + # check if this declaration is in the header + location = os.path.normpath(decl.location[0]) + if location != header or IsInternalName(decl.name): + continue + # ok, check the type of the declaration and export it accordingly + self.HandleDeclaration(decl) + + + def HandleDeclaration(self, decl): + '''Dispatch the declaration to the appropriate method, that must create + a suitable info object for a Exporter, create a Exporter, set its + declarations and append it to the list of exporters. + ''' + dispatch_table = { + Class : ClassExporter, + Enumeration : EnumExporter, + Function : FunctionExporter, + } + + for decl_type, exporter_type in dispatch_table.items(): + if type(decl) == decl_type: + self.HandleExporter(decl, exporter_type) + break + + + def HandleExporter(self, decl, exporter_type): + info = self.info[decl.name] + info.name = decl.FullName() + info.include = self.info.include + exporter = exporter_type(info) + exporter.SetDeclarations(self.declarations) + exporters.exporters.append(exporter) + + + diff --git a/pyste/src/IncludeExporter.py b/pyste/src/IncludeExporter.py new file mode 100644 index 00000000..2a7b0602 --- /dev/null +++ b/pyste/src/IncludeExporter.py @@ -0,0 +1,19 @@ +import os.path +from Exporter import Exporter + +#============================================================================== +# IncludeExporter +#============================================================================== +class IncludeExporter(Exporter): + '''Writes an include declaration to the module. Useful to add extra code + for use in the Wrappers. + This class just reimplements the Parse method to do nothing: the + WriteInclude in Exporter already does the work for us. + ''' + + def __init__(self, info, parser_tail=None): + Exporter.__init__(self, info, parser_tail) + + def Parse(self, parser): + pass + diff --git a/pyste/src/declarations.py b/pyste/src/declarations.py new file mode 100644 index 00000000..adba6fd3 --- /dev/null +++ b/pyste/src/declarations.py @@ -0,0 +1,452 @@ +''' +Module declarations + + Defines classes that represent declarations found in C++ header files. + +''' + +class Declaration(object): + 'Represents a basic declaration.' + + def __init__(self, name, namespace): + # the declaration name + self.name = name + # all the namespaces, separated by '::' = 'boost::inner' + self.namespace = namespace + # tuple (filename, line) + self.location = '', -1 + + + def FullName(self): + 'Returns the full qualified name: "boost::inner::Test"' + namespace = self.namespace or '' + #if not namespace: + # namespace = '' + if namespace and not namespace.endswith('::'): + namespace += '::' + return namespace + self.name + + + def __repr__(self): + return '' % (self.FullName(), id(self)) + + + def __str__(self): + return 'Declaration of %s' % self.FullName() + + + +class Class(Declaration): + 'The declaration of a class or struct.' + + def __init__(self, name, namespace, members, abstract, bases): + Declaration.__init__(self, name, namespace) + # list of members + self.members = members + # whatever the class has any abstract methods + self.abstract = abstract + # instances of Base + self.bases = bases + self._members_count = {} + + + def __iter__(self): + return iter(self.members) + + + def IsAbstract(self): + 'Returns True if any method of this class is abstract' + for member in self.members: + if isinstance(member, Method): + if member.abstract: + return True + return False + + + def RawName(self): + 'Returns the raw name of a template class. name = Foo, raw = Foo' + lesspos = self.name.find('<') + if lesspos != -1: + return self.name[:lesspos] + else: + return self.name + + + def Constructors(self, publics_only=True): + constructors = [] + for member in self: + if isinstance(member, Constructor): + if publics_only and member.visibility != Scope.public: + continue + constructors.append(member) + return constructors + + + def HasCopyConstructor(self): + for cons in self.Constructors(): + if cons.IsCopy(): + return True + return False + + + def HasDefaultConstructor(self): + for cons in self.Constructors(): + if cons.IsDefault(): + return True + return False + + + def IsUnique(self, member_name): + if not self._members_count: + for m in self: + self._members_count[m.name] = self._members_count.get(m.name, 0) + 1 + try: + return self._members_count[member_name] == 1 + except KeyError: + print self._members_count + print 'Key', member_name + + + +class NestedClass(Class): + 'The declaration of a class/struct inside another class/struct.' + + def __init__(self, name, class_, visib, members, abstract, bases): + Class.__init__(self, name, None, members, abstract, bases) + self.class_ = class_ + self.visibility = visib + + + def FullName(self): + return '%s::%s' % (self.class_, self.name) + + + +class Base: + 'Represents a base class of another class.' + + def __init__(self, name, visibility=None): + # class_ is the full name of the base class + self.name = name + # visibility of the derivation + if visibility is None: + visibility = Scope.public + self.visibility = visibility + + + +class Scope: + public = 'public' + private = 'private' + protected = 'protected' + + + +class Function(Declaration): + 'The declaration of a function.' + + def __init__(self, name, namespace, result, params): + Declaration.__init__(self, name, namespace) + # the result type: instance of Type, or None (constructors) + self.result = result + # the parameters: instances of Type + self.parameters = params + + + def PointerDeclaration(self): + 'returns a declaration of a pointer to this function' + result = self.result.FullName() + params = ', '.join([x.FullName() for x in self.parameters]) + return '(%s (*)(%s))%s' % (result, params, self.FullName()) + + + def _MinArgs(self): + min = 0 + for arg in self.parameters: + if arg.default is None: + min += 1 + return min + + minArgs = property(_MinArgs) + + + def _MaxArgs(self): + return len(self.parameters) + + maxArgs = property(_MaxArgs) + + + +class Operator(Function): + 'The declaration of a custom operator.' + def FullName(self): + namespace = self.namespace or '' + if not namespace.endswith('::'): + namespace += '::' + return namespace + 'operator' + self.name + + + +class Method(Function): + 'The declaration of a method.' + + def __init__(self, name, class_, result, params, visib, virtual, abstract, static, const): + Function.__init__(self, name, None, result, params) + self.visibility = visib + self.virtual = virtual + self.abstract = abstract + self.static = static + self.class_ = class_ + self.const = const + + + def FullName(self): + return self.class_ + '::' + self.name + + + def PointerDeclaration(self): + 'returns a declaration of a pointer to this function' + result = self.result.FullName() + params = ', '.join([x.FullName() for x in self.parameters]) + const = '' + if self.const: + const = 'const' + return '(%s (%s::*)(%s) %s)%s' %\ + (result, self.class_, params, const, self.FullName()) + + +class Constructor(Method): + 'A constructor of a class.' + + def __init__(self, name, class_, params, visib): + Method.__init__(self, name, class_, None, params, visib, False, False, False, False) + + + def IsDefault(self): + return len(self.parameters) == 0 + + + def IsCopy(self): + if len(self.parameters) != 1: + return False + param = self.parameters[0] + class_as_param = self.parameters[0].name == self.class_ + param_reference = isinstance(param, ReferenceType) + return param_reference and class_as_param and param.const + + +class Destructor(Method): + 'The destructor of a class.' + + def __init__(self, name, class_, visib, virtual): + Method.__init__(self, name, class_, None, [], visib, virtual, False, False, False) + + def FullName(self): + return self.class_ + '::~' + self.name + + + +class ClassOperator(Method): + 'The declaration of a custom operator in a class.' + + def FullName(self): + return self.class_ + '::operator ' + self.name + + + +class ConverterOperator(ClassOperator): + 'An operator in the form "operator OtherClass()".' + + def FullName(self): + return self.class_ + '::operator ' + self.result.name + + + +class Type(Declaration): + 'Represents a type.' + + def __init__(self, name, const=False, default=None): + Declaration.__init__(self, name, None) + # whatever the type is constant or not + self.const = const + # used when the Type is a function argument + self.default = default + self.volatile = False + + def __repr__(self): + if self.const: + const = 'const ' + else: + const = '' + return '' + + + def FullName(self): + if self.const: + const = 'const ' + else: + const = '' + return const + self.name + + + +class ArrayType(Type): + 'Represents an array.' + + def __init__(self, name, min, max, const=False): + 'min and max can be None.' + Type.__init__(self, name, const) + self.min = min + self.max = max + + + +class ReferenceType(Type): + 'A reference type.' + + def __init__(self, name, const=False, default=None, expandRef=True): + Type.__init__(self, name, const, default) + self.expand = expandRef + + + def FullName(self): + 'expand is False for function pointers' + expand = ' &' + if not self.expand: + expand = '' + return Type.FullName(self) + expand + + + +class PointerType(Type): + 'A pointer type.' + + def __init__(self, name, const=False, default=None, expandPointer=False): + Type.__init__(self, name, const, default) + self.expand = expandPointer + + + def FullName(self): + 'expand is False for function pointer' + expand = ' *' + if not self.expand: + expand = '' + return Type.FullName(self) + expand + + + +class FundamentalType(Type): + 'One of the fundamental types (int, void...).' + + def __init__(self, name, const=False): + Type.__init__(self, name, const) + + + +class FunctionType(Type): + 'A pointer to a function.' + + def __init__(self, result, params): + Type.__init__(self, '', False) + self.result = result + self.parameters = params + self.name = self.FullName() + + + def FullName(self): + full = '%s (*)' % self.result.FullName() + params = [x.FullName() for x in self.parameters] + full += '(%s)' % ', '.join(params) + return full + + + +class MethodType(FunctionType): + 'A pointer to a member function of a class.' + + def __init__(self, result, params, class_): + Type.__init__(self, '', False) + self.result = result + self.parameters = params + self.class_ = class_ + self.name = self.FullName() + + def FullName(self): + full = '%s (%s::*)' % (self.result.FullName(), self.class_) + params = [x.FullName() for x in self.parameters] + full += '(%s)' % ', '.join(params) + return full + + + +class Variable(Declaration): + 'Represents a global variable.' + + def __init__(self, type, name, namespace): + Declaration.__init__(self, name, namespace) + # instance of Type + self.type = type + + + +class ClassVariable(Variable): + 'Represents a class variable.' + + def __init__(self, type, name, class_, visib, static): + Variable.__init__(self, type, name, None) + self.visibility = visib + self.static = static + self.class_ = class_ + + + def FullName(self): + return self.class_ + '::' + self.name + + + +class Enumeration(Declaration): + + def __init__(self, name, namespace): + Declaration.__init__(self, name, namespace) + self.values = {} # dict of str => int + + def ValueFullName(self, name): + assert name in self.values + namespace = self.namespace + if namespace: + namespace += '::' + return namespace + name + + + +class ClassEnumeration(Enumeration): + + def __init__(self, name, class_, visib): + Enumeration.__init__(self, name, None) + self.class_ = class_ + self.visibility = visib + + + def FullName(self): + return '%s::%s' % (self.class_, self.name) + + + def ValueFullName(self, name): + assert name in self.values + return '%s::%s' % (self.class_, name) + + + +class Typedef(Declaration): + + def __init__(self, type, name, namespace): + Declaration.__init__(self, name, namespace) + self.type = type + self.visibility = Scope.public + + + + + + + diff --git a/pyste/src/enumerate.py b/pyste/src/enumerate.py new file mode 100644 index 00000000..099e42ba --- /dev/null +++ b/pyste/src/enumerate.py @@ -0,0 +1,7 @@ +from __future__ import generators + +def enumerate(seq): + i = 0 + for x in seq: + yield i, x + i += 1 diff --git a/pyste/src/exporters.py b/pyste/src/exporters.py new file mode 100644 index 00000000..65536780 --- /dev/null +++ b/pyste/src/exporters.py @@ -0,0 +1,3 @@ + +# a list of Exporter instances +exporters = [] diff --git a/pyste/src/exporterutils.py b/pyste/src/exporterutils.py new file mode 100644 index 00000000..5134a1e5 --- /dev/null +++ b/pyste/src/exporterutils.py @@ -0,0 +1,26 @@ +''' +Various helpers for interface files. +''' + +from settings import * + +#============================================================================== +# FunctionWrapper +#============================================================================== +class FunctionWrapper(object): + '''Holds information about a wrapper for a function or a method. It is in 2 + parts: the name of the Wrapper, and its code. The code is placed in the + declaration section of the module, while the name is used to def' the + function or method (with the pyste namespace prepend to it). If code is None, + the name is left unchanged. + ''' + + def __init__(self, name, code=None): + self.name = name + self.code = code + + def FullName(self): + if self.code: + return namespaces.pyste + self.name + else: + return self.name diff --git a/pyste/src/infos.py b/pyste/src/infos.py new file mode 100644 index 00000000..3d23537e --- /dev/null +++ b/pyste/src/infos.py @@ -0,0 +1,185 @@ +import os.path +import copy +import exporters +from ClassExporter import ClassExporter +from FunctionExporter import FunctionExporter +from IncludeExporter import IncludeExporter +from EnumExporter import EnumExporter +from HeaderExporter import HeaderExporter +from exporterutils import FunctionWrapper + + +#============================================================================== +# DeclarationInfo +#============================================================================== +class DeclarationInfo(object): + + def __init__(self, otherInfo=None): + self.__infos = {} + self.__attributes = {} + if otherInfo is not None: + self.__infos = otherInfo.__infos.copy() + self.__attributes = otherInfo.__attributes.copy() + + + def __getitem__(self, name): + 'Used to access sub-infos' + default = DeclarationInfo() + default._Attribute('name', name) + return self.__infos.setdefault(name, default) + + + def __getattr__(self, name): + return self[name] + + + def _Attribute(self, name, value=None): + if value is None: + # get value + return self.__attributes.get(name) + else: + # set value + self.__attributes[name] = value + + +#============================================================================== +# FunctionInfo +#============================================================================== +class FunctionInfo(DeclarationInfo): + + def __init__(self, name, include, tail=None, otherOption=None): + DeclarationInfo.__init__(self, otherOption) + self._Attribute('name', name) + self._Attribute('include', include) + # create a FunctionExporter + exporter = FunctionExporter(InfoWrapper(self), tail) + exporters.exporters.append(exporter) + + +#============================================================================== +# ClassInfo +#============================================================================== +class ClassInfo(DeclarationInfo): + + def __init__(self, name, include, tail=None, otherOption=None): + DeclarationInfo.__init__(self, otherOption) + self._Attribute('name', name) + self._Attribute('include', include) + # create a ClassExporter + exporter = ClassExporter(InfoWrapper(self), tail) + exporters.exporters.append(exporter) + + +#============================================================================== +# IncludeInfo +#============================================================================== +class IncludeInfo(DeclarationInfo): + + def __init__(self, include): + DeclarationInfo.__init__(self) + self._Attribute('include', include) + exporter = IncludeExporter(InfoWrapper(self)) + exporters.exporters.append(exporter) + + +#============================================================================== +# templates +#============================================================================== +def GenerateName(name, type_list): + name = name.replace('::', '_') + names = [name] + type_list + return '_'.join(names) + + +class ClassTemplateInfo(DeclarationInfo): + + def __init__(self, name, include): + DeclarationInfo.__init__(self) + self._Attribute('name', name) + self._Attribute('include', include) + + + def Instantiate(self, type_list, rename=None): + if not rename: + rename = GenerateName(self._Attribute('name'), type_list) + # generate code to instantiate the template + types = ', '.join(type_list) + tail = 'typedef %s< %s > %s;\n' % (self._Attribute('name'), types, rename) + tail += 'void __instantiate_%s()\n' % rename + tail += '{ sizeof(%s); }\n\n' % rename + # create a ClassInfo + class_ = ClassInfo(rename, self._Attribute('include'), tail, self) + return class_ + + + def __call__(self, types, rename=None): + if isinstance(types, str): + types = types.split() + return self.Instantiate(types, rename) + +#============================================================================== +# EnumInfo +#============================================================================== +class EnumInfo(DeclarationInfo): + + def __init__(self, name, include): + DeclarationInfo.__init__(self) + self._Attribute('name', name) + self._Attribute('include', include) + exporter = EnumExporter(InfoWrapper(self)) + exporters.exporters.append(exporter) + + +#============================================================================== +# HeaderInfo +#============================================================================== +class HeaderInfo(DeclarationInfo): + + def __init__(self, include): + DeclarationInfo.__init__(self) + self._Attribute('include', include) + exporter = HeaderExporter(InfoWrapper(self)) + exporters.exporters.append(exporter) + + +#============================================================================== +# InfoWrapper +#============================================================================== +class InfoWrapper: + 'Provides a nicer interface for a info' + + def __init__(self, info): + self.__dict__['_info'] = info # so __setattr__ is not called + + def __getitem__(self, name): + return InfoWrapper(self._info[name]) + + def __getattr__(self, name): + return self._info._Attribute(name) + + def __setattr__(self, name, value): + self._info._Attribute(name, value) + + +#============================================================================== +# Functions +#============================================================================== +def exclude(option): + option._Attribute('exclude', True) + +def set_policy(option, policy): + option._Attribute('policy', policy) + +def rename(option, name): + option._Attribute('rename', name) + +def set_wrapper(option, wrapper): + if isinstance(wrapper, str): + wrapper = FunctionWrapper(wrapper) + option._Attribute('wrapper', wrapper) + +def instantiate(template, types, rename=None): + if isinstance(types, str): + types = types.split() + return template.Instantiate(types, rename) + diff --git a/pyste/src/policies.py b/pyste/src/policies.py new file mode 100644 index 00000000..977e7f92 --- /dev/null +++ b/pyste/src/policies.py @@ -0,0 +1,75 @@ + + +class Policy: + 'Represents one of the call policies of boost.python.' + + def __init__(self): + raise RuntimeError, "Can't create an instance of the class Policy" + + + def Code(self): + 'Returns the string corresponding to a instancialization of the policy.' + pass + + + def _next(self): + if self.next is not None: + return ', %s >' % self.next.Code() + else: + return ' >' + + + +class return_internal_reference(Policy): + 'Ties the return value to one of the parameters.' + + def __init__(self, param=1, next=None): + ''' + param is the position of the parameter, or None for "self". + next indicates the next policy, or None. + ''' + self.param = param + self.next=next + + + def Code(self): + c = 'return_internal_reference< %i' % self.param + c += self._next() + return c + + + +class with_custodian_and_ward(Policy): + 'Ties lifetime of two arguments of a function.' + + def __init__(self, custodian, ward, next=None): + self.custodian = custodian + self.ward = ward + self.next = next + + def Code(self): + c = 'with_custodian_and_ward< %i, %i' % (self.custodian, self.ward) + c += self._next() + return c + + + +class return_value_policy(Policy): + 'Policy to convert return values.' + + def __init__(self, which, next=None): + self.which = which + self.next = next + + + def Code(self): + c = 'return_value_policy< %s' % self.which + c += self._next() + return c + + +# values for return_value_policy +reference_existing_object = 'reference_existing_object' +copy_const_reference = 'copy_const_reference' +copy_non_const_reference = 'copy_non_const_reference' +manage_new_object = 'manage_new_object' diff --git a/pyste/src/pyste-profile.py b/pyste/src/pyste-profile.py new file mode 100644 index 00000000..d7afff45 --- /dev/null +++ b/pyste/src/pyste-profile.py @@ -0,0 +1,17 @@ +import profile +import pstats +import pyste + +import psyco +import elementtree.XMLTreeBuilder as XMLTreeBuilder +import GCCXMLParser + + +if __name__ == '__main__': + #psyco.bind(XMLTreeBuilder.fixtext) + #psyco.bind(XMLTreeBuilder.fixname) + #psyco.bind(XMLTreeBuilder.TreeBuilder) + #psyco.bind(GCCXMLParser.GCCXMLParser) + profile.run('pyste.Main()', 'profile') + p = pstats.Stats('profile') + p.strip_dirs().sort_stats(-1).print_stats() diff --git a/pyste/src/pyste.py b/pyste/src/pyste.py new file mode 100644 index 00000000..090c7fd9 --- /dev/null +++ b/pyste/src/pyste.py @@ -0,0 +1,154 @@ +''' +Usage: + pyste [options] --module= interface-files + +where options are: + -I add an include path + -D define symbol + --no-using do not declare "using namespace boost"; + use explicit declarations instead + --pyste-ns= set the namespace where new types will be declared; + default is "pyste" +''' + +import sys +import os +import getopt +import exporters +import CodeUnit +import infos +import exporterutils +import settings +from policies import * +from CppParser import CppParser, CppParserError +from Exporter import Exporter +from FunctionExporter import FunctionExporter +from ClassExporter import ClassExporter +from IncludeExporter import IncludeExporter +from HeaderExporter import HeaderExporter + + +def GetDefaultIncludes(): + if 'INCLUDE' in os.environ: + include = os.environ['INCLUDE'] + return include.split(os.pathsep) + else: + return [] + + +def ParseArguments(): + + def Usage(): + print __doc__ + sys.exit(1) + + options, files = getopt.getopt(sys.argv[1:], 'I:D:', ['module=', 'out=', 'no-using', 'pyste-ns=', 'debug']) + includes = GetDefaultIncludes() + defines = [] + module = None + out = None + for opt, value in options: + if opt == '-I': + includes.append(value) + elif opt == '-D': + defines.append(value) + elif opt == '--module': + module = value + elif opt == '--out': + out = value + elif opt == '--no-using': + settings.namespaces.python = 'boost::python::' + CodeUnit.CodeUnit.USING_BOOST_NS = False + elif opt == '--pyste-ns': + settings.namespaces.pyste = value + '::' + elif opt == '--debug': + settings.DEBUG = True + else: + print 'Unknown option:', opt + Usage() + + if not files or not module: + Usage() + if not out: + out = module + '.cpp' + return includes, defines, module, out, files + + +def CreateContext(): + 'create the context where a interface file can be executed' + context = {} + # infos + context['Function'] = infos.FunctionInfo + context['Class'] = infos.ClassInfo + context['Include'] = infos.IncludeInfo + context['Template'] = infos.ClassTemplateInfo + context['Enum'] = infos.EnumInfo + context['AllFromHeader'] = infos.HeaderInfo + # functions + context['rename'] = infos.rename + context['set_policy'] = infos.set_policy + context['exclude'] = infos.exclude + context['set_wrapper'] = infos.set_wrapper + # policies + context['return_internal_reference'] = return_internal_reference + context['with_custodian_and_ward'] = with_custodian_and_ward + context['return_value_policy'] = return_value_policy + context['reference_existing_object'] = reference_existing_object + context['copy_const_reference'] = copy_const_reference + context['copy_non_const_reference'] = copy_non_const_reference + context['manage_new_object'] = manage_new_object + # utils + context['Wrapper'] = exporterutils.FunctionWrapper + return context + + +def Main(): + includes, defines, module, out, interfaces = ParseArguments() + # execute the interface files + for interface in interfaces: + context = CreateContext() + execfile(interface, context) + # parse all the C++ code + parser = CppParser(includes, defines) + exports = exporters.exporters[:] + for export in exports: + try: + export.Parse(parser) + except CppParserError, e: + print '\n' + print '***', e, ': exitting' + return 2 + print + # sort the exporters by its order + exports = [(x.Order(), x) for x in exporters.exporters] + exports.sort() + exports = [x for _, x in exports] + # now generate the wrapper code + codeunit = CodeUnit.CodeUnit(module) + exported_names = [] + for export in exports: + export.GenerateCode(codeunit, exported_names) + exported_names.append(export.Name()) + codeunit.Save(out) + print 'Module %s generated' % module + return 0 + + +def UsePsyco(): + 'Tries to use psyco if it is installed' + try: + import psyco + import elementtree.XMLTreeBuilder as XMLTreeBuilder + import GCCXMLParser + + psyco.bind(XMLTreeBuilder.fixtext) + psyco.bind(XMLTreeBuilder.fixname) + psyco.bind(XMLTreeBuilder.TreeBuilder) + psyco.bind(GCCXMLParser.GCCXMLParser) + except ImportError: pass + + +if __name__ == '__main__': + UsePsyco() + status = Main() + sys.exit(status) diff --git a/pyste/src/settings.py b/pyste/src/settings.py new file mode 100644 index 00000000..e5adfc25 --- /dev/null +++ b/pyste/src/settings.py @@ -0,0 +1,12 @@ + +#============================================================================== +# Global information +#============================================================================== + +DEBUG = False + +class namespaces: + boost = 'boost::' + pyste = '' + python = '' # default is to not use boost::python namespace explicitly, so + # use the "using namespace" statement instead diff --git a/pyste/tests/GCCXMLParserUT.py b/pyste/tests/GCCXMLParserUT.py new file mode 100644 index 00000000..c0a17b33 --- /dev/null +++ b/pyste/tests/GCCXMLParserUT.py @@ -0,0 +1,338 @@ +import sys +sys.path.append('..') +import unittest +import tempfile +import os.path +import GCCXMLParser +from declarations import * + + +class Tester(unittest.TestCase): + + def TestConstructor(self, class_, method, visib): + self.assert_(isinstance(method, Constructor)) + self.assertEqual(method.FullName(), class_.FullName() + '::' + method.name) + self.assertEqual(method.result, None) + self.assertEqual(method.visibility, visib) + self.assert_(not method.virtual) + self.assert_(not method.abstract) + self.assert_(not method.static) + + def TestDefaultConstructor(self, class_, method, visib): + self.TestConstructor(class_, method, visib) + self.assert_(method.IsDefault()) + + def TestCopyConstructor(self, class_, method, visib): + self.TestConstructor(class_, method, visib) + self.assertEqual(len(method.parameters), 1) + param = method.parameters[0] + self.TestType( + param, + ReferenceType, + class_.FullName(), + 'const %s &' % class_.FullName(), + True) + self.assert_(method.IsCopy()) + + + def TestType(self, type_, classtype_, name, fullname, const): + self.assert_(isinstance(type_, classtype_)) + self.assertEqual(type_.name, name) + self.assertEqual(type_.namespace, None) + self.assertEqual(type_.FullName(), fullname) + self.assertEqual(type_.const, const) + + +class ClassBaseTest(Tester): + + def setUp(self): + self.base = GetDecl('Base') + + def testClass(self): + 'test the properties of the class Base' + self.assert_(isinstance(self.base, Class)) + self.assert_(self.base.abstract) + self.assertEqual(self.base.RawName(), 'Base') + + + def testFoo(self): + 'test function foo in class Base' + foo = GetMember(self.base, 'foo') + self.assert_(isinstance(foo, Method)) + self.assertEqual(foo.visibility, Scope.public) + self.assert_(foo.virtual) + self.assert_(foo.abstract) + self.failIf(foo.static) + self.assertEqual(foo.class_, 'test::Base') + self.failIf(foo.const) + self.assertEqual(foo.FullName(), 'test::Base::foo') + self.assertEqual(foo.result.name, 'void') + self.assertEqual(len(foo.parameters), 1) + param = foo.parameters[0] + self.TestType(param, FundamentalType, 'int', 'int', False) + self.assertEqual(foo.namespace, None) + self.assertEqual( + foo.PointerDeclaration(), '(void (test::Base::*)(int) )test::Base::foo') + + def testX(self): + 'test the member x in class Base' + x = GetMember(self.base, 'x') + self.assertEqual(x.class_, 'test::Base') + self.assertEqual(x.FullName(), 'test::Base::x') + self.assertEqual(x.namespace, None) + self.assertEqual(x.visibility, Scope.private) + self.TestType(x.type, FundamentalType, 'int', 'int', False) + self.assertEqual(x.static, False) + + def testConstructors(self): + 'test constructors in class Base' + constructors = GetMembers(self.base, 'Base') + for cons in constructors: + if len(cons.parameters) == 0: + self.TestDefaultConstructor(self.base, cons, Scope.public) + elif len(cons.parameters) == 1: # copy constructor + self.TestCopyConstructor(self.base, cons, Scope.public) + elif len(cons.parameters) == 2: # other constructor + intp, floatp = cons.parameters + self.TestType(intp, FundamentalType, 'int', 'int', False) + self.TestType(floatp, FundamentalType, 'float', 'float', False) + + def testSimple(self): + 'test function simple in class Base' + simple = GetMember(self.base, 'simple') + self.assert_(isinstance(simple, Method)) + self.assertEqual(simple.visibility, Scope.protected) + self.assertEqual(simple.FullName(), 'test::Base::simple') + self.assertEqual(len(simple.parameters), 1) + param = simple.parameters[0] + self.TestType(param, ReferenceType, 'std::string', 'const std::string &', True) + self.TestType(simple.result, FundamentalType, 'bool', 'bool', False) + self.assertEqual( + simple.PointerDeclaration(), + '(bool (test::Base::*)(const std::string &) )test::Base::simple') + + + def testZ(self): + z = GetMember(self.base, 'z') + self.assert_(isinstance(z, Variable)) + self.assertEqual(z.visibility, Scope.public) + self.assertEqual(z.FullName(), 'test::Base::z') + self.assertEqual(z.type.name, 'int') + self.assertEqual(z.type.const, False) + self.assert_(z.static) + + +class ClassTemplateTest(Tester): + + def setUp(self): + self.template = GetDecl('Template') + + def testClass(self): + 'test the properties of the Template class' + self.assert_(isinstance(self.template, Class)) + self.assert_(not self.template.abstract) + self.assertEqual(self.template.FullName(), 'Template') + self.assertEqual(self.template.namespace, '') + self.assertEqual(self.template.name, 'Template') + self.assertEqual(self.template.RawName(), 'Template') + + def testConstructors(self): + 'test the automatic constructors of the class Template' + constructors = GetMembers(self.template, 'Template') + for cons in constructors: + if len(cons.parameters) == 0: + self.TestDefaultConstructor(self.template, cons, Scope.public) + elif len(cons.parameters) == 1: + self.TestCopyConstructor(self.template, cons, Scope.public) + + + def testValue(self): + 'test the class variable value' + value = GetMember(self.template, 'value') + self.assert_(isinstance(value, ClassVariable)) + self.assert_(value.name, 'value') + self.TestType(value.type, FundamentalType, 'int', 'int', False) + self.assert_(not value.static) + self.assertEqual(value.visibility, Scope.public) + self.assertEqual(value.class_, 'Template') + self.assertEqual(value.FullName(), 'Template::value') + + def testBase(self): + 'test the superclasses of Template' + bases = self.template.bases + self.assertEqual(len(bases), 1) + base = bases[0] + self.assert_(isinstance(base, Base)) + self.assertEqual(base.name, 'test::Base') + self.assertEqual(base.visibility, Scope.protected) + + + +class FreeFuncTest(Tester): + + def setUp(self): + self.func = GetDecl('FreeFunc') + + def testFunc(self): + 'test attributes of FreeFunc' + self.assert_(isinstance(self.func, Function)) + self.assertEqual(self.func.name, 'FreeFunc') + self.assertEqual(self.func.FullName(), 'test::FreeFunc') + self.assertEqual(self.func.namespace, 'test') + self.assertEqual( + self.func.PointerDeclaration(), + '(const test::Base & (*)(const std::string &, int))test::FreeFunc') + + + def testResult(self): + 'test the return value of FreeFunc' + res = self.func.result + self.TestType(res, ReferenceType, 'test::Base', 'const test::Base &', True) + + def testParameters(self): + 'test the parameters of FreeFunc' + self.assertEqual(len(self.func.parameters), 2) + strp, intp = self.func.parameters + self.TestType(strp, ReferenceType, 'std::string', 'const std::string &', True) + self.assertEqual(strp.default, None) + self.TestType(intp, FundamentalType, 'int', 'int', False) + self.assertEqual(intp.default, '10') + + + +class testFunctionPointers(Tester): + + def testMethodPointer(self): + 'test declaration of a pointer-to-method' + meth = GetDecl('MethodTester') + param = meth.parameters[0] + fullname = 'void (test::Base::*)(int)' + self.TestType(param, PointerType, fullname, fullname, False) + + def testFunctionPointer(self): + 'test declaration of a pointer-to-function' + func = GetDecl('FunctionTester') + param = func.parameters[0] + fullname = 'void (*)(int)' + self.TestType(param, PointerType, fullname, fullname, False) + + + +# ============================================================================= +# Support routines +# ============================================================================= + +cppcode = ''' +namespace std { + class string; +} +namespace test { +class Base +{ +public: + Base(); + Base(const Base&); + Base(int, float); + + virtual void foo(int = 0.0) = 0; + static int z; +protected: + bool simple(const std::string&); +private: + int x; +}; + +void MethodTester( void (Base::*)(int) ); +void FunctionTester( void (*)(int) ); + + +const Base & FreeFunc(const std::string&, int=10); + +} + +template +struct Template: protected test::Base +{ + T value; + virtual void foo(int); +}; + +Template __aTemplateInt; +''' + +def GetXMLFile(): + '''Generates an gccxml file using the code from the global cppcode. + Returns the xml's filename.''' + # write the code to a header file + tmpfile = tempfile.mktemp() + '.h' + f = file(tmpfile, 'w') + f.write(cppcode) + f.close() + # run gccxml + outfile = tmpfile + '.xml' + if os.system('gccxml "%s" "-fxml=%s"' % (tmpfile, outfile)) != 0: + raise RuntimeError, 'Error executing GCCXML.' + # read the output file into the xmlcode + f = file(outfile) + xmlcode = f.read() + #print xmlcode + f.close() + # remove the header + os.remove(tmpfile) + return outfile + + + +def GetDeclarations(): + 'Uses the GCCXMLParser module to get the declarations.' + xmlfile = GetXMLFile() + declarations = GCCXMLParser.ParseDeclarations(xmlfile) + os.remove(xmlfile) + return declarations + +# the declarations to be analysed +declarations = GetDeclarations() + + +def GetDecl(name): + 'returns one of the top declarations given its name' + for decl in declarations: + if decl.name == name: + return decl + else: + raise RuntimeError, 'Declaration not found: %s' % name + + +def GetMember(class_, name): + 'gets the member of the given class by its name' + + res = None + multipleFound = False + for member in class_: + if member.name == name: + if res is not None: + multipleFound = True + break + res = member + if res is None or multipleFound: + raise RuntimeError, \ + 'No member or more than one member found in class %s: %s' \ + % (class_.name, name) + return res + + +def GetMembers(class_, name): + 'gets the members of the given class by its name' + res = [] + for member in class_: + if member.name == name: + res.append(member) + if len(res) in (0, 1): + raise RuntimeError, \ + 'GetMembers: 0 or 1 members found in class %s: %s' \ + % (class_.name, name) + return res + + +if __name__ == '__main__': + unittest.main() diff --git a/pyste/tests/infosUT.py b/pyste/tests/infosUT.py new file mode 100644 index 00000000..71d5e368 --- /dev/null +++ b/pyste/tests/infosUT.py @@ -0,0 +1,50 @@ +import sys +sys.path.append('../src') +from infos import * +from policies import * +from exporterutils import * +import unittest + + +class InfosTest(unittest.TestCase): + + def testFunctionInfo(self): + info = FunctionInfo('test::foo', 'foo.h') + rename(info, 'hello') + set_policy(info, return_internal_reference()) + set_wrapper(info, FunctionWrapper('foo_wrapper')) + + info = InfoWrapper(info) + + self.assertEqual(info.rename, 'hello') + self.assertEqual(info.policy.Code(), 'return_internal_reference< 1 >') + self.assertEqual(info.wrapper.name, 'foo_wrapper') + + + def testClassInfo(self): + info = ClassInfo('test::IFoo', 'foo.h') + rename(info.name, 'Name') + rename(info.exclude, 'Exclude') + rename(info, 'Foo') + rename(info.Bar, 'bar') + set_policy(info.Baz, return_internal_reference()) + rename(info.operator['>>'], 'from_string') + exclude(info.Bar) + set_wrapper(info.Baz, FunctionWrapper('baz_wrapper')) + + info = InfoWrapper(info) + + self.assertEqual(info.rename, 'Foo') + self.assertEqual(info['Bar'].rename, 'bar') + self.assertEqual(info['name'].rename, 'Name') + self.assertEqual(info['exclude'].rename, 'Exclude') + self.assertEqual(info['Bar'].exclude, True) + self.assertEqual(info['Baz'].policy.Code(), 'return_internal_reference< 1 >') + self.assertEqual(info['Baz'].wrapper.name, 'baz_wrapper') + self.assertEqual(info['operator']['>>'].rename, 'from_string') + + + + +if __name__ == '__main__': + unittest.main() diff --git a/pyste/tests/policiesUT.py b/pyste/tests/policiesUT.py new file mode 100644 index 00000000..bde08543 --- /dev/null +++ b/pyste/tests/policiesUT.py @@ -0,0 +1,59 @@ +import sys +sys.path.append('..') +import unittest +from policies import * + +class PoliciesTest(unittest.TestCase): + + def testReturnInternal(self): + 'tests the code from a simple internal_reference' + + x = return_internal_reference(1) + self.assertEqual(x.Code(), 'return_internal_reference< 1 >') + x = return_internal_reference(3) + self.assertEqual(x.Code(), 'return_internal_reference< 3 >') + + + def testCustodian(self): + 'tests the code from a simple custodian_and_ward' + + x = with_custodian_and_ward(1,2) + self.assertEqual(x.Code(), 'with_custodian_and_ward< 1, 2 >') + x = with_custodian_and_ward(3,4) + self.assertEqual(x.Code(), 'with_custodian_and_ward< 3, 4 >') + + + def testReturnPolicies(self): + 'tests all the return_value_policies' + + ret = 'return_value_policy< %s >' + x = return_value_policy(reference_existing_object) + self.assertEqual(x.Code(), ret % 'reference_existing_object') + x = return_value_policy(copy_const_reference) + self.assertEqual(x.Code(), ret % 'copy_const_reference') + x = return_value_policy(copy_non_const_reference) + self.assertEqual(x.Code(), ret % 'copy_non_const_reference') + x = return_value_policy(manage_new_object) + self.assertEqual(x.Code(), ret % 'manage_new_object') + + + def testReturnWithCustodiam(self): + 'test the mix of return_internal with custodian' + + x = return_internal_reference(1, with_custodian_and_ward(3,2)) + self.assertEqual( + x.Code(), + 'return_internal_reference< 1, with_custodian_and_ward< 3, 2 > >') + + + def testReturnPoliciesWithInternal(self): + 'test the mix of return_internal with return_policy' + + x = return_internal_reference(1, return_value_policy(manage_new_object)) + self.assertEqual( + x.Code(), + 'return_internal_reference< 1, return_value_policy< manage_new_object > >') + + +if __name__ == '__main__': + unittest.main() diff --git a/pyste/tests/runtests.py b/pyste/tests/runtests.py new file mode 100644 index 00000000..f670e3d4 --- /dev/null +++ b/pyste/tests/runtests.py @@ -0,0 +1,14 @@ +import sys +sys.path.append('../src') +import unittest +import os.path +from glob import glob + +if __name__ == '__main__': + loader = unittest.defaultTestLoader + tests = [] + for name in glob('*UT.py'): + module = __import__(os.path.splitext(name)[0]) + tests.append(loader.loadTestsFromModule(module)) + runner = unittest.TextTestRunner() + runner.run(unittest.TestSuite(tests))