Cython has moved to github.

pyrex

view Pyrex/Compiler/Code.py @ 86:da6e97bb7e6d

Multiple compilation fixed
author Gregory Ewing <greg.ewing@canterbury.ac.nz>
date Sat May 24 15:50:12 2008 +1200 (4 years ago)
parents 12072e0e4fd5
children 72fea47648e9
line source
1 ##########################################################################
2 #
3 # Pyrex - Code output module
4 #
5 ##########################################################################
7 import os, re
8 import Naming
9 from Pyrex.Utils import open_new_file
10 from PyrexTypes import py_object_type, c_char_array_type, typecast
12 identifier_pattern = re.compile(r"[A-Za-z_][A-Za-z0-9_]*$")
13 max_intern_length = 30
15 class CCodeWriter:
16 # f file output file
17 # level int indentation level
18 # bol bool beginning of line?
19 # marker string comment to emit before next line
21 def __init__(self, f):
22 #self.f = open_new_file(outfile_name)
23 self.f = f
24 self.level = 0
25 self.bol = 1
26 self.marker = None
28 def putln(self, code = ""):
29 if self.marker and self.bol:
30 self.emit_marker()
31 if code:
32 self.put(code)
33 self.f.write("\n");
34 self.bol = 1
36 def emit_marker(self):
37 self.f.write("\n");
38 self.indent()
39 self.f.write("/* %s */\n" % self.marker)
40 self.marker = None
42 def put(self, code):
43 dl = code.count("{") - code.count("}")
44 if dl < 0:
45 self.level += dl
46 if self.bol:
47 self.indent()
48 self.f.write(code)
49 self.bol = 0
50 if dl > 0:
51 self.level += dl
53 def increase_indent(self):
54 self.level = self.level + 1
56 def decrease_indent(self):
57 self.level = self.level - 1
59 def begin_block(self):
60 self.putln("{")
61 self.increase_indent()
63 def end_block(self):
64 self.decrease_indent()
65 self.putln("}")
67 def indent(self):
68 self.f.write(" " * self.level)
70 def mark_pos(self, pos):
71 file, line, col = pos
72 self.marker = '"%s":%s' % (file, line)
74 def put_var_declarations(self, entries, static = 0, dll_linkage = None,
75 definition = True):
76 for entry in entries:
77 if not entry.in_cinclude:
78 self.put_var_declaration(entry, static, dll_linkage, definition)
80 def put_var_declaration(self, entry, static = 0, dll_linkage = None,
81 definition = True):
82 #print "Code.put_var_declaration:", entry.name, repr(entry.type) ###
83 visibility = entry.visibility
84 if visibility == 'private' and not definition:
85 #print "...private and not definition, skipping" ###
86 return
87 if not entry.used and visibility == "private":
88 #print "not used and private, skipping" ###
89 return
90 storage_class = ""
91 if visibility == 'extern':
92 storage_class = Naming.extern_c_macro
93 elif visibility == 'public':
94 if not definition:
95 storage_class = Naming.extern_c_macro
96 elif visibility == 'private':
97 if static:
98 storage_class = "static"
99 if storage_class:
100 self.put("%s " % storage_class)
101 if visibility <> 'public':
102 dll_linkage = None
103 self.put(entry.type.declaration_code(entry.cname,
104 dll_linkage = dll_linkage))
105 if entry.init is not None:
106 self.put(" = %s" % entry.type.literal_code(entry.init))
107 self.putln(";")
109 def entry_as_pyobject(self, entry):
110 type = entry.type
111 if (not entry.is_self_arg and not entry.type.is_complete()) \
112 or (entry.type.is_extension_type and entry.type.base_type):
113 return "(PyObject *)" + entry.cname
114 else:
115 return entry.cname
117 def as_pyobject(self, cname, type):
118 if type:
119 return typecast(py_object_type, type, cname)
120 else:
121 return cname
123 def put_incref(self, cname, type = None):
124 self.putln("Py_INCREF(%s);" % self.as_pyobject(cname, type))
126 def put_decref(self, cname, type = None):
127 self.putln("Py_DECREF(%s);" % self.as_pyobject(cname, type))
129 def put_var_incref(self, entry):
130 if entry.type.is_pyobject:
131 self.putln("Py_INCREF(%s);" % self.entry_as_pyobject(entry))
133 def put_decref_clear(self, cname, type = None):
134 self.putln("Py_DECREF(%s); %s = 0;" % (
135 self.as_pyobject(cname, type), cname)) # What was wrong with this?
136 #typecast(py_object_type, type, cname), cname))
138 def put_xdecref(self, cname, type):
139 self.putln("Py_XDECREF(%s);" % self.as_pyobject(cname, type))
141 def put_xdecref_clear(self, cname, type):
142 self.putln("Py_XDECREF(%s); %s = 0;" % (
143 self.as_pyobject(cname, type), cname))
145 def put_var_decref(self, entry):
146 if entry.type.is_pyobject:
147 self.putln("Py_DECREF(%s);" % self.entry_as_pyobject(entry))
149 def put_var_decref_clear(self, entry):
150 if entry.type.is_pyobject:
151 self.putln("Py_DECREF(%s); %s = 0;" % (
152 self.entry_as_pyobject(entry), entry.cname))
154 def put_var_xdecref(self, entry):
155 if entry.type.is_pyobject:
156 self.putln("Py_XDECREF(%s);" % self.entry_as_pyobject(entry))
158 def put_var_xdecref_clear(self, entry):
159 if entry.type.is_pyobject:
160 self.putln("Py_XDECREF(%s); %s = 0;" % (
161 self.entry_as_pyobject(entry), entry.cname))
163 def put_var_decrefs(self, entries, used_only = 0):
164 for entry in entries:
165 if not used_only or entry.used:
166 if entry.xdecref_cleanup:
167 self.put_var_xdecref(entry)
168 else:
169 self.put_var_decref(entry)
171 def put_var_xdecrefs(self, entries):
172 for entry in entries:
173 self.put_var_xdecref(entry)
175 def put_var_xdecrefs_clear(self, entries):
176 for entry in entries:
177 self.put_var_xdecref_clear(entry)
179 def put_init_to_py_none(self, cname, type):
180 py_none = typecast(type, py_object_type, "Py_None")
181 self.putln("%s = %s; Py_INCREF(Py_None);" % (cname, py_none))
183 def put_init_var_to_py_none(self, entry, template = "%s"):
184 code = template % entry.cname
185 self.put_init_to_py_none(code, entry.type)
187 def put_pymethoddef(self, entry, term):
188 if entry.doc:
189 doc_code = entry.doc_cname
190 else:
191 doc_code = 0
192 self.putln(
193 '{"%s", (PyCFunction)%s, METH_VARARGS|METH_KEYWORDS, %s}%s' % (
194 entry.name,
195 entry.func_cname,
196 doc_code,
197 term))
199 def put_h_guard(self, guard):
200 self.putln("#ifndef %s" % guard)
201 self.putln("#define %s" % guard)
203 #--------------------------------------------------------------------------
205 class MainCCodeWriter(CCodeWriter):
206 # Code writer for executable C code.
207 #
208 # global_state GlobalCodeState module-wide state
209 # return_label string function return point label
210 # error_label string error catch point label
211 # continue_label string loop continue point label
212 # break_label string loop break point label
213 # label_counter integer counter for naming labels
214 # in_try_finally boolean inside try of try...finally
215 # exc_vars (string * 3) exception vars for reraise, or None
217 in_try_finally = 0
219 def __init__(self, f, base = None):
220 CCodeWriter.__init__(self, f)
221 if base:
222 self.global_state = base.global_state
223 else:
224 self.global_state = GlobalCodeState()
225 self.label_counter = 1
226 self.error_label = None
227 self.exc_vars = None
229 def init_labels(self):
230 self.label_counter = 0
231 self.labels_used = {}
232 self.return_label = self.new_label()
233 self.new_error_label()
234 self.continue_label = None
235 self.break_label = None
237 def new_label(self):
238 n = self.label_counter
239 self.label_counter = n + 1
240 return "%s%d" % (Naming.label_prefix, n)
242 def new_error_label(self):
243 old_err_lbl = self.error_label
244 self.error_label = self.new_label()
245 return old_err_lbl
247 def get_loop_labels(self):
248 return (
249 self.continue_label,
250 self.break_label)
252 def set_loop_labels(self, labels):
253 (self.continue_label,
254 self.break_label) = labels
256 def new_loop_labels(self):
257 old_labels = self.get_loop_labels()
258 self.set_loop_labels(
259 (self.new_label(),
260 self.new_label()))
261 return old_labels
263 def get_all_labels(self):
264 return (
265 self.continue_label,
266 self.break_label,
267 self.return_label,
268 self.error_label)
270 def set_all_labels(self, labels):
271 (self.continue_label,
272 self.break_label,
273 self.return_label,
274 self.error_label) = labels
276 def all_new_labels(self):
277 old_labels = self.get_all_labels()
278 new_labels = []
279 for old_label in old_labels:
280 if old_label:
281 new_labels.append(self.new_label())
282 else:
283 new_labels.append(old_label)
284 self.set_all_labels(new_labels)
285 return old_labels
287 def use_label(self, lbl):
288 self.labels_used[lbl] = 1
290 def put_label(self, lbl):
291 if lbl in self.labels_used:
292 self.putln("%s:;" % lbl)
294 def put_goto(self, lbl):
295 self.use_label(lbl)
296 self.putln("goto %s;" % lbl)
298 def error_goto(self, pos):
299 lbl = self.error_label
300 self.use_label(lbl)
301 return "{%s = %s[%s]; %s = %s; goto %s;}" % (
302 Naming.filename_cname,
303 Naming.filetable_cname,
304 self.lookup_filename(pos[0]),
305 Naming.lineno_cname,
306 pos[1],
307 lbl)
309 def lookup_filename(self, filename):
310 return self.global_state.lookup_filename(filename)
312 def use_utility_code(self, uc):
313 self.global_state.use_utility_code(uc)
315 def get_string_const(self, text):
316 # Get C name for a string constant, adding a new one
317 # if necessary.
318 return self.global_state.get_string_const(text).cname
320 def new_const(self, type):
321 # Get C name for a new precalculated value.
322 return self.global_state.new_const(type).cname
324 def get_py_string_const(self, text):
325 # Get C name for a Python string constant, adding a new one
326 # if necessary. If the string is name-like, it will be interned.
327 return self.global_state.get_py_string_const(text).cname
329 def intern(self, name):
330 return self.get_py_string_const(name)
332 #--------------------------------------------------------------------------
334 class StringConst:
335 # Info held by GlobalCodeState about a string constant.
336 #
337 # cname string
338 # text string
339 # py_const Const Corresponding Python string
341 py_const = None
343 def __init__(self, cname, text):
344 self.cname = cname
345 self.text = text
347 #--------------------------------------------------------------------------
349 class Const:
350 # Info held by GlobalCodeState about a precalculated value.
351 #
352 # cname string
353 # type PyrexType
354 # intern boolean for Python strings
356 intern = 0
358 def __init__(self, cname, type):
359 self.cname = cname
360 self.type = type
362 #--------------------------------------------------------------------------
364 class GlobalCodeState:
365 # State pertaining to code generation for a whole module.
366 #
367 # filename_table {string : int} for finding filename table indexes
368 # filename_list [string] filenames in filename table order
369 # utility_code {int : int} id to utility_list index
370 # utility_list list utility code used
371 # const_counter int for generating const names
372 # string_index {string : String} string constant index
373 # string_consts [StringConst] all string constants
374 # other_consts [Const] other precalculated values
376 def __init__(self):
377 self.filename_table = {}
378 self.filename_list = []
379 self.utility_code = {}
380 self.utility_list = []
381 self.const_counter = 1
382 self.string_index = {}
383 self.string_consts = []
384 self.other_consts = []
386 def lookup_filename(self, filename):
387 try:
388 index = self.filename_table[filename]
389 except KeyError:
390 index = len(self.filename_list)
391 self.filename_list.append(filename)
392 self.filename_table[filename] = index
393 return index
395 def generate_filename_table(self, code):
396 code.putln("")
397 code.putln("static char *%s[] = {" % Naming.filenames_cname)
398 if self.filename_list:
399 for filename in self.filename_list:
400 filename = os.path.basename(filename)
401 escaped_filename = filename.replace("\\", "\\\\").replace('"', r'\"')
402 code.putln('"%s",' %
403 escaped_filename)
404 else:
405 # Some C compilers don't like an empty array
406 code.putln("0")
407 code.putln("};")
409 def use_utility_code(self, uc):
410 i = id(uc)
411 if i not in self.utility_code:
412 self.utility_code[i] = len(self.utility_list)
413 self.utility_list.append(uc)
415 def generate_utility_functions(self, code):
416 code.putln("")
417 code.putln("/* Runtime support code */")
418 code.putln("")
419 code.putln("static void %s(void) {" % Naming.fileinit_cname)
420 code.putln("%s = %s;" %
421 (Naming.filetable_cname, Naming.filenames_cname))
422 code.putln("}")
423 for utility_code in self.utility_list:
424 code.h.put(utility_code[0])
425 code.put(utility_code[1])
427 def new_const_name(self):
428 # Create a new globally-unique name for a constant.
429 name = "%s%s" % (Naming.const_prefix, self.const_counter)
430 self.const_counter += 1
431 return name
433 def new_string_const(self, text):
434 # Add a new C string constant.
435 c = StringConst(self.new_const_name(), text)
436 self.string_consts.append(c)
437 self.string_index[text] = c
438 return c
440 def new_const(self, type, cname = None):
441 if not cname:
442 cname = self.new_const_name()
443 c = Const(cname, type)
444 self.other_consts.append(c)
445 return c
447 def new_py_const(self, cname = None, intern = 0):
448 # Add a new Python constant.
449 c = self.new_const(py_object_type, cname)
450 if intern:
451 c.intern = 1
452 return c
454 def get_string_const(self, text):
455 # Get a C string constant, adding a new one if necessary.
456 c = self.string_index.get(text)
457 if not c:
458 c = self.new_string_const(text)
459 return c
461 def get_py_string_const(self, text):
462 # Get a Python string constant, adding a new one if necessary.
463 # If the string is name-like, it will be interned.
464 s = self.get_string_const(text)
465 if not s.py_const:
466 intern = len(text) <= max_intern_length and identifier_pattern.match(text)
467 if intern:
468 cname = Naming.interned_prefix + text
469 else:
470 cname = s.cname + "p"
471 s.py_const = self.new_py_const(cname, intern)
472 return s.py_const
474 def generate_const_declarations(self, code):
475 self.generate_string_const_declarations(code)
476 self.generate_other_const_declarations(code)
477 self.generate_stringtab(code)
479 def generate_string_const_declarations(self, code):
480 code.putln("")
481 for c in self.string_consts:
482 code.putln('static char %s[] = "%s";' % (c.cname, c.text))
484 def generate_other_const_declarations(self, code):
485 interned = []
486 uninterned = []
487 for c in self.other_consts:
488 if c.intern:
489 interned.append(c)
490 else:
491 uninterned.append(c)
492 interned.sort(lambda c1, c2: cmp(c1.cname, c2.cname))
493 def put_consts(consts):
494 code.putln("")
495 for c in consts:
496 decl = c.type.declaration_code(c.cname)
497 code.putln("static %s;" % decl)
498 put_consts(interned)
499 put_consts(uninterned)
501 def generate_stringtab(self, code):
502 interned = []
503 uninterned = []
504 for s in self.string_consts:
505 p = s.py_const
506 if p:
507 if p.intern:
508 interned.append(s)
509 else:
510 uninterned.append(s)
511 interned.sort(lambda c1, c2: cmp(c1.py_const.cname, c2.py_const.cname))
512 def put_stringtab(consts, intern):
513 for c in consts:
514 cname = c.cname
515 code.putln("{&%s, %d, %s, sizeof(%s)}," % (
516 c.py_const.cname, intern, cname, cname))
517 code.putln("")
518 code.putln("static __Pyx_StringTabEntry %s[] = {" % Naming.stringtab_cname)
519 put_stringtab(interned, 1)
520 put_stringtab(uninterned, 0)
521 code.putln("{0, 0, 0, 0}")
522 code.putln("};")
524 #--------------------------------------------------------------------------
526 class PyrexCodeWriter:
527 # f file output file
528 # level int indentation level
530 def __init__(self, outfile_name):
531 self.f = open_new_file(outfile_name)
532 self.level = 0
534 def putln(self, code):
535 self.f.write("%s%s\n" % (" " * self.level, code))
537 def indent(self):
538 self.level += 1
540 def dedent(self):
541 self.level -= 1