paul@126 | 1 | #!/usr/bin/env python |
paul@126 | 2 | |
paul@126 | 3 | """ |
paul@126 | 4 | Generate C code from object layouts and other deduced information. |
paul@126 | 5 | |
paul@126 | 6 | Copyright (C) 2015, 2016 Paul Boddie <paul@boddie.org.uk> |
paul@126 | 7 | |
paul@126 | 8 | This program is free software; you can redistribute it and/or modify it under |
paul@126 | 9 | the terms of the GNU General Public License as published by the Free Software |
paul@126 | 10 | Foundation; either version 3 of the License, or (at your option) any later |
paul@126 | 11 | version. |
paul@126 | 12 | |
paul@126 | 13 | This program is distributed in the hope that it will be useful, but WITHOUT |
paul@126 | 14 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
paul@126 | 15 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
paul@126 | 16 | details. |
paul@126 | 17 | |
paul@126 | 18 | You should have received a copy of the GNU General Public License along with |
paul@126 | 19 | this program. If not, see <http://www.gnu.org/licenses/>. |
paul@126 | 20 | """ |
paul@126 | 21 | |
paul@126 | 22 | from common import CommonOutput |
paul@126 | 23 | from encoders import encode_bound_reference, encode_function_pointer, \ |
paul@136 | 24 | encode_instantiator_pointer, \ |
paul@136 | 25 | encode_literal_constant, encode_literal_constant_member, \ |
paul@159 | 26 | encode_literal_constant_value, \ |
paul@159 | 27 | encode_literal_instantiator, encode_literal_reference, \ |
paul@136 | 28 | encode_path, \ |
paul@150 | 29 | encode_predefined_reference, encode_size, \ |
paul@150 | 30 | encode_symbol, encode_tablename, \ |
paul@132 | 31 | encode_type_attribute |
paul@126 | 32 | from os import listdir |
paul@126 | 33 | from os.path import isdir, join, split |
paul@126 | 34 | from referencing import Reference |
paul@126 | 35 | |
paul@126 | 36 | def copy(source, target): |
paul@126 | 37 | |
paul@126 | 38 | "Copy a text file from 'source' to 'target'." |
paul@126 | 39 | |
paul@126 | 40 | if isdir(target): |
paul@126 | 41 | target = join(target, split(source)[-1]) |
paul@126 | 42 | infile = open(source) |
paul@126 | 43 | outfile = open(target, "w") |
paul@126 | 44 | try: |
paul@126 | 45 | outfile.write(infile.read()) |
paul@126 | 46 | finally: |
paul@126 | 47 | outfile.close() |
paul@126 | 48 | infile.close() |
paul@126 | 49 | |
paul@126 | 50 | class Generator(CommonOutput): |
paul@126 | 51 | |
paul@126 | 52 | "A code generator." |
paul@126 | 53 | |
paul@126 | 54 | function_type = "__builtins__.core.function" |
paul@126 | 55 | |
paul@158 | 56 | # NOTE: These must be synchronised with the library. |
paul@158 | 57 | |
paul@136 | 58 | predefined_constant_members = ( |
paul@158 | 59 | ("__builtins__.boolean", "False"), |
paul@158 | 60 | ("__builtins__.boolean", "True"), |
paul@136 | 61 | ("__builtins__.none", "None"), |
paul@136 | 62 | ("__builtins__.notimplemented", "NotImplemented"), |
paul@136 | 63 | ) |
paul@136 | 64 | |
paul@159 | 65 | literal_instantiator_types = ( |
paul@159 | 66 | "__builtins__.dict.dict", |
paul@159 | 67 | "__builtins__.list.list", |
paul@159 | 68 | "__builtins__.tuple.tuple", |
paul@159 | 69 | ) |
paul@159 | 70 | |
paul@126 | 71 | def __init__(self, importer, optimiser, output): |
paul@126 | 72 | self.importer = importer |
paul@126 | 73 | self.optimiser = optimiser |
paul@126 | 74 | self.output = output |
paul@126 | 75 | |
paul@126 | 76 | def to_output(self): |
paul@126 | 77 | |
paul@126 | 78 | "Write the generated code." |
paul@126 | 79 | |
paul@126 | 80 | self.check_output() |
paul@126 | 81 | self.write_structures() |
paul@126 | 82 | self.copy_templates() |
paul@126 | 83 | |
paul@126 | 84 | def copy_templates(self): |
paul@126 | 85 | |
paul@126 | 86 | "Copy template files to the generated output directory." |
paul@126 | 87 | |
paul@126 | 88 | templates = join(split(__file__)[0], "templates") |
paul@126 | 89 | |
paul@126 | 90 | for filename in listdir(templates): |
paul@126 | 91 | copy(join(templates, filename), self.output) |
paul@126 | 92 | |
paul@126 | 93 | def write_structures(self): |
paul@126 | 94 | |
paul@126 | 95 | "Write structures used by the program." |
paul@126 | 96 | |
paul@126 | 97 | f_consts = open(join(self.output, "progconsts.h"), "w") |
paul@126 | 98 | f_defs = open(join(self.output, "progtypes.c"), "w") |
paul@126 | 99 | f_decls = open(join(self.output, "progtypes.h"), "w") |
paul@126 | 100 | f_signatures = open(join(self.output, "main.h"), "w") |
paul@126 | 101 | f_code = open(join(self.output, "main.c"), "w") |
paul@126 | 102 | |
paul@126 | 103 | try: |
paul@126 | 104 | # Output boilerplate. |
paul@126 | 105 | |
paul@126 | 106 | print >>f_consts, """\ |
paul@126 | 107 | #ifndef __PROGCONSTS_H__ |
paul@126 | 108 | #define __PROGCONSTS_H__ |
paul@126 | 109 | """ |
paul@126 | 110 | print >>f_decls, """\ |
paul@126 | 111 | #ifndef __PROGTYPES_H__ |
paul@126 | 112 | #define __PROGTYPES_H__ |
paul@126 | 113 | |
paul@126 | 114 | #include "progconsts.h" |
paul@126 | 115 | #include "types.h" |
paul@126 | 116 | """ |
paul@126 | 117 | print >>f_defs, """\ |
paul@126 | 118 | #include "progtypes.h" |
paul@132 | 119 | #include "progops.h" |
paul@126 | 120 | #include "main.h" |
paul@126 | 121 | """ |
paul@126 | 122 | print >>f_signatures, """\ |
paul@126 | 123 | #ifndef __MAIN_H__ |
paul@126 | 124 | #define __MAIN_H__ |
paul@126 | 125 | |
paul@126 | 126 | #include "types.h" |
paul@126 | 127 | """ |
paul@126 | 128 | print >>f_code, """\ |
paul@126 | 129 | #include <string.h> |
paul@159 | 130 | #include <stdio.h> |
paul@126 | 131 | #include "types.h" |
paul@159 | 132 | #include "exceptions.h" |
paul@126 | 133 | #include "ops.h" |
paul@126 | 134 | #include "progconsts.h" |
paul@126 | 135 | #include "progtypes.h" |
paul@126 | 136 | #include "progops.h" |
paul@126 | 137 | #include "main.h" |
paul@126 | 138 | """ |
paul@126 | 139 | |
paul@126 | 140 | # Generate structure size data. |
paul@126 | 141 | |
paul@126 | 142 | size_tables = {} |
paul@126 | 143 | |
paul@126 | 144 | for kind in ["<class>", "<module>", "<instance>"]: |
paul@126 | 145 | size_tables[kind] = {} |
paul@126 | 146 | |
paul@126 | 147 | for ref, structure in self.optimiser.structures.items(): |
paul@126 | 148 | size_tables[ref.get_kind()][ref.get_origin()] = len(structure) |
paul@126 | 149 | |
paul@126 | 150 | size_tables = size_tables.items() |
paul@126 | 151 | size_tables.sort() |
paul@126 | 152 | |
paul@126 | 153 | for kind, sizes in size_tables: |
paul@150 | 154 | self.write_size_constants(f_consts, kind, sizes, 0) |
paul@126 | 155 | |
paul@126 | 156 | # Generate parameter table size data. |
paul@126 | 157 | |
paul@126 | 158 | min_sizes = {} |
paul@126 | 159 | max_sizes = {} |
paul@126 | 160 | |
paul@126 | 161 | for path, parameters in self.optimiser.parameters.items(): |
paul@126 | 162 | argmin, argmax = self.get_argument_limits(path) |
paul@126 | 163 | min_sizes[path] = argmin |
paul@126 | 164 | max_sizes[path] = argmax |
paul@126 | 165 | |
paul@126 | 166 | # Record instantiator limits. |
paul@126 | 167 | |
paul@126 | 168 | if path.endswith(".__init__"): |
paul@126 | 169 | path = path[:-len(".__init__")] |
paul@126 | 170 | |
paul@126 | 171 | self.write_size_constants(f_consts, "pmin", min_sizes, 0) |
paul@126 | 172 | self.write_size_constants(f_consts, "pmax", max_sizes, 0) |
paul@126 | 173 | |
paul@132 | 174 | # Generate parameter codes. |
paul@132 | 175 | |
paul@132 | 176 | self.write_code_constants(f_consts, self.optimiser.all_paramnames, self.optimiser.arg_locations, "pcode", "ppos") |
paul@132 | 177 | |
paul@126 | 178 | # Generate attribute codes. |
paul@126 | 179 | |
paul@132 | 180 | self.write_code_constants(f_consts, self.optimiser.all_attrnames, self.optimiser.locations, "code", "pos") |
paul@126 | 181 | |
paul@126 | 182 | # Generate table and structure data. |
paul@126 | 183 | |
paul@126 | 184 | function_instance_attrs = None |
paul@126 | 185 | objects = self.optimiser.attr_table.items() |
paul@126 | 186 | objects.sort() |
paul@126 | 187 | |
paul@126 | 188 | for ref, indexes in objects: |
paul@126 | 189 | attrnames = self.get_attribute_names(indexes) |
paul@126 | 190 | |
paul@126 | 191 | kind = ref.get_kind() |
paul@126 | 192 | path = ref.get_origin() |
paul@150 | 193 | table_name = encode_tablename(kind, path) |
paul@150 | 194 | structure_size = encode_size(kind, path) |
paul@126 | 195 | |
paul@126 | 196 | # Generate structures for classes and modules. |
paul@126 | 197 | |
paul@126 | 198 | if kind != "<instance>": |
paul@126 | 199 | structure = [] |
paul@126 | 200 | attrs = self.get_static_attributes(kind, path, attrnames) |
paul@126 | 201 | |
paul@126 | 202 | # Set a special instantiator on the class. |
paul@126 | 203 | |
paul@126 | 204 | if kind == "<class>": |
paul@126 | 205 | attrs["__fn__"] = path |
paul@126 | 206 | attrs["__args__"] = encode_size("pmin", path) |
paul@126 | 207 | |
paul@126 | 208 | # Write instantiator declarations based on the |
paul@126 | 209 | # applicable initialiser. |
paul@126 | 210 | |
paul@126 | 211 | init_ref = attrs["__init__"] |
paul@126 | 212 | |
paul@126 | 213 | # Write instantiator definitions. |
paul@126 | 214 | |
paul@159 | 215 | self.write_instantiator(f_code, f_signatures, path, init_ref) |
paul@126 | 216 | |
paul@126 | 217 | # Write parameter table. |
paul@126 | 218 | |
paul@126 | 219 | self.make_parameter_table(f_decls, f_defs, path, init_ref.get_origin()) |
paul@126 | 220 | |
paul@126 | 221 | self.populate_structure(Reference(kind, path), attrs, kind, structure) |
paul@136 | 222 | |
paul@136 | 223 | if kind == "<class>": |
paul@136 | 224 | self.write_instance_structure(f_decls, path, structure_size) |
paul@136 | 225 | |
paul@136 | 226 | self.write_structure(f_decls, f_defs, path, table_name, structure_size, structure, |
paul@136 | 227 | kind == "<class>" and path) |
paul@126 | 228 | |
paul@126 | 229 | # Record function instance details for function generation below. |
paul@126 | 230 | |
paul@126 | 231 | else: |
paul@126 | 232 | attrs = self.get_instance_attributes(path, attrnames) |
paul@126 | 233 | if path == self.function_type: |
paul@126 | 234 | function_instance_attrs = attrs |
paul@126 | 235 | |
paul@126 | 236 | # Write a table for all objects. |
paul@126 | 237 | |
paul@126 | 238 | table = [] |
paul@126 | 239 | self.populate_table(Reference(kind, path), table) |
paul@126 | 240 | self.write_table(f_decls, f_defs, table_name, structure_size, table) |
paul@126 | 241 | |
paul@126 | 242 | # Generate function instances. |
paul@126 | 243 | |
paul@126 | 244 | functions = set() |
paul@126 | 245 | |
paul@126 | 246 | for ref in self.importer.objects.values(): |
paul@126 | 247 | if ref.has_kind("<function>"): |
paul@126 | 248 | functions.add(ref.get_origin()) |
paul@126 | 249 | |
paul@126 | 250 | functions = list(functions) |
paul@126 | 251 | functions.sort() |
paul@126 | 252 | |
paul@126 | 253 | for path in functions: |
paul@126 | 254 | cls = self.function_type |
paul@150 | 255 | table_name = encode_tablename("<instance>", cls) |
paul@150 | 256 | structure_size = encode_size("<instance>", cls) |
paul@126 | 257 | |
paul@126 | 258 | # Set a special callable attribute on the instance. |
paul@126 | 259 | |
paul@126 | 260 | function_instance_attrs["__fn__"] = path |
paul@126 | 261 | function_instance_attrs["__args__"] = encode_size("pmin", path) |
paul@126 | 262 | |
paul@126 | 263 | # Produce two structures where a method is involved. |
paul@126 | 264 | |
paul@126 | 265 | ref = self.importer.get_object(path) |
paul@126 | 266 | parent_ref = self.importer.get_object(ref.parent()) |
paul@126 | 267 | parent_kind = parent_ref and parent_ref.get_kind() |
paul@126 | 268 | |
paul@126 | 269 | # Populate and write each structure. |
paul@126 | 270 | |
paul@126 | 271 | if parent_kind == "<class>": |
paul@126 | 272 | |
paul@132 | 273 | # A bound version of a method. |
paul@132 | 274 | |
paul@132 | 275 | structure = self.populate_function(path, function_instance_attrs, False) |
paul@136 | 276 | self.write_structure(f_decls, f_defs, encode_bound_reference(path), table_name, structure_size, structure) |
paul@132 | 277 | |
paul@126 | 278 | # An unbound version of a method. |
paul@126 | 279 | |
paul@126 | 280 | structure = self.populate_function(path, function_instance_attrs, True) |
paul@126 | 281 | self.write_structure(f_decls, f_defs, path, table_name, structure_size, structure) |
paul@126 | 282 | |
paul@132 | 283 | else: |
paul@132 | 284 | # A normal function. |
paul@126 | 285 | |
paul@126 | 286 | structure = self.populate_function(path, function_instance_attrs, False) |
paul@132 | 287 | self.write_structure(f_decls, f_defs, path, table_name, structure_size, structure) |
paul@126 | 288 | |
paul@154 | 289 | # Functions with defaults need to declare instance structures. |
paul@154 | 290 | |
paul@154 | 291 | if self.importer.function_defaults.get(path): |
paul@154 | 292 | self.write_instance_structure(f_decls, path, structure_size) |
paul@154 | 293 | |
paul@126 | 294 | # Write function declarations. |
paul@126 | 295 | # Signature: __attr <name>(__attr[]); |
paul@126 | 296 | |
paul@126 | 297 | print >>f_signatures, "__attr %s(__attr args[]);" % encode_function_pointer(path) |
paul@126 | 298 | |
paul@126 | 299 | # Write parameter table. |
paul@126 | 300 | |
paul@126 | 301 | self.make_parameter_table(f_decls, f_defs, path, path) |
paul@126 | 302 | |
paul@136 | 303 | # Generate predefined constants. |
paul@136 | 304 | |
paul@136 | 305 | for path, name in self.predefined_constant_members: |
paul@136 | 306 | self.make_predefined_constant(f_decls, f_defs, path, name) |
paul@136 | 307 | |
paul@136 | 308 | # Generate literal constants. |
paul@136 | 309 | |
paul@136 | 310 | for value, n in self.optimiser.constants.items(): |
paul@136 | 311 | self.make_literal_constant(f_decls, f_defs, n, value) |
paul@136 | 312 | |
paul@146 | 313 | # Finish the main source file. |
paul@146 | 314 | |
paul@146 | 315 | self.write_main_program(f_code, f_signatures) |
paul@146 | 316 | |
paul@126 | 317 | # Output more boilerplate. |
paul@126 | 318 | |
paul@126 | 319 | print >>f_consts, """\ |
paul@126 | 320 | |
paul@126 | 321 | #endif /* __PROGCONSTS_H__ */""" |
paul@126 | 322 | |
paul@126 | 323 | print >>f_decls, """\ |
paul@126 | 324 | |
paul@126 | 325 | #define __FUNCTION_TYPE %s |
paul@126 | 326 | #define __FUNCTION_INSTANCE_SIZE %s |
paul@126 | 327 | |
paul@126 | 328 | #endif /* __PROGTYPES_H__ */""" % ( |
paul@126 | 329 | encode_path(self.function_type), |
paul@150 | 330 | encode_size("<instance>", self.function_type) |
paul@126 | 331 | ) |
paul@126 | 332 | |
paul@126 | 333 | print >>f_signatures, """\ |
paul@126 | 334 | |
paul@126 | 335 | #endif /* __MAIN_H__ */""" |
paul@126 | 336 | |
paul@126 | 337 | finally: |
paul@126 | 338 | f_consts.close() |
paul@126 | 339 | f_defs.close() |
paul@126 | 340 | f_decls.close() |
paul@126 | 341 | f_signatures.close() |
paul@126 | 342 | f_code.close() |
paul@126 | 343 | |
paul@136 | 344 | def make_literal_constant(self, f_decls, f_defs, n, value): |
paul@136 | 345 | |
paul@136 | 346 | """ |
paul@136 | 347 | Write literal constant details to 'f_decls' (to declare a structure) and |
paul@136 | 348 | to 'f_defs' (to define the contents) for the constant with the number |
paul@136 | 349 | 'n' with the given literal 'value'. |
paul@136 | 350 | """ |
paul@136 | 351 | |
paul@136 | 352 | const_path = encode_literal_constant(n) |
paul@136 | 353 | structure_name = encode_literal_reference(n) |
paul@136 | 354 | |
paul@136 | 355 | # NOTE: This makes assumptions about the __builtins__ structure. |
paul@136 | 356 | |
paul@136 | 357 | typename = value.__class__.__name__ |
paul@136 | 358 | ref = Reference("<instance>", "__builtins__.%s.%s" % (typename, typename)) |
paul@136 | 359 | |
paul@136 | 360 | self.make_constant(f_decls, f_defs, ref, const_path, structure_name, value) |
paul@136 | 361 | |
paul@136 | 362 | def make_predefined_constant(self, f_decls, f_defs, path, name): |
paul@136 | 363 | |
paul@136 | 364 | """ |
paul@136 | 365 | Write predefined constant details to 'f_decls' (to declare a structure) |
paul@136 | 366 | and to 'f_defs' (to define the contents) for the constant located in |
paul@136 | 367 | 'path' with the given 'name'. |
paul@136 | 368 | """ |
paul@136 | 369 | |
paul@136 | 370 | # Determine the details of the constant. |
paul@136 | 371 | |
paul@136 | 372 | attr_path = "%s.%s" % (path, name) |
paul@136 | 373 | structure_name = encode_predefined_reference(attr_path) |
paul@136 | 374 | ref = self.importer.get_object(attr_path) |
paul@136 | 375 | |
paul@136 | 376 | self.make_constant(f_decls, f_defs, ref, attr_path, structure_name) |
paul@136 | 377 | |
paul@136 | 378 | def make_constant(self, f_decls, f_defs, ref, const_path, structure_name, data=None): |
paul@136 | 379 | |
paul@136 | 380 | """ |
paul@136 | 381 | Write constant details to 'f_decls' (to declare a structure) and to |
paul@136 | 382 | 'f_defs' (to define the contents) for the constant described by 'ref' |
paul@136 | 383 | having the given 'path' and 'structure_name' (for the constant structure |
paul@136 | 384 | itself). |
paul@136 | 385 | """ |
paul@136 | 386 | |
paul@136 | 387 | # Obtain the attributes. |
paul@136 | 388 | |
paul@136 | 389 | cls = ref.get_origin() |
paul@136 | 390 | indexes = self.optimiser.attr_table[ref] |
paul@136 | 391 | attrnames = self.get_attribute_names(indexes) |
paul@136 | 392 | attrs = self.get_instance_attributes(cls, attrnames) |
paul@136 | 393 | |
paul@136 | 394 | # Set the data, if provided. |
paul@136 | 395 | |
paul@136 | 396 | if data is not None: |
paul@136 | 397 | attrs["__data__"] = data |
paul@136 | 398 | |
paul@136 | 399 | # Define the structure details. An object is created for the constant, |
paul@136 | 400 | # but an attribute is provided, referring to the object, for access to |
paul@136 | 401 | # the constant in the program. |
paul@136 | 402 | |
paul@136 | 403 | structure = [] |
paul@150 | 404 | table_name = encode_tablename("<instance>", cls) |
paul@150 | 405 | structure_size = encode_size("<instance>", cls) |
paul@136 | 406 | self.populate_structure(ref, attrs, ref.get_kind(), structure) |
paul@136 | 407 | self.write_structure(f_decls, f_defs, structure_name, table_name, structure_size, structure) |
paul@136 | 408 | |
paul@136 | 409 | # Define a macro for the constant. |
paul@136 | 410 | |
paul@136 | 411 | attr_name = encode_path(const_path) |
paul@136 | 412 | print >>f_decls, "#define %s ((__attr) {&%s, &%s})" % (attr_name, structure_name, structure_name) |
paul@136 | 413 | |
paul@126 | 414 | def make_parameter_table(self, f_decls, f_defs, path, function_path): |
paul@126 | 415 | |
paul@126 | 416 | """ |
paul@126 | 417 | Write parameter table details to 'f_decls' (to declare a table) and to |
paul@126 | 418 | 'f_defs' (to define the contents) for the function with the given |
paul@126 | 419 | 'path', using 'function_path' to obtain the parameter details. The |
paul@126 | 420 | latter two arguments may differ when describing an instantiator using |
paul@126 | 421 | the details of an initialiser. |
paul@126 | 422 | """ |
paul@126 | 423 | |
paul@126 | 424 | table = [] |
paul@150 | 425 | table_name = encode_tablename("<function>", path) |
paul@126 | 426 | structure_size = encode_size("pmax", path) |
paul@126 | 427 | self.populate_parameter_table(function_path, table) |
paul@126 | 428 | self.write_parameter_table(f_decls, f_defs, table_name, structure_size, table) |
paul@126 | 429 | |
paul@126 | 430 | def write_size_constants(self, f_consts, size_prefix, sizes, padding): |
paul@126 | 431 | |
paul@126 | 432 | """ |
paul@126 | 433 | Write size constants to 'f_consts' for the given 'size_prefix', using |
paul@126 | 434 | the 'sizes' dictionary to populate the definition, adding the given |
paul@126 | 435 | 'padding' to the basic sizes. |
paul@126 | 436 | """ |
paul@126 | 437 | |
paul@126 | 438 | print >>f_consts, "enum %s {" % encode_size(size_prefix) |
paul@126 | 439 | first = True |
paul@126 | 440 | for path, size in sizes.items(): |
paul@126 | 441 | if not first: |
paul@126 | 442 | print >>f_consts, "," |
paul@126 | 443 | else: |
paul@126 | 444 | first = False |
paul@126 | 445 | f_consts.write(" %s = %d" % (encode_size(size_prefix, path), size + padding)) |
paul@126 | 446 | print >>f_consts, "\n };" |
paul@126 | 447 | |
paul@132 | 448 | def write_code_constants(self, f_consts, attrnames, locations, code_prefix, pos_prefix): |
paul@126 | 449 | |
paul@126 | 450 | """ |
paul@126 | 451 | Write code constants to 'f_consts' for the given 'attrnames' and |
paul@126 | 452 | attribute 'locations'. |
paul@126 | 453 | """ |
paul@126 | 454 | |
paul@132 | 455 | print >>f_consts, "enum %s {" % encode_symbol(code_prefix) |
paul@126 | 456 | first = True |
paul@126 | 457 | for i, attrname in enumerate(attrnames): |
paul@126 | 458 | if not first: |
paul@126 | 459 | print >>f_consts, "," |
paul@126 | 460 | else: |
paul@126 | 461 | first = False |
paul@132 | 462 | f_consts.write(" %s = %d" % (encode_symbol(code_prefix, attrname), i)) |
paul@126 | 463 | print >>f_consts, "\n };" |
paul@126 | 464 | |
paul@132 | 465 | print >>f_consts, "enum %s {" % encode_symbol(pos_prefix) |
paul@126 | 466 | first = True |
paul@126 | 467 | for i, attrnames in enumerate(locations): |
paul@126 | 468 | for attrname in attrnames: |
paul@126 | 469 | if not first: |
paul@126 | 470 | print >>f_consts, "," |
paul@126 | 471 | else: |
paul@126 | 472 | first = False |
paul@132 | 473 | f_consts.write(" %s = %d" % (encode_symbol(pos_prefix, attrname), i)) |
paul@126 | 474 | print >>f_consts, "\n };" |
paul@126 | 475 | |
paul@126 | 476 | def write_table(self, f_decls, f_defs, table_name, structure_size, table): |
paul@126 | 477 | |
paul@126 | 478 | """ |
paul@126 | 479 | Write the declarations to 'f_decls' and definitions to 'f_defs' for |
paul@126 | 480 | the object having the given 'table_name' and the given 'structure_size', |
paul@126 | 481 | with 'table' details used to populate the definition. |
paul@126 | 482 | """ |
paul@126 | 483 | |
paul@126 | 484 | print >>f_decls, "extern const __table %s;\n" % table_name |
paul@126 | 485 | |
paul@126 | 486 | # Write the corresponding definition. |
paul@126 | 487 | |
paul@126 | 488 | print >>f_defs, "const __table %s = {\n %s,\n {\n %s\n }\n };\n" % ( |
paul@126 | 489 | table_name, structure_size, |
paul@126 | 490 | ",\n ".join(table)) |
paul@126 | 491 | |
paul@126 | 492 | def write_parameter_table(self, f_decls, f_defs, table_name, structure_size, table): |
paul@126 | 493 | |
paul@126 | 494 | """ |
paul@126 | 495 | Write the declarations to 'f_decls' and definitions to 'f_defs' for |
paul@126 | 496 | the object having the given 'table_name' and the given 'structure_size', |
paul@126 | 497 | with 'table' details used to populate the definition. |
paul@126 | 498 | """ |
paul@126 | 499 | |
paul@126 | 500 | print >>f_decls, "extern const __ptable %s;\n" % table_name |
paul@126 | 501 | |
paul@126 | 502 | # Write the corresponding definition. |
paul@126 | 503 | |
paul@126 | 504 | print >>f_defs, "const __ptable %s = {\n %s,\n {\n %s\n }\n };\n" % ( |
paul@126 | 505 | table_name, structure_size, |
paul@126 | 506 | ",\n ".join([("{%s, %s}" % t) for t in table])) |
paul@126 | 507 | |
paul@136 | 508 | def write_instance_structure(self, f_decls, path, structure_size): |
paul@126 | 509 | |
paul@126 | 510 | """ |
paul@136 | 511 | Write a declaration to 'f_decls' for the object having the given 'path' |
paul@136 | 512 | and the given 'structure_size'. |
paul@126 | 513 | """ |
paul@126 | 514 | |
paul@126 | 515 | # Write an instance-specific type definition for instances of classes. |
paul@126 | 516 | # See: templates/types.h |
paul@126 | 517 | |
paul@126 | 518 | print >>f_decls, """\ |
paul@126 | 519 | typedef struct { |
paul@126 | 520 | const __table * table; |
paul@126 | 521 | unsigned int pos; |
paul@126 | 522 | __attr attrs[%s]; |
paul@126 | 523 | } %s; |
paul@136 | 524 | """ % (structure_size, encode_symbol("obj", path)) |
paul@136 | 525 | |
paul@136 | 526 | def write_structure(self, f_decls, f_defs, structure_name, table_name, structure_size, structure, path=None): |
paul@126 | 527 | |
paul@136 | 528 | """ |
paul@136 | 529 | Write the declarations to 'f_decls' and definitions to 'f_defs' for |
paul@136 | 530 | the object having the given 'structure_name', the given 'table_name', |
paul@136 | 531 | and the given 'structure_size', with 'structure' details used to |
paul@136 | 532 | populate the definition. |
paul@136 | 533 | """ |
paul@126 | 534 | |
paul@136 | 535 | if f_decls: |
paul@136 | 536 | print >>f_decls, "extern __obj %s;\n" % encode_path(structure_name) |
paul@136 | 537 | |
paul@136 | 538 | is_class = path and self.importer.get_object(path).has_kind("<class>") |
paul@132 | 539 | pos = is_class and encode_symbol("pos", encode_type_attribute(path)) or "0" |
paul@132 | 540 | |
paul@132 | 541 | print >>f_defs, """\ |
paul@132 | 542 | __obj %s = { |
paul@132 | 543 | &%s, |
paul@132 | 544 | %s, |
paul@132 | 545 | { |
paul@132 | 546 | %s |
paul@132 | 547 | }}; |
paul@132 | 548 | """ % ( |
paul@136 | 549 | encode_path(structure_name), table_name, pos, |
paul@126 | 550 | ",\n ".join(structure)) |
paul@126 | 551 | |
paul@132 | 552 | def get_argument_limits(self, path): |
paul@126 | 553 | |
paul@132 | 554 | """ |
paul@132 | 555 | Return the argument minimum and maximum for the callable at 'path', |
paul@132 | 556 | adding an argument position for a universal context. |
paul@132 | 557 | """ |
paul@132 | 558 | |
paul@126 | 559 | parameters = self.importer.function_parameters[path] |
paul@126 | 560 | defaults = self.importer.function_defaults.get(path) |
paul@132 | 561 | num_parameters = len(parameters) + 1 |
paul@132 | 562 | return num_parameters - (defaults and len(defaults) or 0), num_parameters |
paul@126 | 563 | |
paul@126 | 564 | def get_attribute_names(self, indexes): |
paul@126 | 565 | |
paul@126 | 566 | """ |
paul@126 | 567 | Given a list of attribute table 'indexes', return a list of attribute |
paul@126 | 568 | names. |
paul@126 | 569 | """ |
paul@126 | 570 | |
paul@126 | 571 | all_attrnames = self.optimiser.all_attrnames |
paul@126 | 572 | attrnames = [] |
paul@126 | 573 | for i in indexes: |
paul@126 | 574 | if i is None: |
paul@126 | 575 | attrnames.append(None) |
paul@126 | 576 | else: |
paul@126 | 577 | attrnames.append(all_attrnames[i]) |
paul@126 | 578 | return attrnames |
paul@126 | 579 | |
paul@126 | 580 | def get_static_attributes(self, kind, name, attrnames): |
paul@126 | 581 | |
paul@126 | 582 | """ |
paul@126 | 583 | Return a mapping of attribute names to paths for attributes belonging |
paul@126 | 584 | to objects of the given 'kind' (being "<class>" or "<module>") with |
paul@126 | 585 | the given 'name' and supporting the given 'attrnames'. |
paul@126 | 586 | """ |
paul@126 | 587 | |
paul@126 | 588 | attrs = {} |
paul@126 | 589 | |
paul@126 | 590 | for attrname in attrnames: |
paul@126 | 591 | if attrname is None: |
paul@126 | 592 | continue |
paul@126 | 593 | if kind == "<class>": |
paul@126 | 594 | path = self.importer.all_class_attrs[name][attrname] |
paul@126 | 595 | elif kind == "<module>": |
paul@126 | 596 | path = "%s.%s" % (name, attrname) |
paul@126 | 597 | else: |
paul@126 | 598 | continue |
paul@126 | 599 | |
paul@126 | 600 | # The module may be hidden. |
paul@126 | 601 | |
paul@126 | 602 | attr = self.importer.get_object(path) |
paul@126 | 603 | if not attr: |
paul@126 | 604 | module = self.importer.hidden.get(path) |
paul@126 | 605 | if module: |
paul@126 | 606 | attr = Reference(module.name, "<module>") |
paul@126 | 607 | attrs[attrname] = attr |
paul@126 | 608 | |
paul@126 | 609 | return attrs |
paul@126 | 610 | |
paul@126 | 611 | def get_instance_attributes(self, name, attrnames): |
paul@126 | 612 | |
paul@126 | 613 | """ |
paul@126 | 614 | Return a mapping of attribute names to references for attributes |
paul@126 | 615 | belonging to instances of the class with the given 'name', where the |
paul@126 | 616 | given 'attrnames' are supported. |
paul@126 | 617 | """ |
paul@126 | 618 | |
paul@126 | 619 | consts = self.importer.all_instance_attr_constants[name] |
paul@126 | 620 | attrs = {} |
paul@126 | 621 | for attrname in attrnames: |
paul@126 | 622 | if attrname is None: |
paul@126 | 623 | continue |
paul@126 | 624 | const = consts.get(attrname) |
paul@126 | 625 | attrs[attrname] = const or Reference("<var>", "%s.%s" % (name, attrname)) |
paul@126 | 626 | return attrs |
paul@126 | 627 | |
paul@126 | 628 | def populate_table(self, key, table): |
paul@126 | 629 | |
paul@126 | 630 | """ |
paul@126 | 631 | Traverse the attributes in the determined order for the structure having |
paul@126 | 632 | the given 'key', adding entries to the attribute 'table'. |
paul@126 | 633 | """ |
paul@126 | 634 | |
paul@126 | 635 | for attrname in self.optimiser.structures[key]: |
paul@126 | 636 | |
paul@126 | 637 | # Handle gaps in the structure. |
paul@126 | 638 | |
paul@126 | 639 | if attrname is None: |
paul@126 | 640 | table.append("0") |
paul@126 | 641 | else: |
paul@126 | 642 | table.append(encode_symbol("code", attrname)) |
paul@126 | 643 | |
paul@126 | 644 | def populate_parameter_table(self, key, table): |
paul@126 | 645 | |
paul@126 | 646 | """ |
paul@126 | 647 | Traverse the parameters in the determined order for the structure having |
paul@126 | 648 | the given 'key', adding entries to the attribute 'table'. |
paul@126 | 649 | """ |
paul@126 | 650 | |
paul@126 | 651 | for value in self.optimiser.parameters[key]: |
paul@126 | 652 | |
paul@126 | 653 | # Handle gaps in the structure. |
paul@126 | 654 | |
paul@126 | 655 | if value is None: |
paul@126 | 656 | table.append(("0", "0")) |
paul@126 | 657 | else: |
paul@126 | 658 | name, pos = value |
paul@126 | 659 | table.append((encode_symbol("pcode", name), pos)) |
paul@126 | 660 | |
paul@126 | 661 | def populate_function(self, path, function_instance_attrs, unbound=False): |
paul@126 | 662 | |
paul@126 | 663 | """ |
paul@126 | 664 | Populate a structure for the function with the given 'path'. The given |
paul@126 | 665 | 'attrs' provide the instance attributes, and if 'unbound' is set to a |
paul@126 | 666 | true value, an unbound method structure is produced (as opposed to a |
paul@126 | 667 | callable bound method structure). |
paul@126 | 668 | """ |
paul@126 | 669 | |
paul@126 | 670 | cls = self.function_type |
paul@126 | 671 | structure = [] |
paul@126 | 672 | self.populate_structure(Reference("<instance>", cls), function_instance_attrs, "<instance>", structure, unbound) |
paul@126 | 673 | |
paul@126 | 674 | # Append default members. |
paul@126 | 675 | |
paul@126 | 676 | self.append_defaults(path, structure) |
paul@126 | 677 | return structure |
paul@126 | 678 | |
paul@126 | 679 | def populate_structure(self, ref, attrs, kind, structure, unbound=False): |
paul@126 | 680 | |
paul@126 | 681 | """ |
paul@126 | 682 | Traverse the attributes in the determined order for the structure having |
paul@126 | 683 | the given 'ref' whose members are provided by the 'attrs' mapping, in a |
paul@126 | 684 | structure of the given 'kind', adding entries to the object 'structure'. |
paul@126 | 685 | If 'unbound' is set to a true value, an unbound method function pointer |
paul@126 | 686 | will be employed, with a reference to the bound method incorporated into |
paul@126 | 687 | the special __fn__ attribute. |
paul@126 | 688 | """ |
paul@126 | 689 | |
paul@126 | 690 | origin = ref.get_origin() |
paul@126 | 691 | |
paul@126 | 692 | for attrname in self.optimiser.structures[ref]: |
paul@126 | 693 | |
paul@126 | 694 | # Handle gaps in the structure. |
paul@126 | 695 | |
paul@126 | 696 | if attrname is None: |
paul@126 | 697 | structure.append("{0, 0}") |
paul@126 | 698 | |
paul@126 | 699 | # Handle non-constant and constant members. |
paul@126 | 700 | |
paul@126 | 701 | else: |
paul@126 | 702 | attr = attrs[attrname] |
paul@126 | 703 | |
paul@136 | 704 | # Special function pointer member. |
paul@136 | 705 | |
paul@126 | 706 | if attrname == "__fn__": |
paul@126 | 707 | |
paul@126 | 708 | # Provide bound method references and the unbound function |
paul@126 | 709 | # pointer if populating methods in a class. |
paul@126 | 710 | |
paul@126 | 711 | bound_attr = None |
paul@126 | 712 | |
paul@126 | 713 | # Classes offer instantiators. |
paul@126 | 714 | |
paul@126 | 715 | if kind == "<class>": |
paul@126 | 716 | attr = encode_instantiator_pointer(attr) |
paul@126 | 717 | |
paul@126 | 718 | # Methods offers references to bound versions and an unbound |
paul@126 | 719 | # method function. |
paul@126 | 720 | |
paul@126 | 721 | elif unbound: |
paul@126 | 722 | bound_attr = encode_bound_reference(attr) |
paul@126 | 723 | attr = "__unbound_method" |
paul@126 | 724 | |
paul@126 | 725 | # Other functions just offer function pointers. |
paul@126 | 726 | |
paul@126 | 727 | else: |
paul@126 | 728 | attr = encode_function_pointer(attr) |
paul@126 | 729 | |
paul@132 | 730 | structure.append("{%s, .fn=%s}" % (bound_attr and ".b=&%s" % bound_attr or "0", attr)) |
paul@126 | 731 | continue |
paul@126 | 732 | |
paul@136 | 733 | # Special argument specification member. |
paul@136 | 734 | |
paul@126 | 735 | elif attrname == "__args__": |
paul@150 | 736 | structure.append("{.min=%s, .ptable=&%s}" % (attr, encode_tablename("<function>", origin))) |
paul@126 | 737 | continue |
paul@126 | 738 | |
paul@136 | 739 | # Special internal data member. |
paul@136 | 740 | |
paul@136 | 741 | elif attrname == "__data__": |
paul@136 | 742 | structure.append("{0, .%s=%s}" % (encode_literal_constant_member(attr), |
paul@136 | 743 | encode_literal_constant_value(attr))) |
paul@136 | 744 | continue |
paul@136 | 745 | |
paul@126 | 746 | structure.append(self.encode_member(origin, attrname, attr, kind)) |
paul@126 | 747 | |
paul@126 | 748 | def encode_member(self, path, name, ref, structure_type): |
paul@126 | 749 | |
paul@126 | 750 | """ |
paul@126 | 751 | Encode within the structure provided by 'path', the member whose 'name' |
paul@126 | 752 | provides 'ref', within the given 'structure_type'. |
paul@126 | 753 | """ |
paul@126 | 754 | |
paul@126 | 755 | kind = ref.get_kind() |
paul@126 | 756 | origin = ref.get_origin() |
paul@126 | 757 | |
paul@126 | 758 | # References to constant literals. |
paul@126 | 759 | |
paul@126 | 760 | if kind == "<instance>": |
paul@126 | 761 | attr_path = "%s.%s" % (path, name) |
paul@126 | 762 | |
paul@126 | 763 | # Obtain a constant value directly assigned to the attribute. |
paul@126 | 764 | |
paul@126 | 765 | if self.optimiser.constant_numbers.has_key(attr_path): |
paul@126 | 766 | constant_number = self.optimiser.constant_numbers[attr_path] |
paul@126 | 767 | constant_value = "const%d" % constant_number |
paul@126 | 768 | return "{&%s, &%s} /* %s */" % (constant_value, constant_value, name) |
paul@126 | 769 | |
paul@136 | 770 | # Predefined constant references. |
paul@136 | 771 | |
paul@136 | 772 | if (path, name) in self.predefined_constant_members: |
paul@136 | 773 | attr_path = encode_predefined_reference("%s.%s" % (path, name)) |
paul@136 | 774 | return "{&%s, &%s} /* %s */" % (attr_path, attr_path, name) |
paul@136 | 775 | |
paul@126 | 776 | # General undetermined members. |
paul@126 | 777 | |
paul@126 | 778 | if kind in ("<var>", "<instance>"): |
paul@126 | 779 | return "{0, 0} /* %s */" % name |
paul@126 | 780 | |
paul@126 | 781 | # Set the context depending on the kind of attribute. |
paul@139 | 782 | # For methods: {&<parent>, &<attr>} |
paul@126 | 783 | # For other attributes: {&<attr>, &<attr>} |
paul@126 | 784 | |
paul@126 | 785 | else: |
paul@139 | 786 | if kind == "<function>" and structure_type == "<class>": |
paul@139 | 787 | parent = origin.rsplit(".", 1)[0] |
paul@139 | 788 | context = "&%s" % encode_path(parent) |
paul@139 | 789 | elif kind == "<instance>": |
paul@139 | 790 | context = "&%s" % encode_path(origin) |
paul@139 | 791 | else: |
paul@139 | 792 | context = "0" |
paul@126 | 793 | return "{%s, &%s}" % (context, encode_path(origin)) |
paul@126 | 794 | |
paul@126 | 795 | def append_defaults(self, path, structure): |
paul@126 | 796 | |
paul@126 | 797 | """ |
paul@126 | 798 | For the given 'path', append default parameter members to the given |
paul@126 | 799 | 'structure'. |
paul@126 | 800 | """ |
paul@126 | 801 | |
paul@126 | 802 | for name, default in self.importer.function_defaults.get(path): |
paul@126 | 803 | structure.append(self.encode_member(path, name, default, "<instance>")) |
paul@126 | 804 | |
paul@159 | 805 | def write_instantiator(self, f_code, f_signatures, path, init_ref): |
paul@126 | 806 | |
paul@126 | 807 | """ |
paul@159 | 808 | Write an instantiator to 'f_code', with a signature to 'f_signatures', |
paul@159 | 809 | for instances of the class with the given 'path', with 'init_ref' as the |
paul@159 | 810 | initialiser function reference. |
paul@126 | 811 | |
paul@126 | 812 | NOTE: This also needs to initialise any __fn__ and __args__ members |
paul@126 | 813 | NOTE: where __call__ is provided by the class. |
paul@126 | 814 | """ |
paul@126 | 815 | |
paul@132 | 816 | parameters = self.importer.function_parameters[init_ref.get_origin()] |
paul@126 | 817 | |
paul@126 | 818 | print >>f_code, """\ |
paul@132 | 819 | __attr %s(__attr __args[]) |
paul@126 | 820 | { |
paul@159 | 821 | /* Allocate the structure. */ |
paul@132 | 822 | __args[0] = __new(&%s, &%s, sizeof(%s)); |
paul@159 | 823 | |
paul@159 | 824 | /* Call the initialiser. */ |
paul@146 | 825 | %s(__args); |
paul@159 | 826 | |
paul@159 | 827 | /* Return the allocated object details. */ |
paul@132 | 828 | return __args[0]; |
paul@126 | 829 | } |
paul@126 | 830 | """ % ( |
paul@126 | 831 | encode_instantiator_pointer(path), |
paul@150 | 832 | encode_tablename("<instance>", path), |
paul@150 | 833 | encode_path(path), |
paul@150 | 834 | encode_symbol("obj", path), |
paul@126 | 835 | encode_function_pointer(init_ref.get_origin()) |
paul@126 | 836 | ) |
paul@126 | 837 | |
paul@159 | 838 | print >>f_signatures, "__attr %s(__attr[]);" % encode_instantiator_pointer(path) |
paul@159 | 839 | |
paul@159 | 840 | # Write additional literal instantiators. These do not call the |
paul@159 | 841 | # initialisers but instead populate the structures directly. |
paul@159 | 842 | |
paul@159 | 843 | if path in self.literal_instantiator_types: |
paul@159 | 844 | print >>f_code, """\ |
paul@159 | 845 | __attr %s(__attr __args[], unsigned int number) |
paul@159 | 846 | { |
paul@159 | 847 | __attr data; |
paul@159 | 848 | |
paul@159 | 849 | /* Allocate the structure. */ |
paul@159 | 850 | __args[0] = __new(&%s, &%s, sizeof(%s)); |
paul@159 | 851 | |
paul@159 | 852 | /* Allocate a structure for the data. */ |
paul@159 | 853 | data = __newdata(__args, number); |
paul@159 | 854 | |
paul@159 | 855 | /* Store a reference to the data in the object's __data__ attribute. */ |
paul@159 | 856 | __store_via_object(__args[0].value, %s, data); |
paul@159 | 857 | |
paul@159 | 858 | /* Return the allocated object details. */ |
paul@159 | 859 | return __args[0]; |
paul@159 | 860 | } |
paul@159 | 861 | """ % ( |
paul@159 | 862 | encode_literal_instantiator(path), |
paul@159 | 863 | encode_tablename("<instance>", path), |
paul@159 | 864 | encode_path(path), |
paul@159 | 865 | encode_symbol("obj", path), |
paul@159 | 866 | encode_symbol("pos", "__data__") |
paul@159 | 867 | ) |
paul@159 | 868 | |
paul@159 | 869 | print >>f_signatures, "__attr %s(__attr[], unsigned int);" % encode_literal_instantiator(path) |
paul@159 | 870 | |
paul@146 | 871 | def write_main_program(self, f_code, f_signatures): |
paul@146 | 872 | |
paul@146 | 873 | """ |
paul@146 | 874 | Write the main program to 'f_code', invoking the program's modules. Also |
paul@146 | 875 | write declarations for module main functions to 'f_signatures'. |
paul@146 | 876 | """ |
paul@146 | 877 | |
paul@146 | 878 | print >>f_code, """\ |
paul@146 | 879 | int main(int argc, char *argv[]) |
paul@159 | 880 | { |
paul@159 | 881 | __Try |
paul@159 | 882 | {""" |
paul@146 | 883 | |
paul@146 | 884 | for name in self.importer.modules.keys(): |
paul@146 | 885 | function_name = "__main_%s" % encode_path(name) |
paul@146 | 886 | print >>f_signatures, "void %s();" % function_name |
paul@146 | 887 | |
paul@146 | 888 | # Emit the main module's function last. |
paul@161 | 889 | # Omit the native module. |
paul@146 | 890 | |
paul@161 | 891 | if name not in ("__main__", "native"): |
paul@146 | 892 | print >>f_code, """\ |
paul@165 | 893 | %s();""" % function_name |
paul@146 | 894 | |
paul@146 | 895 | print >>f_code, """\ |
paul@159 | 896 | __main___main__(); |
paul@159 | 897 | return 0; |
paul@159 | 898 | } |
paul@159 | 899 | __Catch_anonymous |
paul@159 | 900 | { |
paul@159 | 901 | fprintf(stderr, "Program terminated due to exception.\\n"); |
paul@159 | 902 | return 1; |
paul@159 | 903 | } |
paul@146 | 904 | } |
paul@146 | 905 | """ |
paul@146 | 906 | |
paul@126 | 907 | # vim: tabstop=4 expandtab shiftwidth=4 |