You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

547 lines
18 KiB

##########################################################################
#
# Pyrex - Code output module
#
##########################################################################
import os, re
import Naming
from Pyrex.Utils import open_new_file
from PyrexTypes import py_object_type, c_char_array_type, typecast
identifier_pattern = re.compile(r"[A-Za-z_][A-Za-z0-9_]*$")
max_intern_length = 30
class CCodeWriter:
# f file output file
# level int indentation level
# bol bool beginning of line?
# marker string comment to emit before next line
def __init__(self, f):
#self.f = open_new_file(outfile_name)
self.f = f
self.level = 0
self.bol = 1
self.marker = None
def putln(self, code = ""):
if self.marker and self.bol:
self.emit_marker()
if code:
self.put(code)
self.f.write("\n");
self.bol = 1
def emit_marker(self):
self.f.write("\n");
self.indent()
self.f.write("/* %s */\n" % self.marker)
self.marker = None
def put(self, code):
dl = code.count("{") - code.count("}")
if dl < 0:
self.level += dl
if self.bol:
self.indent()
self.f.write(code)
self.bol = 0
if dl > 0:
self.level += dl
def increase_indent(self):
self.level = self.level + 1
def decrease_indent(self):
self.level = self.level - 1
def begin_block(self):
self.putln("{")
self.increase_indent()
def end_block(self):
self.decrease_indent()
self.putln("}")
def indent(self):
self.f.write(" " * self.level)
def mark_pos(self, pos):
file, line, col = pos
self.marker = '"%s":%s' % (file, line)
def put_var_declarations(self, entries, static = 0, dll_linkage = None,
definition = True):
for entry in entries:
if not entry.in_cinclude:
self.put_var_declaration(entry, static, dll_linkage, definition)
def put_var_declaration(self, entry, static = 0, dll_linkage = None,
definition = True):
#print "Code.put_var_declaration:", entry.name, repr(entry.type) ###
visibility = entry.visibility
if visibility == 'private' and not definition:
#print "...private and not definition, skipping" ###
return
if not entry.used and visibility == "private":
#print "not used and private, skipping" ###
return
storage_class = ""
if visibility == 'extern':
storage_class = Naming.extern_c_macro
elif visibility == 'public':
if not definition:
storage_class = Naming.extern_c_macro
elif visibility == 'private':
if static:
storage_class = "static"
if storage_class:
self.put("%s " % storage_class)
if visibility <> 'public':
dll_linkage = None
self.put(entry.type.declaration_code(entry.cname,
dll_linkage = dll_linkage))
if entry.init is not None:
self.put(" = %s" % entry.type.literal_code(entry.init))
self.putln(";")
def entry_as_pyobject(self, entry):
type = entry.type
if (not entry.is_self_arg and not entry.type.is_complete()) \
or (entry.type.is_extension_type and entry.type.base_type):
return "(PyObject *)" + entry.cname
else:
return entry.cname
def as_pyobject(self, cname, type):
if type:
return typecast(py_object_type, type, cname)
else:
return cname
def put_incref(self, cname, type = None):
self.putln("Py_INCREF(%s);" % self.as_pyobject(cname, type))
def put_decref(self, cname, type = None):
self.putln("Py_DECREF(%s);" % self.as_pyobject(cname, type))
def put_var_incref(self, entry):
if entry.type.is_pyobject:
self.putln("Py_INCREF(%s);" % self.entry_as_pyobject(entry))
def put_decref_clear(self, cname, type = None):
self.putln("Py_DECREF(%s); %s = 0;" % (
self.as_pyobject(cname, type), cname)) # What was wrong with this?
#typecast(py_object_type, type, cname), cname))
def put_xdecref(self, cname, type):
self.putln("Py_XDECREF(%s);" % self.as_pyobject(cname, type))
def put_xdecref_clear(self, cname, type):
self.putln("Py_XDECREF(%s); %s = 0;" % (
self.as_pyobject(cname, type), cname))
def put_var_decref(self, entry):
if entry.type.is_pyobject:
self.putln("Py_DECREF(%s);" % self.entry_as_pyobject(entry))
def put_var_decref_clear(self, entry):
if entry.type.is_pyobject:
self.putln("Py_DECREF(%s); %s = 0;" % (
self.entry_as_pyobject(entry), entry.cname))
def put_var_xdecref(self, entry):
if entry.type.is_pyobject:
self.putln("Py_XDECREF(%s);" % self.entry_as_pyobject(entry))
def put_var_xdecref_clear(self, entry):
if entry.type.is_pyobject:
self.putln("Py_XDECREF(%s); %s = 0;" % (
self.entry_as_pyobject(entry), entry.cname))
def put_var_decrefs(self, entries, used_only = 0):
for entry in entries:
if not used_only or entry.used:
if entry.xdecref_cleanup:
self.put_var_xdecref(entry)
else:
self.put_var_decref(entry)
def put_var_xdecrefs(self, entries):
for entry in entries:
self.put_var_xdecref(entry)
def put_var_xdecrefs_clear(self, entries):
for entry in entries:
self.put_var_xdecref_clear(entry)
def put_init_to_py_none(self, cname, type):
py_none = typecast(type, py_object_type, "Py_None")
self.putln("%s = %s; Py_INCREF(Py_None);" % (cname, py_none))
def put_init_var_to_py_none(self, entry, template = "%s"):
code = template % entry.cname
self.put_init_to_py_none(code, entry.type)
def put_pymethoddef(self, entry, term):
if entry.doc:
doc_code = entry.doc_cname
else:
doc_code = 0
self.putln(
'{"%s", (PyCFunction)%s, METH_VARARGS|METH_KEYWORDS, %s}%s' % (
entry.name,
entry.func_cname,
doc_code,
term))
def put_h_guard(self, guard):
self.putln("#ifndef %s" % guard)
self.putln("#define %s" % guard)
#--------------------------------------------------------------------------
class MainCCodeWriter(CCodeWriter):
# Code writer for executable C code.
#
# global_state GlobalCodeState module-wide state
# return_label string function return point label
# error_label string error catch point label
# continue_label string loop continue point label
# break_label string loop break point label
# label_counter integer counter for naming labels
# in_try_finally boolean inside try of try...finally
# exc_vars (string * 3) exception vars for reraise, or None
in_try_finally = 0
def __init__(self, f, base = None):
CCodeWriter.__init__(self, f)
if base:
self.global_state = base.global_state
else:
self.global_state = GlobalCodeState()
self.label_counter = 1
self.error_label = None
self.exc_vars = None
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):
n = self.label_counter
self.label_counter = n + 1
return "%s%d" % (Naming.label_prefix, n)
def new_error_label(self):
old_err_lbl = self.error_label
self.error_label = self.new_label()
return old_err_lbl
def get_loop_labels(self):
return (
self.continue_label,
self.break_label)
def set_loop_labels(self, labels):
(self.continue_label,
self.break_label) = labels
def new_loop_labels(self):
old_labels = self.get_loop_labels()
self.set_loop_labels(
(self.new_label(),
self.new_label()))
return old_labels
def get_all_labels(self):
return (
self.continue_label,
self.break_label,
self.return_label,
self.error_label)
def set_all_labels(self, labels):
(self.continue_label,
self.break_label,
self.return_label,
self.error_label) = labels
def all_new_labels(self):
old_labels = self.get_all_labels()
new_labels = []
for old_label in old_labels:
if old_label:
new_labels.append(self.new_label())
else:
new_labels.append(old_label)
self.set_all_labels(new_labels)
return old_labels
def use_label(self, lbl):
self.labels_used[lbl] = 1
def put_label(self, lbl):
if lbl in self.labels_used:
self.putln("%s:;" % lbl)
def put_goto(self, lbl):
self.use_label(lbl)
self.putln("goto %s;" % lbl)
def error_goto(self, pos):
lbl = self.error_label
self.use_label(lbl)
return "{%s; goto %s;}" % (
self.error_setup(pos),
lbl)
def error_setup(self, pos):
return "%s = %s[%s]; %s = %s" % (
Naming.filename_cname,
Naming.filetable_cname,
self.lookup_filename(pos[0]),
Naming.lineno_cname,
pos[1])
def lookup_filename(self, filename):
return self.global_state.lookup_filename(filename)
def use_utility_code(self, uc):
self.global_state.use_utility_code(uc)
def get_string_const(self, text):
# Get C name for a string constant, adding a new one
# if necessary.
return self.global_state.get_string_const(text).cname
def new_const(self, type):
# Get C name for a new precalculated value.
return self.global_state.new_const(type).cname
def get_py_string_const(self, text):
# Get C name for a Python string constant, adding a new one
# if necessary. If the string is name-like, it will be interned.
return self.global_state.get_py_string_const(text).cname
def intern(self, name):
return self.get_py_string_const(name)
#--------------------------------------------------------------------------
class StringConst:
# Info held by GlobalCodeState about a string constant.
#
# cname string
# text string
# py_const Const Corresponding Python string
py_const = None
def __init__(self, cname, text):
self.cname = cname
self.text = text
#--------------------------------------------------------------------------
class Const:
# Info held by GlobalCodeState about a precalculated value.
#
# cname string
# type PyrexType
# intern boolean for Python strings
intern = 0
def __init__(self, cname, type):
self.cname = cname
self.type = type
#--------------------------------------------------------------------------
class GlobalCodeState:
# State pertaining to code generation for a whole module.
#
# filename_table {string : int} for finding filename table indexes
# filename_list [string] filenames in filename table order
# utility_code {int : int} id to utility_list index
# utility_list list utility code used
# const_counter int for generating const names
# string_index {string : String} string constant index
# string_consts [StringConst] all string constants
# other_consts [Const] other precalculated values
def __init__(self):
self.filename_table = {}
self.filename_list = []
self.utility_code = {}
self.utility_list = []
self.const_counter = 1
self.string_index = {}
self.string_consts = []
self.other_consts = []
def lookup_filename(self, filename):
try:
index = self.filename_table[filename]
except KeyError:
index = len(self.filename_list)
self.filename_list.append(filename)
self.filename_table[filename] = index
return index
def generate_filename_table(self, code):
code.putln("")
code.putln("static char *%s[] = {" % Naming.filenames_cname)
if self.filename_list:
for filename in self.filename_list:
filename = os.path.basename(filename)
escaped_filename = filename.replace("\\", "\\\\").replace('"', r'\"')
code.putln('"%s",' %
escaped_filename)
else:
# Some C compilers don't like an empty array
code.putln("0")
code.putln("};")
def use_utility_code(self, uc):
i = id(uc)
if i not in self.utility_code:
self.utility_code[i] = len(self.utility_list)
self.utility_list.append(uc)
def generate_utility_functions(self, code):
code.putln("")
code.putln("/* Runtime support code */")
code.putln("")
code.putln("static void %s(void) {" % Naming.fileinit_cname)
code.putln("%s = %s;" %
(Naming.filetable_cname, Naming.filenames_cname))
code.putln("}")
for utility_code in self.utility_list:
code.h.put(utility_code[0])
code.put(utility_code[1])
def new_const_name(self):
# Create a new globally-unique name for a constant.
name = "%s%s" % (Naming.const_prefix, self.const_counter)
self.const_counter += 1
return name
def new_string_const(self, text):
# Add a new C string constant.
c = StringConst(self.new_const_name(), text)
self.string_consts.append(c)
self.string_index[text] = c
return c
def new_const(self, type, cname = None):
if not cname:
cname = self.new_const_name()
c = Const(cname, type)
self.other_consts.append(c)
return c
def new_py_const(self, cname = None, intern = 0):
# Add a new Python constant.
c = self.new_const(py_object_type, cname)
if intern:
c.intern = 1
return c
def get_string_const(self, text):
# Get a C string constant, adding a new one if necessary.
c = self.string_index.get(text)
if not c:
c = self.new_string_const(text)
return c
def get_py_string_const(self, text):
# Get a Python string constant, adding a new one if necessary.
# If the string is name-like, it will be interned.
s = self.get_string_const(text)
if not s.py_const:
intern = len(text) <= max_intern_length and identifier_pattern.match(text)
if intern:
cname = Naming.interned_prefix + text
else:
cname = s.cname + "p"
s.py_const = self.new_py_const(cname, intern)
return s.py_const
def generate_const_declarations(self, code):
self.generate_string_const_declarations(code)
self.generate_other_const_declarations(code)
self.generate_stringtab(code)
def generate_string_const_declarations(self, code):
code.putln("")
for c in self.string_consts:
code.putln('static char %s[] = "%s";' % (c.cname, c.text))
def generate_other_const_declarations(self, code):
interned = []
uninterned = []
for c in self.other_consts:
if c.intern:
interned.append(c)
else:
uninterned.append(c)
interned.sort(lambda c1, c2: cmp(c1.cname, c2.cname))
def put_consts(consts):
code.putln("")
for c in consts:
decl = c.type.declaration_code(c.cname)
code.putln("static %s;" % decl)
put_consts(interned)
put_consts(uninterned)
def generate_stringtab(self, code):
interned = []
uninterned = []
for s in self.string_consts:
p = s.py_const
if p:
if p.intern:
interned.append(s)
else:
uninterned.append(s)
interned.sort(lambda c1, c2: cmp(c1.py_const.cname, c2.py_const.cname))
def put_stringtab(consts, intern):
for c in consts:
cname = c.cname
code.putln("{&%s, %d, %s, sizeof(%s)}," % (
c.py_const.cname, intern, cname, cname))
code.putln("")
code.putln("static __Pyx_StringTabEntry %s[] = {" % Naming.stringtab_cname)
put_stringtab(interned, 1)
put_stringtab(uninterned, 0)
code.putln("{0, 0, 0, 0}")
code.putln("};")
#--------------------------------------------------------------------------
class PyrexCodeWriter:
# f file output file
# level int indentation level
def __init__(self, outfile_name):
self.f = open_new_file(outfile_name)
self.level = 0
def putln(self, code):
self.f.write("%s%s\n" % (" " * self.level, code))
def indent(self):
self.level += 1
def dedent(self):
self.level -= 1