Lichen

Annotated generator.py

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