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 ...@@ -6,6 +6,7 @@ from Symtab import BuiltinScope, StructOrUnionScope
from Cython.Utils import UtilityCode from Cython.Utils import UtilityCode
from TypeSlots import Signature from TypeSlots import Signature
import PyrexTypes import PyrexTypes
import Naming
builtin_function_table = [ builtin_function_table = [
# name, args, return, C API func, py equiv = "*" # name, args, return, C API func, py equiv = "*"
...@@ -16,6 +17,7 @@ builtin_function_table = [ ...@@ -16,6 +17,7 @@ builtin_function_table = [
('delattr', "OO", "r", "PyObject_DelAttr"), ('delattr', "OO", "r", "PyObject_DelAttr"),
('dir', "O", "O", "PyObject_Dir"), ('dir', "O", "O", "PyObject_Dir"),
('divmod', "OO", "O", "PyNumber_Divmod"), ('divmod', "OO", "O", "PyNumber_Divmod"),
('exec', "OOO", "O", "__Pyx_PyRun"),
#('eval', "", "", ""), #('eval', "", "", ""),
#('execfile', "", "", ""), #('execfile', "", "", ""),
#('filter', "", "", ""), #('filter', "", "", ""),
...@@ -154,6 +156,48 @@ bad: ...@@ -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( intern_utility_code = UtilityCode(
proto = """ proto = """
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
...@@ -273,6 +317,7 @@ Py_XDECREF(__Pyx_PyFrozenSet_Type); __Pyx_PyFrozenSet_Type = NULL; ...@@ -273,6 +317,7 @@ Py_XDECREF(__Pyx_PyFrozenSet_Type); __Pyx_PyFrozenSet_Type = NULL;
""") """)
builtin_utility_code = { builtin_utility_code = {
'exec' : pyexec_utility_code,
'getattr3' : getattr3_utility_code, 'getattr3' : getattr3_utility_code,
'intern' : intern_utility_code, 'intern' : intern_utility_code,
'set' : py23_set_utility_code, 'set' : py23_set_utility_code,
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import string, sys, os, time, copy import string, sys, os, time, copy
import Code import Code
import Builtin
from Errors import error, warning, InternalError from Errors import error, warning, InternalError
import Naming import Naming
import PyrexTypes import PyrexTypes
...@@ -3056,6 +3057,45 @@ class PrintStatNode(StatNode): ...@@ -3056,6 +3057,45 @@ class PrintStatNode(StatNode):
self.arg_tuple.annotate(code) 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): class DelStatNode(StatNode):
# del statement # del statement
# #
......
...@@ -902,6 +902,21 @@ def p_print_statement(s): ...@@ -902,6 +902,21 @@ def p_print_statement(s):
return Nodes.PrintStatNode(pos, return Nodes.PrintStatNode(pos,
arg_tuple = arg_tuple, append_newline = not ends_with_comma) 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): def p_del_statement(s):
# s.sy == 'del' # s.sy == 'del'
pos = s.position() pos = s.position()
...@@ -1316,6 +1331,8 @@ def p_simple_statement(s, first_statement = 0): ...@@ -1316,6 +1331,8 @@ def p_simple_statement(s, first_statement = 0):
node = p_global_statement(s) node = p_global_statement(s)
elif s.sy == 'print': elif s.sy == 'print':
node = p_print_statement(s) node = p_print_statement(s)
elif s.sy == 'exec':
node = p_exec_statement(s)
elif s.sy == 'del': elif s.sy == 'del':
node = p_del_statement(s) node = p_del_statement(s)
elif s.sy == 'break': 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