Lichen

Annotated generator.py

1036:e8ff2a117367
5 months ago Paul Boddie Fixed accessor temporary storage reservation for lambdas and eliminated redundant temporary storage reservation in attribute accesses.
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