Commit b8a099a0 authored by Stefan Behnel's avatar Stefan Behnel

partial implementation of the "exec" statement

- only supports code in strings, not files (should be easy to add)
- only the "exec XYZ in GLOBALS [,LOCALS]" form is supported, not the execution in the current namespace (would require a mapping representation of it)
- also includes an incomplete (3-args only) and untested implementation for the exec() function in Py3
parent 4b1886f9
......@@ -6,6 +6,7 @@ from Symtab import BuiltinScope, StructOrUnionScope
from Cython.Utils import UtilityCode
from TypeSlots import Signature
import PyrexTypes
import Naming
builtin_function_table = [
# name, args, return, C API func, py equiv = "*"
......@@ -16,6 +17,7 @@ builtin_function_table = [
('delattr', "OO", "r", "PyObject_DelAttr"),
('dir', "O", "O", "PyObject_Dir"),
('divmod', "OO", "O", "PyNumber_Divmod"),
('exec', "OOO", "O", "__Pyx_PyRun"),
#('eval', "", "", ""),
#('execfile', "", "", ""),
#('filter', "", "", ""),
......@@ -154,6 +156,48 @@ bad:
}
""")
pyexec_utility_code = UtilityCode(
proto = """
static PyObject* __Pyx_PyRun(PyObject*, PyObject*, PyObject*);
""",
impl = """
static PyObject* __Pyx_PyRun(PyObject* o, PyObject* globals, PyObject* locals) {
PyObject* result;
PyObject* s = 0;
if (!locals && !globals) {
globals = PyModule_GetDict(%s);""" % Naming.module_cname + """
if (!globals)
goto bad;
locals = globals;
} else if (!locals) {
locals = globals;
} else if (!globals) {
globals = locals;
}
if (PyUnicode_Check(o)) {
s = PyUnicode_AsUTF8String(o);
if (!s) goto bad;
o = s;
} else if (!PyString_Check(o)) {
/* FIXME: support file objects and code objects */
PyErr_SetString(PyExc_TypeError,
"exec currently requires a string as code input.");
goto bad;
}
result = PyRun_String(
PyString_AS_STRING(o), Py_file_input, globals, locals);
Py_XDECREF(s);
return result;
bad:
Py_XDECREF(s);
return 0;
}
""")
intern_utility_code = UtilityCode(
proto = """
#if PY_MAJOR_VERSION >= 3
......@@ -273,6 +317,7 @@ Py_XDECREF(__Pyx_PyFrozenSet_Type); __Pyx_PyFrozenSet_Type = NULL;
""")
builtin_utility_code = {
'exec' : pyexec_utility_code,
'getattr3' : getattr3_utility_code,
'intern' : intern_utility_code,
'set' : py23_set_utility_code,
......
......@@ -5,6 +5,7 @@
import string, sys, os, time, copy
import Code
import Builtin
from Errors import error, warning, InternalError
import Naming
import PyrexTypes
......@@ -3056,6 +3057,45 @@ class PrintStatNode(StatNode):
self.arg_tuple.annotate(code)
class ExecStatNode(StatNode):
# exec statement
#
# args [ExprNode]
child_attrs = ["args"]
def analyse_expressions(self, env):
for i, arg in enumerate(self.args):
arg.analyse_expressions(env)
arg = arg.coerce_to_pyobject(env)
arg.release_temp(env)
self.args[i] = arg
self.temp_result = env.allocate_temp_pyobject()
env.release_temp(self.temp_result)
env.use_utility_code(Builtin.pyexec_utility_code)
self.gil_check(env)
gil_message = "Python exec statement"
def generate_execution_code(self, code):
args = []
for arg in self.args:
arg.generate_evaluation_code(code)
args.append( arg.py_result() )
args = tuple(args + ['0', '0'][:3-len(args)])
code.putln("%s = __Pyx_PyRun(%s, %s, %s);" % (
(self.temp_result,) + args))
for arg in self.args:
arg.generate_disposal_code(code)
code.putln(
code.error_goto_if_null(self.temp_result, self.pos))
code.put_decref_clear(self.temp_result, py_object_type)
def annotate(self, code):
for arg in self.args:
arg.annotate(code)
class DelStatNode(StatNode):
# del statement
#
......
......@@ -902,6 +902,21 @@ def p_print_statement(s):
return Nodes.PrintStatNode(pos,
arg_tuple = arg_tuple, append_newline = not ends_with_comma)
def p_exec_statement(s):
# s.sy == 'exec'
pos = s.position()
s.next()
args = [ p_bit_expr(s) ]
if s.sy == 'in':
s.next()
args.append(p_simple_expr(s))
if s.sy == ',':
s.next()
args.append(p_simple_expr(s))
else:
error(pos, "'exec' currently requires a target mapping (globals/locals)")
return Nodes.ExecStatNode(pos, args = args)
def p_del_statement(s):
# s.sy == 'del'
pos = s.position()
......@@ -1316,6 +1331,8 @@ def p_simple_statement(s, first_statement = 0):
node = p_global_statement(s)
elif s.sy == 'print':
node = p_print_statement(s)
elif s.sy == 'exec':
node = p_exec_statement(s)
elif s.sy == 'del':
node = p_del_statement(s)
elif s.sy == 'break':
......
__doc__ = """# no unicode string, not tested in Python3!
#>>> a
#>>> test_module_scope()
#>>> a
>>> test_dict_scope1()
2
>>> d = {}
>>> test_dict_scope2(d)
>>> print d['b']
2
>>> d1 = {}
>>> test_dict_scope3(d1, d1)
>>> print d1['b']
2
>>> d1, d2 = {}, {}
>>> test_dict_scope3(d1, d2)
>>> print d1.get('b'), d2.get('b')
None 2
>>> d1, d2 = {}, {}
>>> test_dict_scope3(d1, d2)
>>> print d1.get('b'), d2.get('b')
None 2
>>> d1, d2 = dict(a=11), dict(c=5)
>>> test_dict_scope_ref(d1, d2)
>>> print d1.get('b'), d2.get('b')
None 16
>>> d = dict(a=11, c=5)
>>> test_dict_scope_ref(d, d)
>>> print d['b']
16
"""
#def test_module_scope():
# global a
# exec "a=1+1"
# return __dict__['a']
def test_dict_scope1():
cdef dict d = {}
exec "b=1+1" in d
return d['b']
def test_dict_scope2(d):
exec "b=1+1" in d
def test_dict_scope3(d1, d2):
exec "b=1+1" in d1, d2
def test_dict_scope_ref(d1, d2):
exec "b=a+c" in d1, d2
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment