summaryrefslogtreecommitdiffstats
path: root/sandbox/sebastien/cpp/apr-2/modules/python/eval.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'sandbox/sebastien/cpp/apr-2/modules/python/eval.hpp')
-rw-r--r--sandbox/sebastien/cpp/apr-2/modules/python/eval.hpp351
1 files changed, 351 insertions, 0 deletions
diff --git a/sandbox/sebastien/cpp/apr-2/modules/python/eval.hpp b/sandbox/sebastien/cpp/apr-2/modules/python/eval.hpp
new file mode 100644
index 0000000000..2dd4b8ba33
--- /dev/null
+++ b/sandbox/sebastien/cpp/apr-2/modules/python/eval.hpp
@@ -0,0 +1,351 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* $Rev$ $Date$ */
+
+#ifndef tuscany_python_eval_hpp
+#define tuscany_python_eval_hpp
+
+/**
+ * Python script evaluation logic.
+ */
+#include <python2.6/Python.h>
+
+#include "list.hpp"
+#include "value.hpp"
+
+namespace tuscany {
+namespace python {
+
+/**
+ * Represent a Python runtime.
+ */
+class PythonRuntime {
+public:
+ PythonRuntime() {
+ if (Py_IsInitialized())
+ return;
+ Py_InitializeEx(0);
+ const char* arg0 = "";
+ PySys_SetArgv(0, const_cast<char**>(&arg0));
+ }
+};
+
+/**
+ * Return the last python error.
+ */
+const string lastError() {
+ if(PyErr_Occurred()) {
+ PyObject* type;
+ PyObject* val;
+ PyObject* trace;
+ PyErr_Fetch(&type, &val, &trace);
+ if (type != NULL && val != NULL) {
+ PyObject* stype = PyObject_Str(type);
+ PyObject* sval = PyObject_Str(val);
+ string msg = string() + PyString_AsString(stype) + " : " + PyString_AsString(sval);
+ Py_DECREF(stype);
+ Py_DECREF(sval);
+ Py_DECREF(type);
+ Py_DECREF(val);
+ Py_XDECREF(trace);
+ PyErr_Print();
+ return msg;
+ }
+ PyErr_Print();
+ Py_XDECREF(type);
+ Py_XDECREF(val);
+ Py_XDECREF(trace);
+ PyErr_Print();
+ return "Unknown Python error";
+ }
+ return "";
+}
+
+/**
+ * Declare conversion functions.
+ */
+PyObject* valueToPyObject(const value& v);
+const value pyObjectToValue(PyObject *o);
+PyObject* valuesToPyTuple(const list<value>& v);
+const list<value> pyTupleToValues(PyObject* o);
+
+/**
+ * Callable python type used to represent a lambda expression.
+ */
+typedef struct {
+ PyObject_HEAD
+ lambda<value(const list<value>&)> func;
+} pyLambda;
+
+PyObject *mkPyLambda(const lambda<value(const list<value>&)>& l);
+
+void pyLambda_dealloc(PyObject* self) {
+ PyMem_DEL(self);
+}
+
+const string pyRepr(PyObject * o) {
+ return PyString_AsString(PyObject_Repr(o));
+}
+
+PyObject* pyLambda_call(PyObject* self, PyObject* args, unused PyObject* kwds) {
+ debug("python::call");
+ const pyLambda* pyl = (pyLambda*)self;
+ const value result = pyl->func(pyTupleToValues(args));
+ debug(result, "python::call::result");
+ Py_DECREF(args);
+ PyObject *pyr = valueToPyObject(result);
+ Py_INCREF(pyr);
+ return pyr;
+}
+
+struct pyProxy {
+ const value name;
+ const lambda<value(const list<value>&)> func;
+
+ pyProxy(const value& name, const lambda<value(const list<value>&)>& func) : name(name), func(func) {
+ }
+
+ const value operator()(const list<value>& args) const {
+ debug(name, "python::proxy::name");
+ const value result = func(cons<value>(name, args));
+ debug(result, "python::proxy::result");
+ return result;
+ }
+};
+
+PyObject* pyLambda_getattr(PyObject *self, PyObject *attrname) {
+ const string name = PyString_AsString(attrname);
+ if (substr(name, 0, 1) == "_")
+ return PyObject_GenericGetAttr(self, attrname);
+
+ if (name == "eval") {
+ Py_INCREF(self);
+ return self;
+ }
+
+ const pyLambda* pyl = (pyLambda*)self;
+ debug(name, "python::getattr::name");
+ PyObject* pyr = mkPyLambda(pyProxy(name, pyl->func));
+ Py_INCREF(pyr);
+ return pyr;
+}
+
+PyTypeObject pyLambda_type = {
+ PyObject_HEAD_INIT(0)
+ 0,
+ "lambda",
+ sizeof(pyLambda),
+ 0,
+ (destructor)pyLambda_dealloc,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ (ternaryfunc)pyLambda_call,
+ 0,
+ (binaryfunc)pyLambda_getattr,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0
+};
+
+/**
+ * Create a new python object representing a lambda expression.
+ */
+PyObject *mkPyLambda(const lambda<value(const list<value>&)>& l) {
+ pyLambda* pyl = NULL;
+ pyl = PyObject_NEW(pyLambda, &pyLambda_type);
+ if (pyl != NULL)
+ pyl->func = l;
+ return (PyObject *)pyl;
+}
+
+/**
+ * Convert a list of values to a python list.
+ */
+PyObject* valuesToPyListHelper(PyObject* l, const list<value>& v) {
+ if (isNil(v))
+ return l;
+ PyList_Append(l, valueToPyObject(car(v)));
+ return valuesToPyListHelper(l, cdr(v));
+}
+
+PyObject* valuesToPyTuple(const list<value>& v) {
+ return PyList_AsTuple(valuesToPyListHelper(PyList_New(0), v));
+}
+
+/**
+ * Convert a value to a python object.
+ */
+PyObject* valueToPyObject(const value& v) {
+ switch (type(v)) {
+ case value::List:
+ return valuesToPyTuple(v);
+ case value::Lambda:
+ return mkPyLambda(v);
+ case value::Symbol:
+ return PyString_FromString(c_str(string("'") + v));
+ case value::String:
+ return PyString_FromString(c_str(v));
+ case value::Number:
+ return PyFloat_FromDouble((double)v);
+ case value::Bool:
+ return (bool)v? Py_True : Py_False;
+ default:
+ return Py_None;
+ }
+}
+
+/**
+ * Convert a python tuple to a list of values.
+ */
+
+const list<value> pyTupleToValuesHelper(PyObject* o, const size_t i, const size_t size) {
+ if (i == size)
+ return list<value>();
+ return cons(pyObjectToValue(PyTuple_GetItem(o, i)), pyTupleToValuesHelper(o, i + 1, size));
+}
+
+const list<value> pyTupleToValues(PyObject* o) {
+ return pyTupleToValuesHelper(o, 0, PyTuple_Size(o));
+}
+
+/**
+ * Lambda function used to represent a python callable object.
+ */
+struct pyCallable {
+ PyObject* func;
+
+ pyCallable(PyObject* func) : func(func) {
+ Py_INCREF(func);
+ }
+
+ ~pyCallable() {
+ Py_DECREF(func);
+ }
+
+ const value operator()(const list<value>& args) const {
+ PyObject* pyargs = valuesToPyTuple(args);
+ PyObject* result = PyObject_CallObject(func, pyargs);
+ Py_DECREF(pyargs);
+ const value v = pyObjectToValue(result);
+ Py_DECREF(result);
+ return v;
+ }
+};
+
+/**
+ * Convert a python object to a value.
+ */
+const value pyObjectToValue(PyObject *o) {
+ if (PyString_Check(o)) {
+ const char* s = PyString_AsString(o);
+ if (*s == '\'')
+ return value(s + 1);
+ return value(string(s));
+ }
+ if (PyBool_Check(o))
+ return value(o == Py_True);
+ if (PyInt_Check(o))
+ return value((double)PyInt_AsLong(o));
+ if (PyLong_Check(o))
+ return value((double)PyLong_AsLong(o));
+ if (PyFloat_Check(o))
+ return value((double)PyFloat_AsDouble(o));
+ if (PyTuple_Check(o))
+ return pyTupleToValues(o);
+ if (PyCallable_Check(o))
+ return lambda<value(const list<value>&)>(pyCallable(o));
+ return value();
+}
+
+/**
+ * Convert a python script path to a module name.
+ */
+const string moduleName(const string& path) {
+ return join(".", tokenize("/", substr(path, 0, length(path) -3)));
+}
+
+/**
+ * Evaluate an expression against a script provided as a python object.
+ */
+const failable<value> evalScript(const value& expr, PyObject* script) {
+
+ // Get the requested function
+ PyObject* func = PyObject_GetAttrString(script, c_str(car<value>(expr)));
+ if (func == NULL) {
+
+ // The start, stop, and restart functions are optional
+ const value fn = car<value>(expr);
+ if (fn == "start" || fn == "stop") {
+ PyErr_Clear();
+ return value(lambda<value(const list<value>&)>());
+ }
+
+ return mkfailure<value>(string("Couldn't find function: ") + car<value>(expr) + " : " + lastError());
+ }
+ if (!PyCallable_Check(func)) {
+ Py_DECREF(func);
+ return mkfailure<value>(string("Couldn't find callable function: ") + car<value>(expr));
+ }
+
+ // Convert args to python objects
+ PyObject* args = valuesToPyTuple(cdr<value>(expr));
+
+ // Call the function
+ PyObject* result = PyObject_CallObject(func, args);
+ Py_DECREF(args);
+ Py_DECREF(func);
+ if (result == NULL)
+ return mkfailure<value>(string("Function call failed: ") + car<value>(expr) + " : " + lastError());
+
+ // Convert python result to a value
+ const value v = pyObjectToValue(result);
+ Py_DECREF(result);
+ return v;
+}
+
+/**
+ * Read a python script from an input stream.
+ */
+const failable<PyObject*> readScript(const string& name, const string& path, istream& is) {
+ const list<string> ls = streamList(is);
+ ostringstream os;
+ write(ls, os);
+ PyObject* code = Py_CompileStringFlags(c_str(str(os)), c_str(path), Py_file_input, NULL);
+ if (code == NULL)
+ return mkfailure<PyObject*>(string("Couldn't compile script: ") + path + " : " + lastError());
+ PyObject* mod = PyImport_ExecCodeModuleEx(const_cast<char*>(c_str(name)), code, const_cast<char*>(c_str(path)));
+ if (mod == NULL)
+ return mkfailure<PyObject*>(string("Couldn't import module: ") + path + " : " + lastError());
+ return mod;
+}
+
+/**
+ * Evaluate an expression against a script provided as an input stream.
+ */
+const failable<value> evalScript(const value& expr, istream& is) {
+ failable<PyObject*> script = readScript("script", "script.py", is);
+ if (!hasContent(script))
+ return mkfailure<value>(reason(script));
+ return evalScript(expr, content(script));
+}
+
+}
+}
+#endif /* tuscany_python_eval_hpp */