Commit 6dcb9c6e authored by Dag Sverre Seljebotn's avatar Dag Sverre Seljebotn

Changed fork design slightly in StringIOTree, begun on forking CCodeWriter

parent 7e982060
...@@ -9,9 +9,43 @@ from Cython.Utils import open_new_file, open_source_file ...@@ -9,9 +9,43 @@ from Cython.Utils import open_new_file, open_source_file
from PyrexTypes import py_object_type, typecast from PyrexTypes import py_object_type, typecast
from TypeSlots import method_coexist from TypeSlots import method_coexist
from Scanning import SourceDescriptor from Scanning import SourceDescriptor
from Cython.StringIOTree import StringIOTree
class CFunctionScope:
"""
Used by CCodeWriters to keep track of state within a
C function. This means:
- labels
- temporary variables
When a code writer forks, it inherits the same scope.
"""
class CCodeWriter: class CCodeWriter:
"""
Utility class to output C code. Each codewriter is forkable (see
StringIOTree).
When forking a code writer one must care about the state that is
kept:
- formatting state (level, bol) is cloned and modifyable in
all forked copies
- labels, temps, exc_vars: One must construct a scope in which these can
exist by calling enter_cfunc_scope/exit_cfunc_scope (these are for
sanity checking and forward compatabilty). When a fork happens, only
the *last* fork will maintain this created scope, while the other
instances "looses" their ability to use temps and labels (as this
is sufficient for current usecases).
- utility code: Same story as with labels and temps; use enter_implementation
and exit_implementation.
- marker: Only kept in last fork.
- filename_table, filename_list: Decision to be made.
"""
# f file output file # f file output file
# buffer StringIOTree
# level int indentation level # level int indentation level
# bol bool beginning of line? # bol bool beginning of line?
# marker string comment to emit before next line # marker string comment to emit before next line
...@@ -31,20 +65,57 @@ class CCodeWriter: ...@@ -31,20 +65,57 @@ class CCodeWriter:
in_try_finally = 0 in_try_finally = 0
def __init__(self, f): def __init__(self, create_from=None, buffer=None):
#self.f = open_new_file(outfile_name) if buffer is None: buffer = StringIOTree()
self.f = f self.buffer = buffer
self._write = f.write self._write = self.buffer.write
self.level = 0 if create_from is None:
self.bol = 1 self.level = 0
self.marker = None self.bol = 1
self.last_marker_line = 0 self.marker = None
self.label_counter = 1 self.last_marker_line = 0
self.filename_table = {}
self.filename_list = []
self.exc_vars = None
self.input_file_contents = {}
self.in_cfunc = False
else:
# Clone formatting state
c = create_from
self.level = c.level
self.bol = c.bol
# Leave other state alone
def create_fork_spinoff(self, buffer):
result = CCodeWriter
def copyto(self, f):
self.buffer.copyto(f)
def fork(self):
other = CCodeWriter(create_from=self, buffer=self.buffer.fork())
# If we need to do something with our own state on fork, do it here
return other
def enter_cfunc_scope(self):
assert not self.in_cfunc
self.in_cfunc = True
self.error_label = None self.error_label = None
self.filename_table = {} self.label_counter = 0
self.filename_list = [] self.labels_used = {}
self.exc_vars = None self.return_label = self.new_label()
self.input_file_contents = {} self.new_error_label()
self.continue_label = None
self.break_label = None
def exit_cfunc_scope(self):
self.in_cfunc = False
del self.error_label
del self.label_counter
del self.labels_used
del self.return_label
del self.continue_label
del self.break_label
def putln(self, code = ""): def putln(self, code = ""):
if self.marker and self.bol: if self.marker and self.bol:
...@@ -124,14 +195,6 @@ class CCodeWriter: ...@@ -124,14 +195,6 @@ class CCodeWriter:
source_desc.get_escaped_description(), line, u'\n'.join(lines)) source_desc.get_escaped_description(), line, u'\n'.join(lines))
self.marker = (line, marker) self.marker = (line, marker)
def init_labels(self):
self.label_counter = 0
self.labels_used = {}
self.return_label = self.new_label()
self.new_error_label()
self.continue_label = None
self.break_label = None
def new_label(self): def new_label(self):
n = self.label_counter n = self.label_counter
self.label_counter = n + 1 self.label_counter = n + 1
......
...@@ -97,7 +97,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -97,7 +97,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
h_extension_types = h_entries(env.c_class_entries) h_extension_types = h_entries(env.c_class_entries)
if h_types or h_vars or h_funcs or h_extension_types: if h_types or h_vars or h_funcs or h_extension_types:
result.h_file = replace_suffix(result.c_file, ".h") result.h_file = replace_suffix(result.c_file, ".h")
h_code = Code.CCodeWriter(open_new_file(result.h_file)) h_code = Code.CCodeWriter()
if options.generate_pxi: if options.generate_pxi:
result.i_file = replace_suffix(result.c_file, ".pxi") result.i_file = replace_suffix(result.c_file, ".pxi")
i_code = Code.PyrexCodeWriter(result.i_file) i_code = Code.PyrexCodeWriter(result.i_file)
...@@ -129,6 +129,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -129,6 +129,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
h_code.putln("PyMODINIT_FUNC init%s(void);" % env.module_name) h_code.putln("PyMODINIT_FUNC init%s(void);" % env.module_name)
h_code.putln("") h_code.putln("")
h_code.putln("#endif") h_code.putln("#endif")
h_code.copyto(open_new_file(result.h_file))
def generate_public_declaration(self, entry, h_code, i_code): def generate_public_declaration(self, entry, h_code, i_code):
h_code.putln("%s %s;" % ( h_code.putln("%s %s;" % (
...@@ -156,7 +158,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -156,7 +158,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
has_api_extension_types = 1 has_api_extension_types = 1
if api_funcs or has_api_extension_types: if api_funcs or has_api_extension_types:
result.api_file = replace_suffix(result.c_file, "_api.h") result.api_file = replace_suffix(result.c_file, "_api.h")
h_code = Code.CCodeWriter(open_new_file(result.api_file)) h_code = Code.CCodeWriter()
name = self.api_name(env) name = self.api_name(env)
guard = Naming.api_guard_prefix + name guard = Naming.api_guard_prefix + name
h_code.put_h_guard(guard) h_code.put_h_guard(guard)
...@@ -209,6 +211,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -209,6 +211,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
h_code.putln("}") h_code.putln("}")
h_code.putln("") h_code.putln("")
h_code.putln("#endif") h_code.putln("#endif")
h_code.copy_to(open_new_file(result.api_file))
def generate_cclass_header_code(self, type, h_code): def generate_cclass_header_code(self, type, h_code):
h_code.putln("%s DL_IMPORT(PyTypeObject) %s;" % ( h_code.putln("%s DL_IMPORT(PyTypeObject) %s;" % (
...@@ -232,11 +236,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -232,11 +236,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
def generate_c_code(self, env, options, result): def generate_c_code(self, env, options, result):
modules = self.referenced_modules modules = self.referenced_modules
if Options.annotate or options.annotate: if Options.annotate or options.annotate:
code = Annotate.AnnotationCCodeWriter(StringIO()) code = Annotate.AnnotationCCodeWriter()
else: else:
code = Code.CCodeWriter(StringIO()) code = Code.CCodeWriter()
code.h = Code.CCodeWriter(StringIO()) code.h = Code.CCodeWriter()
code.init_labels()
self.generate_module_preamble(env, modules, code.h) self.generate_module_preamble(env, modules, code.h)
code.putln("") code.putln("")
...@@ -264,9 +267,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -264,9 +267,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.generate_declarations_for_modules(env, modules, code.h) self.generate_declarations_for_modules(env, modules, code.h)
f = open_new_file(result.c_file) f = open_new_file(result.c_file)
f.write(code.h.f.getvalue()) code.h.copyto(f)
f.write("\n") f.write("\n")
f.write(code.f.getvalue()) code.copyto(f)
f.close() f.close()
result.c_file_generated = 1 result.c_file_generated = 1
if Options.annotate or options.annotate: if Options.annotate or options.annotate:
...@@ -1479,6 +1482,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1479,6 +1482,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("0") code.putln("0")
code.putln("};") code.putln("};")
code.putln() code.putln()
code.enter_cfunc_scope() # as we need labels
code.putln("static int %s(PyObject *o, PyObject* py_name, char *name) {" % Naming.import_star_set) code.putln("static int %s(PyObject *o, PyObject* py_name, char *name) {" % Naming.import_star_set)
code.putln("char** type_name = %s_type_names;" % Naming.import_star) code.putln("char** type_name = %s_type_names;" % Naming.import_star)
code.putln("while (*type_name) {") code.putln("while (*type_name) {")
...@@ -1529,8 +1533,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1529,8 +1533,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("return -1;") code.putln("return -1;")
code.putln("}") code.putln("}")
code.putln(import_star_utility_code) code.putln(import_star_utility_code)
code.exit_cfunc_scope() # done with labels
def generate_module_init_func(self, imported_modules, env, code): def generate_module_init_func(self, imported_modules, env, code):
code.enter_cfunc_scope()
code.putln("") code.putln("")
header2 = "PyMODINIT_FUNC init%s(void)" % env.module_name header2 = "PyMODINIT_FUNC init%s(void)" % env.module_name
header3 = "PyMODINIT_FUNC PyInit_%s(void)" % env.module_name header3 = "PyMODINIT_FUNC PyInit_%s(void)" % env.module_name
...@@ -1584,6 +1590,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1584,6 +1590,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("/*--- Execution code ---*/") code.putln("/*--- Execution code ---*/")
code.mark_pos(None) code.mark_pos(None)
self.body.generate_execution_code(code) self.body.generate_execution_code(code)
if Options.generate_cleanup_code: if Options.generate_cleanup_code:
...@@ -1603,6 +1610,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -1603,6 +1610,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("return NULL;") code.putln("return NULL;")
code.putln("#endif") code.putln("#endif")
code.putln('}') code.putln('}')
code.exit_cfunc_scope()
def generate_module_cleanup_func(self, env, code): def generate_module_cleanup_func(self, env, code):
if not Options.generate_cleanup_code: if not Options.generate_cleanup_code:
......
...@@ -833,7 +833,7 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -833,7 +833,7 @@ class FuncDefNode(StatNode, BlockNode):
lenv = self.local_scope lenv = self.local_scope
# Generate C code for header and body of function # Generate C code for header and body of function
code.init_labels() code.enter_cfunc_scope()
code.return_from_error_cleanup_label = code.new_label() code.return_from_error_cleanup_label = code.new_label()
# ----- Top-level constants used by this function # ----- Top-level constants used by this function
...@@ -970,6 +970,7 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -970,6 +970,7 @@ class FuncDefNode(StatNode, BlockNode):
if self.py_func: if self.py_func:
self.py_func.generate_function_definitions(env, code, transforms) self.py_func.generate_function_definitions(env, code, transforms)
self.generate_optarg_wrapper_function(env, code) self.generate_optarg_wrapper_function(env, code)
code.exit_cfunc_scope()
def put_stararg_decrefs(self, code): def put_stararg_decrefs(self, code):
pass pass
......
...@@ -24,51 +24,42 @@ class StringIOTree(object): ...@@ -24,51 +24,42 @@ class StringIOTree(object):
def write(self, what): def write(self, what):
self.stream.write(what) self.stream.write(what)
def fork(self, count=2): def fork(self):
# Shuffle around the embedded StringIO objects so that # Save what we have written until now
# references to self keep writing at the end. # (would it be more efficient to check with len(self.stream.getvalue())?
# leaving it out for now)
self.prepended_children.append(StringIOTree(self.stream)) self.prepended_children.append(StringIOTree(self.stream))
# Construct the new forked object to return
other = StringIOTree()
self.prepended_children.append(other)
self.stream = StringIO() self.stream = StringIO()
tines = [StringIOTree() for i in range(1, count)] return other
self.prepended_children.extend(tines)
tines.append(self)
return tines
__doc__ = r""" __doc__ = r"""
Implements a forkable buffer. When you know you need to "get back" to a place Implements a forkable buffer. When you know you need to "get back" to a place
and write more later, simply call fork() and get. and write more later, simply call fork() at that spot and get a new
StringIOTree object that is "left behind", *behind* the object that is
The last buffer returned from fork() will always be the object itself; i.e., forked.
if code elsewhere has references to the buffer and writes to it later it will
always end up at the end just as if the fork never happened.
EXAMPLE: EXAMPLE:
>>> a = StringIOTree() >>> pyrex = StringIOTree()
>>> a.write('first\n') >>> pyrex.write('first\n')
>>> b, c = a.fork() >>> cython = pyrex.fork()
>>> c.write('third\n') >>> pyrex.write('third\n')
>>> b.write('second\n') >>> cython.write('second\n')
>>> print a.getvalue() >>> print pyrex.getvalue()
first
second
third
<BLANKLINE>
>>> a.write('fourth\n')
>>> print a.getvalue()
first first
second second
third third
fourth
<BLANKLINE> <BLANKLINE>
>>> d, e, f = b.fork(3) >>> b = cython.fork()
>>> d.write('alpha\n') >>> a = b.fork()
>>> f.write('gamma\n') >>> a.write('alpha\n')
>>> e.write('beta\n') >>> cython.write('gamma\n')
>>> print b.getvalue() >>> b.write('beta\n')
>>> print cython.getvalue()
second second
alpha alpha
beta beta
...@@ -76,7 +67,7 @@ gamma ...@@ -76,7 +67,7 @@ gamma
<BLANKLINE> <BLANKLINE>
>>> out = StringIO() >>> out = StringIO()
>>> a.copyto(out) >>> pyrex.copyto(out)
>>> print out.getvalue() >>> print out.getvalue()
first first
second second
...@@ -84,7 +75,6 @@ alpha ...@@ -84,7 +75,6 @@ alpha
beta beta
gamma gamma
third third
fourth
<BLANKLINE> <BLANKLINE>
""" """
......
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