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@1041 | 6 | Copyright (C) 2015-2019, 2021, 2023, 2024 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@609 | 22 | from common import CommonOutput, copy |
paul@623 | 23 | from encoders import encode_code, \ |
paul@623 | 24 | encode_function_pointer, \ |
paul@136 | 25 | encode_instantiator_pointer, \ |
paul@136 | 26 | encode_literal_constant, encode_literal_constant_member, \ |
paul@378 | 27 | encode_literal_constant_size, encode_literal_constant_value, \ |
paul@786 | 28 | encode_literal_reference, \ |
paul@623 | 29 | encode_path, encode_pcode, encode_pos, encode_ppos, \ |
paul@150 | 30 | encode_predefined_reference, encode_size, \ |
paul@150 | 31 | encode_symbol, encode_tablename, \ |
paul@850 | 32 | encode_trailing_area, \ |
paul@318 | 33 | encode_type_attribute, decode_type_attribute, \ |
paul@318 | 34 | is_type_attribute |
paul@609 | 35 | from os import listdir, mkdir, remove |
paul@609 | 36 | from os.path import exists, isdir, join, split, splitext |
paul@126 | 37 | from referencing import Reference |
paul@126 | 38 | |
paul@126 | 39 | class Generator(CommonOutput): |
paul@126 | 40 | |
paul@126 | 41 | "A code generator." |
paul@126 | 42 | |
paul@971 | 43 | # Generated functions employ result target and context parameters. |
paul@971 | 44 | |
paul@971 | 45 | extra_args = 2 |
paul@971 | 46 | |
paul@274 | 47 | # NOTE: These must be synchronised with the library. |
paul@274 | 48 | |
paul@778 | 49 | dict_type = "__builtins__.dict.dict" |
paul@850 | 50 | float_type = "__builtins__.float.float" |
paul@126 | 51 | function_type = "__builtins__.core.function" |
paul@758 | 52 | int_type = "__builtins__.int.int" |
paul@778 | 53 | list_type = "__builtins__.list.list" |
paul@400 | 54 | none_type = "__builtins__.none.NoneType" |
paul@938 | 55 | string_type = "__builtins__.str.str" |
paul@778 | 56 | tuple_type = "__builtins__.tuple.tuple" |
paul@274 | 57 | type_type = "__builtins__.core.type" |
paul@934 | 58 | unicode_type = "__builtins__.unicode.unicode" |
paul@400 | 59 | |
paul@400 | 60 | none_value = "__builtins__.none.None" |
paul@158 | 61 | |
paul@136 | 62 | predefined_constant_members = ( |
paul@158 | 63 | ("__builtins__.boolean", "False"), |
paul@158 | 64 | ("__builtins__.boolean", "True"), |
paul@136 | 65 | ("__builtins__.none", "None"), |
paul@136 | 66 | ("__builtins__.notimplemented", "NotImplemented"), |
paul@136 | 67 | ) |
paul@136 | 68 | |
paul@778 | 69 | literal_instantiator_types = ( |
paul@778 | 70 | dict_type, list_type, tuple_type |
paul@283 | 71 | ) |
paul@283 | 72 | |
paul@1041 | 73 | # Copiable types. |
paul@1041 | 74 | |
paul@1041 | 75 | copiable_types = [ |
paul@1041 | 76 | "__builtins__.core.wrapper", |
paul@1041 | 77 | float_type, |
paul@1041 | 78 | ] |
paul@1041 | 79 | |
paul@850 | 80 | # Data types with a trailing data member of the given native types. |
paul@850 | 81 | |
paul@850 | 82 | trailing_data_types = { |
paul@850 | 83 | float_type : "double", |
paul@850 | 84 | } |
paul@850 | 85 | |
paul@126 | 86 | def __init__(self, importer, optimiser, output): |
paul@357 | 87 | |
paul@357 | 88 | """ |
paul@357 | 89 | Initialise the generator with the given 'importer', 'optimiser' and |
paul@357 | 90 | 'output' directory. |
paul@357 | 91 | """ |
paul@357 | 92 | |
paul@126 | 93 | self.importer = importer |
paul@126 | 94 | self.optimiser = optimiser |
paul@126 | 95 | self.output = output |
paul@126 | 96 | |
paul@649 | 97 | # The special instance indicator. |
paul@649 | 98 | |
paul@649 | 99 | self.instancepos = self.optimiser.attr_locations["__class__"] |
paul@649 | 100 | |
paul@649 | 101 | def to_output(self, reset=False, debug=False, gc_sections=False): |
paul@126 | 102 | |
paul@126 | 103 | "Write the generated code." |
paul@126 | 104 | |
paul@617 | 105 | self.check_output("debug=%r gc_sections=%r" % (debug, gc_sections)) |
paul@126 | 106 | self.write_structures() |
paul@614 | 107 | self.write_scripts(debug, gc_sections) |
paul@649 | 108 | self.copy_templates(reset) |
paul@126 | 109 | |
paul@649 | 110 | def copy_templates(self, reset=False): |
paul@126 | 111 | |
paul@126 | 112 | "Copy template files to the generated output directory." |
paul@126 | 113 | |
paul@126 | 114 | templates = join(split(__file__)[0], "templates") |
paul@126 | 115 | |
paul@649 | 116 | only_if_newer = not reset |
paul@649 | 117 | |
paul@126 | 118 | for filename in listdir(templates): |
paul@183 | 119 | target = self.output |
paul@354 | 120 | pathname = join(templates, filename) |
paul@354 | 121 | |
paul@354 | 122 | # Copy files into the target directory. |
paul@354 | 123 | |
paul@354 | 124 | if not isdir(pathname): |
paul@649 | 125 | copy(pathname, target, only_if_newer) |
paul@354 | 126 | |
paul@354 | 127 | # Copy directories (such as the native code directory). |
paul@354 | 128 | |
paul@354 | 129 | else: |
paul@354 | 130 | target = join(self.output, filename) |
paul@354 | 131 | |
paul@354 | 132 | if not exists(target): |
paul@354 | 133 | mkdir(target) |
paul@354 | 134 | |
paul@609 | 135 | existing = listdir(target) |
paul@609 | 136 | needed = listdir(pathname) |
paul@609 | 137 | |
paul@609 | 138 | # Determine which files are superfluous by comparing their |
paul@609 | 139 | # basenames (without extensions) to those of the needed |
paul@609 | 140 | # filenames. This should preserve object files for needed source |
paul@609 | 141 | # files, only discarding completely superfluous files from the |
paul@609 | 142 | # target directory. |
paul@609 | 143 | |
paul@609 | 144 | needed_basenames = set() |
paul@609 | 145 | for filename in needed: |
paul@609 | 146 | needed_basenames.add(splitext(filename)[0]) |
paul@609 | 147 | |
paul@609 | 148 | superfluous = [] |
paul@609 | 149 | for filename in existing: |
paul@609 | 150 | if splitext(filename)[0] not in needed_basenames: |
paul@609 | 151 | superfluous.append(filename) |
paul@609 | 152 | |
paul@609 | 153 | # Copy needed files. |
paul@609 | 154 | |
paul@609 | 155 | for filename in needed: |
paul@649 | 156 | copy(join(pathname, filename), target, only_if_newer) |
paul@126 | 157 | |
paul@609 | 158 | # Remove superfluous files. |
paul@609 | 159 | |
paul@609 | 160 | for filename in superfluous: |
paul@609 | 161 | remove(join(target, filename)) |
paul@609 | 162 | |
paul@126 | 163 | def write_structures(self): |
paul@126 | 164 | |
paul@126 | 165 | "Write structures used by the program." |
paul@126 | 166 | |
paul@126 | 167 | f_consts = open(join(self.output, "progconsts.h"), "w") |
paul@126 | 168 | f_defs = open(join(self.output, "progtypes.c"), "w") |
paul@126 | 169 | f_decls = open(join(self.output, "progtypes.h"), "w") |
paul@126 | 170 | f_signatures = open(join(self.output, "main.h"), "w") |
paul@126 | 171 | f_code = open(join(self.output, "main.c"), "w") |
paul@664 | 172 | f_calls = open(join(self.output, "calls.c"), "w") |
paul@664 | 173 | f_call_macros = open(join(self.output, "calls.h"), "w") |
paul@126 | 174 | |
paul@126 | 175 | try: |
paul@126 | 176 | # Output boilerplate. |
paul@126 | 177 | |
paul@126 | 178 | print >>f_consts, """\ |
paul@126 | 179 | #ifndef __PROGCONSTS_H__ |
paul@126 | 180 | #define __PROGCONSTS_H__ |
paul@623 | 181 | |
paul@623 | 182 | #include "types.h" |
paul@126 | 183 | """ |
paul@126 | 184 | print >>f_decls, """\ |
paul@126 | 185 | #ifndef __PROGTYPES_H__ |
paul@126 | 186 | #define __PROGTYPES_H__ |
paul@126 | 187 | |
paul@126 | 188 | #include "progconsts.h" |
paul@126 | 189 | #include "types.h" |
paul@126 | 190 | """ |
paul@126 | 191 | print >>f_defs, """\ |
paul@126 | 192 | #include "progtypes.h" |
paul@132 | 193 | #include "progops.h" |
paul@126 | 194 | #include "main.h" |
paul@126 | 195 | """ |
paul@126 | 196 | print >>f_signatures, """\ |
paul@126 | 197 | #ifndef __MAIN_H__ |
paul@126 | 198 | #define __MAIN_H__ |
paul@126 | 199 | |
paul@126 | 200 | #include "types.h" |
paul@126 | 201 | """ |
paul@126 | 202 | print >>f_code, """\ |
paul@126 | 203 | #include <string.h> |
paul@159 | 204 | #include <stdio.h> |
paul@434 | 205 | #include "gc.h" |
paul@872 | 206 | #include "signals.h" |
paul@126 | 207 | #include "types.h" |
paul@159 | 208 | #include "exceptions.h" |
paul@126 | 209 | #include "ops.h" |
paul@126 | 210 | #include "progconsts.h" |
paul@126 | 211 | #include "progtypes.h" |
paul@490 | 212 | #include "main.h" |
paul@126 | 213 | #include "progops.h" |
paul@664 | 214 | #include "calls.h" |
paul@664 | 215 | """ |
paul@664 | 216 | |
paul@664 | 217 | print >>f_call_macros, """\ |
paul@664 | 218 | #ifndef __CALLS_H__ |
paul@664 | 219 | #define __CALLS_H__ |
paul@664 | 220 | |
paul@664 | 221 | #include "types.h" |
paul@126 | 222 | """ |
paul@126 | 223 | |
paul@126 | 224 | # Generate table and structure data. |
paul@126 | 225 | |
paul@126 | 226 | function_instance_attrs = None |
paul@847 | 227 | objects = self.optimiser.all_attrs.items() |
paul@126 | 228 | objects.sort() |
paul@126 | 229 | |
paul@357 | 230 | self.callables = {} |
paul@357 | 231 | |
paul@847 | 232 | for ref, attrnames in objects: |
paul@126 | 233 | kind = ref.get_kind() |
paul@126 | 234 | path = ref.get_origin() |
paul@150 | 235 | table_name = encode_tablename(kind, path) |
paul@150 | 236 | structure_size = encode_size(kind, path) |
paul@126 | 237 | |
paul@126 | 238 | # Generate structures for classes and modules. |
paul@126 | 239 | |
paul@126 | 240 | if kind != "<instance>": |
paul@126 | 241 | structure = [] |
paul@850 | 242 | trailing = [] |
paul@126 | 243 | attrs = self.get_static_attributes(kind, path, attrnames) |
paul@126 | 244 | |
paul@126 | 245 | # Set a special instantiator on the class. |
paul@126 | 246 | |
paul@126 | 247 | if kind == "<class>": |
paul@126 | 248 | |
paul@126 | 249 | # Write instantiator declarations based on the |
paul@126 | 250 | # applicable initialiser. |
paul@126 | 251 | |
paul@126 | 252 | init_ref = attrs["__init__"] |
paul@126 | 253 | |
paul@126 | 254 | # Write instantiator definitions. |
paul@126 | 255 | |
paul@159 | 256 | self.write_instantiator(f_code, f_signatures, path, init_ref) |
paul@126 | 257 | |
paul@357 | 258 | # Record the callable for parameter table generation. |
paul@357 | 259 | |
paul@357 | 260 | self.callables[path] = init_ref.get_origin() |
paul@126 | 261 | |
paul@357 | 262 | # Define special attributes. |
paul@357 | 263 | |
paul@357 | 264 | attrs["__fn__"] = path |
paul@579 | 265 | attrs["__args__"] = path |
paul@126 | 266 | |
paul@126 | 267 | self.populate_structure(Reference(kind, path), attrs, kind, structure) |
paul@850 | 268 | self.populate_trailing(Reference(kind, path), attrs, trailing) |
paul@136 | 269 | |
paul@136 | 270 | if kind == "<class>": |
paul@884 | 271 | self.write_instance_structure(f_decls, path) |
paul@136 | 272 | |
paul@850 | 273 | self.write_structure(f_decls, f_defs, path, table_name, |
paul@850 | 274 | structure, trailing, ref) |
paul@126 | 275 | |
paul@126 | 276 | # Record function instance details for function generation below. |
paul@126 | 277 | |
paul@126 | 278 | else: |
paul@126 | 279 | attrs = self.get_instance_attributes(path, attrnames) |
paul@126 | 280 | if path == self.function_type: |
paul@126 | 281 | function_instance_attrs = attrs |
paul@126 | 282 | |
paul@357 | 283 | # Record the callable for parameter table generation. |
paul@357 | 284 | |
paul@357 | 285 | self.callables[path] = path |
paul@357 | 286 | |
paul@126 | 287 | # Write a table for all objects. |
paul@126 | 288 | |
paul@126 | 289 | table = [] |
paul@126 | 290 | self.populate_table(Reference(kind, path), table) |
paul@126 | 291 | self.write_table(f_decls, f_defs, table_name, structure_size, table) |
paul@126 | 292 | |
paul@126 | 293 | # Generate function instances. |
paul@126 | 294 | |
paul@195 | 295 | functions = self.importer.function_parameters.keys() |
paul@126 | 296 | functions.sort() |
paul@211 | 297 | extra_function_instances = [] |
paul@126 | 298 | |
paul@126 | 299 | for path in functions: |
paul@195 | 300 | |
paul@195 | 301 | # Instantiators are generated above. |
paul@195 | 302 | |
paul@195 | 303 | if self.importer.classes.has_key(path) or not self.importer.get_object(path): |
paul@195 | 304 | continue |
paul@195 | 305 | |
paul@357 | 306 | # Record the callable for parameter table generation. |
paul@357 | 307 | |
paul@357 | 308 | self.callables[path] = path |
paul@357 | 309 | |
paul@357 | 310 | # Define the structure details. |
paul@357 | 311 | |
paul@126 | 312 | cls = self.function_type |
paul@150 | 313 | table_name = encode_tablename("<instance>", cls) |
paul@126 | 314 | |
paul@126 | 315 | # Set a special callable attribute on the instance. |
paul@126 | 316 | |
paul@126 | 317 | function_instance_attrs["__fn__"] = path |
paul@579 | 318 | function_instance_attrs["__args__"] = path |
paul@126 | 319 | |
paul@575 | 320 | structure = self.populate_function(path, function_instance_attrs) |
paul@850 | 321 | self.write_structure(f_decls, f_defs, path, table_name, structure, [], Reference("<function>", path)) |
paul@126 | 322 | |
paul@154 | 323 | # Functions with defaults need to declare instance structures. |
paul@154 | 324 | |
paul@154 | 325 | if self.importer.function_defaults.get(path): |
paul@884 | 326 | self.write_instance_structure(f_decls, path) |
paul@211 | 327 | extra_function_instances.append(path) |
paul@154 | 328 | |
paul@971 | 329 | # Write function declarations, including result target and |
paul@971 | 330 | # context parameters. |
paul@971 | 331 | # Signature: __attr <name>(__attr, __attr, ...); |
paul@126 | 332 | |
paul@664 | 333 | parameters = self.importer.function_parameters[path] |
paul@971 | 334 | l = ["__attr"] * (len(parameters) + self.extra_args) |
paul@664 | 335 | print >>f_signatures, "__attr %s(%s);" % (encode_function_pointer(path), ", ".join(l)) |
paul@126 | 336 | |
paul@579 | 337 | # Generate parameter table size data. |
paul@579 | 338 | |
paul@579 | 339 | min_parameters = {} |
paul@579 | 340 | max_parameters = {} |
paul@579 | 341 | size_parameters = {} |
paul@664 | 342 | all_max_parameters = set() |
paul@579 | 343 | |
paul@357 | 344 | # Consolidate parameter tables for instantiators and functions. |
paul@357 | 345 | |
paul@357 | 346 | parameter_tables = set() |
paul@126 | 347 | |
paul@357 | 348 | for path, function_path in self.callables.items(): |
paul@579 | 349 | argmin, argmax = self.get_argument_limits(function_path) |
paul@579 | 350 | |
paul@579 | 351 | # Obtain the parameter table members. |
paul@579 | 352 | |
paul@357 | 353 | parameters = self.optimiser.parameters[function_path] |
paul@357 | 354 | if not parameters: |
paul@357 | 355 | parameters = () |
paul@357 | 356 | else: |
paul@357 | 357 | parameters = tuple(parameters) |
paul@579 | 358 | |
paul@579 | 359 | # Define each table in terms of the members and the minimum |
paul@579 | 360 | # number of arguments. |
paul@579 | 361 | |
paul@579 | 362 | parameter_tables.add((argmin, parameters)) |
paul@579 | 363 | signature = self.get_parameter_signature(argmin, parameters) |
paul@579 | 364 | |
paul@579 | 365 | # Record the minimum number of arguments, the maximum number, |
paul@579 | 366 | # and the size of the table. |
paul@579 | 367 | |
paul@579 | 368 | min_parameters[signature] = argmin |
paul@579 | 369 | max_parameters[signature] = argmax |
paul@579 | 370 | size_parameters[signature] = len(parameters) |
paul@664 | 371 | all_max_parameters.add(argmax) |
paul@579 | 372 | |
paul@579 | 373 | self.write_size_constants(f_consts, "pmin", min_parameters, 0) |
paul@579 | 374 | self.write_size_constants(f_consts, "pmax", max_parameters, 0) |
paul@579 | 375 | self.write_size_constants(f_consts, "psize", size_parameters, 0) |
paul@357 | 376 | |
paul@357 | 377 | # Generate parameter tables for distinct function signatures. |
paul@357 | 378 | |
paul@579 | 379 | for argmin, parameters in parameter_tables: |
paul@579 | 380 | self.make_parameter_table(f_decls, f_defs, argmin, parameters) |
paul@126 | 381 | |
paul@136 | 382 | # Generate predefined constants. |
paul@136 | 383 | |
paul@136 | 384 | for path, name in self.predefined_constant_members: |
paul@136 | 385 | self.make_predefined_constant(f_decls, f_defs, path, name) |
paul@136 | 386 | |
paul@136 | 387 | # Generate literal constants. |
paul@136 | 388 | |
paul@397 | 389 | for constant, n in self.optimiser.constants.items(): |
paul@397 | 390 | self.make_literal_constant(f_decls, f_defs, n, constant) |
paul@136 | 391 | |
paul@758 | 392 | # Generate a common integer instance object, referenced when integer |
paul@758 | 393 | # attributes are accessed. |
paul@758 | 394 | |
paul@758 | 395 | self.make_common_integer(f_decls, f_defs) |
paul@758 | 396 | |
paul@146 | 397 | # Finish the main source file. |
paul@146 | 398 | |
paul@146 | 399 | self.write_main_program(f_code, f_signatures) |
paul@146 | 400 | |
paul@211 | 401 | # Record size information for certain function instances as well as |
paul@211 | 402 | # for classes, modules and other instances. |
paul@211 | 403 | |
paul@211 | 404 | size_tables = {} |
paul@211 | 405 | |
paul@211 | 406 | for kind in ["<class>", "<module>", "<instance>"]: |
paul@211 | 407 | size_tables[kind] = {} |
paul@211 | 408 | |
paul@211 | 409 | # Generate structure size data. |
paul@211 | 410 | |
paul@211 | 411 | for ref, structure in self.optimiser.structures.items(): |
paul@211 | 412 | size_tables[ref.get_kind()][ref.get_origin()] = len(structure) |
paul@211 | 413 | |
paul@211 | 414 | for path in extra_function_instances: |
paul@211 | 415 | defaults = self.importer.function_defaults[path] |
paul@211 | 416 | size_tables["<instance>"][path] = size_tables["<instance>"][self.function_type] + len(defaults) |
paul@211 | 417 | |
paul@211 | 418 | size_tables = size_tables.items() |
paul@211 | 419 | size_tables.sort() |
paul@211 | 420 | |
paul@211 | 421 | for kind, sizes in size_tables: |
paul@211 | 422 | self.write_size_constants(f_consts, kind, sizes, 0) |
paul@211 | 423 | |
paul@211 | 424 | # Generate parameter codes. |
paul@211 | 425 | |
paul@623 | 426 | self.write_code_constants(f_consts, self.optimiser.all_paramnames, |
paul@623 | 427 | self.optimiser.arg_locations, |
paul@623 | 428 | "pcode", "ppos", encode_pcode, encode_ppos) |
paul@211 | 429 | |
paul@211 | 430 | # Generate attribute codes. |
paul@211 | 431 | |
paul@623 | 432 | self.write_code_constants(f_consts, self.optimiser.all_attrnames, |
paul@623 | 433 | self.optimiser.locations, |
paul@623 | 434 | "code", "pos", encode_code, encode_pos) |
paul@211 | 435 | |
paul@850 | 436 | # Generate trailing data macros of the form... |
paul@850 | 437 | # #define __TRAILING_typename nativetype trailing; |
paul@850 | 438 | |
paul@850 | 439 | for name, member_type in self.trailing_data_types.items(): |
paul@850 | 440 | print >>f_consts, "#define %s %s trailing;" % (encode_symbol("TRAILING", name), member_type) |
paul@850 | 441 | |
paul@664 | 442 | # Generate macros for calls. |
paul@664 | 443 | |
paul@664 | 444 | all_max_parameters = list(all_max_parameters) |
paul@664 | 445 | all_max_parameters.sort() |
paul@664 | 446 | |
paul@664 | 447 | for argmax in all_max_parameters: |
paul@664 | 448 | l = [] |
paul@664 | 449 | argnum = 0 |
paul@664 | 450 | while argnum < argmax: |
paul@664 | 451 | l.append("ARGS[%d]" % argnum) |
paul@664 | 452 | argnum += 1 |
paul@664 | 453 | |
paul@664 | 454 | print >>f_call_macros, "#define __CALL%d(FN, ARGS) (FN(%s))" % (argmax, ", ".join(l)) |
paul@664 | 455 | |
paul@664 | 456 | # Generate a generic invocation function. |
paul@664 | 457 | |
paul@664 | 458 | print >>f_call_macros, "__attr __call_with_args(__attr (*fn)(), __attr args[], unsigned int n);" |
paul@664 | 459 | |
paul@664 | 460 | print >>f_calls, """\ |
paul@664 | 461 | #include "types.h" |
paul@664 | 462 | #include "calls.h" |
paul@664 | 463 | |
paul@664 | 464 | __attr __call_with_args(__attr (*fn)(), __attr args[], unsigned int n) |
paul@664 | 465 | { |
paul@664 | 466 | switch (n) |
paul@664 | 467 | {""" |
paul@664 | 468 | |
paul@664 | 469 | for argmax in all_max_parameters: |
paul@664 | 470 | print >>f_calls, """\ |
paul@664 | 471 | case %d: return __CALL%d(fn, args);""" % (argmax, argmax) |
paul@664 | 472 | |
paul@664 | 473 | print >>f_calls, """\ |
paul@664 | 474 | default: return __NULL; |
paul@664 | 475 | } |
paul@664 | 476 | }""" |
paul@664 | 477 | |
paul@126 | 478 | # Output more boilerplate. |
paul@126 | 479 | |
paul@126 | 480 | print >>f_consts, """\ |
paul@126 | 481 | |
paul@126 | 482 | #endif /* __PROGCONSTS_H__ */""" |
paul@126 | 483 | |
paul@126 | 484 | print >>f_decls, """\ |
paul@126 | 485 | |
paul@126 | 486 | #define __FUNCTION_TYPE %s |
paul@126 | 487 | #define __FUNCTION_INSTANCE_SIZE %s |
paul@274 | 488 | #define __TYPE_CLASS_TYPE %s |
paul@274 | 489 | #define __TYPE_CLASS_POS %s |
paul@274 | 490 | #define __TYPE_CLASS_CODE %s |
paul@126 | 491 | |
paul@126 | 492 | #endif /* __PROGTYPES_H__ */""" % ( |
paul@126 | 493 | encode_path(self.function_type), |
paul@233 | 494 | encode_size("<instance>", self.function_type), |
paul@274 | 495 | encode_path(self.type_type), |
paul@623 | 496 | encode_pos(encode_type_attribute(self.type_type)), |
paul@623 | 497 | encode_code(encode_type_attribute(self.type_type)), |
paul@126 | 498 | ) |
paul@126 | 499 | |
paul@126 | 500 | print >>f_signatures, """\ |
paul@126 | 501 | |
paul@126 | 502 | #endif /* __MAIN_H__ */""" |
paul@126 | 503 | |
paul@664 | 504 | print >>f_call_macros, """\ |
paul@664 | 505 | |
paul@664 | 506 | #endif /* __CALLS_H__ */""" |
paul@664 | 507 | |
paul@126 | 508 | finally: |
paul@126 | 509 | f_consts.close() |
paul@126 | 510 | f_defs.close() |
paul@126 | 511 | f_decls.close() |
paul@126 | 512 | f_signatures.close() |
paul@126 | 513 | f_code.close() |
paul@664 | 514 | f_calls.close() |
paul@664 | 515 | f_call_macros.close() |
paul@126 | 516 | |
paul@614 | 517 | def write_scripts(self, debug, gc_sections): |
paul@511 | 518 | |
paul@511 | 519 | "Write scripts used to build the program." |
paul@511 | 520 | |
paul@649 | 521 | # Options affect compiling and linking. |
paul@649 | 522 | |
paul@511 | 523 | f_options = open(join(self.output, "options.mk"), "w") |
paul@511 | 524 | try: |
paul@511 | 525 | if debug: |
paul@511 | 526 | print >>f_options, "CFLAGS = -g" |
paul@666 | 527 | else: |
paul@666 | 528 | print >>f_options, "CFLAGS = -O2" |
paul@511 | 529 | |
paul@614 | 530 | if gc_sections: |
paul@614 | 531 | print >>f_options, "include gc_sections.mk" |
paul@614 | 532 | |
paul@649 | 533 | finally: |
paul@649 | 534 | f_options.close() |
paul@649 | 535 | |
paul@649 | 536 | # Native and module definitions divide the program modules into native |
paul@649 | 537 | # and generated code. |
paul@649 | 538 | |
paul@649 | 539 | f_native = open(join(self.output, "native.mk"), "w") |
paul@649 | 540 | f_modules = open(join(self.output, "modules.mk"), "w") |
paul@649 | 541 | try: |
paul@539 | 542 | # Identify modules used by the program. |
paul@511 | 543 | |
paul@539 | 544 | native_modules = [join("native", "common.c")] |
paul@539 | 545 | modules = [] |
paul@511 | 546 | |
paul@511 | 547 | for name in self.importer.modules.keys(): |
paul@511 | 548 | parts = name.split(".", 1) |
paul@511 | 549 | |
paul@511 | 550 | # Identify source files to be built. |
paul@511 | 551 | |
paul@511 | 552 | if parts[0] == "native": |
paul@539 | 553 | native_modules.append(join("native", "%s.c" % parts[1])) |
paul@539 | 554 | else: |
paul@539 | 555 | modules.append(join("src", "%s.c" % name)) |
paul@511 | 556 | |
paul@511 | 557 | print >>f_native, "SRC =", " ".join(native_modules) |
paul@539 | 558 | print >>f_modules, "SRC +=", " ".join(modules) |
paul@511 | 559 | |
paul@511 | 560 | finally: |
paul@511 | 561 | f_native.close() |
paul@539 | 562 | f_modules.close() |
paul@649 | 563 | |
paul@649 | 564 | # Instance position configuration uses the position of the ubiquitous |
paul@649 | 565 | # __class__ attribute as a means of indicating that an object is an |
paul@649 | 566 | # instance. Classes employ special identifying attributes that are |
paul@649 | 567 | # positioned elsewhere and thus cannot be in the same location as the |
paul@649 | 568 | # __class__ attribute. |
paul@649 | 569 | |
paul@649 | 570 | f_instancepos = open(join(self.output, "instancepos.h"), "w") |
paul@649 | 571 | try: |
paul@649 | 572 | print >>f_instancepos, """\ |
paul@649 | 573 | #ifndef __INSTANCEPOS |
paul@649 | 574 | #define __INSTANCEPOS %d |
paul@649 | 575 | #endif |
paul@649 | 576 | """ % self.instancepos |
paul@649 | 577 | finally: |
paul@649 | 578 | f_instancepos.close() |
paul@511 | 579 | |
paul@397 | 580 | def make_literal_constant(self, f_decls, f_defs, n, constant): |
paul@136 | 581 | |
paul@136 | 582 | """ |
paul@136 | 583 | Write literal constant details to 'f_decls' (to declare a structure) and |
paul@136 | 584 | to 'f_defs' (to define the contents) for the constant with the number |
paul@397 | 585 | 'n' with the given 'constant'. |
paul@136 | 586 | """ |
paul@136 | 587 | |
paul@406 | 588 | value, value_type, encoding = constant |
paul@397 | 589 | |
paul@758 | 590 | # Do not generate individual integer constants. |
paul@758 | 591 | |
paul@758 | 592 | if value_type == self.int_type: |
paul@758 | 593 | return |
paul@758 | 594 | |
paul@136 | 595 | const_path = encode_literal_constant(n) |
paul@136 | 596 | structure_name = encode_literal_reference(n) |
paul@136 | 597 | |
paul@397 | 598 | ref = Reference("<instance>", value_type) |
paul@406 | 599 | self.make_constant(f_decls, f_defs, ref, const_path, structure_name, value, encoding) |
paul@136 | 600 | |
paul@136 | 601 | def make_predefined_constant(self, f_decls, f_defs, path, name): |
paul@136 | 602 | |
paul@136 | 603 | """ |
paul@136 | 604 | Write predefined constant details to 'f_decls' (to declare a structure) |
paul@136 | 605 | and to 'f_defs' (to define the contents) for the constant located in |
paul@136 | 606 | 'path' with the given 'name'. |
paul@136 | 607 | """ |
paul@136 | 608 | |
paul@136 | 609 | # Determine the details of the constant. |
paul@136 | 610 | |
paul@136 | 611 | attr_path = "%s.%s" % (path, name) |
paul@136 | 612 | structure_name = encode_predefined_reference(attr_path) |
paul@136 | 613 | ref = self.importer.get_object(attr_path) |
paul@136 | 614 | |
paul@136 | 615 | self.make_constant(f_decls, f_defs, ref, attr_path, structure_name) |
paul@136 | 616 | |
paul@758 | 617 | def make_common_integer(self, f_decls, f_defs): |
paul@758 | 618 | |
paul@758 | 619 | """ |
paul@758 | 620 | Write common integer instance details to 'f_decls' (to declare a |
paul@758 | 621 | structure) and to 'f_defs' (to define the contents). |
paul@758 | 622 | """ |
paul@758 | 623 | |
paul@758 | 624 | ref = Reference("<instance>", self.int_type) |
paul@758 | 625 | self.make_constant(f_decls, f_defs, ref, "__common_integer", "__common_integer_obj") |
paul@758 | 626 | |
paul@406 | 627 | def make_constant(self, f_decls, f_defs, ref, const_path, structure_name, data=None, encoding=None): |
paul@136 | 628 | |
paul@136 | 629 | """ |
paul@136 | 630 | Write constant details to 'f_decls' (to declare a structure) and to |
paul@136 | 631 | 'f_defs' (to define the contents) for the constant described by 'ref' |
paul@758 | 632 | having the given 'const_path' (providing an attribute for the constant) |
paul@758 | 633 | and 'structure_name' (for the constant structure itself). |
paul@406 | 634 | |
paul@406 | 635 | The additional 'data' and 'encoding' are used to describe specific |
paul@406 | 636 | values. |
paul@136 | 637 | """ |
paul@136 | 638 | |
paul@136 | 639 | # Obtain the attributes. |
paul@136 | 640 | |
paul@136 | 641 | cls = ref.get_origin() |
paul@847 | 642 | attrnames = self.optimiser.all_attrs[ref] |
paul@136 | 643 | attrs = self.get_instance_attributes(cls, attrnames) |
paul@136 | 644 | |
paul@136 | 645 | # Set the data, if provided. |
paul@136 | 646 | |
paul@136 | 647 | if data is not None: |
paul@850 | 648 | |
paul@850 | 649 | # Data retained by special attribute. |
paul@850 | 650 | |
paul@850 | 651 | if attrs.has_key("__data__"): |
paul@850 | 652 | attrs["__data__"] = data |
paul@850 | 653 | |
paul@850 | 654 | # Data retained by a trailing data area. |
paul@850 | 655 | |
paul@850 | 656 | elif attrs.has_key("__trailing__"): |
paul@850 | 657 | attrs["__trailing__"] = data |
paul@136 | 658 | |
paul@360 | 659 | # Also set a key for dynamic attribute lookup, if a string. |
paul@360 | 660 | |
paul@397 | 661 | if attrs.has_key("__key__"): |
paul@360 | 662 | if data in self.optimiser.all_attrnames: |
paul@360 | 663 | attrs["__key__"] = data |
paul@360 | 664 | else: |
paul@360 | 665 | attrs["__key__"] = None |
paul@360 | 666 | |
paul@583 | 667 | # Initialise the size, if a string. |
paul@583 | 668 | |
paul@583 | 669 | if attrs.has_key("__size__"): |
paul@583 | 670 | attrs["__size__"] = len(data) |
paul@583 | 671 | |
paul@400 | 672 | # Define Unicode constant encoding details. |
paul@400 | 673 | |
paul@400 | 674 | if cls == self.unicode_type: |
paul@406 | 675 | |
paul@406 | 676 | # Reference the encoding's own constant value. |
paul@406 | 677 | |
paul@406 | 678 | if encoding: |
paul@406 | 679 | n = self.optimiser.constants[(encoding, self.string_type, None)] |
paul@406 | 680 | |
paul@406 | 681 | # Employ a special alias that will be tested specifically in |
paul@406 | 682 | # encode_member. |
paul@406 | 683 | |
paul@609 | 684 | encoding_ref = Reference("<instance>", self.string_type, "$c%s" % n) |
paul@406 | 685 | |
paul@406 | 686 | # Use None where no encoding was indicated. |
paul@406 | 687 | |
paul@406 | 688 | else: |
paul@406 | 689 | encoding_ref = Reference("<instance>", self.none_type) |
paul@406 | 690 | |
paul@406 | 691 | attrs["encoding"] = encoding_ref |
paul@400 | 692 | |
paul@136 | 693 | # Define the structure details. An object is created for the constant, |
paul@136 | 694 | # but an attribute is provided, referring to the object, for access to |
paul@136 | 695 | # the constant in the program. |
paul@136 | 696 | |
paul@136 | 697 | structure = [] |
paul@850 | 698 | trailing = [] |
paul@150 | 699 | table_name = encode_tablename("<instance>", cls) |
paul@136 | 700 | self.populate_structure(ref, attrs, ref.get_kind(), structure) |
paul@850 | 701 | self.populate_trailing(ref, attrs, trailing) |
paul@850 | 702 | self.write_structure(f_decls, f_defs, structure_name, table_name, |
paul@850 | 703 | structure, trailing, ref) |
paul@136 | 704 | |
paul@136 | 705 | # Define a macro for the constant. |
paul@136 | 706 | |
paul@136 | 707 | attr_name = encode_path(const_path) |
paul@626 | 708 | print >>f_decls, "#define %s __ATTRVALUE(&%s)" % (attr_name, structure_name) |
paul@136 | 709 | |
paul@579 | 710 | def make_parameter_table(self, f_decls, f_defs, argmin, parameters): |
paul@126 | 711 | |
paul@126 | 712 | """ |
paul@126 | 713 | Write parameter table details to 'f_decls' (to declare a table) and to |
paul@579 | 714 | 'f_defs' (to define the contents) for the given 'argmin' and |
paul@579 | 715 | 'parameters'. |
paul@126 | 716 | """ |
paul@126 | 717 | |
paul@357 | 718 | # Use a signature for the table name instead of a separate name for each |
paul@357 | 719 | # function. |
paul@357 | 720 | |
paul@579 | 721 | signature = self.get_parameter_signature(argmin, parameters) |
paul@357 | 722 | table_name = encode_tablename("<function>", signature) |
paul@579 | 723 | min_parameters = encode_size("pmin", signature) |
paul@579 | 724 | max_parameters = encode_size("pmax", signature) |
paul@579 | 725 | structure_size = encode_size("psize", signature) |
paul@357 | 726 | |
paul@126 | 727 | table = [] |
paul@357 | 728 | self.populate_parameter_table(parameters, table) |
paul@579 | 729 | self.write_parameter_table(f_decls, f_defs, table_name, min_parameters, max_parameters, structure_size, table) |
paul@126 | 730 | |
paul@579 | 731 | def get_parameter_signature(self, argmin, parameters): |
paul@357 | 732 | |
paul@579 | 733 | "Return a signature for the given 'argmin' and 'parameters'." |
paul@357 | 734 | |
paul@579 | 735 | l = [str(argmin)] |
paul@357 | 736 | for parameter in parameters: |
paul@357 | 737 | if parameter is None: |
paul@357 | 738 | l.append("") |
paul@357 | 739 | else: |
paul@357 | 740 | name, pos = parameter |
paul@361 | 741 | l.append("%s_%s" % (name, pos)) |
paul@357 | 742 | return l and "__".join(l) or "__void" |
paul@357 | 743 | |
paul@357 | 744 | def get_signature_for_callable(self, path): |
paul@357 | 745 | |
paul@357 | 746 | "Return the signature for the callable with the given 'path'." |
paul@357 | 747 | |
paul@357 | 748 | function_path = self.callables[path] |
paul@579 | 749 | argmin, argmax = self.get_argument_limits(function_path) |
paul@357 | 750 | parameters = self.optimiser.parameters[function_path] |
paul@579 | 751 | return self.get_parameter_signature(argmin, parameters) |
paul@357 | 752 | |
paul@126 | 753 | def write_size_constants(self, f_consts, size_prefix, sizes, padding): |
paul@126 | 754 | |
paul@126 | 755 | """ |
paul@126 | 756 | Write size constants to 'f_consts' for the given 'size_prefix', using |
paul@126 | 757 | the 'sizes' dictionary to populate the definition, adding the given |
paul@126 | 758 | 'padding' to the basic sizes. |
paul@126 | 759 | """ |
paul@126 | 760 | |
paul@126 | 761 | print >>f_consts, "enum %s {" % encode_size(size_prefix) |
paul@126 | 762 | first = True |
paul@126 | 763 | for path, size in sizes.items(): |
paul@126 | 764 | if not first: |
paul@126 | 765 | print >>f_consts, "," |
paul@126 | 766 | else: |
paul@126 | 767 | first = False |
paul@126 | 768 | f_consts.write(" %s = %d" % (encode_size(size_prefix, path), size + padding)) |
paul@126 | 769 | print >>f_consts, "\n };" |
paul@126 | 770 | |
paul@623 | 771 | def write_code_constants(self, f_consts, attrnames, locations, code_prefix, |
paul@623 | 772 | pos_prefix, code_encoder, pos_encoder): |
paul@126 | 773 | |
paul@126 | 774 | """ |
paul@126 | 775 | Write code constants to 'f_consts' for the given 'attrnames' and |
paul@126 | 776 | attribute 'locations'. |
paul@126 | 777 | """ |
paul@126 | 778 | |
paul@132 | 779 | print >>f_consts, "enum %s {" % encode_symbol(code_prefix) |
paul@126 | 780 | first = True |
paul@126 | 781 | for i, attrname in enumerate(attrnames): |
paul@126 | 782 | if not first: |
paul@126 | 783 | print >>f_consts, "," |
paul@126 | 784 | else: |
paul@126 | 785 | first = False |
paul@623 | 786 | f_consts.write(" %s = %d" % (code_encoder(attrname), i)) |
paul@126 | 787 | print >>f_consts, "\n };" |
paul@126 | 788 | |
paul@132 | 789 | print >>f_consts, "enum %s {" % encode_symbol(pos_prefix) |
paul@126 | 790 | first = True |
paul@126 | 791 | for i, attrnames in enumerate(locations): |
paul@126 | 792 | for attrname in attrnames: |
paul@126 | 793 | if not first: |
paul@126 | 794 | print >>f_consts, "," |
paul@126 | 795 | else: |
paul@126 | 796 | first = False |
paul@623 | 797 | f_consts.write(" %s = %d" % (pos_encoder(attrname), i)) |
paul@126 | 798 | print >>f_consts, "\n };" |
paul@126 | 799 | |
paul@126 | 800 | def write_table(self, f_decls, f_defs, table_name, structure_size, table): |
paul@126 | 801 | |
paul@126 | 802 | """ |
paul@126 | 803 | Write the declarations to 'f_decls' and definitions to 'f_defs' for |
paul@126 | 804 | the object having the given 'table_name' and the given 'structure_size', |
paul@126 | 805 | with 'table' details used to populate the definition. |
paul@126 | 806 | """ |
paul@126 | 807 | |
paul@126 | 808 | print >>f_decls, "extern const __table %s;\n" % table_name |
paul@126 | 809 | |
paul@126 | 810 | # Write the corresponding definition. |
paul@126 | 811 | |
paul@570 | 812 | print >>f_defs, """\ |
paul@570 | 813 | const __table %s = { |
paul@570 | 814 | %s, |
paul@570 | 815 | { |
paul@570 | 816 | %s |
paul@570 | 817 | } |
paul@570 | 818 | }; |
paul@579 | 819 | """ % (table_name, structure_size, |
paul@579 | 820 | ",\n ".join(table)) |
paul@126 | 821 | |
paul@579 | 822 | def write_parameter_table(self, f_decls, f_defs, table_name, min_parameters, |
paul@579 | 823 | max_parameters, structure_size, table): |
paul@126 | 824 | |
paul@126 | 825 | """ |
paul@126 | 826 | Write the declarations to 'f_decls' and definitions to 'f_defs' for |
paul@579 | 827 | the object having the given 'table_name' and the given 'min_parameters', |
paul@579 | 828 | 'max_parameters' and 'structure_size', with 'table' details used to |
paul@579 | 829 | populate the definition. |
paul@126 | 830 | """ |
paul@126 | 831 | |
paul@570 | 832 | members = [] |
paul@570 | 833 | for t in table: |
paul@579 | 834 | members.append("{.code=%s, .pos=%s}" % t) |
paul@570 | 835 | |
paul@126 | 836 | print >>f_decls, "extern const __ptable %s;\n" % table_name |
paul@126 | 837 | |
paul@126 | 838 | # Write the corresponding definition. |
paul@126 | 839 | |
paul@570 | 840 | print >>f_defs, """\ |
paul@570 | 841 | const __ptable %s = { |
paul@579 | 842 | .min=%s, |
paul@579 | 843 | .max=%s, |
paul@579 | 844 | .size=%s, |
paul@570 | 845 | { |
paul@570 | 846 | %s |
paul@570 | 847 | } |
paul@570 | 848 | }; |
paul@579 | 849 | """ % (table_name, min_parameters, max_parameters, structure_size, |
paul@579 | 850 | ",\n ".join(members)) |
paul@126 | 851 | |
paul@884 | 852 | def write_instance_structure(self, f_decls, path): |
paul@126 | 853 | |
paul@126 | 854 | """ |
paul@884 | 855 | Write a declaration to 'f_decls' for the object having the given 'path'. |
paul@126 | 856 | """ |
paul@126 | 857 | |
paul@884 | 858 | structure_size = encode_size("<instance>", path) |
paul@884 | 859 | |
paul@126 | 860 | # Write an instance-specific type definition for instances of classes. |
paul@126 | 861 | # See: templates/types.h |
paul@126 | 862 | |
paul@850 | 863 | trailing_area = path in self.trailing_data_types and encode_trailing_area(path) or "" |
paul@850 | 864 | |
paul@126 | 865 | print >>f_decls, """\ |
paul@126 | 866 | typedef struct { |
paul@126 | 867 | const __table * table; |
paul@568 | 868 | __pos pos; |
paul@1041 | 869 | __int obj_size; |
paul@126 | 870 | __attr attrs[%s]; |
paul@850 | 871 | %s |
paul@126 | 872 | } %s; |
paul@850 | 873 | """ % (structure_size, trailing_area, encode_symbol("obj", path)) |
paul@136 | 874 | |
paul@850 | 875 | def write_structure(self, f_decls, f_defs, structure_name, table_name, |
paul@850 | 876 | structure, trailing, ref): |
paul@126 | 877 | |
paul@136 | 878 | """ |
paul@136 | 879 | Write the declarations to 'f_decls' and definitions to 'f_defs' for |
paul@136 | 880 | the object having the given 'structure_name', the given 'table_name', |
paul@850 | 881 | the given 'structure' details and any 'trailing' member details, used to |
paul@850 | 882 | populate the definition. |
paul@136 | 883 | """ |
paul@126 | 884 | |
paul@850 | 885 | origin = ref.get_origin() |
paul@850 | 886 | pos = ref.has_kind("<class>") and encode_pos(encode_type_attribute(origin)) or str(self.instancepos) |
paul@850 | 887 | |
paul@850 | 888 | obj_type = ref.has_kind("<instance>") and encode_symbol("obj", origin) or "__obj" |
paul@850 | 889 | obj_name = encode_path(structure_name) |
paul@850 | 890 | |
paul@1041 | 891 | obj_size = ref.has_kind("<class>") and structure_name in self.copiable_types and \ |
paul@1041 | 892 | "sizeof(%s)" % encode_symbol("obj", structure_name) or \ |
paul@1041 | 893 | "0" |
paul@1041 | 894 | |
paul@136 | 895 | if f_decls: |
paul@850 | 896 | print >>f_decls, "extern %s %s;\n" % (obj_type, obj_name) |
paul@132 | 897 | |
paul@132 | 898 | print >>f_defs, """\ |
paul@850 | 899 | %s %s = { |
paul@132 | 900 | &%s, |
paul@132 | 901 | %s, |
paul@1041 | 902 | %s, |
paul@132 | 903 | { |
paul@132 | 904 | %s |
paul@850 | 905 | }, |
paul@850 | 906 | %s |
paul@850 | 907 | }; |
paul@132 | 908 | """ % ( |
paul@850 | 909 | obj_type, obj_name, |
paul@850 | 910 | table_name, |
paul@850 | 911 | pos, |
paul@1041 | 912 | obj_size, |
paul@850 | 913 | ",\n ".join(structure), |
paul@850 | 914 | trailing and ",\n ".join(trailing) or "") |
paul@126 | 915 | |
paul@132 | 916 | def get_argument_limits(self, path): |
paul@126 | 917 | |
paul@132 | 918 | """ |
paul@132 | 919 | Return the argument minimum and maximum for the callable at 'path', |
paul@132 | 920 | adding an argument position for a universal context. |
paul@132 | 921 | """ |
paul@132 | 922 | |
paul@126 | 923 | parameters = self.importer.function_parameters[path] |
paul@126 | 924 | defaults = self.importer.function_defaults.get(path) |
paul@971 | 925 | num_parameters = len(parameters) + self.extra_args |
paul@132 | 926 | return num_parameters - (defaults and len(defaults) or 0), num_parameters |
paul@126 | 927 | |
paul@126 | 928 | def get_static_attributes(self, kind, name, attrnames): |
paul@126 | 929 | |
paul@126 | 930 | """ |
paul@126 | 931 | Return a mapping of attribute names to paths for attributes belonging |
paul@126 | 932 | to objects of the given 'kind' (being "<class>" or "<module>") with |
paul@126 | 933 | the given 'name' and supporting the given 'attrnames'. |
paul@126 | 934 | """ |
paul@126 | 935 | |
paul@126 | 936 | attrs = {} |
paul@126 | 937 | |
paul@126 | 938 | for attrname in attrnames: |
paul@126 | 939 | if attrname is None: |
paul@126 | 940 | continue |
paul@126 | 941 | if kind == "<class>": |
paul@126 | 942 | path = self.importer.all_class_attrs[name][attrname] |
paul@126 | 943 | elif kind == "<module>": |
paul@126 | 944 | path = "%s.%s" % (name, attrname) |
paul@126 | 945 | else: |
paul@126 | 946 | continue |
paul@126 | 947 | |
paul@126 | 948 | # The module may be hidden. |
paul@126 | 949 | |
paul@126 | 950 | attr = self.importer.get_object(path) |
paul@126 | 951 | if not attr: |
paul@126 | 952 | module = self.importer.hidden.get(path) |
paul@126 | 953 | if module: |
paul@126 | 954 | attr = Reference(module.name, "<module>") |
paul@126 | 955 | attrs[attrname] = attr |
paul@126 | 956 | |
paul@126 | 957 | return attrs |
paul@126 | 958 | |
paul@126 | 959 | def get_instance_attributes(self, name, attrnames): |
paul@126 | 960 | |
paul@126 | 961 | """ |
paul@126 | 962 | Return a mapping of attribute names to references for attributes |
paul@126 | 963 | belonging to instances of the class with the given 'name', where the |
paul@126 | 964 | given 'attrnames' are supported. |
paul@126 | 965 | """ |
paul@126 | 966 | |
paul@126 | 967 | consts = self.importer.all_instance_attr_constants[name] |
paul@126 | 968 | attrs = {} |
paul@126 | 969 | for attrname in attrnames: |
paul@126 | 970 | if attrname is None: |
paul@126 | 971 | continue |
paul@126 | 972 | const = consts.get(attrname) |
paul@126 | 973 | attrs[attrname] = const or Reference("<var>", "%s.%s" % (name, attrname)) |
paul@850 | 974 | |
paul@850 | 975 | # Instances with trailing data. |
paul@850 | 976 | |
paul@850 | 977 | if name in self.trailing_data_types: |
paul@850 | 978 | attrs["__trailing__"] = Reference("<var>", "%s.__trailing__" % name) |
paul@850 | 979 | |
paul@126 | 980 | return attrs |
paul@126 | 981 | |
paul@357 | 982 | def populate_table(self, path, table): |
paul@126 | 983 | |
paul@126 | 984 | """ |
paul@126 | 985 | Traverse the attributes in the determined order for the structure having |
paul@357 | 986 | the given 'path', adding entries to the attribute 'table'. |
paul@126 | 987 | """ |
paul@126 | 988 | |
paul@357 | 989 | for attrname in self.optimiser.structures[path]: |
paul@126 | 990 | |
paul@126 | 991 | # Handle gaps in the structure. |
paul@126 | 992 | |
paul@126 | 993 | if attrname is None: |
paul@126 | 994 | table.append("0") |
paul@126 | 995 | else: |
paul@623 | 996 | table.append(encode_code(attrname)) |
paul@126 | 997 | |
paul@357 | 998 | def populate_parameter_table(self, parameters, table): |
paul@126 | 999 | |
paul@126 | 1000 | """ |
paul@357 | 1001 | Traverse the 'parameters' in the determined order, adding entries to the |
paul@357 | 1002 | attribute 'table'. |
paul@126 | 1003 | """ |
paul@126 | 1004 | |
paul@357 | 1005 | for value in parameters: |
paul@126 | 1006 | |
paul@126 | 1007 | # Handle gaps in the structure. |
paul@126 | 1008 | |
paul@126 | 1009 | if value is None: |
paul@126 | 1010 | table.append(("0", "0")) |
paul@126 | 1011 | else: |
paul@126 | 1012 | name, pos = value |
paul@126 | 1013 | table.append((encode_symbol("pcode", name), pos)) |
paul@126 | 1014 | |
paul@523 | 1015 | def populate_function(self, path, function_instance_attrs): |
paul@126 | 1016 | |
paul@126 | 1017 | """ |
paul@126 | 1018 | Populate a structure for the function with the given 'path'. The given |
paul@523 | 1019 | 'attrs' provide the instance attributes. |
paul@126 | 1020 | """ |
paul@126 | 1021 | |
paul@126 | 1022 | structure = [] |
paul@523 | 1023 | self.populate_structure(Reference("<function>", path), function_instance_attrs, "<instance>", structure) |
paul@126 | 1024 | |
paul@126 | 1025 | # Append default members. |
paul@126 | 1026 | |
paul@126 | 1027 | self.append_defaults(path, structure) |
paul@126 | 1028 | return structure |
paul@126 | 1029 | |
paul@523 | 1030 | def populate_structure(self, ref, attrs, kind, structure): |
paul@126 | 1031 | |
paul@126 | 1032 | """ |
paul@126 | 1033 | Traverse the attributes in the determined order for the structure having |
paul@126 | 1034 | the given 'ref' whose members are provided by the 'attrs' mapping, in a |
paul@126 | 1035 | structure of the given 'kind', adding entries to the object 'structure'. |
paul@126 | 1036 | """ |
paul@126 | 1037 | |
paul@847 | 1038 | structure_ref = self.get_target_structure(ref) |
paul@847 | 1039 | origin = structure_ref.get_origin() |
paul@174 | 1040 | |
paul@174 | 1041 | for attrname in self.optimiser.structures[structure_ref]: |
paul@126 | 1042 | |
paul@126 | 1043 | # Handle gaps in the structure. |
paul@126 | 1044 | |
paul@126 | 1045 | if attrname is None: |
paul@477 | 1046 | structure.append("__NULL") |
paul@126 | 1047 | |
paul@126 | 1048 | # Handle non-constant and constant members. |
paul@126 | 1049 | |
paul@126 | 1050 | else: |
paul@126 | 1051 | attr = attrs[attrname] |
paul@126 | 1052 | |
paul@136 | 1053 | # Special function pointer member. |
paul@136 | 1054 | |
paul@126 | 1055 | if attrname == "__fn__": |
paul@126 | 1056 | |
paul@523 | 1057 | # Classes offer instantiators which can be called without a |
paul@523 | 1058 | # context. |
paul@126 | 1059 | |
paul@126 | 1060 | if kind == "<class>": |
paul@126 | 1061 | attr = encode_instantiator_pointer(attr) |
paul@126 | 1062 | else: |
paul@126 | 1063 | attr = encode_function_pointer(attr) |
paul@126 | 1064 | |
paul@577 | 1065 | structure.append("{.fn=%s}" % attr) |
paul@126 | 1066 | continue |
paul@126 | 1067 | |
paul@136 | 1068 | # Special argument specification member. |
paul@136 | 1069 | |
paul@126 | 1070 | elif attrname == "__args__": |
paul@357 | 1071 | signature = self.get_signature_for_callable(ref.get_origin()) |
paul@357 | 1072 | ptable = encode_tablename("<function>", signature) |
paul@357 | 1073 | |
paul@579 | 1074 | structure.append("{.ptable=&%s}" % ptable) |
paul@126 | 1075 | continue |
paul@126 | 1076 | |
paul@136 | 1077 | # Special internal data member. |
paul@136 | 1078 | |
paul@136 | 1079 | elif attrname == "__data__": |
paul@569 | 1080 | structure.append("{.%s=%s}" % ( |
paul@378 | 1081 | encode_literal_constant_member(attr), |
paul@378 | 1082 | encode_literal_constant_value(attr))) |
paul@136 | 1083 | continue |
paul@136 | 1084 | |
paul@583 | 1085 | # Special internal size member. |
paul@583 | 1086 | |
paul@583 | 1087 | elif attrname == "__size__": |
paul@974 | 1088 | structure.append("{.intvalue=__FROMINT(%d)}" % attr) |
paul@583 | 1089 | continue |
paul@583 | 1090 | |
paul@360 | 1091 | # Special internal key member. |
paul@360 | 1092 | |
paul@360 | 1093 | elif attrname == "__key__": |
paul@623 | 1094 | structure.append("{.code=%s, .pos=%s}" % (attr and encode_code(attr) or "0", |
paul@623 | 1095 | attr and encode_pos(attr) or "0")) |
paul@360 | 1096 | continue |
paul@360 | 1097 | |
paul@251 | 1098 | # Special cases. |
paul@251 | 1099 | |
paul@499 | 1100 | elif attrname in ("__file__", "__name__"): |
paul@251 | 1101 | path = ref.get_origin() |
paul@397 | 1102 | value_type = self.string_type |
paul@271 | 1103 | |
paul@489 | 1104 | # Provide constant values. These must match the values |
paul@489 | 1105 | # originally recorded during inspection. |
paul@489 | 1106 | |
paul@271 | 1107 | if attrname == "__file__": |
paul@271 | 1108 | module = self.importer.get_module(path) |
paul@271 | 1109 | value = module.filename |
paul@489 | 1110 | |
paul@489 | 1111 | # Function and class names are leafnames. |
paul@489 | 1112 | |
paul@499 | 1113 | elif attrname == "__name__" and not ref.has_kind("<module>"): |
paul@489 | 1114 | value = path.rsplit(".", 1)[-1] |
paul@489 | 1115 | |
paul@489 | 1116 | # All other names just use the object path information. |
paul@489 | 1117 | |
paul@271 | 1118 | else: |
paul@271 | 1119 | value = path |
paul@271 | 1120 | |
paul@406 | 1121 | encoding = None |
paul@406 | 1122 | |
paul@406 | 1123 | local_number = self.importer.all_constants[path][(value, value_type, encoding)] |
paul@251 | 1124 | constant_name = "$c%d" % local_number |
paul@251 | 1125 | attr_path = "%s.%s" % (path, constant_name) |
paul@251 | 1126 | constant_number = self.optimiser.constant_numbers[attr_path] |
paul@609 | 1127 | constant_value = "__const%s" % constant_number |
paul@251 | 1128 | structure.append("%s /* %s */" % (constant_value, attrname)) |
paul@251 | 1129 | continue |
paul@251 | 1130 | |
paul@499 | 1131 | elif attrname == "__parent__": |
paul@499 | 1132 | path = ref.get_origin() |
paul@499 | 1133 | |
paul@499 | 1134 | # Parents of classes and functions are derived from their |
paul@499 | 1135 | # object paths. |
paul@499 | 1136 | |
paul@499 | 1137 | value = path.rsplit(".", 1)[0] |
paul@577 | 1138 | structure.append("{.value=&%s}" % encode_path(value)) |
paul@577 | 1139 | continue |
paul@577 | 1140 | |
paul@577 | 1141 | # Special context member. |
paul@577 | 1142 | # Set the context depending on the kind of attribute. |
paul@577 | 1143 | # For methods: <parent> |
paul@577 | 1144 | # For other attributes: __NULL |
paul@577 | 1145 | |
paul@577 | 1146 | elif attrname == "__context__": |
paul@577 | 1147 | path = ref.get_origin() |
paul@577 | 1148 | |
paul@577 | 1149 | # Contexts of methods are derived from their object paths. |
paul@577 | 1150 | |
paul@577 | 1151 | context = "0" |
paul@577 | 1152 | |
paul@577 | 1153 | if ref.get_kind() == "<function>": |
paul@577 | 1154 | parent = path.rsplit(".", 1)[0] |
paul@577 | 1155 | if self.importer.classes.has_key(parent): |
paul@577 | 1156 | context = "&%s" % encode_path(parent) |
paul@577 | 1157 | |
paul@577 | 1158 | structure.append("{.value=%s}" % context) |
paul@499 | 1159 | continue |
paul@499 | 1160 | |
paul@318 | 1161 | # Special class relationship attributes. |
paul@318 | 1162 | |
paul@318 | 1163 | elif is_type_attribute(attrname): |
paul@577 | 1164 | structure.append("{.value=&%s}" % encode_path(decode_type_attribute(attrname))) |
paul@318 | 1165 | continue |
paul@318 | 1166 | |
paul@406 | 1167 | # All other kinds of members. |
paul@406 | 1168 | |
paul@126 | 1169 | structure.append(self.encode_member(origin, attrname, attr, kind)) |
paul@126 | 1170 | |
paul@850 | 1171 | def populate_trailing(self, ref, attrs, trailing): |
paul@850 | 1172 | |
paul@850 | 1173 | """ |
paul@850 | 1174 | For the structure having the given 'ref', whose members are provided by |
paul@850 | 1175 | the 'attrs' mapping, adding entries to the 'trailing' member collection. |
paul@850 | 1176 | """ |
paul@850 | 1177 | |
paul@850 | 1178 | structure_ref = self.get_target_structure(ref) |
paul@850 | 1179 | |
paul@850 | 1180 | # Instances with trailing data. |
paul@850 | 1181 | |
paul@850 | 1182 | if structure_ref.get_kind() == "<instance>" and \ |
paul@850 | 1183 | structure_ref.get_origin() in self.trailing_data_types: |
paul@850 | 1184 | trailing.append(encode_literal_constant_value(attrs["__trailing__"])) |
paul@850 | 1185 | |
paul@847 | 1186 | def get_target_structure(self, ref): |
paul@847 | 1187 | |
paul@847 | 1188 | "Return the target structure type and reference for 'ref'." |
paul@847 | 1189 | |
paul@847 | 1190 | # Populate function instance structures for functions. |
paul@847 | 1191 | |
paul@847 | 1192 | if ref.has_kind("<function>"): |
paul@847 | 1193 | return Reference("<instance>", self.function_type) |
paul@847 | 1194 | |
paul@847 | 1195 | # Otherwise, just populate the appropriate structures. |
paul@847 | 1196 | |
paul@847 | 1197 | else: |
paul@847 | 1198 | return ref |
paul@847 | 1199 | |
paul@126 | 1200 | def encode_member(self, path, name, ref, structure_type): |
paul@126 | 1201 | |
paul@126 | 1202 | """ |
paul@126 | 1203 | Encode within the structure provided by 'path', the member whose 'name' |
paul@126 | 1204 | provides 'ref', within the given 'structure_type'. |
paul@126 | 1205 | """ |
paul@126 | 1206 | |
paul@126 | 1207 | kind = ref.get_kind() |
paul@126 | 1208 | origin = ref.get_origin() |
paul@126 | 1209 | |
paul@126 | 1210 | # References to constant literals. |
paul@126 | 1211 | |
paul@338 | 1212 | if kind == "<instance>" and ref.is_constant_alias(): |
paul@338 | 1213 | alias = ref.get_name() |
paul@126 | 1214 | |
paul@406 | 1215 | # Use the alias directly if appropriate. |
paul@406 | 1216 | |
paul@406 | 1217 | if alias.startswith("$c"): |
paul@609 | 1218 | constant_value = encode_literal_constant(alias[2:]) |
paul@406 | 1219 | return "%s /* %s */" % (constant_value, name) |
paul@406 | 1220 | |
paul@126 | 1221 | # Obtain a constant value directly assigned to the attribute. |
paul@126 | 1222 | |
paul@338 | 1223 | if self.optimiser.constant_numbers.has_key(alias): |
paul@758 | 1224 | |
paul@758 | 1225 | # Encode integer constants differently. |
paul@758 | 1226 | |
paul@758 | 1227 | value, value_type, encoding = self.importer.all_constant_values[alias] |
paul@758 | 1228 | if value_type == self.int_type: |
paul@758 | 1229 | return "__INTVALUE(%s) /* %s */" % (value, name) |
paul@758 | 1230 | |
paul@338 | 1231 | constant_number = self.optimiser.constant_numbers[alias] |
paul@406 | 1232 | constant_value = encode_literal_constant(constant_number) |
paul@247 | 1233 | return "%s /* %s */" % (constant_value, name) |
paul@126 | 1234 | |
paul@400 | 1235 | # Usage of predefined constants, currently only None supported. |
paul@400 | 1236 | |
paul@400 | 1237 | if kind == "<instance>" and origin == self.none_type: |
paul@400 | 1238 | attr_path = encode_predefined_reference(self.none_value) |
paul@850 | 1239 | return "{.value=(__ref) &%s} /* %s */" % (attr_path, name) |
paul@400 | 1240 | |
paul@400 | 1241 | # Predefined constant members. |
paul@136 | 1242 | |
paul@136 | 1243 | if (path, name) in self.predefined_constant_members: |
paul@136 | 1244 | attr_path = encode_predefined_reference("%s.%s" % (path, name)) |
paul@850 | 1245 | return "{.value=(__ref) &%s} /* %s */" % (attr_path, name) |
paul@136 | 1246 | |
paul@126 | 1247 | # General undetermined members. |
paul@126 | 1248 | |
paul@126 | 1249 | if kind in ("<var>", "<instance>"): |
paul@404 | 1250 | attr_path = encode_predefined_reference(self.none_value) |
paul@850 | 1251 | return "{.value=(__ref) &%s} /* %s */" % (attr_path, name) |
paul@126 | 1252 | |
paul@126 | 1253 | else: |
paul@850 | 1254 | return "{.value=(__ref) &%s}" % encode_path(origin) |
paul@126 | 1255 | |
paul@126 | 1256 | def append_defaults(self, path, structure): |
paul@126 | 1257 | |
paul@126 | 1258 | """ |
paul@126 | 1259 | For the given 'path', append default parameter members to the given |
paul@126 | 1260 | 'structure'. |
paul@126 | 1261 | """ |
paul@126 | 1262 | |
paul@126 | 1263 | for name, default in self.importer.function_defaults.get(path): |
paul@126 | 1264 | structure.append(self.encode_member(path, name, default, "<instance>")) |
paul@126 | 1265 | |
paul@159 | 1266 | def write_instantiator(self, f_code, f_signatures, path, init_ref): |
paul@126 | 1267 | |
paul@126 | 1268 | """ |
paul@159 | 1269 | Write an instantiator to 'f_code', with a signature to 'f_signatures', |
paul@159 | 1270 | for instances of the class with the given 'path', with 'init_ref' as the |
paul@159 | 1271 | initialiser function reference. |
paul@126 | 1272 | |
paul@126 | 1273 | NOTE: This also needs to initialise any __fn__ and __args__ members |
paul@126 | 1274 | NOTE: where __call__ is provided by the class. |
paul@126 | 1275 | """ |
paul@126 | 1276 | |
paul@664 | 1277 | initialiser = init_ref.get_origin() |
paul@669 | 1278 | parameters = self.importer.function_parameters[initialiser] |
paul@664 | 1279 | argmin, argmax = self.get_argument_limits(initialiser) |
paul@126 | 1280 | |
paul@669 | 1281 | l = [] |
paul@669 | 1282 | for name in parameters: |
paul@669 | 1283 | l.append("__attr %s" % name) |
paul@126 | 1284 | |
paul@929 | 1285 | # Special-case the integer type. |
paul@929 | 1286 | |
paul@932 | 1287 | # Here, the __builtins__.int.new_int function is called with the |
paul@937 | 1288 | # initialiser's parameters. |
paul@932 | 1289 | |
paul@929 | 1290 | if path == self.int_type: |
paul@929 | 1291 | print >>f_code, """\ |
paul@971 | 1292 | __attr %s(__attr __result, __attr __self, __attr number_or_string, __attr base) |
paul@929 | 1293 | { |
paul@971 | 1294 | return __fn___builtins___int_new_int(__NULL, __NULL, number_or_string, base); |
paul@929 | 1295 | } |
paul@929 | 1296 | """ % ( |
paul@929 | 1297 | encode_instantiator_pointer(path), |
paul@929 | 1298 | ) |
paul@929 | 1299 | |
paul@938 | 1300 | # Special-case the string types. |
paul@938 | 1301 | |
paul@938 | 1302 | # Here, the __builtins__.str.new_str function is called with the |
paul@938 | 1303 | # initialiser's parameter. |
paul@938 | 1304 | |
paul@938 | 1305 | elif path == self.string_type: |
paul@938 | 1306 | print >>f_code, """\ |
paul@971 | 1307 | __attr %s(__attr __result, __attr __self, __attr obj) |
paul@938 | 1308 | { |
paul@971 | 1309 | return __fn___builtins___str_new_str(__NULL, __NULL, obj); |
paul@938 | 1310 | } |
paul@938 | 1311 | """ % ( |
paul@938 | 1312 | encode_instantiator_pointer(path), |
paul@938 | 1313 | ) |
paul@938 | 1314 | |
paul@929 | 1315 | # Generic instantiation support. |
paul@929 | 1316 | |
paul@929 | 1317 | else: |
paul@929 | 1318 | print >>f_code, """\ |
paul@971 | 1319 | __attr %s(__attr __result, __attr __self%s) |
paul@126 | 1320 | { |
paul@971 | 1321 | return %s(__NULL, __NEWINSTANCE(%s)%s); |
paul@126 | 1322 | } |
paul@126 | 1323 | """ % ( |
paul@929 | 1324 | encode_instantiator_pointer(path), |
paul@929 | 1325 | l and ", %s" % ",".join(l) or "", |
paul@929 | 1326 | encode_function_pointer(initialiser), |
paul@929 | 1327 | encode_path(path), |
paul@929 | 1328 | parameters and ", %s" % ", ".join(parameters) or "" |
paul@929 | 1329 | ) |
paul@669 | 1330 | |
paul@971 | 1331 | # Signature: __new_typename(__attr __result, __attr __self, ...) |
paul@669 | 1332 | |
paul@971 | 1333 | print >>f_signatures, "__attr %s(__attr __result, __attr __self%s);" % ( |
paul@669 | 1334 | encode_instantiator_pointer(path), |
paul@669 | 1335 | l and ", %s" % ", ".join(l) or "" |
paul@669 | 1336 | ) |
paul@126 | 1337 | |
paul@291 | 1338 | print >>f_signatures, "#define __HAVE_%s" % encode_path(path) |
paul@159 | 1339 | |
paul@146 | 1340 | def write_main_program(self, f_code, f_signatures): |
paul@146 | 1341 | |
paul@146 | 1342 | """ |
paul@146 | 1343 | Write the main program to 'f_code', invoking the program's modules. Also |
paul@146 | 1344 | write declarations for module main functions to 'f_signatures'. |
paul@146 | 1345 | """ |
paul@146 | 1346 | |
paul@146 | 1347 | print >>f_code, """\ |
paul@146 | 1348 | int main(int argc, char *argv[]) |
paul@159 | 1349 | { |
paul@190 | 1350 | __exc __tmp_exc; |
paul@272 | 1351 | |
paul@434 | 1352 | GC_INIT(); |
paul@434 | 1353 | |
paul@872 | 1354 | __signals_install_handlers(); |
paul@872 | 1355 | |
paul@159 | 1356 | __Try |
paul@159 | 1357 | {""" |
paul@146 | 1358 | |
paul@186 | 1359 | for name in self.importer.order_modules(): |
paul@146 | 1360 | function_name = "__main_%s" % encode_path(name) |
paul@146 | 1361 | print >>f_signatures, "void %s();" % function_name |
paul@146 | 1362 | |
paul@354 | 1363 | # Omit the native modules. |
paul@146 | 1364 | |
paul@354 | 1365 | parts = name.split(".") |
paul@354 | 1366 | |
paul@354 | 1367 | if parts[0] != "native": |
paul@146 | 1368 | print >>f_code, """\ |
paul@165 | 1369 | %s();""" % function_name |
paul@146 | 1370 | |
paul@146 | 1371 | print >>f_code, """\ |
paul@159 | 1372 | } |
paul@190 | 1373 | __Catch(__tmp_exc) |
paul@159 | 1374 | { |
paul@626 | 1375 | if (__ISINSTANCE(__tmp_exc.arg, __ATTRVALUE(&__builtins___exception_system_SystemExit))) |
paul@758 | 1376 | return __TOINT(__load_via_object(__VALUE(__tmp_exc.arg), value)); |
paul@464 | 1377 | |
paul@273 | 1378 | fprintf(stderr, "Program terminated due to exception: %%s.\\n", |
paul@272 | 1379 | __load_via_object( |
paul@971 | 1380 | __VALUE(%s(__NULL, __NULL, __tmp_exc.arg)), |
paul@624 | 1381 | __data__).strvalue); |
paul@159 | 1382 | return 1; |
paul@159 | 1383 | } |
paul@477 | 1384 | |
paul@477 | 1385 | return 0; |
paul@146 | 1386 | } |
paul@938 | 1387 | """ % encode_instantiator_pointer("__builtins__.str.str") |
paul@146 | 1388 | |
paul@126 | 1389 | # vim: tabstop=4 expandtab shiftwidth=4 |